/* ===========================================================
   Pogosov studio — главная: секции.
   Общие правила (токены, кнопки, форма, появление) — в base.css.
   =========================================================== */

/* ---------- общая «шапка» секции: номер + линия + заголовок ---------- */
.section-head { display: grid; gap: clamp(28px, 4vw, 56px); margin-bottom: clamp(48px, 7vw, 104px); }
.section-head__meta {
	display: flex; align-items: center; gap: 18px;
	font: 600 .75rem/1 var(--font-text); letter-spacing: .14em; text-transform: uppercase; color: var(--ink-3);
}
.section-head__meta .rule { flex: 1; }
.section-head .h2 { max-width: 16ch; }
.on-night .section-head__meta { color: var(--on-night-2); }

/* линии списков: каждая рисуется слева направо, когда список вошёл в экран */
.list-lines > li { position: relative; }
.list-lines > li::before {
	content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1px;
	background: var(--line); transform-origin: left;
}
.js .list-lines > li::before {
	transform: scaleX(0);
	transition: transform 1200ms var(--ease-in-out);
	transition-delay: calc(var(--i, 0) * 90ms);
}
.js .list-lines.is-in > li::before { transform: none; }
/* «по одному» (телефон, см. css/base.css): линия пункта рисуется вместе с ним, а не каскадом со всем списком */
.js .list-lines.is-each > li::before { transition-delay: 0ms; }
.js .list-lines.is-each > li.is-in::before { transform: none; }
.list-lines > li:last-child::after {
	content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: var(--line);
}

/* ===========================================================
   Hero
   =========================================================== */
