/*
 * f7c_template :: layout-обвязка (.tpl-*).
 * Граница с дизайн-системой — f7c_design docs/THEMING.md, раздел
 * «Граница с шаблоном»: только композиция и геометрия на токенах --ds-*;
 * никаких новых визуальных компонентов, никаких переопределений .ds-*.
 * Брейкпоинты — контрактные (sm 640 / md 768 / lg 1024 / xl 1280).
 */

/* 🔴 ГРАНИЦА СО СКРОЛЛБАРОМ (v0.156.0). Её назвал вслух `c-design` в самом ядре
   и оставил шаблону: вынос стрелок карусели в гаттер считается как
   `max(-54px, calc((100% - 100vw) / 2))`, а `100vw` ВКЛЮЧАЕТ классический
   скроллбар, тогда как клиентская область — нет. Где зажим упирается (узко),
   стрелка встаёт ровно по краю `100vw`, то есть на полширины полосы ЗА краем
   видимого.
   ЗАМЕР (forbion, headful Chromium под Xvfb, полоса 15 px — оверлейная полоса
   headless этот случай НЕ воспроизводит и все мои прежние «прокрутки нет»
   были фактом о МОЁМ браузере):
     до: 1200→+8  1250→+8  1251→+8  1280→+8  1300→+8  1310→+3  1366→0  1440→0
     правый край стрелки на 1200 = 1192.5 при клиентских 1185 — ровно 7.5 = 15/2.
   ⚠ `clip`, а НЕ `hidden`: `hidden` создаёт контейнер прокрутки — он ломает
   `position: sticky` у шапки и делает `overflow-y` вычисляемым как `auto`.
   `clip` не создаёт ни того, ни другого и допускает `overflow-y: visible`.
   ⚠ Safari < 16 `clip` не знает — там останется прежнее поведение (полоса
   прокрутки), то есть НЕ хуже сегодняшнего: дефолт-фолбэк здесь безопасен.
   🔴 ЧЕГО ЭТО ПРАВИЛО ДЕЛАТЬ НЕ ДОЛЖНО — ПРЯТАТЬ СОДЕРЖАНИЕ. Полчаса назад за
   край вьюпортa вылезали чипы-статусы (мой промах с носителем координат,
   v0.155.1): клип срезал бы им правые концы и убрал бы прокрутку — то есть
   убрал бы ЕДИНСТВЕННЫЙ признак дефекта, а не дефект. Поэтому правило вводится
   ТОЛЬКО после замера «за клиентским краем не осталось ничего, кроме стрелки»
   (0 узлов на 16 ширинах), и этот замер — часть приёмки, а не разовая доглядка.
   Широкое содержимое своей прокрутки не теряет: у таблиц и ленты собственные
   контейнеры с `overflow-x: auto`, клип страницы их не касается. */
html { overflow-x: clip; }

/* Сетка карточек: 1 колонка → 2 → 3 */
.tpl-grid {
  /* Значения раскладки объявлены ЗДЕСЬ и один раз; медиаблоки ниже их только
     включают (см. комментарий у ступени хаба). */
  --tpl-grid-cols: repeat(6, 1fr);
  --tpl-grid-span: span 2;
  --tpl-grid-span-lg: span 3;
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--ds-space-6);
}

/* 🔴 СЕТКА РЕШАЕТ ОДНОЙ ГРАНИЦЕЙ 900|901, А НЕ ДВУМЯ СВОИМИ (v0.214.0; наряд
   integrator-c2, находка c-ops бисекцией + перечисление медиазапросов).
   ЗАМЕР КАНОНА: 1 → 3 колонки ОДНИМ шагом на 900|901.
   У нас одно решение канона распалось на ДВА, и НИ ОДНО не совпадало с ним:
       1 → 2 колонки на 639|640      · 2 → 3 колонки на 1023|1024
   ⇒ между 640 и 900 канон СТОЛБИКОМ, а мы уже в две колонки; между 901 и 1023
   канон в ТРИ, а мы всё ещё в две. Два коридора расхождения, и оба невидимы
   на 390 и 1366 — там, где мы мерили весь день.
   ⚠ ПОРОГ 640 СНЯТ ЦЕЛИКОМ, а не сдвинут: канон между 640 и 900 держит одну
   колонку, значит промежуточной ступени у него НЕТ ВОВСЕ. Сдвинуть 640 → 901
   значило бы сохранить ступень, которой у источника нет.
   📌 И это четвёртый за сутки случай «починили одного из потребителей»: 1024
   вынимали из типографики калькулятора и считали предмет закрытым — носитель
   остался и раздавал ту же границу лестнице, cta, inc и герою. */
@media (min-width: 901px) {
  /* 🔴 ШЕСТЬ КОЛОНОК ВМЕСТО ТРЁХ, ДЕФОЛТ — span 2 (v0.115.0). Для секций без
     выделенных карточек это ТО ЖЕ САМОЕ: три штуки в ряд. Но появляется
     выразимость канона «2 больших + 3 обычных»: большая занимает span 3.
     Замер канона (c-design): сетка хаба 6 колонок, big = span 3 (531px),
     обычная = span 2 (349px). */
  .tpl-grid { grid-template-columns: var(--tpl-grid-cols); }
  .tpl-grid > * { grid-column: var(--tpl-grid-span); }
  .tpl-grid > .tpl-card--featured { grid-column: var(--tpl-grid-span-lg); }
  /* Крупная иллюстрация — свойство ВАРИАНТА, не ассета: тот же файл 168×134. */
  .tpl-card--featured { --tpl-illu-w: 168px; }
}

/* 🔴 У СЕТКИ ХАБА СВОЯ СТУПЕНЬ — 761, А НЕ ОБЩАЯ 901 (v0.231.0).
   ЗАМЕР `c-ops` по восемнадцати ширинам на живом и мой по секциям канона:
       #hub  760 → 1 колонка · 761 → «2+3»      ⇒ ступень канона 760|761
       у нас 900 → 1 колонка · 901 → «2+3»      ⇒ полоса 761…900 расходится
   На этих 140 px наша карточка ВДВОЕ шире канонной (737…848 против 348…417).
   ⚠ Это НЕ отмена решения v0.214.0 «одна граница вместо двух»: та мерилась на
   ДРУГОЙ сетке, и там у канона действительно один шаг. Сегодня замерено, что
   ступень у канона СЕКЦИОННАЯ — `#hub` ломается на 761, `#srv` идёт своей
   лестницей (2+2 на 700 · 3+1 на 760…900 · 4 с 1000). Снятая ступень 640 не
   возвращается: между 640 и 760 канон по-прежнему столбиком.
   🔴 ЧИСЛА РАСКЛАДКИ ОБЪЯВЛЕНЫ ОДИН РАЗ (`--tpl-grid-cols|-span|-span-lg` на
   `.tpl-grid`), а медиаблоки их только ВКЛЮЧАЮТ. Скопируй я «repeat(6, 1fr)»
   во второй блок — получилось бы два источника одного значения, и они разъехались
   бы при первой же правке (мой собственный класс, пойманный сегодня на резерве
   иллюстрации: два автора на одну величину).
   ⚠ ОСТАТОК НАЗЫВАЮ: `--tpl-illu-w` в полосе 761…900 остаётся обычным (103) —
   ширины канонной иллюстрации на этой полосе я НЕ мерил, а ставить не мерянное
   значило бы выдать догадку за замер. Резерв абзаца там даёт ядро (78). */
@media (min-width: 761px) and (max-width: 900px) {
  .tpl-grid--hub { grid-template-columns: var(--tpl-grid-cols); }
  .tpl-grid--hub > * { grid-column: var(--tpl-grid-span); }
  .tpl-grid--hub > .tpl-card--featured { grid-column: var(--tpl-grid-span-lg); }
}

/* ПРЕСЕТЫ КАЛЬКУЛЯТОРА (`control: "presets"`, v0.118.0). Канон: конфигурация
   выбирается КНОПКОЙ, полей ввода нет как класса. Под кнопкой — нативный
   radio: без JS выбор работает и читается скринридером, а JS уже умеет читать
   radio-группу (правок скрипта не потребовалось).
   Состояние «выбран» ловим через `:has(input:checked)` — тот же приём, что у
   ядрового ds-segment; сам input визуально убран, но остаётся доступным
   клавиатуре (не `display:none`). */
.tpl-presets {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
  margin: 0;
  padding: 0;
  border: 0;
}

.tpl-preset {
  /* 🔴 КЕГЛЬ КНОПКИ-ПРЕСЕТА — РОЛЬ ПОЛЯ, А НЕ ШКАЛА (v0.175.0; токен ядра
     `--ds-field-size`, у forbion 13, DESIGN v0.228.0).
     ⚠ Токен объявлен и в ядре читается — но правилом `.ds-input/.ds-select/
     .ds-textarea`, а наши пресеты это `.tpl-preset`: замер дал 16 при
     объявленных 13. То есть «значение есть — читателя нет», и читателем должен
     стать МОЙ класс: пресет — способ ввода того же поля, кегль у него полевой.
     ⚠ Фолбэк — прежние 16 (`--ds-fs-md`): бренды без токена не двигаются. */
  font-size: var(--ds-field-size, var(--ds-fs-md));
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-1);
  padding: var(--ds-space-3) var(--ds-space-4);
  border: 1px solid var(--ds-border-default);
  border-radius: var(--ds-radius-md);
  cursor: pointer;
  /* 🔴 ФОН НЕ ЗАДАЁМ. Первая редакция ставила `var(--ds-surface-page)`, и
     объявление было МЁРТВЫМ: замер показал `#ECF2F2` — поверхность контролов
     ядра (`--ds-surface-raised`), которая выигрывает у моего правила. Строка
     читалась как решение, но ничего не решала — наш класс «правило есть,
     эффекта нет», третий случай за сутки и первый в моём же файле.
     Поверхность неактивного контрола принадлежит ЯДРУ (канон подтвердил роль
     #ECF2F2 отдельно от фона секций) — значит и объявлять её должно оно одно. */
}

.tpl-preset input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
}

.tpl-preset:has(input:checked) {
  border-color: var(--ds-color-primary);
  background: var(--ds-surface-raised);
}

.tpl-preset:focus-within { outline: 2px solid var(--ds-color-primary); }

.tpl-preset__price { font-size: var(--ds-fs-lg); font-weight: var(--ds-fw-bold); }

.tpl-preset__cfgs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-1);
}

/* 🔴 СПИСОК КОНФИГУРАЦИЙ ВНУТРИ СТУПЕНИ — ВИДЕН НА ВСЕХ ШИРИНАХ (правка
   17.08, РЕШЕНИЕ ВЛАДЕЛЬЦА ПОВЕРХ КАНОНА). До этой правки список `display:none`
   на ≥901 и `flex` на ≤900, по замеру канона (`#calc .cfg button`, v0.145.0):
   у эталона широкая ступень — ОДНА СТРОКА ЦЕНЫ, имя показывалось только на
   мобильном. Владелец, увидев живой стенд, решил иначе: клиент на десктопе
   обязан видеть, ЧТО он выбирает, не только цену — отступление от канона
   ОСОЗНАННОЕ, записано `DEVIATIONS.md`.
   ⚠ Порог 901 (та же точка, что у шахматки героя и карточек формы) остаётся
   для РЯДА (равные доли/nowrap ниже) — он не про видимость имени, его не трогаю.
   🔴 ПРАВИЛО ПРЯТАВШЕЕ СТОЯЛО ПОСЛЕ БАЗОВОГО `.tpl-preset__cfgs { display: flex }`
   НАРОЧНО (при равной специфичности решает порядок) — это тот же приём, только
   теперь без строки `display:none`, прятать нечем и порядок уже не имеет значения. */
@media (min-width: 901px) {
  /* 🔴 РАВНЫЕ ДОЛИ РЯДА (v0.147.0). Замер канона: набор ступеней 630×54,
     `nowrap`, пять детей ПО 121 — то есть доли РАВНЫЕ, а не по содержимому.
     ⚠ Первая попытка (`flex: 1 1 0` в v0.145.0) дала 47×242 и была снята —
     но виновата была не модель, а ряд шириной 268: поле со ступенями занимало
     одну из двух колонок моей же парной сетки. С `tpl-field--wide` ряд стал
     635, и равные доли дают ~123 — канонные 121 плюс наши паддинги.
     📌 Класс c-design, второй случай за день: ряд, выровненный СОДЕРЖИМЫМ, —
     не выровненный ряд. Пока подписи похожей длины, это выглядит работающим. */
  .tpl-presets { flex-wrap: nowrap; }
  .tpl-preset { flex: 1 1 0; min-width: 0; }
  /* ⚠ ДЕЛЕНИЕ РЯДА ПОРОВНУ (`flex: 1 1 0`) ПРОБОВАЛ И СНЯЛ — ЗАМЕР ХУЖЕ.
     Ступени сели в один ряд, но по 47px при высоте 242: подпись переносилась
     по одному слову, и секция стала выше, а не ниже. Причина в том, что делить
     нечего: набор ступеней у нас 268px шириной, а у канона 630 — потому что
     `.ds-calc` у нас `display:block`, и двухколоночной раскладки калькулятора
     НЕТ ВОВСЕ (у канона grid 669/418). Пока колонок нет, любая правка ступеней
     переставляет мебель в комнате не того размера. Предмет назван числами и
     отдан: он в компоненте ядра, не в ширине пилюли. */
}


.tpl-preset__cfg {
  font-size: var(--ds-fs-xs);
  color: var(--ds-text-secondary);
}

/* ПЕРЕЧЕНЬ ФАКТОВ (`text.items[]`, v0.116.0). Не карточки и не «галочки»
   ядра: простой список с брендовым маркером. Маркер — псевдоэлемент, а не
   символ в тексте: символ в значении слота уехал бы литералом (наш класс), и
   его нельзя было бы ни перекрасить, ни выключить. */
.tpl-facts {
  margin: var(--ds-space-4) 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
}

.tpl-facts li {
  position: relative;
  padding-inline-start: var(--ds-space-6);
}

/* Два уровня в пункте перечня — та же форма, что у преимуществ формы
   (`form.aside.perks[]`): имя крупнее, описание мельче и приглушённое.
   Замер канона: 14.5px/800 против 12px/500; у нас ИМЕНОВАННЫЕ ступени
   (`md`/`sm`) — своё число рядом с типошкалой ядра стало бы вторым источником. */
.tpl-facts__title { font-weight: var(--ds-fw-bold); }

.tpl-facts__text {
  display: block;
  font-size: var(--ds-fs-sm);
  color: var(--ds-text-secondary);
}

.tpl-facts li::before {
  content: "";
  position: absolute;
  inset-inline-start: 0;
  top: 0.55em;
  width: 8px;
  height: 2px;
  background: var(--ds-color-primary);
}

/* Читабельная ширина текстовых секций */
.tpl-prose {
  max-width: 44rem;
}

.tpl-prose p {
  margin: 0 0 var(--ds-space-4);
}

/* Узкая обёртка (формы и пр.) — не растягивать на всю ширину контейнера */
.tpl-narrow {
  max-width: 32rem;
}

/* Карточка входа в кабинет (ЛК 1С, наряд ГП 17.08) — только композиция и
   зазоры на токенах (CONTRACT §3: новый визуальный компонент не заводится).
   `.ds-denial`, `.ds-field`, `.ds-input`, `.ds-btn`, `.ds-form` — роли ядра,
   этот блок их не переопределяет, только расставляет. */
.tpl-login {
  margin-inline: auto;
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-3);
}

.tpl-login .ds-form {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-3);
  margin-top: var(--ds-space-2);
}

.tpl-login__ways {
  margin: 0;
  font-size: var(--ds-fs-sm);
  text-align: center;
}

/* Чекбокс согласия 152-ФЗ: только компоновка, стили текста наследуются */
.tpl-consent {
  display: flex;
  gap: var(--ds-space-2);
  align-items: flex-start;
}

.tpl-consent input {
  margin-top: 0.25em;
}

/* Калькулятор: только ритм; типографика итога — семантика strong */
.tpl-calc__total {
  margin: var(--ds-space-6) 0 var(--ds-space-3);
}

.tpl-calc__disclaimer {
  margin: var(--ds-space-3) 0 0;
}

/* Замещающий текст на месте исчезнувшего по `when` поля (санкция 04.08,
   модель forbion-1c «1С:Фреш» п.4 — «Базовый»: доп. база не продаётся,
   счётчик убран, на его месте «по запросу»). Не форм-контрол — hidden
   переключается JS напрямую, без истинного удаления из DOM. */
.tpl-calc-unavailable {
  color: var(--ds-text-muted);
  font-size: 0.9em;
}

/* Раскрывашка «Ставки расчёта» в калькуляторе (не FAQ — FAQ теперь ds-faq
   дизайн-системы): только ритм, без визуальных стилей */
.tpl-disclosure {
  margin: 0 0 var(--ds-space-3);
}

.tpl-disclosure__q {
  cursor: pointer;
}

.tpl-disclosure__a {
  margin: var(--ds-space-2) 0 0;
  padding-left: var(--ds-space-4);
}

/* 🔴 «СТАВКИ РАСЧЁТА» СКРЫТЫ ОТ КЛИЕНТА С JS, ОСТАЮТСЯ БЕЗ JS (v0.242.0;
   наряд владельца Н5 17.08 — у канона узла нет на видимом скриншоте, а
   условие 3 визы v0.4/v0.5 требует, чтобы посетитель БЕЗ JS мог посчитать
   формулу сам). Разметка НЕ меняется — обязательство цело.
   ⚠ Селектор СКОПИРОВАН именно под `.ds-calc__form`, а не голым
   `.tpl-disclosure`: тот же класс несёт аккордеон тарифов (см. комментарий
   ниже) — задень я общий класс, погасил бы чужой узел, у которого другой
   предмет и другой держатель.
   🔴 ТОЛЬКО `.calc-ready`, БЕЗ `.js` (17.08 — живой инцидент, нашла c-design,
   подтвердил c-web-1). Раньше правило требовало ОБА класса: `.js`
   (JS_DETECT_SCRIPT, инлайн-скрипт) доказывал только «браузер выполняет
   скрипты», `.calc-ready` — что калькулятор ДОРАБОТАЛ. `.calc-ready` ставит
   САМ tpl-calc.js последней строкой, ПОСЛЕ успешной инициализации; при
   сетевом обрыве/исключении/блокировщике этот класс не появится, и правило
   не сработает — узел останется видимым, как и без JS. Это по-прежнему так.
   ⚠ НО `.calc-ready` — СТРОГОЕ ПОДМНОЖЕСТВО «`.js` истинен»: он не может
   стать true без работающего JS, а «`.js` истинен» ничего не говорит о
   том, доработал ли калькулятор. Требовать оба класса разом добавляло
   ЛОЖНУЮ зависимость: на живом стенде CSP (`script-src 'self'` без хеша)
   молча заблокировал инлайн-скрипт, `.js` не появлялся НИКОГДА, и правило
   не срабатывало ВООБЩЕ — панель «Ставки расчёта» была раскрыта постоянно,
   хотя `tpl-calc.js` (внешний файл, не инлайн) работал и `.calc-ready`
   стоял. Урок: наличие уже достаточного сигнала — повод убрать избыточный,
   а не оставить его страховкой; избыточное само стало точкой отказа. */
[data-tpl-calc-root].calc-ready .ds-calc__form > details.tpl-disclosure {
  display: none;
}

/* Аккордеон тарифов (наряд integrator-c 02.08, решение владельца: аккордеон
   вместо новой табличной секции — переиспользует .tpl-disclosure выше, здесь
   только ритм между группами и раскладка чипов). Сам <details> — нативный,
   без JS: содержимое присутствует в HTML независимо от open/closed.
   Чип .cfgc (наряд c-seo §K67/K75/K78, форма — из concept): цвет/декорация
   заданы ЯВНО, не через наследование — тот класс дефекта («правило живёт не
   в классе, а в его СИЛЕ над соседним», .ttab td a перекрашивал чип в
   концепте) здесь структурно исключён: у чипа нет table/td-контекста, где
   могло бы стоять конкурирующее по специфичности правило. */
.tpl-tariff-accordion {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-3);
}

.tpl-tariff-chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
}

.cfgc {
  /* inline-flex, а не inline-block: чипу нужна иконка ПЕРЕД текстом (v0.91.0),
     и выравнивать её baseline'ом пришлось бы магическими числами. */
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1);
  /* 🔴 ГЕОМЕТРИЯ ЧИПА СВЕДЕНА К КАНОНУ (v0.209.0; замер c-design @390, чип к
     чипу: канон `padding: 3px 9px`, `line-height: normal`, высота 21 — у нас
     было 4px 12px, lh 17.5, высота 26).
     🔴 ВЕДУЩАЯ ВЕЛИЧИНА — ГОРИЗОНТАЛЬНОЕ ПОЛЕ, И ЭТО УМНОЖЕНИЕ, А НЕ
     СЛОЖЕНИЕ (числа c-ops на пяти ширинах): чип шире ⇒ в ряд входит меньше ⇒
     РЯДОВ ПЕРЕНОСА больше, и каждый стоит полной высоты чипа. Отсюда +186 на
     390 против +30 на 1366, где переносов нет вовсе. Поправь я одну
     вертикаль — высота ряда сошлась бы, а число рядов осталось.
     ⚠ `line-height: normal`, а не число: у канона чип 21 = кегль 12.5 в
     естественном строчном боксе; ступень `--ds-lh-*` в одностроч­ном чипе
     лишняя. */
  padding: 3px 9px;
  border-radius: var(--ds-radius-full);
  /* Зелёная подложка — это ОБЫЧНОЕ состояние чипа (замер канона: #EDF6E3),
     а не выделение; в выделенной строке чип, наоборот, белеет. Прежний
     `--ds-surface-raised` (#ECF2F2) читался как «неактивный контрол». */
  background: var(--ds-surface-highlight);
  color: var(--ds-text-primary);
  font-size: 12.5px;
  font-weight: 600;
  text-decoration: none;
  line-height: normal;
  /* 🔴 СЛОЙ ПЛОЩАДИ НАЖАТИЯ ВОЗВРАЩЁН, И ЭТО НЕ КОЛЕБАНИЕ, А РАЗВЕДЕНИЕ ЕДИНИЦ
     (v0.211.0). Хронология важна, потому что решение зависело от того, ЧТО
     мерили:
       v0.209.0  слой заведён по прецеденту степпера;
       v0.210.0  снят: замер тапа (c-ops) показал, что Chromium уже тянет
                 попадание до середины промежутка между рядами;
       v0.211.0  возвращён: integrator-c2 уточнил, что отменял ОБЯЗАТЕЛЬНОСТЬ,
                 а не запрещал, и что замеры расходятся ПО ЕДИНИЦЕ.
     ⚠ ДВЕ РАЗНЫЕ ВЕЛИЧИНЫ, обе верные: `elementFromPoint` даёт КОРОБКУ
     ПОПАДАНИЯ (её `::before` реально расширяет — мой замер: 44 со слоем против
     22 без), а тап-прилипание делится МЕЖДУ СОСЕДНИМИ рядами и коробкой не
     управляется. Что из этого чувствует палец — решает ТАП на выложенном
     артефакте, и это замер c-ops, не мой.
     ⚠ ЧИСЛА ЭФФЕКТИВНОЙ ЗОНЫ ЗДЕСЬ НЕТ НАМЕРЕННО, И НИКАКОГО НЕ БУДЕТ ДО
     ЗАМЕРА НА КАНОННОЙ ПЛОТНОСТИ. За один час предмет получил ТРИ числа —
     «≈29» (арифметика integrator-c2, смешавшая НАШ зазор 8 с КАНОННОЙ высотой
     21), «≈25» (его же поправка по канонному шагу) и «44» (моя коробка через
     `elementFromPoint`), — и все три оказались О РАЗНОМ. Число объявляется ПО
     ФАКТУ, с именем единицы и того, кто его снял.
     🔴 И САМА ПРИЧИНА СНЯТИЯ СЛОЯ В v0.210.0 ОКАЗАЛАСЬ НЕВЕРНОЙ: c-ops отозвал
     своё «прилипание делит промежуток пополам» — он вывел это из ОДНОГО
     наблюдения и обобщил на случай, которого не мерил (слой у ВСЕХ чипов).
     Замер при слое у всех: чип с двумя соседями 26 → тап 33; чип с одним
     соседом 40 → 48. ⇒ слой пальцу выигрыш ДАЁТ.
     ⚠ ПОБОЧНОЕ СЛЕДСТВИЕ СЛОЯ ЗАВИСИТ ОТ ПЛОТНОСТИ РЯДОВ, А НЕ ОТ СЛОЯ
     (уточнено c-ops на выложенном v152). На ПРЕЖНЕЙ плотности (чип 26, шаг 34)
     граница между рядами шла НЕ по центру: вверх 22 / вниз 10 — палец, целясь
     чуть ниже чипа, попадал в чип выше. На КАНОННОЙ плотности (чип 21, шаг 24)
     это НЕ воспроизводится: у внутренних чипов 11/10, то есть симметрично в
     пределах пикселя; краевой 61/45.
     ⇒ предмет «предсказуемость границы» ЗАКРЫТ замером, а не передан дальше.
     ⚠ Запись оставлена с УСЛОВИЕМ намеренно: без него она уехала бы как
     действующая — «верно о плотности 26/34, неверно о 21/24». Общее же
     остаётся верным: симметрия ПРАВИЛА не даёт симметрии ПОПАДАНИЯ, когда
     слои соседей перекрываются; переменная здесь — ПЛОТНОСТЬ, не слой. */
  position: relative;
}

