/* ==========================================================================
   Секция 1 — первый экран
   ========================================================================== */

/* ГЛАВНОЕ ОГРАНИЧЕНИЕ СЕКЦИИ: она обязана целиком помещаться в первый экран
   ноутбука — вместе с ценой, аватарками и роботом. Проверено на 1366×768,
   1440×820, 1536×864, 1920×1080.

   Отсюда вся вертикальная ритмика секции завязана не только на ширину, но и
   на высоту вьюпорта. Раньше отступы были фиксированными (24-64px из шкалы),
   а кегли — только от vw: на широком и низком экране ноутбука секция
   вырастала до 980-1160px при вьюпорте 768-820 и обрезала карточку цены,
   соцдоказательство и ноги робота.

   Приём один и тот же по всей секции: clamp(<минимум из шкалы>, Nvh,
   <макетное значение из шкалы>). На высоком экране получается макет,
   на низком — сжатая, но не сломанная композиция. Границы clamp взяты
   из шкалы --s-*, промежуточных значений по-прежнему нет.

   Ниже 1000px этот режим выключается: там колонки складываются в одну,
   секция заведомо длиннее экрана, и жать её незачем. */

/* ЕДИНИЦА ВЫСОТЫ — svh, А НЕ vh. Везде в этом файле.

   1vh на мобильном считается от БОЛЬШОГО вьюпорта, то есть от высоты окна
   со свёрнутыми панелями. На iPhone 14 это 844 при фактически видимых
   730-750, а во встроенном браузере мессенджера панелей две, и разрыв
   ещё больше. Вся вертикальная ритмика на vh получалась на 12% щедрее
   реального окна — ровно настолько, насколько кнопка не влезала.
   1svh считается от МАЛОГО вьюпорта (панели развёрнуты), то есть от того,
   что человек видит в момент загрузки. Свод требует прямо: 100svh,
   не 100vh и не 100dvh.

   dvh не берём сознательно: он пересчитывается на прокрутке с троттлингом,
   и на текстовом блоке это читается рывком (свод, тот же раздел).

   Про поддержку. svh — Chrome 108+, Safari 15.4+. Пола ниже у сайта всё
   равно нет: в base.css стоит `:has()` (Safari 15.4, Chrome 105), то есть
   порог тот же самый. Поэтому парных объявлений с vh не пишем — они
   страховали бы от браузеров, которые эту страницу и так не соберут.

   Снизу отступ минимальный: в макете робот доходит до нижнего края экрана
   (замер по design/экран 1: робот занимает css 149…1080 при экране 1080).
   На мобильном отступ возвращается — там внизу колонки лежат плашки,
   а не картинка. */
.hero {
  position: relative;
  padding-block: clamp(var(--s-3), 2.2svh, var(--s-6)) clamp(var(--s-2), 1.4svh, var(--s-5));
  overflow: hidden;
  /* Подсветка за роботом. Собственного тона у секции нет: общий фон и
     атмосферные пятна лежат на странице целиком (base.css). Пятно затухает
     выше нижней кромки, поэтому стыка со вторым экраном не даёт. */
  background: radial-gradient(90% 66% at 76% 30%, rgba(124, 59, 246, .075) 0%, transparent 68%);
}

/* Концентрические круги за роботом.

   Размер ограничен не только шириной, но и высотой окна. Высота первого
   экрана тянется от vh (как и сам робот — см. --robot-w ниже), а верх круга
   отсчитывается долей от этой высоты, поэтому на невысоком окне круг
   переставал помещаться: при 1366×768 нижняя дуга уходила за кромку секции
   на 42px, при 1440×800 — на 59, при 1440×560 — на 194. На .hero стоит
   overflow: hidden, и все три случая давали на стыке со вторым экраном
   прямой горизонтальный срез дуг — ровно то, что заказчик называет
   оборванным декором.

   84vh — предел, при котором круг встаёт внутрь секции на всех проверенных
   окнах (замер tools/decor-check.js: 1440×560, 1366×650, 1366×768, 1440×800,
   1440×900, 1680×900, 1920×1080). На окнах выше 810px предел не срабатывает
   вовсе и круг остаётся прежним: там меньше 52vw. Ограничивать высотой,
   а не маской, потому что круг работает ореолом за фигурой — он должен
   оставаться целым кругом, а не растворяться снизу. */
.hero__rings {
  width: min(52vw, 780px, 84svh);
  aspect-ratio: 1;
  top: 8%;
  right: 4%;
}

/* ---------- Шапка ---------- */

/* Отбивка под шапкой была 64px намертво — на экране 768px это 8% всей высоты
   под воздух между плашками и заголовком. В макете там 72px при экране 1080,
   то есть те же ~6.5vh; теперь так и записано. */
.hero__top {
  display: flex;
  align-items: center;
  gap: var(--s-5);
  padding-block: clamp(var(--s-1), 0.8svh, var(--s-3)) clamp(var(--s-4), 3.4svh, var(--s-7));
}

/* Логотип, черта и подпись — своя коробка внутри шапки.
   На десктопе она обязана вести себя ровно как раньше, до появления
   обёртки: те же display: flex, align-items: center и gap, что у .hero__top,
   плюс align-self: stretch — черта тянется на высоту всей шапки
   (замер 1440: черта 1×71.1 при шапке 108.9; без stretch она сжимается
   до высоты логотипа, а подпись падает под логотип отдельной строкой).
   Нужна обёртка мобильному: ниже 640 и в ландшафте .hero__top уходит
   в display: contents, и обёртка с квалификаторами становятся прямыми
   детьми .container — только так их можно развести по колонке (order). */
.hero__brand-row {
  display: flex;
  align-items: center;
  align-self: stretch;
  gap: var(--s-5);
}

.hero__brand {
  display: inline-flex;
  align-items: center;
  min-height: 44px;   /* зона нажатия */
}

.hero__logo { width: 197px; height: auto; }   /* замер по макету */

.hero__brand-divider {
  width: 1px;
  align-self: stretch;
  background: var(--line);
}

.hero__brand-note {
  color: var(--muted);
  font-size: var(--fz-body);
}

.hero__facts {
  display: flex;
  gap: var(--s-3);
  margin: 0 0 0 auto;
  padding: 0;
  list-style: none;
}

/* Плашка-факт по макету: 230×100 css при вьюпорте 1920, скругление 24
   (замер белых коробок по design/экран 1). Поля по горизонтали были 28 —
   плашка выходила 248 и вся тройка занимала больше места, чем в макете.
   Поля по вертикали ужимаются вместе с экраном: на 768 они дают 62px высоты
   вместо 78, а это три четверти строки заголовка. */
.hero__fact {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: clamp(var(--s-3), 1.7svh, var(--s-5)) var(--s-5);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--sh-card);
  font-size: var(--fz-small);
  line-height: 1.35;
}

.hero__fact .icon { width: 26px; height: 26px; color: var(--brand); }

/* ---------- Сетка ---------- */

/* Колонка визуала теперь не доля, а auto: её ширина — это ровно ширина робота
   плюс зазор справа (см. вывод над .hero__visual). Раньше стояло
   0.65fr / 1fr, робот брал 77% своей колонки, и текстовая колонка выходила
   666px при 753px в макете (замер: текст в макете занимает css 71…824).
   Из-за этого на 1280-1440 карточка цены не влезала рядом с кнопкой
   и переносилась на вторую строку (+72px), а соцдоказательство ломалось
   на 3-4 строки вместо двух (+28px) — ровно там, где высоты и не хватало.

   С auto весь остаток ширины достаётся тексту, а робот больше не тянет
   колонку за собой: на 1366 текст выходит ~600px вместо 485.
   Правый край колонки визуала при этом не меняется, поэтому геометрия
   плашек и выреза (всё считается от правого края) осталась прежней. */
.hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  gap: clamp(24px, 2.9vw, 56px);
}

/* ---------- Текстовая колонка ---------- */

/* z-index страхует от наползания картинки робота: даже если колонки
   когда-нибудь пересекутся, текст и карточка цены останутся сверху. */
.hero__text {
  position: relative;
  z-index: 2;
}

/* ---------- Бейдж и дата ----------

   Дата интенсива обязана стоять на первом экране: без неё человек не знает,
   когда это и успевает ли он. Места под отдельную строку нет — секция
   помещается в окно ноутбука с запасом 24px на 1440×800 и 20px на 1366×768,
   а строка с датой стоила бы 18px плюс отбивка.

   Поэтому дата встаёт в один ряд с бейджем. Высоту ряда задаёт бейдж
   (min-height 46px из base.css), строка даты вдвое ниже, align-items: center
   ставит её по оси бейджа — ряд остаётся 46px, и высота секции не меняется
   ни на пиксель. Замер A/B (с датой и без) на 1440×800, 1366×768, 1280×800,
   1536×864, 1600×900, 1920×1080: разница ноль на всех шести, секция
   по-прежнему 776 / 748 / 752 / 822 / 863 / 1065.

   flex-wrap — поведение, а не украшение. Бейдж занимает 169px, дата 105px,
   вместе с зазором 290. В полосу они встают с 360px (запас 31px на 360,
   46 на 375, 85 на 414). На 320 пара не помещается, и дата уходит под бейдж —
   там первый экран всё равно вдвое длиннее окна, лишние 26px ничего
   не решают. Тот же перенос спасёт, если текст даты когда-нибудь станет
   длиннее (добавится год): дата уедет вниз, а не выдавит бейдж за край.

   Цвет --ink, а не --muted: это факт того же веса, что и слово «интенсив»,
   и он не должен читаться сноской. Брендовый цвет не берём — по
   docs/visual-language.md акцент на первом экране один и он на кнопке. */
.hero__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s-2) var(--s-4);
}

/* Тире короткое (U+2013) и без пробелов — числовой диапазон, свод.
   nowrap держит «18–20 августа» одной группой: неразрывный пробел внутри
   уже стоит в разметке, а перенос по тире браузер сделал бы сам. */
.hero__date {
  font-size: var(--fz-small);
  font-weight: 600;
  line-height: 1.2;
  white-space: nowrap;
}

/* Своих размеров у бейджа здесь нет. Было 40×20 против общих 46×26 —
   единственный на сайте надзаголовок другой высоты, и на прокрутке
   с экрана 1 на экран 2 он подскакивал на 6px. Аргумент «на первом экране
   он стоит над самым крупным заголовком и может работать мельче» не
   выдерживает проверки: между двумя бейджами полтора экрана прокрутки,
   рядом друг с другом они не стоят никогда, а разнобой видно. */

/* Заголовок — обычная строка, а не флекс-ряд: бейдж версии идёт следом
   за текстом и переносится вместе с ним. Во флексе он на узких ширинах
   вылезал за колонку (на 1024 правый край бейджа был на 5px правее колонки). */
.hero__title {
  margin-block: clamp(var(--s-2), 1.4svh, var(--s-5));
}

/* «ИИ БРО» остаётся самым крупным на экране: кегль режется только высотой
   вьюпорта и только там, где иначе не влезть — на широком и низком окне.
   На всех проверочных ноутбуках (1366×768, 1440×820, 1536×864, 1920×1080)
   ограничение не срабатывает вовсе: 8.85vw там меньше 17.5vh. */
.hero__title .h1 { font-size: min(var(--fz-h1), 17.5svh); }

