/* ==========================================================================
   Камиль — сайты для локального бизнеса
   Один файл стилей, без зависимостей и сборки.

   ПАЛИТРА
   Главный акцент в обеих темах — бордо (кнопки), второй — охра (номера,
   галочки, мелкие акценты). Светлая тема: тёплая бумага и почти чёрный
   тёплый текст. Тёмная (основная): тёмно-зелёный фон, коричневые поверхности.
   Тёмные секции внутри светлой темы — глубокий зелёный.
   Обе темы проверены на контраст: текст ≥ 4.5:1, крупный ≥ 3:1.

   ШРИФТЫ
   Заголовки — серифный «дисплей» (на Mac/iOS это New York, дальше Georgia).
   Текст и интерфейс — системный гротеск. Ничего не грузится из сети.
   Если позже добавим свой шрифт — достаточно вписать его первым в --f-display.
   ========================================================================== */

:root{
  --f-display:ui-serif,Georgia,"Iowan Old Style","Palatino Linotype","Times New Roman",serif;
  --f-ui:system-ui,-apple-system,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif;

  --wrap:1240px;
  --r:8px;
  --r-lg:10px;
  --ease:cubic-bezier(.22,.61,.36,1);

  /* Плавающая шапка: отступ от краёв экрана, высота плашки, её ширина
     и скругление. Отсюда же считается scroll-padding, чтобы якорь
     не заезжал под плашку. Компактные значения — ниже, в :root.stuck. */
  --hdr-gap:10px;
  --hdr-h:62px;
  --hdr-r:999px;
  --hdr-w:var(--wrap);
  --hdr-pad:20px;
}

/* Как только страницу тронули, плашка не просто едет следом, а поджимается
   до самого малого размера, и четыре знака на ней съезжаются к середине.
   Ширину нельзя ставить меньше, чем сумма знаков с их отступами: плашка
   упрётся в содержимое и перестанет сжиматься.

   Класс ставит app.js на <html>, а не на саму шапку, — тогда от этих же
   переменных пересчитывается scroll-padding-top, и якорь по-прежнему встаёт
   ровно под плашкой, какой бы она сейчас ни была. */
:root.stuck{
  --hdr-gap:12px;
  --hdr-h:50px;
  --hdr-w:300px;
  --hdr-pad:14px;
}

/* ---------- светлая тема ---------- */
:root,
:root[data-theme="light"]{
  /* Бумага намеренно не белая и не бледная: тёплый песок, на тон темнее
     обычного «беж». На нём и охра, и бордо звучат в полный голос, а светлая
     тема перестаёт выглядеть черновиком рядом с тёмной.
     Карточки (--surface) светлее фона, полосы разделов (--surface-2) темнее —
     страница читается слоями, а не одним пятном. */
  --bg:#E7DDC9;
  --surface:#F1EADB;
  --surface-2:#DBD0B8;
  --text:#191410;
  --text-2:#3B3128;
  --muted:#5F5244;
  --line:#CBBC9E;
  --line-strong:#9C8A6B;     /* линии на песке должны держать форму */
  --line-soft:#D4C7AC;       /* разделители внутри списка: структура есть, в глаза не лезет */

  --accent:#7A1F2B;          /* бордо — кнопки, акценты */
  --accent-hi:#5E1620;
  --accent-text:#7A1F2B;     /* тот же бордо для мелкого текста */
  --on-accent:#FBF7F0;
  --gold:#D9A441;            /* охра — второй акцент */
  --on-gold:#1A1208;
  --wine-card:#7A1F2B;       /* фон карточки цены */

  --deep:#1E2A22;            /* тёмно-зелёные секции */
  --deep-2:#26332B;
  --deep-line:#35443A;
  --deep-text:#F1EBDE;
  --deep-muted:#A2AFA4;

  /* Живой фон: три пятна, сменяющие друг друга по мере прокрутки. На песке
     их видно куда лучше, чем на белёсой бумаге, поэтому света прибавлено —
     светлая тема тоже должна переливаться, а не быть плоским листом. */
  --atmo-1:#4C8256;          /* зелёный — верх страницы */
  --atmo-2:#B87A2C;          /* охра — середина */
  --atmo-3:#8E2E3E;          /* винный — низ, к контактам */
  --atmo-power:.44;          /* общая сила свечения */
  --veil-soft:80%;           /* насколько плотны светлые полосы поверх фона */
  --veil-deep:90%;           /* то же для тёмных */
  --grain:.055;              /* плёночное зерно по всей странице */

  /* Поток: цвет точек и сила ленты. Цвет — тремя числами, а не готовым
     цветом: скрипт подставляет его прямо в rgba() и ничего не разбирает.
     На бумаге лента рисуется тёмным — это тушь по песку, а не свет. */
  --flow-ink:58,42,26;
  --flow-op:.34;             /* сила: лента не должна бросаться в глаза */

  /* Бегущая лента — отдельная планка поверх страницы: подложка светлее
     бумаги, блик по верхнему ребру, притенение по нижнему и мягкая тень
     наружу в обе стороны. На бумаге планку держит именно тень. */
  --tick-bg:rgba(243,236,222,.94);
  --tick-edge:rgba(255,253,247,.95);
  --tick-under:rgba(25,20,16,.16);
  --tick-sh:rgba(25,20,16,.30);

  /* Растр на стыках светлых полос: край полосы не тает в никуда,
     а рассыпается редкими точками — тем же языком, что шар и лента. */
  --seam-dot:rgba(74,60,40,.30);

  --sh-sm:0 1px 2px rgba(25,20,16,.04), 0 8px 20px -14px rgba(25,20,16,.22);
  --sh:0 2px 6px rgba(25,20,16,.05), 0 22px 44px -26px rgba(25,20,16,.34);
  --sh-lg:0 4px 10px rgba(25,20,16,.07), 0 44px 84px -40px rgba(25,20,16,.44);

  /* стекло плавающей шапки: спокойное наверху страницы (--hdr-bg) и плотнее,
     когда под шапку заехал текст (--hdr-bg-2, включается классом stuck) */
  --hdr-bg:rgba(243,236,223,.70);
  --hdr-bg-2:rgba(243,236,223,.88);
  --hdr-line:rgba(25,20,16,.10);
  --hdr-line-2:rgba(25,20,16,.15);
  --hdr-sh:0 10px 30px -18px rgba(25,20,16,.4);
  --hdr-sh-2:0 16px 40px -20px rgba(25,20,16,.5);

  /* кольцо на экране загрузки: на бумаге белое свечение не читается,
     поэтому в светлой теме кольцо бордовое */
  --load-ring:#7A1F2B;
  --load-glow:rgba(122,31,43,.28);

  /* Круг под портретом на обложке. На свету в нём лежит КРЕМОВЫЙ цвет —
     ровно тот, что на самой фотографии вокруг головы (241 228 211): так
     просил владелец 13.08.2026. Заодно это чинит очки: дужки — прозрачный
     пластик, снятый на этом же фоне, и на кремовом круге они выглядят как
     на фото, а не как светлые палки. Край диска резкий, мягкий только свет
     вокруг (см. «ПОРТРЕТ НА ОБЛОЖКЕ»). */
  --pic-bg:#F1E4D3;
  --pic-ring:rgba(155,138,107,.35);
  --pic-glow:rgba(217,164,65,.5);
  --pic-glow-r:70px;
}

/* ---------- тёмная тема ---------- */
:root[data-theme="dark"]{
  --bg:#12170F;
  --surface:#1A211A;
  --surface-2:#241A12;       /* коричневая поверхность */
  --text:#F1EBDE;
  --text-2:#D8CEBE;
  --muted:#9C9285;
  --line:#2A322A;
  --line-strong:#3C463C;
  --line-soft:#27251D;       /* см. светлую тему: разделители списка гасятся в фон полосы */

  --accent:#8E2233;          /* бордо — кнопки и в тёмной теме тоже */
  --accent-hi:#A62A3C;
  --accent-text:#E3B65B;     /* охра — мелкий текст, номера, галочки */
  --on-accent:#F6EFE4;
  --gold:#D9A441;
  --on-gold:#1A1208;
  --wine-card:#4E1720;

  --deep:#0C100B;
  --deep-2:#151B14;
  --deep-line:#26302A;
  --deep-text:#F1EBDE;
  --deep-muted:#9AA79C;

  /* в темноте свечению можно быть заметнее — оно и есть «ночь с одной лампой» */
  --atmo-1:#356F42;          /* зелёный — верх страницы */
  --atmo-2:#7E4C22;          /* коричневый — середина */
  --atmo-3:#7C2035;          /* винный — низ, к контактам */
  --atmo-power:.62;
  --veil-soft:86%;
  --veil-deep:88%;
  --grain:.035;

  /* в темноте лента — свет, а не тушь */
  --flow-ink:238,234,222;
  --flow-op:.40;

  /* В темноте тень не видна вовсе — планку из фона вынимает подложка
     посветлее фона плюс блик по верхнему ребру. Тень при этом остаётся:
     она работает там, где планка легла на светлое пятно живого фона. */
  --tick-bg:rgba(29,37,26,.95);
  --tick-edge:rgba(241,235,222,.22);
  --tick-under:rgba(0,0,0,.6);
  --tick-sh:rgba(0,0,0,.72);

  --seam-dot:rgba(233,227,213,.17);

  --sh-sm:0 1px 2px rgba(0,0,0,.3), 0 8px 20px -14px rgba(0,0,0,.6);
  --sh:0 2px 6px rgba(0,0,0,.35), 0 22px 44px -26px rgba(0,0,0,.7);
  --sh-lg:0 4px 10px rgba(0,0,0,.4), 0 44px 84px -40px rgba(0,0,0,.8);

  --hdr-bg:rgba(22,29,20,.60);
  --hdr-bg-2:rgba(22,29,20,.80);
  --hdr-line:rgba(241,235,222,.11);
  --hdr-line-2:rgba(241,235,222,.17);
  --hdr-sh:0 10px 30px -18px rgba(0,0,0,.65);
  --hdr-sh-2:0 16px 44px -22px rgba(0,0,0,.85);

  /* в темноте кольцо — обычный белый свет, как в мудборде */
  --load-ring:#F4EFE3;
  --load-glow:rgba(244,239,227,.42);

  /* круг под портретом: в темноте — резкое светящееся кольцо на тёмном диске */
  --pic-bg:#171C16;
  --pic-ring:rgba(241,235,222,.9);
  --pic-glow:rgba(217,164,65,.26);
}

