/* ==========================================================================
   Biznes Paket — стили сайта
   Структура файла: шрифты → токены → сброс → база → утилиты → компоненты
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Шрифты (self-hosted, кириллица + латиница, только нужные начертания)
   -------------------------------------------------------------------------- */

@font-face {
  font-family: 'Golos Text';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/golos-400-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Golos Text';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/golos-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2122;
}
@font-face {
  font-family: 'Golos Text';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/golos-500-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Golos Text';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/golos-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2122;
}
@font-face {
  font-family: 'Golos Text';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/golos-700-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Golos Text';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/golos-700-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2122;
}
@font-face {
  font-family: 'Inter Tight';
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url('../fonts/interTight-900-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'Inter Tight';
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url('../fonts/interTight-900-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2122;
}
@font-face {
  /* Основной дисплейный serif — h1 и все крупные числа сайта (--font-display
     ниже). Лицензия — настоящий SIL OFL, зашита прямо в файл шрифта
     (проверено распаковкой .otf, не только описанием на сайте-агрегаторе —
     см. историю с Safira March, где было наоборот). Кириллица — все 94
     нужных глифа, тоже проверено напрямую через cmap. */
  font-family: 'LT Remark';
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url('../fonts/lt-remark-900-cyrillic.woff2') format('woff2');
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face {
  font-family: 'LT Remark';
  font-style: normal;
  font-weight: 900;
  font-display: swap;
  src: url('../fonts/lt-remark-900-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+2000-206F, U+2122;
}
/* Playfair Display и Yeseva One убраны (были 3-м и 4-м голосом шрифта
   на сайте) — дисциплина в духе Apple: одна гарнитура на весь текст
   (Golos Text), одна на весь дисплей/цифры (LT Remark), ничего третьего.
   См. h2/.lede/.h1__accent ниже — все теперь на этих двух. */

/* --------------------------------------------------------------------------
   2. Дизайн-токены
   -------------------------------------------------------------------------- */

:root {
  /* Светлая палитра «чертёж + гравюра»: тёплая бумага вместо графита,
     глубокие чернильно-синие чернила вместо дымчатого акцента на тёмном.
     Референсы — постер-каталог выставки (сетка, точки, синие чернила
     на светлой бумаге) и винтажная гравюра (тот же синий монохром,
     благородный serif). Синий — акцент, не узор: раньше был на КАЖДОМ
     декоративном элементе (медальоны-разделители, метки секций, номера
     шагов), из-за чего переставал считываться как сигнал. Теперь
     нормирован — только кнопки, активные/выбранные состояния и
     ключевые цифры (тираж, статистика). Единство дизайна держат
     светлая бумага и повторяющиеся формы, не разлитый везде синий. */
  --ink-900: #f1eee6;   /* фон страницы — тёплая бумага, не белый */
  --ink-800: #fbf9f3;   /* карточки, поверхности — светлее страницы */
  --ink-700: #e8edf1;   /* поверхности hover/active — лёгкий холодный вош в цвет акцента */
  --ink-600: #d6dbe0;   /* границы, разделители — приглушённый холодный нейтральный */
  --paper: #20242e;     /* основной текст — не чёрный, тёмно-графитовый с синим подтоном */
  /* #666e7a на фоне страницы #F1EEE6 давал 4.44:1 — на волосок ниже
     порога WCAG AA 4.5:1 для обычного текста (site-audit 2026-09-02,
     находка №3). #656d79 (на 1 пункт темнее по каждому каналу) даёт
     4.51:1 — визуально неотличимо, порог пройден. */
  --paper-dim: #656d79; /* вторичный текст, подписи */
  --signal: #2c4a72;    /* акцент: только кнопки, активные состояния, ключевые цифры — не декоративная графика */
  --signal-ink: #fbf9f3;/* текст на акцентной заливке */
  --error: #e0674a;     /* только ошибки валидации формы — единственный цвет сайта вне синей/нейтральной палитры, нужен чтобы не путать с --signal */

  /* Mesh-фон (единый на весь сайт) — тёплая бумага с лёгким зерном,
     без новых цветов вне палитры. Раньше был ещё угловой тональный вош
     (--mesh-drift-*) — убран, читался как грязное пятно в углу экрана
     (см. комментарий у .mesh-bg__grain). */
  --mesh-grain-opacity: 0.045;

  /* Шрифты */
  --font-display: 'LT Remark', Georgia, serif; /* h1, все h2, все крупные числа — единственный дисплейный голос сайта */
  --font-logo: 'Inter Tight', system-ui, sans-serif; /* только вордмарк BIZNES PAKET в шапке/подвале — вне этой замены */
  --font-text: 'Golos Text', system-ui, sans-serif; /* весь остальной текст сайта */

  /* Типографика (mobile-first, растёт через clamp) */
  --fs-h1: clamp(2.375rem, 1.1rem + 6.5vw, 5.75rem);
  --fs-offer: clamp(4.75rem, 0rem + 24vw, 13.75rem);
  --fs-h2: clamp(1.875rem, 1rem + 4vw, 3.625rem);
  --fs-h3: 1.25rem;
  --fs-lede: clamp(1.125rem, 1.05rem + 0.4vw, 1.3125rem); /* подзаголовок-лид, 5 секций сайта */
  --fs-body: 1.0625rem;   /* 17px */
  --fs-body-lg: 1.125rem; /* 18px, десктоп */
  --fs-small: 0.9375rem;  /* 15px — минимум по требованиям */
  --fs-caption: 0.8125rem; /* 13px — мелкий служебный текст (сноски, ошибки, спеки), 6 мест сайта */
  --fs-label: 0.75rem;    /* 12px, uppercase */
  --fs-title-sm: 1.75rem; /* 28px — номер шага процесса и заголовок поп-апа заявки, было одно и то же число дважды без имени */

  /* Отступы — шкала кратна 4 */
  --sp-1: 0.25rem;
  --sp-2: 0.5rem;
  --sp-3: 0.75rem;
  --sp-4: 1rem;
  --sp-6: 1.5rem;
  --sp-8: 2rem;
  --sp-12: 3rem;
  --sp-16: 4rem;
  --sp-24: 6rem;
  --sp-32: 8rem;

  /* Сетка */
  --container-max: 80rem; /* 1280px */
  --container-pad: 1rem;

  /* Радиусы — воздуха больше нормы, ничего не жмётся к краям */
  --radius-s: 12px;
  --radius-m: 16px;
  --radius-l: 20px;
  --radius-pill: 999px;

  /* Переходы — мягкие и медленные, без резких вспышек */
  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-fast: 180ms;
  --dur-base: 280ms;
  --dur-slow: 420ms;

  /* Высота липкой шапки — используется в scroll-margin-top якорей */
  --header-h: 64px;
}

@media (min-width: 48rem) {
  :root {
    --container-pad: 1.5rem;
    --fs-body: var(--fs-body-lg);
    --header-h: 76px;
  }
}

@media (min-width: 64rem) {
  :root {
    --container-pad: 2rem;
  }
}

/* --------------------------------------------------------------------------
   3. Сброс
   -------------------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  /* Фон намеренно не задаётся здесь: единый mesh-фон (.mesh-bg) лежит
     на z-index:-1 фиксированным слоем — свой непрозрачный фон на body
     красится ПОСЛЕ элементов с отрицательным z-index и перекрыл бы
     mesh целиком. Тёмную основу даёт сам .mesh-bg. */
  color: var(--paper);
  font-family: var(--font-text);
  font-size: var(--fs-body);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img,
svg {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
}

button {
  font: inherit;
  color: inherit;
}

ul {
  margin: 0;
  padding: 0;
  list-style: none;
}

h1, h2, h3, p {
  margin: 0;
}

/* Видимый фокус для клавиатурной навигации */
:focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 3px;
  border-radius: var(--radius-s);
}

/* Выделение текста мышью — без этого правила браузер подставляет свой
   дефолтный (обычно яркий синий) цвет, который спорит со сдержанной
   палитрой сайта. Нейтральный холодный вош вместо акцентного синего —
   тот же принцип, что и у остального декора (акцент только на кнопках/
   активных состояниях/ключевых цифрах, не декоративно). */
::selection {
  background: var(--ink-600);
  color: var(--paper);
}

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* --------------------------------------------------------------------------
   3.5. Mesh-фон — единый на весь сайт
   Слой фиксированный (position: fixed, z-index: -1), лежит под всем
   контентом от первого экрана до подвала. Только CSS + один inline
   SVG для зерна, без картинок и canvas.
   -------------------------------------------------------------------------- */

.mesh-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  pointer-events: none;
  background: var(--ink-900);
}

/* Раньше здесь был .mesh-bg__blur — тональный вош по углам (шёпот
   тепла/холода), задуманный как «шёпот», а не «пятно». На деле, с
   .mesh-bg__grain поверх (mix-blend-mode: overlay — режим нелинейно
   усиливает как раз такие слабоконтрастные цветовые пятна на светлом
   фоне), «шёпот» читался наоборот: заметное серое размытие в углу
   экрана. Заказчик дважды поймал это как баг («тень в углу») — до и
   после того как в этом же углу проходила граница блока «Каталог»,
   то есть дело было не в стыке секций, а в самом вошe. Убран целиком
   вместе с .mesh-bg__blur (index.html) и анимацией mesh-drift — плоский
   цвет страницы + зерно уже дают достаточно «бумажности» без риска
   читаться как грязное пятно. Раньше здесь ещё была курсорная чертёжная
   сетка (.mesh-bg__cursor-grid) — убрана по той же причине (грязное
   пятно линий рядом с фото карусели работ). */

/* Зерно — статично, не анимируется. На светлой бумаге overlay слегка
   притемняет — даёт ощущение бумажной фактуры, а не гладкого экрана. */
.mesh-bg__grain {
  position: absolute;
  inset: 0;
  opacity: var(--mesh-grain-opacity);
  mix-blend-mode: overlay;
  background-repeat: repeat;
  background-size: 180px 180px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* --------------------------------------------------------------------------
   4. Утилиты
   -------------------------------------------------------------------------- */

.container {
  width: 100%;
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-pad);
  position: relative;
  z-index: 1;
}

.section {
  padding-block: clamp(3rem, 2.4rem + 3vw, 6rem);
}

/* Синий акцент здесь раньше был декоративным (метка + точка перед КАЖДЫМ
   заголовком секции) — теперь нейтральный, чтобы --signal оставался
   сигналом «это интерактивно или это ключевая цифра», а не фоновым
   узором, повторяющимся на каждом экране. */
.section-label {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  font-family: var(--font-text);
  font-weight: 700;
  font-size: var(--fs-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--paper-dim);
  margin-bottom: var(--sp-4);
}

.section-label::before {
  content: '';
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
}

/* Гравюрный разделитель перед каждой секцией (кроме hero) — тонкая
   линия с медальоном по центру, референс — виньетки на винтажном
   плакате-гравюре. Раньше медальон был в акцентном синем — на каждой
   секции без исключения, из-за чего --signal читался как фоновый узор,
   а не сигнал «здесь что-то интерактивное». Нейтральный серый держит
   тот же узел по всему сайту, но освобождает синий для реальных
   акцентов (кнопки, выбранные состояния, ключевые цифры). */
.section::before {
  content: '';
  display: block;
  width: 220px;
  max-width: 60%;
  height: 30px;
  margin: 0 auto clamp(1.5rem, 1.2rem + 2vw, 2.5rem);
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 40 40'%3E%3Cg fill='none' stroke='%23666E7A' stroke-width='1'%3E%3Ccircle cx='20' cy='20' r='17'/%3E%3Cpath d='M20,4 L20,36 M4,20 L36,20 M8,8 L32,32 M8,32 L32,8'/%3E%3C/g%3E%3Ccircle cx='20' cy='20' r='2' fill='%23666E7A'/%3E%3C/svg%3E"),
    linear-gradient(var(--ink-600), var(--ink-600));
  background-repeat: no-repeat, no-repeat;
  background-position: center, center;
  background-size: 30px 30px, 100% 1px;
}

.h1 {
  /* Обычный регистр, не капс: сплошные заглавные заметно снижают
     скорость чтения заголовка (слабее распознаётся форма слова) —
     вес и размер и так дают нужный акцент без необходимости в капсе.
     Межстрочный интервал увеличен под строчные буквы — у капса нет
     выносных элементов (у/р/б/д), а у обычного регистра есть. */
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--fs-h1);
  line-height: 1.08;
  letter-spacing: -0.015em;
  /* Балансирует длину строк при переносе — вместо случайных обрывков
     («и» одиноко повисает в конце строки) получаем более ровный,
     собранный геометрически блок текста. Игнорируется старыми
     браузерами без вреда — просто обычный перенос. */
  text-wrap: balance;
}

.h1__accent {
  /* Раньше выделялось отдельной курсивной гарнитурой (Playfair) — теперь
     тот же LT Remark, что и весь заголовок, различие держит только цвет.
     Один голос вместо двух — то же слово, просто акцентировано. */
  font-family: var(--font-display);
  font-weight: 900;
  color: var(--signal);
}

/* LT Remark — единственный дисплейный голос сайта, теперь и у h2 тоже
   (раньше был отдельный гравюрный Yeseva One). */
.h2 {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--fs-h2);
  line-height: 1.05;
  letter-spacing: -0.01em;
}

