/* ===================================================================
   HARTWENG.COM — styles.css
   English for Life & Work — landing page

   Структура файла:
   1. Fonts (@font-face)
   2. Reset & base
   3. Color tokens
   4. Spacing tokens
   5. Radius tokens
   6. Structural tokens (не входят в шкалу --space-*)
   7. Typography classes
   8. Layout: body, page-surface, header, footer, promo-banner
   9. Components: buttons, decor strokes, bullets, toast
   10. Grid systems (Center / Sidebar)
   11. Section base
   12. Section 1 — Hero
   13. Image Block (Sections 2 / 6 / 10)
   14. Content Block (Sections 3–9, внутри Sidebar Grid)
   15. Collage
   16. Principle Cards (Section 5)
   17. CTA Section (Section 11)
   18. Spacing utilities (.mt-*)
   =================================================================== */

/* ===================================================================
   1. FONTS

   font-display: block (не swap, не optional).

   Если моргание видно даже после optional — почти наверняка дело не в
   CSS, а в том, что браузер при обычном Cmd+R не перезапрашивает
   styles.css и продолжает рендерить из кэша СТАРУЮ версию файла (там,
   где ещё стоял swap). Поэтому в index.html у styles.css добавлена
   версия в URL (?v=...) — она меняется при каждой правке файла и
   гарантированно заставляет браузер скачать актуальный CSS вместо
   кэшированного.

   Но даже если дело было не в кэше, а в самой механике — block
   убирает подмену шрифта МЕХАНИЧЕСКИ, на уровне спецификации:
   - swap: текст сразу рисуется системным шрифтом и ВСЕГДА визуально
     подменяется на кастомный, как только тот догрузится, — именно
     эта подмена и есть "моргание".
   - optional: окно ожидания ~100ms, видимой подмены после этого окна
     не бывает (своп = 0с) — но эти первые ~100ms текст невидим, а не
     показан системным шрифтом, как могло visually восприниматься.
   - block: окно ожидания у браузеров обычно ~3с — на это время текст
     невидим (не "системный шрифт", а буквально невидимый текст), а
     затем рисуется СРАЗУ кастомным шрифтом, без какой-либо подмены
     постфактум. Подмены в принципе быть не может, потому что до этого
     момента не показывается вообще никакой текст.

   Чтобы не показывать невидимый текст ощутимое время (а блок-период до
   3с — это уже заметная пауза), шрифты, используемые выше первого
   экрана, преднагружаются через <link rel="preload"> в index.html —
   на любом нормальном соединении preload укладывается в первые
   несколько сотен миллисекунд, то есть видимой паузы не будет:
   контент появляется уже сразу в правильном шрифте.
   =================================================================== */

@font-face {
  font-family: 'Nib Pro';
  src: url('assets/fonts/NibPro-600-normal.woff2') format('woff2'),
       url('assets/fonts/NibPro-600-normal.woff') format('woff');
  font-weight: 600;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: 'Nib Pro';
  src: url('assets/fonts/NibPro-700-normal.woff2') format('woff2'),
       url('assets/fonts/NibPro-700-normal.woff') format('woff');
  font-weight: 700;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: 'William Text Pro';
  src: url('assets/fonts/WilliamTextPro-400-normal.woff2') format('woff2'),
       url('assets/fonts/WilliamTextPro-400-normal.woff') format('woff');
  font-weight: 400;
  font-style: normal;
  font-display: block;
}

@font-face {
  font-family: 'William Text Pro';
  src: url('assets/fonts/WilliamTextPro-400-italic.woff2') format('woff2'),
       url('assets/fonts/WilliamTextPro-400-italic.woff') format('woff');
  font-weight: 400;
  font-style: italic;
  font-display: block;
}


/* В шрифтовом наборе нет Nib Pro Regular (400) и нет William Text Pro Bold (700).
   Запрещаем браузеру самостоятельно "утолщать" Regular до Bold (synthetic bold),
   если где-то по наследованию или fallback запросится вес, которого нет в файле. */
html {
  font-synthesis: none;
}

/* ===================================================================
   2. RESET & BASE
   =================================================================== */

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

/* html без собственного background-color по умолчанию прозрачен —
   под body виден белый канвас браузера. На обычном скролле это никогда
   не видно (body всегда занимает 100% высоты документа), но Chrome на
   macOS рисует rubber-band overscroll на уровне GPU-композитора, минуя
   обычный layout (см. комментарий у .promo-banner и .site-header ниже).
   При резком флике мышью (Magic Mouse) в момент отскока на долю кадра
   может оголиться область за пределами фактически отрисованного бокса
   body — сверху ИЛИ снизу, в зависимости от направления — и тогда
   видно не --bg-primary (тёмные "уши"), а белый канвас html, который
   на глаз почти не отличить от --bg-surface (#F7F6F1, тоже светлый) —
   воспринимается как "вспышка" чужого цвета. Фикс — закрасить сам html
   тем же тёмным цветом, что и body: тогда какую бы область ни оголил
   композитор при отскоке, она будет того же тёмного цвета, что и
   соседние "уши", без видимого шва. */
html {
  margin: 0;
  padding: 0;
  background-color: var(--bg-primary);
}