a.cfgc::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: max(100%, 44px);
  height: 44px;
  transform: translate(-50%, -50%);
}

a.cfgc:hover { background: var(--ds-surface-sunken); }

/* Иконка чипа (v0.91.0). Габариты в `em` — как у ядровых `.ds-badge__icon` /
   `.ds-eyebrow__icon`: иконка обязана масштабироваться с кеглем чипа, а не
   жить в пикселях. `flex-shrink: 0` — чтобы длинное имя конфигурации не
   сплющивало глиф. Класс МОЙ: чип — шаблонный компонент, ядро о нём не знает. */
.cfgc__icon {
  width: 0.9em;
  height: 0.9em;
  flex-shrink: 0;
}

/* Счётчик конфигураций (правка c-design 04.08): факт «сколько внутри»
   обязан читаться и при закрытой группе — живёт В summary, не в
   сворачиваемой части, приглушённым весом относительно цены. */
.tpl-tariff-count {
  font-weight: 400;
  color: var(--ds-text-muted);
}

/* Сноска-дисклеймер под секцией (`note` у `cards`/`text`, v0.89.0). ОДИН класс
   на всех потребителей идиомы — заводить `.tpl-cards-note` и `.tpl-text-note`
   отдельно значило бы через три релиза иметь пять почти одинаковых правил,
   которые разойдутся. Вид — как у `.tpl-compare-foot__note` (вторичный текст,
   ограниченная строка): сноска обязана читаться как сноска, а не как лид.
   Повод не косметический: юр-дистанция «цена ↔ дисклеймер» ±2 секции, и без
   слота её чинили бы перестановкой канонного порядка. */
.tpl-section-note {
  margin: var(--ds-space-4) 0 0;
  font-size: var(--ds-fs-sm);
  color: var(--ds-text-secondary);
  max-width: 44rem;
}

/* `note_kind: "legal"` (v0.256.0, «Шаг 2», наряд c-design 18.08): роль
   `.ds-legal-note` грузится в `assets/ds/index.css` — ПЕРЕД этим файлом
   (`build_page_shell()`: ds → theme → tpl.css) — и при равной специфичности
   (оба класса, 0,1,0) мой `.tpl-section-note` молча перебил бы её font-size/
   color/max-width просто порядком подключения. Компаунд-селектор (0,2,0)
   решает это НАВСЕГДА, а не порядком строк: побеждает при любой перестановке
   файлов.

   🔴 `margin` ДОБАВЛЕН следом (DESIGN v0.373.0, тот же наряд, уточнение
   18.08): роль изначально заявляла только кегль/интерлиньяж/ширину/цвет,
   отступы оставались моими — и это оказалось ПЕРЕЛЁТОМ ЧЕРЕЗ НОЛЬ. Замер
   c-design КОРОБКИ в потоке (не узла): канон `.offer-note.rev` несёт
   отступы 14/9.5 = 23.5, мой `.tpl-section-note` — 16/0 = 16, разница
   7.5px постоянна на всех ширинах. Без токена правка кегля увела бы нас
   из «+14.5 выше канона» в «−7.5 ниже» — величина ЗНАКОВАЯ, «починили
   кегль» ≠ «расхождение упало». Фолбэк — мой прежний `--ds-space-4 0 0`:
   без брендового значения роль отступы не навязывает. */
.tpl-section-note.ds-legal-note {
  margin: var(--ds-legal-note-mar, var(--ds-space-4) 0 0);
  font-size: var(--ds-legal-note-size, var(--ds-fs-sm));
  color: var(--ds-legal-note-ink, var(--ds-text-secondary));
  max-width: var(--ds-legal-note-max, 44rem);
}

/* БРЕНД-ПАТТЕРН декор-слоем секции (`pattern`, v0.87.0). Слой выбран c-design:
   механизм нейтральный здесь, ассет и его правила — в бренд-пакете. Раскладка
   один-в-один с орбами: угол + ступень размера, ничего свободного (свободное
   число потребовало бы inline style, а его запрещает CSP и ловит гейт).

   🔴 ПЛОТНОСТЬ — ССЫЛКА НА ТОКЕН БРЕНДА С РАБОЧИМ ФОЛБЭКОМ, и это не лень.
   `--ds-pattern-opacity-soft/-strong` заводит c-design своим тегом; на момент
   этого релиза их в ДС ЕЩЁ НЕТ (проверил и тег v0.61.0, и её рабочее дерево —
   0 вхождений). `var(--x, 0.06)` в этом случае НЕ немой отказ: применяется
   фолбэк, эффект есть, а как только она затегает токен — её значение победит
   автоматически, без правки шаблона. Поэтому мой же токен-гейт требует
   объявления только для `var()` БЕЗ фолбэка: молча умирает именно такой,
   а не этот. */
.tpl-pattern {
  position: absolute;
  z-index: 0;
  pointer-events: none;
  width: 200px;
  height: auto;
}

.tpl-pattern--sm { width: 140px; }
.tpl-pattern--md { width: 200px; }
.tpl-pattern--lg { width: 300px; }

.tpl-pattern--soft   { opacity: var(--ds-pattern-opacity-soft, 0.06); }
.tpl-pattern--strong { opacity: var(--ds-pattern-opacity-strong, 0.12); }

.tpl-pattern--tl { top: 0; left: 0; }
.tpl-pattern--tr { top: 0; right: 0; }
.tpl-pattern--bl { bottom: 0; left: 0; }
.tpl-pattern--br { bottom: 0; right: 0; }
.tpl-pattern--c  { top: 50%; left: 50%; transform: translate(-50%, -50%); }

/* ОБЩИЙ CTA под рядом карточек (`cards.cta`, v0.86.0). Одно действие на все
   карточки ряда — центрируем под сеткой, отступ ритмом секции. Кнопка своих
   размеров не получает: она ядровая `.ds-btn--lg`, шаблон задаёт только МЕСТО.
   Тот же раздел труда, что у орбов: размещение моё, вид — ядра. */
.tpl-cards-cta {
  display: flex;
  justify-content: center;
  margin-top: var(--ds-space-6);
}

/* ТАБЛИЧНАЯ ФОРМА тарифного блока (form:"table", наряд integrator-c2 11.08,
   решение владельца «таблица, как в прототипе»; CALC-MODEL.md §4-бис).

   🔴 ЗДЕСЬ Я СНОШУ СВОЮ ЖЕ СТРУКТУРНУЮ ЗАЩИТУ, И ЭТО НАДО ЧИТАТЬ ВМЕСТЕ
   С КОММЕНТАРИЕМ ВЫШЕ. Там сказано: дефект «.ttab td a перекрашивал чип»
   (кегль 12.5→13.5, вес 600→800, цвет чернильный→зелёный, ширина 139→151 —
   замер c-design в концепте) исключён СТРУКТУРНО, потому что у чипа нет
   table/td-контекста. Таблица этот контекст ВОЗВРАЩАЕТ — значит структурной
   защиты больше нет, и на её место обязана встать проверяемая:
     1. я НЕ пишу ни одного правила вида `.ttab td a` / `.ttab a` — по
        построению нет конкурента, который перебил бы `.cfgc` по специфичности
        (у `.cfgc` (0,1,0), у `.ttab td a` было бы (0,1,2) и оно бы выиграло);
     2. `.cfgc` выше задаёт цвет, кегль, вес и декорацию ЯВНО, а не
        наследованием — то есть даже унаследованные от таблицы значения до
        чипа не доходят;
     3. проверка — НЕ рассуждением: тест сверяет обе формы на СМЕШАННОМ
        наборе чипов (часть с href, часть без), а равенство computed
        снимается рендером. Структурный довод здесь недостаточен by design:
        он и в прошлый раз звучал убедительно.
   Тот, кто соберётся добавить сюда `.ttab td a { … }`, — прочти пункт 1
   и сначала объясни, почему чип это переживёт. */
.tpl-ttab-wrap {
  /* Три колонки на 360px не помещаются. Прокрутка обязана жить ЗДЕСЬ, иначе
     таблица роняет горизонтальный скролл ВСЕЙ страницы — дефект, невидимый
     для boundingBox секции (переполнение вытекает за коробку, коробка в норме). */
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

.ttab {
  width: 100%;
  border-collapse: collapse;
  text-align: left;
}

/* 🔴 ТИПОГРАФИКА ТАБЛИЦЫ ТАРИФОВ — ПЯТЬ ЧИТАТЕЛЕЙ К ПЯТИ ТОКЕНАМ (v0.218.0;
   `--ds-ttab-*`, DESIGN v0.290.0). Значения ЖИЛИ НА ЖИВОМ И НЕ ЧИТАЛИСЬ НИКЕМ:
   у величины был автор, но не было читателя — зеркало нашей же формы «носитель
   без роли». Своих чисел здесь нет ни одного, только имена.
   🔴 ЗАМЕР ПО ШАГАМ, И ОН ПОКАЗЫВАЕТ ЛОВУШКУ ПРИЁМКИ ПО ИТОГУ (канон 321,
   ряды 61/69/48/48/48):
       шаг 0 как есть        330 · 71/67/49/49/49
       1 lh: normal          321 · 62/67/49/49/49   ← ИТОГ РОВНО КАНОННЫЙ ПРИ ЧЕТЫРЁХ НЕВЕРНЫХ РЯДАХ
       2 + кегль 21          330 · 68/67/50/50/50
       3 + единица 11        330 · 68/67/50/50/50   (внутри строки — высоту не двигает)
       4 + метка 9.5         327 · 65/67/50/50/50
       5 + поля ячейки 11    317 · 63/67/48/48/48
   ⇒ приняв «321» на первом шаге, мы закрыли бы предмет с четырьмя неверными
   рядами. Приёмка идёт ПО РЯДАМ, и это правило integrator-c2, проверенное
   здесь на числах.
   ⚠ ВКЛАД СВОЙСТВА ЗАВИСИТ ОТ СТОПКИ: тот же `lh` до кегля даёт −9, после — +9
   к первому ряду. Поэтому каждое число выше названо ВМЕСТЕ с шагом, на котором
   снято, а не само по себе.
   📌 Итог: ряды 3·4·5 сошлись ТОЧНО (48 = 48), первый +2, второй −2, таблица
   317 против канонных 321 — мы теперь НИЖЕ на 4, и это остаток разной природы,
   а не общий перевес. */
.ttab tbody th {
  font-size: var(--ds-ttab-price-size, inherit);
  line-height: var(--ds-ttab-price-lh, var(--ds-lh-normal, 1.5));
}

.ttab tbody th small { font-size: var(--ds-ttab-unit-size, 1em); }

.ttab .rmark { font-size: var(--ds-ttab-rmark-size, 12px); }

.ttab th,
.ttab td {
  padding: var(--ds-ttab-cell-pad, var(--ds-space-3));
  border-bottom: 1px solid var(--ds-border-default);
  vertical-align: top;
}

.ttab thead th {
  font-size: 13px;
  font-weight: 600;
  color: var(--ds-text-muted);
  white-space: nowrap;
}

.ttab tbody th[scope="row"] {
  font-weight: 700;
  color: var(--ds-text-primary);
  white-space: nowrap;
}

.ttab small {
  font-size: 12px;
  font-weight: 400;
  color: var(--ds-text-muted);
}

/* 🔴 ВЫДЕЛЕННАЯ СТУПЕНЬ — ЗЕРКАЛО, А НЕ ЗАЛИВКА (v0.133.0; замер канона +
   встречный замер c-design). Прежняя редакция красила СТРОКУ в
   `--ds-surface-raised` (#ECF2F2, поверхность неактивного контрола) — то есть
   изобретала третье поведение. Канон:
     выделенная строка: ЯЧЕЙКИ #EDF6E3, чипы внутри БЕЛЫЕ
     обычные строки   : ячейки прозрачны, чипы #EDF6E3
   Выделение у канона — ОТНОШЕНИЕ между соседями, а не значение цвета: тот же
   зелёный носят и выделенные ячейки, и обычные чипы, а разводит их инверсия.
   ⚠ Фон — на ЯЧЕЙКАХ, не на `<tr>`: у строки фон рисуется под ячейками и
   пропадает, как только у ячейки есть свой. Мой прежний замер `tr.hi` дал
   `rgba(0,0,0,0)` и выглядел как «канон ничего не красит» — я мерил элемент,
   который в таблице фон и не несёт. */
/* 🔴 ЗАЛИВКУ ЯЧЕЕК ОТДАЛ ЯДРУ (v0.134.0): `.ds-row--picked > th/td` появился в
   `DESIGN v0.171.0`, и держать своё второе определение того же предмета —
   будущее расхождение, которого никто не заметит в момент появления.
   Здесь остаётся ровно то, чего правило ядра ДОСТАТЬ НЕ МОЖЕТ: побеление чипа.
   Ядро целит `.ds-badge`, а чип конфигурации — `.cfgc`, шаблонный класс (и у
   канона он тоже свой, `.cfgc`, а не бейдж). Одно правило вместо двух. */
.ttab tr.hi .cfgc {
  background: var(--ds-surface-picked-chip);
}

/* Метка ступени. Цвет — `--ds-color-accent` (тем же токеном красит акцентный
   текст ядро: badge/mark). ⚠ Изобретённый токен здесь стоил бы молча: неизвестная
   var делает объявление НЕВАЛИДНЫМ, свойство просто не применяется — ни ошибки,
   ни красного гейта. Поймал на себе в этой же правке: писал `--ds-text-accent`
   и `--ds-border-muted`, в ДС нет ни того, ни другого. */
.rmark {
  display: block;
  margin-top: var(--ds-space-1);
  font-size: 12px;
  font-weight: 600;
  color: var(--ds-color-accent);
  white-space: nowrap;
}

/* Ячейка состава: те же flex-правила, что у аккордеонной .tpl-tariff-chips —
   раскладка чипов одна на обе формы, различается только обёртка строки. */
.cfgcell {
  /* 🔴 ЯЧЕЙКА ОСТАЁТСЯ ЯЧЕЙКОЙ — `display: flex` СНЯТ (v0.211.0; ведущий
     носитель по замеру c-ops, −111 из остатка).
     Я делал раскладку чипов флексом «как у аккордеонной полосы» — и тем самым
     ЗАМЕНИЛ табличный поток на флексовый: у флекса каждый ряд получает свой
     зазор 8 и полную высоту чипа, у строчного потока переносы стоят дешевле.
     ⚠ Своего `display` тут не пишу вовсе: у `<td>` он `table-cell` ПО
     УМОЛЧАНИЮ, и объявить его значило бы завести второго автора тому, что и
     так верно. Снятие — не «поставить другое значение», а убрать лишнее.
     ЗАМЕР (моя сборка против канона): @390 508 → 480 при канонных 480 ·
     @768 421 → 408 при 409 · @899/901/1366 332 → 330 при 321.
     ⚠ Зазор между чипами теперь даёт СТРОЧНЫЙ ПОТОК (пробелы разметки), как у
     канона; `gap` в табличной ячейке не действует и потому снят тоже. */
  min-width: 22ch;
}

/* ⚠ Поля ячейки состава по канону 9px 12px (замер c-design). Доля МАЛАЯ — на
   1366 без переносов это +3 на ячейку, — и правится вместе с чипом, а не
   вместо: ведущая величина здесь ширина чипа.
   🔴 СЕЛЕКТОР С ЭЛЕМЕНТОМ, И ЭТО ЗАМЕР, А НЕ ВКУС: рядом живёт `.ttab td`
   (0,1,1), и правило на голом `.cfgcell` (0,1,0) проиграло бы ему по
   СПЕЦИФИЧНОСТИ — оказалось бы мёртвым при зелёной сборке. */
.ttab td.cfgcell { padding: 9px 12px; }

/* Карусель героя (hero.slides[]): контейнер/слайды/точки/стрелки и их состояние
   .is-active — теперь ВЛАДЕЕТ ЯДРО ДС (components/carousel/carousel.css, DESIGN
   v0.19.0). Здесь остаётся только раскладка панели внутри слайда (зона шаблона):
   центрируем ds-компонент (купон/аудит). Две площадки, один хелпер:
   1) слайд БЕЗ copy — панель центрируется на всю площадь слайда (как было);
   2) слайд С copy (v0.40.0) — обёртка внутри .ds-hero__media центрирует панель
      в ПРАВОЙ КОЛОНКЕ, паритет с эталоном (.promo-visual{justify-content:center},
      сверка c-design-2). Центруем именно обёрткой, а не флексом на самом
      .ds-hero__media: тот глобально сломал бы .ds-media-stack{margin-left:auto}
      слайда-1 — чат-сцена уехала бы от правого края. */
/* ПРЕИМУЩЕСТВА У ФОРМЫ (`form.aside.perks[]`, v0.108.0; 4 значка 19×19 по
   разбору c-design). Список ШАБЛОННЫЙ: у ядрового `.ds-card__checks` каждый
   пункт уже несёт галочку `::before`, и значок стал бы ВТОРОЙ меткой строки —
   здесь метка одна. Карточка формы тёмная, поэтому цвет берём от неё
   (currentColor), своего не объявляем. */
.tpl-perks {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
  /* Канонные кегль и интерлиньяж пункта (`--ds-plate-item-*`, DESIGN v0.215.0):
     15.5/1.45 против наших 16/1.2. ⚠ Наш пункт был НИЖЕ канонного (38 против
     45) — «список длинный» было бы неверным диагнозом: он короче, а плита выше
     по другим причинам. Фолбэк = прежние значения (соседи не двигаются). */
  font-size: var(--ds-plate-item-size, inherit);
  /* 🔴 ФОЛБЭК = ПРЕЖНЕЕ ЗНАЧЕНИЕ, А НЕ «НЕЙТРАЛЬНОЕ». Первая редакция ставила
     здесь `inherit`, а у `.tpl-perk` снимала `--ds-lh-tight` — и для брендов
     БЕЗ токена это меняло интерлиньяж с 1.2 на унаследованный 1.5: замер
     izibuh показал секцию заявки короче на 16 px. «Нейтральное умолчание» и
     «прежний рендер» — разные вещи, и второе здесь обязательно. */
  line-height: var(--ds-plate-item-lh, var(--ds-lh-tight));
}

.tpl-perk {
  display: flex;
  align-items: flex-start;
  gap: var(--ds-space-2);
  /* 🔴 ИНТЕРЛИНЬЯЖ ПУНКТА — КАНОННЫЙ И ПРИХОДИТ СВЕРХУ (v0.170.0). Свой
     `--ds-lh-tight` (1.2) стоял здесь и ПЕРЕБИВАЛ ролевой `--ds-plate-item-lh`
     (1.45), объявленный на списке: замер показал пункт 38 при канонных 45 —
     то есть роль доезжала до `ul`, а рисовал `li`. Четвёртая форма семейства
     «значение + носитель + связь», ровно та, что я нашёл у ядра утром, и снова
     у себя. Наследование оставляем: один источник на величину. */
  line-height: inherit;
}

/* ДВА ТИПОГРАФИЧЕСКИХ УРОВНЯ В ПУНКТЕ (замер канона: имя 15.5/600, описание
   14/500 приглушённое). Кегли — ИМЕНОВАННЫЕ ступени шкалы (`md` 16 и `sm` 14),
   а не канонные 15.5: своё число здесь стало бы вторым источником рядом с
   типошкалой ядра. Приглушение — прозрачностью, а не цветом: карточка формы
   тёмная, и токен светлого текста дал бы на ней обратный эффект. */
.tpl-perk__body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

.tpl-perk__title {
  font-size: var(--ds-fs-md);
  font-weight: var(--ds-fw-semibold);
}

.tpl-perk__text {
  font-size: var(--ds-fs-sm);
  font-weight: var(--ds-fw-medium);
  opacity: 0.78;
}

.tpl-perk__icon { flex: none; width: 19px; height: 19px; opacity: 0.9; }

/* Ссылка в пункте списка панели (`audit.items[].href`, v0.112.0). Цвет —
   от карточки (currentColor), подчёркивание появляется на наведении: пункт
   остаётся пунктом списка, а не превращается в ряд синих ссылок. */
.tpl-check__link { color: inherit; text-decoration: none; }
.tpl-check__link:hover { text-decoration: underline; }

/* ПРОМО-ПАНЕЛЬ СЛАЙДА — ТРИ КАРТОЧКИ (`panel: "promo"`, v0.111.0; решение
   владельца, форма — замер канона c-design: значок 58×58, величина 23px/900,
   заголовок 13.5px/700, текст 12px/500).
   Компонента в ядре ДС нет (проверено на пине v0.138.0: badge…stepper, промо-
   карточки среди них нет) — поэтому классы шаблонные и композиция на токенах.
   🔴 Кегли — ИМЕНОВАННЫЕ ступени шкалы (`xl` 22 · `sm` 14 · `xs` 12), а не
   канонные 23/13.5/12: своё число рядом с типошкалой ядра стало бы вторым
   источником на один предмет. Габарит значка (58) — геометрия, а не типографика,
   его беру как есть.
   Ряд переносится: в узкой колонке слайда три карточки по 355 не встают, и
   перенос честнее сжатия — текст остаётся читаемым. */
.tpl-pcards {
  /* 🔴 `width: 100%` — не косметика. Обёртка панели центрирует содержимое
     гридом (`place-items: center`), поэтому ряд получал ШИРИНУ ПО СОДЕРЖИМОМУ
     (замер: 759px при доступных 1200) и три карточки переносились в два ряда —
     расхождение с каноном, где они в одну строку.
     🔴 15.08, НАРЯД «СДЕЛАТЬ ГРИДОМ» НЕ БЕРУ, И ЭТО ЗАМЕР, А НЕ СПОР. Основание
     наряда: у канона `.promo-grid` — grid, колонки 354.66 ×3, дети 355×141
     равные; у нас флекс и высоты 185 · 203 · 203 ⇒ «правка одной строкой».
     Собрал на ЖИВОМ конфиге: носитель `.tpl-pcards` шириной **494** — он лежит
     ВНУТРИ панели героя, а канонный ряд идёт во всю ширину секции (≈1088).
     При 494 три колонки дают карточки **154×314** вместо канонных 355×141.
     ⇒ у нас это не ряд, а СТОЛБИК: карточки 355 не помещаются по три и
     переносятся, поэтому «185 · 203 · 203» — высоты трёх СТРОК, а не ряда.
     Равновысокость нельзя дать ряду, которого нет: предмет — ГДЕ живёт
     носитель, а это структура, а не строка CSS. Числа отданы наверх. */
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-4);
  justify-content: center;
}