/* На телефоне колонка текста занимает почти всю ширину, и лента проходит
   прямо по строкам. Отодвинуть её некуда — значит, приглушаем: рисунок
   остаётся, а читать он не мешает. Пишем через data-theme, иначе правило
   не перебьёт палитру: у неё селектор «весомее». */
@media (max-width:700px){
  :root[data-theme="light"]{--flow-op:.26}
  :root[data-theme="dark"]{--flow-op:.32}
}

/* ==========================================================================
   БАЗА
   ========================================================================== */
*,*::before,*::after{box-sizing:border-box}

html{
  -webkit-text-size-adjust:100%;
  scroll-behavior:smooth;
  scroll-padding-top:calc(var(--hdr-gap) + var(--hdr-h) + 22px);
}
/* когда работает инерционная прокрутка из app.js, родная плавность мешает */
html.inertia{scroll-behavior:auto}

body{
  margin:0;
  overflow-x:clip;
  background:var(--bg);
  color:var(--text);
  font:400 17px/1.62 var(--f-ui);
  -webkit-font-smoothing:antialiased;
  transition:background-color .35s var(--ease),color .35s var(--ease);
}

img{display:block;max-width:100%;height:auto}
a{color:inherit}
p{margin:0 0 1.05em}
p:last-child{margin-bottom:0}
h1,h2,h3{margin:0}
ul,ol{margin:0;padding:0;list-style:none}
button{font:inherit;color:inherit}

/* .btn задаёт display, поэтому скрытым кнопкам нужен !important,
   иначе на сайте появятся пустые «Написать в Telegram» и «Позвонить» */
[hidden]{display:none !important}

::selection{background:var(--accent);color:var(--on-accent)}

:focus-visible{
  outline:3px solid var(--accent-text);
  outline-offset:3px;
  border-radius:4px;
}
.sec--deep :focus-visible,.cta :focus-visible,.ftr :focus-visible{outline-color:var(--gold)}

.vh{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
}

.skip{
  position:absolute;left:12px;top:-60px;z-index:100;
  background:var(--text);color:var(--bg);padding:12px 18px;border-radius:10px;
  text-decoration:none;font-weight:600;transition:top .2s var(--ease);
}
.skip:focus{top:12px}

.wrap{width:100%;max-width:var(--wrap);margin:0 auto;padding:0 24px}

/* ---------- плашка черновика ---------- */
.draft{
  position:relative;z-index:60;
  background:var(--gold);color:#1A1208;
  font-size:14px;line-height:1.5;padding:10px 24px;text-align:center;
}
.draft code{background:rgba(26,18,8,.14);border-radius:5px;padding:1px 6px;font-size:13px}

/* ==========================================================================
   ТИПОГРАФИКА
   ========================================================================== */
.display{
  font-family:var(--f-display);
  font-size:clamp(40px,7.4vw,96px);
  font-weight:600;
  line-height:1.0;
  letter-spacing:-.022em;
  text-wrap:balance;
}
.display em{font-style:italic;color:var(--accent-text)}
.display--sm{font-size:clamp(32px,5.4vw,66px);line-height:1.04}

.h2{
  font-family:var(--f-display);
  font-size:clamp(30px,4.6vw,58px);
  font-weight:600;
  line-height:1.05;
  letter-spacing:-.02em;
  text-wrap:balance;
}

.lead{
  font-size:clamp(17.5px,1.5vw,20.5px);
  line-height:1.58;
  color:var(--text-2);
  max-width:54ch;
}

/* нумерованная подпись раздела с уходящей вправо линейкой */
.idx{
  position:relative;
  display:flex;align-items:flex-start;gap:14px;
  margin:0 0 22px;
  font-size:12.5px;font-weight:700;letter-spacing:.14em;text-transform:uppercase;
  line-height:1.5;
  color:var(--muted);
}
.idx i{
  font-style:normal;color:var(--accent-text);
  font-variant-numeric:tabular-nums;
}
/* линейка уходит вправо и держится на уровне первой строки подписи */
.idx::after{content:"";flex:1;height:1px;margin-top:.72em;background:var(--line)}
.idx--plain::after{display:none}

/* Раздел въезжает в экран — номер проворачивается на место, а линейка
   прочерчивается слева направо. Движения ровно на один жест: подпись раздела
   и так самая мелкая строчка на экране, большего ей не нужно.

   Ничего нового скриптом не считаем: класс is-in на блоке уже ставит блок 6,
   тот же, что показывает сами блоки. Всё остальное — переход в стилях.
   Правила начинаются с .js: без скрипта класс is-in никто не поставит,
   и номер с линейкой обязаны быть видны сразу. */
.js .idx{perspective:320px}
.js .idx i{
  display:inline-block;transform-origin:50% 60%;
  rotate:1 0 0 -82deg;opacity:0;
  transition:rotate .75s var(--ease) .12s,opacity .45s linear .12s;
}
.js .idx::after{
  transform:scaleX(0);transform-origin:left;
  transition:transform .9s var(--ease) .22s;
}
.js .reveal.is-in .idx i,.js .idx.reveal.is-in i{rotate:none;opacity:1}
.js .reveal.is-in .idx::after,.js .idx.reveal.is-in::after{transform:scaleX(1)}
.idx--deep{color:var(--deep-muted)}
.idx--deep i{color:var(--gold)}
.idx--deep::after{background:var(--deep-line)}

.note{font-size:15px;line-height:1.55;color:var(--muted)}

/* ==========================================================================
   КНОПКИ
   ========================================================================== */
.btn{
  position:relative;
  display:inline-flex;align-items:center;justify-content:center;gap:10px;
  min-height:54px;padding:15px 30px;
  border:1px solid transparent;border-radius:8px;overflow:hidden;
  font-family:var(--f-ui);font-size:16.5px;font-weight:600;letter-spacing:-.005em;
  text-decoration:none;cursor:pointer;
  transition:transform .2s var(--ease),box-shadow .28s var(--ease),
             background-color .22s var(--ease),border-color .22s var(--ease),
             color .22s var(--ease),translate .22s var(--ease),
             min-height .45s var(--ease),padding .45s var(--ease);
  /* «Магнит»: под курсором кнопка тянется к нему на несколько пикселей.
     Сдвиг ставит app.js в отдельное свойство translate — так он не спорит
     с transform, на котором висит нажатие (.btn:active ниже). */
}
/* заливка наезжает снизу — спокойнее, чем прыжок вверх */
.btn::before{
  content:"";position:absolute;z-index:-1;inset:0;
  transform:translateY(101%);
  transition:transform .5s var(--ease);
}
.btn:hover::before{transform:translateY(0)}
.btn:active{transform:scale(.985)}

.btn--accent{background:var(--accent);color:var(--on-accent)}
.btn--accent::before{background:var(--accent-hi)}

