/* ==========================================================================
   Лендинг puzzlebot. Все блоки макета 930-59460 в одном документе, в
   порядке макета: шапка → hero → логотипы → «Как это работает» → кейсы →
   «Почему мы лучшие» → цифры → «Лучше, чем Вайбкодинг» → «Создавайте, мы
   поддержим» → вопросы → футер.

   Здесь живёт только раскладка: сетки, отступы между блоками, поведение
   при сужении. Цвета, типографика, кнопки, карточки, чипы — из
   ../design-system/tokens.css. Если для блока понадобилось значение,
   которого нет в токенах, — это повод завести токен, а не написать число.

   ── Вьюпорты ─────────────────────────────────────────────────────────────
   Вьюпортов ровно три, и каждый блок перестраивается только на их
   границах — даже там, где его собственное содержимое просило бы ступень
   на полсотни пикселей раньше или позже:

     ≥1280  Десктоп. Макетная раскладка целиком.
   768–1279 Планшет. Сетки ломаются: бенто «Почему мы лучшие» встаёт
            в колонку, футер — в две, шапка теряет начертание логотипа
            и «Главную» (промежуточный вариант Figma 942:5983),
            «Как это работает» показывает список слева и один экран
            справа (Figma 1013:9012).
      <768  Мобильная раскладка: шапка прячет меню под кнопку, hero берёт
            мельче кегль, лента кейсов становится слайдером со снапом,
            футер — в одну колонку, «Как это работает» едет по прокрутке.

   Отдельно стоит 380 — это не вьюпорт, а три точечные правки под iPhone
   SE и mini там, где строка не сходится на несколько пикселей.

   Значения обязаны совпадать с константами в скриптах: cases.js держит
   свою копию 767 в matchMedia, потому что снап и автоход исключают
   друг друга и скрипт должен знать, где кончается один режим.
   ========================================================================== */

:root {
  /* Шаг между блоками — 160. Ступени 160 в Spacing/* нет, поэтому она
     собрана из шкалы, а не вписана числом: поедет шкала — поедет и шаг.
     Половина живёт отдельной переменной, потому что зазор делят между
     собой два соседних блока, и каждому достаётся по 80.

     Целая нужна там, где сосед — полоса со своим фоном: лента логотипов
     и футер кончаются краем заливки, добавлять к нему нечего, и весь
     зазор носит блок рядом. ⚠️ завести Spacing/XXXL, когда появится */
  --section-gap: calc(var(--spacing-xxl) * 2 + var(--spacing-xl));
  --section-gap-half: calc(var(--spacing-xxl) + var(--spacing-m));

  /* Полоса логотипов, Figma 930:59564: лента 54, по 16 сверху и снизу,
     линия 2. Живёт здесь, а не в .logos: по ней hero считает высоту
     первого экрана, а из соседней секции переменная не видна */
  --logos-height: 54px;
  --logos-band: calc(var(--logos-height) + var(--spacing-m) * 2 + var(--size-stroke-m));

  /* Высота окна в пикселях — её замеряет инлайн-скрипт в <head> index.html
     один раз и не пересчитывает, пока не сменилась ширина. Здесь запасное
     значение на случай, когда скрипт не отработал.

     Своей единицы для этого нет: в Телеграме нижняя панель ресайзит сам
     webview, и для страницы это обычный resize — svh, dvh и vh едут все
     три, блоки прыгают, контент под пальцем уезжает */
  --vh: 100svh;
}

/* Ступени зазора: 160 на десктопе, 128 на планшете, 96 на телефоне.
   Половины ровно вдвое меньше — блоки вносят по одной каждый */
@media (max-width: 1279px) {
  :root {
    --section-gap: calc(var(--spacing-xxl) * 2);
    --section-gap-half: var(--spacing-xxl);
  }
}

@media (max-width: 767px) {
  :root {
    --section-gap: calc(var(--spacing-xxl) + var(--spacing-xl));
    --section-gap-half: calc(var(--spacing-xl) + var(--spacing-m));
  }
}

body { min-height: 100vh; }

/* ── Шапка ────────────────────────────────────────────────────────────────
   Figma 930:59461 (десктоп) и 714:12024 (все четыре варианта).
   Десктоп: логотип слева, меню по центру, язык и вход справа, шаг 4.
   Узкий экран: кнопка меню слева, знак по центру, язык и вход справа, шаг 16.

   Обе раскладки — одна строка из четырёх элементов. Меняется только то,
   что показано: боковые группы всегда тянутся поровну, поэтому и меню,
   и знак попадают в центр страницы без вычислений.

   ⚠️ Шапка — общий компонент сайта, и собирается он из трёх частей:
     1. разметка <header class="site-header"> на странице;
     2. стили здесь, вместе с растворяющей подложкой (body::before ниже);
     3. поведение в landing/header.js — на узком экране шапка уезжает при
        прокрутке вниз и возвращается при прокрутке вверх.
   Новая страница берёт все три. Ничего из этого не дублируется в стилях
   раздела: правится в одном месте — меняется везде */
.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  gap: var(--spacing-xxs);
  height: var(--size-header);
  padding-inline: var(--page-padding-desktop);
  /* Уезжает вверх на узких экранах, header.js. Самая долгая ступень шкалы:
     шапка идёт поперёк прокрутки, и на 120, 200 и 320 ms её ход не читался
     как движение — она просто оказывалась на месте. will-change держит слой
     готовым — иначе первый кадр уходит на его создание, и начало хода
     теряется */
  transition: transform var(--duration-slower) var(--ease-brand);
  will-change: transform;
  /* Своя заливка, хотя под шапкой уже лежит подложка (body::before). Подложка
     кончается растворением, и на нижнюю треть шапки приходится уже
     полупрозрачный хвост — сквозь него читался текст под шапкой. Заливка
     едет вместе с шапкой и закрывает её целиком, где бы та ни оказалась */
  background: var(--color-bg-primary);
}

/* Подложка под шапкой. Figma 1024:18561 — прямоугольник 1920×70. Из макета
   взята только заливка градиентом от фона страницы к прозрачному; размытие
   (BACKGROUND_BLUR PROGRESSIVE 16 → 0) снято по просьбе Александра.

   Висит на body, а не на шапке: шапка уезжает вверх по прокрутке (header.js),
   и подложка уезжала бы вместе с ней. Растворение края нужно всегда.

   Высота на 16 больше шапки: в макете прямоугольник 70 против её 54. Хвост
   нужен, чтобы градиент кончился за границей шапки, а не на ней.

   z-index 8 — под шапкой (10) и подсказкой doc.css (20), но над всем
   остальным, включая футер: на узком экране футер занимает весь экран, и
   контент над ним должен растворяться так же, как над любой секцией.

   Второй стоп — «transparent 100%», а не голый transparent: без процента
   color-mix мешает поровну и даёт полупрозрачный белый, который обрывается
   краем слоя видимой полосой */
body::before {
  content: "";
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 8;
  pointer-events: none;
  height: calc(var(--size-header) + var(--spacing-m));
  background: linear-gradient(to bottom,
    var(--color-bg-primary),
    color-mix(in srgb, var(--color-bg-primary), transparent 100%));
}

/* Прокрутка вниз убирает шапку, вверх — возвращает (header.js, только <1280) */
.site-header--hidden { transform: translateY(-100%); }

/* Боковые группы — обёртки без собственных паддингов. Это важно: при
   flex-basis 0 паддинг самого flex-элемента не участвует в дележе свободного
   места и прибавляется сверху, из-за чего стороны разъезжаются и центр
   уходит. Паддинг живёт на вложенной ссылке */
.site-header__side { flex: 1; display: flex; align-items: center; min-width: 0; }
.site-header__side--end { justify-content: flex-end; gap: var(--spacing-xxs); }

/* Логотип в макете лежит внутри области 38px с паддингом 8 — повторяем,
   чтобы он попадал в один ряд с кнопками и имел область клика */
.site-header__logo {
  display: flex;
  align-items: center;
  height: var(--size-button-m);
  padding-inline: var(--spacing-xs);
  border-radius: var(--radius-l);
  color: var(--color-logo);
}
/* Знак по центру — квадрат 38 без горизонтального паддинга: он центрируется
   сам, а лишние 16 сдвинули бы его относительно середины строки */
.site-header__logo--mark { display: none; width: var(--size-button-m); padding-inline: 0; justify-content: center; }

.site-header__nav { display: flex; gap: var(--spacing-xxs); }

/* Кнопка меню показывается только в узкой раскладке */
.site-header__menu { display: none; position: relative; }

/* Маркер у <summary> убирается двумя способами сразу: WebKit понимает
   только псевдоэлемент, остальные — list-style */
.site-header__summary { list-style: none; }
.site-header__summary::-webkit-details-marker { display: none; }

/* Гость и вошедший переключаются атрибутом hidden, и его приходится
   поддержать явно: hidden из браузерных стилей даёт display: none, но
   любой авторский display его перебивает — а он здесь у всех троих.
   .btn кладёт кнопкам inline-flex, кружку инициалов ниже нужен grid.
   Без этого правила в шапке разом видны «Войти» и «Проекты», а на месте
   аватара светится пустой серый круг. Проверено на живом сервере
   11.09.2026; в проекте hidden так поддержан и в других местах */
[data-auth-guest][hidden],
[data-auth-client][hidden],
.site-header__avatar[hidden] { display: none; }

/* Аватар вошедшего. 22 — ступень Button/Icon кита, та же, что у иконок
   в кнопках шапки: круг встаёт ровно на место иконки. Отступ от текста,
   высоту и скругление даёт сама .btn, поэтому здесь только сам кружок.

   Инициалы лежат в шапке всегда и гаснут, когда загрузилась картинка, —
   так на месте аватара никогда не бывает пустого круга */
.site-header__avatar {
  width: var(--size-button-icon-m);
  height: var(--size-button-icon-m);
  border-radius: var(--radius-full);
  object-fit: cover;
  flex-shrink: 0;
}

.site-header__avatar--letters {
  display: grid;
  place-items: center;
  background: var(--color-neutral-11);
  color: var(--color-text-secondary);
  font-size: var(--text-p3);
  line-height: 1;
}

/* Поля у кнопки вошедшего свои, не как у обычной .btn: в макете
   714:12023 это 16 слева и 8 справа при зазоре 12 — кружок стоит ближе
   к краю, чем текст. С симметричными 24 от .btn кнопка выходила 142
   против макетных 123. На мобильном её перебивает padding-inline: 0
   ниже: там от кнопки остаётся квадрат под один кружок */
.site-header__client {
  gap: var(--spacing-s);
  padding-left: var(--spacing-m);
  padding-right: var(--spacing-xs);
}

/* Открытое меню, Figma 1089:20964. Панели как таковой нет: под шапкой
   стоят четыре крупные кнопки, страница между ними просвечивает. Держится
   на том, что каждая кнопка сама по себе непрозрачна.

   Раскрытие, Esc и фокус по-прежнему на <details>; скрипт (header.js)
   добавляет только закрытие по клику мимо и по прокрутке */
.site-header__drawer {
  position: absolute;
  inset-block-start: 100%;
  inset-inline-start: 0;
  /* 16 от низа кнопки: в макете первый пункт стоит на 62 от верха страницы,
     а низ кнопки меню — на 46 */
  margin-block-start: var(--spacing-m);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--spacing-m);
}

/* Свечение из макета (1089:20962): круг 406, центр — левый верхний угол
   панели. В макете он голубой (Info/11), но по решению Александра 18.08
   красится белым: голубое пятно на светлом фоне лендинга читалось как
   посторонний блик. В макете это blur 50 по кругу; здесь радиальный
   градиент, он дешевле фильтра и даёт тот же мягкий край.

   Висит на <details>, а не на самой панели: у панели из-за opacity свой
   контекст наложения, и z-index: -1 внутри него не уводит свечение под
   кнопку меню — крестик оказывался забелён. Отсюда же и своя прозрачность
   с переходом: в анимацию ::details-content этот слой не попадает */
.site-header__menu::after {
  --drawer-glow: 406px;
  content: "";
  position: absolute;
  z-index: -1;
  /* центр круга — в левом верхнем углу панели: та же точка, что в макете */
  inset-block-start: calc(100% + var(--spacing-m) - var(--drawer-glow) / 2);
  inset-inline-start: calc(var(--drawer-glow) / -2);
  width: var(--drawer-glow);
  height: var(--drawer-glow);
  opacity: 0;
  pointer-events: none;
  background: radial-gradient(closest-side, var(--color-surface), transparent);
  transition: opacity var(--duration-slow) var(--ease-brand);
}
.site-header__menu[open]::after { opacity: 0.7; }

/* Пункт меню. Кнопка нестандартного размера: в макете подпись H3 и паддинги
   16/32, такой ступени в ките нет — поэтому высота считается содержимым,
   а не берётся у --size-button-*.

   Заливка двухслойная, как в макете: 15% серого поверх белого на 70%.
   Одного серого мало — панель прозрачная, и сквозь пункт читался бы контент.
   Вариант кнопки в разметке не ставим: hover/active у .btn--* перебили бы
   этот фон специфичностью */
.site-header__drawer .btn {
  justify-content: flex-start;
  height: auto;
  padding: var(--spacing-m) var(--spacing-xl);
  white-space: nowrap;
  font-family: var(--font-heading);
  font-size: var(--text-h3);
  line-height: var(--text-h3--line-height);
  font-weight: var(--font-weight-heading);
  color: var(--color-text-primary);
  background:
    linear-gradient(var(--color-button-secondary), var(--color-button-secondary)),
    color-mix(in srgb, var(--color-surface), transparent 30%);
  backdrop-filter: blur(var(--blur-s));
  -webkit-backdrop-filter: blur(var(--blur-s));
}
.site-header__drawer .btn:active {
  background:
    linear-gradient(var(--color-button-secondary-pressed), var(--color-button-secondary-pressed)),
    color-mix(in srgb, var(--color-surface), transparent 30%);
}

/* Появление и скрытие. ::details-content — единственный способ анимировать
   <details> без скрипта: он же и переключает content-visibility, поэтому
   она едет в переходе с allow-discrete. Браузер без поддержки псевдоэлемента
   просто покажет меню сразу — состояние остаётся рабочим */
.site-header__menu::details-content {
  opacity: 0;
  translate: 0 calc(var(--spacing-xs) * -1);
  transition:
    opacity var(--duration-slow) var(--ease-brand),
    translate var(--duration-slow) var(--ease-brand),
    content-visibility var(--duration-slow) allow-discrete;
}
.site-header__menu[open]::details-content { opacity: 1; translate: 0 0; }

/* Открытое меню закрывается той же кнопкой — иконка меняется на крестик.
   Подмена маски, а не вторая нода в разметке: кнопка одна */
.site-header__menu[open] .icon--menu {
  -webkit-mask-image: var(--icon-close);
  mask-image: var(--icon-close);
}

/* Промежуточный вариант, Figma 942:5983. Меню остаётся на месте, но ужимается
   тремя способами сразу: логотип теряет начертание, «Главная» уходит,
   у языка схлопываются паддинги. Шаг остаётся 4 — раскладка ещё десктопная.

   Содержимому этого варианта хватило бы и 820 (логотип 123 + меню 445 +
   EN 69 + «Войти» 93 + зазоры + паддинги = 782), но собственной ступени
   у шапки нет: вьюпортов на лендинге ровно три, и шапка перестраивается
   вместе с сетками остальных блоков. Запас в 459px ничего не ломает —
   вариант и рассчитан на то, чтобы стоять до самого мобильного */
@media (max-width: 1279px) {
  /* Знак вместо полного логотипа делаем подменой маски, а не вторым
     элементом в разметке: ссылка на главную должна быть одна */
  .site-header__logo--full { padding-inline: 0; }
  .site-header__logo--full .logo {
    width: var(--size-button-icon-m);
    -webkit-mask-image: var(--logo-mark-src);
    mask-image: var(--logo-mark-src);
  }
  .site-header__nav-home { display: none; }
  /* Язык сжимается до квадрата 38: подпись «EN» короткая, а паддинги 24
     с каждой стороны на этой ширине — расточительство */
  .site-header__lang { width: var(--size-button-m); padding-inline: 0; flex: none; }
}

/* Мобильный вариант, Figma 714:12024 (Device=Mobile). Меню уезжает под
   кнопку, знак встаёт по центру, шаг между группами становится 16.
   Промежуточный вариант держится до 767: его содержимому нужен 551,
   так что запас есть */
@media (max-width: 767px) {
  .site-header { gap: var(--spacing-m); }
  .site-header__side--end { gap: var(--spacing-xs); }

  .site-header__nav,
  .site-header__logo--full { display: none; }
  .site-header__logo--mark { display: flex; }
  .site-header__menu { display: block; }

  /* У вошедшего остаётся один кружок: в макете 942:5851 Header Avatar
     занимает 38×38 против 123×38 на десктопе, подписи «Проекты» нет.
     Сам аватар не меняется — 22 и там, и там, — сжимается кнопка вокруг
     него, тем же приёмом, что у языка в промежуточном варианте выше.

     ⚠️ В ките мобильные варианты названы наоборот: кружок лежит
     в «Login=False», кнопка «Войти» — в «Login=True». Правда за
     содержимым, а не за именем */
  .site-header__client-text { display: none; }
  .site-header__client { width: var(--size-button-m); padding-inline: 0; flex: none; }
}

/* ── Hero ─────────────────────────────────────────────────────────────────
   Figma 930:59463. Ширина карточки ввода 800, чипов — 684, текст по центру.

   Акцент вынесен в две переменные: --hero-accent красит кнопку и хвост,
   --hero-accent-to — второй стоп градиента обводки. Выбор шаблона
   переставляет обе, поэтому перекраска блока — это две строки в JS,
   а не обход элементов */
.hero {
  --hero-accent: var(--color-button-primary);
  --hero-accent-from: var(--color-brand);
  --hero-accent-to: #42c8d1;

  /* Толщина обводки карточки ввода. Дублирует 4px из .input-card, потому что
     ступени 4 в Stroke/* нет — по ней считаются смещения хвоста и стикера:
     absolute-потомки отсчитываются от padding-box, а координаты в макете
     сняты от внешнего края. ⚠️ завести токен Stroke/L, когда появится */
  --hero-card-stroke: 4px;
  /* Линия сетки, Figma 951:5731. ⚠️ переменной под этот цвет в ките нет */
  --hero-grid-line: #eef5fc;
  /* Шаг сетки: макетные 100 мельче в полтора раза */
  --hero-grid-step: calc(100px / 1.5);
  /* Насколько коробка декораций больше карточки. От неё же считается вылет
     свечения за правый край поля, поэтому значение вынесено, а не вписано */
  --hero-aura-x: 560px;
  --hero-aura-y: 320px;

  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* Описание → карточка ввода 64 */
  gap: var(--spacing-xxl);
  /* От шапки до заголовка 128, до логотипов столько же */
  padding-block: calc(var(--spacing-xxl) * 2);
  /* Первый экран вместе с лентой логотипов занимает 80% высоты окна: на
     большом мониторе контент кончался в двух третях кадра, и под ним
     оставалась пустая полоса. min-height, а не height — на невысоком
     экране контент выше расчёта, и секция остаётся по контенту.

     Высота окна — из --vh: замер один раз (index.html), иначе панель
     Телеграма двигала бы первый экран прямо под пальцем */
  min-height: calc(0.8 * var(--vh) - var(--size-header) - var(--logos-band));
  justify-content: center;
  padding-inline: var(--page-padding-desktop);
  isolation: isolate;
  /* Коробка декораций шире секции — обрезаем её здесь, чтобы страница не
     ползла вбок. Именно clip, а не hidden: по вертикали слой остаётся
     видимым, иначе Пазло срезало бы верхним краем секции */
  overflow-x: clip;
}

/* Декорации фона. Коробка больше карточки во все стороны — сетке и свечению
   нужен запас, — и сама себя обрезает, чтобы сетка не лезла в соседние блоки
   и не растягивала страницу вбок. z-index -1 уводит слой под весь контент
   hero: у .hero есть isolation, ниже секции он не провалится */
.hero__aura {
  position: absolute;
  inset: calc(var(--hero-aura-y) * -1) calc(var(--hero-aura-x) * -1);
  z-index: -1;
  overflow: clip;
  pointer-events: none;
}
/* Сетка: линии Stroke/M через 50 — вдвое мельче шага 100 из макета.
   Двумя repeating-градиентами вместо файла: тайл в 50px из экспортного
   полотна 1900×700 всё равно не нарезать, а затухание к краям в макете
   сделано фильтром feMorphology + blur 125 (на 1900×700 это ~2.9 с на кадр),
   и его в любом случае пришлось бы менять на маску — её считает композитор */