/* Слайд «текст сверху, панель под ним» (`copy_place: "top"`, v0.172.0).
   Копи-блок получает ширину СОДЕРЖИМОГО контейнера — тем же выражением из двух
   объявленных токенов, что и ряд плиток: два места, одна величина, один
   источник. Вертикальный зазор — шаг шкалы, своего числа здесь нет.
   ⚠ ВЫРАВНИВАНИЕ ТЕКСТА НЕ ЗАДАЮ: у канона я его не мерил, а поставить «по
   центру, как красивее» значило бы завести вид без замера. Наследуется левое,
   как во всех прочих слайдах; если у канона иначе — это строка c-design. */
.tpl-slide-stack {
  display: grid;
  gap: var(--ds-space-8);
  padding-inline: var(--ds-container-pad);
}

.tpl-slide-stack > * {
  max-width: calc(var(--ds-container-max) - 2 * var(--ds-container-pad));
  margin-inline: auto;
  width: 100%;
}

/* Панель внутри стопки поле уже получила от неё — снимаем её собственное,
   иначе гаттер удвоится (тот же случай, что у `--bare` внутри `.ds-hero__inner`). */
.tpl-slide-stack > .tpl-carousel__panel--bare { padding-inline: 0; }

/* 🔴 РЯД ТРЁХ ПЛИТОК — ТОЛЬКО ТАМ, ГДЕ У НЕГО ЕСТЬ КАНОННАЯ ШИРИНА (v0.171.0).
   Путь канона (замер c-design, тег `concept-v0.30.20-draft`, 1366):
     .promo-grid ← .wrap (1088) ← .slide.slide-promo (во всю ширину вьюпорта)
     ряд 1088 · колонки 354.66 ×3 · зазор 12 · дети 355×141
   ⇒ ширину даёт КОНТЕЙНЕР слайда, а панели, аналогичной нашей, у канона там нет.
   ⚠ ПОЭТОМУ ГРИД — НЕ БЕЗУСЛОВНЫЙ. У нас промо-слайд несёт копи-колонку, и
   панель получает 494: замер показал, что три колонки там дают карточки
   **154×314** вместо канонных 355×141 — то есть грид не выравнивает, а сжимает.
   Правило вешаю на `--bare` (слайд БЕЗ копи-колонки) — единственное место, где
   у ряда есть ширина контейнера. Пока конфиг держит `copy` у промо-слайда, ряд
   остаётся столбиком, и это НЕ дефект раскладки, а состав: убрать копи-колонку
   — решение `c-web-1`, у меня к нему всё готово.
   ⚠ 355 явно не задаю: у канона `.promo-grid` — роль, её второй экземпляр в
   `#srv` даёт те же 1088 при ЧЕТЫРЁХ колонках. Число колонок следует из состава,
   ширина ряда — из контейнера. Зазор канонный: 12 (`--ds-space-3`). */
.tpl-carousel__panel--bare .tpl-pcards {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--ds-space-3);
}

/* 🔴 ШИРИНУ РЯДУ ДАЁТ КОНТЕЙНЕР — И ЕГО ПРИШЛОСЬ ЗАВЕСТИ ЯВНО. У канона ряд
   лежит в `.wrap` шириной 1088 внутри полноширинного слайда; у нас голая панель
   слайда — 1314 (вьюпорт минус гаттер), своего контейнера в ней нет.
   ⚠ ОПЫТ, который это и показал: сняв потолок карточки (355), я получил колонки
   **430** вместо канонных 354.66 — то есть прежние «355» выходили не из
   контейнера, а из МОЕГО ЖЕ ПОТОЛКА, случайно совпавшего с каноном. Число,
   которое сходится по случайности, — не сошедшееся число.
   ⇒ ряду даётся ширина СОДЕРЖИМОГО контейнера, посчитанная из двух объявленных
   токенов, а не литералом: контейнер border-box, значит content = max − 2·pad.
   Потолок карточки в этой ветке снимаем: иначе он снова подменит источник. */
.tpl-carousel__panel--bare .tpl-pcards {
  max-width: calc(var(--ds-container-max) - 2 * var(--ds-container-pad));
  margin-inline: auto;
}

.tpl-carousel__panel--bare .tpl-pcard { max-width: none; }

.tpl-pcard {
  /* Глобального border-box в ядре нет (решение c-design отложено), поэтому
     объявляю локально: иначе паддинг и рамка прибавляются к max-width, и
     карточка выходит шире, чем сказано. */
  box-sizing: border-box;
  flex: 1 1 260px;
  max-width: 355px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--ds-space-1);
  padding: var(--ds-space-6);
  border-radius: var(--ds-radius-lg);
  background: var(--ds-surface-raised);
  border: 1px solid var(--ds-border-default);
}

.tpl-pcard__icon { width: 58px; height: 58px; color: var(--ds-color-primary); }
.tpl-pcard__icon svg { width: 100%; height: 100%; }

.tpl-pcard__value {
  font-size: var(--ds-fs-xl);
  font-weight: var(--ds-fw-black);
  line-height: var(--ds-lh-tight);
}

.tpl-pcard__title {
  font-size: var(--ds-fs-sm);
  font-weight: var(--ds-fw-bold);
}

.tpl-pcard__text {
  margin: 0;
  font-size: var(--ds-fs-xs);
  font-weight: var(--ds-fw-medium);
  color: var(--ds-text-secondary);
}

/* 🔴 СЕТКА «ПОЧЕМУ МЫ» (`panel: "why"`, v0.238.0; наряд владельца 17.08 через
   c-design — DESIGN v0.340.0 несёт карточку `.ds-card--why`, сетка ВОКРУГ неё
   моя). Числа — замер канона (`.power-grid`/`.wcard`, concept-latest.html):
   база (≥901) grid 2×1fr, gap 14, margin-top 32, max-width 760 · 761…900 grid
   1×1fr, gap 12, margin-top 18 · ≤760 ГОРИЗОНТАЛЬНАЯ ЛЕНТА со scroll-snap
   (карточки не переносятся, а прокручиваются) — та же механика, что канон
   держит и у `.promo-grid`, найдена ПРЯМЫМ чтением канона (наряд называл
   только «колонка (flex)» для узкой ширины — проверка канона дала больше:
   там не простая колонка, а лента; см. письмо c-design). `.tpl-pcards`
   (`panel: "promo"`) этой ленты СЕГОДНЯ не несёт — известный, отдельно
   объявленный гэп, не в этом наряде. */
.tpl-why-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 14px;
  margin-top: 32px;
  max-width: 760px;
}

@media (max-width: 900px) {
  .tpl-why-grid {
    grid-template-columns: 1fr;
    gap: 12px;
    margin-top: 18px;
  }
}

@media (max-width: 760px) {
  .tpl-why-grid {
    display: flex;
    flex-wrap: nowrap;
    gap: 10px;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    -webkit-overflow-scrolling: touch;
    scroll-snap-type: x mandatory;
    scroll-padding-left: 2px;
    padding-bottom: 6px;
    scrollbar-width: none;
  }
  .tpl-why-grid::-webkit-scrollbar { display: none; }
  .tpl-why-grid > * {
    flex: 0 0 78%;
    min-width: 78%;
    scroll-snap-align: start;
  }
  .tpl-why-grid > *:last-child { margin-right: 16px; }
}

/* ИЛЛЮСТРАЦИЯ КАРТОЧКИ (`cards.items[].illustration`, v0.114.0; спецификация —
   замер канона c-design). Абсолют в правом нижнем углу, НЕ заменяет значок:
   значок остаётся в потоке у заголовка, это два разных слота.
   🔴 Размер — свойство РАСКЛАДКИ, не ассета: один файл идёт 168×134 у больших
   карточек и 103×83 у обычных. Пока варианта «большая карточка» в схеме НЕТ
   (сетка 1→2→3 равными), поэтому все получают ОБЫЧНЫЙ габарит — это названное
   отступление, а не молчаливое: появится вариант — размер задаст одна строка
   через `--tpl-illu-w`.
   `currentColor` внутри ассета работает только у инлайна, здесь <img> — цвет
   несёт сам файл (обводка), это осознанно: инлайн 200×160 в каждой карточке
   раздул бы HTML, а тема иллюстрации не касается. */
/* Контекст позиционирования для НАКЛАДОК карточки (иллюстрация и/или паттерн).
   Класс один на оба случая: два класса с одинаковыми объявлениями разъехались
   бы при первой правке, а `position` на ядровом `.ds-card` трогать нельзя. */
.tpl-card--decor {
  position: relative;
  overflow: hidden;
}

/* Паттерн в углу карточки: место и плотность из канона, ступень `card` ядра
   (0.10). `data-ds-decor` обязателен — иначе секционное правило свечения
   поднимет декор в поток и над контентом (см. историю v0.99.0). */
.tpl-card__pattern {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 148px;
  height: auto;
  opacity: var(--ds-pattern-opacity-card);
  pointer-events: none;
  z-index: 0;
}

.tpl-card__illu {
  position: absolute;
  right: var(--ds-space-4);
  bottom: var(--ds-space-4);
  width: var(--tpl-illu-w, 103px);
  height: auto;
  pointer-events: none;
  opacity: 0.9;
}

/* 🔴 РЕЗЕРВ ПОД ИЛЛЮСТРАЦИЮ ДЕРЖИТ БРЕНД — ОКОНЧАТЕЛЬНО (v0.234.0).
   За вечер он трижды менял держателя, и дважды предмет оставался БЕЗ него:
       v0.230.0 мой calc → v0.231.0 снял (наряд «доводит c-design»)
       → c-design снял своё в DESIGN v0.328.0 (дыра: заезд 97 @390, 162 @1366)
       → v0.233.0 вернул мой → DESIGN v0.332.0/v0.334.0 вернул свой ПО РОЛЯМ
   ⚠ Если оставить моё, оно ПОБЕДИТ его молча: (0,2,0) против (0,1,0), и три
   его числа станут мёртвыми — тот самый способ, который я сам и назвал.
   ⇒ снимаю СВОЁ И ПИНЮ ТОТ ЖЕ ТЕГОМ ПАКЕТ, ГДЕ ЕГО ОБЪЯВЛЕНИЯ ЖИВЫ (v0.334.0):
   снятие и приход держателя не должны быть двумя разными подачами — именно так
   и открылась дыра. Гейт ниже проверяет ПИН, а не переписку.
   📌 И числа у него теперь по РОЛЯМ, чего у меня не было вовсе: обычная 112,
   крупная 94 ниже 901 и 186 с 901 — наша картинка 103 против канонных 87 на
   узком, поэтому канонные 78 дали бы заезд. Его форма точнее моей. */
/* ЧИПЫ ПЕРВОГО ЭКРАНА (`hero.chips[]`, v0.107.0; строка A3 сверки).
   Компонента чип-строки в ядре ДС нет (проверено на пине v0.131.0), поэтому
   классы шаблонные, композиция — на токенах. Значок наследует currentColor,
   размер — доля кегля строки: значок и текст обязаны расти вместе, иначе на
   узких строка «поедет» относительно иконки.
   ⚠ Это НЕ плитка `ds-stat`: у чипа одна строка и значок, у плитки — значение
   с подписью. Разные элементы, разные ключи, и смешение уже случалось. */
.tpl-hero__chips {
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
  align-items: flex-start;
}

.tpl-hero__chip {
  display: flex;
  align-items: center;
  gap: var(--ds-space-2);
  font-size: var(--ds-fs-sm);
  line-height: var(--ds-lh-tight);
  color: var(--ds-text-secondary);
}

/* Одиночный чип у карточки цены (`hero.aside_chip`): та же форма, другая роль —
   условие акции, а не свойство тарифа. Прижат к панели, поэтому центрируется
   вместе с ней и не тянет левый край колонки.
   ⚠ Блок был записан ДВАЖДЫ подряд, слово в слово (снято в v0.129.0): второй
   экземпляр ничего не менял и потому не был виден ни в одном замере. */
.tpl-hero__chips--aside {
  align-items: center;
  margin-top: var(--ds-space-3);
}

.tpl-hero__chip-icon {
  flex: none;
  width: 1.15em;
  height: 1.15em;
  color: var(--ds-color-primary);
}

/* Значок телефона в полоске — из набора ДС (v0.107.0), поэтому размер задаём
   мы, а рисунок принадлежит ядру. Прежний запечённый SVG был 15×15. */
.tpl-topbar__tel-icon { width: 15px; height: 15px; flex: none; }
.tpl-topbar__lk-icon  { width: 14px; height: 14px; flex: none;
  margin-inline-end: var(--ds-space-1); }

/* ИДЕНТИФИКАЦИЯ ЮРЛИЦА В ПОДВАЛЕ (`footer.legal_entity[]`, v0.106.0).
   `<address>` — элемент контактных данных владельца документа, а не украшение;
   курсив браузера снимаем: вид не должен нести смысл «цитата». Компонента в
   ядре ДС нет, поэтому композиция на токенах и класс шаблонный. */
/* 🔴 ПУНКТ-ФАКТ В КОЛОНКЕ ПОДВАЛА (`footer.columns[].items[]` без `href`,
   v0.163.0). Реестровая запись — четыре ФАКТА, и адреса у них нет ни в одном
   нашем источнике; сочинять URL в юр-блоке нельзя.
   ⚠ Отдельный класс, а НЕ `ds-footer__link`: тот несёт цвет ссылки и ховер, то
   есть ОБЕЩАНИЕ ПЕРЕХОДА. Обещание без перехода — та же «ссылка в никуда»,
   только выраженная видом, а не адресом.
   Цвет берём тем же токеном роли, что и ссылки: факт стоит в одном столбце с
   ними и не должен читаться как другой сорт текста; отличать его должно
   ОТСУТСТВИЕ реакции, а не другой оттенок. */
.tpl-footer__fact {
  color: var(--ds-footer-link, var(--ds-text-muted));
  font-size: var(--ds-fs-sm);
  cursor: default;
  text-decoration: none;
}

.tpl-footer__entity {
  font-style: normal;
  margin-top: var(--ds-space-4);
  /* Цвет НЕ задаём: подвал уже красит своё содержимое, а свой цвет здесь стал бы
     вторым источником на один предмет — сменят тему подвала, блок отстанет. */
}

.tpl-footer__entity-line {
  margin: 0 0 var(--ds-space-1);
  font-size: var(--ds-fs-sm);
  line-height: var(--ds-lh-base);
  opacity: 0.85;
}

/* ТИХАЯ СТРОКА НАД ЗАГОЛОВКОМ (`pretitle`; канонный `.h2a`, 11 мест).
   🔴 СНАРУЖИ h1/h2 (v0.105.0, вердикт c-seo): заголовок несёт РОВНО ОДНО
   утверждение, кикеры и тихие строки в его текст не входят. Внутри заголовка
   (как было в v0.103.0) accessible name читался склейкой — довод доступности,
   не выдачи.
   🔴 РАЗМЕР — ИМЕНОВАННАЯ СТУПЕНЬ, а не доля кегля. Снаружи `em` от заголовка
   недоступен, а у героя кегль РЕСПОНСИВНЫЙ (3xl / 4xl ≥768 / 2xl ≤479):
   повторить эту лестницу у себя значило бы завести ВТОРОЙ источник на один
   предмет — ровно то, чего мы избегаем весь день. Ступени подобраны по замеру
   канонной доли: секции 0.64 × 28 = 17.92 → `lg` (18); герой 0.64 × 36 = 23.04
   → `xl` (22).
   ⚠ Следствие названо: на ≥768 заголовок героя растёт до 48px, а тихая строка
   остаётся 22 — канонная доля дала бы 30.7. Нужна третья ступень — её заводит
   c-design, механизм подхватит без правок шаблона. */
.tpl-pretitle {
  /* 🔴 ЗАЗОР ДО ЗАГОЛОВКА СНЯТ (v0.217.0; наряд integrator-c2, ход «б»).
     У канона тихая строка живёт ВНУТРИ `h2` (`span.h2a`), то есть зазора между
     ними нет вовсе. Внутрь узел не переносим — это ЗАКРЫТОЕ решение v0.103.0 по
     ДОСТУПНОСТИ (2.4.6): внутри `h2` accessible name склеивался в одну строку
     без паузы, а навигация по заголовкам давала склейку; `c-seo` тогда сам
     сузил свой довод о выдаче. Виза на возврат внутрь ЗАПРОШЕНА И НЕ ДАНА:
     «канон диктует ВИД, а не ПОВЕДЕНИЕ — у канонного span вопроса об
     accessible name не стояло вовсе».
     📌 ЦЕНА ОТЛИЧИЯ НАЗВАНА ЧИСЛОМ И ПЕРЕМЕРЕНА (v0.103.0 давала ≈16px/узел —
     число протухло; свежий замер `c-design` 17.08 ≈3px/узел, ≈33px на 11
     узлах). Зазор снят здесь же (`margin:0`); остаток — ЛИШНИЙ СТРОЧНЫЙ БОКС
     отдельного узла, он остаётся по решению о доступности. Отклонение
     записано числом в `DEVIATIONS.md` (f7c_hq) — решение c-tpl + c-seo,
     v0.103.0, подтверждено ГП 17.08 (П631: наряд на перенос внутрь `h2`
     отменён, потому что не видел этого решения при выдаче). */
  margin: 0;
  font-size: var(--ds-fs-lg);
  font-weight: var(--ds-fw-medium);
  line-height: var(--ds-lh-tight);
  color: var(--ds-text-secondary);
}

.tpl-pretitle--hero { font-size: var(--ds-fs-xl); }

/* 🔴 ПОРОГ 1250 НА ЧИПАХ СНЯТ (v0.129.0) — ОН БЫЛ ИЗМЕРЕН НА ДРУГОМ ОБЪЕКТЕ.
   v0.121.0 прятал `.tpl-hero__chips` ниже 1250 со ссылкой на замер канона:
   «0 чипов на 1024/1100/1240, ШЕСТЬ — с 1280». Число «шесть» и было уликой —
   чипов у нас ЧЕТЫРЕ, и оно лежало в моём же комментарии. Прямой замер канона
   (`concept-v0.30.20-draft`, 15.08, вьюпорты 768…1440) развёл два элемента:
     .fchip  ШЕСТЬ пилюль-статусов, ПРАВАЯ колонка (Бэкап · Менеджер · ФНС ·
             Обновление · SQL · ИТС) — display:none ниже 1280   ← порог ИХ
     .trust  ЧЕТЫРЕ строки со значками, ЛЕВАЯ колонка (ИТС Техно в тарифе ·
             SQL входит · выделенный менеджер · …) — display:flex на ВСЕХ
             ширинах 768…1440, не скрыты нигде                  ← это наши чипы
   Три текста из четырёх совпадают ДОСЛОВНО: предмет опознан по содержимому, а
   не по имени. Шести пилюль у нас нет вовсе (объявленное отступление №3) —
   порог остался без своего объекта и снят.
   🔴 И СВОЙ УЩЕРБ НАЗЫВАЮ. Правило било по БАЗОВОМУ классу, а его несёт ещё и
   одиночный чип у карточки цены (`hero.aside_chip`, модификатор `--aside`).
   Замер живого 15.08: «+15 дней в подарок и F7: диск 20 ГБ, почта, CRM» —
   display:none на 1250 · 1100 · 1024 · 900 · 760 · 390. Обещание владельца не
   доезжало НИ ДО ОДНОГО телефона, и об этом никто не сообщил: дифф снимали на
   1440 и 1024, где оно видно, и заметили лишь МЕСТО строки.
   ⚠ Следующему: порог, написанный для роли-списка, съедает роль-одиночку через
   общее имя. Скрывать можно только МОДИФИКАТОР роли, не базовый класс —
   это стережёт тест «ни одно правило не гасит базовый .tpl-hero__chips». */

/* СТРОКА ЦЕНЫ ДЛЯ УЗКОГО ЭКРАНА (`hero.price_line`, v0.120.0).
   На широком цену держит карточка рядом с заголовком — строка не нужна и
   скрыта; на телефоне карточка уезжает вниз, и цена обязана остаться на первом
   экране. Порог 760px — ИЗМЕРЕННЫЙ c-design (block на 420…720, none на 767),
   не моя догадка: своей точки слома под чужой предмет не завожу.
   🔴 Скрытие — `display:none`, а не визуальное прятание: строка ДУБЛИРУЕТ цену
   из карточки, и на широком экране скринридер прочитал бы её дважды. */
/* 🔴 КЕГЛЬ/ИНТЕРЛИНЬЯЖ — РОЛЬ, А НЕ ШКАЛА (наряд c-design 17.08, замер @390):
   канон 14px/normal, одна строка, высота 16px; было 18px без явного
   line-height (наследовался `--ds-lh-base`=1.5 → 27px, перенос в две строки
   при ширине БОЛЬШЕ канонной — виновен кегль, не теснота). Ширина у нас 358
   против канонных 338, значит перенос ломал не контейнер. */
.tpl-hero__price {
  display: none;
  margin: var(--ds-space-3) 0 0;
  font-size: var(--ds-fs-sm);
  line-height: var(--ds-lh-tight);
  font-weight: var(--ds-fw-bold);
}

@media (max-width: 760px) {
  .tpl-hero__price { display: block; }
}

/* ВЫСОТА ПЕРВОГО ЭКРАНА (`hero.flush`, v0.119.0). Канон даёт герою 0/0, ядро —
   96/96 на ≥768 (`.ds-hero{padding: var(--ds-space-24) 0}`): минус 192px.
   🔴 Правило БЕЗ медиазапроса и той же специфичности, что ядровое, но ПОЗЖЕ по
   порядку подключения (tpl.css грузится после ds/index.css) — поэтому выигрывает
   и на ≥768, и ниже. Вес не поднимаю: гонка специфичности запрещена и не нужна.
   ⚠ Класс ОПТ-ИН: у брендов без ключа ничего не меняется. Токен --ds-space-24
   тронуть нельзя — его же читает отступ подвала. */
.tpl-hero--flush { padding-top: 0; padding-bottom: 0; }

/* СФЕРЫ: позиций у меня БОЛЬШЕ НЕТ — они живут в бренд-теме именами
   (`.ds-sphere--hero-center` и т.д., DESIGN v0.152.0). Прежние `tpl-sphere--tl/tr/…`
   сняты вместе со словарём углов: канонные координаты ПРОЦЕНТНЫЕ (44 % · 4 % · 7 %,
   сходятся на 1280/1440/1920 с точностью ≤1px), а вынос `-32px` резал сферу краем
   ПО ПОСТРОЕНИЮ. Шаблон теперь не знает ни одной координаты — и это правильно:
   геометрия канона принадлежит бренду, а не носителю. */

/* ПОРЯДОК ГЕРОЯ НА УЗКОМ ЭКРАНЕ (v0.102.0, наряд integrator-c2 14.08).
   Замер c-design @390: карточка цены была на 1144 (ниже сгиба на 300); из всех
   вариантов порог берёт только перестановка — карточка ПЕРЕД плитками даёт 632,
   выше сгиба на 212. Копирайт бессилен: плитки 372px, лид 245 — снять лид
   целиком и то мало. И это ВОЗВРАТ К МАКЕТУ: в каноне на 390 карточка на 664.

   Механизм: колонка героя режется на две СВОИ группы, а на точке слома ядра
   становится `display: contents` — группы поднимаются в ту же сетку, и порядок
   задают три `order` на МОИХ классах (`tpl-hero__lead`, `tpl-hero__aside`,
   `tpl-hero__facts`). Ни одного чужого имени: панель несёт своё имя рядом с
   ядровым, поэтому мой же гейт «tpl.css не трогает .ds-hero__media» (охраняет
   ds-media-stack слайда-1) остаётся в силе и НЕ ослаблен под эту задачу.
   🔴 1023px — не круглое число, а ТОЧКА СЛОМА ЯДРА: ровно там
   `.ds-hero--media .ds-hero__inner` уходит в одну колонку (section.css ядра).
   Своей точки я не завожу — вторая точка слома на том же предмете разъехалась бы
   с чужой при первом же её изменении. */
