/* ==========================================================================
   Гав VPN — вариант V3 «Кадры».
   Лента полноэкранных вертикальных кадров, одна мысль на кадр, огромная
   карточка Гавчика как главный объект. Хвост — обычный разворот без снапа.
   Файл правит только композицию: токены и база лежат в общих файлах.
   ========================================================================== */

/* --- Переменные композиции. Значения цвета и ритма — из tokens.css. ------- */

[data-variant='v3'] {
  --col: 720px;                 /* колонка кадра: мобильный макет и на десктопе */
  --headroom: 64px;             /* высота залипающей шапки из base.css         */
  --rail: 96px;                 /* поле по бокам колонки на широком экране     */
  --glow-c: var(--lime);        /* подсветка под маскотом, меняется по кадру   */
}

/* Кегли заголовков — общие токены. Прежние собственные clamp были поправкой
   на широкую Unbounded: её кириллица занимала около 0.91em на букву и рвала
   строку. Дисплейная гарнитура сменилась на Onest (уже на четверть), поправка
   больше не нужна — заголовки идут тем же кеглем, что у соседних вариантов. */
.plate { font-size: var(--t-mega); }
.plate .mark { padding-inline: 0.09em; }
.frame .display { font-size: var(--t-d1); }

/* Слово длиннее своей колонки не должно распирать сетку: перенос разрешён
   в любом месте. При выбранных кеглях это страховка, а не рабочий режим. */
.mega, .display { overflow-wrap: anywhere; }

/* --- Снап ----------------------------------------------------------------
   Только proximity: с mandatory длинный хвост не листается. И только там,
   где листают пальцем. Замер в Chrome: с мышью колесо на 120 px возвращалось
   к началу кадра шесть раз подряд — страница не двигалась вообще, потому что
   ближайшая точка привязки всегда оказывалась той же самой. Палец бросает
   экран далеко, мышь крутит понемногу, поэтому у точного указателя снапа
   нет: [65,65,65,65,65,65] против [120,240,360,480,600,720] без него. */

@media (pointer: coarse) {
  html { scroll-snap-type: y proximity; }
}
html.motion-off { scroll-snap-type: none; }

/* --- Иконки: один рисунок, одна толщина штриха, currentColor. ------------ */