.hero__grid {
  position: absolute;
  inset: 0;
  background:
    repeating-linear-gradient(to right,  var(--hero-grid-line) 0 var(--size-stroke-m), transparent 0 var(--hero-grid-step)),
    repeating-linear-gradient(to bottom, var(--hero-grid-line) 0 var(--size-stroke-m), transparent 0 var(--hero-grid-step));
  /* Центр затухания — середина карточки ввода: она ровно в центре коробки */
  -webkit-mask: radial-gradient(ellipse 52% 52% at 50% 50%, #000 25%, transparent 78%);
  mask: radial-gradient(ellipse 52% 52% at 50% 50%, #000 25%, transparent 78%);
}
/* Слой под параллакс поднимаем только там, где параллакс вообще бывает:
   скрипт включает его при (hover: hover), то есть при мыши. На телефоне
   will-change держал слой с маской впустую, и Safari сдвигал его на доли
   пикселя — карточка на вид дёргалась вверх-вниз, хотя стояла на месте */
@media (hover: hover) {
  .hero__grid { will-change: transform; }
}
/* Свечение: круг 386 с размытием и прозрачностью 10% (Figma 948:5580, 930:59495).
   Стоит справа от поля, по его вертикальной середине. Не двигается — только
   медленно перетекает между синим и голубым фирменного градиента */
.hero__glow {
  position: absolute;
  inset-block-start: 50%;
  /* Центр круга — на 115 левее правого края карточки (макет: 685 из 800).
     Считаем от правого края коробки, чтобы свечение держалось за полем
     на любой ширине, а не съезжало вместе с процентами */
  inset-inline-end: calc(var(--hero-aura-x) + 115px);
  /* Круг 386 с размытием в 120 — это пятно, которое гаснет к краю на
     трёх сигмах, то есть 193 + 360 от центра. Столько и берём шириной,
     а мягкость рисует радиальный градиент: стопы повторяют профиль
     гауссова размытия диска (в центре почти полная плотность, на радиусе
     самого круга половина, дальше плавный сход в ноль).
     Так вместо filter: blur(120px) — обычная заливка. Размытие такого
     радиуса Safari на iPhone пересчитывал при каждой перерисовке hero:
     около десяти мегапикселей блюра на retina, и пока это считалось,
     главный поток стоял — вставала машинопись, лагал Пазло.
     Центр остаётся на месте: translate по-прежнему половина ширины */
  width: 1106px;
  aspect-ratio: 1;
  translate: 50% -50%;
  background: radial-gradient(circle,
    color-mix(in srgb, var(--hero-accent-from) 95%, transparent) 0%,
    color-mix(in srgb, var(--hero-accent-from) 50%, transparent) 35%,
    color-mix(in srgb, var(--hero-accent-from) 13%, transparent) 55%,
    transparent 88%);
  opacity: 0.1;
}

/* Заголовок → описание 24 */
.hero__text { display: flex; flex-direction: column; gap: var(--spacing-l); text-align: center; }
.hero__title { margin: 0; }
.hero__lead { margin: 0; }

/* Карточка ввода → чипы 32 */
.hero__form { display: flex; flex-direction: column; gap: var(--spacing-xl); align-items: center; width: 100%; }

/* Карточка ввода. Ширина 800 из макета, обводка — градиент от бренда
   к акценту выбранного шаблона */
.hero__card {
  position: relative;
  width: min(800px, 100%);
  background:
    linear-gradient(var(--color-surface), var(--color-surface)) padding-box,
    linear-gradient(to top right, var(--hero-accent-from), var(--hero-accent-to)) border-box;
  transition: background var(--duration-base) var(--ease-brand);
}

/* Поле и слой машинописи стоят друг на друге: набор у них общий, поэтому
   вынесен на обёртку и наследуется обоими — разъехаться нечему */
.hero__field {
  position: relative;
  /* Шлейф за кареткой. Цвет ведёт выбранный шаблон, длина, плотность и
     скорость подобраны на глаз — в макете этого следа нет, ступеней тоже.
     Держим на самом поле, а не на секции: то же поле стоит в блоке
     «Готов начать?», где секции .hero нет */
  --hero-caret-trail: color-mix(in srgb, var(--hero-accent) 32%, transparent);
  --hero-caret-trail-length: 56px;
  --hero-caret-trail-fade: 420ms;
  /* Сколько летит одна буква при распаде примера */
  --hero-dust-duration: 700ms;
  /* Перекрёстная подмена строки на буквы. Короче — снова виден рывок,
     длиннее — заметно, что тот же текст мгновение нарисован дважды.
     На 90 рывок в Safari ещё читался, поэтому 160 */
  --hero-dust-fade: 160ms;
  /* H3, Figma 930:59507. Начертание на ступень тоньше макетного thin (500):
     в поле это ввод пользователя, а не заголовок — Regular читается легче */
  font-family: var(--font-heading);
  font-size: var(--text-h3);
  line-height: var(--text-h3--line-height);
  letter-spacing: var(--text-h3--letter-spacing);
  font-weight: var(--font-weight-body);
  /* Поле растёт под запрос от двух строк до одиннадцати, дальше прокрутка.
     Уходящий за край текст растворяется — полоса 32px по макету 979:8083.
     Стороны включает скрипт: пока прокручивать нечего, обе нулевые, иначе
     первая и последняя строка были бы бледными в любом состоянии */
  --hero-field-lines: 11;
  --hero-fade: 32px;
  --hero-fade-top: 0px;
  --hero-fade-bottom: 0px;
}
/* Маска висит только пока есть что прокручивать: она обрезает всё, что
   выходит за поле, а буквам щелчка Таноса нужно вылетать за верхний край */
.hero__field[data-scroll="true"] {
  -webkit-mask: linear-gradient(to bottom,
    transparent 0, #000 var(--hero-fade-top),
    #000 calc(100% - var(--hero-fade-bottom)), transparent 100%);
  mask: linear-gradient(to bottom,
    transparent 0, #000 var(--hero-fade-top),
    #000 calc(100% - var(--hero-fade-bottom)), transparent 100%);
}

.hero__prompt {
  display: block;
  width: 100%;
  /* Высоту по содержимому ставит скрипт; здесь границы, между которыми
     ему разрешено её двигать. Обе включают padding — box-sizing border-box */
  min-height: calc(var(--text-h3--line-height) * 2 + var(--spacing-xs) * 2);
  max-height: calc(var(--text-h3--line-height) * var(--hero-field-lines) + var(--spacing-xs) * 2);
  overflow-y: auto;
  /* Полосу прокрутки прячем: о том, что текст продолжается, говорит
     растворение у края, а сама полоса легла бы поверх двойника */
  scrollbar-width: none;
  padding: var(--spacing-xs);
  border: 0;
  outline: 0;
  resize: none;
  background: transparent;
  /* Текст и каретку рисует двойник: системную каретку не сделать толще
     и не подкрасить под выбранный шаблон, а разнотолщинных линий в одном
     поле быть не должно. Сам текст прячем — он остался бы вторым слоем */
  color: transparent;
  caret-color: transparent;
  font: inherit;
  letter-spacing: inherit;
}
.hero__prompt::-webkit-scrollbar { width: 0; }
/* Пока есть выделение, показываем настоящее поле, а двойник убираем совсем:
   подсветку выделения рисует только поле, и если оставить двойник, тот же
   текст окажется нарисован дважды — это читается как поехавший шрифт */
.hero__field:has([data-selecting="true"]) .hero__prompt { color: var(--color-text-primary); }
[data-selecting="true"] { visibility: hidden; }
/* Пустое поле подсказывает, что писать. Цвет — Text/Tertiary из кита
   (Kit 2.0 4359:15759) */
.hero__prompt::placeholder { color: var(--color-text-tertiary); opacity: 1; }
/* Пока идёт машинопись, placeholder не показываем вовсе: он мигал бы в паузе
   между рассыпавшимся примером и следующим. В живом режиме прячется сам —
   поле там непустое ровно тогда, когда непустой двойник */
.hero__field:has([data-mode="demo"]) .hero__prompt::placeholder { color: transparent; }

/* Двойник поля. Лежит поверх, событий не ловит. Блок с переносами, а не flex:
   текст должен ломаться по строкам ровно так же, как в поле, а куски вокруг
   каретки — оставаться частями одного потока */
.hero__ghost {
  position: absolute;
  inset: 0;
  padding: var(--spacing-xs);
  color: var(--color-text-primary);
  white-space: pre-wrap;
  overflow-wrap: break-word;
  pointer-events: none;
}
/* Длинный запрос прокручивается внутри поля, и двойник едет следом: скрипт
   переносит ему scrollTop, а это работает только при своей прокрутке.
   Включаем её лишь в живом вводе — в машинописи прокручивать нечего,
   зато буквам щелчка нужно вылетать за края */
.hero__ghost[data-mode="live"] { overflow: hidden; }
/* Каретка: вертикальная линия во всю строку и в толщину Stroke/M, а не
   символ «|» и не системный волосок. Цвет ведёт выбранный шаблон.
   Мигает как системная — полсекунды видно, полсекунды нет */
.hero__caret {
  display: none;
  /* Абсолютная — и всё же в точке вставки: координаты у неё те, где она
     стояла бы в потоке. В потоке ей нельзя: инлайн-элемент между половинами
     текста даёт браузеру право перенести строку прямо на себе, и длинное
     слово рвалось в месте курсора — замер показал строку короче на 24px */
  position: absolute;
  /* Коробка нулевая по обеим осям: линию рисует псевдоэлемент, а сам элемент
     в раскладке не участвует. Замер показал, зачем это нужно: коробка в 26px
     (шрифтовой бокс Geist 20/24) распирает line box и двигает текст на 2px,
     в 24px линия не достаёт до края букв, а ширина в 2px раздвигает строку —
     стоит увести каретку в середину, и хвост уезжает ровно на её толщину */
  width: 0;
  height: 0;
  animation: hero-caret 1s steps(1) infinite;
}
/* Линия перекрывает шрифтовой бокс целиком: у Geist 20/24 он на 1px выше
   и на 1px ниже строки. Отсчёт от нулевой коробки, стоящей в точке вставки
   на верхнем крае строки; по горизонтали линия садится на неё серединой */
.hero__caret::after {
  content: '';
  position: absolute;
  inset-inline-start: calc(var(--size-stroke-m) / -2);
  width: var(--size-stroke-m);
  inset-block-start: -1px;
  height: calc(var(--text-h3--line-height) + 2px);
  background: var(--hero-accent);
}
/* В машинописи линия видна всегда, в живом вводе — только пока поле в фокусе */
[data-mode="demo"] .hero__caret,
.hero__prompt:focus + .hero__ghost .hero__caret { display: inline-block; }

/* Щелчок Таноса. Строка распадается по буквам: каждая улетает по своей дуге,
   размываясь и разрастаясь, — координаты, направление и задержку кладёт
   скрипт. Буквы лежат абсолютно на снятых заранее позициях, а не потоком:
   у отдельных inline-block'ов пропадает кернинг, и строка перед распадом
   заметно раздаётся вширь. Исходная строка на это время просто скрыта —
   она держит раскладку. Линия тоже уходит: она наша разметка, а не текст,
   и мигать над рассыпающимся примером ей незачем */
.hero__dust {
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* Проявляется навстречу гаснущей строке. Координаты букв совпадают со
     снятыми до копейки, но Safari рисует набор отдельных span'ов не совсем
     так, как цельную строку: те же позиции, другая укладка глифов. На
     мгновенной подмене это читалось как рывок текста перед распадом.
     Координатами не лечится — лечится тем, что подмены не видно */
  animation: hero-dust-in var(--hero-dust-fade) linear;
}
@keyframes hero-dust-in { from { opacity: 0; } to { opacity: 1; } }
.hero__dust span {
  position: absolute;
  white-space: pre;
  animation: hero-dust var(--hero-dust-duration) var(--ease-brand) forwards;
}
@keyframes hero-dust {
  to { opacity: 0; filter: blur(4px); translate: var(--dx) var(--dy); scale: 1.6; }
}
/* Гаснет, а не пропадает: это вторая половина той же перекрёстной подмены.
   Прозрачность вместо видимости — раскладку строка держит и так, а её
   по-прежнему забирает клик по полю посреди распада */
[data-dusting="true"] [data-ghost-before] {
  opacity: 0;
  transition: opacity var(--hero-dust-fade) linear;
}
/* Обратно — сразу: после распада строка пустая, и в неё тут же печатается
   следующий пример. С переходом первые его буквы проявлялись бы вместе с ней */
[data-ghost-before] { transition: none; }
[data-dusting="true"] .hero__caret { display: none; }
@keyframes hero-caret { 0%, 50% { opacity: 1; } 50.01%, 100% { opacity: 0; } }

/* Шлейф. Тянется за линией с той стороны, откуда она пришла: печатает —
   слева, стирает — справа. Появляется только в движении, на паузе гаснет */
.hero__caret::before {
  content: '';
  position: absolute;
  /* Не во всю строку и размытый: полновысотный чёткий прямоугольник читается
     как выделение текста, а не как след за линией. Отступ по 4px сверху
     и снизу от строки, в которой стоит линия */
  inset-block-start: var(--spacing-xxs);
  height: calc(var(--text-h3--line-height) - var(--spacing-xxs) * 2);
  width: var(--hero-caret-trail-length);
  border-radius: var(--radius-full);
  filter: blur(var(--blur-s));
  opacity: 0;
  transition: opacity var(--hero-caret-trail-fade) var(--ease-brand);
}
/* Сторона и направление градиента держатся отдельно от видимости и остаются
   после остановки. Иначе на паузе вместе с прозрачностью пропадала бы и сама
   заливка — гаснуть было бы нечему, и шлейф исчезал рывком */
[data-dir="type"] .hero__caret::before {
  inset-inline-end: 100%;
  background: linear-gradient(to left, var(--hero-caret-trail), transparent);
}
[data-dir="erase"] .hero__caret::before {
  inset-inline-start: 100%;
  background: linear-gradient(to right, var(--hero-caret-trail), transparent);
}
[data-moving="true"] .hero__caret::before { opacity: 1; }
/* В движении линия не мигает: иначе вместе с ней моргал бы и шлейф.
   Заодно это привычнее — системная каретка при наборе тоже замирает */
[data-moving="true"] .hero__caret { animation: none; }

.hero__row { display: flex; align-items: center; justify-content: space-between; gap: var(--spacing-m); flex-wrap: wrap; }

/* Мессенджеры: три площадки по 30 в ряд (Figma 930:59509). Ни обводки,
   ни подложки — только паддинги 16/4 и шаг 8 */
.hero__messengers {
  display: flex;
  align-items: center;
  gap: var(--spacing-xs);
  margin: 0;
  padding: var(--spacing-xxs) var(--spacing-m);
  border: 0;
}
.hero__messenger {
  display: flex;
  padding: 0;
  border: 0;
  background: none;
  border-radius: var(--radius-full);
  cursor: pointer;
}
.hero__messenger-icon { width: var(--size-button-icon-l); height: var(--size-button-icon-l); }
/* Выключенная площадка: подложка становится Basic/Disabled, глиф остаётся
   белым (Figma 948:5594). Подменяем одну переменную заливки на всех трёх
   иконках, а не гасим фильтром: grayscale оставил бы Telegram голубоватым,
   MAX — серым градиентом, а веб не тронул бы вовсе, он и так серый */
.hero__messenger[aria-pressed="false"] .hero__messenger-icon {
  --messenger-fill: var(--color-disabled);
}

.hero__actions { display: flex; align-items: center; gap: var(--spacing-xs); }

/* Кнопка отправки. Цвет ведёт выбранный шаблон; подпись меняется вместе
   с ним, а ширину под неё доводит скрипт — из auto её не проанимировать.
   overflow нужен на время этой проводки: новая подпись длиннее старой
   вылезала бы за края, пока кнопка ещё не доросла */
.hero__submit {
  overflow: hidden;
  white-space: nowrap;
  background: var(--hero-accent);
  transition: background-color var(--duration-base) var(--ease-brand);
}
@media (hover: hover) {
  .hero__submit:hover:not(:disabled) { background: color-mix(in srgb, var(--hero-accent) 88%, #000); }
}
.hero__submit:active:not(:disabled) { background: color-mix(in srgb, var(--hero-accent) 76%, #000); }
.hero__submit-icon { width: var(--size-button-icon-m); height: var(--size-button-icon-m); fill: currentColor; }

/* Хвост реплики: висит на нижнем левом углу, который у карточки не скруглён.
   В макете (930:59521) он стоит на -14 от внешнего края и упирается низом
   ровно в него. Смещения absolute-потомка отсчитываются от padding-box,
   поэтому к обеим координатам добавлена толщина обводки — без этого хвост
   уезжает на 4 вправо и на 4 вверх */
.hero__tail {
  position: absolute;
  inset-block-end: calc(var(--hero-card-stroke) * -1);
  inset-inline-start: calc(-14px - var(--hero-card-stroke));
  width: 18px;
  height: 23px;
}

/* Пазло сидит на верхнем крае карточки справа: 123×123, низ на 34 ниже края,
   до правого края карточки 87 (Figma 948:5647 даёт 30 — опущен на 4 по правке
   от 14.08, робот вставал слишком высоко). Держим за правый край, а не
   за левый: на узкой карточке отсчёт слева выкинул бы его наружу.
   Обводка компенсируется — координаты сняты от внешнего края */
.hero__sticker {
  position: absolute;
  inset-block-start: calc(-89px - var(--hero-card-stroke));
  inset-inline-end: calc(87px - var(--hero-card-stroke));
  width: 123px;
  height: 123px;
  pointer-events: none;
}

.hero__chips {
  /* Карточка ввода позиционирована, поэтому её тень рисуется поверх всего
     непозиционированного — и ложилась на чипы, гася заливку (#d6eff5 → #cbe2eb).
     Позиционируем и чипы: порядок отрисовки становится порядком в разметке,
     а чипы идут после карточки */
  position: relative;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--spacing-m);
  width: min(684px, 100%);
}
.chip__icon { fill: currentColor; }

/* Обводка есть всегда, просто прозрачная: так при смене выбранного варианта
   меняется только цвет — есть чему плавно гаснуть и загораться, и ширина
   кнопки не пересчитывается. Паддинг ужат на её толщину, поэтому текст
   стоит на 16 от внешнего края, как в макете (Figma 969:8053) */
.hero__chips .chip {
  padding-inline: calc(var(--spacing-m) - var(--size-stroke-m));
  border: var(--size-stroke-m) solid transparent;
  transition: border-color var(--duration-base) var(--ease-brand),
              background-color var(--duration-base) var(--ease-brand);
}
/* Выбранный: заливка остаётся washed /11, обводка загорается цветом
   сущности /100 */
.hero__chips .chip[aria-pressed="true"] {
  border-color: var(--chip-accent, var(--color-brand));
  background: var(--chip-wash, var(--color-fill-secondary));
}
/* Наведение и нажатие сильнее выбора: правило выше перебивало .chip:hover
   из кита по весу, и под неподвижным курсором чип светлел ровно в тот миг,
   когда показ переключал выбор на него, — читалось как мигание.
   Лестница 11 → 22 → 33 не зависит от того, выбран вариант или нет */
@media (hover: hover) {
  .hero__chips .chip:hover { background: var(--chip-wash-22, var(--color-button-secondary-hover)); }
}
.hero__chips .chip:active { background: var(--chip-wash-33, var(--color-button-secondary-pressed)); }
.chip--bot[aria-pressed="true"]    { --chip-accent: var(--entity-bot-accent); }
.chip--app[aria-pressed="true"]    { --chip-accent: var(--entity-app-accent); }
.chip--shop[aria-pressed="true"]   { --chip-accent: var(--entity-shop-accent); }
.chip--funnel[aria-pressed="true"] { --chip-accent: var(--entity-funnel-accent); }
.chip--game[aria-pressed="true"]   { --chip-accent: var(--entity-game-accent); }
.chip--gate[aria-pressed="true"]   { --chip-accent: var(--entity-gate-accent); }

/* «Другое» — не сущность, а нейтральный вариант: палитра Info (975:8073).
   Своего набора --entity-* у него нет, поэтому washes собраны здесь */
.chip--other {
  --chip-wash: var(--color-info-11);
  --chip-wash-22: var(--color-info-22);
  --chip-wash-33: var(--color-info-33);
}
.chip--other[aria-pressed="true"] { --chip-accent: var(--color-info); }

@media (max-width: 767px) {
  /* На телефоне обе половины вдвое меньше: 64 от шапки и 64 до логотипов */
  .hero { padding-block: var(--spacing-xxl); }

  /* E1 (56/60) на телефоне не помещается в строку и распирает страницу.
     Берём следующую ступень шкалы — H1 32/38 для заголовка и H3 20/24 для
     подзаголовка, а не подгоняем размер вручную: ступени на то и заведены.
     Переносы из макета тоже снимаем — на узкой колонке они рвут строку
     в случайных местах */
  .hero__title {
    font-size: var(--text-h1);
    line-height: var(--text-h1--line-height);
    letter-spacing: var(--text-h1--letter-spacing);
  }
  .hero__lead {
    font-size: var(--text-h3);
    line-height: var(--text-h3--line-height);
    letter-spacing: var(--text-h3--letter-spacing);
  }
  .hero__title br, .hero__lead br { display: none; }

  /* «вместе» уходит на телефоне: обещание держится и без него, а строка
     становится короче на слово. Пробел перед словом лежит внутри span —
     иначе после скрытия между «мессенджерах» и «с» остался бы двойной */
  .hero__together { display: none; }

  /* Дальше — мобильный макет 985:8442. Ряд под полем остаётся строкой:
     мессенджеры слева, кнопка справа на весь остаток */
  .hero__actions { flex: 1; }
  .hero__submit { flex: 1; }
  /* Поля вокруг площадок ужимаем вдвое, а зазор до кнопки — с 16 до 8:
     иначе на экранах уже макетных 395px строка не сходится на несколько
     пикселей и кнопка сваливается вниз. В макете группа ровно 122px */
  .hero__messengers { padding-inline: var(--spacing-xs); }
  .hero__row { gap: var(--spacing-xs); }

  /* Карточка ужата на ширину хвоста с обеих сторон: слева в этот запас
     встаёт сам хвост, справа — ради симметрии. В макете 340 из 395 */
  .hero__card {
    width: calc(100% - 28px);
    margin-inline: auto;
  }

  /* Поле под запрос — три строки вместо двух: на узкой колонке в две
     строки помещается заметно меньше текста. Потолок прежний, 11 */
  .hero__prompt { min-height: calc(var(--text-h3--line-height) * 3 + var(--spacing-xs) * 2); }

  /* Пазло ровно десктопный: тот же рост и тот же заход на карточку,
     сдвинут только вправо-влево — отступ от края 32 вместо 87.
     Ради него же отступ описание → карточка поднят до 96: робот торчит
     над карточкой на 89, и в стандартные 64 он садится на подзаголовок */
  .hero { gap: calc(var(--spacing-xxl) + var(--spacing-xl)); }
  .hero__sticker { inset-inline-end: calc(var(--spacing-xl) - var(--hero-card-stroke)); }
}

/* iPhone SE и mini (375): строка сходится только без боковых полей у площадок —
   кнопке нужно 154, а с полями остаётся 145. Уже на 360 строка не держится
   ни при каких полях: кнопка честно уходит вниз на всю ширину */
@media (max-width: 380px) {
  .hero__messengers { padding-inline: 0; }
}

/* Пользователю, которому анимация мешает, не показываем ни машинопись,
   ни движение Пазло (их глушит уже сам JS), ни дыхание волны.
   Свечения в этом списке больше нет: оно теперь не переливается ни у кого */
@media (prefers-reduced-motion: reduce) {
  .hero__caret { animation: none; }
}

/* ── Логотипы ─────────────────────────────────────────────────────────────
   Figma 930:59564. Секция 88 высотой, сверху линия 2, лента 54.

   Логотипы приходят уже собранными: каждый SVG выгружен вместе с боксом
   ячейки и покрашен в Text/Tertiary внутри файла, поэтому размеры здесь
   не задаются — <img> берёт их из самого файла, и оптическая калибровка
   дизайнера (у каждого знака своя высота) доезжает без единого px */
.logos {
  /* Бокс логотипа 121.94 и зазор 64 из макета (шаг ячейки 185.94) сложены
     в один шаг: зазор живёт паддингом ячейки, а не gap ленты. Иначе между
     двумя проходами ленты зазора бы не было, и сдвиг на -50% дал бы стык
     внахлёст */
  --logos-box: 122px;
  --logos-gap: var(--spacing-xxl);
  /* Шаг ячейки и длина одного прохода. Проход 2046 короче широкого экрана,
     поэтому проходов три, а не два: сдвиг равен одному проходу, и оставшиеся
     два (4092) должны закрывать экран целиком — иначе справа зияет пустота */
  --logos-step: calc(var(--logos-box) + var(--logos-gap));
  --logos-count: 11;
  /* Высота ленты (--logos-height) объявлена в :root. Стоит на ячейке, а не
     берётся от картинки: незагруженный <img> занимает 0×0, и высота секции
     росла бы рывками по мере загрузки — а с ней прыгала бы вся страница */
  /* Полный проход ленты. Скорость в макете не задана: 55s на проход 2046
     дают ~37px/с — читается на ходу, но не тянет на себя внимание. Считать
     надо от прохода, а не от секунд: расширили зазор — лента при тех же 40s
     ускорилась бы до 51px/с сама собой */
  --logos-duration: 55s;
  /* Растушёвка краёв, Figma 930:59601 — 649 из 1920, те же 33.8% ширины.
     В макете зеркальный прямоугольник уехал за правый край кадра, здесь
     он на месте: без него логотипы обрубались бы на границе */
  --logos-fade: 33.8%;

  position: relative;
  overflow: hidden;
  border-top: var(--size-stroke-m) solid var(--color-divider);
  background: var(--color-surface);
  /* 88 секции минус линия 2 минус лента 54 = по 16 сверху и снизу */
  padding-block: var(--spacing-m);
}

/* Края гасим двумя градиентами поверх ленты — ровно как в макете
   (930:59601 и 930:59600), а не маской на всей секции. Маска обязывает
   Safari пересчитывать её на каждом кадре движения: слой ленты 8184 в
   ширину, и на iOS этого хватало, чтобы главный поток не успевал
   отрисовывать Пазло — стикер замирал, стоило ленте въехать в кадр.
   Градиенты статичны, их браузер кладёт отдельным слоем один раз */
.logos::before, .logos::after {
  content: "";
  position: absolute;
  inset-block: 0;
  width: var(--logos-fade);
  pointer-events: none;
  /* Оба поверх ленты. Без этого левый уходил под неё: will-change у трека
     заводит свой контекст наложения, и трек встаёт в общий порядок как
     позиционированный — то есть после ::before и до ::after. Правый край
     гас, левый нет */
  z-index: 1;
}
.logos::before { inset-inline-start: 0; background: linear-gradient(to right, var(--color-surface), transparent); }
.logos::after  { inset-inline-end: 0;  background: linear-gradient(to left,  var(--color-surface), transparent); }

.logos__track {
  display: flex;
  width: max-content;
  animation: logos-scroll var(--logos-duration) linear infinite;
  /* Лента едет всегда — пусть у неё будет свой слой, а не пересобирается
     вместе с секцией на каждом кадре */
  will-change: transform;
}

.logos__line {
  display: flex;
  margin: 0;
  padding: 0;
  list-style: none;
}

.logos__item {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  /* box-sizing в проекте border-box, поэтому зазор входит в ширину ячейки,
     а не прибавляется к ней: шаг ленты = бокс плюс зазор */
  width: var(--logos-step);
  height: var(--logos-height);
  padding-inline: calc(var(--logos-gap) / 2);
}

/* Innopolis шире своей ячейки — в макете он тоже вылезает за её границы
   и заезжает в зазор. Соседям это не мешает: пустого поля вокруг хватает */
.logos__logo { display: block; max-width: none; }

/* Сдвиг ровно на длину одного прохода — считаем её от шага ячейки, а не
   долей от ленты: доля зависит от числа копий, и стоит добавить ещё одну,
   как цикл разъезжается. Здесь копии можно добавлять свободно */
@keyframes logos-scroll {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(var(--logos-count) * var(--logos-step) * -1)); }
}

/* На узких растушёвка макетной ширины съела бы почти всю ленту */
@media (max-width: 1279px) {
  .logos { --logos-fade: 12%; }
}

/* На телефоне лента едет так же, но проходов вдвое меньше. Их число задаёт
   не красота, а покрытие: лента сдвигается на один проход, и всё правее
   сдвига должно быть заполнено до края экрана. Четыре прохода держат до
   8184, здесь же экран не шире 767 — двух хватает с запасом (4092 при
   сдвиге 2046), а слой выходит вдвое меньше работы композитору */
@media (max-width: 767px) {
  .logos__line:nth-child(n+3) { display: none; }
  /* Зазор 32 из мобильного макета (1069:19704). Шаг ячейки, а с ним и длина
     прохода считаются от него сами — цикл не разъезжается */
  .logos { --logos-gap: var(--spacing-xl); }
}

/* Движущаяся лента — как раз то, что просят выключить. Остановленная
   лента показывает первые логотипы, остальные уезжают за край: это
   осознанная плата, зато никто не гонится глазами за движением */
@media (prefers-reduced-motion: reduce) {
  .logos__track { animation: none; }
}

/* ── Служебное ────────────────────────────────────────────────────────── */
/* Скрыто визуально, но доступно скринридеру: подписи иконок и легенды */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ── Иконки ───────────────────────────────────────────────────────────────
   Уехали в дизайн-систему (tokens.css, раздел «Иконки»): набор один
   на весь сайт, и держать его двумя копиями — прямой путь к тому,
   что одна и та же стрелка окажется разной толщины на разных страницах */

/* ══════════════════════════════════════════════════════════════════════
   БЛОК «КАК ЭТО РАБОТАЕТ»
   ══════════════════════════════════════════════════════════════════════ */

/* ==========================================================================
   Секция «Как это работает». Figma 930:59603 (до) → 930:62459 (после).

   Три экрана 360×360 и подписи под ними. Всё, что меняется по сценарию,
   помечено классами --is-* и переключается из work.js.

   Значения, которых нет в ките, вынесены в --work-* с указанием источника.
   Дробные пиксели в телефоне не ошибка: iOS-лист в макете отмасштабирован
   в 1.1216 раза (360 из 321), и округление разъезжается на разделителях.
   ========================================================================== */

:root {
  /* ── Чат ─────────────────────────────────────────────────────────────
     Пузырь собственного сообщения. В ките роли Message/Me нет, но значение
     совпадает с Palette/Bern/05 — берём его, а не свой хекс */
  --work-bubble-me: var(--entity-funnel-surface);
  /* Точка невыполненного шага. Figma: Basic/Disabled — ⚠️ токена нет */
  --work-step-dot: #d0d1d2;

  /* ── Канвас ──────────────────────────────────────────────────────────
     Цвета связей — ступень 56 палитры сущности, снято с векторов 930:62611…
     930:62625. Условие (Manila) есть в ките только ступенями 01 и 11,
     поэтому 56 и 100 заведены здесь. ⚠️ добавить в tokens.css */
  --work-wire-shop: var(--entity-inline-stroke);
  --work-wire-app: var(--entity-app-stroke);
  --work-wire-contacts: var(--entity-bot-stroke);
  --work-wire-check: #93cfce;
  --entity-condition-accent: #5ebab9;

  /* Сетка канваса. Figma 930:59641: линии #0089ff толщиной 1.31 с шагом
     65.72, весь слой на непрозрачности 20% */
  --work-grid-line: rgba(0, 137, 255, 0.2);
  --work-grid-step: 65.72px;
  --work-grid-width: 1.31px;
  /* Насколько элемент канваса вправе выехать за рамку. Ровно столько же
     торчит группа Build в макете: её x = -16 при ширине канваса 486 */
  --work-overhang: 16px;
  /* Высота канваса в макете: от неё отсчитаны координаты всех узлов */
  --work-scene: 360px;

  /* ── Подложки иконок ─────────────────────────────────────────────────
     Плитки под 3D-эмодзи. Figma 930:62593…930:62608 — ⚠️ в ките таких
     ступеней нет, это отдельная пастельная шкала под иллюстрации */
  --work-tile-lessons: #cce4ff;
  --work-tile-shop: #f0d9fa;
  --work-tile-app: #dbf5e2;
  --work-tile-contacts: #fbf2de;

  /* ── Телефон ─────────────────────────────────────────────────────────
     Системный шрифт iOS: в макете лист набран SF Pro, у нас его нет —
     отдаём системному стеку, Geist здесь был бы не к месту */
  --work-ios-font: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI", sans-serif;
  --work-phone-card: 112.164px;
  --work-phone-gap: 13.5px;
  --work-phone-pad: 18px;
  --work-phone-radius: 11.216px;
  --work-phone-row-icon: 33.75px;
  --work-phone-sep: rgba(196, 196, 196, 0.34);
  --work-ios-label: #1a1a1a;
  /* Обводка выбранной карточки. Figma 930:59762: градиент по диагонали,
     толщина 2.95 с выравниванием наружу, между карточкой и линией зазор 2 */
  --work-ring-from: #00c86f;
  --work-ring-to: #00a5fc;
  --work-ring-width: 2.952px;
  --work-ring-gap: 4px;
  --work-ring-duration: 4s;
  /* Насколько обводка вылезает за карточку — на столько же ряд карточек
     раздвигает свой бокс, иначе прокрутка срезала бы кольцо */
  --work-ring-overhang: calc(var(--work-ring-gap) + var(--work-ring-width));

  /* Шаг сценария: пауза между строками лога и перед сменой полосы на «Готово» */
  --work-beat: 2s;
  /* Планшет: сколько выбранный пункт стоит в конечном состоянии, прежде чем
     список сам перейдёт к следующему */
  --work-hold: 3s;
  /* Сторона экрана на планшете — квадрат из 1013:9012 */
  --work-tablet: 400px;
  /* Появление и уход элементов сценария */
  --work-fade: 0.25s;
}

/* Угол градиентной обводки. Без @property анимировать conic-gradient нельзя:
   браузер не знает, что --work-ring-angle — это угол, и подменяет скачком */
@property --work-ring-angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

/* Поля секции. Раньше они стояли на body — здесь их носит сама
   секция, иначе они достались бы и соседним блокам.

   Сверху целый шаг за вычетом 16: выше лента логотипов, и её заливка
   того же белого, что и фон страницы, — границы секции глазом не видно.
   Видно последнюю строчку логотипов, а под ней ещё 16 внутреннего поля
   полосы (лого 54 в полосе 88). Не вычесть их — и зазор читается как
   176 при заявленных 160. Вычитаем ровно ту ступень, что стоит
   у логотипов, а не подобранное число.

   Снизу половина — вторую половину даёт блок кейсов */
.work {
  /* Ширина и поля как у «Почему мы лучшие» и футера: макетные 1270 плюс
     поля, добавленные к блоку, а не отъеденные от него. Так секция стоит
     в той же колонке, что соседи, и на широком экране канвас получает
     свои макетные 486 */
  max-inline-size: calc(1270px + 2 * var(--spacing-m));
  margin-inline: auto;
  padding-block: calc(var(--section-gap) - var(--spacing-m)) var(--section-gap-half);
  padding-inline: var(--spacing-m);
}

/* ══ Сетка секции ═══════════════════════════════════════════════════════ */

/* Трек занимает колонку целиком, а не ужимается по содержимому: чат и
   телефон держат макетные 360, весь остаток уходит канвасу. На 1270 он
   получает ровно свои 486 из макета, на 1280 — 464. Ленты с прокруткой
   рукой здесь больше нет: три колонки помещаются на любом десктопе */
.work__track {
  display: flex;
  flex: none;
  gap: var(--spacing-xl);
  inline-size: 100%;
}

/* Ширина живёт на колонке, а не в шаблоне сетки: в режиме ленты трек тот
   же самый, и колонки обязаны остаться макетными. --work-col задаётся
   только на узком экране и там перебивает все три */
.work__col {
  margin: 0;
  flex: none;
  inline-size: var(--work-col, 360px);
}

/* Канвас — единственная колонка, которая дышит. Сцена внутри привязана
   к кромкам, поэтому лишние или недостающие пиксели уходят в её середину,
   а не ломают композицию */
.work__col:nth-child(2) {
  flex: 1;
  inline-size: auto;
  min-inline-size: 0;
}

/* Обводка у всех трёх экранов одна и та же — Utilitarian/Info/05.
   Сглаживание углов в Figma выкручено на 100%: без corner-shape радиус
   ломается на глаз, а на squircle совпадает */
.work__screen {
  position: relative;
  box-sizing: border-box;   /* 360×360 в макете — это размер вместе с обводкой */
  block-size: 360px;
  border: var(--size-stroke-m) solid var(--color-info-05);
  border-radius: calc(var(--radius-xl) * var(--corner-scale));
  corner-shape: var(--corner-smooth);
  background:
    linear-gradient(180deg, var(--color-surface) 0%, rgba(255, 255, 255, 0) 100%),
    var(--color-info-05);
}

/* Отступ 16 по бокам — из блока подписи в макете (930:59635) */
.work__caption {
  margin-block-start: var(--spacing-l);
  padding-inline: var(--spacing-m);
}

.work__title {
  margin: 0;
  font-size: var(--text-h2);
  line-height: var(--text-h2--line-height);
  letter-spacing: var(--text-h2--letter-spacing);
  font-weight: var(--font-weight-heading);
}

/* В макете подпись набрана тем же Text/Primary, что и заголовок */
.work__text {
  margin: var(--spacing-m) 0 0;
  max-inline-size: var(--measure-text);
  font-size: var(--text-p1);
  line-height: var(--text-p1--line-height);
}

/* ══ Колонка 1: чат ═════════════════════════════════════════════════════ */

.chat {
  display: flex;
  flex-direction: column;
  overflow: hidden;
  /* Лента светлеет к низу — Figma 930:62464 */
  background: linear-gradient(180deg, var(--color-surface) 45.6%, var(--color-info-05) 100%);
}

/* Сообщения тают под верхней кромкой. В макете это прямоугольник 360×149
   с белым градиентом (930:62506) — здесь тот же градиент, но поверх ленты */
.chat::after {
  content: '';
  position: absolute;
  inset-block-start: 0;
  inset-inline: 0;
  block-size: 149px;
  background: linear-gradient(180deg, var(--color-surface), rgba(255, 255, 255, 0));
  pointer-events: none;
}

.chat__feed {
  flex: 1;
  overflow-y: hidden;
  padding: var(--spacing-xs) var(--spacing-xs) 0;
}

/* Стек прижат к низу ленты: свежие сообщения всегда у поля ввода.
   margin-block-start:auto, а не justify-content на .chat__feed — иначе
   при переполнении верх ленты становится недоступен для прокрутки */
.chat__stack {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-m);
  min-block-size: 100%;
  justify-content: flex-end;
}

.chat__date {
  align-self: center;
  padding: var(--spacing-xxs) var(--spacing-xs);
  border-radius: var(--radius-l);
  background: var(--color-overlay);
  backdrop-filter: blur(8px);
  color: var(--color-text-on-primary);
  font-size: var(--text-p3);
  line-height: var(--text-p3--line-height);
}

.chat__msg {
  display: flex;
  justify-content: flex-end;
  padding-inline-end: 6px;  /* место под хвостик пузыря */
}

.chat__bubble {
  position: relative;
  margin: 0;
  max-inline-size: 216px;
  padding: var(--spacing-xxs) var(--spacing-xs);
  border-radius: var(--radius-l) var(--radius-l) 0 var(--radius-l);
  background: var(--work-bubble-me);
  font-size: var(--text-p2);
  line-height: var(--text-p2--line-height);
}

/* Хвостик 6×10 из макета (930:62486). Вырезаем вогнутую четверть круга
   маской — SVG ради одной капли не нужен */
.chat__bubble::after {
  content: '';
  position: absolute;
  inset-block-end: 0;
  inset-inline-end: -6px;
  inline-size: 6px;
  block-size: 10px;
  background: var(--work-bubble-me);
  -webkit-mask: radial-gradient(circle at 100% 0, transparent 6px, #000 6px);
  mask: radial-gradient(circle at 100% 0, transparent 6px, #000 6px);
}

.chat__steps {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

.chat__step {
  display: flex;
  align-items: center;
  gap: var(--spacing-xs);
  padding-inline: var(--spacing-xs);
  font-size: var(--text-p2);
  line-height: var(--text-p2--line-height);
  font-weight: var(--font-weight-body-bold);
  color: var(--color-text-secondary);
}

.chat__step--done { color: var(--color-text-primary); }

.chat__dot {
  inline-size: 8px;
  block-size: 8px;
  margin-inline: 7px;   /* центрируем точку в колонке иконок 22px */
  border-radius: var(--radius-full);
  background: var(--work-step-dot);
}

.chat__step-icon,
.chat__mic {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  inline-size: var(--size-button-icon-m);
  block-size: var(--size-button-icon-m);
}

/* Иконки отдают собственный размер из viewBox — растягивать их нельзя */
.chat__step-icon img,
.chat__mic img,
.canvas__badge-icon img { display: block; }

/* Самолётик отдаёт свои 19.25 из viewBox — ровно столько занимает вектор
   внутри Icon 22 в кнопке 38 (930:59632). Растягивать до 22 нельзя:
   съедаются внутренние поля иконки, и она выглядит крупнее макета */
.chat__send img { display: block; }

.chat__answer {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xs);
  padding-inline: var(--spacing-xs);
}

.chat__answer p {
  margin: 0;
  font-size: var(--text-p2);
  line-height: var(--text-p2--line-height);
}

.chat__answer b { font-weight: var(--font-weight-body-bold); }

/* ── Панель ввода ─────────────────────────────────────────────────────── */

.chat__panel {
  display: flex;
  align-items: flex-end;
  gap: var(--spacing-xxs);
  padding: var(--spacing-m);
}

.chat__field {
  flex: 1;
  display: flex;
  align-items: flex-start;
  gap: var(--spacing-xs);
  min-inline-size: 0;
  padding: var(--spacing-xs);
  border: var(--size-stroke-m) solid var(--color-brand);
  border-radius: var(--radius-l);
  background: var(--color-surface);
  /* Figma Focus/Normal: обводка 3 цветом Basic/Focus */
  box-shadow: 0 0 0 3px rgba(66, 148, 255, 0.26);
}

.chat__value {
  flex: 1;
  margin: 0;
  min-inline-size: 0;
  font-size: var(--text-p2);
  line-height: var(--text-p2--line-height);
}

/* До отправки поле занято текстом запроса — микрофону там места нет */
.chat__panel:not(.is-sent) .chat__mic { display: none; }

.chat__send {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  inline-size: var(--size-button-icon-xl);
  block-size: var(--size-button-icon-xl);
  padding: 0;
  border: 0;
  border-radius: var(--radius-l);
  background: var(--color-brand);
  cursor: pointer;
  transition: background 0.15s ease, opacity 0.2s ease;
}

@media (hover: hover) {
  .chat__send:hover { background: var(--color-text-link-hover); }
}
.chat__send:active { background: var(--color-text-link-pressed); }

/* Состояние после отправки: поле пустое и неактивное, кнопка не нужна */
.chat__panel.is-sent .chat__field {
  align-items: center;
  border-color: var(--color-border);
  box-shadow: none;
}

.chat__panel.is-sent .chat__value {
  color: var(--color-text-tertiary);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.chat__panel.is-sent .chat__send { display: none; }

/* ── Полоса генерации. Figma 962:8046 ─────────────────────────────────── */

.chat__gen {
  display: flex;
  align-items: center;
  gap: var(--spacing-xs);
  padding-inline: var(--spacing-xs);
}

.chat__gen-icon {
  flex: none;
  inline-size: var(--size-button-icon-m);
  block-size: var(--size-button-icon-m);
}

.chat__gen-text {
  flex: 1;
  min-inline-size: 0;
  font-size: var(--text-p2);
  line-height: var(--text-p2--line-height);
  font-weight: var(--font-weight-body-medium);
}

.chat__gen-pause {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  inline-size: var(--size-button-icon-m);
  block-size: var(--size-button-icon-m);
  border-radius: var(--radius-s);
  background: var(--color-fill-muted);
  backdrop-filter: blur(8px);
}

.chat__gen-pause img { display: block; }

/* Полосу снимаем атрибутом hidden: так её легко вернуть при откате скролла.
   display:flex его перебивает, поэтому гасим руками — и разрешаем анимировать
   само переключение display, иначе полоса исчезала бы рывком */
.chat__gen {
  transition: opacity 0.2s ease, display 0.2s allow-discrete;
}

.chat__gen[hidden] {
  display: none;
  opacity: 0;
}

/* Появление шага, полосы, пузыря и ответа — одно движение на 4px.
   Только для того, что дописал скрипт: стартовая разметка уже на месте
   и проявляться не должна, иначе при перезагрузке текст моргает.

   Переход, а не keyframes: при прокрутке вверх те же узлы прячутся
   атрибутом hidden, и уход должен быть таким же плавным, как приход */
.chat__stack .is-in {
  transition:
    opacity var(--work-fade) ease,
    translate var(--work-fade) ease,
    display var(--work-fade) allow-discrete;
}

@starting-style {
  .chat__stack .is-in { opacity: 0; translate: 0 4px; }
}

.chat__stack .is-in[hidden] { opacity: 0; translate: 0 4px; }

/* Скрытое скриптом не участвует в раскладке: display у наших блоков свой,
   и атрибут hidden сам по себе его не перебивает */
.work [hidden] { display: none; }

/* ══ Колонка 2: канвас ══════════════════════════════════════════════════ */

/* Ноды выходят за рамку на 16px — обрезать нечем, поэтому фон и сетка
   обрезают себя сами, а сам экран остаётся видимым наружу.
   Обводка нарисована тенью, а не border: иначе координаты узлов считались
   бы от padding-box и вылет за кромку не сходился бы с макетом на 2px */
.canvas {
  overflow: visible;
  border: 0;
  box-shadow: inset 0 0 0 var(--size-stroke-m) var(--color-info-05);
}

.canvas__grid {
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
  background:
    repeating-linear-gradient(to right,  var(--work-grid-line) 0 var(--work-grid-width), transparent 0 var(--work-grid-step)),
    repeating-linear-gradient(to bottom, var(--work-grid-line) 0 var(--work-grid-width), transparent 0 var(--work-grid-step));
  /* Затухание к краям: в макете это белая внутренняя тень 164/118 поверх
     сетки (930:62542), маской считает композитор. Плато держим широким —
     линии и так на 20% непрозрачности, и на середине градиента пропадают */
  -webkit-mask: radial-gradient(ellipse 70% 50% at 50% 50%, #000 20%, transparent 100%);
  mask: radial-gradient(ellipse 70% 50% at 50% 50%, #000 20%, transparent 100%);
}

/* Порядок слоёв: бейджи → нода → связи. Нода перекрывает бейджи, а связи
   идут поверх неё: порт-кружок в макете лежит на белой строке */
.canvas__wires {
  position: absolute;
  inset: 0;
  z-index: 3;
  inline-size: 100%;
  block-size: 100%;
  overflow: visible;
  pointer-events: none;
}

/* Связь живёт в разметке всегда, а гаснет, пока нет одного из концов, —
   так появление стрелки идёт тем же переходом, что и появление узла */
.canvas__wire { transition: opacity var(--work-fade) ease; }
.canvas__wire.is-off { opacity: 0; }

/* Вертикали узлов — абсолютные, от верхнего края канваса (сняты через
   absoluteBoundingBox на 930:62511). Ноду ставим так, чтобы её центр
   совпал с макетом «после»: она растёт от центра.

   По горизонтали нода прижата к левой кромке и выходит за неё на те же
   --work-overhang, что бейджи за правую: так между ней и бейджами остаётся
   максимум места под связи. В макете 930:62511 она стоит на 85, но тогда
   на ужатом канвасе связи складывались вдвое (решение Александра 11.09.2026) */
.canvas__node {
  position: absolute;
  z-index: 2;
  inset-inline-start: calc(var(--work-overhang) * -1);
  inset-inline-end: auto;
  inset-block-start: 116px;
  inline-size: 225px;
  border-radius: var(--radius-l);
  background: var(--entity-app-accent);
  cursor: grab;
}

.canvas__node-title,
.canvas__badge {
  display: flex;
  align-items: center;
  color: var(--color-text-on-primary);
  font-size: var(--text-p2);
  line-height: var(--text-p2--line-height);
  font-weight: var(--font-weight-body-medium);
  white-space: nowrap;
  user-select: none;
}

.canvas__node-title {
  gap: var(--spacing-xs);
  padding: var(--spacing-xs) var(--spacing-m) var(--spacing-xs) var(--spacing-xs);
}

.canvas__node-body { padding: 0 var(--spacing-xxs) var(--spacing-xxs); }

.canvas__rows {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xxs);
  margin: 0;
  padding: var(--spacing-xxs);
  list-style: none;
  border-radius: var(--radius-lg);
  background: var(--color-surface);
}

.canvas__row {
  display: flex;
  align-items: center;
  gap: var(--spacing-xs);
  font-size: var(--text-p3);
  line-height: var(--text-p3--line-height);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.canvas__row + .canvas__row {
  padding-block-start: var(--spacing-xxs);
  border-block-start: 1px solid var(--color-divider);
}

.canvas__row-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  inline-size: var(--size-button-icon-m);
  block-size: var(--size-button-icon-m);
  border-radius: var(--radius-s);
  overflow: hidden;
}

.canvas__row-icon img {
  inline-size: 100%;
  block-size: 100%;
  object-fit: cover;
}

.canvas__row-icon--lessons  { background: var(--work-tile-lessons); }
.canvas__row-icon--shop     { background: var(--work-tile-shop); }
.canvas__row-icon--app      { background: var(--work-tile-app); }
.canvas__row-icon--contacts { background: var(--work-tile-contacts); }

/* Выше слоя связей: на узком экране бейджи стоят так плотно, что чужая
   кривая проходит по ним — пусть уходит за карточку, а не режет её.
   Наконечник останавливается в 12px от цели, так что не прячется */
.canvas__badge {
  position: absolute;
  z-index: 4;
  gap: var(--spacing-xxs);
  padding: var(--spacing-xs) var(--spacing-m) var(--spacing-xs) var(--spacing-xs);
  border-radius: var(--radius-full);
  cursor: grab;
}

.canvas__badge-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  inline-size: var(--size-button-icon-m);
  block-size: var(--size-button-icon-m);
}

/* Все бейджи прижаты к правой кромке и выходят за неё на --work-overhang —
   столько же торчит группа Build в макете. Тот же предел действует и при
   перетаскивании.

   Проверка, что композиция сходится в 360: нода занимает от −16 до 209,
   самый широкий бейдж «Возможности» (151) встаёт на 360 + 16 − 151 = 225.
   Между ними ровно 16, как в 1013:9306 */
.canvas__badge--shop,
.canvas__badge--app,
.canvas__badge--contacts,
.canvas__badge--check {
  inset-inline-start: auto;
  inset-inline-end: calc(var(--work-overhang) * -1);
}

.canvas__badge--shop {
  background: var(--entity-inline-accent);
  inset-block-start: 105px;
}
.canvas__badge--app {
  background: var(--entity-app-accent);
  inset-block-start: 206px;
}
.canvas__badge--contacts {
  background: var(--entity-bot-accent);
  inset-block-start: 331px;
}

/* «Проверка доступа» встаёт над «Товарами» с зазором 32. Считаем от низа,
   а не от верха: так собственная высота бейджа в формулу не входит.
   73 = 105 (верх «Товаров») − 32 (зазор) */
.canvas__badge--check {
  background: var(--entity-condition-accent);
  inset-block-start: auto;
  inset-block-end: calc(100% - 73px);
}

[data-drag]:active,
[data-drag].is-dragging { cursor: grabbing; }

[data-drag]:focus-visible {
  outline: var(--size-stroke-m) solid var(--color-brand);
  outline-offset: 2px;
}

.canvas.is-dragging { user-select: none; }

/* Новый бейдж приезжает тем же движением, что строки лога */
.canvas__badge.is-new {
  transition:
    opacity var(--work-fade) ease,
    translate var(--work-fade) ease,
    display var(--work-fade) allow-discrete;
}

@starting-style {
  .canvas__badge.is-new { opacity: 0; translate: 0 4px; }
}

.canvas__badge.is-new[hidden] { opacity: 0; translate: 0 4px; }

/* Строка раздвигает ноду: высоту auto не анимировать, поэтому едет
   ограничитель — 40px с запасом больше строки в 27 */
.canvas__row.is-new {
  overflow: hidden;
  max-block-size: 40px;
  transition:
    max-block-size 0.35s ease,
    padding-block-start 0.35s ease,
    opacity var(--work-fade) ease,
    display 0.35s allow-discrete;
}

@starting-style {
  .canvas__row.is-new { max-block-size: 0; opacity: 0; }
}

.canvas__row.is-new[hidden] {
  max-block-size: 0;
  padding-block-start: 0;
  opacity: 0;
}

/* ══ Колонка 3: телефон ═════════════════════════════════════════════════ */

.phone {
  overflow: hidden;
  font-family: var(--work-ios-font);
}

/* Лист выезжает снизу и уходит за нижнюю кромку экрана */
.phone__sheet {
  position: absolute;
  inset-block-start: 28px;
  inset-inline: calc(var(--size-stroke-m) * -1);
  block-size: 718px;
  border-start-start-radius: 34.03px;
  border-start-end-radius: 34.03px;
  background: var(--color-info-05);
  overflow: hidden;
}

.phone__toolbar {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-block: 4.478px 8.955px;
}

.phone__grabber {
  inline-size: 32.24px;
  block-size: 4.478px;
  border-radius: var(--radius-full);
  background: var(--color-neutral-22);
}

.phone__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  inline-size: 100%;
  padding-inline: 14.328px;
  margin-block-start: 5.373px;
}

/* Кнопки шапки взяты картинкой целиком (971:8057, 971:8066): рисовать
   iOS-глиф крестика и многоточия символами — заведомо мимо */
.phone__btn {
  display: block;
  inline-size: 39.4px;
  block-size: 39.4px;
}

.phone__name {
  font-size: var(--text-p2);
  line-height: var(--text-p2--line-height);
  font-weight: var(--font-weight-body-medium);
  color: var(--work-ios-label);
}

.phone__content {
  display: flex;
  flex-direction: column;
  gap: 24.75px;
  padding: 12px var(--work-phone-pad) 36px;
}

/* ── Ряд карточек ─────────────────────────────────────────────────────── */

.phone__cards {
  display: flex;
  gap: var(--work-phone-gap);
  /* Ряд шире экрана телефона: пальцем прокручивается сам, мышью — за счёт
     драга из work.js. Полосу прокрутки прячем, она тут лишняя */
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  scrollbar-width: none;
  cursor: grab;
  /* Ряд идёт во всю ширину листа, а боковые поля телефона отдаём ему внутрь:
     карточки уезжают под кромку экрана, а не обрезаются посреди фона.
     Сверху и снизу — запас на обводку активной карточки */
  padding-inline: var(--work-phone-pad);
  margin-inline: calc(var(--work-phone-pad) * -1);
  padding-block: var(--work-ring-overhang);
  margin-block: calc(var(--work-ring-overhang) * -1);
}

.phone__cards::-webkit-scrollbar { display: none; }

/* Пока тянут ряд мышью, картинки и подписи не должны выделяться и уезжать
   нативным драгом изображения */
.phone__cards.is-dragging {
  cursor: grabbing;
  user-select: none;
}

.phone__cards img { -webkit-user-drag: none; }

.phone__card {
  position: relative;
  flex: none;
  margin: 0;
  inline-size: var(--work-phone-card);
  block-size: var(--work-phone-card);
}

.phone__card-body {
  position: relative;
  inline-size: 100%;
  block-size: 100%;
  overflow: hidden;
  border-radius: var(--work-phone-radius);
  background: var(--color-surface);
}

.phone__card-label {
  position: absolute;
  inset-block-start: 10.97px;
  inset-inline-start: 10.66px;
  inline-size: 93.13px;
  font-size: 13.433px;
  line-height: 16.119px;
  color: #000;
}

/* Цветное пятно под иллюстрацией — в макете это большой скруглённый
   квадрат, наполовину ушедший за нижнюю кромку карточки */
.phone__card-blob {
  position: absolute;
  inset-block-start: 66px;
  inset-inline-start: 20px;
  inline-size: 113.73px;
  block-size: 113.73px;
  border-radius: 17.91px;
}

.phone__card-blob--lessons { background: var(--work-tile-lessons); }
.phone__card-blob--shop    { background: var(--work-tile-shop); }
.phone__card-blob--app     { background: var(--work-tile-app); }
.phone__card-blob--contacts { background: var(--work-tile-contacts); }

.phone__card-img {
  position: absolute;
  inset-block-start: calc(50% + 39px);
  inset-inline-start: calc(50% + 22px);
  inline-size: 103.88px;
  block-size: 103.88px;
  translate: -50% -50%;
  object-fit: cover;
}

.phone__card-img--tilt {
  inset-block-start: calc(50% + 49px);
  inline-size: 118.74px;
  block-size: 118.74px;
  rotate: -21.66deg;
}

/* Обводка выбранной карточки: градиент по кругу, крутится без остановки.
   Кольцо рисуем маской из двух слоёв — border-image градиент не скругляет */
.phone__card--active::after {
  content: '';
  position: absolute;
  /* Кольцо маска рисует внутрь своего бокса, поэтому бокс отодвигаем на
     зазор плюс толщину линии — иначе обводка ложится на саму карточку */
  inset: calc((var(--work-ring-gap) + var(--work-ring-width)) * -1);
  border-radius: calc(var(--work-phone-radius) + var(--work-ring-gap) + var(--work-ring-width));
  padding: var(--work-ring-width);
  background: conic-gradient(from var(--work-ring-angle),
              var(--work-ring-from), var(--work-ring-to), var(--work-ring-from));
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  mask-composite: exclude;
  animation: work-ring var(--work-ring-duration) linear infinite;
  pointer-events: none;
}

@keyframes work-ring {
  to { --work-ring-angle: 360deg; }
}

/* Новая карточка раздвигает ряд: соседи уезжают вправо, она проявляется.
   Отрицательный отступ на старте = карточка ещё не занимает места в ряду */
.phone__card.is-new {
  transition:
    opacity 0.4s ease,
    margin-inline-end 0.4s ease,
    display 0.4s allow-discrete;
}

@starting-style {
  .phone__card.is-new {
    opacity: 0;
    margin-inline-end: calc((var(--work-phone-card) + var(--work-phone-gap)) * -1);
  }
}

.phone__card.is-new[hidden] {
  opacity: 0;
  margin-inline-end: calc((var(--work-phone-card) + var(--work-phone-gap)) * -1);
}

/* ── Список ───────────────────────────────────────────────────────────── */

.phone__list {
  margin: 0;
  padding: 0;
  list-style: none;
  border-radius: 11.25px;
  background: var(--color-surface);
  overflow: hidden;
}

.phone__row {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--work-phone-pad);
  padding: 12.375px var(--work-phone-pad);
}

/* Разделитель начинается после иконки — как в iOS-листе */
.phone__row + .phone__row::before {
  content: '';
  position: absolute;
  inset-block-start: 0;
  inset-inline: calc(var(--work-phone-pad) * 2 + var(--work-phone-row-icon)) 0;
  block-size: 0.896px;
  background: var(--work-phone-sep);
}

/* Единственный элемент внутри экранов со сглаженными углами (930:59793).
   Не обрезаем: эмодзи в макете вылезает за плитку на 7.3 со всех сторон
   (930:59795 — картинка 48.36 при x/y −7.3 в коробке 33.75) */
.phone__row-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  inline-size: var(--work-phone-row-icon);
  block-size: var(--work-phone-row-icon);
  border-radius: calc(8.955px * var(--corner-scale));
  corner-shape: var(--corner-smooth);
}

/* Эмодзи в плитке крупнее её: в макете картинка 48 в коробке 33.75 */
.phone__row-icon img {
  inline-size: 48.36px;
  block-size: 48.36px;
  object-fit: cover;
}

.phone__row-icon--lessons  { background: var(--work-tile-lessons); }
.phone__row-icon--shop     { background: var(--work-tile-shop); }
.phone__row-icon--app      { background: var(--work-tile-app); }
.phone__row-icon--contacts { background: var(--work-tile-contacts); }

.phone__row-label {
  flex: 1;
  min-inline-size: 0;
  font-size: 15.224px;
  line-height: 19.701px;
  letter-spacing: -0.2776px;
  color: #000;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.phone__row-chevron {
  display: flex;
  align-items: center;
  justify-content: center;
  flex: none;
  inline-size: var(--work-phone-row-icon);
  block-size: var(--work-phone-row-icon);
}

.phone__row-chevron img {
  display: block;
  rotate: -90deg;
}

/* Нижний край листа тает — Figma 930:59847 */
.phone::after {
  content: '';
  position: absolute;
  inset-block-end: 0;
  inset-inline: 0;
  block-size: 149px;
  background: linear-gradient(0deg, var(--color-surface), rgba(255, 255, 255, 0));
  pointer-events: none;
}

/* ══ Экран выше макетных 360 ════════════════════════════════════════════
   Общее для планшета и телефона: там экран не 360 (400 и во всё окно
   соответственно), а координаты сцены канваса сняты с 360. Растягивать их
   пропорционально нельзя — бейджи разъедутся, и связь до «Контактов»
   протянется через полкадра. Поэтому сцена остаётся макетной, а лишняя
   высота уходит в поля сверху и снизу: сдвиг на половину разницы делает
   calc, второе слагаемое — та же координата, что и на десктопе */

@media (max-width: 1279px) {
  .canvas__node            { inset-block-start: calc(50% - var(--work-scene) / 2 + 116px); }
  .canvas__badge--shop     { inset-block-start: calc(50% - var(--work-scene) / 2 + 105px); }
  .canvas__badge--app      { inset-block-start: calc(50% - var(--work-scene) / 2 + 206px); }
  .canvas__badge--contacts { inset-block-start: calc(50% - var(--work-scene) / 2 + 331px); }
  .canvas__badge--check    { inset-block-end: calc(50% + var(--work-scene) / 2 - 73px); }

  /* Лист выше макетных 718 не бывает, а экран бывает: тянем его до нижней
     кромки, иначе на высоком окне под списком видна ступенька фона */
  .phone__sheet {
    block-size: auto;
    inset-block-end: calc(var(--size-stroke-m) * -1);
  }
}

/* ══ Планшет ════════════════════════════════════════════════════════════
   Figma 1013:9012. Три колонки требуют 1208 с полями, поэтому ниже 1280
   подписи собираются в список слева, а экраны встают друг на друга справа —
   виден выбранный. Список ведёт сценарий: клик проигрывает часть выбранного
   экрана, дальше цепочка идёт сама (work.js).

   Колонка отдаёт детей гриду напрямую через display: contents — так подпись
   и экран попадают в разные столбцы, не переезжая в разметке */

@media (min-width: 768px) and (max-width: 1279px) {
  .work__track {
    display: grid;
    /* Экран держит макетные 400: на нём стоит координатная сцена канваса.
       Отдаёт ширину список — от 400 на просторе до 240 на 768, где текст
       просто переносится на строку ниже */
    grid-template-columns: minmax(0, var(--work-tablet)) var(--work-tablet);
    grid-template-rows: repeat(3, auto);
    /* Между пунктами 32, между списком и экраном 64 — оба из 1013:9012:
       там пункты идут с шагом 124 при высоте 92, а до экрана остаётся 64 */
    gap: var(--spacing-xl) var(--spacing-xxl);
    justify-content: center;
    align-content: center;
    inline-size: auto;
    /* Строки собираются по подписям и вместе бывают ниже экрана — тогда
       высоту треку задаёт он. Экран вынут из потока, сам он её не держит */
    min-block-size: var(--work-tablet);
    position: relative;
  }

  .work__col { display: contents; }

  /* Подписи — в левый столбец, каждая в свою строку. Ни полосы слева, ни
     бокового поля: в 1013:9535 текст стоит вровень с кромкой колонки,
     а выбранный пункт отличается от прочих только цветом */
  .work__caption {
    grid-column: 1;
    margin-block-start: 0;
    padding-inline: 0;
    cursor: pointer;
  }

  .work__col:nth-child(1) .work__caption { grid-row: 1; }
  .work__col:nth-child(2) .work__caption { grid-row: 2; }
  .work__col:nth-child(3) .work__caption { grid-row: 3; }

  /* Невыбранный пункт приглушён целиком — и заголовок, и текст */
  .work__caption .work__title,
  .work__caption .work__text {
    color: var(--color-text-secondary);
    transition: color var(--work-fade) ease;
  }

  /* От невыбранного остаётся один заголовок: в 1013:9535 описание у них
     скрыто, и список читается как оглавление, а не как три абзаца */
  .work__col:not([data-active]) .work__text { display: none; }

  .work__col[data-active] .work__title,
  .work__col[data-active] .work__text { color: var(--color-text-primary); }

  /* Экраны — в правый столбец, все три в одну область.

     Позиция абсолютная, и это не украшательство: обычный элемент, растянутый
     на три строки, раздаёт им свои 400 изнутри, и строки расходятся тем
     сильнее, чем короче подписи. С убранными описаниями строки — 92, 28, 28,
     и каждая получала лишние 63, отчего зазор между пунктами читался как
     сотня вместо честных 32. Вынутый из потока экран строк не касается */
  .work__screen {
    position: absolute;
    grid-column: 2;
    grid-row: 1 / -1;
    inline-size: var(--work-tablet);
    block-size: var(--work-tablet);
    /* Список бывает и выше, и ниже экрана — держим их по общему центру */
    align-self: center;
    /* visibility, а не display: скрытый экран сохраняет размеры, и связи
       канваса считаются даже пока он не показан */
    visibility: hidden;
    opacity: 0;
    transition: opacity var(--work-fade) ease, visibility var(--work-fade);
  }

  .work__col[data-active] .work__screen {
    visibility: visible;
    opacity: 1;
  }
}

/* Наведение — только на невыбранных: выбранному темнеть некуда. Полосы
   больше нет, поэтому отзывается сам заголовок */
@media (min-width: 768px) and (max-width: 1279px) and (hover: hover) {
  @media (hover: hover) {
    .work__col:not([data-active]) .work__caption:hover .work__title {
      color: var(--color-text-primary);
    }
  }
}

/* ══ Узкий экран ════════════════════════════════════════════════════════
   Три части столбиком, каждая со своим наблюдателем в work.js: попала
   в кадр — отыгрывает свою ступень целиком и заново на каждом возвращении.

   Ленты, залипшей сцены и спейсера в четыре экрана здесь больше нет:
   сценарий вёлся прогрессом прокрутки и на устройстве отрабатывал
   рывками. Убрано 11.09.2026 */

@media (max-width: 767px) {
  .work {
    display: block;
    inline-size: auto;
    block-size: auto;
    padding-block: var(--section-gap) var(--section-gap-half);
    padding-inline: 0;
  }

  .work__viewport {
    display: block;
    position: static;
    block-size: auto;
    padding-block: var(--spacing-xxl);
    /* Запас под вылет бейджа здесь не нужен: «Проверка доступа» стоит
       внутри части, а не над её кромкой */
    margin-block: 0;
    box-sizing: border-box;
    overflow: visible;
  }

  /* Ширина блока: вычитаем поля, но не растём выше типовой мобильной.
     Иначе на широком окне канвас растягивается, бейджи разъезжаются
     к правому краю и между ними и нодой зияет дыра */
  .work {
    --work-col: min(calc(100vw - var(--spacing-xl)), 390px);
  }

  /* Столбик, а не лента. Шаг вдвое против того, что был между колонками:
     части идут одна под другой, и прежние 64 читались как зазор внутри
     одной сцены, а не как граница между ними.
     Боковой отступ центрирует блок в окне */
  .work__track {
    display: flex;
    flex-direction: column;
    gap: calc(var(--spacing-xxl) * 2);
    padding-inline: calc((100vw - var(--work-col)) / 2);
  }

  /* Колонки снова видимы гриду: планшетный display: contents отменён.
     Канвас перечислен отдельно — иначе его собственное правило гибкости
     специфичнее этого и растягивает колонку на пол-ленты */
  /* Часть встаёт в кадр целиком: окно без закреплённой шапки и без шага
     до соседа. Высота именно задана, а не «не меньше»: иначе растущая
     лента чата тянула бы карточку за собой, и высота менялась бы на
     каждой новой строке лога */
  .work__col,
  .work__col:nth-child(2) {
    display: flex;
    flex-direction: column;
    flex: none;
    inline-size: var(--work-col);
    block-size: calc(var(--vh) - var(--size-header) - var(--spacing-xxl));
  }

  /* «Проверка доступа» прижата к верхней кромке экрана, а не к макетной
     сцене: экран на телефоне выше своих 360, и лишняя высота уходит в поля
     (разбор — в «Экран выше макетных 360»). Бейдж — верхний элемент
     композиции, и в этом поле он висел посреди пустоты.
     Связь до него рисует скрипт по фактическому положению, пересчитывать
     нечего */
  .canvas__badge--check {
    inset-block-start: var(--spacing-m);
    inset-block-end: auto;
  }

  /* Все три экрана видны сразу, каждый забирает остаток своей части.
     min-block-size: 0 обязателен — без него flex-элемент не ужимается
     ниже содержимого, и лента чата снова распирала бы карточку.
     Лишнее внутри обрезает сам .chat своим overflow: hidden */
  .work__screen {
    inline-size: 100%;
    flex: 1;
    block-size: auto;
    min-block-size: 0;
    visibility: visible;
    opacity: 1;
  }
}

/* Самые узкие экраны: канвас сжимается до 328, и «Возможности» (151)
   перестаёт помещаться справа от ноды (225). Опускаем его под неё —
   там, где нода уже кончилась, а «Контакты» ещё не начались */
@media (max-width: 380px) {
  .canvas__badge--app { inset-block-start: calc(50% - var(--work-scene) / 2 + 285px); }
}

/* ══ Спокойный режим ════════════════════════════════════════════════════
   Сценарий доигрывается мгновенно: никаких появлений, машинописи и
   вращения — только конечный результат */
@media (prefers-reduced-motion: reduce) {
  .chat__stack .is-in,
  .canvas__row.is-new,
  .canvas__badge.is-new,
  .canvas__wire,
  .phone__card.is-new {
    transition: none;
  }

  .phone__card--active::after { animation: none; }
}


/* ══════════════════════════════════════════════════════════════════════
   БЛОК «РАСШИРЯЙТЕ ВОЗМОЖНОСТИ»
   ══════════════════════════════════════════════════════════════════════ */

/* ==========================================================================
   Блок «Расширяйте возможности» — лента карточек кейсов.
   Figma: блок 930:59852, лента 930:59855, карточка 937:1384,
   наведение 937:1555, промо-карточка 930:61977.

   Всё цветное в карточке — одна палитра сущности. Тип продукта меняет
   четыре переменные (--case-tint / --case-tint-hover / --case-wash /
   --case-wash-hover), а не набор правил. Положение медиа — ещё три.
   ========================================================================== */

/* Поля блока. Раньше стояли на body. По половине шага с каждой
   стороны: сверху вторую половину даёт «Как это работает», снизу —
   «Почему мы лучшие» */
.cases {
  padding-block: var(--section-gap-half);
  --case-h: 552px;                  /* Figma: высота карточки фиксирована */
}

/* ── Блок ────────────────────────────────────────────────────────────── */
/* Заголовок держится колонки лендинга, лента идёт от края до края экрана:
   обрезанные карточки по бокам — это и есть подсказка, что ряд едет дальше */
.cases__title {
  max-width: var(--container-landing);
  margin: 0 auto var(--spacing-xl);
  padding-inline: var(--page-padding-desktop);
  text-align: center;
}

/* ── Лента ───────────────────────────────────────────────────────────── */
/* Прокрутка нативная: тачпад, тач и клавиатура работают без единой строки
   скрипта. Мышь добирает перетаскиванием и колесом — это в cases.js.
   Полосу прокрутки прячем: в макете её нет, а положение и так читается
   по обрезанным карточкам у краёв */
.cases__viewport {
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  cursor: grab;
}
.cases__viewport::-webkit-scrollbar { display: none; }
.cases__viewport[data-dragging] { cursor: grabbing; }
/* Пока тянут ленту, содержимое не должно выделяться и перехватывать курсор */
.cases__viewport[data-dragging] * { user-select: none; }

/* Поля с обеих сторон: лента конечная, и последняя карточка не должна
   упираться в край экрана так же, как первая */
.cases__line {
  display: flex;
  gap: var(--spacing-xl);          /* Figma: шаг 434 = 402 + 32 */
  width: max-content;
  padding-inline: var(--spacing-xl);
}

/* Узкие ступени: шаг ленты 16, как у карточек остальных блоков */
@media (max-width: 1279px) {
  .cases__line { gap: var(--spacing-m); }
}

/* ── Карточка ────────────────────────────────────────────────────────── */
/* Внешне это карточка + логотип клиента под ней: 552 + 16 + 54 = 622 */
.case {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-m);
  flex: none;
  width: 402px;                     /* Figma: карточки ленты не тянутся */
}

/* Палитры. Наведение везде — следующая ступень той же шкалы: подложка
   уходит на ступень вперёд, обводка и бейдж — с 11 на 22. Отдельных
   «цветов наведения» в ките нет.
   Позиция медиа — координаты из макета минус 2px обводки: она нарисована
   внутрь, а absolute в CSS считается от padding-box */
.case--app {
  --case-tint: var(--entity-app-tint);
  --case-tint-hover: var(--entity-app-surface);
  --case-wash: var(--entity-app-wash);
  --case-wash-hover: var(--entity-app-wash-22);
  --case-media-left: 12.4372%;
  --case-media-top: 36.6788%;
  --case-media-width: 75.1256%;
}
/* Чат-бот покрашен нейтральной шкалой Berlin, а не палитрой сущности:
   в макете 930:60310 подложка — ступень 05, бейдж — 11 */
.case--bot {
  --case-tint: var(--color-neutral-03);
  --case-tint-hover: var(--color-neutral-11);
  --case-wash: var(--color-neutral-11);
  --case-wash-hover: var(--color-neutral-22);
  --case-media-left: 7.5377%;
  --case-media-top: 25.9124%;
  --case-media-width: 84.9246%;
  /* Единственная карточка, где медиа не упирается в низ: в макете под
     перепиской остаётся 30px поля (930:60319) */
  --case-media-bottom: 5.4745%;
}
.case--event {
  --case-tint: var(--entity-condition-tint);
  --case-tint-hover: var(--entity-condition-surface);
  --case-wash: var(--entity-condition-wash);
  --case-wash-hover: var(--entity-condition-wash-22);
  --case-media-left: 12.4372%;
  --case-media-top: 36.4964%;
  --case-media-width: 75.1256%;
}
/* Курсы взяли шкалу Success, а не палитру сущности — так в макете 930:61832 */
.case--course {
  --case-tint: var(--color-success-01);
  --case-tint-hover: var(--color-success-05);
  --case-wash: var(--color-success-11);
  --case-wash-hover: var(--color-success-22);
  --case-media-left: -0.6281%;
  --case-media-top: 25.9124%;
  --case-media-width: 101.2563%;
}
.case--shop {
  --case-tint: var(--entity-shop-tint);
  --case-tint-hover: var(--entity-shop-surface);
  --case-wash: var(--entity-shop-wash);
  --case-wash-hover: var(--entity-shop-wash-22);
  --case-media-left: 11.9347%;
  --case-media-top: 36.1314%;
  --case-media-width: 75.1256%;
}

/* Цвет подложки как отдельное анимируемое свойство. Без @property браузер
   не умеет интерполировать linear-gradient и подложка переключалась бы
   скачком — здесь же плавно едет сам цвет, а градиент пересчитывается */
@property --case-tint-current {
  syntax: "<color>";
  inherits: true;
  initial-value: transparent;
}

.case__card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-l);
  width: 100%;
  height: var(--case-h);
  /* Обводка в Figma нарисована внутрь, поэтому её толщина съедает паддинг:
     32 по макету = 30 в CSS при border-box. Иначе контент уезжает на 2px */
  padding: var(--spacing-none)
           calc(var(--spacing-xl) - var(--size-stroke-m))
           calc(var(--spacing-xl) - var(--size-stroke-m));
  border: var(--size-stroke-m) solid var(--case-wash);
  border-radius: calc(var(--radius-xl) * var(--corner-scale));
  corner-shape: var(--corner-smooth);
  background: var(--color-surface);
  overflow: hidden;                 /* мокап торчит за нижний край и режется */
  /* Карточка целиком — ссылка, поэтому гасим ссылочную типографику: внутри
     заголовок и бейдж, а не подпись со стрелкой. Кнопка внутри — <span>:
     ссылка в ссылке невалидна, и клик по ней всплывает до карточки */
  color: var(--color-text-primary);
  font-weight: var(--font-weight-body);
  --case-tint-current: var(--case-tint);
  transition: border-color var(--duration-base) var(--ease-brand),
              --case-tint-current var(--duration-base) var(--ease-brand);
}

/* Подложка шапки. Figma 937:1386: 140px тинта, растворяющегося в белый.
   Лежит на обводке — отсюда −2px по трём сторонам */
.case__wash {
  position: absolute;
  inset: calc(-1 * var(--size-stroke-m)) calc(-1 * var(--size-stroke-m)) auto;
  height: 140px;
  background: linear-gradient(to bottom, var(--case-tint-current), transparent);
}

/* ── Бейдж типа ──────────────────────────────────────────────────────── */
/* Приклеен к верхнему краю карточки, по бокам — вогнутые «крылышки»:
   заливка перетекает в горизонталь края. Крылышки — маска 6×10 на
   псевдоэлементах, так они красятся палитрой и не плодят разметку */
.case__tab {
  display: flex;
  align-items: flex-start;
  /* Бейдж прилегает к самому краю карточки — заезжает на обводку */
  margin: calc(-1 * var(--size-stroke-m)) 0 0;
  position: relative;
  z-index: 1;
}
.case__tab::before,
.case__tab::after {
  content: "";
  width: 6px;
  height: 10px;
  background-color: var(--case-wash);
  transition: background-color var(--duration-base) var(--ease-brand);
  -webkit-mask-image: var(--case-notch); mask-image: var(--case-notch);
  -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
  -webkit-mask-size: contain; mask-size: contain;
}
/* Одна и та же маска: слева отражена по вертикали, справа — по обеим осям */
.case__tab::before { transform: scaleY(-1); }
.case__tab::after  { transform: scale(-1, -1); }

/* Data-URI, а не файл: страницы открываются двойным кликом, а внешний SVG
   в mask по file:// блокируется CORS. Путь — Figma 937:1388 «Shape» */
:root {
  --case-notch: url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 6 10%22%3E%3Cpath d=%22M6 0C6 3.21429 4.23529 7.67857 0.705882 8.57143C0.705882 8.57143 0 8.57143 0 9.28572C0 10 0.705882 10 0.705882 10L6 10V0Z%22 fill=%22%23000%22/%3E%3C/svg%3E');
}

.case__badge {
  display: flex;
  align-items: center;
  gap: var(--spacing-xs);
  height: var(--size-chip);
  padding-inline: var(--spacing-m);
  border-radius: 0 0 var(--radius-xl) var(--radius-xl);
  background: var(--case-wash);
  color: var(--color-text-primary);
  font-size: var(--text-p1);
  line-height: var(--text-p1--line-height);
  font-weight: var(--font-weight-body-medium);
  white-space: nowrap;
  transition: background-color var(--duration-base) var(--ease-brand);
}
.case__badge-icon {
  width: var(--size-button-icon-m);
  height: var(--size-button-icon-m);
  flex: none;
}

/* ── Заголовок кейса ─────────────────────────────────────────────────── */
/* H2/thin (500), выделенная часть — Semibold. Смысл выделения: цифра
   результата, ради которой карточка и существует.
   position — чтобы заголовок лежал ПОВЕРХ подложки: она позиционирована,
   а он нет, и без этого тинт затягивал бы текст */
.case__title {
  position: relative;
  margin: 0;
  text-align: center;
}
.case__title b { font-weight: var(--font-weight-heading); }

/* ── Медиа ───────────────────────────────────────────────────────────── */
/* Слот держит что угодно — картинку, видео, свёрстанный экран. Положение
   у каждого кейса своё и живёт в переменных рядом с палитрой */
.case__media {
  position: absolute;
  top: var(--case-media-top);
  left: var(--case-media-left);
  width: var(--case-media-width);
}
.case__media > * {
  display: block;
  width: 100%;
  height: auto;
}
/* У курсов картинка занимает всю ширину карточки и подходит вплотную
   к заголовку — сверху белая растушёвка на 106px (Figma 930:61843) */
.case--course .case__media::after {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: 106px;
  background: linear-gradient(to bottom, var(--color-surface), transparent);
}

/* ── Кнопка «Читать в блоге» ─────────────────────────────────────────── */
/* Базовые размеры от .btn--l (46 / padding 32 / radius 10 / P2 medium) —
   совпадают с макетом. Своё здесь только стекло: белый 70% под серой
   плёнкой Button/Secondary и размытие 8.
   Плёнка — анимируемое свойство-цвет: она лежит градиентным слоем поверх
   белой основы, а градиенты сами по себе не интерполируются */
@property --case-cta-film {
  syntax: "<color>";
  inherits: false;
  initial-value: transparent;
}

.case__cta {
  position: absolute;
  bottom: calc(var(--spacing-xl) - var(--size-stroke-m));
  left: 50%;
  --case-cta-film: var(--color-button-secondary);
  background-color: color-mix(in srgb, var(--color-surface) 70%, transparent);
  background-image: linear-gradient(var(--case-cta-film), var(--case-cta-film));
  backdrop-filter: blur(var(--blur-s));
  -webkit-backdrop-filter: blur(var(--blur-s));
  color: var(--color-text-primary);
  white-space: nowrap;
  opacity: 0;
  transform: translate(-50%, var(--spacing-xs));
  pointer-events: none;
  transition: opacity var(--duration-base) var(--ease-brand),
              transform var(--duration-base) var(--ease-brand),
              --case-cta-film var(--duration-fast) var(--ease-brand);
}
/* Наведение на саму кнопку — плёнка плотнее, как у Button/Secondary в ките.
   Белая основа под ней не меняется */
@media (hover: hover) {
  .case__cta:hover { --case-cta-film: var(--color-button-secondary-hover); }
}
.case__cta:active { --case-cta-film: var(--color-button-secondary-pressed); }
.case__cta-icon {
  width: var(--size-button-icon-m);
  height: var(--size-button-icon-m);
  flex: none;
}

/* ── Наведение ───────────────────────────────────────────────────────── */
/* Три изменения: всплывает кнопка, обводка уходит на ступень глубже
   (11 → 22, как у чипа в ките), подложка шапки становится ярче.

   Второе условие — :focus-visible на самой карточке: с клавиатуры она и
   есть ссылка, а кнопка внутри — просто подпись. :focus-within не годится,
   он держал бы подсветку и после клика мышью, когда курсор уже ушёл.

   [data-active] — это же состояние на телефоне, где курсора нет вовсе:
   его ставит cases.js той карточке, что стоит в центре кадра. Кнопка там
   стоит на всех карточках сразу (см. узкий экран ниже), а обводка и бейдж
   подсвечивают ту, что читают */
.case__card:focus-visible .case__cta {
  opacity: 1;
  transform: translate(-50%, 0);
  pointer-events: auto;
}
.case[data-active] .case__cta {
  opacity: 1;
  transform: translate(-50%, 0);
  pointer-events: auto;
}
@media (hover: hover) {
  .case:hover .case__cta {
    opacity: 1;
    transform: translate(-50%, 0);
    pointer-events: auto;
  }
}
.case__card:focus-visible {
  border-color: var(--case-wash-hover);
  --case-tint-current: var(--case-tint-hover);
}
.case[data-active] .case__card {
  border-color: var(--case-wash-hover);
  --case-tint-current: var(--case-tint-hover);
}
@media (hover: hover) {
  .case:hover .case__card {
    border-color: var(--case-wash-hover);
    --case-tint-current: var(--case-tint-hover);
  }
}
/* Бейдж и его крылышки уходят на ту же ступень, что и обводка */
.case__card:focus-visible .case__badge,
.case__card:focus-visible .case__tab::before,
.case__card:focus-visible .case__tab::after { background-color: var(--case-wash-hover); }
.case[data-active] .case__badge,
.case[data-active] .case__tab::before,
.case[data-active] .case__tab::after { background-color: var(--case-wash-hover); }
@media (hover: hover) {
  .case:hover .case__badge,
  .case:hover .case__tab::before,
  .case:hover .case__tab::after { background-color: var(--case-wash-hover); }
}

/* Логотип клиента лежит снаружи карточки, соседом — до него от фокуса
   внутри неё дотягиваемся только через :has на общем предке */
.case:has(.case__card:focus-visible) .case__client { color: var(--color-text-secondary); }
.case[data-active] .case__client { color: var(--color-text-secondary); }
@media (hover: hover) {
  .case:hover .case__client { color: var(--color-text-secondary); }
}

/* ── Логотип клиента ─────────────────────────────────────────────────── */
/* Бокс всегда 122×54 — от него зависит высота всей карточки. Внутри лежит
   логотип своего размера, отцентрованный. Обычно приглушён до Tertiary,
   на наведении темнеет до Secondary (Figma 937:1675) */
.case__client {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 122px;
  height: 54px;
  color: var(--color-text-tertiary);
  transition: color var(--duration-base) var(--ease-brand);
}
.case__client > svg { display: block; }

/* ── Промо-карточка ──────────────────────────────────────────────────── */
/* Figma 930:61977. Логотипа под ней нет, поэтому карточка кончается на 552
   и в ленте выравнивается по верху остальных */
.case--promo {
  justify-content: flex-start;
  /* --case-wash участвует в сокращении border, и без неё объявление
     становится невалидным — рамка пропадает целиком. Здесь она прозрачная:
     цвет обводки рисует градиент ниже */
  --case-wash: transparent;
  /* Наведение на промо-карточку ничего не меняет: живёт только кнопка,
     а её состояния приходят из кита вместе с .btn--primary */
  --case-wash-hover: transparent;
  --case-tint: transparent;
  --case-tint-hover: transparent;
}
/* Обводка промо — не заливка, а фирменный градиент Пущино (Figma: стиль
   Gradient/Dark/Pushchino на страйке 930:61977). Двумя фонами: белый по
   padding-box закрывает середину, градиент по border-box остаётся видным
   только в полосе обводки. border-image так не умеет — он игнорирует
   скругление, а карточка скруглена на 20 со сглаживанием */
/* Ступеней у градиента нет, поэтому наведение затемняет его третьим слоем
   между белой серединой и самим градиентом. Слой красится переменной, а не
   меняется целиком: background-image браузер не интерполирует и переход был
   бы скачком, а зарегистрированный цвет едет плавно — тот же приём, что у
   --case-tint-current выше */
@property --promo-veil {
  syntax: "<color>";
  inherits: false;
  initial-value: transparent;
}

.case--promo .case__card {
  justify-content: center;
  padding-block: var(--spacing-none);
  background:
    linear-gradient(var(--color-surface), var(--color-surface)) padding-box,
    linear-gradient(var(--promo-veil), var(--promo-veil)) border-box,
    var(--gradient-pushchino) border-box;
  /* Перебивает transition базовой карточки: border-color и --case-tint-current
     у промо прозрачные с обеих сторон, анимировать там нечего */
  transition: --promo-veil var(--duration-base) var(--ease-brand);
}
.case--promo .case__card:focus-visible {
  --promo-veil: var(--color-gradient-veil-hover);
}
@media (hover: hover) {
  .case--promo:hover .case__card {
    --promo-veil: var(--color-gradient-veil-hover);
}
}
.case__sticker {
  width: 128px;
  height: 128px;
  flex: none;
}
/* CTA промо: пилюля 56 с несимметричными паддингами — слева 32, справа 24,
   потому что справа стоит иконка и оптически прижимать её незачем */
.case__hero-cta {
  height: var(--size-button-hero);
  padding-inline: var(--spacing-xl) var(--spacing-l);
  border-radius: var(--radius-full);
  font-size: var(--text-p1);
  line-height: var(--text-p1--line-height);
  white-space: nowrap;              /* подпись не рвём на две строки */
}

/* ── Ряд табов ───────────────────────────────────────────────────────
   Figma 1014:11888. Только телефон: бейдж уезжает из карточки в общую
   ленту чипов над слайдером — по ней видно все типы кейсов сразу, и ею
   же слайдер листается. Сам чип берём из кита (.chip): размеры, шрифт и
   ступени наведения там уже есть. Цвет — из палитры своей карточки, а не
   свой набор переменных: источник один, и чип с карточкой не разъедутся */
.cases__tabs { display: none; }

.cases__tab {
  --chip-wash: var(--case-wash);
  --chip-wash-22: var(--case-wash-hover);
  --chip-wash-33: var(--case-wash-hover);
  flex: none;
  white-space: nowrap;                /* подпись не рвём на две строки */
}
/* Активный — та же ступень 22, что у карточки под наведением */
.cases__tab[aria-current="true"] { background: var(--case-wash-hover); }

/* Чип промо-карточки. Figma 1027:18603: одна синяя звёздочка на голубом,
   без подписи — 24 + иконка + 24. Палитры сущности у промо нет (её обводка
   рисуется градиентом), поэтому берём шкалу Info: тот же синий, что у
   кнопки «Создать проект» на самой карточке.
   Звёздочка вставлена разметкой и красится currentColor: маской по file://
   её не подключить (CORS — та же причина, что у --case-notch выше) */
.cases__tab--promo {
  --case-wash: var(--color-info-11);
  --case-wash-hover: var(--color-info-22);
  padding-inline: var(--spacing-l);
  color: var(--color-brand);
}

@media (prefers-reduced-motion: reduce) {
  .case__card, .case__badge, .case__client, .case__cta,
  .case__tab::before, .case__tab::after { transition: none; }
}

/* ── Узкий экран ─────────────────────────────────────────────────────
   Сцена высотой в экран: заголовок, ряд чипов и лента. Лента листается
   пальцем нативно, снап ставит карточку по центру кадра, ряд чипов идёт
   за ней — связь считает cases.js. Автоход на телефоне не запускается.

   До 11.09.2026 лента ехала вбок от вертикальной прокрутки страницы:
   секция была спейсером в шесть экранов, сцена залипала, положение вела
   арифметика в cases.js. На устройстве это отрабатывало рывками, и
   доводчик через window.scrollTo спорил с инерцией iOS.

   Ширина экрана раскладывается так (Figma 1014:9551, экран 380):
     12 (край соседа слева) + 16 (отступ) + карточка + 16 + 12 (край справа)
   отсюда карточка = 100vw − 56, а шаг ленты — те же 16.

   Поля ленты равны половине оставшегося экрана: только так крайние
   карточки доезжают до центра снапом, а не упираются в край.

   Высота карточки на телефоне макетной больше не держится: карточка
   занимает весь остаток экрана под заголовком и чипами. Так вся сцена
   помещается в кадр целиком — иначе на невысоких телефонах низ карточки
   с кнопкой уезжал бы за нижнюю кромку.

   Мокап при этом отвязан от верха и прижат к низу: доля --case-media-top
   считалась от макетной раскладки, где заголовок помещался в три строки,
   и на узкой карточке мокап полез бы под текст. Снизу же он и в макете
   упирается в край карточки */
@media (max-width: 767px) {
  /* Ширина ужимается только когда экрану действительно тесно. Голый
     100vw − 56 на планшете раздул бы карточку до 711 при макетных 402,
     а высота у неё фиксированные 552 — мокап позиционирован долями
     ширины и вылез бы за нижний край. min() держит макет там, где он
     помещается, и отдаёт ширину экрану там, где нет */
  .cases {
    --case-w: min(402px, calc(100vw - 56px));
    padding-block: var(--section-gap-half);
  }

  /* Сцена ровно в экран: заголовок, ряд чипов и карточка помещаются в кадр
     целиком. Высота из --vh — замер один раз (index.html), иначе панель
     Телеграма меняла бы её прямо под пальцем.

     Залипания и спейсера в шесть экранов здесь больше нет: лента ехала по
     прогрессу прокрутки страницы, и на устройстве это отрабатывало рывками.
     Убрано 11.09.2026 */
  .cases__stage {
    display: flex;
    flex-direction: column;
    block-size: var(--vh);
    padding-block: var(--section-gap-half);
    box-sizing: border-box;
  }

  /* Шаг под заголовком — обычный: сцене нужна вся оставшаяся высота */
  .cases__title { margin-bottom: var(--spacing-m); }

  .cases__tabs {
    display: flex;
    gap: var(--spacing-m);
    margin-bottom: var(--spacing-m);
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
    /* Запасное значение — первый чип встаёт над левым краем карточки.
       Настоящие поля считает cases.js: чтобы крайние чипы доезжали до
       центра, поле равно половине экрана без половины самого чипа, а
       ширины у них разные и CSS их не знает */
    padding-inline: calc((100vw - var(--case-w)) / 2);
  }
  .cases__tabs::-webkit-scrollbar { display: none; }

  /* Лента забирает остаток сцены, карточка тянется на всю её высоту.
     min-block-size снимает минимум flex-элемента по содержимому: без него
     лента не ужимается ниже своей карточки и вылезает за кромку экрана */
  .cases__viewport {
    flex: 1;
    min-block-size: 0;
    /* Лента листается пальцем нативно, снап ставит карточку по центру
       кадра. Горизонтальный overflow объявлен выше, в общем правиле;
       здесь только снап */
    scroll-snap-type: x mandatory;
  }
  .cases__line {
    gap: var(--spacing-m);
    padding-inline: calc((100vw - var(--case-w)) / 2);
    block-size: 100%;
  }
  .case { width: var(--case-w); scroll-snap-align: center; }
  /* Бейдж переехал наверх, в общий ряд табов — в карточке он больше не
     нужен. Верхнее поле, которое он держал собой, отдаём паддингу */
  .case__tab { display: none; }
  /* Кнопка не всплывает, а просто стоит на каждой карточке: наводить на
     телефоне нечем, и появление по центру кадра прятало бы её у соседей —
     ровно там, куда пользователь и собирается свайпнуть */
  .case__cta {
    opacity: 1;
    transform: translate(-50%, 0);
    pointer-events: auto;
    transition: --case-cta-film var(--duration-fast) var(--ease-brand);
  }

  /* Логотипа клиента на телефоне нет: в узкой ленте он читается хуже,
     чем занимает места. Освободившийся шаг снимаем и с самой карточки */
  .case__client { display: none; }
  .case__card {
    height: 100%;
    padding-top: calc(var(--spacing-xl) - var(--size-stroke-m));
  }
  .case__media {
    top: auto;
    bottom: var(--case-media-bottom, 0);
    /* Мокапу отдаём не больше трёх пятых карточки. Доля ширины, по которой
       он живёт в макете, на невысоком телефоне (карточка ужимается до
       экрана) давала мокап выше оставшегося места, и он наползал на
       заголовок — у курсов ещё и растушёвкой съедал строку. На просторных
       экранах ограничение не срабатывает: там мокап и так ниже */
    block-size: 60%;
    display: flex;
    align-items: flex-end;
    justify-content: center;
  }
  /* Обе оси auto с потолком по каждой: так картинка ужимается по тому
     ограничению, которое поймала первым, и сохраняет пропорции */
  .case__media > * {
    inline-size: auto;
    max-inline-size: 100%;
    block-size: auto;
    max-block-size: 100%;
  }
}

/* Когда карточка становится уже кнопки, первой уходит иконка: подпись
   важнее украшения, а рвать её на две строки нельзя.
   Арифметика: кнопка с иконкой — 217, без неё — 187. Внутри карточки
   доступно 100vw − 56 (её ширина) − 4 (обводка) − 60 (паддинги) =
   100vw − 120, отсюда иконка перестаёт помещаться на экране 337px.
   Своей ступени под это нет — забираем на общей 380 с запасом, там же,
   где ужимаются площадки в hero */
@media (max-width: 380px) {
  .case__hero-cta .case__cta-icon { display: none; }
}


/* ══════════════════════════════════════════════════════════════════════
   БЛОК «ПОЧЕМУ МЫ ЛУЧШИЕ»
   ══════════════════════════════════════════════════════════════════════ */

/* ==========================================================================
   Блок «Почему мы лучшие» — бенто из четырёх карточек.
   Figma: блок 930:61982, сетка 930:61985, карточки 930:61986 (логика),
   930:62042 (шесть в одном), 930:62073 (награда), 930:62078 (контекст).

   Цвета и отступы — токены дизайн-системы. Локально заведены только те
   значения, которых в токенах нет: градиенты иконок (Gradient/Dark/* из
   Figma), внутренние тени прорези и монетки, полупрозрачная рамка плитки.
   ========================================================================== */

:root {
  /* Figma: Gradient/Dark/* — заливки круглых иконок в карточке «Шесть в одном».
     В библиотеке токенов градиентов нет, поэтому лежат здесь */
  --why-grad-channel: linear-gradient(45deg, #fca140 0%, #ffcc00 100%);
  --why-grad-group:   linear-gradient(45deg, #45a54d 0%, #92c73d 100%);
  --why-grad-bizchat: linear-gradient(45deg, #eb6959 0%, #fe9b71 100%);
  --why-grad-bot:     linear-gradient(45deg, #4294ff 0%, #42c8d1 100%);
  --why-grad-miniapp: linear-gradient(45deg, #5ebab9 0%, #69cb67 100%);
  --why-grad-forum:   linear-gradient(180deg, #b58bf8 0%, #a862dd 100%);

  /* Внутренние тени: прорезь выглядит углублением, монетка — объёмной.
     Figma 905:54708 и 905:54707, отдельных токенов теней в ките нет */
  --why-hole-shadow: inset 0 8px 8px rgba(155, 206, 245, 0.35);
  --why-coin-shadow: inset 0 4px 4px rgba(186, 186, 186, 0.25);

  /* Шаг таблицы: ячейка 60 + зазор 16 */
  --why-cell: 60px;
  --why-step: 76px;
}

/* ── Блок ────────────────────────────────────────────────────────────── */
/* Поля добавлены к ширине блока, а не отъедены от неё: на широком экране
   сетка получает ровно макетные 1270, на узком — не липнет к краю */
.why {
  max-width: calc(1270px + 2 * var(--spacing-m));
  margin-inline: auto;
  /* Вертикальные поля раньше стояли на body. Сверху половина шага —
     вторую даёт блок кейсов, снизу половина — вторую даёт блок цифр */
  padding-block: var(--section-gap-half);
  padding-inline: var(--spacing-m);
}

.why__title {
  margin: 0 0 var(--spacing-xl);
  padding-inline: var(--spacing-xl);
}

/* Три колонки: логика занимает левую и обе строки, «шесть в одном» —
   верхнюю правую пару. Высоты строк из макета: 284 и 252 */
.why__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-rows: 284px 252px;
  gap: var(--spacing-xl);
}

/* ── Карточка ────────────────────────────────────────────────────────── */
.why-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-m);
  box-sizing: border-box;
  padding: var(--spacing-xl);
  border: var(--size-stroke-m) solid var(--color-info-05);
  border-radius: var(--radius-xl);
  background: var(--color-info-01);
}
/* Сглаживания углов у карточек этого блока нет: cornerSmoothing = 0 у всех
   четырёх (930:61986, 62042, 62073, 62078). Оно есть у табличек внутри
   «Шести в одном» — см. .six__tag-body */

.why-card__title,
.why-card__text {
  position: relative;               /* над фоновой графикой карточки */
  margin: 0;
  color: var(--color-text-primary);
}
.why-card__title { font-weight: var(--font-weight-heading); }
.why-card__text  { max-width: 320px; }

/* ══ 1. Единая логика ═══════════════════════════════════════════════════
   Figma 930:61986. Карточка на две строки, внутри — таблица шире её самой */
.why-card--logic {
  grid-row: span 2;
  overflow: hidden;
  border-color: var(--color-info-05);
  background: var(--color-info-05);
}

/* Таблица шире карточки (516 против 402) и центрирована: крайние
   мессенджеры срезаются краями — так в макете 930:61987 */
.logic__table {
  position: absolute;
  bottom: 14px;
  left: 50%;
  translate: -50% 0;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-m);
}

.logic__grid,
.logic__apps {
  display: grid;
  grid-template-columns: repeat(7, var(--why-cell));
  gap: var(--spacing-m);
}

/* Плитки лежат над слоем монеток — клиент влетает внутрь приёмника, а не
   накрывает его собой */
.logic__apps {
  position: relative;
  z-index: 1;
}

.logic__hole {
  width: var(--why-cell);
  height: var(--why-cell);
  border-radius: var(--radius-full);
  background: var(--color-info-11);
  box-shadow: var(--why-hole-shadow);
}

/* ── Приёмники ───────────────────────────────────────────────────────── */
/* Сглаживания углов здесь нет: у плиток в макете cornerSmoothing = 0,
   в отличие от карточек лендинга — обычная дуга */
.logic__app {
  position: relative;
  display: grid;
  place-items: center;
  box-sizing: border-box;
  width: var(--why-cell);
  height: var(--why-cell);
  border: var(--size-stroke-m) solid transparent;
  border-radius: var(--radius-xl);
}
/* Подключённый канал. Figma 930:62027: белая заливка, поверх неё растяжка
   Info/33 на 20% ко дну, обводка 2px внутрь — и она тоже градиентная, от
   почти прозрачного Info/05 сверху к плотному Info/22 снизу.
   Белых слоёв два: нижний (цвет фона) подкладывается под обводку — она
   полупрозрачна сверху и должна ложиться на плитку, а не на голубой фон
   карточки; верхний обрезан по padding-box и прячет обводку внутри */
.logic__app--tile {
  background-color: var(--color-surface);
  background-image:
    linear-gradient(180deg, transparent 0%, color-mix(in srgb, var(--color-info-33) 20%, transparent) 100%),
    linear-gradient(180deg, var(--color-surface) 0%, var(--color-surface) 100%),
    linear-gradient(180deg, color-mix(in srgb, var(--color-info-05) 40%, transparent) 0%, var(--color-info-22) 100%);
  /* origin, а не только clip: иначе градиент обводки считается от padding-box
     и в самой рамке отрисовывается концом соседнего тайла — вверх ногами */
  background-origin: padding-box, padding-box, border-box;
  background-clip: padding-box, padding-box, border-box;
}

/* Ещё не подключённый канал. Figma 1034:19120 — заливки нет, обводка та же,
   что верхний край у подключённой плитки: Info/05 на 40% */
.logic__app--soon {
  border-color: color-mix(in srgb, var(--color-info-05) 40%, transparent);
}

/* Глобус сидит в сером кружке — Figma 930:62029 */
.logic__globe {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border-radius: var(--radius-full);
  background: var(--color-neutral-100);
}

/* Вспышка: приёмник на мгновение заливается цветом упавшей монетки.
   Цвет приходит скриптом в --why-flash. Заливка идёт под логотипом —
   иконка на пике вспышки должна остаться видна */
.logic__app > * { position: relative; z-index: 1; }
.logic__app::after {
  content: '';
  position: absolute;
  inset: calc(var(--size-stroke-m) * -1);
  border-radius: inherit;
  background: var(--why-flash, transparent);
  opacity: 0;
  pointer-events: none;
}
.logic__app[data-flash]::after { animation: logic-flash 420ms ease-out; }

@keyframes logic-flash {
  0%   { opacity: 0; }
  25%  { opacity: 1; }
  100% { opacity: 0; }
}

/* Метка на правом верхнем углу плитки. Figma 1034:19200 — Badge:
   Info/22 под текстом Info/01, P4/bold. Стоит ниже правила «.logic__app > *»
   намеренно: у них равная специфичность, и position должен остаться absolute.
   Заглавные из CSS, чтобы скринридер читал слово, а не аббревиатуру */
.logic__soon {
  position: absolute;
  top: -6px;
  right: -10px;
  padding: var(--spacing-xxxs) var(--spacing-xs);
  border-radius: var(--radius-full);
  background: var(--color-info-22);
  color: var(--color-info-01);
  text-transform: uppercase;
  white-space: nowrap;
}

/* ── Монетки ─────────────────────────────────────────────────────────── */
/* Слой во всю таблицу, замаскированный сеткой круглых отверстий с тем же
   шагом 76: клиент виден только в прорезях, а между рядами уходит за
   панель — как фишка в «четыре в ряд». Последний ряд отверстий приходится
   ровно на приёмники, поэтому приземление видно целиком */
.logic__coins {
  position: absolute;
  inset: 0;
  mask-image: radial-gradient(circle at 30px 30px, #000 29.5px, transparent 30px);
  mask-size: var(--why-step) var(--why-step);
  -webkit-mask-image: radial-gradient(circle at 30px 30px, #000 29.5px, transparent 30px);
  -webkit-mask-size: var(--why-step) var(--why-step);
  pointer-events: none;
}

/* Клиент падает по своей колонке, гаснет в приёмнике и снимается со сцены.
   Колонка и цвет приходят скриптом; --why-drop — путь до ряда приёмников */
.logic__coin {
  position: absolute;
  top: 0;
  left: calc(var(--why-col) * var(--why-step));
  display: grid;
  place-items: center;
  width: var(--why-cell);
  height: var(--why-cell);
  border-radius: var(--radius-full);
  background: var(--why-coin, var(--color-info-11));
  box-shadow: var(--why-coin-shadow);
  animation: logic-drop var(--why-fall, 1000ms) forwards;
}
.logic__coin img {
  width: 44px;
  height: 44px;
}

@keyframes logic-drop {
  /* Разгон под гравитацию: медленно трогается, в приёмник влетает быстро */
  0%   { translate: 0 calc(var(--why-step) * -1); opacity: 0; animation-timing-function: cubic-bezier(0.5, 0, 0.75, 0); }
  6%   { opacity: 1; }
  76%  { translate: 0 var(--why-drop); scale: 1; opacity: 1; animation-timing-function: ease-out; }
  85%  { translate: 0 calc(var(--why-drop) - 5px); }
  100% { translate: 0 var(--why-drop); scale: 0.55; opacity: 0; }
}

/* Растушёвка под текстом. Figma 930:62037: сплошная полоса 163, под ней
   градиент 194 — таблица утекает под заголовок, а не режется линией */
.logic__wash {
  position: absolute;
  inset: calc(var(--size-stroke-m) * -1) calc(var(--size-stroke-m) * -1) auto;
  height: 357px;
  background:
    linear-gradient(var(--color-info-01) 0 163px, transparent 163px),
    linear-gradient(180deg, var(--color-info-01) 45.66%, transparent 100%);
  pointer-events: none;
}

/* ══ 2. Шесть в одном ═══════════════════════════════════════════════════
   Figma 930:62042. Таблички выходят за правый край — карточка не режет.
   user-select снят на всей карточке: протяжка по табличкам иначе выделяет
   заголовок и подписи. Префикс нужен — Safari без него правило не видит */
.why-card--six {
  grid-column: span 2;
  -webkit-user-select: none;
  user-select: none;
  background:
    linear-gradient(111.26deg, transparent 40.52%, var(--color-info-11) 100%),
    var(--color-info-01);
}

/* Поле разлёта. Figma 930:62045 — 435×301, прижато к нижнему правому углу.
   Координаты табличек в px, поэтому под узкую карточку поле не перестраивается,
   а сжимается целиком: --k считает скрипт, точка опоры — тот угол, к которому
   поле прижато, чтобы правая обрезка осталась макетной */
.six__field {
  position: absolute;
  right: -3px;
  bottom: 1px;
  width: 435px;
  height: 301px;
  scale: var(--k, 1);
  transform-origin: 100% 100%;
}

/* --x/--y — центр таблички из макета, --dx/--dy/--dr — сдвиг от хлопка и
   перетаскивания (скрипт). Поворот на вложенном элементе: иначе мешал бы сдвигу */
.six__tag {
  position: absolute;
  left: var(--x);
  top: var(--y);
  translate: calc(-50% + var(--dx, 0px)) calc(-50% + var(--dy, 0px));
}

/* Хвататься есть за что только там, где работает физика: тот же признак, что
   в why.js. На телефоне и планшете таблички неподвижны — макет 1031:18761 */
@media (min-width: 1280px) and (hover: hover) {
  .six__tag { cursor: grab; }
  /* Атрибут ставит скрипт на время захвата. Курсор меняется на самой табличке:
     пока идёт pointer capture, стиль под курсором берётся именно с неё */
  .six__tag[data-held] { cursor: grabbing; }
}

/* Обводка в макете идёт наружу (strokeAlign OUTSIDE, Figma 930:62057),
   поэтому box-sizing здесь content-box: 2px ложатся поверх габарита,
   а не съедают паддинг */
.six__tag-body {
  display: flex;
  gap: var(--spacing-xs);
  align-items: center;
  box-sizing: content-box;
  padding: var(--spacing-xs) var(--spacing-m);
  border: var(--size-stroke-m) solid var(--color-info-05);
  border-radius: calc(var(--radius-l) * var(--corner-scale));
  corner-shape: var(--corner-smooth);
  background: var(--color-surface);
  rotate: calc(var(--r) + var(--dr, 0deg));
  white-space: nowrap;
}
/* Знак puzzlebot — квадратная плитка без подписи. Figma 930:62050.
   В макете залита #268cff; на лендинге синий один — Primary/Main/Brand */
.six__tag-body--brand {
  justify-content: center;
  width: 54px;
  height: 54px;
  padding: 0;
  border: none;
  background: var(--color-brand);
}

.six__icon {
  display: grid;
  place-items: center;
  flex: none;
  width: var(--size-button-m);
  height: var(--size-button-m);
  border-radius: var(--radius-full);
}
.six__icon--channel { background: var(--why-grad-channel); }
.six__icon--group   { background: var(--why-grad-group); }
.six__icon--bizchat { background: var(--why-grad-bizchat); }
.six__icon--bot     { background: var(--why-grad-bot); }
.six__icon--miniapp { background: var(--why-grad-miniapp); }
/* Единственная иконка со скруглением вместо круга. Figma 930:62070 */
.six__icon--forum   { background: var(--why-grad-forum); border-radius: var(--radius-l); }

.six__label {
  font-family: var(--font-heading);
  font-weight: var(--font-weight-heading-thin);
  color: var(--color-text-primary);
}

/* ══ 3. Награда ═════════════════════════════════════════════════════════
   Figma 930:62073 */
.why-card--award {
  overflow: hidden;
  border-color: var(--entity-game-wash);
  background: var(--color-surface);
  transition: border-color var(--duration-base) var(--ease-brand);
}
/* Обводка уходит на ступень глубже по своей шкале (11 → 22) — то же
   правило, что у карточек кейсов. Звезда в это время крутится: см. why.js */
@media (hover: hover) {
  .why-card--award:hover { border-color: var(--entity-game-wash-22); }
}

/* Звезда вылезает за нижний правый угол и обрезается карточкой.
   Угол поворота ставит скрипт — крутится, пока курсор внутри */
.award__star {
  position: absolute;
  right: -83px;
  bottom: -120px;
  width: 370px;
  height: 370px;
  object-fit: contain;
  pointer-events: none;
}

.award__logo {
  position: absolute;
  right: 18.67px;
  bottom: 18.35px;
  width: 166.33px;
  height: 93.65px;
  pointer-events: none;
}

.award__title {
  flex: 1;
  align-content: start;
}
.award__accent { color: var(--entity-game-accent); }

/* Link Button, размер M. Kit 2.0 4883:3194 — gap 4, радиус 8, текст P1/medium,
   иконка в боксе 16. Подчёркивания у компонента нет ни в одном состоянии:
   Default → Hover → Pressed меняют только цвет, ступень 100 → 120.
   Здесь он набран палитрой Action, а не ссылочной синей (Figma 930:62077) */
/* z-index вместо position: relative — по той же причине, что у ссылок блока
   поддержки: позиционированная ссылка стала бы системой координат для ::after */
.award__link {
  z-index: 1;
  display: inline-flex;
  gap: var(--spacing-xxs);
  align-items: center;
  align-self: start;
  border-radius: var(--radius-lg);
  color: var(--entity-game-accent);
  text-decoration: none;
}
/* Кликабельна вся карточка */
.award__link::after {
  content: "";
  position: absolute;
  inset: 0;
}
.award__link:active { color: var(--entity-game-accent-120); }
@media (hover: hover) {
  .award__link:hover { color: var(--entity-game-accent-120); }
}

/* Бокс иконки 16, стрелка внутри — 9.5. Figma 930:62077 */
.award__link-icon {
  display: grid;
  place-items: center;
  width: var(--size-button-icon-s);
  height: var(--size-button-icon-s);
}

/* ══ 4. Всегда в контексте ══════════════════════════════════════════════
   Figma 930:62078 */
.why-card--context {
  gap: var(--spacing-xl);
  overflow: hidden;
}

.context__emoji {
  width: 64px;
  height: 64px;
}
.context__emoji svg { display: block; }

.context__text {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-m);
}
.context__text .why-card__text { max-width: none; }

/* ── Планшет. Figma 1030:18618 ───────────────────────────────────────── */
/* Две колонки: слева логика и «всегда в контексте», справа награда и
   «шесть в одном». Высоты из макета — низкие карточки 252, высокие 500,
   поэтому средний ряд 216: 216 + 32 (шаг) + 252 = 500.
   minmax вместо жёстких строк: на 768 карточка сужается до ~336 и текст
   набирает лишние строки — ряд растёт, колонки остаются вровень */
@media (max-width: 1279px) {
  .why__grid {
    gap: var(--spacing-m);
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: minmax(252px, auto) minmax(216px, auto) minmax(252px, auto);
  }
  .why-card--logic   { grid-area: 1 / 1 / 3 / 2; }
  .why-card--context { grid-area: 3 / 1 / 4 / 2; }
  .why-card--award   { grid-area: 1 / 2 / 2 / 3; }
  /* Таблички выходят за правый край карточки — здесь она их режет */
  .why-card--six     { grid-area: 2 / 2 / 4 / 3; overflow: hidden; }

  /* Карточка ниже десктопной (500 против 568), таблица поднимается вместе
     с ней: в макете от её низа до края карточки 16 (1030:18623) */
  .logic__table { bottom: 16px; }
}

/* ── Мобильный. Figma 1031:18761 ─────────────────────────────────────── */
/* Одна колонка, порядок макетный: логика, награда, шесть в одном, контекст.
   У логики и «шести в одном» текст сверху, графика снизу — так они и
   собраны, разница только в высоте карточки: 480 вместо 500 */
@media (max-width: 767px) {
  .why__grid {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: none;
  }
  .why-card { grid-area: auto; }

  .why-card--logic   { order: 1; min-height: 480px; }
  .why-card--award   { order: 2; min-height: 252px; }
  .why-card--six     { order: 3; min-height: 480px; }
  .why-card--context { order: 4; min-height: 252px; }
}

/* ── Без анимации ────────────────────────────────────────────────────── */
/* Монетки и разлёт снимает скрипт; здесь гасим то, что живёт в CSS */
@media (prefers-reduced-motion: reduce) {
  .logic__app[data-flash],
  .logic__app[data-flash]::after,
  .logic__coin { animation: none; }
}


/* ══════════════════════════════════════════════════════════════════════
   ЦИФРЫ ПЛАТФОРМЫ
   ══════════════════════════════════════════════════════════════════════ */

/* Figma 930:62085. Три колонки 1fr с шагом 32 внутри сетки 1270,
   поля 64 по краям сетки — те же, что у заголовка «Почему мы лучшие».
   Цифра и подпись стоят по центру колонки.

   Поля блока собраны как у соседей: по половине шага сверху и снизу,
   вторые половины вносят «Почему мы лучшие» и «Лучше, чем Вайбкодинг» */
.stats {
  max-width: calc(1270px + 2 * var(--spacing-m));
  margin-inline: auto;
  padding-block: var(--section-gap-half);
  padding-inline: var(--spacing-m);
}

.stats__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--spacing-xl);
  padding-inline: var(--spacing-xxl);
  text-align: center;
}

.stats__item {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xs);
}

.stats__value { color: var(--color-info); }

/* Подпись держит макетные 260 (Figma 1031:19047 — текст 260) и стоит
   по центру колонки: на всю ширину она вытянулась бы в одну строку.
   Ширину делим поровну между строками — balance, иначе на вторую строку
   свисает одно слово */
.stats__label {
  max-width: 260px;
  margin-inline: auto;
  text-wrap: balance;
}

/* ── Узкий экран ─────────────────────────────────────────────────────── */
/* Планшет — те же три колонки, только теснее: шаг 16, как у карточек
   соседних блоков на узких ступенях.
   Поля сетки — в ступень ниже макетных: 64 по краям на 768 съели бы треть
   строки */
@media (max-width: 1279px) {
  .stats__grid {
    gap: var(--spacing-m);
    padding-inline: var(--spacing-xl);
  }
}

/* Телефон (Figma 1031:19047) — три колонки не встают, блоки идут столбиком:
   цифра, под ней подпись в две строки, всё по центру */
@media (max-width: 767px) {
  .stats__grid {
    /* Полей у сетки нет: 16 по краям блок уже добрал сам,
       и текст встаёт на макетные 16 от края экрана */
    padding-inline: 0;
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }
  .stats__item { padding-block: var(--spacing-l); }
  /* Разделитель только между блоками — над первым и под последним линии нет.
     Полоса 2, к обоим концам растворяется: так же нарисованы линии
     в Figma 1058:19325 */
  .stats__item + .stats__item {
    background: linear-gradient(
      90deg,
      transparent 0%,
      var(--color-divider) 51%,
      transparent 100%
    ) top / 100% var(--size-stroke-m) no-repeat;
  }
}



/* ══════════════════════════════════════════════════════════════════════
   ЛУЧШЕ, ЧЕМ ВАЙБКОДИНГ
   ══════════════════════════════════════════════════════════════════════ */

/* Три макета, один порядок разметки:

     десктоп  930:62220  — 1270×354. Лид занимает левую колонку целиком,
                           четыре карточки встают сеткой 2×2 справа.
     планшет  1061:19329 — лид на левую треть, карточки лентой в правых
                           двух третях. Лид прилипает к шапке и стоит,
                           пока лента идёт мимо.
     телефон  1061:19597 — лид шапкой блока, тоже прилипает, карточки
                           уезжают под него. Прокрутка идёт и пальцем по
                           самому лиду: он приклеен, а не вынут в
                           отдельный скроллер.

   Ячейка — карточка с паддингом 32, заливки у неё нет: блок держится на
   линиях в 2px, а не на плитках.

   Поля блока: по половине шага сверху и снизу — соседи вносят свои
   половины сами (цифры сверху, «Создавайте, мы поддержим» снизу) */
:root {
  /* Бокс эмодзи. Figma 930:62253 — 24. В ките ступени 24 нет:
     Button/XS сразу 22, Button Card на лендинге 40 */
  --better-mark: 24px;
  /* Густота заливки под курсором. Figma 930:62223 — заливка цветом
     Basic/Divider под прозрачностью 40%; здесь прозрачность сложена
     в сам цвет */
  --better-wash-tint: color-mix(in srgb, var(--color-divider) 40%, transparent);
}

.better {
  max-width: calc(1270px + 2 * var(--spacing-m));
  margin-inline: auto;
  padding-block: var(--section-gap-half);
  padding-inline: var(--spacing-m);
}

/* Колонки заполняются сверху вниз, поэтому карточки в разметке идут
   парами по столбцам. Высоты рядов не заданы: в макете 176 — это высота
   самой длинной карточки ряда. Зато ряд общий на обе колонки, и линии
   внутри них встают на одной высоте */
.better__grid {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-template-rows: repeat(2, auto);
  grid-auto-flow: column;
}

/* ── Линии между колонками ───────────────────────────────────────────── */
/* Figma 930:62246 и 930:62282: полоса 2, к обоим концам растворяется */
.better__rule {
  position: absolute;
  z-index: 1;                       /* поверх заливки ячейки, иначе гаснет под ней */
  inset-block: 0;
  width: var(--size-stroke-m);
  translate: -50% 0;
  pointer-events: none;             /* полоса в 2px не должна отбирать наведение */
  background: linear-gradient(
    180deg,
    transparent 0%,
    var(--color-divider) 51%,
    transparent 100%
  );
}
.better__rule--first  { left: calc(100% / 3); }
.better__rule--second { left: calc(200% / 3); }

/* ── Ячейка ──────────────────────────────────────────────────────────── */
/* Эмодзи слева, довод и строка «Не нужно…» друг под другом справа от
   него. В макете под минусом стоит круг с opacity 0 (930:62258) — место
   он держит, видно его никогда, поэтому в разметке его нет: то же поле
   даёт отступ слева у текстовой колонки */
.better-card {
  position: relative;
  display: flex;
  gap: var(--spacing-m);
  align-items: flex-start;
  box-sizing: border-box;
  padding: var(--spacing-xl);
}

.better-card__text {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-m);
  min-width: 0;
}

