/* mdf-card.css больше НЕ подключается через @import отсюда (2026-08-24) —
   без версии в URL это позволяло браузеру бесконечно долго кэшировать
   устаревшую копию файла после его правок, даже когда у ЭТОГО файла
   (homepage.css) URL уже менялся (нашли по репорту "на телефоне в Firefox
   плашка Open на карточке образца не отображается"). Теперь грузится ДО
   этого файла версионированным <link> — см. $preStylesheets в index.php/
   includes/header.php. Порядок (mdf-card.css первым, этот файл вторым)
   сохранён — ниже есть правила, переопределяющие mdf-card.css при равной
   специфичности через порядок каскада, не через доп. специфичность. */

/* Homepage — стили, уникальные для этой страницы. Общие токены/компоненты
   (nav, blk-title, sec-* heading kit, footer, fcta, info-card family
   (.svc-card/.why-card/.svc-quote/.svc-materials), canonical .pcard/.pjcard)
   — в shared.css. */

/* .page (max-width/центрирование/padding) — теперь единственный источник
   shared.css (там же .b1-.b5{padding:30px 0} — единый вертикальный ритм
   секций на всех страницах сайта, не только тут). */

/* .tabbar/.tabseg — общий компонент, см. shared.css. Здесь только
   специфика главной: .tabbar сам sticky (на детальных страницах sticky
   даёт обёртка .toolbar-wrap), поэтому border-bottom, а не border-top. */
.tabbar{position:sticky;top:64px;z-index:90;border-bottom:1px solid var(--line)}

/* Кнопка звонка и CTA "Get a Free Quote" в шапке — скрыты на главной
   (2026-08-18, по прямому запросу, п.3). На мобильном/планшете (≤980px)
   они уже скрыты глобально правилом в shared.css — здесь правило без
   media-query, т.к. homepage.css грузится только на главной: эффективно
   добавляет то же самое ещё и на десктопной ширине, но ТОЛЬКО тут. Другие
   страницы (без hero-CTA/wa-fab-меню) сохраняют оба элемента как обычно. */
.nicon,.nquote{display:none}

/* ── HERO SLIDER ── */
.hero{position:relative;height:calc(100vh - 64px - 60px);min-height:480px;max-height:760px;overflow:hidden;background:var(--bg-3)}
.hslide{position:absolute;inset:0;opacity:0;transition:opacity .6s ease;pointer-events:none}
.hslide.on{opacity:1;pointer-events:auto}
/* БАГ: .hotspot (shared.css) сам себе ставит pointer-events:auto — нужно
   для карусели детальных страниц (#mv-hotspots{pointer-events:none} на
   контейнере, чтобы пустая область вокруг точек не перехватывала клик по
   фото под ней, а сами точки — кликабельны). На hero это ломало обратное:
   точки НЕактивного слайда (тот самый .hslide{pointer-events:none}) тоже
   переопределяли его на auto и оставались кликабельными/наводимыми, хотя
   сам слайд невидим (opacity:0) — курсор реагировал и открывал попап от
   чужого фото. Явно гасим только тут, в самом узком контексте. */
.hslide:not(.on) .hotspot{pointer-events:none}
/* Прозрачность hero-фото (2026-08-24, по прямому запросу — "+0.2
   прозрачности, не равномерно, фейд только в верхних/нижних 10% высоты") —
   базовая opacity увеличена .93→.73 (было 7% прозрачности, стало 27%, т.е.
   +0.2) равномерно по всей высоте КРОМЕ краёв; mask-image добавляет
   дополнительный фейд ТОЛЬКО в верхних/нижних 10% — к самому краю (0%/100%)
   уходит в полную прозрачность, к 10%/90% возвращается к базовой .73 —
   середина (10%-90%) остаётся без доп. фейда, тот же приём, что у
   вертикального фейда боковых каруселей hero (.hero-rail ниже). */
.hslide img{width:100%;height:100%;object-fit:cover;object-position:center 30%;opacity:.73;
  -webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%);
  mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%)}
/* Затемняющий градиент по всей высоте фото (2026-08-24, п.4 — "прозрачность
   как в примере макета"), не только у нижнего края, как было раньше — тот же
   принцип, что в референсе (Ferrara Home - Kintaro Layout.dc.html, #home,
   строка 71): сильнее сверху и особенно снизу (там живёт hero-pitch/
   hprogress), светлее в середине. hero-pitch теперь БЕЗ своей плашки-фона
   (см. ниже) — контраст текста держит именно этот градиент + text-shadow. */
.hshade{position:absolute;inset:0;background:linear-gradient(180deg,color-mix(in srgb,var(--overlay-dark) 50%,transparent) 0%,color-mix(in srgb,var(--overlay-dark) 14%,transparent) 30%,color-mix(in srgb,var(--overlay-dark) 30%,transparent) 55%,color-mix(in srgb,var(--overlay-dark) 80%,transparent) 100%)}
/* .hotspot/.hspop — canonical в shared.css (общие с project.php). */
.hcard{position:absolute;left:24px;bottom:24px;width:300px;background:var(--bg-main);padding:24px 26px;z-index:6;border-radius:var(--r)}
.hcard h2{font-family:var(--display);font-size:22px;font-weight:700;line-height:1.2;letter-spacing:-.01em;margin-bottom:10px}
.hcard p{color:var(--ink-dim);font-size:13px;line-height:1.55;margin-bottom:16px}
.hcard-link{font-size:12.5px;font-weight:500;text-decoration:underline;text-underline-offset:3px;color:var(--ink)}
.hcard-link:hover{color:var(--accent)}
/* Hero-offer + CTA (п.1, 2026-08-17; переработано 2026-08-18 несколько раз,
   последний — уменьшенный шрифт/20% непрозрачность/шире/приподнята над
   полосками таймера, по прямому запросу). Нижний ЛЕВЫЙ угол — .hcard
   (per-slide подпись) тоже слева, но на текущих данных сайта show_hcard=0
   у всех слайдов (см. admin/adm-homepage.php), коллизии нет.
   bottom:56px — не 24px, как раньше: .hprogress (полоски-таймер) сидит на
   bottom:20px высотой 20px (занимает 20-40px снизу), плашка теперь должна
   быть НАД ней, а не вплотную/за одну линию (по прямому запросу).
   Фон убран целиком (2026-08-24, п.4, по прямому запросу — "плашку не
   выделять, чтобы её не было видно, только текст") — контраст держит
   только усиленный .hshade-градиент (выше) + text-shadow ниже, как в
   референс-макете (там у текстового блока тоже нет подложки, см.
   Ferrara Home - Kintaro Layout.dc.html, #home, строка 79-87). padding
   оставлен — не создаёт видимой плашки сам по себе, просто держит текст на
   том же отступе от края, что и раньше. */
/* left:0 — не 24px, как раньше (2026-08-24, п.3) — офсет от края теперь
   даёт padding самой .hero-inner (см. ниже, только в pin-режиме ≥981px),
   тот же отступ, что у карточек Why/How/Projects. На мобильном/планшете
   (≤980px) свой явный left/right/bottom, см. media-queries ниже — эта
   база их не задевает. */
.hero-pitch{position:absolute;left:0;bottom:56px;z-index:7;width:min(62%,660px);padding:26px 30px;text-align:left}
/* Текст-тень — при 20%-прозрачной плашке фон уже не гарантирует контраст
   сам по себе (как было при 90%), поэтому держит его текст напрямую, тот
   же приём, что у .card-id-name (текст поверх фото без подложки, shared.css). */
.hero-pitch .hero-h1,.hero-pitch-area,.hero-pitch-tags{text-shadow:0 1px 3px color-mix(in srgb,var(--shadow-black) 65%,transparent),0 2px 10px color-mix(in srgb,var(--shadow-black) 45%,transparent)}
/* font-size — 40px (2026-08-24, по прямому запросу, было 32px). */
.hero-pitch .hero-h1{position:static;max-width:none;margin:0 0 14px;text-align:left;font-family:var(--display);font-size:40px;font-weight:700;line-height:1.2;color:var(--text-on-accent)}
.hero-pitch-area{margin:0 0 18px;font-size:18px;color:var(--text-on-accent);line-height:1.5}
.hero-pitch-cta{display:flex;gap:10px;flex-wrap:wrap;justify-content:flex-start;margin-bottom:14px}
.hero-pitch-cta .fcta-main,.hero-pitch-cta .fcta-sub{padding:12px 22px;font-size:13px}
/* .fcta-sub (shared.css) — тонкая обводка БЕЗ фона, тёмный текст по
   умолчанию (рассчитан на светлый фон .fcta) — на тёмной плашке нужен
   светлый текст/обводка; отдельный фон уже не нужен — его даёт сама плашка. */
.hero-pitch-cta .fcta-sub{color:var(--text-on-accent);border-color:color-mix(in srgb,var(--text-on-accent) 60%,transparent);text-shadow:0 1px 3px color-mix(in srgb,var(--shadow-black) 65%,transparent)}
.hero-pitch-cta .fcta-sub:hover{border-color:var(--text-on-accent);background:color-mix(in srgb,var(--text-on-accent) 12%,transparent)}
.hero-pitch-tags{display:flex;align-items:center;flex-wrap:wrap;justify-content:flex-start;gap:10px;font-size:11px;font-weight:600;letter-spacing:.03em;text-transform:uppercase;color:color-mix(in srgb,var(--text-on-accent) 85%,transparent)}
/* Полоски-таймер автопрокрутки hero (2026-08-15, заменили точки-навигацию) —
   одна на слайд, ширина адаптивная (от 1 до многих слайдов, max-width на
   контейнере не даёт им растянуться на всю ширину при малом числе слайдов).
   --hprog-dur — задаётся инлайн из HERO_INTERVAL_MS в index.php, чтобы
   длительность CSS-анимации всегда совпадала с реальным JS-таймером. */
