/* INV-010: шрифт только локально из fonts/, без внешних CDN (T-PD-001).
   Лицензия — fonts/Manrope-OFL.txt (SIL Open Font License 1.1).

   На сайте одна гарнитура — один вариативный файл с начертаниями от 200 до 800. Второй
   не нужен: заголовок отличается от текста начертанием, кеглем и регистром. Так нечему
   разъезжаться и нечему не догрузиться.

   До этого заголовки были объявлены Bebas Neue, в котором 461 глиф и ни одной русской
   буквы: кириллицу рисовал запасной системный шрифт, а цифры — сам Bebas, и в одном слове
   сходились две гарнитуры. Вид страницы при этом зависел от того, что нашлось у посетителя
   в системе. Замена на одну гарнитуру с полной кириллицей закрывает и это. */
@font-face{
  font-family:'Manrope';
  src:url('../fonts/Manrope-Variable.woff2') format('woff2-variations'),url('../fonts/Manrope-Variable.woff2') format('woff2');
  font-weight:200 var(--weight-heavy);
  font-style:normal;
  font-display:swap;
}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
/* TOK-line.hair на экранах с дробным масштабом (T-0006: перенесено из canon.css —
   тот теперь только собранные токены, руками не правится).
 *
 * Волосяная линия в один пиксель ложится на дробную сетку физических пикселей
 * и размазывается между двумя: часть разделителей выходит тусклее остальных.
 * Замер 04.09.2026 на таблице цен: при плотности 1,25 яркость линий 29,0 и
 * 36,7 — разница в четверть, видна глазом. Ни способ отрисовки (граница,
 * фон, тень), ни отдельный слой этого не лечат: дело в положении границы
 * строки, а не в том, чем её рисуют.
 *
 * Два пикселя на дробной плотности выравнивают: разброс падает до 0,8 —
 * это уже незаметно. Размен назван вслух: на масштабированных экранах линия
 * чуть толще задуманной волосяной, зато одинаковая. Неровные линии дороже:
 * они читаются как небрежность во всей таблице.
 *
 * Целые плотности (1 и 2) правило не трогает — там линия и так ровная.
 */
@media (min-resolution: 1.01dppx) and (max-resolution: 1.99dppx) {
  :root { --line-hair: 2px; }
}
/* Гарнитура одна,
поэтому вся разница между заголовком и текстом лежит здесь: заголовок
   берёт самое жирное начертание и верхний регистр (TOK-weight.heavy). Правило общее,
иначе
   заголовки-теги взяли бы жирное по умолчанию браузера,
а те же заголовки в div — обычное,
и одинаковые по смыслу надписи разошлись бы по насыщенности. */
.nav-logo,
.footer-logo,
.hero-title,
.stat-item,
.truck-header-title,
.truck-info-val,
.journey-step-title,
.features-title,
.feature-name,
.products-title,
.pricing-title,
.pricing-row td:last-child,
.contact-title,
.legal-title,
.showcase-category,
.showcase-option-name{font-weight:var(--weight-heavy)}
/* Требование ко всем экранам (ui-ux.md, правка 2026-08-02): «системных синих ссылок на странице
   нет» — общего правила цвета для `a` не было вовсе, поэтому браузер подставлял дефолтный синий
   везде, где автор CSS не задал цвет явно (напр. телефон/почта в .contact-row-val, ссылка внутри
   .pricing-result, которую рендерит js/pricing.js). Цвет по умолчанию наследуется от окружающего
   текста; конкретные ссылки-акценты (CTA, чекбокс согласия, подвал) переопределяют его ниже
   отдельными правилами — decoration здесь не трогаем, чтобы не потерять подчёркивание там, где
   это единственный неpalette-признак кликабельности (WCAG 1.4.1). */
a{color:inherit}
/* Фон объявлен и на html: при оттягивании страницы Safari красит область над ней фоном
   корневого элемента, а не body — иначе там видна чужая светлая полоса. */
html{scroll-behavior:auto;max-width:100%;background:var(--color-coal);color-scheme:dark}
/* Высота закреплённой шапки, собранная из тех же токенов, что и она сама: два отступа плюс
   строка логотипа. Нужна всем, кто прижат к верху окна, — иначе содержимое уезжает под неё. */
:root{--nav-height:calc(var(--space-md) * 2 + var(--text-subheading) * 1.2)}

/* Фон страницы — угольная сцена, а не плоский чёрный: тёплый отсвет вверху,
   тлеющий низ, слабая зернистость. Один на весь документ, поэтому между
   разделами нет ни швов, ни перепадов. */
body::before{content:"";position:fixed;inset:0;z-index:-2;pointer-events:none;
  background:
    radial-gradient(ellipse 120% 80% at 50% -10%,var(--color-coal-warm),transparent 60%),
    radial-gradient(ellipse 90% 60% at 50% 110%,var(--color-ember-dim),transparent 65%),
    linear-gradient(to bottom,var(--color-coal) 0%,var(--color-coal-deep) 45%,var(--color-coal) 100%)}
body::after{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;opacity:.5;
  background-image:radial-gradient(var(--color-hairline) 1px,transparent 1px);
  background-size:var(--space-sm) var(--space-sm)}

body{background:var(--color-coal);color:var(--color-text);font-family:var(--font-ui);overflow-x:hidden;max-width:100vw}
body::selection{background:var(--color-ember);color:var(--color-coal)}
/* T-A11Y-001: видимый фокус на всех интерактивных элементах, без исключения.
   ui-ux.md (правка 2026-08-02): у ПОЛЕЙ ВВОДА (text/tel/number/textarea — класс .form-input)
   обводки-outline нет — активное поле подсвечивается границей поля в цвет жара
   (.form-input:focus-visible ниже), потому что браузер трактует любой фокус текстового поля,
   включая клик мышью, как "focus-visible" (текстовый ввод — эвристика "always visible"), и
   раньше оранжевое кольцо появлялось даже от простого клика. У ссылок, кнопок и чекбокса —
   обводка остаётся: это не текстовый ввод, там нативная эвристика клавиатуры/мыши работает как
   ожидается, и видимый фокус для клавиатуры (T-A11Y-001) обязателен. */
a:focus-visible,button:focus-visible,[tabindex]:focus-visible,input[type=checkbox]:focus-visible{outline:var(--line-thick) solid var(--color-ember-light);outline-offset:3px;border-radius:var(--line-thick)}
.skip-link{position:absolute;left:var(--space-sm);top:-60px;z-index:2000;background:var(--color-ember);color:var(--color-coal);padding:var(--space-xs) var(--space-md);font-weight:var(--weight-bold);font-size:var(--text-label);text-decoration:none;transition:top .2s}
.skip-link:focus{top:var(--space-sm)}
/* T-01: имя, слышимое вспомогательными технологиями (заголовок таблицы, дефолтный текст
   результата расчёта), но не видимое на экране — визуальный вид страницы не меняется. */
.visually-hidden{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
/* prefers-reduced-motion: гасит декоративные CSS-анимации (тикер, частицы, дым, пульс), не
   трогает саму раскладку — работает вместе с JS-веткой облегчённого режима (L-011, EC-007). */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important;scroll-behavior:auto!important}
}
/* T-06: шапка держит ту же горизонтальную вертикаль, что и содержимое разделов — общий
   var(--grid-gutter), а не собственный clamp() с другими шагами. У двух формул разный
   потолок и разный вклад vw, и на широком экране они расходятся — левый край логотипа
   не совпадал с левым краем заголовков ниже. */
nav{position:fixed;top:0;left:0;right:0;z-index:1000;display:flex;justify-content:space-between;align-items:center;padding:var(--space-md) var(--grid-gutter);transition:background .35s,backdrop-filter .35s,border-color .35s;border-bottom:var(--line-hair) solid transparent;padding-inline:max(var(--grid-gutter),calc((100% - var(--grid-max)) / 2))}
nav.scrolled{background:var(--color-backdrop-soft);backdrop-filter:blur(var(--blur-md));border-color:var(--color-hairline)}
/* Знак набран своей разрядкой, не общей TOK-track.wide: та держит ещё тринадцать
   правил — меню, метки, кнопки, шапку таблицы цен, — и сузить её ради знака значило
   бы сдвинуть типографику всей страницы. У COCOCHAR восемь букв против пяти у
   прежнего EMBER, и на той же разрядке слово расползалось. */
.nav-logo,.footer-logo{font-family:var(--font-display);font-size:var(--text-subheading);letter-spacing:var(--track-logo);color:var(--color-text);text-decoration:none}
.nav-logo span,.footer-logo span{color:var(--color-ember)}
.nav-links{display:flex;gap:var(--space-xl);list-style:none}
/* Единый ритм секций (решение 2026-08-02): .products-tag/.pricing-tag добавлены в общий список
   надстрочных меток — раньше .pricing-tag не входил сюда и рендерился обычным абзацем без
   капса/разрядки,
из-за чего раздел цен визуально выбивался из ритма остальных секций. */
.nav-links a,
.hero-tag,
.truck-header-tag,
.journey-step-num,
.features-tag,
.products-tag,
.pricing-tag,
.contact-tag,
.feature-num,
.spec-label,
.contact-row-label,
.form-label,
.footer-copy{font-family:var(--font-ui);font-weight:var(--weight-bold);font-size:var(--text-label);letter-spacing:var(--track-wide);color:var(--color-muted);text-decoration:none;text-transform:uppercase;align-items:flex-start;justify-content:flex-start;text-align:start}
.nav-links a{transition:color .25s}
.nav-links a:hover{color:var(--color-ember-light)}
/* T-A11Y-001: переключатель мобильного меню — скрыт на десктопе, показан только на узких экранах
   (TOK-break.lg, медиа ниже). Наименьшая область нажатия — TOK-hit.min, фокус — общим правилом
   focus-visible. */
.nav-toggle{display:none;align-items:center;justify-content:center;width:var(--space-2xl);height:var(--space-2xl);padding:0;border:var(--line-hair) solid var(--color-line);border-radius:var(--radius-sm);background:transparent;color:var(--color-text);cursor:pointer;transition:border-color .2s,background .2s}
.nav-toggle:hover{border-color:var(--color-ember-light)}
.nav-toggle svg{width:var(--space-md);height:var(--space-md);stroke:currentColor;fill:none;stroke-width:1.6}
.hero{min-height:100svh;display:flex;flex-direction:column;justify-content:center;align-items:flex-start;padding:var(--space-section) var(--grid-gutter) var(--space-section);position:relative;overflow:hidden}
.hero-bg{position:absolute;inset:0;background:radial-gradient(ellipse at 74% 42%,var(--color-ember-glow),transparent 56%),radial-gradient(ellipse at 22% 78%,var(--color-gold-glow),transparent 52%),linear-gradient(120deg,var(--color-coal-deep),var(--color-coal-warm) 58%,var(--color-coal))}
.hero-grid{position:absolute;inset:0;background-image:linear-gradient(var(--color-hairline-soft) 1px,transparent 1px),linear-gradient(90deg,var(--color-hairline-soft) 1px,transparent 1px);background-size:80px 80px;mask-image:linear-gradient(to bottom,var(--color-void) 0%,transparent 95%)}
/* Ширина сцены ограничена: на широком экране половина окна — это почти тысяча точек,
   сцена раздувалась, а текстовой колонке не оставалось места под самое длинное слово
   заголовка, и оно уходило под уголь. */
#coalHeroCanvas{position:absolute;right:1%;top:8%;width:min(50%,46rem);height:82%;pointer-events:none;display:block;filter:drop-shadow(0 0 64px var(--color-ember-glow));z-index:1}
/* T-06: предел ширины содержимого — из канона (var(--grid-max)), не собственное значение
   58vw. На текущий текст это не меняет рендер (hero-title/.hero-desc уже уже сами по себе
   9ch/var(--grid-measure)), но ширина содержимого перестаёт быть уникальной для одного
   раздела. */
.hero-content{position:relative;z-index:3;max-width:var(--grid-max);margin-inline:auto;width:100%}
.hero-tag{color:var(--color-ember-light);margin-bottom:var(--space-md);opacity:0;transform:translateY(18px)}
.hero-title{font-family:var(--font-display);font-size:var(--text-display);line-height:var(--leading-display);letter-spacing:var(--track-tight);margin-bottom:var(--space-lg);max-width:11ch}
.hero-title .line{display:block;transform:translateY(100%);opacity:0}
.hero-title .line-ember{color:var(--color-ember)}
.hero-desc{font-size:var(--text-lead);font-weight:var(--weight-medium);color:var(--color-text);line-height:var(--leading-normal);max-width:var(--grid-measure);opacity:0;transform:translateY(18px)}
/* UI-hero.cta (US-001): единственное действие первого экрана. Оранжевый — «дорогой цвет»
   (DESIGN.md), тот же контур-кнопка, что и у остальных CTA сайта — без градиента и тени. */