.better-card__title {
  color: var(--color-text-primary);
}

.better-card__desc {
  color: var(--color-text-primary);
}

/* Строка «Не нужно…»: серая, стилем P1 — в макете Text/Secondary
   и кегль абзаца, а не довода (930:62262) */
.better-card__note {
  color: var(--color-text-secondary);
}

/* ── Лид-ячейка ──────────────────────────────────────────────────────── */
/* Заголовок блока живёт внутри сетки: отдельного заголовка над ней нет.
   Единственная ячейка, где содержимое стоит по центру — в макете текст
   на 79 от верха при высоте 354 и содержимом 196 (930:62222) */
.better-card--lead {
  flex-direction: column;
  grid-row: 1 / span 2;
  justify-content: center;
}

/* Текст лида отбит от паддинга ячейки ещё на шаг сверху и снизу: в макете
   он лежит в своей рамке с полями 16 (930:62225, 1061:19334, 1061:19601).
   Он же — то, что прилипает на планшете, поэтому отдельным узлом */
.better-card__lead-text {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-m);
  padding-block: var(--spacing-m);
}

.better-card__lead-title {
  color: var(--color-text-primary);
}

/* ── Эмодзи ──────────────────────────────────────────────────────────── */
/* Ролик приходит по требованию и рисуется в svg внутри этого бокса.
   Размеры на самом контейнере, чтобы до загрузки строка не прыгала */