/* Бейдж версии. Был прозрачным, с рамкой в полный брендовый цвет —
   пустая коробка, у которой самая заметная часть это обводка. В прототипе
   у него заливка (замер по design/экран 1: 99×71, подложка #F5F3FD,
   рамка светло-сиреневая), и по docs/visual-language.md акцентная
   подложка берёт не плоский цвет, а перелив --grad-soft: плоская заливка
   там названа самым дешёвым приёмом набора. Рамку заменяет --glow-brand —
   кольцо в 1px по контуру плюс мягкое свечение под бейджем. Это та самая
   микродеталь вместо крупного украшения, и приём общий с акцентными
   кружками и главной кнопкой, а не изобретённый для одного места.

   Контраст держится: --brand на середине перелива (там, где стоят цифры)
   даёт 4.51:1 при норме 4.5.

   Размер тоже по прототипу: 71px при заголовке 170px — 0.42 кегля h1,
   поэтому кегль привязан к ширине окна (1.78vw ≈ 34px при 1920), а поля
   и минимальная ширина заданы в em, чтобы бейдж менялся целиком.
   Строку заголовка он не распирает: line-box у .h1 равен 1.22 кегля,
   то есть вдвое выше бейджа на любой ширине, и высота секции от него
   не зависит. Нижняя граница 18px — прежний мобильный размер.

   Второй предел в min() — та же страховка по высоте окна, что у заголовка
   и подзаголовка. На широком и низком окне (1920×700) заголовок режется
   до 112px, его строка — до 137px, и бейдж в 70px начинает эту строку
   растягивать: секция прибавляла 7px там, где до края экрана оставалось 9.
   На всех проверочных ноутбуках предел не срабатывает вовсе: 3.5vh там
   больше, чем 1.78vw. */
.hero__version {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.9em;
  margin-left: var(--s-3);
  padding: .53em .64em;
  border-radius: var(--r-md);
  background: var(--grad-soft);
  box-shadow: var(--glow-brand);
  color: var(--brand);
  font-size: min(var(--fz-h3), 3.5svh);
  font-weight: 600;
  line-height: 1;
  vertical-align: middle;
}

/* Замеры по макету (вьюпорт 1920): строки подзаголовка 42/33/30px по высоте
   выносных элементов — это кегль около 42. Описание ниже — 19-20px.
   Второй предел в min() — та же страховка по высоте окна, что и у заголовка. */
.hero__claim {
  font-size: min(var(--fz-verdict), 4.8svh);
  font-weight: 600;
  line-height: 1.22;
  max-width: 12.5em;   /* в макете фраза ложится ровно в три строки */
}

/* Ширина в em, а не в ch и не в процентах: кегль описания меняется вместе
   с экраном, и предел строки обязан меняться с ним же — иначе на широком
   экране текст расползается, а перенос уезжает.
   27em — это ровно первая строка макета «За 3 дня … под реальный проект,»:
   она влезает целиком, а «поставишь» уже нет. Второй перенос шириной
   не берётся (см. .hero__desc-br). */
.hero__desc {
  margin-top: clamp(var(--s-2), 1.6svh, var(--s-5));
  max-width: 27em;
  color: var(--muted);
  font-size: min(var(--fz-body), 2.4svh);
}

/* ==========================================================================
   ПЕРЕКЛЮЧЕНИЕ ВАРИАНТОВ ПЕРВОГО ЭКРАНА
   --------------------------------------------------------------------------
   Все три текста лежат в разметке, показывается один. Выбор делает атрибут
   data-hero на <html> — его ставит скрипт в <head> ДО того, как разобрано
   тело страницы, поэтому подмены никто не видит: нужный вариант просто
   оказывается единственным с первой отрисовки.

   Без скрипта и без метки атрибута нет, и остаётся базовый вариант —
   на нём же держится выдача.

   display, а не visibility и не opacity: скрытый вариант обязан пропасть
   и из потока, и из дерева доступности. Иначе диктор прочитает подряд три
   заголовка, а высота первого экрана уедет втрое.
   ========================================================================== */
.hero__v { display: none; }
.hero__v--a { display: block; }

[data-hero="bro"] .hero__v--a { display: none; }
[data-hero="bro"] .hero__v--bro { display: block; }

[data-hero="target"] .hero__v--a { display: none; }
[data-hero="target"] .hero__v--target { display: block; }

/* Вторая строка описания у варианта target. Отбивка меньше, чем между
   описанием и тем, что стоит выше: это продолжение мысли, а не новый блок. */
.hero__desc--after {
  margin-top: var(--s-2);
}

/* ПОСАДКА ДЛИННЫХ ВАРИАНТОВ.

   Первый экран рассчитан помещаться в окно ноутбука целиком — на 1440×800
   у базового варианта остаётся 24px запаса. Тексты двух других длиннее:
   с настройками базового фраза ложилась в четыре строки вместо трёх,
   и секция вырастала до 845 и 853px, то есть кнопка и цена уезжали
   под сгиб. Ради этого экрана всё и делалось, поэтому здесь не «немного
   не влезло», а потеря смысла.

   Лечится шириной, а не кеглем в первую очередь: дать строке больше места
   дешевле, чем мельчить. Кегль опущен настолько, насколько понадобилось
   после расширения, — на 10 и 14 процентов. Проверено на 1440×800,
   1366×768 и 375: запас 15-36px, как у базового.

   Ширина в em, поэтому предел строки едет вместе с кеглем — тот же приём,
   что у .hero__desc выше. */

[data-hero="bro"] .hero__claim {
  max-width: 17em;
  font-size: min(calc(var(--fz-verdict) * 0.9), 4.8svh);
}
[data-hero="bro"] .hero__desc {
  max-width: 34em;
}

[data-hero="target"] .hero__claim {
  max-width: 18em;
  font-size: min(calc(var(--fz-verdict) * 0.86), 4.8svh);
}
[data-hero="target"] .hero__desc {
  max-width: 34em;
  font-size: min(calc(var(--fz-body) * 0.95), 2.4svh);
}
/* У этого варианта описание из двух абзацев, и отбивка между ними меньше
   обычной: это продолжение мысли, а не следующий блок. */
[data-hero="target"] .hero__desc--after {
  margin-top: var(--s-1);
}

/* Перенос после «заявок».
   В макете описание разбито на три строки: после «проект,» и после «заявок».
   Первый перенос даёт ширина колонки, второй — нет: строка «поставишь
   на обработку заявок и продашь» короче первой строки, и любая ширина,
   в которую влезает первая, вмещает и «и продашь». Поэтому здесь стоит
   явный <br>, но он живёт только на широких экранах — ниже 1200px текст
   снова ложится сам, по месту. */
@media (max-width: 1200px) {
  .hero__desc-br { display: none; }
}

/* Кнопка и карточка цены обязаны помещаться в текстовую колонку: в макете
   они рядом (423 + 32 + 300 = 755 ≈ вся колонка 753). После перехода
   колонки визуала на auto текста хватает на это уже с 1280px, но ряд
   по-прежнему с переносом — на случай узкого окна.
   align-items: stretch — тот самый приём, которым кнопка добирает высоту
   до карточки, и он же сам себя выключает, когда добирать нечем.
   Высота флекс-строки равна самому высокому её элементу, а stretch тянет
   до неё остальных. Пока кнопка и карточка стоят рядом, строку задаёт
   карточка, и кнопка бесплатно вырастает до её высоты: секция не меняется
   ни на пиксель. Как только колонка сузилась и ряд перенёсся, кнопка
   остаётся одна в своей строке, тянуться ей не за кем, и она возвращается
   к штатным 56px из .btn — ровно как было до правки.
   Это важнее, чем кажется: жёсткая высота у кнопки в перенесённом ряду
   ложится в высоту секции целиком (+34px) и на окнах 1001…1550px
   выдавливала первый экран за границу вьюпорта. */
.hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  row-gap: var(--s-4);
  column-gap: clamp(16px, 1.6vw, 32px);
  margin-top: clamp(var(--s-3), 3.2svh, var(--s-7));
}

/* Кнопка — главное действие экрана, и по массе она обязана выигрывать
   у карточки цены, а не проигрывать ей. Замер по design/экран 1: кнопка
   425×119, карточка 296×134, то есть по высоте кнопка — 0.89 карточки,
   по ширине в 1.43 раза шире её. У нас было 423×56: ровно вдвое ниже
   соседа, и ряд читался как карточка с приклеенной кнопкой.

   Высоты своей у кнопки поэтому нет вовсе: её даёт align-items: stretch
   у ряда (разбор — над .hero__actions). Рядом с карточкой кнопка
   выравнивается по ней вровень — 99px при окне 700, 108px при 1080,
   114.6px при 1334 и выше; в перенесённом ряду сама собой возвращается
   к 56px. В прототипе кнопка на 11% ниже карточки, здесь заподлицо:
   разница в пользу здешнего варианта, потому что верх и низ у пары
   совпадают и ряд читается как одна деталь, а не как две разной высоты.

   Ширина берётся не базисом, а долей свободного места. Базис 280px
   трогать нельзя: именно сумма базисов решает, переносится ли карточка
   на вторую строку, а запас до переноса самый тонкий на 1280×800
   (280 + 190 + 21 при колонке 508). Поэтому базис прежний, а растёт
   кнопка вдвое охотнее карточки (grow 1.5 против 1), и потолок поднят
   с 423 до 440.

   Кегль надписи привязан к ширине окна. В прототипе он 33px при кнопке
   425px, но на 1280 кнопка ужимается до 290px, и 33px туда не влезают
   вместе со стрелкой. 26px сверху — то, что помещается с запасом при
   кнопке 440px; 17px снизу — чтобы надпись не мельчала ниже базовой
   кнопки сайта. Стрелка задана в em и меняется вместе с надписью. */
.hero__actions .btn--primary {
  flex: 1.5 1 280px;
  max-width: 440px;
  font-size: var(--fz-lead);
}

.hero__actions .btn--primary .icon { width: 1.15em; height: 1.15em; }

/* Карточка была 116px высотой при 130 в макете, но макетные 130 — это
   при экране 1080. На 768 столько же занимать нельзя: поля по вертикали
   ужимаются вместе с экраном и дают 90-100px.
   Ширина 190…300: 300 — макет при 1920, 190 — минимум, при котором цена
   и зачёркнутая прежняя цена ещё стоят в строку. */
.hero__price {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--s-2);
  flex: 1 1 190px;
  max-width: 300px;
  padding: clamp(var(--s-2), 1.2svh, var(--s-4)) var(--s-5);
}

.hero__price-badge {
  align-self: flex-start;
  padding: 5px 12px;
  border-radius: var(--r-pill);
  background: var(--brand);
  color: #fff;
  font-size: var(--fz-caption);
  font-weight: 600;
}

.hero__price-row { display: flex; align-items: baseline; gap: var(--s-3); }

.hero__price-now { font-size: var(--fz-num-sm); font-weight: 700; line-height: 1; }

.hero__price-old { font-size: var(--fz-h3); color: var(--muted-2); text-decoration-thickness: 2px; }