body {
  margin: 0;
  font-family: 'William Text Pro', serif;
  font-weight: 400;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

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

a {
  color: inherit;
  text-decoration: none;
}

button {
  font-family: inherit;
  cursor: pointer;
  border: none;
  background: none;
  color: inherit;
}

svg {
  display: block;
}

/* Видимый фокус для клавиатурной навигации */
a:focus-visible,
button:focus-visible {
  outline: 2px solid var(--accent-main);
  outline-offset: 2px;
}

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

/* ===================================================================
   3. COLOR TOKENS
   =================================================================== */

:root {
  /* ===== Tier 1 — Primitives ===== */
  --color-ink: #363531;
  --color-paper: #F7F6F1;
  --color-rust: #B1461C;
  --color-clay: #876F57;
  --color-olive: #6E6449;
  --color-white: #FFFFFF;

  /* State layer — наложение нейтрального слоя для hover/pressed.
     Светлый слой (white) на тёмные/акцентные цвета, тёмный слой
     (black) на светлые. white/black — не цвета интерфейса, только
     входной параметр формулы, в палитру не входят. */
  --state-layer-hover: 6%;
  --state-layer-pressed: 10%;

  --color-ink-hover: color-mix(in srgb, var(--color-ink), white var(--state-layer-hover));
  --color-ink-pressed: color-mix(in srgb, var(--color-ink), white var(--state-layer-pressed));
  --color-rust-hover: color-mix(in srgb, var(--color-rust), white var(--state-layer-hover));
  --color-rust-pressed: color-mix(in srgb, var(--color-rust), white var(--state-layer-pressed));
  --color-paper-hover: color-mix(in srgb, var(--color-paper), black var(--state-layer-hover));
  --color-paper-pressed: color-mix(in srgb, var(--color-paper), black var(--state-layer-pressed));
  --color-white-hover: color-mix(in srgb, var(--color-white), black var(--state-layer-hover));
  --color-white-pressed: color-mix(in srgb, var(--color-white), black var(--state-layer-pressed));

  /* ===== Tier 2 — Semantic roles ===== */

  /* Text */
  --text-primary: var(--color-ink);
  --text-primary-reversed: var(--color-paper);
  --text-secondary-reversed: color-mix(in srgb, var(--color-paper), transparent 50%);
  --text-accent: var(--color-rust);
  --text-accent-pressed: var(--color-rust-pressed);

  /* Backgrounds */
  --bg-primary: var(--color-ink);
  --bg-secondary: var(--color-white);
  --bg-overlay-paper-20: color-mix(in srgb, var(--color-paper), transparent 80%);
  --bg-overlay-paper-80: color-mix(in srgb, var(--color-paper), transparent 20%);
  --bg-surface: var(--color-paper);
  --bg-color-paper: var(--color-paper);
  --bg-color-rust: var(--color-rust);
  --bg-color-clay: var(--color-clay);
  --bg-color-olive: var(--color-olive);

  /* Accents */
  --accent-main: var(--color-rust);

  /* Borders */
  --border-light: color-mix(in srgb, var(--color-ink), transparent 80%);
  --border-dark: var(--color-ink);

  /* Underline — подчёркивание ссылок (body-link / button-link) */
  --underline-dark: color-mix(in srgb, var(--color-ink), transparent 90%);
  --underline-dark-hover: var(--color-rust);
  --underline-dark-pressed: var(--color-rust-pressed);
  --underline-light: color-mix(in srgb, var(--color-paper), transparent 90%);
  --underline-light-hover: var(--color-paper);
  --underline-light-pressed: var(--color-paper-pressed);

  /* Icons */
  --icon-primary: var(--color-ink);
  --icon-primary-reversed: var(--color-paper);
  --icon-accent: var(--color-rust);
  --icon-accent-hover: var(--color-rust);
  --icon-accent-pressed: var(--color-rust-pressed);

  /* Logo */
  --logo-primary: var(--color-ink);
  --logo-primary-reversed: var(--color-paper);

  /* ===== Tier 3 — Component tokens (кнопки) =====
     Архитектура классов (.btn--primary/--secondary/--accent/--reversed-bg)
     не меняется — только подключаем под неё state-layer для hover/pressed,
     которых раньше не было вообще. Полная привязка к компонентной системе
     именования (Button-M-Primary-Hug-Dark и т.п.) — отдельный шаг позже. */
  --btn-primary-bg: var(--color-ink);
  --btn-primary-bg-hover: var(--color-ink-hover);
  --btn-primary-bg-pressed: var(--color-ink-pressed);

  --btn-secondary-bg: var(--color-white);
  --btn-secondary-bg-hover: var(--color-white-hover);
  --btn-secondary-bg-pressed: var(--color-white-pressed);

  --btn-accent-bg: var(--color-rust);
  --btn-accent-bg-hover: var(--color-rust-hover);
  --btn-accent-bg-pressed: var(--color-rust-pressed);

  /* Полупрозрачный paper-слой (для тёмных/акцентных фонов под кнопкой) —
     state layer накладывается на уже готовый translucent-токен, а не на
     --color-paper напрямую: см. Design Tokens v2, раздел 3. */
  --btn-reversed-bg: var(--bg-overlay-paper-20);
  --btn-reversed-bg-hover: color-mix(in srgb, var(--btn-reversed-bg), black var(--state-layer-hover));
  --btn-reversed-bg-pressed: color-mix(in srgb, var(--btn-reversed-bg), black var(--state-layer-pressed));
}

/* ===================================================================
   4. SPACING TOKENS
   Источник: требования-к-разработке.docx → "Отступы (Spacing System)"
   Переопределяется ТОЛЬКО 4 переменных на брейкпоинте < 1200px,
   остальные остаются константными на всех разрешениях.
   =================================================================== */

:root {
  --space-4: 4px;
  --space-6: 6px;
  --space-8: 8px;
  --space-12: 12px;
  --space-14: 14px;
  --space-20: 20px;
  --space-24: 24px;
  --space-32: 32px;
  --space-40: 40px;
  --space-48: 48px;
  --space-52: 52px;
  --space-60: 60px;
  --space-80: 80px;
  --space-140: 140px;

  /* Смысловой слой — именованные отступы с фиксированным назначением */
  --space-paragraph-gap: var(--space-20);
  --space-block-gap: var(--space-40);
  --space-section-gap: var(--space-80);
}

@media (max-width: 1199px) {
  :root {
    --space-40: 32px;
    --space-60: 40px;
    --space-80: 48px;
    --space-140: 60px;
  }
}

/* ===================================================================
   5. RADIUS TOKENS
   --radius-surface — плашки, бэкграунды, surface, фото, иллюстрации
   --radius-control — только кнопки (.btn, .button-m-center)
   =================================================================== */

:root {
  --radius-surface: 4px;
  --radius-control: 12px;
}

/* ===================================================================
   6. STRUCTURAL TOKENS
   Однократные конструктивные размеры интерфейса — не часть шкалы
   --space-*, но вынесены в переменные, чтобы не дублировать магические
   числа в нескольких местах кода (DRY).
   =================================================================== */

:root {
  --promo-banner-height: 28px;
  --logo-width: 115px;
  --logo-height: 32px;
  --avatar-size: 32px;
  --hit-area: 44px;
  /* Полная высота floating-header (внутренний ряд + верх/низ border
     1px) — используется для расчёта sticky-офсета sidebar-навигации
     в секциях 3–9 (см. требования: "top: [header_height + 20]px"). */
  --header-floating-height: calc(var(--space-12) * 2 + var(--logo-height) + 2px);
  /* Тот же офсет переиспользуется как scroll-margin-top для якорных
     ссылок (#section-problem и т.п.) — без него браузер прокручивает
     ровно до верхней границы блока, а плавающий header перекрывает
     первые ~98px заголовка. */
  --scroll-offset: calc(var(--promo-banner-height) + var(--space-12) + var(--header-floating-height) + var(--space-20));
}

html {
  scroll-behavior: smooth;
}

/* Применяется глобально к любому потенциальному якорю — безопасно
   даже для id, которые сейчас не используются как scroll-target. */
[id] {
  scroll-margin-top: var(--scroll-offset);
}

/* ===================================================================
   7. TYPOGRAPHY CLASSES
   =================================================================== */

/* --- Heading 1 --- */

.h1-center {
  font-family: 'Nib Pro', serif;
  font-weight: 600;
  font-size: 60px;
  line-height: 66px;
  text-align: center;
  text-indent: 0;
}

.h1-left-indented {
  font-family: 'Nib Pro', serif;
  font-weight: 600;
  font-size: 60px;
  line-height: 66px;
  text-align: left;
  text-indent: 60px;
}

/* --- Heading 2 --- */

.h2-center {
  font-family: 'Nib Pro', serif;
  font-weight: 600;
  font-size: 40px;
  line-height: 48px;
  text-align: center;
  text-indent: 0;
}

.h2-left-indented {
  font-family: 'Nib Pro', serif;
  font-weight: 600;
  font-size: 40px;
  line-height: 48px;
  text-align: left;
  text-indent: 60px;
}

/* --- Heading 3 --- */

.h3-center {
  font-family: 'Nib Pro', serif;
  font-weight: 600;
  font-size: 24px;
  line-height: 32px;
  text-align: center;
  text-indent: 0;
}

.h3-left {
  font-family: 'Nib Pro', serif;
  font-weight: 600;
  font-size: 24px;
  line-height: 32px;
  text-align: left;
  text-indent: 0;
}

.h3-left-indented {
  font-family: 'Nib Pro', serif;
  font-weight: 600;
  font-size: 24px;
  line-height: 32px;
  text-align: left;
  text-indent: 60px;
}

/* --- Body / Primary --- */

.body-primary-left {
  font-family: 'William Text Pro', serif;
  font-weight: 400;
  font-style: normal;
  font-size: 14px;
  line-height: 20px;
  text-align: left;
  text-indent: 0;
}

.body-primary-left-indented {
  font-family: 'William Text Pro', serif;
  font-weight: 400;
  font-style: normal;
  font-size: 14px;
  line-height: 20px;
  text-align: left;
  text-indent: 60px;
}

.body-primary-center {
  font-family: 'William Text Pro', serif;
  font-weight: 400;
  font-style: normal;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  text-indent: 0;
}

/* --- Body / Accent (Italic) --- */

.body-accent-italic-left {
  font-family: 'William Text Pro', serif;
  font-weight: 400;
  font-style: italic;
  font-size: 14px;
  line-height: 20px;
  text-align: left;
  text-indent: 0;
}

.body-accent-italic-left-indented {
  font-family: 'William Text Pro', serif;
  font-weight: 400;
  font-style: italic;
  font-size: 14px;
  line-height: 20px;
  text-align: left;
  text-indent: 60px;
}

/* --- Body / Small --- */

.body-small-center {
  font-family: 'William Text Pro', serif;
  font-weight: 400;
  font-style: normal;
  font-size: 12px;
  line-height: 20px;
  text-align: center;
  text-indent: 0;
}

/* --- Caption --- */

.caption-left {
  font-family: 'William Text Pro', serif;
  font-weight: 400;
  font-style: normal;
  font-size: 12px;
  line-height: 20px;
  text-align: left;
  text-indent: 0;
}

.caption-center {
  font-family: 'William Text Pro', serif;
  font-weight: 400;
  font-style: normal;
  font-size: 12px;
  line-height: 20px;
  text-align: center;
  text-indent: 0;
}

.caption-right {
  font-family: 'William Text Pro', serif;
  font-weight: 400;
  font-style: normal;
  font-size: 12px;
  line-height: 20px;
  text-align: right;
  text-indent: 0;
}

/* --- Tag --- */

.tag-left {
  font-family: 'William Text Pro', serif;
  font-weight: 400;
  font-style: normal;
  font-size: 12px;
  line-height: 20px;
  text-align: left;
  text-indent: 0;
  text-transform: uppercase;
}

.tag-left-indented {
  font-family: 'William Text Pro', serif;
  font-weight: 400;
  font-style: normal;
  font-size: 12px;
  line-height: 20px;
  text-align: left;
  text-indent: 60px;
  text-transform: uppercase;
}

.tag-center {
  font-family: 'William Text Pro', serif;
  font-weight: 400;
  font-style: normal;
  font-size: 12px;
  line-height: 20px;
  text-align: center;
  text-indent: 0;
  text-transform: uppercase;
}

/* --- Quote --- */

.quote-left-indented {
  font-family: 'Nib Pro', serif;
  font-weight: 600;
  font-size: 24px;
  line-height: 32px;
  text-align: left;
  text-indent: 60px;
}

/* --- Button --- */

.button-m-center {
  font-family: 'Nib Pro', serif;
  font-weight: 700;
  font-size: 14px;
  line-height: 20px;
  text-align: center;
  text-indent: 0;
  text-transform: lowercase;
}

.button-link-s-center {
  font-family: 'Nib Pro', serif;
  font-weight: 700;
  font-size: 12px;
  line-height: 20px;
  text-align: center;
  text-indent: 0;
  text-transform: lowercase;
  text-decoration: underline;
  text-decoration-thickness: 1.2px;
  text-underline-offset: 2.6px;
  text-decoration-skip-ink: auto;
}

/* --- Body-Link ---
   В отличие от sidebar-nav-content (подчёркивание появляется только на
   hover — см. требования-к-разработке.docx, раздел про accent-stroke),
   у Body-Link и Button-Link подчёркивание присутствует ВСЕГДА — оно
   единственный сигнал "это ссылка" среди обычного текста, поэтому
   text-decoration: underline зашит в класс постоянно, меняется только
   цвет через --underline-dark/-light + hover/pressed состояния. */

.body-link-primary-left {
  font-family: 'William Text Pro', serif;
  font-weight: 400;
  font-style: normal;
  font-size: 14px;
  line-height: 20px;
  text-align: left;
  text-indent: 0;
  text-decoration: underline;
  text-decoration-thickness: 1.2px;
  text-underline-offset: 2.6px;
  text-decoration-skip-ink: auto;
}

.body-link-small-left {
  font-family: 'William Text Pro', serif;
  font-weight: 400;
  font-style: normal;
  font-size: 12px;
  line-height: 20px;
  text-align: left;
  text-indent: 0;
  text-decoration: underline;
  text-decoration-thickness: 1.2px;
  text-underline-offset: 2.6px;
  text-decoration-skip-ink: auto;
}

.body-link-small-center {
  font-family: 'William Text Pro', serif;
  font-weight: 400;
  font-style: normal;
  font-size: 12px;
  line-height: 20px;
  text-align: center;
  text-indent: 0;
  text-decoration: underline;
  text-decoration-thickness: 1.2px;
  text-underline-offset: 2.6px;
  text-decoration-skip-ink: auto;
}

/* --- Tablet (768px – 1199px) --- */
@media (min-width: 768px) and (max-width: 1199px) {
  .h1-center, .h1-left-indented { font-size: 48px; line-height: 52px; }
  .h2-center, .h2-left-indented { font-size: 30px; line-height: 36px; }
  .h3-center, .h3-left, .h3-left-indented, .quote-left-indented { font-size: 20px; line-height: 28px; }
}

/* --- Mobile (< 768px) --- */
@media (max-width: 767px) {
  .h1-center, .h1-left-indented { font-size: 36px; line-height: 42px; }
  .h2-center, .h2-left-indented { font-size: 24px; line-height: 34px; }
  .h3-center, .h3-left, .h3-left-indented, .quote-left-indented { font-size: 18px; line-height: 28px; }
}

/* ===================================================================
   8. LAYOUT: BODY, PAGE-SURFACE, HEADER, FOOTER, PROMO-BANNER
   =================================================================== */

/* --- Body — тёмный фон (Background), "уши" по бокам от page-surface ---
   6px — фиксированный боковой gutter на всех разрешениях (= --space-6,
   единственный токен шкалы, не меняющийся ни на одном брейкпоинте). */

body {
  background-color: var(--bg-primary);
  padding: 0 var(--space-6);
}

/* --- Promo Banner ---
   position: fixed — прибит к верху viewport, не участвует в rubber-band
   overscroll. Контент под ним начинается после .promo-banner-spacer
   в нормальном document flow. */

.promo-banner {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 200;
  height: var(--promo-banner-height);
  background-color: var(--bg-primary);
}

.promo-banner__inner {
  max-width: 1728px;
  height: 100%;
  margin: 0 auto;
  padding: 0 var(--space-12);
}

/* Вся зона баннера (28px по высоте) кликабельна — ссылка растянута на
   весь .promo-banner__inner, текст центрируется внутри неё самой. Весь
   текст баннера — одна ссылка на один адрес, поэтому используется
   .body-link-small-center (постоянное подчёркивание, см. требования-
   к-разработке.docx) вместо .body-small-center, который тут был
   ошибочно — баннер целиком кликабелен, значит весь текст и есть Link. */
.promo-banner__link {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  color: var(--text-primary-reversed);
  text-decoration-color: var(--underline-light);
  transition: text-decoration-color 0.15s ease;
}

@media (hover: hover) {
  .promo-banner__link:hover {
    text-decoration-color: var(--underline-light-hover);
  }
}

.promo-banner__link:active {
  text-decoration-color: var(--underline-light-pressed);
}

/* Распорка — занимает место fixed promo-banner в потоке */
.promo-banner-spacer {
  height: var(--promo-banner-height);
}

/* --- Page Surface ---
   Единый светлый контейнер: header (sticky) + site-wrapper лежат
   внутри одной карточки с общим border-radius и общим визуальным полем.
   Намеренно БЕЗ overflow:hidden — оно ломает position:sticky внутри себя
   (создаёт собственный scroll-container, и sticky перестаёт прилипать
   к viewport). Скругления верхних углов не требуют клиппинга: и
   page-surface, и header по умолчанию используют один и тот же цвет
   (--bg-surface == --bg-color-paper), поэтому видимого шва нет даже
   без обрезки. */

.page-surface {
  max-width: 1728px;
  margin: 0 auto;
  background-color: var(--bg-surface);
  border-radius: var(--radius-surface);
  /* will-change: transform — явный compositing layer для page-surface.
     Гипотеза по баг-репорту: на macOS Chrome рисует rubber-band overscroll
     на уровне GPU-композитора (см. комментарий у .site-header ниже). Сам
     page-surface не позиционирован (обычный block-элемент), но содержит
     внутри position:sticky потомка (.site-header) — это иногда вынуждает
     браузер неявно промоутить page-surface в отдельный composited layer
     для скролла БЕЗ явных, корректных границ слоя, и при резком отскоке
     (быстрый флик Magic Mouse) этот неявный слой на кадр прорисовывается
     шире своего реального бокса — на всю ширину viewport, поверх тёмных
     "ушей" — сплошным заливом --bg-surface. will-change: transform даёт
     браузеру явный, корректно ограниченный слой заранее, что обычно
     убирает такие гонки рендеринга. Визуально ни на что не влияет (не
     меняет layout/paint, только подсказка композитору). */
  will-change: transform;
}

/* --- Site Wrapper ---
   Внутренние отступы surface: --space-12 по горизонтали (низ — отдельным
   отступом перед футером). Верхний отступ обеспечивает сам header. */

.site-wrapper {
  padding: 0 var(--space-12) var(--space-80);
}

/* --- Header ---
   Единый элемент с двумя визуальными состояниями, переключаемыми классом
   .is-floating (см. script.js):

   1) Дефолт (scrollY === 0): header — обычный блок в document flow,
      без полей, без обводки, фон совпадает с фоном surface — визуально
      монолитен с ним. Именно поэтому при верхнем rubber-band (когда
      scrollY физически остаётся 0, а страница лишь визуально "тянется"
      вниз) header не отделяется от surface никаким костылём — он просто
      часть того же потока и тянется вместе с ним, без единого пикселя
      разрыва и без видимого фона за ним.

   2) Floating (scrollY > 0): header сужается с боков (margin) и
      отступает от верха — но верхний отступ сделан через сам sticky
      `top`, а не через margin-top. margin-top на застрявшем sticky-
      элементе всё равно резервирует место в потоке документа и при
      каждом переключении класса двигал бы весь контент под header на
      --space-12 вниз-вверх (reflow/прыжок). `top` на sticky влияет
      только на точку прилипания относительно viewport и НЕ резервирует
      место в потоке — поэтому смена офсета анимируется как сдвиг
      самого header, без единого пикселя скачка остального контента.
      Заодно header получает обводку по периметру, скругления со всех
      сторон и blur-подложку — становится "плавающей пилюлей". Так как
      sticky уже "застрял" в этом состоянии, нижний rubber-band
      (overscroll за конец страницы) на него не влияет — это нативное
      поведение position: sticky, JS физику скролла не подменяет. */

.site-header {
  position: sticky;
  top: var(--promo-banner-height);
  z-index: 100;
  margin: 0;
  border: 1px solid transparent;
  border-radius: var(--radius-surface) var(--radius-surface) 0 0;
  background-color: var(--bg-color-paper);
  transition: top 0.25s ease, margin 0.25s ease, border-color 0.25s ease,
              border-radius 0.25s ease, background-color 0.25s ease,
              backdrop-filter 0.25s ease;
}

.site-header.is-floating {
  top: calc(var(--promo-banner-height) + var(--space-12));
  margin: 0 var(--space-12);
  border-color: var(--border-light);
  border-radius: var(--radius-surface);
  background-color: var(--bg-overlay-paper-80);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* 12+32+12 = 56px — совпадает с 6+44+6 для зоны клика CTA, поэтому
     лого и кнопки выравниваются по одной визуальной высоте header. */
  height: calc(var(--space-12) * 2 + var(--logo-height));
  padding: 0 var(--space-12);
}

/* Лого + аватар: единый вид на всех размерах */
.site-header__brand {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--space-8);
  text-decoration: none;
  color: var(--logo-primary);
}