.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }
.ico {
  width: 1em; height: 1em; flex: none;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* --- Ссылка «к содержанию» для клавиатуры -------------------------------- */

.skip {
  position: absolute; left: var(--s4); top: var(--s4); z-index: 200;
  padding: var(--s3) var(--s5);
  background: var(--lime); color: #0a0a0a;
  font-family: var(--f-ui); font-size: var(--t-label);
  letter-spacing: var(--tr-label); text-transform: uppercase;
  translate: 0 -200%;
}
.skip:focus-visible { translate: 0 0; }

/* --- Полоса прогресса: --sq считает shell.js по всему <main>. -------------
   Начало розовое, конец лаймовый: по цвету полосы видно, далеко ли ушли. */

.progress {
  position: fixed; inset: 0 0 auto 0; z-index: 60;
  height: 3px;
  background: color-mix(in oklab, var(--paper) 12%, transparent);
  pointer-events: none;
}
.progress__bar {
  display: block; height: 100%;
  background: linear-gradient(90deg, var(--pink), var(--lime) 46%, var(--lime-hi));
  transform: scaleX(var(--sq, 0));
  transform-origin: left center;
  will-change: transform;
}

/* --- Шапка на узком телефоне ---------------------------------------------
   На 360 px кнопка «ТРИ ДНЯ БЕСПЛАТНО» вылезала за правый край на 22 px и
   пряталась только за overflow-x: hidden у body. Поджимаем сам ряд: знак,
   зазор и поля кнопки, — вместо того чтобы прятать дефект. */

@media (max-width: 430px) {
  .site-head__in { gap: var(--s4); }
  .site-head .btn {
    padding-inline: var(--s4);
    font-size: var(--t-label);
    letter-spacing: 0.035em;
  }
  .logo { gap: var(--s2); }
  .logo__paw { width: 26px; height: 26px; }
  .logo__word { font-size: calc(0.9375rem * var(--kui)); }
}

/* ==========================================================================
   Кадр
   ========================================================================== */

.frame {
  position: relative;
  min-height: 100svh;
  scroll-snap-align: start;
  display: grid;
  padding:
    calc(var(--headroom) + var(--s5))
    var(--gutter)
    calc(var(--s6) + env(safe-area-inset-bottom));
  overflow-x: clip;
}

/* Подсветка под маскотом: ярче всего, когда кадр стоит в экране. */
.frame::before {
  content: '';
  position: absolute; left: 50%; bottom: -14%;
  translate: -50% 0;
  width: min(150%, 980px); aspect-ratio: 1;
  background: radial-gradient(50% 50% at 50% 50%,
              color-mix(in oklab, var(--glow-c) 15%, transparent), transparent 68%);
  opacity: calc(1 - ((var(--sp, 0.5) - 0.5) * (var(--sp, 0.5) - 0.5) * 3.1));
  pointer-events: none;
}

/* Рельсы колонки: на широком экране кадр читается как высокая карточка. */
.frame::after {
  content: '';
  position: absolute; top: 0; bottom: 0; left: 50%;
  translate: -50% 0;
  width: min(100% - var(--gutter) * 2, calc(var(--col) + var(--rail)));
  border-inline: var(--bw) solid var(--line);
  opacity: 0;
  pointer-events: none;
}
@media (min-width: 1040px) { .frame::after { opacity: 1; } }

.frame__in {
  position: relative; z-index: 1;
  width: 100%; max-width: var(--col);
  margin-inline: auto;
  display: grid;
  gap: var(--s5);
  align-content: center;
  grid-template-columns: minmax(0, 1fr);
}

.frame__head { grid-area: head; display: grid; gap: var(--s4); }
.frame__body { grid-area: body; display: grid; gap: var(--s4); }
.frame__foot { grid-area: foot; display: grid; gap: var(--s4); justify-items: start; }
/* Обёртка карточки — сетка во всю ширину своей доли. Выравнивание задаём
   через justify-items, а не justify-self: сжатая по содержимому обёртка даёт
   круговую зависимость (ширина карточки считается от 100% обёртки). */
.frame__mascot { grid-area: dog; display: grid; justify-items: center; }

/* Параллакс: маскот отстаёт от заголовка. --sp даёт shell.js. */
.frame[data-scrub] .frame__head { transform: translateY(calc((var(--sp, 0.5) - 0.5) * -78px)); }
.frame[data-scrub] .frame__mascot { transform: translateY(calc((var(--sp, 0.5) - 0.5) * -40px)); }

/* Счётчик кадров: не украшение, а место в ленте. Текущий номер розовый —
   тот же сигнал, что у молний на куртке Гавчика. */
.frame__no {
  position: absolute; z-index: 2;
  top: calc(var(--headroom) + var(--s3)); left: 50%;
  translate: -50% 0;
  width: min(100% - var(--gutter) * 2, var(--col));
  text-align: right;
  font-family: var(--f-ui); font-weight: 500;
  font-size: var(--t-label); letter-spacing: var(--tr-label);
  color: var(--paper-mute);
}
.frame__no b { color: var(--pink); font-weight: 500; }
.frame__no i { font-style: normal; }

/* --- Плита-заголовок -----------------------------------------------------
   Лаймовая заливка во всю долю заголовка, жёсткие края, чёрный текст.
   Стоит через кадр (2, 4, 6): смена плоскости работает только чередованием,
   подряд залитые кадры снова читаются одинаково. */

.slab {
  display: block;
  width: fit-content;
  max-width: 100%;
  background: var(--lime);
  color: #0a0a0a;
  padding: 0.14em var(--gutter) 0.2em;
  margin-inline: calc(var(--gutter) * -1);
  text-wrap: balance;
}
/* У тихого кадра плита стоит по центру — вынос за колонку сделал бы её
   несимметричной. */
.frame--quiet .slab { margin-inline: auto; }

/* --- Карточка Гавчика внутри кадра --------------------------------------- */

/* Ширина считается от допустимой высоты и пропорций мастера, поэтому
   карточка никогда не выше кадра и никогда не шире своего мастера. */
.frame__mascot .mascot,
.sec__mascot .mascot {
  width: min(100%, var(--card-dw, 720px), calc(var(--mh, 34svh) * var(--ar, 0.62)));
}
.sec__mascot .mascot { margin-inline: auto; }

/* Вход карточки: маска раскрывается снизу вверх.
   Маска висит на <picture>, а не на самой <figure>: clip-path на элементе,
   за которым следит IntersectionObserver, обнуляет его пересечение с экраном,
   и появление не срабатывает никогда (проверено в Chrome — карточки
   оставались невидимыми). */
.mascot-rise picture {
  clip-path: inset(100% 0 0 0);
  transition: clip-path var(--d3) var(--e-out);
}
.mascot-rise.is-in picture { clip-path: inset(0 0 0 0); }
html.no-js .mascot-rise picture,
html.motion-off .mascot-rise picture { clip-path: none; transition: none; }

/* У кадров 1 и 7 текст стоит над карточкой, поэтому её рост считается от
   остатка высоты экрана: иначе на низком окне кадр выталкивает соседа.
   У первого кадра из остатка вычтена ещё и полоса пояснений. */
.m-hero    { --ar: 0.643; --card-dw: 1440px; --mh: min(40svh, max(110px, calc(100svh - 578px))); }
.m-run     { --ar: 0.655; --card-dw: 720px;  --mh: 33svh; }
.m-point   { --ar: 0.662; --card-dw: 720px;  --mh: 26svh; }
.m-laptop  { --ar: 0.857; --card-dw: 720px;  --mh: 19svh; }
.m-phone   { --ar: 0.521; --card-dw: 720px;  --mh: 22svh; }
.m-hush    { --ar: 0.573; --card-dw: 720px;  --mh: 44svh; }
.m-jump    { --ar: 0.742; --card-dw: 720px;  --mh: min(36svh, max(110px, calc(100svh - 505px))); }
.m-approve { --ar: 0.575; --card-dw: 720px;  --mh: 42svh; }
.m-guard   { --ar: 0.931; --card-dw: 720px;  --mh: 34svh; }
.m-friends { --ar: 0.645; --card-dw: 720px;  --mh: 40svh; }

/* ==========================================================================
   Кадр 1 · Пропуск
   ========================================================================== */

.frame--hero .frame__in {
  grid-template-areas: 'head' 'facts' 'dog' 'foot';
  align-content: space-between;
}
.frame--hero .frame__foot {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s4) var(--s6);
}
.frame--hero .lead { max-width: 34ch; }

