/* =========================================================================
   гочи — лендинг в один экран
   Макет: Figma «v2a», фрейм 111:38, 1491 × 1024.
   Все размеры из макета хранятся в px-константах и умножаются на --k,
   поэтому пропорции держатся на любом вьюпорте.
   ========================================================================= */

@font-face {
  font-family: "SB Sans Interface";
  src: url("assets/fonts/SBSansUI-Semibold.woff2") format("woff2");
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  /* Заголовок модалки в макете — Bold. Без этого начертания браузер
     синтезировал жир из Semibold. */
  font-family: "SB Sans Interface";
  src: url("assets/fonts/SBSansUI-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "SB Sans Text Cond";
  src: url("assets/fonts/SBSansTextCond-Bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  overflow: hidden;                    /* один экран, скролла нет */
  background: #0d1524;
  color: #fff;
  font-family: "SB Sans Interface", -apple-system, BlinkMacSystemFont,
               "Helvetica Neue", Arial, sans-serif;
  font-weight: 600;
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
}
img, svg, video { display: block; max-width: 100%; }
[hidden] { display: none !important; }
ul { list-style: none; margin: 0; padding: 0; }
p  { margin: 0; }
a  { color: inherit; text-decoration: none; }

/* ---------- масштаб ----------
   --k — коэффициент, при котором фрейм макета целиком помещается во вьюпорт.
   Размеры масштабируются, но сами блоки прижаты к краям ЭКРАНА, а не фрейма,
   поэтому на широких мониторах они просто расходятся по углам. */
:root {
  --dw: 1491;
  --dh: 1024;
  --k: min(calc(100vw / var(--dw)), calc(100vh / var(--dh)));

  /* В макете отступ 70 со всех сторон. Боковые расширены до 120 по правке. */
  --pad-x: calc(120 * var(--k));
  --pad-y: calc(70 * var(--k));

  /* Кадрирование как в макете. Картинка из Figma (1024×483) оказалась кадром
     этого видео, обрезанным по бокам: crop=3998×1886 от x=240, т.е. она
     занимает долю [0.0546 … 0.9636] ширины кадра. Пересчёт того же окна
     на полный кадр (4398×1886) даёт 66.8% вместо прежних 69%. */
  --media-x: 66.8%;
  --media-y: 50%;

  /* Ширина градиента резиновая — доля ширины экрана, а не масштаб макета.
     В макете слой 544px при кадре 1491px, это и есть 36.5vw. */
  --scrim-w: 36.5vw;
}

/* ---------- сцена ---------- */
.stage {
  position: fixed;
  inset: 0;
  height: 100dvh;
  overflow: hidden;
}

/* ---------- фон: fill ---------- */
.media {
  position: absolute;
  inset: 0;
  background: #0d1524;
}
.media__el {
  /* Постер и видео лежат друг на друге, поэтому оба выведены из потока.
     Раньше здесь хватало width/height: 100%, но с двумя элементами они
     встали бы столбиком. */
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--media-x) var(--media-y);
}
/* Постер — подложка под видео и только: ни кликов, ни перетаскивания. */
.media__poster {
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}
/* Видео поверх постера. Оно вдвое резче и с зерном, поэтому подмена кадра
   заметна как щелчок — проявление за четверть секунды растворяет стык.
   Прятать видео можно только когда есть кому его показать: класс .is-fade
   ставит скрипт, так что без JS всё остаётся видимым сразу. */
.media--fade .media__video {
  opacity: 0;
  transition: opacity .25s ease-out;
}
.media--fade .media__video.is-ready { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .media--fade .media__video { transition: none; }
}
/* На десктопе видео — фон на весь экран, растворять его нечем и незачем. */
.media__cut { display: none; }
/* В макете вуали нет — картинка слева и так тёмная. Слой оставлен выключенным:
   когда фон станет видео, включи его, если кадры окажутся светлее. */
.media::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: none;
}

/* ---------- градиент под левым блоком ----------
   Frame 166 из макета: 544×1024 от левого края, прижат к левому, верхнему и
   нижнему краям, ширина тянется за вьюпортом. Цвет затемнения — общий с
   мобильными вуалями (.stage, .media__cut--top): #060A14. В макете тут стоял
   более светлый #16212C, из-за него левый край отсвечивал синим. */