/* ---------- Потолок кеглей: карточка их не вмещала ----------

   Разбор и числа — в css/tokens.css, рядом со шкалой. Коротко: карточка
   ограничена 300px, содержимого в ней 252, а кегли росли от vw — пара
   тенговых сумм на 1920 просила 342px, и зачёркнутая цена уезжала
   на фон мимо белой коробки (замер: 90px на ширинах 1380-1920).

   Потолок общий для обеих валют, без развилки по data-currency. Пока
   на первой странице стояли доллары, развилка была нужна: «$15 / $115»
   помещалось с запасом 120px, и сажать доллар с 51px до 36 заодно значило
   бы платить за чужую беду. Доллары ушли, остались белорусские рубли
   и тенге — обе пары длинные, обеим потолок нужен.

   Замер до снятия развилки, index.html без потолка: карточка раздавалась
   до 307px на 1440, 334 на 1600 и 383 на 1920 при макетных 300. Вылета
   при этом не было — её держал min-width ниже, — поэтому обычная проверка
   на вылезший текст такую поломку не видит. Смотреть надо ширину коробки.

   Медиазапрос обязателен, а не для красоты: ниже по файлу мобильные блоки
   срезают .hero__price-old до --fz-caption тем же весом (0,1,0), и они
   обязаны продолжать выигрывать. Граница 1001 выбрана так, чтобы
   ландшафтный блок (max-width: 1000) в неё не попадал, а сам потолок
   ниже 1367 всё равно не срабатывает. */
@media (min-width: 1001px) {
  .hero__price-now { font-size: var(--fz-hero-price); }
  .hero__price-old { font-size: var(--fz-hero-price-old); }
}

/* Коробка не может стать уже своего содержимого. Штатный автоминимум
   флекс-элемента (min-width: auto) здесь бесполезен: по спецификации он
   обрезается действующим max-width, то есть теми же 300px, — и текст просто
   вылезал бы наружу. Явный min-width применяется ПОСЛЕ max-width
   (используемая ширина = max(min, min(max, width))) и эту обрезку снимает.

   При нынешних ценах правило холостое: потолок кеглей выше держит карточку
   в макетных 300. Оно на два других случая — подмена шрифта на время
   загрузки (метрический фолбэк считан по средней ширине кириллицы, цифры
   и ₸ могут лечь шире) и смена цены в таблице tools/build-html.js. Тогда
   карточка тихо раздастся вместо того, чтобы выпустить текст на фон. */
.hero__price { min-width: min-content; }

/* ---------- Соцдоказательство ---------- */

.hero__proof {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  margin-top: clamp(var(--s-3), 2.6svh, var(--s-7));
}

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

.hero__avatars li { margin-left: -12px; }
.hero__avatars li:first-child { margin-left: 0; }

/* 52 вместо 62: в макете кружок 82px при экране 1080, но там он и стоит
   в самом низу свободной колонки. Здесь это последний блок перед краем
   экрана, и его высота напрямую решает, видно его целиком или нет. */
.hero__avatars img {
  width: 52px;
  height: 52px;
  border-radius: var(--r-pill);
  border: 3px solid var(--bg-tint);
  box-shadow: 0 0 0 1.5px var(--brand-soft);
  object-fit: cover;
}

.hero__proof-text { font-size: var(--fz-small); line-height: var(--lh-body); color: var(--muted); }
.hero__proof-text strong { color: var(--brand); }

/* ---------- Робот и плашки ---------- */

/* Сцена собрана одной ячейкой грида: картинка робота лежит в потоке и задаёт
   высоту колонки, список плашек накрывает её вторым слоем. Коробка списка
   при этом в точности равна коробке картинки по вертикали и колонке
   по горизонтали — значит проценты в позициях плашек считаются от реальной
   высоты робота, а правый край у всех трёх общий.
   aspect-ratio здесь не нужен: пропорцию держат width/height самой картинки.

   ГЕОМЕТРИЯ РОБОТА И ПЛАШЕК СВЯЗАНА ЖЁСТКО — менять только парой.
   Нижняя плашка обязана накрыть вырез в картинке (на его месте в макете
   лежала эта же плашка). Зона выреза задана в долях от размеров картинки:
       левый край   0.8519 ширины
       правый край  1.2763 ширины  — зона шире самой картинки: в макете плашка
                                     выступала за плечо робота вправо
   Отсюда ровно два следствия, и оба линейны по ширине робота:
       зазор справа от робота  ≥ 0.2763 его ширины  (иначе до правого края
                                                     зоны не дотянуться)
       ширина плашки           ≥ зазор + 0.1481 ширины робота
   В сумме: ширина плашки ≥ 0.4244 ширины робота, независимо ни от чего.
   Сверху ограничение от головы: плашка не должна доставать до силуэта,
   который в верхних строках доходит до 0.741 ширины картинки, то есть
       ширина плашки ≤ зазор + 0.259 ширины робота.
   Ниже взяты минимумы плюс 5-6px запаса на округления — они держатся
   на всех ширинах.

   ШИРИНА РОБОТА теперь считается от высоты окна, а не от ширины колонки.
   Робот — самый высокий блок секции, и именно он раньше не давал ей влезть
   в экран: 77% колонки давали 932px высоты при вьюпорте 820.
   Высота картинки = ширина × 1624/1310 = 1.2397 × ширина, а на робота
   в секции остаётся вьюпорт минус шапка с отбивкой и минус поля секции.
   Отсюда прямая: ширина ≤ 0.78 × высота окна − 122px, что и записано
   во втором пределе. Запас после этого — от 17px (1920×1080) до 50px
   (1280×800) на всех проверочных ноутбуках.
   752px — размер робота в макете на вьюпорте 1920 (замер по design/экран 1),
   выше него не растём. 46vw — страховка от переезда на широком и высоком
   окне, где по высоте робот влез бы, а по ширине выдавил бы текст. */
.hero__visual {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);

  /* Потолок по высоте окна вынесен в отдельную переменную не для красоты.
     Единица svh в кастомном свойстве не проверяется при разборе: браузер
     без её поддержки принимает объявление и падает уже при подстановке —
     --robot-w становится «invalid at computed-value time», width: var(...)
     превращается в auto, и колонка auto в гриде уезжает по ширине картинки
     (1310px). Отдельная ручка + @supports снимают этот случай целиком:
     не поддержал svh — остался прежний vh, раскладка не шелохнулась. */
  --robot-h-cap: calc(78vh - 122px);
  --robot-w:   min(752px, var(--robot-h-cap), 46vw);
  --robot-gap: calc(0.2763 * var(--robot-w) + 5px);
  --ability-w: calc(0.4244 * var(--robot-w) + 11px);
}

@supports (height: 1svh) {
  .hero__visual { --robot-h-cap: calc(78svh - 122px); }
}

/* <picture> НЕ ДОЛЖЕН СТАНОВИТЬСЯ ЭЛЕМЕНТОМ СЕТКИ.

   Вся сцена держится на том, что картинка робота и список плашек лежат
   в ОДНОЙ ячейке грида: у .hero__robot стоит grid-area: 1 / 1, у списка —
   тоже, и коробка списка от этого в точности равна коробке картинки.
   Проценты в позициях плашек считаются от неё (разбор над .hero__visual).

   Как только <img> завернули в <picture> ради мобильного кропа, элементом
   сетки стал сам <picture>, а grid-area на .hero__robot перестал что-либо
   значить — картинка больше не участвует в раскладке грида напрямую.
   Дальше срабатывает автоматическое размещение: ячейка 1/1 занята списком,
   и <picture> уезжает в неявную вторую строку. Список остаётся один
   в первой строке, а высота у него своя — ноль, потому что все его дети
   абсолютные.

   Замер на десктопе до правки (1440×900): коробка списка 129, высота 0;
   все три плашки схлопнулись в 129…209 одна на другой, и вырез на куртке
   робота (48.89…64.29% высоты) остался ничем не накрыт — то самое, что
   docs/design-review.md держит как обязательное. На 1280×800 и 1920×1080
   то же самое. Мобильному повезло: там .hero__visual задаёт
   grid-template-rows: auto auto, а список стоит в 2/1, поэтому <picture>
   автоматически попадал в первую строку и раскладка совпадала со
   задуманной случайно.

   display: contents убирает коробку <picture> из раскладки целиком, и
   элементом сетки снова становится сам <img> — то есть всё правило ниже
   (grid-area, justify-self, ширина, поля, маска) работает как до появления
   <picture>, и на десктопе не двигается ни один пиксель (проверено сверкой
   с прежней разметкой на 1280×800, 1440×900, 1920×1080: коробка списка
   119/129/157 и высота 622/719/893 совпадают до пикселя).
   На выбор файла это не влияет вовсе: <source> — механизм разметки,
   а не раскладки. */
.hero__visual picture { display: contents; }

/* Робот прижат к правому краю колонки, но не вплотную: между ним и краем
   лежит --robot-gap, ровно в этот зазор уходит правая часть зоны выреза.
   Ширина колонки теперь равна как раз robot-w + robot-gap (колонка auto),
   поэтому «прижат к правому краю» выполняется само собой. */
/* Низ фигуры растворяется в фоне.
   Картинка вырезана из макета по пояс, и её нижний край — жёсткая
   горизонтальная линия. Пока робот доходил до края экрана, срез читался
   как обрезка кадром и вопросов не вызывал; теперь секция помещается
   в первый экран целиком, срез оказался внутри страницы и стал похож
   на брак вырезки.

   Растушёвка идёт по анатомии фигуры, а не по круглым процентам. Замер
   по самой картинке (assets/robot-hero.webp, 1310×1624): скрещенные руки
   занимают 78…88% высоты, локоть левого рукава — 82…88%, ниже 89.7% идёт
   уже пояс с молнией, и до самого низа кадра там только корпус.
   Прежние «#000 82% → transparent 100%» начинали таять прямо на локтях
   и при этом оставляли внизу серую дымку: линейная альфа даёт 0.22 на
   96% и 0.11 на 98%, а куртка почти чёрная — на светлом фоне это
   отчётливое пятно, а не растворение.

   Теперь наоборот: зона короче и злее. До 89% альфа ровно 1 (руки
   и локти нетронуты), затем четыре точки гнут кривую вниз — сначала
   мягко, чтобы не было видно шва в месте старта, потом всё быстрее,
   и к 98.2% альфа ноль. Последние 1.8% кадра прозрачны намеренно:
   там уже ничего нет, кроме края вырезки.

   Про уголок на левом борту. На 96.7…97.2% высоты левый рукав кончается
   ровным горизонтальным резом, а корпус под ним идёт дальше — стык этих
   двух краёв даёт на 21.8% ширины маленький острый уголок. Маска гасит
   его до альфы ~0.1, но не до нуля: при взгляде вплотную он различим.
   Пробовали убрать его обрезкой кадра снизу (1624 → 1566, срез на 3px
   выше начала растушёвки рукава) — заказчик посмотрел и вернул исходную
   картинку: фигура в полном кадре ему нравится больше, а уголок его
   не смущает. Кадр не режем. Если к этому вернутся — обрезка
   воспроизводится одной строкой: срезать снизу 58px от 1624.

   Робот выходит из фона, а не приклеен поверх него. Приём стандартный
   для персонажей на лендингах — маска, а не картинка с зашитым фоном,
   поэтому работает на любом цвете подложки.

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

   -webkit-mask-image нужен для Safari: там свойство до сих пор
   с префиксом. */
.hero__robot {
  grid-area: 1 / 1;
  justify-self: end;
  align-self: start;
  width: var(--robot-w);
  margin-right: var(--robot-gap);
  height: auto;
  -webkit-mask-image: linear-gradient(to bottom,
      #000 89%,
      rgba(0, 0, 0, .85) 92%,
      rgba(0, 0, 0, .55) 94.5%,
      rgba(0, 0, 0, .22) 96.5%,
      transparent 98.2%);
  mask-image: linear-gradient(to bottom,
      #000 89%,
      rgba(0, 0, 0, .85) 92%,
      rgba(0, 0, 0, .55) 94.5%,
      rgba(0, 0, 0, .22) 96.5%,
      transparent 98.2%);
}