.hero-cta{margin-top:var(--space-sm);opacity:0;transform:translateY(18px)}
.hero-scroll-hint{position:absolute;bottom:var(--space-xl);left:max(var(--grid-gutter),calc((100vw - var(--grid-max)) / 2));display:flex;align-items:center;gap:var(--space-sm);color:var(--color-muted);opacity:0;z-index:4}
.hero-scroll-hint span{font-family:var(--font-ui);font-size:var(--text-label);font-weight:var(--weight-bold);letter-spacing:var(--track-wide);text-transform:uppercase}
.scroll-line{width:var(--space-3xl);height:var(--space-2xs);background:var(--color-ember);animation:pulse-line 2s ease-in-out infinite}
@keyframes pulse-line{0%,100%{opacity:.32}50%{opacity:1}}
.stats-strip{background:var(--color-ember);padding:var(--space-md) 0;overflow:hidden;position:relative;z-index:3}
.stats-ticker{display:flex;gap:var(--space-3xl);white-space:nowrap;animation:ticker 28s linear infinite;width:max-content}
.stat-item{font-family:var(--font-display);font-size:var(--text-lead);letter-spacing:var(--track-wide);color:var(--color-coal);display:flex;align-items:center;gap:var(--space-md)}
.stat-dot{width:var(--space-2xs);height:var(--space-2xs);background:var(--color-coal);border-radius:var(--radius-full);opacity:.55}
@keyframes ticker{to{transform:translateX(-50%)}}
/* Ширина грузовика и вычисленная из неё высота — один источник на весь раздел: по высоте
   отводится место под нижнюю полосу кадра, чтобы цифры по центру никогда не наехали на
   кузов. 0.2075 — доля высоты к ширине у снимка (assets/truck-delivery-*.webp, 1152×239);
   она же объявлена в aspect-ratio у .truck-img, чтобы место было отведено до загрузки. */
.truck-section{height:220vh;position:relative;--truck-width:min(var(--grid-max),86vw);--truck-height:calc(var(--truck-width) * 0.2075)}
/* CC-EMBER-002: .products-sticky убран из этого правила — каталог больше не закреплённая
   на весь экран секция (см. .products-section ниже), только доставка и путь товара остаются
   повествованием с фиксацией по прокрутке. */
.truck-sticky,.journey-sticky{position:sticky;top:0;height:100svh;overflow:hidden;padding-inline:max(var(--grid-gutter),calc((100% - var(--grid-max)) / 2))}
/* Три яруса кадра сверху вниз: заголовок, условия отгрузки, дорога с грузовиком. Дорога и
   грузовик выведены из потока (они едут во всю ширину окна), поэтому под них отведён нижний
   отступ, а не строка сетки. Средний ярус — 1fr: условия стоят по центру того, что осталось,
   и остаются по центру на любой высоте окна, без отдельных правил под каждый размер. */
.truck-sticky{display:grid;grid-template-rows:auto 1fr;justify-items:start;align-content:stretch;padding-block:var(--space-section) calc(var(--truck-height) + var(--space-3xl));padding-inline:max(var(--grid-gutter),calc((100% - var(--grid-max)) / 2))}
/* T-06: было ...var(--color-coal-warm) 100% — нижний край раздела уходил в тёплый тон,
   а следующий раздел (.journey-sticky) начинается с var(--color-coal). Стык читался
   отдельной полосой. Возврат к var(--color-coal) на обоих краях делает соседние разделы
   одним фоном — тёплый тон остаётся только серединной ступенью. */
.truck-bg{position:absolute;inset:0;background:transparent;transition:filter .25s}
.road{position:absolute;left:0;right:0;bottom:0;height:220px;z-index:3;background:linear-gradient(to top,var(--color-coal-warm) 0%,var(--color-panel-backdrop) 45%,transparent)}
.road-surface{position:absolute;bottom:0;left:0;right:0;height:var(--space-5xl);background:linear-gradient(to bottom,var(--color-coal-edge),var(--color-coal-warm) 70%,var(--color-coal-deep));box-shadow:var(--shadow-overlay) inset}
.road-surface::before{content:'';position:absolute;inset:0;background:repeating-linear-gradient(90deg,var(--color-hairline-soft) 0,var(--color-hairline-soft) 1px,transparent 1px,transparent 128px);opacity:.22}
.road-line{position:absolute;bottom:var(--space-3xl);left:0;right:0;height:var(--space-2xs);background:repeating-linear-gradient(90deg,var(--color-gold-light) 0,var(--color-gold-light) 78px,transparent 78px,transparent 178px);opacity:.48}
.road-edge{display:none}
/* ui-ux.md «Требования ко всем экранам»: заголовки разделов — по левому краю, центрированных
   нет. truck-header был центрирован через text-align + align-items:center родителя (.truck-sticky
   — display:flex) — align-self:flex-start вынимает блок из центрирования по кросс-оси, ширина и
   паддинг равны отступам остальных секций (тот же clamp, что у .hero/.features-sticky/.contact-sticky). */
/* T-06: max-width добавлен — .truck-wrap ниже уже ограничен var(--grid-max) (min(...,88vw)),
   а сам заголовок ничем не ограничивался и на широком экране мог тянуться шире предела,
   принятого во всех остальных разделах. У остальных разделов паддинг стоит на родителе,
   а max-width — на самом заголовке (предел = содержимому). Здесь паддинг и max-width на
   одном элементе с box-sizing:border-box — без поправки предел считал бы паддинг частью
   ширины и содержимое оказалось бы уже, чем в остальных разделах. calc() возвращает предел
   именно к содержимому, оставаясь выражением из тех же двух токенов. */
/* Заголовок вернулся в поток: отступ слева и сверху задаёт сетка .truck-sticky, а не
   собственные координаты. Так метка, заголовок и описание идут одним ритмом и стоят на той
   же вертикали, что содержимое остальных разделов. */
.truck-header{position:relative;z-index:8;padding-top:var(--space-xl);max-width:var(--grid-measure)}
/* Устройство раздела то же, что у остальных (ui-ux.md, «Все разделы устроены одинаково»):
   метка цветом жара с тем же отступом, что у .features-tag/.products-tag/.pricing-tag, и
   заголовок ровно теми же ступенями, что .features-title. Раньше метка была единственной
   на странице серой, а у заголовка стояло своё межбуквенное расстояние — раздел выбивался. */
/* Отступ от надстрочной метки до заголовка один во всех разделах. Было три величины —
   lg у первого экрана, md у доставки и преимуществ, sm у каталога и цены, — и заголовки
   стояли на разном расстоянии от своей метки. */
.truck-header-tag{color:var(--color-ember-light);margin-bottom:var(--space-md)}
.truck-header-title{font-family:var(--font-display);font-size:var(--text-title);line-height:var(--leading-display);color:var(--color-text)}
/* Низ грузовика стоит на верхней кромке дорожного полотна: .road-surface — та же высота,
   var(--space-5xl) (было var(--space-2xl) — грузовик «висел» над дорогой отдельным зазором). */
.truck-wrap{position:absolute;bottom:var(--space-3xl);left:0;width:var(--truck-width);will-change:transform;z-index:4}
.truck-img{display:block;width:100%;height:auto;aspect-ratio:1152 / 239;filter:drop-shadow(0 16px 42px var(--color-scrim)) drop-shadow(0 0 24px var(--color-ember-glow))}
/* UI-delivery.terms. Проявление ведёт сценарий: он выставляет --появление от нуля до единицы
   по ходу проезда. Значение по умолчанию — единица: без сценария условия просто стоят на
   месте, а не исчезают. Тем же числом гасится и уход, когда грузовик покидает кадр. */
.truck-info{grid-row:2;justify-self:center;align-self:center;display:flex;gap:clamp(var(--space-3xl),8vw,var(--space-5xl));z-index:8}
.truck-info-stat{display:flex;flex-direction:column;align-items:center;text-align:center;min-width:0;opacity:var(--появление,1);transform:translateY(calc((1 - var(--появление,1)) * var(--space-lg)));will-change:opacity,transform}
/* Ступенью ниже заголовка раздела: условия отгрузки — подпись к нему, а не второй заголовок.
   На одной с ним ступени они спорили за внимание и не оставляли воздуха до грузовика.
   Свечения нет: ни один другой заголовок на странице не светится, и цифры выбивались. */
.truck-info-val{font-family:var(--font-display);font-size:var(--text-subtitle);line-height:var(--leading-display);color:var(--color-text);font-variant-numeric:var(--numeric-tabular);text-transform:uppercase;white-space:nowrap}
/* Черта прочерчивается от середины к краям по мере проезда — это и есть «рисуется». */
.truck-info-rule{width:100%;height:var(--line-thick);margin-top:var(--space-md);background:linear-gradient(90deg,transparent,var(--color-ember),transparent);box-shadow:var(--shadow-glow);transform:scaleX(var(--появление,1));transform-origin:center}
/* Ступень TOK-track.tight принадлежит самым крупным заголовкам: канон говорит «чем крупнее
   знак, тем теснее он стоит». Без этого правила ступень была объявлена и не использована
   нигде — мёртвое значение в каноне. */
.truck-header-title,.features-title,.products-title,.pricing-title,.contact-title,
.journey-step-title,.legal-title,.truck-info-val{letter-spacing:var(--track-tight)}
.truck-info-label{font-family:var(--font-ui);font-size:var(--text-label);font-weight:var(--weight-medium);letter-spacing:var(--track-wide);color:var(--color-muted);text-transform:uppercase;margin-top:var(--space-sm)}
/* UI-journey.progress: было 4 шага (460vh, 115vh/шаг), после удаления «обжига» и «ISO» (INV-014)
   осталось 2 (step0 — происхождение, step1 — доставка) — высота секции пересчитана на тот же
   темп прокрутки за шаг. */
.journey-section{height:230vh;position:relative;background:transparent}
.journey-sticky{display:flex;align-items:center;justify-content:center;background:linear-gradient(120deg,var(--color-coal),var(--color-coal-deep) 58%,var(--color-coal))}
.journey-bg{position:absolute;inset:0;background:transparent;transition:opacity .3s}
/* Место отведено заранее и квадратное: сцена рисуется в квадрат (fitRenderer(...,square)),
   и без объявленного соотношения содержимое прыгало бы в момент первого кадра. Предел по
   высоте окна — чтобы на низком экране сцена не вылезала за кадр. */
.coal-3d-wrap{position:absolute;right:8%;top:50%;transform:translateY(-50%);width:min(40rem,44vw,66svh);aspect-ratio:1;filter:drop-shadow(0 0 58px var(--color-ember-glow));z-index:2}
#coalJourneyCanvas{width:100%!important;height:auto!important;display:block}
.journey-texts{position:absolute;inset:0;pointer-events:none;z-index:4}
/* T-06: left был на собственном clamp(...,5vw,...) — на 1440 давал иной отступ, чем
   var(--grid-gutter) у остальных разделов (у той формулы другой потолок и вклад vw).
   Заголовок шага «пути товара» стоял не на общей левой оси сайта. */
.journey-step{position:absolute;left:max(var(--grid-gutter),calc((100% - var(--grid-max)) / 2));top:50%;transform:translateY(-46%) translateX(-34px);max-width:var(--grid-measure);opacity:0;visibility:hidden;transition:opacity .28s ease,transform .32s cubic-bezier(.23,1,.32,1)}
.journey-step.active{opacity:1;visibility:visible;transform:translateY(-50%) translateX(0)}
.journey-step-num{color:var(--color-ember-light);margin-bottom:var(--space-md)}
/* Верхний регистр как у всех заголовков: значение срока приходит строкой «1–3 дня», и без
   этого правила в одном заголовке сходились прописные и строчные. */