/* Пояснения первого кадра: всплывают одно за другим по ходу прокрутки.
   Считаются от --sq — доли пройденного пути по всему <main>, которую даёт
   shell.js. Именно от неё, а не от --sp самого кадра: --sq растёт только
   вниз, поэтому появившееся пояснение дальше уже не гаснет. Пороги подобраны
   замером: пятое встаёт примерно на 350-м пикселе прокрутки, то есть внутри
   первого экрана. Без JS и при выключенном движении --sq равен 1 — тогда
   все пять видны сразу. */
.facts {
  grid-area: facts;
  display: flex; flex-wrap: wrap;
  gap: 6px;
}
.fact {
  --at: 0;
  --on: clamp(0, (var(--sq, 1) - var(--at)) * 500, 1);
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px var(--s2) 5px 7px;
  border: var(--bw) solid var(--line-strong);
  font-family: var(--f-ui); font-weight: 500;
  font-size: var(--t-ui-xs); letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--paper-dim);
  white-space: nowrap;
  opacity: var(--on);
  translate: 0 calc((1 - var(--on)) * 10px);
  transition: opacity var(--d1) linear, translate var(--d2) var(--e-out);
}
.fact::before {
  content: ''; flex: none;
  width: 5px; height: 5px;
  background: var(--pink);
}
.fact:nth-child(1) { --at: 0.0012; }
.fact:nth-child(2) { --at: 0.005; }
.fact:nth-child(3) { --at: 0.010; }
.fact:nth-child(4) { --at: 0.016; }
.fact:nth-child(5) { --at: 0.023; }
html.no-js .fact { opacity: 1; translate: none; }

.hint {
  display: inline-flex; align-items: center; gap: var(--s2);
  width: 100%;
  font-family: var(--f-ui); font-size: var(--t-label);
  letter-spacing: var(--tr-label); text-transform: uppercase;
  color: var(--paper-dim);
}
.hint .ico { font-size: 1.1rem; color: var(--pink); animation: v3-bob 2.6s var(--e-soft) infinite; }
@keyframes v3-bob { 0%, 100% { translate: 0 0; } 50% { translate: 0 5px; } }
html.motion-off .hint .ico { animation: none; }

/* ==========================================================================
   Кадр 2 · Через нас — плита-заголовок, плотный список, маскот справа
   ========================================================================== */