.hero {
	position: relative; min-height: 100svh;
	display: flex; align-items: flex-end;
	padding: calc(var(--header-h) + clamp(48px, 10vh, 120px)) 0 clamp(36px, 6vh, 72px);
	overflow: clip;
}
.hero__lines {
	position: absolute; inset: 0; pointer-events: none;
	-webkit-mask-image: linear-gradient(to bottom, #000 0, #000 46%, transparent 72%);
	mask-image: linear-gradient(to bottom, #000 0, #000 46%, transparent 72%);
}
.hero__lines > div {
	position: relative; height: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter);
}
.hero__lines i {
	position: absolute; top: 0; bottom: 0; width: 1px; background: var(--line);
	left: calc(var(--gutter) + (100% - var(--gutter) * 2) * var(--p));
	transform-origin: top;
}
.hero__lines i:nth-child(1) { --p: .25; }
.hero__lines i:nth-child(2) { --p: .5; }
.hero__lines i:nth-child(3) { --p: .75; }
.js .hero__lines i { transform: scaleY(0); transition: transform 1800ms var(--ease-in-out); transition-delay: calc(200ms + var(--i, 0) * 160ms); }
.js.is-ready .hero__lines i { transform: none; }
.hero__lines i:nth-child(1) { --i: 0; } .hero__lines i:nth-child(2) { --i: 1; } .hero__lines i:nth-child(3) { --i: 2; }

.hero__inner { position: relative; }
.hero__title {
	margin: clamp(28px, 5vh, 56px) 0 clamp(40px, 8vh, 96px);
	font-size: var(--fs-display); font-weight: 250; letter-spacing: -.05em; line-height: .96;
}
/* «курсор» — слэш из логотипа: наклон ≈10° (как у знака в вордмарке).
   Скос вокруг центра уводит верх вправо, а низ влево на ~.06em: левый отступ увеличен,
   правый нужен, чтобы маска .w (overflow: hidden) не срезала верх. */
.cursor {
	display: inline-block; width: .075em; height: .7em; margin: 0 .04em 0 .16em;
	background: var(--accent); vertical-align: baseline;
	transform: skewX(-10deg);
}
.is-ready .cursor { animation: blink 1.1s steps(1, end) 1.5s infinite backwards; }
@keyframes blink { 0%, 49.9% { opacity: 1; } 50%, 100% { opacity: 0; } }

.hero__foot { display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: clamp(16px, 2vw, 32px); row-gap: 32px; align-items: end; }
.hero__lead { grid-column: 1 / span 6; max-width: 36ch; font-size: var(--fs-lead); line-height: 1.5; color: var(--ink-2); }
.hero__actions { grid-column: 8 / -1; display: grid; justify-items: end; gap: clamp(20px, 3vh, 32px); }
.hero__tag { font-size: .875rem; line-height: 1.5; letter-spacing: .01em; color: var(--ink-2); text-align: right; }
.hero__tag .brace { margin-inline: .25em; }
.hero__cta { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 20px 32px; }
.hero__note { max-width: 24ch; font-size: .875rem; line-height: 1.5; color: var(--ink-3); text-align: right; }

@media (max-width: 860px) {
	.hero { min-height: 92svh; }
	.hero__lead, .hero__actions { grid-column: 1 / -1; }
	.hero__actions { justify-items: start; }
	.hero__tag, .hero__note { text-align: left; }
	.hero__cta { justify-content: flex-start; }
	.hero__lines i:nth-child(2) { display: none; }
}
@media (max-width: 600px) { .hero__lines { display: none; } }

/* ===========================================================
   Услуги
   =========================================================== */
.services__list { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: clamp(16px, 1.6vw, 24px); align-items: stretch; }
.service {
	position: relative; display: flex; flex-direction: column;
	padding: clamp(28px, 2.8vw, 44px);
	background: var(--white); border: 1px solid var(--line); border-radius: 28px;
	transition: border-color 250ms var(--ease-out), background-color 250ms var(--ease-out);
}
.service::after {
	content: ""; position: absolute; left: clamp(28px, 2.8vw, 44px); right: clamp(28px, 2.8vw, 44px); top: -1px; height: 2px;
	background: var(--accent); transform: scaleX(0); transform-origin: var(--stripe-origin, left);   /* сторону пишет js/main.js по входу и выходу курсора */
	transition: transform 450ms var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
	.service:hover { border-color: var(--line-strong); }
	.service:hover::after { transform: scaleX(1); }
}
.service__index { font: 600 .75rem/1 var(--font-text); letter-spacing: .14em; color: var(--ink-3); }
.service__name { margin-top: 22px; font-size: clamp(1.5rem, 1.1rem + 1.2vw, 2.125rem); font-weight: 400; letter-spacing: -.03em; }
.service__price {
	display: flex; align-items: baseline; gap: 10px; margin-top: clamp(20px, 2.4vw, 32px);
	font: 300 clamp(2.5rem, 1.7rem + 2.6vw, 3.75rem)/1 var(--font-display); letter-spacing: -.05em; white-space: nowrap;
}
.service__from { font: 500 .875rem/1 var(--font-text); letter-spacing: 0; color: var(--ink-3); }
.service__desc { margin-top: 20px; font-size: .9375rem; line-height: 1.6; color: var(--ink-2); }
.service__list { margin: clamp(24px, 3vw, 36px) 0 clamp(28px, 3vw, 40px); }
.service__list li {
	display: flex; align-items: baseline; gap: 12px;
	padding: 12px 0; border-top: 1px solid var(--line);
	font-size: .9375rem; line-height: 1.45; color: var(--ink);
}
.service__list li::before { content: ""; flex: none; width: 10px; height: 1px; background: var(--accent); transform: translateY(-.3em); }
.service__list li.is-meta { color: var(--ink-2); }
/* кнопка — по центру карточки; размер самой крупной из трёх пишет js/main.js в --service-btn-w / --service-btn-h, остальные тянутся до него */
.service .btn {
	margin-top: auto; align-self: center;
	min-width: min(var(--service-btn-w, 0px), 100%);
	min-height: max(var(--btn-h), var(--service-btn-h, 0px));
}
.service .btn--ghost { --bg-h: var(--accent); --fg-h: var(--ink); }   /* заливка при наведении — оранжевая */

@media (max-width: 1080px) { .services__list { grid-template-columns: minmax(0, 1fr); max-width: 640px; } }
.service .btn { max-width: 100%; white-space: normal; text-align: center; line-height: 1.25; padding-block: 12px; }
@media (max-width: 420px) {
	.service .btn { padding-inline: 1.2rem; gap: .5em; font-size: .875rem; }
	/* на телефоне боковые поля карточки — как поля страницы (20 px, было 28): в строку встаёт самая длинная кнопка
	   «заказать корпоративный сайт» (на 360 px она переносилась, и все три кнопки становились двухстрочными) */
	.service { padding-inline: 20px; }
	.service::after { left: 20px; right: 20px; }
}
.services { padding-bottom: var(--section-y-close); }   /* услуги ведут прямо к манифесту-заявлению студии — пауза короче обычной */

/* ===========================================================
   Преимущества
   =========================================================== */
.adv__split {
	display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: clamp(16px, 2vw, 32px);
	--adv-pad: clamp(28px, 3.2vw, 44px);
	--adv-title: clamp(1.375rem, 1.1rem + 1vw, 2rem);   /* кегль заголовков пунктов: от него считаются цифры и сдвиг заголовка раздела */
}
.adv__head { grid-column: 1 / span 5; position: sticky; top: calc(var(--header-h) + 32px); align-self: start; }
/* заголовок отпускается вместе с последним пунктом: линия над ним и линия над «05» уезжают одной линией.
   Закреплённый блок держит внутри раздела свой margin-box, поэтому нижний отступ = (высота «05» − высота заголовка)
   переносит момент, когда он отпускается, ровно туда, где линии совпали. Разницу мерит js/main.js (2в), бывает и < 0 */
.adv__head { margin-bottom: var(--adv-stop, 0px); }
/* линия над заголовком стоит вровень с линией над пунктом: у .section-head перед h2 идёт
   линия (.rule, высота 1px) + gap — здесь gap подогнан под тот же отступ, что и padding-top пункта */
.adv__head .section-head { margin-bottom: 0; gap: calc(var(--adv-pad) - 1px); }
/* …а верх строчных букв заголовка — на одной горизонтали с верхом цифр и строчных пункта (их верх ниже верха строки
   на .418 кегля пункта, см. ниже). У заголовка (Manrope, line-height 1.02) верх строчных ниже верха строки на
   (1.02 + 1.066 − .3)/2 − .54 = .353 его кегля — поднимаем h2 на разницу. Выравнивание по глифам, не по боксам:
   тот же расчёт, что у цифр; при смене шрифта или line-height пересчитать. */
.adv__head .h2 { margin-top: calc(var(--adv-title) * .418 - .353em); }
.adv__list { grid-column: 7 / -1; }
.adv__item {
	--adv-no: calc(var(--adv-title) * .74);              /* кегль цифр: их высота (.7275em у Inter) = высоте строчных заголовка (.54em у Manrope) */
	display: grid; grid-template-columns: 56px 1fr; column-gap: 8px;
	padding: var(--adv-pad) 0;
}
/* Верх цифр стоит ровно на верху строчных букв заголовка (x-height), при любом кегле: сдвиг считается по метрикам шрифтов, а не «на глаз».
   Заголовок (Manrope, line-height 1.15): верх строчных ниже верха строки на (1.15 + 1.066 − .3)/2 − .54 = .418 кегля.
   Цифры (Inter, line-height 1): верх цифр ниже верха строки на (1 + .969 − .241)/2 − .7275 = .1365 кегля.
   Раз кегль цифр .74 от заголовочного, совпадает и их низ — с базовой линией заголовка. Метрики сменятся вместе со шрифтом — пересчитать. */
.adv__no {
	padding-top: calc(var(--adv-title) * .418 - var(--adv-no) * .1365);
	font: 500 var(--adv-no)/1 var(--font-text); letter-spacing: .04em; color: var(--accent);
}
.adv__title { font-size: var(--adv-title); font-weight: 400; letter-spacing: -.03em; line-height: 1.15; }
.adv__text { margin-top: 14px; max-width: 46ch; color: var(--ink-2); }
.adv__item .adv__title { transition: transform 350ms var(--ease-out); }
@media (hover: hover) and (pointer: fine) { .adv__item:hover .adv__title { transform: translateX(8px); } }

/* ---------- манифест: оранжевая сцена ----------
   Без скрипта и при prefers-reduced-motion — обычный статичный оранжевый блок с готовым текстом.
   Живая версия (класс .is-live ставит js/manifest.js — только когда движение разрешено и экран не ниже 520 px)
   превращает блок в «дорожку» высотой в экран + --manifest-travel, по которой едет закреплённая сцена на весь экран.
   Прогресс прокрутки по дорожке пишет скрипт, дальше всё идёт по нему. В начале и в конце дорожки — по «тихому шагу» прокрутки
   (≈ 60svh), когда ничего не происходит: сначала «Преимущества» ушли и мозаика ещё не началась, в конце весь текст уже стоит на месте;
   и только после этого сцена уезжает вверх и начинается следующий раздел. Между ними:
     1. из центра растёт мозаика (<canvas>): клетки встают кругом одна за другой, у каждой светлый контур;
     2. экран залит — контуры гаснут, фон становится однотонным (.is-flat);
     3. слова проявляются по одному (--reveal от 0 до 1 пишется в .manifest-stage и наследуется текстом и скобками):
        от середины текста к краям, итоговая строка — отдельным шагом после; вместе с ними по бокам экрана
        съезжаются огромные бледные скобки { } (.manifest-stage__braces).
   Текст на оранжевом чернильный: основной — чернила 82 % (5.1:1 на #FE6D41, 4.5:1 и на участках под бледными скобками),
   итоговая строка — чистые (6.61:1); ни белый (2.81:1), ни --ink-2 (3.45:1) здесь не годятся.
   Скобки { } вокруг итоговой строки чернильные: оранжевые на оранжевом не видны. */
.advantages { padding-top: var(--section-y-close); padding-bottom: 0; }   /* сверху короткая пауза после услуг, снизу дальше сцена сама держит отступы */
.manifest-stage {
	--pin-h: 100svh;
	--manifest-travel: 420svh;         /* сколько прокрутки занимает вся анимация, сверх одного экрана. Запасное значение: js/manifest.js пишет сюда сумму фаз своего расписания */
	position: relative; overflow: clip;   /* clip, не hidden: не создаёт контейнер прокрутки, sticky у внуков работает; режет скобки по краю блока */
	margin-top: var(--section-y); padding-block: var(--section-y);
	background: var(--accent); color: var(--ink);
}
/* Стабильный большой экран: панели Safari не меняют сетку и дорожку посреди прокрутки. */
@supports (height: 100lvh) { .manifest-stage { --pin-h: 100lvh; } }
.manifest-stage__mosaic { display: none; }

.manifest { position: relative; width: 100%; padding-inline: var(--gutter); }   /* без --maxw: ширину строки держит сам текст в em */
.manifest__text {
	--soft: 5;                         /* сколько слов проявляются одновременно (волна) */
	/* крупный кегль: от ширины экрана, но не выше 7.5 % высоты — иначе на низком ноутбуке текст (6 строк + итоговая: неразрывные
	   пробелы держат «или&nbsp;конструкторы&nbsp;—» вместе) не влезет в закреплённый экран, ≈ 2/3 его высоты. Ширина — в em, а не
	   по контейнеру сайта: на любом экране строки той же длины и итоговая строка {…} помещается целиком (≈ 17.3em) */
	--manifest-size: clamp(1.75rem, min(1rem + 3.7vw, 7.5svh), 4.75rem);
	max-width: 19.5em; margin-inline: auto;
	/* по центру — туда же, откуда растёт мозаика; balance выравнивает длину строк, иначе по центру рваный край режет глаз */
	text-align: center; text-wrap: balance;
	font: 400 var(--manifest-size)/1.16 var(--font-display); letter-spacing: -.035em;
	color: var(--ink);
	color: color-mix(in srgb, var(--ink) 82%, var(--accent));
}
/* итоговая строка — отдельно, под основным текстом: «заявление → вывод» */
.manifest__text mark { display: block; margin-top: .45em; background: none; color: var(--ink); font-weight: 500; }
.manifest .brace { color: var(--ink); }

/* Огромные тонкие { } по бокам экрана, обрезанные его краями: обрамляют текст. Чернила ~7 %: тон в тон, читаться не мешают
   (под ними чернильный текст 82 % всё равно держит 4.5:1). Прозрачность и сдвиг идут от того же --reveal, что и слова:
   скобки проступают вместе с текстом и съезжаются из-за краёв к месту. Без скрипта --reveal = 1 — скобки просто на месте.
   Кегль — от высоты экрана (74svh: скобка целиком помещается по высоте), но не шире 52 vw (на телефоне), поэтому em-смещения ниже
   считаются от него */
.manifest-stage__braces {
	position: absolute; inset: 0;
	display: flex; align-items: center; justify-content: space-between;
	font: 200 min(74svh, 52vw)/1 var(--font-display);
	color: var(--ink);
	opacity: calc(.07 * var(--reveal, 1));
	pointer-events: none; user-select: none;
}
.manifest-stage__braces > span {
	--brace-side: -1;                  /* левая уходит за левый край, правая — за правый */
	transform: translateX(calc(var(--brace-side) * (.12em + (1 - var(--reveal, 1)) * .18em)));
}
.manifest-stage__braces > span + span { --brace-side: 1; }
/* слово проявляется, когда волна --reveal (0…1) дошла до его номера --word из --words; без скрипта спанов нет,
   со скриптом, но без движения --reveal = 1 и всё видно. Имена длинные нарочно: короткие вроде --t, --i уже заняты
   (раньше и --n: он был зарегистрирован через @property под счётчик цифр, не наследовался — и слова получали 0).
   Порядок номеров — от середины к краям, считает js/manifest.js по раскладке строк.
   Обычный inline, не inline-block: у атомарных блоков вокруг есть место для переноса, и запятая могла бы отвалиться от скобки */
.manifest__text .mw {
	opacity: clamp(0, calc((var(--reveal, 1) * (var(--words, 0) + var(--soft)) - var(--word, 0)) / var(--soft)), 1);
}

@media (max-width: 560px) {
	.manifest { padding-inline: clamp(32px, 9vw, 48px); }
	.manifest__text {
		--manifest-size: clamp(1.125rem, min(5.4vw, 3.5svh), 1.5rem);
		max-width: 18em; line-height: 1.24; letter-spacing: -.025em;
		opacity: var(--reveal, 1);
	}
	.manifest__text .mw { opacity: 1; }
	.manifest-stage__braces { font-size: min(80svh, 88vw); }
}

@media (max-width: 860px) {
	.manifest-stage__mosaic, .why-stage__mosaic { transform: translateZ(0); }
}

@media screen and (prefers-reduced-motion: no-preference) and (min-height: 520px) {
	.manifest-stage.is-live { height: calc(var(--pin-h) + var(--manifest-travel)); margin-top: 0; padding-block: 0; background: none; }
	.manifest-stage.is-live .manifest-stage__pin {
		position: sticky; top: 0; height: var(--pin-h); overflow: clip;
		display: flex; align-items: center;
	}
	.manifest-stage.is-live.is-flat .manifest-stage__pin { background: var(--accent); }
	.manifest-stage.is-live .manifest-stage__mosaic { display: block; position: absolute; inset: 0; width: 100%; height: 100%; }
	.manifest-stage.is-live.is-flat .manifest-stage__mosaic { visibility: hidden; }
}

@media (max-width: 960px) {
	.adv__split { display: block; }
	.adv__head { position: static; margin-bottom: clamp(36px, 6vw, 56px); }
	.adv__head .h2 { margin-top: 0; }   /* заголовок над списком, а не рядом — ровнять не с чем */
}

/* ===========================================================
   О компании: цифры
   =========================================================== */
/* сетка 4 колонки × 2 строки: первая строка — три цифры (четвёртая колонка там
   пустует), во второй — оранжевая плашка-приглашение (под первыми двумя колонками)
   и ещё две цифры: одна прямо под третьей цифрой сверху, другая — в пустующей
   четвёртой колонке. Ширина колонки — ширина её самого широкого числа (подписи и
   плашка в расчёт не идут, см. contain ниже), а весь остаток ширины поровну уходит
   в промежутки (space-between): первое число стоит в левом поле, «24/7» — в правом,
   и между любыми соседями по строке, включая плашку, одинаковый зазор. Видимых рамок
   между ячейками нет — ритм держат отступы и .stat__bar под каждым числом */
.about { padding-bottom: var(--section-y-close); }   /* цифры и портфолио — одна мысль о доказательствах, пауза короче обычной */
.stats {
	--stats-col-gap: clamp(16px, 2vw, 32px);         /* наименьший зазор — на случай, если числам станет тесно */
	--stats-row-gap: clamp(40px, 5vw, 64px);
	display: grid;
	grid-template-columns: repeat(4, auto);
	justify-content: space-between;
	column-gap: var(--stats-col-gap);
	row-gap: var(--stats-row-gap);
	border-top: 1px solid var(--line);
	padding-top: clamp(32px, 4vw, 56px);
}
.stat:nth-child(1) { grid-column: 1; grid-row: 1; }
.stat:nth-child(2) { grid-column: 2; grid-row: 1; }
.stat:nth-child(3) { grid-column: 3; grid-row: 1; }
.stats__cta { grid-column: 1 / span 2; grid-row: 2; }
.stat:nth-child(5) { grid-column: 3; grid-row: 2; }
.stat:nth-child(6) { grid-column: 4; grid-row: 2; }
/* подпись и плашка не раздвигают колонку: для расчёта сетки их ширина — ноль,
   поэтому колонка равна числу, подпись переносится по его ширине, а плашка
   растягивается ровно от левого поля до правого края второго числа над ней */
.stat__text, .stats__cta { contain: inline-size; }

/* крупнее прежнего: верх и низ столбика «число + подпись» должны совпадать
   с верхом и низом соседней оранжевой плашки в той же строке сетки (см. .stats__cta) */
.stat__num {
	position: relative;
	display: inline-block;                /* сжимается по цифрам — .stat__bar ниже меряет ширину от неё */
	font: 200 clamp(4.75rem, 2rem + 8.4vw, 9.5rem)/1 var(--font-display);
	letter-spacing: -.055em;
	white-space: nowrap;
}
.stat__unit { position: relative; top: .3em; margin-left: .06em; font-size: clamp(1.375rem, .6rem + 2vw, 2.75rem); font-weight: 300; letter-spacing: 0; vertical-align: top; }
.stat__text { margin-top: clamp(14px, 1.6vw, 24px); max-width: 18ch; font-size: .9375rem; line-height: 1.45; color: var(--ink-2); }

/* оранжевая плашка-приглашение в сетке цифр: текст сверху, круглая стрелка
   внизу справа. При наведении заливка становится чёрной, а стрелка
   поворачивается на 45° против часовой (→ становится ↗) — тот же приём,
   что у .btn--primary */
.stats__cta {
	position: relative;
	display: flex;
	flex-direction: column;
	justify-content: space-between;
	gap: clamp(16px, 2.4vw, 28px);
	min-height: clamp(160px, 13vw, 220px);   /* высота подогнана под соседнюю цифру справа (.stat__num выше) */
	padding: clamp(16px, 1.8vw, 26px);
	border-radius: 30px;
	background: var(--accent);
	color: var(--white);
	transition: background-color 320ms var(--ease-out);
}
/* .js [data-reveal] (css/base.css) уже задаёт transition на этом же элементе
   (opacity/transform для появления при прокрутке) с той же специфичностью —
   шорткод не складывается, а полностью перекрывает предыдущий, поэтому без
   этого правила заливка при наведении переключалась бы мгновенно, без анимации */
.stats__cta[data-reveal] {
	transition: opacity 900ms var(--ease-out), transform 1000ms var(--ease-out), background-color 320ms var(--ease-out);
}
.stats__cta-text {
	font: 400 clamp(1.125rem, .85rem + 1vw, 1.625rem)/1.25 var(--font-display);
	letter-spacing: -.03em;
}
.stats__cta-arrow {
	align-self: flex-end;
	flex: none;
	display: grid;
	place-items: center;
	width: clamp(40px, 3.4vw, 52px);
	height: clamp(40px, 3.4vw, 52px);
	border-radius: 50%;
	background: var(--white);
	color: var(--ink);
}
.stats__cta-icon { width: 20px; height: 20px; transition: transform 260ms var(--ease-out); }
@media (hover: hover) and (pointer: fine) {
	.stats__cta:hover { background-color: var(--ink); }
	.stats__cta:hover .stats__cta-icon { transform: rotate(-45deg); }
}

/* подсчёт 0 → число ведёт js/main.js (блок 2г): число заранее разрезано на ячейки-разряды
   шириной с итоговую цифру каждого разряда, бегущая цифра стоит по центру своей ячейки.
   Цифры Manrope пропорциональные — без ячеек ширина числа менялась бы каждый кадр и край
   дрожал. inline-flex, а не inline-block: цифра шире ячейки («9» в ячейке «1») выходит
   за неё поровну в обе стороны, а не только вправо. Ячейки стоят в строке — базовая линия
   у них общая с «%»; прежний слой поверх числа (::after, position: absolute) садился
   на 22 px выше строки, и в конце счёта число проседало */
.count__cell { display: inline-flex; justify-content: center; }

/* тонкая линия акцента наполняется вместе со счётом: скрипт начинает подсчёт по
   animationstart этой линии и считает с той же длительностью и кривой, так что
   число и линия приходят к финалу одновременно. Перекликается с линиями-разделителями
   под заголовками (.rule) и рамками сетки .stats */
.stat__bar {
	position: absolute; left: 0; bottom: -.16em;    /* em от .stat__num — держит пропорцию у крупной и мелкой цифры */
	width: 100%; height: 1.5px;
	background: var(--accent);
	transform: scaleX(0); transform-origin: left;
}
.js .stat.is-in .stat__bar { animation: stat-bar 1000ms ease-out both; animation-delay: calc(var(--i, 0) * 90ms); }
@keyframes stat-bar { from { transform: scaleX(0); } to { transform: scaleX(1); } }

@media (prefers-reduced-motion: reduce) {
	.stat__bar { transform: scaleX(1); }
	.js .stat.is-in .stat__bar { animation: none; }
}

/* сетка 4×2 держится, пока в строке есть место под 4 ровные колонки;
   уже на планшете это теснит подписи — ниже отдельной строкой на каждую
   цифру и на плашку-приглашение, просто без сетки */
@media (max-width: 900px) {
	.stats { display: block; }
	.stats > * { margin-top: clamp(32px, 6vw, 48px); }
	.stats > *:first-child { margin-top: 0; }
}

/* ===========================================================
   Портфолио
   =========================================================== */
.portfolio { padding-top: var(--section-y-close); }   /* продолжение мысли о доказательствах — см. .about */

/* Кейсы — во всю ширину экрана, но на сетке .container: 12 колонок, --gutter по краям, промежуток как у .grid.
   Контейнерные единицы (1cqw = 1% ширины .cases, без полосы прокрутки, в отличие от vw) дают края колонок
   прямо в CSS: --c-edge — левый край сетки (с него начинаются номер и описание, как заголовок «портфолио»
   выше), --c-split — левый край 4-й колонки (отсюда картинка и вертикальная линия), --c-desc — три колонки
   под описание. Линии белые, 1 px, как на референсе (alejandroha.com, секция Servicios); текст на оранжевом
   тоже белый — так попросил пользователь (контраст 2.8:1, решение осознанное, как в FAQ). */
.cases {
	container-type: inline-size;
	--c-wrap: min(100cqw, var(--maxw));
	--c-gap: clamp(16px, 2vw, 32px);
	--c-col: calc((var(--c-wrap) - 2 * var(--gutter) - 11 * var(--c-gap)) / 12);
	--c-edge: calc((100cqw - var(--c-wrap)) / 2 + var(--gutter));
	--c-split: calc(var(--c-edge) + 3 * (var(--c-col) + var(--c-gap)));
	--c-desc: calc(3 * var(--c-col) + 2 * var(--c-gap));
	--c-line: var(--white);
	background: var(--accent);
	color: var(--white);
}

/* Шапка кейса: тонкий крупный номер слева, плотное название справа, по одной базовой линии (контраст как
   у референса: лёгкий номер против жирного названия); линия сверху — граница между кейсами.
   Цифры Manrope 300 в строке высотой 1em сидят на .013em ниже середины (замерено по настоящей базовой
   линии — нулевой inline-block — и measureText цифр) — поля сверху/снизу разведены на эту величину,
   чтобы цифры стояли ровно посередине между линиями, а не их боксы. */
.case__row {
	--row-pad: clamp(14px, 1.4vw, 24px);
	--no-size: clamp(1.75rem, 1.1rem + 2vw, 3.25rem);
	display: flex; align-items: baseline; justify-content: space-between; gap: var(--c-gap);
	padding: calc(var(--row-pad) - .013 * var(--no-size)) var(--c-edge) calc(var(--row-pad) + .013 * var(--no-size));
	border-top: 1px solid var(--c-line);
}
.case__index {
	flex: none;
	font: 300 var(--no-size)/1 var(--font-display); letter-spacing: -.02em;
	font-variant-numeric: tabular-nums;
}
.case__title {
	min-width: 0;
	font: 600 clamp(1.375rem, .9rem + 1.45vw, 2.5rem)/1.05 var(--font-display); letter-spacing: -.03em;
	text-align: right; text-wrap: balance;
}

/* Визуал: описание прижато к низу левых трёх колонок, картинка — от 4-й колонки до правого края экрана.
   Высота здесь — для статичного списка; в закреплённой сцене её точно под экран ставит js/cases-stage.js. */
.case__visual { position: relative; height: clamp(420px, 46vw, 720px); }
/* линия под шапкой — часть визуала, а не нижняя граница шапки: когда визуал схлопнут, она пропадает вместе
   с ним, и шапки соседних кейсов разделяет одна линия, а не две вплотную */
.case__visual::before {
	content: ""; position: absolute; z-index: 3; top: 0; left: 0; right: 0;
	height: 1px; background: var(--c-line);
}
/* вертикаль по краю 4-й колонки: из неё раскрывается картинка следующего кейса */
.case__visual::after {
	content: ""; position: absolute; top: 0; bottom: 0; left: var(--c-split);
	width: 1px; background: var(--c-line);
}
.case__text {
	position: absolute; z-index: 1; left: var(--c-edge); bottom: clamp(24px, 2.6vw, 44px);
	width: var(--c-desc);
	font-size: clamp(1rem, .92rem + .3vw, 1.1875rem); line-height: 1.45;
	text-wrap: pretty;
}
.case__media {
	position: absolute; z-index: 2; top: 0; bottom: 0; left: var(--c-split); right: 0;
	overflow: hidden; background: var(--paper-2);
	transform-origin: 100% 0;   /* уходящая картинка сжимается к правому верхнему углу (js/cases-stage.js) */
}
.case__img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: top; }