.site-header__logo {
  width: var(--logo-width);
  height: var(--logo-height);
  color: var(--logo-primary);
  flex: none;
  display: block;
}

.site-header__avatar {
  width: var(--avatar-size);
  height: var(--avatar-size);
  border-radius: var(--radius-surface);
  overflow: hidden;
  flex: none;
}

.site-header__avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Button-links: зона клика --hit-area (44×44px) */
.site-header__actions {
  display: flex;
  align-items: center;
  /* "Горизонтальный отступ между CTA и CTA 2" — --space-14, единое
     значение на всех разрешениях (на мобильном текст схлопывается до
     иконок, но сам gap не меняется). */
  gap: var(--space-14);
}

.cta-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-6);
  color: var(--text-primary);
  text-decoration-color: var(--underline-dark);
  white-space: nowrap;
  /* "Button-Link: внутренние отступы сверху/снизу --space-6, слева/
     справа 0px" — добавлено поверх уже существующей hit-area 44×44px
     (min-width/min-height), которую трогать не нужно: 44px остаётся
     минимальным размером зоны клика, padding только довносит
     корректный внутренний отступ контента, не уменьшая саму зону. */
  padding: var(--space-6) 0;
  min-width: var(--hit-area);
  min-height: var(--hit-area);
}

.cta-link svg {
  width: 16px;
  height: 16px;
  color: var(--icon-primary);
  flex: none;
}

@media (hover: hover) {
  .cta-link:hover {
    color: var(--text-accent);
    text-decoration-color: var(--underline-dark-hover);
  }
}
@media (hover: hover) {
  .cta-link:hover svg {
    color: var(--icon-accent-hover);
  }
}
.cta-link:active {
  color: var(--text-accent-pressed);
  text-decoration-color: var(--underline-dark-pressed);
}
.cta-link:active svg {
  color: var(--icon-accent-pressed);
}