.hero__abilities {
  grid-area: 1 / 1;
  position: relative;   /* коробка-ориентир для плашек */
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Поля по макету: 27px по горизонтали при 1920 (замер белых коробок), было 34.
   Кружок остаётся штатным .icon-badge--sm (48px) — это ступень шкалы из base,
   промежуточных размеров не заводим, а по оптическому весу 48px ближе всего
   к макету, где иконка нарисована на 44px вообще без кружка. */
.hero__ability {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  margin: 0;
  padding: var(--s-4) var(--s-5);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--sh-card);
  font-size: var(--fz-small);
  line-height: 1.35;
}

/* Три плашки одной роли — одна ширина, один правый край, равный шаг.
   Ширина зажата с двух сторон, и оба предела — из геометрии робота:
       снизу  --ability-w        иначе не накрыт вырез
       сверху зазор + 0.259×W    иначе плашка лезет на шлем и лицо
   Между ними стоит желаемая ширина по содержимому: 8em — самая длинная
   строка («квалифицирует»), 112px — кружок 48 + промежуток 16 + поля 2×24.
   Верхний предел раньше не был записан вовсе, и держался только на том,
   что робот всегда крупный. Как только робот стал считаться от высоты окна,
   на низком широком окне он ужался до 431px, а плашка осталась при своих
   256px по содержимому — и уехала роботу на голову (замер: −20px).
   Теперь этого не может случиться по построению: если содержимому тесно,
   текст в плашке переносится, а плашка остаётся в своих границах.
   4px — запас на округления.
   Шаг 18.7% высоты робота при высоте плашки 16.6% даёт зазор 2.1%
   (в макете плашка 129px и зазор 26px при роботе 931px — те же доли).

   ВСЕ ПРОЦЕНТЫ НИЖЕ СЧИТАЮТСЯ ОТ ВЫСОТЫ КАДРА, а кадр — полный,
   1310×1624 (см. блок про маску выше). */
.hero__abilities .hero__ability {
  position: absolute;
  right: 0;
  width: clamp(
    var(--ability-w),
    calc(8em + 112px),
    calc(var(--robot-gap) + 0.259 * var(--robot-w) - 4px)
  );
  min-height: 16.6%;
}

.hero__ability--1 { top: 10.8%; }
.hero__ability--2 { top: 29.5%; }

/* 48.2% + 16.6% = 48.2…64.8% высоты картинки: зона выреза — 48.89…64.29%,
   закрыта с запасом 0.69% сверху и 0.51% снизу (6px и 5px на 1920).
   Проценты считаются от коробки списка, а она равна коробке робота,
   поэтому от абсолютного размера робота ничего не зависит. */
.hero__ability--pinned { top: 48.2%; }

/* ---------- Адаптив ---------- */

@media (max-width: 1200px) {
  /* Три квалификатора аудитории («без знаний в IT», «80% практики»,
     «для маркетологов и таргетологов») раньше здесь просто выключались
     (display: none) — то есть снимались ровно у той аудитории, которая
     приходит с платного трафика в мессенджере и других ответов на эти
     три возражения на первом экране не получает.

     Плашками они ниже 1200 не помещаются: три белые карточки 230×100
     занимают 750px по ширине, а справа от логотипа с подписью столько
     нет уже на 1200. Поэтому здесь та же тройка работает строкой:
     ни подложки, ни тени, ни скругления — только значок и текст.
     Это не другой элемент, а тот же список в другой громкости.

     Своя строка (flex-basis 100%), а не остаток ряда с логотипом:
     на 1200 тройка формально влезала бы справа, но на 1000 уже нет,
     и одна и та же строка на соседних ширинах то стояла бы справа,
     то падала вниз с прижимом вправо (margin-left: auto). Одно
     положение на весь диапазон дешевле любой экономии высоты.

     Разметку не трогаем: <ul class="hero__facts"> лежит внутри
     <header class="hero__top">, то есть до заголовка. Поставить строку
     под подзаголовок, как предлагает docs/site-upgrade-plan.md (п. 23),
     из CSS нельзя — это соседний узел, а не потомок .hero__text.
     Здесь она читается как продолжение подписи «интенсив от платформы
     NextBot»: интенсив — без знаний в IT, 80% практики, для маркетологов. */
  .hero__top { flex-wrap: wrap; }

  .hero__facts {
    flex: 1 1 100%;
    flex-wrap: wrap;
    margin: 0;
    /* 8 по вертикали внутри группы, 24 по горизонтали между пунктами:
       разрыв между соседними пунктами обязан читаться крупнее, чем
       разрыв между строками одного пункта. */
    gap: var(--s-2) var(--s-5);
  }

  .hero__fact {
    padding: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
    gap: var(--s-2);
    color: var(--muted);
    font-size: var(--fz-caption);
    line-height: 1.45;
  }

  /* Ручной <br> внутри пункта остаётся ради плашек от 1201: там ширина
     плашки задана макетом (230px) и обе строки посчитаны под неё.
     Ниже 1000 он гасится (см. блок 1000) — в разметке перед ним теперь
     стоит пробел, поэтому display: none больше не склеивает слова
     в «без знанийв IT». Это и был единственный повод держать его
     во всех диапазонах. */
  .hero__fact .icon { width: 18px; height: 18px; }

  /* Ниже 1200 плашек-фактов нет, и робот остаётся единственным, что упирается
     в ширину: доля от vw здесь жёстче, иначе текстовой колонке не остаётся
     места на кнопку с карточкой. */
  .hero__visual { --robot-h-cap: calc(78vh - 90px); --robot-w: min(752px, var(--robot-h-cap), 40vw); }
  @supports (height: 1svh) {
    .hero__visual { --robot-h-cap: calc(78svh - 90px); }
  }

  /* Плашка ужимается вместе с колонкой. На узкой колонке верхний предел
     ширины (зона между вырезом и головой) наступает раньше, и без этого
     плашка упиралась бы в него собственным содержимым — то есть текст
     в ней переносился бы посреди слова «квалифицирует».
     84px — кружок 40 + промежуток 12 + поля 2×16. */
  .hero__ability {
    gap: var(--s-3);
    padding: var(--s-3) var(--s-4);
  }

  .hero__abilities .hero__ability {
    width: clamp(
      var(--ability-w),
      calc(8em + 84px),
      calc(var(--robot-gap) + 0.259 * var(--robot-w) - 4px)
    );
  }

  .hero__ability .icon-badge--sm { width: 40px; height: 40px; }
  .hero__ability .icon-badge--sm .icon { width: 18px; height: 18px; }
}

/* Очень низкое окно на широком экране (вьюпорт 700-760px при ширине от 1001).
   Соотношение сторон здесь под 2.7:1 — кегли стоят на своих максимумах от vw,
   а высоты нет вовсе. Общая vh-ритмика секции такое окно не вытягивает:
   не хватало 26px на 1440×700 и 40px на 1920×700.
   Порядок тот же, что и по всей секции: сначала отступы, потом вспомогательные
   размеры (аватарки), и только последними — кегли, да и то заголовок режется
   пределом от высоты окна, который выше 760px не включается вовсе.
   Граница 760px выбрана так, чтобы ни один проверочный ноутбук
   (768, 820, 864, 1080) сюда не попадал. */
@media (min-width: 1001px) and (max-height: 760px) {
  .hero__title { margin-block: var(--s-2); }
  .hero__desc { margin-top: var(--s-2); }
  .hero__actions { margin-top: var(--s-4); }
  .hero__proof { margin-top: var(--s-3); }
  .hero__avatars img { width: 44px; height: 44px; }

  .hero__title .h1 { font-size: min(var(--fz-h1), 16svh); }
  .hero__claim { font-size: min(var(--fz-verdict), 4.4svh); }
}