/* 🔴 БАЗОВЫХ ПРАВИЛ У ГРУПП НЕТ, И ЭТО РЕШЕНИЕ, А НЕ ЗАБЫВЧИВОСТЬ. Первая
   редакция делала колонку и группы флексами с `gap` — на 1500 это сдвинуло
   купон на 12px, а плитки на 24px: флекс отменяет схлопывание отступов, и
   вертикальный ритм считался заново. Дефекта не видно глазом, но «композиция
   не поехала» стало бы неправдой. Группы оставлены обычными блоками: на широкой
   разметка ведёт себя ровно как до дробления колонки (замер: ноль отличий),
   а на узкой боксы всё равно исчезают под `display: contents`. */

/* 🔴 ПОРОГ ГЕРОЯ — 900/901, А НЕ 1023 (v0.121.0; замер c-design: канон
   переключает раскладку на десяти порогах, ядро знает два, совпадений НОЛЬ).
   Ядро схлопывает `.ds-hero--media` в одну колонку на ≤1023, канон — на ≤900.
   Разрыв 901…1023 = у канона две колонки, у нас одна: экран владельца ровно
   оттуда, а наши замеры на 390/1440 обе точки этого разрыва ПРОПУСКАЮТ по
   построению.
   ⚠ Поэтому здесь я СОЗНАТЕЛЬНО перекрываю ядровую точку слома — но только
   внутри своего опт-ин класса: у брендов без `hero.aside` ничего не меняется.
   Просить c-design сдвинуть порог в ядре нельзя: это число общее всем брендам,
   а канон — forbion. Радиус решает класс, а не уговор. */
/* 🔴 ЧИПЫ УХОДЯТ ВНИЗ И ВСТАЮТ В ДВА СТОЛБЦА НА УЗКОМ (v0.148.0; строки 81–82
   диффа партии 5). ЗАМЕР КАНОНА @390: порядок по вертикали —
   h1 128 · лид 260 · кнопка 338 · цена 392 · реестр 423 · ЧИПЫ 478,
   то есть строки со значками стоят ПОСЛЕДНИМИ, а не сразу под заголовком;
   сами чипы — `flex-wrap: wrap`, ширина 338, ДВА ряда по два.
   🔴 МЕХАНИЗМ КАНОНА ВЗЯТ ЕГО ЖЕ: у него `.trust { order: 9 }` — в разметке
   чипы стоят вторыми (после h1), а вниз их уводит ПОРЯДОК. Разметку не трогаю:
   на широком чипы обязаны остаться в правой клетке шахматки, и DOM-порядок там
   несущий. Одно правило вместо двух разметок.
   ⚠ ОСТАТОК НАЗЫВАЮ: у канона чипы идут ПОСЛЕ плашки реестра, у нас — после
   цены, но ДО реквизитов: реквизиты лежат в соседней группе (`tpl-hero__facts`),
   и чтобы пропустить чипы за них, пришлось бы расплющить обе обёртки и
   переназначить порядок ВСЕМ узлам — это тронуло бы измеренную раскладку
   v0.102.0 (карточка цены перед плитками, замер @390: 1144 → 632). Предмет
   назван числом, дешёвая половина сделана. */
@media (max-width: 900px) {
  /* 🔴 РИТМ ГРУППЫ ЗАДАЁТ НОСИТЕЛЬ — И ДО v0.150.0 ЕГО НЕ ЗАДАВАЛ НИКТО.
     Находка c-design (строка 83, которую он сперва объявил своей и вернул мне
     замером): у всех детей `margin: 0` — это КОНТРАКТ ЯДРА («ритм задаёт
     раскладка»), а `.tpl-hero__lead` был `display:block` без полей и зазора.
     Ядро своё выполнило, носитель — нет: зазор между заголовком, лидом и
     кнопками выходил РОВНО НОЛЬ. Глазами это читалось как «значки наезжают на
     h1» — но наезд давала сфера (её c-design закрыл в v0.190.0), а слипание
     строк давал отсутствующий ритм. Два разных дефекта в одном кадре.
     ⚠ ЗАМЕР КАНОНА @390: контейнер `grid` с `gap: 6`, но у КАЖДОГО ребёнка свой
     `margin-top` (16 · 16 · 8 · 9 · 13) — эффективные зазоры 22 · 22 · 14 · 15 · 19.
     Ритм у канона НЕРАВНОМЕРНЫЙ, одним значением он не воспроизводится, а
     раздавать своим детям поля нельзя: `margin: 0` — контракт ядра, и я его
     не переписываю ради одной секции. Беру `--ds-space-6` (24) — ближайшая
     ступень к доминирующим 22; расхождение с нижними 14…19 объявлено. */
  .tpl-hero__lead { display: flex; flex-direction: column; gap: var(--ds-space-6); }
  .tpl-hero__chips { order: 9; flex-direction: row; flex-wrap: wrap; }
  .tpl-hero__chip { flex: 0 0 calc(50% - var(--ds-space-2)); }
}

/* 🔴 ОСТАТОК v0.148.0 ЗАКРЫТ: РЕЕСТР НАД ПАНЕЛЬЮ, ДОВЕРИЕ ПОСЛЕ РЕЕСТРА
   (v0.226.0, решение integrator-c2 16.08 по собранным парам узлов).
   ЗАМЕР КАНОНА @390, порядок по вертикали (пары наш ↔ канонный названы до чисел):
       h1 → лид → кнопки → цена → реестр → доверие → [панель]
   У нас было: h1 → лид → кнопки → цена → доверие → [панель] → реестр,
   то есть реквизиты стояли ПОД панелью, а у канона весь текст НАД ней.
   ⚠ В v0.148.0 я назвал это остатком и оценил как «расплющить обе обёртки и
   переназначить порядок ВСЕМ узлам». Оценка была верной по механизму и НЕВЕРНОЙ
   по цене: переупорядочивать надо не все узлы, а только МОИ. Ядровые дети
   (`h1`, лид, кнопки) остаются с `order: 0` и держат DOM-порядок между собой —
   ни одного чужого имени класса в расстановке по-прежнему нет.
   🔴 РИТМ: расплющив группы, я теряю их флекс-зазор 24 — все ряды садятся на
   сеточный `row-gap` 48, то есть вертикальный ритм текста удвоился бы. Поэтому
   зазор сетки на узком становится тем же `--ds-space-6` (24) — и это ПРИБЛИЖЕНИЕ
   к канону с двух сторон сразу: у него внутри текста 22·22·14·15·19, а между
   колонкой и панелью 25, тогда как у нас между группами стояло 48. Своего числа
   не завожу: 24 — та же ступень, что стояла внутри группы до расплющивания. */
@media (max-width: 900px) {
  /* Колонка исчезает как БОКС, но не как группировка: её дети становятся
     элементами той же сетки, и между ними работает сеточный gap. То же и с
     двумя группами внутри неё — иначе доверие и реестр лежат в разных боксах и
     не могут встать друг за другом. */
  .tpl-hero--reorder .tpl-hero__col,
  .tpl-hero--reorder .tpl-hero__lead,
  .tpl-hero--reorder .tpl-hero__facts { display: contents; }
  .tpl-hero--reorder .ds-hero__inner { row-gap: var(--ds-space-6); }
  /* Порядок — только на СВОИХ классах; `.tpl-hero__facts > *` адресует реквизиты
     через мою обёртку, не называя ядровое имя. */
  .tpl-hero--reorder .tpl-hero__price { order: 5; }
  .tpl-hero--reorder .tpl-hero__facts > * { order: 6; }
  .tpl-hero--reorder .tpl-hero__chips { order: 7; }
  .tpl-hero--reorder .tpl-hero__aside { order: 8; }
}

/* 901…1023: ядро уже сложило сетку в одну колонку, а канон держит две.
   Возвращаем две ровно в этом окне и ровно для опт-ин героя. */
@media (min-width: 901px) and (max-width: 1023px) {
  .tpl-hero--reorder .ds-hero__inner { grid-template-columns: 1.05fr 0.95fr; }
}

/* 🔴 ДВУХПОЛОСНАЯ ТЕКСТОВАЯ КОЛОНКА (`hero.split_text`, v0.130.0).
   ЗАМЕР КАНОНА (`concept-v0.30.20-draft`, 1440 и 1024): текстовая колонка героя
   — САМА сетка в две клетки, три строки:
       h1        | чипы
       лид       | кнопка
       реквизиты | —
   🔴 КЛЕТКИ НЕ НАЗНАЧАЮ — У КАНОНА ЧИСТАЯ АВТОРАССТАНОВКА, и повторяю именно её.
   Назначь я `grid-column/row` поимённо — правило было бы привязано к сегодняшнему
   составу узлов, и любой новый ключ молча уехал бы в чужую клетку. Цена решения
   объявлена и вынесена в ОТКАЗ сборки: лишний узел (кикер, плитки) сдвигает
   шахматку целиком, поэтому валидатор роняет сборку с именем ключа.
   ⚠ Обёртки становятся `display: contents` — они группировка, а не бокс: дети
   поднимаются в ту же сетку. Тот же приём, что в блоке ≤900 выше; ни одного
   чужого имени класса в расстановке не участвует.
   Отношение `1.15fr 1fr` — ЗАМЕРЕНО: 558.406/485.578 на 1440 и 496.359/431.625
   на 1024 дают одно и то же с точностью до 0.001, то есть это отношение канона,
   а не подобранное число.
   🔴 ЗАЗОР — ТОКЕН НАЗНАЧЕНИЯ `--ds-hero-grid-gap` (DESIGN v0.167.0), И ЭТО
   ЧЕТВЁРТЫЙ ВАРИАНТ, ЛУЧШЕ МОЕГО МЕНЮ. Я предлагал выбрать между «завести
   ступень 44», «взять 48 с объявленным расхождением 4px» и «ждать»; в v0.130.0
   стояло второе. c-design померил СЛОВАРЬ ЗАЗОРОВ КАНОНА ЦЕЛИКОМ — 12·9·14·7·8·
   16·11·44·10·18·26·28·30, и 44 встречается ШЕСТЬ РАЗ. Значит это собственное
   значение макета, а не округление нашего: у канона своя система, к шкале ДС не
   сводимая, и «расхождение 4px» описывало бы несуществующее родство.
   ⇒ число живёт в БРЕНД-ПАКЕТЕ (forbion 44, ядро — var(--ds-space-12), izibuh
   как был 48), а шаблон читает ИМЯ и не знает ни одного числа.
   📌 Мой инстинкт «своего числа рядом с чужой шкалой не завожу» был верен, а
   вывод из него — нет: я искал ступень В шкале, тогда как предмет требовал
   токена НАЗНАЧЕНИЯ. Не всякое чужое число обязано лечь в чужую же шкалу.
   Ниже 901 композиция одноколоночная — там правит блок переупорядочивания. */
@media (min-width: 901px) {
  .tpl-hero--split-text .tpl-hero__col {
    display: grid;
    grid-template-columns: 1.15fr 1fr;
    gap: var(--ds-hero-grid-gap);
    align-items: center;
  }
  .tpl-hero--split-text .tpl-hero__lead,
  .tpl-hero--split-text .tpl-hero__facts { display: contents; }
}

/* 3) hero.aside (v0.98.0) — панель в правой колонке ПЕРВИЧНОГО героя, когда
      карточка обязана быть на первом экране рядом с h1. Селектор ГРУППОВОЙ, а не
      второе правило с теми же объявлениями: два правила на один смысл расходятся
      молча (ровно тот дефект, что я поймал у себя на .cfgc). Третья площадка
      центрируется тем же кодом — значит и выглядит так же по построению. */
.tpl-carousel__panel,
.tpl-hero__panel {
  display: grid;
  place-items: center;
  min-height: 100%;
  /* 🔴 ГАТТЕР КОНТЕЙНЕРА — обязателен, а не косметика (c-design, контракт купона
     v0.44.0). Декор ядра намеренно торчит наружу: .ds-coupon__hole вынесен на
     -min(15px, --ds-container-pad). Канон это не переполняет, потому что купон
     стоит В КОНТЕЙНЕРЕ, и вылет уходит в его поле.
     Слайд С `copy` рендерится через .ds-hero__inner — там паддинг есть. А ЭТА
     ветка (панель БЕЗ копи-колонки) клала панель голой: поля нет → вылет упирался
     во вьюпорт и давал горизонтальный скролл на узких. Я сам померил это на своей
     фикстуре и передал c-design как ДЕФЕКТ ЯДРА — но корень был здесь, в моей
     разметке (izibuh чист именно потому, что у него слайды с copy).
     Поле даём здесь: «плейсмент — зона шаблона», ядру незачем знать, кто его
     обернул.
     🔴 ТОЛЬКО на --bare: у слайда С `copy` поле уже даёт .ds-hero__inner, и общий
     padding удвоил бы гаттер (16+16=32) — замер izibuh @360 показал сужение
     купона 396→364. Вопрос задал c-design, подтвердил замером я. */
}

.tpl-carousel__panel--bare {
  padding-inline: var(--ds-container-pad);
}

/* Рамка карусели — сужает ЯДРОВУЮ .ds-carousel до фактической контент-ширины,
   чтобы вынос стрелки (.ds-carousel__arrow left/right:-54px @≥1200px, оффсет ядра)
   лёг в боковое поле, а не за вьюпорт. Секция полноширинная (фон), поэтому
   несуженная .ds-carousel растягивалась бы во всю ширину и стрелки уезжали за
   экран (замер c-design-2 на корне: prev −54, next 1493). Сужаем шаблонной
   обёрткой, НЕ оверрайдя .ds-* (§2).
   ⚠ Ширина = fit-content, НЕ var(--ds-container-max): ядровый .ds-hero__inner —
   grid (1.05fr 0.95fr), его min-content ПРЕВЫШАЕТ --ds-container-max (замер: токен
   1100, реальная колонка 1132 — max-width у грида не зажимает min-content). Рамка
   с фикс-токеном 1100 сдавила бы герой на 32px (регресс). fit-content ужимает
   рамку по НАТУРАЛЬНОЙ ширине содержимого — колонка не едет (1132→1132), стрелки
   садятся в гаттер относительно ИСТИННОГО края, и это отслеживает контент, а не
   прибитое число. max-width:100% — фолбэк на узких (< контент-ширины). */
.tpl-carousel-frame {
  width: fit-content;
  max-width: 100%;
  margin-inline: auto;
  /* 🔴 КОНТЕКСТ ПОЗИЦИОНИРОВАНИЯ ДЛЯ ОРГАНОВ СЛАЙДЕРА — СТАВИТСЯ ЗАРАНЕЕ И
     НАМЕРЕННО (v0.229.0, парная правка с DESIGN v0.318.0).
     У канона `.sctrl` (точки + метка экрана) стоит ВНЕ ПОТОКА и висит над низом
     слайда — вклад в высоту НОЛЬ; у нас те же два узла стоят В ПОТОКЕ и берут
     62 px на всех трёх ширинах. Ядро сделает свою половину — `position:
     absolute; bottom: 15` у `.ds-carousel__dots`.
     ⚠ И БЕЗ ЭТОЙ СТРОКИ ЕГО ПОЛОВИНА ОПАСНА, А НЕ ПРОСТО БЕСПОЛЕЗНА: рамка была
     `static`, поэтому абсолютные органы уехали бы к ближайшему позиционированному
     предку — то есть неизвестно куда. Поэтому строку кладу РАНЬШЕ его тега:
     сама по себе она инертна (позиционированных детей у рамки сегодня нет,
     замер: у соседей и у forbion ноль расхождений рендера), но она снимает
     возможность приземлить его половину в одиночку. Порядок правок не должен
     быть условием их безопасности. */
  position: relative;
}

/* Отзывы-strip (reviews.paged, concept-v1.7.2): горизонтальный скролл-вьюпорт с
   листанием на ЯДРОВОЙ стрелке .ds-carousel__arrow (её визуал/позиция берём из
   ядра). Здесь — только раскладка strip'а (зона шаблона) + состояние гашения
   стрелки на краю: скоуплено в .tpl-reviews + data-tpl-off, чтобы НЕ рестайлить
   глобально DS-класс. Без JS вьюпорт скроллится/свайпается нативно. */
.tpl-reviews { position: relative; margin-top: var(--ds-space-6); }

.tpl-reviews__viewport {
  overflow-x: auto;
  scroll-behavior: smooth;
  scroll-snap-type: x proximity;
  scrollbar-width: none;               /* Firefox */
}
.tpl-reviews__viewport::-webkit-scrollbar { display: none; }

.tpl-reviews__track {
  display: flex;
  gap: var(--ds-space-4);
  width: max-content;
}

.tpl-reviews__track > .ds-review {
  width: var(--ds-marquee-item-width, 340px);   /* тот же токен, что у ленты */
  flex-shrink: 0;
  scroll-snap-align: start;
}

/* гашение стрелки на краю — template-состояние, скоуплено внутри .tpl-reviews */
.tpl-reviews .ds-carousel__arrow[data-tpl-off] {
  opacity: 0;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .tpl-reviews__viewport { scroll-behavior: auto; }
}

/* Лого в подвале (плейсмент — зона шаблона; ассет/вариант — владелец бренда).
   Только раскладка: инлайн-блок + отступ до описания. Визуал лого — из ассета. */
.tpl-footer-logo {
  display: inline-block;
  margin-bottom: var(--ds-space-3);
}

/* 🔴 Мобильный горизонтальный скролл (замер c-ops playwright@360, воспроизвёл:
   docScrollWidth=416 при vw=360, +56px). Причина: подвальное лого несёт АТРИБУТЫ
   width/height из logo.display.footer пакета (izibuh 400×92) — они обязательны
   (анти-CLS, P1.6 c-seo), но без max-width дают min-content 400px, и 1fr-трек
   .ds-footer__inner растягивает контейнер за вьюпорт. Глобального img-ресета в
   ЯДРЕ нет (шапку ядро клампит точечно с DESIGN v0.26.0, подвал — нет).
   Клампим СВОЁ лого (наш класс, наша разметка — плейсмент подвала зона шаблона),
   ядро не оверрайдим. height:auto обязателен в паре: без него атрибутная высота
   92px осталась бы при сузившейся ширине и лого исказилось бы. */
.tpl-footer-logo img {
  max-width: 100%;
  height: auto;
}

/* Дисклеймер У ЦЕНЫ в split-панели (v0.65.0, условие входной визы c-legal).
   Стоит ПОД ядровым __foot (цена ↔ кнопка) — тот flex-ряд, третий элемент внутри
   встал бы в строку с ценой.
   🔴 Цвет — ТА ЖЕ идиома, что у ядрового .ds-split-panel__lead:
   color-mix(on-primary 80%). Не изобретаю свой уровень контраста и не беру
   --ds-text-muted: панель тёмная градиентная, а on-primary парен к ней по норме
   п.12 (у forbion on-primary ЧЁРНЫЙ — литерал #fff сломал бы его).
   Кегль sm, не xs: это юридическое примечание, его обязано быть видно. */
.tpl-split-note {
  margin: var(--ds-space-4) 0 0;
  font-size: var(--ds-fs-sm);
  color: color-mix(in srgb, var(--ds-color-on-primary) 80%, transparent);
}

/* Лид секции (наряд D-4 п.2). В ядре есть только .ds-hero__lead — секционного
   нет, а заводить визуальный компонент шаблону нельзя (CONTRACT §2), поэтому
   здесь только композиция на токенах, ритм как у ядрового лида героя. */
.tpl-section-lead {
  max-width: 40rem;
  margin: var(--ds-space-3) 0 0;
  color: var(--ds-text-secondary);
  /* 🔴 ЛИД ЧИТАЕТ РОЛЕВЫЕ ТОКЕНЫ ЛИДА, А НЕ ОБЩУЮ ШКАЛУ (v0.222.0;
     `--ds-section-lead-size|-lh`, DESIGN v0.306.0). Раньше здесь стояли ступени
     шкалы (`--ds-fs-lg` 18 / `--ds-lh-loose` 1.7) — они и были «вторым
     автором» канонных 15.5/1.6: значение объявлено у бренда, а читатель брал
     своё. Фолбэк = прежние ступени ⇒ izibuh и tezum не двигаются. */
  font-size: var(--ds-section-lead-size, var(--ds-fs-lg));
  line-height: var(--ds-section-lead-lh, var(--ds-lh-loose));
}

/* 🔴 РОЛЕВОЙ КЕГЛЬ ЛИДА (v0.228.0). Бренд объявил `--ds-honest-lead-size` 16 и
   `--ds-calc-lead-size` 14.5 (канонные исключения из базовых 15.5), класс роли
   приезжал в разметку — а ЧИТАТЕЛЯ не было, и обе секции держали базовые 15.5.
   Ровно наша форма «значение объявлено, читателя нет»: токен на живом ЕСТЬ,
   а действия у него НОЛЬ.
   ⚠ Почему не переопределяю `--ds-section-lead-size` на самой роли: запись вида
   `--ds-section-lead-size: var(--ds-honest-lead-size, var(--ds-section-lead-size))`
   — ЦИКЛ, и по спецификации всё свойство становится недействительным. Поэтому
   роль читает СВОЙ токен здесь, а общий остаётся вторым звеном цепочки: роль →
   общий → ступень шкалы. Три звена вместо двух — но ни одно не выдумано мной.
   📌 Знак расхождения с каноном у `#honest` МЕНЯЕТСЯ с шириной (замер c-design:
   −69 @390 · +84 @901 · +112 @1366) ⇒ кегль правит все три сразу и в разные
   стороны; подгонять по одной ширине здесь нельзя. */
.tpl-section--honest .tpl-section-lead {
  font-size: var(--ds-honest-lead-size, var(--ds-section-lead-size, var(--ds-fs-lg)));
}

.tpl-section--calc .tpl-section-lead {
  font-size: var(--ds-calc-lead-size, var(--ds-section-lead-size, var(--ds-fs-lg)));
}

/* Карточка с НОМЕРОМ-МЕТКОЙ (v0.70.1, вердикт c-design-2 по канону
   concept-izibuh-v2.3.0: `.card + card-num`). Номер — ТЕКСТ в углу, иконка
   ПЕРВОЙ в потоке.
   🔴 Почему не ядровый .ds-card--step: его счётчик-кружок ::before стоит В
   ПОТОКЕ сверху и выталкивает иконку вниз на 69px — это и был дефект блока
   «Что берёт главбух». Ядро не трогаем: --step ВЕРЕН для «Недели» (там иконок
   нет), правка ядра сломала бы соседний блок.
   Здесь только КОМПОЗИЦИЯ: position + типографика на токенах. Новый визуальный
   компонент не заводится, `.ds-card` не переопределяется — `position:relative`
   висит на МОЁМ классе, добавленном к тому же элементу.
   Смещения 22/24px — из канона дословно (24 = --ds-space-6; для 22 точного
   токена нет, канон-паритет важнее округления до шкалы). */
.tpl-card--num {
  position: relative;
}

.tpl-card-num {
  position: absolute;
  top: 22px;
  right: var(--ds-space-6);
  font-size: var(--ds-fs-sm);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-text-muted);
  line-height: 1;
}