.frame--thru { --glow-c: var(--lime); }
/* На телефоне карточка делит клетку со списком и стоит в его правом нижнем
   углу: восемь названий по слову в строке узкими не становятся, а отдельная
   строка под маскот выталкивала кадр за пределы экрана. */
.frame--thru .frame__in { grid-template-areas: 'head' 'body'; }
.frame--thru .frame__mascot { grid-area: body; align-self: end; justify-items: end; }
.frame--thru .frame__body { position: relative; z-index: 1; }
.frame--thru .lead { max-width: 34ch; }

.way { gap: var(--s2); }
.way .ico { font-size: 1.05rem; }

.thru { display: grid; max-width: 62%; }
.thru li {
  font-family: var(--f-display); font-weight: 800;
  font-size: calc(clamp(1.3rem, 6.2vw, 2rem) * var(--khead));
  line-height: 1.28;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  border-bottom: var(--bw) solid var(--line);
}

/* ==========================================================================
   Кадр 3 · Напрямую — одно слово-плита, мелкие плашки, маскот слева
   ========================================================================== */

.frame--direct { --glow-c: var(--pink); }
.frame--direct .frame__in { grid-template-areas: 'head' 'body' 'dog' 'foot'; }
.frame--direct .frame__mascot { align-self: end; justify-items: start; }

.direct { display: flex; flex-wrap: wrap; gap: var(--s2); }
.direct li {
  padding: 7px var(--s3);
  border: var(--bw) solid var(--line-strong);
  font-family: var(--f-ui); font-weight: 500;
  font-size: var(--t-btn-sm); letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--paper-dim);
}

/* Сноска идёт во всю ширину колонки под маскотом. Прежние 50 % ширины
   превращали её в столбик по три слова в строке. */
.note { font-size: var(--t-sm); line-height: 1.45; color: var(--paper-dim); }

/* ==========================================================================
   Кадр 4 · Устройства — плита-заголовок, три рисованных силуэта
   ========================================================================== */

.frame--devices .frame__in { grid-template-areas: 'head' 'body' 'dog' 'foot'; }
.frame--devices .frame__mascot { align-self: end; justify-items: start; }

.kinds { display: grid; gap: var(--s4); }
.kinds li {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr);
  grid-template-areas: 'ico title' 'ico text';
  gap: 2px var(--s3);
  padding-bottom: var(--s4);
  border-bottom: var(--bw) solid var(--line);
}
.kinds__ico { grid-area: ico; font-size: 1.4rem; color: var(--lime); align-self: start; }
.kinds h3 { grid-area: title; }
.kinds p { grid-area: text; font-size: var(--t-sm); color: var(--paper-dim); }

/* ==========================================================================
   Кадр 5 · Три шага — крупные цифры, маскот в углу того же поля
   ========================================================================== */

.frame--steps { --glow-c: var(--pink); }
.frame--steps .frame__in { grid-template-areas: 'head' 'body' 'dog'; }
.frame--steps .frame__mascot { align-self: end; justify-items: end; }
.frame--steps .frame__body { position: relative; z-index: 1; }

.steps { display: grid; gap: var(--s5); }
.steps li {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas: 'n title' 'n text';
  gap: 2px var(--s4);
  align-items: baseline;
}
.steps__n {
  grid-area: n;
  font-family: var(--f-display); font-weight: 800;
  font-size: calc(clamp(2rem, 9vw, 3rem) * var(--khead));
  line-height: 0.9;
  letter-spacing: -0.05em;
  color: var(--lime);
  font-variant-numeric: tabular-nums;
}
.steps h3 { grid-area: title; }
.steps p { grid-area: text; font-size: var(--t-sm); color: var(--paper-dim); }

/* ==========================================================================
   Кадр 6 · Карту не привязываем — тихий кадр, максимум воздуха
   ========================================================================== */

.frame--quiet .frame__in { grid-template-areas: 'head' 'dog'; gap: var(--s7); }
.frame--quiet .frame__head { justify-items: center; text-align: center; }
.frame--quiet .lead { max-width: 26ch; }

/* ==========================================================================
   Кадр 7 · Три дня бесплатно
   ========================================================================== */

.frame--trial .frame__in { grid-template-areas: 'head' 'dog' 'foot'; align-content: space-between; }
.frame--trial .frame__foot { justify-items: stretch; }
.frame--trial .frame__foot .btn { justify-self: start; }
.frame--trial .lead { max-width: 30ch; }
.frame--trial .link { justify-self: start; }