/* Подзаголовок-лид — раньше курсивный serif (Playfair), теперь обычный
   Golos Text: держит воздух и читаемость без третьей гарнитуры на
   странице. .lede встречается в 5 секциях — приём один и тот же. */
.lede {
  font-family: var(--font-text);
  font-weight: 500;
  /* Раньше значение было прямо здесь, вне общей шкалы (случайно на 1px
     от соседних размеров) — теперь именованный шаг --fs-lede в :root,
     как остальные --fs-*. Используется в 5 секциях сайта осознанно
     одним и тем же приёмом, поэтому заслуживает своего имени в шкале. */
  font-size: var(--fs-lede);
  line-height: 1.5;
  color: var(--paper-dim);
  max-width: 40ch;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  top: -100%;
  left: var(--sp-4);
  z-index: 100;
  background: var(--signal);
  color: var(--signal-ink);
  padding: var(--sp-3) var(--sp-4);
  border-radius: var(--radius-s);
  font-weight: 700;
  transition: top var(--dur-fast) var(--ease-out);
}

.skip-link:focus {
  top: var(--sp-4);
}

/* --------------------------------------------------------------------------
   5. Кнопки
   -------------------------------------------------------------------------- */

/* Любой класс с собственным display перебивает UA-правило [hidden]
   (у него нулевая специфичность) — элемент с атрибутом hidden всё равно
   остаётся видимым. Ловилось на кнопке «Изменить» у загрузки лого:
   btn.hidden = true стоял, а кнопка показывалась. Одно правило на весь
   сайт, чтобы hidden всегда работал как ожидается. */
[hidden] {
  display: none !important;
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  /* Критик ремесла design-loop сравнил в лоб с кнопкой Apple: у нас
     паддинг почти вдвое крупнее и текст жирным 700 — «плотный,
     напористый блок» вместо тихой пилюли. Смягчено (не до буквального
     400/8px Apple — своя дисциплина, не копия), но заметно ближе. */
  padding: 0.75em 1.4em;
  border-radius: var(--radius-pill);
  border: 1px solid transparent;
  font-family: var(--font-text);
  font-weight: 600;
  font-size: var(--fs-small);
  letter-spacing: 0.01em;
  cursor: pointer;
  text-decoration: none;
  transition: transform var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out);
  white-space: nowrap;
}

.btn:active {
  transform: scale(0.97);
}

.btn-primary {
  background: var(--signal);
  color: var(--signal-ink);
}

.btn-ghost {
  background: transparent;
  color: var(--paper);
  border-color: var(--ink-600);
}

.btn-ghost:hover {
  border-color: var(--paper-dim);
  background: var(--ink-800);
}

.btn-sm {
  /* Компактность даёт паддинг, а не отдельный кегль на 1px меньше
     общего --fs-small — тот отличался от остальных кнопок сайта без
     причины. */
  padding: 0.6em 1.2em;
  font-size: var(--fs-small);
}

.btn[disabled] {
  opacity: 0.6;
  cursor: not-allowed;
}

/* --------------------------------------------------------------------------
   6. Логотип
   -------------------------------------------------------------------------- */

.logo {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  text-decoration: none;
}

.logo__mark {
  /* Знак «BP» (символ #logo-bp в SVG-спрайте вверху index.html) — монограмма
     из первых букв вордмарка, тем же начертанием Inter Tight 900. Раньше
     здесь был абстрактный пунктирный кружок с точкой — не считывался как
     что-то принадлежащее именно этому бренду. fill не задан на самих path
     в спрайте — берётся отсюда через наследование SVG-свойства fill. */
  width: 22px;
  height: 22px;
  fill: var(--paper);
  flex-shrink: 0;
}

.logo__text {
  font-family: var(--font-logo);
  font-weight: 900;
  /* Раньше 1.125rem был отдельным, ничем не обоснованным значением —
     тот же размер уже есть в шкале как --fs-body-lg. Один шаг вместо
     случайного нового. */
  font-size: var(--fs-body-lg);
  letter-spacing: -0.01em;
  text-transform: uppercase;
  color: var(--paper);
}

/* --------------------------------------------------------------------------
   7. Шапка
   -------------------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  /* Лёгкая светлая подложка есть всегда, не только после скролла —
     логотип и навигация не должны сливаться с тем, что происходит
     позади них (mesh-фон, текст hero), даже в самом верху страницы. */
  background: linear-gradient(to bottom, rgba(251, 249, 243, 0.75) 0%, rgba(251, 249, 243, 0.25) 80%, transparent 100%);
  transition: background var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out);
  border-bottom: 1px solid transparent;
}

.site-header.is-scrolled {
  /* Полностью непрозрачный фон — иначе текст страницы под шапкой
     просвечивает и сливается с логотипом/навигацией при скролле. */
  background: var(--ink-800);
  border-bottom-color: var(--ink-600);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  height: var(--header-h);
  transition: height var(--dur-base) var(--ease-out);
}

/* После скролла шапке больше не нужно быть такой крупной — заголовок
   страницы уже виден, роль шапки становится чисто служебной (логотип +
   быстрый доступ к разделам). Чуть меньше — значит чуть больше места
   контенту, а не декоративная деталь. */
.site-header.is-scrolled .site-header__inner {
  height: calc(var(--header-h) - 14px);
}

.site-nav {
  display: none;
}

.site-nav a {
  text-decoration: none;
  font-size: var(--fs-small);
  color: var(--paper-dim);
  transition: color var(--dur-fast) var(--ease-out);
}

.site-nav a:hover {
  color: var(--paper);
}

.site-header__actions {
  display: flex;
  align-items: center;
  gap: var(--sp-6);
}

.site-header__phone {
  display: none;
  text-decoration: none;
  font-size: var(--fs-small);
  font-weight: 700;
  color: var(--paper);
  white-space: nowrap;
}

@media (min-width: 64rem) {
  .site-nav {
    display: flex;
    gap: var(--sp-8);
  }
  .site-header__phone {
    display: inline;
  }
}

/* --------------------------------------------------------------------------
   8. Hero
   -------------------------------------------------------------------------- */

.hero {
  padding-block: clamp(2rem, 1rem + 4vw, 4rem) clamp(3rem, 2rem + 5vw, 6rem);
  overflow: hidden;
}