.btn--gold{background:var(--gold);color:var(--on-gold)}
.btn--gold::before{background:#F0C063}

.btn--ghost{border-color:var(--line-strong);color:var(--text)}
.btn--ghost::before{background:var(--text)}
.btn--ghost:hover{color:var(--bg);border-color:var(--text)}

.btn--light{border-color:rgba(255,255,255,.3);color:var(--deep-text)}
.btn--light::before{background:rgba(255,255,255,.16)}
.btn--light:hover{border-color:rgba(255,255,255,.65)}

/* Второстепенные способы связи: тот же характер, но тише главной кнопки
   и капсулой — чтобы строка читалась как список, а не как четыре равных
   призыва. Заливка при наведении и «магнит» достаются от .btn. */
.btn--line{
  min-height:46px;padding:12px 24px;
  border-color:rgba(255,255,255,.22);border-radius:999px;
  color:var(--deep-text);font-size:15.5px;
}
.btn--line::before{background:rgba(255,255,255,.12)}
.btn--line:hover{border-color:rgba(255,255,255,.5)}

.btn--sm{min-height:44px;padding:11px 22px;font-size:15px}
.btn--lg{min-height:62px;padding:19px 38px;font-size:18px}
.btn--wide{width:100%;margin-top:28px}

.row{display:flex;flex-wrap:wrap;gap:14px;margin-top:34px}
.row--center{justify-content:center}
.row--soc{margin-top:16px;gap:10px}

/* ==========================================================================
   ШАПКА
   ========================================================================== */
/* Шапка — не полоса во всю ширину, а плавающая плашка со скруглением
   и стеклянным размытием, с отступом от краёв экрана.

   Сам .hdr прозрачен и служит только рамкой: его верхний отступ — это зазор
   над плашкой, и он же даёт зазор, когда шапка прилипла к верху окна. */
.hdr{
  position:sticky;top:0;z-index:50;
  padding:var(--hdr-gap) var(--hdr-gap) 0;
  transition:padding .45s var(--ease);
}

/* Стекло — отдельным слоем, а не фоном самой .hdr или .hdr__in.
   Важно: backdrop-filter на элементе-предке сделал бы его точкой отсчёта для
   position:fixed, и мобильное меню схлопнулось бы в высоту шапки. У псевдо-
   элемента такого побочного действия нет: он ничьим предком не является. */
/* Четыре знака разнесены по всей ширине плашки (space-between). Отдельной
   анимации «съезжания» нет и не нужно: расстояние между ними задаёт ширина
   плашки, а она у прокрученной страницы другая — знаки сходятся к середине
   ровно тем же переходом, что сжимает саму плашку. gap здесь — не расстояние,
   а нижний предел: на узком экране знаки не должны слипнуться. */
.hdr__in{
  position:relative;
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  height:var(--hdr-h);max-width:var(--hdr-w);border-radius:var(--hdr-r);
  padding:0 var(--hdr-pad);
  transition:height .45s var(--ease),max-width .45s var(--ease),
             padding .45s var(--ease);
}
.hdr__in::before{
  content:"";position:absolute;inset:0;z-index:1;
  border-radius:inherit;pointer-events:none;
  background:var(--hdr-bg);
  border:1px solid var(--hdr-line);
  box-shadow:var(--hdr-sh);
  backdrop-filter:saturate(1.4) blur(14px);
  -webkit-backdrop-filter:saturate(1.4) blur(14px);
  transition:background-color .3s var(--ease),border-color .3s var(--ease),
             box-shadow .3s var(--ease);
}
/* Как только под плашку заехал текст, стекло становится плотнее.
   Меняем не свойства, а переменные — значения приходят из палитры темы. */
:root.stuck{--hdr-bg:var(--hdr-bg-2);--hdr-line:var(--hdr-line-2);--hdr-sh:var(--hdr-sh-2)}
/* в компактном виде знаки мельче — плашка сжимается вместе с ними */
:root.stuck .tglr,:root.stuck .burger,:root.stuck .talk{width:40px;height:40px}
:root.stuck .logo{font-size:20px}

/* Содержимое шапки лежит поверх стекла. Панель меню (position:fixed)
   наоборот уходит под него — иначе открытая панель закрыла бы плашку. */
.logo,.nav,.tglr,.talk,.burger,.prog{position:relative;z-index:2}

/* ПОЛОСА ПРОГРЕССА.

   Висит под плашкой и ровно по её ширине, с теми же отступами, что у знаков
   на ней. Ширину задавать не нужно: полоса растянута между левым и правым
   краем плашки, а плашка сжимается сама — полоса сжимается вместе с ней
   тем же переходом.

   Лежит абсолютом, поэтому не занимает места: высота шапки считается из
   переменных, от неё же берётся отступ якоря, и лишние два пикселя сдвинули
   бы всё разом.

   На самом верху страницы полосы нет вовсе: пустая шкала у нетронутой
   страницы — мусор на экране. Появляется тем же движением, каким плашка
   поджимается, — по классу stuck.

   Заполнение двигает scaleX, а не ширину: ширина заставила бы браузер
   пересчитывать разметку на каждый пиксель прокрутки. */
.prog{
  position:absolute;left:var(--hdr-pad);right:var(--hdr-pad);
  top:calc(100% + 7px);height:2px;
  border-radius:999px;overflow:hidden;
  background:var(--hdr-line);
  opacity:0;
  transition:opacity .45s var(--ease),left .45s var(--ease),right .45s var(--ease);
}
:root.stuck .prog{opacity:1}
.prog i{
  display:block;height:100%;
  border-radius:inherit;
  background:var(--gold);
  transform:scaleX(var(--prog,0));transform-origin:left;
}

.logo{
  display:grid;text-decoration:none;line-height:1.05;
  flex:0 1 auto;min-width:0;
  font-family:var(--f-display);font-size:23px;font-weight:600;letter-spacing:-.01em;
  transition:font-size .45s var(--ease);
}
.logo__name{white-space:nowrap}
.logo__dot{
  display:inline-block;width:6px;height:6px;margin-left:3px;
  border-radius:50%;background:var(--accent);
}
/* В компактном виде подпись под именем убирается — так плашка не только
   ниже, но и уже, и логотип не переносится на две строки. Схлопываем
   высотой, а не display:none: display не умеет плавно. */
.logo__sub{
  font-family:var(--f-ui);font-size:11px;font-weight:600;
  letter-spacing:.1em;text-transform:uppercase;color:var(--muted);
  margin-top:5px;max-height:16px;overflow:hidden;white-space:nowrap;
  transition:opacity .25s var(--ease),max-height .45s var(--ease),
             margin-top .45s var(--ease);
}
/* width:0 и min-width:0 здесь не для красоты: подпись длиннее имени, и пока
   у неё есть ширина, колонка логотипа остаётся широкой даже со схлопнутой
   высотой — сжатая плашка не смогла бы стать узкой, а знаки вылезли бы
   за её край. min-width:0 обязателен: у элемента сетки минимальный размер
   по умолчанию равен ширине содержимого. */
:root.stuck .logo__sub{opacity:0;max-height:0;margin-top:0;width:0;min-width:0}

/* Разделы живут за двумя линиями на любом экране: панель разворачивается
   поверх страницы и уходит ПОД стекло плашки (z-index 0 против 1),
   поэтому сама плашка остаётся видна, а знаки на ней — поверх обоих.
   Отступ до первой ссылки отмеряет app.js от низа плашки (--nav-top);
   значение в padding — запасное, на случай если скрипт не сработал. */
.nav{
  position:fixed;inset:0;z-index:0;
  display:grid;align-content:start;gap:0;
  margin:0;padding:var(--nav-top,calc(var(--hdr-gap) + var(--hdr-h) + 28px)) 24px 40px;
  background:var(--bg);
  transform:translateY(-100%);
  visibility:hidden;
  transition:transform .38s var(--ease),visibility .38s;
}
.nav.is-open{transform:none;visibility:visible}
/* Ссылки держим в той же колонке, что и текст страницы: на большом экране
   разделители во всю ширину окна смотрятся как таблица, а не как меню. */
.nav a{
  display:flex;align-items:center;gap:16px;
  width:100%;max-width:calc(var(--wrap) - 48px);margin:0 auto;
  padding:20px 2px;border-bottom:1px solid var(--line);
  text-decoration:none;color:var(--text);
  font-family:var(--f-display);font-size:clamp(26px,4vw,40px);font-weight:600;
  letter-spacing:-.015em;line-height:1.1;
  transition:color .2s var(--ease),padding-left .3s var(--ease);
}
.nav a i{
  font-style:normal;font-size:12px;font-weight:700;letter-spacing:.06em;
  color:var(--accent-text);opacity:.75;font-family:var(--f-ui);
}
.nav a:hover{color:var(--accent-text);padding-left:10px}
body.menu-open{overflow:hidden}

/* кружок «обсудить» — той же величины, что луна и две линии */
.talk{
  display:inline-flex;align-items:center;justify-content:center;
  width:46px;height:46px;flex:none;
  border-radius:999px;background:var(--accent);color:var(--on-accent);
  text-decoration:none;
  transition:background-color .22s var(--ease),transform .22s var(--ease),
             width .45s var(--ease),height .45s var(--ease);
}
.talk:hover{background:var(--accent-hi)}
.talk svg{width:21px;height:21px}

/* переключатель темы */
.tglr{
  display:inline-flex;align-items:center;justify-content:center;
  width:46px;height:46px;padding:0;flex:none;
  background:transparent;border:1px solid var(--line-strong);border-radius:999px;
  cursor:pointer;color:var(--text);
  transition:border-color .2s var(--ease),background-color .2s var(--ease),
             width .45s var(--ease),height .45s var(--ease);
}
/* бургер поджимается вместе с плашкой (кнопке это умеет сам .btn) */
.burger{transition:width .45s var(--ease),height .45s var(--ease)}
.tglr:hover{border-color:var(--text);background:color-mix(in srgb,var(--text) 5%,transparent)}
.tglr svg{width:19px;height:19px}
.tglr__moon{display:none}
:root[data-theme="dark"] .tglr__sun{display:none}
:root[data-theme="dark"] .tglr__moon{display:block}

.burger{
  display:flex;width:46px;height:46px;padding:0;flex:none;
  align-items:center;justify-content:center;flex-direction:column;gap:5px;
  background:transparent;border:1px solid var(--line-strong);border-radius:999px;cursor:pointer;
}
.burger:hover{border-color:var(--text);background:color-mix(in srgb,var(--text) 5%,transparent)}
.burger span{display:block;width:18px;height:1.5px;background:var(--text);border-radius:2px;transition:transform .25s var(--ease)}
.burger[aria-expanded="true"] span:first-child{transform:translateY(3.2px) rotate(45deg)}
.burger[aria-expanded="true"] span:last-child{transform:translateY(-3.2px) rotate(-45deg)}

/* ==========================================================================
   ЭКРАН ЗАГРУЗКИ
   Круг света собирается из двух дуг и уходит, открывая страницу.

   Показывается только при включённом скрипте (.js) — прячет заставку тоже
   скрипт, и без него человек остался бы перед пустым экраном. Сколько она
   живёт, решает app.js: минимум секунду, максимум — пока не загрузится
   страница, но не дольше отсечки. Держать человека дольше нельзя ни при
   каких условиях, это витрина, а не кино.
   ========================================================================== */
.load{display:none}
.js .load{
  position:fixed;inset:0;z-index:999;
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:clamp(20px,3vw,30px);
  background:var(--bg);
  transition:opacity .65s var(--ease),visibility .65s;
}
.load.is-off{opacity:0;visibility:hidden}
body.loading{overflow:hidden}

.load__ring{width:clamp(94px,15vw,146px);aspect-ratio:1;color:var(--load-ring)}
/* overflow видимый: иначе свечение обрежется по краю картинки */
.load__ring svg{display:block;width:100%;height:100%;overflow:visible;
                animation:load-turn 9s linear infinite}
.load__arc{
  fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;
  stroke-dasharray:100;stroke-dashoffset:100;
  filter:drop-shadow(0 0 3px var(--load-glow)) drop-shadow(0 0 14px var(--load-glow));
  animation:load-draw 1.05s var(--ease) forwards;
}
.load__arc--b{animation-delay:.07s}
@keyframes load-draw{to{stroke-dashoffset:0}}
@keyframes load-turn{to{transform:rotate(360deg)}}

.load__name{
  margin:0;font-family:var(--f-display);
  font-size:15px;font-weight:600;letter-spacing:.34em;text-transform:uppercase;
  color:var(--muted);
  opacity:0;animation:load-in 1s var(--ease) .45s forwards;
}
@keyframes load-in{to{opacity:1}}

/* ==========================================================================
   ЖИВОЙ ФОН
   Три больших размытых пятна за всей страницей: зелёное вверху, коричневое
   в середине, винное к контактам. Пока человек прокручивает, они едут с разной
   скоростью и передают свет друг другу — страница «переливается».

   Почему слой закреплён в окне, а не растянут по документу: так перелив не
   рвётся на границах секций и не зависит от того, какой длины страница.

   Почему это не тормозит: меняются только transform и opacity — их браузер
   считает на видеокарте и разметку не пересчитывает. Ни одной картинки,
   ни одного запроса в сеть, ни одной бесконечной анимации.
   ========================================================================== */
.atmo{
  position:fixed;inset:0;z-index:-1;
  pointer-events:none;overflow:hidden;
  contain:layout paint;
  opacity:var(--atmo-power);
  /* Очень медленный дрейф всего слоя: пока человек читает и не трогает
     страницу, фон всё равно чуть дышит. Полтора оборота глазом не поймать —
     это фон, а не анимация. Двигается только transform всего слоя, пятна
     внутри по-прежнему считает прокрутка. Увеличение обязательно: без него
     на поворот вылезут пустые углы. */
  animation:atmo-drift 96s ease-in-out infinite;
}
@keyframes atmo-drift{
  0%,100%{transform:scale(1.12) rotate(-2.4deg)}
  50%{transform:scale(1.18) rotate(2.4deg)}
}
/* Пятна заметно больше окна и заходят за его края: так виден только мягкий
   склон свечения, а не круг с очерченной серединой. */
.atmo i{
  position:absolute;display:block;
  width:max(1150px,88vw);aspect-ratio:1;
  border-radius:50%;
  opacity:0;
}
/* Зерно по всей странице, а не только в тёмных секциях.

   Две работы разом: на светлой бумаге это и есть та фактура, без которой она
   выглядела ровной заливкой, а на любом фоне оно разбивает кольца-ступеньки,
   которыми большое размытое пятно идёт на 8 битах цвета.

   Слой отдельный, а не внутри .atmo: там его плотность зависела бы от силы
   свечения и в светлой теме пропадала бы вовсе. Текстура — та же встроенная
   SVG-картинка, что в тёмных секциях, лишнего запроса в сеть нет. */
body::after{
  content:"";position:fixed;inset:0;z-index:-1;
  pointer-events:none;
  opacity:var(--grain);
  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='.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

.atmo i:nth-child(1){left:-34%;top:-46%;background:radial-gradient(closest-side,var(--atmo-1),transparent 80%)}
.atmo i:nth-child(2){right:-46%;top:-14%;background:radial-gradient(closest-side,var(--atmo-2),transparent 80%)}
.atmo i:nth-child(3){left:-24%;bottom:-30%;background:radial-gradient(closest-side,var(--atmo-3),transparent 80%)}

/* Без скрипта пятна не появятся вовсе — на всякий случай показываем среднее
   положение, чтобы фон не выглядел пустым. Скрипт эти значения перебивает. */
html:not(.js) .atmo i{opacity:.5}

/* ==========================================================================
   ШАР ИЗ ТОЧЕК
   Медленно вращающийся глобус из точек — из мудборда владельца.
   Собирает его app.js (блок 15): 12 меридианов и 5 параллелей, каждая —
   одна окружность с пунктиром из круглых точек. Крутится только родитель,
   поэтому браузеру нужно пересчитать 17 маленьких картинок, а не сотни точек.
   Пунктир задан через pathLength=100, так что число точек не зависит
   от радиуса и от размера шара.
   ========================================================================== */
.globe{
  position:absolute;pointer-events:none;
  width:var(--gs);height:var(--gs);
  perspective:900px;
  color:var(--text);
  opacity:var(--go,.4);
}
.globe > b{
  position:absolute;inset:0;
  transform-style:preserve-3d;
  animation:globe 38s linear infinite;
}
.globe i{position:absolute;inset:0}
.globe svg{display:block;width:100%;height:100%;overflow:visible}
.globe circle{
  fill:none;stroke:currentColor;stroke-width:1.7;
  stroke-linecap:round;stroke-dasharray:0 2.5;
}
@keyframes globe{
  from{transform:rotateX(-16deg) rotateY(0deg)}
  to{transform:rotateX(-16deg) rotateY(360deg)}
}
/* Цвет шар берёт от секции, а не от темы: раздел «Проекты» тёмный в обеих
   темах, и точки там всегда светлые. На светлой полосе было бы наоборот. */
.sec--deep .globe,.cta .globe{color:var(--deep-text)}
.globe--work{--gs:clamp(190px,25vw,330px);--go:.34;right:3%;top:clamp(64px,8vw,110px);z-index:-1}

/* ==========================================================================
   ПОТОК — витая лента из точек на первом экране.

   Холст лежит внутри самой обложки и уезжает вместе с ней: за человеком по
   странице лента не идёт. Рисует app.js (блок 16), здесь только место и сила.

   Мягкость сделана не размытием, а разрешением холста: скрипт рисует ленту
   заметно мельче, чем занимает холст, и браузер растягивает картинку сам.
   Точки от этого расплываются в мягкие пятнышки — как надо, — и считать их
   при этом нужно втрое меньше. Размытие фильтром стоило бы кадра работы
   видеокарте, а выглядело бы так же.
   ========================================================================== */
.flow{
  position:absolute;inset:0;z-index:-1;
  width:100%;height:100%;
  pointer-events:none;
  opacity:0;                     /* до первой отрисовки холст не показываем */
  /* Края растворяем, чтобы лента не обрывалась о границу экрана ножницами.
     Слева растворяем шире: там колонка текста. */
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 32%,#000 92%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 32%,#000 92%,transparent);
}

/* ==========================================================================
   УХОД ТЕМНОТЫ — стык тёмного раздела «Проекты» и того, что ниже.

   Полоса без единого предмета. Круглых предметов здесь перебывало три —
   воронка из мягких колец, затмение с волоском света по ободу и капля,
   обходящая обод, — и все завёрнуты владельцем. Причина, похоже, не в
   исполнении: прямо над этим местом, в «Проектах», уже висит шар из точек,
   и второй круг под ним читается как повтор. Больше предметов сюда
   не ставим.

   Работа у полосы осталась одна и важная: темнота «Проектов» не обрывается
   линией через всю страницу, а гаснет в бумагу. Спуск идёт по кривой —
   держится наверху, отпускает в середине и подходит к нулю почти
   касательно, поэтому обоих концов рампы глаз не находит. В нижней половине
   к нему добавлен тот же растр, что на стыках светлых полос: темнота не
   просто тает, а рассыпается на точки и уже точками сходит на нет.
   Высота без диска больше не нужна прежняя — полосе хватает своей длины
   на сам уход.
   ========================================================================== */
.swirl{
  position:relative;
  height:clamp(190px,22vw,300px);
  overflow:clip;
  pointer-events:none;
  --band:color-mix(in srgb,var(--deep) var(--veil-deep),transparent);
  background:linear-gradient(to bottom,
    var(--band) 0,
    color-mix(in srgb,var(--band) 93%,transparent) 24%,
    color-mix(in srgb,var(--band) 66%,transparent) 48%,
    color-mix(in srgb,var(--band) 32%,transparent) 70%,
    color-mix(in srgb,var(--band) 9%,transparent) 87%,
    transparent 100%);
}

/* ==========================================================================
   БЕГУЩАЯ СТРОКА
   Лента из ниш едет между разделами — очень медленно, без рывков и без
   ссылок: это фактура, а не навигация, поэтому она aria-hidden.
   Ряд удваивается скриптом, лента едет ровно на половину своей ширины —
   поэтому шов не виден. Без скрипта строка просто стоит на месте.

   Лента не растворяется в странице, а лежит на ней ПЛАНКОЙ. Раньше края
   гасила горизонтальная маска, и вместо ленты получалось грязное пятно;
   теперь у полосы своя подложка, а от страницы её отделяют четыре тени
   в одном свойстве:
     • блик по верхнему ребру — свет падает сверху, ребро его ловит;
     • притенение по нижнему ребру — оно у планки в тени;
     • мягкие тени наружу вверх и вниз — то самое «на воздухе».
   В темноте наружная тень почти не читается, и планку из фона вынимает
   подложка посветлее фона вместе с бликом (цвета — в палитре, --tick-*).
   Слова обрезает край экрана — так и надо: у планки есть край, и обрыв
   на нём читается как задуманный, а не как размытие.
   ========================================================================== */
.tick{
  position:relative;z-index:1;      /* планка лежит ПОВЕРХ соседних полос */
  overflow:hidden;
  padding:clamp(26px,3.6vw,46px) 0;
  background:var(--tick-bg);
  /* Тени идут парами: плотная у самого ребра и широкая дальняя. Одна дальняя
     тень даёт вялое пятно, у которого не видно, где именно край; ближняя
     ставит под ребром чёткую подпорку — и линия ребра начинает читаться как
     закрытый стык, а не как штрих поверх ровного места. Владелец просил
     «теней к линии побольше» — вот они. */
  box-shadow:inset 0 1px 0 var(--tick-edge),
             inset 0 -1px 0 var(--tick-under),
             0 -3px 9px -3px var(--tick-sh),
             0 4px 11px -3px var(--tick-sh),
             0 -22px 50px -26px var(--tick-sh),
             0 26px 56px -24px var(--tick-sh);
}
.tick__row{display:flex;align-items:center;gap:clamp(16px,2.6vw,36px);width:max-content}
.tick__row.is-loop{animation:tick 72s linear infinite}
.tick b{
  font-family:var(--f-display);font-weight:600;
  font-size:clamp(26px,4.2vw,52px);letter-spacing:-.02em;line-height:1.1;
  white-space:nowrap;
  color:color-mix(in srgb,var(--text) 28%,transparent);
}
.tick i{
  font-style:normal;font-size:clamp(14px,1.6vw,20px);
  color:var(--accent-text);opacity:.55;
}
@keyframes tick{
  from{transform:translate3d(0,0,0)}
  to{transform:translate3d(-50%,0,0)}
}

/* ==========================================================================
   СЕКЦИИ
   ========================================================================== */
.sec{padding:clamp(88px,11vw,176px) 0}

/* ПОЛОСЫ СЕКЦИЙ И СТЫКИ МЕЖДУ НИМИ.

   Полосы чуть прозрачны — ровно настолько, чтобы перелив фона был виден
   сквозь них. Плотность задана в палитре: --veil-soft / --veil-deep.

   Главное здесь — края. Раньше полоса начиналась и кончалась обрывом, и на
   каждом стыке через всю страницу шла линия: сверху один цвет, снизу другой.
   Теперь цвет полосы не обрывается, а растворяется: сверху он набирает силу,
   снизу отпускает. Двух соседних цветов встык больше нигде нет.

   Растворение сделано самим фоном (linear-gradient), а не отдельным слоем:
   маской пришлось бы закрывать всю секцию, а вместе с фоном она погасила бы
   и текст. У тёмной полосы растворение меньше: у неё светлые буквы, и если
   увести фон в прозрачность слишком высоко, верхние строки останутся на
   голой бумаге. Низ у неё вообще не гаснет — его подхватывает полоса ухода
   темноты, которая идёт следом.

   ФОРМА РАСТВОРЕНИЯ. Раньше это была почти прямая рампа с одной остановкой
   посередине, и у неё два излома — там, где растворение начинается, и там,
   где кончается. Самих цветов глаз на таком стыке не различает (разница
   между бумагой и полосой всего десяток ступеней яркости), а вот излом
   находит — и читает его как грязь. Теперь рампа идёт по S-образной кривой
   (стопы 16 / 50 / 84 процента на четвертях): к обоим концам она подходит
   почти касательно, изломов нет вовсе, и стык становится нечем зацепиться.
   Зона стала длиннее — на такой малой разнице цвета торопиться некуда. */
.sec--soft{
  --band:color-mix(in srgb,var(--surface-2) var(--veil-soft),transparent);
  --seam:clamp(96px,11.5vw,196px);
  background:linear-gradient(to bottom,
    transparent 0,
    color-mix(in srgb,var(--band) 16%,transparent) calc(var(--seam) * .25),
    color-mix(in srgb,var(--band) 50%,transparent) calc(var(--seam) * .5),
    color-mix(in srgb,var(--band) 84%,transparent) calc(var(--seam) * .75),
    var(--band) var(--seam),
    var(--band) calc(100% - var(--seam)),
    color-mix(in srgb,var(--band) 84%,transparent) calc(100% - var(--seam) * .75),
    color-mix(in srgb,var(--band) 50%,transparent) calc(100% - var(--seam) * .5),
    color-mix(in srgb,var(--band) 16%,transparent) calc(100% - var(--seam) * .25),
    transparent 100%);
}
.sec--deep{
  --band:color-mix(in srgb,var(--deep) var(--veil-deep),transparent);
  --seam:clamp(60px,6.5vw,116px);
  background:linear-gradient(to bottom,
    transparent 0,
    color-mix(in srgb,var(--band) 16%,transparent) calc(var(--seam) * .25),
    color-mix(in srgb,var(--band) 50%,transparent) calc(var(--seam) * .5),
    color-mix(in srgb,var(--band) 84%,transparent) calc(var(--seam) * .75),
    var(--band) var(--seam),
    var(--band) 100%);
  color:var(--deep-text);
}

/* РАСТР В ЗОНЕ СТЫКА.
   Одной кривой мало: растворение без единой зацепки — это ровно то самое
   «размыто, но не красиво». Поэтому край светлой полосы не тает в никуда,
   а РАССЫПАЕТСЯ: в зоне стыка проступает очень редкая сетка точек, гуще
   всего там, где цвет набрал половину силы, и сходит на нет к обоим краям
   зоны. Точка — родной язык этой страницы: из точек собран шар в «Проектах»
   и витая лента на обложке.

   Тем же растром рассыпается и уход темноты после «Проектов» — только там
   точки не светлого края полосы, а самой темноты (--seam-dot переопределён
   у .swirl), и живут они в нижней половине, где темнота и отпускает.

   Слой висит на ::before: у светлой полосы и у полосы ухода он свободен.
   У .sec--deep и .cta обе псевдострочки заняты (свет сверху и зерно), и два
   правила на один псевдоэлемент уже однажды затёрли друг друга — растр им
   и не нужен: там стык идёт по большой разнице яркости, его держит кривая.

   Сетка нарисована повторяющимся радиальным градиентом — ни картинки,
   ни запроса в сеть, рисуется один раз и не двигается. */
.sec--soft{position:relative;isolation:isolate}
.sec--soft::before,
.swirl::before{
  content:"";position:absolute;z-index:-1;inset:0;pointer-events:none;
  /* Два слоя точек со сдвигом на полшага — кладка, а не миллиметровка:
     ровная квадратная сетка сразу читается строками и столбцами. */
  background-image:
    radial-gradient(circle at 50% 50%,var(--seam-dot) 0 .9px,transparent 1.6px),
    radial-gradient(circle at 50% 50%,var(--seam-dot) 0 .9px,transparent 1.6px);
  background-size:16px 22px;
  background-position:0 0,8px 11px;
  -webkit-mask-image:var(--seam-mask);
  mask-image:var(--seam-mask);
}
/* Зона держится в пределах самого растворения: точки густеют там, где цвет
   набрал половину силы, и пропадают ровно к тому месту, где полоса набрала
   полную. Дальше в разделе идёт текст — там точкам делать нечего. */
.sec--soft::before{
  --seam-mask:linear-gradient(to bottom,
    transparent 0,
    #000 calc(var(--seam) * .44),
    transparent calc(var(--seam) * 1.02),
    transparent calc(100% - var(--seam) * 1.02),
    #000 calc(100% - var(--seam) * .44),
    transparent 100%);
}
/* У полосы ухода точки — это сама темнота, поэтому цвет берётся от --deep,
   а не от палитры стыков. Живут они в нижней половине: сверху темнота ещё
   сплошная, крошиться там нечему. */
.swirl::before{
  --seam-dot:color-mix(in srgb,var(--deep) 45%,transparent);
  --seam-mask:linear-gradient(to bottom,
    transparent 32%,
    #000 62%,
    transparent 100%);
}
.sec--deep .h2{color:var(--deep-text)}
.sec--deep .note{color:var(--deep-muted)}

/* --------------------------------------------------------------------------
   АТМОСФЕРА тёмных секций.
   Мудборд владельца — ночные кадры: тёплый свет ламп в темноте, световая
   полоса фонарей машины, зерно плёнки. Всё это рисуем средствами CSS:
   ни одной лишней картинки, ни одного запроса в сеть.
   -------------------------------------------------------------------------- */
.sec--deep,.cta,.swirl{position:relative;isolation:isolate}

/* тёплое световое пятно сверху — как лампа за кадром */
.sec--deep::before,.cta::before{
  content:"";position:absolute;z-index:-1;pointer-events:none;
  left:50%;translate:-50% 0;top:-14%;
  width:min(1100px,92%);height:62%;
  background:radial-gradient(ellipse at center,rgba(217,164,65,.13),transparent 68%);
}

/* Зерно: одна встроенная SVG-текстура, около 300 байт.
   Полосе затмения оно нужно по той же причине, что и тёмным секциям: иначе
   на стыке видно, что у соседних полос разная фактура, и граница читается
   даже при одинаковом цвете. */
.sec--deep::after,.cta::after,.swirl::after{
  content:"";position:absolute;z-index:-1;inset:0;pointer-events:none;
  opacity:.045;
  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='.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}
/* Зерно обязано таять там же, где тает сама полоса. Иначе цвет уже перетёк
   в соседний, а фактура ещё обрывается — и граница читается по ней,
   хотя цвета по обе стороны одинаковые. На эту мелочь и жаловался владелец:
   «на одной панели ингредиент есть, на другой нет». */
.sec--deep::after,.cta::after{
  --seam-mask:linear-gradient(to bottom,
    transparent 0,
    rgba(0,0,0,.16) calc(var(--seam) * .25),
    rgba(0,0,0,.5) calc(var(--seam) * .5),
    rgba(0,0,0,.84) calc(var(--seam) * .75),
    #000 var(--seam),#000 100%);
  -webkit-mask-image:var(--seam-mask);
  mask-image:var(--seam-mask);
}
/* Зерно уходит той же кривой, что и сама полоса, — иначе цвет уже перетёк
   в бумагу, а фактура ещё обрывается, и стык читается по ней. */
.swirl::after{
  -webkit-mask-image:linear-gradient(to bottom,#000 0,rgba(0,0,0,.93) 24%,rgba(0,0,0,.66) 48%,rgba(0,0,0,.32) 70%,rgba(0,0,0,.09) 87%,transparent 100%);
  mask-image:linear-gradient(to bottom,#000 0,rgba(0,0,0,.93) 24%,rgba(0,0,0,.66) 48%,rgba(0,0,0,.32) 70%,rgba(0,0,0,.09) 87%,transparent 100%);
}

.sec__head{max-width:46ch;margin-bottom:clamp(52px,6vw,88px)}
.sec__head .h2{max-width:24ch}

/* ==========================================================================
   ПЕРВЫЙ ЭКРАН
   ========================================================================== */
/* Заголовок во всю ширину, без большого скриншота: экран должен читаться
   как обложка, а не как витрина чужого сайта. */
/* overflow:clip держит внутри всё, что первый экран рисует поверх краёв —
   сейчас это тёплое пятно света. Одного overflow-x:clip у body мало: ширина
   документа считается по нему, и горизонтальная прокрутка всё равно
   появляется. Обрезаем там же, где вылезает. clip, а не hidden: hidden
   сделал бы секцию прокручиваемой и сломал бы липкие элементы внутри. */
.hero{position:relative;isolation:isolate;overflow:clip;
      padding:clamp(60px,7vw,116px) 0 clamp(80px,9vw,132px)}
.hero::before{
  content:"";position:absolute;z-index:-1;pointer-events:none;
  left:50%;translate:-50% 0;top:-10%;
  width:min(1100px,100%);height:110%;
  background:radial-gradient(50% 44% at 50% 42%,rgba(217,164,65,.16),transparent 70%);
}
.hero .display{font-size:clamp(42px,7.6vw,104px)}
.hero .lead{margin:0}

/* Лента из точек проходит прямо по мелкому тексту первого экрана — именно
   там она в полную силу. Крупному заголовку это не мешает: у него толстые
   штрихи. А мелкие строки получают мягкий ореол цвета фона: на ровном месте
   его не видно вовсе, но там, где под букву легла яркая точка, он
   возвращает букве её собственный фон, и края не размываются.
   Это дешевле и честнее, чем гасить ленту до невидимости. */
.hero .idx,.hero .lead,.hero .strip li{
  text-shadow:0 0 3px var(--bg),0 0 8px var(--bg);
}

.hero__bottom{display:grid;gap:30px;align-items:end;margin-top:clamp(36px,4vw,58px)}
.hero__bottom .row{margin-top:0}

/* ==========================================================================
   ПОРТРЕТ НА ОБЛОЖКЕ
   --------------------------------------------------------------------------
   Круг справа, снимок во весь рост. Показывали четыре рамки
   (круг, арка дуотоном, паспарту, мелкий круг в строке «00») — выбран круг;
   потом три способа поставить круг и человека друг к другу («обхват»,
   «нимб», «сдвиг») — выбран «обхват».

   ГЛАВНОЕ, слово владельца 13.08.2026: «низ должен быть в кругу, а верх
   может выходить за круг — верхняя полусфера выходит, нижняя нет». То есть
   ниже середины круга снимок обрезан его дугой, а выше не обрезан ничем:
   голова и плечи свободно выходят наружу. Фотография при этом целая, кадр
   не подрезан под голову — это разные вещи, и владелец на них указал прямо.

   Как это сделано. Маска из ДВУХ слоёв, они складываются (у mask-image
   слои по умолчанию объединяются, а не пересекаются):
     1) круг — ровно тот же, что нарисован диском под снимком;
     2) всё, что выше середины круга, — прямоугольник до верха кадра.
   Объединение и даёт «низ в круге, верх снаружи». Граница второго слоя
   проходит РОВНО через середину круга и никуда больше: там круг шире всего,
   и переход от «обрезано дугой» к «не обрезано» не виден. Поднимите границу
   выше середины — и по ней пойдёт горизонтальный срез через руки.
   По той же причине круг обязан быть не уже фигуры на своей середине:
   на этой высоте (59 % кадра) полуширина фигуры около 47,6 % ширины кадра,
   а полукруг — 51,5 %. Меняете --pic-d или --pic-dy — проверяйте это заново.

   Второе условие: низ круга должен приходиться ровно на низ кадра
   (59 % + 41 % = 100 %). Уйдёт выше — дуга начнёт срезать руки высоко,
   по самому широкому месту, и фигура будет выглядеть втиснутой в круг
   (владелец 13.08.2026: «я немного хуёво обрезан»). Уйдёт ниже — из-под
   дуги вылезет ровный нижний срез самой фотографии.

   Радиусы маски заданы дважды, потому что проценты по осям считаются
   от разных сторон: --pic-d — доля ШИРИНЫ кадра, а по высоте тот же радиус
   равен доле 0.7937 от неё (это отношение сторон кадра 900×1134).

   overflow:hidden тут по-прежнему нет: круглым окном фигуру не обрезать,
   иначе вместе с низом срежется и голова.

   Диск живёт в `.pic::before`, снимок — обычный `img` поверх него. Порядок
   слоёв держит z-index, а не порядок в разметке: у позиционированного
   псевдоэлемента приоритет выше, чем у непозиционированной картинки, и без
   z-index диск лёг бы НА лицо.

   Снимок цветной. Чёрно-белым он был, пока в круге было одно лицо; когда
   стала видна вся фигура, владелец попросил цвет — коричневый кардиган
   ложится в палитру страницы сам.

   Ещё: там, где портрет заезжает на витую ленту (блок 16 в app.js), лента
   приглушается — два узора в одном месте дерутся.
   ========================================================================== */
.pic{margin:0}
/* picture — только обёртка над источниками; пусть в раскладке её не будет
   вовсе, иначе местом снимка распоряжается она, а не сам снимок. */
.pic picture{display:contents}

/* Портрет сбоку отнимает у заголовка треть ширины. Если оставить прежний
   кегль, заголовок разваливается с трёх строк на пять и уводит кнопки
   и полосу фактов за нижний край — обложка перестаёт быть обложкой.
   Поэтому заголовок мельче: он занимает столько же вертикали, сколько
   занимал во всю ширину. */
.hero .display{font-size:clamp(36px,5.2vw,70px);max-width:62%}
.hero .lead{max-width:52ch}
.hero{--flow-op:.16}

/* Снимок — вырезка во весь рост (make_hero.py), 900×1134. Место головы
   в кадре оттуда же и напечатано при сборке: середина по ширине 50,3 %,
   макушка 0,7 % высоты, ширина головы 32,2 % ширины кадра. Отсюда и мерки
   круга ниже — если кадр пересоберут, числа надо сверить заново.

   Диск, кольцо и свечение собраны по двум референсам владельца: в темноте —
   резкое светящееся кольцо на тёмном диске, на свету — бледный диск с тёплым
   свечением из-за него. Край диска РЕЗКИЙ, мягкий только свет вокруг:
   «размытые края читаются как грязь» — давняя грабля проекта.

   ВАЖНО ПРО ОТСЧЁТ. Портрет вынут из потока, но точка отсчёта у него —
   НЕ обложка, а строка «00», внутри которой он лежит: `.idx` явно назначена
   точкой отсчёта через `position:relative`. Поэтому `top`
   отмеряется от строки «00», а `right` — от её правого края, то есть от края
   колонки .wrap, а не экрана. Это удобно и устойчиво: строка едет вместе
   с текстом обложки, а с включённым и выключенным JS отсчёт одинаковый.

   Ширина растёт медленнее экрана (26vw) НЕ на глаз. Снимок вытянутый
   (высота — 1,26 ширины), и при 33vw между 1024 и 1280 он обгоняет обложку
   и наезжает на полосу фактов: заголовок там ещё умещается в три строки,
   а снимок уже вымахал — замерено стендом, наезд доходил до 46 пикселей.
   Лечится долей vw, а не потолком: потолок узкие экраны не спасает.
   После любой правки размера снимка или кегля заголовка проверять зазор
   между низом снимка и линейкой фактов (сейчас не меньше 150 px). */
.hero .pic{
  display:block;position:absolute;z-index:1;
  right:0;
  /* Макушка совпадает со строкой «00 · разработка сайтов». Самые первые
     непрозрачные пиксели волос лежат на 2–3 px ниже края файла, поэтому
     сам прозрачный кадр начинается чуть выше строки. */
  top:-3px;
  width:clamp(230px,26vw,390px);
  /* высоту задаёт сам снимок: у .pic нет ни aspect-ratio, ни обрезки */

  /* Круг: поперечник в долях ширины кадра, середина — точкой внутри кадра.
     Тот же круг рисует диск и режет маска, поэтому мерки одни на двоих. */
  --pic-d:103%;--pic-dx:50.3%;--pic-dy:59%;
  /* тот же радиус по каждой оси: по ширине и, через отношение сторон, по высоте */
  --pic-rx:calc(var(--pic-d) / 2);
  --pic-ry:calc(var(--pic-d) / 2 * 0.7937);
}
.hero .pic img{
  position:relative;z-index:1;      /* поверх диска, см. шапку блока */
  display:block;width:100%;height:auto;
  /* Низ — по дуге круга, верх — свободно. Первый слой маски круг, второй —
     всё выше его середины; слои складываются. -webkit- нужен старым Safari,
     без него маски просто нет и снимок кончается ровным срезом. */
  -webkit-mask-image:
    radial-gradient(var(--pic-rx) var(--pic-ry) at var(--pic-dx) var(--pic-dy),
                    #000 99.4%,transparent 100%),
    linear-gradient(#000 0 var(--pic-dy),transparent var(--pic-dy));
  mask-image:
    radial-gradient(var(--pic-rx) var(--pic-ry) at var(--pic-dx) var(--pic-dy),
                    #000 99.4%,transparent 100%),
    linear-gradient(#000 0 var(--pic-dy),transparent var(--pic-dy));
}
/* Диск. Ставится не «куда-нибудь по центру», а по точке внутри снимка:
   --pic-dx / --pic-dy — середина круга в долях кадра, --pic-d — поперечник
   в долях ШИРИНЫ кадра (проценты у width всегда считаются от ширины,
   у top — от высоты, поэтому оси и разведены). */
.hero .pic::before{
  content:"";position:absolute;z-index:0;
  left:var(--pic-dx);top:var(--pic-dy);
  width:var(--pic-d);aspect-ratio:1;translate:-50% -50%;
  border-radius:50%;
  background:var(--pic-bg);
  box-shadow:
    0 0 0 var(--pic-ring-w,1.5px) var(--pic-ring),
    0 0 var(--pic-glow-r,90px) var(--pic-glow);
}

/* Телефон: сбоку места нет, портрет встаёт НАД строкой «00» — иначе подпись
   ужимается в четыре коротких строки сбоку от высокого снимка.

   Строка «00» тут перестаёт быть флексом. Во флексе портрет не умеет занять
   свою строку: главный размер задаст flex-basis, а не width, и снимок
   разъедется во всю колонку (на это уже наступали), а вместе с ним уедет
   и круг — он считает своё место в долях .pic. В обычном блоке снимок встаёт
   своей строкой и слушается width, а «00» отделяется от подписи полем.
   Линейка справа (.idx::after) в блоке остаётся пустым инлайном и пропадает
   сама: на телефоне подпись и так занимает всю ширину, ей там места нет. */
@media (max-width:860px){
  .hero .idx{display:block}
  .hero .idx i{margin-right:14px}
  .hero .pic{
    position:relative;inset:auto;
    width:clamp(200px,62vw,270px);
    /* прижат к правому краю: сбоку от текста на телефоне места нет,
       но сторона остаётся той же, что на большом экране */
    margin:0 0 18px auto;
    --pic-glow-r:52px;
  }
  .hero .display{max-width:none}
}

/* полоса фактов под первым экраном */
.strip{
  display:grid;
  margin-top:clamp(52px,6vw,86px);
  border-top:1px solid var(--line);
}
.strip li{padding:24px 0;border-bottom:1px solid var(--line)}
.strip b{
  display:block;font-family:var(--f-display);font-size:26px;font-weight:600;
  letter-spacing:-.01em;margin-bottom:6px;
}
.strip span{color:var(--muted);font-size:15.5px}

/* окно браузера — рамка для скриншота проекта */
.browser{
  border-radius:var(--r);overflow:hidden;
  background:var(--surface);border:1px solid var(--line);box-shadow:var(--sh-lg);
}
.browser__bar{
  display:flex;align-items:center;gap:7px;
  padding:11px 15px;background:var(--surface-2);border-bottom:1px solid var(--line);
}
.browser__bar i{width:9px;height:9px;border-radius:50%;background:var(--line-strong);flex:none}
.browser__url{
  margin-left:12px;padding:5px 14px;
  background:var(--surface);border:1px solid var(--line);border-radius:999px;
  font-size:12px;color:var(--muted);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.browser--dark{background:var(--deep-2);border-color:var(--deep-line)}
.browser--dark .browser__bar{background:#20291F;border-bottom-color:var(--deep-line)}
.browser--dark .browser__bar i{background:#3A473A}
.browser--dark .browser__url{background:#161D15;border-color:var(--deep-line);color:var(--deep-muted)}

/* ==========================================================================
   КАРТОЧКИ НИШ
   ========================================================================== */
/* perspective нужна карточкам ниш: без неё наклон за курсором остался бы
   плоским сжатием, а не поворотом. Величина большая нарочно — чем она больше,
   тем слабее перспектива, а наклон здесь всего в пару градусов. */
.cards{
  display:grid;gap:18px;grid-template-columns:repeat(auto-fit,minmax(248px,1fr));
  perspective:1200px;
}
.card{
  position:relative;overflow:hidden;
  padding:32px 28px 34px;
  background:var(--surface);
  border:1px solid var(--line);border-radius:var(--r-lg);
  /* Наклон пишется в отдельное свойство rotate, а не в transform: на transform
     висит подъём при наведении. Переход короткий — карточка должна идти
     за рукой, а не догонять её. */
  transition:transform .32s var(--ease),border-color .32s var(--ease),
             box-shadow .32s var(--ease),rotate .28s var(--ease);
}
.card:hover{transform:translateY(-5px);border-color:var(--accent);box-shadow:var(--sh)}

/* Световая полоса в углу карточки — у каждой ниши свой цвет.
   Отсылка к ночным кадрам из мудборда: фонарь машины, лампа, зелень склона. */
.card::before{
  content:"";position:absolute;top:30px;right:-18px;
  width:64px;height:3px;border-radius:999px;
  background:var(--glow,var(--accent));
  box-shadow:0 0 22px 3px var(--glow,var(--accent));
  opacity:.55;
  transition:width .4s var(--ease),opacity .4s var(--ease),right .4s var(--ease);
}
.card:hover::before{width:104px;right:-8px;opacity:.9}

/* Карточка с фотографией: снимок сверху, текст внизу поверх вуали.
   Вуаль почти непрозрачна там, где лежит текст, — иначе контраст поплывёт. */
.card--photo{
  isolation:isolate;
  min-height:clamp(360px,42vw,440px);
  display:flex;flex-direction:column;justify-content:flex-end;
  padding-top:clamp(120px,18vw,180px);
}
.card__bg{position:absolute;z-index:-2;inset:0}

/* Три кадра лежат стопкой в порядке разметки, показан самый верхний непрозрачный.
   Первый кадр непрозрачен всегда — он основание стопки. Это не мелочь: если
   гасить уходящий кадр одновременно с проявлением нового, в середине перехода
   суммарная непрозрачность просядет и сквозь снимки мигнёт фон карточки.
   Порядок наложения даёт сама разметка, z-index здесь не нужен. */
/* Переход короче интервала смены (3,2 с в app.js) с заметным запасом:
   иначе кадр не успевает постоять на месте и карточка «дышит» без остановки. */
.card__fr{
  position:absolute;inset:0;
  opacity:0;
  transition:opacity .9s var(--ease);
}
.card__fr.is-on{opacity:1}
.card__fr img{width:100%;height:100%;object-fit:cover}
.card--photo::after{
  content:"";position:absolute;z-index:-1;inset:0;
  background:linear-gradient(180deg,
    color-mix(in srgb,var(--surface) 12%,transparent) 0%,
    color-mix(in srgb,var(--surface) 76%,transparent) 46%,
    var(--surface) 70%);
}
.card--photo .card__n{margin-bottom:14px}

.card__n{
  display:block;margin-bottom:26px;
  font-size:12px;font-weight:700;letter-spacing:.16em;color:var(--accent-text);
  font-variant-numeric:tabular-nums;
}
.card__t{
  font-family:var(--f-display);font-size:24px;font-weight:600;
  line-height:1.15;letter-spacing:-.01em;margin-bottom:14px;
}
.card p{color:var(--muted);font-size:16px;line-height:1.55}

/* ==========================================================================
   РАСКЛАДКА «ЗАГОЛОВОК СЛЕВА + СПИСОК СПРАВА»
   ========================================================================== */
.split{display:grid;gap:clamp(38px,5vw,72px)}
.split__note{margin-top:22px;color:var(--muted);max-width:42ch}

/* Разделители внутри списка гашены до --line-soft: владелец 12.08.2026 показал
   эту линию как лишнюю — она делит пункты между собой, но висит посреди одного
   цвета и читается как случайный штрих. Своей палитрой, а не общим --line:
   тот же токен держит рамки карточек и линейки разделов, их трогать нельзя. */
.bullets li{
  display:grid;gap:8px;
  padding:26px 0;border-top:1px solid var(--line-soft);
}
.bullets li:last-child{border-bottom:1px solid var(--line-soft)}
.bullets b{
  font-family:var(--f-display);font-size:clamp(20px,2vw,25px);
  font-weight:600;letter-spacing:-.01em;
}
.bullets span{color:var(--muted);max-width:52ch}

.chips{display:flex;flex-wrap:wrap;gap:12px;align-content:flex-start}
.chips li{
  padding:14px 24px;
  background:var(--surface);border:1px solid var(--line);border-radius:999px;
  font-size:16.5px;
  transition:border-color .25s var(--ease),transform .25s var(--ease),color .25s var(--ease);
}
.chips li:hover{border-color:var(--accent);color:var(--accent-text);transform:translateY(-2px)}

/* ==========================================================================
   СПИСКИ С ГАЛОЧКАМИ
   ========================================================================== */
.ticks{display:grid;gap:12px}
.ticks li{position:relative;padding-left:32px;line-height:1.5}
.ticks li::before{
  content:"";position:absolute;left:0;top:.42em;
  width:15px;height:8px;
  border-left:2px solid var(--accent-text);border-bottom:2px solid var(--accent-text);
  transform:rotate(-45deg);
}
/* на тёмном и на бордовой карточке галочки золотые */
.sec--deep .ticks li::before,
.price__main .ticks li::before{border-color:var(--gold)}
.sec--deep .ticks li{color:#E7E2D6}

/* отзыв */
.quote{
  margin:0;padding:clamp(30px,4vw,50px);
  background:var(--deep-2);border:1px solid var(--deep-line);border-radius:var(--r-lg);
}
.quote blockquote{margin:0}
.quote p{
  margin:0;font-family:var(--f-display);
  font-size:clamp(20px,2.4vw,29px);line-height:1.36;letter-spacing:-.012em;
  color:#fff;max-width:42ch;
}
.quote figcaption{
  display:flex;align-items:center;gap:14px;flex-wrap:wrap;
  margin-top:32px;padding-top:26px;border-top:1px solid var(--deep-line);
}
.quote__ava{
  display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:44px;height:44px;border-radius:50%;
  background:var(--gold);color:var(--on-gold);
  font-family:var(--f-display);font-size:19px;font-weight:600;
}
.quote__who{display:grid;line-height:1.35}
.quote__who b{font-size:16px}
.quote__who i{font-style:normal;font-size:14.5px;color:var(--deep-muted)}
.quote__link{
  margin-left:auto;font-size:15px;font-weight:600;color:var(--deep-text);
  text-decoration:none;border-bottom:2px solid var(--gold);padding-bottom:2px;
  transition:color .2s var(--ease);
}
.quote__link:hover{color:var(--gold)}

/* ==========================================================================
   ЭТАПЫ
   ========================================================================== */
.steps{display:grid;gap:26px;grid-template-columns:repeat(auto-fit,minmax(232px,1fr))}
.step{
  display:grid;gap:12px;align-content:start;
  padding-top:24px;border-top:1px solid var(--line-strong);
}
.step__n{
  font-size:12px;font-weight:700;letter-spacing:.16em;color:var(--accent-text);
  font-variant-numeric:tabular-nums;
}
.step b{font-family:var(--f-display);font-size:22px;font-weight:600;line-height:1.2;letter-spacing:-.01em}
.step__d{color:var(--muted);font-size:16px;line-height:1.55}

/* ==========================================================================
   ЦЕНЫ
   ========================================================================== */
.price{display:grid;gap:24px;align-items:start}
.price__main,.price__side{padding:clamp(30px,3.6vw,48px);border-radius:var(--r-lg)}
.price__main{
  position:relative;overflow:hidden;
  background:var(--wine-card);color:#FBF3E8;
  box-shadow:var(--sh-lg);
}
.price__main::after{
  content:"";position:absolute;right:-110px;top:-110px;
  width:300px;height:300px;border-radius:50%;
  background:radial-gradient(closest-side,rgba(217,164,65,.32),transparent);
  pointer-events:none;
}
.price__side{background:var(--surface);border:1px solid var(--line)}

.price__label{
  margin:0 0 18px;font-size:12.5px;font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;color:var(--muted);
}
.price__main .price__label{color:rgba(251,243,232,.62)}

.price__sum{
  margin:0;font-family:var(--f-display);
  font-size:clamp(46px,6.2vw,86px);font-weight:600;
  letter-spacing:-.026em;line-height:1;
}
.price__sum--sm{
  display:flex;align-items:baseline;gap:14px;flex-wrap:wrap;
  font-size:clamp(36px,4.4vw,56px);
}
.price__sum--sm i{
  font-family:var(--f-ui);font-style:normal;font-size:15.5px;font-weight:600;
  color:var(--muted);letter-spacing:0;
}
.price__cap{margin:14px 0 32px;color:rgba(251,243,232,.7);font-size:16px}
.price__main .ticks li{color:#F3E9DC}
.price__main .ticks li::before{border-color:var(--gold)}
.price__side .ticks{margin-top:26px}
.price__side .note{margin-top:26px;padding-top:22px;border-top:1px solid var(--line)}

/* ==========================================================================
   ОБО МНЕ
   ========================================================================== */
.about{display:grid;gap:clamp(40px,5vw,64px);align-items:start}
.about__text .lead{margin:24px 0 22px}
.about__text p{color:var(--text-2);max-width:60ch}

/* Список фактов стоит во второй колонке раздела «Обо мне» — там, где
   раньше был портрет. На узком экране колонка одна, и он просто уходит
   под текст, поэтому верхний отступ ему нужен только там. */
.facts{display:grid;gap:0;border-top:1px solid var(--line)}
.facts li{
  display:flex;flex-wrap:wrap;align-items:baseline;gap:6px 18px;
  padding:18px 0;border-bottom:1px solid var(--line);
}
/* Подпись всегда со своей строки. В узкой колонке «11ch» работали через раз:
   короткие строки вставали рядом с подписью, длинная («Работаю удалённо») её
   переносила, и список получался рваным. */
.facts b{font-family:var(--f-display);font-size:21px;font-weight:600;width:100%}
.facts span{color:var(--muted);font-size:15.5px}

/* ==========================================================================
   FAQ
   ========================================================================== */
.faq{border-top:1px solid var(--line-strong)}
.faq details{border-bottom:1px solid var(--line-strong)}
.faq summary{
  display:flex;align-items:center;justify-content:space-between;gap:24px;
  padding:26px 0;cursor:pointer;list-style:none;
  font-family:var(--f-display);
  font-size:clamp(19px,2vw,23px);font-weight:600;letter-spacing:-.012em;line-height:1.3;
  transition:color .2s var(--ease);
}
.faq summary::-webkit-details-marker{display:none}
.faq summary:hover{color:var(--accent-text)}
.faq summary::after{
  content:"";flex:none;width:13px;height:13px;
  border-right:2px solid currentColor;border-bottom:2px solid currentColor;
  transform:translateY(-3px) rotate(45deg);
  transition:transform .25s var(--ease);
}
.faq details[open] summary::after{transform:translateY(2px) rotate(-135deg)}
.faq__a{padding:0 0 28px;max-width:70ch}
.faq__a p{color:var(--muted);font-size:16.5px}

/* ==========================================================================
   ФИНАЛЬНЫЙ ПРИЗЫВ
   ========================================================================== */
/* Цвет здесь тот же, что у подвала под ним, — иначе на их границе видна
   ступенька. Сверху полоса набирает силу так же, как у остальных секций. */
.cta{
  --band:color-mix(in srgb,var(--deep) var(--veil-deep),transparent);
  --seam:clamp(60px,6.5vw,116px);
  background:linear-gradient(to bottom,
    transparent 0,
    color-mix(in srgb,var(--band) 16%,transparent) calc(var(--seam) * .25),
    color-mix(in srgb,var(--band) 50%,transparent) calc(var(--seam) * .5),
    color-mix(in srgb,var(--band) 84%,transparent) calc(var(--seam) * .75),
    var(--band) var(--seam),
    var(--band) 100%);
  color:var(--deep-text);
  padding:clamp(84px,10vw,152px) 0;
}
/* Фон финального экрана: снимок уходит в атмосферу, текст остаётся читаемым.
   Верх снимка гасим маской — там полоса ещё прозрачная, и без маски картинка
   торчала бы над её краем прямоугольником. */
.cta__bg{
  position:absolute;z-index:-3;inset:0;overflow:hidden;
  -webkit-mask-image:linear-gradient(to bottom,transparent 0,#000 calc(var(--seam) * 2));
  mask-image:linear-gradient(to bottom,transparent 0,#000 calc(var(--seam) * 2));
}
.cta__bg img{width:100%;height:100%;object-fit:cover;opacity:.34}
/* Края снимка растворяем в цвете секции, иначе виден шов с соседними блоками.
   Слой висит на самом снимке, а не на ::after секции: там уже живёт зерно
   тёмных секций, и два правила на один псевдоэлемент затирали друг друга —
   растворение краёв не работало вовсе. */
.cta__bg::after{
  content:"";position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(180deg,
    var(--band) 0%,
    color-mix(in srgb,var(--band) 55%,transparent) 16%,
    color-mix(in srgb,var(--band) 70%,transparent) 74%,
    var(--band) 100%);
}
.cta__in{max-width:900px;margin:0 auto;text-align:center}
.cta .idx{justify-content:center}
.cta .idx::after{display:none}
.cta .display{color:#fff}
.cta__sub{margin:26px auto 0;max-width:52ch;color:var(--deep-muted);font-size:17.5px}
.cta__todo{margin:26px 0 0;font-size:15px;color:var(--deep-muted)}
.cta__todo code{background:rgba(255,255,255,.09);border-radius:6px;padding:2px 8px;font-size:14px}

/* ==========================================================================
   ПОДВАЛ
   ========================================================================== */
.ftr{
  background:color-mix(in srgb,var(--deep) var(--veil-deep),transparent);
  color:var(--deep-muted);padding:0 0 48px;
}
.ftr__in{display:grid;gap:26px;padding-top:48px;border-top:1px solid var(--deep-line)}
.ftr__name{
  margin:0;font-family:var(--f-display);font-size:24px;font-weight:600;
  color:var(--deep-text);
}
.ftr__name span{color:var(--gold)}
.ftr__city{margin:6px 0 0;font-size:15px}
.ftr__nav{display:flex;flex-wrap:wrap;gap:8px 24px}
.ftr__nav a,.ftr__contacts a{
  font-size:15px;text-decoration:none;color:#C3CCC4;
  transition:color .2s var(--ease);
}
.ftr__nav a:hover,.ftr__contacts a:hover{color:#fff}
.ftr__contacts{display:flex;flex-wrap:wrap;gap:8px 24px}
.ftr__contacts a{font-weight:600;border-bottom:2px solid var(--gold);padding-bottom:2px}
.ftr__todo{font-size:14px;opacity:.7}
.ftr__year{margin:0;font-size:14px;opacity:.7}

/* ==========================================================================
   ПОЯВЛЕНИЕ ПРИ ПРОКРУТКЕ
   ========================================================================== */
.js .reveal{
  opacity:0;transform:translateY(22px);
  transition:opacity .75s var(--ease),transform .75s var(--ease);
}
.js .reveal.is-in{opacity:1;transform:none}

/* Заголовки не всплывают, а проявляются снизу вверх — шторкой.

   Важно: маска висит на ДОЧЕРНЕМ заголовке, а следит за появлением родитель.
   Если закрыть маской сам наблюдаемый элемент, его видимая площадь станет
   нулевой, IntersectionObserver перестанет считать его видимым — и заголовок
   не раскроется никогда.

   Отрицательные отступы в clip-path нужны, чтобы не срезало хвосты курсива
   и выносные элементы букв (р, у, д).

   Это запасной вариант: как только app.js разберёт заголовки на слова, он
   ставит на <html> класс words — и шторка уступает место построчному выезду. */
.js .reveal--h{opacity:1;transform:none}
.js:not(.words) .reveal--h .display,
.js:not(.words) .reveal--h .h2,
.js:not(.words) .sec__head.reveal .h2{
  clip-path:inset(-14% -6% 102% -3%);
  transition:clip-path 1.05s var(--ease);
}
.js:not(.words) .reveal--h.is-in .display,
.js:not(.words) .reveal--h.is-in .h2,
.js:not(.words) .sec__head.reveal.is-in .h2{clip-path:inset(-14% -6% -14% -3%)}

/* --------------------------------------------------------------------------
   ЗАГОЛОВКИ ПО СЛОВАМ.
   app.js оборачивает каждое слово: .w — неподвижное окно, span внутри —
   само слово, оно выезжает снизу со сдвигом по времени (задержку скрипт
   пишет прямо в стиль слова).

   Почему окно режется clip-path, а не overflow:hidden: overflow у inline-block
   переносит его базовую линию на нижний край, и строка поехала бы вверх
   относительно остального текста. clip-path на раскладку не влияет вовсе.

   Нижняя граница окна опущена на .18em (padding-bottom, компенсированный
   отрицательным margin), иначе на месте срезало бы хвосты у, р, д — у
   заголовков межстрочный ровно 1.0, и буквы вылезают за строку.
   Поэтому же слово прячется на 140% своей высоты, а не на 100%: оно должно
   уйти ниже опущенной границы целиком.
   -------------------------------------------------------------------------- */
.js.words .w{
  display:inline-block;
  padding-bottom:.18em;margin-bottom:-.18em;
  clip-path:inset(-30% -8% 0 -8%);
}
.js.words .w > span{
  display:inline-block;
  transform:translateY(140%);
  transition:transform .95s var(--ease);
}
.js.words .reveal.is-in .w > span{transform:none}


/* ==========================================================================
   АДАПТИВ
   ========================================================================== */
@media (max-width:639px){
  .row .btn{width:100%}
  /* а вот второстепенные способы связи в полную ширину не растягиваем:
     четыре одинаковых полосы подряд читались бы как один длинный список */
  .row--soc .btn{width:auto;flex:1 1 auto;min-width:0}
}

@media (min-width:768px){
  .cards{grid-template-columns:repeat(2,1fr)}
  .price{grid-template-columns:1fr 1fr}
  .strip{grid-template-columns:repeat(3,1fr);border-bottom:1px solid var(--line)}
  .strip li{padding:26px 28px 26px 0;border-bottom:0}
  .strip li + li{padding-left:32px;border-left:1px solid var(--line)}
  .ftr__in{grid-template-columns:1fr auto;align-items:start}
  .ftr__nav{justify-content:flex-end}
  .ftr__contacts{grid-column:1}
  .ftr__year{grid-column:1/-1}
}

@media (min-width:900px){
  /* на большом экране плашка выше и отодвинута от краёв заметнее,
     а в компактном виде ещё и заметно сужается */
  :root{--hdr-gap:14px;--hdr-h:70px;--hdr-pad:26px}
  :root.stuck{--hdr-gap:18px;--hdr-h:54px;--hdr-w:330px;--hdr-pad:16px}

  /* Кнопки — под текстом, а не в правой колонке: справа стоит портрет,
     и вторая колонка наехала бы прямо на него. */
  .hero__bottom{grid-template-columns:minmax(0,1fr)}
  .split{grid-template-columns:minmax(0,.86fr) minmax(0,1.14fr)}
  .about{grid-template-columns:minmax(0,1.2fr) minmax(0,.8fr)}
  .price{grid-template-columns:minmax(0,1.08fr) minmax(0,.92fr)}
}

@media (min-width:1024px){
  .cards{grid-template-columns:repeat(4,1fr)}
  .split__l{position:sticky;top:118px}
}

/* --- узкий экран: подпись под именем не помещается между знаками --- */
@media (max-width:899px){
  .logo__sub{display:none}
  /* колонка одна — список фактов уходит под текст, ему нужен отступ сверху */
  .facts{margin-top:36px}
}

/* --- телефон --- */
@media (max-width:560px){
  body{font-size:16.5px}
  .wrap{padding:0 20px}
  .btn{min-height:56px}
  /* плашка уже отодвинута от краёв экрана — внутри неё отступы меньше,
     иначе знаки уезжают от края дальше, чем текст страницы */
  :root{--hdr-pad:14px}
  :root.stuck{--hdr-w:290px;--hdr-pad:12px}
  .tglr,.burger,.talk{width:44px;height:44px}
  .logo{font-size:21px}
  .sec__head{max-width:none}
  .card{padding:28px 24px 30px}
  .quote figcaption{gap:12px}
  .quote__link{margin-left:0;width:100%}
}

/* ==========================================================================
   МЕНЬШЕ ДВИЖЕНИЯ — уважаем системную настройку
   ========================================================================== */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.001ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.001ms !important;
  }
  .js .reveal{opacity:1;transform:none}
  /* шторки тоже отменяем: содержимое должно быть видно сразу */
  .js .reveal--h .display,.js .reveal--h .h2,.js .sec__head.reveal .h2{clip-path:none}
  /* app.js при этой настройке заголовки на слова не разбирает вовсе; правило
     на случай, если настройку включили уже после загрузки страницы */
  .js.words .w > span{transform:none}
  /* всё, что двигается само, останавливаем целиком, а не ускоряем:
     иначе лента доедет до конца кадра и встанет сдвинутой */
  .atmo,.tick__row,.globe > b{animation:none;transform:none}
  /* заставку показываем уже собранной и сразу убираем (app.js не ждёт) */
  .load__ring svg,.load__arc,.load__name{animation:none}
  .load__arc{stroke-dashoffset:0}
  .load__name{opacity:1}
  .card:hover,.chips li:hover{transform:none}
  /* наклон карточек скрипт при этой настройке не включает вовсе; правило
     на случай, если настройку включили уже после загрузки страницы —
     inline-стиль перебивается только так */
  .card{rotate:none !important}
  /* номер раздела и линейка показываются сразу, без проворота и прочерка */
  .js .idx i{rotate:none;opacity:1}
  .js .idx::after{transform:none}
  .btn::before{display:none}
}