.scrim {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: var(--scrim-w);
  background: linear-gradient(89.842deg, #060A14 0.27%, rgba(6, 10, 20, 0) 99.76%);
  pointer-events: none;
}

/* ---------- сетка: три угла, всё гарантированно в экране ---------- */
.ui {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: auto 1fr auto;
  gap: calc(24 * var(--k));
  padding: var(--pad-y) var(--pad-x);
  padding-top: max(var(--pad-y), env(safe-area-inset-top));
  padding-bottom: max(var(--pad-y), env(safe-area-inset-bottom));
}

/* ---------- верх-лево: логотип + текст ---------- */
.brand {
  grid-column: 1;
  grid-row: 1;
  justify-self: start;
  align-self: start;
  min-width: 0;
}
.brand__logo {
  width: calc(448 * var(--k));
  height: auto;
}
.brand__copy {
  margin-top: calc(46 * var(--k));     /* 323 − (70 + 207) */
  display: flex;
  flex-direction: column;
  gap: calc(32 * var(--k));
  width: calc(398 * var(--k));
}
.lede {
  font-size: calc(20 * var(--k));
  line-height: 1.15;
  color: rgba(255,255,255,.8);
}
.feats {
  display: flex;
  flex-direction: column;
  gap: calc(20 * var(--k));
}
.feat {
  display: flex;
  align-items: flex-start;
  gap: calc(25 * var(--k));
}
.feat__icon {
  flex: 0 0 auto;
  width: calc(33 * var(--k));
  height: calc(33.24 * var(--k));
  /* Прозрачности нет: в макете opacity 80% висит только на тексте,
     иконки идут в своих цветах (#707983 / white). */
}
.feat__text {
  flex: 1 1 auto;
  min-width: 0;
  font-size: calc(20 * var(--k));
  line-height: 1.15;
  color: rgba(255,255,255,.8);
}

/* ---------- верх-право: кнопка ---------- */
.download {
  grid-column: 2;
  grid-row: 1;
  justify-self: end;
  align-self: start;
  /* Макет даёт повёрнутый бокс 252.93 × 96.17 в точке (1160; 97.29).
     Обратная тригонометрия при −3.06° даёт неповёрнутый 248.85 × 83.00,
     отсюда фиксированная высота. Поворот раздувает bbox на ~2px за край
     border-box'а, поэтому кнопка отодвигается на эти 2px внутрь — тогда её
     видимый правый край точно совпадает с боковым отступом сетки. */
  margin-top: calc(33.9 * var(--k));
  margin-right: calc(2 * var(--k));
  height: calc(83 * var(--k));
  display: inline-flex;
  align-items: center;
  gap: calc(6.8 * var(--k));
  padding: 0 calc(25 * var(--k)) 0 calc(12 * var(--k));
  border-radius: calc(25.5 * var(--k));
  background: #fff;
  color: #000;
  white-space: nowrap;
  /* Наклон из макета живёт в отдельном свойстве rotate, а не в transform:
     так его можно качать анимацией, не трогая hover-масштаб. Значение —
     стартовое, для reduced-motion; в анимации кнопка ходит от −3° до 3°. */
  rotate: -3.06deg;
  transform-origin: center;
  /* Горизонтальный сдвиг и амплитуда подъёма вынесены в переменные: на мобиле
     кнопка центруется через translate: -50%, и без этого пришлось бы держать
     второй комплект кейфреймов. Здесь, на десктопе, сдвига нет. */
  --dl-x: 0px;
  --dl-lift: 10px;
  /* Альфа 54% — из макета (Figma 112:264). На 25% внутренняя тень почти не
     читалась на белой кнопке, и объём терялся. */
  box-shadow: inset calc(-3.4 * var(--k)) calc(-3.4 * var(--k)) calc(6.8 * var(--k)) rgba(0,0,0,.54);
  transition: transform .2s ease, filter .2s ease;
  /* Покачивание живёт в translate по той же причине — индивидуальные
     translate/rotate/scale применяются до transform, поэтому три слоя
     (сдвиг, наклон, ховер-масштаб) складываются и не стирают друг друга.
     У покачивания 1.2s — это полный цикл вверх-вниз, то есть 0.6s на ход.
     У наклона alternate, поэтому его 0.6s — это уже один ход, от −3° до 3°:
     оба слоя идут в одном темпе.
     Отрицательная задержка −0.3s разводит их по фазе на четверть цикла. Без
     неё максимум наклона попадает на верхнюю точку подъёма, и у краёв кнопки
     поворот складывается со сдвигом с разными знаками: левый край проходит
     20px, правый — 3px. В квадратуре оба края ходят одинаково. */
  animation: download-float 1.2s infinite,
             download-tilt .6s ease-in-out -.3s infinite alternate;
}
/* Пока открыта модалка, кнопка замирает — она уже под затемнением. */
.download[aria-expanded="true"] { animation-play-state: paused; }
/* Полный цикл прописан кейфреймами, а не alternate: так у хода вверх и хода
   вниз своя кривая, и их видно в коде явно. */
@keyframes download-float {
  0%   { translate: var(--dl-x) 0;                    animation-timing-function: ease-out; }  /* вверх */
  50%  { translate: var(--dl-x) calc(-1 * var(--dl-lift)); animation-timing-function: ease-in; }  /* вниз */
  100% { translate: var(--dl-x) 0; }
}
/* Наклону alternate не вредит: ease-in-out симметричен, и развёрнутый задом
   наперёд он остаётся собой — маятник качается ровно в обе стороны. */
@keyframes download-tilt {
  from { rotate: -3deg; }
  to   { rotate: 3deg; }
}
.download:hover  { transform: scale(1.03); filter: brightness(.97); }
.download:active { transform: scale(.99); }
.download__icon {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  width: calc(48 * var(--k));
  height: calc(48 * var(--k));
  opacity: .34;
}
.download__icon img {
  width: calc(27 * var(--k));
  height: calc(34.49 * var(--k));
}
.download__label {
  font-family: "SB Sans Text Cond", "SB Sans Interface", sans-serif;
  font-weight: 700;
  font-size: calc(42.16 * var(--k));
  line-height: 1.1;
}
/* Кнопка стала <button> ради семантики диалога — гасим дефолты браузера.
   Заодно снимаем всё, чем браузер сам «подсвечивает» нажатие: выделение
   надписи при двойном клике и серый флеш тапа в мобильных вебвью. Кольцо
   фокуса это не трогает — оно ниже, в :focus-visible, и нужно клавиатуре. */
.download {
  border: 0;
  font: inherit;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}

/* ---------- низ-лево: бета-тестер ---------- */
.credit {
  grid-column: 1;
  grid-row: 3;
  justify-self: start;
  align-self: end;
  display: flex;
  align-items: center;
  gap: calc(30.6 * var(--k));
}
.credit__avatar {
  flex: 0 0 auto;
  width: calc(76 * var(--k));
  height: calc(102 * var(--k));
  border-radius: calc(23 * var(--k));
  object-fit: cover;
  /* Аватарка — живая: наведение (тап на телефоне) сыплет сердечки. Родное
     перетаскивание картинки и подсветка тапа этому только мешают: drag
     обрывает hover на полуслове, а серый флеш в вебвью выглядит поломкой. */
  user-select: none;
  -webkit-user-select: none;
  -webkit-user-drag: none;
  -webkit-tap-highlight-color: transparent;
}

/* Подпись и копирайт — одна колонка, она центрируется по аватарке:
   в макете её верх на 861.4, низ на 945.4, середина совпадает с 903. */
.credit__body {
  display: flex;
  flex-direction: column;
  gap: calc(12 * var(--k));
}
.credit__text {
  width: calc(279 * var(--k));
  font-size: calc(20 * var(--k));
  line-height: calc(24 * var(--k));
  color: rgba(255,255,255,.8);
}

/* Холст сердечек. Фиксирован во вьюпорте и лежит поверх всего: частицы живут
   в координатах экрана, чтобы одинаково работать и на десктопе, где .stage
   прибита к экрану, и на мобильной раскладке, где страница скроллится. */
.hearts {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 50;
  pointer-events: none;
}

/* ---------- копирайт ---------- */
.legal {
  width: calc(234 * var(--k));
  font-size: calc(20 * var(--k));
  line-height: calc(24 * var(--k));
  color: rgba(255,255,255,.3);
}

/* ==========================================================================
   МОДАЛКА «Скачать Gochi»
   Макет Paper: окно 621 × 491, паддинги 56 / 68 / 68, гэп 36, радиус 48.
   Свой коэффициент --ms: на десктопе он равен --k, на узких экранах окно
   разжимается до ширины вьюпорта, иначе при --k оно было бы с ноготок.
   ========================================================================== */
:root { --ms: var(--k); }

.dl {
  display: none;                        /* [open] переключит на flex */
  position: fixed;
  inset: 0;
  width: 100%;
  max-width: 100%;
  height: 100%;
  max-height: 100%;
  margin: 0;
  padding: calc(24 * var(--ms));
  border: 0;
  background: none;
  overflow: visible;
  color: #fff;
}
.dl__card:focus { outline: none; }
.dl[open] {
  display: flex;
  align-items: center;
  justify-content: center;
}
.dl::backdrop { background: rgba(0, 0, 0, .4); }

.dl__card {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: calc(36 * var(--ms));
  width: calc(621 * var(--ms));
  max-width: 100%;
  padding: calc(56 * var(--ms)) calc(68 * var(--ms)) calc(68 * var(--ms));
  border-radius: calc(48 * var(--ms));
  background: #40464E;
  background: color(display-p3 .252 .274 .306);
}

.dl__title {
  margin: 0;
  align-self: stretch;
  font-size: max(24px, calc(48 * var(--ms)));
  line-height: 1.21;
  letter-spacing: -.01em;
  font-weight: 700;                     /* в макете Bold, а не Semibold */
  color: #F4F4F4;
}

/* ---------- две плитки сторов ---------- */
.dl__grid {
  align-self: stretch;
  display: flex;
  gap: calc(36 * var(--ms));
}
.store {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  height: calc(273 * var(--ms));
  border-radius: calc(37 * var(--ms));
  background: #4D5258;
  background: color(display-p3 .302 .321 .345);
}
.store__art {
  position: absolute;
  inset: 0 0 auto;
  display: flex;
  justify-content: center;
}
.store__name {
  position: absolute;
  top: calc(211 * var(--ms));
  left: 0;
  width: 100%;
  text-align: center;
  font-size: max(15px, calc(25 * var(--ms)));
  line-height: 1.2;
  color: #fff;
}
/* Логотипы стоят на своих местах из макета: андроид ниже и шире, яблоко выше. */
.store__logo--android {
  width: calc(112 * var(--ms));
  height: calc(65 * var(--ms));
  margin-top: calc(76.5 * var(--ms));
}
.store__logo--ios {
  width: calc(76 * var(--ms));
  height: calc(93 * var(--ms));
  margin-top: calc(58.5 * var(--ms));
}
/* iOS ещё не вышла: содержимое приглушено, плитка не кликается. */
.store--soon .store__art,
.store--soon .store__name { opacity: .3; }

/* В макете плашка повёрнута вокруг своего левого верхнего угла. Здесь она
   вращается вокруг центра (так её удобно «выщёлкивать» масштабом), поэтому
   координаты пересчитаны на ту же точку центра: 141.093 / −8.496. */
.store__badge {
  position: absolute;
  top: calc(-8.496 * var(--ms));
  left: calc(141.093 * var(--ms));
  display: flex;
  align-items: center;
  justify-content: center;
  /* Размер задан явно: пересчёт координат ниже верен только для бокса
     85.8 × 38 из макета. На своей ширине по содержимому плашка выходила
     66 × 34, и центр уезжал на 10px влево. */
  width: calc(85.8 * var(--ms));
  height: calc(38 * var(--ms));
  padding: calc(5 * var(--ms)) calc(6 * var(--ms));
  border-radius: calc(infinity * 1px);
  background: #fff;
  color: #000;
  font-size: max(12px, calc(19 * var(--ms)));
  line-height: calc(24 / 19);
  white-space: nowrap;
  transform: rotate(6.77deg);
  transform-origin: 50% 50%;
}

/* ---------- крестик ---------- */
.dl__close {
  position: absolute;
  /* кнопка крупнее макета, но центр крестика остался на месте:
     отступы компенсируют прирост радиуса. */
  top: calc(23.33 * var(--ms));
  right: calc(22.48 * var(--ms));
  display: flex;
  align-items: center;
  justify-content: center;
  width: calc(53.33 * var(--ms));
  height: calc(53.33 * var(--ms));
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: rgba(255, 255, 255, .1);
  color: #868A8E;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
.dl__close svg {
  width: calc(26.67 * var(--ms));
  height: calc(26.67 * var(--ms));
}
.dl__close:hover { background: rgba(255, 255, 255, .18); color: #D6D9DC; }

/* ---------- живость: наведение и нажатие ---------- */
.store:not(.store--soon) {
  cursor: pointer;
  transition: background-color .2s ease, transform .3s cubic-bezier(.22, 1, .28, 1);
}
.store:not(.store--soon):hover {
  background: color(display-p3 .35 .372 .4);
  transform: translateY(calc(-3 * var(--ms)));
}
.store:not(.store--soon):active { transform: translateY(0) scale(.985); }
.dl__close:active { transform: scale(.92); }

.dl a:focus-visible,
.dl button:focus-visible {
  outline: calc(3 * var(--ms)) solid #fff;
  outline-offset: calc(4 * var(--ms));
}

/* ==========================================================================
   МОДАЛКА: анимация
   Два состояния, которые вешает app.js: .is-open — вход, .is-closing — выход.
   Вход длинный и каскадный: сначала наливается затемнение с блюром, следом
   окно выезжает снизу с лёгким перелётом, а его содержимое подтягивается по
   очереди — задержка каждого элемента лежит в --d.
   Выход вдвое короче и без каскада: закрытие не должно заставлять ждать.
   ========================================================================== */
/* Затемнение описано состоянием, а не концом анимации: fill: forwards на
   ::backdrop живёт до первой перерисовки псевдоэлемента и в паре движков
   слетает, оставляя окно висеть на прозрачном фоне. */
.dl[open]::backdrop {
  background-color: rgba(0, 0, 0, .4);
  -webkit-backdrop-filter: blur(6px) saturate(.9);
          backdrop-filter: blur(6px) saturate(.9);
}

.dl.is-open::backdrop {
  animation: dl-veil-in .42s ease;
}
.dl.is-closing::backdrop {
  animation: dl-veil-in .22s ease reverse forwards;
}
@keyframes dl-veil-in {
  from {
    background-color: rgba(0, 0, 0, 0);
    -webkit-backdrop-filter: blur(0px) saturate(1);
            backdrop-filter: blur(0px) saturate(1);
  }
  to {
    background-color: rgba(0, 0, 0, .4);
    -webkit-backdrop-filter: blur(6px) saturate(.9);
            backdrop-filter: blur(6px) saturate(.9);
  }
}

.dl.is-open .dl__card {
  animation: dl-card-in .62s cubic-bezier(.18, 1.1, .3, 1) backwards;
}
.dl.is-closing .dl__card {
  animation: dl-card-out .2s cubic-bezier(.4, 0, .8, .3) forwards;
}
@keyframes dl-card-in {
  from { opacity: 0; transform: translateY(calc(38 * var(--ms))) scale(.9); }
  60%  { opacity: 1; }
  to   { opacity: 1; transform: none; }
}
@keyframes dl-card-out {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(calc(12 * var(--ms))) scale(.96); }
}

/* Содержимое. backwards, а не forwards: после проигрыша элемент возвращается
   к своим обычным стилям, и ховер-трансформы плиток снова работают.
   Крестик в каскад не входит: он появляется вместе с окном, без своей
   отдельной анимации. */
.dl.is-open .dl__title,
.dl.is-open .store {
  animation: dl-item-in .5s cubic-bezier(.2, 1, .28, 1) var(--d, 0s) backwards;
}
@keyframes dl-item-in {
  from { opacity: 0; transform: translateY(calc(18 * var(--ms))); }
  to   { opacity: 1; transform: none; }
}

/* Плашка «Скоро» приходит последней и выщёлкивается — единственный
   пружинистый жест в окне, поэтому взгляд цепляется именно за неё. */
.dl.is-open .store__badge {
  animation: dl-badge-in .52s cubic-bezier(.2, 1.35, .4, 1) .3s backwards;
}
@keyframes dl-badge-in {
  from { opacity: 0; transform: rotate(-4deg) scale(.4); }
  to   { opacity: 1; transform: rotate(6.77deg) scale(1); }
}

/* Содержимое на выходе не разбегается — уезжает вместе с окном. */
.dl.is-closing .dl__title,
.dl.is-closing .store,
.dl.is-closing .store__badge { animation: none; }

/* Кнопка, открывшая окно, чуть проседает — связь «нажал → появилось». */
.download[aria-expanded="true"] { transform: scale(.97); }

html.dl-open { overflow: hidden; }

/* ==========================================================================
   ПОРТРЕТ / МОБИЛКА
   Отдельная раскладка по макету Paper «гочи — mobile 390» (390 × 1340):
   вертикальная страница со скроллом, где видео — не фон на весь экран,
   а полоса в потоке, срезанная градиентами сверху и снизу.

   Все размеры — из макета, умноженные на --m. Порядок блоков задаётся
   через order: DOM менять не пришлось, .ui / .brand / .brand__copy
   раскрываются в display: contents, и их дети становятся элементами
   одной колонки .stage.
   ========================================================================== */
@media (max-aspect-ratio: 1/1), (max-width: 760px) {
  :root {
    /* Масштаб от ширины экрана. Потолок 1.2 нужен для планшетов в портрете:
       они проходят по max-aspect-ratio, и без него на 768px кегль улетел бы
       в 33px. Колонка там просто центрируется. */
    --m: min(calc(100vw / 390), 1.2px);
  }

  html, body { height: auto; }
  body { overflow: visible; background: #0D1524; }

  .stage {
    position: relative;
    inset: auto;
    height: auto;
    min-height: 100dvh;
    overflow: visible;
    width: min(100vw, calc(390 * var(--m)));
    margin-inline: auto;
    display: flex;
    flex-direction: column;
    align-items: stretch;
    /* Копирайт кончается на 1238, страница в макете — 1278. */
    padding-bottom: calc(40 * var(--m));
    /* Верхнее затемнение из макета: 473px от верха страницы. */
    background:
      linear-gradient(180deg,
        rgba(6,10,20,.68) 0,
        rgba(6,10,20,.34) calc(287 * var(--m)),
        rgba(6,10,20,0)   calc(473 * var(--m))) no-repeat,
      #0D1524;
  }

  /* Раскрываем обёртки, чтобы логотип, лид, фичи и плашка стали
     соседями по одной колонке и их можно было переставить order'ом. */
  .ui, .brand, .brand__copy { display: contents; }
  .scrim { display: none; }

  /* Всё, что лежит поверх полосы с видео. */
  .brand__logo, .lede, .feats, .credit { position: relative; z-index: 1; }
  .download { z-index: 2; }

  /* ---------- 1. логотип ---------- */
  .brand__logo {
    order: 1;
    width: calc(318 * var(--m));
    height: calc(148 * var(--m));
    margin: calc(39 * var(--m)) auto 0;
  }

  /* ---------- 2. лид ---------- */
  .lede {
    order: 2;
    width: calc(292 * var(--m));
    margin: calc(21 * var(--m)) auto 0;
    font-size: calc(20 * var(--m));
    line-height: calc(23 * var(--m));
    text-align: center;
    color: rgba(255,255,255,.8);
  }

  /* ---------- 3. полоса с видео ---------- */
  .media {
    order: 3;
    position: relative;
    /* z-index обязателен: order меняет не только раскладку, но и порядок
       отрисовки флекс-элементов, и полоса перекрывала фичи с кнопкой. */
    z-index: 0;
    inset: auto;
    width: 100%;
    /* Лид подрос до 20/23 и стал на 7px выше (46 вместо 39), а полоса в макете
       осталась на 247. Отрицательный отступ возвращает её на место: верх полосы
       всё равно прозрачен по маске, так что перекрытие лида ничем не видно. */
    margin-top: calc(-7 * var(--m));
    /* Полоса укоротилась: в макете кадр теперь 247…790 вместо 247…839. */
    aspect-ratio: 390 / 543;
    overflow: hidden;
    background: none;
    /* Верхний срез делает не оверлей поверх видео, а маска самой полосы.
       Оверлей в принципе не может закрыть границу: чтобы граница исчезла, на
       ней нужна непрозрачность 1, а это уже плашка цвета фона с собственным
       чётким краем. Маска же гасит альфу видео до нуля, и оно растворяется
       ровно в том, что нарисовано под ним, — в фоне .stage вместе с его
       затемнением. Стык становится невидимым буквально, а не приблизительно.

       Стопы повторяют прежний градиент среза (.68 → .34 на 60.7% → 0 на 75):
       альфа маски — это доля видимого кадра, то есть 1 минус та самая
       непрозрачность. Разница лишь в первом стопе: он доведён до нуля.

       Снизу маска по-прежнему снимает 2px: там срез уже уходит в сплошной
       #0D1524, и волосок неприкрытого видео на дробной высоте полосы
       (543/390 от ширины экрана) гасится этим же приёмом. */
    -webkit-mask-image: linear-gradient(180deg,
      transparent 0,
      rgba(0,0,0,.66) calc(45.5 * var(--m)),
      #000 calc(75 * var(--m)),
      #000 calc(100% - 2px), transparent 100%);
    mask-image: linear-gradient(180deg,
      transparent 0,
      rgba(0,0,0,.66) calc(45.5 * var(--m)),
      #000 calc(75 * var(--m)),
      #000 calc(100% - 2px), transparent 100%);
  }
  .media::after { content: none; }
  /* Кадр из макета: видно окно x 2308…3295, y 3…1701 кадра 4398×1886 —
     это 986×1698, то есть по вертикали кадр обрезан. Через object-fit: cover
     так не сделать: на боксе 390×670.9 cover масштабирует по высоте и всегда
     показывает все 1886 строк, давая более общий план. Поэтому видео задано
     увеличенным элементом внутри полосы, а полоса его обрезает.
     Проценты считаются от полосы, так что кадрирование остаётся резиновым. */
  .media__el {
    position: absolute;
    /* Кадрирование в макете не менялось — сместились только границы полосы.
       Проценты по вертикали пересчитаны с 592 на 543, чтобы видео осталось
       того же размера и на том же месте: 111.056 % × 592 = 657.45 = 121.078 %
       от 543, 0.149 % × 592 = 0.88 = 0.162 % от 543. */
    left: -199.714%;
    top: -0.162%;
    width: 393.109%;
    height: 121.078%;
    max-width: none;              /* иначе ресет обрежет элемент до ширины полосы */
    object-fit: cover;
  }
  .media__cut {
    display: block;
    position: absolute;
    left: 0;
    width: 100%;
    pointer-events: none;
  }
  /* Верхний срез переехал в маску .media (см. выше). Оставить его нельзя:
     под ним видео уже прозрачно, и он лёг бы затемнением на голый фон —
     тёмной полосой с той же чёткой границей, которую и надо было убрать. */
  .media__cut--top { display: none; }
  .media__cut--bottom {
    /* Срез стартует на 509 от верха страницы, то есть на 262 от верха полосы,
       и упирается в её низ (790). Это ровно 281px — как слой в макете,
       поэтому стопы берутся из него без пересчёта. */
    top: calc(262 * var(--m));
    bottom: 0;
    height: auto;
    background: linear-gradient(180deg,
      rgba(13,21,36,0) 0%, rgba(13,21,36,.34) 43.83%, #0D1524 100%);
  }

  /* ---------- кнопка поверх полосы ----------
     В макете её бокс стоит на 686 и повёрнут вокруг левого верхнего угла, из-за
     чего видимый прямоугольник уезжает вверх: 675.9…753.5. У нас точка поворота
     по центру, поэтому бокс поднят до 681 — так видимые границы совпадают. */
  .download {
    position: absolute;
    /* 681 из макета минус 30: кнопка и весь блок под ней подняты на 30px
       (см. отрицательный margin-top у .feats — он тянет вверх фичи, плашку
       и копирайт тем же шагом, чтобы промежутки не поехали). */
    top: calc(651 * var(--m));
    bottom: auto;
    left: 50%;
    right: auto;
    margin: 0;
    height: calc(67.56 * var(--m));
    gap: calc(7 * var(--m));
    padding: calc(15 * var(--m)) calc(25 * var(--m)) calc(15 * var(--m)) calc(12 * var(--m));
    border-radius: calc(25.5 * var(--m));
    box-shadow: inset calc(-3.4 * var(--m)) calc(-3.4 * var(--m)) calc(6.8 * var(--m)) rgba(0,0,0,.54);
    /* Центрирование живёт в индивидуальном translate, а не в transform.
       Порядок применения — translate, rotate, scale, потом transform, — то есть
       сдвиг на −50% ложится снаружи поворота, в неповёрнутых осях родителя.
       Именно этого мы и хотели от translateX(-50%) внутри transform, только
       теперь наклон остаётся в rotate и качается той же анимацией, что и на
       десктопе, а transform свободен под ховер-масштаб. */
    --dl-x: -50%;
    /* Подъём меньше десктопных 10px: кнопка тут ниже, и тот же ход читался бы
       как прыжок, а не как покачивание. */
    --dl-lift: calc(8 * var(--m));
    transform: none;
    translate: var(--dl-x) 0;
  }

  /* Окно перестаёт жить по --k (там оно вышло бы с ноготок) и разжимается
     до ширины экрана минус поля. 621 — его ширина в макете. */
  :root { --ms: calc((min(100vw, 430px) - 32px) / 621); }
  /* Иконка вписана в высоту кнопки (67.56 − 2 × 15). В макете тут стоит
     слот 48px, который в 67.56 не помещается — от этого её и разорвало. */
  .download__icon     { width: calc(37.56 * var(--m)); height: calc(37.56 * var(--m)); }
  .download__icon img { width: calc(21.13 * var(--m)); height: calc(26.99 * var(--m)); }
  .download__label    { font-size: calc(26 * var(--m)); line-height: calc(32 * var(--m)); }

  /* ---------- 4. фичи ---------- */
  .feats {
    order: 4;
    /* Левый край 26, шаг строк 20. В макете блок стоит на 802, полоса кончается
       на 790 — отсюда зазор 12. Из него вычтены те же 30, на которые подняты
       кнопка и всё, что идёт после полосы: фичи, плашка и копирайт. */
    margin: calc(-18 * var(--m)) 0 0 calc(26 * var(--m));
    width: calc(342 * var(--m));
    gap: calc(20 * var(--m));
  }
  .feat       { gap: calc(20 * var(--m)); }
  .feat__icon { width: calc(32 * var(--m)); height: calc(32 * var(--m)); }
  .feat__text { font-size: calc(20 * var(--m)); line-height: calc(23 * var(--m)); }

  /* ---------- 5. бета-тестер и копирайт ----------
     Плашка — строка «аватарка + подпись», копирайт уходит отдельной строкой
     под неё и начинается от того же левого края. В разметке копирайт лежит
     внутри .credit__body, вынуть его оттуда нечем: order работает только среди
     соседей. Поэтому .credit становится гридом, а .credit__body раскрывается
     в display: contents — подпись и копирайт попадают в ячейки напрямую.

     Фичи начинаются на 802 и при строках 46/69/46 с шагом 20 кончаются
     на 1008, плашка начинается на 1079 — отсюда отступ 71. align-self
     перебивает align-self: end от десктопного грида. */
  .credit {
    order: 5;
    align-self: start;
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    /* Строчного зазора нет: копирайт отбит собственным margin-top, иначе
       к нему приплюсовался бы gap, унаследованный от десктопа. */
    gap: 0 calc(28 * var(--m));
    margin: calc(71 * var(--m)) 0 0 calc(35 * var(--m));
    /* 68 аватарка + 28 зазор + 234 подпись. */
    width: calc(330 * var(--m));
  }
  .credit__avatar {
    grid-area: 1 / 1;
    width: calc(68 * var(--m));
    height: calc(93 * var(--m));
    border-radius: calc(13.26 * var(--m));
  }
  .credit__body { display: contents; }
  .credit__text {
    grid-area: 1 / 2;
    width: auto;
    font-size: calc(20 * var(--m));
    line-height: calc(23 * var(--m));
    text-align: left;
  }
  /* Плашка кончается на 1172, копирайт начинается на 1222.
     Притушен он вдвойне: в макете на нём 80% в цвете и ещё 30%
     прозрачности слоя — вместе это 24%. */
  .legal {
    grid-area: 2 / 1 / 2 / -1;
    margin-top: calc(50 * var(--m));
    width: calc(234 * var(--m));
    font-size: calc(14 * var(--m));
    line-height: calc(16 * var(--m));
    text-align: left;
    color: rgba(255,255,255,.24);
  }
}

/* ---------- доступность ---------- */
@media (prefers-reduced-motion: reduce) {
  /* rotate не гасим: без анимации он остаётся статичным наклоном из макета.
     translate гасим не в none, а в исходный сдвиг: на мобиле он центрирует
     кнопку, и обнуление увело бы её вправо на половину ширины. */
  .download { transition: none; animation: none; translate: var(--dl-x) 0; }
  .download:hover, .download:active { transform: none; }

  /* Окно не летает и не пружинит — остаётся только проявление. */
  .dl.is-open::backdrop   { animation-duration: .12s; }
  .dl.is-closing::backdrop { animation-duration: .12s; }
  .dl.is-open .dl__card,
  .dl.is-closing .dl__card { animation: dl-fade .12s ease backwards; }
  .dl.is-closing .dl__card { animation-direction: reverse; animation-fill-mode: forwards; }
  .dl.is-open .dl__title,
  .dl.is-open .store,
  .dl.is-open .store__badge { animation: none; }
  .store:not(.store--soon) { transition: background-color .2s ease; }
  .store:not(.store--soon):hover,
  .store:not(.store--soon):active { transform: none; }
  @keyframes dl-fade { from { opacity: 0 } to { opacity: 1 } }
}
.download:focus-visible {
  outline: calc(3 * var(--k)) solid #fff;
  outline-offset: calc(4 * var(--k));
}