.hprogress{position:absolute;bottom:0px;left:50%;transform:translateX(-50%);display:flex;gap:6px;z-index:8;width:min(70%,260px)}
/* .hprog-seg — сама кликабельная зона (SEO/UX-аудит, находка №34: 3px
   высотой мимо рекомендованных ~44px под палец) — теперь высокая
   invisible-зона нажатия, а видимая тонкая полоска — вложенный .hprog-track.
   Не сделали в полный рекомендованный размер (высота ограничена, чтобы не
   наезжать на .hcard/.hero-h1/hotspot-точки, тоже живущие в нижней части
   hero) — компромисс между кликабельностью и не-перекрытием другого
   интерактива на том же экране. */
.hprog-seg{position:relative;flex:1 1 0;height:20px;display:flex;align-items:center;cursor:pointer}
.hprog-track{position:relative;width:100%;height:3px;border-radius:2px;background:color-mix(in srgb,var(--text-on-accent) 35%,transparent);overflow:hidden}
/* Обратный таймер (2026-08-18, по прямому запросу — уточнение): выбранный
   слайд СРАЗУ подсвечивается ПОЛНОСТЬЮ (width:100%), а по мере отсчёта
   подсветка "стекает" до 0 — не заполняется, а исчезает. Раньше было
   наоборот (0→100%, пустая полоска заполнялась к концу отсчёта). base
   .hprog-fill{width:0} остаётся дефолтом для "ещё не начатых" сегментов —
   .hprog-seg.done больше не переопределяется отдельно: уже пройденный
   сегмент, догорев до 0% в конце своей анимации (animation-fill-mode:
   forwards), просто теряет класс .active при смене слайда и возвращается
   к тому же base width:0 без видимого скачка.
   Направление "стекания" (уточнение 2026-08-18, п.1) — right:0 вместо
   left:0 (было через inset:0): при абсолютном позиционировании с явным
   width край, НЕ указанный явно (left/right), остаётся "плавающим" и
   двигается при изменении width, а противоположный — фиксирован. Раньше
   left:0 был зафиксирован (через inset:0), поэтому полоска "съедалась"
   справа налево (правый край уезжал к левому). Теперь зафиксирован
   правый край — полоска съедается слева направо (левый край уезжает к
   правому, remaining-часть всегда прижата к правому краю трека). */
.hprog-fill{position:absolute;top:0;right:0;bottom:0;width:0;background:var(--text-on-accent);border-radius:2px}
.hprog-seg.active .hprog-fill{animation:hprogfill var(--hprog-dur,6500ms) linear forwards}
@keyframes hprogfill{from{width:100%}to{width:0}}
@media(prefers-reduced-motion:reduce){
  .hprog-seg.active .hprog-fill{animation:none;width:65%}
}

/* ── USP STRIP ──
   height:60px фиксированная (2026-08-24, п.3 — "hero должен занимать весь
   экран и оканчиваться каруселью USP"; уточнение того же дня — было 44px,
   затем временно 80px, теперь 60px) — .hero/.hero-spacer в pin-режиме
   (@media(min-width:981px) ниже) считают свою высоту как calc(100vh - 60px)
   ИМЕННО под эту цифру, чтобы hero+usp-strip суммарно давали ровно 100vh
   на любой высоте экрана. Раньше высота была "плавающей" (от контента —
   реально ~28px, а не предполагаемые 44px) — из-за этого расхождения hero
   не дотягивал до низа экрана, а usp-strip после него обрезался о нижний
   край viewport. */
.usp-strip{background:var(--accent-dim);border-top:1px solid var(--line);border-bottom:1px solid var(--line);overflow:hidden;height:60px;display:flex;align-items:center}
.usp-inner-pad{padding:0 28px;width:100%}
.usp-track{max-width:1280px;margin:0 auto;display:flex;gap:0;align-items:center;justify-content:center;flex-wrap:wrap}
.usp-item{display:flex;align-items:center;gap:10px;padding:0 30px;flex:0 0 auto}
.usp-item span{font-size:14px;color:var(--ink);font-weight:600;white-space:nowrap}
.usp-ico{font-size:16px;color:var(--ink)}
.usp-sep{width:4px;height:4px;border-radius:50%;background:color-mix(in srgb,var(--shadow-black) 30%,transparent);flex:0 0 auto}
/* Бегущая лента (2026-08-24, редизайн по референсу FerraraHome_ver1) —
   контент задублирован в PHP (index.php), CSS просто едет на -50% (ровно
   половина ширины — граница между первой и второй копией) и зацикливается. */
.usp-track--marquee{flex-wrap:nowrap;justify-content:flex-start;width:max-content;animation:usp-marquee 38s linear infinite}
.usp-strip:hover .usp-track--marquee{animation-play-state:paused}
@keyframes usp-marquee{from{transform:translateX(0)}to{transform:translateX(-50%)}}
@media(prefers-reduced-motion:reduce){.usp-track--marquee{animation:none}}

/* ── HERO — контент-колонка (текст+карусели) той же ширины, что остальные
   секции (2026-08-24, п.3, по прямому запросу) — .hero-inner, а не полный
   viewport. Базовое правило — БЕЗ ограничения ширины (мобильный/планшет
   ≤980px, где .hero-pitch продолжает жить своими собственными left/right
   офсетами, как раньше, см. media-queries ниже) — .page-подобные max-
   width/padding добавляются ТОЛЬКО в pin-режиме (@media(min-width:981px)
   ниже), там же появляются и сами карусели. */
.hero-inner{position:absolute;inset:0;pointer-events:none}
.hero-inner>*{pointer-events:auto}
/* Логотип над hero-pitch (2026-08-25, по прямому запросу) — только десктоп,
   см. @media(min-width:981px) ниже; на мобильном/планшете не показывается. */
.hero-logo{display:none}

/* ── HERO — боковые "конвейеры" фото + pin-эффект при скролле (2026-08-24,
   редизайн по референсу FerraraHome_ver1; п.2 2026-08-24 — обе карусели
   рядом у правого края вместо разных краёв экрана, по прямому запросу) ── */
