:root{
  /* Светлая база. Серые были заметно глуше; подняли светлоту, тон (тёплый
     беж) сохранили, чтобы не поехали остальные страницы. */
  --bg:#f8f7f5; --ink:#15171b; --ink-2:#3a3f45; --ink-3:#5a5f66;
  --line:#e6e3dd; --surface:#efeeea; --chip:#ededeb;
  --lime:#cdf25e; --lime-ink:#111a0a;
  /* Карточка шота была серой (#e6e6e5) и почти сливалась с фоном страницы.
     По рефу она белая: чистая плашка на тёплом беже, контраст даёт сама
     белизна, а не отдельный серый тон. Переменную не переименовывала,
     чтобы не трогать .shot. */
  --card-light:#ffffff;
}
*{box-sizing:border-box}
html{-webkit-font-smoothing:antialiased; scroll-behavior:smooth}
body{margin:0; background:var(--bg); color:var(--ink); font-family:"Onest",system-ui,sans-serif}
a{color:inherit; text-decoration-thickness:1px; text-underline-offset:4px}
::selection{background:var(--lime)}
img{max-width:100%}

/* ---------- фон главной ----------
   Только на главной (body.home); остальные страницы берут сплошной --bg.
   Три слоя:

     1. фон самого body  — светло-серая макушка, уходящая в --bg;
     2. .home::before    — основная масса, жёстко прибитая к правому
                           верхнему углу: цвет максимально плотный ровно
                           в углу и тянется диагональю вниз-влево;
     3. .home::after     — подвижные пятна поверх неё, в трёх оттенках.

   Слои 2 и 3 лежат в псевдоэлементах с z-index:-1 — они вне потока и ничего
   не перекрывают, поэтому разметку трогать не пришлось. Серый вынесен в фон
   body, чтобы оба псевдоэлемента остались под цветные пятна.

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

   Что НЕ анимируем: transform. Сдвиг или поворот абсолютного слоя вынесет
   его за вьюпорт и даст горизонтальную прокрутку, а overflow-x:hidden на
   body сломает position:sticky у шапки. Двигаем background-position и
   background-size: фон всегда рисуется внутри бокса элемента.

   Периоды 9 и 13 секунд не кратны, поэтому цикл на глаз не повторяется. */