/* ==========================================================================
   Узкий экран и низкий экран: кадр обязан остаться в своих границах
   ========================================================================== */

/* Крупная кнопка с длинной подписью на 360 px не помещается в строку. */
@media (max-width: 470px) {
  .btn--lg { padding-inline: var(--s5); font-size: var(--t-btn); }
  .frame__foot .btn, .foot-cta .btn { max-width: 100%; white-space: normal; text-align: center; }
}

/* ==========================================================================
   Кадры шире 620 px: колонка та же, композиция раскладывается на две доли
   ========================================================================== */

@media (min-width: 620px) {
  .frame__in { gap: var(--s6); }

  .frame--thru .frame__in {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.78fr);
    grid-template-areas: 'head head' 'body dog';
    align-items: end;
  }
  /* Долю карточки надо назначить явно: без этого маскот остаётся в клетке
     списка и наезжает на названия сервисов. */
  .frame--thru .frame__mascot { grid-area: dog; justify-items: center; }
  /* Список раскрывается во всю долю, поэтому строки идут плотнее: восемь
     строк с прежним интерлиньяжем выталкивали кадр за высоту экрана. */
  .frame--thru .thru { max-width: none; }
  .frame--thru .thru li { line-height: 1.2; }

  /* Плита в одно слово требует всю ширину колонки — заголовок идёт над
     обеими долями, а маскот встаёт слева от плашек. */
  .frame--direct .frame__in {
    grid-template-columns: minmax(0, 0.62fr) minmax(0, 1fr);
    grid-template-areas: 'head head' 'dog body' 'dog foot';
    align-items: start;
  }
  .frame--direct .frame__mascot { align-self: end; justify-items: center; }
  /* Сноска садится на одну линию с лапами Гавчика, а не повисает в середине
     пустой доли. */
  .frame--direct .frame__foot { align-self: end; }
  .frame--direct .note { max-width: 46ch; }

  .frame--devices .frame__in {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.56fr);
    grid-template-areas: 'head head' 'body body' 'foot dog';
    align-items: end;
  }
  .frame--devices .kinds { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .frame--devices .kinds li { grid-template-columns: minmax(0, 1fr); grid-template-areas: 'ico' 'title' 'text'; gap: var(--s2); }
  .frame--devices .frame__mascot { justify-items: end; }
  .frame--devices .note { max-width: 46ch; }

  .frame--steps .frame__in {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.5fr);
    grid-template-areas: 'head head' 'body dog';
    align-items: end;
  }
  .frame--steps .frame__mascot { grid-area: dog; justify-items: center; }

  .m-hero { --mh: min(48svh, max(140px, calc(100svh - 555px))); }
  .frame--hero .lead { max-width: 44ch; }
  .m-jump { --mh: min(38svh, max(130px, calc(100svh - 585px))); }
  .m-hush { --mh: 52svh; }
  .m-run { --mh: 40svh; }
  .m-point { --mh: 40svh; }
  .m-phone { --mh: 40svh; }
  .m-laptop { --mh: 28svh; }
}

/* ==========================================================================
   Кадры шире 1100 px: колонка расширяется, кадр разводится на две доли.
   До этой правки контент стоял в полосе 720 px, а по бокам оставались две
   пустые обочины по 300 px с каждой стороны.
   ========================================================================== */