.hero__grid {
  display: grid;
  /* column-gap — тот же зазор между текстом и фото, что был раньше
     (--sp-16 как единый gap). row-gap:0 отдельно — раньше секция была
     всего из 2 элементов (текстовый блок целиком + фото), gap работал
     только между ними. Теперь эйебрау/заголовок/остальной текст — три
     отдельные строки грида (см. areas ниже), и общий gap лёг бы между
     КАЖДОЙ парой строк — 64px между эйебрау и заголовком там, где раньше
     было 16px через margin у .section-label. row-gap:0 отдаёт вертикальные
     промежутки обратно margin'ам самих элементов, как было. */
  row-gap: 0;
  column-gap: var(--sp-16);
  /* stretch — держит .hero__visual без своего aspect-ratio на десктопе:
     фото стретчится на высоту той строки грида, где стоит (см.
     grid-template-areas ниже — это строка заголовка, не весь текстовый
     блок). На мобильной раскладке (одна колонка, без areas) фото и
     текст — разные строки грида, тут это ни на что не влияет, квадратные
     пропорции возвращаются через aspect-ratio в media-запросе у
     .hero__visual. */
  align-items: stretch;
}

@media (min-width: 64rem) {
  .hero__grid {
    grid-template-columns: 1.1fr 0.9fr;
    /* Симметрия — только с заголовком, не со всем текстовым блоком.
       Раньше фото стретчилось на высоту .hero__content целиком (эйебрау
       + h1 + лид + кнопки + маркеры) — верх совпадал с эйебрау, а не с
       h1, и низ уезжал далеко за кнопки. Заказчик явно отметил на
       скриншоте: граница — верх и низ ИМЕННО заголовка. Явные
       grid-template-areas сажают фото в одну строку с h1 и ни с чем
       больше — эйебрау сверху и весь остальной текст снизу в эту
       строку не попадают. */
    grid-template-areas:
      "eyebrow eyebrow"
      "heading photo"
      "below   below";
  }

  .hero__eyebrow { grid-area: eyebrow; }
  .hero__heading { grid-area: heading; }
  .hero__visual { grid-area: photo; }
  .hero__below { grid-area: below; }
}

.hero__ctas {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  margin-top: var(--sp-8);
}

/* Раньше здесь было отдельное «жидкое стекло» в расчёте на кнопки
   поверх фото — по факту .hero__ctas всегда в текстовой колонке,
   а не на изображении (на мобильной раскладке фото просто идёт
   отдельным блоком выше). Обычные .btn-primary/.btn-ghost уже верно
   выглядят на светлой странице — никакой спецверсии не нужно. */
.hero__ctas .btn-ghost {
  background: var(--ink-800);
}

.hero__markers {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-4) var(--sp-6);
  margin-top: var(--sp-8);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--ink-600);
}

@media (min-width: 48rem) {
  .hero__markers {
    grid-template-columns: repeat(4, auto);
  }
}

.hero__markers li {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  font-size: var(--fs-small);
  color: var(--paper-dim);
}

.hero__markers-num {
  font-family: var(--font-display);
  font-weight: 900;
  /* Было 1.375rem — случайное значение в 1px от .lede (21px), не шаг
     шкалы. --fs-h3 уже используется как «крупный акцентный» размер
     (заголовки карточек) — тот же шаг здесь, без нового числа. */
  font-size: var(--fs-h3);
  color: var(--paper);
  text-transform: uppercase;
}

/* Без рамки — раньше тонкая обводка превращала фото в «карточку среди
   карточек» рядом с кнопками и текстом такого же веса. Фото — самый
   сильный визуальный элемент первого экрана, ему не нужна рамка, чтобы
   казаться «на своём месте», как остальным блокам. */
.hero__visual {
  position: relative;
  border-radius: var(--radius-l);
  overflow: hidden;
}

/* Квадрат — только пока фото и текст лежат в одну колонку (раскладка
   ниже) и высоту неоткуда взять от соседа. На десктопе aspect-ratio
   снят: .hero__grid растягивает (stretch) фото на высоту строки
   заголовка (grid-area: heading/photo), так верх и низ фото совпадают
   именно с h1, а не со всем текстовым блоком. */
@media (max-width: 63.9375rem) {
  .hero__visual {
    aspect-ratio: 1 / 1;
  }
}

/* На мобильной/планшетной раскладке блоки идут в одну колонку, и текст
   (заголовок + лид + кнопки + маркеры) занимает почти весь первый экран —
   фото целиком уезжало за пределы вьюпорта при заходе на страницу.
   Поднимаем его перед текстом, чтобы оно было видно сразу, без скролла.
   margin-bottom — эйебрау/заголовок/остальной текст теперь три отдельные
   строки грида с row-gap:0 (см. .hero__grid), общий gap между фото и
   первой из них больше не работает сам по себе — отступ после фото
   держим явным margin на самом фото, раз оно здесь идёт первым. */
@media (max-width: 63.9375rem) {
  .hero__visual {
    order: -1;
    margin-bottom: var(--sp-16);
  }
}

.hero__photo {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* --------------------------------------------------------------------------
   9. Каталог-конфигуратор — светлая секция, но со своим цветовым акцентом
   Раньше это была единственная тёмная (графитовая) секция сайта —
   осознанный контраст. По решению заказчика перекрашена в светлую: сайт
   остаётся цельным, ни одного тёмного экрана. Значимость блока (это
   главная интерактивная демонстрация — «примерьте лого на упаковке»)
   теперь держится не контрастом тёмное/светлое, а цветом: секция сидит на
   собственном холодном синеватом фоне (изначально брали --ink-700
   остального сайта, #E8EDF1, «вош в акцент» — заказчик попросил фон на
   пару тонов темнее, чтобы блок не сливался с белыми карточками, сейчас
   #D6E2EC) вместо тёплой бумаги everywhere else, а акцент здесь — тот же
   самый глубокий --signal (#2C4A72), что и в кнопках по всему сайту, не
   разбавленный вариант, как было на графите.

   Механика прежняя: секция объявляет ЛОКАЛЬНЫЕ значения тех же токенов
   (--ink-800, --paper, --signal и т.д.) — переиспользуемые компоненты
   (.card, .carousel__track, .catalog__summary) сами перекрашиваются через
   обычное наследование custom properties, без единого нового класса.
   Тени/градиентная светотень в 3D-мокапе (.c-*) — по-прежнему намеренное
   исключение из правила «теней нет нигде»: это структурная иллюзия
   объёма упаковки, а не декоративная тень на плоской панели, только сами
   тени теперь мягче (тёмно-графитовый тон вместо чёрного — на светлом
   фоне чёрная тень читалась бы грязным пятном).
   -------------------------------------------------------------------------- */

#catalog {
  /* Свой холодный вош вместо тёплой бумаги остального сайта — единственное,
     что теперь маркирует блок как особый (--ink-700 остального сайта и
     есть этот же тон, дизайн-система его уже описывает как «вош в акцент»,
     просто здесь он не мелькает под курсором, а несёт всю секцию). */
  --ink-800: #ffffff;   /* карточки — светлее фона секции, как везде на сайте */
  /* Активная карточка ЛЕГЧЕ фона секции (а не темнее и не «в тон» ему) —
     первая версия делала #dbe6f0 на фоне #e8edf1, разница в светлоте
     была на грани заметности: выбранная карточка почти не отличалась
     от заднего плана, весь блок читался слипшимся бледно-голубым
     пятном. Здесь ink-700 ближе к белому ink-800, чем к фону — активная
     карточка выглядит «подсвеченной», а не одного тона с фоном. */
  --ink-700: #eef4f9;
  --ink-600: #aec0d0;   /* границы — темнее прежнего, фон стал глубже и старая рамка в нём терялась */
  --paper: #20242e;     /* тот же текст, что и на всём сайте */
  /* #5c6672 на фоне каталога #D6E2EC давал 4.43:1 — тот же WCAG-фикс,
     что и у sitewide --paper-dim выше (site-audit 2026-09-02, находка №3),
     здесь фон холоднее, поэтому нужно чуть больше — 2 пункта темнее. */
  --paper-dim: #5a6470;
  --signal: #2c4a72;    /* тот же акцент, что в кнопках по всему сайту — на графите он был разбавлен до #4e7ab8, здесь в этом нет нужды */
  --signal-ink: #fbf9f3;
  position: relative;
  /* На пару тонов темнее первой версии (#e8edf1) — по заявке заказчика
     «фон можно на пару тонов темнее, чтобы виднее было»: секция должна
     явно отличаться от белых карточек и от остального сайта, а не
     сливаться в единое бледное пятно. */
  background: #d6e2ec;
  color: var(--paper);
}

/* Мягкий шов на границах секции — раньше это была scroll-driven анимация
   (--reveal, писал initCatalogReveal() в main.js): лист цвета страницы
   поверх секции таял по мере скролла. Задумывался для старой ТЁМНОЙ
   версии секции — драматичный переход светлое→тёмное стоило показывать
   именно как жест при скролле. Когда секцию перекрасили в светлую, сам
   переход стал маленьким (тёплая бумага → холодный вош, не тьма), но
   анимация осталась завязана на scroll — и после того как --reveal
   доезжал до 100% (что происходит почти сразу, ещё до того как секция
   толком видна), нашлёпка исчезала СОВСЕМ, оставляя ровно то, от чего
   должна была спасать: жёсткую границу двух плоских цветов без всякого
   перехода. Заказчик поймал это на скриншоте — "лесенка", не слияние.

   Теперь это не анимация, а обычный статичный градиент — виден всегда,
   не зависит от скролла и не требует JS вообще. Фиксированные пиксели
   у краёв (0-120px), не проценты от высоты секции: с высокой секцией
   (1400px+) процентная зона неизбежно наехала бы на текст (та же грабля,
   что уже ловили на самой этой секции раньше). Второй, зеркальный шов —
   .catalog__seam--bottom — сшивает нижний край с «Оффером» (он сам
   полупрозрачный поверх той же тёплой бумаги, см. .offer). */
.catalog__seam {
  position: absolute;
  left: 0;
  right: 0;
  z-index: 6;
  pointer-events: none;
}

.catalog__seam--top {
  top: 0;
  height: 120px;
  background: linear-gradient(to bottom, #f1eee6 0%, transparent 100%);
}

.catalog__seam--bottom {
  bottom: 0;
  height: 120px;
  background: linear-gradient(to top, #f1eee6 0%, transparent 100%);
}

.catalog__intro {
  margin-top: var(--sp-4);
}

.carousel-block {
  margin-top: var(--sp-8);
}

.carousel-block__title {
  font-family: var(--font-text);
  font-weight: 700;
  font-size: var(--fs-label);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--paper-dim);
  margin-bottom: var(--sp-4);
}

/* Карусель */

/* Раньше это была горизонтальная scroll-snap карусель со стрелками —
   при 5–7 карточках на ось стрелки почти всегда были нужны, чтобы
   увидеть весь набор. Сетка с переносом строк показывает все варианты
   сразу, без скролла и без стрелок, на любой ширине экрана: колонки
   пересчитываются сами (auto-fill), на мобильном схлопывается в одну. */
.carousel__track {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--sp-4);
}

.carousel__btn {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: 1px solid var(--ink-600);
  background: var(--ink-800);
  color: var(--paper);
  font-size: var(--fs-h3);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out), opacity var(--dur-fast) var(--ease-out);
}

.carousel__btn:hover {
  border-color: var(--paper-dim);
}

.carousel__btn[disabled] {
  opacity: 0.3;
  cursor: default;
}

/* Карточки внутри карусели */

/* border всегда 2px — меняется только border-color между состояниями.
   Раньше .is-active/.is-recommended переопределяли border-width на 2px
   поверх базового 1px: рамка активной карточки физически толще, весь
   ряд CSS Grid подрастал на 2px и раскладка соседних карточек/строк
   заметно «прыгала» при каждом клике. */
.card {
  position: relative;
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-6);
  background: var(--ink-800);
  border: 2px solid var(--ink-600);
  border-radius: var(--radius-m);
  cursor: pointer;
  text-align: left;
  transition: border-color var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}