/* Закреплённая сцена (.is-live ставит js/cases-stage.js: движение разрешено, экран не уже 861 и не ниже 520 px —
   условия продублированы там в matchMedia). Сцена стоит под шапкой сайта и едет по «дорожке» высотой
   --cases-travel экранов (скрипт пишет сумму своих фаз). Кейсы лежат друг на друге абсолютно, их место,
   обрезку визуала и картинки скрипт задаёт только через transform и clip-path — высоты и ширины на прокрутке
   не меняются (правило движения в base.css). */
.cases.is-live {
	--screen-h: calc(100svh - var(--header-h));
	height: calc(var(--screen-h) * (1 + var(--cases-travel, 2.8)));
	background: none;
}
@supports (height: 100dvh) {
	.cases.is-live { --screen-h: calc(100dvh - var(--header-h)); }
}
.cases.is-live .cases__pin {
	position: sticky; top: var(--header-h);
	height: var(--screen-h); overflow: clip;
	background: var(--accent);
}
.cases.is-live .case { position: absolute; top: 0; left: 0; right: 0; will-change: transform; }
.cases.is-live .case__media { will-change: transform; }

/* Планшет и телефон: кейсы списком — шапка, под ней картинка во всю ширину, под картинкой описание */
@media (max-width: 860px) {
	.case__row { --row-pad: 16px; }
	.case__visual { height: auto; display: flex; flex-direction: column; }
	.case__visual::after { display: none; }
	.case__media { position: relative; inset: auto; order: -1; aspect-ratio: 4 / 3; }
	.case__text { position: static; width: auto; max-width: calc(36ch + 2 * var(--c-edge)); padding: 20px var(--c-edge) 36px; }
}