/* Выравнивание строк FAQ (S3, вердикт владельца: TOP-anchor). Свёрнутые summary
   разной длины (1 строка vs 2) давали разную высоту → ряды не выровнены (замер
   c-ops-2: 53 vs 74). Равняем к 2 строкам min-height'ом; первые строки вопросов
   встают на один уровень, номер (.ds-faq__num, top:15 у первой строки) — вровень.
   🔴 min-height:2lh — content-box (в ядре border-box нет): 42 контента + 32
   паддинга ядра = 74; НЕ 84 (тот при content-box дал бы 116 — box-sizing).
   align-items — СЕГОДНЯ не несущий (A/B-замер playwright, v0.52.0, izibuh-тема:
   flex-start и ядровый baseline дают ИДЕНТИЧНО boxH=74, текст=18, номер=15 и на
   1-, и на 2-строчном summary — байт-в-байт). Причина: единственный in-flow
   flex-элемент здесь — текст (номер absolute, шеврон align-self:center), а для
   одного baseline-элемента baseline ≡ flex-start. Ядро на v0.52.0 = baseline
   (center был лишь в откаченном v0.53.0). Фиксирую start ЯВНО как робастность:
   держу izibuh-намерение «первая строка» независимо от дефолта ядра, если в
   summary появится второй in-flow элемент иной высоты. Это вид, решённый
   владельцем, а не расчёт на текущее значение ядра.
   min-height аддитивен (в ядре его нет), align-items — по указанию integrator-c
   в мой слой (развилка «в шаблон», не в ядро). */
.tpl-faq-q {
  /* 🔴 РЕЗЕРВ В ДВЕ СТРОКИ — РЕШЕНИЕ ВЛАДЕЛЬЦА IZIBUH, А НЕ ОБЩЕЕ ПРАВИЛО
     (v0.185.0). `c-design` замерил у forbion: канон держит вопрос ОДНОЙ строкой
     (`summary` 65), а `min-height: 2lh` даёт +18 на каждой закрытой строке — на
     четырёх рядах это 72 из 113 всего расхождения списка. Признак, по которому
     это мой слой, он назвал верно: правило пришло из `tpl.css`, в его
     компоненте `min-height` нет вовсе.
     ⚠ НО СНЯТЬ ЕГО ЦЕЛИКОМ НЕЛЬЗЯ: резерв заводился под канон izibuh («равняем
     к двум строкам», решение владельца, v0.52.0) — там он не расхождение, а
     принятый вид. Снял бы — сменил бы чужой бренд молча, и это ровно то, за что
     мы сегодня ловили друг друга пять раз.
     ⇒ величина уезжает в токен с фолбэком на ПРЕЖНЕЕ значение: forbion объявит
     своё (канонная одна строка), izibuh не объявляет — и остаётся при своих
     двух. Одно выражение вместо ветвления, и у величины один автор. */
  min-height: var(--ds-faq-q-min-h, 2lh);
  align-items: flex-start;
}

/* Визуально скрыто, семантически ЕСТЬ (sr-only). Для осиротевшего label
   поля-матрицы (v0.71.0): с JS оси подписаны легендами сегментов, обёрточный
   заголовок «стаканит»; но БЕЗ JS единственный контроль — <select>, и подпись
   ему нужна. Прячем визуально, оставляем скринридеру и no-JS-семантике.
   Стандартный sr-only-паттерн (clip-rect), не display:none — иначе label
   выпал бы и из доступности тоже. */
.tpl-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* Матрица калькулятора (зависимые оси, RFC): SSG-база — один select валидных
   пар; JS enhance'ит в два сегмента. Здесь только раскладка — стили сегмента и
   инпутов приходят из дизайн-системы; гашение несовместимой опции — по классу
   .is-disabled (aria-disabled ставит JS), не inline-style. */
.tpl-matrix {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-4);
}

.tpl-matrix__axis .ds-field__label {
  margin-bottom: var(--ds-space-2);
}

.ds-segment__btn.is-disabled {
  opacity: .4;
  cursor: not-allowed;
}

/* Калькулятор v0.5: смета и заголовки вариантов (табы — прогрессивное улучшение) */
.tpl-calc__lines {
  margin: var(--ds-space-3) 0 0;
  padding-left: var(--ds-space-4);
}

.tpl-calc__variant-title {
  cursor: pointer;
  margin: var(--ds-space-6) 0 var(--ds-space-3);
}

.tpl-calc__variant-title--active {
  text-decoration: underline;
}

/* Плавный скролл к якорям — поведение страницы (зона шаблона, согласовано
   с c-design); гасится предпочтением пользователя */
@media (prefers-reduced-motion: no-preference) {
  html {
    scroll-behavior: smooth;
  }
}

/* Honeypot-поле формы (антиспам c-api): визуальное скрытие без display:none
   и без inline-style; вне таб-порядка и вне aria-дерева */
.tpl-hp {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* 🔴 [hidden] ОБЯЗАН ГАСИТЬ (v0.55.0). Браузерный `[hidden]{display:none}` имеет
   специфичность (0,1,0) и ПРОИГРЫВАЕТ любому компонентному правилу с display:
   `.ds-calc__includes li{display:flex}` = (0,1,1) перебивает его → скрытая строка
   состава остаётся ВИДНОЙ. c-ops поймал рендером на превью: на ОСН показаны
   строки УСН/АУСН — ЛОЖНЫЙ состав платной услуги. Баг прошёл ТРИ итерации, потому
   что мерили HTML-атрибут (он есть), а не РЕНДЕР (display) — мой же класс
   «генерация ≠ исполнение».
   Чиним КЛАСС, а не инстанс: единое правило на ВСЕ [hidden] (стандартный
   reset-приём, как в normalize). Задевает и тумблер темы (.tpl-theme-sw —
   display:inline-flex, показывался бы в SSG без JS), и любой будущий hidden на
   компоненте с display. !important здесь ЗАКОНЕН: у [hidden] нет валидного
   состояния «скрыт И виден» — это семантический контракт, а не борьба стилей.
   JS-энхансмент не задет: он УБИРАЕТ атрибут (prev.hidden=false), правило
   перестаёт матчиться. Слайды карусели не [hidden] (они .is-active+opacity) —
   кроссфейд цел. */
[hidden] { display: none !important; }
/* Defense-in-depth (v0.58.0, запрос c-ops): scoped-дубль на строки состава.
   Глобальное правило выше УЖЕ гасит их — доказано на DESIGN v0.32.0
   (getComputedStyle=none, выигравшее правило «[hidden] !important»): `!important`
   бьёт специфичность безусловно, а `.ds-calc__includes li{display:flex}` —
   (0,1,1) БЕЗ важности. Это правило — страховка на случай, если ядро когда-то
   добавит `display:…!important` (тогда при РАВНОМ !important победит бОльшая
   специфичность (0,2,1) этого селектора). Сегодня оно избыточно, но стоит
   дёшево и закрывает класс окончательно. */
.ds-calc__includes li[hidden] { display: none !important; }

/* ── ЛОГО ШАПКИ ПО ТЕМЕ: механика УШЛА В ЯДРО (DESIGN v0.29.0).
   Здесь были свои .tpl-logo--light/--dark с теми же правилами — это был ДУБЛЬ:
   два источника истины о том, кто виден в тёмной. c-design прав по слою:
   «шаблон даёт data-theme, ядро даёт палитру» (граница v0.25.0). Рендер теперь
   эмитит классы ядра .ds-header__logo-light/-dark, правил здесь нет.
   Один вопрос — один механизм. */

/* Кнопка действия в панели итога (v0.50.0). Панель — flex-колонка (ядро), и
   кнопка обязана быть ВНИЗУ: посетитель дочитал цифру → действие под рукой.
   `margin-top:auto` прижимает её к низу, а заодно съедает часть пустоты между
   строками и низом панели. ⚠ Пустоту это НЕ лечит целиком: в каноне высоту
   держит растягивающийся блок «что входит» (.calc-includes{flex:1}) — его в
   ядре нет, наряд у c-design. Не выдаю полумеру за решение.
   Инверсия цвета: панель тёмная/градиентная, поэтому кнопка светлая (как в
   каноне: .calc-out .btn{background:#fff}) — на токенах, без своих цветов. */
.tpl-calc__cta {
  margin-top: auto;
  align-self: flex-start;
  /* 🔴 v0.51.1: было var(--ds-surface-page) — он ПЕРЕВОРАЧИВАЕТСЯ в тёмной теме
     (#ffffff → #1a1e27), а панель под кнопкой --ds-gradient-panel НЕ
     переворачивается (её нет в тёмном блоке ядра). В тёмной теме получалась
     тёмная кнопка на тёмной панели. Тот же корень, что у лого шапки: поверхность
     ВСЕГДА тёмная, значит и контент на ней обязан быть стабильным, а не
     «страничным».
     Берём ПАРУ on-primary/primary — ту же, которой панель красит свой текст
     (норма п.12: ломается пара, а не значение; у forbion on-primary ЧЁРНЫЙ
     намеренно, и пара это учитывает — фиксированный #fff сломал бы его). */
  /* 🔴 v0.189.0: БЫЛА ЗАЛИВКА ТОКЕНОМ ЧЕРНИЛ. `--ds-color-on-primary` — это
     цвет текста ПОВЕРХ основного (#1D1D1B у forbion), а не поверхность; отсюда
     тёмная кнопка там, где у канона зелёная пилюля. Находка c-design.
     ⚠ Беру ТОКЕНЫ CTA (`--ds-calc-cta-bg` / `--ds-calc-cta-ink`, объявлены
     forbion-у в DESIGN v0.246.0) с фолбэком на прежнее, а НЕ класс
     `.ds-btn--gradient` напрямую. Довод автора («модификатор брендопараметричен,
     у izibuh он даст ЕГО градиент») верен по механизму — но у izibuh кнопка
     СЕГОДНЯ белая, и включение роли сменило бы ей вид. Роль включает владелец
     бренда объявлением токена; я потребляю. Разница ровно та, что мы весь день
     держали: «включить роль» безопасно, когда роль ОБЪЯВЛЕНА, а не когда она
     доступна. */
  background: var(--ds-calc-cta-bg, var(--ds-color-on-primary));
  color: var(--ds-calc-cta-ink, var(--ds-color-primary));
  box-shadow: none;
}
.tpl-calc__cta:hover { opacity: 0.9; }

/* Пилюли «что входит всегда» под итогом (v0.191.0). Замер канона: ряд 354×20,
   кегль 10.5, фон БЕЛЫЙ НЕПРОЗРАЧНЫЙ, радиус 999 — стоят НА ПРОЗРАЧНОМ, вне
   карточки, поэтому берут собственный фон, а не наследуют поверхность.
   ⚠ Белый здесь — роль «чип на выделенном носителе» (`--ds-surface-picked-chip`),
   та же, что у чипов выделенной строки тарифа: у канона они тоже выбелены
   поверх крашеного. Своего литерала не ставлю. */
.tpl-calc__sat {
  display: flex;
  flex-wrap: wrap;
  /* 🔴 ЗАЗОР РЯДА — ЧИТАТЕЛЬ (v0.223.0; `--ds-calc-pill-gap`, DESIGN v0.308.0).
     Замер c-design разобрал наш «+2»: он оказался НЕ пилюлей, а ЗАЗОРОМ —
     канон 2×20 + 6 = 46, мы 2×20 + 8 = 48. Сама пилюля сошлась 20 = 20, то
     есть пара «поля + интерлиньяж» из v0.221.0 сработала РОВНО.
     📌 Мы искали остаток в узле, а он сидел МЕЖДУ узлами: у ряда с переносом
     зазор входит в высоту столько раз, сколько рядов, и потому выглядит как
     свойство элемента. Фолбэк — прежние 8: соседи не двигаются. */
  gap: var(--ds-calc-pill-gap, var(--ds-space-2));
}

.tpl-calc__pill {
  background: var(--ds-surface-picked-chip, var(--ds-surface-page));
  color: var(--ds-text-primary);
  border-radius: var(--ds-radius-full);
  /* 🔴 ВЕРТИКАЛЬНОЕ ПОЛЕ ПИЛЮЛИ — ЧИТАТЕЛЬ, А НЕ ЧИСЛО (v0.220.0; замер
     c-design: у канона `span` несёт `padding 4/4`, ряд 46 против наших 40 —
     МЫ НИЖЕ, направление ДОБАВИТЬ). Ставлю имя с фолбэком 0 = сегодняшнее
     значение: рендер не двинется, пока бренд не объявит 4. Своё число здесь
     было бы ровно тем же промахом, что кегль 12 против канонных 10.5. */
  padding: var(--ds-calc-pill-pad-y, 0) var(--ds-space-2);
  /* ⚠ ВТОРОЙ ЧИТАТЕЛЬ ТОЙ ЖЕ ПРАВКИ (v0.221.0; `--ds-calc-pill-lh`, DESIGN
     v0.304.0). У этой пары НЕТ ЗЕЛЁНОЙ ПОЛОВИНЫ: одни поля дают перелёт +4,
     один `lh: normal` делает хуже на 8, вместе — ровно канон. Поэтому оба
     значения читает ОДНО правило, и ставить их порознь нельзя — иначе
     промежуточный кадр хуже исходного, а «шаг за шагом» здесь означало бы
     дважды ухудшить, прежде чем улучшить.
     ⚠ Фолбэк `inherit` = сегодняшний рендер: пока бренд молчит, ничего не
     двигается. */
  line-height: var(--ds-calc-pill-lh, inherit);
  /* ⚠ КЕГЛЬ СНЯТ (v0.207.0): бренд объявляет 10.5px (канон), а мой
     `var(--ds-fs-xs)` = 12 перебивал его ПОРЯДКОМ при равной
     специфичности. Форма та же, что у `padding` и `gap` карточки,
     только вывернутая: у меня стоял ТОКЕН, у них ЛИТЕРАЛ — и всё
     равно канонное число было мёртвым. Значит признак дефекта не
     «литерал против токена», а КТО ДЕРЖАТЕЛЬ ЧИСЛА. */
  white-space: nowrap;
}

/* Ссылка «полный состав тарифа →» — последняя строка карточки (v0.191.0). */
.tpl-calc__more { font-size: var(--ds-fs-xs); }

/* 🔴 ИМЯ ТАРИФА И ПОДПИСЬ С ЦЕНОЙ — ПЕРВЫЕ ДВЕ СТРОКИ КАРТОЧКИ (v0.193.0).
   ⚠ ТОЛЬКО РАСКЛАДКА, НИ ОДНОГО КЕГЛЯ: канонные 21px и 15px — числа канона,
   значит их держатель `c-design`, а мой — узел. Свой `font-size` здесь стал бы
   вторым автором величины, и разошлись бы они молча (сегодня платили за это
   трижды). До объявления ролей строки наследуют кегль карточки — это видно,
   но не врёт. */
.tpl-calc__tariff,
.tpl-calc__price { display: block; }
/* ⚠ ВЕС ИМЕНИ СНЯТ (v0.197.0): `DESIGN v0.262.0` объявил 800 в бренде, и
   теперь у величины ровно один автор — он. Временное 700 стояло здесь один
   тег по порядку «сперва новый автор объявляет, потом старый снимает»:
   без объявления вес падал на 400, а 400 хуже, чем 700 не по канону.
   Гейт «ровно один автор» (ни двух, ни нуля) остаётся и стережёт обе стороны. */
.tpl-calc__price + .ds-calc__includes { margin-top: var(--ds-space-4); }

/* 🔴 БЛОК ИТОГА ОДНИМ ДЕТЁНЫШЕМ ПАНЕЛИ (v0.194.0; замер c-ops: у канона у
   панели РОВНО два ребёнка и ОДИН зазор 41, у нас было семь и шесть зазоров —
   +95 из расхождения давали именно они).
   ⚠ `flex-column`, а не блочный поток: в блочном соседние margin схлопнулись
   бы, и обёртка молча изменила бы ритм внутри блока. Здесь ни одного своего
   отступа — дети остаются со своими, меняется ЧИСЛО детей у панели. */
.tpl-calc__sum {
  display: flex;
  flex-direction: column;
  /* 🔴 ДВЕ ПОСЛЕДНИЕ ВЕЛИЧИНЫ БЛОКА — ЧИТАТЕЛИ, А НЕ ЧИСЛА (v0.219.0; замер
     канонного блока @901, integrator-c2 + c-design):
         кнопка mar-top   канон 18 · у нас 0
         поле блока снизу канон  6 · у нас 0
     Три зазора выше уже объявлены ядром (`--ds-calc-sum-gap-after-caption |
     -total | -line`) и приезжают из бренда; для этих двух имён нет — завожу
     ЧИТАТЕЛЕЙ с ФОЛБЭКОМ = сегодняшнее значение, чтобы рендер не двинулся ни у
     кого, пока c-design не объявит числа.
     ⚠ Своих 18 и 6 не пишу: числа канонные, держатель — он. Это та же форма,
     что с пилюлей 10.5 и кеглем имени 800, где мой литерал/токен убивал
     канонное значение. */
  padding-bottom: var(--ds-calc-sum-pad-bottom, 0);
}

.tpl-calc__sum > .tpl-calc__cta {
  margin-top: var(--ds-calc-sum-gap-before-cta, 0);
}

/* Смета переехала в раскрывашку «Ставки расчёта» (v0.193.0, виза
   integrator-c2 на уточнение `CONTRACT §1.2а`): в панели её место заняла
   строка расчёта, а построчный вклад остаётся В РАЗМЕТКЕ и раскрывается
   нативным <details> — БЕЗ скрипта. Раскладку строк держит `.ds-calc__lines`
   ядра; здесь только отбивка от списка ставок. */
.tpl-disclosure__a + .ds-calc__lines { margin-top: var(--ds-space-3); }

/* 🔴 ПАНЕЛЬ ПЕРЕСТАЁТ КРАСИТЬСЯ ВМЕСТЕ С ПОЯВЛЕНИЕМ КАРТОЧКИ — ОДНОЙ ПРАВКОЙ
   (v0.190.0, вариант «Г», решение integrator-c2). У канона правая колонка
   ПРОЗРАЧНА, красится карточка внутри неё.
   ⚠ Развилка была из трёх ходов, и выбран адрес, а не механизм: `c-design`
   предлагал `.ds-calc__panel:has(.ds-card--picked)` в ядре, но такой селектор
   читает СЛЕДСТВИЕ («внутри есть карточка») и выдаёт его за НАМЕРЕНИЕ («колонка
   больше не красится») — положи кто-нибудь в панель другую `--picked` карточку,
   и заливка пропадёт молча. Класс объявляет генератор, который роль ЗНАЕТ.
   ⚠ И правка АТОМАРНА: носитель и снятие заливки едут одним тегом, поэтому
   промежуточного кадра не существует ни с двумя фонами, ни без фона вовсе —
   порядок между моим тегом и чужим перестаёт быть предметом.
   ⚠ `transparent` в роли накрывает ОБЕ темы: у панели своя тёмная
   (`rgba(35,35,29,0.68)`), и половинное снятие оставило бы плиту под карточкой
   в тёмной — наш третий класс протечки. */
.tpl-calc__panel--carded { --ds-calc-panel-bg: transparent; }

/* Карточка внутри панели итога (v0.189.0). Своих цветов не несёт: поверхность
   даёт `ds-card--picked` (у forbion `--ds-surface-picked` = rgba(255,255,255,.72),
   ровно канонная), геометрия — своя. */
/* 🔴 ПРАВИЛО СНЯТО ЦЕЛИКОМ (v0.207.0; наряд integrator-c2 по перечислению
   `who-wins.js`: 17 мест, где `tpl.css` перебивает ДС ПОРЯДКОМ).
   Здесь я объявлял `display: flex`, `flex-direction: column` и
   `gap: var(--ds-space-3)` — а `.ds-card` ядра объявляет РОВНО ТО ЖЕ,
   причём зазор через `var(--ds-card-gap, …)`, который бренд задаёт равным 4px.
   ⇒ два первых свойства были дублем с одинаковым значением, третье — числом
   МИМО ТОКЕНА, убивавшим канонные 4. Ни одного своего числа у меня здесь нет,
   поэтому снимается ВСЁ правило, а не строка (тот же разбор, что у `padding`
   в v0.197.0: «провести своё в чужую ручку нужно, когда своё число ЕСТЬ»).
   ⚠ Радиуса тут не было и нет: 16 внутри `--carded` объявляет бренд. */

/* ── ПОЛОСКА ПОТОЛКА (v0.48.0, наряд integrator-c; канон concept-v2.1.0):
   часы + tel: + переключатель темы. НЕ sticky (требование наряда).
   Классы ШАБЛОННЫЕ: компонента topbar в ядре ДС нет (проверено на v0.26.0),
   изобретать ds-* из шаблона нельзя. Стиль — только на токенах, поэтому
   тёмная тема ядра красит полоску сама, без единого своего цвета. */
.tpl-topbar {
  background: var(--ds-surface-sunken, var(--ds-surface-raised));
  border-bottom: 1px solid var(--ds-border-default);
  font-size: var(--ds-fs-xs);
  color: var(--ds-text-muted);
}

.tpl-topbar__in {
  max-width: var(--ds-container-max);
  margin: 0 auto;
  padding: var(--ds-space-2) var(--ds-container-pad);
  display: flex;
  align-items: center;
  gap: var(--ds-space-4);
  flex-wrap: wrap;
}

/* часы занимают свободное место, tel: и переключатель прижаты вправо */
.tpl-topbar__hours { flex: 1 1 auto; min-width: 0; }

.tpl-topbar__tel {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1);
  color: var(--ds-text-primary);
  text-decoration: none;
  font-weight: var(--ds-fw-bold);
  white-space: nowrap;
}
.tpl-topbar__tel:hover { color: var(--ds-color-primary); }

/* Ссылки-значки полоски (`topbar.links[]`, v0.100.0 — мессенджеры). Тач-цель
   44×44 даже при иконке 20px: полоска узкая, и по высоте цель добирается
   паддингом, а не высотой строки (иначе на coarse-указателе промах). Цвет —
   от полоски, значок наследует currentColor, поэтому тема работает без правил
   на сам <svg>. */
.tpl-topbar__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  color: var(--ds-text-primary);
  text-decoration: none;
}
.tpl-topbar__link:hover { color: var(--ds-color-primary); }
.tpl-topbar__link-icon { width: 20px; height: 20px; }

/* Переключатель темы: нативный checkbox + рисованная «капсула».
   Сам input оставляем доступным клавиатуре (не display:none) — прячем
   визуально, фокус ловим на капсуле через :focus-visible + . */
.tpl-theme-sw { position: relative; display: inline-flex; cursor: pointer; }
.tpl-theme-sw input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}
/* Тумблер темы по КАНОНУ (v0.60.0, спека c-design-2): скруглённый КВАДРАТ 34×34
   с ЛУНОЙ, на checked заливается брендовым градиентом. Было — iOS-пилюля.
   ⚠ Луна — через `mask` + `background-color` ТОКЕНОМ, а НЕ `background-image:
   data:` с литеральными цветами (как в исходной спеке): так луна ТЕМИЗИРУЕТСЯ
   (--ds-text-muted флипается в тёмной), а не прибита к #5b6178, и CSP чист
   (mask data: под `img-src 'self' data:`, цвет — обычное свойство). Тот же приём,
   что «шеврон маской» в ядре. Визуал канона идентичен. Маппинг c-design-2:
   surface→--ds-surface-raised, line→--ds-border-default, grad→--ds-gradient-brand,
   pink-soft→--ds-color-accent, cobalt→--ds-border-focus. */