.journey-step-title{font-family:var(--font-display);font-size:var(--text-title);line-height:var(--leading-display);text-transform:uppercase;margin-bottom:var(--space-md)}
/* Три роли текста на весь сайт, и больше никаких.
   1. Вводный абзац раздела — крупнее обычного, светлый: .hero-desc, .pricing-lead.
   2. Абзац — обычный размер, светлый: описания шагов, позиций, контактов.
   3. Подпись — мельче, приглушённая: пояснения к полям, описания карточек, оговорки.
   Роль определяет и размер, и цвет разом. Раньше .feature-desc был объявлен дважды с
   противоположными значениями, а вводные абзацы разных разделов набирались один
   светлым, другой приглушённым — при одинаковом назначении. */
.journey-step-desc,.contact-info p,.showcase-desc{font-size:var(--text-body);font-weight:var(--weight-medium);color:var(--color-text);line-height:var(--leading-normal)}
.journey-progress{position:absolute;right:var(--grid-gutter);top:50%;transform:translateY(-50%);display:flex;flex-direction:column;gap:var(--space-md);z-index:5}
.progress-dot{width:var(--space-xs);height:var(--space-xs);border:var(--line-hair) solid var(--color-hairline);border-radius:var(--radius-full);transition:all .25s}
.progress-dot.active{background:var(--color-ember);border-color:var(--color-ember);box-shadow:var(--shadow-glow)}/* нет токена: канон не содержит прозрачного варианта --color-ember для свечения (glow) */
/* T-06: было linear-gradient(coal,coal-deep) — нижний край уходил в coal-deep, а
   .products-section ниже начинается с coal, стык был виден отдельной полосой. Coal-deep
   остаётся серединной ступенью, оба края — coal, как и у соседей. */
/* Раздел останавливается под шапкой, пока следующий наезжает на него — это и есть ритм
   повествования, тот же, что у доставки и пути. Запас прокрутки короткий: раньше стояло
   2,2 экрана при содержимом в один, и полтора экрана уходили в пустоту. Треть экрана —
   ровно столько, чтобы остановка читалась как остановка, а не как провал. */
/* Одинаковая задержка на каждом разделе: попав на него, читатель прокручивает ещё две трети
   экрана, прежде чем следующий наедет сверху. Ритм один и тот же на телефоне, планшете
   и ноутбуке — прежде на узких экранах закрепление отключалось совсем, и разделы
   пролетали без остановки. У доставки и пути запас больше: там по прокрутке едет
   грузовик и сменяются шаги. */
.features{height:165svh;min-height:165svh;position:relative;z-index:4}
/* Содержимое начинается от одной верхней линии во всех разделах. При justify-content:center
   отступ сверху зависел от высоты содержимого, и надстрочная метка каждого раздела
   оказывалась на своей высоте: 110 у преимуществ, 144 у каталога, 182 у цены, 119 у
   контактов. Устройство разделов одинаковое (ui-ux.md), значит и линия одна. */
/* Устройство закреплённого раздела: заголовок стоит на общей верхней линии, а содержимое
   стоит по центру того, что осталось. Так на невысоком ноутбуке ничего не наезжает, а на
   планшете содержимое не прижимается к шапке, оставляя внизу пустую половину экрана.
   Одна раскладка на любую высоту окна — без отдельного правила под каждый размер. */
/* Заголовок стоит на общей для всех разделов верхней линии, содержимое — по центру того,
   что осталось. Так на невысоком экране ноутбука линия заголовков одна во всех разделах
   (замер при центровке группы давал 110, 170, 131, 240, 147 — разброс в сто тридцать
   точек), а низ раздела не пустует. На высоком экране это правило меняется — см. блок
   TOK-break.tall в конце файла. */
/* прежнее пояснение: заголовок и содержимое стояли одной группой по центру. Не «заголовок сверху,
   содержимое в остатке»: на высоком экране планшета между ними разверзалась дыра.
   Не «всё сверху»: тогда внизу оставалась пустая половина. Отступ по краям —
   нижняя граница, чтобы на невысоком ноутбуке группа не упиралась в шапку. */
/* Закреплённый раздел непрозрачен: пока он висит под шапкой, следующий наезжает на него
   сверху, и сквозь прозрачный фон читались оба разом — на телефоне заголовок «НАШ
   АССОРТИМЕНТ» лёг прямо поверх карточки предыдущего раздела. Тон и крапчатая сетка те же,
   что у страницы (body::before/::after), поэтому стык разделов не виден. */
.truck-section,.journey-section,.features,.products-section,.pricing-section,.contact{
  /* Ровный тон и та же крапчатая сетка, что у страницы. Сетка задана фоном самого раздела,
     а не слоем поверх: фон рисуется под содержимым сам собой, и трогать положение детей —
     как в прошлой попытке, где абсолютные слои уезжали в поток, — не требуется.

     background-attachment:fixed привязывает крап к окну, а не к разделу: точки не начинают
     отсчёт заново на каждом стыке, и фон читается как один сплошной. Собственного градиента
     у разделов нет — с ним у каждого выходил свой светлый верх и тёмный низ, а на стыках
     проступали полосы.

     Фон на самом разделе, а не на закреплённом блоке внутри него: блок высотой в экран, а
     раздел выше — под блоком открывалась страница, и на переходе была видна ступенька тона. */
  background-color:var(--color-coal);
  background-image:radial-gradient(var(--color-hairline-soft) 1px,transparent 1px);
  background-size:var(--space-sm) var(--space-sm);
  background-attachment:fixed;
}
/* Крапчатой сетки внутри разделов нет намеренно: чтобы положить её слоем, пришлось бы
   поднимать содержимое над ней, а такое правило переводило в поток абсолютные слои —
   дорогу, грузовик, задники — и раскладка рассыпалась. Крап живёт на странице целиком
   (body::after) и виден там, где разделы прозрачны. */
/* Заголовок и содержимое стоят одной группой по центру экрана. Раньше заголовок держался
   на верхней линии, а содержимое центровалось в остатке — на высоком экране планшета
   между ними разверзалась пустота, и раздел читался как две несвязанные части. */
/* прежнее пояснение: заголовок стоял на общей верхней линии, а содержимое растягивается
   на весь остаток высоты. Не центрировать группу целиком: тогда верхняя линия у каждого
   раздела своя (замер давал 146, 97, 218, 143 — разброс в сотню точек). Не оставлять
   содержимое в своей высоте: на планшете внизу зияла пустая половина экрана. */
.features-sticky{position:sticky;top:0;min-height:100svh;padding:var(--space-section) var(--grid-gutter);display:grid;grid-template-rows:auto 1fr;gap:var(--space-xl)}
/* T-06: margin-inline:auto здесь центрировало заголовок ВНУТРИ уже отступленного паддингом
   .features-sticky блока — паддинг родителя и центрирование складывались, сдвигая левый
   край заметно дальше, чем в разделах без такого центрирования. Единая левая ось у всех
   разделов — max-width ограничивает рост вправо, без центрирования блока внутри себя. */
.features-header{padding-top:var(--space-xl);margin-bottom:0;max-width:var(--grid-max);margin-inline:auto;width:100%}
.features-tag{color:var(--color-ember-light);margin-bottom:var(--space-md)}
.features-title{font-family:var(--font-display);font-size:var(--text-title);line-height:var(--leading-display);max-width:var(--grid-measure);max-width:var(--grid-max);margin-inline:auto;width:100%}
/* Строки объявлены здесь, а карточки берут их через subgrid: тогда значок, номер, заголовок
   и описание стоят на общих линиях во всех шести карточках, и высоты равны сами собой —
   без назначенной высоты заголовку. Прежде заголовку жёстко отводились две строки, и у
   однострочного названия под ним зияла пустая строка. */
.features-grid{align-self:center;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));grid-template-rows:repeat(8,auto);gap:var(--space-2xs);background:var(--color-hairline);max-width:var(--grid-max);margin-inline:auto;width:100%}
.feature-card{display:grid;grid-row:span 4;grid-template-rows:subgrid;align-content:start;gap:0;background:var(--color-panel);padding:var(--space-lg);position:relative;overflow:hidden;opacity:0;transform:translateY(32px);transition:filter .25s}
.feature-card::before{content:'';position:absolute;top:0;left:0;width:100%;height:var(--space-2xs);background:var(--color-ember);transform:scaleX(0);transform-origin:left;transition:transform .4s}
.feature-card:hover{filter:brightness(1.22)}
.feature-card:hover::before{transform:scaleX(1)}
.feature-icon{display:block;width:var(--space-xl);height:var(--space-xl);margin-bottom:var(--space-sm);color:var(--color-ember-light)}
.feature-icon svg{width:100%;height:100%;fill:none;stroke:currentColor;stroke-width:1.4}
.feature-num{color:var(--color-ember-light);margin-bottom:var(--space-xs)}
/* Длинный текст владельца (INV-007 — предел 400 знаков) обрезается по строкам: заголовок
   двумя, описание тремя. Высоту при этом никто не назначает — общие линии держит subgrid. */
.feature-name{font-family:var(--font-display);font-size:var(--text-subheading);line-height:var(--leading-tight);letter-spacing:var(--track-normal);text-transform:uppercase;margin-bottom:var(--space-sm);color:var(--color-text);display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:2;overflow:hidden}
.feature-desc{font-size:var(--text-small);color:var(--color-muted);line-height:var(--leading-snug);display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden}
/* UI-catalog (правка 2026-08-02): раздел приведён к тому же ритму, что «Почему выбирают нас» —
   закрепление при прокрутке (.products-sticky, тот же паттерн, что .features-sticky), те же
   отступы, появился заголовок раздела. Карточки компактные и целиком помещаются в высоту экрана.
   T-06: упомянутая здесь ранее центровка сетки (US-011, наследие удалённого .products-grid) к
   текущей витрине (.showcase) не относится — она больше не центрируется отдельно от заголовка,
   стоит на общей левой оси раздела (см. .showcase ниже). */
.products-section::before{content:"";position:absolute;left:0;right:0;top:calc(var(--space-5xl) * -3);height:calc(var(--space-5xl) * 6);pointer-events:none;z-index:0;background:radial-gradient(ellipse 90% 50% at 50% 50%,var(--color-ember-dim),transparent 70%)}
.products-section{height:165svh;min-height:165svh;position:relative;z-index:5}
.products-section--empty{height:auto;min-height:100svh}
.products-sticky{position:sticky;top:0;min-height:100svh;padding:var(--space-section) var(--grid-gutter);display:grid;grid-template-rows:auto 1fr;gap:var(--space-xl);overflow:hidden}
.products-section--empty .products-sticky{align-items:center}
/* T-06: та же правка, что у .features-header — margin-inline:auto удвоило отступ поверх
   паддинга .products-sticky, заголовок стоял не на общей левой оси с остальными разделами. */
.products-header{padding-top:var(--space-xl);margin-bottom:0;max-width:var(--grid-max);margin-inline:auto;width:100%}
.products-tag{color:var(--color-ember-light);margin-bottom:var(--space-md)}
.products-title{font-family:var(--font-display);font-size:var(--text-title);line-height:var(--leading-display);max-width:var(--grid-measure);max-width:var(--grid-max);margin-inline:auto;width:100%}
.catalog-empty{max-width:var(--grid-panel);text-align:left;display:flex;flex-direction:column;align-items:flex-start;gap:var(--space-lg)}
.catalog-empty p{font-size:var(--text-lead);line-height:var(--leading-normal);color:var(--color-text)}
/* auto-fit (не auto-fill): неиспользуемые треки схлопываются, карточки держат свой компактный
   размер (minmax) и не растягиваются на всю ширину секции. justify-content:center — ui-ux.md,
   раздел catalog: «при малом числе позиций сетка центрируется, а не прижимается влево» (US-011).
   Прошлая правка (CC-EMBER-003) заменяла center на start, сославшись на правило «заголовки
   разделов — по левому краю» (ui-ux.md строка 124) — но то правило про сам заголовок раздела
   («НАШ АССОРТИМЕНТ» остаётся у левого края, .products-header это не трогает), а не про сетку
   карточек внутри раздела; для сетки спека прямо требует обратное. Возвращено к center: при двух
   позициях карточки стоят по центру контейнера, а не образуют пустоту у правого края экрана.
   Правка 2026-08-02 (эталон DESIGN.md «Шаблон раздела»): max-height/overflow-y убраны — внутренняя
   прокрутка панели каталога запрещена эталоном («содержимое помещается в высоту экрана целиком,
   внутренней прокрутки и обрезки нет»). Карточки компактны и текущий ассортимент помещается без
   неё; при разрастании таблицы товаров содержимое уменьшается версткой карточки, а не скроллится. */