.better-card__emoji {
  flex: none;
  width: var(--better-mark);
  height: var(--better-mark);
}
.better-card__emoji svg {
  display: block;
}

/* ── Линия между рядами ──────────────────────────────────────────────── */
/* По низу ячейки. Ячейка знает только направление своей растушёвки, сама
   полоса у всех одна. Две линии-колонки стоят в разметке первыми, поэтому
   ячейки идут с третьего ребёнка: 3 — лид, 4 и 5 — средняя колонка,
   6 и 7 — правая. На десктопе линия нужна только верхнему ряду */
.better-card::after {
  content: none;
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  height: var(--size-stroke-m);
  background: var(--better-hrule);
}
.better-card:nth-child(4)::after,
.better-card:nth-child(6)::after { content: ''; }

/* У средней колонки линия сплошная (930:62264), у правой гаснет к
   внешнему краю блока (930:62301) */
.better-card {
  --better-hrule: var(--color-divider);
}
.better-card:nth-child(6),
.better-card:nth-child(7) {
  --better-hrule: linear-gradient(90deg, var(--color-divider) 0%, transparent 100%);
}

/* ── Заливка под курсором ────────────────────────────────────────────── */
/* В макете все заливки скрыты: статика блока — только линии. Заливка
   остаётся состоянием наведения и живёт лишь там, где есть курсор.

   Отдельным слоем, а не фоном самой ячейки: так она гаснет и загорается
   плавно — background-image переходами не берётся. Градиент собран прямо
   здесь, а не переменной в :root: var() внутри переменной подставляется
   там, где переменная объявлена, и угол ячейки до неё бы не доехал */