.cta-link__label {
  display: inline;
}

/* Мобильный: текст схлопывается, остаются иконки с hit-area 44×44px */
@media (max-width: 767px) {
  .cta-link__label {
    display: none;
  }

  .cta-link svg {
    width: 20px;
    height: 20px;
  }
}

/* --- Footer ---
   Обычный блок в потоке, тёмный фон, вне page-surface. */

.site-footer {
  max-width: 1728px;
  margin: 0 auto;
  background-color: var(--bg-primary);
  /* "Минимальные отступы сверху и снизу: --space-12" + "слева/справа:
     (--space-6 + --space-12) = 18px" — компонент_Page_Shell.docx.
     18px не сводится к одному токену шкалы — сумма двух, как и в доке. */
  padding: var(--space-12) calc(var(--space-6) + var(--space-12));
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  /* "Содержит три под-контейнера с отступом --space-20 между ними" */
  gap: var(--space-20);
}

.site-footer__logo {
  width: var(--logo-width);
  height: var(--logo-height);
  color: var(--logo-primary-reversed);
  flex: none;
  /* "Внутренние отступы от краёв контейнера до контента сверху и снизу: --space-6" */
  padding: var(--space-6) 0;
}

.site-footer__copy {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-12);
  max-width: 840px;
  flex: 1;
  text-align: center;
  /* "Внутренние отступы от краёв контейнера до контента сверху и снизу: --space-6" */
  padding: var(--space-6) 0;
}

.site-footer__copy p {
  margin: 0;
}

.site-footer__copy .body-small-center:first-child {
  color: var(--text-secondary-reversed);
}

.site-footer__copy .body-small-center:last-child {
  color: var(--text-primary-reversed);
}

.site-footer__actions {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  /* "Отступ между Button-Link 1 и Button-Link 2: --space-4" (было
     ошибочно --space-12) */
  gap: var(--space-4);
  flex: none;
}

.footer-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-6);
  color: var(--text-primary-reversed);
  text-decoration-color: var(--underline-light);
  white-space: nowrap;
  /* "Внутренние отступы сверху и снизу: --space-6, слева/справа: 0px" —
     было ошибочно без вертикального отступа вообще. */
  padding: var(--space-6) 0;
}

.footer-link svg {
  width: 16px;
  height: 16px;
  color: var(--icon-primary-reversed);
  flex: none;
}

@media (hover: hover) {
  .footer-link:hover {
    color: var(--text-primary-reversed);
    text-decoration-color: var(--underline-light-hover);
  }
}
.footer-link:active {
  text-decoration-color: var(--underline-light-pressed);
}

@media (max-width: 767px) {
  .site-footer {
    flex-direction: column;
    align-items: center;
    gap: var(--space-24);
  }

  .site-footer__actions {
    align-items: center;
    width: 100%;
    order: 3;
  }

  .site-footer__copy {
    order: 2;
  }
}

/* ===================================================================
   9. COMPONENTS: BUTTONS, DECOR STROKES, BULLETS, TOAST
   =================================================================== */

/* --- Buttons (CTA) ---
   Внутренние отступы: верх/низ --space-14 (14px), право/лево --space-20
   (20px). Скругление — только --radius-control (только у кнопок). */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-6);
  /* верх/низ --space-14 (14px), право/лево --space-24 (24px) */
  padding: var(--space-14) var(--space-24);
  border-radius: var(--radius-control);
  border: 1px solid var(--border-light);
  white-space: nowrap;
}

.btn svg {
  width: 20px;
  height: 20px;
  flex: none;
}

.btn--primary {
  background-color: var(--btn-primary-bg);
  color: var(--text-primary-reversed);
  transition: background-color 0.15s ease;
}
.btn--primary svg { color: var(--icon-primary-reversed); }
@media (hover: hover) { .btn--primary:hover { background-color: var(--btn-primary-bg-hover); } }
.btn--primary:active { background-color: var(--btn-primary-bg-pressed); }

.btn--secondary {
  background-color: var(--btn-secondary-bg);
  color: var(--text-primary);
  transition: background-color 0.15s ease;
}
.btn--secondary svg { color: var(--icon-primary); }
@media (hover: hover) { .btn--secondary:hover { background-color: var(--btn-secondary-bg-hover); } }
.btn--secondary:active { background-color: var(--btn-secondary-bg-pressed); }

.btn--accent {
  background-color: var(--btn-accent-bg);
  color: var(--text-primary-reversed);
  transition: background-color 0.15s ease;
}
.btn--accent svg { color: var(--icon-primary-reversed); }
@media (hover: hover) { .btn--accent:hover { background-color: var(--btn-accent-bg-hover); } }
.btn--accent:active { background-color: var(--btn-accent-bg-pressed); }

.btn--reversed-bg {
  background-color: var(--btn-reversed-bg);
  color: var(--text-primary-reversed);
  transition: background-color 0.15s ease;
}
.btn--reversed-bg svg { color: var(--icon-primary-reversed); }
@media (hover: hover) { .btn--reversed-bg:hover { background-color: var(--btn-reversed-bg-hover); } }
.btn--reversed-bg:active { background-color: var(--btn-reversed-bg-pressed); }

.btn-group {
  display: flex;
  /* "Горизонтальный отступ между CTA 1 и CTA 2" — --space-12 */
  gap: var(--space-12);
  flex-wrap: wrap;
}

.btn--block {
  width: 100%;
}

/* Адаптив (< 768px): группа кнопок встаёт друг под друга и
   растягивается на всю ширину контейнера. Сейчас единственный
   потребитель .btn-group — Hero (.btn-group--center), но правило
   написано без привязки к конкретной секции, чтобы будущим парным
   CTA было что переиспользовать вместо копирования той же логики.
   :not(.btn--block) — кнопки, у которых "растягивание на всю ширину"
   уже задано БЕЗУСЛОВНО (не только на мобильном), не нужно трогать
   здесь повторно — но это и не помешало бы, не более чем
   декларативная избыточность. На десктопе отдельное правило не
   требуется: .btn — inline-flex, по умолчанию сжимается по контенту. */
@media (max-width: 767px) {
  .btn-group {
    flex-direction: column;
  }
  .btn-group .btn:not(.btn--block) {
    width: 100%;
  }
}

/* --- Decor strokes (handwritten underline accents) ---
   Метод «Акцентный штрих»: для одного слова / короткой фразы.
   SVG вставляется как настоящий инлайн-элемент (не background/mask),
   поэтому currentColor внутри файла наследует цвет напрямую. */

.accent-stroke {
  position: relative;
  display: inline-block;
  /* isolation:isolate создаёт новый stacking context, чтобы текстовый
     узел span рисовался поверх дочернего SVG с z-index:-1 только
     внутри этого контекста, не уходя под фон родителя. */
  isolation: isolate;
  /* text-indent наследуется, а inline-block формирует собственный
     блочный контекст — без сброса унаследованный отступ строк вроде
     .h2-left-indented (text-indent: 60px) применяется ВТОРОЙ раз к
     первой (единственной) строке самого span, раздувая его ширину и
     сдвигая штрих относительно слова. */
  text-indent: 0;
  /* Без этого инлайн-блок не переносится по словам родителя — его
     ширина считается по полному "preferred width" содержимого, и
     если внутри ровно одно длинное слово (напр. "Альтернативный" на
     320px), оно физически не помещается в строку, но т.к. внутри
     span нет пробелов, перенести его не на чем — блок раздувается
     до полной ширины слова и вылезает за пределы surface вместе со
     своим SVG-штрихом (он масштабируется на 100% от ширины span).
     max-width ограничивает span шириной родителя, overflow-wrap
     разрешает перенос непосредственно внутри слова, когда оно не
     помещается — на обычных разрешениях слово целиком помещается на
     одной строке и перенос не срабатывает, это чисто защитный
     механизм для предельно узких экранов. */
  max-width: 100%;
  overflow-wrap: break-word;
}

/* Компенсация для штриха внутри *-left-indented контента (text-indent:
   60px). Браузер считает shrink-to-fit ширину инлайн-блока по полной
   ширине строки, не вычитая text-indent первой строки, но позиционирует
   сам блок уже ПОСЛЕ отступа — итоговый правый край блока выходит за
   границы контейнера ровно на величину отступа (60px). Это заметно
   только если содержимое accent-stroke не помещается в одну строку и
   переносится внутри самого span (напр. длинная фраза на decor3/decor3
   в .h3-left-indented на узких экранах) — тогда max-width: 100% выше
   не спасает, т.к. браузер изначально завышает "100%" на те же 60px.
   Значение 60px — то же самое число, что задаёт text-indent у
   *-left-indented (см. секцию 7. TYPOGRAPHY CLASSES); если отступ там
   изменится, поменять и здесь. */
.h1-left-indented .accent-stroke,
.h2-left-indented .accent-stroke,
.h3-left-indented .accent-stroke,
.quote-left-indented .accent-stroke {
  max-width: calc(100% - 60px);
}

/* SVG-штрих под текстом. width:100% — масштабируется по ширине span,
   height — фиксированная нативная высота (не деформируется по Y).
   preserveAspectRatio="none" на самом SVG-теге обязателен — без него
   браузер сохраняет пропорции и масштабирует по обеим осям. Значения
   bottom — фиксированная подгонка под конкретный decor-asset, не часть
   шкалы --space-* (микропозиционирование декоративного элемента). */
.accent-stroke__svg {
  position: absolute;
  left: 0;
  width: 100%;
  height: 21px;
  pointer-events: none;
  color: var(--accent-main);
  z-index: -1;
  overflow: visible;
}