/* Раньше здесь под курсором зажигалось синее пятно-прожектор
   (radial-gradient за --spot-x/--spot-y) — декоративный акцент без
   функции, и hover визуально путался с реальным выбранным состоянием
   (оба тянули карточку в синеватый тон). Обычная смена border-color
   достаточно ясно показывает hover, не занимая акцент. */
.card:hover {
  border-color: var(--paper-dim);
}

/* Раньше — та же 2px синяя рамка, что и у .is-active: «рекомендовано»
   выглядело неотличимо от «уже выбрано», хотя это разные состояния и
   в сводку рекомендованная карточка не попадает сама по себе. Рамка
   тоньше, а смысл несёт явная подпись, а не толщина линии. */
.card.is-recommended {
  border-color: var(--signal);
}

/* Раньше стояла в потоке (flex-item) — сдвигала весь контент карточки
   вниз на свою высоту, из-за чего рекомендованная карточка съезжала
   относительно соседей в том же ряду (общая базовая линия ломалась).
   position:absolute в верхнем ЛЕВОМ углу — тот же приём, что у галочки
   .is-active::after в правом углу, только с другой стороны, чтобы не
   пересекаться, если оба состояния совпадут на одной карточке. Помещается
   в верхний паддинг карточки (--sp-6=24px), не касаясь иконки/заголовка. */
.card.is-recommended::before {
  content: 'Рекомендуем';
  position: absolute;
  top: var(--sp-2);
  left: var(--sp-4);
  font-size: var(--fs-caption);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--signal);
}

.card.is-active {
  border-color: var(--signal);
  background: var(--ink-700);
}

/* Галочка в углу — однозначный сигнал «выбрано», отдельно от рамки
   и подсветки, которые на бегу можно не заметить. */
.card.is-active::after {
  content: '';
  position: absolute;
  top: var(--sp-4);
  right: var(--sp-4);
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background-color: var(--signal);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23FBF9F3' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='5 13 10 18 19 7'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 13px;
}

/* «В заказе, но сейчас не на 3D-сцене» — можно выбрать сразу два формата
   этикетки (round + strip) на один заказ; сцена и панель загрузки лого
   всегда показывают только ОДИН (см. state.focusShape в configurator.js).
   Это состояние — заметно слабее .is-active (рамка + маленькая пустая
   галочка, без заливки), чтобы «что реально сейчас на сцене» оставалось
   однозначным даже когда выбрано два формата разом. Тот же приём
   различия силы сигнала, что уже даёт «рекомендовано» vs «выбрано». */
.card.is-selected {
  border-color: var(--signal);
}

.card.is-selected::after {
  content: '';
  position: absolute;
  top: var(--sp-4);
  right: var(--sp-4);
  width: 22px;
  height: 22px;
  border-radius: 50%;
  border: 1.5px solid var(--signal);
  background-color: var(--ink-800);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%232C4A72' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='5 13 10 18 19 7'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: center;
  background-size: 11px;
}

.card__shape {
  width: 64px;
  height: 64px;
}

.card__title {
  font-family: var(--font-text);
  font-weight: 700;
  font-size: var(--fs-h3);
  line-height: 1.25;
  /* Заголовки разной длины иначе сдвигали бы весь остальной контент
     карточки на разную высоту — резервируем место под две строки
     всегда, даже когда заголовок умещается в одну. */
  min-height: calc(var(--fs-h3) * 1.25 * 2);
  /* «Металлизированная» — одно длинное слово без пробелов, браузеру
     некуда перенести его без этого правила: текст вылезал за правый
     край карточки и наезжал на соседнюю (выглядело так, будто карточки
     слиплись). overflow-wrap разрешает перенос внутри слова. */
  overflow-wrap: break-word;
}

.card__text {
  font-size: var(--fs-small);
  color: var(--paper-dim);
  flex-grow: 1;
}

.card__specs {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  font-size: var(--fs-caption);
  color: var(--paper-dim);
}

.card__specs li::before {
  content: '— ';
}

/* Итоговая сводка выбора — единственная точка перехода к форме, после
   всех трёх осей выбора, а не с каждой отдельной карточки. */
.catalog__summary {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  /* Было var(--sp-10) — такого токена нет в шкале отступов (--sp-8,
     дальше сразу --sp-12), custom property не резолвилась и margin-top
     фактически был 0: сводка сидела вплотную к сетке над ней, карточки
     и белая плашка визуально сливались в одно пятно. */
  margin-top: var(--sp-12);
  padding: var(--sp-6) var(--sp-8);
  background: var(--ink-800);
  border: 1px solid var(--ink-600);
  border-radius: var(--radius-m);
}

.catalog__summary-text {
  font-size: var(--fs-small);
  color: var(--paper-dim);
}

.catalog__summary-text strong {
  color: var(--paper);
  font-weight: 700;
}

.catalog__summary-text-wrap {
  flex: 1;
  min-width: 220px;
}

/* Два коротких смысловых акцента у кнопки — закрывают боли клиента
   (платное клише, переплата за маленький тираж) прямо рядом с расчётом,
   а не отдельным лендинг-блоком с длинным текстом. */
.conf3d__perks {
  margin-top: var(--sp-2);
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}

.conf3d__perks li {
  font-size: var(--fs-caption);
  color: var(--paper-dim);
}

.conf3d__perks li::before {
  content: '✓ ';
  color: var(--signal);
}

/* ── боковые списки: формат этикетки / вид упаковки ─────────────────
   Та же карточка (.card), что и в остальном сайте — только один столбец
   вместо сетки, раз тут всего по два варианта на список. flex вместо
   grid: на десктопе колонка стретчится по высоте самой высокой из трёх
   (.conf3d__grid ниже), и без этого две карточки списка просто повисали
   у верхнего края с пустой графитовой полосой под ними — critic ремесла
   design-loop замерил разрыв в ~250px между низом левой и правой колонки.
   space-between раздвигает карточки на всю стретченную высоту. */
.conf3d__col--left,
.conf3d__col--right {
  display: flex;
  flex-direction: column;
}

.carousel__track--sidebar {
  display: flex;
  flex-direction: column;
  flex: 1;
}

/* space-between (первая попытка) раздвигало 2 карточки на всю
   стретченную высоту колонки — в рейке с короткими карточками
   («Упаковка») зазор между ними доходил до 266px, крупнее самих
   карточек, и рвал зрительную группировку двух вариантов одного
   выбора. flex:1 1 0 вместо этого растит каждую карточку рейки
   поровну — низ колонок по-прежнему совпадает, но без дыры внутри
   списка. Найдено повторным кругом критика ремесла design-loop. */
.carousel__track--sidebar > .carousel__item {
  flex: 1 1 0;
  display: flex;
}

.carousel__track--sidebar > .carousel__item > .card {
  width: 100%;
}

.conf3d__pick {
  appearance: none;
  align-items: flex-start;
}

.conf3d__pick .card__shape {
  width: 48px;
  height: 48px;
}

/* Размеры круглой этикетки: ряд пилюль внутри карточки формата. 47 мм
   выбран по умолчанию — сам факт, что он уже активен при загрузке,
   плюс спокойная подпись ниже (.conf3d__size-hint) достаточно подсказывают
   «это лучший вариант», без кричащего бейджа поверх карточки. */
.conf3d__sizes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.conf3d__size-btn {
  border: 1px solid var(--ink-600);
  background: transparent;
  color: var(--paper-dim);
  padding: 0.4em 0.9em;
  border-radius: var(--radius-pill);
  font-family: var(--font-text);
  font-weight: 700;
  font-size: var(--fs-caption);
  cursor: pointer;
  transition: border-color var(--dur-fast) var(--ease-out),
              background var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}

.conf3d__size-btn:hover {
  border-color: var(--paper-dim);
}

.conf3d__size-btn.is-active {
  border-color: var(--signal);
  background: var(--signal);
  color: var(--signal-ink);
}

.conf3d__size-hint {
  font-size: var(--fs-caption);
  color: var(--paper-dim);
}

/* Подсказка про мультивыбор форматов сидит ПЕРЕД списком карточек, а не
   внутри одной из них (как обычный .conf3d__size-hint) — глобальный сброс
   `p { margin: 0 }` и то, что .conf3d__col--left это flex-column БЕЗ gap,
   иначе слепили бы её вплотную и к «Формат» сверху, и к первой карточке
   снизу (see design-loop грабля про margin/gap на flex-колонках). */