.home{position:relative;
  background:
    linear-gradient(180deg,#eff0ed 0%,#f5f5f2 46%,var(--bg) 100%) no-repeat top left /
      100% clamp(260px,39vh,490px),
    var(--bg);
}

/* Контейнер фона. На нём же лежит неподвижная масса в углу: центр каждого
   градиента вынесен точно в 100% 0% — это и даёт «исходит из угла», а не
   «светится рядом с углом». Радиусы больше 100%, поэтому цвет уходит далеко
   по диагонали вниз-влево.
   Под зелёным два подслоя: тёплый жёлто-песочный и холодный мятный. По
   отдельности они не читаются, но дают зелёному глубину и перламутр на
   стыках — без них пятно плоское.
   overflow:hidden здесь принципиален: он обрезает подвижные пятна по краю
   окна, поэтому их можно двигать трансформацией, не боясь горизонтальной
   прокрутки. На body такой трюк не прошёл бы — overflow сломал бы
   position:sticky у шапки.
   Но ровно из-за него по нижней кромке шла видимая линия: пятна обрывались
   на границе контейнера. Поэтому слой дополнительно гасится маской —
   непрозрачность плавно уходит в ноль к низу, и фон растворяется, а не
   обрезается. Обрезка при этом никуда не делась, просто её край больше
   ничего не красит. По правому краю такой линии нет: там граница контейнера
   совпадает с краем окна. */
.home .bg{position:absolute; left:0; right:0; top:0;
  height:clamp(270px,41vh,510px); overflow:hidden; z-index:-1; pointer-events:none;
  -webkit-mask-image:linear-gradient(180deg, #000 0%, #000 52%, rgba(0,0,0,.5) 78%, transparent 100%);
          mask-image:linear-gradient(180deg, #000 0%, #000 52%, rgba(0,0,0,.5) 78%, transparent 100%);
  background:
    radial-gradient(64% 116% at 100% 0%, rgba(205,242,94,.34) 0%, rgba(205,242,94,.16) 26%, rgba(205,242,94,0) 64%),
    radial-gradient(52% 92% at 100% 0%, rgba(243,232,122,.42) 0%, rgba(243,232,122,.16) 30%, rgba(243,232,122,0) 66%),
    radial-gradient(43% 78% at 100% 4%, rgba(146,220,190,.34) 0%, rgba(146,220,190,0) 62%);
}

/* Подвижные пятна. Каждое — отдельный элемент, который едет через
   transform. Так браузер переносит его на свой композитный слой и двигает
   на видеокарте, не перерисовывая градиент каждый кадр.
   Предыдущая версия анимировала background-size/position на слоях во весь
   экран — это заставляет CPU заново растрировать все градиенты 60 раз в
   секунду, и вкладка вставала колом. Записываю, чтобы не повторить.
   closest-side даёт мягкое затухание ровно до края элемента. */
.home .bg i{position:absolute; display:block; border-radius:50%;
  will-change:transform, opacity;}

.home .bg i:nth-child(1){width:36vw; height:29vw; right:-8vw; top:-11vw;
  background:radial-gradient(closest-side, rgba(205,242,94,.55), rgba(205,242,94,0));
  animation:bgA 13s ease-in-out infinite alternate}
.home .bg i:nth-child(2){width:28vw; height:23vw; right:2vw; top:-7vw;
  background:radial-gradient(closest-side, rgba(243,226,112,.44), rgba(243,226,112,0));
  animation:bgB 17s ease-in-out infinite alternate}
.home .bg i:nth-child(3){width:24vw; height:21vw; right:-3vw; top:3vw;
  background:radial-gradient(closest-side, rgba(138,218,188,.40), rgba(138,218,188,0));
  animation:bgC 11s ease-in-out infinite alternate}
.home .bg i:nth-child(4){width:20vw; height:18vw; right:9vw; top:-3vw;
  background:radial-gradient(closest-side, rgba(176,228,120,.42), rgba(176,228,120,0));
  animation:bgD 19s ease-in-out infinite alternate}

/* Пятое пятно сидит ровно в углу и никуда не едет — только дышит размером.
   Без него масса в углу была бы мёртвой: двигались бы одни края, а именно
   на это и была жалоба. Центр смещён за пределы окна, поэтому при любом
   масштабе пятно остаётся углом, а не превращается в кружок посреди экрана. */
.home .bg i:nth-child(5){width:46vw; height:38vw; right:-15vw; top:-17vw;
  background:radial-gradient(closest-side, rgba(205,242,94,.52) 0%, rgba(205,242,94,.3) 44%, rgba(205,242,94,0) 100%);
  animation:bgE 9s ease-in-out infinite alternate}

/* Размах пропорционален размеру пятен: когда уменьшили сами пятна вдвое,
   траектории уменьшили тем же множителем. Иначе пятно уезжало бы далеко за
   пределы своей зоны и в углу оставалась бы дыра.
   Периоды 13/17/11/19 с взаимно не кратны, поэтому рисунок не повторяется. */
@keyframes bgA{
  0%  {transform:translate3d(0,0,0) scale(1);          opacity:.75}
  50% {transform:translate3d(-19vw,3vw,0) scale(1.3);  opacity:1}
  100%{transform:translate3d(-6vw,7vw,0) scale(.85);   opacity:.6}
}
@keyframes bgB{
  0%  {transform:translate3d(0,0,0) scale(.9);         opacity:.6}
  50% {transform:translate3d(-15vw,6vw,0) scale(1.35); opacity:.95}
  100%{transform:translate3d(-24vw,1vw,0) scale(1.05); opacity:.7}
}
@keyframes bgC{
  0%  {transform:translate3d(0,0,0) scale(1.15);       opacity:.5}
  50% {transform:translate3d(-12vw,-4vw,0) scale(.8);  opacity:.9}
  100%{transform:translate3d(-22vw,3vw,0) scale(1.25); opacity:.62}
}
@keyframes bgD{
  0%  {transform:translate3d(0,0,0) scale(1);          opacity:.7}
  50% {transform:translate3d(8vw,5vw,0) scale(1.4);    opacity:1}
  100%{transform:translate3d(-13vw,-2vw,0) scale(.9);  opacity:.55}
}
@keyframes bgE{
  0%  {transform:scale(1);    opacity:.85}
  100%{transform:scale(1.22); opacity:1}
}

@media (prefers-reduced-motion:reduce){
  .home .bg i{animation:none; opacity:.7}
}
/* На узком экране «правый верхний угол» физически совпадает с колонкой
   текста: свечение садится прямо под заголовок и съедает контраст.
   Гасим весь слой целиком, траектории те же. */
@media (max-width:700px){
  .home .bg{opacity:.62}
}

/* Подложка шапки во всю ширину окна.
   Сама шапка лежит в колонке .inner, поэтому обычный фон давал бы на
   градиенте бледный прямоугольник по ширине колонки. Растягиваем заливку
   через box-shadow с большим spread: он рисуется позади фона, не влияет
   на поток и, в отличие от width:100vw, не добавляет горизонтальной
   прокрутки. clip-path обрезает тень сверху и снизу, оставляя ровную
   полосу высотой со шапку.
   Фон элемента обязателен и ровно того же цвета: внешняя тень по спецификации
   не рисуется под border-box элемента, так что без собственного фона в
   колонке осталась бы дыра — под шапку заезжали бы тёмные карточки кейсов.
   Одинаковая альфа у фона и тени даёт ровную полосу без шва.
   backdrop-filter снят: он размывает только бокс элемента, то есть опять
   по ширине колонки, и проявлял бы её границу.

   Альфа подложки умножена на (1 - --dock): пока шапка широкая, полоса нужна —
   она лежит поверх градиента и держит навигацию читаемой. Как только шапка
   ужимается в плавающую панель, полоса во всю ширину окна противоречила бы
   самой идее «отдельной панели», поэтому к концу трансформации гаснет
   полностью. К этому моменту верхний градиент уже уехал вверх за край окна,
   так что читаемость не страдает — под панелью ровный --bg.
   clip-path оставлен inset(0 …): он режет только по горизонтали за пределами
   бокса, скруглённые углы внутри высоты шапки не обрезаются. Собственной
   падающей тени здесь нет именно поэтому — её бы срезало по нижней кромке.

   Обводки у собранной панели больше нет: остался только фон. Поэтому в
   box-shadow единственный слой — растянутая подложка. */
.home header{background:rgba(248,247,245,.94); backdrop-filter:none;
  box-shadow:0 0 0 100vmax rgba(248,247,245, calc(.94 * (1 - var(--dock))));
  clip-path:inset(0 -100vmax)}

.mono{font-family:"Martian Mono",ui-monospace,monospace; font-size:12px; letter-spacing:.04em; text-transform:uppercase; color:var(--ink-3)}
.mono-s{font-family:"Martian Mono",ui-monospace,monospace; font-size:11px; letter-spacing:.04em; text-transform:uppercase; color:var(--ink-3)}
.wrap{padding:0 clamp(20px,5vw,72px)}
/* Колонка — две трети экрана, но не шире 1080. max(…,640px) не даёт правилу
   «две трети» душить контент на планшете; на телефоне ни один предел не
   срабатывает — там ширину задаёт padding у .wrap. */
.inner{max-width:min(1080px, max(66.67vw, 640px)); margin:0 auto}

/* ---------- header ----------
   Шапка «докируется» при прокрутке: из широкой навигационной полосы во всю
   колонку превращается в компактную скруглённую панель по центру.

   Прогресс 0…1 лежит в переменной --dock, её на каждом кадре ставит
   assets/header-dock.js. Все величины ниже — линейные функции от неё, поэтому
   это не два состояния с переключением класса, а непрерывная трансформация,
   привязанная к позиции скролла: прокрутка назад проигрывает её в обратную
   сторону без отдельного кода.

   Про отсутствие сдвига контента. Вертикальные padding уменьшаются на 10px с
   каждой стороны и ровно столько же прибавляется в margin — сумма высоты
   margin-box постоянна, страница под шапкой не дёргается.

   Зазор от верхнего края окна задан отдельным top, хотя margin-top уже есть.
   Это не дублирование: проверено в браузере — в залипшем состоянии margin
   шапки не участвует в вычислении смещения, border-box прижимается ровно к
   top, поэтому без него панель липла бы к самой кромке окна. margin отвечает
   за высоту в потоке, top — за положение при залипании, и оба равны одной и
   той же величине, так что в начале прокрутки перехода не видно.

   Горизонтально ужимаемся через width в процентах при margin-inline:auto —
   панель остаётся строго по центру колонки на любом промежуточном кадре.
   Трансформ тут не годится: scale мял бы текст.

   Обводки у собранной панели нет — форму держат скругление, фон и blur.
   box-shadow убран целиком, а не обнулён по альфе: нулевая тень всё равно
   остаётся слоем отрисовки, а здесь она не нужна вовсе. */
header{--dock:0;
  position:sticky; top:calc(var(--dock) * 10px); z-index:30;
  display:flex; align-items:center; justify-content:space-between;
  gap:16px; flex-wrap:wrap;
  /* Сужение задано в процентах от колонки, а колонка не шире 1080px — значит
     компактная ширина сама по себе не превышает 864px. Отдельный max-width в
     1000px здесь не нужен: проверено, что на экране 1512px он срезал шапку до
     1000px уже в исходном состоянии, то есть ломал требование «во всю ширину
     контейнера» ровно там, где оно важнее всего. */
  width:calc(100% - 20% * var(--dock));
  margin:calc(var(--dock) * 10px) auto;
  padding:calc(18px - 10px * var(--dock)) calc(var(--dock) * 26px);
  border-radius:calc(var(--dock) * 38px);
  background:rgba(248,247,245,.88); backdrop-filter:blur(10px)}

/* Прокрутка — единственный источник прогресса, никакой самостоятельной
   анимации у шапки нет. Но «уезжающая» и меняющая форму панель — всё же
   движение, поэтому при reduce оставляем обычную статичную полосу: скрипт
   в этом режиме не трогает --dock, а правило ниже страхует, если он
   почему-то не подключился. */
@media (prefers-reduced-motion:reduce){
  header{--dock:0 !important}
}

/* Телефон. Здесь колонка равна экрану минус padding у .wrap, а содержимое
   шапки уже переносится на вторую строку. Измерено в браузере на 375px:
   nav.top требует 313.5px при доступных 335.3px, то есть запаса всего ~21px.
   Десктопная схема (минус 20% ширины плюс 26px внутренних отступов с каждой
   стороны) отбирает больше сорока — навигация уезжала на третью строку,
   высота шапки прыгала с 101px до 121px и весь контент под ней съезжал.

   Поэтому на узком экране компактность набирается не сужением, а наоборот:
   панель расширяется к краям экрана ровно на столько, сколько забирает себе
   внутренний отступ. width растёт на 16px, margin по бокам уходит в минус на
   8px (сумма с containing block сходится, панель остаётся по центру), padding
   прибавляет те же 8px. Ширина содержимого в итоге не меняется ни на пиксель
   — перенос строки арифметически невозможен, а не «вроде влез».

   Побочно это и есть «уменьшить боковые отступы» из постановки: зазор от
   кромки экрана сокращается с 20px до 12px, панель при этом за экран не
   выходит. Радиус 24px безопасен: внутренний отступ 8px больше, чем закругление
   съедает на высоте логотипа (~6px), так что логотип не наезжает на скругление. */
@media (max-width:720px){
  header{
    width:calc(100% + 16px * var(--dock));
    margin:calc(var(--dock) * 10px) calc(var(--dock) * -8px);
    padding:calc(18px - 10px * var(--dock)) calc(var(--dock) * 8px);
    border-radius:calc(var(--dock) * 24px);
  }
}
.identity{display:flex; align-items:center; gap:10px}

/* «Москва» уходит в начале прокрутки: в компактной панели это самый
   необязательный элемент, и без него логотип и контакты читаются спокойнее.
   Множитель 2.5 нужен, чтобы надпись исчезла раньше самой трансформации —
   opacity сама обрезает значения ниже нуля, так что к --dock ≈ 0.4 (это
   примерно 60px прокрутки) её уже нет, а панель ещё только начала собираться.

   Гаснет именно opacity, место при этом сохраняется. Это осознанно: логотип
   прижат влево, навигация вправо, так что пустое место слева не видно — зато
   доступная ширина содержимого не меняется. Если бы надпись сворачивалась по
   ширине, на узком экране навигация в какой-то момент уместилась бы с
   логотипом в одну строку, высота шапки скакнула бы, и страница под ней
   дёрнулась. Проверено, что при текущем варианте число строк постоянно. */
.identity .mono{opacity:calc(1 - var(--dock) * 2.5)}
/* В собранной панели монограмма «АБ» сменяется именем целиком.

   Оба варианта лежат в одной ячейке грида (grid-area:1/1), поэтому не
   двигают друг друга и кроссфейд идёт на месте, без скачка базовой линии.

   Ширина плашки едет от 28px (квадрат с монограммой) до --logo-w — ширины
   надписи, которую один раз замеряет assets/header-dock.js и кладёт в ту же
   переменную инлайном. Значение по умолчанию здесь тоже 28px, и это важно:
   пока скрипт не отработал, и на страницах со старой разметкой
   (<a class="logo">АБ</a> без вложенных span) логотип остаётся прежним
   квадратом, а не растягивается в пустую плашку.

   Множители 2.2 и смещение 1.2 разводят два текста по времени: «АБ» гаснет
   к --dock ≈ 0.45, имя начинает проявляться с ≈ 0.55. Между ними короткий
   зазор, где плашка пустая. Это намеренно: при любом перекрытии в середине
   видно, как сквозь «АБ» просвечивает «Анастасия».

   overflow:hidden нужен, пока плашка уже, чем надпись, иначе длинное имя
   вылезало бы за скруглённый край; white-space:nowrap — чтобы оно не
   переносилось и измерение оставалось верным. */
.logo{--logo-w:28px;
  display:inline-grid; align-items:center; justify-items:center;
  width:calc(28px + (var(--logo-w) - 28px) * var(--dock)); height:28px;
  border-radius:calc(8px + 6px * var(--dock));
  background:var(--ink); color:var(--bg); font-weight:700; font-size:13px;
  text-decoration:none; overflow:hidden; white-space:nowrap}
.logo > span{grid-area:1/1}
.logo .logo-ab{opacity:calc(1 - var(--dock) * 2.2)}
.logo .logo-full{opacity:calc(var(--dock) * 2.2 - 1.2)}
nav.top{display:flex; align-items:center; gap:14px; font-size:15px; font-weight:500}
nav.top a{text-decoration:none; border-bottom:1px solid transparent}
nav.top a:hover{border-bottom-color:var(--ink)}
nav.top .sep{color:#c6c3bc}

/* ---------- hero ---------- */
.hero{display:flex; flex-direction:column; align-items:center; text-align:center;
  gap:clamp(20px,2.4vw,34px); padding:clamp(34px,5vw,70px) 0 clamp(40px,5vw,78px)}
/* height:auto обязателен: без него атрибут height="800" из разметки задаёт
   высоту блока, aspect-ratio отключается, и object-fit:contain оставляет
   по 300px пустоты сверху и снизу от фото */
/* Кадр теперь квадратный: в avatar-circle.webp круг вписан в рамку впритык,
   прозрачность по углам уже в самом файле. Прежний 205/200 оставил бы по
   бокам пустые поля и круг бы «болтался». */
.hero .ava{width:clamp(140px,14vw,205px); height:auto; aspect-ratio:1/1; object-fit:contain}
.hero h1{margin:0; font-size:clamp(34px,3.46vw,62px); font-weight:700; letter-spacing:-.03em; line-height:1.02}
.tagline{display:flex; align-items:center; justify-content:center; gap:8px; flex-wrap:wrap; margin-top:14px}
.tagline .role{color:#5a5f66; font-size:clamp(16px,1.06vw,19px); line-height:1.55}
/* lime-бейджи: скругление 4–5, не пилюля, начертание Regular */
.badge{display:inline-flex; align-items:center; padding:1px 8px; border-radius:4px;
  background:var(--lime); color:var(--lime-ink); font-size:clamp(16px,1.06vw,19px); font-weight:400}
.badge.note{padding:3px 10px; border-radius:5px; font-size:clamp(15px,.95vw,17px)}
.statement{margin:0; max-width:820px; font-size:clamp(24px,2.57vw,46px); font-weight:600;
  letter-spacing:-.03em; line-height:1.12}
.statement .hi{display:block; margin-bottom:.55em}
/* Удержание первого экрана до запуска вступительной анимации.
   Ставит класс и снимает его assets/hero-intro.js — правило живёт ровно
   столько, сколько проходит от первой отрисовки до первого кадра
   анимации (ожидание document.fonts, не дольше 350мс). Скрыта только
   прозрачность: раскладка не меняется, поэтому ни измерения, ни
   положение соседних блоков это правило не искажает. Без JS и при
   prefers-reduced-motion класс не появляется вовсе, и первый экран
   отображается обычным образом. */
.intro-hold .hero{opacity:0}

.chips{display:flex; flex-wrap:wrap; justify-content:center; gap:10px}
/* По рефу чип — не серая заливка, а белая пилюля с тонкой обводкой и тёмным
   текстом. Серый #ededeb почти совпадал с фоном #f8f7f5, и чипы читались как
   пятна; белое поле + линия дают чёткую форму, а текст можно сделать темнее.
   border учтён в box-sizing (*{box-sizing:border-box} в начале файла), поэтому
   высота чипа не выросла. */
.chip{padding:9px 20px; border-radius:999px; background:#fff; color:var(--ink-2);
  border:1px solid var(--line);
  font-size:clamp(15px,.95vw,17px); font-weight:400; line-height:1.2}

/* ---------- section heads ---------- */
.sec-head{display:flex; align-items:baseline; justify-content:space-between; gap:20px;
  flex-wrap:wrap; padding-bottom:clamp(16px,2vw,28px)}
/* H2 «Избранные кейсы» = 34 SemiBold; H2 «Другие работы, шоты» = 46 Medium */
.sec-head h2{margin:0; font-size:clamp(24px,1.9vw,34px); font-weight:600; letter-spacing:-.03em}
.sec-head.big h2{font-size:clamp(28px,2.57vw,46px); font-weight:500; letter-spacing:-.035em}
section{padding:clamp(28px,4vw,60px) 0}

/* ---------- featured case cards ---------- */
.cases{display:flex; flex-direction:column; gap:clamp(20px,2.4vw,32px)}
/* container-type, а не vw: колонка сузилась до 1080, и привязанный к окну
   кегль перестал попадать в карточку — заголовок уезжал в четыре строки,
   а Stats (450px) не влезал в текстовую колонку (428px). Теперь размеры
   считаются от самой карточки, как у шотов. */
.case{container-type:inline-size; position:relative;
  display:grid; grid-template-columns:1fr 1fr; border-radius:clamp(20px,2.2vw,34px);
  overflow:hidden; color:#f4f3f0; text-decoration:none}
.case.c-crypto{background:#15171b}
.case.c-crypto .media{background:#0f1113}
.case.c-wallet{background:#1f2a3a}
.case.c-wallet .media{background:#18212e}
.case.c-exmo{background:#1b2130}
.case.c-exmo .media{background:#141927}
/* колонка контента в фигме 840 с padding 56 и gap 22 */
.case .content{display:flex; flex-direction:column; gap:22px; padding:clamp(24px,4.4cqw,56px); min-width:0}
.case .tags{display:flex; flex-wrap:wrap; gap:8px}
/* теги на тёмных карточках — полупрозрачный белый, не заливка */
.case .tags span{padding:6px 14px; border-radius:999px; background:rgba(255,255,255,.09); color:#f4f3f0;
  font-size:14px; font-weight:400}
.case.c-wallet .tags span{color:#eef1f5}
.case h3{margin:0; font-size:clamp(28px,4.07cqw,58px); font-weight:600; letter-spacing:-.035em; line-height:1.04}
.case p{margin:0; max-width:46ch; font-size:clamp(16px,1.76cqw,19px); line-height:1.45; color:#b0b4b8}
.case.c-wallet p{color:#a6b0bf}
.case .grow{flex:1}
/* Stats — тоже полупрозрачный белый, текст светлый (как в фигме) */
.stats{display:inline-flex; align-self:flex-start; border-radius:18px;
  background:rgba(255,255,255,.09); color:#f4f3f0; overflow:hidden}
.case.c-wallet .stats{color:#eef1f5}
/* ячейка хагает по контенту (в фигме 180 и 268 при padding 16/28) — поэтому nowrap:
   подписи короткие («Экранов», «Роль», «Флоу»), переносов быть не должно */
.stats > div{padding:16px clamp(16px,2.6cqw,28px); display:flex; flex-direction:column; gap:4px;
  white-space:nowrap; min-width:clamp(120px,14cqw,180px)}
.stats > div + div{border-left:1px solid rgba(255,255,255,.14)}
.stats .k{font-size:clamp(13px,.78vw,14px); font-weight:400; line-height:1.4; color:#b0b4b8}
.case.c-wallet .stats .k{color:#a6b0bf}
.stats .v{font-size:clamp(24px,2.96cqw,40px); font-weight:600; letter-spacing:-.02em; line-height:1.1}
/* CTA в фигме — фиксированные 520 с контентом по центру, не хаг */
.cta{display:flex; align-self:flex-start; align-items:center; justify-content:center; gap:12px;
  width:min(520px,100%); padding:20px 32px; border-radius:16px; background:var(--lime);
  color:var(--lime-ink); font-size:clamp(17px,1.85cqw,24px); font-weight:600; text-decoration:none}
.cta::before{content:""; width:9px; height:9px; border-radius:50%; background:var(--lime-ink)}
.case:hover .cta.stretch{filter:brightness(1.05)}

/* Рядом с «Смотреть кейс» встала вторая, внешняя ссылка — на живой сайт.
   Вложенные <a> невалидны, поэтому карточка с двумя ссылками перестала быть
   ссылкой целиком: теперь это div, а кейс открывает растянутый на всю
   карточку псевдоэлемент основной кнопки. */
.cta-row{display:flex; align-self:flex-start; align-items:stretch; flex-wrap:wrap; gap:12px; max-width:100%}
/* боковые поля ужаты с 32 до 20: пара кнопок «Смотреть кейс» + «Открыть сайт»
   уезжала в две строки — на 32px она требовала 461px, на 24px — 399px, а в
   текстовой колонке при окне ~1425px доступно только 396 */
.cta-row .cta{width:auto; align-self:auto; padding-left:20px; padding-right:20px; white-space:nowrap}
/* z-index нужен: .media позиционирована и идёт следом по DOM, без слоя
   она бы легла поверх растяжки и половина карточки не кликалась бы */
.cta.stretch::after{content:""; position:absolute; inset:0; z-index:1}
/* внешняя ссылка поверх растяжки, иначе клик по ней ушёл бы в кейс */
.cta.ghost{position:relative; z-index:2; background:none; color:#f4f3f0;
  box-shadow:inset 0 0 0 1px rgba(255,255,255,.26)}
.cta.ghost::before{background:var(--lime)}
.cta.ghost:hover{background:rgba(255,255,255,.07)}
.case .media{position:relative; display:grid; place-items:center; padding:clamp(20px,2.4vw,34px); min-height:340px}
/* height:auto — картинка тянется только по ширине, пропорции не ломаются */
.case .media img{border-radius:clamp(14px,1.8vw,32px); display:block; height:auto}
.case.c-crypto .media img{width:min(775px,100%)}
.case.c-wallet .media img{width:min(286px,72%)}
/* обложка exmo почти квадратная (2454×2094) — по полной ширине колонки
   она перебивала бы текстовую половину по высоте */
.case.c-exmo .media img{width:min(560px,100%)}
/* видео в кейсе живёт по тем же правилам, что и картинка; квадратная запись
   занимает колонку целиком — в отличие от прежнего скриншота телефона,
   который был узкой вертикалью на 286px */
.case .media video{border-radius:clamp(14px,1.8vw,32px); display:block; width:100%; height:auto}
.case .badge-co{position:absolute; left:clamp(20px,2.4vw,34px); top:clamp(20px,2.4vw,34px);
  padding:6px 12px; border-radius:999px; background:rgba(255,255,255,.09); color:#f4f3f0;
  font-family:"Martian Mono",ui-monospace,monospace; font-size:12px; letter-spacing:.04em}
.case.c-wallet .badge-co{color:#eef1f5}

/* ---------- lead paragraph ---------- */
.lead{margin:0; max-width:1400px; font-size:clamp(24px,2.57vw,46px); font-weight:500;
  letter-spacing:-.035em; line-height:1.2; color:var(--ink)}
mark{background:var(--lime); color:var(--lime-ink); padding:0 .18em; border-radius:4px}

/* ---------- shots grid ---------- */
/* Одна колонка: карточка во всю ширину контента (до 1568px).
   grid-auto-rows:auto — каждая карточка своей высоты, иначе 1fr растягивал
   бы все строки по самой высокой и в коротких зияла бы пустота. */
.grid{display:grid; grid-template-columns:1fr; grid-auto-rows:auto; gap:clamp(24px,3.2vw,56px)}
/* Плашка есть, но работа лежит в ней встык: поля только у текста, снизу.
   Поэтому padding живёт не на .body, а на текстовых детях, а overflow:hidden
   сам подрезает верхние углы картинки по радиусу карточки — своё скругление
   медиа не нужно, иначе у верхней кромки вылезли бы серые уголки. */
/* Белая карточка на фоне #f8f7f5 отличается от него всего на пару процентов
   светлоты — одной заливки мало, край карточки пропадал бы. На рефе плашка
   читается за счёт мягкой тени, поэтому тень, а не обводка: обводка дала бы
   жёсткий контур поверх скриншота, а тень оставляет край мягким.
   Два слоя: короткий плотный для самой кромки и длинный размытый для объёма. */
.shot{container-type:inline-size; display:flex; border-radius:min(4.22cqw,34px);
  overflow:hidden; background:var(--card-light); color:var(--ink); text-decoration:none;
  box-shadow:0 1px 2px rgba(21,23,27,.05), 0 12px 32px -12px rgba(21,23,27,.10)}
.shot .body{flex:1; display:flex; flex-direction:column;
  gap:clamp(12px,2.73cqw,24px); padding:0 0 clamp(20px,6.95cqw,56px); min-width:0}
/* картинка не растягивается и не кропится: ширина колонки, высота — натуральная */
/* видео живёт по тем же правилам, что и картинка: без рамок, фона и отступов */
.shot .body > img,
.shot .body > video{display:block; width:100%; height:auto; border-radius:0;
  border:0; outline:0; margin:0; padding:0; background:none}
/* боковые поля — только текстовому блоку */
.shot .body > .tags,
.shot .body > h3,
.shot .body > p{padding-left:clamp(20px,6.95cqw,56px); padding-right:clamp(20px,6.95cqw,56px)}
/* Ссылка на живой проект: подчёркнутая строка, а не кнопка — шот сам по себе
   не кликабелен, и кнопка обещала бы переход в кейс, которого нет.
   Отступ слева — margin, а не padding: иначе lime-подсветка на ховере
   растянулась бы от самого края карточки. */
.site-link{align-self:flex-start; display:inline-flex; align-items:center; gap:8px;
  margin-left:clamp(20px,6.95cqw,56px); max-width:calc(100% - clamp(20px,6.95cqw,56px) * 2);
  font-size:clamp(15px,2.36cqw,20px); font-weight:500; color:var(--ink);
  text-decoration:underline; text-decoration-thickness:1px; text-underline-offset:4px}
.site-link::after{content:"↗"; font-weight:400}
.site-link:hover{background:var(--lime)}
/* gap от картинки 24px, а по бокам 56px — добираем воздух сверху, чтобы текст
   не висел вплотную под кадром */
.shot .body > .tags{padding-top:clamp(8px,1.6cqw,20px)}
.shot .tags{display:flex; flex-wrap:wrap; gap:8px}
/* теги шотов — заливка своего же цвета с малой прозрачностью, текст в цвет карточки */
/* clamp, а не max: на мобильном карточка ~340px, и чистый cqw давал теги 6px,
   описание 8px — нечитаемо; а в одну колонку карточка 1568px, и тот же cqw
   раздувал заголовок до 113px. Между порогами живут фигмовские 58/19/14 */
/* Теги на шоте — тот же приём, что у чипов первого экрана: белая пилюля с
   обводкой. Полупрозрачная заливка rgba(20,22,31,.07) на серой карточке
   читалась, но на белой карточке превратилась бы в еле заметное пятно. */
.shot .tags span{padding:6px 14px; border-radius:999px; background:#fff; color:var(--ink);
  border:1px solid var(--line);
  font-size:clamp(13px,1.74cqw,15px); font-weight:400}
/* верхняя граница 46px — ровно как у заголовка секции: подзаголовок карточки
   не должен звучать громче «Web, Analytics» */
.shot h3{margin:0; font-size:clamp(22px,7.2cqw,46px); font-weight:600; letter-spacing:-.035em; line-height:1.08}
.shot p{margin:0; font-size:clamp(15px,2.36cqw,20px); line-height:1.4; color:var(--ink-2); max-width:72ch}
.shot .grow{flex:1; min-height:0}

/* ---------- секция Mobile ---------- */
/* только видео: ни карточки, ни фона, ни подписей */
/* Записи разной пропорции: три квадрата и один вертикальный телефон (0.46).
   Поэтому не сетка, а горизонтальная лента: высота у всех одна и задана
   явно, ширину каждый кадр берёт из своей пропорции (width:auto +
   aspect-ratio). Сетка с равными колонками либо резала бы квадраты, либо
   оставляла вокруг телефона огромные поля. */
.flows-wrap{position:relative; container-type:inline-size}
.flows{display:flex; gap:clamp(20px,2.8vw,48px); overflow-x:auto; scroll-behavior:smooth;
  scroll-snap-type:x proximity; -webkit-overflow-scrolling:touch;
  scrollbar-width:none; -ms-overflow-style:none}
.flows::-webkit-scrollbar{display:none}
/* aspect-ratio прописан в CSS, а не выведен из атрибутов: при preload="none"
   браузер ещё не знает натуральных размеров, и без него width:auto схлопнулся
   бы в дефолтные 300px до загрузки метаданных */
/* Высота считается от ширины ленты (cqw), а не окна: колонка теперь уже
   окна, и привязка к vw давала квадрат в 612px на ленте в 1080 — в кадр
   влезало полтора ролика.
   Верхняя граница 86vw — у квадратной записи ширина равна высоте, и без неё
   на телефоне кадр оказывался шире самой ленты: целиком его было не увидеть
   ни при какой прокрутке. */
.flows video{flex:0 0 auto; display:block; height:min(clamp(320px,46cqw,520px),86vw); width:auto;
  aspect-ratio:1/1; object-fit:contain; scroll-snap-align:start;
  border:0; outline:0; margin:0; padding:0; background:none}
.flows video.phone{aspect-ratio:556/1214}

/* стрелки — поверх ленты по краям; прячутся у соответствующего конца (hidden
   ставит скрипт), поэтому на коротком списке их не видно вовсе */
.flows-arrow{position:absolute; top:50%; translate:0 -50%; z-index:2;
  display:flex; align-items:center; justify-content:center;
  width:52px; height:52px; padding:0; border:0; border-radius:999px;
  background:rgba(248,247,245,.92); color:var(--ink); cursor:pointer;
  box-shadow:0 2px 14px rgba(21,23,27,.16);
  transition:background .15s ease, box-shadow .15s ease}
.flows-arrow:hover{background:#fff; box-shadow:0 4px 18px rgba(21,23,27,.22)}
.flows-arrow:focus-visible{outline:2px solid var(--ink); outline-offset:3px}
.flows-arrow[hidden]{display:none}
.flows-arrow.prev{left:0; translate:-40% -50%}
.flows-arrow.next{right:0; translate:40% -50%}
/* на узком экране сдвигать стрелки наружу некуда — контент уже у края */
@media (max-width:980px){
  .flows-arrow.prev{translate:0 -50%}
  .flows-arrow.next{translate:0 -50%}
}

/* ---------- skills ---------- */
.skills{display:flex; flex-wrap:wrap; gap:10px; padding-top:clamp(24px,3vw,48px)}
/* чипы навыков — скругление 16, а не пилюля */
.skills .chip{padding:18px 20px; border-radius:16px; color:#3a3f45; font-size:clamp(14px,.89vw,16px)}

/* ---------- personal block ---------- */
.personal{display:flex; flex-direction:column; align-items:center; text-align:center;
  gap:clamp(24px,3vw,40px); max-width:1100px; margin:0 auto; padding:clamp(40px,5vw,80px) 0}
.personal .photo{width:200px; height:200px; border-radius:50%; object-fit:cover}
.personal h2{margin:0; font-size:clamp(26px,2.23vw,40px); font-weight:600; letter-spacing:-.03em}
.personal .sub{margin:10px 0 0; color:var(--ink-3); font-size:clamp(15px,.95vw,17px)}
.personal .para{margin:0; font-size:clamp(20px,1.67vw,30px); font-weight:500;
  letter-spacing:-.02em; line-height:1.3; color:var(--ink-2)}

/* ---------- footer ---------- */
footer{border-top:1px solid var(--line); padding:clamp(28px,3vw,48px) 0 clamp(40px,4vw,64px);
  display:flex; align-items:center; justify-content:space-between; gap:20px; flex-wrap:wrap}
footer .headline{font-size:clamp(20px,1.56vw,28px); font-weight:600; letter-spacing:-.025em}
footer .contacts{display:flex; flex-wrap:wrap; align-items:center; gap:10px;
  font-size:clamp(15px,.95vw,17px); font-weight:500}
footer .contacts a{text-decoration:none; border-bottom:1px solid var(--line)}
footer .contacts a:hover{background:var(--lime); border-bottom-color:transparent}
footer .sep{color:#c6c3bc}

@media (max-width:980px){
  .case{grid-template-columns:1fr}
  .case .media{order:-1; min-height:0}
  /* padding-top освобождает место под бейджем компании — иначе он ложится на
     картинку. Нужен только там, где бейдж остался */
  .case.c-crypto .media{padding-top:76px}
  /* лента флоу остаётся лентой: на мобильном её листают пальцем */
}

/* ---------- мобильный ---------- */
@media (max-width:720px){
  /* Stats: две ячейки по 180px + «End-to-end» в nowrap дают 449px и вылезают
     за карточку — на узком экране кладём их друг под друга */
  .stats{display:grid; grid-template-columns:1fr; align-self:stretch; width:100%}
  .stats > div{min-width:0; padding:14px 20px}
  .stats > div + div{border-left:0; border-top:1px solid rgba(255,255,255,.14)}
}

/* ---------- UX-разбор: камера по записи экрана ---------- */
/* aspect-ratio держит высоту до загрузки видео — CLS = 0.
   overflow:hidden обязателен: камера двигает кадр за пределы рамки. */
/* container-type — чтобы чипы и выводы мерились шириной кадра, а не карточки:
   один набор cqw-размеров работает и на десктопе, и на телефоне */
.uxc{position:relative; container-type:inline-size}
/* aspect-ratio = пропорции самой записи (1632×1080). 16/9, как было у старого
   ролика, при object-fit:cover срезал бы у нового кадра верх и низ */
.ux{position:relative; width:100%; aspect-ratio:1632/1080; overflow:hidden;
  border-radius:min(3.97cqw,32px); background:#0b0d12; isolation:isolate}
.ux__video{display:block; width:100%; height:100%; object-fit:cover;
  transform-origin:50% 50%; will-change:transform; backface-visibility:hidden}

/* Оверлеи живут вне трансформируемого видео: иначе зум размывал бы текст
   и таскал бы чипы вместе с кадром. */
.ux__layer{position:absolute; inset:0; pointer-events:none;
  font-family:"Onest",system-ui,sans-serif}
/* лёгкая подложка сверху и снизу — чипы и кнопка читаются на любом кадре */
.ux__layer::before{content:""; position:absolute; inset:0; pointer-events:none;
  background:linear-gradient(180deg,rgba(8,10,16,.30) 0%,rgba(8,10,16,0) 20%,
    rgba(8,10,16,0) 62%,rgba(8,10,16,.34) 100%)}

/* ---- чипы ---- */
.ux__chip{position:absolute; z-index:2; max-width:min(42cqw,420px);
  padding:min(0.95cqw,9px) min(1.5cqw,15px); border-radius:999px;
  background:rgba(255,255,255,.82); color:#14161f;
  font-size:clamp(11px,1.45cqw,15px); font-weight:500; line-height:1.25; letter-spacing:-.01em;
  white-space:nowrap; text-overflow:ellipsis; overflow:hidden;
  box-shadow:0 2px 10px rgba(8,10,16,.18), 0 0 0 1px rgba(255,255,255,.5) inset;
  backdrop-filter:blur(14px) saturate(1.4); -webkit-backdrop-filter:blur(14px) saturate(1.4);
  opacity:0; transform:translateY(10px); transition:opacity .5s ease, transform .5s cubic-bezier(.22,.8,.26,1)}
.ux__chip.is-on{opacity:1; transform:translateY(0)}
.ux__chip--tl{top:min(3.2cqw,26px); left:min(3.2cqw,26px)}
.ux__chip--tr{top:min(3.2cqw,26px); right:min(3.2cqw,26px)}
/* 9cqw — выше полосы прогресса и кнопки, чтобы ничего не наезжало */
.ux__chip--br{bottom:max(44px,min(9cqw,72px)); right:min(3.2cqw,26px)}

/* ---- выводы ---- */
/* Вывод в три строки — это ~50% ширины и ~20% высоты кадра. Интерфейс на
   записи плотный, и на зуме под такой блок не остаётся пустого места:
   в любом углу он закрывает то, ради чего камера туда приехала.
   Поэтому выводы живут под кадром, в карточке. Поверх записи остаются
   только чипы — они короткие и встают в свободные зоны.
   Контейнер с фиксированной высотой, выводы сложены друг на друга и
   перекрёстно гаснут — при смене текста ничего не прыгает. */
/* пиксельный пол: на средних ширинах текст переходит на третью строку
   раньше, чем 8cqw успевает вырасти */
.ux__notes{position:relative; margin-top:min(1.6cqw,14px); min-height:clamp(92px,8cqw,112px);
  pointer-events:none}
.ux__note{position:absolute; inset:0 0 auto 0; margin:0; color:var(--ink);
  opacity:0; transform:translateY(12px); transition:opacity .55s ease, transform .55s cubic-bezier(.22,.8,.26,1)}
.ux__note.is-on{opacity:1; transform:translateY(0)}
.ux__note-k{display:block; margin-bottom:min(.55cqw,6px);
  font-family:"Martian Mono",ui-monospace,monospace; font-size:clamp(9px,.92cqw,11px);
  letter-spacing:.08em; text-transform:uppercase; color:var(--ink-3)}
/* через .ux__notes — иначе общее правило .shot p перебивает размер и цвет */
.ux__notes .ux__note-t{margin:0; font-size:clamp(14px,1.72cqw,22px); font-weight:500;
  line-height:1.32; letter-spacing:-.02em; text-wrap:balance; color:var(--ink);
  max-width:min(62cqw,72ch)}

/* ---- прогресс и управление ---- */
.ux__bar{position:absolute; z-index:3; left:0; right:0; bottom:0; height:2px;
  background:rgba(244,243,240,.18)}
/* строго > i: иначе правило ловит и палочки-метки внутри .ux__marks */
.ux__bar > i{display:block; height:100%; background:var(--lime);
  transform:scaleX(0); transform-origin:0 50%}
.ux__marks{position:absolute; inset:0}
.ux__marks i{position:absolute; top:0; width:1px; height:100%; background:rgba(244,243,240,.5)}

.ux__toggle{position:absolute; z-index:4; right:min(3.2cqw,26px); bottom:min(2.6cqw,22px);
  display:inline-flex; align-items:center; gap:7px; padding:8px 14px;
  border:0; border-radius:999px; cursor:pointer;
  background:rgba(16,18,24,.52); color:#f4f3f0;
  backdrop-filter:blur(14px); -webkit-backdrop-filter:blur(14px);
  font-family:"Martian Mono",ui-monospace,monospace; font-size:10px;
  letter-spacing:.06em; text-transform:uppercase;
  opacity:0; transition:opacity .25s ease}
.uxc:hover .ux__toggle,.ux__toggle:focus-visible{opacity:1}
.ux__toggle::before{content:"Пауза"}
/* класс is-playing висит на .uxc — его ставит скрипт по событиям video */
.uxc:not(.is-playing) .ux__toggle{opacity:1}
.uxc:not(.is-playing) .ux__toggle::before{content:"Смотреть"}

@media (max-width:720px){
  /* на узкой карточке cqw-высота блока выводов уже не спасает:
     текст занимает больше строк, поэтому резерв задаём в пикселях */
  .ux__notes{min-height:100px; margin-top:12px}
  .ux__notes .ux__note-t{max-width:none}
  .ux__chip{max-width:70cqw}
  /* на телефоне кнопка «Смотреть» занимает фиксированные 30px и в cqw
     не уменьшается — нижние чипы поднимаем выше неё */
  .ux__chip--br{bottom:14cqw}
}

@media (prefers-reduced-motion: reduce){
  /* камера стоит, оверлеи появляются без сдвига, автозапуска нет */
  .ux__video{transform:none !important}
  .ux__chip,.ux__note{transform:none !important}
}

/* ---------- доступность ---------- */
:focus-visible{outline:2px solid var(--ink); outline-offset:3px; border-radius:6px}
.skip-link{position:absolute; left:-9999px; top:0; z-index:100; padding:12px 18px;
  background:var(--ink); color:var(--bg); border-radius:0 0 10px 0; text-decoration:none}
.skip-link:focus{left:0}

@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  *{animation-duration:.001ms !important; transition-duration:.001ms !important}
}