.hero-rail{display:none}
@media(min-width:981px){
  .hero-inner{max-width:1280px;margin:0 auto;padding:0 40px;display:flex;flex-direction:column}
  /* Логотип над hero-pitch — position:absolute (2026-08-25, по прямому
     запросу), НЕ участвует в flex-колонке .hero-inner ниже, поэтому никак не
     задевает margin-top:auto/15vh-расчёт .hero-pitch (нет конфликта, т.к.
     абсолютно спозиционированные элементы полностью выведены из потока).
     top — 64px (высота .nav) + 5vh отступ "5% ниже от меню"; left:40px —
     тот же отступ, что и padding-box .hero-inner (40px), т.е. тот же левый
     край контента, что и у .hero-pitch (см. её position:static ниже, без
     собственных left/right — начинается сразу от content-box родителя).
     Ширина — инлайн style="width:...px" на самом <img> (index.php, поле
     администратора hero_logo_width_px). */
  .hero-logo{display:block;position:absolute;top:calc(100px + 5vh);left:65px;z-index:6;
    filter:blur(var(--logo-blur,0px));transform:translateX(var(--logo-shift,0px));
    transition:filter var(--hero-cover-smoothing) ease,transform var(--hero-cover-smoothing) ease}
  .hero-logo img{width:100%;height:auto}
  /* Позиционирование .hero-pitch — низ отступ 15vh от нижней границы
     окна, но верхний край не выше 20vh от верхней границы (2026-08-24,
     по прямому запросу) — чистый flex-расчёт, без JS-измерений высоты
     текста: .hero-top-guard — первый flex-элемент колонки, всегда
     занимает свои 20vh (не сжимается — flex:0 0 20vh); .hero-pitch —
     margin-top:auto забирает всё оставшееся свободное место, прижимая
     блок к низу, margin-bottom:15vh держит нижний отступ. Если текст
     настолько большой, что 20vh+высота текста+15vh не помещаются в
     экран — margin-top:auto не может стать отрицательным, поэтому верх
     блока остаётся ровно на границе стража (20vh), а не уезжает выше —
     именно то ограничение, которое просили ("верхний край не выше
     20%"); .hero-rails — position:absolute, в этом flex-расчёте не
     участвует, не задет. */
  .hero-top-guard{flex:0 0 20vh;pointer-events:none}
  .hero-pitch{position:static;margin:auto 0 15vh}
  /* .hero-rails — пара каруселей рядом, flex-ряд, прижатый к правому краю
     .hero-inner (= тот же правый отступ, что у карточек Why/How/Projects
     ниже). Сдвиг+масштаб при перекрытии (--carousel-shift/--carousel-scale,
     JS в index.php) — на всю пару разом.
     bottom:60px, не 0 — карусель не занимает нижние 60px, перекрытые
     непрозрачной usp-strip (z-index:10) на первом экране: иначе нижняя
     вертикальная фейд-зона карусели (mask-image на .hero-rail ниже,
     18%…100%) была бы там частично обрезана лентой, а верхняя — нет. */
  .hero-rails{position:absolute;right:0;top:0;bottom:60px;z-index:2;display:flex;gap:14px;
    transform:translateX(var(--carousel-shift,0px)) scale(var(--carousel-scale,1));
    transition:transform var(--hero-cover-smoothing) ease}
  /* Ширина карточки карусели — 17.5% ширины блока (2026-08-24, было 15%:
     min(12vw,180px), 180px/1200px content-width .hero-inner = 15%) —
     min(14vw,210px), 210px/1200px = 17.5%, тот же принцип пропорции.
     aspect-ratio:3/4 у самих <img> ниже не трогали — сохраняется
     автоматически, высота карточки следует за новой шириной. */
  .hero-rail{display:block;position:relative;height:100%;width:min(14vw,210px);overflow:hidden;border-radius:2px;
    -webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 18%,black 82%,transparent 100%);
    mask-image:linear-gradient(to bottom,transparent 0%,black 18%,black 82%,transparent 100%)}
  /* padding:0 8px — тени карточек ниже обрезались собственным
     overflow:hidden у .hero-rail (нужен для вертикальной маски выцветания
     выше) — карточки шли впритык к его краям, тени было буквально некуда
     рисовать. Отступ даёт тени место по бокам, не меняя саму ширину
     колонки/промежуток между парой колонок (.hero-rails{gap:14px}). */
  .hero-rail-track{display:flex;flex-direction:column;gap:20px;padding:0 14px;animation:hero-rail-scroll linear infinite}
  .hero-rail-track--reverse{animation-direction:reverse}
  /* Фильтр яркости/насыщенности убран (2026-08-24, п.4, по прямому запросу
     "прозрачность не полностью, а только в участках фейда") — раньше фото
     карусели были притушены ЦЕЛИКОМ (brightness(.65)), теперь чёткие/яркие
     везде, кроме самих зон фейда ниже (те и дают ощущение "прозрачности"
     по краям, а не общее затемнение). */
  .hero-rail-track img{width:100%;aspect-ratio:3/4;object-fit:cover;flex:none;border-radius:10px;box-shadow:0 5px 14px color-mix(in srgb,var(--shadow-black) 45%,transparent)}
  /* Фейд к внешним краям ПАРЫ (2026-08-24, п.2/4) — не к краям экрана, как
     было (тогда каждая карусель сидела у своего края отдельно), а к
     внешним сторонам самой пары: левая сторона ПЕРВОЙ карусели (переход к
     тексту/фото по центру) и правая сторона ПОСЛЕДНЕЙ (переход к правому
     краю фото за пределами .hero-inner). Между собой (там, где карусели
     соприкасаются через gap) — без фейда, видны чётко. :only-child —
     если админ заполнил только одну из двух каруселей, она одна получает
     фейд с ОБЕИХ сторон (объявлен ПОСЛЕ :first-child/:last-child, поэтому
     переопределяет обоих на одном элементе). Второй, ГОРИЗОНТАЛЬНЫЙ
     mask-image на ВЛОЖЕННОМ .hero-rail-track (не на .hero-rail, у которого
     уже свой вертикальный mask выше) — маски на разных уровнях вложенности
     перемножаются сами по себе через рендеринг, mask-composite не нужен. */
  .hero-rails .hero-rail:first-child .hero-rail-track{
    -webkit-mask-image:linear-gradient(to right,rgba(0,0,0,.2) 0%,black 30%,black 100%);
    mask-image:linear-gradient(to right,rgba(0,0,0,.2) 0%,black 30%,black 100%)}
  .hero-rails .hero-rail:last-child .hero-rail-track{
    -webkit-mask-image:linear-gradient(to left,rgba(0,0,0,.2) 0%,black 30%,black 100%);
    mask-image:linear-gradient(to left,rgba(0,0,0,.2) 0%,black 30%,black 100%)}
  .hero-rails .hero-rail:only-child .hero-rail-track{
    -webkit-mask-image:linear-gradient(to right,rgba(0,0,0,.2) 0%,black 20%,black 80%,rgba(0,0,0,.2) 100%);
    mask-image:linear-gradient(to right,rgba(0,0,0,.2) 0%,black 20%,black 80%,rgba(0,0,0,.2) 100%)}
  @keyframes hero-rail-scroll{from{transform:translateY(0)}to{transform:translateY(-50%)}}
  @media(prefers-reduced-motion:reduce){.hero-rail-track{animation:none}}
}

/* ── HERO — pin-эффект (fixed + блюр/уменьшение при скролле мимо) теперь и
   на мобильном/планшете, не только на десктопе (2026-08-25, по прямому
   запросу: "высоту сделать во всю высоту браузера, как на десктопе",
   "анимация, чтобы hero-фото оставалось неподвижным", "блюр и уменьшение,
   как на десктопе") — те же правила, что раньше жили только внутри
   @media(min-width:981px) выше, теперь без ограничения по ширине.
   Десктоп-специфика (hero-inner flex-колонка/hero-top-guard/hero-pitch
   position:static, ПАРА hero-rails) осталась только в блоке выше — на
   мобильном/планшете hero-pitch по-прежнему на своих left/right/bottom из
   media-правил ниже по файлу (не тронуты), карусель — своя, одна,
   горизонтальная (.hero-rail-mobile, следующий блок). Объединяет две
   исходные desktop-правки .hero{filter:...} (position:fixed-версию и более
   позднюю blur-cap-версию, переопределявшую её filter/transition) в одно
   правило — то же итоговое поведение, без дублирования каскада. */
.hero{position:fixed;top:0;left:0;right:0;z-index:1;
  height:100vh;min-height:544px;max-height:none;overflow:visible}
/* Блюр НЕ общий на всём .hero — каждая группа элементов получает СВОЁ
   готовое значение напрямую (--pitch-blur/--carousel-blur/--bg-blur),
   уже посчитанное с учётом своего диапазона перекрытия, initHeroPinScroll()
   в index.php выставляет их на #hero каждый кадр; наследуются потомками
   как обычные CSS-переменные. */
.hslide,.hshade{filter:blur(var(--bg-blur,0px));transition:filter var(--hero-cover-smoothing) ease}
.hero-rails,.hero-rail-mobile-wrap{filter:blur(var(--carousel-blur,0px));transition:filter var(--hero-cover-smoothing) ease}
/* Светлый ободок при блюре — перевылет фото/градиента за границы .hero на
   40px (тот же приём halo-фикса, что раньше был только на десктопе). */