@media (max-width: 1000px) {
  /* ВЕРТИКАЛЬНЫЙ РИТМ ЗДЕСЬ ТОЖЕ ОТ ВЫСОТЫ ОКНА, А НЕ ФИКСИРОВАННЫЙ.

     Было: колонки сложились в одну, секция заведомо длиннее экрана —
     значит отступы можно вернуть к «спокойным» значениям из шкалы
     (32/16/32/24/24/32/32). А ниже стоял отдельный медиазапрос
     `(max-width: 1000px) and (max-height: 720px)`, который на низком
     окне сжимал их обратно.

     Два режима со ступенькой между ними и дали дыру, которую видно
     на замере: при vh 720 низ карточки цены был 715 (влезал), при vh 721
     скачком 771 — и не влезал до vh 780. В полосу 721…779 попадает
     ровно встроенный браузер мессенджера на iPhone 14/15: экран 844,
     полезная высота 730…750. То есть ступенька стояла посреди самого
     частого сценария.

     Стало: одна непрерывная кривая. Тот же приём, что в десктопной части
     секции — clamp(<минимум из шкалы>, N svh, <макетное значение из шкалы>),
     только считается от МАЛОГО вьюпорта. Ступеньки нет вовсе: между
     600 и 900 по высоте отбивки меняются плавно, а на реальном телефоне
     svh уже учитывает панели браузера. Отдельный низкий медиазапрос
     удалён — он больше ничего не решает.

     Границы clamp — по-прежнему только ступени шкалы --s-*.

     Внизу колонки уже не робот, а плашки — значит это обычный стык двух
     секций, и нижнее поле берётся из общей ручки (--section-y). */
  .hero { padding-block: clamp(var(--s-4), 3svh, var(--s-6)) var(--section-y); }
  .hero__top { padding-block: clamp(var(--s-2), 1.6svh, var(--s-4)) clamp(var(--s-4), 3.6svh, var(--s-6)); }
  /* Своего padding у факта здесь больше нет: ниже 1200 это строка,
     а не плашка (см. блок 1200 выше). Прежнее «20px var(--s-5)»
     переоткрывало плашку обратно. */
  .hero__title { margin-block: clamp(var(--s-3), 2.4svh, var(--s-5)); }
  .hero__desc { margin-top: clamp(var(--s-3), 2.4svh, var(--s-5)); }
  .hero__actions { margin-top: clamp(var(--s-4), 3.4svh, var(--s-6)); }
  .hero__proof { margin-top: clamp(var(--s-4), 3.4svh, var(--s-6)); }
  .hero__price { padding: var(--s-4) var(--s-5); }
  .hero__avatars img { width: 56px; height: 56px; }

  /* Кегль описания от высоты окна больше не зависит.
     min(--fz-body, 2.4svh) придуман для широкого и НИЗКОГО окна ноутбука,
     где секция обязана влезть в экран. На телефоне она влезать не обязана
     (одна колонка, три экрана прокрутки), а предел срабатывает как раз
     там, где вреднее всего: на 320×568 он давал 13.6px основного текста.
     Экономия при этом нулевая — пять строк по 13.6 вместо 16 это 30px
     при секции в 1400. */
  .hero__desc { font-size: var(--fz-body); }

  /* Длина строки в ch, а не долей от кегля.
     27em считались от кегля описания и на планшете давали 44 знака при
     колонке во всю ширину — колонка втрое уже заголовка над ней.
     Мера длины строки задана в знаках (свод: 45-75, ориентир 66;
     ch в кириллице откалибрована почти идеально), поэтому и предел
     ставится в ch. 46ch — низ рекомендованного коридора: описание здесь
     не текст статьи, а три строки под заголовком. */
  .hero__desc { max-width: 46ch; }

  .hero__grid { grid-template-columns: 1fr; gap: var(--s-6); }
  .hero__claim { max-width: none; }

  /* ---------- Жёсткий перенос в факте снимается ----------

     <br> внутри пункта посчитан под ПЛАШКУ десктопа: там ширина задана
     макетом (230px) и текст обязан лечь в две строки. Ниже 1000 плашки нет —
     факт работает строкой без подложки (см. блок 1200 выше), и жёсткий
     перенос там ничего не выравнивает, а делит короткую метку пополам.

     Строки после снятия переноса, замер на 375 (колонка 327, на 320 — 288):
       «без знаний в IT»                  184px
       «80% практики, сидим и делаем»     246px
       «для маркетологов и таргетологов»  268px
     Самая длинная влезает и на нижней границе диапазона.

     В разметке перед <br> стоит пробел — иначе display: none склеил бы
     соседние слова в «без знанийв IT». */
  .hero__fact-br { display: none; }

  /* Кегль факта — 15, а не 13.5. Самый мелкий кегль страницы стоял
     на трёх ответах про аудиторию: «без знаний в IT», «80% практики»,
     «для маркетологов и таргетологов». Роль не служебная (это снятие
     возражений), и мельчить её незачем — строка и в 15px укладывается
     в одну (замер выше). --fz-caption остаётся у подписи под логотипом:
     там роль действительно служебная. */
  .hero__fact { font-size: var(--fz-small); }

  /* Колонки сложились, робот уехал в самый низ секции — ореол едет за ним.
     Прежде круг оставался на top: 8% и висел над заголовком отдельным
     пятном: приём формально был, к фигуре не относился.

     ГОРИЗОНТАЛЬ — 50%, А НЕ 36%.

     36% были замером центра КОРОБКИ картинки. Коробка здесь намеренно
     шире фигуры и почти наполовину прозрачна справа (разбор кропа ниже),
     поэтому её центр к фигуре отношения не имеет. Замер по альфа-каналу
     отрисованного кадра — bbox непрозрачных пикселей против середины
     экрана: 160/160 на 320, 188/188 на 375, 214/215 на 430, 383/384
     на 768. То есть --robot-shift уже ставит саму фигуру ровно по центру
     колонки, и ореол обязан стоять там же.

     Это чинит и обрубленный узор. При 36% левый край круга уходил
     за кромку на 26px (320), 30 (375), 31 (390), 34 (414 и 430),
     и на снимке дуга обрывалась прямой линией по x=0 — то самое,
     что docs/visual-language.md запрещает («узор гасится маской
     и не доходит до кромки»). При 50% круг min(88vw, 560) целиком
     внутри вьюпорта на всех ширинах: гасить нечем, потому что нечего.
     Маска не нужна — она бы съела часть ореола просто так.

     Вертикаль по-прежнему через центр фигуры: 72% высоты секции.
     Потолок 84svh здесь не нужен: круг привязан к центру фигуры,
     а не к верхней кромке, и на низком окне ему некуда упираться. */
  .hero__rings {
    --rings-size: min(88vw, 560px);
    width: var(--rings-size);
    left: 50%;
    right: auto;
    transform: translateX(-50%);
    top: calc(72% - var(--rings-size) / 2);
  }
  /* Предел строки описания (27em) снимать нельзя: колонка здесь во всю
     ширину, и без него на 768 строка выходит под 90 знаков. */

  /* ---------- Робот на узком экране: кадр по грудь ----------

     ЧТО БЫЛО. Робот стоял в полный рост, а плашка «передаёт горячих»
     оставалась на нём абсолютом, потому что обязана накрывать вырез
     (зона 85.19…127.63% ширины, 48.89…64.29% высоты — docs/design-review.md).
     Две другие плашки лежали списком под фигурой. Три элемента одной роли
     вели себя по-разному, и это читалось как сбой раскладки
     (docs/site-upgrade-plan.md, п. 25). Плюс на 768 и 375 было видно,
     как жакет обрывается прямой вертикалью по правому краю кадра (п. 26):
     замер по альфе — силуэт достаёт до 99.85% ширины на 64.29% высоты
     и держится у самой кромки до 79%. Нижнюю кромку гасит маска,
     правую гасить было нечем.

     ЧТО СТАЛО. Ровно то решение, которое записано в docs/design-review.md
     как принятое заказчиком: «на мобильном робот кадрируется по грудь,
     зона заплатки в кадр не попадает». Обе беды снимаются одним движением:
     вырез уходит из кадра — плашка освобождается и встаёт третьей в общий
     список; правый срез уходит из кадра — гасить нечего.

     ЦИФРЫ. Замер по альфа-каналу assets/robot-hero.webp (1310×1624),
     построчно левый и правый край непрозрачных пикселей:

       верхние 40% кадра — силуэт 34.66…86.34% ширины
       верхние 44%       — 34.20…87.56%
       верхние 46%       — 34.20…89.69%
       верхние 47%       — 32.60…93.28%   ← уже тянется к кромке
       на 48.89%         — правый край 95.50%, ниже 64.29% — 99.85%

     Отсюда рез на 46% высоты кадра: до выреза (48.89%) остаётся 2.89%,
     это 47px исходника, и правый край силуэта там ещё 89.69% — до кромки
     больше десятой доли ширины. На 47% запас уже съеден.

     Геометрия задана процентами от ширины колонки, чтобы не считать
     её саму: aspect-ratio даёт высоту коробки от ширины, а не от calc
     с процентом (процент в height считался бы от высоты родителя).
       высота коробки = 0.46 × 1.2397 × ширина кадра = 0.5703 × ширина
       → aspect-ratio: 1000 / 570.
     object-fit: cover при такой коробке масштабирует картинку по ширине
     (0.570 < 1.2397), лишнее по высоте срезается, object-position: 50% 0
     оставляет верх кадра.

     Силуэт занимает 34.20…89.69% кадра, то есть 55.49% его ширины и стоит
     не по центру. Поэтому кадр вылезает влево отрицательным margin:
       margin-left = (100% − доля силуэта в колонке)/2 − 0.342 × ширина кадра,
     и тогда по центру колонки оказывается сама фигура, а не коробка.
     Пустая правая часть кадра вылезает за колонку и режется overflow: hidden
     на .hero — там прозрачные пиксели, видеть нечего.

     Две ступени, потому что кроп — это увеличение: исходник даёт на фигуру
     727px по ширине, и растягивать их бесконечно нельзя.
       641…1000: фигура 62% колонки (на 768 это 443css, 1.22× при DPR2);
       ≤640:     фигура 84% колонки (на 375 это 281css, 0.77× при DPR2).
     0.62 / 0.5549 = 1.1173;  0.84 / 0.5549 = 1.5138. */
  .hero__visual {
    --robot-w: 111.73%;
    --robot-shift: -19.21%;   /* (100−62)/2 − 34.2 × 111.73 */
    grid-template-rows: auto auto;
  }

  .hero__robot {
    justify-self: start;
    width: var(--robot-w);
    /* base.css держит на всех картинках max-width: 100%, а кадр здесь
       намеренно шире колонки — иначе увеличения не получится. */
    max-width: none;
    height: auto;
    aspect-ratio: 1000 / 570;
    object-fit: cover;
    object-position: 50% 0;
    margin-left: var(--robot-shift);
    margin-right: 0;
    /* Растворение нижней кромки кадра. Логика та же, что у полной фигуры
       выше (плотно до последней трети, потом быстро в ноль), но проценты
       свои: здесь это не низ куртки, а грудь, и растворяться должно
       заметно раньше — иначе рез читается как рез.
       0.72 × 46% ≈ 33% кадра остаются нетронутыми: голова, шлем, капюшон
       и плечи целиком. */
    -webkit-mask-image: linear-gradient(to bottom,
        #000 72%,
        rgba(0, 0, 0, .72) 82%,
        rgba(0, 0, 0, .34) 91%,
        transparent 100%);
    mask-image: linear-gradient(to bottom,
        #000 72%,
        rgba(0, 0, 0, .72) 82%,
        rgba(0, 0, 0, .34) 91%,
        transparent 100%);
  }

  /* Плашки стоят под фигурой и принадлежат фигуре, а фигура отцентрована
     (см. блок про кольца выше) — значит группа плашек тоже по центру.
     Влево их равнять не на что: слева от них ничего нет, робот кончился. */
  .hero__abilities {
    grid-area: 2 / 1;
    position: static;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--s-3);
    margin-top: var(--s-5);
  }

  /* ШИРИНА ПО СОДЕРЖИМОМУ, А НЕ ПО КОНТЕЙНЕРУ.

     Было flex: 1 1 230px — то есть плашки растягивались на всю ширину
     колонки: 280px на 320, 335 на 375, 390 на 430, а на 768 третья
     оставалась одна в ряду и раздувалась до 704. Текста в них
     69…115px: «отвечает мгновенно» занимало 24% ширины плашки на 375,
     «квалифицирует заявки» 34%, «передаёт горячих» 21%. Две трети
     площади — пустая белая заливка.

     Это наследство десктопной композиции: там ширину задавала геометрия
     робота (плашка обязана накрыть вырез, вывод над .hero__visual),
     и 230px были минимумом, а не желаемым размером. На мобильном
     фигура кадрируется по грудь, вырез в кадр не попадает вовсе
     (docs/design-review.md — принятое решение), накрывать нечего,
     и единственное, что осталось задавать ширину, — сам текст.

     flex: 0 0 auto + wrap: плашка ровно по содержимому, ряд заполняется
     сколько влезло. Получается 3 в ряд на 768 и 834 (одна строка),
     2 + 1 на 414 и 430, по одной на 320…390. Это и есть «две строки
     со смещением»: ряды разной длины читаются метками у фигуры,
     а не таблицей.
     max-width страхует от сверхдлинного слова: плашка не может стать
     шире колонки. */
  .hero__abilities .hero__ability {
    position: static;
    width: auto;
    min-width: 0;
    min-height: 0;
    margin-top: 0;
    flex: 0 0 auto;
    max-width: 100%;
  }
}