/* Снимок занимает отведённую область целиком. Прежние 74% с полями были подпоркой под
   снимки на белом фоне: на тёмной странице они читались светлым прямоугольником, и его
   отодвигали от краёв, чтобы не смотрелся наклейкой. Снимки приходят пережатыми под эту
   же область и с тёмным фоном — подпорка больше не нужна и только оставляла рамку
   пустоты вокруг товара. object-fit:cover, а не contain: соотношения области и снимка
   совпадают (16:11), обрезать нечего, зато случайный снимок другой формы заполнит поле,
   а не оставит поля по бокам. */
/* Ни собственного скругления, ни тени. Скруглением занимается одна только неподвижная
   рамка области (.showcase-visual): когда его вешали ещё и на слой, которому меняют
   прозрачность при переключении позиций, браузер на время перехода уводил слой в
   отдельную поверхность и терял на ней скруглённую обрезку — углы на долю секунды
   становились прямыми. Тень к тому же бессмысленна: снимок занимает область целиком,
   ронять её некуда, а фильтр создаёт ровно ту самую отдельную поверхность. */
.product-img{width:100%;height:100%;object-fit:cover;position:relative;z-index:2}
.product-img[hidden]{display:none}
/* EC-013: у товара нет фото — оформленная заглушка в стиле сцены,
та же геометрия,
что и с фото). */
/* Заглушка занимает ту же область и повторяет ту же форму,
что и снимок. Раньше она была
   вдвое меньше и со своим скруглением: при переключении позиций — у одной снимок есть,
у другой нет — рамка на глазах меняла и размер,
и форму. Пунктир остаётся: он честно
   говорит,
что здесь ожидается фотография,
а не такова задумка. */
.product-photo-placeholder{position:relative;z-index:2;width:100%;height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--space-xs);border:var(--line-hair) dashed var(--color-line);color:var(--color-muted)}
.product-photo-placeholder svg{width:var(--space-xl);height:var(--space-xl);stroke:var(--color-ember-light);fill:none;stroke-width:1.2;opacity:.85}
.product-photo-placeholder span{font-family:var(--font-ui);font-size:var(--text-label);font-weight:var(--weight-bold);letter-spacing:var(--track-wide);text-transform:uppercase;text-align:center;max-width:22ch}
.product-glow{position:absolute;inset:-28%;border-radius:var(--radius-full);opacity:.14;filter:blur(var(--blur-glow))}
.glow-1{background:var(--color-gold)}.glow-2{background:var(--color-ember)}
продвигающее конкретную позицию (запрет DESIGN/LEG-27): появляется только
   по этому явному клику,
тонкая приглушённая обводка{flex:0 0 auto;margin-top:var(--space-2xs);width:var(--space-lg);height:var(--space-lg);border-radius:var(--radius-full);border:var(--line-hair) solid var(--color-line);background:transparent;color:var(--color-muted);display:inline-flex;align-items:center;justify-content:center;cursor:pointer;transition:border-color .2s,color .2s}
из-за чего карточки в ряду теряли общую высоту. Теперь
   у статуса всегда своя строка целиком{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--space-xs);margin-bottom:var(--space-xs)}
/* INV-004: ровно три статуса, различимы словом (текст в разметке) — цвет здесь дополнение,
   не единственный признак. white-space:nowrap — метка держится в одну строку (эталон DESIGN.md). */
.stock-badge{display:inline-flex;align-items:center;gap:var(--space-2xs);font-family:var(--font-ui);font-size:var(--text-label);font-weight:var(--weight-bold);letter-spacing:var(--track-wide);text-transform:uppercase;white-space:nowrap;padding:var(--space-2xs) var(--space-xs);border-radius:var(--radius-lg);border:var(--line-hair) solid currentColor}
.stock-badge::before{content:'';width:var(--space-2xs);height:var(--space-2xs);border-radius:var(--radius-full);background:currentColor;flex:0 0 auto}
/* Палитра — строго INV-009, других цветов нет. Различие статусов даёт текст метки; цвет —
   только дополнительный, приглушённый акцент по той же шкале. */
.stock-in-stock{color:var(--color-text)}
.stock-low-stock{color:var(--color-gold)}
.stock-on-order{color:var(--color-muted)}
/* Слово владельца, которого нет в перечне: нейтральное оформление — цвета обещают наличие,
   а мы не толкуем чужое слово (решение 23.09.2026). */
.stock-other{color:var(--color-muted)}
/* INV-004: у выбранной позиции показывается ровно одна метка наличия. Разметка выводит метку
   для каждой позиции и помечает выбранную классом (js/catalog-showcase.js), но правила,
   скрывающего остальные, не было вовсе — метки стояли рядом все сразу, и наличие «не
   переключалось»: менять было нечего, обе надписи и так висели на экране.
   Метки лежат в одной ячейке сетки: тогда строка не меняет высоту при переключении. */
.showcase-stock-stack{display:grid;justify-items:start}
.showcase-stock-stack .stock-badge{grid-area:1 / 1;opacity:0;visibility:hidden;transition:opacity var(--motion-normal) var(--motion-ease)}
.showcase-stock-stack .stock-badge.is-active{opacity:1;visibility:visible}
/* Карточка компактная и несёт минимум: название,
наличие,
ключевые характеристики (решение
   2026-08-02) — описания и рекламного довеска в нём («стабильный жар»,
«крупный кубик») на
   карточке нет вовсе,
UI-catalog.details). */
/* CC-EMBER-002 / правка 2026-08-02 (карточка не разъезжается от длинных значений вроде
   «96 кубиков / 1 кг» — три равные колонки вместо flex-строки,
значение переносится словами
   внутри своей колонки вместо растягивания всей карточки на лишние строки). */

/* Правка CC-EMBER-003: раньше здесь стоял justify-content:flex-end — подписи держались на общей
   нижней линии,
но у длинного значения («96 кубиков / 1 кг»,
3 строки) из-за этого САМО значение
   «всплывало» заметно выше соседних однострочных значений («60 мин»,
«<2%») — именно на это
   жаловался пользователь. flex-start возвращает выравнивание к обычному верху: первая строка
   любого значения стоит на одной общей линии у всех трёх колонок,
длинное значение растёт вниз
   (к своей подписи),
а не вверх — соседние колонки не двигает. Подписи в этом случае у каждой
   колонки на своей высоте (следуют сразу за «своим» значением),
не на общей линии — так и просили. */


/* T-08: единый размер и начертание цифр у всех трёх характеристик (было — короткие значения
   крупнее,
длинные мельче,
из-за чего строка читалась как разные единицы измерения). Длинное
   значение держится в одну строку и усекается многоточием (.spec-val ниже),
а не переносится
   и не меняет кегль — сетка не растягивается при любой длине из таблицы. */


.product-cta,
.form-submit,
.hero-cta{display:inline-flex;align-items:center;justify-content:center;gap:var(--space-sm);min-height:var(--space-2xl);padding:0 var(--space-lg);border:var(--line-hair) solid var(--color-ember);font-family:var(--font-ui);font-size:var(--text-label);font-weight:var(--weight-bold);letter-spacing:var(--track-wide);text-transform:uppercase;text-decoration:none;color:var(--color-text);background:transparent;cursor:pointer;transition:background .25s,color .25s,transform .25s}
.product-cta:hover,.form-submit:hover,.hero-cta:hover{background:var(--color-ember);color:var(--color-coal);transform:translateY(-1px)}
/* Правка CC-EMBER-003: «Узнать подробности» переносилось на две строки внутри узкой карточки —
   кнопка не должна переноситься (белого текста «на палочке» не подразумевалось дизайном), поэтому
   держим одну строку и даём кнопке сжать внутренние отступы вместо переноса слов, если места
   впритык на самой узкой ширине карточки (230px, см. .products-grid minmax). */
/* UI-pricing — та же схема закрепления/отступов, что у остальных разделов (решение 2026-08-02):
   .pricing-sticky аналогичен .features-sticky/.products-sticky.
   T-06: комментарий про центрирование блока «по центру страницы» относился к прежней версии
   (.pricing-inner с margin-inline:auto) — сейчас, как и в остальных разделах, ось левая,
   задаётся паддингом .pricing-sticky (var(--grid-gutter)), без отдельного центрирования. */
/* T-06: было ...coal-deep 46%,coal-deep — нижний край раздела заканчивался в coal-deep,
   а .contact ниже начинается с coal (сплошной). Возврат к coal на обоих краях убирает
   видимую полосу между разделами. */
.pricing-section{height:165svh;min-height:165svh;position:relative;z-index:6}
.pricing-sticky{position:sticky;top:0;min-height:100svh;padding:var(--space-section) var(--grid-gutter);display:grid;grid-template-rows:auto;justify-items:stretch;overflow:hidden}
/* T-06: margin-inline:auto было избыточно (align-items:flex-start на родителе уже держит
   блок у левого края) и меняло бы поведение при случайной правке align-items — убрано
   ради единообразия с остальными разделами, где ось задаёт только паддинг родителя. */
.pricing-inner{padding-top:var(--space-xl);width:100%;max-width:var(--grid-max);margin-inline:auto}
.pricing-tag{color:var(--color-ember-light);margin-bottom:var(--space-md)}
.pricing-title{font-family:var(--font-display);font-size:var(--text-title);line-height:var(--leading-display);margin-bottom:var(--space-xl);max-width:var(--grid-max);margin-inline:auto;width:100%}
/* Правка 2026-08-02 (эталон DESIGN.md): поле объёма больше не сидит в собственной боксированной
   панели («панель в панели» — задник+рамка+паддинг отдельной карточки внутри раздела). Оно часть
   общей композиции раздела, на той же левой оси, что заголовок. */
/* Разметка объявляла два столбца (.pricing-grid), а правил для них в стилях не было вовсе —
   раздел шёл одной колонкой, и правая половина ширины пустовала. Слева ввод объёма и
   результат, справа таблица ступеней. */
.pricing-grid{display:grid;grid-template-columns:minmax(0,4fr) minmax(0,6fr);gap:var(--space-4xl);align-items:start}
.pricing-table-col{min-width:0}
.pricing-calc{margin-bottom:0}
.pricing-calc .form-label{display:block;margin-bottom:var(--space-xs)}
.pricing-calc-row{display:flex;align-items:baseline;flex-wrap:wrap;gap:var(--space-xs) var(--space-lg)}
.pricing-input{width:100%;max-width:var(--grid-narrow);font-size:var(--text-lead)}
/* UI-pricing.calc (решение 2026-08-02): минимальный заказ показан сразу, до ввода объёма —
   не только в сообщении об ошибке ниже (EC-014). Стоит в одной строке с полем — не отдельной
   панелью снизу. */
.pricing-hint{font-size:var(--text-label);color:var(--color-muted)}
/* Расчёт — главное в разделе, а не подпись под полем: ставка за килограмм ступенью
   заголовка карточки, сумма партии строкой ниже. Место под него отведено заранее, поэтому
   при вводе объёма ничего не прыгает. */
.pricing-result{display:grid;gap:var(--space-2xs);margin-top:var(--space-xl);min-height:calc(var(--text-subtitle) + var(--space-2xs) + 1.5em);align-content:start}
.pricing-result-value{font-family:var(--font-display);font-size:var(--text-subtitle);font-weight:var(--weight-heavy);line-height:var(--leading-display);letter-spacing:var(--track-tight);color:var(--color-ember-light);font-variant-numeric:var(--numeric-tabular)}
.pricing-result-note{font-size:var(--text-small);font-weight:var(--weight-medium);color:var(--color-muted);line-height:var(--leading-snug);font-variant-numeric:var(--numeric-tabular)}
/* Ссылка внутри сообщения калькулятора (js/pricing.js EC-014, и «договорная» в pricing-table
   ниже) — палитра INV-009, не системный синий. */
.pricing-result a{color:var(--color-ember-light);text-decoration:underline;text-underline-offset:var(--space-2xs)}
.pricing-table{width:100%;border-collapse:separate;border-spacing:0;margin-bottom:var(--space-md)}
/* Разделитель ступеней: граница у ячейки, но таблица без схлопывания.
 *
 * Было `border-collapse: collapse`, и граница между строками принадлежала двум
 * ячейкам сразу. Chromium красит такую общую грань один раз, WebKit — от
 * обеих: замер 04.09.2026 показал 61,3 вместо задуманных 36,7, то есть линию
 * вдвое ярче, а у шапки правая треть рисовалась в один слой — отсюда перелом
 * «слева ярко, справа тускло» ровно на границе колонок.
 *
 * `separate` убирает общие грани: каждая ячейка рисует свою линию один раз,
 * соседние ячейки строки дают одну сплошную. `border-spacing: 0` возвращает
 * прежнюю плотность.
 *
 * Псевдоэлементом на строке это делать нельзя, хотя и заманчиво: `position:
 * relative` на `<tr>` движки трактуют по-разному, и в Safari линии пропали
 * совсем. Проверено на человеке — он их не увидел.
 */