@media (min-width: 1100px) {
  [data-variant='v3'] { --col: min(1200px, calc(100vw - 200px)); }

  .frame__in { column-gap: var(--s9); }

  /* У кадров 1 и 7 плита стоит в левой доле, а не во всей колонке: на полном
     кегле «ИНТЕРНЕТ» и «БЕСПЛАТНО» не помещались в строку и рвались посреди
     слова. Потолок 6rem — тот же, что держит остальная страница. */
  .frame--hero .plate, .frame--trial .plate { font-size: min(var(--t-mega), 6rem); }

  /* Кадр 1: текст слева, пояснения под ним, огромный Гавчик справа. */
  .frame--hero .frame__in {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.86fr);
    grid-template-areas: 'head dog' 'facts dog' 'foot dog';
    align-content: center;
    align-items: start;
    row-gap: var(--s7);
  }
  .frame--hero .frame__mascot { align-self: center; justify-items: center; }
  .frame--hero .facts { flex-direction: column; align-items: flex-start; gap: var(--s2); }
  .frame--hero .fact { font-size: var(--t-label); padding: 7px var(--s3) 7px var(--s2); }
  .m-hero { --mh: min(74svh, calc(100svh - 200px)); }

  /* Кадр 2: список во всю левую долю, карточка крупнее. */
  .frame--thru .frame__in { grid-template-columns: minmax(0, 1fr) minmax(0, 0.62fr); }
  .m-run { --mh: 50svh; }

  /* Кадр 3: плита слева над плашками, маскот держит левую долю. */
  .frame--direct .frame__in { grid-template-columns: minmax(0, 0.52fr) minmax(0, 1fr); }
  .m-point { --mh: 52svh; }

  /* Кадр 4: три силуэта во всю ширину, сноска и маскот — нижняя строка. */
  .frame--devices .frame__in { grid-template-columns: minmax(0, 1fr) minmax(0, 0.44fr); }
  .m-laptop { --mh: 34svh; }

  /* Кадр 5: шаги слева, Гавчик с телефоном справа во весь рост доли. */
  .frame--steps .frame__in { grid-template-columns: minmax(0, 1fr) minmax(0, 0.46fr); }
  .m-phone { --mh: 56svh; }

  /* Кадр 6: тихий кадр тоже перестаёт быть колонкой по центру. */
  .frame--quiet .frame__in {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.82fr);
    grid-template-areas: 'head dog';
    align-items: center;
    gap: var(--s6) var(--s9);
  }
  .frame--quiet .frame__head { justify-items: start; text-align: left; }
  .frame--quiet .slab { margin-inline: calc(var(--gutter) * -1) 0; }
  .frame--quiet .lead { max-width: 30ch; }
  .m-hush { --mh: 72svh; }

  /* Кадр 7: призыв слева, прыжок справа. */
  .frame--trial .frame__in {
    grid-template-columns: minmax(0, 1fr) minmax(0, 0.82fr);
    grid-template-areas: 'head dog' 'foot dog';
    align-content: center; align-items: start;
    row-gap: var(--s7);
  }
  .frame--trial .frame__mascot { align-self: center; }
  .m-jump { --mh: min(62svh, calc(100svh - 240px)); }
}

/* ==========================================================================
   Низкое окно (короткий телефон, ноутбук, альбомный планшет): поджимаем
   воздух, кегль и рост карточки, чтобы кадр укладывался в свою высоту.
   Блок идёт последним намеренно: ограничение по высоте должно перебивать
   раскладку по ширине, иначе на ноутбуке 1280×720 кадр вылезает за экран.
   ========================================================================== */

@media (max-height: 820px) {
  .frame {
    padding-top: calc(var(--headroom) + var(--s3));
    padding-bottom: calc(var(--s5) + env(safe-area-inset-bottom));
  }
  .frame__in { gap: var(--s4); }
  .frame--quiet .frame__in { gap: var(--s5); }
  .frame--hero .frame__in, .frame--trial .frame__in { row-gap: var(--s4); }
  .frame .display { font-size: calc(clamp(1.75rem, 6.4vw, 2.6rem) * var(--khead)); }
  .frame .plate { font-size: calc(clamp(2.2rem, 9.4vw, 3.8rem) * var(--khead)); }
  .thru li { font-size: calc(clamp(1.15rem, 4.4vw, 1.5rem) * var(--khead)); }
  .m-hero { --mh: min(40svh, max(104px, calc(100svh - 530px))); }
  .m-run { --mh: 34svh; }
  .m-point { --mh: 30svh; }
  .m-laptop { --mh: 22svh; }
  .m-phone { --mh: 24svh; }
  .m-hush { --mh: 46svh; }
  .m-jump { --mh: min(38svh, max(104px, calc(100svh - 470px))); }
  .btn--lg { min-height: 54px; }
  .steps { gap: var(--s4); }
  .facts { gap: 4px; }
}

@media (max-height: 820px) and (max-width: 619px) {
  .m-hero { --mh: min(34svh, max(96px, calc(100svh - 500px))); }
  .m-run { --mh: 25svh; }
  .m-point { --mh: 19svh; }
  .m-laptop { --mh: 16svh; }
  .m-phone { --mh: 18svh; }
  .m-hush { --mh: 34svh; }
  .fact { font-size: calc(0.5625rem * var(--kui)); padding-inline: 6px 6px; }
}