/* ===========================================================
   Почему выбирают
   =========================================================== */
/* «POGOSOV/STUDIO?» — одно неделимое слово (≈8.4em в капсе): на экранах уже ~350px кегль
   подстраивается под ширину контейнера, чтобы слово не вылезало за край */
.why .h2 { font-size: min(var(--fs-h2), calc((100vw - 2 * var(--gutter)) / 8.8)); }
.why__grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: clamp(28px, 3vw, 56px) clamp(20px, 2.4vw, 40px); }
.why__item { position: relative; padding-top: 28px; }
.why__item::before { content: ""; position: absolute; left: 0; top: 0; width: 8px; height: 8px; border-radius: 50%; background: var(--accent); }
.why__item::after { content: ""; position: absolute; left: 20px; right: 0; top: 3.5px; height: 1px; background: var(--line); }
/* оранжевая линия поверх серой — та же, что .stat__bar у цифр «О компании»: в живой сцене наполняется слева направо */
.why__bar { position: absolute; left: 20px; right: 0; top: 3.25px; z-index: 1; height: 1.5px; background: var(--accent); transform-origin: left; }
.why__title { font-size: clamp(1.25rem, 1.05rem + .8vw, 1.75rem); font-weight: 400; letter-spacing: -.03em; line-height: 1.15; }
.why__text { margin-top: 14px; max-width: 30ch; color: var(--ink-2); }
.why__cta { margin-top: clamp(48px, 6vw, 88px); }
@media (max-width: 1080px) { .why__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px) { .why__grid { grid-template-columns: 1fr; } }

/* ---------- сцена: чёрная мозаика растёт слева направо ----------
   Тот же приём, что у манифеста в «преимуществах» (js/manifest.js, здесь — js/why-stage.js):
   без скрипта и при prefers-reduced-motion — обычный статичный тёмный блок с готовым текстом.
   С живой версией (.is-live) блок становится «дорожкой» высотой в экран + --why-travel, по которой
   едет закреплённая на весь экран сцена. Отличия от манифеста: (1) направление роста — клетки встают
   не кругом из центра, а неровной волной от левого края экрана к правому (см. js/why-stage.js), без
   контура и сразу окончательным цветом (в манифесте клетки сначала со светлым контуром, потом контуры
   гаснут в сплошную заливку — здесь этого шага нет); дойдя до правого края, экран уже сплошной (.is-flat);
   (2) текст не «проявляется прокруткой», как слова манифеста, а играет по отметкам: прокрутка доходит до отметки —
   скрипт ставит класс, дальше анимация идёт во времени (см. ниже, «появление»); (3) цвет клеток — тёмный (--night), как у футера, а не акцентный, поэтому фон и
   текст берут готовую тёмную тему сайта (.on-night, base.css) — точка-маркер и кнопка «заказать» остаются
   акцентно-оранжевыми и заметно проступают на чёрном */