@media (min-width: 1280px) and (hover: hover) {
  .better-card::before {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(
      var(--better-angle),
      transparent 8.95%,
      var(--better-wash-tint) 91.05%
    );
    opacity: 0;
    transition: opacity var(--duration-base) var(--ease-brand);
  }
  @media (hover: hover) {
    .better-card:hover::before { opacity: 1; }
  }

  /* Содержимое над заливкой: без своего слоя оно осталось бы под ней */
  .better-card > * { position: relative; }

  /* Угол — по месту ячейки в сетке: каждая светит в середину блока.
     Средняя колонка 171.08° и 8.92° макетные (930:62223 и 1037:19236),
     правая — их зеркала по вертикальной оси. Лид занимает обе строки,
     и середина блока у него ровно справа */
  .better-card--lead        { --better-angle: 90deg; }
  .better-card:nth-child(4) { --better-angle: 171.08deg; }
  .better-card:nth-child(5) { --better-angle: 8.92deg; }
  .better-card:nth-child(6) { --better-angle: 229.46deg; }
  .better-card:nth-child(7) { --better-angle: 310.54deg; }
}

/* ── Планшет ─────────────────────────────────────────────────────────── */
/* Figma 1061:19329: лид на левую треть, четыре карточки одна под другой
   в правых двух третях, между ними линии */
@media (min-width: 768px) and (max-width: 1279px) {
  .better__grid {
    grid-template-columns: 1fr 2fr;
    grid-template-rows: none;
    grid-auto-flow: row;
  }

  /* Лид перекрывает все четыре ряда карточек, а прилипает внутри него
     сам текст — так он и «следует» по блоку до его конца */
  .better-card--lead {
    grid-row: 1 / span 4;           /* карточек в разметке ровно четыре */
  }

  /* Текст стоит у верха полосы, цепляется за середину окна и уходит
     вместе с ней вниз до конца полосы — путь сверху донизу.

     Из top вычитается половина высоты текста: без неё по середине окна
     встал бы его верхний край. Своей высоты у sticky в CSS не спросить
     (проценты в inset считаются от окна), поэтому её замеряет better.js
     и кладёт в --better-lead-h. Скрипта нет — текст просто цепляется
     верхним краем, блок остаётся читаемым */
  .better-card--lead {
    justify-content: flex-start;
  }
  .better-card__lead-text {
    position: sticky;
    flex: none;
    top: calc(50svh - var(--better-lead-h, 0px) / 2);
  }

  /* Колонок две, и делит их одна линия — по границе трети */
  .better__rule--second { display: none; }

  /* Линия теперь под каждой карточкой, кроме последней. Лид стоит
     отдельной колонкой, ему линия не нужна */
  .better-card::after { content: ''; }
  .better-card--lead::after,
  .better-card:last-child::after { content: none; }

  /* Все линии в одной колонке — гаснут к правому краю блока
     (1061:19485). Селектор с родителем не для красоты: без него правило
     слабее колоночных :nth-child выше */
  .better__grid .better-card {
    --better-hrule: linear-gradient(90deg, var(--color-divider) 0%, transparent 100%);
  }
}

/* ── Телефон ─────────────────────────────────────────────────────────── */
/* Figma 1061:19597: лид шапкой блока, карточки лентой под ним */
@media (max-width: 767px) {
  .better__grid {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: none;
    grid-auto-flow: row;
  }
  .better__rule { display: none; }

  /* Боковых полей у ячейки нет: в макете текст идёт от края блока
     (1061:19625), поля даёт сама секция */
  .better-card {
    padding-inline: 0;
  }

  /* Лид идёт первой ячейкой в потоке. Заливка тут не состояние наведения,
     а фон: снизу по центру радиальная растушёвка из макета (1061:19599:
     211×260 от точки 212,198 при ячейке 423×198, сверху прозрачность 40%).

     До 11.09.2026 лид приклеивался к шапке, а карточки уезжали под него —
     под ним же лежала полоса фона, закрывавшая место уехавшей шапки.
     Залипание убрано вместе с остальной анимацией по прокрутке, и полоса
     вместе с ним: закрывать ей нечего */
  .better-card--lead {
    grid-row: auto;
    background:
      radial-gradient(50% 131% at 50% 100%, var(--better-wash-tint) 0%, transparent 86.8%),
      var(--color-bg-primary);
  }

  /* Линия под каждой ячейкой, кроме последней, — под лидом тоже
     (1061:19623), и уезжает вместе с ним. Оба края блока теперь по
     бокам линии, поэтому гаснет к обоим (1061:19640) */
  .better-card::after { content: ''; }
  .better-card:last-child::after { content: none; }
  .better__grid .better-card {
    --better-hrule: linear-gradient(
      90deg,
      transparent 0%,
      var(--color-divider) 50%,
      transparent 100%
    );
  }
}


/* ══════════════════════════════════════════════════════════════════════
   СОЗДАВАЙТЕ, МЫ ПОДДЕРЖИМ
   ══════════════════════════════════════════════════════════════════════ */

/* Figma 930:62320 — бенто из пяти карточек.

   Раскладка повторяет блок «Почему мы лучшие»: та же ширина контейнера,
   тот же заголовок с отступом 32 по краям, тот же шаг сетки. Отличие
   одно — строк три, и высоты у них из макета: 196, 60, 196. Странная
   средняя строка не ошибка: на неё заходят две высокие карточки
   (сообщество сверху, «Соберём за вас» снизу), и 196 + 32 + 60 даёт их
   макетные 288 */
.support {
  max-width: calc(1270px + 2 * var(--spacing-m));
  margin-inline: auto;
  /* Сверху половина шага, вторую даёт блок над ним; снизу так же */
  padding-block: var(--section-gap-half);
  padding-inline: var(--spacing-m);
}

.support__title {
  margin: 0 0 var(--spacing-xl);
  padding-inline: var(--spacing-xl);
  color: var(--color-text-primary);
  font-family: var(--font-heading);
  font-weight: var(--font-weight-heading);
}

.support__grid {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  /* 200, хотя в макете 196: Figma рисует обводку внутрь фрейма и место под
     неё не отводит, поэтому её 32 паддинга отсчитаны от внешнего края.
     В CSS обводка занимает свои 2 сверху и снизу до паддинга — те же
     поля и тот же текст дают карточку на 4 выше. Ужимать паддинг до 30
     не стали: 32 стоит у всех карточек лендинга.
     minmax, а не 200 намертво: длиннее текст — карточка вырастет, а не
     обрежет строку */
  grid-template-rows: minmax(200px, auto) 60px minmax(200px, auto);
  gap: var(--spacing-xl);
}

/* ── Карточка ────────────────────────────────────────────────────────── */
/* База — .card из дизайн-системы: паддинг 32, обводка 2, скругление 20.
   Здесь только раскладка содержимого и место каждой карточки в сетке */
.support-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-m);
  box-sizing: border-box;
  /* Графика карточек выходит за их края: сетка аватарок вправо, сердце
     вниз и вправо. Режет их карточка */
  overflow: hidden;
  /* Наведение уводит обводку глубже по её собственной шкале. Трём карточкам
     из пяти обводка достаётся от .card — там это Berlin/22, и на белом фоне
     соседняя ступень не читается, поэтому взята Berlin/56. Двум цветным
     ступень переопределена ниже: на тинте хватает и соседней */
  --support-hover-border: var(--color-neutral-56);
  transition: border-color var(--duration-base) var(--ease-brand);
}
@media (hover: hover) {
  .support-card:hover { border-color: var(--support-hover-border); }
}

.support-card__title,
.support-card__text {
  position: relative;               /* над фоновой графикой карточки */
  margin: 0;
  color: var(--color-text-primary);
}
.support-card__title { font-weight: var(--font-weight-heading); }

.support-card__text { max-width: 320px; }

/* У двух карточек из пяти текст тянется на весь остаток, и ссылка от этого
   прижимается к низу: в макете они выше своего содержимого (930:62326,
   930:62368). Остальным трём высоты ровно по содержимому, и растягивать
   там нечего — на планшете, где карточка догоняет соседку по строке,
   ссылка осталась бы висеть у нижнего края в одиночестве */
.support-card--community .support-card__text,
.support-card--build .support-card__text { flex: 1; }

/* z-index, а не position: relative — ссылка прямой ребёнок flex-контейнера,
   и слой ей поднимает сам z-index. Позиционировать её нельзя: тогда ::after
   ниже растянулся бы по ней самой, а не по карточке */
.support-card__link { z-index: 1; align-self: start; }
/* Кликабельна вся карточка: ссылка кладёт поверх неё прозрачный слой.
   Отдельной обёртки не заводим — она бы задвоила ссылку для скринридера */
.support-card__link::after {
  content: "";
  position: absolute;
  inset: 0;
}
/* Бокс иконки 16, стрелка внутри 9.5 — Link Button, Figma 930:62360 */
.support-card__link-icon { display: grid; place-items: center; }

/* ══ 1. Активное сообщество. Figma 930:62324 ═══════════════════════════
   Две колонки и две строки. Ссылка набрана палитрой Action, а не
   ссылочной синей — как и в карточке награды блока «Почему мы лучшие» */
.support-card--community {
  grid-area: 1 / 1 / 3 / 3;
  border-color: var(--entity-game-wash);
  background: var(--entity-game-tint);
  --link-color: var(--entity-game-accent);
  --support-hover-border: var(--entity-game-wash-22);
}
.support-card--community .support-card__link:active { --link-color: var(--entity-game-accent-120); }
@media (hover: hover) {
  .support-card--community .support-card__link:hover { --link-color: var(--entity-game-accent-120); }
}

/* Сетка аватарок. Шесть в ряд по 52 с шагом 16 — 392 из отведённых 396.
   Стоит по вертикальной середине карточки и на 23 правее её края:
   правый столбец срезан, и сетка читается как продолжение за карточкой.

   Высота — ровно пять рядов, и всё, что ниже, сетка обрезает. Ячеек в
   разметке шестьдесят, по одной на картинку: сколько их встанет в ряд,
   решает ширина, а лишние ждут за нижним краем. Иначе при каждой новой
   ширине последний ряд оказывался бы неполным и в углу зияла дыра */
.crowd {
  --crowd-swap: 620ms;              /* вся подмена: сжатие и рост обратно */

  position: absolute;
  top: 50%;
  right: -23px;
  display: flex;
  overflow: hidden;
  flex-wrap: wrap;
  gap: var(--spacing-m);
  width: 396px;
  height: 324px;                    /* 5 рядов по 52 с шагом 16 */
  transform: translateY(-50%);
}

.crowd__cell {
  width: 52px;
  height: 52px;
}
/* Скругление на самой картинке, а не на ячейке-обёртке: в подмене
   картинка уменьшается, и круглая маска ячейки перестала бы её задевать —
   вместо кружка сжимался бы квадрат */
.crowd__cell img {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: var(--radius-full);
  object-fit: cover;
}

/* Подмена: старый уходит в точку, новый вырастает обратно с перелётом.
   Картинка меняется в середине — на кадре, где её не видно (support.js) */
.crowd__cell[data-swap] img { animation: crowd-swap var(--crowd-swap) var(--ease-brand); }

@keyframes crowd-swap {
  0%   { opacity: 1; transform: scale(1); }
  36%  { opacity: 0; transform: scale(0.2); }
  72%  { opacity: 1; transform: scale(1.08); }
  100% { opacity: 1; transform: scale(1); }
}

/* Счётчик поверх середины сетки, ровно на паре кружков под собой: он и
   шириной ровно с них — 52 + 16 + 52. Сдвиг на 2 влево оттуда же: сетке
   в макете отведено 396, а сами шесть колонок занимают 392, и середина
   пары приходится на 196, а не на 198 */
.crowd__badge {
  position: absolute;
  top: 50%;
  left: calc(50% - 2px);
  display: grid;
  place-items: center;
  width: 120px;
  height: 52px;
  border-radius: var(--radius-full);
  background: var(--entity-game-wash);
  color: var(--entity-game-accent);
  font-family: var(--font-heading);
  font-weight: var(--font-weight-heading);
  transform: translate(-50%, -50%);
}

/* ══ 2. Курсы по разработке. Figma 930:62361 ═══════════════════════════ */
.support-card--courses { grid-area: 1 / 3 / 2 / 4; }

/* ══ 3. Соберём за вас. Figma 930:62365 ════════════════════════════════ */
.support-card--build {
  grid-area: 2 / 3 / 4 / 4;
  background: var(--color-neutral-03);
}

/* Стикер вместо картинки 64×64 из макета: в макете стоит замерший кадр
   этого же ролика */
.support-card__sticker {
  position: relative;
  display: block;
  width: 64px;
  height: 64px;
}

/* ══ 4. База знаний. Figma 930:62370 ═══════════════════════════════════ */
.support-card--docs { grid-area: 3 / 1 / 4 / 2; }

/* ══ 5. Кстати, это Паззло. Figma 930:62376 ════════════════════════════
   Сердце свешивается за правый нижний угол и режется им, Паззло сидит
   в сердце. Ссылки у карточки нет — она только знакомит */
.support-card--puzzlo {
  grid-area: 3 / 2 / 4 / 3;
  border-color: var(--entity-accent-wash);
  --support-hover-border: var(--entity-accent-wash-22);
}
.support-card__accent { color: var(--entity-accent-100); }

.support-card__heart {
  position: absolute;
  right: -51px;
  bottom: -52.5px;
  width: 258px;
  height: 221px;
  color: var(--entity-accent-wash);
  transition: color var(--duration-base) var(--ease-brand);
}
/* Сердце из той же шкалы, что и обводка, и на наведении идёт за ней:
   иначе фон остался бы светлее контура и карточка разъехалась бы по тону */
@media (hover: hover) {
  .support-card--puzzlo:hover .support-card__heart { color: var(--entity-accent-wash-22); }
}

.support-card__sticker--robot {
  position: absolute;
  right: 14px;
  bottom: 14.5px;
  width: 128px;
  height: 128px;
}

/* ── Планшет ─────────────────────────────────────────────────────────── */
/* Макета этой ступени в Figma нет. Собрано по образцу блока «Почему мы
   лучшие»: две колонки, сообщество занимает обе и держит свои 288,
   остальные встают парами */
@media (max-width: 1279px) {
  .support__grid {
    /* Шаг карточек узких ступеней — 16, как в «Почему мы лучшие» и FAQ */
    gap: var(--spacing-m);
    grid-template-columns: repeat(2, minmax(0, 1fr));
    grid-template-rows: none;
    grid-auto-rows: minmax(196px, auto);
  }
  .support-card--community { grid-area: 1 / 1 / 2 / 3; min-height: 288px; }
  .support-card--courses   { grid-area: 2 / 1 / 3 / 2; }
  .support-card--build     { grid-area: 2 / 2 / 3 / 3; }
  .support-card--docs      { grid-area: 3 / 1 / 4 / 2; }
  .support-card--puzzlo    { grid-area: 3 / 2 / 4 / 3; }
}

/* ── Одна колонка. Figma 1039:19267 ──────────────────────────────────── */
/* Карточки встают друг под друга в макетном порядке, а сетка аватарок
   переезжает под текст: сбоку в узкую карточку она уже не помещается.

   Ширина — вся карточка плюс по 16 наружу с каждой стороны, как в
   макете, но не меньше 528: шести макетных колонок (396) хватает ровно
   до экрана 396, а с 402 они перестают закрывать карточку и справа
   открывается пустая полоса. 528 — это восемь колонок, запас на любой
   телефон; на экранах шире ряд просто возьмёт больше ячеек */
@media (max-width: 767px) {
  .support__grid {
    grid-template-columns: minmax(0, 1fr);
    grid-auto-rows: auto;
  }
  .support-card { grid-area: auto; }

  .support-card--community { order: 1; }
  .support-card--courses   { order: 2; }
  .support-card--build     { order: 3; }
  .support-card--docs      { order: 4; }
  .support-card--puzzlo    { order: 5; }

  /* Тянуть текст незачем — низ карточки занимает сетка */
  .support-card--community .support-card__text { flex: none; }
  /* Порядок внутри карточки: заголовок, текст, ссылка, сетка. В разметке
     сетка стоит выше ссылки — там ей место на широком экране, где она
     вынута из потока и порядок ни на что не влияет */
  .support-card--community .support-card__link { order: 1; }

  /* relative, а не static: плашка счётчика внутри — absolute, и ей нужен
     отсчёт от сетки, иначе она уедет в середину карточки */
  .crowd {
    position: relative;
    top: auto;
    right: auto;
    left: 50%;
    order: 2;
    flex: none;                     /* флекс-родитель не вправе её ужимать */
    /* Ширина — ровно по колонкам, иначе остаток до полной колонки уходит
       в пустую полосу у правого края. Сколько их, считает скрипт: число
       обязано быть чётным, чтобы середина сетки попала в стык между
       кружками, а не в середину кружка — там стоит плашка счётчика */
    width: calc(var(--crowd-cols, 8) * (52px + var(--spacing-m)) - var(--spacing-m));
    /* Паддинг карточки внизу сетке не нужен, и ещё 24 она уходит под
       нижний край: последний ряд срезан */
    margin-block-end: calc(-1 * (var(--spacing-xl) + 24px));
    transform: translateX(-50%);
  }

  /* Здесь ширина считается ровно по колонкам, лишних четырёх пикселей
     нет — и поправка на них не нужна: середина сетки и есть стык пары */
  .crowd__badge { left: 50%; }

  /* Верх сетки растворяется в фоне карточки — Figma 1039:19309,
     прямоугольник 396×188 с заливкой от цвета карточки к прозрачному.
     Псевдоэлемент, а не отдельная нода: сетке он и нужен только затем,
     чтобы первые ряды не спорили с текстом над ними */
  .crowd::after {
    content: '';
    position: absolute;
    inset: 0 0 auto;
    height: 188px;
    background: linear-gradient(var(--entity-game-tint), transparent);
    pointer-events: none;
  }
}

/* ── Без анимации ────────────────────────────────────────────────────── */
/* Смену аватарок и счёт снимает скрипт: сетка остаётся такой, какой
   пришла в разметке */
@media (prefers-reduced-motion: reduce) {
  .crowd__cell[data-swap] img { animation: none; }
}


/* ══════════════════════════════════════════════════════════════════════
   ЧАСТО ЗАДАВАЕМЫЕ ВОПРОСЫ
   ══════════════════════════════════════════════════════════════════════ */

/* Figma 930:62382 — сетка 1270×352: слева колонка 402 со стикером и
   заголовком, справа 836 со списком, между ними 32.

   Список собран на <details>/<summary>: раскрытие по клику на всю строку
   заголовка, состояние — в самом элементе, без единой строки js. Анимации
   раскрытия нет по требованию: строка меняет высоту мгновенно, стрелка
   поворачивается без перехода */
:root {
  /* Колонки. Figma 930:62383 и 930:62386 — 402 и 836 при сетке 1270.
     Заданы долями, а не пикселями: на полной сетке они дают ровно
     макетные 402 и 836 (402 + 32 + 836 = 1270), а на планшете сжимаются
     в той же пропорции, вместо того чтобы отъедать ширину у списка */
  --faq-lead-column: 402fr;
  --faq-list-column: 836fr;
  /* Стикер. Figma 930:62384 — 64×64 */
  --faq-sticker: 64px;
  /* Бокс стрелки. Figma 930:62390 — Button/Icon/L, в ките это 30 */
  --faq-caret: 30px;
}

/* Поля блока как у соседей по странице: по половине шага сверху и снизу.
   Вторые половины вносят «Поддержка» сверху и «Готов начать?» снизу */
.faq {
  max-width: calc(1270px + 2 * var(--spacing-m));
  margin-inline: auto;
  padding-block: var(--section-gap-half);
  padding-inline: var(--spacing-m);
}

.faq__grid {
  display: grid;
  grid-template-columns: minmax(0, var(--faq-lead-column)) minmax(0, var(--faq-list-column));
  gap: var(--spacing-xl);
}