.conf3d__format-hint {
  margin: var(--sp-2) 0 var(--sp-4);
}

/* ── 3D-конфигуратор: сетка формат / сцена / упаковка ─────────────── */
.conf3d {
  margin-top: var(--sp-8);
}

.conf3d__grid {
  display: grid;
  gap: var(--sp-6);
}

@media (min-width: 61.25rem) {
  .conf3d__grid {
    grid-template-columns: 236px minmax(0, 1fr) 236px;
    /* stretch (не start) — все три колонки делят высоту самой высокой
       (обычно левой, у круглой этикетки есть ещё блок размеров) вместо
       того чтобы каждая заканчивалась там, где кончился её контент. */
    align-items: stretch;
  }
}

.conf3d__col--stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-4);
}

.conf3d__scene {
  position: relative;
  width: 100%;
  /* Сцена крупная намеренно: это главная демонстрация блока — клиент
     должен разглядеть СВОЁ лого на упаковке, а не гадать по миниатюре.
     Увеличивать можно только пропорционально (см. aspect-ratio у .stage):
     геометрия куба и угол скоса пломбы посчитаны от соотношения сторон
     сцены, при другой пропорции грани разъедутся. */
  max-width: 460px;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background var(--dur-base) var(--ease-out);
  border-radius: var(--radius-l);
}

.conf3d__scene.is-dragover {
  background: var(--ink-700);
}

.arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 44px;
  height: 44px;
  border: 0;
  background: none;
  cursor: pointer;
  font-size: var(--fs-title-sm);
  line-height: 1;
  color: var(--paper-dim);
  z-index: 5;
  transition: color var(--dur-fast) var(--ease-out);
}

.arrow--prev {
  left: -8px;
}

.arrow--next {
  right: -8px;
}

.arrow:hover {
  color: var(--signal);
}

/* ── подиум и сама 3D-упаковка (слои-грани + градиенты — намеренное
   исключение из правила «теней нет нигде», см. заголовок раздела) ─── */
/* Раньше высота была clamp(260px, 32vw, 380px) — привязана к ширине
   ВЬЮПОРТА, а не к реальной ширине сцены (max-width:340px у
   .conf3d__scene). На мобильном 32vw давал маленькое число, clamp
   поднимал его до пола в 260px независимо от того, насколько узкая
   сцена — на 375px мокап становился ШИРЕ, чем выше (соотношение сторон
   стакана съезжало с 1.22 на десктопе до 0.84 на телефоне). Теперь
   aspect-ratio держит те же пропорции, что и на десктопе (340×380),
   на любой ширине — высота всегда пересчитывается от фактической
   ширины сцены, а не от vw. Найдено критиком ремесла design-loop. */
.stage {
  position: relative;
  overflow: hidden;
  width: 100%;
  aspect-ratio: 340 / 380;
  /* Ровно max-width сцены × 380/340 — держит ту же пропорцию, что и
     aspect-ratio выше. Разойдутся — поедет вся 3D-геометрия. */
  max-height: 514px;
}

.stage__glow {
  position: absolute;
  left: 50%;
  top: 42%;
  transform: translate(-50%, -50%);
  width: 126%;
  height: 92%;
  pointer-events: none;
  /* Тонировано в --signal, а не нейтральный серый — единственное место
     секции, где заказчик просил именно «доиграть» цвет, а не просто
     сделать блок светлым: свечение за продуктом читается как фирменный
     синий, не просто амбиентный свет. */
  background: radial-gradient(ellipse at 50% 50%, rgba(44, 74, 114, 0.12) 0%, rgba(44, 74, 114, 0.05) 42%, transparent 72%);
  filter: blur(8px);
}

.stage__podium {
  position: absolute;
  left: 50%;
  bottom: 18.5%;
  transform: translateX(-50%);
  width: 78%;
  height: 7.5%;
  border-radius: 50%;
  z-index: 1;
  pointer-events: none;
  background: radial-gradient(ellipse at 50% 34%, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0.18) 44%, transparent 74%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.6), 0 14px 30px rgba(32, 36, 46, 0.16);
}

.stage__main {
  position: absolute;
  inset: 0 0 22% 0;
  z-index: 3;
}

.c {
  position: absolute;
  inset: 0;
}

.c-shadow,
.c-body,
.c-side,
.c-top,
.c-cap,
.c-field {
  position: absolute;
}

.c-shadow {
  left: 50%;
  transform: translateX(-50%);
  width: 62%;
  height: 6%;
  border-radius: 50%;
  /* Тон тени — тёмно-графитовый (--paper секции), не чёрный: на светлом
     фоне чёрная тень такой плотности читалась бы грязным пятном. */
  background: radial-gradient(ellipse at 50% 50%, rgba(32, 36, 46, 0.32) 0%, rgba(32, 36, 46, 0.16) 46%, transparent 72%);
  filter: blur(5px);
}

/* Светлая гамма стакана — тот же рисунок светотени (ритм тёмное/светлое
   вдоль грани, имитирующий рёбра бумажного стакана), что и в тёмной
   версии, просто перенесённый в светлую семью тонов: было #101116..#3b3e46
   (почти-чёрный/тёмно-серый), стало #a9afb7..#eef0f2 (светло-серый/почти
   белый) — сохранены те же относительные перепады яркости между стопами. */