.why-stage {
	--pin-h: 100svh;
	--why-travel: 380svh;   /* запасное значение: js/why-stage.js сам пишет сюда сумму фаз своего расписания */
	position: relative; overflow: clip;
	background: var(--night); color: var(--on-night);
}
@supports (height: 100lvh) { .why-stage { --pin-h: 100lvh; } }
.why-stage__mosaic { display: none; }
.why-stage__content { position: relative; width: 100%; }

.why-stage .why__item::after { background: var(--night-line); }
.why-stage .why__text { color: var(--on-night-2); }

/* ---------- появление: три отметки на прокрутке, анимация — во времени ----------
   js/why-stage.js ставит на сцену классы, когда прокрутка проходит отметку фазы TEXT, и снимает, если вернуться назад:
     .is-title-in — слова заголовка выезжают из-под маски (как у всех заголовков сайта: .w/.w__in, base.css);
     .is-grid-in  — причины, как цифры «О компании»: блок приходит слева (fade-left), точка раскрывается,
                    оранжевая линия наполняется от точки вправо (1000 мс ease-out, как .stat__bar), каскадом по --i;
                    кнопка — тем же fade-left, следующим шагом каскада (--i: 4 в разметке), сразу за последней причиной.
   Правила ниже — для скрытого состояния, их transition играет при снятии класса: быстро и без каскада, чтобы
   при прокрутке назад текст успел погаснуть раньше, чем отступающая мозаика откроет светлую страницу.
   Только в живой сцене (.is-live): без неё всё стоит на месте, линия наполнена. */
.why-stage.is-live .w__in {
	transform: translateY(112%);
	transition: transform 360ms var(--ease-out); transition-delay: 0ms;
}
.why-stage.is-live.is-title-in .w__in {
	transform: none;
	transition: transform 1000ms var(--ease-out); transition-delay: calc(var(--w, 0) * 70ms);
}
.why-stage.is-live .why__item,
.why-stage.is-live .why__cta {
	opacity: 0; transform: translateX(-24px);
	transition: opacity 300ms var(--ease-out), transform 300ms var(--ease-out);
}
.why-stage.is-live.is-grid-in .why__item,
.why-stage.is-live.is-grid-in .why__cta {
	opacity: 1; transform: none;
	transition: opacity 900ms var(--ease-out), transform 1000ms var(--ease-out); transition-delay: calc(var(--i, 0) * 80ms);
}
.why-stage.is-live .why__item::before { transform: scale(0); transition: transform 300ms var(--ease-out); }
.why-stage.is-live.is-grid-in .why__item::before {
	transform: none;
	transition: transform 700ms var(--ease-out); transition-delay: calc(var(--i, 0) * 80ms);
}
.why-stage.is-live .why__bar { transform: scaleX(0); transition: transform 300ms var(--ease-out); }
.why-stage.is-live.is-grid-in .why__bar {
	transform: scaleX(1);
	transition: transform 1000ms ease-out; transition-delay: calc(120ms + var(--i, 0) * 90ms);   /* сначала точка, потом из неё линия */
}

@media screen and (prefers-reduced-motion: no-preference) and (min-height: 520px) {
	/* Выход (js/why-stage.js, фаза CLEAR): ночь уходит той же волной за правый край и уносит текст — скрипт обрезает
	   содержимое по ещё чёрным клеткам (clip-path: path). Фон закреплённого экрана прозрачный, и в погасших клетках
	   видно то, что лежит под сценой, — «План работы» (см. ниже). Ушла целиком (.is-day) — канвас не нужен,
	   содержимое скрыто. */
	.why-stage.is-live { height: calc(var(--pin-h) + var(--why-travel)); padding-block: 0; background: none; }
	.why-stage.is-live .why-stage__pin {
		position: sticky; top: 0; height: var(--pin-h); overflow: clip;
		display: flex; align-items: center;
		padding-top: var(--header-h);   /* шапка «плавающая» (base.css, .header — своей высоты в потоке не занимает) и перекрыла бы центрированный заголовок */
	}
	.why-stage.is-live.is-flat .why-stage__pin { background: var(--night); }
	.why-stage.is-live .why-stage__mosaic { display: block; position: absolute; inset: 0; width: 100%; height: 100%; }
	.why-stage.is-live.is-flat .why-stage__mosaic, .why-stage.is-live.is-day .why-stage__mosaic { visibility: hidden; }

	/* «План работы» под сценой. Сцена «отдаёт» вниз экран и ещё --why-tail — прокрутку фаз clear + rest (пишет
	   js/why-stage.js), — и раздел заходит под неё так, что к началу выхода его верх стоит у верха экрана. Сцена лежит
	   выше (z-index). Содержимое раздела закреплено (sticky) ровно на --why-tail: пока уходит ночь, оно стоит на месте,
	   а дальше вместе со сценой уезжает обычной прокруткой. Распорка ::after даёт sticky этот запас; после отпускания
	   содержимое лежит поверх неё, поэтому лишнего пустого места в разделе не видно. Верхний отступ — не меньше высоты
	   шапки с воздухом: в закреплённом кадре заголовок не должен уходить под неё.
	   Указатель сцена пропускает насквозь, кроме сплошной ночи (.is-flat — под ней ничего доступного нет) и самого текста:
	   он обрезан по клеткам и для указателя тоже, так что в погасших клетках работает то, что под ними. */
	.why-stage.is-live { z-index: 1; margin-bottom: calc(-1 * (var(--pin-h) + var(--why-tail, 0svh))); pointer-events: none; }
	.why-stage.is-live.is-flat .why-stage__pin, .why-stage.is-live .why-stage__content { pointer-events: auto; }
	.why-stage.is-live + .steps { --steps-top: max(var(--section-y-close), calc(var(--header-h) + 32px)); padding-top: var(--steps-top); }
	.why-stage.is-live + .steps > .container { position: sticky; top: var(--steps-top); }
	.why-stage.is-live + .steps::after { content: ""; display: block; height: var(--why-tail, 0svh); }
}

/* Телефон. Содержимое закреплённой сцены должно целиком стоять в экране под шапкой: заголовок не уходит под логотип,
   кнопка не обрезается. Столбик из четырёх причин (так они стоят в статичной раскладке, см. .why__grid выше) выше экрана
   телефона: ≈ 660 px против 600 под шапкой на 390 × 664 — высота 844 бывает только без панелей браузера. Поэтому, пока сцена
   закреплена, причины стоят сеткой 2 × 2, как на планшете, их текст — 15 px / 1.45 (как подписи цифр «О компании»),
   а отступы плотнее и зависят от высоты экрана: --why-air — запас воздуха, 0 на экране 553 px (iPhone SE с панелями браузера)
   и 8 px от 664 px (iPhone 12–16). Название причины не шире колонки: «индивидуальный» — одно слово, ≈ 7.6em, на экране уже
   ~365 px кегль подстраивается под колонку (тот же приём, что у .why .h2).
   Если и так не помещается (совсем низкий экран, крупный системный шрифт), js/why-stage.js сцену не включает — остаётся
   статичный блок в один столбец. Решение пользователя, 2026-10-12. */
@media screen and (max-width: 560px) and (prefers-reduced-motion: no-preference) and (min-height: 520px) {
	.why-stage.is-live { --why-air: clamp(0px, 7.2svh - 40px, 8px); }
	.why-stage.is-live .section-head { margin-bottom: calc(16px + var(--why-air, 8px)); }
	.why-stage.is-live .why__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: calc(12px + var(--why-air, 8px)) 16px; }
	.why-stage.is-live .why__item { padding-top: calc(14px + var(--why-air, 8px)); }
	.why-stage.is-live .why__title { font-size: min(1.25rem, calc((100vw - 2 * var(--gutter) - 16px) / 15.4)); }
	.why-stage.is-live .why__text { margin-top: 8px; font-size: .9375rem; line-height: 1.45; }
	.why-stage.is-live .why__cta { margin-top: calc(16px + var(--why-air, 8px)); }
}

/* Один непрерывный слой мозаики на телефоне: смена направления не включает
   канвас заново после замены на фон. Сплошной и пустой кадры сохраняет JS. */
@media screen and (max-width: 860px) and (prefers-reduced-motion: no-preference) and (min-height: 520px) {
	.manifest-stage.is-live.is-flat .manifest-stage__pin,
	.why-stage.is-live.is-flat .why-stage__pin { background: none; }
	.manifest-stage.is-live.is-flat .manifest-stage__mosaic,
	.why-stage.is-live.is-flat .why-stage__mosaic,
	.why-stage.is-live.is-day .why-stage__mosaic { visibility: visible; }
}

/* В чёрном блоке оставляем место для кнопки при открытых панелях Safari.
   Манифест центрируется по полной высоте сцены, как и декоративные скобки. */
@supports (height: 100lvh) {
	@media (max-width: 860px) {
		.why-stage.is-live .why-stage__pin { padding-bottom: calc(100lvh - 100svh); }
	}
}

