/* =========================================================================
   ПОРТФОЛИО ЖЕМАЛА ХАМИДУНА — дизайн-система
   -------------------------------------------------------------------------
   Основа: agent.nomadnet.ai (визуальный эталон владельца) — токены, сцены,
   типографика, ритм акцентов, светлые панели-паузы.
   Добавлено из hamidun.com/vibecoding: система растяжимости (--container + zoom),
   потому что владелец явно указал брать растяжимость оттуда.
   Новое здесь: галерея-слайдер, CTA-полосы, форма, лента кейсов, no-js фолбэк.
   ========================================================================= */

:root{
  /* ── палитра эталона (снята с живой страницы, не придумана) ── */
  --bg:#050506; --ink:#F4F5FA; --muted:#8A8C99;
  --electric:#3B6BFF; --cyan:#29A9FF; --gold:#F2C24E;
  --terra:#CC785C; --terra-ink:#B5613F; --terra-lite:#E2A182;
  --light-bg:#F4F1EA; --light-ink:#101322; --light-muted:#4c4f5e;

  --f:'Inter Tight',-apple-system,sans-serif;
  --mono:'JetBrains Mono',monospace;

  /* единый горизонтальный отступ: шапка, сцены, панели, подвал — на одной вертикали */
  --pad:clamp(20px,4.4vw,112px);
  --hdr-pad-y:clamp(16px,.5vw + 12px,26px);
  --hdr:calc(var(--hdr-pad-y) * 2 + 40px);   /* уточняется рантаймом */
  --hdr-air:clamp(22px,1.8vw,42px);

  /* ── растяжимость ──
     Требование владельца: «размер контента и текста растягивается под размер экрана,
     а НЕ наполняется воздухом при увеличении контейнеров».

     На /vibecoding это решено связкой `--container: min(92vw,1900px)` + `body{zoom}
/* Боковая прокрутка на узких экранах. Ниже 900 px документ выходил за
   экран на 20-50 px, и страница ездила вбок. Причина размазана: ни одна
   лента и ни одна секция по отдельности её не дают. `overflow-x` на body
   не помогает — браузер переносит его на вьюпорт, если у html своего нет.
   Обрезаем на main: `clip` вместо `hidden`, чтобы не создавать
   скролл-контейнер и не ломать липкие элементы внутри. */
main{overflow-x:clip}
`
     на 2200/2560. Здесь zoom НЕ используется, и вот почему: там он понадобился только
     потому, что потолок H1 равен 72px — на большом экране такой заголовок выглядит
     мелким, и zoom это компенсировал. У эталона потолок 132px берётся клампом напрямую,
     то есть та же задача решена без побочных эффектов.

     А побочки у zoom тяжёлые и все три уже стоили отладки:
       · ломает offsetTop/offsetHeight (rect даёт масштабированные px, offset — нет),
         из-за чего морф на 2560 срабатывал примерно на четверть экрана раньше;
       · ломает getComputedStyle().fontSize и documentElement.scrollHeight;
       · ломает позиционирование iframe-порталов — а вся витрина кейсов на iframe.
     Последнее здесь решающее.

     Итог: ширина тянется через --pad и --container, кегль — через двучленные clamp
     ниже. Оба растут с экраном, воздух не копится. */
  --container:min(92vw,1900px);

  --r:clamp(16px,.5vw + 10px,22px);          /* общий радиус карточек */
}

*{box-sizing:border-box;margin:0;padding:0}
html{scroll-behavior:auto}
body{background:var(--bg);color:var(--ink);font-family:var(--f);
  -webkit-font-smoothing:antialiased;overflow-x:hidden}
a{color:inherit;text-decoration:none}
img{max-width:100%;display:block}

/* ── фон: поле частиц под всем, зерно поверх ── */
#field{position:fixed;inset:0;width:100vw;height:100vh;z-index:0;pointer-events:none}
.grain{position:fixed;inset:0;z-index:1;pointer-events:none;opacity:.04;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E")}
.progress{position:fixed;left:0;top:0;height:2px;z-index:50;width:0;
  background:linear-gradient(90deg,var(--electric),var(--cyan),var(--gold))}

/* ── шапка ── */
header{position:fixed;top:0;left:0;right:0;z-index:40;padding:var(--hdr-pad-y) var(--pad);
  display:flex;align-items:center;justify-content:space-between;gap:18px}
header::before{content:'';position:absolute;inset:0 0 -22px 0;z-index:-1;pointer-events:none;
  background:linear-gradient(180deg,rgba(5,5,10,.88) 0%,rgba(5,5,10,.62) 50%,rgba(5,5,10,0) 100%);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  -webkit-mask-image:linear-gradient(180deg,#000 0%,#000 55%,transparent 100%);
  mask-image:linear-gradient(180deg,#000 0%,#000 55%,transparent 100%)}
.brand{display:flex;align-items:center;gap:clamp(10px,.25vw + 6px,15px);font-weight:700;
  font-size:clamp(19px,.42vw + 13px,29px);letter-spacing:-.02em;text-shadow:0 1px 10px rgba(5,5,8,.9)}
.brand span{color:var(--muted);font-weight:500;font-size:clamp(14px,.3vw + 9.5px,21px)}
.hdr-right{display:flex;align-items:center;gap:clamp(20px,.7vw + 10px,38px)}
.hdr-link{font-size:clamp(13px,.26vw + 9px,19px);letter-spacing:.02em;opacity:.7;transition:opacity .3s}
.hdr-link:hover{opacity:1}
.nav-cta{font-family:var(--mono);font-size:clamp(12px,.24vw + 8.5px,17px);text-transform:uppercase;
  letter-spacing:.14em;padding:clamp(11px,.3vw + 7px,18px) clamp(20px,.6vw + 12px,34px);
  border-radius:30px;background:var(--ink);color:var(--bg);font-weight:500}
@media(max-width:860px){.hdr-link{display:none}}

/* ── морф-сцены ── */
main{position:relative;z-index:2}
.scene{min-height:100vh;display:flex;align-items:safe center;
  padding:calc(var(--hdr) + var(--hdr-air)) var(--pad);position:relative}
.scene.right{justify-content:flex-end;text-align:right}
.scene.center{justify-content:center;text-align:center}
.wrap{max-width:clamp(560px,48vw,1080px);position:relative;margin-block:auto}

/* Читаемость поверх частиц — только тенью, без тёмной вуали:
   вуаль глушила бы сами частицы, ради которых всё и затевалось. */
.wrap h1,.wrap h2{text-shadow:0 2px 4px rgba(5,5,8,.96),0 3px 20px rgba(5,5,8,.9),0 0 46px rgba(5,5,8,.82)}
.lead{text-shadow:0 1px 3px rgba(5,5,8,.98),0 2px 14px rgba(5,5,8,.94)}

.eyebrow{font-family:var(--mono);font-size:clamp(12px,.25vw + 8.8px,16px);letter-spacing:.24em;
  text-transform:uppercase;color:var(--cyan);margin-bottom:clamp(24px,.6vw + 16px,34px);
  display:flex;align-items:center;gap:12px;text-shadow:0 1px 9px rgba(5,5,8,.96)}
.eyebrow::before{content:'';width:30px;height:1px;background:var(--gold)}
.scene.right .eyebrow{justify-content:flex-end}
.scene.right .eyebrow::before{order:2}
.scene.center .eyebrow{justify-content:center}

/* Кегль и колонка — одно решение: заголовок держится в 3-4 строки на любой ширине.
   Двучленный clamp (vw + px), как на /vibecoding: средние экраны не разносит, а на
   1920/2560 кегль не упирается в потолок, который читался бы мелко. */
h1{font-size:clamp(48px,5.65vw + 3px,132px);font-weight:600;line-height:.96;letter-spacing:-.035em;text-wrap:balance}
h2{font-size:clamp(46px,5.6vw + 3px,131px);font-weight:600;line-height:.96;letter-spacing:-.03em;text-wrap:balance}

/* Два акцента, чередуются по сценам: синий — нечётные, терракота — .warm.
   background-image, не сокращение background: сокращение сбрасывает background-clip,
   и акцент рисуется сплошным прямоугольником. */
h1 .g,h2 .g,h1 .t,h2 .t{-webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent;-webkit-box-decoration-break:clone;
  box-decoration-break:clone;text-shadow:none}
h1 .g,h2 .g{background-image:linear-gradient(105deg,#2549C7 0%,#2E62F1 28%,#407FF8 55%,#2896E1 78%,#4FB8FF 100%)}
h1 .t,h2 .t,.scene.warm h2 .g{background-image:linear-gradient(105deg,#8E4526 0%,#B5613F 26%,#CC785C 54%,#DD9670 78%,#F0C9A4 100%)}
.scene.warm .eyebrow{color:var(--terra-lite)}
.scene.warm .eyebrow::before{background:var(--terra-ink)}

.lead{margin-top:clamp(26px,.66vw + 17px,34px);font-size:clamp(17px,.62vw + 14px,30px);
  line-height:1.48;color:#C3C6D4;max-width:52ch}
.scene.right .lead{margin-left:auto}
.actions{margin-top:clamp(30px,.9vw + 17px,54px);display:flex;gap:clamp(14px,.4vw + 8px,22px);flex-wrap:wrap}
.scene.right .actions{justify-content:flex-end}
.scene.center .actions{justify-content:center}
.btn{font-family:var(--mono);font-size:clamp(12px,.28vw + 8.4px,17px);text-transform:uppercase;
  letter-spacing:.14em;padding:clamp(16px,.42vw + 10px,26px) clamp(28px,.8vw + 16px,46px);
  border-radius:36px;font-weight:500;transition:transform .25s,background .25s,border-color .25s;
  display:inline-block;cursor:pointer;border:0}
.btn--p{background:var(--electric);color:#fff}
.btn--p:hover{transform:translateY(-2px);background:#5B84FF}
.btn--g{border:1px solid rgba(255,255,255,.22);color:var(--ink);background:none}
.btn--g:hover{border-color:var(--cyan)}
.metric{margin-top:clamp(30px,.7vw + 20px,48px);font-family:var(--mono);
  font-size:clamp(13px,.3vw + 9px,19px);color:var(--muted);
  display:flex;gap:clamp(18px,.6vw + 10px,34px);flex-wrap:wrap}
.metric b{color:var(--ink)}
.scene.right .metric{justify-content:flex-end}

/* ── плоские панели (галереи, лента, форма) ── */
.panel{position:relative;z-index:2;padding:clamp(80px,5vw,120px) var(--pad)}
.panel-in{max-width:var(--container);margin:0 auto}
.panel .eyebrow{text-shadow:none}
.panel h2{font-size:clamp(32px,2.2vw + 34px,90px)}
.panel .sub{margin-top:18px;font-size:clamp(16px,.43vw + 13px,24px);line-height:1.55;
  color:var(--muted);max-width:56ch}
.panel--light{background:var(--light-bg);color:var(--light-ink)}
.panel--light .eyebrow{color:#2E62F1}
.panel--light .eyebrow::before{background:#B98A1E}
.panel--light .sub{color:var(--light-muted)}
.panel--light h2{text-shadow:none}
.panel--light h2 .g{background-image:linear-gradient(105deg,#1B3AA8 0%,#2549C7 30%,#2E62F1 60%,#2896E1 100%)}

/* ── ГАЛЕРЕЯ-СЛАЙДЕР ──
   Листается человеком, не таймером: ни автоигры, ни бесконечной прокрутки.
   Скролл-снап + стрелки; на тач-устройствах работает свайпом штатно. */
.gal{margin-top:clamp(36px,2vw + 18px,64px);position:relative}
.gal-track{display:flex;gap:clamp(16px,1vw + 8px,28px);overflow-x:auto;scroll-snap-type:x mandatory;
  scroll-behavior:smooth;padding-bottom:12px;scrollbar-width:none}
.gal-track::-webkit-scrollbar{display:none}
.gal-card{scroll-snap-align:start;flex:0 0 clamp(280px,26vw,460px);
  background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.12);
  border-radius:var(--r);overflow:hidden;transition:transform .3s,border-color .3s;
  display:flex;flex-direction:column}
.panel--light .gal-card{background:#fff;border-color:rgba(16,19,34,.10);
  box-shadow:0 10px 34px rgba(16,19,34,.07)}
.gal-card:hover{transform:translateY(-4px);border-color:var(--cyan)}
.gal-shot{aspect-ratio:16/10;background:#0b0d16;overflow:hidden;position:relative}
.panel--light .gal-shot{background:#e9e5db}
.gal-shot img{width:100%;height:100%;object-fit:cover;object-position:top center}
.gal-body{padding:clamp(18px,.8vw + 10px,28px);display:flex;flex-direction:column;gap:10px;flex:1}
.gal-tag{font-family:var(--mono);font-size:clamp(10px,.18vw + 8px,13px);text-transform:uppercase;
  letter-spacing:.16em;color:var(--gold)}
.panel--light .gal-tag{color:#B98A1E}
.gal-card h3{font-size:clamp(18px,.55vw + 13px,28px);font-weight:600;line-height:1.22}
.gal-card p{font-size:clamp(14px,.34vw + 10.5px,20px);line-height:1.5;color:var(--muted)}
.panel--light .gal-card p{color:var(--light-muted)}
/* цифра-доказательство: то, ради чего карточку читают */
.gal-fact{margin-top:auto;padding-top:12px;font-family:var(--mono);
  font-size:clamp(13px,.3vw + 9.5px,18px);color:var(--cyan);line-height:1.4}
.panel--light .gal-fact{color:#2549C7}
.gal-link{font-family:var(--mono);font-size:clamp(11px,.2vw + 8.5px,14px);letter-spacing:.1em;
  text-transform:uppercase;color:var(--muted);border-top:1px solid rgba(255,255,255,.1);
  padding:14px clamp(18px,.8vw + 10px,28px);transition:color .25s}
.panel--light .gal-link{border-top-color:rgba(16,19,34,.09);color:var(--light-muted)}
.gal-link:hover{color:var(--cyan)}
.gal-nav{display:flex;gap:10px;margin-top:22px;justify-content:flex-end}
.gal-nav button{width:clamp(40px,1.4vw + 28px,56px);aspect-ratio:1;border-radius:50%;
  border:1px solid rgba(255,255,255,.2);background:none;color:inherit;cursor:pointer;
  font-size:18px;line-height:1;transition:border-color .25s,opacity .25s}
.panel--light .gal-nav button{border-color:rgba(16,19,34,.18)}
.gal-nav button:hover:not(:disabled){border-color:var(--cyan)}
.gal-nav button:disabled{opacity:.28;cursor:default}

/* живое демо в галерее — рамка под iframe */
.demo-frame{margin-top:clamp(36px,2vw + 18px,64px);border-radius:var(--r);overflow:hidden;
  border:1px solid rgba(16,19,34,.12);background:#fff}
.demo-frame iframe{width:100%;border:0;display:block}

/* ── БЛОК ВОПРОСОВ ──
   Правила были в эталоне, но в этот файл не переехали — карточки выводились
   голым текстом без сетки и рамок. Фон плотный и с блюром намеренно: за
   карточками проходит яркая морф-форма, на полупрозрачном фоне текст в неё
   проваливался. */
.obj-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(clamp(250px,20vw,420px),1fr));
  gap:clamp(18px,.9vw + 8px,32px);margin-top:clamp(40px,2vw + 22px,72px)}
/* Карточка «а если ты уйдёшь» отвечает на главный вопрос осторожного
   заказчика и стоит последней. В сетке из четырёх колонок она оставалась
   одна в ряду, с пустотой справа; растягиваем её на всю строку — заодно
   это ставит на ней смысловое ударение. */
.obj-card--wide{grid-column:1/-1}

.obj-card{border:1px solid rgba(255,255,255,.16);border-radius:var(--r);
  padding:clamp(28px,1.1vw + 16px,42px) clamp(26px,1vw + 15px,40px);
  background:rgba(7,8,13,.90);box-shadow:0 18px 50px rgba(0,0,0,.45);
  -webkit-backdrop-filter:blur(14px) saturate(.9);backdrop-filter:blur(14px) saturate(.9)}
.obj-card .q{font-family:var(--mono);font-size:clamp(11px,.2vw + 9px,14px);color:var(--gold);
  text-transform:uppercase;letter-spacing:.14em;margin-bottom:clamp(14px,.4vw + 8px,20px);line-height:1.5}
.obj-card h3{font-size:clamp(18px,.55vw + 13px,28px);font-weight:600;line-height:1.25;
  margin-bottom:clamp(10px,.3vw + 6px,16px)}
.obj-card p{color:var(--muted);font-size:clamp(14px,.38vw + 10.5px,21px);line-height:1.6}

/* ── ФОРМА: та же подложка ──
   Форма стоит на тёмной секции поверх поля частиц, и поля ввода без подложки
   читались как текст на движущемся фоне. */
#form .panel-in{position:relative}
#form .form-grid{padding:clamp(20px,1.2vw + 12px,34px);border-radius:var(--r);
  background:rgba(7,8,13,.86);border:1px solid rgba(255,255,255,.10);
  -webkit-backdrop-filter:blur(16px) saturate(.95);backdrop-filter:blur(16px) saturate(.95);
  box-shadow:0 18px 50px rgba(0,0,0,.40)}

/* ── CTA-полоса: разные формулировки, одна форма ── */
/* ── CTA ──
   Раньше это была тонкая полоса с двумя линейками — на тёмной странице поверх
   частиц она просто не читалась как отдельный блок и терялась между секциями.
   Теперь у неё своя карточка: плотная подложка, лёгкая синяя подсветка от акцента
   и заметная кнопка. */
.cta{position:relative;z-index:2;padding:clamp(40px,2.4vw + 22px,80px) var(--pad)}
.cta-in{max-width:var(--container);margin:0 auto;display:flex;align-items:center;
  justify-content:space-between;gap:clamp(24px,2vw,56px);flex-wrap:wrap;
  padding:clamp(28px,1.8vw + 16px,52px) clamp(26px,1.8vw + 15px,56px);
  border-radius:clamp(18px,.7vw + 12px,26px);
  background:
    radial-gradient(120% 160% at 0% 0%,rgba(59,107,255,.18),rgba(59,107,255,0) 62%),
    rgba(9,10,17,.90);
  border:1px solid rgba(59,107,255,.30);
  -webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);
  box-shadow:0 22px 60px rgba(0,0,0,.45),inset 0 1px 0 rgba(255,255,255,.06)}
.cta h3{font-size:clamp(24px,1.4vw + 15px,44px);font-weight:600;line-height:1.14;
  letter-spacing:-.02em;max-width:22ch}
.cta p{margin-top:12px;color:#B9BDCC;font-size:clamp(15px,.36vw + 11px,21px);max-width:46ch}
.cta .btn{flex:0 0 auto}

/* ── лента: все карточки кейсбука строками ── */
.feed{margin-top:clamp(32px,1.6vw + 18px,56px)}
.feed-filters{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:26px}
.feed-filters button{font-family:var(--mono);font-size:clamp(11px,.2vw + 8.5px,14px);
  text-transform:uppercase;letter-spacing:.1em;padding:9px 16px;border-radius:20px;
  border:1px solid rgba(255,255,255,.18);background:none;color:inherit;cursor:pointer;
  opacity:.6;transition:opacity .2s,border-color .2s}
.feed-filters button.on,.feed-filters button:hover{opacity:1;border-color:var(--cyan)}
.feed-row{display:grid;grid-template-columns:auto 1fr auto;gap:clamp(14px,1vw + 6px,28px);
  align-items:baseline;padding:clamp(14px,.5vw + 9px,20px) 0;
  border-bottom:1px solid rgba(255,255,255,.07)}
.feed-id{font-family:var(--mono);font-size:clamp(11px,.2vw + 8.5px,14px);color:var(--gold);min-width:3.5em}
.feed-what{font-size:clamp(14px,.36vw + 10.5px,21px);line-height:1.45}
.feed-cat{font-family:var(--mono);font-size:clamp(10px,.18vw + 8px,13px);
  text-transform:uppercase;letter-spacing:.1em;color:var(--muted);white-space:nowrap}
@media(max-width:760px){
  .feed-row{grid-template-columns:auto 1fr}
  .feed-cat{grid-column:2;color:#6b6d7a}
}

/* ── форма ── */
.form-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(clamp(240px,22vw,380px),1fr));
  gap:clamp(16px,.8vw + 8px,26px);margin-top:clamp(32px,1.6vw + 18px,52px)}
.field{display:flex;flex-direction:column;gap:9px}
.field label{font-family:var(--mono);font-size:clamp(11px,.2vw + 8.5px,14px);
  text-transform:uppercase;letter-spacing:.12em;color:var(--muted)}
.field input,.field textarea,.field select{font-family:var(--f);
  font-size:clamp(15px,.36vw + 11px,20px);padding:clamp(13px,.4vw + 9px,19px) clamp(15px,.45vw + 10px,22px);
  border-radius:12px;border:1px solid rgba(255,255,255,.18);background:rgba(255,255,255,.04);
  color:var(--ink);transition:border-color .25s}
.field input:focus,.field textarea:focus,.field select:focus{outline:none;border-color:var(--cyan)}
.field textarea{min-height:130px;resize:vertical}
.field--wide{grid-column:1/-1}
.form-note{margin-top:18px;font-size:clamp(13px,.28vw + 10px,17px);color:var(--muted)}
.form-note a{color:var(--cyan)}

/* ── подпись и подвал ── */
.signature{position:relative;height:60vh;min-height:380px;display:flex;align-items:center;justify-content:center}
#sig{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
footer{position:relative;z-index:2;padding:60px var(--pad) 50px;border-top:1px solid rgba(255,255,255,.08);
  display:flex;justify-content:space-between;align-items:flex-end;flex-wrap:wrap;gap:30px;
  color:var(--muted);font-size:clamp(13px,.26vw + 10px,18px)}
footer a:hover{color:var(--ink)}
.foot-links{display:flex;gap:26px;font-family:var(--mono);font-size:clamp(11px,.2vw + 8.5px,14px);
  text-transform:uppercase;letter-spacing:.1em}

/* ── прелоадер ──
   У эталона он снимается только успешной инициализацией WebGL — и без GPU
   висит вечно чёрным экраном. Здесь снимается по таймауту и по ошибке тоже
   (логика в portfolio.js), CSS только оформляет. */
.loading{position:fixed;inset:0;z-index:90;background:var(--bg);display:flex;
  align-items:center;justify-content:center;font-family:var(--mono);
  font-size:clamp(11px,.2vw + 8.5px,15px);letter-spacing:.22em;text-transform:uppercase;
  color:var(--muted);transition:opacity .7s}
.loading.done{opacity:0;pointer-events:none}
.scrollhint{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);z-index:30;
  font-family:var(--mono);font-size:clamp(9px,.16vw + 7px,12px);letter-spacing:.2em;
  text-transform:uppercase;color:#4a4c5a;transition:opacity .4s}
@media(max-width:760px){.scrollhint{display:none}}

/* ── появление блоков ──
   Без JS всё видно: html.no-js снимает reveal целиком. Иначе страница без JS
   была бы пустой — это уже случалось на /vibecoding и чинилось этим же приёмом. */
.reveal{opacity:0;transform:translateY(24px);transition:opacity .7s,transform .7s}
.reveal.is-in{opacity:1;transform:none}
html.no-js .reveal{opacity:1!important;transform:none!important}
html.no-js .loading{display:none!important}
html.no-js #field{display:none}
@media (prefers-reduced-motion:reduce){
  .reveal{opacity:1!important;transform:none!important;transition:none}
  .gal-track{scroll-behavior:auto}
}

/* ═══ ВИТРИНА: барабан кейсов × экраны кейса ═══════════════════════════════
   Вертикаль — барабан слева, крутится колесом. Горизонталь — экраны справа.
   Геометрия барабана держится на трёх согласованных числах (радиус и шаг
   заданы в reel.js, высота слота здесь): вертикальный разнос соседей
   340 × sin20° ≈ 116px должен быть больше высоты слота 106px, иначе
   карточки наезжают друг на друга. */
/* Витрина стоит поверх поля частиц, и без подложки облако просвечивает сквозь
   интерфейс: барабан теряется, а экран продукта читается как через помехи.
   Эталон решает это непрозрачной светлой панелью; здесь панель тёмная, поэтому
   подложка — плотная затемняющая вуаль, которая гасит частицы под контентом,
   но оставляет их по краям кадра. */
/* Верхний отступ обязан быть больше фиксированной шапки, иначе ряд вкладок
   уезжает под неё и клики достаются шапке: вкладка «нажимается» только
   со второго раза или не нажимается вовсе. Меру берём ту же, что на
   лендингах, — высота шапки плюс её воздух. */
.reel{min-height:100vh;display:flex;align-items:center;align-items:safe center;
  position:relative;
  padding:calc(var(--hdr) + var(--hdr-air) + 14px) var(--pad) clamp(40px,5vh,80px);
  overflow:hidden;z-index:2}
.reel::before{content:'';position:absolute;inset:0;z-index:-1;pointer-events:none;
  background:radial-gradient(120% 80% at 50% 50%,rgba(5,5,6,.97) 40%,rgba(5,5,6,.86) 70%,rgba(5,5,6,.5) 100%)}
/* Слой абсолютный и во всю ширину секции. Нажимать в нём нечего —
   надпись и счётчик, — а клики он перехватывал: через полсекунды после
   переключения накрывал ряд вкладок, и следующая вкладка не нажималась. */
.reel-head{position:absolute;top:clamp(24px,4vh,54px);left:var(--pad);right:var(--pad);
  pointer-events:none;
  display:flex;justify-content:space-between;align-items:baseline;gap:20px;flex-wrap:wrap}
.reel-count{font-family:var(--mono);font-size:clamp(12px,.24vw + 9px,17px);
  color:var(--muted);letter-spacing:.1em}
/* Колонки равняются ПО ВЕРХУ: высоту и отступ барабану считает скрипт, ставя
   его ровно напротив кадра. При выравнивании по центру барабан центрировался
   относительно всей правой колонки — а в ней ещё вкладки, точки и подписи, —
   и активная карточка уезжала ниже кадра. */
.reel-in{display:grid;gap:clamp(24px,3vw,64px);width:100%;align-items:start;
  grid-template-columns:minmax(240px,clamp(260px,22vw,400px)) minmax(0,1fr);
  max-width:var(--container);margin:0 auto}

/* Высоту и отступ сверху барабану задаёт скрипт: он равняет колонку РОВНО по
   кадру. Колонка стоит в сетке рядом со «сценой», а в сцене кроме кадра ещё
   строка вкладок, точки, подпись и задача, — при выравнивании по центру сцены
   активная карточка уезжала ниже кадра. */
.drum{position:relative;height:clamp(380px,58vh,640px);perspective:1100px;
  /* Растворение краёв — МАСКОЙ, а не шторками поверх. Шторки были нарисованы
     внутри барабана: карточку, вылезшую за его высоту, они не доставали — та
     проявлялась снова, и низ колонки выглядел сломанным. Хуже того, красили
     они СПЛОШНЫМ цветом фона поверх облака частиц, и получалась тёмная плита
     с видимой границей. Маска гасит сам барабан вместе со всем, что за его
     границей, а фон под ним остаётся живым. */
  -webkit-mask-image:linear-gradient(180deg,transparent 0,#000 20%,#000 80%,transparent 100%);
  mask-image:linear-gradient(180deg,transparent 0,#000 20%,#000 80%,transparent 100%);
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-size:100% 100%;mask-size:100% 100%}
/* сцена отодвинута назад ровно на радиус: активный слот тогда стоит в плоскости
   экрана и не раздувается перспективой */
.drum-stage{position:absolute;inset:0;transform-style:preserve-3d;transform:translateZ(-380px)}
/* Высота слота — не украшение, а вместимость. При 106 пикселях карточка
   держала номер, заголовок в ОДНУ строку и две строки описания. Стоило
   заголовку перенестись («Phoenix — CRM налогового консультанта»), и описание
   срезалось посреди букв, а подложка активной карточки обводила этот обрез
   рамкой — выходила плита, наехавшая на соседнюю. Считаем по худшему случаю:
   номер 11 + заголовок 2×30 + описание 2×21 + отступы 24 ≈ 150.
   Разнос соседей завязан на ту же величину: RADIUS × sin(STEP_DEG). При
   STEP_DEG 20° он давал 116 — меньше самой карточки. */
.slot{position:absolute;left:0;right:0;top:50%;height:150px;margin-top:-75px;
  transform-origin:50% 50%;backface-visibility:hidden;overflow:hidden;cursor:pointer;
  padding:14px 18px;border-radius:14px;border:1px solid transparent;
  transition:transform .42s cubic-bezier(.22,.61,.36,1),opacity .42s,filter .42s,
             border-color .3s,background .3s,box-shadow .3s}
.slot .num{font-family:var(--mono);font-size:11px;letter-spacing:.16em;color:var(--gold);opacity:.85}
.slot h3{font-size:clamp(17px,.5vw + 13px,25px);font-weight:600;line-height:1.2;margin-top:6px}
/* описание строго в две строки: третья обрезалась бы посреди буквы */
.slot p{font-size:clamp(12px,.24vw + 9.5px,15px);color:var(--muted);margin-top:6px;line-height:1.4;
  display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.slot.is-active{border-color:rgba(59,107,255,.5);background:rgba(59,107,255,.07)}
.slot.is-active h3{color:#fff}
.slot:not(.is-active){filter:saturate(.55)}
.slot:focus-visible{outline:2px solid var(--cyan);outline-offset:2px}
.drum-hint{position:absolute;left:50%;bottom:-80px;transform:translateX(-50%);z-index:4;
  font-family:var(--mono);font-size:10px;letter-spacing:.18em;text-transform:uppercase;
  color:#4a4c5a;white-space:nowrap;transition:opacity .4s}

.stage{min-width:0}
.tabs{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px}
.tabs button{font-family:var(--mono);font-size:11px;letter-spacing:.12em;text-transform:uppercase;
  padding:8px 15px;border-radius:20px;border:1px solid rgba(255,255,255,.18);background:none;
  color:inherit;cursor:pointer;opacity:.55;transition:opacity .2s,border-color .2s}
.tabs button.on,.tabs button:hover{opacity:1;border-color:var(--cyan)}
/* Экран продукта: непрозрачный, иначе частицы просвечивают сквозь интерфейс.
   Пропорция 16/10 — но не ниже 520px по высоте: интерфейсы продуктов свёрстаны
   под десктоп, и в низкой рамке их содержимое схлопывается в узкую колонку. */
/* Пропорция 16/10, но ширина главнее: min-height вместе с aspect-ratio диктует
   ширину (высота × 1.6), и на 768px рамка вырастала до 852px — шире вьюпорта,
   край уезжал под обрез. Поэтому min-height только там, где для него есть место. */
.screen{position:relative;border-radius:16px;overflow:hidden;background:#0b0d16;
  border:1px solid rgba(255,255,255,.14);aspect-ratio:16/10;width:100%;max-width:100%;
  box-shadow:0 24px 70px rgba(0,0,0,.55)}
@media(min-width:1100px){
  /* интерфейсы свёрстаны под десктоп — на широком экране рамке нужен запас высоты */
  .screen{min-height:clamp(420px,52vh,620px)}
}
.screen .shot{width:100%;height:100%;display:block;border:0;opacity:0;transition:opacity .35s}
.screen .shot.on{opacity:1;position:relative;z-index:1}
.screen .shot:not(.on){position:absolute;inset:0;pointer-events:none}
.screen img.shot{object-fit:cover;object-position:top center}
/* «уши» листания экранов */
/* Строка кадра: ухо, кадр, ухо. Уши больше не лежат поверх продукта —
   у них своя колонка по краям, и внутри рамки остаётся только сам
   продукт. Ширину кадр отдаёт ровно на две кнопки с зазором. */
.screenrow{position:relative;display:grid;grid-template-columns:auto minmax(0,1fr) auto;
  align-items:center;gap:clamp(6px,.7vw,14px)}
.ear{position:relative;z-index:2;flex:0 0 auto;
  width:clamp(38px,2.6vw,52px);aspect-ratio:1;border-radius:50%;cursor:pointer;
  border:1px solid rgba(255,255,255,.22);background:rgba(5,5,8,.6);color:var(--ink);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  font-size:17px;line-height:1;transition:border-color .25s,opacity .25s}
.ear:hover:not(:disabled){border-color:var(--cyan)}
.ear:disabled{opacity:.25;cursor:default}
.ear--prev{grid-column:1}
.ear--next{grid-column:3}
/* Узкий экран: две колонки под кнопки отнимали у кадра четверть ширины
   (346 → 258 px на телефоне). Шаги там листаются смахиванием и рядом
   вкладок, поэтому уши убираем и ширину возвращаем продукту. */
@media (max-width:719px){
  .screenrow{grid-template-columns:minmax(0,1fr)}
  .ear{display:none}
}
/* Листание вправо-влево читалось плохо: два кружка по краям, и всё.
   Теперь у правого края видна ПОЛОСКА СЛЕДУЮЩЕГО экрана — тот же приём, что у
   стопки карточек: видно, что за краем что-то есть. Полоска приглушена и
   растворяется к центру, чтобы не спорить с текущим кадром. */
/* Края соседних экранов. Их ДВА: справа то, что впереди, слева то, что уезжает
   назад. С одной правой полоской переход читался как подмена картинки —
   барабан узнают по тому, что соседи видны с обеих сторон. */
.screen .peek{position:absolute;top:0;bottom:0;z-index:2;pointer-events:none;
  width:clamp(34px,3.4vw,64px);overflow:hidden;opacity:0;
  transition:opacity .35s,width .35s}
.screen .peek--next{right:0;
  -webkit-mask-image:linear-gradient(270deg,#000 34%,rgba(0,0,0,.35) 72%,transparent);
  mask-image:linear-gradient(270deg,#000 34%,rgba(0,0,0,.35) 72%,transparent)}
.screen .peek--prev{left:0;
  -webkit-mask-image:linear-gradient(90deg,#000 34%,rgba(0,0,0,.35) 72%,transparent);
  mask-image:linear-gradient(90deg,#000 34%,rgba(0,0,0,.35) 72%,transparent)}
/* Полоска предыдущего показывает ПРАВЫЙ край того документа, а не левый —
   иначе слева виднелось бы начало страницы, будто это ещё один следующий. */
.screen .peek--prev iframe{left:auto;right:0;transform-origin:top right}
.screen .peek.on{opacity:.42}
.screen .peek iframe,.screen .peek img{position:absolute;top:0;left:0;border:0;
  width:1280px;height:800px;transform-origin:top left;filter:saturate(.7) brightness(.72)}
/* Тень-обрыв поверх полоски: край страницы, а не приклеенная картинка. */
.screen::after{content:'';position:absolute;top:0;bottom:0;right:0;width:clamp(46px,5vw,86px);
  z-index:3;pointer-events:none;opacity:.9;
  background:linear-gradient(270deg,rgba(5,5,8,.62),rgba(5,5,8,.26) 56%,rgba(5,5,8,0))}
.screen::before{content:'';position:absolute;top:0;bottom:0;left:0;width:clamp(30px,3vw,54px);
  z-index:3;pointer-events:none;opacity:.75;
  background:linear-gradient(90deg,rgba(5,5,8,.6),rgba(5,5,8,0))}
.screen.at-first::before{opacity:0}
.screen.at-last::after{opacity:0}
.screen.at-last .peek--next{opacity:0}
.screen.at-first .peek--prev{opacity:0}
/* Работа из одного экрана: листать нечего — «уши» и полосы убираем совсем,
   иначе две погашенные кнопки выглядят как сломанные. */
/* Класс solo скрипт вешает на сам кадр, а уши теперь его соседи —
   поэтому цепляемся за строку, у которой кадр внутри. */
.screenrow:has(.screen.solo) .ear{visibility:hidden}
.screen.solo .peek{display:none}

.ear{box-shadow:0 6px 22px rgba(0,0,0,.45)}
.ear--next:not(:disabled){animation:earNudge 2.6s ease-in-out infinite}
@keyframes earNudge{
  0%,72%,100%{transform:translateX(0)}
  80%{transform:translateX(5px)}
  88%{transform:translateX(0)}
}
@media (prefers-reduced-motion:reduce){.ear--next:not(:disabled){animation:none}}

/* Объёмный переход между экранами — тот же приём, что у барабана слева:
   уходящий кадр отклоняется вбок и вглубь, приходящий встаёт на его место. */
/* ── ГОРИЗОНТАЛЬНЫЙ БАРАБАН ЭКРАНОВ ───────────────────────────────────────
   Рамка — это ОКНО: у неё край, тень и обрезка, она стоит на месте. Крутится
   слой ЗА окном, как у вертикального барабана слева, где колонка неподвижна,
   а карточки едут по цилиндру. Раньше поворот вешался на саму рамку, и она
   дёргалась целиком вместе с краем — перелистывание читалось как подмена
   картинки, а не как ход барабана.

   Поворачивать сами кадры нельзя: на каждом висит встроенный `scale`, которым
   живой интерфейс ужимается под ширину рамки, а CSS-анимация сильнее
   встроенного стиля и этот масштаб отменяет. Отсюда общий слой `.cyl` — у него
   своего масштаба нет.

   Числа взяты из цилиндра: грань, отстоящая на 19°, при радиусе около двух
   ширин экрана сдвинута вбок примерно на треть ширины и вглубь на 90 пикселей.
   Отсюда translateX(38%) и translateZ(-90px). */
.screen{perspective:1200px;perspective-origin:50% 50%}
.cyl{position:absolute;inset:0;transform-style:preserve-3d}
.cyl.turn-next{animation:cylNext .52s cubic-bezier(.22,.61,.36,1)}
.cyl.turn-prev{animation:cylPrev .52s cubic-bezier(.22,.61,.36,1)}
@keyframes cylNext{
  from{transform:translateX(38%) rotateY(-19deg) translateZ(-90px);opacity:.3}
  to{transform:none;opacity:1}
}
@keyframes cylPrev{
  from{transform:translateX(-38%) rotateY(19deg) translateZ(-90px);opacity:.3}
  to{transform:none;opacity:1}
}
@media (prefers-reduced-motion:reduce){
  .cyl.turn-next,.cyl.turn-prev{animation:none}
}

/* Кнопки листания барабана: колесо мыши как единственный способ листать
   сорок пять работ человек не угадывает — подсказка внизу мелкая, а жест
   ничем не обозначен. */
/* Обёртка нужна, чтобы кнопкам было от чего отсчитываться: сам барабан
   закрыт маской, и всё внутри него у краёв гаснет. Верхний отступ ставит
   скрипт ИМЕННО обёртке — тогда её верх совпадает с верхом барабана, и
   отрицательные top/bottom кладут стрелки ровно над ним и под ним. */
.drumwrap{position:relative}
.drumbtn{position:absolute;left:50%;transform:translateX(-50%);z-index:4;
  width:40px;height:34px;border-radius:11px;cursor:pointer;
  border:1px solid rgba(255,255,255,.2);background:rgba(5,5,8,.62);color:var(--ink);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  font-size:15px;line-height:1;display:flex;align-items:center;justify-content:center;
  transition:border-color .25s,background .25s,opacity .25s}
.drumbtn:hover:not(:disabled){border-color:var(--cyan);background:rgba(59,107,255,.16)}
.drumbtn:disabled{opacity:.22;cursor:default}
.drumbtn--up{top:-44px}
.drumbtn--down{bottom:-44px}
/* ── лента работ: переключатель для телефона ───────────────────────────────
   Барабан на узком экране сжимается до 190 пикселей и листается вертикальным
   смахиванием — ровно тем же движением, каким прокручивают страницу. Попасть
   в него почти невозможно, а кнопки листания там садятся на сами карточки.
   Поэтому на телефоне вместо барабана лента: листается вбок, прилипает к
   карточкам, край следующей выглядывает — жест читается без подсказки. */
.casebar{display:none}
@media (max-width:900px){
  .drum{display:none}.drumwrap{display:none}
  .casebar{display:flex;gap:8px;overflow-x:auto;overflow-y:hidden;
    scroll-snap-type:x proximity;-webkit-overflow-scrolling:touch;
    padding:2px 2px 12px;margin:0 calc(var(--pad) * -1) 2px;
    padding-left:var(--pad);padding-right:var(--pad);
    scrollbar-width:none;
    /* правый край растворяется: видно, что лента продолжается за экран */
    -webkit-mask-image:linear-gradient(90deg,#000 88%,rgba(0,0,0,.15));
    mask-image:linear-gradient(90deg,#000 88%,rgba(0,0,0,.15))}
  .casebar::-webkit-scrollbar{display:none}
  .chip{flex:0 0 auto;scroll-snap-align:center;max-width:64vw;cursor:pointer;
    display:flex;align-items:center;gap:9px;padding:10px 15px;border-radius:13px;
    border:1px solid rgba(255,255,255,.14);background:rgba(255,255,255,.045);
    color:var(--muted);font:inherit;font-size:13px;line-height:1.15;white-space:nowrap;
    transition:border-color .25s,background .25s,color .25s}
  .chip i{font-family:var(--mono);font-style:normal;font-size:10px;
    letter-spacing:.14em;color:var(--gold);opacity:.85}
  .chip span{overflow:hidden;text-overflow:ellipsis}
  .chip.on{border-color:rgba(59,107,255,.55);background:rgba(59,107,255,.14);color:#fff}
  .chip:focus-visible{outline:2px solid var(--cyan);outline-offset:2px}
  .drumbtn{display:none}
  .screen::after{width:34px}
  .screen .peek{width:30px}
}
.dots{display:flex;gap:7px;justify-content:center;margin-top:14px}
.dots i{width:6px;height:6px;border-radius:50%;background:rgba(255,255,255,.24);
  transition:background .25s,width .25s}
.dots i.on{background:var(--cyan);width:20px;border-radius:3px}
/* Окно подписи: высота фиксирована, иначе страница дёргается при листании.
   Под текстом — матовое стекло: подпись лежит поверх облака частиц, и на светлых
   участках формы тёмный текст по тёмному фону переставал читаться. Размытие
   гасит частицы ровно под текстом, оставляя их видимыми вокруг. */
/* Высота задаётся из JS по самой длинной подписи текущего кейса: фиксированная
   обрезала длинные тексты, а автоматическая дёргала страницу при листании. */
/* Отступы намеренно НЕ на окне, а на самой подписи.
   overflow:hidden режет по padding-боксу, поэтому с внутренним отступом на окне
   в правой полосе было видно начало СЛЕДУЮЩЕЙ подписи — рядом с текущей висел
   обрывок чужого текста. С отступами на .cap лента обрезается ровно по краю. */
.capwin{margin-top:16px;overflow:hidden;min-height:clamp(64px,5.6vh,88px);
  transition:height .25s;
  border-radius:14px;
  background:rgba(10,11,18,.62);border:1px solid rgba(255,255,255,.08);
  -webkit-backdrop-filter:blur(16px) saturate(1.1);backdrop-filter:blur(16px) saturate(1.1)}
.captrack{display:flex;transition:transform .35s cubic-bezier(.22,.61,.36,1)}
.cap{flex:0 0 100%;font-size:clamp(14px,.36vw + 10.5px,21px);line-height:1.45;
  color:#D6D9E4;
  padding:clamp(10px,.5vw + 6px,16px) clamp(14px,.7vw + 8px,22px)}
.cap b{color:#fff;font-weight:600}
/* Строка с цифрой-доказательством и ссылкой «открыть вживую».
   Правило было только в прототипе и в боевой CSS не переехало — строка висела
   неоформленной прямо на частицах. Та же матовая подложка, что у подписи. */
.meta{margin-top:12px;display:inline-flex;align-items:center;flex-wrap:wrap;
  gap:clamp(14px,1.4vw,30px);font-family:var(--mono);
  font-size:clamp(12px,.26vw + 9px,16px);color:var(--muted);
  padding:clamp(8px,.4vw + 5px,13px) clamp(14px,.7vw + 8px,20px);border-radius:12px;
  background:rgba(10,11,18,.58);border:1px solid rgba(255,255,255,.07);
  -webkit-backdrop-filter:blur(14px);backdrop-filter:blur(14px)}
.meta:empty{display:none}
.meta b{color:var(--ink);font-weight:500}
.meta a{color:var(--cyan);transition:color .25s}
.meta a:hover{color:var(--ink)}

/* ═══ БЛОК «ПРО МЕНЯ» ══════════════════════════════════════════════════════ */
.me-grid{display:grid;grid-template-columns:minmax(0,1.15fr) minmax(0,1fr);
  gap:clamp(24px,2.6vw,64px);margin-top:clamp(32px,2vw + 16px,60px);align-items:start}
.me-fact{display:flex;gap:clamp(12px,.8vw + 6px,22px);padding:clamp(14px,.6vw + 8px,22px) 0;
  border-bottom:1px solid rgba(16,19,34,.10)}
.me-fact i{font-family:var(--mono);font-style:normal;font-size:clamp(11px,.2vw + 8.5px,14px);
  color:#B98A1E;padding-top:.35em}
.me-fact p{font-size:clamp(15px,.4vw + 11px,22px);line-height:1.5;color:#2a2d3a}
.me-fact b{color:var(--light-ink);font-weight:600}
.me-chips{display:flex;flex-wrap:wrap;gap:9px;margin-top:clamp(18px,1vw + 10px,28px)}
.me-chips span{font-size:clamp(12px,.24vw + 9px,16px);padding:8px 15px;border-radius:20px;
  border:1px solid rgba(16,19,34,.14);color:#4c4f5e}
.me-quote{background:#fff;border-radius:var(--r);padding:clamp(24px,1.4vw + 14px,40px);
  box-shadow:0 14px 44px rgba(16,19,34,.08)}
.me-quote p{font-size:clamp(15px,.4vw + 11px,22px);line-height:1.55;color:#2a2d3a}
.me-quote p + p{margin-top:1em}
.me-quote footer{margin-top:clamp(18px,1vw + 10px,26px);font-family:var(--mono);
  font-size:clamp(11px,.2vw + 8.5px,14px);letter-spacing:.12em;text-transform:uppercase;
  color:#8a8d9a}

/* ── узкий экран: барабан ложится горизонтально, объект за текстом ── */
@media(max-width:900px){
  .reel-in{grid-template-columns:1fr}
  .reel{padding-top:104px}
  .drum{height:190px;perspective:760px}
  .drum-stage{transform:translateZ(-240px)}
  .slot p{display:none}
  .slot{height:84px;margin-top:-42px}
  .me-grid{grid-template-columns:1fr}
  /* Пропорция 16/10 при ширине 331px давала высоту 207px — в такой рамке интерфейс
     продукта не разглядеть вообще. На узком экране рамке важнее высота, чем
     пропорция: отдаём ей больше половины экрана. */
  .screen{aspect-ratio:auto;height:56vh;min-height:340px}
}

/* ── мобильная раскладка: объект за текстом, всё в одну колонку ── */
@media(max-width:760px){
  :root{--pad:22px;--hdr-pad-y:14px;--hdr:calc(14px * 2 + 34px);--hdr-air:16px}
  .scene{padding:calc(var(--hdr) + var(--hdr-air)) var(--pad);justify-content:center;
    text-align:center;min-height:88vh}
  .scene.right{justify-content:center;text-align:center}
  .wrap{max-width:100%}
  /* на мобиле объект стоит ЗА текстом, поэтому здесь вуаль нужна */
  .wrap::before{content:'';position:absolute;inset:-36px -20px;z-index:-1;
    background:radial-gradient(115% 84% at 50% 46%,rgba(5,5,8,.88),rgba(5,5,8,.55) 54%,transparent 84%)}
  .eyebrow,.scene.right .eyebrow{justify-content:center}
  .scene .lead,.scene.right .lead{margin-left:auto;margin-right:auto}
  .actions,.scene.right .actions{justify-content:center}
  .metric,.scene.right .metric{justify-content:center;gap:14px}
  h1{font-size:clamp(40px,10.5vw + 4px,72px)}
  h2{font-size:clamp(36px,9vw + 3px,62px)}
  .lead{font-size:clamp(16px,1.6vw + 10px,19px);max-width:38ch}
  .panel h2{font-size:clamp(30px,7.4vw + 3px,52px)}
  .panel{padding:70px var(--pad)}
  .gal-card{flex:0 0 82%}
  .cta-in{flex-direction:column;align-items:flex-start}
  footer{flex-direction:column;align-items:flex-start}
  .signature{height:42vh;min-height:280px}
}

/* ── СТЕКЛЯННЫЕ КУБЫ ───────────────────────────────────────────────────────
   Два блока: «стек» (кубы сходятся в связку) и «соберу что угодно» (одиночный
   куб крутится гранями). Стороны чередуются, как во всех сценах: в первом
   текст слева и фигура справа, во втором наоборот.
   Кубы рисует свой WebGL-канвас поверх страницы, поэтому фон блока прозрачный —
   поле частиц морфа за ними приглушается из cubes.js, а не гасится совсем. */
.cubes{position:relative;z-index:2;min-height:100vh;
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  align-items:center;gap:clamp(20px,3vw,64px);
  padding:clamp(70px,5vw,120px) var(--pad)}
.cubes-txt{position:relative;max-width:clamp(420px,40vw,760px);justify-self:end}
/* Вуаль под текстом. Поле частиц — фиксированный слой под всей страницей, и в
   этих двух блоках оно оказывается ровно за заголовком: буквы читаются через
   раз, а надстрочник пропадает совсем. Гасим фон только там, где текст, и
   мягко — по краям вуали частицы видны как задумано. */
.cubes-txt::before{content:'';position:absolute;inset:-8% -12% -10% -14%;z-index:-1;
  pointer-events:none;
  background:radial-gradient(68% 60% at 44% 50%,
    rgba(6,8,16,.88) 0%, rgba(6,8,16,.72) 40%, rgba(6,8,16,.34) 66%, rgba(6,8,16,0) 82%)}
.cubes--flip .cubes-txt::before{background:radial-gradient(68% 60% at 56% 50%,
    rgba(6,8,16,.88) 0%, rgba(6,8,16,.72) 40%, rgba(6,8,16,.34) 66%, rgba(6,8,16,0) 82%)}
.cubes--flip .cubes-txt{order:2;justify-self:start}
.cubes--flip .cubes-art{order:1}
.cubes-art{position:relative;height:min(76vh,760px);border-radius:24px;
  /* лёгкий ореол, чтобы стекло не висело в пустоте на чёрном */
  background:radial-gradient(60% 60% at 50% 50%,rgba(59,107,255,.10),transparent 70%)}
.cubes-art canvas{position:absolute;inset:0;width:100%!important;height:100%!important;
  pointer-events:none}
/* WebGL2 не поднялся — прячем пустую рамку, текст остаётся на месте */
.cubes-art--off{display:none}

.stack-grid{margin-top:clamp(22px,1.6vw + 12px,38px);
  display:flex;flex-wrap:wrap;gap:8px}
.stack-grid span{font-family:var(--mono);font-size:clamp(12px,.22vw + 9.6px,15px);
  letter-spacing:.02em;color:var(--muted);
  padding:7px 13px;border-radius:22px;
  border:1px solid rgba(255,255,255,.13);background:rgba(255,255,255,.035);
  -webkit-backdrop-filter:blur(8px);backdrop-filter:blur(8px);
  transition:color .25s,border-color .25s,background .25s}
.stack-grid span:hover{color:var(--ink);border-color:rgba(59,107,255,.42);
  background:rgba(59,107,255,.10)}
.cubes h2{font-size:clamp(38px,3.4vw + 22px,96px)}
.cubes .lead{max-width:46ch}

@media (max-width:900px){
  /* На узком экране колонки не помещаются: фигура уходит под текст, но
     остаётся крупной — она здесь смысловая, а не декоративная. */
  .cubes{grid-template-columns:1fr;min-height:auto;padding:64px var(--pad);gap:26px}
  .cubes-txt,.cubes--flip .cubes-txt{order:1;justify-self:stretch;max-width:100%;text-align:center}
  .cubes-art,.cubes--flip .cubes-art{order:2;height:min(52vh,420px)}
  .cubes .eyebrow{justify-content:center}
  .cubes .lead{margin-inline:auto}
  .stack-grid{justify-content:center}
  .cubes .metric{justify-content:center}
}

/* ── ВКЛАДКИ ВИТРИНЫ НА УЗКОМ ЭКРАНЕ ──────────────────────────────────────
   На десктопе вкладки переносятся на вторую строку — там места хватает.
   На телефоне восемь вкладок занимали ТРИ строки и 112 px: лента съедала
   экран, и кадр с продуктом уезжал под сгиб. Поэтому здесь лента листается
   вбок одной строкой — привычное для телефона поведение; шаги при этом
   по-прежнему переключаются «ушами» ‹ ›, так что ни один не теряется. */
@media (max-width:640px){
  .tabs{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;
    -webkit-overflow-scrolling:touch;padding-bottom:2px}
  .tabs::-webkit-scrollbar{display:none}
  .tabs button{flex:0 0 auto}
  /* активная вкладка сама подъезжает в видимую часть ленты */
  .tabs button.on{scroll-snap-align:center}
}

/* ── ПОДПИСЬ И КНОПКИ ОТПРАВКИ ЗАЯВКИ ─────────────────────────────────────
   Форма ничего не отправляла: обработчика не было. Теперь она собирает текст
   заявки, кладёт его в буфер и показывает результат — подпись под кнопкой
   меняет тон, а если владелец укажет канал, рядом появятся кнопки отправки. */
.form-note--ok{color:#7FE0B0}
.form-note--warn{color:var(--terra-lite)}
.form-send{display:flex;gap:12px;flex-wrap:wrap;margin-top:14px}

/* ── АВТОПОКАЗ КЕЙСА ──────────────────────────────────────────────────────
   Кадр не стоит статичной картинкой: сценарий листает сайт, нажимает кнопки,
   печатает в диалоге. Кнопка сверху останавливает и запускает показ.
   Курсор рисуется поверх кадра — без него нажатие читается как самопроизвольный
   скачок интерфейса, и человек не понимает, что произошло. */
/* Кнопка стоит в одной строке с экранами работы, а не поверх кадра: над
   кадром её перекрывал интерфейс продукта, и она терялась. */
.tabsrow{display:flex;align-items:center;gap:14px;margin-bottom:16px}
.tabsrow .tabs{margin-bottom:0;flex:1 1 auto;min-width:0}
.playbtn{position:relative;flex:0 0 auto;z-index:6;
  width:34px;height:34px;border-radius:50%;cursor:pointer;
  border:1px solid rgba(255,255,255,.20);background:rgba(9,10,17,.72);
  -webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);
  transition:background .25s,border-color .25s,opacity .25s;opacity:.8}
.playbtn:hover{opacity:1;background:rgba(9,10,17,.92);border-color:rgba(59,107,255,.55)}
/* пауза — две полосы; play — треугольник. Рисуем фигурами, без иконочного шрифта */
.playbtn::before,.playbtn::after{content:'';position:absolute;top:50%;left:50%;
  transform:translate(-50%,-50%);transition:all .2s}
.playbtn::before{width:3px;height:13px;background:var(--ink);margin-left:-4px}
.playbtn::after{width:3px;height:13px;background:var(--ink);margin-left:4px}
.playbtn.is-play::before{width:0;height:0;margin-left:1px;background:none;
  border-left:11px solid var(--ink);border-top:7px solid transparent;
  border-bottom:7px solid transparent}
.playbtn.is-play::after{width:0;height:0;background:none;border:0}

.demo-cursor{position:absolute;top:0;left:0;z-index:5;pointer-events:none;
  width:16px;height:16px;margin:-8px 0 0 -8px;border-radius:50%;
  background:rgba(59,107,255,.30);border:1.5px solid rgba(120,170,255,.95);
  box-shadow:0 0 0 4px rgba(59,107,255,.12),0 3px 12px rgba(0,0,0,.5);
  opacity:0;transition:transform .42s cubic-bezier(.3,.7,.3,1),opacity .3s}
.demo-cursor.is-on{opacity:1}
.demo-cursor.is-tap{animation:demoTap .26s ease}
@keyframes demoTap{0%{box-shadow:0 0 0 4px rgba(59,107,255,.12)}
  50%{box-shadow:0 0 0 14px rgba(59,107,255,.28)}
  100%{box-shadow:0 0 0 4px rgba(59,107,255,.12)}}
@media (prefers-reduced-motion:reduce){
  .demo-cursor{transition:none}
}

/* ── ЗАДАЧА ГЛАЗАМИ ПОЛЬЗОВАТЕЛЯ (JTBD) ───────────────────────────────────
   Под кадром — не перечень технологий, а работа, которую человек нанимает
   продукт делать. Ставится отдельным блоком, спокойнее подписи к экрану:
   подпись объясняет ЭТОТ экран, а это — зачем продукт нужен вообще. */
.jtbd{margin-top:12px;padding:13px 16px;border-radius:12px;
  border:1px solid rgba(255,255,255,.09);
  background:linear-gradient(180deg,rgba(59,107,255,.07),rgba(9,10,17,.55));
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}
.jtbd[hidden]{display:none}
.jtbd-line{font-size:clamp(13px,.3vw + 10.6px,17px);line-height:1.55;color:#C9CCDA}
.jtbd-line b{color:var(--ink);font-weight:600}
.jtbd-line i{font-style:normal;color:var(--cyan)}
@media (max-width:640px){ .jtbd{padding:11px 13px;margin-top:10px} }

/* Знак заказчика в карточке барабана: угол, малый размер, приглушён —
   у активной работы ярче.

   Карточке НЕ нужно `position:relative` — она и так `absolute` (см. `.slot`
   выше), и этого достаточно, чтобы знак лёг в её угол. Правило стояло здесь и,
   идя ниже с той же силой, перебивало `absolute` на `relative`: карточки
   переставали лежать друг на друге в одной точке и вставали в столбик по
   потоку. Барабан от этого «уезжал» — активная работа оказывалась тем ниже
   кадра, чем дальше она по списку: тринадцатая на 900 точек, сорок пятая на
   3300. Именно это владелец видел трижды как «активный блок должен быть
   напротив экрана айфрейма». */
.slot-logo{position:absolute;top:12px;right:14px;max-width:52px;max-height:20px;
  object-fit:contain;opacity:.42;filter:grayscale(1);transition:opacity .3s,filter .3s}
.slot.is-active .slot-logo{opacity:.9;filter:none}

/* ── ПОРТРЕТ И БЛАГОДАРНОСТИ ──────────────────────────────────────────────
   Секция «Про меня» была текстовой: факты и цитата, но ни лица, ни пруфов.
   Портрет ставим рядом с фактами, письма — лентой под ними: письмо узнают по
   бланку и печати, а читать его целиком никто не станет. Клик открывает
   полный размер. */
/* Отступ обязателен: заголовок в две строки заканчивался ровно там, где
   начиналось фото (зазор 0 px), и портрет читался как наезд на строку. */
.me-top{margin-top:clamp(20px,2vw,34px);display:grid;grid-template-columns:minmax(0,220px) minmax(0,1fr);
  gap:clamp(20px,2vw,38px);align-items:start}
@media(max-width:760px){.me-top{grid-template-columns:minmax(0,1fr)}}
.me-photo{border-radius:var(--r);overflow:hidden;background:rgba(16,19,34,.06);
  box-shadow:0 12px 34px -14px rgba(16,19,34,.34)}
.me-photo img{width:100%;height:auto;display:block}
.me-photo figcaption{padding:11px 14px 13px;font-family:var(--mono);font-size:11px;
  letter-spacing:.1em;text-transform:uppercase;color:var(--light-muted)}

.thanks{margin-top:clamp(26px,2.2vw,44px)}
.thanks-head{display:flex;align-items:baseline;justify-content:space-between;gap:16px;
  flex-wrap:wrap;margin-bottom:14px}
.thanks-head h3{font-size:clamp(19px,.7vw + 14px,26px);font-weight:600;letter-spacing:-.01em}
.thanks-head span{font-family:var(--mono);font-size:11.5px;letter-spacing:.12em;
  text-transform:uppercase;color:var(--light-muted)}
/* ── ЛЕНТА ПИСЕМ ─────────────────────────────────────────────────────────
   Писем двадцать, в кадр помещается пять. О том, что лента продолжается,
   раньше говорила только системная полоса прокрутки — на светлой панели она
   читается как чужая планка, приклеенная к блоку. Даём свои органы: кнопки
   по краям и тонкий ползунок под лентой. */
.thanks-rail{position:relative}
.thanks-strip{display:flex;align-items:flex-start;gap:14px;overflow-x:auto;padding:4px 2px 16px;
  scroll-snap-type:x proximity;
  /* системную полосу прячем — вместо неё своя, ниже */
  scrollbar-width:none;-ms-overflow-style:none;
  /* края растворяются: видно, что лента уходит за границу блока */
  -webkit-mask-image:linear-gradient(90deg,transparent 0,#000 26px,#000 calc(100% - 26px),transparent 100%);
  mask-image:linear-gradient(90deg,transparent 0,#000 26px,#000 calc(100% - 26px),transparent 100%)}
.thanks-strip.at-start{-webkit-mask-image:linear-gradient(90deg,#000 calc(100% - 26px),transparent 100%);
  mask-image:linear-gradient(90deg,#000 calc(100% - 26px),transparent 100%)}
.thanks-strip.at-end{-webkit-mask-image:linear-gradient(90deg,transparent 0,#000 26px);
  mask-image:linear-gradient(90deg,transparent 0,#000 26px)}
.thanks-strip.at-start.at-end{-webkit-mask-image:none;mask-image:none}

/* Кнопки листания. Светлая панель — поэтому кнопка светлая с тенью, а не
   тёмная, как «уши» у кадра на чёрном фоне. */
.railbtn{position:absolute;top:calc(50% - 14px);transform:translateY(-50%);z-index:3;
  width:clamp(34px,2.4vw,44px);aspect-ratio:1;border-radius:50%;cursor:pointer;
  display:flex;align-items:center;justify-content:center;
  border:1px solid rgba(16,19,34,.18);background:rgba(255,255,255,.97);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  color:var(--light-ink);font-size:18px;line-height:1;
  box-shadow:0 8px 22px -8px rgba(16,19,34,.55);
  transition:opacity .25s,border-color .25s,background .25s,transform .2s}
.railbtn--prev{left:-6px}
.railbtn--next{right:-6px}
.railbtn:hover:not(:disabled){border-color:rgba(16,19,34,.3);transform:translateY(-50%) scale(1.06)}
.railbtn:disabled{opacity:0;pointer-events:none}
.railbtn:focus-visible{outline:2px solid var(--cyan);outline-offset:2px}

/* Ползунок. Дорожка во всю ширину, бегунок шириной по видимой доле ленты —
   он же показывает, сколько писем осталось за краем. */
.railbar{position:relative;height:4px;border-radius:4px;
  background:rgba(16,19,34,.10);cursor:pointer;margin-top:2px}
.railbar i{position:absolute;left:0;top:0;height:100%;min-width:34px;border-radius:4px;
  background:rgba(16,19,34,.42);transition:background .25s}
.railbar:hover i{background:rgba(16,19,34,.62)}
.railbar.is-drag i{background:var(--blue,#3b6bff);transition:none}
@media (prefers-reduced-motion:reduce){.railbtn{transition:none}}
.thanks-strip::-webkit-scrollbar{display:none}
/* Ширина — по пропорции самого письма, общая только ВЫСОТА. Иначе половину
   писем (они альбомные, 1.41-1.44) режет по краям, и в кадре остаётся
   «одарность» вместо «БЛАГОДАРНОСТЬ». Так упакованы благодарности на
   лендинге вайбкодинга — оттуда и взято. */
.thanks-card{flex:0 0 auto;width:auto;max-width:none;scroll-snap-align:start;
  border-radius:12px;overflow:hidden;background:#fff;text-decoration:none;
  box-shadow:0 6px 20px -10px rgba(16,19,34,.4);transition:transform .25s,box-shadow .25s}
.thanks-card:hover{transform:translateY(-3px);box-shadow:0 14px 30px -12px rgba(16,19,34,.5)}
.thanks-card img{height:clamp(212px,19vw,296px);width:auto;max-width:min(400px,68vw);display:block;
  aspect-ratio:auto;object-fit:contain;background:#fff}
.thanks-card b{display:block;padding:9px 11px 11px;font-size:12px;font-weight:600;
  overflow-wrap:anywhere;
  line-height:1.3;color:var(--light-ink)}

/* кусочек подсказки, который верен только там, где кнопки видны */