/* ==========================================================================
   ТЕЛЕФОН, ПОРТРЕТ (≤640): первый экран собран заново
   ==========================================================================

   ЭТО НЕ СЖАТЫЙ ДЕСКТОП. Разрешение заказчика дословно: «первый экран нужно
   полностью переосмыслять, это будет отдельный экран, который может даже
   какие-то другие элементы иметь и выглядеть не так, как десктопная версия.
   Десктопную версию мы не меняем». Поэтому весь блок ниже живёт только
   на узких экранах, а всё, что выше 640, остаётся байт в байт прежним.

   ЧТО БЫЛО (замер 375×667, сверху вниз — семь отдельных полос):
       1  44px   логотип + «интенсив от платформы NextBot»
       2  46px   плашка «Интенсив» + «18–20 августа»
       3  56px   «ИИ БРО 1.0»
       4  77px   «Собери кореша…»
       5 102px   «За 3 дня соберёшь…»
       6  64px   кнопка «Создать ИИ БРО»
       7  64px   карточка цены ($15 / $115 / Скидка 87%)
      (+ 104px  аватарки и «Более 420…», 522px робот с плашками,
         89px   три квалификатора)
   Семь горизонтальных полос подряд, из них четыре — служебные строки
   в один-два слова. Заказчик: «перегруз очень сильный для мобильных».

   ЧТО СТАЛО — пять объектов вместо семи полос:
       A  логотип                       (одна метка, без подписи)
       B  ЛОКАП: «интенсив · 18–20 августа» + «ИИ БРО 1.0» + обещание
       C  РЯД ДЕЙСТВИЯ: кнопка | $15 $115 | Скидка 87%
       D  описание — под кнопкой, как пояснение к ней
       E  СЦЕНА: робот и три плашки-способности одной фигурой
       F  соцдоказательство (за сгибом)

   Правил три, и каждое отвечает на конкретную фразу заказчика.

   1. «Название интенсива, дату — может, крупным заголовком.»
      Строки 2 и 3 сведены в один локап: надстрочная метка
      «ИНТЕНСИВ · 18–20 АВГУСТА» стоит вплотную над «ИИ БРО 1.0»
      (отбивка 4–8px против 24px между блоками) и читается частью
      заголовка, а не отдельной полосой. Плашка бейджа на телефоне
      разбирается до текста: белая коробка с рамкой, тенью и полями
      по 26px — это ещё одна плашка на экране, где их и так много.
      Остаётся точка-маячок (она общая для всего сайта) и капс.
      Сам заголовок вырос с 40 до 13.2vw (49.5px на 375, 56.8 на 430):
      прежние 40px были нижней границей --fz-h1, посчитанной под тесную
      раскладку; в новой ему есть куда встать.

   2. «Кнопку и информацию про скидку и 15 долларов объединить в одну
      строку: кнопка, информация, процент — в одной строке.»
      Полосы 6 и 7 стали одним рядом. Кнопка тянется на весь остаток
      ширины (209px при 375), цена стоит справа двумя короткими строками
      общей шириной около 100px. Кнопка остаётся главным пятном экрана:
      64px по высоте (зона нажатия с запасом к 44px), заливка в брендовом
      переливе против текста без подложки.
      Карточка цены перестала быть карточкой намеренно: белый прямоугольник
      с тенью рядом с кнопкой читается вторым объектом того же веса,
      а по docs/visual-language.md акцент на экране один и он на действии.
      По той же причине «Скидка 87%» больше не залитая пилюля, а слово
      цветом бренда: два фиолетовых пятна рядом гасят друг друга.

   3. «Подзаголовок куда-то в отдельное место вывести.»
      Описание «За 3 дня соберёшь…» уехало ПОД ряд действия. Порядок
      чтения от этого не ломается, а выправляется под экран: имя →
      обещание → цена и действие → пояснение. Тот же приём уже принят
      в ландшафте (см. блок в конце файла), здесь он работает по той же
      причине — четыре строки серого текста между обещанием и кнопкой
      отодвигали кнопку и разрывали связку «обещание → действие».

   ЧТО СНЯТО С ТЕЛЕФОНА И ПОЧЕМУ.

   • «интенсив от платформы NextBot» и разделитель. Слово «интенсив»
     стоит строкой ниже в локапе, «NextBot» — это сам логотип рядом.
     Строка целиком повторяет своих соседей, а стоит 20px и одного
     из семи этажей.

   • Три квалификатора («без знаний в IT», «80% практики», «для
     маркетологов и таргетологов»). Здесь нужна оговорка: это ответы
     на возражения, и терять их нельзя. Они и не теряются.
     «Для маркетологов и таргетологов» дословно повторено в строке
     соцдоказательства, которая на телефоне остаётся: «Более 420
     маркетологов и таргетологов уже автоматизировали заявки с NextBot».
     То есть адресат экрана назван, и назван вместе с числом — сильнее,
     чем меткой. Два других ответа развёрнуты ниже по странице целыми
     секциями: «Без IT-головняка» (экран 3) и «80% интенсива — сидим,
     собираем ИИ БРО под реальный проект» (экран 4).
     Итог: из двух блоков, оба про аудиторию и оба стоявших подряд
     в конце секции, остался один, и это тот, который несёт больше.
     На 641…1000 и в ландшафте квалификаторы на месте — там ширины
     хватает, и снимать их незачем.

   РОБОТ. Заказчик: «он некрасиво обрезан — видно только голову, странные
   кнопочки на него наезжают и странно висят, ни к чему не прибиты».
   Разбор — над .hero__visual ниже. Коротко: фигура и плашки собраны
   в один объект, плашки стоят справа от фигуры и заходят на неё —
   ровно та связь, что на десктопе, только в узкой колонке. */