/* ===========================================================
   План работы — змейка в два этажа и маршрут

   Шире 900 px шаги стоят в две колонки (по шесть колонок сетки .container, зазор как у .grid) на двух этажах:
   01 и 02, под ними 03 и 04. Правый шаг этажа опущен на --stair-y — высоту цифр номера: верх цифр стоит на --step-top
   ниже линии, поэтому цифры правого шага начинаются там, где у левого базовая линия. Внутри шага номер слева, название
   и текст справа; всё содержимое отступает от угла шага на --step-in — и у плашки «05» поля те же, так что номера, названия
   и тексты 01, 03 и 05 стоят на общих вертикалях. «05» — оранжевая плашка во всю ширину под вторым этажом, с кнопкой
   «заказать» справа внизу.

   Маршрут — оранжевая линия 1.5 px, змейкой: по верху 01 (.step__run) до левого края 02, вниз на ступень (.step__drop),
   по верху 02 до правого поля; с конца этажа (.step--edge) — вниз по правому краю до второго этажа, обратно влево над ним
   во всю ширину (.step__back — рисуется справа налево) и вниз на ступень к углу 03 (.step__in) — это разворот; дальше так же
   через 03 и 04, а от 04 — вниз по правому краю в правый верхний угол плашки: этот угол у неё прямой, остальные скруглены.
   Серой линии сверху у шагов в змейке нет: верх шага — сам маршрут. Уже 900 px — столбик: шаги друг под другом, маршрут —
   вертикаль по левому краю (.step__drop во всю высоту шага), пробегов и разворота нет, сверху у шага серая линия; прямой
   у плашки — левый верхний угол.

   Движение — js/steps-route.js (.is-live на списке). Линия идёт во времени, а не за прокруткой: шаг «открывается»,
   когда он на экране и над ним не осталось ночи «почему выбирают» (раздел заходит под её закреплённый экран, см. выше),
   и кончик едет к нему — с разгоном и торможением. Дошёл до угла шага — .is-reached: номер и название выезжают из-под
   маски, текст проявляется; у плашки заливка раскрывается кругом из угла, куда пришла линия, потом текст и кнопка.
   Без скрипта всё нарисовано сразу.
   =========================================================== */
.steps { padding-bottom: var(--section-y-close); }   /* план работы и faq — тот же практический разговор, пауза короче обычной */
.steps .section-head { margin-bottom: clamp(36px, 4.5vw, 72px); }   /* сверху у шагов своя линия — заголовку хватает меньшего отступа */
.steps__list {
	--stair-g: clamp(16px, 2vw, 32px);               /* зазор колонок — как у .grid */
	--no-col: clamp(66px, 10vw, 128px);              /* колонка номера в столбике */
	--step-pad: clamp(14px, 1.6vw, 24px);            /* от линии до номера и от текста до правого края */
	--step-no: clamp(2.5rem, 1.6rem + 3vw, 5rem);    /* кегль номера */
	--step-top: clamp(22px, 2.2vw, 34px);            /* от линии до верха цифр */
	--plate-x: clamp(20px, 2.4vw, 36px);             /* поля плашки «05» */
	--fill-at: 0 0;                                  /* угол, из которого раскрывается заливка плашки (куда приходит линия) */
	position: relative;
}
/* Верх цифр и верх строчных названия — на одной горизонтали, по глифам, а не по боксам (метрики Manrope: ascent 1.066,
   descent .3, цифры .72, строчные .54 кегля). Верх строки выше верха глифа на line-height/2 + (ascent − descent)/2 − высота:
   у номера (line-height 1) (1 + .766)/2 − .72 = .163 кегля, у названия (1.15) (1.15 + .766)/2 − .54 = .418 кегля.
   На эти величины оба подняты отрицательным margin-top и упираются верхом глифов в верх ряда; отступ шага увеличен
   на те же .163 кегля номера — верх цифр стоит ровно на --step-top от линии. Номер занимает все ряды — текст идёт сразу
   под названием; если текст короче номера, лишняя высота уходит в последний ряд (1fr), а не раздвигает название и текст. */
.step {
	position: relative;
	display: grid; grid-template-columns: var(--no-col) minmax(0, 1fr); grid-template-rows: auto auto 1fr; align-items: start;
	padding: calc(var(--step-top) + var(--step-no) * .163) 0 clamp(40px, 4.6vw, 72px);
}
.step::before {
	content: ""; position: absolute; left: 0; right: 0; top: 0; height: 1px;
	background: var(--line); transform-origin: left;
}
/* маршрут: 1.5 px, как .stat__bar и .why__bar; от целого пикселя (на экране 2× это ровно 3 точки — линия чёткая).
   В столбике — только вертикаль по левому краю шага до угла следующего */
.step__run, .step__drop, .step__back, .step__in { position: absolute; top: 0; left: 0; background: var(--accent); pointer-events: none; }
.step__run { display: none; height: 1.5px; transform-origin: left; }
.step__drop { width: 1.5px; height: 100%; transform-origin: top; }
.step__back { display: none; height: 1.5px; transform-origin: right; }   /* разворот змейки — идёт справа налево */
.step__in { display: none; width: 1.5px; transform-origin: top; }

.step__no {
	grid-column: 1; grid-row: 1 / span 3; margin-top: -.163em; padding-left: var(--step-pad);
	font: 200 var(--step-no)/1 var(--font-display); letter-spacing: -.05em;
	font-variant-numeric: tabular-nums;
}
.step__title {
	grid-column: 2; grid-row: 1; margin-top: -.418em; padding-right: var(--step-pad);
	font-size: clamp(1.375rem, 1.1rem + 1vw, 2rem); font-weight: 400; letter-spacing: -.03em; line-height: 1.15;
}
.step__lift { display: block; }
.step__text { grid-column: 2; grid-row: 2; max-width: 52ch; margin-top: clamp(10px, 1vw, 16px); padding-right: var(--step-pad); color: var(--ink-2); }
.step__cta { grid-column: 2; grid-row: 3; margin-top: clamp(24px, 2.6vw, 36px); }

/* «05» — сплошная оранжевая плашка; прямой угол — тот, куда приходит маршрут. Серой линии у неё нет.
   Текст на ней только чернильный: --ink-2 на оранжевом даёт 3.45:1, --ink — 6.61:1 */
.step--final {
	padding-top: calc(clamp(26px, 2.8vw, 44px) + var(--step-no) * .163); padding-bottom: clamp(32px, 3.4vw, 52px);
	background: var(--accent); border-radius: 0 28px 28px 28px;
}
.step--final::before { content: none; }
.step--final .step__title, .step--final .step__text { padding-right: var(--plate-x); }
.step--final .step__text { color: var(--ink); }

/* кнопка на оранжевом: оранжевая таблетка там пропала бы — она чернильная, белый круг стрелки остаётся. При наведении
   заливка бумажная, круг — оранжевый (в слое заливки js/cursor.js, без него — у самой кнопки) */
.step__cta .btn--primary { --bg: var(--ink); --fg: var(--paper); --bg-h: var(--paper); --fg-h: var(--ink); }
.step__cta .btn--primary .btn__fill .btn__arrow { background: var(--accent); }
@media (hover: hover) and (pointer: fine) {
	.step__cta .btn--primary:not(.has-fill):hover .btn__arrow { background: var(--accent); }
}

@media (min-width: 900px) {
	.steps__list {
		--stair-y: calc(var(--step-no) * .72);       /* ступень = высота цифр номера */
		--floor-gap: clamp(32px, 3.4vw, 56px);       /* от этажа до линии разворота и от второго этажа до плашки */
		--step-in: var(--plate-x);                   /* отступ содержимого от угла шага — те же поля, что у плашки */
		--no-w: calc(var(--step-no) * 1.4);          /* колонка номера: цифры (.98 кегля) и воздух до названия */
		--fill-at: 100% 0;
		display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
		column-gap: var(--stair-g); row-gap: var(--floor-gap);
	}
	/* строка тянет шаги до общего низа (align-self: stretch) — по нему считается спуск с конца этажа */
	.step {
		grid-template-columns: var(--no-w) minmax(0, 1fr);
		padding: calc(var(--step-top) + var(--step-no) * .163) 0 0 var(--step-in);
	}
	.step::before { content: none; }
	.step__no { padding-left: 0; }
	.step__title, .step__text { padding-right: 0; }
	/* лесенка: правый шаг этажа ниже левого на ступень; второй этаж начинается линией разворота, 03 — на ступень ниже неё */
	.step--edge, .step--turn { margin-top: var(--stair-y); }
	.step--turn + .step--edge { margin-top: calc(2 * var(--stair-y)); }
	.step--edge { padding-right: var(--step-pad); }   /* по правому краю спускается линия — текст отступает от неё */

	/* 100% — вся колонка (у абсолютного слоя отсчёт от рамки отступов шага, отступы входят); +1.5px — заход на соседний
	   кусок: углы без щели */
	.step__run { display: block; width: calc(100% + var(--stair-g) + 1.5px); }   /* до левого края правого шага */
	.step__drop { left: calc(100% + var(--stair-g)); height: calc(var(--stair-y) + 1.5px); }
	.step--edge .step__run { width: 100%; }
	.step--edge .step__drop { left: calc(100% - 1.5px); height: calc(100% + var(--floor-gap) + 1.5px); }
	.step__back { display: block; top: calc(-1 * var(--stair-y)); width: calc(200% + var(--stair-g)); }
	.step__in { display: block; top: calc(-1 * var(--stair-y)); height: calc(var(--stair-y) + 1.5px); }

	/* плашка: те же поля и та же колонка номера, что у шагов, — название и текст на вертикали текстов 01 и 03; кнопка справа
	   внизу, по низу текста */
	.step--final {
		grid-column: 1 / -1; margin-top: 0;
		grid-template-columns: var(--no-w) minmax(0, 1fr) auto; grid-template-rows: auto 1fr;
		padding: calc(clamp(26px, 2.8vw, 44px) + var(--step-no) * .163) var(--plate-x) clamp(32px, 3.4vw, 52px) var(--step-in);
		border-radius: 28px 0 28px 28px;
	}
	.step--final .step__no { grid-row: 1 / span 2; }
	.step--final .step__title, .step--final .step__text { padding-right: 0; }
	.step--final .step__text { max-width: 60ch; }
	.step--final .step__cta { grid-column: 3; grid-row: 1 / span 2; align-self: end; margin: 0 0 0 var(--stair-g); }
}
@media (max-width: 860px) {
	.steps__list { --step-pad: 14px; --step-no: 2rem; }
}