.hslide{inset:-40px}
.hshade{inset:-40px}
.hero-spacer{display:block;height:calc(100vh - 60px);min-height:544px;max-height:none}
body:has(#hero) .page-body{padding-top:0}
.hero-lift{position:relative;z-index:10}
.hero-pitch{
  transform:scale(var(--pitch-scale,1)) translateY(var(--pitch-shift,0px));
  filter:blur(var(--pitch-blur,0px));
  opacity:var(--pitch-opacity,1);
  transition:transform var(--hero-cover-smoothing) linear,opacity var(--hero-cover-smoothing) ease,filter var(--hero-cover-smoothing) ease}
.hprogress{opacity:var(--pitch-opacity,1);transition:opacity var(--hero-cover-smoothing) ease}
/* Заливка sec-b2/sec-how — bleed-through сквозь неподвижный hero. */
#sec-b2.section-content,
#sec-how.section-content{background:color-mix(in srgb,var(--bg-main) 60%,transparent)}
/* .hero-pitch-hidden — единственный оставшийся boolean-класс (только для
   pointer-events, всё остальное — прямые значения из custom properties
   выше, см. initHeroPinScroll() в index.php и animation-tokens.css). */
body.hero-pitch-hidden .hero-pitch,
body.hero-pitch-hidden .hprogress{pointer-events:none}
/* Service Areas/финальный CTA/футер — вне .hero-lift (см. footer.php),
   поверх зафиксированного hero нужен свой z-index (та же логика, что была
   только на десктопе, теперь hero зафиксирован и на мобильном тоже). */
body:has(#hero) #sec-areas,
body:has(#hero) #final-cta,
body:has(#hero) .foot{position:relative;z-index:10}
body:has(#hero) .foot{margin-top:0;padding-top:70px}
@media(prefers-reduced-motion:reduce){
  .hero-spacer{display:none}
}
@media(min-width:981px){
  /* .hprogress — bottom:70px, приподнят над низом hero-pitch, гаснет
     вместе с ним (opacity:var(--pitch-opacity), см. выше по файлу).
     Только десктоп — на мобильном свой отдельный расчёт позиции
     (.hprogress{bottom:0px}, база), не трогаем. */
  .hprogress{bottom:70px}
}

/* ── HERO — карусель на мобильном/планшете (2026-08-25, по прямому
   запросу) — ОДНА горизонтальная лента, не пара вертикальных, как на
   десктопе (на узком экране пара 25-30%-х вертикальных колонок вышла бы
   слишком узкой/нечитаемой; горизонтальная лучше подходит портретной
   ориентации). Список фото — оба десктопных набора
   (heroRailLeft/heroRailRight), смешанные ПООЧЕРЁДНО в один список в PHP
   (см. $heroRailMobile, index.php), а не два раздельных блока. Та же
   техника бесконечной прокрутки, что у .hero-rail-track/.usp-track-
   marquee — контент задублирован в PHP, translateX едет ровно на -50%
   (граница между копиями) и зацикливается. Блюр при pin-scroll наследуется
   автоматически — лента лежит внутри .hero-inner внутри .hero, а
   filter:blur() родителя блюрит всё поддерево целиком, свой filter не
   нужен.
   Размер/позиция — правка 2026-08-25 (2), по прямому запросу: "карточки
   крупнее, до 35-40%" (высоты браузера — тот же принцип, что раньше был
   у desktop-варианта "25-30% ширины", только тут поперечная к прокрутке
   ось — высота, не ширина) — height:150px/120px → 38vh, единое значение на
   всех мобильных шириных (vh уже сам масштabируется под меньший экран,
   отдельный override на ≤560px не нужен). "Опустить на 20% от верхней
   границы" — top:0 → top:20vh (заодно ушла из-под прозрачной шапки —
   раньше верхний ряд фото частично перекрывался логотипом/гамбургером).
   Обёртка .hero-rail-mobile-wrap (снаружи) / .hero-rail-mobile (внутри) —
   блюр/масштаб острый край давал ровно потому, что раньше filter:blur()
   стоял на ТОМ ЖЕ элементе, что и overflow:hidden+mask — сам блюр
   обрезался собственной границей элемента. Позиция/размер/блюр/масштаб —
   на внешней НЕобрезающей .hero-rail-mobile-wrap; overflow:hidden+mask —
   на внутренней .hero-rail-mobile (100% размера обёртки), блюр
   композитится уже НАД ней, свободно перевыступая за края. */
.hero-rail-mobile-wrap{display:none}
@media(max-width:980px){
  .hero-rail-mobile-wrap{display:block;position:absolute;top:10vh;left:0;right:0;height:38vh;z-index:2;
    transform:scale(var(--carousel-scale,1));transition:transform var(--hero-cover-smoothing) ease}
  .hero-rail-mobile{width:100%;height:100%;overflow:hidden;
    -webkit-mask-image:linear-gradient(to right,transparent 0%,black 8%,black 92%,transparent 100%);
    mask-image:linear-gradient(to right,transparent 0%,black 8%,black 92%,transparent 100%)}
  /* padding:8px 0 — та же причина, что у десктопной .hero-rail-track
     (см. её комментарий выше): overflow:hidden у .hero-rail-mobile
     обрезал тень карточек вплотную к их верху/низу. */
  .hero-rail-mobile-track{display:flex;flex-direction:row;gap:16px;height:100%;width:max-content;padding:14px 0;animation:hero-rail-mobile-scroll linear infinite}
  /* offset-y:3px, blur:10px — вниз тень уходит на 13px (offset+blur),
     укладывается в padding:14px 0 у трека выше без обрезки (при offset:5/
     blur:14 вниз было 19px — на 5px больше отступа, обрезалось снизу). */
  .hero-rail-mobile-track img{height:100%;width:auto;aspect-ratio:3/4;object-fit:cover;flex:none;border-radius:10px;box-shadow:0 3px 10px color-mix(in srgb,var(--shadow-black) 45%,transparent)}
  @keyframes hero-rail-mobile-scroll{from{transform:translateX(0)}to{transform:translateX(-50%)}}
  @media(prefers-reduced-motion:reduce){.hero-rail-mobile-track{animation:none}}
}
/* Движение обеих лент (.hero-rail-track десктоп / .hero-rail-mobile-track
   мобильный) — БЕЗ гейтинга, крутится непрерывно с самой загрузки
   страницы (animation:...infinite выше, без play-state паузы). */

/* ── Фоновое фото секции целиком (2026-08-24) — Why/How, опционально из
   adm-homepage.php. Низкая непрозрачность + светлый градиент поверх, чтобы
   карточки/текст поверх сохраняли контраст на любом фото. */
.has-bg-photo{position:relative;overflow:hidden}
.has-bg-photo::before{content:'';position:absolute;inset:0;z-index:0;
  background:var(--section-bg-photo) center/cover no-repeat;opacity:.2;filter:saturate(.7)}
.has-bg-photo::after{content:'';position:absolute;inset:0;z-index:0;
  background:linear-gradient(180deg,var(--bg-main) 0%,color-mix(in srgb,var(--bg-main) 55%,transparent) 40%,var(--bg-main) 100%)}
.has-bg-photo>*{position:relative;z-index:1}

/* ── Reveal-on-scroll (2026-08-24) — IntersectionObserver в site.js
   переключает .is-visible, тот же приём, что уже используется для
   hotspot-попапов/тач-паузы карусели в этой же кодовой базе. ── */
.reveal-item{opacity:0;transform:translateY(var(--reveal-move-y));transition:opacity var(--reveal-duration) ease,transform var(--reveal-duration) ease}
.reveal-item.is-visible{opacity:1;transform:translateY(0)}
@media(prefers-reduced-motion:reduce){
  .reveal-item{opacity:1;transform:none;transition:none}
}

/* ── WHY PLASTER (layout only — .why-card lives in shared.css) ── */
.why-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:36px}
/* Карточки разной высоты (2026-08-24, найдено по прямому запросу) — грид
   по умолчанию (align-items:stretch) растягивает СВОИ прямые дети до
   высоты ряда, но прямой ребёнок здесь — .why-reveal (обёртка для
   scroll-reveal анимации, index.php), а не сама .why-card; .why-reveal
   растягивалась, а .why-card внутри нею оставалась по высоте контента —
   визуально карточки с более длинным текстом были выше. display:flex на
   обёртке + flex:1 на самой карточке — .why-card дотягивается до полной
   высоты уже растянутой обёртки. */
.why-grid .why-reveal{display:flex}
.why-grid .why-reveal .why-card{flex:1 1 auto}
/* Реveal-анимация карточек #sec-b2 (2026-08-25, по прямому запросу) — свой
   класс .why-reveal, НЕ .reveal-item: тот наблюдается общим
   initRevealOnScroll() (site.js), у которого другие threshold/rootMargin и
   он ДОБАВЛЯЕТ opacity:0 в стартовое состояние — если бы карточки остались
   на .reveal-item, здесь оказалось бы ДВА независимых IntersectionObserver
   на одних и тех же элементах, переключающих один и тот же класс
   .is-visible вразнобой (та же гонка, что нашли и исправили у .how-card
   этой же сессией). Здесь карточка видна СРАЗУ (opacity не анимируется,
   только blur+сдвиг) — по прямому запросу "карточка должна быть видна
   сразу при прокрутке, но ... размыта блюром". initWhyCardsReveal()
   (index.php) — свой IntersectionObserver, rootMargin:'0px 0px -5% 0px'
   threshold:0 (срабатывает, когда верхний край элемента пересекает линию
   на 5% выше нижней границы окна, не зависит от доли видимой площади
   самого элемента) + toggle (не одноразово — при обратном скролле мимо
   границы карточка возвращается в состояние покоя и проигрывает анимацию
   заново). */
.why-reveal{opacity:1;transform:translateY(var(--why-reveal-move-y));filter:blur(var(--why-reveal-blur));transition:transform var(--why-reveal-move-duration) ease,filter var(--why-reveal-blur-duration) ease}
.why-reveal.is-visible{transform:translateY(0);filter:blur(0)}
/* Заголовок раздела — тот же триггер (initWhyCardsReveal()), без
   отдельного класса (нет причины: .section-title тут уникален в контексте
   #sec-b2, и до этой правки вообще ни в какой reveal-системе не состоял —
   конфликтовать не с чем, доп. имя только усложнило бы разметку).
   --section-title-* — общие с #sec-how .section-title, см.
   assets/animation-tokens.css (мобильный вариант — тоже там, отдельного
   @media здесь больше не нужно). */
#sec-b2 .section-title{transform:translateX(var(--section-title-move-x));filter:blur(var(--section-title-blur));transition:transform var(--section-title-move-duration) ease,filter var(--section-title-blur-duration) ease}
#sec-b2 .section-title.is-visible{transform:translateX(0);filter:blur(0)}
@media(prefers-reduced-motion:reduce){
  .why-reveal,#sec-b2 .section-title{opacity:1;transform:none;filter:none;transition:none}
}

/* ── SERVICES — .svc-card/.svc-quote/.svc-materials/.svc-desc/.svc-grid все живут в shared.css ── */

/* .section-content (фон секции) — общий, shared.css. */

/* ── PRODUCT CAROUSEL (Block 3, редизайн v2) — сам компонент (.rail/.card/
   .rail-progress/.rail-nav и т.п.) целиком в shared.css, переиспользуется
   и для Sample/Portfolio ниже. Здесь только специфика секции. Старый
   вертикальный список+вьюер (.mat-layout, .vcar-family, .mview-family,
   .mname-big, .mdesc и их JS в index.php) убран целиком при переходе на
   новую карусель. */

/* ── SAMPLE COLLECTION / PORTFOLIO (Blocks 4/5, редизайн v2) — сам
   компонент карусели (.rail/.rail--sample/.rail--project/.card-grid и
   т.п.) целиком в shared.css, общий с Block 3 (Product) выше. Здесь только
   специфика секций. Старая горизонтальная лента (.rail-scroll/.rel-nav +
   scrollRail() в index.php) убрана целиком при переходе на новую карусель. */

/* ── РЕДИЗАЙН ГЛАВНОЙ 2026-08-17 ─────────────────────────────────────── */

/* Пилюли-табы + стрелки под каруселью образцов — НЕ удалены из разметки (по
   прямому запросу, п.2.2), просто скрыты здесь. .rail--product тоже
   использовал .rail-nav, но убран с главной вместе с сам Block 3, так что
   это правило безопасно применяется только к Samples. */
.rail--sample .rail-nav{display:none}

/* "Покрепче" (.sec-viewall--strong) — CSS перенесён в shared.css
   (2026-08-19, "ссылки Explore сделай как на главной" — теперь общий вид
   для ВСЕХ "Explore …" ссылок на детальных страницах, не только главной). */

/* WhatsApp FAB + раскрывающееся ВВЕРХ меню (п.2.7/2.9) — CSS перенесён в
   shared.css (2026-08-19, "добавь кнопку меню чата на всех остальных
   публичных страницах" — .fab-stack/.wa-fab-*), т.к. работает теперь на
   каждой публичной странице, не только на главной; разметка — в
   includes/footer.php, JS — initWaFab() в site.js. Здесь ничего не
   осталось — класс не удалён из homepage.css целиком, чтобы не потерять
   историю комментария при будущем поиске. */

/* CTA в разделах Samples/Projects — уменьшены по прямому запросу (п.3,
   затем расширено на Projects п.4, 2026-08-17): CTA внутри секции, идёт
   сразу после карусели/сетки, не должен быть таким же тяжёлым, как обычный
   финальный .fcta перед футером (padding:100px 0, h2 до 58px). Тот же
   компонент/классы, просто компактнее именно в этих местах (id проброшен
   через $htmlId в renderFctaSection()) — финальный .fcta (#sec-fcta,
   footer.php) остаётся полноразмерным, это заключительный CTA страницы. */
/* НАЙДЕНО И ИСПРАВЛЕНО (2026-08-19): раньше здесь стоял padding:0 — id и
   класс .fcta сидят на ОДНОМ И ТОМ ЖЕ теге (<section class="fcta"
   id="sec-fcta-samples">, см. renderFctaSection() в includes/fcta.php),
   поэтому этот ID-селектор (специфичность выше класса) полностью
   перебивал верхний/нижний padding базового .fcta{padding-top:20px;
   padding-bottom:20px} из shared.css — на главной эти секции оставались
   вообще без своего вертикального отступа, только на ритме родителя
   (.b1/.b2/.b3{padding:30px 0}). По прямому запросу (2026-08-19) отступ
   .fcta восстановлен явно, тем же значением 20px/20px, что и у базового
   класса — секция снова сама держит вертикальный отступ независимо от
   родителя. Остальные переопределения ниже (h2/p/кнопки/trust-badges)
   не трогали — они и раньше были нужны именно здесь. */
#sec-fcta-samples,
#sec-fcta-projects,
#sec-fcta{padding-top:00px;padding-bottom:00px}
#sec-fcta-samples h2,
#sec-fcta-projects h2,
#sec-fcta h2{font-size:clamp(19px,2.4vw,26px);margin-bottom:15px}
#sec-fcta-samples p,
#sec-fcta-projects p,
#sec-fcta p{font-size:13px;margin-bottom:16px}
#sec-fcta-samples .fcta-main,#sec-fcta-samples .fcta-sub,
#sec-fcta-projects .fcta-main,#sec-fcta-projects .fcta-sub,
#sec-fcta .fcta-main,#sec-fcta .fcta-sub{padding:10px 20px;font-size:12.5px}
#sec-fcta-samples .trust-badges,
#sec-fcta-projects .trust-badges,
#sec-fcta .trust-badges{display:none}

/* ── HOW WE WORK — степпер-список (2026-08-25, редизайн по прямому запросу,
   заменил автопрокручиваемый .how-slideshow + отдельную .how-grid).
   Карточки в столбик, каждая 80% ширины .page, прижата к правому краю
   .how-row (margin-left:auto) — слева остаётся колонка под номер+линию
   (.how-row-num-wrap, .how-steps-track). Одна СКВОЗНАЯ вертикальная линия
   (не по сегменту на пару кружков) — .how-steps-track, абсолютно
   спозиционирована по центру номеров (left = половина ширины кружка),
   от центра ПЕРВОГО кружка до центра ПОСЛЕДНЕГО (--how-track-top/-bottom,
   считает initHowSteps() в index.php через getBoundingClientRect — точные
   позиции кружков не выразить чистым CSS, т.к. высота карточек зависит от
   контента). .how-steps-track-fill растёт по высоте (0→100%) от прогресса
   скролла через блок — та же rAF-поллинг механика, что у initHeroPinScroll/
   initNavScroll (событие 'scroll' при активном Lenis иногда не успевает
   сработать вовремя, см. их комментарии). Номер "оживает" (блюр→резкость,
   увеличение), когда КУРСОР (страничная координата середины окна, не
   прогресс блока) дотягивается до позиции самого кружка — "граница
   подсветки линии начинается, когда номер пересекает середину высоты
   браузера" (2026-08-25, по прямому запросу, initHowSteps() в index.php).
   Появление самой карточки (блюр+выезд справа) и заголовка (translateX,
   см. ниже) — реверсивный IntersectionObserver (toggle, не одноразовый add
   — 2026-08-25, по прямому запросу "обратная анимация при уходе из
   вьюпорта", применено ко всем reveal-блокам ниже hero). Пунктирная линия
   (repeating-linear-gradient, не сплошная заливка — 2026-08-25, по
   прямому запросу) — и у трека-подложки, и у залитого сегмента. ── */
.how-steps{position:relative;margin-top:10px;display:flex;flex-direction:column;gap:28px}
/* Сплошная линия (2026-08-25, было пунктиром — вернули обратно по прямому
   запросу). Проблема, которую решал пунктир (заблюренная/масштабируемая
   кайма покоящегося .how-row-num-wrap полупрозрачна — блюр всегда чуть
   "рассеивает" непрозрачный край элемента наружу, см. тот же эффект у
   .hero в halo-фиксе выше по файлу — сквозь эту кайму была видна линия под
   кружком) решена иначе: mask-image с прозрачными зазорами ровно в зоне
   каждого кружка — --how-track-mask считает initHowSteps() (index.php,
   layoutTrack()) через реальный offsetWidth кружка (не getBoundingClientRect
   — тот уже учитывает текущий transform:scale(), дал бы МЕНЬШИЙ зазор,
   пока кружок ещё маленький/покоится, а зазор должен быть рассчитан на
   ПОЛНЫЙ размер) + запас 8px на саму мягкую кайму. Пересчитывается на
   resize/load, не на каждый кадр скролла — позиции кружков относительно
   трека не меняются во время самого скролла. */
.how-steps-track{position:absolute;left:27px;top:var(--how-track-top,0);bottom:var(--how-track-bottom,0);width:2px;z-index:0;
  background:var(--line-2);mask-image:var(--how-track-mask,none);-webkit-mask-image:var(--how-track-mask,none)}
.how-steps-track-fill{display:block;width:100%;height:0%;background:var(--accent)}
.how-row{position:relative;width:100%}
.how-row-num-wrap{position:absolute;left:0;top:50%;width:56px;height:56px;border-radius:50%;background:var(--bg-main);border:1px solid var(--line-2);display:flex;align-items:center;justify-content:center;z-index:1;
  transform:translateY(-50%) scale(var(--how-num-scale-dormant));filter:blur(var(--how-num-blur));opacity:var(--how-num-opacity-dormant);
  transition:filter var(--how-num-duration) ease,opacity var(--how-num-duration) ease,transform var(--how-num-duration) cubic-bezier(.22,1.3,.36,1)}
.how-row-num-wrap.is-active{transform:translateY(-50%) scale(1);filter:blur(0);opacity:1}
.how-row-num{font-family:var(--mono);font-size:17px;font-weight:900;color:var(--ink)}
.how-card{position:relative;width:80%;margin-left:auto;overflow:hidden;border-radius:var(--r);background:var(--bg-card);border:1px solid var(--line-2);min-height:220px;display:flex;align-items:flex-end;padding:26px 28px;box-shadow:0 4px 10px var(--shadow-card);
  opacity:0;filter:blur(var(--how-card-blur));transform:translateX(var(--how-card-move-x));transition:opacity var(--how-card-duration) ease,filter var(--how-card-duration) ease,transform var(--how-card-duration) ease}
.how-card.is-visible{opacity:1;filter:blur(0);transform:translateX(0)}
.how-card-bg{position:absolute;inset:0;z-index:0}
.how-card-bg img{width:100%;height:100%;object-fit:cover;display:block}
/* Тот же приём контраста текста, что у .hero-pitch/.card--overlay — градиент
   темнее к низу (там сидит текст) + text-shadow на самом тексте, а не
   сплошная плашка-подложка поверх фото. */
.how-card-shade{position:absolute;inset:0;z-index:1;background:linear-gradient(180deg,color-mix(in srgb,var(--overlay-dark) 10%,transparent) 0%,color-mix(in srgb,var(--overlay-dark) 68%,transparent) 100%)}
.how-card-text{position:relative;z-index:2;max-width:640px}
.how-card-text h3{font-family:var(--display);font-size:clamp(20px,2.2vw,26px);font-weight:700;margin:0 0 10px;letter-spacing:-.005em;
  opacity:0;transform:translateX(var(--how-card-text-move-x));transition:opacity .29s ease .12s,transform .35s ease .12s}
.how-card-text p{font-size:14.5px;line-height:1.65;color:var(--ink-dim);margin:0;
  opacity:0;transform:translateX(var(--how-card-text-move-x));transition:opacity .29s ease .22s,transform .35s ease .22s}
.how-card.is-visible .how-card-text h3,
.how-card.is-visible .how-card-text p{opacity:1;transform:translateX(0)}
.how-card--photo .how-card-text h3,
.how-card--photo .how-card-text p{color:var(--text-on-accent);text-shadow:0 1px 3px color-mix(in srgb,var(--shadow-black) 55%,transparent),0 2px 10px color-mix(in srgb,var(--shadow-black) 40%,transparent)}
.how-card-placeholder-n{position:absolute;right:24px;bottom:8px;z-index:0;font-family:var(--display);font-size:clamp(70px,9vw,120px);font-weight:700;color:var(--line-2)}
/* Заголовок раздела — тот же приём, триггер и переменные (--section-title-*,
   assets/animation-tokens.css), что у #sec-b2 .section-title (2026-08-25,
   по прямому запросу "как в блоке Why Clients Choose"); своего подзаголовка
   у этого раздела в разметке нет — анимировать нечего. */
#sec-how .section-title{transform:translateX(var(--section-title-move-x));filter:blur(var(--section-title-blur));transition:transform var(--section-title-move-duration) ease,filter var(--section-title-blur-duration) ease}
#sec-how .section-title.is-visible{transform:translateX(0);filter:blur(0)}
@media(prefers-reduced-motion:reduce){
  .how-row-num-wrap,.how-card,.how-card-text h3,.how-card-text p,
  #sec-how .section-title{transition:none;filter:none;opacity:1;transform:none}
}
@media(max-width:980px){
  .how-steps-track{left:21px}
  .how-row-num-wrap{width:44px;height:44px}
  .how-row-num{font-size:14px}
  .how-card{min-height:190px;padding:20px 22px}
}
@media(max-width:560px){
  .how-steps{gap:16px}
  .how-steps-track{left:16px}
  .how-row-num-wrap{width:32px;height:32px}
  .how-row-num{font-size:11px}
  .how-card{min-height:160px;padding:16px 16px}
  .how-card-text h3{font-size:17px}
  .how-card-text p{font-size:13px}
}
/* "For Trade" — заглушка (см. комментарий в index.php: нет ни /for-trade/,
   ни B2B-программы под неё) — тёмная тонкая полоса, тот же тёмный тон
   (--ink), что у остальных акцентов "на контрасте" сайта (напр. .wa-fab). */
.trade-banner{margin: 15px 0px; background:var(--ink);color:color-mix(in srgb,var(--text-on-accent) 82%,transparent)}
/* НАЙДЕНО при подборе тёмной палитры (2026-08-26): --ink здесь используется
   как "всегда тёмный акцентный фон" (см. комментарий выше), а не как цвет
   текста — но именно --ink обязан стать СВЕТЛЫМ в тёмной теме (это токен
   текста). Без этой правки полоса в тёмной теме перекрасилась бы в светлую,
   и золотая ссылка (--trade-gold) стала бы нечитаемой. Точечный оверрайд —
   светлая тема не тронута (тот же #2E2420, что и раньше давал var(--ink)). */
:root[data-theme="dark"] .trade-banner{background:#2E2420}
/* Сама плашка — БЕЗ прозрачности (2026-08-25, уточнение по прямому запросу:
   первая версия правки полупрозрачную заливку ставила на весь .trade-banner
   целиком — оказалось не тем, что просили). Прозрачность/hero-просвечивание
   нужны ТОЛЬКО в зоне собственного margin:15px 0px плашки (см. правило
   выше) — margin никогда не красится фоном самого элемента (это пустой
   зазор снаружи бокса), поэтому эти 15px сверху/снизу — тот же класс бага,
   что уже находили у #final-cta/.foot (footer.php) в этой же сессии: пустой
   зазор между opaque-блоками, сквозь который на homepage (зафиксированный
   hero на всю высоту позади всего) видно то, что лежит ниже по z-index. Там
   мы эту дыру закрывали (переносили margin в padding, чтобы вообще НЕ было
   прозрачности); здесь — наоборот, ПРОЗРАЧНОСТЬ в этой зоне нужна нарочно
   (по прямому запросу), поэтому не наглухо закрываем, а рисуем в неё
   собственный полупрозрачный фон через ::before/::after (сама margin
   по-прежнему не крашена — псевдоэлементы просто накладываются НА эту
   область, визуально неотличимо).
   Больше НЕ ограничено ≥981px (2026-08-25(2), нашли по прямому репорту —
   комментарий ниже был устаревшим: раньше hero не был зафиксирован на
   мобильном, теперь (см. универсальный pin-блок выше по файлу) hero fixed
   на всех ширинах, sec-b2/sec-how уже давно универсальны — trade-banner
   тогда забыли перенести, отсюда баг "на мобильном фона в зоне маржин
   нет". */
.trade-banner{position:relative}
/* Тот же цвет заливки, что у sec-b2/sec-how bleed-through (2026-08-25, по
   прямому запросу "фон в зоне маржин сделать так же, как в блоке how we
   work") — rgba(246,241,232,.6), не тёмный rgba(46,36,32,.6), как было. */
.trade-banner::before,
.trade-banner::after{content:'';position:absolute;left:0;right:0;height:15px;background:color-mix(in srgb,var(--bg-main) 60%,transparent)}
.trade-banner::before{top:-15px}
.trade-banner::after{bottom:-15px}
.trade-banner-inner{display:flex;flex-wrap:wrap;align-items:center;gap:14px;padding:18px 40px}
.trade-banner-inner p{flex:1;min-width:240px;margin:0;font-size:14.5px;line-height:1.5}
.trade-banner-inner a{color:var(--trade-gold);border-color:var(--trade-gold-border)}
.trade-banner-inner a:hover{color:var(--text-on-accent);border-color:var(--text-on-accent)}
@media(max-width:560px){
  /* НАЙДЕНО И ИСПРАВЛЕНО: ссылка "For Trade →", прижатая вправо в общей
     строке с текстом, оказывалась в том же нижнем правом углу экрана, что
     и .wa-fab-root (position:fixed) — перекрывалась кнопкой при скролле,
     когда баннер попадал в эту зону viewport'а. В колонку — ссылка уходит
     из угла совсем, а не просто уступает кнопке чуть места. */
  .trade-banner-inner{flex-direction:column;align-items:flex-start;padding:16px 10vw}
}

@media(max-width:980px){
  .usp-item{padding:0 22px}
  .nav-mid{display:none}
  .hcard{left:20px;right:20px;max-width:none;bottom:70px;padding:24px}
  /* .hero-pitch на планшете — тот же нижний левый угол, шире под прямому
     запросу (было max-width:55%), только теги скрыты — под заголовок+
     подстрочник+CTA места хватает благодаря самой плашке.
     bottom:calc(20vh + 60px) (2026-08-25, по прямому запросу "поднять
     блок выше, низ на 20% высоты браузера + высота usp") — было 48px;
     60px — высота .usp-strip (та же константа, что в calc(100vh - 60px) у
     .hero-spacer выше по файлу). */
  .hero-pitch{width:auto;max-width:68%;left:16px;bottom:calc(5vh + 60px);padding:22px 24px}
  .hero-pitch .hero-h1{font-size:clamp(15px,2.6vw,19px)}
  .hero-pitch-tags{display:none}
  /* .hprogress на мобильном — база (bottom:0px) пряталась под непрозрачной
     usp-strip (60px, z-index:10 выше hero) — не было видно с самой
     загрузки. В ЗАЗОРЕ между .hero-pitch и usp-strip, не поверх текста: у
     .hero-pitch bottom:calc(5vh + 60px) — это ФИКСИРОВАННАЯ позиция его
     НИЖНЕГО края (сам блок растёт вверх от неё, высота зависит от текста,
     но нижний край всегда там же), а верх usp-strip — ровно 60px от низа
     hero — между ними стабильный зазор высотой 5vh, не зависящий от
     длины текста pitch. bottom:calc(60px + 2vh) — примерно посередине
     этого зазора, без своей плашки/фона (по прямому запросу — раньше был
     добавлен фон, чтобы не сливалось с .hero-pitch-tags, но раз элемент
     в зазоре, а не поверх текста, подложка не нужна). */
  .hprogress{bottom:calc(60px + 2vh)}
  .why-grid{grid-template-columns:repeat(2,1fr)}
}
@media(max-width:560px){
  /* .nav{padding:0 20px} — теперь общее правило в shared.css. */
  /* .page — единая ширина карточки (--card-side-pad, см. header.php,
     считается из site_settings.mobile_card_width_pct, настраивается в
     adm-settings.php) — та же переменная, что и на других страницах
     (карусель b1 её не использует, см. --card-w-fixed). .fcta-wrap — своя
     отдельная ширина (80%, не завязана на эту настройку — не карточка). */
  .page{padding-left:var(--card-side-pad);padding-right:var(--card-side-pad)}
  .fcta-wrap{padding-left:0vw;padding-right:0vw}
  .usp-item{padding:0 16px}
  .why-grid{grid-template-columns:1fr}
  /* Фиксированная высота 550px (2026-08-24) ОТМЕНЕНА (2026-08-25, по
     прямому запросу "высоту сделать во всю высоту браузера, как на
     десктопе") — .hero теперь 100vh на всех ширинах, универсальное правило
     см. выше по файлу (pin-блок вне media-запроса). Тут больше нечего
     переопределять. */
  /* .hero-pitch на мобильном — та же плашка нижнего левого угла, что на
     планшете (bottom наследуется из @980px — calc(20vh + 60px), не
     переопределён тут), мельче шрифт/паддинги/кнопки под самый узкий
     экран. left/right — по прямому запросу заменены на равные отступы с
     обеих сторон (было только left:10px, а правый край плавал по ширине
     контента) + сам отступ чуть увеличен (10px→14px), тот же приём
     left+right без width, что у .hcard на @980px выше. */
  .hero-pitch{max-width:none;left:14px;right:14px;padding:16px 18px}
  .hero-pitch .hero-h1{font-size:clamp(24px,3.4vw,15px);margin-bottom:8px;line-height:1.3}
  .hero-pitch-area{font-size:13px;margin-bottom:10px}
  .hero-pitch-cta{gap:8px;margin-bottom:10px}
  .hero-pitch-cta .fcta-main,.hero-pitch-cta .fcta-sub{padding:8px 14px;font-size:11.5px}
  /* По прямому запросу (2026-08-19) теги снова видны на мобильном — раньше
     .hero-pitch-tags{display:none} из @980px (тег наследуется вниз по
     каскаду на все более узкие ширины) скрывал их и на телефоне тоже, хотя
     решение "места хватает и без тегов" принималось именно под планшет.
     Здесь — свой, более мелкий/компактный вид под самый узкий экран. */
  .hero-pitch-tags{display:flex;font-size:9.5px;gap:6px}
  .usp-item span{font-size:12.5px}
  .usp-item{padding:0 14px}
  /* Вертикальный отступ блоков b1-b5 на мобильном — ОДИН параметр
     (--section-pad-y-m), заданный один раз и применённый одной строкой
     сразу ко всем пяти секциям, top и bottom всегда равны по построению
     (padding: N 0 не может дать разные top/bottom). Раньше у b4 стояло
     10px 0 20px (по отдельной, более ранней просьбе) — теперь везде
     одинаковое значение, по прямому запросу "как для карусели образцов". */
  :root{ --section-pad-y-m: 10px; --section-subtitle-mt-m: 5px; }
  .b1,.b2,.b3,.b4,.b5{ padding: var(--section-pad-y-m) 0; }
  /* Отступ подзаголовка (.section-subtitle, shared.css — там margin-top:14px
     общий для всех страниц) — на мобильном главной сужен одним параметром
     сразу для всех блоков (сама разметка .section-subtitle только на
     homepage.css грузится на этой странице, доп. scoping не нужен). */
  .section-subtitle{ margin-top: var(--section-subtitle-mt-m); }
  /* .section-head (shared.css: padding-bottom:20px общий) — на мобильном
     главной сужен для всех блоков, было 0 только для b4, теперь 10px везде. */
  .section-head{padding-bottom:10px}
  #sec-b1 .rail-nav{margin-top:0}
  /* .sec-more (shared.css: margin-top:20px общий) — было переопределено
     только для b4 (10px), b3/b5 отставали на базовых 20px — теперь везде
     одинаково. */
  .sec-more{margin-top:10px}
}

/* ══════════════════════════════════════════════════════════════════
   SAMPLE RAIL (b1) — карточки-планшеты MDF, бесконечная peek-карусель
   БЕЗ клонов и БЕЗ native scroll (см. assets/infinite-carousel.js —
   InfiniteCarousel, подключается и настраивается в index.php). Карточки
   позиционируются через transform:translateX(), который считает JS по
   --visible/--peek/--card-gap ниже; сам CSS не знает ни про virtual-
   index, ни про модульную арифметику — только геометрию и брейкпоинты.
   .rail--project/b3 не затронут, использует свой старый механизм.

   --visible — сколько карточек показано ЦЕЛИКОМ; --peek — % ширины
   viewport'а, отданный под "кусочек" соседней карточки С КАЖДОЙ стороны;
   мобильный брейкпоинт (по умолчанию, без media) — 1 карточка + peek
   10-15%, планшет (≥768px) — 2 карточки, десктоп (≥1024px) — 3. ══ */
.rail--sample{
  --visible: 1;
  --peek: 12%;
  --card-gap: 16px;
}
@media(min-width:768px){
  .rail--sample{ --visible: 2; --peek: 9%; }
}
@media(min-width:1024px){
  /* По прямому запросу: на десктопе карточка фиксированной ширины 360px
     (не доля от --visible/--peek — см. --card-w-fixed в
     infinite-carousel.js::_measure()), отступ между карточками — тот же,
     что на мобильном (--card-gap:16px, не 24px). Сколько карточек в итоге
     влезает с кусочками соседних по краям при контейнере 1200px (.rail
     {max-width:1200px}) — уже не вопрос настройки, а прямое следствие
     этих двух чисел: 1200 − 3×360 − 2×16 = 88px остатка, поровну по 44px
     с каждого края на peek соседних карточек. --visible/--peek здесь
     больше не используются (card-w-fixed их полностью подменяет). */
  .rail--sample{ --card-w-fixed: 360px; --card-gap: 16px; }
}

.rail--sample .rail-viewport{
  position: relative;
  overflow: hidden;
  /* aspect-ratio:auto ОБЯЗАТЕЛЕН: shared.css (общее правило с ещё
     нетронутым .rail--project) на мобильном форсит
     .rail--sample,.rail--project .rail-viewport{aspect-ratio:1/1} —
     квадратный viewport. Карточка-планшет портретная (3:4, выше чем
     шире), высоту viewport'а теперь считает .card-grid (см. ниже) — без
     явного сброса aspect-ratio старое правило снова включалось бы и
     обрезало карточку снизу квадратной рамкой. */
  aspect-ratio: auto;
  height: auto;
  touch-action: pan-y; /* горизонтальный жест ловит сама карусель (pointer-события), вертикальный скролл страницы не блокируем */
  /* НАЙДЕНО И ИСПРАВЛЕНО: зона затухания маски (8%) была БОЛЬШЕ, чем
     --peek на десктопе (7%) — весь "кусочек" соседней карточки целиком
     попадал в полностью прозрачную часть градиента и визуально исчезал
     (на мобильном --peek:12% > 8%, поэтому там было незаметно). Зона
     затухания уменьшена до 3% — заметно меньше самого маленького --peek
     из всех брейкпоинтов, так что часть соседней карточки всегда остаётся
     видимой и только у самого края мягко сходит на нет. */
  -webkit-mask-image: linear-gradient(to right, transparent 0, #000 3%, #000 97%, transparent 100%);
  mask-image: linear-gradient(to right, transparent 0, #000 3%, #000 97%, transparent 100%);
}
.rail--sample .rail-viewport.is-dragging{ cursor: grabbing; }

/* НАЙДЕНО И ИСПРАВЛЕНО: раньше запас под тень карточки давали padding на
   .rail-viewport + отрицательный margin той же величины, чтобы "погасить"
   его влияние на соседние блоки (.rail-nav ниже) — арифметика на практике
   не сходилась, .rail-nav наезжал на .card-grid. Проще и надёжнее: запас
   зашит ПРЯМО в высоту .card-grid (+46px: 12 сверху + 34 снизу под тень —
   верхний отступ уменьшен по прямому запросу, было 28),
   а сама карточка сдвинута вниз на ту же величину через top у .mdf-card —
   никаких компенсирующих отрицательных отступов, .rail-nav просто идёт
   следом в обычном потоке за реальной, ничем не облегчённой высотой ряда. */
.rail--sample .card-grid{
  position: relative;
  height: calc(var(--card-w, 200px) * 4 / 3 + 46px);
}
.rail--sample .mdf-card{
  --mdf-w: var(--card-w);
  position: absolute;
  top: 12px;
  left: 50%;
  /* transform (translateX(-50%) translateX(offset)) ставит JS каждый
     кадр — см. InfiniteCarousel._render(). Транзишен ТОЛЬКО на box-shadow —
     базовое правило .mdf-card (mdf-card.css) вешает transition:transform
     .38s ..., box-shadow .38s ease для эффекта наклона/hover; transform
     обязательно без транзишена — иначе браузер пытался бы плавно
     доанимировать КАЖДЫЙ inline transform, который ставит rAF-цикл
     движка (много раз в секунду), поверх уже своей eased-анимации — то же
     "размазывание", что получалось бы от двух наложенных анимаций сразу.
     box-shadow, наоборот, меняется только по :hover (см. ниже) — транзишен
     на нём безопасен и даёт плавное появление/скрытие тени вместо
     мгновенного скачка (по прямому запросу 2026-08-18). */
  transition: box-shadow .3s ease;
  /* Курсор-ссылка (по прямому запросу 2026-08-18) — любая карточка либо
     сразу открывает страницу образца, либо доводит карусель до неё же
     (см. click-обработчик в initSampleRail()), в обоих случаях это не
     "перетаскивание рельсы за карточку", поэтому grab/grabbing больше не
     подходит как основной курсор. */
  cursor: pointer;
}
/* Во время реального драга (не просто hover) — grabbing, как и раньше:
   отдельное состояние жеста, не конфликтует с курсором-ссылкой в покое. */
.rail--sample .rail-viewport.is-dragging .mdf-card{ cursor: grabbing; }

/* Раньше здесь была отдельная хореография "увеличенная тень у центральной
   карточки, погашенная у соседей" (.is-centered/is-prev-card/is-next-card
   + JS в initSampleRail()) — убрана целиком по прямому запросу 2026-08-18.
   Теперь тень — обычный :hover любой карточки (базовое правило
   .mdf-card:hover в mdf-card.css, box-shadow.28→.38 unit — уже плавное за
   счёт transition выше, отдельно переопределять здесь больше не нужно). */

/* ── стрелки-подсказки у краёв карусели — по прямому запросу: визуально
   отличить "клик здесь пролистает карусель" от "клик здесь откроет
   образец". НАЙДЕНО И ИСПРАВЛЕНО дважды: (1) раньше это был ::after ПРЯМО
   НА .mdf-card — двигался вместе с карточкой во время слайд-анимации (тот
   же transform, что и сама карточка); (2) когда стали отдельными кнопками
   внутри .rail-viewport — начали затухать под его градиентной маской при
   приближении к самому краю (нужному по следующей правке "сместить ближе
   к краю"). Теперь — снаружи .rail-viewport, дети .rail--sample напрямую
   (см. разметку в index.php) — не двигаются с карточками (не внутри
   .card-grid) и не затухают (не внутри замаскированного элемента).
   Размер — свой clamp(), НЕ через --card-w: та переменная лежит на
   .rail-viewport, а не на .rail (кнопки больше не его потомки — custom
   property наследуется только вниз по DOM, не в сторону/вверх). */
.rail--sample{ position: relative; }
.rail--sample .rail-side-nav{
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 40px;
  height: 40px;
  border-radius: 50%;
  border: none;
  background: color-mix(in srgb,var(--overlay-dark) 68%,transparent);
  box-shadow: 0 3px 8px color-mix(in srgb,var(--shadow-black) 35%,transparent);
  color: var(--text-on-accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  z-index: 6;
  opacity: .92;
}
.rail--sample .rail-side-nav--prev{ left: 1%; }
.rail--sample .rail-side-nav--next{ right: 1%; }

/* ── вкладки (.rail-tabs) — тот же принцип позиционирования (см. п.3
   запроса — "аналогично как и для карточек"), отдельный InfiniteCarousel
   с draggable:false, ведомый от карточек через onFrame()/setPos(). Ширина
   плашки — ОДНА и та же у всех (без этого offset*step в движке не
   работает — шаг должен быть одинаковым), но не угаданное число: JS
   (index.php::initSampleRail) меряет реальные названия образцов и берёт
   ширину самой длинной + запас — большинство названий не обрезаются
   вообще, многоточием режется только явный выброс длины. Сама ширина
   приходит через fixedWidth в InfiniteCarousel, --card-w ниже — то же
   имя переменной, что и у карточек (общий канал, которым движок сообщает
   посчитанную ширину элемента в CSS), но здесь означает "ширина вкладки",
   не "доля от --visible/--peek". */
.rail--sample .rail-tabs{
  --card-gap: 8px;
  position: relative;
  height: 30px;
  /* сброс старых scroll-based свойств из shared.css (.rail-tabs) — теперь
     позиционирование через transform (см. .mdf-card выше), не scrollLeft.
     Ограничено .rail--sample: Product (.rail--product, b3) по-прежнему
     работает по старой scrollLeft-схеме (initTabLoop, shared.css flex-
     layout) — без этого ограничения все плашки там схлопывались в одну
     точку (left:50%, position:absolute без своего --card-w). */
  overflow: hidden;
  padding: 0;
}
.rail--sample .rail-tabs .tag{
  position: absolute;
  top: 0; left: 50%;
  width: var(--card-w);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  text-align: center;
  cursor: pointer;
  /* transition:none — та же причина, что у .rail--sample .mdf-card выше:
     shared.css вешает на .rail-tabs .tag transition по transform (и
     transform:scale(1)/scale(1.12) на .tag--active) — без сброса это
     конфликтовало бы с ежекадровым inline-transform от setPos()/rAF. */
  transition: none;
}

/* ── ПОЛОСКА-ТАЙМЕР автопрокрутки (по прямому запросу) — тот же принцип,
   что у .hprogress/.hprog-fill на hero (homepage.css выше): сразу
   полностью залита, затем "стекает" до нуля к моменту смены карточки.
   Один сегмент (не по одному на карточку, как на hero) — это счётчик "до
   следующего шага", число карточек здесь не маленькое и заранее
   известное, как у слайдов hero. --rail-prog-dur — из
   site_settings.carousel_autoplay_speed_ms (index.php), совпадает с
   реальным JS-таймером автопрокрутки (initSampleRail()).
   Позиция (2026-08-19, уточнение) — поднята ближе к карточкам, по прямому
   запросу: .card-grid резервирует 34px пустого места СНИЗУ (нужны они
   самому .rail-viewport — там overflow:hidden, место под несрезанную тень
   .mdf-card:hover, см. комментарий у .card-grid выше), из-за чего между
   реальным нижним краем карточки и полоской набегал разрыв в 34+16=50px.
   Отрицательный margin-top компенсирует бо́льшую часть этого резерва
   (полоска и так вне .rail-viewport — в его overflow:hidden не попадает,
   просто визуально всплывает выше, под тень средней карточки). */
.rail-progress-bar{max-width:280px;margin:-20px auto 0}
.rail-progress-track{position:relative;display:block;height:3px;border-radius:2px;background:var(--line-2);overflow:hidden}
.rail-progress-fill{position:absolute;top:0;right:0;bottom:0;width:0;background:var(--accent)}
.rail-progress-bar.active .rail-progress-fill{animation:railProgressFill var(--rail-prog-dur,4000ms) linear forwards}
@keyframes railProgressFill{from{width:100%}to{width:0}}
@media(prefers-reduced-motion:reduce){
  .rail-progress-bar.active .rail-progress-fill{animation:none;width:65%}
}

/* ══════════════════════════════════════════════════════════════════
   PROJECTS GRID (b3, редизайн 2026-08-18/макет Home-v2-D) — обычная сетка
   читаемых карточек (.card, includes/cards/project/classic.php — тот же
   компонент, что каталог /projects/ рендерит через .proj-grid своим
   assets/project-collection.css, здесь свой набор колонок под главную:
   2 в ряд, а не 3, — крупнее, под акцент "витрины", как в референс-макете).
   .card сама умеет адаптив (shared.css) — раздельная desktop/mobile-
   разметка (masonry-плитка + список), которая тут была раньше, не нужна.
   ══════════════════════════════════════════════════════════════════ */
#sec-b3 .proj-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:28px}
@media(max-width:900px){
  /* ≤900px, включая мобильные — 1 колонка карточек с фото. Раньше на
     ≤560px карточки заменялись текстовым списком (.proj-list-mobile) —
     убран по прямому запросу (2026-08-19), настоящие карточки нужны и на
     мобильном тоже. */
  #sec-b3 .proj-grid{grid-template-columns:1fr}
}

/* PROJECT CARDS REVEAL (2026-08-24, project-cards-reveal-animation.md, по
   прямому запросу — "только на главной", как в задании) — ТОЛЬКО #sec-b3,
   не .proj-grid вообще (тот же класс переиспользуют /projects/ и detail-page
   "X in this Y" блоки — их не трогаем).

   Reveal: направленный слайд+блюр вместо обычного fade+translateY
   .reveal-item (тот механизм — общий IntersectionObserver initRevealOnScroll()
   в index.php, здесь просто своя анимация для тех же .is-visible/не-visible
   состояний за счёт более специфичного селектора). Левая колонка (чётные,
   proj-reveal--left) прилетает слева, правая (нечётные) — справа.

   Hover-приглушение подписи (card-photo-bottom{opacity:.2} до наведения) —
   убрано по прямому запросу (2026-08-26): подпись остаётся видимой всегда,
   как на остальных карточках сайта, без затемнения/анимации при наведении/
   уходе курсора. */
#sec-b3 .proj-grid .reveal-item{opacity:0;transform:translateX(calc(-1 * var(--proj-reveal-move-x)));filter:blur(var(--proj-reveal-blur));transition:opacity var(--proj-reveal-duration) ease,transform var(--proj-reveal-duration) ease,filter var(--proj-reveal-duration) ease}
#sec-b3 .proj-grid .reveal-item.proj-reveal--right{transform:translateX(var(--proj-reveal-move-x))}
#sec-b3 .proj-grid .reveal-item.is-visible{opacity:1;transform:translateX(0);filter:blur(0)}
@media(prefers-reduced-motion:reduce){
  #sec-b3 .proj-grid .reveal-item{opacity:1;transform:none;filter:none;transition:none}
}

/* ── Плавающая кнопка чата — скрыта, пока не показался следующий раздел
   после hero (2026-08-24, по прямому запросу). .fab-stack — общий
   компонент (footer.php/shared.css, все страницы) — здесь переопределяется
   ТОЛЬКО на главной (body:has(#hero), см. initFabVisibility() в index.php,
   следит за .usp-strip через IntersectionObserver). На остальных страницах
   кнопка остаётся видимой всегда, как раньше. ── */
body:has(#hero) .fab-stack{opacity:0;pointer-events:none;transition:opacity .3s ease}
body:has(#hero) .fab-stack.fab-visible{opacity:1;pointer-events:auto}
@media(prefers-reduced-motion:reduce){
  body:has(#hero) .fab-stack{opacity:1;pointer-events:auto;transition:none}
}
