/* Custom Club - лендинг. Ночная стоянка, печатанная в две краски. */

@font-face{font-family:Mont;src:url(../fonts/mont-400.woff2) format('woff2');font-weight:400;font-display:swap}
@font-face{font-family:Mont;src:url(../fonts/mont-600.woff2) format('woff2');font-weight:600;font-display:swap}
@font-face{font-family:Mont;src:url(../fonts/mont-800.woff2) format('woff2');font-weight:800;font-display:swap}
@font-face{font-family:Signwood;src:url(../fonts/signwood.woff2) format('woff2');font-weight:400;font-display:swap}

:root{
  --night:#0b0c0d;
  --night-2:#111214;
  --night-3:#17191c;
  --edge:#24272b;

  --hot:#f6a129;          /* цвет бренда */
  --hot-deep:#c97c14;
  --ember:#ff8a2b;        /* см. --ember-rgb ниже */
  --glow:#ffd08a;         /* самый яркий, только для свечения */

  --paper:#f4f1ea;
  --smoke:#b3a99b;
  /* #8a8175 давал 4.89:1 на ночном фоне - мало для 13-14px подписей ступеней и подвала */
  --smoke-dim:#9a9184;

  /* граница поля и текст ошибки - разные светлоты: тёмная не набирает 4.5:1 на фоне */
  --bad:#e2603a;
  --bad-text:#f08a63;

  /* rgb-тройки для rgba(): без них смена палитры значила бы правку каждого места вручную */
  --hot-rgb:246,161,41;
  --night-rgb:11,12,13;
  --smoke-rgb:179,169,155;
  --glow-rgb:255,208,138;
  --ember-rgb:255,138,43;

  --ink-hot:#14100a;      /* текст на оранжевом - тёмный, не чёрный */
  --edge-hi:#31353a;      /* граница под курсором */

  --ease-cam:cubic-bezier(.62,0,.18,1); /* кривая пролёта камеры, общая для CSS и app.js */

  --wrap:1180px;
  --r:14px;       /* контейнер */
  --r-ctrl:10px;  /* управление */
  --r-chip:8px;   /* мелкая метка */
}

*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
  margin:0;
  background:var(--night);
  color:var(--paper);
  font-family:Mont,system-ui,sans-serif;
  font-size:17px;
  line-height:1.65;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}
img{max-width:100%;height:auto;display:block}
a{color:inherit}

.wrap{width:min(100% - 40px,var(--wrap));margin-inline:auto}
.wrap--narrow{width:min(100% - 40px,780px)}

.skip{position:absolute;left:-9999px;top:0;background:var(--hot);color:var(--ink-hot);padding:12px 18px;z-index:100;font-weight:600}
.skip:focus{left:12px;top:12px}

/* ---------- зерно трафарета ---------- */
body::after{
  content:"";position:fixed;inset:0;pointer-events:none;z-index:60;opacity:.5;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='.055'/%3E%3C/svg%3E");
}

/* ---------- типографика ---------- */
.h2{
  font-size:clamp(28px,4.4vw,52px);
  font-weight:800;
  line-height:1.08;
  letter-spacing:-.03em;
  margin:0 0 28px;
  text-wrap:balance;
}
.lead{font-size:clamp(17px,1.6vw,20px);color:var(--smoke);max-width:66ch;margin:0 0 20px}
p{max-width:70ch}

.kicker{
  font-size:12px;font-weight:600;letter-spacing:.18em;text-transform:uppercase;
  color:var(--smoke-dim);margin:0 0 14px;
}

/* Уход регистра: оранжевый прогон лёг на доли миллиметра мимо чёрного. */
.print{display:block;position:relative}
.print--hot{color:var(--hot)}
.print--hot::before{
  content:attr(data-ghost);position:absolute;inset:0;
}
/* Свечение смещено вниз (огонь снизу по центру), уход регистра - 2px вправо, как в печати. */
.hero__title .print,.close__title .print{
  text-shadow:0 5px 30px rgba(255,140,50,.26), 2px 2px 0 rgba(var(--ember-rgb),.09);
}
.hero__title .print--hot,.close__title .print--hot{
  text-shadow:0 5px 34px rgba(var(--hot-rgb),.42), 0 2px 14px rgba(var(--glow-rgb),.22), 2px 2px 0 rgba(var(--glow-rgb),.14);
}

/* ---------- кнопки ---------- */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:10px;
  font-family:inherit;font-size:15px;font-weight:600;letter-spacing:.01em;white-space:nowrap;
  padding:15px 26px;border-radius:var(--r-ctrl);border:1.5px solid transparent;
  text-decoration:none;cursor:pointer;transition:transform .18s cubic-bezier(.2,.8,.3,1),background-color .18s,border-color .18s,color .18s,box-shadow .18s,opacity .18s;
}
.btn--hot{background:var(--hot);color:var(--ink-hot);box-shadow:0 10px 26px rgba(var(--hot-rgb),.22),0 2px 4px rgba(0,0,0,.4)}
.btn--hot:hover{background:var(--glow);transform:translateY(-2px)}
.btn--wire{border-color:var(--edge);color:var(--paper);background:rgba(255,255,255,.02)}
.btn--wire:hover{border-color:var(--hot);color:var(--hot);transform:translateY(-2px)}
.btn--ghost{border-color:var(--edge);color:var(--paper);padding:11px 18px;font-size:14px}
.btn--ghost:hover{border-color:var(--hot);color:var(--hot)}
.btn--wide{width:100%;padding:17px 26px}
.btn:focus-visible,a:focus-visible,summary:focus-visible,button:focus-visible,input:focus-visible{
  outline:2px solid var(--hot);outline-offset:3px
}
/* Гасим и hover на disabled - иначе кнопка светлеет под курсором и выглядит рабочей. */
.btn[disabled]{opacity:.5;cursor:not-allowed;transform:none}
.btn--hot[disabled]:hover{background:var(--hot);transform:none}
.btn--wire[disabled]:hover,.btn--ghost[disabled]:hover{border-color:var(--edge);color:var(--paper);transform:none}

/* ---------- шапка ---------- */
/* Шапка липкая везде: 75px от 561px и шире, 67px на телефоне (≤560px, там же уже своя
   верстка шапки). scroll-margin-top держим впритык к реальной высоте (+3px на всякое
   округление) - раньше запас (88px) был заметно больше шапки, и после перехода по якорю
   сверху проглядывал хвост предыдущей секции вместо начала нужной. */
main[id],section[id]{scroll-margin-top:78px}
.topbar{
  position:sticky;top:0;z-index:50;
  display:flex;align-items:center;gap:28px;
  padding:14px max(20px,calc((100vw - var(--wrap))/2));
  background:linear-gradient(to bottom,rgba(var(--night-rgb),.92),rgba(var(--night-rgb),.72) 70%,transparent);
  backdrop-filter:blur(8px);
}
/* логотип ведёт наверх - тап-таргет; высота добирается отступами, не размером знака */
.topbar__logo{display:flex;align-items:center;min-height:44px}
.topbar__logo img{width:172px;height:auto;display:block}
.topbar__nav{display:flex;gap:24px;margin-left:auto;font-size:14px;font-weight:600}
/* padding-block добирает тап-цель до 24px: сама строка 14px даёт только 23 */
.topbar__nav a{color:var(--smoke);text-decoration:none;transition:color .18s;padding-block:6px}
.topbar__nav a:hover{color:var(--hot)}
/* Видна только там, где .topbar__nav становится панелью (≤900px) */
.topbar__toggle{display:none}

/* ---------- 1. первый экран ---------- */
.hero{position:relative;min-height:clamp(600px,92svh,860px);display:flex;align-items:center;padding:64px 0 84px;overflow:hidden}
.hero__night{position:absolute;inset:0;z-index:0}
/* Пересобрана из оригинала брендбука (Элементы_карта.png, 3508x1800 без потерь) - прежний
   файл был пережатой копией с блочными артефактами 8x8, заметными при растяжении до 120%
   ширины секции. Цвет/контраст запечены в файл, не в CSS-фильтр. Узкому экрану - файл 900px. */