.tpl-theme-sw__box {
  width: 34px;
  height: 34px;
  border-radius: 10px;
  border: 1.5px solid var(--ds-border-default);
  background: var(--ds-surface-raised);
  display: grid;
  place-items: center;
  transition: background var(--ds-anim-pop-duration, .18s) ease,
              border-color var(--ds-anim-pop-duration, .18s) ease;
}
.tpl-theme-sw__box::after {
  content: "";
  width: 17px;
  height: 17px;
  background-color: var(--ds-text-muted);
  -webkit-mask: var(--tpl-moon) no-repeat center / 17px;
  mask: var(--tpl-moon) no-repeat center / 17px;
  transition: background-color var(--ds-anim-pop-duration, .18s) ease;
}
/* луна-маска (fill=none stroke — как в каноне); цвет даёт background-color выше */
.tpl-theme-sw__box {
  --tpl-moon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z'/%3E%3C/svg%3E");
}
.tpl-theme-sw:hover .tpl-theme-sw__box { border-color: var(--ds-color-accent); }
.tpl-theme-sw input:checked + .tpl-theme-sw__box {
  background: var(--ds-gradient-brand);
  border-color: transparent;
}
/* луна на градиенте: цвет-на-primary (у forbion он ЧЁРНЫЙ — норма п.12, не #fff) */
.tpl-theme-sw input:checked + .tpl-theme-sw__box::after {
  background-color: var(--ds-color-on-primary);
}
.tpl-theme-sw input:focus-visible + .tpl-theme-sw__box {
  outline: 2px solid var(--ds-border-focus);
  outline-offset: 2px;
}

/* ── ОКНО ЗАЯВКИ (v0.48.0): нативный <dialog>. Esc/focus-trap/inert фона даёт
   браузер — своего не пишем. Без JS окна нет вовсе, а CTA ведёт якорем к форме
   (честная деградация, требование c-design-2). */
.tpl-lead {
  border: 0;
  padding: 0;
  border-radius: var(--ds-radius-xl, 30px);
  background: var(--ds-surface-page);
  color: var(--ds-text-primary);
  box-shadow: var(--ds-shadow-lg);
  max-width: min(92vw, 32rem);
  width: 100%;
}
.tpl-lead::backdrop { background: rgb(0 0 0 / 0.45); }
.tpl-lead__inner { position: relative; padding: var(--ds-space-8); }

.tpl-lead__close {
  position: absolute;
  top: var(--ds-space-4);
  right: var(--ds-space-4);
  border: 0;
  background: transparent;
  color: var(--ds-text-muted);
  font-size: var(--ds-fs-md);
  line-height: 1;
  padding: var(--ds-space-2);
  cursor: pointer;
  border-radius: var(--ds-radius-full);
}
.tpl-lead__close:hover { color: var(--ds-text-primary); }

.tpl-lead__title { margin: 0; font-size: var(--ds-fs-xl); }
.tpl-lead__sub {
  margin: var(--ds-space-1) 0 var(--ds-space-4);
  font-size: var(--ds-fs-sm);
  color: var(--ds-text-muted);
}

/* Снимок калькулятора: показываем посетителю ровно то, что уедет с формой —
   скрытое поле, о котором он не знает, было бы нечестным. */
.tpl-lead__ctx {
  margin: 0 0 var(--ds-space-4);
  padding: var(--ds-space-3) var(--ds-space-4);
  border-radius: var(--ds-radius-md, 12px);
  background: var(--ds-surface-sunken, var(--ds-surface-raised));
  font-size: var(--ds-fs-xs);
  color: var(--ds-text-muted);
}

@media (max-width: 479px) {
  .tpl-lead__inner { padding: var(--ds-space-6); }
}

/* Группа полей калькулятора (fieldset): сброс рамки + вертикальный ритм.
   ВНУТРЕННИЙ ритм (v0.40.0, замерка c-ops на живом dvl): fieldset по дефолту
   display:block — дочерние .ds-field шли встык, зазор 0px (range-поле низом
   ровно в подпись «Сотрудников»: bottom 2188 == top 2188, константа на всех
   ширинах 360…2560). 24px существовали только МЕЖДУ tpl-group (grid gap
   .ds-calc__form). Лечим сеткой с тем же шагом --ds-space-6 (=1.5rem/24px),
   чтобы ритм внутри группы совпадал с ритмом между группами.
   NB: гипотеза про float:right у .ds-range__value ОПРОВЕРГНУТА замеркой —
   float контейнится (.ds-field держит h=83), в ядро ДС не эскалируем. */
.tpl-group {
  border: 0;
  /* 🔴 МОЙ ДУБЛЬ СНЯТ ОДНИМ ВЫРАЖЕНИЕМ, БЕЗ ПОДБОРА ЧИСЛА (v0.178.0).
     Замер показал у величины ДВУХ авторов: перед строкой «Оплата за год» стояло
     40 = моя `margin-bottom` 16 + `row-gap` 24 ядрового контейнера. Снять свою
     16 «по месту» я отказался: канонного ритма «между шагами» у меня не было, а
     правка ради одного места меняла бы правило для всех брендов.
     ⇒ `c-design` обнулил КОНТЕЙНЕРНЫЙ зазор токеном (`--ds-calc-form-gap`, у
     forbion 0), и теперь мой отступ читает ТОТ ЖЕ токен: у бренда с канонным
     ритмом он 0, у прочих — прежние 16. Одно выражение вместо ветвления, и у
     величины снова ОДИН автор.
     ⚠ Четыре канонных промежутка (15 · 10 · 7 · 0) одним `gap` невыразимы по
     построению — они УБЫВАЮТ. Ставить их полями элементов буду отдельно и по
     замеру, а не подбором: пока снят только дубль. */
  margin: 0 0 var(--ds-calc-form-gap, var(--ds-space-4));
  padding: 0;
  display: grid;
  /* 🔴 ЗАЗОР ОПЦИЙ — ДВА РАЗНЫХ ЧИСЛА ПО ОСЯМ (v0.176.0; токены
     `--ds-calc-option-row-gap` 6 / `--ds-calc-option-col-gap` 12,
     DESIGN v0.229.0). У нас было 24 по обеим.
     🔴 ОСЬ ВЫВЕДЕНА ИЗ ЧИСЕЛ, А НЕ ИЗ ЗАПИСИ «6/12»: высоты опций канона
     63·63·77·77·92·92 дают три ряда 63+77+92 = 232 при колонке 244; разница 12
     это ДВА междурядья по 6 ⇒ вертикальный 6, горизонтальный 12. Другого
     разложения у двенадцати здесь нет.
     ⚠ Шестёрку НЕ пишу как `calc(var(--ds-space-3) / 2)`, хотя арифметически
     это те же 6: такая запись утверждала бы, что канонная шестёрка ЕСТЬ
     половина двенадцати, а замера на это нет ни у меня, ни у c-design.
     Совпадение значений не есть совпадение ролей.
     ⚠ Фолбэки — прежние 24: бренды без токенов не двигаются. */
  row-gap: var(--ds-calc-option-row-gap, var(--ds-space-6));
  column-gap: var(--ds-calc-option-col-gap, var(--ds-space-6));
  /* Кегль опций — роль поля (канон 13). Ставлю НАСЛЕДОВАНИЕМ на своём носителе,
     а не правилом на `.ds-field`: чужой компонент я не переопределяю, а
     наследование — законная композиция. Фолбэк `inherit` = прежние 16. */
  font-size: var(--ds-field-size, inherit);
}

/* 🔴 РИТМ КОЛОНКИ КАЛЬКУЛЯТОРА — ЧЕТЫРЕ УБЫВАЮЩИХ ПРОМЕЖУТКА (v0.180.0;
   токены `--ds-calc-gap-*`, DESIGN v0.233.0). Замер канона: 15 · 10 · 7 · 0.
   Одним `gap` это невыразимо по построению — потому и поля элементов.
   ⚠ ИМЕНА ТОКЕНОВ — ОТНОШЕНИЯ, а не номера шагов: номер сместится при вставке
   шага, отношение — нет. То же различение «роль против значения», которым мы
   закрывали поля секций и кегли карточек.
   ⚠ ВЫЧИТАНИЕ ЗАЗОРА СЕТКИ СНЯТО — ЗАМЕРОМ. Первая редакция писала
   `calc(токен − row-gap)`, считая, что между подписью и первым рядом зазор
   сетки участвует. Замер: 9 и 1 вместо 15 и 7 — `legend` не участвует в
   раскладке грида как обычный элемент, и зазор к нему не прибавляется.
   ⇒ поля равны токенам, без арифметики: «утверждение о своей раскладке»
   оказалось неверным утверждением, и его сняла проверка, а не рассуждение. */
.tpl-group > legend { margin-bottom: var(--ds-calc-gap-before-options, 0); }

.tpl-group--single > legend { margin-bottom: var(--ds-calc-gap-after-step-label, 0); }

.tpl-group + .tpl-group { margin-top: var(--ds-calc-gap-after-control, 0); }

/* ⚠ А после СЕТКИ опций у канона ноль — и это отдельный токен, не тот же:
   «после контрола» и «после сетки» разные отношения, и цена ошибки видна —
   первая редакция дала здесь 10 вместо 0. */
.tpl-group + .ds-field { margin-top: var(--ds-calc-gap-after-options, 0); }

/* 🔴 ПАРНАЯ РАСКЛАДКА ПОЛЕЙ (канон concept-izibuh-v2.3.0 `.calc-row`:
   grid-template-columns: 1fr 1fr — поля идут ПАРАМИ в ряд).
   Было `grid-template-columns: none` → каждое поле на ВСЮ ширину, и справа от
   узкого степпера оставалась пустая зона (её обвёл владелец; замер c-ops-2
   воспроизвёл на 768 и 1440, обе темы; на 360 расхождения нет).
   Легенда группы (`<legend>`) обязана занимать ряд целиком — иначе она встала
   бы первой ячейкой пары и утащила поле к себе в строку.
   Ниже 640 — столбик, как в каноне на 360. */
@media (min-width: 640px) {
  .tpl-group {
    grid-template-columns: 1fr 1fr;
    /* ⚠ ЗДЕСЬ ЖИЛ ВТОРОЙ ИСТОЧНИК ГОРИЗОНТАЛЬНОГО ЗАЗОРА: правило ниже по файлу
       перебивало токен, и замер дал 24 при объявленных 12 — ровно тот дефект,
       который `c-design` чинил утром у себя (`padding` сокращённым свойством
       после модификаторов). Своя же формулировка: величина, выраженная в двух
       местах, обязана браться из одного источника. */
    column-gap: var(--ds-calc-option-col-gap, var(--ds-space-6));
  }
  .tpl-group > legend,
  .tpl-group > .tpl-field--wide {
    grid-column: 1 / -1;
  }
}

/* Мелкая сноска-дисклеймер копи-колонки слайда карусели (hero.slides[].copy.note,
   эталон concept-v1.7.3: «не публичная оферта · условия акции — в договоре»).
   Шаблонный класс: компонента в ядре ДС нет; стиль — только на токенах. */
/* Значок плитки-факта: класс ЯДРА `.ds-stat__icon` с DESIGN v0.109.0.
   Мой временный `.tpl-stat__icon` (v0.100.0) СНЯТ — долг объявлялся дублем ДО
   работы и закрыт, как только c-design завёл слот. Правил здесь больше нет
   намеренно: размер и цвет держит ядро (1.25em от кегля ПОДПИСИ, приглушённый),
   и второе определение отстало бы при первой же их правке. */

.tpl-hero__note {
  margin: 0;
  font-size: var(--ds-fs-xs);
  color: var(--ds-text-muted);
}

/* Форма-заявка «cta-final»: ТЁМНАЯ ГРАДИЕНТНАЯ КАРТОЧКА (как в концепте) —
   заголовок + инфо-колонка + форма + дисклеймер внутри скруглённого блока.
   Фон — бренд-токен --ds-gradient-panel, текст белый. */
.tpl-cta-card {
  position: relative;
  overflow: hidden;
  border-radius: var(--ds-radius-xl, 30px);
  padding: var(--ds-space-12);
  background: var(--ds-gradient-panel, var(--ds-surface-inverse));
  color: var(--ds-text-inverse);
  box-shadow: var(--ds-shadow-lg);
}

.tpl-cta-card > .ds-section__title { color: var(--ds-text-inverse); margin-top: 0; }

/* Мобиль (аудит под адаптив ДС v0.13.0): крупный внутренний паддинг cta-карточки
   ужимается — на узком экране --ds-space-12 давил контент. Скролла не давал
   (сетка внутри — minmax(min(100%,…))), это ритм, а не переполнение. */
@media (max-width: 479px) {
  .tpl-cta-card { padding: var(--ds-space-6); }
}

.tpl-cta {
  display: grid;
  /* auto-fit: две колонки, как только помещаются (~2×22rem), иначе одна. */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr));
  gap: var(--ds-space-8);
  align-items: start;
}

@media (min-width: 1024px) {
  .tpl-cta { gap: var(--ds-space-12); }
}

/* 🔴 ОДИН МЕХАНИЗМ ОТСТУПА, А НЕ ДВА (v0.170.0; находка моя, при разборе
   левой плиты по слагаемым). Было: у носителя `gap: 24` И у детей свои
   `margin-top` 12 и 16 — между списком и сноской выходило 36 там, где задумано
   одно расстояние; в сумме промежутки давали 100 против канонных 34.
   Это тот же дефект, что `c-design` чинил утром в ядре (зазор во флексе и в
   потоке), только моей рукой. **Величина, выраженная в двух местах, обязана
   браться из одного источника** — поэтому поля детей сняты, расстояние держит
   ТОЛЬКО зазор носителя. */
.tpl-cta__info {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-6);
}

/* ⚠ ОБЛАСТЬ — ТОЛЬКО ПЛИТА СПЛИТА, и это поймано замером соседа. Первая
   редакция меняла зазор у `.tpl-cta__info` вообще — а этот же класс живёт и в
   НЕ-сплитной форме, которой пользуются izibuh и tezum: замер показал у izibuh
   секцию заявки короче на 16 px (два зазора по 8). Разбор канона снят с ПЛИТЫ,
   значит и правка принадлежит плите. */
/* ⚠ И селектор — по ЗАМЕРУ РАЗМЕТКИ, а не по её образу в голове: плита это
   ОДИН узел с тремя классами (`tpl-cta__info tpl-cta__card ds-card--plate`), а
   не `__card > __info`. Вложенный селектор не совпал ни с чем и молча ничего не
   делал — правило было, эффекта не было. */
.tpl-cta--cards > .tpl-cta__info { gap: var(--ds-space-4); }

.tpl-cta--cards > .tpl-cta__info > .tpl-perks__note,
.tpl-cta--cards > .tpl-cta__info > .tpl-perks__total { margin-top: 0; }

/* Заголовок левой плиты (`form.aside.title`, v0.170.0). Своих чисел не несёт:
   кегль и вес — ролевые, поле снизу даёт зазор носителя (один механизм). */
.tpl-cta__title { margin: 0; font-size: var(--ds-fs-lg); font-weight: var(--ds-fw-bold); }

.tpl-cta__lead { margin: 0; max-width: 34rem; opacity: .82; }

/* Поля формы на тёмной карточке: белые инпуты, светлые подписи/текст */
.tpl-cta-card .ds-field__label { color: var(--ds-text-inverse); }
.tpl-cta-card .ds-checkbox { color: rgba(255, 255, 255, .72); }
.tpl-cta-card .ds-form a,
.tpl-cta-card .tpl-cta__disclaimer a { color: #fff; text-decoration: underline; }

/* Акция-купон на тёмной карточке: пунктирная светлая рамка (как в концепте) */
.tpl-promo {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
  position: relative;
  padding: var(--ds-space-6);
  margin-top: var(--ds-space-3);
  border-radius: var(--ds-radius-lg);
  border: 2px dashed rgba(255, 255, 255, .4);
  background: rgba(255, 255, 255, .07);
}

.tpl-promo__tag {
  align-self: flex-start;
  font-size: .8em;
  font-weight: 700;
  color: var(--ds-color-on-primary);
  background: var(--ds-color-primary);
  padding: var(--ds-space-1) var(--ds-space-3);
  border-radius: var(--ds-radius-full);
}

.tpl-promo__headline { color: #fff; }
.tpl-promo__note { color: rgba(255, 255, 255, .75); font-size: .95em; }

/* Контакты в инфо-колонке */
.tpl-contacts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-6);
}

.tpl-contact { display: flex; flex-direction: column; }
.tpl-contact b { color: #fff; }
.tpl-contact span { color: rgba(255, 255, 255, .6); font-size: .9em; }

.tpl-cta__disclaimer {
  grid-column: 1 / -1;
  margin: var(--ds-space-8) 0 0;
  padding-top: var(--ds-space-4);
  border-top: 1px solid rgba(255, 255, 255, .14);
  color: rgba(255, 255, 255, .6);
  font-size: .85em;
}

/* Подвал compare: цена + CTA под колонками (напр. «Главбух» в концепте) */
.tpl-compare-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--ds-space-4);
  margin-top: var(--ds-space-8);
}

.tpl-compare-foot__note { color: var(--ds-text-secondary); max-width: 40rem; }

/* Врезка-цитата (напр. «Слово руководителя»); цвета текста наследуются от секции
   (в т.ч. инверсной), роль приглушается opacity — работает на любом фоне */
.tpl-quote {
  display: flex;
  gap: var(--ds-space-6);
  align-items: flex-start;
  max-width: 52rem;
}

.tpl-quote__avatar {
  flex-shrink: 0;
  width: 3.5rem;
  height: 3.5rem;
  display: grid;
  place-items: center;
  border-radius: var(--ds-radius-full);
  background: var(--ds-color-primary);
  color: var(--ds-color-on-primary);
  font-weight: 700;
}

.tpl-quote__text { margin: 0 0 var(--ds-space-3); font-size: 1.15em; }
.tpl-quote__who { display: flex; flex-direction: column; }
.tpl-quote__role { opacity: .7; font-size: .9em; }

/* 🔴 ОБЩИЙ НОСИТЕЛЬ ПОЗИЦИОНИРОВАНИЯ (v0.244.0; найдено ИСПОЛНЕНИЕМ — оба
   баннера ОТДЕЛЬНЫМИ `position: fixed` легли на одно место экрана, и при
   обоих ключах разом (`integrations.metrika` + `integrations.notice`)
   второй перехватывал pointer-events у кнопок первого. Playwright поймал
   как таймаут клика — статика такое не видит в принципе, она не знает,
   ГДЕ окажутся два узла друг относительно друга).
   ⇒ Позиционирование — ТОЛЬКО здесь, на общем контейнере; сами плашки —
   обычный поток внутри него (flex-column), стек, а не оверлей. Если видна
   ОДНА — вторая `[hidden]` не занимает места, JS-координация не нужна. */
.tpl-banner-stack {
  position: fixed;
  inset-inline: var(--ds-space-4);
  bottom: var(--ds-space-4);
  z-index: 40;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--ds-space-3);
}

/* Плашка согласия на аналитику (наряд integrator-c 01.08, consent-gate) И
   плашка-уведомление (Н4, 17.08) — ОДИН узел вида с двумя потребителями,
   РАЗНЫЙ жанр и разный JS-гейт (tpl-consent.js/tpl-notice.js, каждый свой
   ключ показа). SSG отдаёт [hidden] обоим — свой скрипт снимает его,
   только если выбора/показа ещё не было. */
.tpl-consent, .tpl-notice {
  max-width: 40rem;
  width: 100%;
  border-radius: var(--ds-radius-xl, 30px);
  background: var(--ds-surface-page);
  color: var(--ds-text-primary);
  box-shadow: var(--ds-shadow-lg);
}

/* Плашка согласия: отступы АСИММЕТРИЧНЫ, и это несущее, а не декор (поправка
   c-design 12.08 по эталону). Канон `.ckbar .in` даёт `13px 26px` — отношение
   ~1:2: у полосы во всю ширину экрана читаются именно ГОРИЗОНТАЛЬНЫЕ поля, при
   равномерных 16px текст прижимается к краю.
   ⚠ В каноне ДВА правила, а не одно: базовое `13px 26px` и под
   `@media (max-width:760px)` — `11px 16px`. c-design процитировала первое;
   второе я нашёл разбором по БАЛАНСУ СКОБОК (первая попытка — «ближайший
   предшествующий @media» — соврала: она читала позицию, а не вложенность).
   Беру оба: узкий экран не должен терять 48px ширины на поля. Ближайшее в
   нашей шкале: 12/24 базово и 12/16 на узком; брейкпоинт — контрактный 768,
   не канонные 760. Если c-design считает, что шаг лишний, — снять один блок. */
.tpl-consent__inner, .tpl-notice__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-3);
  padding: var(--ds-space-3) var(--ds-space-4);
}

@media (min-width: 768px) {
  .tpl-consent__inner, .tpl-notice__inner {
    gap: var(--ds-space-4);
    padding: var(--ds-space-3) var(--ds-space-6);
  }
}

.tpl-consent__text, .tpl-notice__text {
  flex: 1 1 16rem;
  margin: 0;
  font-size: var(--ds-fs-sm);
  color: var(--ds-text-secondary);
}

.tpl-consent__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-3);
}

/* ═══ ЛЕСТНИЦА ШАПКИ — В КОНЦЕ ФАЙЛА НАМЕРЕННО ═══
   Базовые правила полоски объявлены выше, специфичность у них та же (0,1,0),
   и при равном весе решает ПОРЯДОК. Первая редакция стояла до них — и правило
   «ниже 1320 марок нет» молча не работало: замер показал `display:flex` на всех
   ширинах, включая 940. Медиазапрос веса не добавляет, поэтому единственный
   честный способ — стоять позже, а не поднимать селектор. */

/* 🔴 ЛЕСТНИЦА ШАПКИ (v0.123.0; замер c-design, таблица порогов 3-окт).
   Пять из десяти канонных порогов — про ОДНУ шапку, и они складываются в
   лестницу: на каждом шаге уходит СВОЙ носитель, а не «всё сжимается».
       1320  марки мессенджеров     ниже — нет
       1250  чипы первого экрана    ниже — нет (сделано в v0.121.0)
       1180  ТЕКСТ телефона         ниже — остаётся один значок
       1040  ширина пилюли 88 %     ниже — ссылки ужимаются
        940  бургер                 НЕ ПОРОГ, А НОСИТЕЛЬ: интерактивный контрол
                                    со скриптом; порогом не выражается, ждёт решения
   ⚠ Правила бьют только по узлам, которые СУЩЕСТВУЮТ: у брендов без `topbar.links`
   или без телефона не меняется ничего — радиус держит наличие узла, не бренд. */
@media (max-width: 1320px) {
  .tpl-topbar__link { display: none; }
}

@media (max-width: 1180px) {
  /* Текст уходит, значок остаётся: у ссылки сохраняется цель и доступное имя
     (`tel:` + подпись значка), поэтому это раздевание, а не потеря. */
  .tpl-topbar__tel { font-size: 0; gap: 0; }
  .tpl-topbar__tel-icon { width: 18px; height: 18px; }
}

@media (max-width: 1040px) {
  /* Ниже порога пилюля перестаёт быть пилюлей: ширину отдаём контейнеру,
     чтобы ссылки не ужимались до нечитаемого. Число 88 % живёт в токене
     `--ds-header-max` у бренда — своего не завожу. */
  .ds-header--pill .ds-header__inner { max-width: 100%; }
}


/* 🔴 РАСКРЫТИЕ О СОСТАВЕ ЦЕНЫ В КАРТОЧКЕ (`hero.aside.details`, v0.132.0).
   ЗАМЕР КАНОНА (`concept-v0.30.20-draft`, блок `.price-tag > details.vat`):
     .vat       font-size 11px · color sub · border-top 1px solid · mt 12 · pt 9
     .vat sum   flex · gap 7 · align-items flex-start · line-height 1.45
     .vq        15×15 · круг · градиент soft→rz · вес 800 · кегль 10
     .vd        mt 7 · pt 7 · border-top 1px DASHED · line-height 1.5
   🔴 РАЗДЕЛИТЕЛЬ, КОТОРЫЙ ПРОСИЛИ ОТДЕЛЬНОЙ СТРОКОЙ НАРЯДА, — ЭТО `border-top`
   ЭТОГО ЖЕ БЛОКА. Не два узла, а один: линия принадлежит раскрытию и уезжает
   вместе с ним. Отдельный `<hr>` пришлось бы согласовывать с наличием ключа,
   и при пустом ключе висел бы разделитель, не делящий ничего.
   ⚠ ЧИСЛА КАНОНА, У КОТОРЫХ НЕТ СТУПЕНИ, ЗАМЕНЕНЫ ТОКЕНАМИ — И РАСХОЖДЕНИЕ
   ОБЪЯВЛЕНО: 11 → `--ds-fs-xs` (12), 9 → `--ds-space-2` (8), 7 → `--ds-space-2`.
   Своей ступени не завожу: c-design показал на зазоре 44, что у канона СВОЯ
   система чисел; заводить её по одному значению — второй источник. Понадобится
   точность — это токен назначения у бренда, как `--ds-hero-grid-gap`.
   Маркер `<summary>` снят: у канона его нет, а его место занимает кружок «?». */