@media (max-width: 640px) {

  /* Колонка секции. .hero__top уходит в display: contents, поэтому
     обёртка бренда и (на этой ширине скрытые) квалификаторы становятся
     прямыми детьми .container и подчиняются order.
     gap здесь один-единственный — между логотипом и локапом; все
     остальные отбивки заданы полями самих блоков, потому что они
     разной величины (внутри локапа 4–12, между блоками 24–48). */
  .hero .container {
    display: flex;
    flex-direction: column;
    gap: clamp(var(--s-5), 3.4svh, var(--s-7));
  }

  .hero__top { display: contents; }

  .hero__brand-row {
    order: 1;
    align-self: auto;
    gap: var(--s-3);
    min-height: 44px;
  }

  .hero__logo { width: 116px; }

  /* Подпись и разделитель сняты — разбор в шапке блока. */
  .hero__brand-note,
  .hero__brand-divider { display: none; }

  /* Квалификаторы сняты — разбор в шапке блока. */
  .hero__facts { display: none; }

  /* ---------- Одна колонка вместо сетки ----------

     .hero__text растворяется (display: contents), и все его дети —
     метка, заголовок, обещание, описание, ряд действия, соцдоказательство —
     становятся прямыми детьми .hero__grid вместе с .hero__visual.
     Только так их можно переставить: описание уходит под кнопку,
     а соцдоказательство — в самый низ, за сцену с роботом. Order внутри
     одного родителя это делает, а через два уровня вложенности — нет.

     gap: 0 намеренно. У блоков разный вес, и одинаковый зазор между
     всеми шестью вернул бы ту самую «стопку полос»: внутри локапа
     нужно 4–12px, между группами 24–48. Поэтому отбивки живут
     на самих блоках. */
  .hero__grid {
    order: 2;
    display: flex;
    flex-direction: column;
    /* stretch, а не унаследованный от десктопа center: в колонке
       поперечная ось горизонтальна, и center посадил бы каждый блок
       по его собственной ширине посреди экрана — заголовок, метка
       и обещание встали бы на трёх разных вертикалях. */
    align-items: stretch;
    gap: 0;
  }

  .hero__text { display: contents; }

  .hero__meta   { order: 1; }
  .hero__title  { order: 2; }
  .hero__claim  { order: 3; }
  .hero__actions{ order: 4; }
  .hero__desc   { order: 5; }
  .hero__visual { order: 6; }
  .hero__proof  { order: 7; }

  /* ---------- Локап: метка + имя + обещание ----------

     Метка «ИНТЕНСИВ · 18–20 АВГУСТА» набрана одним кеглем, одним
     трекингом и стоит одной строкой: 15px капс, «интенсив» цветом
     бренда, дата цветом текста. Разделитель — точка через CSS, а не
     в разметке: это типографский знак между двумя готовыми строками,
     тексты не меняются.

     --badge-dot-x обязателен. От него base.css считает посадку
     расходящегося кольца у точки-маячка; при полях 26 → 0 кольцо
     осталось бы висеть в 26px правее самой точки. */
  .hero__meta {
    gap: var(--s-2);
    flex-wrap: nowrap;
  }

  .hero__meta .badge {
    --badge-dot-x: 4px;
    min-height: 0;
    padding: 0;
    border: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
    gap: var(--s-2);
  }

  .hero__date {
    color: var(--ink);
    letter-spacing: var(--ls-caps-sm);
    text-transform: uppercase;
  }

  /* Дни недели на узких экранах не показываем.

     Ниже 640px метка и дата сжимаются в одну строку капсом с трекингом —
     «ИНТЕНСИВ · 15–17 СЕНТЯБРЯ, ВТ–ЧТ». Замер: такой строке нужно 337px,
     а в контейнере при ширине окна 320 остаётся 288, при 360 — 328.
     То есть на распространённых андроидах строка вылезала за край.

     Выбор между «перенести» и «убрать» решён в пользу второго: перенос
     ломает саму идею этой строки — одна строка, точка-разделитель. Сами дни
     недели при этом с экрана не пропадают, они стоят на карточках дней
     чуть ниже: «вт, 22 сентября».

     С 641px строка снова полноразмерная, и суффикс возвращается. */
  .hero__date-dow { display: none; }

  .hero__date::before {
    content: '·';
    margin-right: var(--s-2);
    color: var(--muted-2);
    font-weight: 400;
    letter-spacing: 0;
  }

  /* Отбивка метка → имя — самая тесная на экране: это одна группа.
     Заголовок и метка при разборе плашки бейджа стоят на одной
     вертикали (обе начинаются от левого края колонки), поэтому
     локап читается одним объектом. */
  .hero__title { margin-block: clamp(var(--s-1), .8svh, var(--s-2)) 0; }

  /* Имя продукта — самое крупное на экране, и его размер задаёт ширина
     телефона, а не нижняя граница общего токена.
     --fz-h1 = clamp(40px, 8.85vw, 170px): на телефоне 8.85vw это 28…38px,
     то есть работает нижняя граница 40 — число, посчитанное под старую
     тесную раскладку. В новой заголовку есть куда встать, и он занимает
     постоянную долю ширины: 13.2vw даёт 42.2 на 320, 49.5 на 375,
     51.9 на 393, 56.8 на 430. «ИИ БРО» с бейджем версии на всех четырёх
     ложится в одну строку (замер ниже, в отчёте).
     Потолок по высоте окна оставлен прежним по смыслу: 12svh страхует
     низкое окно (320×568 → 68px), выше него заголовок не растёт. */
  .hero__title .h1 { font-size: min(max(var(--fz-h1), 13.2vw), 12svh); }

  .hero__claim { margin-top: clamp(var(--s-2), 1.6svh, var(--s-3)); }

  /* ---------- Ряд действия: кнопка, цена, процент ----------

     Прямое указание заказчика. nowrap обязателен: с переносом ряд
     на узком экране разваливался бы обратно на два этажа, то есть
     ровно в то, от чего уходим.

     Кнопка забирает весь остаток ширины (flex: 1 1 auto при min-width: 0),
     цена стоит по содержимому. Замер остатка под кнопку:
     209px при 375, 176 при 320 — на обеих надпись со стрелкой помещается
     (числа и запас — в отчёте по секции). */
  .hero__actions {
    order: 4;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: center;
    column-gap: var(--s-3);
    margin-top: clamp(var(--s-5), 4svh, var(--s-6));
  }

  /* Кегль и поля кнопки посчитаны, а не подобраны. Надпись
     «СОЗДАТЬ ИИ БРО» при 15px и трекинге .08em занимает 152.2px,
     стрелка 16.5, промежуток 8 — с полями по 16 кнопке нужно 208.7.
     Остаток под кнопку узок ровно в двух точках полосы, и обе проверены:
        360  колонка 312 − цена 79.8 − зазор 12 = 220.2   запас 11.5
        320  колонка 288 − цена 79.8 − зазор 12 = 196.2   запас 12.0
             (там стрелка гасится, кнопке нужно 184.2 — см. блок ≤359)
     Между 360 и 640 остаток растёт вместе с шириной экрана, а нужда
     кнопки постоянна, поэтому худший случай полосы — её нижняя граница;
     ниже 360 поле контейнера падает с 24 до 16, и полоса начинается
     заново, поэтому проверены обе нижние границы, а не одна.
     При --fz-body (16px) надпись занимает 162.4, кнопке нужно 218.9 —
     и на 360…374 она ломалась бы на две строки (проверено: ломалась). */
  .hero__actions .btn--primary {
    flex: 1 1 auto;
    min-width: 0;
    max-width: none;
    min-height: 64px;
    gap: var(--s-2);
    padding-inline: var(--s-4);
    font-size: var(--fz-small);
    letter-spacing: var(--ls-caps-sm);
  }

  .hero__actions .btn--primary .icon { width: 1.1em; height: 1.1em; }

  /* Цена — не карточка. Разбор в шапке блока: белая коробка с тенью
     рядом с кнопкой читается вторым объектом того же веса и отбирает
     у кнопки роль главного пятна. */
  .hero__price {
    flex: 0 0 auto;
    max-width: none;
    flex-direction: column;
    align-items: flex-end;
    justify-content: center;
    gap: var(--s-1);
    padding: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
    text-align: right;
  }

  /* Порядок внутри цены перевёрнут: сверху сумма, под ней процент.
     Крупное число встаёт на оптическую линию надписи в кнопке,
     а условие скидки читается пояснением к нему. */
  .hero__price-row   { order: 1; gap: var(--s-1); }
  .hero__price-badge {
    order: 2;
    align-self: flex-end;
    padding: 0;
    border-radius: 0;
    background: none;
    color: var(--brand);
  }

  /* Ширину всего блока цены задаёт «Скидка 87%» — 79.8px при 13.5px
     кегля, ниже которого шкала не идёт. Значит и строка сумм обязана
     уложиться в те же 80: «$15» при 24px это 44.5, зазор 4, на старую
     цену остаётся 31. --fz-body (16px) давал 33.6 и раздувал блок
     до 86.1, а каждый лишний пиксель здесь отнимается у кнопки. */
  .hero__price-old { font-size: var(--fz-caption); }

  /* ---------- Описание: пояснение под кнопкой ---------- */
  .hero__desc { margin-top: clamp(var(--s-3), 2svh, var(--s-4)); }

  /* ---------- Сцена: робот и плашки — один объект ----------

     ЧТО БЫЛО. Кадр робота во всю ширину колонки (фигура 84% колонки,
     коробка 151% — шире экрана), под ним три плашки-способности
     отдельным центрированным списком. Заказчик: «видно только голову,
     странные кнопочки на него наезжают и странно висят, ни к чему
     не прибиты». Так и было: нижняя кромка кадра растворялась маской
     ровно там, где начинались плашки, поэтому они читались лежащими
     поверх растворяющегося торса, а не относящимися к нему. Плюс
     сцена занимала 522px высоты при 375 — треть всей секции.

     ЧТО СТАЛО. Ряд из двух частей: фигура слева, столбец плашек справа,
     и столбец ЗАХОДИТ на фигуру. Это ровно та связь, что на десктопе
     (там плашки лежат на роботе), только в узкой колонке — значит
     приём на сайте один, а не два разных.

     ГЕОМЕТРИЯ. Замер силуэта по альфа-каналу кадра (он же в блоке
     про кроп выше): непрозрачные пиксели занимают 34.20…89.69% ширины
     кадра, то есть 55.49% ширины, и стоят не по центру.
     Отсюда три числа, и все три — доли ширины кадра:
         margin-left  = −0.342 × W   левый прозрачный край уходит
                                     за кромку колонки, силуэт встаёт
                                     ровно на левый край
         margin-right = −0.103 × W − 32px
                                     правый прозрачный край втягивается
                                     обратно, и сверх него столбец
                                     заходит на силуэт на 32px
     W = 95% ширины колонки. Тогда:
         силуэт        0.5549 × 0.95 = 52.7% колонки (152px при 320,
                       172 при 375, 182 при 393, 201 при 430)
         высота кадра  0.5703 × 0.95 = 54.2% колонки (156 / 177 / 187 / 207)
         столбец       остаток + 32px нахлёста (168 / 187 / 195 / 213)
     Фигура и столбец получаются примерно равными половинами, и обе
     половины меняются вместе с шириной экрана — пропорция сцены
     на всех четырёх телефонах одна.

     Кадр и маска не тронуты: кроп по грудь (aspect-ratio 1000/570,
     верхние 46% кадра) и проценты растворения остаются те же, что
     в блоке ≤1000. Это принятое заказчиком решение — зона заплатки
     в кадр не попадает (docs/design-review.md), и трогать её нечем.

     ВЫСОТА СЦЕНЫ: 522 → 194px при 375. Освободившиеся 328px — это
     половина экрана телефона. */
  .hero__visual {
    display: flex;
    align-items: center;
    --robot-w: 95%;
    --robot-shift: calc(-0.342 * var(--robot-w));
    --robot-pull: calc(-0.103 * var(--robot-w) - var(--s-6));
    margin-top: clamp(var(--s-6), 5svh, var(--s-7));
  }

  .hero__robot {
    flex: none;
    margin-right: var(--robot-pull);
  }

  /* Столбец плашек лежит поверх фигуры: position + z-index, потому что
     нахлёст сделан отрицательным полем, а не абсолютом. */
  .hero__abilities {
    position: relative;
    z-index: 1;
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    flex-wrap: nowrap;
    align-items: stretch;
    justify-content: center;
    gap: var(--s-3);
    margin: 0;
  }

  /* Все три одной ширины и с общим левым и правым краем — на десктопе
     так же (там ширина одна и правый край общий). Ширину задаёт столбец,
     а не содержимое: три метки разной длины, выключенные по левому краю
     с общей кромкой, читаются одним объектом; три коробки разной ширины
     читались бы обломками списка.
     Поля и кружок ужаты под столбец шириной 168…213: кружок 32 вместо 40,
     поля 8/12 вместо 12/16. */
  .hero__abilities .hero__ability {
    flex: 0 0 auto;
    width: auto;
    max-width: none;
    min-height: 0;
    gap: var(--s-2);
    padding: var(--s-2) var(--s-3);
    font-size: var(--fz-small);
    /* Плашка — метка из двух слов, а не абзац. base.css включает
       hyphens: auto на всех li, и в столбце шириной 168px «квалифицирует»
       рвалось по слогам («квалифи-цирует») — на плашке в две строки
       это читается сломанной вёрсткой. Свод держит hyphens: none
       на кнопках, бейджах и заголовках ровно по этой причине;
       здесь тот же случай. */
    hyphens: none;
  }

  .hero__ability .icon-badge--sm { width: 32px; height: 32px; }
  .hero__ability .icon-badge--sm .icon { width: 16px; height: 16px; }

  /* Перенос внутри плашки здесь работает и обязан работать: все три
     метки становятся ровно двухстрочными, высоты плашек совпадают,
     столбец получается регулярным. Прежний медиазапрос, гасивший этот
     <br> ниже 414px, снят — он обслуживал старую раскладку, где плашки
     стояли в ряд по содержимому и перенос делил короткую метку пополам
     без всякой пользы. */
  .hero__ability-br { display: inline; }

  /* ---------- Соцдоказательство: одна строка в самом низу ----------

     Было: аватарки отдельной строкой, текст под ними, всё это между
     описанием и роботом — то есть посреди первого экрана. Стало:
     одна строка [лица] + фраза, и стоит она последней в секции, после
     сцены. Это единственное, что осталось от двух прежних блоков про
     аудиторию (разбор в шапке блока), и здесь оно работает подписью
     ко всему экрану, а не полосой в его середине.
     Жёсткий перенос в тексте снят: он посчитан под полную ширину
     колонки, а здесь колонка вдвое уже. */
  .hero__proof {
    flex-direction: column;
    align-items: flex-start;
    gap: var(--s-2);
    margin-top: clamp(var(--s-5), 4svh, var(--s-6));
  }

  /* Текст остаётся во всю ширину колонки, а не встаёт справа от лиц.
     Пробовали в строку: колонка под текст выходит 195px, фраза
     ложится в четыре строки рядом с полоской кружков в 36px, и блок
     читается перекошенным. Во всю ширину работает авторский перенос
     из разметки — ровно две строки, «Более 420 маркетологов
     и таргетологов» / «уже автоматизировали заявки с NextBot»
     (замер: 281 и 275px при колонке 288 на самом узком экране).
     hyphens: none — страховка от того же переноса по слогам:
     «автоматизировали» на 320 стоит в 2px от правой кромки,
     и словарь готов разорвать его без нужды. */
  .hero__proof-text { font-size: var(--fz-small); hyphens: none; }

  /* Нахлёст пропорционален кружку: на десктопе 12 при 52px (видно 77%),
     здесь 8 при 36 — те же 78%. Прежние 12 на уменьшенном кружке
     съедали половину лица.
     flex: none обязателен. .hero__avatars — флекс-элемент внутри
     .hero__proof, и без запрета на сжатие кружки давятся по ширине
     до 26px при высоте 36: круглые лица становятся овальными
     (видно на снимке до правки). */
  .hero__avatars { flex: none; }
  .hero__avatars li { margin-left: calc(-1 * var(--s-2)); }
  .hero__avatars img { width: 36px; height: 36px; border-width: 2px; }

  /* ---------- Ореол за фигурой ----------

     Правило из docs/visual-language.md не меняется: объект вырезан
     без фона — значит кольца, центр круга на объекте, диаметр примерно
     в полтора раза больше его ширины.
     Менялся объект: фигура уехала из центра колонки в левую половину
     и стала вдвое меньше, значит и круг едет за ней и уменьшается.
     Прежние min(88vw, 560) с центром по середине экрана оставили бы
     ореол размером в экран вокруг фигуры в 172px — это уже не ореол,
     а фон.
     Горизонталь: центр силуэта = поле контейнера + 0.263 × ширина
     колонки, в долях вьюпорта это 28.8% (320), 29.2% (375), 29.4% (393),
     29.5% (430) — то есть 29% на всей полосе.
     Размер: 56vw при силуэте 52.7% колонки даёт отношение 1.36 —
     внутри «примерно в полтора раза».
     Центр круга стоит на 30%, а не на 29%: разница в один процент
     ширины — это запас до левой кромки. При 29% левая дуга оказывалась
     бы в 1% от края вьюпорта, при 30% — в 2% (6.4px при 320, 7.5 при 375,
     8.6 при 430), правая на 58%. Резать нечего, маска не нужна.

     ВЕРТИКАЛЬ — 70% высоты секции, и это компромисс, а не замер.
     Круг лежит на .hero (position: relative), то есть его проценты
     считаются от секции целиком, а не от сцены. Центр фигуры внутри
     секции гуляет по ширинам, потому что над сценой стоит текст,
     а в тексте меняется число строк: обещание ложится в три строки
     до 412 и в две от 414, описание — в четыре до 393 и в три от 412.
     Замер центра фигуры в долях высоты секции: 67.3 (320), 68.9 (375),
     72.0 (393), 72.9 (430). 70% — середина этого разброса: промах
     от +23 до −25px при диаметре круга 179…241, то есть фигура при
     любом промахе остаётся внутри ореола.
     Точнее сделать нельзя, не перенося сам <div> внутрь .hero__visual,
     а перенос сломал бы десктоп: там координаты круга заданы долями
     от секции и через долю от коробки визуала не выражаются. */
  .hero__rings {
    /* Потолка в пикселях нет намеренно. Предел здесь не абсолютный,
       а геометрический: центр круга стоит на 30% ширины вьюпорта,
       значит радиус не может быть больше 30vw, иначе левая дуга уйдёт
       за кромку. 56vw — это 28vw радиуса, то есть 2% запаса на всех
       ширинах сразу. Отношение круга к силуэту при этом держится
       постоянным: 1.18 (320), 1.22 (375), 1.20 (430), 1.15 (640).
       Прежний потолок в 260px срабатывал с 464px ширины и делал круг
       МЕНЬШЕ фигуры — на 640 это 260 против силуэта в 312. */
    --rings-size: 56vw;
    width: var(--rings-size);
    left: 30%;
    right: auto;
    transform: translateX(-50%);
    top: calc(70% - var(--rings-size) / 2);
  }
}