/* ---------- движение (js/steps-route.js) ---------- */
/* кончик маршрута — точка, как у причин «почему выбирают»; центр — на оси линии (0.75 px). Координаты — от угла списка */
.steps__dot {
	position: absolute; left: -3.25px; top: -3.25px; z-index: 1;
	width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
	pointer-events: none; opacity: 0; transition: opacity 300ms var(--ease-out);
}
.steps__list.is-routing .steps__dot { opacity: 1; }

.steps__list.is-live .step::before { transform: scaleX(0); transition: transform 1200ms var(--ease-in-out); }
.steps__list.is-live .step.is-reached::before { transform: none; }

/* маска у номера и названия — clip-path родителя с запасом под выносные; сам текст уезжает под неё и выезжает обратно */
.steps__list.is-live .step__no, .steps__list.is-live .step__title { clip-path: inset(-.3em -.3em -.3em -.3em); }
.steps__list.is-live .step__lift {
	transform: translateY(calc(100% + .4em));
	transition: transform 1000ms var(--ease-out); transition-delay: var(--reach-d, 0ms);
}
.steps__list.is-live .step__title .step__lift { transition-delay: calc(var(--reach-d, 0ms) + 90ms); }
.steps__list.is-live .step__text {
	opacity: 0; transform: translateY(20px);
	transition: opacity 900ms var(--ease-out), transform 1000ms var(--ease-out);
	transition-delay: calc(var(--reach-d, 0ms) + 200ms);
}
/* кнопка — последним шагом; пока она не видна, её нет и для указателя и клавиатуры (visibility) */
.steps__list.is-live .step__cta {
	opacity: 0; transform: translateY(16px); visibility: hidden;
	transition: opacity 900ms var(--ease-out), transform 1000ms var(--ease-out), visibility 0s;
	transition-delay: calc(var(--reach-d, 0ms) + 320ms);
}
/* пока шаг или заголовок не открыт, его не видно — нет его и для указателя: под уходящей ночью «почему» курсор не должен
   находить скрытый текст (выделение, текстовый курсор) */
.steps__list.is-live .step:not(.is-reached), .js .steps [data-held]:not(.is-in) { pointer-events: none; }
.steps__list.is-live .step.is-reached .step__lift,
.steps__list.is-live .step.is-reached .step__text,
.steps__list.is-live .step.is-reached .step__cta { opacity: 1; transform: none; visibility: visible; }

/* плашка «05»: заливка — слой под текстом, раскрывается кругом из угла, куда пришла линия (--fill-at). Кривая —
   ease-in-out, как у раскрытий [data-clip]: с ease-out круг почти весь вырастал за первые 300 мс и роста из угла было не видно.
   Текст стартует позже (--reach-d), когда круг уже накрыл угол и начало строки — выезжает на оранжевое */
.steps__list.is-live .step--final { --reach-d: 450ms; background: none; isolation: isolate; }
.steps__list.is-live .step--final::before {
	content: ""; position: absolute; inset: 0; z-index: -1; height: auto;   /* height — сброс от серой линии .step::before */
	border-radius: inherit; background: var(--accent);
	transform: none; clip-path: circle(0 at var(--fill-at)); transition: clip-path 1100ms var(--ease-in-out);
}
.steps__list.is-live .step--final.is-reached::before { clip-path: circle(150% at var(--fill-at)); }

@media (prefers-reduced-motion: reduce) {
	/* линии сразу целиком, точки нет; шаги и заливка «05» только проявляются */
	.steps__dot { display: none; }
	.steps__list.is-live .step::before { transform: none; transition: none; }
	.steps__list.is-live .step__lift, .steps__list.is-live .step__title .step__lift, .steps__list.is-live .step__text,
	.steps__list.is-live .step__cta {
		transform: none; opacity: 0; transition: opacity 300ms ease, visibility 0s; transition-delay: 0ms;
	}
	.steps__list.is-live .step--final::before { clip-path: none; opacity: 0; transition: opacity 300ms ease; }
	.steps__list.is-live .step--final.is-reached::before { opacity: 1; }
}

/* ===========================================================
   FAQ — стопка карточек

   В покое видны только «язычки»: верхний контур (2 px, скругление 30 px, низ открыт) и номер. Под указателем
   карточка вырастает вниз (карточки ниже уезжают), а акцентовая заливка поднимается снизу вверх — с какой бы
   стороны ни зашёл указатель. Текст в оранжевой плашке белый. Открыта одна.

   Низ открытой карточки повторяет очертание следующей плашки — скругление ВНУТРЬ: прямая нижняя кромка стоит
   по верху следующей плашки, а по бокам оранжевое заходит вниз ещё на радиус (--faq-ext = 30 px) и там его
   вырезают скруглённые верхние углы следующей плашки. Для этого у каждой карточки непрозрачная подложка цвета
   страницы, следующая карточка лежит поверх (порядок в DOM), а открытая заходит под неё отрицательным margin-bottom —
   в раскладке она по-прежнему занимает ровно свой слот. У последней карточки плашки под ней нет: низ скруглён наружу.

   Верх цифр, вопроса и ответа стоит на одной линии — по верху заглавных, а не по границам боксов. Строка шрифта
   выше заглавной на --k-* em (считано по метрикам Inter и Manrope: line-height/2 + (ascent − descent)/2 − capHeight);
   на эту величину каждый текст поднят отрицательным margin-top, и все три упираются в верх ряда.

   Размеры пишет js/main.js: --faq-h — своя высота карточки, --faq-open — высота самой высокой. С мышью все карточки
   раскрываются до --faq-open: при переходе с одной на другую суммарная высота стопки не меняется, и указатель не
   «выпадает» из карточки, когда соседняя схлопывается. Поэтому у высоты одинаковые время и кривая и на раскрытие,
   и на закрытие. На телефоне — своя высота, страница просто сдвигается.

   Без скрипта — обычный список: у каждой карточки видны вопрос и ответ.
   =========================================================== */
.faq {
	padding-top: var(--section-y-close);   /* продолжение «плана работы» — см. .steps */
	--faq-stroke: 2px;
	--faq-radius: 30px;
	--faq-line: #B5B5B5;                                   /* цвет контура — с макета */
	--faq-pad-x: 15px;
	--faq-pad-y: clamp(10px, .9vw, 12px);
	--faq-gap: clamp(24px, 3.3vw, 48px);                   /* воздух между открытой нижней карточкой и тёмным блоком */
	--faq-no: clamp(1.75rem, 1rem + 1.9vw, 2.5rem);        /* кегль номера */
	--faq-peek: calc(var(--faq-stroke) + 2 * var(--faq-pad-y) + var(--faq-no));   /* высота закрытой карточки: 66 px на 1440 */
	--faq-dur: 540ms;                                      /* высота, отступ, заливка — один ритм */
	/* своя кривая, не из токенов base.css: те либо «выстреливают» (за 120 мс проходят половину пути), либо слишком
	   вялые для наведения. Мягкий старт (3% пути за первую двенадцатую времени), половина к четверти времени, длинный хвост */
	--faq-ease: cubic-bezier(.3, 0, .1, 1);
	/* от верха строки до верха заглавной, em: номер (Inter, 1), вопрос (Manrope, 1.15), ответ (Inter, 1.6) */
	--k-no: .1365;
	--k-q: .238;
	--k-a: .4365;
}
.faq__stack { margin: 0; padding: 0; list-style: none; }