.tpl-coupon__vat {
  margin-top: var(--ds-space-3);
  padding-top: var(--ds-space-2);
  border-top: 1px solid var(--ds-border-default);
  font-size: var(--ds-fs-xs);
  color: var(--ds-text-muted);
}

.tpl-coupon__vat-sum {
  display: flex;
  gap: var(--ds-space-2);
  align-items: flex-start;
  line-height: var(--ds-lh-base);
  cursor: pointer;
  /* Оба свойства нужны: `list-style` снимает маркер в Firefox, а
     `::-webkit-details-marker` — в WebKit. Одного не хватает. */
  list-style: none;
}

.tpl-coupon__vat-sum::-webkit-details-marker { display: none; }

/* Кружок «?» — метка, а не кнопка: нажимается вся строка `summary`.
   Цвет и фон — токены ядра; своего градиента не пишу. */
.tpl-coupon__vat-q {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.25em;
  height: 1.25em;
  border-radius: var(--ds-radius-full);
  background: var(--ds-surface-page);
  color: var(--ds-text-primary);
  font-weight: var(--ds-fw-bold);
}

.tpl-coupon__vat-body {
  margin: var(--ds-space-2) 0 0;
  padding-top: var(--ds-space-2);
  border-top: 1px dashed var(--ds-border-default);
  line-height: var(--ds-lh-base);
}

/* Оговорка «цена справочная» (`hero.aside.offer_note`/`slides[].offer_note`,
   v0.238.0). ЗАМЕР КАНОНА (`concept-latest.html:495`, `.offer-note`):
     font-size 9.5px · color sub · opacity .8 · margin-top 7 · line-height 1.45
   🔴 ИСПРАВЛЕНО 17.08 (карта остатка #hero, разбор волны 2): округление до
   `--ds-fs-xs` (12) казалось безобидным — тот же класс, что у `.tpl-coupon__vat`
   (11→12) — но НЕ БЫЛ: замер живой сборки дал 90px высоты узла против
   канонных 55 НА ТОМ ЖЕ ТЕКСТЕ (строка дословная, символ в символ) — round-up
   на 2.5px перегнал текст через границу переноса на ЛИШНЮЮ СТРОКУ, чего у
   `.vat` не случилось (там запас у текста был). ⚠ Урок: «округление до
   ближайшей ступени безопасно» — предположение, не факт; для строк вплотную
   к переносу его обязан проверять замер, не глаз. Держатель — брендовый
   токен с фолбэком РОВНО в канонное число (тот же приём, что у
   `--ds-card-price-mt/-mb`), а не ступень шкалы.
   7 → `--ds-space-2` (8) — здесь округление БЕЗОПАСНО (зазор, не текст,
   переносу не подвержен). */
.tpl-coupon__offer-note {
  margin-top: var(--ds-space-2);
  font-size: var(--ds-coupon-offer-note-size, 9.5px);
  line-height: var(--ds-coupon-offer-note-lh, 1.45);
  color: var(--ds-text-muted);
  opacity: .8;
}

/* 🔴 ПРОМО-СТРОКА НАД ТАБЛИЦЕЙ ТАРИФОВ (`tariff-accordion.promo_row[]`,
   v0.133.0; канон `.satrow`). ЗАМЕР КАНОНА:
     .satrow  flex wrap · gap 9 · margin-top 16
     .sat     пилюля · фон rgba(255,255,255,.72) · r 999 · padding 9/16 ·
              кегль 13 · вес 700
     .sat .sv вес 900 · tabular-nums · кегль 15 · цвет #3E7013
   Фон пилюли — `--ds-surface-picked` (ровно это значение и завёл c-design под
   роль «поднято к белому»): у промо-пилюли та же роль, что у выбранной
   карточки, — она лежит НАД цветной секцией, а не красит себя.
   ⚠ ЧИСЛА БЕЗ СТУПЕНИ ЗАМЕНЕНЫ ТОКЕНАМИ, РАСХОЖДЕНИЯ ОБЪЯВЛЕНЫ:
     9 → --ds-space-2 (8) · 16 → --ds-space-4 · 13 → --ds-fs-sm (14)
     15 → --ds-fs-md (16) · #3E7013 → --ds-color-accent (#478016)
   Своих чисел и своего зелёного не завожу: у канона СВОЯ система значений
   (c-design показал это на зазоре 44 — шесть вхождений). Понадобится точность —
   это токены назначения у бренда, как `--ds-hero-grid-gap`.
   🔴 `tabular-nums` — НЕ косметика: в строке стоят «22», «15», «−10 %», и при
   пропорциональных цифрах они прыгают по ширине между сборками с разными
   числами. Табличные держат ряд стабильным. */
.tpl-satrow {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
  margin: var(--ds-space-4) 0 0;
}

.tpl-sat {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
  padding: var(--ds-space-2) var(--ds-space-4);
  border-radius: var(--ds-radius-full);
  background: var(--ds-surface-picked);
  box-shadow: var(--ds-shadow-sm);
  font-size: var(--ds-fs-sm);
  font-weight: var(--ds-fw-bold);
  color: var(--ds-text-primary);
}

.tpl-sat__v {
  font-size: var(--ds-fs-md);
  /* `--ds-fw-price` (DESIGN v0.174.0) — токен НАЗНАЧЕНИЯ, а не ступень шкалы.
     Я сказал «шкала кончилась на 800, литерала не пишу»; c-design померил канон
     и завёл имя: вес 900 стоит там на 26 узлах (цена, крупные числа, словомарк),
     то есть это верхняя ступень ЕГО системы, а не выброс. Ядро = 800, forbion =
     900. Тот же ход, что с зазором 44: не всякое чужое число ложится в чужую
     шкалу — иногда ему нужно СВОЁ ИМЯ. */
  font-weight: var(--ds-fw-price);
  font-variant-numeric: tabular-nums;
  color: var(--ds-color-accent);
}

.tpl-sat__icon {
  flex: none;
  width: 1.15em;
  height: 1.15em;
  color: var(--ds-color-primary);
}

/* 🔴 НОМЕР ШАГА КАЛЬКУЛЯТОРА (`tpl-group__num`, v0.136.0; канон `.cstep > .cnum`).
   ЗАМЕР КАНОНА: кружок 25×25, `border-radius:50%`, кегль 12, вес 800, чернила
   #1D1D1B; фон — ВЕРТИКАЛЬНЫЙ градиент `#7CC43C → #63A427` плюс блик радиальным
   градиентом сверху-слева; сама строка шага — flex, зазор 10, кегль 13.5/800.
   🔴 ЗОНД ЕДВА НЕ СОВРАЛ ЗДЕСЬ ЖЕ: `backgroundColor` вернул `rgba(0,0,0,0)`, и
   первый замер я записал как «кружок прозрачный». `backgroundColor` НЕ ВИДИТ
   градиента — тот живёт в `background-image`. Тот же класс, что у `bbox`
   закрытого `<details>`: свойство честно отвечает про себя и молчит про предмет.
   ⚠ ГРАДИЕНТ СОБРАН ИЗ ИМЁН, А НЕ ИЗ ЛИТЕРАЛОВ: нижняя точка канона #63A427 —
   это ровно `--ds-color-primary-hover`, верхняя #7CC43C чуть светлее нашего
   `--ds-color-primary` (#70B62C). Расхождение верхней точки ОБЪЯВЛЕНО; своего
   цвета не пишу. Понадобится точность — это токен назначения у бренда,
   как `--ds-hero-grid-gap` и `--ds-fw-price`.
   ⚠ `aria-hidden` на номере: порядок шагов несёт сама последовательность
   legend'ов, а озвучивать «один» перед «Конфигурация» — читать вслух оформление. */
.tpl-group__num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 25px;
  height: 25px;
  margin-right: var(--ds-space-2);
  border-radius: var(--ds-radius-full);
  background: linear-gradient(180deg,
              var(--ds-color-primary), var(--ds-color-primary-hover));
  color: var(--ds-text-primary);
  font-size: var(--ds-fs-xs);
  /* `--ds-fw-black` (800), а НЕ `--ds-fw-price` (900): у канона `.cnum` весит
     ровно 800, и ступень шкалы совпадает точно. Первая редакция взяла свежий
     токен цены «потому что он тяжелее» — и разошлась с каноном там, где
     совпадение было даром. ⚠ Новый токен не делает старый неверным. */
  font-weight: var(--ds-fw-black);
  vertical-align: middle;
}

/* 🔴 ФОРМА ДВУМЯ КАРТОЧКАМИ (`form.split`, v0.141.0). ЗАМЕР КАНОНА (секция
   `.fin`, 1440): заголовок ЦЕНТРИРОВАН и стоит НАД сеткой; `.fin-grid` — две
   РАВНЫЕ колонки 495/495, зазор 30; обе карточки радиус 20.
   🔴 Расхождение здесь — ЧИСЛО НОСИТЕЛЕЙ, а не цвет и не размер: у нас одна
   карточка держала и шапку, и обе колонки. Поэтому ключ меняет структуру, а
   ПОВЕРХНОСТЬ карточек остаётся за c-design — он назвал её своей частью пункта.
   Здесь только геометрия: колонки, зазор, радиус, центровка шапки.
   ⚠ Числа канона без своей ступени заменены токенами, расхождение объявлено:
   зазор 30 → `--ds-space-8` (32), радиус 20 → `--ds-radius-lg`.
   Ниже 900 — столбик: у канона на узком карточки идут одна под другой. */
.tpl-cta--split > .ds-section__title,
.tpl-cta--split > .tpl-pretitle,
.tpl-cta--split > .ds-eyebrow,
.tpl-cta--split > .tpl-section-lead { text-align: center; }

.tpl-cta--split > .ds-eyebrow { display: block; }

/* 🔴 РЯД ПЛИТ УЖЕ КОНТЕЙНЕРА, И ЭТО ДВА ЧИСЛА, А НЕ ОДНО (v0.167.0; токены
   `--ds-cta-row-max` / `--ds-cta-row-gap`, DESIGN v0.213.0).
   Правило канона: `.fin-grid { grid-template-columns: 1fr 1fr; gap: 30;
   max-width: 1020; margin: 34px auto 0 }` — плиты 495 ‖ 495 при родителе 1020.
   ⚠ Я СЧИТАЛ ЭТО ОДНИМ НЕИЗВЕСТНЫМ и ошибся: из «плиты 495, контейнер 1088»
   вывел зазор 98 и объявил его «не ступенью шкалы». В уравнении было ДВА
   неизвестных — ширина ряда и зазор, — а ширину ряда я принял равной
   контейнеру, и весь остаток (68) свалился в зазор. Одно слагаемое всегда
   находится и всегда выглядит убедительно.
   ⚠ Фолбэки = прежний рендер: соседи токенов не объявляют, `max-width: none`
   оставляет ряд во всю ширину, зазор — прежней ступенью. */
.tpl-cta--cards {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--ds-cta-row-gap, var(--ds-space-8));
  margin-top: var(--ds-space-8);
  margin-inline: auto;
  max-width: var(--ds-cta-row-max, none);
  /* 🔴 `stretch`, а не унаследованный от `.tpl-cta` `start`: у канона правило
     несёт `align-items: stretch`, и это не вкус — плиты стоят РЯДОМ, и разная
     высота читалась бы как «правая не доделана». Замер до правки: 443 и 394.
     Соседей не двигает по построению: `.tpl-cta--cards` рождается только при
     `form.split`, а izibuh и tezum его не объявляют (проверено по конфигам). */
  align-items: stretch;
}

@media (min-width: 901px) {
  .tpl-cta--cards { grid-template-columns: 1fr 1fr; }
}

/* 🔴 РАДИУС — ТОКЕН РОЛИ, А НЕ СТУПЕНЬ ШКАЛЫ, и это поймано сборкой. Первая
   редакция взяла `--ds-radius-lg` «как крупный» и дала у forbion 6px: их шкала
   намеренно мелкая внизу (sm 2 · md 3 · lg 6), а роль карточки живёт отдельным
   именем `--ds-radius-card` (26). Тот же урок, что с зазором 44 и весом 900:
   у канона своя система, и попадать в неё надо ИМЕНЕМ РОЛИ.
   ⚠ Расхождение объявлено: у канона 20, у нас 26 — своей ступени не завожу. */
.tpl-cta__card {
  /* Поля плиты — канонные, и они РАЗНЫЕ ПО ОСЯМ: 26 сверху/снизу, 28 по бокам
     (`--ds-plate-pad`, DESIGN v0.215.0). Одним числом это не выражается, и мой
     прежний `--ds-space-8` (32) давал +12 к высоте плиты.
     ⚠ 26 совпадает с `--ds-container-pad`, но контейнерный отступ здесь брать
     нельзя: совпадение ЗНАЧЕНИЙ не есть совпадение РОЛЕЙ — свяжи их одним
     числом, и правка контейнера двинет плиту. */
  /* ⚠ ПОЛЯ СНЯТЫ (v0.207.0; перечисление who-wins, вердикт 4 из 17): ядро
     объявляет `.ds-card--plate { padding: var(--ds-plate-pad, …) }`, бренд —
     `--ds-plate-pad: 26px 28px`. Моё правило читало ТОТ ЖЕ токен, но со СВОИМ
     фолбэком — то есть было вторым автором с почти тем же значением.
     ⚠ Проверено ЭФФЕКТОМ, а не доводом: узлов `.tpl-cta__card` у izibuh и
     tezum ноль, поэтому мой фолбэк не держал ничьего прежнего рендера. */
  /* 🔴 ПЛИТА, А НЕ ПРОСТО КОЛОНКА (v0.164.0; партия 8, замер c-design: секция
     несёт ДВЕ БЕЛЫЕ КАРТОЧКИ 495×397 на светлом фоне, у нас их было 0).
     Узлы существовали и раньше — с радиусом и полями, но БЕЗ поверхности:
     прозрачная карточка с радиусом невидима, и «карточек 0» в чужом замере
     было правдой о рендере, а не о разметке.
     ⚠ Композиция на токенах, а не класс ядра `.ds-card`: тот несёт свой
     `display: flex` с `gap` и своё поле, и наложился бы на раскладку колонок.
     🔴 ПОВЕРХНОСТЬ — КЛАСС ЯДРА `ds-card--plate` (v0.165.0), и радиус тоже его:
     свой `--ds-radius-card` рядом с их `--ds-radius-plate` был бы вторым
     источником одной величины. Замер ролей у forbion показал, почему выбирать
     оттенок самому было нельзя: `raised` #ecf2f2 серая, `page` #f7faf3 сливалась
     бы с белой секцией, `--ds-shadow-card: none`. У канона плита не плоская
     вовсе — блик поверх перехода плюс многослойная тень (`--silk`). */
}

/* Строка итога перечня подарков (`form.aside.total`). Закрывает список суммой:
   без неё перечень читается как «вот что бывает», а не «вот что вы получаете
   бесплатно». Величина отдельным узлом — её вес задаётся ролью, не разметкой. */
.tpl-perks__total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--ds-space-4);
  margin: var(--ds-space-4) 0 0;
  padding-top: var(--ds-space-4);
  border-top: 1px solid var(--ds-border-default);
}

.tpl-perks__total-value {
  font-size: var(--ds-fs-lg);
  font-weight: var(--ds-fw-price);
  color: var(--ds-color-accent);
}

/* Примечание под перечнем подарков (`form.aside.perks_note`, v0.142.0).
   Стоит МЕЖДУ перечнем и строкой итога: условие относится к перечню, а не к
   секции, и в футноте внизу оно перестаёт быть ограничением обещания. */
.tpl-perks__note {
  margin: var(--ds-space-3) 0 0;
  font-size: var(--ds-fs-xs);
  color: var(--ds-text-muted);
  line-height: var(--ds-lh-base);
}

/* 🔴 СЕТКА СЕКЦИИ «ЧТО ВХОДИТ» (`cards.variant: "inc"`, v0.143.0).
   ЗАМЕР КАНОНА (`#inc`, 1440): ЧЕТЫРЕ колонки по 263, зазор 12, ВОСЕМЬ плиток;
   девятая позиция — не плитка, а ПОЛОСА во всю ширину.
   Общая сетка даёт три в ряд (6 колонок, дефолт `span 2`) — это верно для хаба,
   где канон просит «2 больших + 3 обычных», и неверно здесь. Число колонок —
   свойство ВАРИАНТА, поэтому правило висит на объявленном варианте секции.
   ⚠ Зазор канона 12 → `--ds-space-3`; расхождения нет, ступень совпала.
   ⚠ Полоса выражается уже существующим `featured`: у этой сетки он занимает
   ВСЮ строку (`1 / -1`), а не span 3, — потому что «во всю ширину» здесь и есть
   роль полосы, а не «карточка покрупнее». */
/* 🔴 РИТМ СЕКЦИИ «ЧТО ВХОДИТ» — РАСПРЕДЕЛЕНИЕ, А НЕ СУММА (v0.186.0; токены
   `--ds-inc-gap-before-grid` 18 / `--ds-inc-gap-before-strip` 34,
   DESIGN v0.243.0). Замер: у нас 32/24, у канона 18/34 — сумма почти та же
   (56 против 52), распределение ОБРАТНОЕ: канон прижимает сетку к заголовку и
   даёт воздух перед полосой.
   ⚠ Фолбэки — прежние 32 и 24: у izibuh и tezum эта секция своя, они не
   объявляют токенов и не двигаются. */
/* 🔴 СВОЁ ПОЛЕ СНЯТО — ЧИСЛО ИДЁТ ЧЕРЕЗ РУЧКУ ЯДРА (v0.188.0). Зазор держали
   ДВА поля: мой `margin-top` у сетки и `margin-bottom` заголовка в ядре; они
   схлопывались в `max(32, 18) = 32`. `c-design` завёл `--ds-section-title-gap`
   и прямо попросил НЕ обнулять его рядом с моим числом: тогда авторов снова
   двое, просто оба мои. ⇒ провожу свой токен в их ручку, поле снимаю. */
.tpl-section--inc { --ds-section-title-gap: var(--ds-inc-gap-before-grid, var(--ds-space-8)); }

/* 🔴 ТА ЖЕ РУЧКА ДЛЯ `calc` И `faq` (v0.208.0; замер канона c-design: 4 и 6
   против наших 32). Числа — их, ручка — их же (`--ds-section-title-gap`); моё
   здесь только ПРОВЕДЕНИЕ токена роли: у канона зазор под заголовком не одно
   число, а пять, поэтому «поставить 8 в бренде» закрыло бы большинство и
   сломало бы `#inc`, где мы уже стоим верно (18 = 18).
   ⚠ Фолбэк = прежние 32: пока бренд роль не объявил, класса нет вовсе (гейт
   в генераторе), а приедет правило — рендер не двинется. */
.tpl-section--calc { --ds-section-title-gap: var(--ds-calc-title-gap, var(--ds-space-8)); }
.tpl-section--faq { --ds-section-title-gap: var(--ds-faq-title-gap, var(--ds-space-8)); }

.tpl-grid--inc + .tpl-tail,
.tpl-grid--inc + .tpl-incstrip {
  margin-top: var(--ds-inc-gap-before-strip, var(--ds-space-6));
}

/* 🔴 ЗАЗОР СЕТКИ `#inc` — 12 НА ВСЕХ ШИРИНАХ (v0.203.0; наряд integrator-c2,
   замер c-design @390: у канона 12 на ОБЕИХ ширинах, у нас 7 × 24 = 168 против
   7 × 12 = 84, то есть +84 из +462 расхождения секции — при СОШЕДШЕМСЯ составе,
   одна колонка и восемь плиток против восьми).
   🔴 ЧИСЛО НЕ НОВОЕ: те же `--ds-space-3` уже стояли у этой сетки ВЫШЕ 1024 —
   я всего лишь запер их порогом, которого у канона нет. Порог, которого нет у
   источника, — это УТВЕРЖДЕНИЕ О ИСТОЧНИКЕ, а выглядит осторожностью: тот же
   класс, что моя вчерашняя «ветка одна из двух» и его собственный порог 900,
   проживший сутки и стоивший 425 px на 390.
   ⚠ Соседи не двигаются, и это ЗАМЕР, а не довод: `tpl-grid--inc` у izibuh и
   tezum — 0 вхождений, вариант `inc` объявляет только forbion. */
.tpl-grid--inc,
/* 🔴 ТОТ ЖЕ ЗАЗОР У `#srv` И ПО ТОЙ ЖЕ ПРИЧИНЕ (v0.204.0; наряд integrator-c2
   объединил два адреса в один предмет). Замер @390: у канона 36 = 3 × 12, у нас
   72 = 3 × 24 — и снова 12 были заперты порогом 1024, где стояли всё это время.
   ⚠ ОДИН предмет, ДВЕ секции: чинить порознь значило бы дважды править и
   дважды перемерять; поэтому оба варианта стоят одним селектором.
   ⚠ И это НЕ отмена довода v0.157.0 «не сводить `--inc` и `--promo` одним
   селектором»: там речь о ЧИСЛЕ КОЛОНОК (совпадение двух замеров, а не общий
   механизм) — оно осталось врозь, в своих медиазапросах. Общим стало ровно то,
   что общее по механизму: зазор плиточной сетки на всех ширинах. */
.tpl-grid--promo { gap: var(--ds-space-3); }

/* 🔴 ИСПРАВЛЕНО 17.08 (замер c-design: прокрутка +2 на 901, «содержимое
   вылезает на 28» — 232/179/214/216 против контейнера 849). Предыдущая
   запись здесь была ДОГАДКОЙ, объявленной как допущение («канонных чисел в
   коридоре 901…1023 нет ни у кого») — и оказалась НЕВЕРНОЙ ДОГАДКОЙ:
   `repeat(4, 1fr)` — это `minmax(auto, 1fr)`, а `auto` снизу равен
   `min-content`; когда четыре колонки не помещаются, дорожки распирает
   содержимым, сумма превышает контейнер, и это САМА ПРИЧИНА прокрутки, а не
   побочный эффект узкого экрана.
   🔴 ГРАНИЦЫ ЧИСЛОМ БОЛЬШЕ НЕТ — МЕХАНИЗМ КАНОНА ФЛЮИДНЫЙ, не ступенчатый.
   Прямое чтение источника (`concept-latest.html:637`, правило `.inc`):
   `display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));
   gap:12px`. У канона число колонок не назначено медиазапросом вовсе — оно
   вычисляется браузером из ширины контейнера, как у `.ds-trust__list` (та же
   формула, тот же довод: «текучая величина, не ступень и не медиазапрос»).
   c-design нашёл порог 1100 СВИПОМ (880…1400 через 20) и это число ВЕРНОЕ —
   но оно не отдельная константа, а следствие формулы: на нашем реальном
   контейнере (849 при вьюпорте 901) `repeat(auto-fill, minmax(250px, 1fr))`
   считает ТРИ колонки по 275 — до знака совпадает с замером c-design
   («канон 3 колонки × 275, контейнер 849»), прокрутка при этом 0. Проверено
   плейрайтом на живой сборке forbion (не только моделью): 901→3×275,
   1050→3×325, 1099→4×253, 1366→4×263 (последнее — прежнее число, не сдвинулось). */