.c-body {
  background:
    linear-gradient(97deg,
      #b9bfc6 0%, #d2d7dc 9%, #eef0f2 20%, #dde1e5 33%,
      #c7ccd2 50%, #b3b9c0 66%, #d5d9dd 84%, #c2c7cd 94%, #a9afb7 100%);
  box-shadow:
    inset 0 1.5px 0 rgba(255, 255, 255, 0.65),
    inset 2px 0 0 rgba(255, 255, 255, 0.35),
    inset -2px 0 0 rgba(255, 255, 255, 0.45),
    inset 0 -14px 22px rgba(32, 36, 46, 0.16),
    0 20px 34px rgba(32, 36, 46, 0.14);
}

.c-side {
  /* Была заметно темнее передней грани (.c-body) — читалась плоской
     дырой, а не освещённой боковой стенкой. Пик выведен ближе к
     светлоте .c-body, блик усилен — критик ремесла design-loop. Здесь
     всё ещё самая тёмная из граней (боковая, дальше от света), но
     «тёмная» — в светлой гамме, не абсолютная. */
  background: linear-gradient(100deg, #c2c7cd 0%, #d8dce0 35%, #b7bcc2 70%, #a5abb3 100%);
  box-shadow: inset 0 1.5px 0 rgba(255, 255, 255, 0.5), inset 0 -10px 18px rgba(32, 36, 46, 0.15);
}

.c-top,
.c-cap {
  background: linear-gradient(102deg, #eef0f2 0%, #f8f9fa 26%, #e7e9eb 58%, #dadde0 100%);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.7);
}

.c-side,
.c-top,
.c-cap {
  display: none;
}

/* Стакан */
.c--cup {
  --fx: 31%;
  --fy: 42%;
  --fw: 38%;
  --fh: 26%;
}
.c--cup .c-body {
  left: 24%;
  right: 24%;
  top: 23%;
  bottom: 4%;
  clip-path: polygon(0 0, 100% 0, 85% 100%, 15% 100%);
}
.c--cup .c-cap {
  display: block;
  left: 21.5%;
  right: 21.5%;
  top: 17.5%;
  height: 6%;
  border-radius: 6px 6px 2px 2px;
}
.c--cup .c-top {
  display: block;
  left: 21.5%;
  right: 21.5%;
  top: 15.5%;
  height: 3.6%;
  border-radius: 50%;
}
.c--cup .c-shadow {
  bottom: 1%;
  width: 50%;
}
/* «Контроль качества» на стакане — пломба на крышку: идёт с ободка
   крышки (.c-cap занимает y 17.5..23.5%) вниз до середины стакана
   (корпус y 23..96%, середина 59.5%), пересекая шов крышка/корпус.
   Наклеенная так этикетка не даёт снять крышку не порвав её — в этом и
   смысл формата. Раньше полоса просто лежала посреди корпуса и ничего
   не «держала». Длина 40.5% высоты = 120px, ширина по 28:90 = 37.3px:
   поле сделано ровно под неё, чтобы полоса не гуляла внутри рамки. */
.c--cup .c-field.sh--strip {
  left: 44.51%;
  top: 19%;
  width: 10.98%;
  height: 40.5%;
}

/* Обычный куб в 3/4 ракурсе — по референсу заказчика (белая коробка на
   белом фоне): три грани, рёбра параллельные (аксонометрия, без точки
   схода), светотень раскладывается по граням — верх самый светлый,
   перед средний, правый бок самый тёмный. Секция теперь сама светлая,
   так что здесь наконец можно взять с референса не только форму, а и
   буквальный цвет — куб светло-серый/белый, а не графитовый, как было,
   пока секция была тёмной.

   Геометрия (в % контейнера .c, он всегда 340:296.4 благодаря
   aspect-ratio на .stage — проценты держат форму на любом экране):
   передняя грань x 18..62, y 46..88; вектор глубины +18 / −16.
   Все clip-path посчитаны из этих чисел, не подобраны на глаз. */
.c--box {
  --fx: 24.85%;
  --fy: 50.05%;
  --fw: 27.6%;
  --fh: 27.6%;
}
/* передняя грань — просто прямоугольник, без clip-path */
.c--box .c-body {
  left: 13.35%;
  right: 36.05%;
  top: 39.7%;
  bottom: 12%;
  border-radius: 0;
  background: linear-gradient(168deg, #eef0f2 0%, #e2e5e8 45%, #d4d8db 100%);
  box-shadow: none;
}
/* правая грань — самая тёмная */
.c--box .c-side {
  display: block;
  left: 63.95%;
  right: 15.35%;
  top: 21.3%;
  bottom: 12%;
  clip-path: polygon(0% 27.59%, 100% 0%, 100% 72.41%, 0% 100%);
  border-radius: 0;
  background: linear-gradient(100deg, #c7cbcf 0%, #bcc0c5 60%, #b0b4ba 100%);
  box-shadow: none;
}
/* верхняя грань — самая светлая */
.c--box .c-cap {
  display: block;
  left: 13.35%;
  right: 15.35%;
  top: 21.3%;
  height: 18.4%;
  clip-path: polygon(0% 100%, 70.97% 100%, 100% 0%, 29.03% 0%);
  border-radius: 0;
  background: linear-gradient(150deg, #fcfcfd 0%, #f5f6f7 55%, #eceeef 100%);
  box-shadow: none;
}
.c--box .c-shadow {
  bottom: 8%;
  width: 69%;
}
/* Грани куба плоские — затемнение к краям (.c-wrap) имитирует кривизну
   цилиндра и на коробке физически неверно, поэтому здесь его нет. */
.c--box .c-wrap {
  display: none;
}

/* Этикетка «Контроль качества» — ровно по 2-му референсу заказчика:
   лежит на ВЕРХНЕЙ грани, переваливает через переднее ребро и свисает
   по передней грани, заканчиваясь полукруглым язычком. Так наклеенная
   пломба физически не даёт открыть коробку — это и есть смысл формата
   (на карточке ровно это и написано: «на шов, крышку или сбоку»).

   Сегмент на верхней грани (.c-field, с настоящим лого клиента) скошен
   в плоскость этой грани: skewX(-52.23deg) = atan(61.2px / 47.4px),
   ровно вектор глубины куба, поэтому этикетка ложится на грань, а не
   лежит «наклейкой поверх картинки». На референсе лого тоже на верхней
   грани — там, где его видно первым.
   Передний свисающий сегмент (.c-seal-flap) — продолжение той же
   плёнки, без лого: bake-контур редактора кадрирования выдаёт одно
   изображение на одну рамку, резать его по ребру на два куска — это
   отдельная переделка редактора, не побочная правка мокапа.

   Длины сегментов держат честные 28×90: ширина 12% (40.8px) → полная
   длина 131px = 77px по верхней грани (вся глубина) + 54px по фронту. */
.c--box .c-field.sh--strip {
  overflow: hidden;
  left: 33.912%;
  top: 29.01%;
  width: 9.47%;
  height: 10.7%;
  transform-origin: bottom;
  transform: skewX(-52.23deg);
}
/* На верхней грани этикетка занимает сегмент целиком — честные
   пропорции 28×90 набираются двумя сегментами вместе (см. выше), а не
   каждым по отдельности, поэтому здесь aspect-ratio снимается. */
.c--box .c-field.sh--strip .c-label {
  width: 100%;
  height: 100%;
  aspect-ratio: auto;
  border-radius: 0;
  box-shadow: none;
}

/* Лого при разрезе ребром: оба сегмента рисуют ОДНУ картинку (var(--logo),
   её ставит stageHTML() на общий контейнер .c), но каждый показывает свой
   кусок — иначе на крышке и на фронте оказались бы две копии этикетки
   вместо одной, перегнутой через ребро.

   Лента симметрична относительно ребра — по 45px реальной длины на
   каждую грань (вся этикетка 90px при ширине 28px, честные 28:90).
   Из симметрии следует, что каждому сегменту достаётся ровно половина
   рисунка, поэтому фон у обоих 200% высоты: верхнему выравниваем по
   верху (видит первую половину), нижнему по низу (вторую). Стык
   приходится ровно на ребро. */
.c--box .c-label--seal.has-logo {
  background-image: var(--logo), linear-gradient(158deg, #f4f2ec 0%, #eae7e0 55%, #ddd9d1 100%);
  background-repeat: no-repeat, no-repeat;
  background-size: 100% 200%, 100% 100%;
  background-position: 0 0, 0 0;
}
.c--box .c-seal-flap.has-logo {
  background-image: var(--logo), linear-gradient(180deg, #dcd8d0 0%, #cdc8be 55%, #c2bdb2 100%);
  background-repeat: no-repeat, no-repeat;
  background-size: 100% 200%, 100% 100%;
  background-position: 0 100%, 0 0;
}
.c-seal-flap {
  position: absolute;
  z-index: 4;
  pointer-events: none;
}
.c--box .c-seal-flap {
  left: 33.912%;
  width: 9.47%;
  top: 39.7%;
  height: 17.46%;
  /* Полукруглый язычок снизу: горизонтальный радиус = половина ширины
     (50%), вертикальный подобран так, чтобы в пикселях он совпал с
     горизонтальным (38% от 53.7px = 20.4px = половина 40.8px) — иначе
     вместо полукруга выходит вытянутый эллипс. */
  border-radius: 0 0 50% 50% / 0 0 31.1% 31.1%;
  /* Передняя грань освещена слабее верхней — свисающий кусок плёнки
     темнее того, что лежит на крышке, плюс тень сгиба у самого ребра. */
  background: linear-gradient(180deg, #dcd8d0 0%, #cdc8be 55%, #c2bdb2 100%);
  box-shadow: inset 0 5px 7px -3px rgba(32, 36, 46, 0.35);
}

/* ── поле этикетки: формы «Топ продукт» (круг) и «Контроль качества»
   (узкая полоса) — вписываются в поле упаковки, никогда не вылезают ─── */
.c-field {
  left: var(--fx);
  top: var(--fy);
  width: var(--fw);
  height: var(--fh);
  z-index: 4;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: left 0.45s var(--ease-out), top 0.45s var(--ease-out),
              width 0.45s var(--ease-out), height 0.45s var(--ease-out);
}

.c-label {
  position: relative;
  overflow: hidden;
  container-type: size; /* печать масштабируется под размер этикетки */
  display: flex;
  align-items: center;
  justify-content: center;
  --ink-c: #26262a;
  --gl: 0.45;
  --gb: 0px;
  border: 1px solid transparent;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.42);
  transition: background 0.45s var(--ease-out), box-shadow 0.45s var(--ease-out),
              border-radius 0.45s var(--ease-out), border-color 0.45s var(--ease-out),
              width 0.45s var(--ease-out), height 0.45s var(--ease-out);
}

.sh--round .c-label {
  height: 100%;
  aspect-ratio: 1;
  border-radius: 50%;
  /* --size-scale ставит stageHTML() (configurator.js) — 0.64/1/1.28
     для 30/47/60мм, чтобы выбор размера реально было видно на мокапе,
     а не только в тексте сводки. */
  transform: scale(var(--size-scale, 1));
  transition: background 0.45s var(--ease-out), box-shadow 0.45s var(--ease-out),
              border-color 0.45s var(--ease-out), transform 0.45s var(--ease-out);
}
/* Пропорции — настоящие 28×90 мм, а не «примерно узкий прямоугольник»:
   когда-то width:42%/height:100% давали ~1:1.4, почти квадрат, хотя
   рядом на карточке написано 28 × 90 мм (1:3.2) — мокап противоречил
   подписи. Теперь длину задаёт поле (у каждой упаковки своя, см. ниже),
   а aspect-ratio держит честную ширину. */
.sh--strip .c-label {
  height: 100%;
  aspect-ratio: 28 / 90;
  border-radius: 8px;
}

.c-ink {
  position: relative;
  z-index: 2;
  text-align: center;
  color: var(--ink-c);
  padding: 6% 9%;
  transition: color 0.45s var(--ease-out);
}
.c-ink__mark {
  border-radius: 2px;
  background: currentColor;
  opacity: 0.85;
  width: min(10cqw, 22cqh);
  height: min(10cqw, 22cqh);
  margin: 0 auto min(4cqw, 9cqh);
}
.c-ink__name {
  font-weight: 800;
  letter-spacing: 0.12em;
  line-height: 1.15;
  font-size: min(12cqw, 26cqh);
}
.c-ink__rule {
  height: 1px;
  background: currentColor;
  opacity: 0.4;
  width: 34cqw;
  margin-block: min(4cqw, 8cqh);
  margin-inline: auto;
}
.c-ink__sub {
  letter-spacing: 0.18em;
  opacity: 0.62;
  font-weight: 600;
  font-size: min(6.6cqw, 15cqh);
}

/* Загруженное лого клиента — заменяет плейсхолдер «ВАШ БРЕНД» целиком.
   Раньше было 78% с object-fit: contain — картинка «плавала» внутри
   этикетки с полями по краям, форму не заполняла. Теперь редактор
   (applyEditor() в configurator.js) отдаёт сюда PNG, уже обрезанный
   ровно по форме этикетки, поэтому лого кладётся на всё поле: то, что
   клиент собрал в редакторе, ровно то и видно на упаковке. */
.c-logo {
  position: absolute;
  inset: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Кривизна цилиндра/грани — и стакан, и коробка бургера объёмные.
   Ослаблено вместе с удалением бликов: осталось только затемнение к
   краям (этикетка уходит за изгиб упаковки), без белых засветов
   посередине, которые выбеливали лого. */
.c-wrap {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: linear-gradient(90deg,
    rgba(0, 0, 0, 0.34) 0%, rgba(0, 0, 0, 0.1) 16%, rgba(0, 0, 0, 0) 40%,
    rgba(0, 0, 0, 0) 62%, rgba(0, 0, 0, 0.12) 85%, rgba(0, 0, 0, 0.34) 100%);
}

/* ── фактура плёнки этикетки: выбор материала убран из интерфейса
   (мокап всегда рисуется matte, см. stageHTML() в configurator.js),
   но классы оставлены — использует .c-label ниже, могут понадобиться
   для будущего возврата выбора материала.

   Бликов больше нет: раньше поверх этикетки лежал слой .c-sheen —
   белое радиальное пятно, ездившее за курсором (initLivingLight()),
   плюс глянцевые градиенты самой плёнки. По просьбе заказчика убрано
   целиком: блик перекрывал загруженное лого и мешал увидеть, как
   этикетка выглядит на самом деле. Осталась только ровная матовая
   подложка + мягкая кривизна .c-wrap по краям. ─────────────────── */
.mat--matte {
  background: linear-gradient(158deg, #f4f2ec 0%, #eae7e0 55%, #ddd9d1 100%);
}
.mat--glossy {
  background: linear-gradient(158deg, #f7f6f2 0%, #edebe6 50%, #dcd9d2 100%);
}
.mat--transparent {
  background: linear-gradient(150deg, rgba(255, 255, 255, 0.13) 0%, rgba(255, 255, 255, 0.03) 55%, rgba(255, 255, 255, 0.09) 100%);
  border-color: rgba(255, 255, 255, 0.26);
  --ink-c: #f4f4f6;
}

/* ── загрузка лого: кнопка + drag&drop-зона под сценой ─────────────── */
.conf3d__logo {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  text-align: center;
  padding: var(--sp-4);
  width: 100%;
  max-width: 340px;
  border: 1px dashed var(--ink-600);
  border-radius: var(--radius-m);
  transition: border-color var(--dur-fast) var(--ease-out), background var(--dur-fast) var(--ease-out);
}

.conf3d__logo.is-dragover {
  border-color: var(--signal);
  background: var(--ink-700);
}

.conf3d__logo-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--sp-2);
}

.conf3d__logo-hint {
  font-size: var(--fs-caption);
  color: var(--paper-dim);
  max-width: 32ch;
}

/* --------------------------------------------------------------------------
   10. Оффер
   -------------------------------------------------------------------------- */

.offer {
  /* Полупрозрачная, не сплошная заливка — единый mesh-фон страницы
     продолжает читаться и здесь, секция лишь слегка выделена. */
  background: rgba(251, 249, 243, 0.6);
  /* border-block раньше рисовал свою жёсткую линию 1px у верхнего и
     нижнего края секции — поверх/сразу под мягким градиентным швом
     .catalog__seam--bottom получался тот самый резкий обрыв, который
     градиент должен был убрать: плавное затухание, потом внезапная
     чёткая черта. Плюс это дублировало общий .section::before (тот же
     медальон-разделитель, что у всех соседних секций) — теперь у
     «Оффера» ровно один разделитель, как у всех, без второй линии. */
  position: relative;
}

/* Раньше здесь был decorative radial-gradient вош (сперва синий, потом
   нейтральный) — но .offer имеет класс .section, и .offer::before
   занимал ТОТ ЖЕ псевдоэлемент, что и общий разделитель .section::before
   (медальон + волосяная линия перед каждой секцией сайта). Правило
   .offer::before просто побеждало в каскаде (одинаковая специфичность,
   объявлено позже) — из-за этого перед «Оффером» единственным на сайте
   не было стандартного разделителя, узор выпадал из общего ритма. Убран
   целиком — секция получает тот же .section::before, что и все соседи. */
.offer__inner {
  position: relative;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
}

.offer__number-row {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  margin-block: var(--sp-4) var(--sp-2);
}

.offer__number {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--fs-offer);
  line-height: 0.85;
  color: var(--signal);
}

.offer__number-unit {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: clamp(1.25rem, 1rem + 2vw, 2rem);
  text-transform: uppercase;
  color: var(--paper-dim);
}

.offer__lede {
  max-width: 56ch;
  margin-inline: auto;
}

.offer__includes {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-6);
  margin-block: var(--sp-8);
  text-align: left;
  width: 100%;
  max-width: 46rem;
}

@media (min-width: 48rem) {
  .offer__includes {
    grid-template-columns: repeat(4, 1fr);
  }
}

.offer__includes li {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  font-size: var(--fs-small);
  color: var(--paper-dim);
}

.offer__includes span {
  font-family: var(--font-text);
  font-weight: 700;
  color: var(--paper);
}

.offer__photo-frame {
  width: 100%;
  max-width: 46rem;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-l);
  overflow: hidden;
  margin-top: var(--sp-4);
  margin-bottom: var(--sp-8);
}

.offer__photo-frame img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Risk-reversal под самой конверсионной кнопкой блока — /offers разбор
   2026-09-02: у оффера не было ни одной гарантии, самый слабый рычаг в
   формуле ценности (доверие, что результат совпадёт с ожиданием). Факт
   не новый — «согласование макета» и так есть в реальном процессе
   (data/site.js → productionSteps), здесь он просто назван явно, рядом
   с кнопкой, а не спрятан в шаге процесса, который клиент не обязан
   изучать заранее. Тот же визуальный вес, что у аналогичной подписи под
   кнопкой в форме заявки (.lead-form__note). */
.offer__guarantee {
  font-size: var(--fs-caption);
  color: var(--paper-dim);
  margin: var(--sp-3) 0 0;
  text-align: center;
}

/* --------------------------------------------------------------------------
   11. (было «Как мы работаем» — блок убран с сайта по просьбе заказчика,
   не нужен; CSS удалён вместе с разметкой в index.html)
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   12. О компании
   -------------------------------------------------------------------------- */

.about__grid {
  display: grid;
  gap: var(--sp-8);
}

@media (min-width: 64rem) {
  .about__grid {
    grid-template-columns: 1fr 1fr;
    align-items: center;
  }
}

/* Три технических УТП (клей/лак/рулон) — по фидбеку заказчика. Галочка
   нейтрального цвета (--paper-dim), не --signal: акцент здесь был бы
   декоративным (список, не кнопка/активное состояние/ключевая цифра) —
   тот же класс проблемы, что уже чистили на медальонах/эйебрау/номерах
   шагов по всему сайту, не повторяем его в новом списке.
   Раньше список был мельче и легче .lede над ним (--fs-small/400 против
   --fs-lede/500) — два разных голоса подряд читались слоями «бутерброда»,
   а не одной мыслью (фидбек заказчика по скриншоту). Теперь список
   продолжает ровно ту же типографику, что и .lede (тот же кегль/вес/
   интерлиньяж/ширина строки) — визуально это её прямое продолжение,
   а не отдельный чужеродный блок. */
.about__perks {
  margin: var(--sp-6) 0 0;
  padding: 0;
  list-style: none;
  max-width: 40ch;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.about__perks li {
  font-family: var(--font-text);
  font-weight: 500;
  font-size: var(--fs-lede);
  line-height: 1.5;
  color: var(--paper-dim);
  padding-left: var(--sp-6);
  position: relative;
}

.about__perks li::before {
  content: '✓';
  position: absolute;
  left: 0;
  color: var(--paper-dim);
  font-weight: 700;
}

.about__stats {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-6);
  margin: 0;
}

.about__stat dt {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: clamp(1.75rem, 1.3rem + 1.5vw, 2.5rem);
  color: var(--signal);
}

.about__stat dd {
  margin: 0;
  font-size: var(--fs-small);
  color: var(--paper-dim);
}

.todo-badge {
  display: inline-block;
  margin-left: var(--sp-2);
  padding: 0.15em 0.6em;
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border: 1px dashed var(--paper-dim);
  border-radius: var(--radius-pill);
  color: var(--paper-dim);
}

.todo-note {
  font-size: var(--fs-small);
  color: var(--paper-dim);
}

/* --------------------------------------------------------------------------
   13. Примеры работ — 3D-карусель (coverflow)
   -------------------------------------------------------------------------- */

.works-carousel {
  margin-top: var(--sp-8);
  overflow-x: clip;
}

.works-carousel__stage {
  position: relative;
  height: clamp(320px, 48vw, 520px);
  perspective: 1400px;
}

.works-carousel__track {
  position: absolute;
  inset: 0;
  margin: 0;
  padding: 0;
  list-style: none;
}

.works-carousel__slide {
  position: absolute;
  top: 50%;
  left: 50%;
  width: clamp(200px, 24vw, 320px);
  aspect-ratio: 3 / 4;
  transition: transform var(--dur-slow) var(--ease-out), opacity var(--dur-slow) var(--ease-out);
  cursor: pointer;
}

.works-carousel__card {
  width: 100%;
  height: 100%;
  border-radius: var(--radius-l);
  overflow: hidden;
  background: var(--ink-800);
  border: 1px solid var(--ink-600);
}

.works-carousel__card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.works-carousel__card.is-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at 50% 38%, var(--ink-700) 0%, var(--ink-800) 72%);
}

.works-carousel__mark {
  width: 30%;
  height: 30%;
}

.works-carousel__nav {
  display: flex;
  gap: var(--sp-3);
  justify-content: center;
  margin-top: var(--sp-8);
}

/* --------------------------------------------------------------------------
   14. Форма заявки
   -------------------------------------------------------------------------- */

.lead__grid {
  display: grid;
  gap: var(--sp-12);
}

@media (min-width: 64rem) {
  .lead__grid {
    grid-template-columns: 0.9fr 1.1fr;
  }
}

.lead__alt {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  margin-top: var(--sp-8);
}

.lead__alt li {
  display: contents;
}

.lead-form {
  display: flex;
  flex-direction: column;
  gap: var(--sp-6);
  background: var(--ink-800);
  border: 1px solid var(--ink-600);
  border-radius: var(--radius-l);
  padding: clamp(1.5rem, 1rem + 2vw, 2.5rem);
}

.field {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.field label {
  font-size: var(--fs-small);
  font-weight: 700;
}

.field__optional {
  font-weight: 400;
  color: var(--paper-dim);
  font-size: var(--fs-caption);
}

.field input[type="text"],
.field input[type="tel"],
.field textarea {
  width: 100%;
  padding: 0.85em 1em;
  /* Единственное на сайте место, где поверхность красится в --ink-900
     (цвет фона страницы), а не в --ink-800 (цвет карточек, см. .lead-form
     ниже) — намеренно: поля выглядят утопленными в карточку формы, а не
     ещё одной плоской панелью того же тона. Не «исправлять» на ink-800. */
  background: var(--ink-900);
  border: 1px solid var(--ink-600);
  border-radius: var(--radius-s);
  color: var(--paper);
  font-family: var(--font-text);
  font-size: var(--fs-body);
  resize: vertical;
  transition: border-color var(--dur-fast) var(--ease-out);
}

.field input:focus,
.field textarea:focus {
  border-color: var(--signal);
  outline: none;
}

.field input[aria-invalid="true"] {
  border-color: var(--error);
}

.field__error {
  min-height: 1.2em;
  font-size: var(--fs-caption);
  color: var(--error);
  margin: 0;
}

.field__error:empty {
  display: none;
}

.field__hint {
  font-size: var(--fs-caption);
  color: var(--paper-dim);
  margin: 0;
}

.field--honeypot {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
  pointer-events: none;
}

.field--checkbox {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--sp-3);
}

.field--checkbox input {
  margin-top: 0.2em;
  accent-color: var(--signal);
  width: 18px;
  height: 18px;
  flex-shrink: 0;
}

.field--checkbox label {
  font-weight: 400;
  font-size: var(--fs-small);
  color: var(--paper-dim);
}

/* Чекбокс — нативный контрол (appearance:auto), border-color на него
   визуально не действует в большинстве браузеров, поэтому обычный
   .field input[aria-invalid="true"] { border-color: var(--error) }
   для него не срабатывает: единственное обязательное поле оставалось
   без видимого состояния ошибки. Красим подпись рядом вместо рамки. */
.field--checkbox:has(input[aria-invalid="true"]) label {
  color: var(--error);
}

.field--checkbox label a {
  color: var(--paper);
}

.field--checkbox .field__error {
  width: 100%;
}

.lead-form__submit {
  width: 100%;
}

.lead-form__note {
  font-size: var(--fs-caption);
  color: var(--paper-dim);
  margin: 0;
  text-align: center;
}

.form-status {
  min-height: 1.5em;
  text-align: center;
  font-size: var(--fs-small);
  font-weight: 700;
  border-radius: var(--radius-s);
}

.form-status--error {
  color: var(--error);
}

/* --------------------------------------------------------------------------
   14.5. Поп-ап успешной заявки
   <dialog> вместо кастомного оверлея — фокус-трэп, ESC и ::backdrop
   браузер даёт бесплатно, JS (form.js) только показывает/прячет.
   -------------------------------------------------------------------------- */

.success-modal {
  border: none;
  border-radius: var(--radius-l);
  padding: clamp(2.5rem, 2rem + 2.5vw, 3.75rem);
  max-width: 27rem;
  width: calc(100% - 2rem);
  background: var(--ink-800);
  /* <dialog> не наследует color от body — браузерный UA-стиль сбрасывает
     его на initial (чёрный) при показе через showModal(), раз диалог
     уходит в top layer. Без явного color заголовок/текст поп-апа были
     чёрными вместо --paper, хотя сами .success-modal__title/__text
     ничего не переопределяли и полагались на наследование. */
  color: var(--paper);
  text-align: center;
}

.success-modal::backdrop {
  background: rgba(32, 36, 46, 0.45);
}

.success-modal[open] {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
  animation: success-modal-in var(--dur-slow) var(--ease-out);
}

@keyframes success-modal-in {
  from {
    opacity: 0;
    transform: translateY(12px) scale(0.98);
  }
  to {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

.success-modal__close {
  position: absolute;
  top: var(--sp-3);
  right: var(--sp-3);
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--ink-600);
  background: transparent;
  color: var(--paper-dim);
  cursor: pointer;
  font-size: var(--fs-small);
  line-height: 1;
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}

.success-modal__close:hover {
  border-color: var(--paper-dim);
  color: var(--paper);
}

.success-modal__icon {
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: var(--signal);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--signal-ink);
  flex-shrink: 0;
}

.success-modal__icon svg {
  width: 30px;
  height: 30px;
}

.success-modal__title {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--fs-title-sm);
  line-height: 1.15;
}

.success-modal__text {
  /* Раньше был отдельный третий оттенок серого вне палитры (#454B56,
     «на тон темнее --paper-dim для читаемости») — --paper даёт то же
     самое (темнее, контрастнее), не изобретая лишний токен. */
  color: var(--paper);
  font-size: var(--fs-body);
  line-height: 1.65;
  max-width: 30ch;
}

.success-modal__alt {
  font-size: var(--fs-small);
  color: var(--paper);
  line-height: 1.6;
  max-width: 30ch;
}

.success-modal__alt a {
  color: var(--signal);
  font-weight: 700;
  text-decoration: none;
}

.success-modal__alt a:hover {
  text-decoration: underline;
}

.success-modal__ok {
  margin-top: var(--sp-2);
  min-width: 10rem;
}

/* ── Редактор лого: кадрирование загруженного файла под форму этикетки ──
   Живёт на уровне <body> (top layer), поэтому здесь светлая палитра
   сайта, а не графитовые токены #catalog. color задаём явно — <dialog>
   через showModal() не наследует его от body (см. грабли в CLAUDE.md). */
.logo-editor {
  border: none;
  border-radius: var(--radius-l);
  padding: clamp(1.5rem, 1rem + 2vw, 2.5rem);
  max-width: 30rem;
  width: calc(100% - 2rem);
  background: var(--ink-800);
  color: var(--paper);
  text-align: center;
}

.logo-editor::backdrop {
  background: rgba(32, 36, 46, 0.45);
}

.logo-editor[open] {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-4);
}

.logo-editor__close {
  position: absolute;
  top: var(--sp-3);
  right: var(--sp-3);
  width: 32px;
  height: 32px;
  border-radius: 50%;
  border: 1px solid var(--ink-600);
  background: transparent;
  color: var(--paper-dim);
  cursor: pointer;
  font-size: var(--fs-small);
  line-height: 1;
  transition: border-color var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out);
}

.logo-editor__close:hover {
  border-color: var(--paper-dim);
  color: var(--paper);
}

.logo-editor__title {
  font-family: var(--font-display);
  font-weight: 900;
  font-size: var(--fs-title-sm);
}

.logo-editor__hint {
  font-size: var(--fs-caption);
  color: var(--paper-dim);
  max-width: 34ch;
}

/* Клетчатая подложка — как в графических редакторах: сразу видно, где
   у логотипа прозрачность, а где реальный фон файла. */
.logo-editor__stage {
  display: flex;
  justify-content: center;
  width: 100%;
  padding: var(--sp-4);
  border-radius: var(--radius-m);
  background:
    repeating-conic-gradient(var(--ink-700) 0% 25%, var(--ink-800) 0% 50%) 50% / 18px 18px;
}

.logo-editor__frame {
  position: relative;
  overflow: hidden;
  cursor: grab;
  touch-action: none; /* иначе на телефоне жест уводит в скролл страницы */
  background: #f4f2ec; /* цвет самой плёнки — тот же, что запекается в PNG */
  border: 2px solid var(--ink-600);
}

.logo-editor__frame:active {
  cursor: grabbing;
}

.logo-editor__frame--round {
  width: 260px;
  height: 260px;
  border-radius: 50%;
}

.logo-editor__frame--strip {
  /* Те же 28 × 90 мм, что и на мокапе, чтобы кадр совпадал с печатью. */
  height: 288px;
  aspect-ratio: 28 / 90;
  border-radius: 10px;
}

.logo-editor__img {
  position: absolute;
  left: 50%;
  top: 50%;
  transform-origin: center;
  max-width: none; /* перебиваем глобальный img{max-width:100%} — масштаб задаёт редактор */
  user-select: none;
  -webkit-user-drag: none;
  pointer-events: none;
}

.logo-editor__controls {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  width: 100%;
}

.logo-editor__zoom {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  flex: 1;
  font-size: var(--fs-caption);
  color: var(--paper-dim);
}

.logo-editor__zoom input {
  flex: 1;
  accent-color: var(--signal);
}

.logo-editor__actions {
  display: flex;
  gap: var(--sp-3);
  width: 100%;
}

.logo-editor__actions .btn {
  flex: 1;
}

/* --------------------------------------------------------------------------
   15. Подвал
   -------------------------------------------------------------------------- */

.site-footer {
  /* Полупрозрачная заливка — как и в .offer, mesh-фон продолжается. */
  background: rgba(251, 249, 243, 0.6);
  border-top: 1px solid var(--ink-600);
  padding-block: var(--sp-12) var(--sp-6);
}

.site-footer__grid {
  display: grid;
  gap: var(--sp-8);
}

@media (min-width: 48rem) {
  .site-footer__grid {
    grid-template-columns: 1.2fr 1fr 1fr;
  }
}

.site-footer__tagline {
  margin-top: var(--sp-4);
  color: var(--paper-dim);
  font-size: var(--fs-small);
  max-width: 32ch;
}

.site-footer__col h3 {
  font-size: var(--fs-label);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--paper-dim);
  margin-bottom: var(--sp-4);
  font-weight: 700;
}