/* ── Левая колонка ───────────────────────────────────────────────────── */
/* Паддинг 32 со всех сторон, между стикером и заголовком тоже 32
   (930:62384 стоит на 32/32, текст начинается на 128) */
.faq__lead {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xl);
  align-items: flex-start;
  padding: var(--spacing-xl);
}

/* Ролик приходит по требованию и рисуется в svg внутри этого бокса.
   Размеры на самом контейнере, чтобы до загрузки заголовок не прыгал */
.faq__sticker {
  flex: none;
  width: var(--faq-sticker);
  height: var(--faq-sticker);
}
.faq__sticker svg {
  display: block;
}

.faq__title {
  color: var(--color-text-primary);
}

/* ── Список ──────────────────────────────────────────────────────────── */
/* Линия под строкой — Basic/Stroke (Border)/Normal, 2. У последней строки
   её нет: в макете 930:62405 идёт без обводки */
.faq__item {
  border-bottom: var(--size-stroke-m) solid var(--color-border);
}
.faq__item:last-child {
  border-bottom: none;
}

/* Заголовок вопроса. Кликается вся строка целиком — это и есть <summary>,
   поэтому у него паддинги и ширина строки, а не у текста внутри.
   Родной треугольник маркера убираем двумя способами: list-style для всех
   браузеров и ::-webkit-details-marker для старых Safari */
.faq__question {
  display: flex;
  gap: var(--spacing-m);
  align-items: center;
  justify-content: space-between;
  padding-block: var(--spacing-l);
  cursor: pointer;
  list-style: none;
  /* Строку открывают-закрывают подряд, и второй клик подряд браузер
     считает двойным: слово под курсором выделяется синим. Выделять в
     заголовке нечего — он кнопка, а не текст, поэтому выделение снято.
     Ответ ниже выделяется как обычно: его копируют */
  user-select: none;
}
.faq__question::-webkit-details-marker {
  display: none;
}

.faq__question-text {
  color: var(--color-text-primary);
}

/* Закрытая строка показывает CaretRight, раскрытая — CaretDown. Это одна
   и та же стрелка, повёрнутая на 90°; переход не ставим — блок без
   анимаций, стрелка меняется в тот же кадр, что и высота строки */
.faq__caret {
  display: block;
  flex: none;
  width: var(--faq-caret);
  height: var(--faq-caret);
}
.faq__item[open] .faq__caret {
  rotate: 90deg;
}

/* Ответ. Своего верхнего отступа нет — его даёт нижний паддинг заголовка */
.faq__answer {
  padding-bottom: var(--spacing-m);
  color: var(--color-text-primary);
}

.faq__link {
  color: var(--color-text-link);
  font-weight: var(--font-weight-body-medium);
  text-decoration: none;
}
@media (hover: hover) {
  .faq__link:hover {
    color: var(--color-text-link-hover);
}
}
.faq__link:active {
  color: var(--color-text-link-pressed);
}

/* ── Узкий экран ─────────────────────────────────────────────────────── */
/* Макета для узкого нет. Планшет держит десктопную раскладку — две
   колонки со стикером, только сжатые: колонки заданы долями и едут вместе
   с шириной экрана.

   Разворот в одну колонку начинается там же, где у остальных блоков
   лендинга — на 767. Стикер там скрыт совсем, по требованию Александра.
   Ролик при этом даже не скачивается: наблюдатель lottieLazy ждёт, когда
   бокс попадёт в экран, а скрытый не попадает никогда */
/* Шаг колонок узких ступеней — 16, как у карточек остальных блоков */
@media (max-width: 1279px) {
  .faq__grid { gap: var(--spacing-m); }
}

@media (max-width: 767px) {
  .faq__grid {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--spacing-m);
  }
  .faq__sticker {
    display: none;
  }
  /* Паддинг колонки был нужен, чтобы отбить её от списка справа. В одну
     колонку отбивать не от чего — остаётся отступ под заголовком */
  .faq__lead {
    padding: 0;
  }
  /* Макетные переносы заголовка рассчитаны на колонку в 402 — здесь строку
     ломает ширина экрана. Перед каждым <br> в разметке стоит пробел: без
     него погашенный перенос склеивает слова */
  .faq__title br {
    display: none;
  }
}


/* ══════════════════════════════════════════════════════════════════════
   ФУТЕР
   ══════════════════════════════════════════════════════════════════════ */

/* ==========================================================================
   Футер. Figma 936:1354 (полоса на 1920) → 930:62413 (сетка 1270).

   Сетка снята с макета, а не подобрана: 32 + 220.75×4 + 32×4 + 195 + 32
   = 1270. Поэтому четыре колонки — 1fr, а пятая фиксированная: подставь
   ей 1fr, и она получит 241, ссылки в ней перестанут ломаться по макету.

   Значений, которых нет в ките, ровно три, все вынесены в --footer-*
   с указанием источника.
   ========================================================================== */

:root {
  /* Ширина футера из макета. В ките есть --container-landing 1290, но
     футер нарисован на 1270 — держим макетное, расхождение в 20px
     разъедет колонки на полсимвола */
  --footer-max: 1270px;
  /* Пятая колонка. Figma 930:62451 — 195, ширина продиктована строкой
     «puzzlebot входит в реестр российского ПО», которая ложится в три строки */
  --footer-support: 195px;
  /* Логотип в футере крупнее шапочного: 110×23 против 107×22 (930:62418) */
  --footer-logo-width: 110px;
  --footer-logo-height: 23px;

}

/* ── Полоса ──────────────────────────────────────────────────────────── */
/* Figma 936:1354: подложка Primary/Berlin/01, сверху обводка Stroke/M */
.footer {
  position: relative;
  border-top: var(--size-stroke-m) solid var(--color-border);
  background: var(--color-neutral-01);
}

/* ── Сетка колонок. Figma 930:62413 ──────────────────────────────────── */
.footer__inner {
  display: grid;
  /* В макете 1fr было четыре: колонку «Сравнение» убрали 13.09.2026 */
  grid-template-columns: repeat(3, 1fr) var(--footer-support);
  gap: var(--spacing-xl);
  align-items: start;
  max-width: var(--footer-max);
  margin-inline: auto;
  padding: var(--spacing-xxl) var(--spacing-xl);
}

.footer__col {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xl);
  /* min-content у grid-элемента по умолчанию auto: длинная строка
     «Политика в отношении обработки…» иначе распирает свою колонку */
  min-width: 0;
}
/* У колонок-списков шаг между заголовком и списком 16, а не 32 (930:62421) */
.footer__col--nav { gap: var(--spacing-m); }

/* ── Колонка бренда ──────────────────────────────────────────────────── */
/* Клавиша. Figma 930:62415 — квадрат 128. Это Lottie-контейнер: рамку и
   заливку кнопки снимаем, курсор оставляем — по клику играет анимация */
.footer__key {
  width: 128px;
  height: 128px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  /* Пока Lottie не загрузился, кнопка не должна схлопываться в ноль */
  flex: none;
}
.footer__key svg { display: block; }

/* Блок «логотип + подпись». Figma 930:62417, шаг 16 */
.footer__brand {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-m);
}
.footer__logo {
  width: var(--footer-logo-width);
  height: var(--footer-logo-height);
}

.footer__copy { color: var(--color-text-tertiary); }

/* ── Колонки-списки ──────────────────────────────────────────────────── */
.footer__list {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-m);
  margin: 0;
  padding: 0;
  list-style: none;
}
/* Ссылки футера — не синие ссылки кита: в макете это Text/Secondary
   весом 500 (930:62423). Состояния берём из шкалы Text/Secondary, а не
   осветляем фильтром */
.footer__list a {
  color: var(--color-text-secondary);
  font-size: var(--text-p1);
  line-height: var(--text-p1--line-height);
  font-weight: var(--font-weight-body-medium);
  transition: color var(--duration-fast) var(--ease-brand);
}
@media (hover: hover) {
  .footer__list a:hover { color: var(--color-text-secondary-hover); }
}
.footer__list a:active { color: var(--color-text-secondary-pressed); }

/* ── Колонка поддержки ───────────────────────────────────────────────── */
.footer__col--support { width: var(--footer-support); }

/* Заголовок и адрес — одна строка в две линии, зазора между ними нет */
.footer__support-title { line-height: var(--text-h3--line-height); }

/* Адрес — кнопка (клик копирует), но выглядеть должна ссылкой: сбрасываем
   всё кнопочное и наследуем типографику заголовка. Тот же адрес в тексте
   правовых документов (.doc__mail) ведёт себя так же */
.footer__mail,
.doc__mail {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--color-text-link);
  cursor: pointer;
  transition: color var(--duration-fast) var(--ease-brand);
}
@media (hover: hover) {
  .footer__mail:hover,
  .doc__mail:hover { color: var(--color-text-link-hover); }
}
.footer__mail:active,
.doc__mail:active { color: var(--color-text-link-pressed); }

/* Кнопки соцсетей. Figma 930:62453 — три Icon Button L с шагом 8 */
.footer__social {
  display: flex;
  gap: var(--spacing-xs);
  margin: 0;
  padding: 0;
  list-style: none;
}

.footer__registry { color: var(--color-text-secondary); }
.footer__note { color: var(--color-text-tertiary); }

/* ── Подсказка «Скопировано» ─────────────────────────────────────────
   Kit 2.0, нода 4457:8395: подложка Tooltip/Surface, текст P3 белым,
   паддинги 8/4, скругление Corner/S, стрелка 11.5×5 сверху по центру.
   Стрелка — треугольник на псевдоэлементе: ради 11×5 пикселей тащить
   отдельный SVG незачем.

   Позиция считается в скрипте: подсказка одна на футер и переезжает
   под тот элемент, который её вызвал.

   Вид общий с подсказкой документа (.doc__tooltip): раньше там была своя
   копия без стрелки и с другими паддингами. Своё у каждой — только
   позиционирование: здесь absolute от футера, в doc.css fixed от окна */
.footer__tooltip {
  position: absolute;
  /* Стартовая точка — левый верхний угол футера. Без неё подсказка стоит там,
     где лежит в потоке (после последней колонки), торчит на 22px за низ футера
     и растягивает документ: внизу страницы появлялась пустая белая полоса.
     Скрипт всё равно перезаписывает top/left при показе */
  top: 0;
  left: 0;
  z-index: 1;
}
.footer__tooltip,
.doc__tooltip {
  padding: var(--spacing-xxs) var(--spacing-xs);
  border-radius: var(--radius-s);
  background: var(--color-tooltip-surface);
  color: var(--color-tooltip-text);
  white-space: nowrap;
  pointer-events: none;

  /* Скрытое состояние — не hidden: атрибут переключается мгновенно и
     анимировать его нечем. visibility гасим с задержкой в длительность
     перехода, иначе она сработает сразу и съест исчезновение.
     -50% по X — перевод из «центра под элементом» в координаты угла,
     -4px по Y — тот самый выезд, ради которого всё и затевалось */
  opacity: 0;
  visibility: hidden;
  transform: translate(-50%, -4px);
  transition: opacity var(--duration-base) var(--ease-brand),
              transform var(--duration-base) var(--ease-brand),
              visibility 0s linear var(--duration-base);
}
.footer__tooltip.is-visible,
.doc__tooltip.is-visible {
  opacity: 1;
  visibility: visible;
  transform: translate(-50%, 0);
  transition-delay: 0s;
}
.footer__tooltip::before,
.doc__tooltip::before {
  content: "";
  position: absolute;
  top: -5px;
  left: 50%;
  width: 11.5px;
  height: 5px;
  transform: translateX(-50%);
  background: inherit;
  clip-path: polygon(50% 0, 100% 100%, 0 100%);
}

/* ── Планшет: пять колонок не помещаются ─────────────────────────────── */
@media (max-width: 1279px) {
  .footer__inner { grid-template-columns: repeat(2, 1fr); }
  /* Фикс 195 нужен только макетной раскладке: в две колонки поддержка
     занимает свою половину целиком */
  .footer__col--support { width: auto; }
}

/* ── Мобильный: одна колонка ───────────────────────────────────────────
   Содержимому хватило бы и 640 (две колонки по 288 держат самую длинную
   строку «puzzlebot или Botmother»), но своей ступени у футера нет:
   он ломается вместе с остальной мобильной раскладкой */
@media (max-width: 767px) {
  .footer__inner {
    grid-template-columns: 1fr;
    gap: var(--spacing-xl);
    padding: var(--spacing-xl) var(--spacing-m);
  }
}

/* Анимацию по клику никто не ждёт, но выключатель движения уважаем:
   первый кадр остаётся, кнопка перестаёт быть кнопкой */
@media (prefers-reduced-motion: reduce) {
  .footer__key { cursor: default; }
  /* У подсказки убираем выезд, но не проявление: reduce — про движение,
     а смена прозрачности движением не является */
  .footer__tooltip,
  .doc__tooltip { transform: translate(-50%, 0); }
}

/* ══════════════════════════════════════════════════════════════════════
   Блок «Возможности платформы». Figma 930:62091 — пять карточек в три
   ряда сетки 1270 с шагом 32: две по 666, широкая на 480 и две по 316.
   ══════════════════════════════════════════════════════════════════════ */

/* Скругление плитки интеграций. В шкале Corner такого шага нет: в макете
   (930:62139) у плиток стоит 28, а Corner/XL — 20. Держим значение здесь
   одной переменной, чтобы оно не расползлось по правилам хардкодом */
.offer {
  --tile-radius: 28px;
  /* Сторона гнезда логотипа. На промежуточных экранах её пересчитывает
     offer.js, чтобы колонки заняли ширину карточки ровно, без полей по
     бокам. Значение живёт на блоке, а не на плитке: иначе собственное
     объявление плитки перебило бы то, что скрипт ставит выше по дереву */
  --tile-cell: 64px;
  /* Сколько гнездо стоит скелетоном после того, как целиком вошло
     в экран. Без выдержки логотип встаёт на место в тот же кадр,
     и подмены скелетона зритель не замечает */
  --tile-boot-hold: 300ms;
}

/* Поля блока как у соседей по странице (.stats, .better): по половине
   шага сверху и снизу, вторые половины вносят соседние секции */
.offer {
  max-width: calc(1270px + 2 * var(--spacing-m));
  margin-inline: auto;
  padding-block: var(--section-gap-half);
  padding-inline: var(--spacing-m);
}

.offer__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--spacing-xl);
}

/* База — .card из дизайн-системы: паддинг 32, обводка 2, скругление 20.
   Макетные 316 высоты плюс 4: Figma рисует обводку внутрь фрейма и места
   под неё не отводит, а в CSS она съедает свои 2px сверху и снизу */
.offer-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-xl);
  box-sizing: border-box;
  min-height: 320px;
}

/* ══ 1. Верхние карточки. Figma 930:62102, 930:62093, 930:62108 ═══════
   «Умные сценарии» белые с обводкой Paris/22 (.card--entity). Конструктору
   заливку Info/01 и обводку Info/05 даёт .card--tint из дизайн-системы,
   широкой они достаются градиентами (ниже). Паддинг
   снизу у первых двух нулевой: и сцена сценария, и скриншот конструктора
   доходят до самого края карточки */
.offer-card--mailing,
.offer-card--builder {
  min-height: 670px;                /* макетные 666 плюс 4 на обводку */
  padding-bottom: var(--spacing-none);
}
/* Конструктор кадр режет, «Умные сценарии» — нет: в макете (930:62102)
   clipsContent выключен, и плашки свешиваются наружу. Габарит карточки
   619×666, отрисовка 635×688 — по 8 в стороны и 11 вверх */
.offer-card--builder { overflow: hidden; }

/* Заливка и обводка — два градиента, оба уходят вправо-вниз и тают
   (930:62108: fill Info/01 → 0%, stroke Info/11 → 0%; сплошная заливка
   Info/01 в макете отключена, поэтому .card--tint здесь не годится).

   Прозрачный конец набран белым, а не transparent: слой заливки идёт по
   padding-box и должен закрывать середину, иначе сквозь него проступит
   слой обводки, лежащий под ним по border-box. Приём тот же, что у
   промо-карточки кейсов и у «Создай проект» ниже */
.offer-card--integrations {
  min-height: 484px;                /* макетные 480 плюс 4 на обводку */
  border-color: transparent;
  background:
    linear-gradient(127deg, var(--color-info-01), var(--color-surface)) padding-box,
    linear-gradient(153deg, var(--color-info-11), var(--color-surface)) border-box;
  /* Текст и плитки лежат в одной ячейке: в макете плитки заходят под
     текстовую колонку по горизонтали и расходятся с ней по вертикали
     (930:62113). Колонкой их не разложить — не хватит высоты */
  display: grid;
}
.offer-card--integrations .offer-card__text {
  grid-area: 1 / 1;
  align-self: start;
  z-index: 1;
}

/* ══ 2. Сцена сценария. Figma 1489:5731 ═══════════════════════════════
   Поле 555×513 стоит у нижнего края карточки, дорожки уходят вверх под
   текст. Плашки свешиваются за края поля, их режет карточка.

   В потоке лежит обёртка: её высота — поле с учётом масштаба. Само поле
   абсолютное, центрировано по карточке и сжимается от нижнего края.
   --k считает offer.js: ширина карточки к макетным 619, не больше 1 */
.offer-card--mailing {
  --card-entity: var(--entity-bot-wash-22);   /* Reserved/Paris (Command)/22 */
  gap: var(--spacing-none);
}
.offer-card--mailing .offer-card__text { position: relative; z-index: 1; }

.scene {
  position: relative;
  /* Свой слой: клиент летит над плашками, но вся сцена целиком остаётся
     под текстом карточки. Без изоляции он выныривал поверх описания */
  isolation: isolate;
  flex: none;
  height: calc(513px * var(--k, 1));
  margin-top: auto;
  -webkit-user-select: none;
  user-select: none;
  pointer-events: none;
}

.scene__stage {
  position: absolute;
  left: 50%;
  /* Поле стоит на нижнем крае карточки, а не на внутреннем: в макете
     оно кончается ровно там же, где карточка (1489:5731, y 153 + 513 = 666) */
  bottom: calc(-1 * var(--size-stroke-m));
  width: 555px;
  height: 513px;
  translate: -50% 0;
  scale: var(--k, 1);
  transform-origin: 50% 100%;
}

/* Трубы рисует offer.js из тех же кривых, по которым едет клиент: одна
   геометрия на движение и на рисунок, разъехаться им нечем. Сами кривые
   сняты с осей лучей макета (1496:6767).

   Слой лежит над едущим клиентом: подложка трубы полупрозрачная, и клиент
   виден сквозь неё приглушённым — то самое стекло из макета. Трубы входов
   уходят за поле, режет их край карточки */
.scene__tubes {
  position: absolute;
  inset: 0;
  z-index: 2;
  overflow: visible;
}

/* Плашка: заливка сущности, по ней белый блик к левому нижнему углу */
.scene__plate {
  position: absolute;
  left: var(--x);
  top: var(--y);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--spacing-m);
  box-sizing: border-box;
  width: var(--w, auto);
  padding: var(--spacing-m);
  border-radius: var(--tile-radius);
  background:
    linear-gradient(220deg, transparent 31%, color-mix(in srgb, var(--color-surface) 30%, transparent) 100%),
    var(--plate);
  color: var(--color-text-primary);
  white-space: nowrap;
  z-index: 3;
}
.scene__plate--row { flex-direction: row; }
.scene__plate--wallet { align-items: flex-end; }

.scene__plate--bot    { --plate: var(--entity-shop-wash-22); }
.scene__plate--bell   { --plate: var(--entity-game-wash-22); }
.scene__plate--app    { --plate: var(--entity-condition-wash-22); }
.scene__plate--wallet { --plate: var(--entity-app-wash-22); }
.scene__plate--mail   { --plate: var(--color-info-22); }

.scene__sum { display: flex; flex-direction: column; }
.scene__total { display: flex; align-items: center; gap: var(--spacing-xs); }
.scene__amount { font-weight: var(--font-weight-heading); }
.scene__plus { color: var(--color-success); }

/* Кольцо на углу плашки: обводка в цвет плашки, внутри гнездо или
   монетка. Центр — макетная точка. Два кольца с аватарами повёрнуты
   на 5°, как в макете */
.scene__ring {
  position: absolute;
  left: var(--x);
  top: var(--y);
  display: grid;
  place-items: center;
  box-sizing: border-box;
  width: 80px;
  height: 80px;
  translate: -50% -50%;
  border: 4px solid var(--plate);
  border-radius: var(--radius-full);
  background: var(--color-info-01);
  z-index: 4;
}
.scene__ring--bot    { --plate: var(--entity-shop-wash-22); }
.scene__ring--bell   { --plate: var(--entity-game-wash-22); }
.scene__ring--app    { --plate: var(--entity-condition-wash-22); }
.scene__ring--wallet { --plate: var(--entity-app-wash-22); rotate: 5deg; }
.scene__ring--mail   { --plate: var(--color-info-22); rotate: 5deg; }

/* Гнездо и монетка — те же, что в «Единой логике» (.logic__hole, .logic__coin).
   Здесь у гнезда есть вторая работа: оно режет своим кругом двойника
   монетки, который в него вкатывается, — как круглый фрейм с clipsContent
   в макете (Figma 1516:9709) */
.scene__hole {
  position: relative;
  overflow: hidden;
  width: var(--why-cell);
  height: var(--why-cell);
  border-radius: var(--radius-full);
  background: var(--color-info-11);
}
/* Тень колодца отдельным слоем поверх монетки, а не box-shadow на самом
   гнезде: вложенная тень рисуется под содержимым, и въезжающая монетка
   стирала бы её собой — колодец на глазах переставал быть колодцем */
.scene__hole::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
  box-shadow: var(--why-hole-shadow);
  pointer-events: none;
}

/* Двойник монетки внутри гнезда. Смещение и поворот ставит offer.js
   каждый кадр: смещение — вектор от центра гнезда до центра монетки,
   поворот — тот же, что у настоящей */
.scene__ghost {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
}


.scene__coin {
  display: grid;
  place-items: center;
  width: var(--why-cell);
  height: var(--why-cell);
  border-radius: var(--radius-full);
  background: var(--coin);
  box-shadow: var(--why-coin-shadow);
}
.scene__coin img { width: 44px; height: 44px; }

/* ── Летящие клиенты ──────────────────────────────────────────────────
   Клиент появляется у дальнего конца верхней дорожки, спускается к
   «Началу рассылки» и дальше идёт по одной из двух веток до «Заработано».
   Маршрут и время — offer.js, здесь только вид и сами переходы.

   Слой своего контекста не заводит, но порядок внутри сцены задан явно:
   клиент в пути (1) — трубы (2) — плашка (3) — кольцо (4). В
   гнезде клиент лежит внутри самого гнезда, поэтому поверх плашки не
   вылезает. Вся сцена при этом остаётся под текстом карточки */
.scene__flow {
  position: absolute;
  inset: 0;
}

/* Едет по кривой: путь кладёт скрипт в --path, offset-distance гонит
   анимация. Поворот пути выключен здесь, на самом клиенте, а не только в
   лунке: с offset-rotate: auto движок доворачивал коробку по касательной,
   и к качению вложенного слоя примешивался лишний замах, а у лунки он
   разом обнулялся.

   Размытия в пути нет. Стекло трубы лежит слоем выше и приглушает
   клиента само; blur на летящем узле давал ровно ту ступеньку, из-за
   которой монетка «выходила из блюра» у самого кольца */
.scene__flyer {
  position: absolute;
  left: 0;
  top: 0;
  z-index: 1;                       /* в пути — под трубой, сквозь её стекло */
  width: var(--why-cell);
  height: var(--why-cell);
  /* Сам путь ставит offer.js: он для каждого перегона свой */
  offset-rotate: 0deg;
  /* Проявление и уход ведёт offer.js, а не переход в стилях: монетка и её
     двойник в лунке должны светлеть одинаково, иначе на кромке гнезда
     видна ступенька яркости. До первого кадра монетки не видно */
  opacity: 0;
}
/* Доехал до кошелька: монетка встала ровно в лунку и поднялась над
   кольцом. Клипа не нужно — монетка и лунка одного размера, гнездо она
   закрывает собой ровно. Тогда же видна и галочка */
.scene__flyer[data-done] { z-index: 5; }

/* Крутится сам клиент, а не траектория. Оборот считает offer.js по
   пройденному пути — монетка катится по дорожке, а не вертится сама */
.scene__flyer-spin {
  display: grid;
  place-items: center;
  width: 100%;
  height: 100%;
}

/* Подложку монетки (--coin) раздаёт скрипт из палитры сущностей, как
   монеткам в «Единой логике» */

/* Кружок с галочкой вместо лица. Figma 1497:7037 — Entity/MiniApp/Primary
   под белой галочкой 30 */
.scene__done {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  border-radius: var(--radius-full);
  background: var(--entity-app-primary);
  opacity: 0;
  transition: opacity 200ms ease-out;
}
.scene__flyer[data-done] .scene__done { opacity: 1; }

/* ── Удар о лунку ─────────────────────────────────────────────────────
   Клиент нигде не стоит: этап он проходит насквозь и в этот миг скрыт
   кольцом. Удар читается вспышкой — загорается сама лунка, а следом
   коротко подсвечивается плашка этапа. Бегущего перелива больше нет:
   он тянулся секунду и читался как «этап работает прямо сейчас», а нужен
   отклик на удар. Атрибут выдаёт и снимает offer.js */
/* Только цвет, без рывка масштаба: гнездо теперь ещё и режет собой
   двойника монетки, и вместе с ним масштабировалось бы его смещение —
   монетка дёргалась бы в лунке на каждой вспышке (до 4px на глаз).

   Вспышка идёт на ступень вверх по той же шкале, Info/11 → Info/01, а не
   в чистый белый: белым лунка выжигалась до плоского пятна и теряла
   форму колодца. Ярче нужно — следующий шаг тут только --color-surface */
.scene__ring[data-hit] .scene__hole { animation: scene-hit 420ms ease-out; }
@keyframes scene-hit {
  0%   { background: var(--color-info-11); }
  18%  { background: var(--color-info-01); }
  100% { background: var(--color-info-11); }
}

/* Плашка вспыхивает плоской заливкой во всю себя, без градиента и без
   хода: он уводил бы взгляд вбок от лунки, по которой пришёлся удар.
   Заливка белая и полупрозрачная: слой лежит поверх текста, и сплошным
   цветом надпись пропадала бы. Силу вспышки задаёт пик в scene-hit-plate,
   он же вторая ручка яркости после цвета лунки */
.scene__plate { overflow: hidden; }
.scene__plate::after {
  content: '';
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--color-surface) 50%, transparent);
  opacity: 0;
  pointer-events: none;
}
.scene__plate[data-hit]::after { animation: scene-hit-plate 420ms ease-out; }
@keyframes scene-hit-plate {
  0%   { opacity: 0; }
  18%  { opacity: 0.5; }
  100% { opacity: 0; }
}

/* Прибавка справа от суммы: держится, пока сумма догоняет новое значение */
.scene__gain {
  color: var(--color-success);
  opacity: 0;
  transition: opacity 400ms ease-out;
}
.scene__gain[data-shown] { opacity: 1; transition-duration: 200ms; }

/* Движение здесь — украшение: без него сцена просто стоит пустой */
@media (prefers-reduced-motion: reduce) {
  .scene__ring[data-hit] .scene__hole,
  .scene__plate[data-hit]::after { animation: none; }
}

/* ══ 3. Запись конструктора. Figma 930:62094 ══════════════════════════
   Видео вписано по ширине карточки, свешивается за нижний край на 34
   и режется им. Верх гасит заливка карточки: 116 сплошной, дальше 194
   растворяющейся — под ней и лежит текст */
/* Ширина задана явно, а не парой left/right: только тогда aspect-ratio
   считает высоту сам, а с ним и object-fit обрезает картинку по низу */
.offer-card__shot {
  position: absolute;
  bottom: -34px;
  left: calc(-1 * var(--size-stroke-m));
  width: calc(100% + 2 * var(--size-stroke-m));
  aspect-ratio: 619 / 577;
  object-fit: cover;
  object-position: top;
}
.offer-card__veil {
  position: absolute;
  inset: calc(-1 * var(--size-stroke-m)) calc(-1 * var(--size-stroke-m)) auto;
  height: 310px;
  background: linear-gradient(var(--color-info-01) 116px, transparent);
}
.offer-card--builder .offer-card__text { position: relative; }

/* ══ 4. Плитки интеграций. Figma 930:62113 ════════════════════════════
   Шесть плиток разной высоты стоят пятью колонками и висят на общей
   линии низа; CRM с криптовалютой делят третью колонку.

   Раскладка сеткой, а не колонками-стеками: все макетные отметки кратны
   восьми, поэтому строка сетки — 8, и каждая плитка занимает свой
   отрезок. Так одна и та же разметка раскладывается и здесь, и на
   телефоне, где порядок другой */
/* На широких экранах обёртка ничего не делает: коллаж стоит в сетке
   карточки сам. Своя раскладка и зум включаются только на телефоне */
.tiles__frame { display: contents; }

/* Колонки разметки нужны только телефону — здесь плитки встают в сетку
   поштучно, поэтому обёртки распускаем */
.tiles__col { display: contents; }

.tiles {
  grid-area: 1 / 1;
  align-self: end;
  justify-self: end;
  display: grid;
  grid-template-columns: 152px 224px 152px 152px 224px;
  grid-auto-rows: 8px;
  align-items: start;
  column-gap: var(--spacing-m);
}

.tile--sheets    { grid-column: 1; grid-row: 37 / span 16; }
.tile--ai        { grid-column: 2; grid-row: 19 / span 34; }
.tile--crm       { grid-column: 3; grid-row: 10 / span 16; }
.tile--crypto    { grid-column: 3; grid-row: 28 / span 25; }
.tile--global    { grid-column: 4; grid-row: 16 / span 37; }
.tile--acquiring { grid-column: 5; grid-row: 1 / span 52; }

/* Ширина плитки считается от числа колонок иконок: 64 на гнездо, 8 между
   ними и 8 по краям — 152 на две колонки и 224 на три, как в макете.
   Без явной ширины плитку растягивает длинная подпись («Криптовалюта»
   шире своих двух гнёзд) */
.tile {
  --tile-cols: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  width: calc(var(--tile-cols) * var(--tile-cell) + (var(--tile-cols) + 1) * var(--spacing-xs));
  overflow: hidden;                 /* свечение под «+10» режется плиткой */
  border-radius: var(--tile-radius);
  background: var(--tile-wash);
}
.tile--ai,
.tile--acquiring { --tile-cols: 3; }

/* Подпись плитки: сверху 16, снизу 8 — иконки стоят к ней ближе, чем к
   краям (930:62140). По бокам вместо макетных 16 стоит 8: в макете у
   дизайнера «экваринг» без «й», и правильное написание в 192 не влезает
   ровно на пиксель. Ужать поле дешевле, чем разгонять плитку на строку */
.tile__title {
  width: 100%;
  padding: var(--spacing-m) var(--spacing-xs) var(--spacing-xs);
  text-align: center;
  color: var(--color-text-primary);
}

.tile__icons {
  display: grid;
  grid-template-columns: repeat(var(--tile-cols), var(--tile-cell));
  gap: var(--spacing-xs);
  padding: var(--spacing-xs);
}

/* Логотипы из макета приходят со своей белой плашкой, логотипам моделей
   её даёт карточка — отсюда два размера иконки в одном гнезде */
.tile__icon {
  display: grid;
  place-items: center;
  width: var(--tile-cell);
  aspect-ratio: 1;
  border-radius: var(--radius-xl);
  background: var(--color-surface);
}
/* Логотип занимает всё гнездо и растёт вместе с ним */
.tile__icon img { display: block; width: 100%; height: 100%; }
/* Логотипы, которые не влезли в целое число рядов: их прячет offer.js,
   чтобы «+10» стоял последним гнездом ряда, а не висел один в строке */
.tile__icon--off { display: none; }
/* Логотипам моделей плашку рисует гнездо, поэтому они меньше — те же
   40 из 64, что и в макете, только долей, а не пикселями */
.tile__icon--inset img { width: 62.5%; height: 62.5%; }

/* ── Загрузка логотипов ──────────────────────────────────────────────
   Коллаж собирается на глазах: гнёзда стоят пустыми плашками с бегущим
   бликом, а логотипы проявляются волной, один за другим.

   Скелетон вешает offer.js сразу, а снимает с каждого гнезда отдельно —
   когда оно целиком вошло в экран. Поэтому логотипы досыпаются рядами
   по мере прокрутки, а не по общему таймеру. Скрипта нет, как и при
   reduced-motion, — плитки просто стоят собранными.

   Плашка «+10» идёт тем же чередом: она такое же гнездо ряда */
.tile__icon { position: relative; }

/* Плашка и блик — два слоя, а не два фона одного. Блик едет transform'ом:
   позиция фона считается на перерисовке, и три десятка гнёзд роняли кадр
   вдвое по всей странице — просадка держалась и когда коллаж стоял далеко
   за экраном. Полоса шире гнезда и обрезается им, отсюда overflow */