.pricing-table th{text-align:start;font-family:var(--font-ui);font-weight:var(--weight-bold);font-size:var(--text-label);letter-spacing:var(--track-wide);text-transform:uppercase;color:var(--color-muted);padding:0 0 var(--space-xs);border-bottom:var(--line-hair) solid var(--color-line)}
.pricing-row td{padding:var(--space-sm) 0;border-bottom:var(--line-hair) solid var(--color-line);font-size:var(--text-body);color:var(--color-text);transition:color .25s;font-variant-numeric:var(--numeric-tabular)}
/* Последняя ступень линией не закрывается: разделитель разделяет строки, а
   замыкающая черта внизу читается как граница между таблицей и тем, что за
   ней, — а её там нет. Убрано по слову человека 04.09.2026. */
.pricing-table tbody tr:last-child td{border-bottom:0}
.pricing-table th:last-child{text-align:end}
.pricing-row td:last-child{font-family:var(--font-display);font-size:var(--text-lead);letter-spacing:var(--track-normal);color:var(--color-text);text-align:right}
.pricing-row.active{color:var(--color-ember-light)}
.pricing-row.active td{color:var(--color-ember-light)}
.pricing-row.active td:last-child{color:var(--color-ember-light)}
/* L-008: ступень без цены — «договорная», ведёт к форме, а не к числу */
.pricing-row.pricing-row--custom td:last-child{font-family:var(--font-ui);font-size:var(--text-label);font-weight:var(--weight-bold);letter-spacing:var(--track-wide);text-transform:uppercase}
.pricing-row--custom td:last-child a{color:var(--color-ember-light);text-decoration:underline;text-underline-offset:var(--space-2xs)}
.pricing-note{font-size:var(--text-label);color:var(--color-muted);margin-bottom:var(--space-xs)}
/* LEG-21: дисклеймер «не оферта» обязателен на экране цен, п. 9.5 _legal.md */
/* Черты над юридическим текстом нет: над ним уже стоит нижняя граница последней строки
   таблицы, и две линии подряд читались как обрыв раздела. Отделяет отступ. */
.pricing-disclaimer{margin-top:var(--space-lg);font-size:var(--text-label);color:var(--color-muted);line-height:var(--leading-snug)}
.contact{height:165svh;min-height:165svh;position:relative;overflow:visible;background:transparent;z-index:6}
.contact-sticky{position:sticky;top:0;min-height:100svh;padding:var(--space-section) var(--grid-gutter);display:flex;flex-direction:column;justify-content:flex-start;overflow:hidden}
.contact-bg{position:absolute;inset:0;background:radial-gradient(ellipse at 28% 72%,var(--color-ember-glow),transparent 58%);pointer-events:none}
/* Метка стоит на той же вертикали, что заголовок и содержимое: без предела по ширине и
   центрирования она прижималась к краю экрана, а не к оси раздела — 40 против 70. */
.contact-tag{color:var(--color-ember-light);margin-top:var(--space-xl);margin-bottom:var(--space-md);max-width:var(--grid-max);margin-inline:auto;width:100%;opacity:0;transform:translateY(18px)}
/* Правка 2026-08-02 (эталон DESIGN.md «Шаблон раздела»): заголовок был крупнее и выше, чем у
   остальных разделов (clamp(48,6.2vw,82) против clamp(44,5.2vw,68) у «Почему выбирают нас»/
   каталога) — тот же размер, что у эталона, и заметно меньший отступ снизу освобождают высоту,
   без которой форма с полем согласия и кнопкой не помещалась в 900px и обрезалась снизу. */
.contact-title{font-family:var(--font-display);font-size:var(--text-title);line-height:var(--leading-display);margin-bottom:var(--space-lg);opacity:0;transform:translateY(30px);max-width:var(--grid-max);margin-inline:auto;width:100%}
/* CC-EMBER-002: было minmax(0,1fr) слева — на широких экранах панель контактов растягивалась
   куда шире своего короткого содержимого (несколько строк текста), а форма оставалась зажата
   в 500px рядом с этой пустотой. Обе колонки теперь на одной сетке — близкие капы ширины вместо
   одной свободной и одной жёсткой, max-width не даёт паре расползтись на весь широкий десктоп. */
/* Доли, а не предельные ширины: с minmax(...,460px)/minmax(...,520px) колонки набирали
   980 точек при контейнере в 1300, и справа от формы оставалась пустая четверть ширины.
   align-items:start — обе колонки начинаются от одной верхней линии; при center содержимое
   левой висело в середине, и между заголовком и контактами зиял провал в полторы сотни точек. */
.contact-grid{margin-block:auto;display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);gap:var(--space-4xl);align-items:start;max-width:var(--grid-max);position:relative;z-index:1;margin-inline:auto;width:100%}
.contact-info,.contact-form{opacity:0}
.contact-info{transform:translateX(-30px)}
.contact-form{transform:translateX(30px);display:flex;flex-direction:column;gap:var(--space-sm)}
.contact-info p{margin-bottom:var(--space-md);max-width:var(--grid-measure)}
.contact-details{display:flex;flex-direction:column;gap:var(--space-xs)}
.contact-row{display:grid;grid-template-columns:110px 1fr;gap:var(--space-md);align-items:baseline;border-bottom:var(--line-hair) solid var(--color-hairline);padding-bottom:var(--space-xs)}
.contact-row-label,.form-label{color:var(--color-ember-light)}
.contact-row-val{font-size:var(--text-body);color:var(--color-text);font-weight:var(--weight-medium)}
/* Телефон/почта — системный синий убран (палитра INV-009): наследует цвет .contact-row-val,
   лёгкий акцент в жар при наведении/фокусе — тот же язык, что у .footer-contacts a. */
.contact-row-val a{color:inherit;text-decoration:none;transition:color .2s}
.contact-row-val a:hover,.contact-row-val a:focus-visible{color:var(--color-ember-light)}
/* 18+ (LEG-22) в блоке каталога — по центру блока (решение 2026-08-02), не прижата к левому
   краю вместе с остальным текстом секции. */
/* Отметка о назначении товара стоит вплотную под карточкой, к которой относится, и по
   центру. Раньше висела на полтора отступа ниже и читалась подписью ко всему экрану. */
.age-notice{font-size:var(--text-label);color:var(--color-muted);letter-spacing:var(--track-normal);margin:var(--space-md) auto 0;max-width:var(--grid-max);width:100%;text-align:center}
.form-group{display:flex;flex-direction:column;gap:var(--space-xs)}
/* Контакты/форма (решение 2026-08-02): имя, компания+телефон в паре, сообщение — колонками, без
   «лестницы» из полей, каждое на своей полной строке. .form-row — сетка на два поля внутри формы,
   на мобильном схлопывается в один столбец (медиа-запрос ниже). */
.form-row{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-sm) var(--space-md)}
/* T-07: поле было голой нижней линией цветом --color-line (общий разделитель страницы) — визуально
   не читалось как поле ввода среди остальных боксированных элементов сцены (карточки, панель
   подробностей, метка наличия, опции витрины — все с рамкой+radius). Канон называет назначение
   явно: --color-hairline = «Границы полей», --color-surface-tint = «Приподнятый блок на тёмном
   фоне», --radius-sm = «Поле ввода» — рамка на весь периметр вместо только нижней линии, эти же
   токены, а не --color-line. Вертикальный паддинг не меняется (было var(--space-sm) 0) — высота
   поля и бюджет высоты раздела (900px, эталон DESIGN.md) остаются прежними. */
.form-input{background:var(--color-surface-tint);border:var(--line-hair) solid var(--color-hairline);border-radius:var(--radius-sm);padding:var(--space-sm) var(--space-md);font-family:var(--font-ui);font-size:var(--text-body);color:var(--color-text);outline:none;transition:border-color .25s,background .25s}
/* ui-ux.md (правка 2026-08-02): у полей ввода нет обводки-outline вовсе (общее правило
   focus-visible выше по файлу больше не включает input/textarea) — активное поле подсвечивается
   только сменой цвета границы, от клавиатуры и от мыши одинаково (граница видна в любом случае —
   это и есть видимый фокус для клавиатуры, T-A11Y-001). */
.form-input:focus-visible{border-color:var(--color-ember)}
.form-input::placeholder{color:var(--color-muted-dark)}
/* Правка 2026-08-02: было 88px — вместе с меткой/предупреждением под ним самое высокое поле формы,
   именно оно не давало разделу «Свяжитесь с нами» уместиться в 900px целиком (эталон DESIGN.md).
   64px всё ещё ~3 строки видимого текста, поле не превращается в однострочное. */
textarea.form-input{resize:none;min-height:var(--space-3xl)}
/* UI-contact.form: ошибка под полем (EC-010 и др.) — рамка поля уходит в жар, текст читаем без цвета как единственного признака */
.form-group:has(.field-error:not(:empty)) .form-input{border-color:var(--color-ember-light)}
.field-error{font-size:var(--text-label);color:var(--color-ember-light);min-height:1.3em;font-weight:var(--weight-medium)}
.field-error:empty{display:none}
.field-warning{font-size:var(--text-label);color:var(--color-muted);line-height:var(--leading-normal)}
.contact-form.is-submitting{opacity:.72;pointer-events:none}
.form-submit:disabled{opacity:.4;cursor:not-allowed;background:transparent;color:var(--color-muted)}
.form-submit:disabled:hover{background:transparent;color:var(--color-muted);transform:none}
.form-result{font-size:var(--text-small);font-weight:var(--weight-medium);color:var(--color-text);min-height:1.4em}
.form-result:empty{margin:0}
/* UI-contact.consent (LEG-04/05/06): непредустановленный чекбокс, ссылки внутри подписи не
   переключают его (обработчик — lead-form.js), здесь только визуальный контраст с фокусом. */
/* Правка 2026-08-02: паддинг сверху уменьшен (был 16px) — часть той же экономии высоты, что и у
   .contact-title/textarea, ради помещения формы целиком в 900px (эталон DESIGN.md). */
.consent-group{margin-top:var(--space-2xs);padding-top:var(--space-sm);border-top:var(--line-hair) solid var(--color-line)}
/* UI-lead.consent: нажимается подпись целиком — область не меньше TOK-hit.min, видимый квадрат
   (input) при этом остаётся мелким. */
.consent-label{display:flex;align-items:flex-start;gap:var(--space-sm);min-height:var(--hit-min);font-size:var(--text-small);line-height:var(--leading-normal);color:var(--color-muted);cursor:pointer}
.consent-label input[type=checkbox]{flex:0 0 auto;width:var(--space-md);height:var(--space-md);margin-top:var(--space-2xs);accent-color:var(--color-ember);cursor:pointer}
.consent-label a{color:var(--color-ember-light);text-decoration:underline;text-underline-offset:var(--space-2xs)}
.trap-field{display:none!important}
/* T-07: подвал — та же левая ось и тот же предел ширины содержимого, что у остальных разделов
   (см. .features-header/.pricing-inner max-width:var(--grid-max)). Раньше .footer-main ничем не
   ограничивался и на широком экране тянулся во всю ширину окна, тогда как контент разделов выше
   упирается в var(--grid-max) — подвал визуально не совпадал по ширине с остальной страницей. */
footer{padding:var(--space-2xl) var(--grid-gutter);border-top:var(--line-hair) solid var(--color-hairline);display:flex;flex-direction:column;gap:var(--space-md);background:var(--color-coal);padding-inline:max(var(--grid-gutter),calc((100% - var(--grid-max)) / 2));text-align:start}
/* Сетка, а не одна строка с переносами: раньше логотип, копирайт, контакты, ссылки и
   возрастная оговорка стояли в один ряд и переносились как придётся — читалось рвано.
   Слева знак и правовые ссылки, справа контакты и копирайт, внизу оговорка во всю ширину. */