.hero__topo{
  position:absolute;inset:-10%;
  background:url(../img/topo.webp?v=2) center/cover no-repeat;
  opacity:.16;
  mask-image:radial-gradient(120% 90% at 50% 78%,#000 20%,transparent 72%);
}
/* По дорогам изредка проходят экспедиции клуба - единственное движение первого экрана.
   Маска дорог шире, чем у рельефа: дорога должна доходить до края кадра, карте можно
   истаять раньше. */
.routes{
  position:absolute;inset:-10%;width:120%;height:120%;pointer-events:none;
  mask-image:radial-gradient(120% 90% at 50% 78%,#000 34%,transparent 92%);
}
/* Пунктир картографической тонкости с тёмной окантовкой - иначе тонет в горизонталях
   рельефа той же толщины и светлоты. */
.route__road{
  fill:none;stroke:rgba(var(--hot-rgb),.26);stroke-width:1.4;
  stroke-dasharray:5 8;stroke-linecap:round;
  filter:drop-shadow(0 0 2px rgba(var(--night-rgb),.95));
}
/* Два огня с разрывом - головная машина и та, что держит дистанцию. Длины в единицах
   viewBox, не в долях пути, иначе на длинной дороге огни растянулись бы вдвое. Хвост 3000
   глушит повтор узора - не даёт второй колонне выехать следом. Въезд и выезд всегда за краем
   кадра, без зажигания-гашения. */
.route__run{
  fill:none;stroke:var(--glow);stroke-width:2.6;stroke-linecap:round;
  stroke-dasharray:26 10 14 3000;stroke-dashoffset:60px;
  filter:drop-shadow(0 0 5px rgba(255,170,70,.9));
  animation:convoy var(--dur,19s) linear var(--delay,0s) infinite;
}
/* Ход - 44% цикла, остальное тишина: экспедиции идут изредка. Скорость = --len / (0.44 * --dur).
   Дальняя дорога медленнее ближней - расстояние читается темпом. Периоды взаимно просты,
   чтобы колонны не выходили парами. */
.route--pass{--len:1232px;--dur:26s;--delay:11s}
.route--near{--len:1385px;--dur:19s;--delay:0s}
.route--phone{--len:423px;--dur:11s;--delay:3s;display:none}
/* верхняя дорога дальше: тоньше, тусклее и медленнее */
.route--pass .route__run{stroke-width:2.2;opacity:.82}
.route--pass .route__road{stroke:rgba(var(--hot-rgb),.21)}
@keyframes convoy{
  0%   {stroke-dashoffset:60px}
  44%  {stroke-dashoffset:calc(-1 * (var(--len) + 60px))}
  100% {stroke-dashoffset:calc(-1 * (var(--len) + 60px))}
}
/* Замеренные доли пути по кривой серпантина: разворот 0.46-0.537, торможение с 0.42, разгон
   до 0.60. Ступени вместо плавной кривой - иначе колонна встала бы намертво на стыке с
   горизонтальной касательной. */
.route--pass .route__run{animation-name:convoy-pass}
@keyframes convoy-pass{
  0%   {stroke-dashoffset:60px}
  17%  {stroke-dashoffset:calc(var(--len) * -.42)}  /* подход к развороту */
  19%  {stroke-dashoffset:calc(var(--len) * -.47)}  /* сброс до 91% хода */
  25%  {stroke-dashoffset:calc(var(--len) * -.55)}  /* сам разворот: 48% хода */
  28%  {stroke-dashoffset:calc(var(--len) * -.6)}   /* разгон, 61% */
  44%  {stroke-dashoffset:calc(-1 * (var(--len) + 60px))}
  100% {stroke-dashoffset:calc(-1 * (var(--len) + 60px))}
}
/* за экраном первый экран не тикает */
.hero[data-off="1"] .route__run,.hero[data-off="1"] .sparks span{animation-play-state:paused}

/* Огонь - источник света снизу по центру. */
.hero__fire{
  position:absolute;left:50%;bottom:-22%;translate:-50% 0;
  width:min(1100px,120vw);aspect-ratio:1/.72;
  background:
    radial-gradient(closest-side,rgba(var(--glow-rgb),.30),rgba(var(--ember-rgb),.16) 42%,transparent 74%),
    radial-gradient(closest-side,rgba(var(--hot-rgb),.22),transparent 68%);
  filter:blur(12px);
}
.hero__inner{position:relative;z-index:2;width:min(100% - 40px,var(--wrap));margin-inline:auto}
/* Заголовок держим в три строки: на 82px он ломался на шесть и выдавливал кнопки за экран.
   Размер и трекинг общие с .close__title - открывающий и закрывающий эмоциональный акцент
   страницы, одна и та же роль, а не два случайно разошедшихся числа. */
.hero__title,.close__title{
  font-size:clamp(32px,4.9vw,64px);font-weight:800;line-height:1.06;letter-spacing:-.035em;
}
.hero__title{margin:0 0 24px;max-width:21ch}
/* Тот же размер, что у .lead - разными их делала только история правок. Своя max-width:
   колонка первого экрана уже. */
.hero__lead{font-size:clamp(17px,1.6vw,20px);color:var(--smoke);max-width:54ch;margin:0 0 30px}
.hero__actions{display:flex;gap:14px;flex-wrap:wrap}
.hero__slogan{
  font-family:Signwood,Mont,cursive;font-size:clamp(18px,2vw,26px);line-height:1.2;
  color:var(--hot);opacity:.85;margin:34px 0 0;letter-spacing:.02em;
}

/* Искры: единственный непрерывный мотив страницы. Поднимаются от огня и гаснут. */
.sparks{position:absolute;left:50%;bottom:6%;translate:-50% 0;width:min(760px,90vw);height:60%;pointer-events:none}
.sparks span{
  position:absolute;bottom:0;width:3px;height:3px;border-radius:50%;
  background:var(--glow);box-shadow:0 0 8px 1px rgba(255,160,60,.7);
  opacity:0;animation:spark 7s ease-out infinite;
}
.sparks span:nth-child(1){left:12%;animation-delay:0s}
.sparks span:nth-child(2){left:26%;animation-delay:1.1s}
.sparks span:nth-child(3){left:38%;animation-delay:2.4s}
.sparks span:nth-child(4){left:47%;animation-delay:.6s}
.sparks span:nth-child(5){left:56%;animation-delay:3.2s}
.sparks span:nth-child(6){left:68%;animation-delay:1.8s}
.sparks span:nth-child(7){left:79%;animation-delay:4.1s}
.sparks span:nth-child(8){left:88%;animation-delay:2.9s}
.sparks span:nth-child(9){left:33%;animation-delay:5.3s}
@keyframes spark{
  0%{opacity:0;transform:translate(0,0) scale(.6)}
  12%{opacity:.9}
  70%{opacity:.5}
  100%{opacity:0;transform:translate(var(--dx,18px),-460px) scale(.2)}
}
.sparks span:nth-child(even){--dx:-26px}
.sparks span:nth-child(3n){--dx:34px}

/* ---------- 2. о клубе ---------- */
.club{padding:clamp(80px,10vw,140px) 0;background:var(--night-2);border-top:1px solid var(--edge)}
.club__grid{display:grid;grid-template-columns:1.05fr .95fr;gap:clamp(30px,5vw,72px);align-items:start}
.club__text p{margin:0 0 18px}
.club__punch{
  font-size:clamp(19px,2vw,24px);font-weight:600;line-height:1.4;color:var(--paper);
  border-left:2px solid var(--hot);padding-left:20px;margin-top:28px;text-wrap:balance;
}
.who{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:2px}
.who li{
  display:flex;gap:16px;align-items:baseline;
  padding:18px 20px;background:var(--night-3);border-radius:var(--r-ctrl);
  font-size:16px;line-height:1.5;color:var(--paper);
}
.who__n{
  font-weight:800;font-size:13px;color:var(--hot);letter-spacing:.06em;flex:0 0 auto;
  font-variant-numeric:tabular-nums;
}

/* ---------- 3. что даёт участие ---------- */
.perks{padding:clamp(80px,10vw,140px) 0}
/* Реестр, не карточки с иконками - каждая строка читается записью в журнале. */
.ledger{margin:0;display:flex;flex-direction:column}
.ledger__row{
  display:grid;grid-template-columns:minmax(220px,.9fr) 1.7fr;gap:clamp(16px,3vw,48px);
  padding:26px 0;border-top:1px solid var(--edge);align-items:baseline;
}
/* Своей нижней границы у последней строки нет нарочно: секция .life сразу следом уже
   начинается собственным border-top (как club, apply - общий узор границ между секциями).
   Раньше здесь стояли обе линии подряд - граница реестра и граница секции, разделённые
   пустым паддингом с обеих сторон - читалось двумя разделителями пустоты. */
.ledger dt{font-size:clamp(18px,1.9vw,23px);font-weight:800;letter-spacing:-.02em;line-height:1.2}
.ledger dd{margin:0;color:var(--smoke);font-size:16px;line-height:1.6;max-width:62ch}
.ledger__row--hot dt{color:var(--hot)}
.ledger__row--hot dd{color:var(--paper)}
/* Фото вклеено внутрь dd обтеканием, не третьей колонкой грида - отдельная колонка сужала
   dd только в этой строке, и правый край текста переставал совпадать с остальными пятью
   записями реестра. Обтекание держит dt/dd на тех же долях грида, что у всех строк - меняется
   только то, как заворачивается текст внутри уже привычной ширины dd. */
.ledger__exhibit{
  float:right;margin:0 0 12px 20px;width:300px;
  border:1px solid var(--edge);border-radius:var(--r);overflow:hidden;background:var(--night-3);
}
.ledger__exhibit img{display:block;width:100%;aspect-ratio:899/506;object-fit:cover}
@media (max-width:560px){
  .ledger__exhibit{float:none;width:100%;margin:0 0 14px}
}
/* Правее и крупнее только там, где снаружи .wrap физически есть запас. .wrap = min(100vw-40px,
   1180px): до 1220px поля вокруг него ровно 20px и не растут, вылезать там некуда - только за
   1220px поля начинают расти вместе с экраном. На 1300px поле уже ≥60px - хватает на 50px
   вылета с запасом, ниже этой ширины риск горизонтального скролла. */
@media (min-width:1300px){
  .ledger__exhibit{width:360px;margin:0 -50px 12px 24px}
}
/* Не таблица: обещала бы полноту прайса, которого у клуба нет. */
.price{display:flex;flex-direction:column;gap:6px;margin:16px 0 10px;max-width:320px}
.price__row{display:flex;align-items:baseline;justify-content:space-between;gap:16px;
  padding:9px 12px;border-radius:var(--r-chip);background:var(--night-3);font-size:15px}
.price__row b{font-weight:800;font-variant-numeric:tabular-nums;letter-spacing:-.01em}
.price__row--club{background:rgba(var(--hot-rgb),.12);color:var(--paper)}
.price__row--club b{color:var(--hot)}
.price__note{display:block;font-size:13px;color:var(--smoke-dim);max-width:52ch}

/* ---------- 4. жизнь клуба ---------- */
.life{padding:clamp(80px,10vw,140px) 0;background:var(--night-2);border-top:1px solid var(--edge)}
/* Два крыла клуба - не карточки выбора: строка со знаком и текстом, разделённая линией,
   читается частями одного целого. */
.wings__intro{margin:0 0 22px;color:var(--smoke);max-width:62ch}
.wings{display:grid;grid-template-columns:1fr 1fr;gap:clamp(24px,4vw,56px);
  margin:0 0 clamp(34px,4vw,54px);padding-top:26px;border-top:1px solid var(--edge)}
.wing{display:flex;gap:16px;align-items:flex-start}
/* 49px, не 46: холст 220x220 получил поле (хвост банданы трэвла упирался в край и срезался),
   сами знаки внутри стали на 6% меньше - ширина компенсирует, чтобы размер на экране не изменился. */
.wing__mark{width:49px;height:auto;flex:0 0 auto;opacity:.92}
/* clamp, не фиксированный px: единственный заголовок компонента на странице, у которого
   размер не откликался на ширину экрана - остальные (ledger dt, step__name, qa summary)
   давно на этой схеме. */
.wing__name{margin:0 0 6px;font-size:clamp(16px,1.4vw,18px);font-weight:800;letter-spacing:.02em;text-transform:uppercase}
.wing p{margin:0;color:var(--smoke);font-size:15px;line-height:1.6;max-width:46ch}
/* ============ Лента кадров: партиями по 3 ============
   Раньше была бесконечная лента со скроллом - следующий кадр всегда торчал по краю экрана
   (читалось незаконченным списком), а сама лента специально уходила за правый край монитора,
   выбиваясь из общего центрированного макета страницы (везде остальное - в ширине .wrap).
   Теперь блок остаётся в этой же ширине, показывает ровно --per-page карточек, следующая
   партия не видна вообще. Перелистывание - transform, не нативный скролл: тот конфликтовал
   со scroll-snap при программном вызове кнопками. Ширину карточки в пикселях (--frame-w)
   считает JS от ширины видимой области и --per-page - в чистом CSS её взять неоткуда,
   проценты у flex-детей считаются от ширины родителя-трека, а не видимой области. */
.album{position:relative;--per-page:3}
/* Низкий экран (13") - дефицит высоты, не ширины: поджимаем поля секции, чтобы заголовок
   влез над кадрами. */
@media (max-height:860px){
  .life{padding-block:clamp(56px,7vw,92px)}
}
.album__viewport{overflow:hidden}
/* touch-action:pan-y - без него браузер на реальном телефоне какое-то время решает, жест
   горизонтальный или это скролл страницы, и может отдать его себе раньше, чем сработает
   наш touchend. Свайп в JS уже был (touchstart/touchend, порог 40px), но без этой строки
   мог не срабатывать именно на живом устройстве - в эмуляции CDP разницы не видно. */
.album__track{display:flex;gap:14px;transition:transform .4s var(--ease-cam);touch-action:pan-y}
/* Карточки одной ширины, не по пропорции кадра, как в прежней ленте - три ровные колонки,
   фото обрезает object-fit:cover. */
.frame{
  margin:0;position:relative;border-radius:var(--r);overflow:hidden;background:var(--night-3);
  flex:0 0 var(--frame-w, 300px);width:var(--frame-w, 300px);
  display:flex;flex-direction:column;
}
.frame__img{display:block;width:100%;aspect-ratio:4/3;object-fit:cover}

/* Указатель занятой части слева, кнопки справа - к ним тянется рука. Указатель показывает
   долю видимых карточек от общего числа партиями, не "кадр 3 из 6". */
.album__bar{display:flex;align-items:center;gap:10px;margin-top:16px}
.album__nav{
  width:44px;height:44px;flex:0 0 auto;display:grid;place-items:center;
  background:none;border:1.5px solid var(--edge);border-radius:var(--r-ctrl);
  color:var(--paper);cursor:pointer;
  transition:border-color .18s,color .18s,opacity .18s;
}
.album__nav svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.album__nav:hover:not([disabled]){border-color:var(--hot);color:var(--hot)}
.album__nav[disabled]{opacity:.32;cursor:default}
.album__rail{flex:1 1 auto;height:2px;background:var(--edge);border-radius:2px;position:relative;overflow:hidden}
/* scaleX вместо width: длина и позиция едут одним трансформом, без пересчёта раскладки. */
.album__lens{
  position:absolute;top:0;bottom:0;left:0;width:100%;background:var(--hot);border-radius:2px;
  transform-origin:left center;
  transition:transform .3s var(--ease-cam);
}
/* Плашка «черновой кадр» скрыта по просьбе Кристины 13.08.2026 - разметка (data-place,
   data-todo на каждой figure) осталась нетронутой, это по-прежнему временные рендеры и
   их всё ещё планируют заменить на живые кадры, просто без служебной пометки для
   посетителя. Вернуть - убрать display:none. */
.frame::before{
  content:attr(data-place);position:absolute;top:12px;left:12px;z-index:2;
  font-size:11px;font-weight:600;letter-spacing:.12em;text-transform:uppercase;
  color:var(--hot);background:rgba(var(--night-rgb),.72);padding:5px 9px;border-radius:var(--r-chip);
  display:none;
}
/* Плашка черновика приглушена: она служебная и не должна спорить с подписью кадра */
.frame--draft::before{color:var(--smoke-dim);background:rgba(var(--night-rgb),.82)}
.frame figcaption{
  padding:16px 18px 18px;font-weight:800;font-size:17px;letter-spacing:-.01em;
}
.frame figcaption span{display:block;font-weight:400;font-size:14px;color:var(--smoke);margin-top:4px;letter-spacing:0}

/* ---------- 5. круг участия ---------- */
/* Низ короче верха - лишний отступ отдан секции «Лучшие истории», которой при той же
   ширине не хватало высоты. */
.circle{padding:clamp(80px,10vw,140px) 0 clamp(60px,7vw,100px);position:relative;overflow:hidden}
/* Больше воздуха под заголовком, чем стандартные 28px у .h2: верхняя карточка списка -
   всегда «Офицер клуба» (.step--full, см. ниже), её кегль и оранжевый цвет читаются вторым
   заголовком, а не первым пунктом списка - без запаса она прилипает к настоящему h2. */
.circle .h2{margin-bottom:clamp(32px,6vw,80px)}
/* Список уровней держится верхом колонки, не центрируется по высоте: иначе раскрытие
   уровня или разная длина текста двигали бы его по вертикали. */
.circle__stage{--stage-gap:clamp(30px,5vw,70px);position:relative;
  display:grid;grid-template-columns:minmax(280px,420px) 1fr;gap:var(--stage-gap);align-items:stretch}
/* ============ Склон вместо круга ============
   Круги вокруг костра читались рассадкой: гость с краю, Full Patch у огня. Склон говорит
   другое - человек в начале пути, а не «ещё не заслужил». При выборе уровня камера подъезжает
   к отметке; слои двигаются с разной силой, поэтому ближний склон обгоняет дальнюю гряду -
   это и даёт ощущение пролёта, а не зума картинки.
   Гора выходит за левый край колонки к краю экрана (нет рамки и скруглений - это сцена, не
   картинка в рамке), тянется во всю высоту рядом со списком уровней. Пропорция блока равна
   пропорции viewBox, поэтому сцена заполняет его без пустот снизу и масштаб не зависит от
   того, какой уровень раскрыт. Обрезка обязательна - без неё рисунок вылезает за карточки
   и вниз на телефоне. */
.peak{
  position:relative;align-self:start;aspect-ratio:508/480;
  overflow:hidden;contain:layout paint;
  margin-left:calc(-1 * max(20px, (100vw - var(--wrap)) / 2));
  /* Справа сцена занимает и жёлоб сетки - между рисунком и карточками не должно быть полосы
     пустоты. Гору при этом не продолжаем, шире становится только окно: масштаб задаёт
     высота (slice), поэтому открывается уже нарисованный склон, а не дорисованный. */
  width:calc(100% + max(20px, (100vw - var(--wrap)) / 2) + var(--stage-gap, 0px));
}
/* Сцена гаснет градиентом к левому краю и коротким сходом справа у карточек - без этого
   горизонтали обрывались бы резким срезом рамки. */
.peak::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(to right,var(--night) 0,rgba(var(--night-rgb),0) 22%,
    rgba(var(--night-rgb),0) calc(100% - 26px),var(--night) 100%);
}
.peak__svg{display:block;width:100%;height:100%;overflow:visible}
.peak__layer{
  transform:translate(var(--tx,0px),var(--ty,0px)) scale(var(--sc,1));
  transform-origin:125px 300px;
  transition:transform .9s var(--ease-cam);
}
/* Дальняя гряда бледнее и тоньше: расстояние читается воздухом, а не размером. */
.ridge{fill:none;stroke:rgba(var(--smoke-rgb),.16);stroke-width:1.1;stroke-linecap:round}
.ridge--2{stroke:rgba(var(--smoke-rgb),.12)}
/* самые дальние вершины почти растворены в ночи - держат кадр, но не спорят с горой */
.ridge--3{stroke:rgba(var(--smoke-rgb),.08)}
/* Силуэт склона - единственная сплошная линия рисунка, всё остальное тише. */
.slope{fill:none;stroke:rgba(var(--hot-rgb),.5);stroke-width:1.6;stroke-linecap:round;stroke-linejoin:round}
/* Бергштрихи - картографический приём для крутизны: гуще и длиннее там, где падение резче,
   ближе к камере проявляются сильнее. */
.hatch{fill:none;stroke:rgba(var(--smoke-rgb),.26);stroke-width:.9;stroke-linecap:round;
  transition:stroke .5s ease}
.peak[data-near="1"] .hatch{stroke:rgba(var(--smoke-rgb),.42)}
/* Передний хребет держит масштаб - без него гора читалась бы холмом. */
.rocks{fill:none;stroke:rgba(var(--smoke-rgb),.3);stroke-width:1.1;stroke-linejoin:round}
.rocks--near{stroke:rgba(var(--smoke-rgb),.2)}
.rocks--far{stroke:rgba(var(--smoke-rgb),.13)}

/* Насечка уровня подходит к отметке слева - высота вместо цифры */
.level{fill:none;stroke:rgba(var(--smoke-rgb),.14);stroke-width:1;stroke-dasharray:2 6}
.level--top{stroke:rgba(var(--hot-rgb),.2)}

/* Тропа - пунктир поверх склона. Пройденная часть сплошная и горячая, дорисовывается до
   выбранной отметки, поэтому уровень читается ещё и длиной пути. */
.trail{fill:none;stroke:rgba(var(--hot-rgb),.4);stroke-width:1.3;stroke-dasharray:4 7;stroke-linecap:round}
.trail--done{
  stroke:var(--hot);stroke-width:1.5;stroke-dasharray:1000;stroke-dashoffset:1000;
  transition:stroke-dashoffset .9s var(--ease-cam);
}
/* Свечение тропы - 3 слоя нарастающей ширины и убывающей прозрачности вместо блюра: один
   слой без размытия читался бы резким ободком. Раньше был drop-shadow - дорогой на пути в
   1400 единиц, перерисовывался на каждом кадре пролёта камеры; рисованные слои растеризуются
   как обычные линии. */
.trail--glow{
  fill:none;stroke-linecap:round;stroke-dasharray:1000;stroke-dashoffset:1000;
  transition:stroke-dashoffset .9s var(--ease-cam);
}
.trail--glow-3{stroke:rgba(var(--hot-rgb),.05);stroke-width:12}
.trail--glow-2{stroke:rgba(var(--hot-rgb),.09);stroke-width:7}
.trail--glow-1{stroke:rgba(var(--hot-rgb),.16);stroke-width:3.2}

/* Клик по отметке = выбор уровня. Невидимый круг ловит клик - сама точка (7 единиц радиусом)
   на телефоне даёт <11px, слишком мало для тапа. Остальная сцена клики пропускает сквозь
   себя, иначе съедала бы их у карточек, заходя в жёлоб сетки. */
.peak__svg{pointer-events:none}
.mark{pointer-events:auto;cursor:pointer}
.mark__hit{fill:transparent}

/* Тёмная обводка идёт под буквами (paint-order), поэтому подпись читается и там, где
   попадает на бергштрихи. .mark__tag гасит зум камеры: при наезде растёт склон, а не буквы;
   берёт ту же длительность и кривую, что слои, иначе подпись доезжала бы до места отдельно
   от своей точки. */
.mark__tag{
  transform:scale(var(--inv,1));transform-origin:0 0;
  transition:transform var(--cam-dur,900ms) var(--cam-ease,var(--ease-cam));
}
.mark__name{
  font-family:Mont,system-ui,sans-serif;font-size:13px;font-weight:600;letter-spacing:.09em;
  text-transform:uppercase;text-anchor:end;
  fill:var(--smoke-dim);paint-order:stroke;stroke:rgba(var(--night-rgb),.9);stroke-width:3.5px;
  transition:fill .45s ease;
}
/* пройденные подписаны светлее, выбранная - огнём: тот же язык, что у самих точек.
   Офицер (5) стоит выше вершины и не сдвигает гору - при его выборе все четыре метки
   склона читаются пройденными, как и при выборе вершины. */
.peak[data-active="2"] .mark--1 .mark__name,
.peak[data-active="3"] .mark--1 .mark__name,
.peak[data-active="3"] .mark--2 .mark__name,
.peak[data-active="4"] .mark--1 .mark__name,
.peak[data-active="4"] .mark--2 .mark__name,
.peak[data-active="4"] .mark--3 .mark__name,
.peak[data-active="5"] .mark--1 .mark__name,
.peak[data-active="5"] .mark--2 .mark__name,
.peak[data-active="5"] .mark--3 .mark__name,
.peak[data-active="5"] .mark--4 .mark__name{fill:var(--smoke)}
.peak[data-active="1"] .mark--1 .mark__name,
.peak[data-active="2"] .mark--2 .mark__name,
.peak[data-active="3"] .mark--3 .mark__name,
.peak[data-active="4"] .mark--4 .mark__name{fill:var(--hot)}
/* hover: подпись светлеет до бумажного цвета, точка светлеет обводкой - без прыжка, иначе
   на склоне это читалось бы ошибкой отрисовки */
.mark:hover .mark__name{fill:var(--paper)}
.mark:hover .mark__dot{stroke:var(--glow)}
/* Подпись вершины стоит под точкой по центру, не сбоку от края, как у остальных трёх -
   разметка объясняет почему (флагшток). */
.mark--4 .mark__name{text-anchor:middle}

/* Непройденная отметка - тёмный кружок с холодной обводкой, заливка не сплошная: конец
   горячей линии приходит ровно в центр отметки, и глухой диск цвета фона резал тропу. */
.mark__dot{
  fill:rgba(var(--night-rgb),.55);stroke:var(--smoke-dim);stroke-width:1.6;
  transition:fill .5s ease,stroke .5s ease,r .5s var(--ease-cam);
}
/* Пройденные отметки горят - тропа идёт сквозь зажжённые точки, не дыры. Комбинации
   перечислены руками: CSS не умеет «все младше активного». */
.peak[data-active="2"] .mark--1 .mark__dot,
.peak[data-active="3"] .mark--1 .mark__dot,
.peak[data-active="3"] .mark--2 .mark__dot,
.peak[data-active="4"] .mark--1 .mark__dot,
.peak[data-active="4"] .mark--2 .mark__dot,
.peak[data-active="4"] .mark--3 .mark__dot,
.peak[data-active="5"] .mark--1 .mark__dot,
.peak[data-active="5"] .mark--2 .mark__dot,
.peak[data-active="5"] .mark--3 .mark__dot,
.peak[data-active="5"] .mark--4 .mark__dot{
  fill:var(--hot-deep);stroke:var(--hot);
}
/* Выбранная отметка горит ярче пройденных и обзаводится ореолом: на ней сейчас человек. */

.mark--4 .mark__dot{stroke:rgba(var(--hot-rgb),.6)}

/* Ореол выбранной отметки - круг с радиальным градиентом, не drop-shadow: фильтр внутри
   слоя, который камера двигает и масштабирует, переразмывался на каждом кадре пролёта -
   это была самая дорогая вещь в сцене. */
.mark__halo{
  fill:url(#peakGlow);opacity:0;pointer-events:none;
  transition:opacity .45s ease;
}
.peak[data-active="1"] .mark--1 .mark__halo,
.peak[data-active="2"] .mark--2 .mark__halo,
.peak[data-active="3"] .mark--3 .mark__halo,
.peak[data-active="4"] .mark--4 .mark__halo{opacity:.85}

/* Выбранная отметка: заливка горячим, светлая обводка, чуть крупнее остальных. */
.peak[data-active="1"] .mark--1 .mark__dot,
.peak[data-active="2"] .mark--2 .mark__dot,
.peak[data-active="3"] .mark--3 .mark__dot,
.peak[data-active="4"] .mark--4 .mark__dot{
  fill:var(--hot);stroke:var(--glow);r:8;
}

/* На вершине флаг вместо костра, светится ореолом под ним - без фильтра. */
.flag__pole{fill:none;stroke:var(--glow);stroke-width:1.6;stroke-linecap:round;opacity:.9}
.flag{fill:var(--hot)}
/* Флаг появляется только на «Участник клуба» - вырастает из отметки коротким движением
   вверх. На Офицере гаснет: у него уже своя звезда в небе, флаг не должен оставаться
   зажжённым, будто это тот же статус. */
.summit-flag{
  opacity:0;transform:translateY(9px) scaleY(.55);transform-origin:0 0;
  transition:opacity .5s ease .25s,transform .7s cubic-bezier(.22,1,.28,1) .25s;
}
.peak[data-active="4"] .summit-flag{opacity:1;transform:translateY(0) scaleY(1)}

.mark--4 .mark__dot{stroke:rgba(var(--hot-rgb),.6)}

/* ---------- звезда офицера: небо над вершиной ----------
   Не ещё одна метка на склоне - точка выше горы, вне тропы. Спарк вместо круглой точки,
   чтобы читаться звездой. Ореол тот же .mark__halo, что у остальных отметок (один язык
   на всю сцену), только у звезды он ещё и распускается по масштабу при зажигании.
   17 фоновых спарков (.sky-star) сидят в той же .peak__layer--near, что гора и сама звезда -
   едут и масштабируются с горой всегда одинаково, поэтому видны на любом уровне без всякой
   привязки к [data-active]: наложений с горой быть не может, координаты выбраны выше её
   силуэта в той же системе отсчёта, что и сам рельеф.
   Четыре яруса вместо трёх - звёздная величина настоящего неба сильно смещена к тусклым,
   ярких звёзд всегда мало. Классовое значение - базовая яркость яруса, конкретное число
   у каждой звезды переопределено inline в разметке (чуть светлее/темнее соседей по ярусу,
   как у настоящих звёзд одной видимой величины). */
.sky-star{fill:var(--smoke-dim);pointer-events:none;opacity:.4}
.sky-star--sm{opacity:.58}
.sky-star--md{fill:var(--smoke);opacity:.8}
.sky-star--lg{fill:var(--smoke);opacity:1}
.star__spark{fill:var(--smoke-dim);transition:fill .6s ease;transform-origin:center;transform-box:fill-box}
.mark--star:hover .star__spark{fill:var(--paper)}
.peak[data-active="5"] .mark--star .star__spark{fill:var(--hot)}
.peak[data-active="5"] .mark--star .mark__name{fill:var(--hot)}
.mark--star .mark__halo{
  transform:scale(.45);transform-origin:0 0;
  transition:opacity .6s ease,transform 1.1s cubic-bezier(.22,1,.28,1);
}
/* Дыхание зажжённой звезды: халo и сам спарк пульсируют одним ритмом (3.2s), но халo -
   заметнее (глубина .85→1, масштаб до 1.16), спарк - едва (до 1.06), иначе острые лучи
   формы при таком же размахе выглядели бы дёрганно, не мягко. animation-delay ставит
   старт цикла чуть позже конца transition роста (1.1s) - дыхание подхватывает ровно там,
   где кончается появление звезды, без видимого скачка. */
.peak[data-active="5"] .mark--star .mark__halo{
  opacity:.85;transform:scale(1);
  animation:star-breathe-halo 3.2s ease-in-out 1.2s infinite;
}
.peak[data-active="5"] .mark--star .star__spark{
  animation:star-breathe-spark 3.2s ease-in-out 1.2s infinite;
}
@keyframes star-breathe-halo{
  0%,100%{transform:scale(1);opacity:.85}
  50%{transform:scale(1.16);opacity:1}
}
@keyframes star-breathe-spark{
  0%,100%{transform:scale(1)}
  50%{transform:scale(1.06)}
}

/* ============ Широкий экран: гора становится сценой всей секции ============
   Пока гора стояла колонкой слева, вершина приходилась на треть ширины страницы. Здесь сцена
   уходит во всю ширину окна, карточки садятся на неё панелью справа, вершина выходит примерно
   на середину страницы. Высота сцены считается от ширины окна, не от списка, поэтому масштаб
   не прыгает при переключении уровня; список получает нижнюю границу через min-height, так
   что пустоты под сценой не остаётся. */
@media (min-width:1100px){
  .circle__stage{
    /* clamp вместо фиксированных 440px (тексты уровней складывались в узкий столбик):
       626px на 1360, потолок 700px держит строку в комфортной длине. */
    --cards:clamp(440px, 48vw, 700px);
    /* Поле от карточек до края экрана - раньше колонка стояла по краю сетки, и справа
       оставалась пустая треть монитора. */
    --gutter-r:40px;
    margin-right:calc(var(--gutter-r) - max(20px, (100vw - var(--wrap)) / 2));
    grid-template-columns:1fr var(--cards);
    /* Раньше --peak-h считался от ширины окна (100vw*480/1280) - список карточек справа
       от ширины НЕ зависит вообще, его высота держится на количестве и длине текста
       открытой карточки (~672px у короткого «Гость», ~742px у «Офицер клуба», самого
       длинного). Из-за этого несовпадения формулы на широких мониторах (2560px) гора
       улетала за 1000px+, оставляя пустоту под списком (это чинили потолком в 700px), а
       на средних десктопных ширинах (1280-1470px, где формула ещё не в потолке) выходило
       ровно наоборот - гора получалась короче списка на 30-130px, пустой фон вылезал
       сбоку от нижней части карточек. Оба симптома - следствие одной причины: высота
       горы была завязана не на то, с чем её реально нужно было совмещать.
       Починено тем, что --peak-h вообще перестал зависеть от ширины и держится вплотную к
       высоте самого длинного списка на любом десктопном экране: 740px - высота
       «Офицер клуба» (742px) с рождения помещается, 90vh - страховка на случай
       невысокого окна браузера. Зазор между горой и списком проверен вживую на
       1100-2560px (от 0 до +88px, всегда гора ↔ список, нигде не переворачивается).
       На узкой стороне диапазона (1100px, где картинке физически достаётся ~530px
       ширины при 740px высоты) гора становится заметно уже собственной высоты и
       сильнее обрезается/зумится по горизонтали, чем раньше - это не ломает активную
       отметку текущего уровня (формула camera() в app.js всегда ставит её ровно в
       целевую точку кадра независимо от ширины контейнера, это гарантия самой
       математики трансформа, не что-то, что можно случайно сломать высотой), но
       периферийные декоративные детали (дальние звёзды, соседние отметки) на этой
       ширине уходят чуть глубже за края, чем раньше.
       --peak-drop опускает саму гору (просьба Кристины - раньше гора стояла впритык к
       заголовку), доля от её высоты, но с потолком в 20px - выше он спорит со второй
       просьбой (звезда «Офицер клуба» вровень с заголовком карточки справа, см. ty
       уровня 5 в CAM в app.js): 20px - минимум, при котором гора ещё отделена от
       заголовка секции, но вершина звёздного неба не проваливается далеко под заголовок
       карточки. min-height учитывает высоту горы плюс этот отступ, иначе сцена вылезала
       бы за низ строки грида. */
    --peak-h:min(90vh, 740px);
    --peak-drop:min(calc(var(--peak-h) * .3), 20px);
    min-height:calc(var(--peak-h) + var(--peak-drop));
    align-items:start;
  }
  .peak{
    position:absolute;top:var(--peak-drop);z-index:0;
    left:calc(-1 * max(20px, (100vw - var(--wrap)) / 2));
    /* Сцена кончается там, где начинаются карточки - иначе они читались бы стоящими
       посреди картинки. Высота считается от ширины ОКНА, не блока - она задаёт масштаб,
       кадр обрезается по горизонтали, но гора остаётся того же размера. */
    width:calc(100vw - var(--cards) - var(--gutter-r));
    height:var(--peak-h);
    aspect-ratio:auto;margin-left:0;
  }
  /* справа сцена уходит под карточки, гасить её там не нужно - гаснет только левый край */
  .peak::after{background:linear-gradient(to right,var(--night) 0,rgba(var(--night-rgb),0) 18%)}
  .steps{position:relative;z-index:1;grid-column:2}
}

.steps{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:10px;counter-reset:s}
.step{border:1px solid var(--edge);border-radius:var(--r);background:var(--night-2);overflow:hidden;transition:border-color .2s}
.step[data-open="1"]{border-color:rgba(var(--hot-rgb),.5)}
.step__head{
  width:100%;display:flex;align-items:center;gap:14px;
  background:none;border:0;padding:20px 22px;cursor:pointer;color:inherit;font-family:inherit;text-align:left;
}
.step__dot{width:9px;height:9px;border-radius:50%;background:var(--smoke-dim);flex:0 0 auto;transition:background-color .2s,box-shadow .2s}
.step[data-open="1"] .step__dot{background:var(--hot);box-shadow:0 0 12px rgba(var(--hot-rgb),.8)}
.step__name{font-size:clamp(17px,1.8vw,21px);font-weight:800;letter-spacing:-.01em;line-height:1.25}
/* Раскрытие анимируется grid-template-rows 0fr->1fr: height:auto не умеет переход, max-height
   с придуманным потолком даёт рваный конец. Контент остаётся в потоке, поэтому читалка его
   видит, а inert из JS убирает свёрнутое из фокуса. */
.step__body{display:grid;grid-template-rows:0fr;color:var(--smoke);
  transition:grid-template-rows .34s cubic-bezier(.16,1,.3,1)}
.step[data-open="1"] .step__body{grid-template-rows:1fr}
/* padding-bottom не на обёртке (не схлопывается со строкой grid, оставлял 22px пустоты
   под каждым закрытым уровнем), а на последнем потомке. */
.step__inner{overflow:hidden;min-height:0;padding:0 22px 0 45px;
  opacity:0;transition:opacity .26s ease .04s}
.step__inner > :last-child{margin-bottom:22px}
.step[data-open="1"] .step__inner{opacity:1}
.step__body p{margin:0 0 12px}
/* тихий хвост FAQ: строка текста, не кнопка - третий призыв подряд в хвосте страницы давил бы */
.faq__after{margin:30px 0 0;text-align:center;font-size:15px;color:var(--smoke)}
.faq__after a{color:var(--hot);text-underline-offset:3px}

/* list-style-type задан явно: этот ul лежит внутри li нумерованного списка ступеней, браузер
   считает его вложенным и по умолчанию рисует полый circle вместо точки. */
.step__body ul{margin:0;padding-left:18px;display:flex;flex-direction:column;gap:6px;font-size:15px;list-style-type:disc}
.step__body li::marker{color:var(--hot);font-size:.85em}
.step--full .step__name{color:var(--hot)}
/* Support стоит в общем ряду, не с отступом (пробовали - ряд рвался): что это ответвление,
   а не ступень, говорят подпись справа и текст внутри. */

/* ---------- 6. заявка ---------- */
/* Верх секции по-прежнему полноразмерный, низ ужат вдвое: форма кончается кнопкой, и
   пустая тёмная полоса под ней не нужна. */
.apply{padding-top:clamp(80px,10vw,140px);padding-bottom:clamp(40px,5vw,70px);background:var(--night-2);border-top:1px solid var(--edge)}
/* Ловушка для ботов - не display:none и не hidden (часть спам-скриптов такие поля
   пропускает), а вынесена за экран и не занимает места. */
.trap{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
.form{margin-top:34px;display:flex;flex-direction:column;gap:12px}
.field{display:flex;flex-direction:column;gap:6px}
.field label{font-size:14px;font-weight:600;color:var(--paper)}
.field__opt{font-weight:400;color:var(--smoke-dim)}
.field input,.field textarea{
  font-family:inherit;font-size:16px;color:var(--paper);
  background:var(--night);border:1.5px solid var(--edge);border-radius:var(--r-ctrl);padding:14px 16px;
  transition:border-color .18s,background-color .18s;
}
.field input::placeholder,.field textarea::placeholder{color:var(--smoke-dim)}
.field input:hover,.field textarea:hover{border-color:var(--edge-hi)}
.field input:focus,.field textarea:focus{border-color:var(--hot);outline:none;background:#0d0e10}
.field input[aria-invalid="true"]{border-color:var(--bad)}
/* Растёт по высоте сама (JS пишет --field-h по scrollHeight) - resize:none, чтобы человек
   не тянул угол сам, пока поле и так подстраивается. Без transition на height: это layout-
   свойство, анимировать его - пересчитывать раскладку на каждом кадре ради поля, которое
   и так меняется только по нажатию клавиши - высота лучше откликается мгновенно, без лага
   при наборе. overflow:hidden прячет промежуточный скролл, пока высота ещё не догнала
   контент. Потолок - 8 строк, дальше обычная прокрутка. */
.field textarea{
  resize:none;overflow:hidden;line-height:1.5;
  height:var(--field-h, 52px);min-height:88px;max-height:216px;
}
/* Выбор мессенджера и ник - один визуальный бокс на двоих, не два соседних поля:
   граница и фон живут на обёртке, select и input внутри прозрачные. Разделитель между
   ними - тонкая линия того же цвета, что общая граница. */
.messenger{
  display:flex;align-items:stretch;background:var(--night);
  border:1.5px solid var(--edge);border-radius:var(--r-ctrl);
  transition:border-color .18s,background-color .18s;
}
.messenger:hover{border-color:var(--edge-hi)}
.messenger:focus-within{border-color:var(--hot);background:#0d0e10}
/* Свой listbox вместо <select> - см. комментарий в разметке. Кнопка занимает место,
   которое раньше держал select: та же граница-разделитель, тот же паддинг. */
.msgpick{position:relative;flex:0 0 auto;border-right:1.5px solid var(--edge)}
.msgpick__btn{
  display:flex;align-items:center;gap:8px;height:100%;
  font-family:inherit;font-size:16px;font-weight:600;color:var(--paper);
  background:none;border:none;border-radius:var(--r-ctrl) 0 0 var(--r-ctrl);
  padding:14px 12px 14px 16px;cursor:pointer;
}
.msgpick__btn:focus-visible{outline:none}
.msgpick__chevron{
  width:14px;height:14px;flex:0 0 auto;stroke:var(--smoke-dim);fill:none;
  stroke-width:2;transition:transform .18s var(--ease-cam);
}
.msgpick__btn[aria-expanded="true"] .msgpick__chevron{transform:rotate(180deg)}
.msgpick__list{
  position:absolute;top:calc(100% + 6px);left:0;z-index:20;min-width:calc(100% + 40px);
  margin:0;padding:6px;list-style:none;
  background:var(--night-3);border:1.5px solid var(--edge);border-radius:var(--r-ctrl);
  box-shadow:0 14px 30px rgba(0,0,0,.45);
}
.msgpick__opt{
  padding:10px 12px;border-radius:calc(var(--r-ctrl) - 4px);
  font-size:15px;font-weight:600;color:var(--paper);white-space:nowrap;cursor:pointer;
}
.msgpick__opt[aria-selected="true"]{color:var(--hot)}
.msgpick__opt.is-active{background:rgba(var(--hot-rgb),.14)}
.messenger input{
  flex:1;min-width:0;font-family:inherit;font-size:16px;color:var(--paper);
  background:transparent;border:none;border-radius:0 var(--r-ctrl) var(--r-ctrl) 0;padding:14px 16px;
}
.messenger input::placeholder{color:var(--smoke-dim)}
.messenger input[aria-invalid="true"]{box-shadow:inset 2px 0 0 var(--bad)}
/* Место под ошибку зарезервировано всегда (не :empty{display:none}) - иначе текст ошибки,
   появляясь, резко толкает кнопку и всё, что ниже неё, вниз. */
.field__err{margin:0;font-size:13px;line-height:1.4;min-height:18px;color:var(--bad-text)}
/* Вся строка согласия - label, клик работает и по тексту. Но чекбокс стоит перед кнопкой
   отправки, в него ещё и целятся пальцем: min-height даёт нормальную зону касания, не
   раздувая сам квадрат. */
.agree{display:flex;gap:12px;align-items:flex-start;font-size:14px;color:var(--smoke);cursor:pointer;
  min-height:44px;padding:6px 0}
/* 24px, не 22: минимальный размер цели, а флажок - обязательное поле формы */
.agree input{width:24px;height:24px;accent-color:var(--hot);margin-top:0;flex:0 0 auto}
/* skip-ink:none - иначе браузер приподнимает подчёркивание вокруг хвостиков «д», «ц», «щ»,
   и на «данных»/«персональных» это читается обрывом линии, не буквой без подчёркивания. */
.agree a{color:var(--hot);text-decoration-skip-ink:none}
/* Искры при удачной отправке - разовый отклик на действие, тот же язык, что искры
   у костра на первом экране, но не бесконечный цикл: JS добавляет span'ы только
   при успехе и убирает их по окончании анимации. height:0, чтобы контейнер не
   двигал раскладку - искры всплывают над ним, а не раздвигают форму. */
.celebrate{position:relative;height:0}
.celebrate span{
  position:absolute;bottom:0;width:3px;height:3px;border-radius:50%;
  background:var(--glow);box-shadow:0 0 8px 1px rgba(255,160,60,.7);
  opacity:0;animation:celebrate-spark 1.6s ease-out forwards;
}
@keyframes celebrate-spark{
  0%{opacity:0;transform:translateY(0) scale(.6)}
  15%{opacity:1}
  100%{opacity:0;transform:translate(var(--dx,0px),-140px) scale(.3)}
}
/* Три класса статуса вместо инлайн-цвета из JS - раньше цвет ошибки жил в скрипте
   отдельно от палитры. */
/* max-width:70ch у общего правила p{} (стр. 83) держит длинные абзацы читаемыми, но короткую
   строку статуса под кнопкой оно ужимало у́же самой кнопки - от этого центровка съезжала
   влево. Здесь ширина не нужна: снимаем её явно. */
.form__status{margin:0;font-size:14px;line-height:1.65;min-height:23.1px;color:var(--smoke);text-align:center;max-width:none}
.form__status--ok{color:var(--hot)}
.form__status--bad{color:var(--bad-text)}

/* ---------- 7. FAQ ---------- */
.faq{padding:clamp(80px,10vw,140px) 0}
.qa{display:flex;flex-direction:column;border-top:1px solid var(--edge)}
.qa details{border-bottom:1px solid var(--edge)}
.qa summary{
  list-style:none;cursor:pointer;padding:22px 40px 22px 0;position:relative;
  font-size:clamp(16px,1.7vw,19px);font-weight:600;line-height:1.4;transition:color .18s;
}
.qa summary::-webkit-details-marker{display:none}
.qa summary::after{
  content:"";position:absolute;right:6px;top:50%;width:11px;height:11px;
  border-right:2px solid var(--hot);border-bottom:2px solid var(--hot);
  translate:0 -70%;rotate:45deg;transition:rotate .22s cubic-bezier(.2,.8,.3,1);
}
.qa details[open] summary::after{rotate:-135deg;translate:0 -30%}
.qa summary:hover{color:var(--hot)}
.qa p{margin:0 0 22px;color:var(--smoke);max-width:70ch}

/* ---------- 8. финал ---------- */
.close{position:relative;padding:clamp(90px,12vw,240px) 0;text-align:center;overflow:hidden;border-top:1px solid var(--edge)}
.close__night{position:absolute;inset:0;overflow:hidden}
/* Кадр приглушён и обесцвечен наполовину - держит настроение, не спорит с текстом.
   Вуаль сверху добивает контраст до читаемого. */
.close__photo{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:.36;filter:grayscale(.4) contrast(1.05) brightness(.9)}
.close__veil{position:absolute;inset:0;
  background:
    radial-gradient(120% 80% at 50% 50%,rgba(var(--night-rgb),.86),rgba(var(--night-rgb),.6) 60%,rgba(var(--night-rgb),.42)),
    linear-gradient(to bottom,var(--night),transparent 22%,transparent 78%,var(--night))}
.close__fire{
  position:absolute;left:50%;bottom:-40%;translate:-50% 0;width:min(900px,110vw);aspect-ratio:1/.7;
  background:radial-gradient(closest-side,rgba(var(--ember-rgb),.20),transparent 70%);filter:blur(10px);
}
.close .wrap{position:relative;z-index:2}
.close__title{margin:0 0 22px}
.close .lead{margin-inline:auto;text-align:center}
/* Подсказка «дальше вниз», не кнопка: полупрозрачная, без рамки и фона (лишний бордер на
   фото читался бы декором), отвечает только цветом. Стоит у низа секции, где светит .close__fire. */
.close__scroll{
  position:absolute;left:50%;bottom:clamp(28px,8vw,90px);translate:-50% 0;z-index:2;
  display:inline-flex;align-items:center;justify-content:center;
  width:72px;height:56px;
  color:var(--paper);opacity:.6;
  transition:opacity .2s ease,color .2s ease;
}
/* non-scaling-stroke держит толщину пера постоянной при увеличенном viewBox - значок
   вырос втрое, перо осталось прежним. */
.close__scroll svg{
  width:72px;height:48px;fill:none;stroke:currentColor;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round;
  animation:close-scroll-cue 1.8s ease-in-out infinite;
}
.close__scroll:hover,.close__scroll:focus-visible{opacity:1;color:var(--hot)}
@keyframes close-scroll-cue{
  0%,100%{transform:translateY(0)}
  50%{transform:translateY(10px)}
}

/* ---------- подвал ---------- */
.foot{padding:44px 0;border-top:1px solid var(--edge);background:var(--night-2)}
.foot__inner{display:flex;align-items:center;gap:20px;flex-wrap:wrap}
.foot__mark{width:44px;height:auto;opacity:.9}
.foot__name{font-weight:800;margin:0;letter-spacing:-.01em}
.foot__sub{margin:2px 0 0;font-size:14px;color:var(--smoke-dim);max-width:52ch}
/* 13px цветом --smoke-dim дают 6.3:1 на фоне - с запасом, мельчить нельзя: строка
   юридически значима. */
.foot__legal-id{margin:8px 0 0;font-size:13px;color:var(--smoke-dim);letter-spacing:.01em}
/* Тот же приём, что у .foot__legal-id рядом (точка-разделитель, --smoke-dim) - без
   явного цвета точка между ссылками наследовала обычный светлый текст абзаца и выглядела
   ярче самих ссылок. align-self:flex-end ставит строку к нижнему краю подвала, вровень
   с реквизитами слева - тем же уровнем мелкого шрифта, а не серединой строки, где она
   раньше зависала одна против всего блока названия и подписи. */
.foot__legal{margin:0 0 0 auto;align-self:flex-end;font-size:13px;color:var(--smoke-dim)}
.foot__legal a{color:var(--smoke);text-decoration:none;border-bottom:1px solid var(--edge)}
.foot__legal a:hover{color:var(--hot)}

/* ---------- планшет ---------- */
@media (max-width:900px){
  /* Три цели в строке тесно на телефоне - ужимаем зазор и поля кнопки, логотип не
     трогаем (тоже тап-таргет, см. выше). */
  .topbar{gap:8px}
  .topbar__cta{margin-left:auto;padding:11px 16px}
  .topbar__toggle{
    display:grid;place-items:center;flex:0 0 auto;width:44px;height:44px;
    background:none;border:1.5px solid var(--edge);border-radius:var(--r-ctrl);
    color:var(--paper);cursor:pointer;transition:border-color .18s,color .18s;
  }
  .topbar__toggle:hover{border-color:var(--hot);color:var(--hot)}
  .topbar__toggle svg{width:20px;height:20px}
  .topbar__toggle path{
    fill:none;stroke:currentColor;stroke-width:1.8;stroke-linecap:round;
    transform-box:fill-box;transform-origin:center;
    transition:transform .25s var(--ease-cam),opacity .2s ease;
  }
  /* Раскрывашка превращается в крестик - состояние читается той же формой, без иконки-дублёра. */
  .topbar--menu-open .topbar__toggle-l1{transform:translateY(5px) rotate(45deg)}
  .topbar--menu-open .topbar__toggle-l2{opacity:0}
  .topbar--menu-open .topbar__toggle-l3{transform:translateY(-5px) rotate(-45deg)}

  /* Тот же .topbar__nav, что в строке на десктопе - здесь панель под шапкой. Открыта/закрыта
     классом на .topbar, не display:none на самом nav: ссылки гаснут переходом, а не
     пропадают из DOM резко. */
  .topbar__nav{
    position:absolute;top:100%;left:0;right:0;
    display:flex;flex-direction:column;gap:0;margin-left:0;
    padding:4px max(20px,calc((100vw - var(--wrap))/2)) 12px;
    background:var(--night-2);border-bottom:1px solid var(--edge);
    transform:translateY(-6px);opacity:0;pointer-events:none;
    transition:transform .22s var(--ease-cam),opacity .18s ease;
  }
  .topbar__nav a{padding:14px 2px;font-size:16px;border-top:1px solid var(--edge)}
  .topbar__nav a:first-child{border-top:none}
  .topbar--menu-open .topbar__nav{transform:translateY(0);opacity:1;pointer-events:auto}

  .club__grid{grid-template-columns:1fr}
  .circle__stage{grid-template-columns:1fr}
  /* Узкому экрану - узкая текстура: 85 КБ вместо 160. */
  .hero__topo{background-image:url(../img/topo-900.webp?v=2)}
  /* backdrop-filter пересчитывается на каждом кадре прокрутки - самая дорогая вещь на
     слабом телефоне. Плотная заливка вместо блюра: разница почти не видна, прокрутка не
     спотыкается. */
  .topbar{backdrop-filter:none;background:linear-gradient(to bottom,rgba(var(--night-rgb),.97),rgba(var(--night-rgb),.9) 70%,rgba(var(--night-rgb),.72))}
  /* Гора идёт от края до края и на планшете, и на телефоне: колонка 340px запирала её в
     рамку. Соотношение 8/5, не 1/1 - сцена шире, чем высокий квадрат, viewBox и так шире
     контейнера в любой из этих пропорций (slice всегда показывает его целиком по высоте),
     поэтому квадрат на планшете просто добавлял лишнюю высоту сверх нужной ширины кадра,
     не открывая больше композиции - на телефоне это отдельно возвращается к 1/1 ниже. */
  .peak{width:100vw;height:auto;aspect-ratio:8/5;margin-left:-20px;margin-right:-20px}
  .peak::after{background:linear-gradient(to right,var(--night) 0,rgba(var(--night-rgb),0) 12%)}
  /* Кадр мельче на телефоне, единицы подписи и цели клика те же, что у сцены - без поправки
     подпись падала бы до 9px, цель клика до 38px. */
  .mark__name{font-size:17px;stroke-width:4.5px}
  .mark__hit{r:32}
  /* Узкий экран: текст занимает почти весь первый экран, свободна только полоса под
     слоганом - оставляем одну короткую дорогу там, длинные шли бы прямо по буквам. */
  .routes{transform:translateY(58px)}
  .route--pass,.route--near{display:none}
  .route--phone{display:block}
  .wings{grid-template-columns:1fr;gap:22px}
  .ledger__row{grid-template-columns:1fr;gap:8px;padding:22px 0}
  /* На планшете 3 узкие карточки мельчат фото и подпись - партия короче. */
  .album{--per-page:2}
}

/* ---------- телефон ---------- */
@media (max-width:560px){
  body{font-size:16px}
  .topbar{padding-block:10px}
  .topbar__logo img{width:132px}
  main[id],section[id]{scroll-margin-top:70px}
  /* На телефоне колонка узкая - квадрат ужимает кадр вертикально настолько, что композиция
     ещё держит масштаб марок и тропы; на планшете тот же квадрат уже занимал экран, который
     нечем было занять, поэтому там гора ниже (правило выше, 8/5), а здесь возвращаем 1/1. */
  .peak{aspect-ratio:1/1}
  .hero{min-height:auto;padding:56px 0 72px}
  .hero__title{max-width:none}
  .hero__actions .btn{width:100%}
  /* На телефоне одна карточка в партии - две узкие рядом были бы нечитаемы. */
  .album{--per-page:1}
  .step__inner{padding-left:22px}
  .foot__legal{margin-left:0;width:100%}
}

/* Низкие телефоны (SE/8 и подобные, ~640-700px высоты): при обычном ритме кнопки первого
   экрана уходят за фолд целиком - три строки заголовка на полном кегле разворачиваются в
   8 строк на узкой колонке. Правим только вертикальный ритм и кегль заголовка, макет и
   текст те же - высокие телефоны и десктоп это правило не видят. */
@media (max-width:560px) and (max-height:740px){
  .hero{padding:28px 0 44px}
  .kicker{margin-bottom:8px}
  .hero__title{font-size:26px;margin-bottom:14px}
  .hero__lead{margin-bottom:16px}
}

/* ---------- уважение к настройке «меньше движения» ----------
   Раньше стояло глобальное *{transition-duration:.01ms!important} - оно убивало и движение,
   и отклик (подсветку отметки, границу поля в фокусе). Разделено по смыслу: то, что ЕДЕТ, -
   мгновенно, то, что ОТВЕЧАЕТ ЦВЕТОМ, - остаётся. */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  /* 1. Бесконечные циклы (чистая декорация) выключены целиком. Дороги остаются - они
        часть рисунка, не анимации; уезжают только огни колонн. */
  .sparks{display:none}
  .route__run{animation:none;opacity:0}
  .close__scroll svg{animation:none}
  .celebrate span{animation:none;display:none}
  .mark--star .mark__halo,.mark--star .star__spark{animation:none}
  /* 2. Крупные перемещения и масштабы - мгновенно: пролёт камеры, подписи высот, раскрытие
        карточки, дорисовка тропы, флаг на вершине. Состояние меняется без дороги до него. */
  .peak__layer,.mark__tag,.trail--done,.trail--glow,.mark__halo,.summit-flag,.step__body,
  .topbar__nav,.topbar__toggle path,.album__track,.msgpick__chevron{
    transition-duration:0s !important;transition-delay:0s !important;
  }
  /* 3. Подъём кнопки под курсором - тоже движение, а её ответ и без него читается цветом. */
  .btn:hover{transform:none}
  /* 4. Переходы цвета, свечения и прозрачности не трогаем - это обратная связь, не
        движение. Без них выбор уровня, наведение на отметку и фокус поля перестают читаться. */
}