/* ---------- Самый узкий телефон (≤359) ----------

   Ряд «кнопка + цена» на 320 держится на 176px под кнопку. Надпись
   «СОЗДАТЬ ИИ БРО» со стрелкой при 16px и полях 24 занимает больше,
   поэтому здесь кегль опускается на ступень (--fz-small, 15px),
   а поля — на ступень шкалы (--s-4). Замер и запас — в отчёте.
   Прежний блок на этой границе (промежуток в .hero__meta ради пары
   «плашка + дата») снят: плашки бейджа на телефоне больше нет,
   метка стала строкой в 15px и помещается на 320 целиком. */
@media (max-width: 359px) {
  /* Стрелка снимается: 16.5px значка плюс 12px промежутка — это ровно
     те 28.5px, которых кнопке не хватает на 320 (нужно 198.5 при
     остатке 189.9). Без неё нужно 170 при остатке 189.9, запас 19.9.
     Кнопка от этого не перестаёт быть кнопкой: она и так самое крупное
     цветное пятно экрана, а на 320 надпись в две строки стоила бы
     ей высоты и всей опоры ряда. */
  .hero__actions .btn--primary .icon { display: none; }
  .hero__actions { column-gap: var(--s-3); }

  /* Столбец плашек здесь на 18px у́же (168 против 186 на 375), и текст
     в нём получает 104px при 116 нужных «квалифицирует». Ужимаются
     кружок и боковые поля — не кегль: 15px это и есть нижняя ступень
     шкалы, ниже неё метки уходят в мелочь. */
  .hero__ability .icon-badge--sm { width: 28px; height: 28px; }
  .hero__abilities .hero__ability { padding-inline: var(--s-2); }

  /* Соцдоказательство на ступень мельче. Авторский перенос делит фразу
     на «Более 420 маркетологов и таргетологов» (300px при 15px кегля)
     и «уже автоматизировали заявки с NextBot»; в колонку 288 первая
     не влезает, и вместо двух строк выходит четыре — блок вырастает
     с 92 до 140px. При 13.5px строка занимает 270 и снова ложится
     в две. Это последняя ступень шкалы, ниже неё не идём. */
  .hero__proof-text { font-size: var(--fz-caption); }
}

/* ---------- Телефон в ландшафте ----------

   Сюда попадают 844×390 и 932×430 — iPhone 14/15 и Pro Max, положенные
   набок. Условие по ширине обязательно: без него в запрос заезжает
   низкое окно ноутбука, а десктоп принят и трогать его нельзя.

   ЧТО БЫЛО. Ветвление шло только по ширине, поэтому 844 и 932 получали
   ТУ ЖЕ одноколоночную раскладку, что телефон в портрете. Замеры:
   высота секции 1335px при вьюпорте 390 (3.4 экрана) и 1405 при 430 (3.3);
   низ кнопки 566 при видимых 390 — за сгибом на 176px, вместе с ценой;
   верх робота 686, то есть фигура начиналась на 1.8 экрана ниже сгиба;
   кнопка раздута до 102px (её тянет вровень с карточкой цены), а справа
   от текста стояло 500 с лишним пикселей чистой пустоты — колонка текста
   занимала 780 из 844 и всё равно упиралась в 43 знака описания.

   ЧТО СТАЛО. Ландшафт — это не «узкий экран», а ШИРОКИЙ И НИЗКИЙ:
   ширины вдоволь, высоты нет вовсе. Значит композиция обратная
   портретной — двухколоночная, как на десктопе, только сжатая
   по вертикали:

   - текст слева, фигура справа (сетка возвращается к двум колонкам,
     40% под визуал — при 844 это 312px, при 932 — 347);
   - шапка в одну строку, квалификаторы вниз (тот же механизм
     display: contents + order, что ниже 640, только повод другой:
     там не хватало высоты в принципе, здесь её нет совсем);
   - кнопке возвращается своя высота: align-items: center вместо
     stretch снимает подтягивание под карточку цены, остаются
     штатные 56px из .btn;
   - карточка цены ложится в строку (как ниже 640) — это ещё 30px;
   - отбивки шапки и блоков с --s-6/--s-7 на --s-3/--s-4;
   - кегли сами приходят к месту: min(--fz-h1, 17.5svh) при 390
     даёт 68px вместо 74, min(--fz-verdict, 4.8svh) — 18.7 вместо 21.

   Замер после правки — ниже, в отчёте по секции. */
@media (max-width: 1000px) and (orientation: landscape) and (max-height: 500px) {
  .hero { padding-block: clamp(var(--s-2), 2svh, var(--s-4)) clamp(var(--s-4), 5svh, var(--s-7)); }

  .hero .container {
    display: flex;
    flex-direction: column;
    gap: clamp(var(--s-2), 2.5svh, var(--s-4));
  }

  .hero__top { display: contents; }

  .hero__brand-row {
    order: 1;
    align-self: auto;
    gap: var(--s-3);
    min-height: 44px;
  }

  .hero__grid {
    order: 2;
    /* 40% под фигуру — тот же порядок, что на десктопе (там колонка
       визуала auto и берёт около трети). Меньше нельзя: ниже 36%
       силуэт в 262px мельчает до иконки, и правая половина экрана
       снова начинает читаться пустой. */
    grid-template-columns: minmax(0, 1fr) 40%;
    align-items: center;
    gap: var(--s-6);
  }

  .hero__facts { order: 3; flex: 0 0 auto; }

  .hero__logo { width: 116px; }
  .hero__brand-note { font-size: var(--fz-caption); line-height: 1.3; }
  .hero__brand-divider { align-self: center; height: 22px; }

  /* ОПИСАНИЕ ПЕРЕЕЗЖАЕТ ПОД КНОПКУ.

     Считаем бюджет первого экрана при высоте 390: поле сверху 8,
     строка бренда 44, зазор 10, ряд бейджа 46, заголовок с отбивками 78,
     подзаголовок в две строки 46 — и до кнопки остаётся 158px.
     Описание в этой колонке ложится в три строки (120 знаков при
     41 знаке в строке) и занимает 72 из этих 158 вместе с отбивкой.
     С ним пара «кнопка + цена» кончается на 468 при видимых 390,
     без него — на 313.

     Порядок чтения при этом не ломается, а выправляется под экран:
     заголовок → обещание → цена и действие → пояснение. Описание
     остаётся ровно там, куда смотрит человек после кнопки, и это
     буквально «рядом с кнопкой», а не третьим абзацем до неё.
     Поставить его В СТРОКУ с кнопкой нельзя по арифметике: колонка
     текста 436px, а кнопка с карточкой цены занимают 330 из них.

     Делается порядком во флекс-колонке, разметка не меняется —
     на всех остальных ширинах порядок прежний. */
  .hero__text { display: flex; flex-direction: column; }
  .hero__desc  { order: 1; margin-top: var(--s-3); }
  .hero__proof { order: 2; margin-top: var(--s-3); }

  .hero__title { margin-block: clamp(var(--s-2), 1.4svh, var(--s-3)); }

  /* ---------- Ряд действия: тот же, что в портрете ----------

     Это тот же телефон, только повёрнутый, поэтому ряд собран так же,
     как в блоке ≤640: кнопка тянется на весь остаток, цена стоит справа
     двумя строками без подложки. Разбор и арифметика — там, здесь
     только числа этой ориентации.

     ЧТО БЫЛО. Кнопка и карточка цены делили колонку текста в 440px
     по содержимому (flex: 0 1 auto у обеих). Карточке доставалось 250,
     кнопке 170 — и надпись «СОЗДАТЬ ИИ БРО» ломалась на две строки
     («СОЗДАТЬ / ИИ БРО»), а стрелка вставала сбоку от двухстрочного
     блока. При этом карточка была шире кнопки: главным пятном ряда
     читалась цена, а не действие.

     ЧТО СТАЛО. Цена разобрана до текста (79.8px), кнопке остаётся
     348 при нужных 208.7 — надпись в одну строку с запасом 139.
     Высота ряда падает с 69 до 64, то есть в ландшафте, где высоты
     нет вовсе, ряд ещё и дешевле. */
  .hero__actions {
    margin-top: var(--s-4);
    flex-wrap: nowrap;
    align-items: center;
    column-gap: var(--s-3);
  }

  .hero__actions .btn--primary {
    flex: 1 1 auto;
    min-width: 0;
    max-width: none;
    min-height: 56px;
    gap: var(--s-2);
    padding-inline: var(--s-4);
    font-size: var(--fz-small);
    letter-spacing: var(--ls-caps-sm);
  }

  .hero__price {
    flex: 0 0 auto;
    max-width: none;
    flex-direction: column;
    align-items: flex-end;
    justify-content: center;
    gap: var(--s-1);
    padding: 0;
    border-radius: 0;
    background: none;
    box-shadow: none;
    text-align: right;
  }

  .hero__price-row   { order: 1; gap: var(--s-1); }
  .hero__price-badge {
    order: 2;
    align-self: flex-end;
    padding: 0;
    border-radius: 0;
    background: none;
    color: var(--brand);
  }

  .hero__price-old { font-size: var(--fz-caption); }

  /* Фигура берёт колонку так же плотно, как в портрете ниже 640:
     84% ширины колонки (вывод и формулы — над кропом выше).
     При 111.73% (ступень для 641…1000) силуэт занял бы 62% колонки,
     то есть 193px при 312 — фигура в четверть экрана рядом
     с 68-пиксельным заголовком читается сноской. */
  .hero__visual {
    --robot-w: 151.38%;
    --robot-shift: -43.77%;
  }

  /* Аватарки в ландшафте НЕ мельчим, хотя соблазн есть: строка
     соцдоказательства рядом с четырьмя кружками по 56px ломается
     на четыре строки вместо двух. Пробовали 44px — не помогло и навредило.
     Высоту секции в ландшафте задаёт не текстовая колонка, а правая:
     кадр робота плюс три плашки столбиком. Замер: секция как была 626.9
     при 390 (1.61 экрана), так и осталась, а текстовая колонка, став
     короче, только переехала ниже от align-items: center — низ кнопки
     на 932×430 ушёл с 370 на 394. Меньше текста наверху — ниже кнопка;
     это не тот размен, ради которого стоит трогать размер. */

  .hero__abilities { margin-top: var(--s-3); gap: var(--s-2); }

  /* Ореол уходит с центра секции на центр правой колонки — фигура
     теперь там. 78% ширины секции: центр колонки визуала при 40%
     ширины и поле контейнера 32 дают 656 из 844 (77.7%) и 726 из 932
     (77.9%). Размер вдвое меньше портретного: колонка вдвое у́же
     экрана, а круг обязан оставаться ореолом фигуры, а не фоном
     половины страницы. При 42vw правая дуга не достаёт до кромки
     (835 из 844, 904 из 932) — резать нечего. */
  .hero__rings {
    --rings-size: min(42vw, 420px);
    width: var(--rings-size);
    left: 78%;
    right: auto;
    transform: translateX(-50%);
    top: calc(33% - var(--rings-size) / 2);
  }
}