.footer-main{display:grid;grid-template-columns:1fr auto;gap:var(--space-lg) var(--space-2xl);align-items:center;max-width:var(--grid-max);margin-inline:auto;width:100%}
.footer-logo{grid-column:1;grid-row:1;font-size:var(--text-subheading)}
.footer-copy{grid-column:2;grid-row:1;justify-self:end;color:var(--color-muted);font-size:var(--text-label)}
/* UI-footer: контакты (INV-002) в подвале, как и на экране contact — тот же спокойный акцент. */
.footer-contacts{grid-column:2;grid-row:2;justify-self:end;display:flex;gap:var(--space-lg);flex-wrap:wrap}
.footer-contacts a{font-family:var(--font-ui);font-weight:var(--weight-bold);font-size:var(--text-label);letter-spacing:var(--track-wide);color:var(--color-muted);text-decoration:none}
.footer-contacts a:hover{color:var(--color-ember-light)}
.footer-links{grid-column:1;grid-row:2;display:flex;gap:var(--space-lg);flex-wrap:wrap;align-items:center;justify-content:flex-start;text-align:start}
.footer-links a{font-family:var(--font-ui);font-weight:var(--weight-bold);font-size:var(--text-label);letter-spacing:var(--track-wide);color:var(--color-muted);text-decoration:underline;text-underline-offset:var(--space-2xs);text-transform:none}
.footer-links a:hover{color:var(--color-ember-light)}
/* LEG-22: отметка живёт отдельной нижней строкой подвала, мелко и приглушённо — не между
   разделами страницы (решение пользователя 2026-08-01). Тонкая верхняя линия отделяет её от
   остальных строк подвала, как .footer-requisites ниже. */
/* По левому краю, как всё остальное на странице: посередине оговорка выглядела
   отдельной подписью, ни к чему не относящейся. */
.footer-age-notice{grid-column:1 / -1;font-size:var(--text-label);color:var(--color-muted);line-height:var(--leading-snug);text-align:start;border-top:var(--line-hair) solid var(--color-hairline);padding-top:var(--space-md)}
.footer-requisites{width:100%;font-size:var(--text-label);color:var(--color-muted);border-top:var(--line-hair) solid var(--color-line);padding-top:var(--space-md);order:11}
/* Reviewer #15 (LEG-03, LEG-22, DESIGN): /privacy и /consent — та же типографика и палитра, что
   на главной (body{background,color,font-family} уже задан общим правилом выше в этом файле).
   Текст документов (_legal.md) не меняется — здесь только обрамление и вёрстка страницы. */
.legal-page{min-height:100vh;display:flex;flex-direction:column}
.legal-header{padding:var(--space-lg) clamp(var(--space-lg),4vw,var(--space-2xl))}
.legal-header a{font-family:var(--font-ui);font-weight:var(--weight-bold);font-size:var(--text-label);letter-spacing:var(--track-wide);color:var(--color-ember-light);text-decoration:none}
.legal-header a:hover{text-decoration:underline}
.legal-main{flex:1 0 auto;width:100%;max-width:var(--grid-measure);margin:0 auto;padding:var(--space-lg) clamp(var(--space-lg),4vw,var(--space-xl)) var(--space-3xl)}
/* Ограничение в 16 знаков пришло от витринных заголовков в две-три
   короткие строки. У правового документа заголовок длинный: «СОГЛАСИЕ НА
   ОБРАБОТКУ ПЕРСОНАЛЬНЫХ ДАННЫХ» складывался в четыре строки узким столбцом
   при пустой половине страницы. Заголовок документа занимает ширину меры,
   как и его текст. */
.legal-title{font-family:var(--font-display);font-size:var(--text-subtitle);line-height:var(--leading-display);letter-spacing:var(--track-normal);margin-bottom:var(--space-md);text-wrap:balance}
.legal-meta{color:var(--color-muted);font-size:var(--text-label);margin-bottom:var(--space-lg)}
.legal-meta a{color:var(--color-ember-light)}
/* UI-legal.policy/UI-legal.consent: текст показан абзацами (T-01), не разметкой файла —
   .legal-p/.legal-list рендерятся PHP из текста редакции построчно, каждый — уже готовая
   строка для показа (одиночные переносы внутри абзаца уже собраны в пробел на стороне PHP,
   поэтому здесь обычный перенос по ширине колонки, а не pre-wrap по ширине исходного файла). */
.legal-text{font-size:var(--text-small);line-height:var(--leading-normal);color:var(--color-text)}
.legal-p{margin-bottom:var(--space-md)}
/* Перечни и строки-таблицы (реквизиты, поля) держат каждый свою строку — .legal-list оборачивает
   несколько .legal-p подряд без зазора между ними, единый отступ стоит после списка целиком. */
.legal-list{margin-bottom:var(--space-md)}
.legal-list .legal-p{margin-bottom:0}
/* L-011 / EC-007 / EC-008: нет WebGL, мало ядер или системное уменьшение движения — 3D не
   запускается (app.js добавляет .lite-mode на <body> до попытки инициализации сцен), вместо
   canvas показывается статичная CSS/SVG-заглушка того же силуэта, без загрузки лишнего веса. */
.lite-mode #coalHeroCanvas,.lite-mode #coalJourneyCanvas{display:none}
.hero-coal-fallback,.journey-coal-fallback{display:none}
.lite-mode .hero-coal-fallback,.lite-mode .journey-coal-fallback{display:flex;align-items:center;justify-content:center}
.hero-coal-fallback{position:absolute;right:1%;top:8%;width:50%;height:82%;z-index:1}
.journey-coal-fallback{position:relative;width:100%;height:100%}
.coal-fallback-glow{position:relative;width:min(58%,320px);aspect-ratio:1;border-radius:var(--radius-md);background:radial-gradient(ellipse at 42% 38%,var(--color-ember-halo),transparent 62%),linear-gradient(155deg,var(--color-coal-lit),var(--color-coal));box-shadow:var(--shadow-glow) inset,0 30px 60px var(--color-scrim)}
.coal-fallback-glow::before{content:'';position:absolute;inset:14%;border-radius:var(--radius-md);background:radial-gradient(circle at 50% 50%,var(--color-ember-hot),var(--color-ember-dim) 60%,transparent 75%);filter:blur(var(--blur-sm));animation:coal-fallback-pulse 3.6s ease-in-out infinite}
@keyframes coal-fallback-pulse{0%,100%{opacity:.55}50%{opacity:1}}
.ember-layer{position:absolute;inset:0;overflow:hidden;pointer-events:none;z-index:1}
.ember-particle{position:absolute;width:var(--space-2xs);height:var(--space-2xs);border-radius:var(--radius-full);background:var(--color-ember);box-shadow:var(--shadow-glow);opacity:.55;animation:ember-rise linear infinite}
.smoke-wisp{position:absolute;width:170px;height:170px;border-radius:var(--radius-full);background:radial-gradient(circle,var(--color-hairline),transparent 68%);filter:blur(var(--blur-sm));opacity:.34;animation:smoke-drift ease-in-out infinite}
@keyframes ember-rise{0%{transform:translate3d(0,40px,0) scale(.45);opacity:0}14%{opacity:.7}100%{transform:translate3d(var(--drift),-120px,0) scale(1.25);opacity:0}}
@keyframes smoke-drift{0%,100%{transform:translate3d(0,0,0) scale(.9);opacity:.16}50%{transform:translate3d(38px,-28px,0) scale(1.12);opacity:.38}}
/* Точка перестроения — TOK-break.lg. Было отдельное число, не совпадавшее с каноном. */

/* T-A11Y-001: без горизонтальной прокрутки на самых узких экранах — таблица ступеней превращается
   в карточки. Точка — TOK-break.sm: запрос размера экрана не читает переменные оформления
   (платформенное ограничение), поэтому число совпадает со значением токена, а не ссылается на
   него. Было max-width:var(--grid-panel) — недействительная запись, весь блок молча не работал,
   и таблица (2 колонки) не собиралась в карточки ни на одной ширине. */
/* До TOK-break.lg два столбца в цене и контактах становятся тесными: ввод и таблица
   ступеней встают друг под друга. Число совпадает со значением токена — запрос размера
   экрана не умеет читать переменные оформления. */
/* Две колонки только в промежутке между точками: без нижней границы это правило стоит
   в файле позже правила для телефона и перебивало его — на 390 точках карточки
   оставались в две колонки по 125 точек, и названия не помещались. */




/* UI-catalog — витрина ассортимента (решение 2026-08-02): слева снимок выбранной позиции,
   справа выбор размера, характеристики и переход к форме. Помещается в высоту экрана. */
/* Кегль здесь не повторяется: он уже задан общим правилом заголовков разделов выше, а это
   объявление стояло в файле после правил для узкого экрана и перебивало их — «НАШ
   АССОРТИМЕНТ» оставался крупным и не помещался в ширину телефона. */
.products-title{line-height:var(--leading-display);max-width:var(--grid-max);margin-inline:auto;width:100%}
/* T-06: margin-inline:auto центрировало витрину отдельно от .products-header над ней —
   на широком экране заголовок стоял на левой оси раздела, а витрина съезжала к центру
   страницы. Убрано для единой левой оси. */
.showcase{align-self:center;display:grid;grid-template-columns:minmax(320px,1.05fr) minmax(340px,1fr);gap:clamp(var(--space-lg),3vw,var(--space-2xl));
  align-items:start;width:100%;max-width:var(--grid-max);background:linear-gradient(150deg,var(--color-coal-warm),var(--color-coal));
  border:var(--line-hair) solid var(--color-hairline);border-radius:var(--radius-md);padding:clamp(var(--space-md),2.2vw,var(--space-xl));margin-inline:auto}
/* У области снимка своя рамка и свой тёмный фон. Без рамки снимок сливался с панелью, а
   в скруглённых углах проступала подложка панели — светлее самого снимка, и углы
   читались засветкой. Фон под снимком того же тона, что страница: если снимок придёт с
   прозрачностью, товар ляжет на тёмное, а не на тёплое пятно. */
/* clip-path вдобавок к overflow: Safari не применяет скруглённую обрезку родителя к тем
   потомкам, которых сам вынес на отдельный слой — а слои снимков он выносит, потому что
   им меняется прозрачность при переключении позиций. overflow:hidden он в этот момент
   просто не смотрит, и углы становятся прямыми. clip-path — обрезка геометрическая, её
   он соблюдает и для вынесенных слоёв. Оставлены оба: overflow нужен старым сборкам,
   clip-path — Safari. Радиус тот же, дважды обрезать одну и ту же форму безвредно. */
.showcase-visual{position:relative;aspect-ratio:16/11;border-radius:var(--radius-md);overflow:hidden;clip-path:inset(0 round var(--radius-md));isolation:isolate;border:var(--line-hair) solid var(--color-hairline);
  background:var(--color-coal)}
/* Слой снимка не скругляет ничего сам: обрезкой занимается неподвижная рамка области.
   Скругление на слое, которому меняют прозрачность, браузер терял на время перехода. */
.showcase-photo{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  opacity:0;transition:opacity .35s ease}
.showcase-photo.is-active{opacity:1}
.showcase-photo .product-img{align-self:stretch}
/* Название и метка наличия стоят в одну строку: метка — уточнение к названию, а не
   отдельная строка под ним. Название ступенью выше — это заголовок панели. */
/* Название вровень с верхом снимка: у панели свой отступ сверху не нужен, а у снимка нет —
   без этого правая колонка начиналась ниже левой и стык читался неряшливо. */
.showcase-title-row{display:flex;align-items:center;flex-wrap:wrap;gap:var(--space-md);margin-top:calc(var(--space-2xs) * -1);margin-bottom:var(--space-xl)}
.showcase-category{font-family:var(--font-display);font-size:var(--text-subheading);letter-spacing:var(--track-normal);
  /* Цвет жара на странице носят надстрочные метки, а это заголовок панели: в оранжевом он
     был единственным цветным заголовком и спорил с заголовком самого раздела. */
  color:var(--color-text);text-transform:uppercase}
.showcase-hint{margin:0 0 var(--space-sm);font-family:var(--font-ui);font-size:var(--text-small);color:var(--color-muted)}
.showcase-options{display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:var(--space-sm);margin-bottom:var(--space-2xl)}
/* Отметка выбора стоит по центру кнопки: при выравнивании по верху она вставала вровень
   с первой строкой названия, а не с самой кнопкой, и в прямоугольнике смотрелась смещённой. */