@media (min-width: 901px) {
  .tpl-grid--inc { grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); }
  .tpl-grid--inc > * { grid-column: span 1; }
  .tpl-grid--inc > .tpl-card--featured { grid-column: 1 / -1; }
  /* 🔴 РАВЕНСТВО РЯДОВ ЗДЕСЬ НЕ КАНОН — НЕ ЗАВОДИТЬ СНОВА (v0.170.0).
     В v0.169.0 я выровнял ряды по решению integrator-c2 («ряд читается как
     ряд»). Решение отменено ЗАМЕРОМ КАНОНА: `c-ops` снял обе стороны одним
     зондом, в одной единице и с признаком видимости:
         канон #inc: 8 плиток · 4 колонки · ряды 138 | 178  — РАЗНЫЕ
         живое #inc: 8 плиток · 4 колонки · ряды 174 | 154  — тоже разные
     ⇒ выравнивание уводило бы ОТ макета, а не приближало, и стоило +20 к
     секции. `grid-auto-rows` снят.
     ⚠ Запись оставлена намеренно: без неё «ряды разной высоты» прочтут как
     недоделку в третий раз. У канона второй ряд ВЫШЕ первого, у нас ниже —
     это следует за содержимым плиток (наши тексты короче), предметом не
     является. */
}

/* 🔴 ЗАЗОР СЕТКИ ХАБА (`cards.variant: "hub"`, v0.162.0; токен назначения
   `--ds-hub-grid-gap`, DESIGN v0.206.0).
   ЗАМЕР КАНОНА (c-design): `.hub` — те же ШЕСТЬ колонок, что у меня, но зазор 14:
   `537 = 3×169.66 + 2×14`, `353 = 2×169.66 + 14`. При моих 24 та же сетка даёт
   532/347 — то есть механизм совпадал, расходился только зазор.
   🔴 ПОЧЕМУ НЕ СТУПЕНЬ ШКАЛЫ. Ближайшие — 12 и 16, и любая из них сдвинула бы
   ШИРИНЫ КАРТОЧЕК, а они уже канонные. Зазор здесь не «расстояние вообще», а
   величина, из которой считаются ширины: подгонка к шкале сломала бы то, ради
   чего правка делается. Поэтому токен назначения, а не ступень.
   ⚠ Фолбэк = прежний зазор: izibuh и tezum токена не объявляют и не двигаются.
   Правило висит на ВАРИАНТЕ, а не на `.tpl-grid`: общий зазор — их же. */
/* 🔴 И ЗДЕСЬ ПОРОГ СНЯТ — ТРЕТИЙ СЛУЧАЙ ПОДРЯД (v0.205.0; замер канона
   c-design на ЧЕТЫРЁХ ширинах: `#hub` держит зазор 14 на 390 · 768 · 1024 ·
   1366, меняется только число колонок 1 → 6). У меня 14 стояли выше 1024, а
   ниже оставались 24 — то же «верное число, запертое порогом», что у `--inc`
   и `--promo`.
   📌 ПРАВИЛО КАНОНА, НАЗВАННОЕ ПО ТРЁМ СЛУЧАЯМ: он держит ЗАЗОР ПОСТОЯННЫМ,
   а РАСКЛАДКУ переменной. Мои пороги меняли обе величины разом, потому что
   заводились «под узкий экран вообще», а не под один предмет.
   ⚠ Фолбэк прежний: izibuh и tezum токена не объявляют, и у них зазор остаётся
   24 на всех ширинах — как и был (узлов `tpl-grid--hub` у них ноль, замерено). */
.tpl-grid--hub { gap: var(--ds-hub-grid-gap, var(--ds-space-6)); }

/* 🔴 СЕТКА СЕКЦИИ «СВОЙ СЕРВЕР» (`cards.variant: "promo"`, v0.157.0).
   ЗАМЕР КАНОНА (`c-design`, партия 7): ЧЕТЫРЕ карточки в ОДИН ряд, 263 каждая;
   у нас было 3 + 1, и лишний ряд — половина расхождения по высоте секции
   (124 у канона против 184 у нас; вторая половина — размещение значка,
   `ds-card--icon-inline`, ждёт подъёма пина).
   Число колонок — свойство ВАРИАНТА (прецедент `--inc`, v0.143.0), поэтому
   правило висит на объявленном варианте, а не угадывается по числу детей:
   `:has()` выбирал бы сетку ПО СОДЕРЖИМОМУ, то есть читал бы намерение из данных.
   🔴 ПОЧЕМУ НЕ ОДИН СЕЛЕКТОР С `--inc`, хотя числа совпали до пикселя (4×263,
   зазор 12). Совпадение здесь — ФАКТ ДВУХ ЗАМЕРОВ, а не общий механизм: у канона
   это две разные секции, и совпали они потому, что плиточная сетка на странице
   одна. Склей я селекторы — расхождение любой из секций завтра пришлось бы
   расклеивать, и правка выглядела бы «зачем-то разделил одинаковое».
   ⚠ У этой сетки `featured` НЕ применяется: в каноне промо-секции выделенной
   карточки нет. Если появится — она займёт всю строку, как у `--inc`, потому
   что «шире остальных» в четырёхколоночном ряду выражается только так. */
/* 🔴 ИСПРАВЛЕНО 17.08 — ТА ЖЕ ПРИЧИНА, ЧТО У `--inc` ВЫШЕ (c-design нашёл
   дефект у `#inc`, попросил проверить `--promo` тем же вопросом «сам не
   мерил»). Прямое чтение источника (`concept-latest.html:1809`, `#srv
   .promo-grid`): `style="grid-template-columns:repeat(auto-fit,
   minmax(215px,1fr))"` — флюидный механизм, не `repeat(4, 1fr)`. У промо
   видимой прокрутки не было (карточки этой секции однородны по контенту,
   дорожки не распирало), но СОСТАВ был неверный — 4 колонки вместо канонных
   3 на 901: проверено плейрайтом на живой сборке (не только моделью) —
   901→3×275 (совпадает с `--inc` на этой же ширине, у обеих секций один
   контейнер), 1366→4×263 (прежнее число, не сдвинулось, наряд v0.157.0
   «четыре карточки в один ряд» цел). */
@media (min-width: 901px) {
  .tpl-grid--promo { grid-template-columns: repeat(auto-fit, minmax(215px, 1fr)); }
  .tpl-grid--promo > * { grid-column: span 1; }
  .tpl-grid--promo > .tpl-card--featured { grid-column: 1 / -1; }
}

/* Подсказка поля формы (`form.hints`, v0.146.0). Связана с полем через
   `aria-describedby`: без этого она — просто текст рядом, и скринридер прочтёт
   её как отдельный абзац, не относя к полю. */
.tpl-field__hint {
  margin: var(--ds-space-1) 0 0;
  font-size: var(--ds-fs-xs);
  color: var(--ds-text-muted);
  line-height: var(--ds-lh-base);
}

/* 🔴 ЦЕНА КРУПНО В КАРТОЧКЕ ФОРМАТА (`cards.items[].price`, v0.149.0).
   ЗАМЕР КАНОНА (`.lp` в лесенке форматов): величина 25px/900 основными
   чернилами, ЮНИТ отдельным узлом 12.5px/600 приглушённым.
   ⚠ Числа канона без своей ступени заменены токенами, расхождения объявлены:
   25 → `--ds-fs-2xl` (28) · 900 → `--ds-fw-price` · 12.5 → `--ds-fs-xs` (12).
   Юнит НЕ наследует вес величины: у канона он легче и тише — это отдельная
   роль (единица измерения), а не часть числа. */
/* 🔴 ПОЛЯ ЦЕНЫ — 10/2, НЕ ОДИН ШАГ ШКАЛЫ (карта остатка #ladder 17.08, парная
   правка с `.tpl-card__go` ниже — c-design: порознь каждая ХУЖЕ, чем ничего,
   вместе закрывают @1366 РОВНО). Было `margin: --ds-space-2 0 0` = 8px/0px;
   канон — 10px сверху / 2px снизу. Числа НЕ ложатся ни на одну ступень шкалы
   (8/16/24…) — тот же класс, что у подвала-ссылки: узел новый, готового
   значения в шкале нет, фолбэк — замеренное число канона, а держатель —
   бренд-пакет через ИМЕНОВАННЫЙ токен, как только его туда положат. */
.tpl-card__price {
  margin: var(--ds-card-price-mt, 10px) 0 var(--ds-card-price-mb, 2px);
  font-size: var(--ds-fs-2xl);
  font-weight: var(--ds-fw-price);
  line-height: var(--ds-lh-tight);
  color: var(--ds-text-primary);
}

.tpl-card__price-unit {
  margin-left: var(--ds-space-1);
  font-size: var(--ds-fs-xs);
  font-weight: var(--ds-fw-semibold);
  color: var(--ds-text-muted);
}

/* 🔴 ПОДВАЛ-ССЫЛКА (`cards.items[].link_style: "link"`, v0.227.0).
   ЗАМЕР КАНОНА: `<span class="hgo">Подробнее <i>→</i></span>` — flex, gap 7,
   кегль 13, вес 800, цвет #3E7013, высота 15; у нас кнопка 32 + поле 12,
   то есть +29 на КАЖДОЙ карточке (c-design: 12 из них он снял своим токеном,
   17 — роль узла, мой слой).
   🔴 ЧИСЕЛ КАНОНА Я ЗДЕСЬ НЕ ПИШУ, И ЭТО НЕ ОСТОРОЖНОСТЬ, А ПРАВИЛО: вид
   диктует канон, держатель чисел — бренд-пакет. Обычный «нейтральный фолбэк =
   сегодняшнее значение» тут неприменим — узел НОВЫЙ, сегодняшнего значения у
   него нет. Поэтому фолбэки собраны из УЖЕ СУЩЕСТВУЮЩИХ ядровых токенов
   (`--ds-fs-sm` 13.5 · `--ds-fw-bold` · `--ds-color-accent` #478016 у forbion ·
   `--ds-space-2`), а рядом объявлены ЧЕТЫРЕ ИМЕНИ, которыми c-design положит
   канонные 13 · 800 · #3E7013 · 7 в бренд-пакет, ничего не прося у меня.
   Расхождение фолбэка с каноном объявляю числом: кегль 13.5 против 13,
   цвет #478016 против #3E7013. */
/* 🔴 КЕГЛЬ ПОДВАЛА-ССЫЛКИ ОБЪЯВЛЯЮ Я (v0.233.0, решение integrator-c2 16.08).
   Канон — 13 (`span.hgo` @1366). Читатель стоял у меня с `v0.227.0`, держатель
   по таблице контракта (`docs/TOKENS-EXTERNAL.md:45`) — тоже шаблон, а
   ОБЪЯВЛЕНИЯ не было НИ У КОГО: на живом «читателей 1, объявлений 0» ⇒ работал
   фолбэк 14 (`--ds-fs-sm`). Токен, ПЕРЕЧИСЛЕННЫЙ в таблице, читался как токен
   ЗАДАННЫЙ — вторая половина нашего «читатель есть, значения нет».
   🔴 ПОЧЕМУ `html`, А НЕ `:root`. Мне нужно умолчание, которое бренд может
   перебить. `:root` — (0,1,0), столько же, сколько у бренд-темы, а `tpl.css`
   грузится ПОЗЖЕ ⇒ мой `:root` выигрывал бы у бренда по порядку и запирал бы
   значение. `html` — (0,0,1), ниже по СПЕЦИФИЧНОСТИ, и объявление бренда бьёт
   его при любом порядке подключения. Умолчание шаблона остаётся умолчанием.
   🔴 ОБЪЯВЛЯЮ ВСЕ ПЯТЬ, А НЕ ОДИН ЗАПРОШЕННЫЙ: таблица контракта держателем
   всех пяти называет шаблон, а объявлено не было НИ ОДНОГО — тот же «перечислен
   ≠ задан», просто впятером. Числа канона оттуда же: 13 · 800 · #3E7013 · 7 ·
   normal (замер `c-design` парой узлов @1366).
   ⚠ ЦВЕТ — САМОЕ СПОРНОЕ ЗДЕСЬ, и я это называю: `#3E7013` НЕ равен акценту
   forbion (`#478016`), то есть в шаблоне лежит бренд-число. Держу его только
   потому, что держателя нет; **как только роль возьмёт второй бренд, цвет
   обязан уехать в бренд-пакет** — и уедет без единой правки у меня: объявление
   бренда выигрывает у `html` по специфичности.
   🔴 СНЯТО В v0.234.0: бренд объявил все пять (DESIGN v0.333.0), решение ГП —
   префикс `--ds-*` это пространство ядра и бренда, `--tpl-*` моё. Механизм
   отработал ровно как задумано: цвет уехал в пакет БЕЗ ЕДИНОЙ ПРАВКИ у меня,
   мои умолчания просто перестали применяться. Канонные числа остаются в
   фолбэках читателя ниже — фолбэк не объявление и с брендом не спорит. */

.tpl-card__go {
  /* БЛОЧНЫЙ flex, как у канона (`.hgo` — flex, 309 шириной): у строчного
     варианта строку задаёт СТРУТ родителя (подвал 12 + 24 = 36 при ссылке 16),
     и подвал не худеет. Замер: inline-flex 36 · flex 28 при канонных 15+12. */
  display: flex;
  align-items: center;
  /* Фолбэки — КАНОННЫЕ числа (13 · 800 · #3E7013 · 7, замер c-design парой
     узлов @1366), а не ближайшие ступени шкалы: ступени давали 14/700/#478016/8,
     то есть промах по всем четырём. Фолбэк не объявление — он не спорит с
     брендом (тот объявил все пять в DESIGN v0.333.0) и применяется только там,
     где держателя ещё нет. */
  gap: var(--ds-card-go-gap, 7px);
  font-size: var(--ds-card-go-size, 13px);
  font-weight: var(--ds-card-go-weight, 800);
  color: var(--ds-card-go-color, #3E7013);
  /* `normal` — ЗАМЕРЕННОЕ значение канона (`.hgo` line-height: normal), а не
     моё удобство. «Нейтральный фолбэк = прежний рендер» здесь неприменим:
     прежнего рендера у узла нет, наследуемые 1.5 давали бы строку 21 при
     канонных 15. */
  line-height: var(--ds-card-go-lh, normal);
  text-decoration: none;
}

/* Стрелка — курсив по умолчанию браузера, у канона `font-style: normal`.
   Это снятие браузерного умолчания, а не число макета. */
.tpl-card__go > i { font-style: normal; }

/* 🔴 СЛОЙ КАСАНИЯ 44 px — ТОТ ЖЕ ПРИЁМ, ЧТО У ЧИПА КОНФИГУРАЦИИ (v0.213.0).
   Кнопка давала палец 32 px, канонная ссылка — 15. Копировать 15 нельзя:
   у канона это макет, а у нас живой сайт, и «геометрия — нижняя граница, решает
   тап» — моя же запись. Слой невидим и НЕ участвует в раскладке (absolute), то
   есть высота подвала остаётся канонной: 44 берёт палец, 16 видит глаз. */
.tpl-card__go { position: relative; }

.tpl-card__go::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 100%;
  height: 44px;
  transform: translateY(-50%);
}

.tpl-card__go:hover { text-decoration: underline; }

/* 🔴 ЧИПЫ-СТАТУСЫ ПЕРВОГО ЭКРАНА (`hero.status_chips`, v0.151.0).
   ЗАМЕР КАНОНА: шесть чипов лесенкой (шаг 72 по вертикали, y 134…494), видимы
   на ≥1251 и `display:none` на ≤1250. Позиции держит компонент ядра
   (`ds-chip--s1…--s6`, DESIGN v0.184.0) — координат у меня нет ни одной.
   🔴 ПОРОГ ИМЕНЕМ, А НЕ КООРДИНАТОЙ. Число 1250 стоит И в конфиге (`hide_at`),
   И здесь; расходиться им нельзя, поэтому конфиг выбирает из ОБЪЯВЛЕННЫХ, а
   класс несёт то же число в имени. CSP не даёт эмитить медиазапрос под каждый
   конфиг — свободное число в ключе разъехалось бы с правилом молча.
   ⚠ Ровно это число однажды уже стояло в моём CSS: измеренное на ЭТИХ шести
   пилюлях, оно было применено к четырём строкам левой колонки и побочно гасило
   чип подарка на всех телефонах. Теперь оно рядом со своим предметом.
   ⚠ Контейнер — якорь позиционирования: у канона чипы висят в правой колонке
   первого экрана, а не в потоке. `aria-hidden` на контейнере: то же содержание
   несут чипы левой колонки и реквизиты, и дублировать его для скринридера
   значило бы читать первый экран дважды. */
/* 🔴 ПРАВЫЙ БЛОК ГЕРОЯ — САМ СЕТКА (v0.154.0; модель c-design
   `docs/MODEL-hero-three-columns.md`). У канона три колонки — это ОДНО правило,
   применённое ДВАЖДЫ: `.in { grid-template-columns: 1.15fr 1fr; gap: 44 }`
   висит и на обёртке героя, и на правом блоке `.shot`. В разметке не три
   колонки, а ДВЕ В ДВУХ: [текст | [карточка | чипы]].
   ЗАМЕР КАНОНА: 1366 → [558] 44 [486 → 236 | 205]; 1024 → [496] 44 [432 → 207 | 180].
   Отношение 1.15:1 держится на ОБОИХ уровнях, постоянен только зазор.
   ⚠ НИЖЕ 1250 ЧИПЫ СКРЫТЫ, А КОЛОНКА ОСТАЁТСЯ — и это не забытое правило:
   на 1024 карточка занимает 207 из 432, вторая половина пуста, и именно она
   держит вертикальный центр карточки при смене ширины. Уберу колонку вместе с
   чипами — карточка поедет по горизонтали на каждом пороге (предупреждение
   c-design; проверять буду замером на обеих сторонах порога).
   Зазор — тот же токен назначения, что у текстовой шахматки. */
@media (min-width: 901px) {
  .tpl-hero__aside {
    display: grid;
    grid-template-columns: 1.15fr 1fr;
    gap: var(--ds-hero-grid-gap);
    align-items: center;
  }
}

/* 🔴 НОСИТЕЛЬ КООРДИНАТ — ВЕСЬ ПРАВЫЙ БЛОК, А НЕ КОЛОНКА ЧИПОВ (v0.155.1).
   Ядро ставит чипы ДОЛЯМИ: `left: 53…75%`. Доли чего — сказано там же, в замере
   канона внутри ядра: «носитель 486 шириной: 258 · 321 · 354 · 309 · 367 · 258»,
   а 486 — это ВЕСЬ правый блок (карточка 236 + зазор 44 + колонка 205), не его
   вторая колонка. Якорем я сделал колонку (у нас 221), и каждая доля считалась
   от неё: 0.53·221 + отступ колонки = 415 от края блока вместо 258.
   ЗАМЕР ДО ПРАВКИ (forbion, 1366): лесенка уезжала на 126 px за правый край
   СВОЕГО ЖЕ блока, а на 1251…1366 — за край ВЬЮПОРТА: горизонтальная прокрутка
   +71 / +57 / +47 / +14. Шире 1440 её нет — не потому что верно, а потому что
   поля страницы шире промаха; чипы всё равно висели снаружи колонки.
   🔴 И РОВНО ЭТУ ПРОКРУТКУ Я ШЁЛ ЗАКРЫВАТЬ `overflow-x: clip`-ом. Клип срезал бы
   правые концы пилюль ВМЕСТЕ с прокруткой: дефект стал бы невидимым, а не
   исчезнувшим, — а прокрутка была единственным, что о нём кричало.
   ⚠ Колонка остаётся (она держит ширину карточки — условие канона), но носителем
   координат быть перестаёт.
   🔴 v0.159.0: СВОЁ ПРАВИЛО СНЯТО — носитель назван ЯДРОМ. `c-design` завёл
   `.ds-chip-anchor` (DESIGN v0.201.0) ровно после этого промаха: пока носитель
   жил в комментарии словом «носитель», выбирал его потребитель, и любой выбор
   выглядел законным. Держать своё `position: relative` рядом с их классом
   значило бы иметь ДВА источника у одной величины — они разъедутся молча в тот
   день, когда ядро сменит механизм. Класс выдаёт генератор (только при чипах). */

.tpl-schips {
  align-self: stretch;
  pointer-events: none;
}

@media (max-width: 1250px) {
  .tpl-schips--hide-1250 { display: none; }
}

/* Носитель полосы варианта `inc` (v0.169.0). Полоса вышла ИЗ СЕТКИ: иначе
   `grid-auto-rows: 1fr` растягивал её вместе с плитками (замер: 133 → 174), а
   `align-self: start` оставлял под ней ДЫРУ в 41 px — её ряд всё равно 1fr.
   Отступ — тот же, что зазор сетки: полоса читается как её продолжение. */
.tpl-incstrip { margin-top: var(--ds-space-3); }

/* 🔴 ПОЛОСА ПОД СЕТКОЙ (`cards.tail`, v0.153.0). ЗАМЕР КАНОНА (`.freebar`):
   1088×50, flex, зазор 26, фон #EDF6E3, радиус 28, три пары «подпись: текст».
   Полоса — НЕ плитка: у неё свой носитель и N пар в строке, тогда как плитка
   несёт одну пару и живёт по правилам сетки.
   ⚠ Числа канона токенами, расхождения объявлены: зазор 26 → --ds-space-6 (24),
   радиус 28 → --ds-radius-xl (у forbion ровно 28 — совпало), поля 16/26 →
   --ds-space-4 / --ds-space-6. Фон — роль «выделение» (--ds-surface-highlight),
   а не свой цвет: тот же зелёный, что у чипов конфигураций. */
.tpl-tail {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-6);
  margin-top: var(--ds-space-6);
  padding: var(--ds-space-4) var(--ds-space-6);
  border-radius: var(--ds-radius-xl);
  background: var(--ds-surface-highlight);
  color: var(--ds-text-primary);
  font-size: var(--ds-fs-sm);
}

.tpl-tail__label { margin-right: var(--ds-space-1); }

/* Подарок ВНУТРИ карточки цены (`hero.aside.gift`). У канона `.gift` — часть
   `.price-tag`, последней строкой; сосед карточки — это другой ключ. */
.tpl-coupon__gift {
  margin: var(--ds-space-2) 0 0;
  font-size: var(--ds-fs-xs);
  color: var(--ds-text-secondary);
  line-height: var(--ds-lh-base);
}

/* Подпись экрана у точек карусели (`hero.slide_labels`, v0.155.0).
   ЗАМЕР КАНОНА (`.slabel`): 11.5px/800, uppercase, трекинг 1.15, цвет
   приглушённый; стоит СРАЗУ ЗА точками, в одном ряду с ними.
   ⚠ Числа канона токенами, расхождения объявлены: 11.5 → --ds-fs-xs (12),
   800 → --ds-fw-black, трекинг 1.15 → 0.08em (у канона абсолютный).
   ⚠ `aria-live="polite"`: подпись меняется без перезагрузки и без действия
   пользователя (автосмена) — без объявления смена экрана для скринридера
   происходит молча. */
/* 🔴 СНЯТА ИЗ ПОТОКА (наряд c-design 18.08, замер @390: +26px давал именно этот
   узел — 18px текста + 8px внешнего отступа, В ПОТОКЕ, при том что точки РЯДОМ
   (`.ds-carousel__dots`) уже `position:absolute` токеном бренда). Контекст
   позиционирования (`.tpl-carousel-frame { position: relative }`) уже стоял
   заранее — та самая парная правка v0.229.0/DESIGN v0.318.0, комментарий над
   ней прямо называл это как ВТОРУЮ половину («снимет свой .tpl-slabel из
   потока»), только сделана она не была. `bottom` — тот же токен, что у точек,
   не отдельное число: одна строка, один источник высоты ряда. */
.tpl-slabel {
  position: absolute;
  bottom: var(--ds-carousel-dots-bottom, auto);
  right: 0;
  margin: 0;
  text-align: right;
  font-size: var(--ds-fs-xs);
  font-weight: var(--ds-fw-black);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ds-text-muted);
}