.accent-stroke--decor1 .accent-stroke__svg {
  bottom: -10px;
}

/* decor-underline-2.svg — нативный viewBox 233×8 (намного площе и тоньше
   decor1's 781×21), используется под H2 (40px) вместо H1 (60px) —
   собственная высота и более тесный bottom, не общие 21px/-10px. */
.accent-stroke--decor2 .accent-stroke__svg {
  height: 8px;
  bottom: -4px;
}

/* decor-underline-3.svg — нативный viewBox 348×8 (та же тонкая
   "плоская" пропорция, что у decor2), используется под H3 (24px).
   Без явного height здесь наследовался дефолтный height: 21px из
   базового правила (откалиброван под decor1, 781×21) — а с
   preserveAspectRatio="none" любое несовпадение height с нативной
   высотой ассета означает реальное вертикальное растяжение картинки
   (~2.6×), а не просто другой бокс. Отсюда и видимое искажение формы
   штриха — высота обязана быть зафиксирована под конкретный ассет,
   как и у decor2/decor4 ниже. */
.accent-stroke--decor3 .accent-stroke__svg {
  height: 8px;
  bottom: -4px;
}

/* decor-underline-4.svg — нативный viewBox 127×11. Пока нигде не
   используется, но та же логика, что у decor2/decor3 — без явного
   height унаследовал бы 21px по умолчанию и точно так же исказился
   бы при первом использовании. */
.accent-stroke--decor4 .accent-stroke__svg {
  height: 11px;
  bottom: -6px;
}

/* decor-underline-5.svg — нативный viewBox 592×15, используется под H2
   (40px) для длинной фразы "формат и структура занятий" (Section 8).
   По дизайну штрих приходится ровно на вторую строку H2 (фраза сама
   по себе становится отдельной строкой при переносе) — единый росчерк
   без дублирования по словам, как и у "Без домашних заданий из
   учебников" в Section 5 (тот же класс задач: длинная фраза, что
   физически уже укладывается в одну строку благодаря самой длине
   текста H2 выше). */
.accent-stroke--decor5 .accent-stroke__svg {
  height: 15px;
  bottom: -6px;
}

/* Метод «Текстовый штрих»: для длинных фраз/H2, которые могут
   переноситься на несколько строк. currentColor не работает для
   background-image/mask-image SVG (ограничение самого CSS — фоновые
   SVG не наследуют цвет родителя), поэтому штрих вставляется как
   настоящий инлайн <svg> через JS (см. script.js → initTextStrokes) и
   растягивается на фактическую ширину обёрнутого текста. */

.text-stroke {
  position: relative;
  display: block;
  /* см. комментарий у .accent-stroke — тот же риск двойного
     text-indent, тут актуально для display:block. */
  text-indent: 0;
}

.text-stroke__svg-row {
  display: flex;
  width: 100%;
  overflow: hidden;
  pointer-events: none;
  color: var(--accent-main);
}

.text-stroke__svg-row svg {
  flex: none;
  height: auto;
}

/* --- Bullet (списки с кружком-маркером) --- */

.bullet-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.bullet-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-24);
}

.bullet-item__dot {
  width: 12px;
  height: 12px;
  flex: none;
  margin-top: var(--space-6);
  color: var(--accent-main);
}

/* --- Toast (плашка уведомления при копировании) --- */

.toast {
  position: fixed;
  top: -100px;
  left: 50%;
  transform: translateX(-50%);
  background-color: var(--bg-primary);
  color: var(--text-primary-reversed);
  padding: var(--space-8) var(--space-14);
  border-radius: var(--radius-surface);
  z-index: 1000;
  transition: top 0.3s ease;
  white-space: nowrap;
}

.toast--visible {
  top: calc(var(--promo-banner-height) + var(--space-8));
}

/* ===================================================================
   10. GRID SYSTEMS
   =================================================================== */

/* --- Center Grid ---
   Используется для: H1, разделительных изображений, финального
   CTA-блока. Боковой gutter обеспечивает .site-wrapper (--space-12),
   здесь дополнительный паддинг не нужен — иначе отступ задваивается. */

.center-grid {
  max-width: 1080px;
  margin: 0 auto;
}

.center-grid--text {
  max-width: 840px;
}

/* --- Sidebar Grid ---
   Используется в секциях 3–9: левая навигация + правый контент.
   Вся зона 3–9 — ОДНА секция (.sidebar-section) с одним общим
   .sidebar-layout: навигация — sticky-элемент слева, остаётся
   прижатой на всём протяжении скролла через все под-блоки (включая
   разделительные Image-блоки вроде Section 6 — они просто ещё один
   блок внутри той же колонки контента, навигация для них не меняется
   и не пропадает). nav реализована как flex-item с align-items:
   flex-start на родителе — её "containing block" для sticky остаётся
   высотой во всю .sidebar-layout (по самому высокому соседу,
   .sidebar-content), поэтому она отлипает только у самого низа всей
   секции — там, где стандартный отступ между секциями
   (.section:not(:last-child) { padding-bottom: var(--space-80) })
   уже отделяет её от Section 10. Никакой дополнительной логики под
   "отступ --space-80 перед концом навигации" не требуется — это
   и есть тот самый отступ. */

.sidebar-layout {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
}

/* top: высота promo-banner + верхний inset floating-header (--space-12)
   + сама высота floating-header + --space-20 (20px) — формула из
   требования "position: sticky; top: [header_height + 20]px". К
   моменту, когда navigation вообще попадает во viewport (контент
   секций 3–9 всегда далеко за пределами Hero), header уже гарантированно
   в floating-состоянии, так что его floating-высота — корректная
   базовая величина для расчёта. */