.showcase-option{display:flex;align-items:center;gap:var(--space-xs);text-align:left;padding:var(--space-sm) var(--space-md);border-radius:var(--radius-md);
  border:var(--line-hair) solid var(--color-hairline);background:var(--color-hairline-soft);color:var(--color-text);cursor:pointer;/* нет токена: чистый белый глянцевый оверлей неотмеченной опции — в каноне нет белого/glass-цвета, --color-line другой оттенок (тёплый) */
  transition:border-color .2s,background .2s}
.showcase-option:hover{border-color:var(--color-ember-glow)}
.showcase-option.is-active{border-color:var(--color-ember);background:var(--color-ember-glow)}/* нет токена: канон не содержит прозрачного варианта --color-ember для фонового акцента */
.showcase-option-mark{flex:none;width:var(--space-md);height:var(--space-md);border-radius:var(--radius-full);
  border:var(--line-hair) solid var(--color-hairline);position:relative}
.showcase-option.is-active .showcase-option-mark{border-color:var(--color-ember)}
.showcase-option.is-active .showcase-option-mark::after{content:"";position:absolute;inset:var(--space-2xs);border-radius:var(--radius-full);background:var(--color-ember)}
.showcase-option-text{display:flex;flex-direction:column;gap:var(--space-2xs);min-width:0}
.showcase-option-name{font-family:var(--font-display);font-size:var(--text-lead);line-height:var(--leading-tight);text-transform:uppercase}
/* T-08: подробности всех позиций стоят в одной ячейке сетки (grid-area:1/1) — высота панели
   не зависит от того, какая позиция выбрана, и переключение не двигает фото рядом (было
   display:none/block — высота панели скакала вместе с длиной описания). */
.showcase-details{display:grid;grid-template-columns:1fr}
.showcase-detail{grid-area:1/1;visibility:hidden}
.showcase-detail.is-active{visibility:visible}
.showcase-desc{margin:var(--space-md) 0 0}
.showcase-detail .product-cta{display:inline-flex;white-space:nowrap}
/* Точка перестроения — TOK-break.lg. Было отдельное число, не совпадавшее с каноном. */


/* Характеристики позиции — по референсу: значок, крупное значение, подпись под ним.
   Значения набираются гротеском (Manrope 800): у Bebas Neue цифры разной ширины и в строке
   «96 кубиков / 1 кг» выглядели разнокалиберно (замечание пользователя 2026-08-02). */
/* Колонок столько, сколько влезает по мере значения, а не жёстко три. При трёх колонках
   на узкой панели планшета «96 кубиков / 1 кг» обрезалось многоточием: колонке доставалось
   около ста десяти точек. Предел в 9rem держит колонку достаточно широкой для самого
   длинного значения, а сколько их встанет в ряд — решает сама ширина. */
.product-specs{display:grid;grid-template-columns:repeat(auto-fit,minmax(9rem,1fr));gap:var(--space-md);margin:0 0 var(--space-md)}
.product-specs .spec{display:flex;align-items:flex-start;gap:var(--space-xs);min-width:0}
.spec-icon{flex:none;width:var(--space-lg);height:var(--space-lg);margin-top:var(--space-2xs);color:var(--color-ember)}
.spec-icon svg{width:100%;height:100%;fill:none;stroke:currentColor;stroke-width:1.4;stroke-linecap:round;stroke-linejoin:round}
.spec-text{display:flex;flex-direction:column;gap:var(--space-2xs);min-width:0}
.spec-val{display:block;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-family:var(--font-ui);font-weight:var(--weight-bold);font-size:var(--text-small);line-height:var(--leading-tight);color:var(--color-text);letter-spacing:var(--track-normal);font-variant-numeric:var(--numeric-tabular)}
.spec-label{font-family:var(--font-ui);font-weight:var(--weight-medium);font-size:var(--text-label);letter-spacing:var(--track-wide);text-transform:uppercase;color:var(--color-muted)}
.showcase-option-name{font-family:var(--font-ui);font-weight:var(--weight-bold);font-size:var(--text-body);letter-spacing:var(--track-normal);text-transform:uppercase;line-height:var(--leading-tight)}
.showcase-desc{font-family:var(--font-ui);margin:0}

/* Правила приспособления витрины стоят в самом конце файла намеренно: объявления самой
   витрины идут ниже основных блоков приспособления, и правка, вписанная выше, молча
   проигрывала им — вес у правил одинаковый, побеждает последнее.

   Панель обязана помещаться в экран телефона целиком: раздел закреплён, и низ с кнопкой
   иначе уходит под срез, откуда до него не добраться. Замер до правки: панель 711 при
   доступных 532. */


/* На телефоне разделы не закрепляются.
   Это не упрощение, а следствие замера: в одну колонку содержимое выше экрана — карточки
   преимуществ на 687 точек, форма заявки на 397, ступени цены на 295, витрина на 192.
   Закреплённый раздел показывает ровно экран, и всё, что ниже, становится недостижимо:
   до кнопки «отправить запрос» нельзя добраться ни прокруткой, ни жестом.
   Ритм остановок остаётся там, где содержимое в экран помещается, — от планшета и выше. */


/* ─────────────────────────────────────────────────────────────────────────
   ПРИСПОСОБЛЕНИЕ

   Все правила под размер экрана собраны здесь, в конце файла, и идут строго
   от широкого к узкому. Так каждое следующее перебивает предыдущее — вес у
   них одинаковый, побеждает то, что ниже.

   Раньше они были рассыпаны по файлу восемью блоками: три одинаковых «до
   64rem», два «до 30rem» и один диапазон. Восемь селекторов объявлялись
   сразу в нескольких блоках, а часть основных правил стояла ниже блоков
   приспособления и молча их перебивала — правка про соотношение сторон
   витрины не работала вовсе именно поэтому.

   Точки — из канона: TOK-break.lg (64rem), TOK-break.md (48rem),
   TOK-break.sm (30rem). Запрос размера экрана не умеет читать переменные
   оформления, поэтому числа совпадают со значениями токенов, а не ссылаются
   на них.
   ───────────────────────────────────────────────────────────────────────── */

@media (min-width:48rem) and (max-width:64rem){
  .features-grid{grid-template-columns:repeat(2,minmax(0,1fr));grid-template-rows:repeat(12,auto)}
}

@media (max-width:64rem){
  .nav-links{display:none}
  /* T-A11Y-001: мобильное меню — панель под навигацией, открывается кнопкой navToggle (app.js),
     позиционируется абсолютно относительно nav (nav — position:fixed, задаёт containing block). */
  .nav-toggle{display:inline-flex}
  nav.nav-open .nav-links{display:flex;position:absolute;top:100%;left:var(--space-md);right:var(--space-md);margin-top:var(--space-xs);flex-direction:column;gap:var(--space-2xs);background:var(--color-backdrop);backdrop-filter:blur(var(--blur-md));border:var(--line-hair) solid var(--color-line);border-radius:var(--radius-md);padding:var(--space-xs) var(--space-md);box-shadow:var(--shadow-overlay)}/* нет токена: var(--color-backdrop) — прозрачность --color-coal под backdrop-blur, альфа-варианта в каноне нет; var(--color-scrim) — цвет тени канон не определяет */
  nav.nav-open .nav-links li{width:100%;border-bottom:var(--line-hair) solid var(--color-line)}
  nav.nav-open .nav-links li:last-child{border-bottom:none}
  nav.nav-open .nav-links a{display:block;padding:var(--space-md) var(--space-2xs);font-size:var(--text-label)}
  /* Первый экран занимает ровно видимую область — не больше и не меньше. Снизу отведено
     место под подсказку «прокрутите вниз»: она приклеена к нижнему краю раздела, и если
     раздел перерастал окно, подсказка уезжала под сгиб. Высота окна у телефонов разная,
     поэтому цифрами её не задать — остаток забирает холст с моделью (ниже). */
  .hero{justify-content:flex-start;height:100svh;min-height:100svh;overflow:hidden;
    padding-top:calc(var(--nav-height) + var(--space-lg));
    padding-bottom:calc(var(--space-xl) + var(--space-2xl))}
  .hero-content{flex:0 0 auto}
  .hero-content{max-width:100%}
  .hero-title{max-width:11ch}
  /* Модель вписана в холст по его высоте: на телефоне холст был низкий и широкий, уголь
     занимал середину, а по бокам оставалась пустота. Высоту добавили — уголь вырос вместе
     с ней, разметка первого экрана при этом ещё помещается. */
  /* Холст забирает всю высоту, оставшуюся от текста: на длинном экране уголь крупнее, на
     коротком — мельче, но подсказка внизу видна везде одинаково. Модель вписана в холст по
     его высоте, поэтому подстраивается сама, без отдельных правил под каждый телефон. */
  #coalHeroCanvas,.hero-coal-fallback{position:relative;right:auto;top:auto;width:100%;
    flex:1 1 auto;height:auto;min-height:22svh;margin-top:var(--space-sm);order:2}
  /* На узком экране грузовик занимает всю ширину — значит и место под него отводится по ней. */
  .truck-section{height:260svh;--truck-width:100vw}
  /* Раздел закреплён у верха окна, а шапка висит поверх: верхний отступ должен быть больше
     её высоты, иначе надстрочная метка стоит ровно под ней и не читается вовсе. */
  /* Пока меню свёрнуто в кнопку, шапка висит поверх закреплённого раздела. Верхний отступ
     у всех разделов один и заведомо больше её высоты: при обычном отступе надстрочная
     метка вставала ровно под шапкой и не читалась. */
  .features-sticky,.products-sticky,.pricing-sticky,.contact-sticky{padding-top:var(--space-4xl)}
  .truck-sticky{padding-block:var(--space-4xl) calc(var(--truck-height) + var(--space-3xl) + var(--space-xl))}
  .truck-info{gap:var(--space-xl);flex-wrap:wrap;justify-content:center}
  .journey-section{height:260vh}.coal-3d-wrap{right:auto;left:50%;top:66%;width:86vw;transform:translate(-50%,-50%)}.journey-step{left:var(--space-lg);right:var(--space-lg);top:28%;max-width:none}.journey-progress{right:var(--space-lg)}
  /* На телефоне три колонки оставляли карточке около ста двадцати точек: названия не
     помещались, а сетка растягивала страницу вбок (424 при окне 390). Одна колонка, и
     строк ровно столько, сколько занимают шесть карточек по четыре ряда каждая —
     число строк объявляет родитель, иначе subgrid не на что опереть. */
  
  
  .contact-grid{grid-template-columns:1fr;gap:var(--space-2xl)}.contact-row{grid-template-columns:1fr;gap:var(--space-xs)}
  footer{align-items:flex-start}
  .footer-main{flex-direction:column;align-items:flex-start;gap:var(--space-sm)}
  .footer-logo{margin-right:0}
  .pricing-grid{grid-template-columns:1fr;gap:var(--space-2xl)}
  .pricing-calc{margin-bottom:0}
  .contact-grid{grid-template-columns:1fr;gap:var(--space-2xl)}

  .showcase-visual{aspect-ratio:16/10}
}

@media (max-width:48rem){
  .features,.products-section,.pricing-section,.contact{height:auto;min-height:0}
  .features-sticky,.products-sticky,.pricing-sticky,.contact-sticky{
    position:relative;min-height:0;padding-block:var(--space-2xl)}
}

@media (max-width:30rem){
  .pricing-table,.pricing-table thead,.pricing-table tbody,.pricing-table tr,.pricing-table td{display:block;width:100%}
  .pricing-table thead{display:none}
  .pricing-row{border:var(--line-hair) solid var(--color-line);border-radius:var(--radius-sm);padding:var(--space-sm) var(--space-md);margin-bottom:var(--space-xs)}
  .pricing-row td{border:none;padding:var(--space-2xs) 0;text-align:left!important}
  .pricing-row td:last-child{font-size:var(--text-subheading)}
  .contact-row{grid-template-columns:1fr}
  .features-grid{grid-template-columns:1fr;grid-template-rows:repeat(24,auto)}
  .form-row{grid-template-columns:1fr}
  /* На самом узком экране ступень заголовка ниже: слово в десять знаков крупным кеглем
     не помещается ни при каком пределе по знакам. */
  .truck-info{gap:var(--space-lg)}
  .showcase-visual{aspect-ratio:16 / 9}
  .showcase-title-row{margin-bottom:var(--space-md)}
  .showcase-options{grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--space-xs);margin-bottom:var(--space-lg)}
  .showcase-option{padding:var(--space-xs) var(--space-sm)}
  .showcase-option-name{font-size:var(--text-body);white-space:nowrap}
  .showcase-option{gap:var(--space-2xs);padding:var(--space-xs)}
  .showcase-hint{margin-bottom:var(--space-xs)}
  /* Характеристики на телефоне — строками, а не тремя колонками по сто десять точек:
     в колонках «96 кубиков / 1 кг» обрезалось многоточием, а подписи ломались на две
     строки. Строкой слева подпись, справа значение — читается сразу и целиком. */
  .product-specs{grid-template-columns:1fr;gap:0;margin-bottom:0}
  .product-specs .spec{align-items:center;padding:var(--space-xs) 0;border-bottom:var(--line-hair) solid var(--color-line)}
  .product-specs .spec:last-child{border-bottom:none}
  .spec-icon{margin-top:0}
  .spec-text{flex:1;flex-direction:row-reverse;align-items:baseline;justify-content:space-between;gap:var(--space-md)}
  .spec-val{overflow:visible;text-overflow:clip;text-align:end}
  .showcase-desc{margin:var(--space-md) 0}
  .showcase-detail .product-cta{width:100%}
}