/* ==========================================================================
   Разворот: тарифы, честно, друзья, вопросы. Снапа здесь нет.
   ========================================================================== */

.spread { position: relative; border-top: var(--bw) solid var(--line); }

.sec { padding-block: var(--s10); position: relative; }
.sec + .sec { border-top: var(--bw) solid var(--line); }
.hold { width: 100%; max-width: 880px; margin-inline: auto; padding-inline: var(--gutter); }

.sec__head { display: grid; gap: var(--s4); margin-bottom: var(--s8); }
.sec[data-scrub] .sec__mascot { transform: translateY(calc((var(--sp, 0.5) - 0.5) * -24px)); }

/* --- Тарифы -------------------------------------------------------------- */

.plans { display: grid; gap: var(--s3); }
.plan {
  display: grid; gap: var(--s3); align-content: start;
  height: 100%;
  padding: var(--s5);
  background: var(--raise);
  border: var(--bw) solid var(--line-strong);
  border-top: var(--bw-strong) solid var(--lime);
  transition: border-color var(--d1) linear, background-color var(--d1) linear,
              transform var(--d2) var(--e-out);
}
/* Рамка выбранной карточки — розовая: тот же сигнал, что у счётчика кадров. */
.plan:hover, .plan:focus-visible { border-color: var(--pink); transform: translateY(-3px); }
.plan__term {
  font-family: var(--f-display); font-weight: 800;
  font-size: var(--t-h); text-transform: uppercase; letter-spacing: -0.02em;
}
.plan__price { font-family: var(--f-ui); font-size: calc(1.25rem * var(--kui)); color: var(--lime); }
.plan__dev { font-size: var(--t-sm); color: var(--paper-dim); }
.plan__go {
  display: inline-flex; align-items: center; gap: var(--s2);
  margin-top: var(--s2);
  font-family: var(--f-ui); font-size: var(--t-btn-sm);
  letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--paper-dim);
}
.plan:hover .plan__go { color: var(--pink); }
.plan:hover .plan__go .ico { transform: translateX(4px); }
.plan__go .ico { font-size: 1rem; transition: transform var(--d2) var(--e-out); }

.ph {
  font-family: var(--f-mono); font-weight: 500;
  font-size: 0.95em;
  padding: 1px 5px;
  border: var(--bw) dashed var(--line-strong);
  color: inherit;
  /* nowrap только там, где хватает места: на 320 px {{ОТВЕТ-ПРО-ТРАФИК}}
     вылезал за край, и прокрутки не было лишь из-за overflow-x: hidden. */
  white-space: nowrap;
}
@media (max-width: 400px) {
  .ph { white-space: normal; overflow-wrap: anywhere; }
}
/* В четырёхколоночной сетке тарифов длинный плейсхолдер шире карточки —
   пусть лучше переносится, чем обрезается по краю. */
.plan__dev { display: grid; gap: 4px; justify-items: start; }
.plan__dev .ph { white-space: normal; overflow-wrap: anywhere; max-width: 100%; }

.sec--tariffs .sec__mascot {
  display: grid; gap: var(--s5); align-items: end;
  margin-top: var(--s8);
  padding-top: var(--s7);
  border-top: var(--bw) solid var(--line);
}
.sec--tariffs .sec__mascot .lead { align-self: center; }

/* --- Честно -------------------------------------------------------------- */

.sec--honest { background: var(--sink); }
.honest { display: grid; gap: var(--s7); }
.honest__list { display: grid; }
.honest__list li {
  display: grid; gap: var(--s2);
  padding-block: var(--s5);
  border-top: var(--bw) solid var(--line);
}
.honest__list li:last-child { border-bottom: var(--bw) solid var(--line); }
.honest__list p { color: var(--paper-dim); }

/* --- Друзья -------------------------------------------------------------- */

.friends { display: grid; gap: var(--s7); }
.friends__text { display: grid; gap: var(--s5); justify-items: start; }
/* Заголовок стоит внутри узкой доли — кегль считаем от неё, а не от окна. */
.friends__text .display { font-size: calc(clamp(1.9rem, 5.6vw, 3rem) * var(--khead)); }

/* --- Вопросы ------------------------------------------------------------- */