.sidebar-nav {
  position: sticky;
  top: var(--scroll-offset);
  flex: none;
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

.sidebar-nav__item {
  display: flex;
  align-items: baseline;
  /* "Отступы между нумерацией и текстом (горизонтальные)" — --space-52 */
  gap: var(--space-52);
  /* Шрифтовые свойства .sidebar-nav-content-left — один класс на все
     состояния, без отдельной копии под hover (см. требования-к-разработке.docx,
     обсуждение sidebar-nav: подчёркивание появляется только на hover/focus,
     по умолчанию его нет вообще, не плавный переход из прозрачного). */
  font-family: 'William Text Pro', serif;
  font-weight: 400;
  font-style: normal;
  font-size: 14px;
  line-height: 20px;
  text-align: left;
  text-decoration: none;
  color: var(--text-primary);
  transition: color 0.15s ease;
}

@media (hover: hover) {
  .sidebar-nav__item:hover,
  .sidebar-nav__item:focus-visible {
    color: var(--text-accent);
  }
}

/* Подчёркивание — только на тексте, не на нумерации: цвет (affordance
   состояния) красит весь пункт целиком, а линия подчёркивания — только
   под словом, иначе визуально читается как баг на цифре. */
@media (hover: hover) {
  .sidebar-nav__item:hover .sidebar-nav__label,
  .sidebar-nav__item:focus-visible .sidebar-nav__label {
    text-decoration: underline;
    text-decoration-color: var(--underline-dark-hover);
    text-decoration-thickness: 1.2px;
    text-underline-offset: 2.6px;
    text-decoration-skip-ink: auto;
  }
}

.sidebar-nav__item:active {
  color: var(--text-accent-pressed);
}
.sidebar-nav__item:active .sidebar-nav__label {
  text-decoration-color: var(--underline-dark-pressed);
}

.sidebar-nav__item.is-active {
  color: var(--accent-main);
}

.sidebar-nav__num {
  width: 32px;
  flex: none;
}

.sidebar-nav__label {
  width: 132px;
  flex: none;
}

/* flex: 1 1 auto (НЕ фиксированная ширина) — принципиально.
   .sidebar-content должна занимать ВСЁ оставшееся место в строке
   после .sidebar-nav, а не иметь собственный потолок. Ширина
   текстовой колонки (840px) задаётся ВНУТРИ — на .content-block
   (max-width: 840px; margin-left: auto — "прижато к правому краю"). */
.sidebar-content {
  flex: 1 1 auto;
  min-width: 0;
}

/* "Center Grid"-блоки (Section 6/10-стиль — .center-grid.image-block)
   внутри Sidebar Grid НЕ центрируются в свободном пространстве (этот
   вариант обсуждался и был сознательно отклонён — визуально читался
   бы как "контент справа закончился", хотя это не так). Вместо этого
   — тот же принцип, что и у текстовых .content-block: прижатие к
   правому краю, просто в более широком боксе (1080px вместо 840px).
   Правый край блока совпадает с правым краем текста над/под ним,
   левый — уходит дальше влево, расширяя блок как "перебивку" ритма,
   не теряя визуальной связи с остальным контентом справа. */
.sidebar-content .center-grid {
  /* Только margin-left/-right — margin-top сюда специально не входит:
     вертикальный отступ перед блоком задаёт .mt-8xl (см. секцию 16),
     и эта строка не должна его перебивать. margin: 0 0 0 auto тут
     стоял раньше и именно это и делал — обнулял margin-top, потому
     что специфичность этого селектора (2 класса) выше, чем у .mt-8xl
     (1 класс), и побеждает вне зависимости от порядка объявления. */
  margin-right: 0;
  margin-left: auto;
}

/* На мобильном (< 768px) Sidebar Grid целиком уступает место
   одноколоночной вёрстке — навигация не показывается (см. также
   требования: "Mobile (< 768px): Отказ от Sidebar Grid в пользу
   одноколоночной верстки. Пункты навигации на мобильных устройствах
   не отображаются"). На планшете (768–1199px) сетка остаётся —
   --space-* токены уже масштабируются на этом брейкпоинте сами по
   себе, отдельного коэффициента 0.8–0.9 не потребовалось. */
@media (max-width: 767px) {
  .sidebar-nav {
    display: none;
  }
  .sidebar-content {
    flex: 1 1 auto;
    max-width: 100%;
    margin-left: 0;
  }
}

/* ===================================================================
   11. SECTION BASE
   =================================================================== */

.section {
  position: relative;
}

/* Отступ между крупными контентными секциями (как они нарезаны в
   /figma-export — Section_1, Section_2, ... — кроме
   Section_0_Surface*.png, которые описывают сам контейнер
   surface/header/footer, а не контентную секцию) — фиксированно
   --space-80 (80px десктоп / 48px на <1200px). Единая точка истины:
   применяется ко всем секциям, кроме последней (после последней
   секции отступ перед концом surface даёт .site-wrapper). Так каждая
   новая секция автоматически получает правильный отступ без
   дублирования правила. */
.section:not(:last-child) {
  padding-bottom: var(--space-section-gap);
}

/* ===================================================================
   12. SECTION 1 — HERO
   =================================================================== */

.hero-section__inner {
  /* "От верхнего края Surface Wrapper до Tag" — --space-140 (140px) в
     терминах макета, ИЛИ --space-80 (80px), если отступ замеряется от
     header до Tag по коду (формулировка из обновлённой документации).
     header у нас — отдельный sibling-элемент ВНЕ .site-wrapper (а не
     часть Surface Wrapper), поэтому верхний паддинг .hero-section__inner
     технически и есть "отступ от header до Tag" — используем
     соответствующее этому методу значение, --space-80. */
  padding-top: var(--space-80);
  padding-bottom: 0;
}

.hero-section__tag {
  color: var(--text-primary);
  margin: 0;
}

.hero-section__title {
  color: var(--text-primary);
  /* "Между Tag и Heading 1" — --space-32 */
  margin: var(--space-32) 0 0;
}

.hero-section__subtitle-wrap {
  /* "Между Heading 1 и Subtitle" — --space-40 */
  margin-top: var(--space-40);
}

.hero-section__subtitle {
  color: var(--text-primary);
  margin: 0;
}

.hero-section__buttons {
  /* "Между Subtitle и Buttons" — --space-60 */
  margin-top: var(--space-60);
}

.btn-group--center {
  justify-content: center;
}

/* ===================================================================
   13. IMAGE BLOCK
   Переиспользуемый компонент для разделительных изображений с двумя
   caption (верхний слева, нижний справа) — встречается идентично в
   Section 2, Section 6 и Section 10 (отличаются только изображением и
   текстом caption). Контейнер — .center-grid (max-width: 1080px),
   собственного бокового паддинга не задаёт.
   =================================================================== */

.image-block__caption-top {
  color: var(--text-primary);
  margin: 0;
}

.image-block__img {
  display: block;
  width: 100%;
  /* Десктоп/планшет (≥600px — граница сознательно отдельная от
     768px/1200px, на которых переключается раскладка, см. комментарий
     у мобильного override ниже): высота ЗАФИКСИРОВАНА (не max-height) —
     632px в Center Grid (Section 2/6/10), 520px внутри Sidebar Grid
     (см. override ниже). Ширина при этом продолжает гибко занимать
     100% контейнера (но не больше max-width — см. ниже), поэтому при
     сужении контейнера эффективное соотношение сторон бокса меняется —
     object-fit: cover докадрирует картинку по бокам, чтобы высота
     оставалась неизменной величиной на любой ширине вплоть до 600px. */
  height: 632px;
  max-width: 1080px;
  object-fit: cover;
  object-position: center;
  border-radius: var(--radius-surface);
  /* "От верхнего Caption до изображения" — --space-8 */
  margin-top: var(--space-8);
}

/* Внутри Sidebar Grid (Section 3–9) контейнер уже 840px, а не 1080 —
   и зафиксированная высота фото там другая, 520px (не 632). */
.sidebar-content .image-block__img {
  height: 520px;
}

/* Section 6 — концептуально "Center Grid" тип изображения (как
   Section 2/10, высота 632px), а не обычное инлайн-фото внутри
   Sidebar Grid (520px) — физически лежит внутри .sidebar-content
   (чтобы navigation не пропадала рядом с ним), поэтому высота
   переопределена точечно. ID-селектор гарантированно перебивает
   правило выше по специфичности — порядок объявления тут ни при чём. */
#section-image-2.image-block .image-block__img {
  height: 632px;
}

.image-block__caption-bottom {
  color: var(--text-primary);
  /* "От изображения до нижнего Caption" — --space-8 */
  margin-top: var(--space-8);
  margin-bottom: 0;
}

/* req. (уточнено отдельно от общего layout-брейкпоинта в 768px):
   "плавающий по пропорции" режим с floor-высотой относится только к
   по-настоящему узким экранам — горизонтальные фотографии —
   min-height: 258px; вертикальные — min-height: 424px. На десктопе и
   планшете (от 600px и выше, включая весь диапазон 600–767px, где
   текстовая раскладка уже одноколоночная, но фото остаётся широким)
   высота зафиксирована (632px / 520px, см. выше) независимо от
   ширины контейнера — это сознательно ОТДЕЛЬНАЯ граница от
   768px/1200px, на которых переключается раскладка (sidebar nav,
   карточки, токены --space-*): высота фото не обязана дробиться по
   тем же точкам, что и колоночная сетка. Ниже 600px высота снова
   пропорциональна нативному соотношению сторон картинки и НЕ
   опускается ниже этого порога: как только узкий контейнер сжал бы её
   сильнее, min-height останавливает сжатие, а object-fit: cover
   аккуратно докадрирует превышение без искажения пропорций.
   .image-block__img по умолчанию — горизонтальная ориентация (258px);
   для вертикальных изображений добавляется модификатор
   .image-block__img--portrait (424px). */
@media (max-width: 599px) {
  .image-block__img,
  .sidebar-content .image-block__img,
  #section-image-2.image-block .image-block__img {
    height: auto;
    min-height: 258px;
  }

  .image-block__img--portrait {
    min-height: 424px;
  }
}

/* ===================================================================
   14. CONTENT BLOCK
   Один топик внутри .sidebar-content (Section 3 Проблема, далее
   аналогично — История, Подход, Процесс, Занятия, Кому). Сам
   .sidebar-content теперь не имеет собственной ширины — растягивается
   на всё оставшееся место после .sidebar-nav (см. соответствующую
   секцию выше), поэтому ширина текстовой колонки (840px) и прижатие
   к правому краю — забота самого .content-block (правило сразу под
   этим комментарием), а не родителя.

   Вертикальные интервалы между элементами (Tag → H2 → Параграф →
   Bullet → Image → ...) у каждой секции свои — комбинаторный список
   sibling-селекторов под каждую возможную пару типов быстро не
   масштабируется на 7 разных секций с разным ритмом. Вместо этого
   отступ навешивается явно, через одну из утилит .mt-* — её ставят
   на элемент, СТОЯЩИЙ ПОСЛЕ нужного промежутка, прямо в разметке.
   Это самый прямой способ отразить в коде то, как промежутки заданы
   в самой документации (от X до Y — конкретный токен), без скрытой
   логики в CSS, которую пришлось бы держать в голове при правках. */

.content-block {
  /* "Right Content: ширина контейнера 840px, прибит к правому краю" */
  max-width: 840px;
  margin-left: auto;
}

.content-block__tag {
  color: var(--text-primary);
  margin: 0;
}

.content-block__title {
  color: var(--text-primary);
  margin: 0;
}

.content-block__subtitle {
  color: var(--text-primary);
  margin: 0;
}

.content-block__text {
  color: var(--text-primary);
  margin: 0;
}

/* Для отдельно стоящих параграфов, у которых "Максимальный размер
   контейнера текста" — 780px, а не полные 840px (Section 9, Параграф
   3) — обычно это значит "тот же предел ширины, что и у bullet-текста
   выше по разметке, просто без своего bullet". */
.content-block__text--narrow {
  max-width: 780px;
}

.content-block__bullet {
  display: flex;
  align-items: flex-start;
  /* "Отступ между bullet и параграфом текста (горизонтальный)" —
     --space-48. При ширине контейнера 840px это ровно совпадает с
     лимитом текста (12px кружок + 48px gap + 780px текст = 840px) —
     никакого "лишнего" места справа от текста не остаётся. */
  gap: var(--space-48);
}

.content-block__bullet-dot {
  width: 12px;
  height: 12px;
  flex: none;
  /* Оптическое выравнивание кружка с обычным наборным текстом
     (.body-primary-left и т.п.) — top-padding контейнера с bullet,
     --space-4. */
  margin-top: var(--space-4);
  color: var(--accent-main);
}

/* Та же строка, но второй элемент — заголовок (H3), а не обычный
   текст: у более крупного кегля своя оптическая середина, кружок
   нужно сдвинуть ниже — --space-4 + --space-6 = 10px. Единичный
   составной случай, поэтому не отдельный токен, а сумма двух
   существующих — так и в документации. */
.content-block__bullet-dot--heading {
  margin-top: calc(var(--space-4) + var(--space-6));
}

.content-block__bullet-text {
  color: var(--text-primary);
  margin: 0;
  /* "Максимальный размер контейнера текста" для bullet-параграфов — 780px */
  max-width: 780px;
}

/* Вариант .content-block__bullet, где вторым элементом в строке —
   заголовок (H3), а не параграф (Section 7 «Процесс»: bullet + "Через
   3 месяца..."). Сам флекс-ряд (.content-block__bullet) переиспользуется
   как есть — меняется только то, что внутри него лежит. */