.site-footer__col ul {
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.site-footer__col a {
  text-decoration: none;
  color: var(--paper);
  font-size: var(--fs-small);
}

.site-footer__col a:hover {
  color: var(--signal);
}

.site-footer__bottom {
  margin-top: var(--sp-8);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--ink-600);
  color: var(--paper-dim);
  font-size: var(--fs-caption);
}

/* --------------------------------------------------------------------------
   16. Плавающая кнопка (мобильная)
   -------------------------------------------------------------------------- */

/* Сентинел для initFloatingCta() в main.js — 1px высоты, не 0: у элемента
   нулевой площади IntersectionObserver может вести себя неопределённо в
   разных браузерах (нулевая ширина/высота — вырожденный прямоугольник).
   Тот же приём, что уже даёт сентинел шапки в initHeaderScrollState(). */
#catalog-end-sentinel {
  height: 1px;
}

.floating-cta {
  position: fixed;
  bottom: var(--sp-4);
  left: var(--sp-4);
  right: var(--sp-4);
  z-index: 40;
  display: flex;
  gap: var(--sp-3);
  align-items: center;
}

.floating-cta[hidden] {
  display: none;
}

.floating-cta__main {
  flex-grow: 1;
}

.floating-cta__icon {
  width: 52px;
  height: 52px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--ink-800);
  border: 1px solid var(--ink-600);
  color: var(--paper);
}

@media (min-width: 64rem) {
  .floating-cta {
    display: none;
  }
}