.tiles.is-boot :is(.tile__icon, .tile__more) { overflow: hidden; }

.tiles.is-boot :is(.tile__icon, .tile__more)::before,
.tiles.is-boot :is(.tile__icon, .tile__more)::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: inherit;
}
.tiles.is-boot :is(.tile__icon, .tile__more)::before {
  background-color: var(--tile-shade);
}
.tiles.is-boot :is(.tile__icon, .tile__more)::after {
  /* Блик уже плашки и проходит по диагонали — свет, идущий по стеклу */
  inset-inline: -75%;
  background-image: linear-gradient(
    100deg,
    transparent 35%,
    var(--color-surface) 50%,
    transparent 65%
  );
  animation: tile-shimmer 1.4s linear infinite;
}
.tiles.is-boot :is(.tile__icon img, .tile__more) { opacity: 0; }

/* Гнездо показалось целиком: плашка гаснет, логотип проступает */
.tiles.is-boot :is(.tile__icon, .tile__more).is-ready::before,
.tiles.is-boot :is(.tile__icon, .tile__more).is-ready::after,
.tiles.is-boot :is(.tile__icon.is-ready img, .tile__more.is-ready) {
  animation-delay: var(--tile-boot-hold);
  animation-duration: var(--duration-base);
  animation-timing-function: var(--ease-brand);
  animation-fill-mode: forwards;
}
.tiles.is-boot :is(.tile__icon, .tile__more).is-ready::before { animation-name: tile-boot-veil; }
.tiles.is-boot :is(.tile__icon, .tile__more).is-ready::after {
  /* Блик бежит до самой выдержки, поэтому анимации две */
  animation-name: tile-shimmer, tile-boot-veil;
  animation-duration: 1.4s, var(--duration-base);
  animation-timing-function: linear, var(--ease-brand);
  animation-delay: 0s, var(--tile-boot-hold);
  animation-iteration-count: infinite, 1;
}
.tiles.is-boot :is(.tile__icon.is-ready img, .tile__more.is-ready) { animation-name: tile-boot-in; }

@keyframes tile-shimmer {
  /* Ход тот же, что был у фона: полоса шириной в два с половиной гнезда
     выезжает слева и уходит за правый край */
  from { transform: translateX(-60%); }
  to   { transform: translateX(90%); }
}
@keyframes tile-boot-veil {
  to { opacity: 0; }
}
@keyframes tile-boot-in {
  from { opacity: 0; transform: scale(0.82); }
  to   { opacity: 1; transform: none; }
}

/* Размытое пятно цвета плитки — в макете круг 224 с блюром 50 (930:62152).
   Лежит между иконками и плашкой «+10»: соседние логотипы оно выбеливает,
   а сама плашка остаётся поверх. Отсюда три ступени z-index в одном
   контексте, а не ::before у самой плашки — оттуда пятно накрывало бы и её.

   Центр пятна — центр последнего гнезда: 8 поля сетки плюс половина
   гнезда. Пятно есть только там, где есть «+10» */
.tile__icons { position: relative; }
.tile--ai .tile__icons::before,
.tile--global .tile__icons::before,
.tile--acquiring .tile__icons::before,
.tile--crypto .tile__icons::before {
  content: "";
  position: absolute;
  z-index: 1;
  right: calc(var(--spacing-xs) + var(--tile-cell) / 2);
  bottom: calc(var(--spacing-xs) + var(--tile-cell) / 2);
  transform: translate(50%, 50%);
  width: 224px;
  height: 224px;
  border-radius: var(--radius-full);
  background: var(--tile-wash);
  filter: blur(50px);
  opacity: 0.7;
}
/* У криптовалют пятно меньше и на ступень темнее фона плитки:
   в макете 153×153, Berlin/22 (1441:5398) */
.tile--crypto .tile__icons::before {
  width: 153px;
  height: 153px;
  background: var(--color-neutral-22);
}

/* «+10» — плашка цветом сущности, поверх пятна */
.tile__more {
  position: relative;
  z-index: 2;
  display: grid;
  place-items: center;
  width: var(--tile-cell);
  aspect-ratio: 1;
  border-radius: var(--radius-xl);
  background: var(--tile-accent);
  color: var(--color-text-on-primary);
}

/* Плашка скелетона — следующая ступень той же палитры: фон плитки идёт
   по одиннадцатой, гнездо — по двадцать второй. Так пустое гнездо
   читается углублением в плитке, а не чужеродным серым квадратом */
.tile--sheets     { --tile-wash: var(--entity-api-wash);      --tile-shade: var(--entity-api-wash-22); }
.tile--crm        { --tile-wash: var(--entity-scenario-wash); --tile-shade: var(--entity-scenario-wash-22); }
.tile--crypto     { --tile-wash: var(--color-neutral-11);     --tile-shade: var(--color-neutral-22);
                    --tile-accent: var(--color-neutral-100); }
.tile--ai         { --tile-wash: var(--entity-ai-wash);       --tile-shade: var(--entity-ai-wash-22);
                    --tile-accent: var(--entity-ai-accent); }
.tile--global     { --tile-wash: var(--entity-app-wash);      --tile-shade: var(--entity-app-wash-22);
                    --tile-accent: var(--entity-app-accent); }
.tile--acquiring  { --tile-wash: var(--color-info-11);        --tile-shade: var(--color-info-22);
                    --tile-accent: var(--color-info); }

/* ══ 5. Открытый API & MCP. Figma 930:62207 ═══════════════════════════
   Серая карточка: заливка Berlin/05, обводка Berlin/22 приходит от .card.
   Наведение уводит обводку на ступень глубже — тот же приём, что у
   карточек блока «Создавайте, мы поддержим» */
.offer-card--api {
  justify-content: center;
  background: var(--color-neutral-03);
  transition: border-color var(--duration-base) var(--ease-brand);
}
.offer-card--api:has(.offer-card__link:focus-visible) { border-color: var(--color-neutral-56); }
@media (hover: hover) {
  .offer-card--api:hover { border-color: var(--color-neutral-56); }
}

/* Стикер вместо картинки 64×64 из макета: в макете стоит замерший кадр
   этого же ролика */
.offer-card__sticker {
  display: block;
  flex: none;
  width: 64px;
  height: 64px;
}

.offer-card__text {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-m);
  max-width: 370px;
}

.offer-card__title,
.offer-card__desc { color: var(--color-text-primary); }

.offer-card__link { align-self: start; }

/* Кликается вся карточка: ссылка растянута псевдоэлементом на
   .offer-card (у неё position: relative, у ссылки и .btn — нет).
   Ссылка в разметке одна, скринридер читает её подпись, а не всю карточку */
.offer-card__link::after,
.offer-card__cta::after {
  content: "";
  position: absolute;
  inset: 0;
}
/* Бокс иконки 16, стрелка внутри 9.5 — Link Button, Figma 930:62212 */
.offer-card__link-icon { display: grid; place-items: center; }

/* ══ 6. Создай проект вместе с нами. Figma 930:62214 ══════════════════
   В макете обводка сплошная Info/100, здесь — фирменный градиент
   Пущино: по решению Александра карточка ведёт себя как промо-карточка
   в кейсах, а у той обводка градиентная.

   Двумя фонами: белый по padding-box закрывает середину, градиент по
   border-box остаётся видным только в полосе обводки. border-image так
   не умеет — он игнорирует скругление, а карточка скруглена на 20
   со сглаживанием.

   Наведение затемняет градиент третьим слоем между белой серединой и
   самим градиентом. Слой красится переменной, а не меняется целиком:
   background-image браузер не интерполирует и переход был бы скачком,
   а зарегистрированный цвет едет плавно */
@property --offer-veil {
  syntax: "<color>";
  inherits: false;
  initial-value: transparent;
}

.offer-card--cta {
  border-color: transparent;
  background:
    linear-gradient(var(--color-surface), var(--color-surface)) padding-box,
    linear-gradient(var(--offer-veil), var(--offer-veil)) border-box,
    var(--gradient-pushchino) border-box;
  transition: --offer-veil var(--duration-base) var(--ease-brand);
}
.offer-card--cta:has(.offer-card__cta:focus-visible) { --offer-veil: var(--color-gradient-veil-hover); }
@media (hover: hover) {
  .offer-card--cta:hover { --offer-veil: var(--color-gradient-veil-hover); }
}

/* Заголовок забирает весь остаток высоты, поэтому кнопка стоит у нижнего
   края карточки, а не сразу под текстом (Figma: заголовок Fill) */
.offer-card__lead {
  flex: 1;
  color: var(--color-text-primary);
}

/* CTA: пилюля 56 с несимметричными паддингами — слева 32, справа 24,
   потому что справа стоит иконка и оптически прижимать её незачем.
   Те же значения, что у .case__hero-cta: при вклейке в styles.css эти
   два правила просятся в один класс кита */
.offer-card__cta {
  align-self: start;
  height: var(--size-button-hero);
  padding-inline: var(--spacing-xl) var(--spacing-l);
  border-radius: var(--radius-full);
  font-size: var(--text-p1);
  line-height: var(--text-p1--line-height);
  white-space: nowrap;              /* подпись не рвём на две строки */
}

/* Стикер сидит в правом нижнем углу карточки, на 14 от обоих краёв */
.offer-card__sticker--puzzlo {
  position: absolute;
  right: 14px;
  bottom: 14px;
  width: 128px;
  height: 128px;
  pointer-events: none;
}

/* Карточка интеграций занимает оба столбца сетки */
.offer-card--integrations { grid-column: 1 / -1; }

/* Обёртка правой колонки нужна только планшету: там две карточки
   едут за экраном одним куском. На остальных ступенях они — обычные
   ячейки сетки, поэтому обёртка распускается */
.offer__side { display: contents; }

/* ── Узкий экран ─────────────────────────────────────────────────────
   Макета этой ступени в Figma нет. Собрано по образцу соседних блоков:
   карточки встают друг под друга, а высота отпускается по содержимому —
   держать макетные высоты в столбце не за чем.

   Плитки интеграций при этом не масштабируются, а перестраиваются:
   ряд превращается в перенос, и колонка CRM с криптовалютой едет
   в общий поток отдельными плитками */
@media (max-width: 1279px) {
  .offer__grid { grid-template-columns: minmax(0, 1fr); gap: var(--spacing-m); }
  .offer-card { min-height: 0; }
  .offer-card--mailing,
  .offer-card--builder { min-height: 0; }

  /* Скриншот конструктора встаёт в поток под текст: абсолютом он держал
     высоту карточки на макетных 666, а в столбце карточка ужимается по
     содержимому и картинка вылезала бы за неё */
  .offer-card--builder { overflow: hidden; }
  /* Верхний auto прижимает картинку к низу карточки: в паре со сценой
     конструктор выравнивается по её высоте, и без него внизу оставалась
     бы пустота, а картинка висела бы посередине.

     В потоке ширина в процентах считается от содержимого, поэтому по бокам
     оставались поля карточки: вычитаем и паддинг, и обводку — кадр идёт
     от края до края, как на десктопе */
  .offer-card__shot {
    --shot-bleed: calc(var(--spacing-xl) + var(--size-stroke-m));
    position: static;
    order: 1;
    width: calc(100% + 2 * var(--shot-bleed));
    margin: auto calc(-1 * var(--shot-bleed)) -34px;
    /* Вуали здесь нет — текст над видео, а не на нём. Верх растворяет
       маска, иначе край кадра лёг бы под текстом ровной линией */
    -webkit-mask-image: linear-gradient(transparent, black 30%);
    mask-image: linear-gradient(transparent, black 30%);
  }
  .offer-card__veil { display: none; }

  /* Текст больше ни с чем не пересекается — плитки уходят под него.
     Раскладка коллажа отсюда и ниже одна на все узкие ступени: две
     колонки, узкая слева и широкая справа (Figma 1074:20029). Сколько
     гнёзд взять каждой, считает offer.js — отсюда и колонки по
     содержимому: их ширину задают сами плитки */
  .offer-card--integrations { display: flex; }
  .tiles {
    display: grid;
    grid-template-columns: auto auto;
    grid-auto-rows: auto;             /* сбрасываем восьмипиксельные строки */
    justify-content: center;
    align-items: start;
    gap: var(--spacing-m);
    justify-self: auto;
    /* Карточка здесь уже колонка, и align-self: end из десктопной сетки
       ужимал бы коллаж по содержимому вместо ширины карточки */
    align-self: stretch;
  }
  .tiles__col {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--spacing-m);
  }
  .tile { grid-column: auto; grid-row: auto; }
}

/* ── Планшет: та же сетка в два столбца. Figma 1074:20229 ────────────
   Воронка и конструктор стоят парой, как на десктопе, а нижний ряд
   делится пополам: слева коллаж со своей мобильной раскладкой, справа
   «Открытый API» и «Создай проект» одна под другой.

   Правая колонка вдвое ниже коллажа, поэтому едет за экраном — тем же
   приёмом, что лид «Вайбкодинга» (styles.css): цепляется за середину
   окна и уходит с ней вниз до конца ряда. Из top вычитается половина
   своей высоты, иначе по середине встал бы верхний край; высоту меряет
   offer.js и кладёт в --offer-side-h. Окно ниже самой колонки — max
   опускает её под шапку, чтобы не срезало верх */
@media (min-width: 768px) and (max-width: 1279px) {
  .offer__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* Коллаж занимает левую колонку, а не оба столбца, как на десктопе */
  .offer-card--integrations { grid-column: 1; }

  /* Макетные 316 плюс 4 на обводку: по содержимому «Создай проект»
     вышла бы ниже, и стикер Паззло наехал бы на кнопку */
  .offer-card--api,
  .offer-card--cta { min-height: 320px; }

  .offer__side {
    grid-column: 2;
    display: flex;
    flex-direction: column;
    gap: var(--spacing-m);
    position: sticky;
    align-self: start;                /* иначе колонка растянется на ряд */
    top: max(var(--size-header), calc(50svh - var(--offer-side-h, 0px) / 2));
  }
}

/* ── Коллаж уже макетного: композиция целиком с зумом ────────────────
   Ниже 500 в колонку не влезают даже макетные 2 и 3 гнезда, и резиновая
   раскладка сломалась бы до одного столбца. Поэтому здесь коллаж держит
   макетные 392×800 (1074:20029) и ужимается под карточку целиком —
   вместе с логотипами и подписями (1074:20450).

   На планшете под коллаж отведена половина ряда, и та же беда начинается
   раньше: 984 — та ширина окна, на которой карточке остаётся ровно
   макетные 392 ((984 − 32 полей секции − 32 шага) / 2 − 68 на паддинг
   и обводку). Всё, что уже, снова уходит в зум.

   Сжимает его transform; на раскладку он не влияет, поэтому высоту
   в потоке отдаёт обёртка своей пропорцией. Коэффициент считает offer.js.
   Отметки плиток снова кратны восьми — сетка из строк по 8 */
@media (max-width: 499px), (min-width: 768px) and (max-width: 983px) {
  .tiles__frame {
    position: relative;
    display: block;
    width: 100%;
    max-width: 392px;
    margin-inline: auto;
    aspect-ratio: 392 / 800;
  }

  /* Коллаж вынут из потока: в потоке он занимал бы неотмасштабированные
     800 и пропорция обёртки на высоту уже не влияла бы */
  .tiles {
    position: absolute;
    inset: 0 auto auto 0;
    grid-template-columns: 152px 224px;
    grid-auto-rows: 8px;
    align-items: start;
    column-gap: var(--spacing-m);
    row-gap: var(--spacing-none);
    width: 392px;
    height: 800px;
    transform: scale(var(--tiles-scale, 1));
    transform-origin: top left;
  }

  .tiles__col { display: contents; }


  .tile--global    { grid-column: 1; grid-row: 1 / span 37; }
  .tile--crypto    { grid-column: 1; grid-row: 40 / span 25; }
  .tile--crm       { grid-column: 1; grid-row: 67 / span 16; }
  .tile--sheets    { grid-column: 1; grid-row: 85 / span 16; }
  .tile--acquiring { grid-column: 2; grid-row: 1 / span 52; }
  .tile--ai        { grid-column: 2; grid-row: 55 / span 34; }
}

/* В узкой карточке кнопка и стикер делят одну строку по низу:
   217 подписи и 128 стикера рядом не помещаются, поэтому стикер уходит
   на ступень меньше. Граница та же, что у зума коллажа выше: на планшете
   карточка становится тесной примерно там же, где и он */
@media (max-width: 983px) {
  .offer-card__sticker--puzzlo { width: 88px; height: 88px; }
}

@media (prefers-reduced-motion: reduce) {
  .offer-card--api, .offer-card--cta { transition: none; }
}

/* ══════════════════════════════════════════════════════════════════════
   Блок «Готов начать?» — финальный призыв. Figma 1071:19740.

   Плашка 1398 со скруглением 20, внутри по центру колонка шагом 24:
   заголовок 800 → карточка ввода 800 → подпись 800. Высоту держат
   вертикальные поля 128: 128 + 60 + 24 + 144 + 24 + 56 + 128 = 564.
   ⚠️ В макете шаг 32 и высота 580 — шаг ужат до 24 по правке от 18.08.

   Фон плашки рисует шейдер на <canvas> (cta.js) — тот же, что крутится
   на dev-front.puzzlebot.top/login. Градиент ниже виден до первого кадра
   и остаётся насовсем там, где WebGL2 нет.

   Скрипт блока — cta.js, механизм поля ввода общий с hero (field.js).
   ══════════════════════════════════════════════════════════════════════ */

/* Тень плашки. В библиотеке её нет: --shadow-card набран цветом #3e4260
   под белые карточки, а здесь эффект из макета — синий #1961a4 под синей
   плашкой. Пять слоёв макета сведены к четырём: у самого дальнего
   (0 284px 39.5px) прозрачность 0, рисовать нечего */
.cta {
  /* Цвет каретки и её шлейфа. На первом экране акцент переставляет выбор
     шаблона, здесь шаблонов нет — стоит тот же, что у кнопки */
  --hero-accent: var(--color-button-primary);

  --cta-shadow:
    0 11px 12.5px rgba(25, 97, 164, 0.10),
    0 45px 22.5px rgba(25, 97, 164, 0.09),
    0 102px 30.5px rgba(25, 97, 164, 0.05),
    0 182px 36.5px rgba(25, 97, 164, 0.01);
}

/* Сверху половина шага, вторую даёт блок вопросов. Снизу целый — дальше
   футер, полоса со своей заливкой, своей доли он не вносит.
   Колонка при этом шире общей: в макете плашка 1398 при артборде 1920 —
   поля 261 против 325 у секций на 1270 */
.cta {
  max-width: calc(1398px + 2 * var(--spacing-m));
  margin-inline: auto;
  padding-block: var(--section-gap-half) var(--section-gap);
  padding-inline: var(--spacing-m);
}

.cta__plate {
  position: relative;
  overflow: hidden;                 /* холст фона обрезается скруглением */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--spacing-l);
  padding: calc(var(--spacing-xxl) * 2) var(--spacing-xxl);
  border-radius: var(--radius-xl);
  /* Фирменный Pushchino — те же #4294ff → #42c8d1, что в макете стоят
     заливкой плашки, и почти тот же угол (22.5° против «to top right») */
  background: var(--gradient-pushchino);
  box-shadow: var(--cta-shadow);
}

/* Скругление продублировано на самом холсте: overflow: hidden у плашки
   режет обычные потоки надёжно, а вот содержимое <canvas> WebKit по нему
   обрезает не всегда — в Safari углы фона оставались бы острыми */
.cta__bg {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  border-radius: inherit;
}

/* Обёртка содержимого нужна только телефону — там она залипает по
   середине плашки. Здесь её нет в раскладке вовсе, и заголовок, форма
   и подпись остаются прямыми элементами колонки плашки */
.cta__inner { display: contents; }

/* Холст лежит в потоке первым и абсолютом выпадает из него, но рисуется
   под непозиционированными соседями. Поднимаем содержимое над ним */
.cta__title,
.cta__form,
.cta__lead { position: relative; }

/* Заголовок и подпись выровнены не по центру плашки, а по началу строки
   плейсхолдера (Figma 1075:20583 и 1075:20584): макетные 24 — это ровно
   поля карточки (16) плюс поля поля ввода (8), поэтому три текста стоят
   в одну вертикаль на любой ширине */
.cta__title,
.cta__lead {
  box-sizing: border-box;
  width: min(800px, 100%);
  margin: 0;
  padding-inline-start: var(--spacing-l);
  color: var(--color-text-on-primary);
}

.cta__form { width: min(800px, 100%); }

/* Карточка ввода. Скругление и тень те же, что у hero, а вот градиентной
   обводки .input-card здесь нет: на синей плашке макет рисует карточку
   белой и без канта (1071:19742) */
.cta__card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--spacing-m);
  padding: var(--spacing-m);
  border-radius: var(--radius-input) var(--radius-input) var(--radius-input) 0;
  background: var(--color-surface);
  box-shadow: var(--shadow-card);
}
/* Поле общее с hero — стили и механизм там же (styles.css, field.js).
   Здесь только высота: в макете 1071:19743 поле однострочное, 40 = строка
   24 плюс поля 8, а на первом экране оно начинается с двух строк */
.cta__card .hero__prompt {
  min-height: calc(var(--text-h3--line-height) + var(--spacing-xs) * 2);
}

/* Пустое поле ждёт ввода: линия мигает ещё до фокуса. На первом экране
   вместо этого идёт машинопись, поэтому правило только здесь.
   :placeholder-shown — то же самое, что «в поле пусто», но без флага из JS */
.cta .hero__prompt:placeholder-shown + .hero__ghost .hero__caret {
  display: inline-block;
}

.cta__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-m);
  flex-wrap: wrap;
}

/* Мессенджеры: три площадки по 30 в ряд, поля 16/4, шаг 8 (1071:19746) */
.cta__messengers {
  display: flex;
  align-items: center;
  gap: var(--spacing-xs);
  margin: 0;
  padding: var(--spacing-xxs) var(--spacing-m);
  border: 0;
}
.cta__messenger {
  display: flex;
  padding: 0;
  border: 0;
  background: none;
  border-radius: var(--radius-full);
  cursor: pointer;
}
.cta__messenger-icon { width: var(--size-button-icon-l); height: var(--size-button-icon-l); }
/* Выключенная площадка гаснет до Basic/Disabled подменой одной заливки —
   ровно как в hero, иконки на это рассчитаны */
.cta__messenger[aria-pressed="false"] .cta__messenger-icon {
  --messenger-fill: var(--color-disabled);
}

/* Короткая подпись ждёт своего экрана. Порог 760 — тот же, на котором
   меняет подпись hero (см. narrow в hero.js) */
.cta__submit-label--short { display: none; }
@media (max-width: 760px) {
  .cta__submit-label { display: none; }
  .cta__submit-label--short { display: inline; }
}

.cta__submit-icon { width: var(--size-button-icon-m); height: var(--size-button-icon-m); fill: currentColor; }

/* Хвост реплики (1071:19758): висит на нижнем левом углу, который у
   карточки не скруглён. Обводки у карточки нет, поэтому смещение
   макетное как есть — без поправки на её толщину, в отличие от hero */
.cta__tail {
  position: absolute;
  inset-block-end: 0;
  inset-inline-start: -14px;
  width: 18px;
  height: 23px;
  color: var(--color-surface);
}

/* Пазло сидит на верхнем крае карточки справа. Рост и заход на карточку
   те же, что в hero (123×123, низ на 34 ниже края), отступ от правого
   края — макетные 70 (1071:19802). Размер и отступ вынесены в переменные:
   по ним же считается ширина заголовка, чтобы тот не заходил под робота */
.cta {
  --cta-sticker-size: 123px;
  --cta-sticker-inset: 70px;
}
.cta__sticker {
  position: absolute;
  inset-block-start: -89px;
  inset-inline-end: var(--cta-sticker-inset);
  width: var(--cta-sticker-size);
  height: var(--cta-sticker-size);
  pointer-events: none;
}

@media (max-width: 1279px) {
  /* Поля 64 съедают на планшете половину ширины карточки — ужимаем вдвое */
  .cta__plate { padding-inline: var(--spacing-xl); }
}

@media (max-width: 767px) {
  /* Плашка идёт от края до края экрана: боковые поля снимаем у секции,
     а скругление — у самой плашки, иначе по краям экрана торчали бы
     срезанные углы */
  /* Снизу плашка встык с футером: между двумя заливками зазор читался бы
     полосой фона страницы, а не воздухом блока */
  .cta { padding-inline: var(--spacing-none); padding-block-end: var(--spacing-none); }

  /* Вертикальные поля те же 128, что на десктопе. Боковые — 24: на
     десктопе этот отступ несёт сам заголовок, здесь он переехал на плашку,
     чтобы по нему же встали карточка и подпись. В них слева помещается
     хвост карточки */
  .cta__plate {
    padding-inline: var(--spacing-l);
    border-radius: 0;
    /* Обрезка холста нужна была скруглению, а его здесь нет. Заодно
       уходит и помеха: с overflow: hidden плашка становится скролл-портом
       для содержимого, и залипание ниже не сработало бы вовсе */
    overflow: visible;
    /* Тень уходит вместе со скруглением: у плашки от края до края и
       встык с футером её просто негде увидеть */
    box-shadow: none;
  }

  /* Содержимое идёт обычным потоком. До 11.09.2026 плашка была ростом
     в два экрана, а содержимое проходило её насквозь, залипнув по середине
     окна: залипание убрано вместе с остальной анимацией по прокрутке */
  .cta__inner {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--spacing-l);
    width: 100%;
  }

  /* E1 (56/60) в узкую колонку не встаёт, подпись H2 — тоже впритык.
     Берём следующие ступени шкалы, как это сделано у hero */
  .cta__title {
    font-size: var(--text-h1);
    line-height: var(--text-h1--line-height);
    letter-spacing: var(--text-h1--letter-spacing);
  }
  .cta__lead {
    font-size: var(--text-h3);
    line-height: var(--text-h3--line-height);
    letter-spacing: var(--text-h3--letter-spacing);
  }
  .cta__lead br { display: none; }

  /* Пазло торчит над карточкой на 89 и на узком экране встаёт вровень
     с заголовком. Разводим их не отступом, а по горизонтали: заголовок
     обрезан по левому краю робота, между ними шаг 16 */
  .cta__title {
    padding-inline-end: calc(var(--cta-sticker-size) + var(--cta-sticker-inset) + var(--spacing-m));
  }

  /* Поле в две строки: в одну на узкой колонке не помещается ни подсказка,
     ни короткий запрос */
  .cta__card .hero__prompt { min-height: calc(var(--text-h3--line-height) * 2 + var(--spacing-xs) * 2); }

  /* Колонка во всю ширину плашки: запас под хвост дают её боковые поля,
     ужимать саму карточку больше не нужно */
  .cta__title,
  .cta__lead,
  .cta__form { width: 100%; }

  /* Ряд остаётся строкой: мессенджеры слева, кнопка на весь остаток */
  .cta__messengers { padding-inline: var(--spacing-xs); }
  .cta__row { gap: var(--spacing-xs); }
  .cta__submit { flex: 1; }

  /* Пазло тот же, только ближе к краю: 32 вместо 70 */
  .cta { --cta-sticker-inset: var(--spacing-xl); }
}

/* iPhone SE и mini (375): строка сходится только без боковых полей
   у площадок — кнопке нужно место под полную подпись */
@media (max-width: 380px) {
  .cta__messengers { padding-inline: 0; }
}

/* Отступ залипания отдельным свойством: sticky-fix.js читает его при любом
   position, а из top прочитать нельзя — у static и absolute он auto.
   Регистрация нужна, чтобы calc считался в пиксели, а не оставался строкой.

   ⚠️ ОТКЛЮЧЕНО 11.09.2026 вместе с sticky-fix.js — свойство читал только он.
@property --stick-top {
  syntax: "<length>";
  inherits: false;
  initial-value: 0px;
}
*/

/* ══ Обход бага sticky в Safari на iOS ══════════════════════════════════
   ⚠️ ОТКЛЮЧЁН 11.09.2026 вместе с sticky-fix.js: все мобильные залипания
   убраны, и подопечных у обхода не осталось. Порядок возврата и разбор
   самого бага — в шапке sticky-fix.js.

   Раскладка ниже описывала три состояния, которые переключал скрипт.
   Маркеры внутренних комментариев сняты — в CSS они не вкладываются.

@media (max-width: 767px) {
   Якорь для absolute: своего position у секций-спейсеров нет 
  .sticky-fix .work,
  .sticky-fix .cases { position: relative; }

  .sticky-fix .work__viewport,
  .sticky-fix .cases__stage {
    position: absolute;
    inset-inline: 0;
    inset-block-start: 0;
  }

   Путь идёт — сцена держится окна 
  .sticky-fix .work__viewport[data-stuck="on"],
  .sticky-fix .cases__stage[data-stuck="on"] { position: fixed; }

   Путь кончился — сцена остаётся в хвосте спейсера 
  .sticky-fix .work__viewport[data-stuck="end"],
  .sticky-fix .cases__stage[data-stuck="end"] {
    inset-block-start: auto;
    inset-block-end: 0;
  }

   Обычные залипающие блоки. Отступ залипания остаётся за CSS — у
     .cta__inner он считается от переменной, которую ставит cta.js 
  .sticky-fix [data-stuck-item="before"] { position: static; }
  .sticky-fix [data-stuck-item="on"] { position: fixed; }
  .sticky-fix [data-stuck-item="end"] {
    position: absolute;
    inset-block-start: auto;
    inset-block-end: 0;
  }
}
   ══════════════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════════════
   СТРАНИЦА ТАРИФОВ. Figma 1327:4221, десктопный фрейм 1306:5443.

   Здесь только раскладка. Сами блоки — компоненты дизайн-системы:
   .segmented, .plan-card, .stepper, .ai-badge, .sale-banner,
   .note-row, .info-tile, .faq-item. Ни одного цвета и ни одного размера
   компонента в этом файле быть не должно.
   ══════════════════════════════════════════════════════════════════════ */

.price {
  /* Полоса содержимого 1270: в макете 1920 поля по 325. Это не
     --container-landing (1290) — у страницы тарифов своя ширина, и
     подгонять её под другую секцию значит разъехаться с макетом.
     Расходится с макетом — править здесь, в одном месте */
  --price-max: 1270px;
  /* Внутреннее поле полосы: переключатели и сноски отступают от края
     сильнее, чем карточки. В макете это 32 внутри 1270 */
  --price-inset: var(--spacing-xl);

  /* Поля прибавлены к ширине, а не вычтены из неё: при box-sizing:
     border-box max-width 1270 дал бы полосу 1238, и четвёртая плитка
     перестала бы вмещать заголовок в одну строку */
  max-width: calc(var(--price-max) + 2 * var(--page-padding-desktop));
  margin-inline: auto;
  padding-inline: var(--page-padding-desktop);
  padding-block: 128px var(--spacing-xxl);
}

.price__title {
  text-align: center;
  margin-block-end: var(--spacing-xxl);
}

/* Баннер акции: он есть на странице не всегда, поэтому отступ снизу живёт
   на нём самом, а не на соседе — нет баннера, нет и пустоты */
.price .sale-banner {
  margin-inline: var(--price-inset);
  margin-block-end: var(--spacing-xl);
}

/* ── Переключатели ────────────────────────────────────────────────────
   Период слева, валюта справа. Между ними в макете пусто — блоки
   разъезжаются по краям полосы, а не выравниваются по карточкам */
.price__controls {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-m);
  flex-wrap: wrap;
  margin-inline: var(--price-inset);
  margin-block-end: var(--spacing-xl);
}

.price__flag { flex: none; border-radius: var(--radius-full); }

/* Подпись периода в одном узле: между «мес» и хвостом «яца» не должен
   встать gap сегмента — он стоит между подписью и плашкой процента */
.segmented__text { white-space: nowrap; }

/* Валюта держится правого края полосы, даже когда съехала на свою строку:
   в одну строку её и так разводит space-between, а после переноса без
   этого правила она жалась бы к левому краю под периодом */
.price__controls .segmented:last-child { margin-inline-start: auto; }

/* Валюта одними значками: флаг и глобус узнаются без подписи, а пара
   переключателей снова встаёт в одну строку. Класс ставит price.js —
   решает не ширина экрана, а то, перенеслась строка или нет: в неё
   входят ещё и плашки скидок, а они появляются и исчезают */
.price__controls--icons .segmented__label { display: none; }