.content-block__bullet-heading {
  color: var(--text-primary);
  margin: 0;
  max-width: 780px;
}

/* Параграф СРАЗУ ПОД bullet-рядом (не внутри него), но выровненный по
   тексту заголовка, а не по кружку — поэтому свой margin-left,
   повторяющий смещение текста в строке выше (12px кружок + 48px
   --space-48 gap = 60px). 60px + 780px = 840px — ровно ширина
   .content-block, поэтому дополнительный margin-right не нужен. */
.content-block__bullet-followup-text {
  color: var(--text-primary);
  margin: 0;
  margin-left: 60px;
  max-width: 780px;
}

/* ===================================================================
   15. COLLAGE
   Композиция из нескольких фото (+ лого/иллюстрация) на цветной
   плашке-фоне. Переиспользуется между инстансами (Section 4 — 3 фото
   + лого; позже Section 8 — фото + SVG-иллюстрация), но внутреннее
   расположение элементов у каждого инстанса своё — общая только
   "оболочка" (.collage): фон, скругление, фиксированные пропорции,
   клиппинг. Сами фото и лого позиционируются в процентах от размеров
   плашки (840×520 — её базовый размер по доке), поэтому вся
   композиция масштабируется как единое целое на любой ширине
   контейнера, без брейкпоинтов. Исключение — лого: по доке его размер
   "фиксирован независимо от адаптива" (115×32px), поэтому у него
   ширина/высота в px, а не в %, в отличие от фото.
   =================================================================== */

/* ===================================================================
   15. COLLAGE
   Композиция из нескольких медиа-элементов на цветной плашке-фоне.
   Переиспользуется между инстансами (Section 4 — 3 фото + лого;
   Section 8 — фото + SVG-иллюстрация), но внутреннее расположение
   элементов у каждого инстанса своё — общая только "оболочка"
   (.collage): фон, скругление. Высота — НЕ часть общей оболочки:
   у Section 4 она зафиксирована через aspect-ratio (нужна явная
   система координат под position: absolute у детей), а у Section 8
   высота естественно следует из контента (padding + flex), потому
   что на мобильном плашка должна расти выше при вертикальной
   раскладке — единый aspect-ratio на оба случая работать не может.
   Каждый вариант задаёт высоту сам, в своём собственном модификаторе.
   =================================================================== */

.collage {
  position: relative;
  width: 100%;
  max-width: 840px;
  border-radius: var(--radius-surface);
  /* "От верхнего Caption до изображения" — --space-8. .image-block__img
     получает этот же отступ через собственное правило, но коллаж — не
     <img>, а отдельный div, то правило на него не распространяется. */
  margin-top: var(--space-8);
}

/* --- Section 4 «История»: 3 фото + лого ---
   Раскладка — по принципу "приклеено к углам" (доки: "Изображение 1 —
   в левом верхнем углу", "Изображения 2 и 3 — ... в правом верхнем
   углу", "Лого — в левом нижнем углу"), а не сетка с фиксированным
   гэпом между фото-1 и колонкой фото-2/3 — отступ между ними в доке
   не задан явно, потому что это попросту остаток ширины плашки между
   двумя элементами, закреплёнными каждый у своего края. */

.collage--story {
  background-color: var(--bg-color-clay);
  /* Фиксированная система координат под position: absolute у фото/лого
     внутри (см. их % координаты ниже) — в отличие от .collage--lessons,
     у этого варианта раскладки нет иного способа держать "приклеенные
     к углам" элементы на месте при ресайзе. */
  aspect-ratio: 840 / 520;
  overflow: hidden;
}

/* Инсет 12px (--space-12) от краёв плашки, в процентах от её базовых
   840×520: 12/840 = 1.4286% по горизонтали, 12/520 = 2.3077% по
   вертикали — используется ниже у каждого элемента. */

.collage--story__photo-main {
  position: absolute;
  top: 2.3077%;
  left: 1.4286%;
  /* 172/840, 172/520 */
  width: 20.4762%;
  height: 33.0769%;
  object-fit: cover;
  border-radius: var(--radius-surface);
}

.collage--story__photo-side {
  position: absolute;
  right: 1.4286%;
  /* 294/840, 172/520 */
  width: 35%;
  height: 33.0769%;
  object-fit: cover;
  border-radius: var(--radius-surface);
}

.collage--story__photo-side--1 {
  top: 2.3077%;
}

/* top второго фото = верхний инсет + высота первого + гэп между ними
   (--space-12 = 12px = 2.3077% от 520) — 2.3077 + 33.0769 + 2.3077 */
.collage--story__photo-side--2 {
  top: 37.6923%;
}

.collage--story__logo {
  position: absolute;
  left: 1.4286%;
  bottom: 2.3077%;
  width: var(--logo-width);
  height: var(--logo-height);
  color: var(--logo-primary-reversed);
}

/* --- Section 8 «Занятия»: фото + SVG-иллюстрация ---
   В отличие от Section 4 ("приклеено к углам", нужен position:
   absolute с фиксированной системой координат), тут всего два элемента
   в ряд — обычный flex справляется без всякой математики с процентными
   координатами. Высота плашки НЕ зафиксирована заранее (в отличие от
   .collage--story) — она следует из контента: padding + высота детей,
   а на мобильном, где раскладка становится вертикальной, плашка
   естественным образом вырастает выше под два блока друг над другом. */

/* --- Section 8 «Занятия»: фото + SVG-иллюстрация ---
   Контейнер делится на 5 условных частей с --space-12 между ними: фото
   занимает 2/5, иллюстрация — 3/5. CSS Grid с grid-template-columns:
   2fr 3fr — это и есть буквально "5 частей, 2 одной стороне, 3 другой"
   без отдельной арифметики на пиксели. На десктопе/планшете у плашки
   фиксированный аспект (840:520, как и у .collage--story) — это даёт
   grid'у определённую высоту строки, и grid stretch (поведение по
   умолчанию) растягивает оба элемента на 100% этой высоты. На
   мобильном, где раскладка становится одноколоночной, фиксированный
   аспект плашки снимается — высота вырастает из контента (две
   картинки друг под другом, каждая со своим нативным соотношением
   сторон). */

.collage--lessons {
  background-color: var(--bg-color-olive);
  display: grid;
  grid-template-columns: 2fr 3fr;
  /* "Внутри контейнера отступы от краёв до контента --space-12" +
     "между [частями] --space-12" — один и тот же токен для обоих. */
  gap: var(--space-12);
  padding: var(--space-12);
  aspect-ratio: 840 / 520;
}

/* min-width: 0 — без этого правила grid (как и flex) не даёт элементу
   с собственным intrinsic-размером (img/svg) сжаться у́же его
   контентного минимума: по умолчанию у grid-/flex-детей min-width:
   auto, а не 0. Из-за этого на узких десктопных ширинах (примерно
   с 1199px и ниже) иллюстрация переставала сжиматься вместе с
   колонкой и вылезала за пределы экрана — сам контейнер сужался, а
   она — нет. */
.collage--lessons__photo,
.collage--lessons__illustration {
  min-width: 0;
  width: 100%;
  height: 100%;
}

.collage--lessons__photo {
  object-fit: cover;
  object-position: center;
  border-radius: var(--radius-surface);
}

/* "На момент схлапывания под друг другом на брейкпоинте 767 они
   просто встанут друг под другом и займут ширину контейнера" —
   .collage--lessons__illustration получает order: -1 (стоит первой
   визуально, при этом в DOM остаётся второй — без отдельного
   HTML-варианта под мобильный порядок). aspect-ratio: auto на самой
   плашке — без фиксированного 840:520 в одну колонку высота должна
   расти по контенту, а не подгоняться под старый широкий аспект. */
@media (max-width: 767px) {
  .collage--lessons {
    grid-template-columns: 1fr;
    aspect-ratio: auto;
  }

  .collage--lessons__illustration {
    order: -1;
  }

  .collage--lessons__photo,
  .collage--lessons__illustration {
    height: auto;
  }

  /* Без общей высоты плашки на мобильном держим пропорции каждого
     элемента отдельно, по его нативным размерам файла. */
  .collage--lessons__photo {
    aspect-ratio: 1696 / 2528;
  }

  .collage--lessons__illustration {
    aspect-ratio: 450 / 496;
  }
}

/* ===================================================================
   16. PRINCIPLE CARDS (Section 5 «Подход»)
   Две карточки в ряд (десктоп) / друг под другом (мобайл). Важное
   требование доки: карточки должны быть одинаковой высоты, а отступ
   перед иллюстрацией у БОЛЕЕ КОРОТКОЙ карточки — динамический, чтобы
   её иллюстрация "доезжала" до низа карточки вровень со второй.
   Реализовано без JS: .principle-card — flex-колонка, .content-block__cards
   — flex-ряд с align-items: stretch (обе карточки растягиваются под
   высоту самой высокой). У карточки, которая короче по контенту,
   обёртка иллюстрации получает margin-top: auto — flexbox сам
   досчитывает недостающий отступ так, чтобы карточка заполнила всю
   стретч-высоту. У второй (более длинной по контенту) карточки —
   обычный фиксированный отступ --space-20, как и задано в доке.
   =================================================================== */

.content-block__cards {
  display: flex;
  align-items: stretch;
  /* "В десктопе карточки выстраиваются горизонтально с отступом
     между ними --space-12" */
  gap: var(--space-12);
}