/* Подвал на телефоне — одной колонкой. В две ссылке на политику доставалось сто точек
   ширины, и она ломалась посреди слова. Порядок сверху вниз: знак, контакты, правовые
   ссылки, копирайт, возрастная оговорка. */
@media (max-width:48rem){
  /* На телефоне подвал стоит в две колонки: слева знак и контакты, справа копирайт и
     документы. Одной колонкой он вытягивался в длинную башню, а всё по центру оставляло
     половину ширины пустой. Предупреждение 18+ идёт во всю ширину — оно относится ко всему. */
  /* Левая колонка по ширине содержимого (её задаёт почта), правой достаётся остальное:
     иначе копирайт с разрядкой ломался на три строки в узком столбце. */
  .footer-main{grid-template-columns:auto 1fr;gap:var(--space-lg) var(--space-md);align-items:start}
  /* Знак и копирайт стоят в одном ряду, но высота у них разная: знак — одна крупная
     строка, копирайт — две мелкие. При выравнивании по верху ряда они читались как
     съехавшие друг относительно друга, поэтому здесь центрируем по ряду. */
  .footer-logo{grid-column:1;grid-row:1;justify-self:start;align-self:center}
  /* Копирайт — служебная строка, а не заголовок. В прописных с широкой разрядкой он в
     узкой колонке ломался на три строки; обычным набором помещается в две. */
  .footer-copy{grid-column:2;grid-row:1;justify-self:end;align-self:center;text-align:end;text-transform:none;letter-spacing:var(--track-normal)}
  .footer-contacts{grid-column:1;grid-row:2;justify-self:start;flex-direction:column;align-items:flex-start;gap:var(--space-2xs)}
  .footer-links{grid-column:2;grid-row:2;justify-self:end;flex-direction:column;align-items:flex-end;gap:var(--space-2xs);text-align:end}
  .footer-age-notice{grid-column:1 / -1;grid-row:3;justify-self:start;text-align:start;margin-top:var(--space-xs)}
}

/* Доставка на телефоне: грузовик крупнее и выше.
   При ширине в экран его высота — 81 точка на 844: полоска у нижнего края, которую на
   телефоне просто не замечаешь. Ширина больше экрана делает его заметным (края уходят за
   кадр, и это верно — он едет мимо), а подъём над нижним краем возвращает его в поле
   зрения. Дорога поднимается вместе с ним, чтобы колёса остались на полотне. */
@media (max-width:48rem){
  .truck-section{--truck-width:168vw}
  /* Вся сцена доставки сдвинута к середине экрана: сверху отступ крупнее, снизу дорога
     поднята меньше. Прежде сверху оставалось 96, снизу 169 — раздел заваливался вверх,
     а между цифрами и грузовиком зияло больше, чем над заголовком. */
  .road{bottom:9svh}
  .truck-wrap{bottom:calc(9svh + var(--space-3xl))}
  .truck-sticky{padding-block:var(--space-5xl) calc(var(--truck-height) + 9svh + var(--space-3xl) + var(--space-md))}
}

/* На телефоне место под ответ заранее не отводится: столбцы стоят друг под другом, и пустая
   строка под полем читалась провалом перед таблицей. Ответ появляется ниже поля и ничего
   уже показанного не сдвигает — прыжка, ради которого отводят место, здесь не возникает. */
@media (max-width:48rem){
  .pricing-result{min-height:0;margin-top:var(--space-lg)}
  .pricing-grid{gap:var(--space-xl)}
}

/* Самостоятельные ссылки — телефон, почта, правовые документы, «договорная» — на сенсорном
   экране должны иметь цель размером не меньше TOK-hit.min. Высота строки текста даёт 17–22
   точки: пальцем в такую попадают через раз. Ссылки внутри предложения сюда не входят —
   их цель считается по строке, а не по элементу. */

/* Доставка на телефоне не закрепляется: грузовик просто едет мимо, пока страница идёт
   вниз. Закрепление на узком экране давало неприятный разрыв — картинка стояла на месте,
   а палец продолжал тянуть; на широком экране, где виден весь кадр с дорогой и цифрами,
   остановка читается как задумано, а на телефоне — как залипание. */
@media (max-width:48rem){
  /* Доставка остаётся закреплённой и на телефоне: её содержимое помещается в экран целиком
     (замер на 393×808: блок ровно 808), а без запаса прокрутки грузовику некуда ехать —
     раздел занимал ровно экран, прогресс скакал с нуля на единицу, и на прокрутку ничего
     не отзывалось, кроме разметки дороги. */
  .truck-section{height:200svh;min-height:100svh}
  .truck-sticky{position:sticky;top:0;min-height:100svh}
}

/* Строки контактов на телефоне идут одним шагом. При двух колонках подпись и значение
   стояли парой, а в одну колонку между ними оставался зазор из другой раскладки —
   до телефона и почты отступ выходил больше, чем до города. */
@media (max-width:48rem){
  .contact-row{gap:var(--space-2xs)}
  .contact-details{display:grid;gap:var(--space-md)}
  /* Все строки контактов одной высоты. Телефон и почта — ссылки, им отведена цель нажатия
     в TOK-hit.min, а город ссылкой не является: строки выходили 74, 74 и 52, и до города
     отступ читался меньше, чем до остальных. Ту же высоту получает и значение без ссылки. */
  .contact-row-val{display:flex;align-items:center;min-height:var(--hit-min)}
}

/* Правая колонка не растягивается на высоту снимка: содержимое идёт подряд от заголовка —
   выбор размера, характеристики, описание, — а действие отделено от них шагом крупнее
   остальных. Растягивание на всю высоту разгоняло блоки по углам и рвало связь между ними;
   высоту панели задаёт снимок, а не пустота под текстом. */
.showcase-info{display:flex;flex-direction:column}
.showcase-detail .product-cta{margin-top:var(--space-xl)}

/* Цель нажатия у самостоятельных ссылок — не меньше 24 точек на любом устройстве (WCAG 2.2,
   «Target Size (Minimum)»), и не меньше TOK-hit.min там, где нажимают пальцем. Высота строки
   текста даёт 17: мышью в такую попадают, но промахиваются чаще, чем следует. Ссылки внутри
   предложения под правило не подпадают — их цель считается по строке, а не по элементу. */
.nav-links a,.contact-row-val a,.footer-contacts a,.footer-links a,
.pricing-row--custom td:last-child a{display:inline-flex;align-items:center;min-height:1.5rem}

/* Сенсорный экран: цель не меньше TOK-hit.min. Правило стоит после базового «не меньше 24»
   намеренно — вес у них одинаковый, побеждает то, что ниже. В прошлой сборке сенсорное
   правило оказалось выше базового и молча перестало действовать: на телефоне цели
   вернулись к 24 точкам. */
@media (pointer:coarse),(max-width:64rem){
  .nav-links a,.contact-row-val a,.footer-contacts a,.footer-links a,
  .pricing-row--custom td:last-child a,.nav-logo{min-height:var(--hit-min)}
  .nav-logo{display:inline-flex;align-items:center}
  .showcase-option{min-height:var(--hit-min)}
}

/* Высокий экран (TOK-break.tall): заголовок и содержимое центруются одной группой.
   На планшете содержимое занимает заметно меньше высоты раздела, и заголовок, прижатый к
   верхней линии, отрывается от него — между ними остаётся дыра, а раздел читается двумя
   несвязанными частями. Точка по высоте, а не по ширине: планшет в альбоме шире ноутбука,
   но свободной высоты у него больше. Число совпадает со значением токена — запрос размера
   экрана не умеет читать переменные оформления. */
@media (min-height:60rem){
  .features-sticky,.products-sticky,.pricing-sticky{grid-template-rows:auto auto;align-content:center}
  /* Доставка тоже: заголовок и условия отгрузки одной группой по центру той высоты, что
     осталась над дорогой. Иначе между ними на планшете зияет две сотни точек.

     Дорога с грузовиком поднимается над нижним краем: на планшете отступ сверху до
     заголовка выходил 263 точки, а под грузовиком оставалось 64 — раздел заваливался вниз.
     Подъём в 17svh уравнивает их: при высоте окна 1024 сверху и снизу получается примерно
     по 170. Дорога поднимается на столько же за вычетом того, на сколько колёса утоплены
     в полотно, — иначе грузовик поедет над дорогой. */
  .truck-sticky{grid-template-rows:auto auto;align-content:center;
    padding-block:var(--space-section) calc(var(--truck-height) + 17svh)}
  .truck-info{align-self:auto;margin-top:var(--space-3xl)}
  .truck-wrap{bottom:17svh}
  .road{bottom:calc(17svh - var(--space-3xl))}
  .contact-sticky{justify-content:center}
  .features-grid,.showcase{align-self:auto}
  .contact-grid{margin-block:0}
}

/* Текст первого экрана не заходит под сцену. Сцена занимает правую половину окна
   (#coalHeroCanvas: right 1%, width 50%), значит текст обязан кончаться левее её края.
   Предел по мере строки сам по себе этого не гарантирует: при ширине окна 1194 строка в
   42rem доходила до 707 при начале сцены на 585 — подзаголовок ложился прямо на уголь.
   Берём то из двух, что меньше: меру строки или сорок четыре сотых ширины окна. */
/* Предел по ширине окна нужен только там, где сцена стоит СБОКУ от текста. Ниже
   TOK-break.lg сцена уходит под текст (см. блок приспособления), и тот же предел там
   лишний: при ширине 834 сорок четыре сотых дают 367 точек, и слово «НАСТОЯЩЕГО» в
   заголовок уже не помещается. */
/* Строго шире точки, а не «от точки и выше»: ровно на 64rem сцена ещё стоит под текстом
   (правило перестроения включительное), и предел по ширине окна там лишний — заголовок
   от него ломался. */
@media (width > 64rem){
  /* Мера строки — предел для прозы, а не для крупного заголовка: самое длинное слово в нём
     шире меры, и предел резал его. Заголовку хватает того, что сцена больше не разрастается. */
  .hero-tag,.hero-desc{max-width:min(var(--grid-measure),44vw)}
}

/* Подвал на телефоне: шаг между строками тот же, что между полями формы, и не больше.
   Прежде строки стояли на отступе в полтора десятка точек больше нужного, а до возрастной
   оговорки набегало ещё столько же — подвал занимал полтора экрана ни на чём. */
@media (max-width:48rem){
  /* Строки подвала стоят вплотную, без собственного зазора: у ссылок уже есть цель нажатия
     в TOK-hit.min, и вокруг короткой строки текста остаётся по тринадцать точек воздуха
     сверху и снизу. Зазор поверх этого складывался с ним вдвое, и подвал разъезжался.
     Ритм задаёт сама высота цели, а не отступ между ними. */
  footer{padding-block:var(--space-xl)}
  .footer-main{gap:0}
  .footer-contacts{gap:0 var(--space-lg)}
  .footer-links{gap:0 var(--space-lg)}
  .footer-copy{padding-block:var(--space-xs)}
  .footer-age-notice{margin-top:var(--space-sm);padding-top:var(--space-md)}
}

/* Витрина складывается в одну колонку только на телефоне. На планшете в столбик она
   набирала 865 точек высоты и переставала помещаться в экран (замер на 834×1194: блок
   1226 при 1194), хотя на две колонки по 380 места там достаточно. */
@media (max-width:48rem){
  .showcase{grid-template-columns:1fr;gap:var(--space-md)}
}