/* ── Тарифы ───────────────────────────────────────────────────────── */
.price__plans {
  display: grid;
  /* Три в ряд до самого телефона, но доли неравные. Первые две
     minmax(0, 1fr) — сжимаются свободно: у 1fr нижняя граница min-content,
     и цена в 56px не дала бы колонке сузиться, страница уехала бы вбок.

     Третья — minmax(min-content, 1fr): «Про» перестаёт делиться поровну
     и забирает у соседей ровно столько, сколько нужно его калькулятору.
     Числом этот минимум не записан: min-content карточки и есть строка
     «Пользователей + степпер» — слово не переносится, степпер не жмётся,
     и браузер считает 348 сам. Поменяется ширина степпера — минимум
     поедет за ней, править нечего */
  grid-template-columns: repeat(3, minmax(min-content, 1fr));
  /* Ниже своего содержимого карточки не сжимаются: min-content это
     строка «Пользователей + степпер» в «Про», кнопка в неразрывную
     строку у соседей и бейдж «Puzzlo AI» рядом с заголовком. Перестало
     помещаться — ряд едет вбок скроллом, а не ломает карточки.
     Скроллится только ряд: страница вбок не уезжает */
  overflow-x: auto;
  /* Поля полосы перенесены внутрь скролл-бокса: иначе карточка на краю
     обрывается по границе контейнера, будто её обрезали, а так она
     спокойно уезжает под край экрана */
  margin-inline: calc(var(--page-padding-desktop) * -1);
  padding-inline: var(--page-padding-desktop);
  /* Тень фокуса и обводки карточек не должны срезаться скролл-боксом */
  padding-block: var(--size-stroke-m);
  gap: var(--spacing-xl);
  /* stretch, а не start: описания у тарифов разной длины, и без
     растягивания цена в каждой карточке встаёт на своей высоте.
     В макете этого не видно — там у всех трёх рыба одинаковой длины */
  align-items: stretch;
  /* До плиток 64: в макете 1306:5443 ряд кончается на 810, плитки
     начинаются на 874 */
  margin-block-end: var(--spacing-xxl);
}
/* Цена и кнопка прижимаются к низу верхней части, описание добирает
   остаток. Так три цены стоят на одной линии, а списки лимитов
   начинаются на одной высоте — без единого магического числа */
.price__plans .plan-card { height: 100%; }
.price__plans .plan-card__top { flex: 1; }
.price__plans .plan-card__desc { flex: 1; }

/* ── Плитки ───────────────────────────────────────────────────────────
   Четыре равные колонки с вертикальными линиями между ними. Линия —
   элемент разметки, а не border у плитки: у крайних плиток бордюра быть
   не должно, а :not(:last-child) на четырёх колонках ломается в адаптиве,
   когда их становится две */
/* Плитки и линии между ними — один блок, а не ряд элементов вперемешку
   с <hr>. Линия рисуется самой плиткой слева и гаснет у первой в ряду:
   при любой раскладке — четыре в строку, две или одна — линии стоят
   ровно между соседями, и правило про них одно на всю сетку */
.price__tiles {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin-block-end: 128px;
}
.price__tiles .info-tile { position: relative; }
/* Эмодзи плиток — ролики, их рисует tiles.js в svg внутри бокса. Размер
   задан самому боксу в дизайн-системе, поэтому до загрузки заголовок не
   прыгает; svg только убирает строчный зазор под собой */
.price__tiles .info-tile__emoji svg { display: block; }
.price__tiles .info-tile::before {
  content: '';
  position: absolute;
  inset-block: 0;
  inset-inline-start: 0;
  width: var(--size-stroke-m);
  /* Линия набирает цвет к середине и растворяется к обоим концам —
     Figma 1314:2675. Тот же приём, что у разделителя цифр платформы */
  background: linear-gradient(to bottom, transparent, var(--color-divider) 50%, transparent);
}
/* Горизонтальная линия между рядами. Появляется у плиток второго ряда —
   у каждой, а не одной на ширину блока: так она делится вместе с сеткой
   и не повисает над пустотой, когда рядов больше двух */
/* Геометрия горизонтали — одна на все раскладки, включают её медиазапросы
   одним content. Раньше она жила только у второго ряда планшетной сетки,
   и на телефоне линия у второй плитки получала цвет, но не размеры —
   между первой и второй плиткой её не было */
.price__tiles .info-tile::after {
  content: none;
  position: absolute;
  inset-inline: 0;
  inset-block-start: 0;
  height: var(--size-stroke-m);
}
.price__tiles .info-tile:nth-child(4n + 1)::before { content: none; }
/* Вторая строка текста в плитке — обычно ссылка «Подробнее». Свой зазор
   ей не нужен: между строками уже стоит gap плитки */
.price__tiles .info-tile__body + .info-tile__body { margin-block-start: calc(var(--spacing-m) * -1 + var(--spacing-xs)); }

/* ── FAQ ──────────────────────────────────────────────────────────────
   Слева колонка шириной с тарифную карточку, справа список. Зазор тот же,
   что между карточками, — блок читается как продолжение сетки выше */
.price-faq {
  display: grid;
  /* Доли, а не 402px: с фиксированной колонкой на планшете левая часть
     забирала половину полосы, и список справа оказывался зажат. На
     главной та же пара долей, блоки должны читаться одинаково */
  grid-template-columns: minmax(0, var(--faq-lead-column)) minmax(0, var(--faq-list-column));
  gap: var(--spacing-xl);
}
.price-faq__lead {
  padding: var(--spacing-xl);
}
.price-faq__sticker {
  display: block;
  width: var(--size-button-icon-xxl);
  height: var(--size-button-icon-xxl);
  margin-block-end: var(--spacing-xl);
}
.price-faq__sticker svg { display: block; }

/* Шеврон строки: закрытая смотрит вправо, открытая — вниз. Поворот, а не
   вторая иконка: рисунок один и тот же */
.faq-item[open] > .faq-item__title .faq-item__icon { transform: rotate(90deg); }
.faq-item__icon { transition: transform var(--duration-fast) var(--ease-brand); }
.faq-item__title::-webkit-details-marker { display: none; }
.faq-item__title { list-style: none; }

/* ── Планшет ──────────────────────────────────────────────────────────
   Ряд из трёх держится до телефона — карточки ужимаются ступенями, и
   первой жертвуется не «Про»: в нём калькулятор, и ему нужна ширина.

   Порядок уступок задан Александром 11.09.2026: сначала подпись «без
   автоплатежа» уходит под цену (это делает сам flex-wrap, без правил),
   потом цена садится с Extra на H1, и только потом «Бесплатный» со
   «Стартом» ужимаются в пользу «Про» — за это отвечает minmax выше.

   Плитки встают 2×2, вертикальные линии между парами гаснут: вертикаль
   между двумя рядами ничего не разделяет */
@media (max-width: 1279px) {
  .price { padding-block-start: var(--spacing-xxl); }

  /* Две колонки: линия возвращается третьей плитке и гаснет у первой
     в каждой строке */
  .price__tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .price__tiles .info-tile::before { content: ''; }
  .price__tiles .info-tile:nth-child(2n + 1)::before { content: none; }
  /* Крест 2×2 сходится к центру — Figma 1058:19312. Линия у каждой плитки
     своя, а не одна на блок: ряды разной высоты, и только по кромке ячейки
     линия встаёт ровно на стык. Поэтому цвет у каждой половины набирается
     к середине блока и растворяется наружу — вертикаль у верхнего ряда
     книзу, у нижнего кверху */
  .price__tiles .info-tile:nth-child(-n + 2)::before {
    background: linear-gradient(to bottom, transparent, var(--color-divider));
  }
  .price__tiles .info-tile:nth-child(n + 3)::before {
    background: linear-gradient(to bottom, var(--color-divider), transparent);
  }
  /* Второй ряд отбивается горизонталью: у левой плитки цвет к правому
     краю, у правой — к левому, и обе сходятся в центре креста */
  .price__tiles .info-tile:nth-child(n + 3)::after {
    content: '';
    background: linear-gradient(to right, transparent, var(--color-divider));
  }
  .price__tiles .info-tile:nth-child(2n)::after {
    background: linear-gradient(to right, var(--color-divider), transparent);
  }

  /* Блок вопросов ужимает зазор, но остаётся в две колонки — так же,
     как FAQ главной: разворот в одну там начинается только на телефоне */
  .price-faq { gap: var(--spacing-m); }
}

/* Подпись съехала под цену — значит съехала во всех трёх карточках.
   Класс ставит price.js: он же решает, когда подписи перестали
   помещаться рядом с ценой, потому что цена «Про» зависит от степперов */
.price__plans--stacked .plan-card__price-row {
  flex-direction: column;
  align-items: flex-start;
  gap: var(--spacing-xxs);
}
.price__plans--stacked .plan-card__note { text-align: left; }

/* Ряд стал тесен — цена садится на H1, и вместе с ней уходит всё, без
   чего карточка читается: описание тарифа, слово «Puzzlo» в бейдже и
   половина зазора между карточками.

   Ставит класс price.js, а не медиазапрос: ширина экрана про тесноту не
   говорит. На 1024 карточка выходит 300, и «3 590 ₽» в Extra там стоит
   свободно — жать его по брейкпоинту значило бы мельчить на ровном месте.
   Скрипт же смотрит на то, что есть: помещается ряд в полосу или нет */
.price__plans--tight .plan-card__price {
  font-size: var(--text-h1);
  line-height: var(--text-h1--line-height);
  letter-spacing: var(--text-h1--letter-spacing);
}
/* Зазор ряда — то, что отдаём вместо поля карточки: поле с 12.09.2026
   ниже 16 не опускается, и на планшете в портрете ряд из трёх дотягивает
   до полосы ровно этими двенадцатью */
.price__plans--tight { gap: var(--spacing-s); }
.price__plans--tight .plan-card__desc { display: none; }
.price__plans--tight .ai-badge__brand { display: none; }
/* Без описания кнопка отходит от цены на столько же, на сколько цена
   отошла от названия — иначе она висит в карточке отдельно от блока,
   к которому относится. Свой отступ кнопки снят, остаётся gap карточки */
.price__plans--tight .plan-card__cta { margin-block-start: 0; }
/* Подписи кнопок теряют хвост: «Начать бесплатно» → «Начать»,
   «Попробовать 7 дней» → «Попробовать». Подпись кнопки задаёт минимум
   карточки, и на планшете в портрете это те самые сто пикселей, из-за
   которых ряд не вставал целиком */
.price__plans--tight .plan-card__cta-more { display: none; }
/* Строки лимитов садятся на ступень ниже. Именно они держат ширину:
   «Пользователей» со значением это 182 из 201, что остаётся карточке на
   планшете в портрете. В P2 строка занимает 162, и ряд из трёх наконец
   встаёт в 736 целиком, без скролла */
.price__plans--tight .plan-card__row {
  font-size: var(--text-p2);
  line-height: var(--text-p2--line-height);
}
/* Поле карточки — последнее, что отдаём. Ниже 16 не опускается: это
   минимум внутреннего поля на всей странице, Александр 12.09.2026.
   Ряд, которому не хватило и этого, уезжает в скролл */
.price__plans--tight .plan-card { --plan-pad: var(--spacing-m); }

/* Ниже 1024 поля карточки и зазор ряда уходят на ступень: иначе соседям
   «Про» не остаётся места на кнопку, и её подпись рвётся на две строки */
@media (max-width: 1023px) {
  .plan-card { --plan-pad: var(--spacing-l); }
}

/* Последняя ступень полей перед телефоном. Кегль кнопки при этом не
   трогаем: подпись «Попробовать 7 дней» входит в минимальную ширину
   карточки, и ряд скорее уедет в скролл, чем кнопка станет мельче */
@media (max-width: 959px) {
  .plan-card { --plan-pad: var(--spacing-m); }
}

/* ── Телефон ──────────────────────────────────────────────────────────
   Карточки в колонку в порядке макета: бесплатный, старт, про. Цена 56px
   вместе с подписью в 360 не помещается — уходит на ступень вниз, а
   подпись встаёт под неё, а не сбоку */
@media (max-width: 767px) {
  .price {
    --price-inset: 0px;
    padding-block: var(--spacing-xl) var(--spacing-xl);
  }

  /* Заголовок страницы — телефонная ступень Extra, как и цена ниже */
  .price__title {
    font-size: var(--text-e1-mobile);
    line-height: var(--text-e1-mobile--line-height);
  }

  /* Карточки друг под другом во всю полосу. Поле карточки остаётся
     минимальным: в колонке она во всю ширину экрана, и 16 по краям
     дают тексту больше места, чем рамка в 32 */
  .price__plans {
    grid-template-columns: 1fr;
    gap: var(--spacing-m);
  }
  .plan-card { --plan-pad: var(--spacing-m); }

  /* Бренд в бейдже возвращается: сокращали его из-за тесноты в ряду, а в
     колонке карточка снова во всю полосу — «Puzzlo AI» встаёт рядом с
     заголовком с запасом */
  .ai-badge__brand { display: inline; }

  /* Описание снято: в колонке человек читает подряд все три карточки, и
     три абзаца между ценами превращают выбор в чтение. Лимиты внизу
     карточки говорят то же самое короче. Вместе с описанием уходит и
     свой отступ кнопки — см. .price__plans--tight выше */
  .plan-card__desc { display: none; }
  .plan-card__cta { margin-block-start: 0; }

  /* Цена — телефонная ступень той же шкалы (Extra 42/46 против 56/60):
     карточка шире, чем колонка в ряду, и H1 здесь уже мелковат */
  .plan-card__price {
    font-size: var(--text-e1-mobile);
    line-height: var(--text-e1-mobile--line-height);
  }

  /* Переключатели разнесены по краям полосы. В строку вдвоём они на
     телефоне не встают: с плашками скидок период занимает 253 из 358,
     валюта ещё 160 — и второй уходит на свою строку. Прижимаем его
     вправо, чтобы пара держала края, а не жалась к левому */
  .price__controls { justify-content: space-between; gap: var(--spacing-xs); }
  .price__controls .segmented__item { padding-inline: var(--spacing-xs); }
  /* Пилюля процента тоже на ступень уже: с ней переключатель периода
     вырастает на 32, и пара перестаёт помещаться в строку */
  .price__controls .segmented__pill { padding-inline: var(--spacing-xs); }
  .price__controls .segmented__item:has(.segmented__pill:not([hidden])) {
    padding-inline-start: var(--spacing-s);
  }


  .price__tiles {
    grid-template-columns: minmax(0, 1fr);
    margin-block-end: var(--spacing-xxl);
  }
  .price__tiles .info-tile { padding-inline: 0; }
  .price__tiles .info-tile::before { content: none; }
  /* В одной колонке плитки отбиваются горизонталью — каждая, кроме
     первой: делить сверху нечего. Линия набирает цвет к середине и
     растворяется к обоим краям, Figma 1061:19703 */
  .price__tiles .info-tile:not(:first-child)::after {
    content: '';
    background: linear-gradient(to right, transparent, var(--color-divider) 50%, transparent);
  }

  /* Телефон: одна колонка, стикер снят — его ролик тогда даже не
     скачивается, наблюдатель ждёт появления скрытого бокса в экране.
     Переносы заголовка рассчитаны на колонку 402, здесь строку ломает
     ширина экрана */
  .price-faq { grid-template-columns: minmax(0, 1fr); }
  .price-faq__lead { padding: 0; }
  .price-faq__sticker { display: none; }
  .price-faq__title br { display: none; }

}

/* ── Узкий телефон ────────────────────────────────────────────────────
   Перенос «без автоплатежа» в подписи гасится: на 360 строка и так
   ломается сама, а лишний <br> делает из подписи лесенку */
@media (max-width: 380px) {
  .plan-card__note br { display: none; }

  /* Строка «Пользователей + степпер» не сжимается: слово не переносится,
     кнопки не жмутся. На 360 карточка упиралась в неё и вылезала за
     экран — колонка числа возвращается к макетным 90. «10 000 000» в них
     не помещается целиком, но на этой ширине выбор такой: обрезанный
     хвост в поле или горизонтальный скролл всей страницы. Поле карточки
     на ступень больше не уходит: 16 — минимум, он стоит уже с 767 */
  .stepper { --size-stepper-value: 90px; }

  /* Последняя защита для совсем узких (320): если строка не влезла даже
     так, степпер уходит под подпись, а не выталкивает страницу вбок.
     Правило живёт только здесь — в ряду из трёх перенос сломал бы
     минимум колонки «Про», которым держится вся раскладка */
  .plan-card__row { flex-wrap: wrap; }
  .plan-card__row .stepper { margin-inline-start: auto; }
}

/* ══════════════════════════════════════════════════════════════════════
   СТРАНИЦА ТАРИФОВ СТАРОЙ СЕТКИ — /price.

   Отличий от блока выше ровно три: карточек четыре вместо трёх, под
   плитками стоит таблица различий, и адаптив у карточек свой. Всё
   остальное — те же .price__* , модификатор их только переопределяет.

   Правила лежат ниже блока /price и весом выше на один класс, поэтому
   перекрывают его, включая медиазапросы: у .price--old своя ступень
   там, где у /price карточки уходят в колонку.
   ══════════════════════════════════════════════════════════════════════ */

/* Четыре колонки в той же полосе 1270: (1270 − 3 × 32) / 4 = 293 на
   карточку. Полосу не расширяем — она общая с /price, и разъезжаться
   страницам нельзя */
.price--old .price__plans {
  grid-template-columns: repeat(4, minmax(0, 1fr));
  /* Зазор вдвое меньше, чем у трёх карточек /price: четвёртая съедает
     место, и 32 между ними оставляли карточке 293 против 305 сейчас */
  gap: var(--spacing-m);
}
/* Поле карточки 16 на любой ширине, включая десктоп — решение Александра
   12.09.2026. Четырём карточкам в полосе 1270 тесно: при 32 под
   содержимое оставалось 229, и «Подписчиков на проект» ломалось об
   «20 000» в каждой */
.price--old .plan-card { --plan-pad: var(--spacing-m); }

/* ── Периоды ───────────────────────────────────────────────────────────
   Четыре отдельные пилюли вместо одной общей: у невыбранной своя обводка,
   у выбранной заливка того же цвета, что и кант, — поэтому по высоте они
   вровень с переключателем валюты, где кант один на всю группу.

   Раскладка как на /price, решение Александра 13.09.2026: пилюли шириной
   по содержимому и валюта — один поток. Влезает в строку — периоды слева,
   валюта справа. Не влезает — валюта встаёт первой, пилюли переносятся
   за ней по одной. Шаг выбирает price.js, классы на .price__controls.
   ponytail: группа периодов display: contents, иначе пилюли не делят
   строку с валютой. В старых браузерах такая группа могла теряться для
   скринридера — если всплывёт, пилюли выносятся в разметку рядом с валютой */
.price--old .price__controls { justify-content: flex-start; gap: var(--spacing-xs); }
.price--old [data-switch="period"] { display: contents; }
.price--old .price__controls--row { flex-wrap: nowrap; }
.price--old .price__controls--first [data-switch="currency"] { order: -1; margin-inline-start: 0; }
.price--old [data-switch="period"] .segmented__item {
  justify-content: center;
  border: var(--size-stroke-m) solid var(--segmented-stroke, var(--entity-bot-wash));
  padding-inline: var(--spacing-m);
}
/* Поля пилюль одни на всех ширинах. Телефонные правила переключателей
   (ниже 768 поля сегмента и плашки на ступень уже) писались под общую
   группу страницы новой сетки. Здесь они давали лесенку: на 768 строка
   просила 672 при полосе 672 и уходила в 2×2, а на 700 уже с узкими
   полями снова влезала в строку. Весом эти правила выше общих, поэтому
   действуют и внутри медиазапроса */
.price--old [data-switch="period"] .segmented__item:has(.segmented__pill:not([hidden])) {
  padding-inline: var(--spacing-m) var(--spacing-xxs);
}
.price--old [data-switch="period"] .segmented__pill { padding-inline: var(--spacing-s); }
.price--old .plan-card__list { padding-block: var(--spacing-m); }

/* Верх карточки и список лимитов встают на одну линию во всех четырёх:
   ряд отдаёт карточке две свои строки, карточка берёт их через subgrid.
   Без этого «Пользователей на проект» переносится рядом с «10 000», но
   не рядом с «150», список растёт на строку, и цена в этой карточке
   уезжает выше соседних. Растягиванием через flex это не лечится —
   высоту задаёт нижняя часть, а равнять надо границу между частями */
.price--old .price__plans { grid-template-rows: auto auto; }
.price--old .plan-card {
  display: grid;
  grid-row: span 2;
  grid-template-rows: subgrid;
  /* Subgrid наследует зазор ряда, и между верхом и списком вставал лишний
     16: над лимитами выходило 32, под ними 16. Отбивку списка задают
     только его поля */
  row-gap: 0;
}
/* Строки лимитов на ступень мельче: «Пользователей на проект» рядом с
   «20 000» в колонку 305 в один ряд не встаёт, и список в одной карточке
   оказывается на строку длиннее соседних. В P2 строка занимает те же
   162, что и в тесном ряду на /price, — переносов не остаётся ни в одной
   из четырёх. На телефоне карточка во всю полосу, там кегль штатный */
@media (min-width: 768px) {
  .price--old .plan-card__row {
    font-size: var(--text-p2);
    line-height: var(--text-p2--line-height);
  }
}

/* ── Таблица различий ──────────────────────────────────────────────────
   Заголовок по центру, как у первого экрана страницы */
.price__table-title {
  text-align: center;
  margin-block: var(--spacing-xxl);
}
.price__table {
  margin-inline: var(--price-inset);
  margin-block-end: 128px;
}

/* ── Планшет ──────────────────────────────────────────────────────────
   Четыре в ряд не влезают — сетка 2×2, а не горизонтальный скролл:
   решение Александра 13.09.2026, за скроллом половину тарифов не видно.
   Subgrid продолжает работать: вторая пара карточек берёт строки 3–4,
   неявные строки грида те же auto */
@media (max-width: 1279px) {
  .price--old .price__plans { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  /* Таблица влезает целиком, без прокрутки: колонка с названием ужата
     до трети, и длинные названия переносятся в две-три строки. Прокрутка
     здесь хуже переноса — из четырёх тарифов в кадр попадало два, и
     сравнивать приходилось по памяти.

     table-layout: fixed обязателен: при автоматической разметке ширина
     считается по содержимому, и заданные проценты браузер игнорирует,
     как только название длиннее колонки */
  .price__table {
    margin-inline: 0;
    /* Страховка: при длинном названии тарифа колонки перестанут делиться
       и таблица вылезет. Пусть тогда прокручивается она, а не страница */
    overflow-x: auto;
  }
  .price__table .limits-table { table-layout: fixed; }
  .price__table .limits-table :is(th, td) { padding: var(--spacing-xs) var(--spacing-s); }
  /* Ширина задаётся ячейке шапки, а не строке с названием: при
     table-layout: fixed колонки меряются по первой строке таблицы, и
     width на th[scope="row"] из tbody браузер не смотрит вовсе —
     первая колонка оставалась такой же, как четыре тарифных */
  .price__table .limits-table thead :is(th, td):first-child { width: 30%; }

  /* Имя тарифа в шапке — мельче содержимого. «Профессиональный» одним
     куском просит 154px при колонке в 125, и таблица распирается до 822,
     хотя влезает вся. Кегль решает это без переносов: рвать имя тарифа
     по слогам браузер предлагает как «Професси-ональный» — словарь
     расставляет их по своему разумению, и на витрине это читается хуже
     мелкого шрифта. Переносы оставлены только названиям строк слева,
     они из нескольких слов и рвутся по пробелу */
  .price__table .limits-table thead th {
    font-size: var(--text-p1);
    line-height: var(--text-p1--line-height);
  }
  .price__table .limits-table th[scope="row"] { hyphens: auto; }
}

/* ── Телефон ──────────────────────────────────────────────────────────
   Карточки в колонку: на 390 карточка в 320 смотрится обрезанной, и
   горизонтальный скролл спорит с вертикальным пальцем */
@media (max-width: 767px) {
  .price--old .price__plans {
    grid-auto-flow: row;
    grid-auto-columns: auto;
    grid-template-columns: 1fr;
    overflow-x: visible;
    padding-block-end: 0;
  }


  /* ── Таблица на телефоне ────────────────────────────────────────────
     Четыре колонки в 390 не живут никак, поэтому таблица разворачивается
     в список: строка становится блоком, её название — заголовком блока,
     а под ним идут значения, каждое подписано своим тарифом из
     data-label. Приём и разметка те же, что у .table дизайн-системы,
     только там подпись стоит над значением, а здесь — слева от него:
     значения короткие, и парой в строку они читаются быстрее */
  .price__table .limits-table { table-layout: auto; }
  .price__table .limits-table thead { display: none; }
  .price__table .limits-table :is(table, tbody, tr, th, td) { display: block; }

  .price__table .limits-table tbody tr {
    padding: var(--spacing-s) var(--spacing-m);
    margin-block-end: var(--spacing-xs);
    border-radius: calc(var(--radius-m) * var(--corner-scale));
    corner-shape: var(--corner-smooth);
  }
  /* Фон возвращается на строку: в списке она снова один блок, и
     скруглять отдельные ячейки больше не нужно */
  .price__table .limits-table tbody tr:nth-child(odd) { background: var(--color-info-01); }
  .price__table .limits-table tbody tr:nth-child(odd) :is(th, td) {
    background: none;
    border-radius: 0;
  }

  .price__table .limits-table th[scope="row"] {
    width: auto;
    padding: 0 0 var(--spacing-xs);
    font-weight: var(--font-weight-body-medium);
  }
  /* Подпись слева, значение справа — тот же рисунок строки, что в
     карточке тарифа выше */
  .price__table .limits-table td {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--spacing-m);
    padding: var(--spacing-xxs) 0;
    text-align: end;
  }
  /* Подпись приглушена, значение — основным цветом: так же расставлены
     веса у .table дизайн-системы, там метка ячейки тоже вторична */
  .price__table .limits-table td::before {
    content: attr(data-label);
    color: var(--color-text-secondary);
  }
  /* Иконка встаёт в правый край пары, а не по центру блока */
  .price__table .limits-table__yes { margin-inline: 0 0; }

  .price__table { margin-block-end: var(--spacing-xxl); }
}

/* ==========================================================================
   Плашка согласия на cookie. Figma 1417:5372.

   Общий элемент сайта, как шапка и футер: разметка лежит в
   landing/partials/footer.html и разносится оттуда по всем страницам —
   и лендинга, и базы знаний (kb подключает этот файл).

   Пилюля висит по центру нижнего края, в 16 от него. Центрируется не
   через translate, а inset-inline + margin-inline: auto: так ширина сама
   упирается в экран минус поля, и на узком телефоне текст переносится
   вместо того, чтобы выехать за край.

   z-index 20 — тот же слой, что у подсказки doc.css: над всем контентом.
   С шапкой (10) не спорит, они на разных краях экрана.
   ========================================================================== */
.cookie {
  position: fixed;
  z-index: 20;
  inset-block-end: var(--spacing-m);
  inset-inline: var(--page-padding-mobile);
  margin-inline: auto;
  width: fit-content;
  display: flex;
  align-items: center;
  gap: var(--spacing-m);
  padding: var(--spacing-m) var(--spacing-l);
  border: var(--size-stroke-m) solid var(--color-info-05);
  border-radius: var(--radius-full);
  background: var(--color-info-01);
  box-shadow: var(--shadow-cookie);
}
/* Плашки нет до того, как скрипт проверит согласие: display: flex выше
   перебил бы штатное скрытие [hidden] */
.cookie[hidden] { display: none; }

/* Печенье и кнопка держат свой размер, сжимается только текст */
.cookie .icon,
.cookie .btn { flex: none; }

.cookie__text { margin: 0; }

/* ── Телефон ──────────────────────────────────────────────────────────
   Строка «иконка + текст + кнопка» на 390 не сходится с макетными
   отступами: 24 по бокам и 16 в зазорах дают 350 при доступных 354 —
   впритык, а при системном крупном шрифте уже за краем. Ужимаем паддинг
   и зазор на ступень, кегль и высота кнопки остаются макетными */
@media (max-width: 767px) {
  .cookie {
    gap: var(--spacing-xs);
    padding: var(--spacing-xs) var(--spacing-m);
  }
}

/* ══════════════════════════════════════════════════════════════════════
   СТРАНИЦА «НОВЫЕ ТАРИФЫ», price-new-info.html.

   Статья базы знаний на лендинге: колонка 800 и блоки из kb.css, а ряд
   тарифов выходит за колонку на полосу /price. Корень страницы — .price,
   за него цепляется price-new.js. Своих компонентов здесь нет, только
   раскладка.
   ══════════════════════════════════════════════════════════════════════ */

/* Без сайдбара и оглавления: сетка kb-main не нужна вовсе. Воздух сверху
   и снизу даёт .price, у kb-раскладки он свой и дал бы двойной отступ */
.price--info .kb-main {
  display: block;
  padding: 0;
}

.price--info .kb-article-head,
.price--info .content {
  max-width: var(--kb-column);
  margin-inline: auto;
}

/* Ряд тарифов шире колонки: три карточки со степперами «Про» в 800 не
   встают, скрипт схлопнул бы их в --tight и спрятал описания */
/* Заголовок раздела стоит в одной строке с переключателем периода:
   слева название, справа месяц/год — там, где на /price валюта.
   Отступ снизу у ряда уже есть, браузерный margin заголовку не нужен */
.price__wide-title { margin: 0; }
/* Строка заголовка и переключателя — шириной колонки статьи и по её
   краям, а не по полосе карточек: так заголовок стоит в одну линию
   с остальными заголовками страницы */
.price--info .price__controls {
  max-width: var(--kb-column);
  margin-inline: auto;
}

.price--info .price__wide {
  max-width: var(--price-max);
  margin-inline: auto;
  margin-block: var(--spacing-xl);
}

/* Карточки тарифов сглажены, как на /price. kb.css гасит сглаживание на
   всей странице, блоки статьи остаются дугой, как во всей базе знаний */
.price--info .price__wide,
.price--info .personal { --corner-smooth: var(--corner-smooth-brand); }

/* ── Тариф клиента после перехода. Figma 1537:12221 ───────────────────
   Рамка и поле — те же, что у блоков статьи (kb-faq, kb-quote): кант M,
   скругление XL, поле M */
.personal {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-m);
  padding: var(--spacing-m);
  border: var(--size-stroke-m) solid var(--color-border);
  border-radius: var(--radius-xl);
}
/* Скрытое скриптом остаётся скрытым: kb-card и блоки здесь flex,
   а display перебивает атрибут hidden */
.price--info [hidden] { display: none; }
.personal__guest { margin: 0; }

/* Шаг внутри блока держит gap — M, как в макете. Ритм статьи добавлял
   плашкам ещё 12 сверху и снизу, и шаги выходили 28 */
.personal .kb-card { margin-block: 0; }

/* Карточка тарифа в блоке по макету 1537:12221: по бокам поле M,
   сверху и снизу у верхней части XL, у списка лимитов L */
.personal .plan-card { --plan-pad: var(--spacing-m); }
.personal .plan-card__top { padding-block: var(--spacing-xl); }
/* Бейдж поднимается к углу на поле карточки — формула в tokens.css
   берёт --plan-pad, общее для всех сторон. Сверху поле здесь XL, и та
   же формула получает его, иначе бейдж висел бы на 24 ниже угла */
.personal .plan-card__badge {
  margin-block-start: calc((var(--spacing-xl) - var(--spacing-xs) + var(--size-stroke-m)) * -1);
}
.personal__body {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-m);
}

/* Карточка и расчёт поровну. Уже двух колонок расчёт уходит под
   карточку: 390 на телефоне на двоих не делится */
.personal__row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--spacing-xl);
}
/* Абзацы правила 25%, скидка и итог — одной колонкой с шагом M,
   как в макете. Ритм статьи между абзацами здесь не нужен */
.personal__side > p:not(.personal__total) { padding-block: 0; }
.personal__side {
  display: flex;
  flex-direction: column;
  gap: var(--spacing-m);
}
/* Абзацы стоят сверху и забирают свободное место, скидка и итог
   прижаты к низу колонки — вровень с низом карточки (Figma 1538:12304).
   Колонка тянется на высоту ряда сама: ячейка сетки по умолчанию stretch */
.personal .personal__discount { margin-block-start: auto; }
/* Строкой, а не столбиком: kb-card раскладывает содержимое колонкой
   под абзацы, а здесь подпись и процент стоят в одну линию. Селектор
   двойной, потому что kb.css подключён позже и при равном весе перебил бы */
.personal .personal__discount {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: var(--spacing-m);
  font-size: var(--text-p1);
  line-height: var(--text-p1--line-height);
  font-weight: var(--font-weight-body-medium);
  background: var(--gradient-slavsk);
}
/* Ритм статьи даёт первому ребёнку карточки поле снизу, последнему
   сверху — под абзацы. В строке это разводит подпись и процент по высоте */
.personal__discount > * { padding-block: 0; }
.personal__total {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--spacing-xs);
  margin: 0;
  font-family: var(--font-heading);
  font-weight: var(--font-weight-heading);
}
/* «Итого:» забирает остаток строки и толкает цены к правому краю */
.personal__total > :first-child { flex: 1; }
.personal__list { color: var(--color-text-secondary); }

@media (max-width: 767px) {
  .personal__row { grid-template-columns: minmax(0, 1fr); }
}