.faq__card {
	position: relative; overflow: hidden;
	border: var(--faq-stroke) solid var(--faq-line); border-bottom: 0;
	border-radius: var(--faq-radius) var(--faq-radius) 0 0;
	background: var(--paper);                              /* непрозрачная: закрывает оранжевое соседней карточки, заходящее под неё */
	color: var(--ink);
}
/* Заливку (акцентовый слой) рисует сам фон карточки, а не псевдоэлемент: фон по умолчанию уходит под рамку, и рамка
   ложится поверх него. Слой внутри рамки (как было раньше) на дробных координатах — а ширина карточки почти всегда
   дробная — давал светлый шов вдоль правого края и по дуге угла: край слоя и внутренний край рамки сглаживаются
   независимо, и между ними просвечивает подложка. */

.faq__inner {
	display: grid; grid-template-columns: repeat(12, minmax(0, 1fr)); column-gap: clamp(16px, 2vw, 32px);
	align-items: start;
	/* верх заглавных стоит там же, где стоял верх цифр в покое: контур + отступ + запас строки номера */
	padding: calc(var(--faq-pad-y) + var(--k-no) * var(--faq-no)) var(--faq-pad-x) clamp(24px, 2.6vw, 36px);
}
.faq__no {
	grid-column: 1;
	margin-top: calc(var(--k-no) * -1em);
	font: 400 var(--faq-no)/1 var(--font-text); font-variant-numeric: tabular-nums;
	color: var(--accent);                                  /* декоративный номер, крупный — акцентом можно */
	transition: color 260ms var(--ease-out);
}
/* вопрос вплотную к номеру: колонка 2 (было 3), ширина прежняя — строки переносятся как раньше; колонка 6 — воздух до ответа */
.faq__q {
	grid-column: 2 / span 4; margin-top: calc(var(--k-q) * -1em);
	font-size: clamp(1.375rem, 1.1rem + 1vw, 2rem); font-weight: 400; letter-spacing: -.03em; line-height: 1.15;
}
.faq__a { grid-column: 7 / -1; max-width: 52ch; margin-top: calc(var(--k-a) * -1em); }
.faq__a p + p, .faq__a ul, .faq__a p + ul { margin-top: 12px; }
.faq__a li { display: flex; align-items: baseline; gap: 12px; padding: 3px 0; }
.faq__a li::before { content: ""; flex: none; width: 10px; height: 1px; background: currentColor; transform: translateY(-.3em); }
.faq__a .faq__lead { margin-top: 16px; }

/* кнопку для клавиатуры и скринридера собирает js/main.js: вопрос остаётся заголовком, внутри него — кнопка */
.faq__btn {
	display: block; width: 100%; margin: 0; padding: 0; border: 0; background: none;
	color: inherit; font: inherit; letter-spacing: inherit; text-align: left; cursor: inherit;
}
.faq__btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 6px; border-radius: 4px; }

/* --- с включённым скриптом --- */
.js .faq__card {
	/* 2000px — запасное значение до того, как JS намерил высоты: слой заливки при нём уезжает далеко вниз, а не заливает карточку */
	--faq-slot: var(--faq-h, 2000px);                      /* сколько места открытая карточка занимает в стопке */
	--faq-ext: var(--faq-radius);                          /* насколько заходит под следующую плашку: это вогнутые углы */
	--faq-target: calc(var(--faq-slot) + var(--faq-ext));  /* её полная высота */
	height: var(--faq-peek); cursor: pointer;
	/* Заливка: акцентовый слой высотой в конечный размер карточки. Закрытая — слой лежит ниже карточки, открытая — на ней;
	   «уровень» (верхняя кромка слоя) поднимается снизу вверх с любой стороны наведения. Высота слоя — конечная, а не
	   текущая, поэтому кромка идёт вверх строго монотонно и не сползает вниз вместе с растущим низом карточки. */
	background-image: linear-gradient(var(--accent), var(--accent));
	background-repeat: no-repeat;
	background-origin: border-box; background-clip: border-box;
	background-size: 100% var(--faq-target);
	background-position: 0 var(--faq-target);
	transition: height var(--faq-dur) var(--faq-ease), margin-bottom var(--faq-dur) var(--faq-ease),
		border-radius var(--faq-dur) var(--faq-ease), border-color 240ms var(--ease-out),
		background-position var(--faq-dur) var(--faq-ease);
}
.js .faq__slot:last-child .faq__card { --faq-ext: 0px; }  /* под последней плашки нет */
/* открытая: высота + заход под следующую плашку; margin-bottom возвращает его обратно, так что в раскладке
   карточка занимает ровно --faq-slot, а высота и отступ меняются одной кривой и в сумме дают плавный рост слота */
.js .faq__card.is-open { height: var(--faq-target); margin-bottom: calc(var(--faq-ext) * -1); border-color: var(--accent); background-position: 0 0; }
/* у последней плашки под ней нет, углы скругляются наружу — как верхние */
.js .faq__slot:last-child .faq__card.is-open { border-radius: var(--faq-radius); }
/* цифра белеет, когда «уровень» заливки до неё дошёл (он проходит её примерно на 280–350-й мс), а не раньше;
   при закрытии возвращается к акценту, едва заливка стекла ниже */
.js .faq__card .faq__no { transition: color 180ms var(--ease-out) 60ms; }
.js .faq__card.is-open .faq__no { color: var(--white); transition: color 200ms var(--ease-out) 250ms; }
.js .faq__card .brace { color: inherit; }                  /* скобки в тексте — того же цвета, что и текст */

/* вопрос и ответ: в закрытой карточке не видны; ответ ещё и убран из дерева доступности, а кнопка вопроса остаётся в фокусе.
   Открываясь, они поднимаются на место снизу — в ту же сторону, что и заливка. Текст белый: виден только на оранжевом,
   а без скрипта этих правил нет, и там он остаётся чернильным на бумаге */
.js .faq__q, .js .faq__a { color: var(--white); opacity: 0; transform: translateY(16px); transition: opacity 160ms ease, transform 160ms ease; }
.js .faq__a { visibility: hidden; transition: opacity 160ms ease, transform 160ms ease, visibility 0s linear 160ms; }
.js .faq__card.is-open .faq__q { opacity: 1; transform: none; transition: opacity 400ms var(--ease-out) 200ms, transform 540ms var(--faq-ease) 200ms; }
.js .faq__card.is-open .faq__a { opacity: 1; transform: none; visibility: visible; transition: opacity 400ms var(--ease-out) 260ms, transform 540ms var(--faq-ease) 260ms, visibility 0s; }

/* мышь: раскрываемся до высоты самой высокой карточки, а место под неё держим в стопке заранее — страница
   ниже не прыгает. Запас — не меньше обычного нижнего отступа секции, поэтому в покое ритм секций тот же. */
@media (hover: hover) and (pointer: fine) {
	.js .faq { padding-bottom: 0; }
	.js .faq__stack { min-height: calc(var(--faq-n, 0) * var(--faq-peek) + max(var(--faq-open, 0px) - var(--faq-peek) + var(--faq-gap), var(--section-y))); }
	.js .faq__card { --faq-slot: var(--faq-open, var(--faq-h, 2000px)); }
}

@media (max-width: 860px) {
	.faq__inner { row-gap: 20px; }                          /* боксы подрезаны по верху заглавных — зазор считаем от глифов, поэтому больше прежних 12 */
	.faq__no, .faq__q, .faq__a { grid-column: 1 / -1; }
	.faq__a { max-width: none; }
}

@media (prefers-reduced-motion: reduce) {
	.js .faq__card { transition: border-color 200ms ease; }    /* заливка, высота и скругление переключаются сразу */
	.js .faq__card.is-open .faq__no { transition-delay: 0ms; }
	.js .faq__q, .js .faq__a { transform: none; }
	.js .faq__card.is-open .faq__q, .js .faq__card.is-open .faq__a { transition-property: opacity; transition-duration: 200ms; transition-delay: 0ms; }
}

/* ===========================================================
   Заявка (тёмный блок)
   =========================================================== */
.order { padding-bottom: clamp(72px, 9vw, 140px); }
.order__grid { align-items: start; row-gap: clamp(40px, 6vw, 72px); }
.order__intro { grid-column: 1 / span 6; }
.order__meta { margin-bottom: clamp(28px, 4vw, 56px); }
.order__title { font-size: clamp(2.75rem, 1rem + 6.6vw, 8rem); font-weight: 200; letter-spacing: -.05em; line-height: .98; }
.order__sub { margin-top: clamp(24px, 3vw, 40px); max-width: 26ch; font-size: var(--fs-lead); line-height: 1.5; color: var(--on-night-2); }
.order__form { grid-column: 8 / -1; padding-top: clamp(0px, 1vw, 12px); }
@media (max-width: 960px) { .order__intro, .order__form { grid-column: 1 / -1; } }

/* prefers-reduced-motion: бесконечные и «декоративные» анимации отключаем совсем */
@media (prefers-reduced-motion: reduce) {
	.is-ready .cursor { animation: none; }
	.js .hero__lines i { transition: none; transform: none; }
	.js .list-lines > li::before { transform: none; transition: none; }
	.service::after { transition: none; }
}