.qas { display: grid; }
.qa { border-top: var(--bw) solid var(--line); }
.qa:last-child { border-bottom: var(--bw) solid var(--line); }
.qa__q {
  display: flex; align-items: center; gap: var(--s4);
  padding-block: var(--s4);
  cursor: pointer;
  list-style: none;
}
.qa__q::-webkit-details-marker { display: none; }
.qa__face { flex: none; width: 56px; }
.qa__face .mascot img { width: 56px; height: auto; }
.qa__t { font-weight: 600; font-size: var(--t-h); line-height: 1.25; }
.qa__sign {
  margin-left: auto; font-size: 1.35rem;
  color: var(--lime);
  transition: rotate var(--d2) var(--e-out);
}
/* Раскрытый вопрос — единственный активный элемент списка, и он подписан
   розовым: заголовок, знак и верхняя линия. */
.qa[open] { border-top-color: color-mix(in oklab, var(--pink) 55%, transparent); }
.qa[open] .qa__t { color: var(--pink); }
.qa[open] .qa__sign { rotate: 45deg; color: var(--pink); }
.qa__a {
  padding: 0 0 var(--s5) calc(56px + var(--s4));
  color: var(--paper-dim);
  max-width: 62ch;
}

/* --- Текстовая ссылка: подчёркивание розовое ----------------------------- */

.link::after { background: var(--pink); }
.link:hover { color: var(--pink); }

/* ==========================================================================
   Разворот шире 720 px
   ========================================================================== */

@media (min-width: 560px) {
  .plans { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 720px) {
  .sec--tariffs .sec__mascot { grid-template-columns: 240px minmax(0, 1fr); }
  .honest { grid-template-columns: minmax(0, 1fr) 260px; align-items: end; }
  .friends { grid-template-columns: minmax(0, 1fr) 240px; align-items: end; }
}

@media (min-width: 980px) {
  .plans { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* Разворот выходит из той же узкой полосы, что и кадры. Правило стоит здесь,
   а не в общем блоке 1100 px: базовый `.hold` объявлен ниже него и при равной
   силе перебивал бы ширину обратно на 880 px. */
@media (min-width: 1100px) {
  .hold { max-width: 1040px; }
}

/* ==========================================================================
   Подвал: единственная плита цвета во всю ширину — под главным призывом
   ========================================================================== */

.foot-cta { background: var(--lime); color: #0b0d05; }
.foot-cta .site-foot__cta { align-content: center; justify-items: start; }
.foot-cta__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--s4) var(--s6); }
.foot-cta .btn { --btn-bg: #0b0d05; --btn-fg: var(--lime); }
.foot-cta .btn:hover { box-shadow: 0 12px 32px -12px rgb(0 0 0 / 0.55); }
.foot-cta .link { color: #2a3305; }
.foot-cta .link::after { background: currentColor; }
.foot-cta .link:hover { color: #0b0d05; }
.foot-cta :focus-visible { outline-color: #0b0d05; }

.foot-list { display: grid; gap: var(--s3); margin-top: var(--s4); }
.foot-list a { color: var(--paper-dim); transition: color var(--d1) linear; }
.foot-list a:hover { color: var(--lime); }
.site-foot__cols .legal { margin-top: var(--s5); }
.site-foot__bar .logo__paw { width: 22px; height: 22px; }
.site-foot__bar .logo__word { font-size: calc(0.875rem * var(--kui)); }
.site-foot__bar .spacer { display: inline-flex; align-items: center; gap: var(--s2); }
.site-foot__bar .ico { font-size: 1rem; }

/* ==========================================================================
   Движение выключено твиком или настройкой системы: страница остаётся целой
   ========================================================================== */

html.motion-off .frame[data-scrub] .frame__head,
html.motion-off .frame[data-scrub] .frame__mascot,
html.motion-off .sec[data-scrub] .sec__mascot { transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-snap-type: none; }
  .progress { display: none; }
  .frame[data-scrub] .frame__head,
  .frame[data-scrub] .frame__mascot,
  .sec[data-scrub] .sec__mascot { transform: none; }
  .mascot-rise picture { clip-path: none; }
  .frame::before { opacity: 1; }
  /* Скраб не работает — пояснения первого кадра просто видны. */
  .fact { opacity: 1; translate: none; }
}

/* Печать: кадры не растягиваем на страницу, снап не нужен. */
@media print {
  html { scroll-snap-type: none; }
  .progress, .hint, .frame__no { display: none; }
  .frame { min-height: 0; padding-block: var(--s6); }
  .fact { opacity: 1; translate: none; }
}