.principle-card {
  display: flex;
  flex-direction: column;
  /* Карточки равной ширины, могут сжиматься (иначе при узком
     контейнере вместе с border+padding они не влезут в строку). */
  flex: 1 1 0;
  min-width: 0;
  border: 1px solid var(--border-dark);
  border-radius: var(--radius-surface);
  /* "Внутренние отступы — --space-20, дальше начинается контент." */
  padding: var(--space-20);
}

.principle-card__illustration {
  /* Карточка 2 (статичный отступ по доке) — переопределяется ниже для
     более короткой карточки 1. */
  margin-top: var(--space-20);
}

/* Карточка 1 — короче по тексту, "доезжает" иллюстрацией до низа
   карточки вровень с карточкой 2 (см. комментарий к разделу выше). */
.principle-card__illustration--auto {
  margin-top: auto;
}

.principle-card__img {
  display: block;
  width: 100%;
  /* НЕ max-width: 374px (как у фото) — у иллюстраций другая логика:
     это векторная SVG-графика, она не теряет качества при масштабе
     больше нативного, поэтому ничто не должно ограничивать её рост.
     На "сжатой" одноколоночной раскладке (~768px, когда карточки уже
     стоят друг под другом, но контейнер всё ещё широкий) бывший cap в
     374px не давал иллюстрации заполнить всю ширину карточки —
     оставался пустой хвост места справа. aspect-ratio держит пропорцию
     374:239 на любой ширине — locked aspect ratio, без обрезки (тут
     нечего кадрировать, в отличие от фото: см. .image-block__img). */
  aspect-ratio: 374 / 239;
  height: auto;
  border-radius: var(--radius-surface);
  margin-top: var(--space-8);
}

/* Мобильный (< 768px): карточки друг под другом, "доезжающий" отступ
   карточки 1 больше не нужен — обе карточки равны по высоте сами по
   себе по своему контенту, а не по соседке в ряду. */
@media (max-width: 767px) {
  .content-block__cards {
    flex-direction: column;
  }

  .principle-card__illustration--auto {
    margin-top: var(--space-20);
  }
}

/* ===================================================================
   17. CTA SECTION (Section 11)
   Финальный блок страницы — Tag + Heading 2 в .center-grid--text
   (840px, тот же приём вложенности, что у Hero, см. секцию 10: GRID
   SYSTEMS), ниже — две карточки .cta-card.

   .cta-card переиспользует существующие типографские классы
   .content-block__tag/__subtitle/__text/__bullet-text (как и
   .principle-card до этого, см. секцию 16) — без них пришлось бы
   заново объявлять те же margin/color правила. Карточка 1 — на
   тёмном (--bg-color-olive) фоне, поэтому весь её типографский текст
   нужно перекрасить в --text-primary-reversed; для этого случая —
   единственный отдельный модификатор ниже, .cta-card--reversed,
   который точечно перекрашивает только переиспользуемые типографские
   классы, не трогая сами эти классы глобально (карточка 2 продолжает
   использовать их в дефолтном, не-реверснутом виде).

   Расцветка кнопок (4 шт. на карточку 1, 3 шт. на карточку 2)
   переиспользует все 4 существующих варианта .btn без единого нового
   цвета — см. разметку в index.html, расцветка каждой CTA в доке 1-в-1
   совпадает с уже готовыми .btn--accent/--primary/--secondary/
   --reversed-bg. Сами CTA-кнопки внутри карточки — плоские соседние
   элементы с .btn--block (всегда 100% ширины) и .mt-s/.mt-m между
   ними, без обёртки .btn-group: по доке они всегда вертикальный
   список на любом брейкпоинте, обёртка-flex-ряд тут не нужна. */

.cta-section__cards {
  display: flex;
  flex-direction: column;
  /* "В десктопе карточки выстраиваются вертикально с отступом между
     ними --space-12". Колонка одна и та же на любой ширине — карточки
     и так уже вертикальные на десктопе, в отличие от .content-block__cards
     (Section 5), где десктоп/мобайл отличаются направлением ряда. */
  gap: var(--space-12);
}

.cta-card {
  display: flex;
  /* align-items не задан — используется дефолтное значение flex-box,
     stretch. Это намеренно: "по высоте [фото] подстраиваются под
     размер контента из левой части" — единственный способ получить
     это нативно, без лишнего JS/ResizeObserver, это и есть stretch:
     .cta-card__photo не имеет собственной высоты (height: auto),
     поэтому stretch растягивает её до высоты .cta-card__content —
     более высокого из двух соседей в подавляющем большинстве случаев
     (текст + кнопки против одной фотографии). */
  /* "В desktop—горизонтально (слева—контент, справа—изображение;
     отступ между ними --space-20)" */
  gap: var(--space-20);
  /* "Внутренние отступы — --space-20, дальше начинается контент" */
  padding: var(--space-20);
  border-radius: var(--radius-surface);
}

.cta-card--olive {
  background-color: var(--bg-color-olive);
}

/* "Обводка у карточки: --border-dark" (Карточка 2) — без заливки
   фона, та же логика, что и у .principle-card (граница, контент
   читается на фоне обычного surface). */
.cta-card--outline {
  border: 1px solid var(--border-dark);
}

/* Точечная перекраска переиспользуемых типографских классов под тёмный
   фон Карточки 1 — см. комментарий к разделу выше. content-block__bullet-dot
   сюда не входит: цвет кружка-маркера (--accent-main) одинаков в обеих
   карточках, по доке. */
.cta-card--reversed .content-block__tag,
.cta-card--reversed .content-block__subtitle,
.cta-card--reversed .content-block__text,
.cta-card--reversed .content-block__bullet-text {
  color: var(--text-primary-reversed);
}

.cta-card__content,
.cta-card__photo {
  /* "Занимают 50/50% от контейнера" — строго резиновое деление
     строки пополам (минус половина gap на каждую сторону), без
     потолка в пикселях: на очень широких экранах обе половины
     продолжают расти вместе с карточкой. */
  flex: 1 1 0;
  min-width: 0;
}

.cta-card__photo {
  /* .cta-card__photo сейчас — пустой <div>-контейнер, а не сам <img>
     (см. index.html). Это намеренно: у <img> как replaced-элемента
     всегда есть СОБСТВЕННЫЙ intrinsic aspect-ratio (из реальных
     пикселей файла) — и per спеке flexbox, align-items: stretch не
     растягивает элементы с aspect-ratio, даже если явно сбросить CSS
     aspect-ratio в auto (это убирает только ratio, выведенный из
     HTML width/height атрибутов, но не intrinsic ratio самого
     декодированного файла). У пустого div никакого intrinsic ratio
     нет, поэтому он растягивается нормально — а уже внутри него
     <img> кадрируется через position: absolute на 100%/100% +
     object-fit: cover (ниже). */
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  border-radius: var(--radius-surface);
  overflow: hidden;
}

.cta-card__photo img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* Мобильный (< 768px): карточка — вертикальный стек (контент сверху,
   фото снизу — порядок и так совпадает с DOM), обе половины растут на
   всю ширину карточки. Высота фото на десктопе была "подстроиться под
   контент" — в колонку этот приём не переносится (там она была бы
   равна ширине, т.е. никак не ограничена), поэтому на мобильном у неё
   собственное, фиксированное значение — 258px, то же самое, что у
   всех остальных горизонтальных фото сайта на узких экранах (см.
   .image-block__img, секция 13: IMAGE BLOCK) — для визуальной
   консистентности, а не получено из адаптивного aspect-ratio самого
   .cta-card__photo: брейкпоинт здесь 768px, а не 599px, как у
   .image-block__img, и на ширинах ближе к 768px чистый aspect-ratio
   увёл бы фото намного выше 258px — то есть высота скакала бы внутри
   одного и того же мобильного диапазона. Фиксированное значение
   держит её одинаковой на всём диапазоне до 768px. */
@media (max-width: 767px) {
  .cta-card {
    flex-direction: column;
  }

  .cta-card__content,
  .cta-card__photo {
    flex-basis: auto;
  }

  .cta-card__photo {
    height: 258px;
  }
}

/* ===================================================================
   18. SPACING UTILITIES (.mt-*)
   Единственный способ задавать вертикальные интервалы между элементами
   внутри .content-block (Tag → H2/H3 → Параграф → Bullet → Image →
   Collage → ...). У каждой секции (Проблема, История, Подход...) свой
   ритм отступов — комбинаторный список sibling-селекторов под каждую
   возможную пару типов не масштабируется на 7+ разных секций. Вместо
   этого нужный отступ ставится явно, прямо в разметке, на элемент,
   СТОЯЩИЙ ПОСЛЕ нужного промежутка — это самый прямой способ отразить
   в коде то, как промежутки заданы в самой документации (от X до Y —
   конкретный токен), без скрытой логики в CSS.

   ВАЖНО: этот блок должен оставаться ПОСЛЕДНИМ в файле (либо как
   минимум — позже любого правила, которое назначает margin-top тем же
   элементам). У классов здесь та же специфичность (один класс), что и
   у `.content-block__title { margin: 0; }` и подобных базовых сбросов
   — при равной специфичности побеждает то правило, что идёт ПОЗЖЕ по
   тексту файла. Если добавить новый компонент со своим margin-top
   ниже этого блока, его обнуление по ошибке перебьёт .mt-*. */

.mt-s   { margin-top: var(--space-8); }
.mt-m   { margin-top: var(--space-12); }
.mt-l   { margin-top: var(--space-14); }
.mt-xl  { margin-top: var(--space-20); }
.mt-2xl { margin-top: var(--space-24); }
.mt-4xl { margin-top: var(--space-40); }
.mt-5xl { margin-top: var(--space-48); }
.mt-8xl { margin-top: var(--space-80); }
