/* ==========================================================================
   RELAID · /css/style.css
   Направление «Контур». Все значения — через var() из /css/tokens.css,
   который ОБЯЗАН подключаться первым. Хардкод цвета/радиуса/отступа = брак.

   ПОРЯДОК ФАЙЛА
   0. /* Все три семейства ВАРИАБЕЛЬНЫЕ: один файл на семейство+сабсет покрывает
   весь диапазон весов. Отдельных файлов на 400/500/600/700 нет — раньше здесь
   стояло 18 @font-face на несуществующие имена, прогон ловил 15 × 404.
   Файлы скачаны 26.08.2026 с fonts.gstatic.com, лицензии OFL-*.txt рядом. */
@font-face{
  font-family:'Geologica'; font-style:normal; font-weight:400 600; font-display:swap;
  src:url('/fonts/geologica-cyr.woff2') format('woff2');
  unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face{
  font-family:'Geologica'; font-style:normal; font-weight:400 600; font-display:swap;
  src:url('/fonts/geologica-lat.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'Onest'; font-style:normal; font-weight:400 700; font-display:swap;
  src:url('/fonts/onest-cyr.woff2') format('woff2');
  unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face{
  font-family:'Onest'; font-style:normal; font-weight:400 700; font-display:swap;
  src:url('/fonts/onest-lat.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'JetBrains Mono'; font-style:normal; font-weight:400 500; font-display:swap;
  src:url('/fonts/jetbrainsmono-cyr.woff2') format('woff2');
  unicode-range:U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}
@font-face{
  font-family:'JetBrains Mono'; font-style:normal; font-weight:400 500; font-display:swap;
  src:url('/fonts/jetbrainsmono-lat.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ==========================================================================
   1. СЕМАНТИЧЕСКИЕ АЛИАСЫ
   Собственных значений здесь нет — только комбинации токенов.
   Мобильный вертикальный ритм — СВОЯ шкала, меньшая (§8.2a стандарта),
   а не доля от десктопной.
   ========================================================================== */
:root{
  --sec-py:   var(--s-8);   /* 64px — вертикальный отступ секции внутри блока */
  --sec-gap:  var(--s-6);   /* 32px — зазор между рядами внутри секции */
  --burger:   44px;         /* тач-цель Apple HIG, не «размер» дизайна */
}

/* ==========================================================================
   2. RESET — WEB-СТАНДАРТ §1.2
   ========================================================================== */
*,*::before,*::after{ box-sizing:border-box; margin:0; padding:0 }

html{
  overflow-x:hidden;
  scroll-behavior:auto;              /* НЕ smooth — ломает ScrollTrigger */
  -webkit-text-size-adjust:100%;
}

body{
  background:var(--page);
  color:var(--ink);
  font-family:var(--f-body);
  font-size:var(--t-body);
  line-height:var(--lh-body);
  overflow-x:hidden;
  -webkit-font-smoothing:antialiased;
  -moz-osx-font-smoothing:grayscale;
}

img,video,svg{ max-width:100%; display:block }
a{ color:inherit; text-decoration:none }
button{ border:none; background:none; cursor:pointer; font:inherit; color:inherit }
ul,ol{ list-style:none }
table{ border-collapse:collapse; width:100% }
:focus-visible{ outline:2px solid var(--acc-hi); outline-offset:3px; border-radius:var(--s-1) }
::selection{ background:var(--acc); color:var(--acc-ink) }

/* ==========================================================================
   3. БАЗОВАЯ ТИПОГРАФИКА
   Заголовки — только Geologica 500 (600 в логотипе).
   ========================================================================== */
h1,h2,h3{ font-family:var(--f-disp); font-weight:500; letter-spacing:-.01em }
h4,h5,h6{ font-family:var(--f-body); font-weight:600 }

h1{ font-size:var(--t-hero); line-height:var(--lh-tight); letter-spacing:var(--tr-hero) }
h2{ font-size:var(--t-h2);   line-height:var(--lh-head) }
h3{ font-size:var(--t-h3);   line-height:1.35 }
h4{ font-size:var(--t-h4);   line-height:1.3 }

p{ margin:0 }
p + p{ margin-top:var(--s-3) }
b,strong{ font-weight:600 }
small{ font-size:var(--t-small) }

.lead{ font-size:var(--t-lead); line-height:1.55; color:var(--soft) }
.blk--dark .lead{ color:var(--soft-i) }

.kicker{
  font-family:var(--f-mono); font-size:var(--t-micro); font-weight:400;
  letter-spacing:.12em; text-transform:uppercase; color:var(--acc);
  margin:0 0 var(--s-4);
}
.blk--dark .kicker{ color:var(--acc-hi) }

.muted{ color:var(--soft) }
.blk--dark .muted{ color:var(--soft-i) }

.mono{ font-family:var(--f-mono); font-variant-numeric:tabular-nums }

/* Текстовая колонка не длиннее 62 символов, центрированный тезис — 26 */
/* 🔴 56ch, а не 62. Тот же пересчёт, что у --measure: 1ch в Onest — это
   1,257 знака кириллицы, значит 62ch давали 78 знаков в среднем и до 93
   на отдельных строках. 56ch = 70 знаков — середина нормы 45–75. */
.measure{ max-width:56ch }
.measure--tight{ max-width:26ch }

/* Ссылка в тексте */
.link{ color:var(--acc); text-decoration:underline; text-underline-offset:.18em;
       text-decoration-thickness:1px; transition:color var(--d-fast) var(--e-out) }
.blk--dark .link{ color:var(--acc-hi) }

/* ==========================================================================
   4. LAYOUT — полотно и блоки
   ========================================================================== */

/* Полотно: поля --pad-page, блоки в столбик с зазором --gap-blk */
.shell{
  width:100%;
  max-width:calc(var(--container) + var(--pad-page) * 2);
  margin-inline:auto;
  padding:var(--pad-page);
  display:flex; flex-direction:column; gap:var(--gap-blk);
}
.stack{ display:flex; flex-direction:column; gap:var(--gap-blk) }

/* Крупный блок на полотне */
.blk{ position:relative; border-radius:var(--r-blk); overflow:hidden }
.blk--light{ background:var(--surf) }
.blk--dark{
  background-color:var(--bg);
  background-image:var(--grad);
  color:var(--ink-i);
  box-shadow:var(--sh-inset);
}
/* Зерно поверх градиента — убирает бандинг, даёт печатную фактуру */
.blk--dark::after,
.card--dark::after{
  content:""; position:absolute; inset:0; pointer-events:none;
  opacity:.05; mix-blend-mode:overlay; background-image:var(--grain);
}
.blk--dark > *,
.card--dark > *{ position:relative; z-index:1 }

/* Секция внутри блока */
.sec{ padding:var(--sec-py) var(--pad-blk) }
.sec--tight{ padding-block:var(--s-6) }
.sec + .sec{ border-top:1px solid var(--line) }
.blk--dark .sec + .sec{ border-top-color:var(--line-i) }

.sec__head{ margin-bottom:var(--sec-gap) }
.sec__head--center{ text-align:center; margin-inline:auto }

/* Сетки */
.grid{ display:grid; gap:var(--col-gap) }
.grid--2{ grid-template-columns:repeat(auto-fit,minmax(min(100%,320px),1fr)) }
.grid--3{ grid-template-columns:repeat(auto-fit,minmax(min(100%,260px),1fr)) }
.grid--4{ grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr)) }
.split{ display:grid; gap:var(--sec-gap); grid-template-columns:1fr }


















/* 🔴 ТЗ 33, класс E. Пара колонок с одинаковым устройством (подзаголовок
   и абзац) вставала на разные базовые линии, когда один подзаголовок
   переносился на вторую строку: 26–33 px расхождения на 768 и 1024
   (/biznes/obshchepit/). Подсетка отдаёт обеим колонкам одни и те же ряды.
   Ограничение «не больше двух блоков в колонке» обязательно: третий ребёнок
   при grid-row:span 2 ушёл бы в неявный ряд и лёг поверх второго. */
@media (min-width:768px){



    /* 🔴 ТЗ 34. Колонка-абзац из подсетки выведена. display:grid превращает
     <p> в грид-контейнер, и ссылка внутри предложения блокируется: на
     /dogovor/ «info@relaid.ru» встало отдельной строкой поверх хвоста
     фразы «…до подписания, разберём». Подсетка нужна колонкам ИЗ БЛОКОВ,
     а у абзаца блочных детей нет по определению. */



}

/* Иконка секции — линейная, stroke 1.5, 26px, цвет акцента */
.ico{ width:26px; height:26px; color:var(--acc); margin-bottom:var(--s-4) }
.blk--dark .ico{ color:var(--acc-hi) }

/* ==========================================================================
   5. КОМПОНЕНТЫ
   ========================================================================== */

/* ---------- 5.1 Скип-ссылка ---------- */
.skip{
  position:absolute; left:var(--s-4); top:calc(var(--s-4) * -6); z-index:var(--z-toast);
  background:var(--surf); color:var(--ink); border-radius:var(--r-pill);
  padding:var(--s-3) var(--s-5); font-size:var(--t-small); font-weight:500;
  box-shadow:var(--sh-lift);
}
.skip:focus{ top:var(--s-4) }

/* ---------- 5.2 Логотип ---------- */
.logo{ display:inline-flex; align-items:center; gap:var(--s-2); color:currentColor }
.logo__mark{ width:27px; height:27px; flex:0 0 auto; fill:currentColor }
.logo__word{
  font-family:var(--f-disp); font-weight:600; font-size:var(--t-small);
  letter-spacing:var(--tr-mark); line-height:1;
}

/* ---------- 5.3 ШТОРКА НАВИГАЦИИ ----------
   Белая пластина начинается ровно на верхней кромке тёмного блока.
   Стыки с тёмным — ВОГНУТЫЕ скругления r=--r-notch через radial-mask.
   Это вырез в блоке, а не панель поверх него.
   ------------------------------------------------------------------------ */
.topbar{
  position:relative; z-index:var(--z-notch);
  display:flex; align-items:flex-start; gap:var(--s-4);
  padding-inline:var(--s-5); min-height:46px;
}
.topbar__logo{ padding-block:var(--s-2); flex:0 0 auto }

.notch{
  position:relative; top:0; margin-inline:auto; flex:0 1 auto;
  height:42px; background:var(--surf);
  border-radius:0 0 var(--r-card) var(--r-card);
  display:flex; align-items:center; gap:var(--s-1);
  padding-inline:var(--s-2);
}
/* вогнутые углы: белый квадрат с вырезанной четвертью круга */
.notch::before,
.notch::after{
  content:""; position:absolute; top:0;
  width:var(--r-notch); height:var(--r-notch); background:var(--surf);
}
.notch::before{
  left:calc(var(--r-notch) * -1);
  -webkit-mask:radial-gradient(circle at 0 100%, #0000 0 var(--r-notch), #000 calc(var(--r-notch) + .5px));
          mask:radial-gradient(circle at 0 100%, #0000 0 var(--r-notch), #000 calc(var(--r-notch) + .5px));
}
.notch::after{
  right:calc(var(--r-notch) * -1);
  -webkit-mask:radial-gradient(circle at 100% 100%, #0000 0 var(--r-notch), #000 calc(var(--r-notch) + .5px));
          mask:radial-gradient(circle at 100% 100%, #0000 0 var(--r-notch), #000 calc(var(--r-notch) + .5px));
}

.notch__list{ display:flex; align-items:center; gap:var(--s-1) }
.notch__link{
  display:block; font-size:var(--t-small); color:var(--ink);
  padding:var(--s-2) var(--s-3); border-radius:var(--r-pill); white-space:nowrap;
  transition:background var(--d-fast) var(--e-out), color var(--d-fast) var(--e-out);
}
.notch__link[aria-current="page"]{ background:var(--surf-2); color:var(--acc) }

/* Крест и город внутри мобильной шторки — на десктопе не нужны.
   Прячем сам <li>, иначе пустой пункт продолжает съедать gap списка. */
.notch__mob{ display:none }
.notch__close,
.notch__city{ display:none }

/* Бургер: 44×44, показывается на ≤767 */
.burger{
  display:none; width:var(--burger); height:var(--burger); flex:0 0 auto;
  border-radius:var(--r-pill); align-items:center; justify-content:center;
}
.burger__box{ display:block; width:20px; height:14px; position:relative }
.burger__box i{
  position:absolute; left:0; width:100%; height:2px; border-radius:var(--r-pill);
  background:var(--ink);
  transition:transform var(--d-base) var(--e-inout), opacity var(--d-fast) var(--e-out);
}
.burger__box i:nth-child(1){ top:0 }
.burger__box i:nth-child(2){ top:6px }
.burger__box i:nth-child(3){ top:12px }
.burger[aria-expanded="true"] .burger__box i:nth-child(1){ transform:translateY(6px) rotate(45deg) }
.burger[aria-expanded="true"] .burger__box i:nth-child(2){ opacity:0 }
.burger[aria-expanded="true"] .burger__box i:nth-child(3){ transform:translateY(-6px) rotate(-45deg) }

/* 🔴 .topbar задаёт z-index, то есть создаёт КОНТЕКСТ НАЛОЖЕНИЯ: все z-index
   внутри него (шторка, оверлей) считаются относительно --z-notch, а не корня.
   Из-за этого cookie-полоса (--z-toast в корне) закрашивала открытое меню.
   Пока меню открыто — поднимаем весь контекст шапки над слоем тостов. */
.nav-open .topbar{ z-index:calc(var(--z-toast) + 2) }
.nav-open{ overflow:hidden }
.nav-open body{ overflow:hidden }

/* Оверлей мобильного меню */
.nav-ovl{
  position:fixed; inset:0; background:var(--bg); opacity:0; visibility:hidden;
  z-index:var(--z-modal);
  transition:opacity var(--d-base) var(--e-out), visibility 0s linear var(--d-base);
}
.nav-ovl.is-open{ opacity:.55; visibility:visible; transition-delay:0s }

/* ---------- 5.4 Переключатель города ---------- */
.city{
  display:inline-flex; align-items:center; gap:var(--s-2);
  padding:var(--s-2) var(--s-3); border-radius:var(--r-pill);
  font-size:var(--t-small); color:var(--ink-i); flex:0 0 auto;
  align-self:center; min-height:var(--burger);
  transition:background var(--d-fast) var(--e-out);
}
.city__ic{
  display:grid; place-items:center; width:26px; height:26px; border-radius:50%;
  background:var(--ink-i); color:var(--acc); flex:0 0 auto;
}
.city__ic svg{ width:14px; height:14px }
.city__cur{ font-weight:500 }

/* ---------- 5.5 КНОПКИ ----------
   Фирменная деталь — круглый кружок-стрелка 26px справа.
   ------------------------------------------------------------------------ */
.btn{
  display:inline-flex; align-items:center; justify-content:center; gap:var(--s-3);
  min-height:var(--burger);
  padding:var(--s-2) var(--s-2) var(--s-2) var(--s-5);
  border:1px solid transparent; border-radius:var(--r-pill);
  font-family:var(--f-body); font-size:var(--t-small); font-weight:500; line-height:1;
  text-align:left; cursor:pointer;
  transition:background var(--d-fast) var(--e-out), color var(--d-fast) var(--e-out),
             border-color var(--d-fast) var(--e-out), box-shadow var(--d-fast) var(--e-out);
}
.btn__ic{
  display:grid; place-items:center; width:26px; height:26px; flex:0 0 auto;
  border-radius:50%;
  transition:transform var(--d-fast) var(--e-spring),
             background var(--d-fast) var(--e-out), color var(--d-fast) var(--e-out);
}
.btn__ic svg{ width:12px; height:12px }

.btn--primary{ background:var(--btn); border-color:var(--btn); color:var(--acc-ink) }
.btn--primary .btn__ic{ background:var(--ink-i); color:var(--btn) }

.btn--ghost{ background:var(--surf); border-color:var(--line); color:var(--ink) }
.btn--ghost .btn__ic{ background:var(--surf-2); color:var(--btn) }

.btn--light{ background:var(--surf); border-color:var(--surf); color:var(--ink) }
.btn--light .btn__ic{ background:var(--btn); color:var(--ink-i) }

.btn:focus-visible{ outline:2px solid var(--acc-hi); outline-offset:3px }

.btn[disabled],
.btn[aria-disabled="true"]{
  background:var(--surf-2); border-color:var(--line); color:var(--soft);
  opacity:.6; cursor:not-allowed; pointer-events:none;
}
.btn[disabled] .btn__ic,
.btn[aria-disabled="true"] .btn__ic{ background:var(--line); color:var(--soft) }

.btn--wide{ width:100%; justify-content:space-between }
.btn-row{ display:flex; flex-wrap:wrap; gap:var(--s-3); align-items:center }

/* ---------- 5.6 ПОЛЯ ФОРМЫ ----------
   font-size:16px обязателен — меньше и iOS зумит страницу при фокусе.
   ------------------------------------------------------------------------ */
.form{ display:flex; flex-direction:column; gap:var(--s-4) }

.field{ display:block }
.field__lbl{
  display:block; font-size:var(--t-small); color:var(--soft);
  margin-bottom:var(--s-2);
}
.field__ctl{
  display:block; width:100%;
  font-family:var(--f-body); font-size:16px; line-height:1.4;
  min-height:var(--burger);
  padding:var(--s-3) var(--s-4);
  border:1px solid var(--line); border-radius:var(--r-fld);
  background:var(--surf); color:var(--ink);
  appearance:none; -webkit-appearance:none;
  transition:border-color var(--d-fast) var(--e-out), box-shadow var(--d-fast) var(--e-out);
}
textarea.field__ctl{ min-height:calc(var(--s-8) + var(--s-5)); resize:vertical }
.field__ctl::placeholder{ color:var(--soft); opacity:1 }
.field__ctl:focus-visible{ outline:none; border-color:var(--acc); box-shadow:0 0 0 3px var(--surf-2) }
.field__ctl:user-invalid{ border-color:var(--err) }
.field__err{ display:block; margin-top:var(--s-2); font-size:var(--t-small); color:var(--err) }
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button{ -webkit-appearance:none; margin:0 }

/* Форма на тёмном блоке */
.form--dark .field__lbl{ color:var(--soft-i) }
.form--dark .field__ctl{ background:var(--bg-2); border-color:var(--line-i); color:var(--ink-i) }
.form--dark .field__ctl::placeholder{ color:var(--soft-i) }
.form--dark .field__ctl:focus-visible{ border-color:var(--acc-hi); box-shadow:none }
.form--dark .consent{ color:var(--soft-i) }

/* Чекбокс согласия 152-ФЗ. Текст рядом сокращается, механизм согласия — нет. */
.consent{
  display:flex; align-items:flex-start; gap:var(--s-2);
  font-size:var(--t-small); line-height:1.5; color:var(--soft);
}
.consent__hit{
  flex:0 0 auto; display:grid; place-items:center;
  width:var(--burger); height:var(--burger);
  margin:calc(var(--s-3) * -1) 0 calc(var(--s-3) * -1) calc(var(--s-3) * -1);
}
.consent input[type="checkbox"]{
  width:20px; height:20px; margin:0; accent-color:var(--acc); cursor:pointer;
}
.consent a{ color:var(--acc); text-decoration:underline; text-underline-offset:.18em }
.form--dark .consent a{ color:var(--acc-hi) }

/* Ответ формы */
.form__note{ font-size:var(--t-small); line-height:1.5 }
.form__note[hidden]{ display:none }
.form__note--ok{ color:var(--ok) }
.form__note--err{ color:var(--err) }
.form--dark .form__note--ok{ color:var(--acc-hi) }
.form--dark .form__note--err{ color:var(--err) }

/* ---------- 5.7 КАРТОЧКИ ---------- */
.card{
  position:relative; display:flex; flex-direction:column; gap:var(--s-3);
  background:var(--surf); border:1px solid var(--line);
  border-radius:var(--r-card); padding:var(--s-5);
  transition:border-color var(--d-fast) var(--e-out), box-shadow var(--d-base) var(--e-out),
             transform var(--d-base) var(--e-out);
}
.card__title{ font-family:var(--f-disp); font-weight:500; font-size:var(--t-h4); line-height:1.25 }
.card__text{ font-size:var(--t-small); line-height:1.6; color:var(--soft); margin:0 }
.card__price{ font-family:var(--f-mono); font-size:var(--t-small); color:var(--acc); margin-top:auto }

/* Тёмная карточка — на экране максимум одна, она обозначает главное предложение */
.card--dark{
  overflow:hidden; border-color:transparent; color:var(--ink-i);
  background-color:var(--bg); background-image:var(--grad-card);
  box-shadow:var(--sh-inset);
}
.card--dark .card__text{ color:var(--soft-i) }
.card--dark .card__price{ color:var(--acc-hi) }

/* ---------- 5.8 СТАТЫ ---------- */
.stats{ display:flex; flex-wrap:wrap; gap:var(--s-6) var(--s-9); justify-content:center }
.stat{ display:block }
.stat__num{
  display:block; font-family:var(--f-disp); font-weight:500; font-size:var(--t-h2);
  line-height:var(--lh-head); letter-spacing:-.01em;
}
.stat__cap{
  display:block; font-size:var(--t-small); line-height:1.45;
  color:var(--soft); margin-top:var(--s-1); max-width:14ch;
}
.blk--dark .stat__cap{ color:var(--soft-i) }

/* ---------- 5.9 ПРАЙС-ТАБЛИЦА ---------- */
.price-wrap{ overflow-x:auto; -webkit-overflow-scrolling:touch }
.price-table{ min-width:420px; font-size:var(--t-small) }
.price-table th,
.price-table td{ text-align:left; padding:var(--s-3); border-bottom:1px solid var(--line) }
.price-table th{
  font-family:var(--f-mono); font-size:var(--t-micro); font-weight:400;
  letter-spacing:.1em; text-transform:uppercase; color:var(--soft);
}
.price-table td.num,
.price-table th.num{
  text-align:right; font-family:var(--f-mono); font-variant-numeric:tabular-nums;
  white-space:nowrap;
}
.price-table tbody tr:last-child td{ border-bottom:0 }
.price-table caption{ text-align:left; font-size:var(--t-small); color:var(--soft); padding-bottom:var(--s-3) }
.blk--dark .price-table th,
.blk--dark .price-table td{ border-bottom-color:var(--line-i) }
.blk--dark .price-table th{ color:var(--soft-i) }

/* ---------- 5.10 FAQ — аккордеон на <details>, работает без JS ---------- */
.faq{ display:flex; flex-direction:column }
.faq__item{ border-bottom:1px solid var(--line) }
.faq__item:first-child{ border-top:1px solid var(--line) }
.faq__q{
  display:flex; align-items:flex-start; gap:var(--s-4);
  padding:var(--s-4) 0; min-height:var(--burger);
  font-family:var(--f-body); font-size:var(--t-h4); font-weight:600; line-height:1.35;
  cursor:pointer; list-style:none;
  transition:color var(--d-fast) var(--e-out);
}
.faq__q::-webkit-details-marker{ display:none }
.faq__q::after{
  content:""; flex:0 0 auto; margin-left:auto; width:26px; height:26px;
  border-radius:50%; background:var(--surf-2);
  background-image:linear-gradient(var(--ink),var(--ink)),linear-gradient(var(--ink),var(--ink));
  background-size:11px 1.5px, 1.5px 11px;
  background-position:center, center;
  background-repeat:no-repeat;
  transition:transform var(--d-base) var(--e-inout), background-size var(--d-base) var(--e-out);
}
.faq__item[open] > .faq__q::after{ transform:rotate(90deg); background-size:11px 1.5px, 0 0 }
.faq__a{ padding:0 0 var(--s-5); max-width:62ch; color:var(--soft); font-size:var(--t-body) }
.blk--dark .faq__item,
.blk--dark .faq__item:first-child{ border-color:var(--line-i) }
.blk--dark .faq__a{ color:var(--soft-i) }

/* ---------- 5.10-бис СВОРАЧИВАНИЕ: видно три, остальное под кнопкой ----------
   Разметку и класс .clp-hidden ставит js/collapse.js, а не CSS: без скрипта
   все вопросы обязаны остаться видимыми (qa/nojs.mjs). Поэтому здесь только
   вид, и ни одного правила, которое что-то прячет само по себе.
   ------------------------------------------------------------------------ */
.clp-hidden{ display:none !important }
.clp-open .clp-hidden{ display:revert !important }

.clp-more{
  display:inline-flex; align-items:center; justify-content:center;
  margin:var(--s-5) 0 0;
  min-height:var(--burger);
  padding:var(--s-2) var(--s-6);
  border:1px solid var(--line); border-radius:var(--r-pill);
  background:var(--surf); color:var(--ink);
  font-family:var(--f-body); font-size:var(--t-small); font-weight:500; line-height:1;
  cursor:pointer;
  transition:background var(--d-fast) var(--e-out), border-color var(--d-fast) var(--e-out),
             color var(--d-fast) var(--e-out);
}
.clp-more:hover{ border-color:var(--acc); color:var(--acc) }
.clp-more:focus-visible{ outline:2px solid var(--acc-hi); outline-offset:3px }

/* На тёмной подложке кнопка берёт инверсные токены — фиксированных цветов
   в файле нет, палитра под reference lock ТЗ 29. */
.blk--dark .clp-more{
  background:transparent; border-color:var(--line-i); color:var(--ink-i);
}
.blk--dark .clp-more:hover{ border-color:var(--acc-hi); color:var(--acc-hi) }

/* ---------- 5.11 ХЛЕБНЫЕ КРОШКИ ---------- */
.crumbs{ display:flex; flex-wrap:wrap; align-items:center; gap:var(--s-2);
         font-size:var(--t-small); color:var(--soft) }
.crumbs li{ display:flex; align-items:center; gap:var(--s-2) }
.crumbs li + li::before{ content:"/"; color:var(--line) }
.crumbs a{ color:var(--soft); transition:color var(--d-fast) var(--e-out) }
.crumbs [aria-current="page"]{ color:var(--ink) }
.blk--dark .crumbs,
.blk--dark .crumbs a{ color:var(--soft-i) }
.blk--dark .crumbs li + li::before{ color:var(--line-i) }
.blk--dark .crumbs [aria-current="page"]{ color:var(--ink-i) }

/* ---------- 5.12 КАРТОЧКА КОНТАКТОВ ----------
   Наезжает на нижнюю границу героя. Контейнер overflow:visible,
   сам блок героя — overflow:hidden.
   ------------------------------------------------------------------------ */
/* Блок героя. Нижнее поле резервирует зону, куда наезжает .hero__cc:
   без него центрированный h1 сталкивается с карточкой на десктопе. */
.hero{ padding:var(--s-9) var(--pad-blk) }

.hero-wrap{ position:relative; overflow:visible; padding-bottom:var(--s-8) }
.hero__cc{
  position:absolute; left:var(--pad-blk); bottom:0; z-index:2;
  width:min(280px,52%);
  background:var(--bg-2); border:1px solid var(--line-i);
  border-radius:var(--r-card); padding:var(--s-4); color:var(--ink-i);
}
/* уголковые засечки в противоположных углах */
.hero__cc::before,
.hero__cc::after{
  content:""; position:absolute; width:var(--s-3); height:var(--s-3);
  border:1.5px solid var(--acc-hi);
}
.hero__cc::before{ top:var(--s-2); left:var(--s-2); border-right:0; border-bottom:0 }
.hero__cc::after{ bottom:var(--s-2); right:var(--s-2); border-left:0; border-top:0 }
/* ТЗ 33, сквозная правка 17. В карточке ровно три строки: телефон, почта,
   Телеграм. Кикер «Как со мной связаться:», кружки tg/wa/vk и обращение
   «Напишите …» сняты решением владельца — карточка сквозная и правится
   в одном месте. Иконка контурная, одного веса штриха, размером по строке:
   ширина задана в em, поэтому она следует кеглю и не требует своих правил
   на каждом брейкпоинте. */
.hero__cc-v{ font-size:var(--t-h4); font-weight:500; margin:0; word-break:break-word }
.hero__cc-v + .hero__cc-v{ margin-top:var(--s-3) }
.hero__cc-v a{ display:flex; align-items:center; gap:var(--s-3) }
.hero__cc-i{
  flex:0 0 auto; display:grid; place-items:center;
  width:1.15em; height:1.15em; color:var(--acc-hi);
}
.hero__cc-i svg{ width:100%; height:100% }

/* ---------- 5.13 ПОДВАЛ ---------- */
.foot{ font-size:var(--t-small) }
.foot__top{
  display:grid; gap:var(--sec-gap) var(--col-gap);
  grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr));
  padding:var(--sec-py) var(--pad-blk) var(--s-6);
}
.foot__brand{ display:flex; flex-direction:column; gap:var(--s-3); color:var(--ink) }
.foot__ttl{
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.12em;
  text-transform:uppercase; color:var(--soft); margin:0 0 var(--s-4);
}
.foot__list{ display:flex; flex-direction:column; gap:var(--s-2) }
.foot__list a{ display:inline-block; padding-block:var(--s-1); color:var(--ink);
               transition:color var(--d-fast) var(--e-out) }
.foot__req{
  display:grid; gap:var(--s-2) var(--s-4); margin:0;
  grid-template-columns:auto 1fr; color:var(--soft); line-height:1.5;
}
.foot__req dt{ color:var(--soft) }
.foot__req dd{ margin:0; color:var(--ink); font-family:var(--f-mono);
               font-variant-numeric:tabular-nums }
.foot__bottom{
  display:flex; flex-wrap:wrap; gap:var(--s-3) var(--s-5); align-items:center;
  padding:var(--s-5) var(--pad-blk);
  border-top:1px solid var(--line); color:var(--soft);
}
.foot__legal{ display:flex; flex-wrap:wrap; gap:var(--s-3) var(--s-5); margin-left:auto }
.foot__legal a{ padding-block:var(--s-1); transition:color var(--d-fast) var(--e-out) }

/* ---------- 5.14 ЮРИДИЧЕСКИЕ СТРАНИЦЫ ---------- */
.legal{ max-width:60ch;   /* 74ch = 93 знака кириллицы, см. --measure */ font-size:var(--t-body); line-height:var(--lh-body); color:var(--ink) }
.legal > * + *{ margin-top:var(--s-4) }
.legal h2{ font-size:var(--t-h3); margin-top:var(--s-7) }
.legal h3{ font-size:var(--t-h4); font-family:var(--f-body); font-weight:600; margin-top:var(--s-6) }
.legal p{ color:var(--soft) }
.legal ol,
.legal ul{ display:flex; flex-direction:column; gap:var(--s-2); color:var(--soft);
           padding-left:var(--s-5) }
.legal ol{ list-style:decimal }
.legal ul{ list-style:disc }
.legal li::marker{ color:var(--line) }
.legal a{ color:var(--acc); text-decoration:underline; text-underline-offset:.18em }
.legal dl{ display:grid; grid-template-columns:auto 1fr; gap:var(--s-2) var(--s-5); color:var(--soft) }
.legal dd{ margin:0; color:var(--ink) }
.legal__meta{
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.1em;
  text-transform:uppercase; color:var(--soft);
}

/* ---------- 5.15 COOKIE-ПОЛОСА (не модалка) ---------- */
.cookie{
  position:fixed; left:0; right:0; bottom:0; z-index:var(--z-toast);
  background:var(--surf); border-top:1px solid var(--line);
  box-shadow:var(--sh-lift);
  padding:var(--s-4) var(--pad-page);
  transform:translateY(110%);
  transition:transform var(--d-base) var(--e-out);
}
.cookie[hidden]{ display:none }
.cookie.is-on{ transform:none }
.cookie__in{
  max-width:var(--container); margin-inline:auto;
  display:flex; flex-wrap:wrap; align-items:center; gap:var(--s-3) var(--s-5);
}
.cookie__txt{ flex:1 1 320px; font-size:var(--t-small); line-height:1.5; color:var(--soft) }
.cookie__txt a{ color:var(--acc); text-decoration:underline; text-underline-offset:.18em }
.cookie__act{ display:flex; flex-wrap:wrap; gap:var(--s-3) }

/* ---------- 5.16 ПОЯВЛЕНИЕ СЕКЦИЙ ----------
   Прячем только когда JS точно жив: без JS контент виден всегда.
   stagger 60мс задаётся переменной --rv-i из main.js.
   ------------------------------------------------------------------------ */
.has-js [data-rv]{
  opacity:0; transform:translateY(16px);
  transition:opacity var(--d-slow) var(--e-out), transform var(--d-slow) var(--e-out);
  transition-delay:calc(var(--rv-i,0) * 60ms);
}
.has-js [data-rv].is-in{ opacity:1; transform:none }

/* 🔴 data-rv НЕ вешать на блок, внутри которого лежит .notch__list или .nav-ovl:
   transform у предка делает его containing block для position:fixed,
   и мобильная шторка «прилипнет» внутрь шапки вместо экрана.
   will-change здесь не ставим намеренно (§7 стандарта: не на всех подряд). */

/* ==========================================================================
   6. УТИЛИТЫ
   ========================================================================== */
.sr-only{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
}
.u-center{ text-align:center }
.u-mt-0{ margin-top:0 }
.u-hide{ display:none }
.u-nowrap{ white-space:nowrap }
.u-balance{ text-wrap:balance }
.u-flow > * + *{ margin-top:var(--s-4) }

/* ==========================================================================
   7. МЕДИАЗАПРОСЫ
   ========================================================================== */

/* ---------- 7.1 hover — только мышь. Просто :hover залипает на тач ----------
   🔴 Разрозненные правила отсюда СНЯТЫ ТЗ 35, §4.2. Здесь их было
   шестнадцать, и каждое придумывало своё: одно красило текст, другое фон,
   третье рамку. Замер по 23 страницам показал результат: 96 ссылок-стрелок
   `.card__more` из 115 — главный интерактивный элемент сайта — не реагировали
   на наведение вообще, вместе с 276 ссылками выпадающего меню и логотипом.
   Вся система наведения, нажатия и фокуса собрана одним разделом
   «КАНОН ТЗ 35 · НАВЕДЕНИЕ» в конце файла. Правил ховера в этом файле
   выше канона больше нет. */

/* ---------- 7.2 Тач-цели ≥ 44px ----------
   Чекбокс согласия исключён намеренно: его тач-цель даёт обёртка .consent__hit,
   а раздутый чекбокс ломает строку согласия (§8.2a, WCAG 2.5.8).
   ------------------------------------------------------------------------ */
@media (pointer:coarse){
  a:not(.crumbs a):not(.legal a):not(.consent a):not(.cookie__txt a),
  button,
  [role="button"],
  input:not([type="checkbox"]):not([type="radio"]),
  select,
  textarea{ min-height:var(--burger) }

  .notch__link{ display:flex; align-items:center; padding-inline:var(--s-4) }
  .foot__list{ gap:var(--s-3) }
  .foot__list a{ min-height:var(--burger); display:flex; align-items:center }

  .burger,.notch__close,.city{ min-width:var(--burger) }

  /* min-height не действует на строчный элемент — переводим в inline-flex */
  .hero__cc-v a,
  .foot__brand a,
  .foot__legal a{ display:inline-flex; align-items:center; min-height:var(--burger) }

  /* 🔴 ТЗ 31, ревью. Тот же случай, что тремя строками выше, и он был
     пропущен: у .card__more стоит min-height:44px из общего правила,
     но сам элемент строчный — и правило молча не работает. Замер на 375
     с эмуляцией касания: 31 ссылка «Подробнее →» высотой 17 px вместо 44
     на 12 страницах. Ширина у них большая, поэтому глазом не видно —
     видно только замером. */
  .card__more{ display:inline-flex; align-items:center; min-height:var(--burger) }

  /* крошки: цель добираем полями строки, ряд остаётся тонким */
  .crumbs a{ padding-block:var(--s-3) }
}

/* ---------- 7.3a ≤1139: средний десктоп ----------
   Полная шторка со всеми пунктами + логотип + город в 1024 не помещаются
   (замер: не хватало 7px). До 1140 ужимаем поля пунктов и убираем второй
   город из переключателя — он остаётся в aria-label и в подвале.
   ------------------------------------------------------------------------ */
@media (max-width:1139px){
  .topbar{ padding-inline:var(--s-4) }
  .notch{ gap:0; padding-inline:var(--s-1) }
  .notch__link{ padding-inline:var(--s-2) }
  .city__label{ display:none }
}

/* ---------- 7.3b 768–1023: шторка ужимается сильнее ----------
   Самое узкое место макета: шторка со всеми пунктами, логотип и город
   в одной строке. На 768 (первый пиксель, где бургера ещё нет) запас
   считанный, поэтому режем всё лишнее: поля топбара, зазоры пунктов
   и второй город в переключателе.
   ------------------------------------------------------------------------ */
@media (max-width:1023px){
  .topbar{ padding-inline:var(--s-3); gap:var(--s-2) }
  .notch__link{ padding-inline:var(--s-1); font-size:var(--t-micro);
                font-family:var(--f-body); letter-spacing:0; text-transform:none }
  .city{ padding-inline:var(--s-2); gap:var(--s-1) }

}

/* ---------- 7.4 ≤767: вогнутые углы OFF, обычная панель, бургер ----------
   Мобильный вертикальный ритм — своя, МЕНЬШАЯ шкала (§8.2a).
   ------------------------------------------------------------------------ */
@media (max-width:767px){
  :root{
    --sec-py:var(--s-6);    /* 64 → 32 */
    --sec-gap:var(--s-5);   /* 32 → 24 */
  }

  .topbar{ align-items:center; gap:var(--s-2); padding-inline:var(--s-4);
           min-height:calc(var(--burger) + var(--s-3)) }
  .topbar__logo{ margin-right:auto }

  /* шторка → обычная панель, вогнутые углы выключены */
  .notch{
    margin-inline:0; height:auto; padding:var(--s-1);
    border-radius:var(--r-card);
  }
  .notch::before,
  .notch::after{ display:none }

  .burger{ display:flex }

  /* Список шторки уезжает в панель. Открытие через clip-path, НЕ translateX. */
  .notch__list{
    position:fixed; top:0; right:0;
    width:min(340px,86vw); height:100svh; min-height:100%;
    z-index:calc(var(--z-toast) + 1);
    flex-direction:column; align-items:stretch; justify-content:flex-start;
    gap:var(--s-1);
    padding:calc(var(--burger) + var(--s-5)) var(--s-5) var(--s-6);
    background:var(--surf); overflow-y:auto;
    clip-path:inset(0 0 0 100%);
    transition:clip-path var(--d-base) var(--e-inout);
  }
  .notch__list.is-open{ clip-path:inset(0) }
  .notch__link{ font-size:var(--t-lead); font-weight:500; padding:var(--s-3) var(--s-4) }

  .notch__mob{ display:block }
  .notch__close{
    display:grid; place-items:center;
    position:absolute; top:var(--s-3); right:var(--s-3);
    width:var(--burger); height:var(--burger); border-radius:var(--r-pill);
    background:var(--surf-2); color:var(--ink);
  }
  .notch__close svg{ width:16px; height:16px }

  .notch__mob.notch__city{
    display:flex; align-items:center; gap:var(--s-2);
    margin-top:var(--s-5); padding:var(--s-3) var(--s-4);
    border-top:1px solid var(--line); color:var(--soft); font-size:var(--t-small);
  }

  .city{ padding-inline:var(--s-2) }
  .city__label{ display:none }

  /* Карточка контактов — в поток, без нахлёста, резерв под неё не нужен */
  .hero{ padding-block:var(--s-7) }
  .hero-wrap{ padding-bottom:0 }
  .hero__cc{
    position:static; width:auto; margin-top:var(--gap-blk);
    padding:var(--s-5);
  }

  .stats{ gap:var(--s-5) var(--s-6); justify-content:flex-start }
  .stat__cap{ max-width:18ch }







  .btn-row .btn{ width:100%; justify-content:space-between }

  .foot__req{ grid-template-columns:1fr }
  .foot__req dt{ margin-top:var(--s-2) }
  .foot__legal{ margin-left:0 }
  .legal dl{ grid-template-columns:1fr }

  /* Полоса cookie не должна съедать треть экрана: текст в две строки,
     кнопки в один ряд (§8.2a — юр-текст сворачивается в строку). */
  .cookie{ padding:var(--s-3) var(--s-4) }
  .cookie__in{ gap:var(--s-3) }
  .cookie__act{ flex-wrap:nowrap; width:100% }
  .cookie__act .btn{ flex:1 1 0; min-width:0; justify-content:center;
                     padding-inline:var(--s-3) }
  .cookie__act .btn__ic{ display:none }
}

/* ---------- 7.5 ≤559: узкий телефон ---------- */
@media (max-width:559px){
  .stats{ display:grid; grid-template-columns:1fr 1fr; gap:var(--s-5) }
  .sec{ padding-inline:var(--s-5) }
  .foot__top,
  .foot__bottom{ padding-inline:var(--s-5) }
  .hero__cc{ width:auto }
}

/* ---------- 7.6 prefers-reduced-motion — обнуляем движение ---------- */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms !important;
    animation-iteration-count:1 !important;
    transition-duration:.01ms !important;
    scroll-behavior:auto !important;
  }
  .has-js [data-rv]{ opacity:1; transform:none; transition:none }
}

/* ---------- 7.7 Печать ---------- */
@media print{
  .topbar,.cookie,.nav-ovl,.btn,.skip{ display:none !important }
  body{ background:var(--surf) }
  .blk{ break-inside:avoid; box-shadow:none }
  .blk--dark{ background:var(--surf); color:var(--ink) }
  .blk--dark::after{ display:none }

  /* 🔴 Найдено при сборке PDF прайса (ТЗ 27, правка 9): фон тёмной секции
     в печати становится белым, но потомки, которым цвет задан явно
     (--ink-i, --soft-i), оставались белым по белому — оглавление печаталось
     одними номерами, текст пунктов исчезал. Возвращаем цвет потомкам,
     плашки .plate при этом остаются тёмными и читаются своим белым. */
  .blk--dark :not(.plate):not(.plate *){ color:var(--ink) }
  .blk--dark .muted,
  .blk--dark .lead,
  .blk--dark figcaption{ color:var(--soft) }

  /* details в печати не раскрывается сам — оглавление и FAQ уехали бы
     заголовком без содержимого. */
  details{ display:block }
  details > :not(summary){ display:revert }

  /* 🔴 Найдено при сборке PDF прайса: reveal-сцены гасят элементы в opacity:0
     до того, как до них доедет ScrollTrigger. В печати он не доезжает никогда,
     и прайс печатался пустыми строками — тот же класс отказа, что и «мерить
     контраст до проигрывания сцен». В печати сцен нет: гасим их целиком.
     !important нужен, потому что GSAP пишет opacity инлайном. */
  [data-rv], [data-mo] > *, [data-mo] tbody tr,
  [data-mo] rect, [data-mo] text,
  .scheme__cone, .scheme__wave path, .scheme__link, .scheme__relay, .scheme__btn,
  .fact__n, .fact__c, .stat__num, .stat__cap{
    opacity:1 !important; transform:none !important;
    clip-path:none !important; visibility:visible !important;
    /* 🔴 Без этой строки правило выше не работает: у [data-rv] объявлен
       transition на opacity, а переход по спецификации сильнее !important.
       В момент включения печатных стилей opacity уезжает 0 → 1 переходом,
       и печать снимается с середины перехода, то есть почти с нуля.
       Замер «правило применилось» тут врёт: в каскаде оно выигрывает,
       на пикселе — нет. */
    transition:none !important; animation:none !important;
  }
}

/* ==========================================================================
   8. КОМПОНЕНТЫ ГЛАВНОЙ (/)
   Добавлено при сборке index.html. Собственных значений нет — только токены.
   Всё, что здесь лежит, переиспользуемо на внутренних страницах.
   ========================================================================== */

/* ---------- 8.1 Split: две колонки от 768, пропорции от 1024 (§8.3) ----------
   В базовом каркасе .split — стопка на всех ширинах. Здесь добавлены
   десктопные пропорции 50/50, 60/40 и 40/60.
   ------------------------------------------------------------------------ */
@media (min-width:768px){
  .split{ grid-template-columns:1fr 1fr; align-items:start }
  .split--60{ grid-template-columns:3fr 2fr }
  .split--40{ grid-template-columns:2fr 3fr }
}

/* ---------- ТЗ 33, класс A. Шапка секции как левая колонка ----------
   Восемь поясов на сайте были сужены классом sec--narrow: содержимое
   стояло колонкой 600–770 px, а плита под ним оставалась во всю ширину,
   и справа висело 380–560 px голого фона (замер на 1440 и 1200).
   Приём для них один и уже был в каркасе — .split--40. Здесь только
   снимается нижнее поле шапки: внутри ряда оно отбивало бы не от
   следующего блока, а от собственной колонки. */
/* 🔴 min-width:0 обязателен. У элемента грида min-width по умолчанию auto,
   и колонка отказывается сжиматься уже min-content своего содержимого:
   после перевода поясов на .split--40 таблица и список внутри правой колонки
   раздували ряд, и текст вылезал за пояс на 39–86 px (замер на 1440,
   /dogovor/ и /biznes/obshchepit/). Классический grid blowout. */
.split > *{ min-width:0 }
.split > .sec__head{ margin-bottom:0 }

/* ---------- 8.2 Ряд кнопок по центру ---------- */
.btn-row--center{ justify-content:center }

/* ---------- 8.3 Заголовок пониженного ранга ----------
   h2 в узкой секции: ранг документа сохраняется, кегль опускается до --t-h3.
   ------------------------------------------------------------------------ */
.h--sm{ font-size:var(--t-h3); line-height:1.35 }

/* ---------- 8.4 Ряд брендов оборудования ----------
   Начертания, а не логотипы: чужие товарные знаки картинками не кладём.
   ------------------------------------------------------------------------ */
/* 🔴 justify-content:space-between и gap var(--s-4)/var(--s-6) сняты 31.08.2026
   (контр-аудит 30.08, долг после К4). Ниже по файлу, в блоке правки 7, .brands
   определён второй раз — с flex-start и чипами; работало именно оно. Два
   определения одного класса в одном файле — мина: следующая правка ушла бы
   в верхнее и не дала эффекта. Раскладка целиком живёт в блоке правки 7. */
.brands{
  display:flex; flex-wrap:wrap; align-items:center;
}
.brands li{
  font-family:var(--f-disp); font-weight:500; font-size:var(--t-lead);
  color:var(--soft); letter-spacing:-.01em;
}

/* ---------- 8.5 Заглушка вместо изображения ----------
   Битую картинку не ставим: место под кадр держит CSS-плашка с подписью,
   что именно сюда встанет.
   ------------------------------------------------------------------------ */
.ph{
  display:grid; place-items:center; text-align:center;
  padding:var(--s-5); border-radius:var(--r-card);
  background:var(--surf-2); border:1px dashed var(--line);
  color:var(--soft); font-family:var(--f-mono); font-size:var(--t-micro);
  letter-spacing:.12em; text-transform:uppercase; line-height:1.7;
}
.ph--45{ aspect-ratio:4/5 }
.ph--43{ aspect-ratio:4/3 }
.blk--dark .ph{ background:var(--bg-2); border-color:var(--line-i); color:var(--soft-i) }

/* ---------- 8.6 Карточка: сумма-ориентир и строка-переход ---------- */
.card__sum{
  font-family:var(--f-mono); font-weight:500; font-size:var(--t-h3);
  font-variant-numeric:tabular-nums; letter-spacing:-.01em; color:var(--acc);
}
.card--dark .card__sum{ color:var(--acc-hi) }

.card__more{
  margin-top:auto; font-size:var(--t-small); font-weight:600; color:var(--acc);
  transition:color var(--d-fast) var(--e-out);
}
.card--dark .card__more{ color:var(--acc-hi) }

/* ---------- 8.6a Карточка раздела, которого ещё нет ----------
   Страницы /biznes/ и /partneram/ выходят релизом 2. Ссылки на них не ставим:
   битый URL хуже отсутствия перехода. Карточка перестаёт быть <a>, hover с неё
   снимается (иначе она обещает клик, которого нет), внутри — честная строка
   с переходом на реальные контакты. Значения — только из tokens.css. */
[data-card="soon"]{ background:var(--surf-2) }
.card__soon{
  margin-top:auto; font-size:var(--t-small); line-height:1.5; color:var(--soft);
}
.card__soon a{ color:var(--acc); text-decoration:underline; text-underline-offset:.18em }

/* ---------- 8.7 Нумерованный список ограничений ---------- */
.nlist{ display:grid; gap:var(--s-5); counter-reset:nl }
.nlist > li{
  display:grid; grid-template-columns:auto 1fr; gap:var(--s-4);
  counter-increment:nl;
}
.nlist > li::before{
  content:counter(nl,decimal-leading-zero);
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.12em;
  color:var(--acc); padding-top:.5em;
}
.nlist p{ color:var(--soft); max-width:62ch }
.nlist strong{ color:var(--ink) }

/* ---------- 8.8 Стеклянная карточка на тёмном ----------
   -webkit-backdrop-filter обязателен: без него Safari даёт плоскую заливку.
   ------------------------------------------------------------------------ */
.glass{
  background:rgba(255,255,255,.06);
  border:1px solid var(--line-i); border-radius:var(--r-card);
  padding:var(--s-5);
  /* 🔴 brightness в backdrop обязателен. Ядро градиента у тёмного блока стоит
     в правом нижнем углу (88% 94%), и карточка садится прямо на пик #7566FF:
     тихий текст --soft-i и ссылки --acc-hi на нём пропадают. Затемняем не
     цветом поверх, а самой подложкой — «дымчатое стекло». */
  -webkit-backdrop-filter:blur(9px) brightness(.28);
  backdrop-filter:blur(9px) brightness(.28);
}
/* Ссылка --acc-hi на затемнённом ядре всё равно даёт 3.4:1 — под юридическим
   текстом это провал AA. Внутри стекла ссылка белая, различается подчёркиванием. */
.glass .consent a{ color:var(--ink-i) }
/* Firefox/Safari без backdrop-filter: непрозрачная подложка тем же токеном */
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .glass{ background:var(--bg-2) }
}

/* ---------- 8.9 Строка реквизитов под текстом ---------- */
.req-line{
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.1em;
  line-height:1.9; color:var(--soft);
  /* 🔴 ТЗ 33, классы H и A. Строка набрана моноширинным, и там ch — это
     ровно один знак: общая мера в 52ch давала здесь 52 знака при норме 75,
     то есть колонку в 343 px внутри пояса 726 px. Для моноширинного текста
     мера задаётся в знаках напрямую. */
  max-width:min(100%, 75ch);
}
.blk--dark .req-line{ color:var(--soft-i) }

/* ---------- 8.10 Селект: своя стрелка вместо снятой appearance ----------
   🔴 Селектор обязан быть НЕ ЛЕГЧЕ .form--dark .field__ctl (0,2,0): та рулетка
   задаёт фон СОКРАЩЁННЫМ свойством background, а сокращённое сбрасывает
   background-repeat / position / size в initial. При более лёгком селекторе
   стрелка размножается плиткой по всему полю.
   ------------------------------------------------------------------------ */
.form .field__ctl--sel{
  padding-right:var(--s-8);
  background-repeat:no-repeat;
  background-position:right var(--s-4) center;
  background-size:12px 8px;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' fill='none' stroke='%236B7080' stroke-width='1.6'><path d='M1 1.5 6 6.5 11 1.5'/></svg>");
}
.form--dark .field__ctl--sel{
  padding-right:var(--s-8);
  background-repeat:no-repeat;
  background-position:right var(--s-4) center;
  background-size:12px 8px;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' fill='none' stroke='%239C9CC4' stroke-width='1.6'><path d='M1 1.5 6 6.5 11 1.5'/></svg>");
}

/* ---------- 8.11 Вертикальные интервалы внутри секции ----------
   Ряд после шапки секции и мелкий шаг между абзацем и рядом.
   ------------------------------------------------------------------------ */
.u-mt{ margin-top:var(--sec-gap) }
.u-mt-s{ margin-top:var(--s-5) }

/* ---------- 8.12 Статы героя ----------
   Хвостовые правила базового каркаса уводят статы влево и в две колонки —
   в центрированном герое они должны остаться по центру.

   🔴 Подпись стата белая, а не --soft-i. Ядро градиента стоит в правом нижнем
   углу блока, статы — самая нижняя строка героя: тихий --soft-i на пике
   #7566FF даёт 2.1:1. Белая подпись на том же месте — 5.5:1. Иерархию держит
   кегль (38px против 13px), а не понижение контраста.
   ------------------------------------------------------------------------ */
.hero .stat__cap{ margin-inline:auto; color:var(--ink-i) }
.blk--dark .hero .stat__cap{ color:var(--ink-i) }

@media (max-width:767px){
  .hero .stats{ justify-content:center }
}
@media (max-width:559px){
  .hero .stats{ justify-items:center }
  .hero .stat:last-child{ grid-column:1 / -1 }
}

/* ==========================================================================
   9. КОМПОНЕНТЫ СТРАНИЦ УСЛУГ (/kvartira/, /bystryy-start/)
   Добавлено при сборке страниц услуг. Собственных значений нет — только токены.
   Что уже есть в разделе 8 (главная), здесь НЕ переопределяется:
   .ph / .ph--45 / .ph--43, .field__ctl--sel, .nlist, .glass, .h--sm,
   .split--60, .card__sum, .card__more, .u-mt, .u-mt-s — берутся оттуда.
   ========================================================================== */

/* ---------- 9.1 Заглушка изображения: недостающие кадрирования ---------- */
.ph--32{  aspect-ratio:3/2 }
.ph--169{ aspect-ratio:16/9 }

/* ---------- 9.2 Шаги процесса ----------
   Номер в кружке слева, заголовок и мета-строка (срок, цена) справа.
   ------------------------------------------------------------------------ */
.steps{ display:flex; flex-direction:column; gap:var(--s-5) }
.step{
  display:grid; grid-template-columns:auto 1fr; gap:var(--s-2) var(--s-5);
  align-items:start;
  padding-top:var(--s-5); border-top:1px solid var(--line);
}
.step:first-child{ padding-top:0; border-top:0 }
.step__n{
  grid-row:span 2; display:grid; place-items:center;
  width:38px; height:38px; border-radius:50%;
  background:var(--surf-2); color:var(--acc);
  font-family:var(--f-mono); font-size:var(--t-small); line-height:1;
}
.step__t{ font-family:var(--f-disp); font-weight:500; font-size:var(--t-h4); line-height:1.3 }
.step__m{
  display:block; margin-top:var(--s-1);
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.12em;
  text-transform:uppercase; color:var(--acc);
}
.step__x{ font-size:var(--t-body); line-height:var(--lh-body); color:var(--soft);
          max-width:62ch; margin:0 }
.blk--dark .step{ border-top-color:var(--line-i) }
.blk--dark .step__n{ background:var(--bg-2); color:var(--acc-hi) }
.blk--dark .step__m{ color:var(--acc-hi) }
.blk--dark .step__x{ color:var(--soft-i) }

/* ---------- 9.3 Пункт без карточной обвязки ----------
   Заголовок + абзац в ячейке сетки — там, где рамка карточки была бы шумом.
   ------------------------------------------------------------------------ */
.pt__t{ font-family:var(--f-disp); font-weight:500; font-size:var(--t-h4);
        line-height:1.3; margin:0 0 var(--s-2) }
.pt__x{ font-size:var(--t-small); line-height:1.6; color:var(--soft); margin:0 }
.blk--dark .pt__x{ color:var(--soft-i) }

/* ---------- 9.4 Список-чеклист ----------
   Заполненная точка — подходит, полый круг — не подходит.
   Глифов «галочка/крестик» и эмодзи не используем (анти-slop чек стиля).
   ------------------------------------------------------------------------ */
.marks{ display:flex; flex-direction:column; gap:var(--s-3) }
.marks li{
  position:relative; padding-left:var(--s-6);
  font-size:var(--t-body); line-height:var(--lh-body); color:var(--soft);
  max-width:62ch;
}
.marks li::before{
  content:""; position:absolute; left:0; top:.55em;
  width:9px; height:9px; border-radius:50%; background:var(--acc);
}
.marks--no li::before{ background:transparent; border:1.5px solid var(--line) }
.blk--dark .marks li{ color:var(--soft-i) }
.blk--dark .marks li::before{ background:var(--acc-hi) }
.blk--dark .marks--no li::before{ background:transparent; border-color:var(--line-i) }

/* ---------- 9.5 Врезка-пояснение ---------- */









/* ---------- 9.6 Прайс-таблица: широкая, подстроки и итог ---------- */
.price-table--wide{ min-width:560px }
.price-table .sub td{ color:var(--soft) }
.price-table .sub td:first-child{ padding-left:var(--s-6) }
.price-table .tot td{ font-weight:600; color:var(--ink) }
.blk--dark .price-table .sub td{ color:var(--soft-i) }
.blk--dark .price-table .tot td{ color:var(--ink-i) }

/* ---------- 9.7 Кнопка-контур на тёмном ----------
   .btn--ghost белая и на тёмном блоке читается как вторая основная.
   Здесь нужна именно вторичная: прозрачная, по линии тёмного блока.
   ------------------------------------------------------------------------ */
.btn--line{ background:transparent; border-color:var(--line-i); color:var(--ink-i) }
.btn--line .btn__ic{ background:var(--line-i); color:var(--ink-i) }

/* ---------- 9.8 Селект в тёмной форме ----------
   🔴 Баг, найденный прогоном: в тёмной форме стрелка селекта размножалась
   по всему полю штриховкой. Причина — `.form--dark .field__ctl{background:…}`
   это ШОРТХЕНД: он сбрасывает repeat/position/size обратно в repeat/0 0/auto,
   и по специфичности (0,2,0) перебивает longhand-ы из `.field__ctl--sel`
   (0,1,0). Картинку при этом ставит `.form--dark .field__ctl--sel` — тоже
   (0,2,0). Итог: одна стрелка размножается в плитку. Возвращаем longhand-ы
   с той же специфичностью ниже по файлу.
   Плюс явный фон опций: без него список в тёмном поле уходит в белое по белому.
   ------------------------------------------------------------------------ */
.form--dark .field__ctl--sel{
  background-repeat:no-repeat;
  background-position:right var(--s-4) center;
  background-size:12px 8px;
}
.form--dark select.field__ctl option{ background:var(--bg-2); color:var(--ink-i) }

/* ---------- 9.9 Плита на тёмном блоке ----------
   🔴 Найдено замером по пикселям (§1.8), а не на глаз. Ядро --grad стоит в
   точке 88% 94% блока: чем выше блок, тем ярче его правый низ. На плите
   сметы (2,4 экрана) нижние строки таблицы приходят на #5645EA, и --soft-i
   там падает до 2,3:1 при пороге 4,5. То же грозит стеклянной карточке:
   .glass прозрачна на 6% и берёт яркость из-под себя, а ссылки согласия
   набраны --acc-hi — на светлой части градиента это почти 1:1.
   Лечение — сплошная подложка --bg-2 (её роль по токенам: «тёмная карточка
   на тёмном»). Контраст перестаёт зависеть от места в блоке:
   --acc-hi = 4,58:1 · --soft-i = 7,1:1 · --ink-i = 18,8:1.
   ------------------------------------------------------------------------ */
.plate{
  background:var(--bg-2);
  border:1px solid var(--line-i); border-radius:var(--r-card);
  padding:var(--s-5);
}
@media (max-width:767px){
  .plate{ padding:var(--s-4) }
}

/* ---------- 9.10 hover для добавленных компонентов ---------- */
@media (hover:hover) and (pointer:fine){
  .btn--line:hover{ border-color:var(--acc-hi); color:var(--acc-hi) }
  .btn--line:hover .btn__ic{ background:var(--acc-hi); color:var(--acc-ink) }
}

/* ---------- 9.11 ≤767: мобильный ритм добавленных компонентов (§8.2a) ---------- */
@media (max-width:767px){
  .steps{ gap:var(--s-4) }
  .step{ padding-top:var(--s-4); gap:var(--s-2) var(--s-4) }
  .step__n{ width:32px; height:32px }

}

/* ---------- 8.13 Центрирование ограниченной по ширине колонки ---------- */
.u-mx-auto{ margin-inline:auto }

/* ---------- 8.14 Ссылка-переход отдельной строкой ----------
   .link внутри предложения по WCAG 2.5.8 выведена из требования к размеру
   цели, а самостоятельная строка-переход — нет: на тач добираем до 44px.
   ------------------------------------------------------------------------ */
@media (pointer:coarse){
  .link--cta{ display:inline-flex; align-items:center; min-height:var(--burger) }
}

/* ---------- 8.15 Список «термин — расшифровка» ----------
   Пришёл на место таблицы там, где вторая колонка — предложение, а не число:
   таблица с min-width уезжала в горизонтальный скролл внутри блока и на
   375 читалась боком. Здесь на мобиле пара просто встаёт в две строки.
   ------------------------------------------------------------------------ */
.deflist{ display:grid; border-top:1px solid var(--line) }
/* 🔴 ТЗ 31, правка 3. Было border-bottom у каждой строки — и вместе
   с border-top у .deflist dt/dd (правило ниже по файлу) это давало ДВЕ
   параллельные линии на каждом стыке, 40 px друг от друга. Замер:
   /o-sebe/ 6 пар, /price/ 3, /kontakty/ 2 на 1440. Линия переехала
   в промежуток: у первой строки её нет, её рисует рамка группы. */
.deflist > div{
  display:grid; gap:var(--s-2);
  padding-block:var(--s-4);
}
.deflist > div + div{ border-top:1px solid var(--line) }
.deflist dt{
  font-family:var(--f-disp); font-weight:500; font-size:var(--t-h4);
  line-height:1.25; color:var(--ink);
}
.deflist dd{
  margin:0; color:var(--soft); font-size:var(--t-small);
  /* ТЗ 33, класс H: было 62ch — 84–88 знаков в реальной строке. */
  line-height:1.6; max-width:min(100%, var(--measure-row));
}
@media (min-width:768px){
  .deflist > div{
    grid-template-columns:minmax(0,1fr) minmax(0,2fr);
    gap:var(--col-gap); align-items:start;
  }
}
.blk--dark .deflist{ border-top-color:var(--line-i) }
.blk--dark .deflist > div + div{ border-top-color:var(--line-i) }
.blk--dark .deflist dt{ color:var(--ink-i) }
.blk--dark .deflist dd{ color:var(--soft-i) }

/* ==========================================================================
   10. КОМПОНЕНТЫ СТРАНИЦ /kak-rabotaem/ И /o-sebe/
   Добавлено при сборке этих двух страниц. Собственных значений нет — токены.
   Всё, что уже есть в разделах 8 и 9 (.steps, .nlist, .deflist, .plate,
   .note-b, .ph, .req-line, .btn--line, .field__ctl--sel), здесь НЕ
   переопределяется — берётся оттуда.
   ========================================================================== */

/* ---------- 10.1 Статы с выключкой влево ----------
   Базовые .stats центрируются: это верно для центрированного героя главной
   (.hero.u-center). На внутренних страницах герой выключен влево, и ряд статов
   обязан идти по той же оптической оси, что h1 и кнопки, иначе блок читается
   как две разные сетки в одном экране.
   ------------------------------------------------------------------------ */
.stats--start{ justify-content:flex-start }

/* ---------- 10.2 Портрет в колонке split ----------
   .ph держит место кадра через aspect-ratio. В колонке 2fr шириной ~450px
   кадр 4:5 вырастает до 560px и перевешивает текстовую колонку рядом.
   Ограничиваем ширину кадра, а не ломаем пропорцию.
   ------------------------------------------------------------------------ */
.ph--portrait{ width:100%; max-width:320px }


/* ---------- 10.3 Подпись стата в выключенном влево герое ----------
   Базовые 14ch рассчитаны на короткую подпись главной («Гарантия на работы
   по договору»). Подписи этапов длиннее и на 14ch рассыпаются в пять строк
   по одному-два слова — та самая кривизна, которую §8.2a требует ловить.
   Ширина берётся из уже принятой меры тезиса (--measure--tight, 26ch).
   ------------------------------------------------------------------------ */
@media (min-width:768px){
  .stats--start .stat__cap{ max-width:26ch }
}

/* ---------- 10.4 Этапы в три колонки на десктопе ----------
   🔴 Крупный блок не терпит пустоты (фирменный стиль, §1). Ряд .steps в одну
   колонку упирается в 62ch текста и оставляет правую половину тёмного блока
   пустой — блок читается как недоделанный. На ≥1024 номер, заголовок со
   сроком и текст встают в один ряд: блок заполнен, высота падает почти вдвое,
   срок остаётся рядом с названием этапа.
   Ниже 1024 раскладка прежняя (номер слева, заголовок и текст справа).
   ------------------------------------------------------------------------ */
/* 🔴 ТЗ 31, правки 4 и 5. Раскладка «номер | название | текст» перестала
   быть исключением для --wide и стала общим устройством ряда. Причина та же,
   что описана выше, только теперь она замерена: после снятия columns:2
   (правка 5) список шёл одной колонкой 599 px внутри пояса 1237 px —
   638 px пустоты справа, по пять–семь раз на странице. Это и есть
   «текстовая лента» из правки 4. Разрешение Р2: место справа получает
   работу — уже имеющийся текст пункта, ничего не придумано.
   Порядок чтения при этом сверху вниз, как у обычного списка: пункт 2
   стоит под пунктом 1, а не справа от него.
   То же самое у .numlist — у его пункта ровно те же три части
   (цифра, заголовок, пояснение). */
@media (min-width:1024px){
  .steps--wide .step{
    grid-template-columns:auto minmax(0,2fr) minmax(0,3fr);
    align-items:start;
  }
  .steps--wide .step__n{ grid-row:auto }
}
/* Общее устройство ряда для .steps и .numlist объявлено в самом конце файла:
   и .step, и .numlist > li ниже переопределяются точечными правилами
   разделов 24 и «правки 30.08», и раньше этих переопределений правило
   молча не срабатывает. Проверено замером: 121 блок с пустотой не сдвинулся
   с места, пока правило стояло здесь. */

/* ---------- 10.5 Нумерованный список на ТЁМНОМ блоке ----------
   🔴 Замером, а не на глаз (§1.8). .nlist пришёл со светлой страницы услуг,
   и его текст набран --soft (#6B7080) — это чернила ДЛЯ СВЕТЛОГО фона. На
   тёмном блоке они дают 1,93–3,29:1 при пороге 4,5. Возвращаем роль:
   на тёмном текст --soft-i, выделение --ink-i, счётчик --acc-hi.
   Сам список лежит на .plate (§9.9): на живом градиенте даже --soft-i
   в нижней части блока падает ниже порога.
   ------------------------------------------------------------------------ */
.blk--dark .nlist p{ color:var(--soft-i) }
.blk--dark .nlist strong{ color:var(--ink-i) }
.blk--dark .nlist > li::before{ color:var(--acc-hi) }

/* ---------- 10.6 Строка реквизитов в герое ----------
   🔴 Тот же замер: строка идёт по нижней кромке героя, а ядро --grad стоит
   в точке 88% 94% — правый конец строки ложится на #6E5EF9 и --soft-i там
   даёт 1,72:1. Приём тот же, что §8.12 применил к подписям статов: белые
   чернила на том же месте, иерархию держит кегль 11px и трекинг, а не
   понижение контраста. Плюс .measure в разметке — строка не доезжает до
   самой светлой точки блока.
   ------------------------------------------------------------------------ */
.hero .req-line{ color:var(--ink-i) }
.blk--dark .hero .req-line{ color:var(--ink-i) }

/* ==========================================================================
   11. КОМПОНЕНТЫ /nastroyka/ и /price/
   Добавлено при сборке страницы настройки и открытого прайса.
   Своих значений нет — только токены. Новых компонентов не понадобилось:
   .steps / .marks / .note-b / .nlist / .deflist / .plate / .price-table
   взяты из разделов 8 и 9 как есть.
   ========================================================================== */

/* ---------- 11.1 Подпись таблицы на тёмном блоке ----------
   🔴 Найдено замером по пикселям (§1.8): .price-table caption набран --soft
   (роль токена — «тихий текст на СВЕТЛОМ»), и на плите --bg-2 даёт 3,88:1
   при пороге AA 4,5. Тёмный блок своей рулетки для caption не имел — только
   для th и td. Ставим тихий текст ТЁМНОЙ стороны: --soft-i на --bg-2 = 7,1:1.
   ------------------------------------------------------------------------ */
.blk--dark .price-table caption{ color:var(--soft-i) }

/* ---------- 10.7 Список ограничений в две колонки ----------
   Тот же довод, что и у 10.4: пункт упирается в 62ch, и правая половина
   тёмного блока остаётся пустой. Четыре несвязанных ограничения читаются
   парами не хуже, чем столбиком, — на ≥1024 раскладываем 2×2.
   ------------------------------------------------------------------------ */
@media (min-width:1024px){
  .nlist--two{ grid-template-columns:1fr 1fr; column-gap:var(--col-gap) }
}

/* ==========================================================================
   12. КОМПОНЕНТЫ /dogovor/, /kontakty/, /policy/, /soglasie/
   Добавлено при сборке договорной, контактной и двух юр-страниц.
   Собственных значений нет — только токены из tokens.css.
   Всё, что уже есть в разделах 5, 8–11 (.legal, .deflist, .plate, .note-b,
   .marks, .steps, .req-line, .form, .price-table), здесь НЕ переопределяется.
   ========================================================================== */

/* ---------- 12.1 Табличные данные документа ----------
   🔴 Почему не .price-table. У .price-table min-width:420px и внешний
   .price-wrap с overflow-x:auto — это верно для прайса, где ячейка это число.
   Здесь вторая и третья колонки — предложения («В тот же день, если письмо
   пришло до 20:00»), и та же таблица на 375 уезжает боком: текст читается
   по два слова в строку, а часть колонок вообще за краем контейнера. §8.2a
   требует ловить именно такую кривизну, а не только горизонтальный скролл.

   Поэтому база компонента — МОБИЛЬНАЯ: строка таблицы разворачивается в
   запись с подписями, подпись берётся из data-l и рисуется через ::before.
   Колонки возвращаются модификатором и только там, где под них есть ширина:
     .dtable--cols     — с 768px (таблица во всю ширину секции)
     .dtable--cols-lg  — с 1024px (таблица внутри узкой колонки .legal, 74ch)
   Таблица без модификатора остаётся записями на любой ширине — так свёрстан
   перечень получателей данных: четыре колонки в 74ch нечитаемы даже на 1600.

   Разметка остаётся настоящей <table> с thead: на мобиле шапка уходит в
   sr-only, но остаётся в дереве доступности, и скринридер по-прежнему
   связывает ячейку с её заголовком. ::before — подпись для глаз, не для AT.
   Значение data-l ДОЛЖНО совпадать с текстом <th> той же колонки.
   ------------------------------------------------------------------------ */
.dtable{ width:100%; font-size:var(--t-small); line-height:1.6; color:var(--soft) }

.dtable thead{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
}
/* 🔴 Одного sr-only на thead мало: clip-path прячет отрисовку, но ячейки
   внутри продолжают раскладываться по своей ширине, и getBoundingClientRect
   у <th> отдаёт right = 451 при экране 375. Прогон честно считает это
   вылетом за правый край (§8.6), хотя на экране ничего не видно. Схлопываем
   и сами ячейки — тогда замер совпадает с картинкой. */
.dtable thead th{
  display:block; width:1px; height:1px; overflow:hidden;
  padding:0; border:0;
}
.dtable tr{ display:block; padding-block:var(--s-4); border-bottom:1px solid var(--line) }
.dtable tbody tr:first-child{ padding-top:0 }
.dtable tbody tr:last-child{ padding-bottom:0; border-bottom:0 }
.dtable td{ display:block; padding:0; border:0 }
.dtable td + td{ margin-top:var(--s-3) }
.dtable td::before{
  content:attr(data-l); display:block; margin-bottom:var(--s-1);
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.12em;
  text-transform:uppercase; color:var(--acc);
}
.dtable .num{ font-family:var(--f-mono); font-variant-numeric:tabular-nums; color:var(--ink) }
.dtable a{ color:var(--acc); text-decoration:underline; text-underline-offset:.18em }

/* Возврат в настоящие колонки. Вынесено в @supports-независимый миксин
   двумя медиазапросами, чтобы не плодить третий набор правил. */
@media (min-width:768px){
  .dtable--cols thead{
    position:static; width:auto; height:auto; padding:0; margin:0;
    overflow:visible; clip-path:none; white-space:normal;
  }
  .dtable--cols tr{ display:table-row; padding:0; border-bottom:0 }
  .dtable--cols thead th{
    display:table-cell; width:auto; height:auto; overflow:visible;
    text-align:left; vertical-align:bottom;
    padding:var(--s-3); border-bottom:1px solid var(--line);
    font-family:var(--f-mono); font-weight:400; font-size:var(--t-micro);
    letter-spacing:.12em; text-transform:uppercase; color:var(--soft);
  }
  .dtable--cols td{
    display:table-cell; vertical-align:top;
    padding:var(--s-3); margin-top:0; border-bottom:1px solid var(--line);
  }
  .dtable--cols td::before{ display:none }
  .dtable--cols tbody tr:last-child td{ border-bottom:0 }
}
@media (min-width:1024px){
  .dtable--cols-lg thead{
    position:static; width:auto; height:auto; padding:0; margin:0;
    overflow:visible; clip-path:none; white-space:normal;
  }
  .dtable--cols-lg tr{ display:table-row; padding:0; border-bottom:0 }
  .dtable--cols-lg thead th{
    display:table-cell; width:auto; height:auto; overflow:visible;
    text-align:left; vertical-align:bottom;
    padding:var(--s-3); border-bottom:1px solid var(--line);
    font-family:var(--f-mono); font-weight:400; font-size:var(--t-micro);
    letter-spacing:.12em; text-transform:uppercase; color:var(--soft);
  }
  .dtable--cols-lg td{
    display:table-cell; vertical-align:top;
    padding:var(--s-3); margin-top:0; border-bottom:1px solid var(--line);
  }
  .dtable--cols-lg td::before{ display:none }
  .dtable--cols-lg tbody tr:last-child td{ border-bottom:0 }
  /* Узкая первая колонка под «№» перечня целей обработки. */
  .dtable--n td:first-child,
  .dtable--n thead th:first-child{ width:var(--s-7) }
}

/* Тёмный блок: чернила и линии меняют сторону (§1.8 стандарта). */
.blk--dark .dtable{ color:var(--soft-i) }
.blk--dark .dtable tr,
.blk--dark .dtable--cols thead th,
.blk--dark .dtable--cols td,
.blk--dark .dtable--cols-lg thead th,
.blk--dark .dtable--cols-lg td{ border-bottom-color:var(--line-i) }
.blk--dark .dtable--cols thead th,
.blk--dark .dtable--cols-lg thead th{ color:var(--soft-i) }
.blk--dark .dtable td::before{ color:var(--acc-hi) }
.blk--dark .dtable .num{ color:var(--ink-i) }
.blk--dark .dtable a{ color:var(--acc-hi) }

/* ---------- 12.2 Реквизиты: «поле — значение» ----------
   Ключи здесь длинные («Регистрационный номер в реестре операторов…»),
   поэтому .deflist не подходит: его dt набран Geologica 18px и такой ключ
   читается как заголовок раздела. Здесь ключ тихий, значение — основное.
   ------------------------------------------------------------------------ */
.reqs{ display:grid; border-top:1px solid var(--line) }
/* 🔴 ТЗ 31, правка 3, сквозной прогон. Тот же случай, что у .toc__l
   и .deflist: линия висела на каждой строке снизу, и нижний край группы
   получал вторую границу поверх рамки соседнего блока. Линия переезжает
   в промежуток, края остаются чистыми. */
.reqs > div{
  display:grid; gap:var(--s-1);
  padding-block:var(--s-3);
}
.reqs > div + div{ border-top:1px solid var(--line) }
.reqs dt{ font-size:var(--t-small); line-height:1.5; color:var(--soft) }
.reqs dd{ margin:0; font-size:var(--t-body); line-height:1.5; color:var(--ink);
          max-width:min(100%, var(--measure-row));   /* ТЗ 33, класс H */
          overflow-wrap:anywhere }
.reqs dd a{ color:var(--acc); text-decoration:underline; text-underline-offset:.18em }
@media (min-width:768px){
  .reqs > div{
    grid-template-columns:minmax(0,1fr) minmax(0,1.4fr);
    gap:var(--s-2) var(--col-gap); align-items:baseline;
  }
}
/* .legal dl задаёт свою сетку (0,1,1) и перебил бы базовую .reqs (0,1,0). */
.legal .reqs{ grid-template-columns:none }
.blk--dark .reqs{ border-top-color:var(--line-i) }
.blk--dark .reqs > div + div{ border-top-color:var(--line-i) }
.blk--dark .reqs dt{ color:var(--soft-i) }
.blk--dark .reqs dd{ color:var(--ink-i) }
.blk--dark .reqs dd a{ color:var(--acc-hi) }

/* ---------- 12.3 Содержание юридического документа ----------
   Техническое примечание к текстам, п. 6: юр-страницы верстаются
   «с содержанием-якорями по разделам». Ссылки подчёркнуты: признак
   ссылки не должен быть только цветом (WCAG 1.4.1).
   ------------------------------------------------------------------------ */
.doc-toc{
  display:grid; gap:var(--s-1);
  padding:var(--s-5); border-radius:var(--r-card); background:var(--surf-2);
}
.doc-toc a{
  display:block; padding-block:var(--s-1);
  font-size:var(--t-small); line-height:1.5; color:var(--ink);
  text-decoration:underline; text-underline-offset:.18em;
  transition:color var(--d-fast) var(--e-out);
}
@media (min-width:768px){
  .doc-toc{ grid-template-columns:1fr 1fr; column-gap:var(--col-gap) }
}
@media (pointer:coarse){
  .doc-toc a{ display:flex; align-items:center; min-height:var(--burger) }
}
@media (hover:hover) and (pointer:fine){
  .doc-toc a:hover{ color:var(--acc) }
}

/* ---------- 12.4 Мелочи юр-колонки ----------
   .legal p набран --soft; выделение внутри абзаца обязано вернуться к --ink,
   иначе «жирный тихий» читается как ошибка, а не как акцент.
   scroll-margin — чтобы якорь из содержания не приклеивал заголовок к кромке.
   ------------------------------------------------------------------------ */
.legal strong{ color:var(--ink) }

/* 🔴 Замером по пикселям, а не на глаз (§1.8). Дата редакции в герое набрана
   .legal__meta, а его цвет --soft — это «тихий текст на СВЕТЛОМ». В тёмном
   герое строка ложится на градиент и даёт 1,05:1 на светлой части ядра при
   пороге 4,5. Тот же случай, что §10.6 разобрал для .req-line: возвращаем
   белые чернила, иерархию держит кегль 11px и трекинг, а не понижение
   контраста. Белое на самой светлой точке ядра (#695CF7) = 4,69:1.
   В конце документа (на светлом блоке) .legal__meta остаётся --soft. */
.hero .legal__meta,
.blk--dark .hero .legal__meta{ color:var(--ink-i) }
/* 🔴 Найдено прогоном заголовков (§8.2a, проверка 5): «5. Сроки хранения и
   уничтожение персональных данных» и ещё три заголовка раздела роняли
   последнее слово в отдельную строку — на 1280 последняя строка занимала
   15–17% ширины первой. Лечится text-wrap:balance, а не ручными <br>.
   Заголовки страниц balance получают классом .u-balance; в .legal
   заголовков десятки, поэтому правило ставится на компонент. */
.legal h2,
.legal h3{ text-wrap:balance }
.legal h2[id],
.legal h3[id]{ scroll-margin-top:var(--s-5) }
.legal .dtable{ margin-top:var(--s-5) }

/* ---------- 12.5 ≤767: мобильный ритм добавленных компонентов (§8.2a) ---------- */
@media (max-width:767px){
  .doc-toc{ padding:var(--s-4) }
  .reqs > div{ padding-block:var(--s-3) }
}

/* ═══════════════════════════════════════════════════════════════════════
   ФИКС ПРОГОНА QA 26.08.2026, §2 — колонка «Цена» на телефоне

   Замер Playwright на 375: .price-table--wide имеет min-width 560px внутри
   контейнера 291px. Скрыто 269px (48%). На СЕМИ таблицах /price/ колонка
   с ценой не попадала на экран вообще — на странице, которая называется
   «Прайс». Документ вбок не ехал (overflow-x:auto отрабатывал), поэтому
   проверка «горизонтального скролла нет» показывала 33/33 OK и дефект
   не ловила. Индикатора свайпа в вёрстке не было.

   Это же нарушало собственное правило §12.1: .price-table с боковым
   скроллом допустима там, «где ячейка это число». Колонки «Расчёт»
   на /price/ и «Как считается» на /kvartira/ — предложения.

   Решение: на телефоне .price-table переходит в тот же режим записей,
   что и .dtable (метка из data-l над значением). С 768px — обычная
   таблица без изменений. data-l проставлен во все ячейки скриптом
   по заголовкам thead, 19 таблиц в 6 файлах.
   ═══════════════════════════════════════════════════════════════════════ */
@media (max-width:767px){
  .price-wrap{ overflow-x:visible }
  .price-table,
  .price-table--wide{ min-width:0; width:100% }

  /* thead убираем совсем: подписи колонок продублированы в td::before
     из data-l, поэтому скринридеру он уже не нужен, а visually-hidden
     оставлял ячейки th шириной по контенту — прогон ловил их как вылет
     за правый край (th.num на /price/, th на /kak-rabotaem/). */
  .price-table thead{ display:none }
  .price-table tr{
    display:block; padding-block:var(--s-4);
    border-bottom:1px solid var(--line);
  }
  .price-table tbody tr:last-child{ border-bottom:0; padding-bottom:0 }
  .price-table td,
  .price-table th[scope="row"]{
    display:block; padding:0; border:0; text-align:left;
  }
  .price-table td.num{ text-align:left }
  .price-table td + td,
  .price-table th[scope="row"] + td{ margin-top:var(--s-3) }
  .price-table td:empty{ display:none }

  /* Метка колонки над значением. У первой ячейки не нужна:
     название работы читается само, метка «РАБОТА» была бы шумом. */
  .price-table td::before{
    content:attr(data-l); display:block; margin-bottom:var(--s-1);
    font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.12em;
    text-transform:uppercase; color:var(--acc);
  }
  .price-table td:first-child::before,
  .price-table td:not([data-l])::before{ content:none; display:none }

  .price-table td:first-child,
  .price-table th[scope="row"]{ font-weight:600; color:var(--ink) }
  .price-table .sub td:first-child{ padding-left:0 }
  .price-table caption{ padding-bottom:var(--s-4) }

  .blk--dark .price-table tr{ border-bottom-color:var(--line-i) }
  .blk--dark .price-table td:first-child,
  .blk--dark .price-table th[scope="row"]{ color:var(--ink-i) }
  .blk--dark .price-table td::before{ color:var(--acc-hi) }
}

/* ФИКС ПРОГОНА QA §7 — контраст подписи в заглушках изображений.
   --soft #6B7080 на --surf-2 #F4F4F1 давал 4,48:1 при норме WCAG AA 4,5:1.
   Не хватало 0,02 при кегле 11px. Правится точечно, токен --soft не трогаем:
   он на белом даёт 4,94:1 и там вопросов нет. #5F6474 на --surf-2 = 5,35:1. */
.ph, .ph *{ color:#5F6474 }

/* ФИКС ПРОГОНА QA §10 — висячие слова в заголовках.
   Прогон нашёл 17 заголовков на 375, где последняя строка короче 22% ширины
   («года» 43 из 241px). Класс .u-balance в проекте был, но повешен не на все.
   Ставим балансировку на все заголовки разом — браузер без поддержки
   text-wrap:balance просто игнорирует правило. */
h1, h2, h3, .card__t, .step__t{ text-wrap:balance }

/* ==========================================================================
   13. РЕДИЗАЙН СЕКЦИЙ — 26.08.2026 (промпт 08)
   Дайлы: DESIGN_VARIANCE 8 · MOTION_INTENSITY 7 · VISUAL_DENSITY 7.
   Значения только из tokens.css. Ничего из разделов 1–12 не переопределяется
   без явного комментария, зачем.
   ========================================================================== */

/* ---------- 13.0 СЛОЙ ПОДСВЕТКИ ПОД ПАРАЛЛАКС ----------
   🔴 Двигать сам .blk--dark нельзя: transform у предка делает его containing
   block для position:fixed, и мобильная шторка (.notch__list) прилипает внутрь
   шапки вместо экрана — баг уже ловили, см. коммент у .has-js [data-rv].
   Поэтому параллакс едет на отдельном слое, который шторке не предок.
   Базовый --grad остаётся на блоке: слой лишь добавляет встречную подсветку.
   ------------------------------------------------------------------------ */
.blk__glow{
  position:absolute; inset:-12% -6%; z-index:0; pointer-events:none;
  background:radial-gradient(46% 52% at 78% 88%, rgba(150,134,255,.30) 0%, rgba(150,134,255,0) 66%);
  will-change:transform;   /* единственное место с will-change: слой реально едет */
}

/* ==========================================================================
   13.1 ПЕРВЫЙ ЭКРАН
   Было: пять центрированных элементов подряд, заполненность 35,5 % при пороге
   45 %, 0 типов моушена, самый крупный кегль = --t-h2 как и во всех остальных
   двенадцати секциях. Стало: 7/5 + схема + полоса цифр в --t-num.
   ========================================================================== */
.hero{ padding:var(--s-9) var(--pad-blk) var(--s-8) }

/* Первый экран обязан быть экраном. 100svh (не vh: iOS считает адресную строку),
   минус поля полотна сверху и снизу и зазор до следующего блока. */
@media (min-width:768px){
  .hero{
    display:flex; flex-direction:column;
    gap:clamp(var(--s-6), 4vh, var(--s-8));
  }
}

/* 🔴 min-height:100svh на герое НЕ СТАВИТСЯ, и это осознанно.
   Первая версия правки его ставила — «первый экран обязан быть экраном».
   Замер: содержимое героя на 1440×900 набирает 763px, а min-height требовал
   846px. Лишние 83px некуда деть: при justify-content:flex-start они собираются
   в дыру внизу слева (147×836 px, под карточкой контактов её видно только
   глазами), при space-between — в дыру посреди экрана между кнопками
   и полосой цифр. Растянуть схему нельзя: колонка ограничена по ширине,
   и при 6:5 высота упирается в неё, а не в высоту ряда.
   Естественная высота даёт 0,85 экрана — внутри вилки 0,8–1,4, и следующий
   блок подглядывает снизу, что в блочной композиции «Контур» и нужно. */

.hero__grid{ display:grid; gap:var(--sec-gap); align-items:center }
@media (min-width:900px){
  .hero__grid{ grid-template-columns:var(--grid-7-5); gap:var(--s-8) }
}

.hero__say{ min-width:0 }
.hero h1{ margin:0 }

/* Кикер на тёмном: --acc-hi даёт 4.6:1 на дне градиента и 3.1:1 на пике.
   Кикер — метка 11px, к ней применяется порог 4.5:1, поэтому на герое
   он белый с приглушением, а цветом отмечена только точка перед ним. */
.hero .kicker{ color:var(--ink-i); opacity:.72; display:flex; align-items:center; gap:var(--s-2) }
.hero .kicker::before{
  content:""; width:18px; height:1px; background:var(--acc-hi); flex:0 0 auto;
}

/* ---------- 13.2 СХЕМА ПРИЁМА (вместо заглушки) ----------
   Фото объектов нет. Пустая пунктирная рамка с подписью в скобках на первом
   экране недопустима, поэтому здесь разрез: перекрытие → реле → светильник,
   и отдельно кнопка на стене, связанная по радио. Пропорция 4:3 сохранена —
   фотография встанет на это место без пересборки сетки.
   ------------------------------------------------------------------------ */
.hero__fig{ margin:0; min-width:0 }
/* 6:5 — под колонку рядом с четырёхстрочным h1. Пропорция слота задана явно,
   чтобы фотография объекта позже встала на это место без пересборки сетки. */
.scheme{ width:100%; height:auto; aspect-ratio:6/5; overflow:visible }

.scheme__slab rect{ fill:rgba(255,255,255,.05); stroke:var(--line-i); stroke-width:1 }
.scheme__hatch path{ stroke:rgba(255,255,255,.16); stroke-width:1; fill:none }
.scheme__web,
.scheme__floor,
.scheme__wall{ stroke:var(--line-i); stroke-width:1.5; fill:none }
.scheme__floor{ stroke-dasharray:3 5 }

.scheme__relay rect{ fill:var(--acc-hi); stroke:none }
.scheme__pin{ stroke:var(--acc-hi); stroke-width:1.5; fill:none }
.scheme__lead{ stroke:var(--acc-hi); stroke-width:1.5; fill:none }
.scheme__lamp{ fill:var(--ink-i) }

/* Конус света: заливка градиентом сверху вниз, без свечения и мигания */
.scheme__cone{
  fill:url(#cone); stroke:none;
  transform-origin:68px 150px;   /* вершина конуса: раскрытие идёт от лампы */
}
/* Радиосвязь кнопка → реле. Пунктир и есть смысл: провода между ними нет. */
.scheme__link{
  stroke:var(--acc-hi); stroke-width:1.5; fill:none;
  stroke-dasharray:2 7; stroke-linecap:round; opacity:.55;
}
.scheme__btn rect{ fill:var(--surf); stroke:none }
.scheme__btn circle{ fill:var(--btn) }
.scheme__wave path{ stroke:var(--acc-hi); stroke-width:1.5; fill:none; stroke-linecap:round }

.scheme__lbl{
  fill:var(--soft-i); font-family:var(--f-mono); font-size:10px;
  letter-spacing:.08em; text-transform:uppercase;
}
.scheme__lbl--acc{ fill:var(--acc-hi); letter-spacing:.1em }

/* Подпись-строка под схемой. Показывается только там, где SVG-текст мелок. */
.scheme__cap{ display:none }
.scheme__cap b{ color:var(--ink-i); font-weight:600 }

/* ---------- 13.3 ПОЛОСА ЦИФР-ФАКТОВ ----------
   Приём из разбора референса: цифра — самостоятельный визуальный объект,
   а не строка текста. Кегль 84 против 13 у подписи = 6,5:1.
   Хайрлайн сверху вместо рамки карточки: плотнее и не плодит прямоугольники.
   ------------------------------------------------------------------------ */
.facts{
  display:grid; gap:var(--s-5) var(--col-gap);
  grid-template-columns:1fr;
}
@media (min-width:560px){ .facts{ grid-template-columns:repeat(3,1fr) } }

.fact{ padding-top:var(--s-4); border-top:var(--rule-i) }
.fact__n{
  display:block; font-family:var(--f-disp); font-weight:500;
  font-size:var(--t-num); line-height:var(--lh-num); letter-spacing:var(--tr-num);
  font-variant-numeric:tabular-nums; color:var(--ink-i);
}
/* Единица измерения — та же строка, но кегль подписи: «84 px + 17 px»
   читается как одно число с хвостом, а не как два разных заголовка. */
.fact__u{
  font-family:var(--f-body); font-weight:500; font-size:var(--t-lead);
  letter-spacing:0; white-space:nowrap;
}
.fact__c{
  display:block; margin-top:var(--s-3);
  font-size:var(--t-small); line-height:1.5; color:var(--ink-i); opacity:.78;
  max-width:26ch;
}

/* ---------- 13.4 КАРТОЧКА КОНТАКТОВ: приём развёрнут ----------
   Была слева и накрывала собой полосу цифр. Уводим вправо, туда, где полоса
   заканчивается: наезд на границу блока сохраняется (утверждённая деталь
   стиля), столкновение уходит.
   🔴 Только от 900. Замер на 834: карточка шириной 280px стояла слева поверх
   подписей «Двушка на объекте…» и «Штробы в стенах…» — текст читался сквозь
   карточку. Между 768 и 899 на три колонки цифр плюс карточку ширины не
   хватает ни слева, ни справа, поэтому там карточка возвращается в поток,
   как уже сделано на телефоне.
   ------------------------------------------------------------------------ */
@media (min-width:900px){
  .hero__cc{ left:auto; right:var(--pad-blk); width:min(300px,34%) }
  /* полоса цифр не заезжает под карточку */
  .facts{ padding-right:calc(min(300px,34%) + var(--s-6)) }

  /* ---------- ТЗ 33, класс D. Крупное число не обрезается и не наезжает ----
     Замер на 1440: «33 946 / 38 644 ₽» занимает 641 px, а вместе с двумя
     соседями ряд просит 1 053 px при 905 px свободных — хвост числа уезжал
     под карточку контактов, знак рубля не было видно вовсе.
     Кегль берётся ОТ ШИРИНЫ ПОЯСА, а не из шкалы: .facts объявлен
     контейнером запроса, и одно правило держится на 1440, 1200 и 1024
     без отдельных значений под каждую ширину. Класс ставится только там,
     где замер показал вылет: где числа влезают, шкала не трогается. */
  .facts{ container-type:inline-size }
  .facts--fit .fact__n{ font-size:min(var(--t-num), 6.5cqi) }
}

/* ---------- ТЗ 33, класс D. 900–1199: карточка и цифры не делят одну полосу --
   Резерв под карточку — фиксированные 300 px плюс отбивка. На 1440 это
   27 % пояса, на 1024 — уже 37 %, и три числа в оставшиеся 564 px
   не помещаются ни при каком кегле, который ещё читается как цифра оффера
   (расчёт: 11,55·кегль + 186 ≤ 564 даёт 32 px при базовых 65).
   Ниже 1200 карточка возвращается в поток — тем же приёмом, что уже
   применён на 768–899. Правило адресное: только там, где под карточкой
   действительно идёт полоса цифр; на страницах без неё карточка остаётся
   на своём месте и высоту первого экрана не растит. */
@media (min-width:900px) and (max-width:1199px){
  .hero-wrap:has(.facts){ padding-bottom:0 }
  .hero-wrap:has(.facts) .hero__cc{
    position:static; width:auto; margin-top:var(--gap-blk); padding:var(--s-5);
  }
  .hero-wrap:has(.facts) .facts{ padding-right:0 }
}

/* ---------- 13.4a 768–899: планшет ----------
   Сетка героя здесь ещё в одну колонку, и схема во всю ширину раздувалась
   до 750px по высоте — рисунок начинал спорить с заголовком. Ограничиваем
   и прижимаем вправо: асимметрия остаётся, масса уходит.
   ------------------------------------------------------------------------ */
@media (min-width:768px) and (max-width:899px){
  .hero__fig{ width:min(100%,440px); margin-left:auto }
  .hero__cc{ position:static; width:auto; margin-top:var(--gap-blk); padding:var(--s-5) }
  .hero-wrap{ padding-bottom:0 }
}

/* ---------- 13.5 МОУШЕН ПЕРВОГО ЭКРАНА ----------
   Три типа: parallax (слой подсветки), scheme (разрез оживает), mask (цифра
   раскрывается снизу). Стартовые состояния — только под .has-js: без JS
   и при prefers-reduced-motion всё видно сразу.
   h1 НЕ анимируется намеренно: это LCP-элемент страницы (§9.8 стандарта),
   скрывать его до старта JS — значит отложить LCP на время загрузки скрипта.
   ------------------------------------------------------------------------ */
/* 🔴 Гейт — .has-scenes, а НЕ .has-js. Класс ставит js/scenes.js только после
   того, как GSAP действительно загрузился и движение разрешено. На .has-js
   вешать нельзя: main.js ставит его всегда, и если vendor-файл не доехал
   (CSP, обрыв сети, блокировщик), цифры и схема остались бы спрятаны навсегда.
   scenes.js сам не ставит класс при prefers-reduced-motion — поэтому
   отдельного @media-отката здесь не нужно. */
.has-scenes [data-mo~="ghost"]{ clip-path:inset(100% 0 0 0) }
/* Поля формы: стартовое состояние только под .has-scenes — без GSAP форма
   видна и работает, это критично, она собирает заявки. */
.has-scenes [data-mo~="stagger"] > *{ opacity:0 }
/* Строки прайса: стартовое состояние только под .has-scenes и только
   для первых 14 — остальные ставит видимыми сама сцена. */
.has-scenes [data-mo~="rows"] tbody tr{ opacity:0 }
.has-scenes [data-mo~="draw"] rect,
.has-scenes [data-mo~="draw"] text{ opacity:0 }

.has-scenes .scheme__cone,
.has-scenes .scheme__wave path,
.has-scenes .scheme__link,
.has-scenes .scheme__relay,
.has-scenes .scheme__btn{ opacity:0 }
.has-scenes .fact__n,
.has-scenes .fact__c,
.has-scenes [data-mo~="mask"] .stat__num,
.has-scenes [data-mo~="mask"] .stat__cap{ opacity:0 }

@media (prefers-reduced-motion:reduce){
  .blk__glow{ will-change:auto }
}

/* ---------- 13.6 ПЕРВЫЙ ЭКРАН НА ТЕЛЕФОНЕ ----------
   Свой ритм, меньший (§8.2a стандарта). Схема остаётся: это единственное
   изображение на экране, убрать её — вернуть пустоту. Но она уходит под текст
   и ужимается по высоте, чтобы блок влез в 1,6 экрана.
   ------------------------------------------------------------------------ */
@media (max-width:767px){
  .hero{ padding:var(--s-6) var(--pad-blk) var(--s-6) }
  .hero__grid{ gap:var(--s-5) }
  .hero .kicker{ font-size:var(--t-micro) }

  /* Метки внутри рисунка гасим, факты — строкой под ним обычным текстом */
  .scheme__lbl{ display:none }
  .scheme__cap{
    display:flex; flex-wrap:wrap; gap:var(--s-1) var(--s-5);
    margin-top:var(--s-3); font-size:var(--t-small); line-height:1.5;
    color:var(--ink-i); opacity:.8;
  }
  .facts{ gap:var(--s-4) }
  .fact__c{ max-width:none }
  /* цифры в строку по три: на 390 это 3 колонки по ~108px, кегль клампится
     до 40px и подпись уходит в две строки — читается, в 1,6 экрана влезает */
  .facts{ grid-template-columns:repeat(3,1fr) }
  .fact__n{ font-size:clamp(26px,8.2vw,36px) }
  .fact__u{ display:block; font-size:var(--t-small) }
  .fact__c{ font-size:var(--t-micro); font-family:var(--f-body);
            letter-spacing:0; text-transform:none; line-height:1.4 }
}

/* ==========================================================================
   13.7 БЕНТО «ТРИ ВХОДА»
   Было: центрированная шапка над тремя равными карточками — паттерн, который
   до правки повторялся в девяти секциях из тринадцати. Заполненность 46 %,
   высота 0,5 экрана, один тип раскладки на всю страницу.
   Стало: 5/7. Слева заголовок и тёмная карточка главного предложения,
   справа две светлые стопкой. Ячеек ровно три — по числу входов.
   ========================================================================== */
.bento{ display:grid; gap:var(--col-gap); grid-template-columns:1fr }

/* 🔴 Раскладка через ДВЕ КОЛОНКИ-ОБЁРТКИ, а не через grid-row у карточек.
   Первая версия расставляла карточки через `[data-card="soon"]:nth-of-type(n)` —
   и промахнулась: :nth-of-type считает элементы по ТЕГУ, а не по классу,
   а среди div-детей .bento первым идёт заголовок. Итог: «Партнёрам» уезжали
   в первый ряд, «Бизнесу» во второй, порядок в разметке и на экране разошёлся.
   Обёртки убирают и это, и связанность высот: ряд правой колонки больше
   не растягивается под высоту заголовка в левой. */
@media (min-width:900px){
  .bento{ grid-template-columns:5fr 7fr; align-items:start }
}
.bento__left,
.bento__right{ display:flex; flex-direction:column; gap:var(--col-gap); min-width:0 }
.bento__left{ gap:var(--sec-gap) }
.bento__lead{ flex:1 }               /* тёмная карточка добирает высоту колонки */
.bento__right > .card{ flex:1 }

.bento__say{ margin:0; max-width:14ch }

/* ---------- 13.8 ЦИФРА В КАРТОЧКЕ ----------
   Сумма-ориентир перестаёт быть строчкой внутри абзаца и становится вторым
   по величине объектом карточки: 52px против 13px текста = 4,0.
   ------------------------------------------------------------------------ */
.num{
  display:block; font-family:var(--f-disp); font-weight:500;
  font-size:var(--t-num-sm); line-height:var(--lh-num); letter-spacing:var(--tr-num);
  font-variant-numeric:tabular-nums;
}
.num--card{ margin-block:var(--s-3) var(--s-2); color:var(--acc-hi);
            display:flex; align-items:baseline; gap:.14em }
.card:not(.card--dark) .num--card{ color:var(--acc) }
/* «от» и «₽» — служебные части суммы. В кегле цифры они спорят с ней за
   внимание, поэтому опускаются до кегля лида и приглушаются. */
.num__pre{
  font-family:var(--f-body); font-weight:400; font-size:var(--t-lead);
  letter-spacing:0; opacity:.7;
}
.num__v{ font-variant-numeric:tabular-nums }

/* Мета-строка карточки: срок под суммой, кеглем подписи */
.card__meta{
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.1em;
  text-transform:uppercase; color:var(--soft);
}
.card--dark .card__meta{ color:var(--soft-i) }

/* ==========================================================================
   13.9 БРЕНДЫ: ЛЕНТА ВМЕСТО РЯДА
   Было: шесть серых слов через justify-content:space-between, дыры по 120px
   между ними, вся секция — один тип визуальных сущностей (текст).
   Стало: заголовок и пояснение в два столбца + бегущая лента.
   ========================================================================== */
.brands-row{ display:grid; gap:var(--s-4); align-items:start }
@media (min-width:768px){
  .brands-row{ grid-template-columns:var(--grid-7-5); gap:var(--col-gap) }
}
.brands-row__t{ font-size:var(--t-h3); line-height:1.3; margin:0; max-width:34ch }
.brands-row__x{ margin:0; font-size:var(--t-small); line-height:1.6; max-width:44ch }

/* ---------- Лента ----------
   Единственная бегущая строка на сайте (правило «не больше одной на страницу»).
   Дубль трека нужен, чтобы шов не был виден; он aria-hidden — список брендов
   для скринридера читается один раз.
   ------------------------------------------------------------------------ */
.marquee{
  margin-top:var(--s-5); display:flex; gap:var(--s-8); overflow:hidden;
  /* края растворяются, лента не обрывается о границу блока */
  -webkit-mask-image:linear-gradient(90deg,#0000,#000 10%,#000 90%,#0000);
          mask-image:linear-gradient(90deg,#0000,#000 10%,#000 90%,#0000);
  border-top:var(--rule); padding-top:var(--s-5);
}
.marquee__track{
  display:flex; align-items:center; gap:var(--s-8); flex:0 0 auto;
  animation:brands-run 34s linear infinite;
}
.marquee__track li{
  font-family:var(--f-disp); font-weight:500; font-size:var(--t-h3);
  color:var(--soft); letter-spacing:-.01em; white-space:nowrap;
  display:flex; align-items:center; gap:var(--s-8);
}
/* Точка-разделитель между брендами. Декоративная, читателю не озвучивается. */
.marquee__track li::after{
  content:""; width:4px; height:4px; border-radius:50%;
  background:var(--line); flex:0 0 auto;
}
@keyframes brands-run{ from{ transform:translateX(0) } to{ transform:translateX(calc(-100% - var(--s-8))) } }

/* Наведение останавливает ленту: имя бренда можно прочитать спокойно. */
@media (hover:hover) and (pointer:fine){
  .marquee:hover .marquee__track{ animation-play-state:paused }
}
/* «Меньше движения» — лента встаёт. Строка остаётся видимой и читаемой. */
@media (prefers-reduced-motion:reduce){
  .marquee__track{ animation:none }
  .marquee{ overflow-x:auto }
  .marquee__track + .marquee__track{ display:none }
}

/* ==========================================================================
   13.10 СЕКЦИЯ «РЕЛЕ УХОДИТ В ПОТОЛОК»
   ========================================================================== */
/* Сетка 7/5 с текстом слева. Отдельное имя, а не .split--60: та пара
   используется на страницах услуг, менять её поведение нельзя. */
.split-75{ display:grid; gap:var(--sec-gap); grid-template-columns:1fr }
@media (min-width:900px){
  /* stretch, а не start: карточка добирает высоту текстовой колонки, иначе
     справа под ней остаётся незаполненный угол. */
  .split-75{ grid-template-columns:var(--grid-7-5); gap:var(--s-8); align-items:stretch }
}

/* ---------- Фраза-перелом ----------
   Кегль --t-h3 против --t-body: 27/15 = 1,8 внутри абзацного потока.
   Не заголовок (ранга в документе не добавляет), а акцент внутри текста.
   ------------------------------------------------------------------------ */
.pivot{
  margin-block:var(--s-5);
  padding-left:var(--s-5); border-left:2px solid var(--acc);
  font-family:var(--f-disp); font-weight:500; font-size:var(--t-h3);
  line-height:1.25; color:var(--ink);
}
.blk--dark .pivot{ border-left-color:var(--acc-hi); color:var(--ink-i) }

/* ---------- Текст в две колонки ----------
   VISUAL_DENSITY 7: восемь строк в одну колонку — стена, в две — текст.
   Ниже 1024 колонка одна: 62ch в половину ширины планшета не помещаются.
   ------------------------------------------------------------------------ */






/* ---------- Масштабная пара ----------
   Тёмная карточка: ломает череду белых блоков и даёт рисунку контраст,
   которого на белом у линейной графики нет.
   ------------------------------------------------------------------------ */
.scale-fig{
  margin:0; padding:var(--s-5);
  border-radius:var(--r-card);
  background-color:var(--bg); background-image:var(--grad-card);
  box-shadow:var(--sh-inset); color:var(--ink-i);
  transform-style:preserve-3d;
}
.scale-fig{ display:flex; flex-direction:column; justify-content:center }
.scale-fig svg{ width:100%; height:auto }
.scale-fig figcaption{
  margin-top:var(--s-4); font-size:var(--t-small); line-height:1.5; color:var(--soft-i);
}

.sz__box rect{ fill:none; stroke:var(--line-i); stroke-width:1.5 }
.sz__drawer{ fill:rgba(255,255,255,.06) }
.sz__relay rect{ fill:var(--acc-hi) }
.sz__pin{ stroke:var(--acc-hi); stroke-width:1.5; fill:none }
.sz__dim{ stroke:var(--soft-i); stroke-width:1; fill:none; opacity:.6 }
.sz__lbl{
  fill:var(--soft-i); font-family:var(--f-mono); font-size:11px;
  letter-spacing:.08em; text-transform:uppercase;
}
.sz__lbl--acc{ fill:var(--acc-hi) }

/* ==========================================================================
   13.11 ЛЕСЕНКА «ЧЕТЫРЕ ВЕЩИ»
   Было: четыре равные колонки — тот же ритм, что во всех остальных секциях.
   Стало: 7/5 → 5/7. Ширины чередуются, ряд перестаёт читаться как таблица.
   ========================================================================== */
/* align-items:start — ячейки НЕ тянутся под самую высокую в ряду.
   Замер до правки: у «Свет выключается сам» цены нет, у соседа есть, и под
   первой ячейкой висела дыра 90px (§8.2a стандарта — разброс расстояний
   у соседей в ряду). Рваный низ здесь — часть асимметрии, а не брак. */
.stair{ display:grid; gap:var(--sec-gap) var(--col-gap); grid-template-columns:1fr;
        align-items:start }
@media (min-width:768px){
  .stair{ grid-template-columns:repeat(12,1fr) }
  .stair__i{ grid-column:span 6 }
  .stair__i--wide{ grid-column:span 6 }
}
@media (min-width:1024px){
  /* лесенка: широкая → узкая, узкая → широкая */
  .stair__i{ grid-column:span 5 }
  .stair__i--wide{ grid-column:span 7 }
}
.stair__i{
  padding-top:var(--s-5); border-top:var(--rule-i);
  display:flex; flex-direction:column; gap:var(--s-2); min-width:0;
}
.blk--light .stair__i{ border-top:var(--rule) }
.stair__t{ font-family:var(--f-disp); font-weight:500; font-size:var(--t-h4);
           line-height:1.3; margin:0 }
.stair__x{ font-size:var(--t-small); line-height:1.6; color:var(--soft-i); margin:0; max-width:46ch }
.blk--light .stair__x{ color:var(--soft) }

/* Цена в ячейке: была внутри абзаца и терялась. Кегль --t-num-sm против 13px
   подписи = 4,0 — цифру видно раньше, чем читаешь строку. */
.stair__p{
  margin-top:var(--s-3); padding-top:var(--s-4);
  display:flex; flex-wrap:wrap; gap:var(--s-3) var(--s-7);
}
/* Пара «сумма + за что». Раньше суммы и подписи шли одной строкой через
   flex-wrap и слипались в «3 000 ₽ НАКЛАДНОЙ ЗАМОК 6 000 ₽ ВРЕЗНОЙ». */
.pricelet{ display:flex; flex-direction:column; gap:var(--s-1) }

/* 🔴 Цифра на тёмном — БЕЛАЯ, а не --acc-hi. Ядро градиента стоит в правом
   нижнем углу блока (88 % 94 %), а цены лежат как раз внизу справа: --acc-hi
   (#7566FF) на пике #7566FF даёт контраст около 1:1 — «6 000 ₽» просто
   исчезало. Белым на том же месте — 3,6:1, для 52px это AA (порог 3:1).
   Иерархию здесь держит кегль (52 против 11), а не цвет. Та же логика уже
   применена к .stat__cap в разделе 8.12. */
.num--row{ font-size:var(--t-num-sm); color:var(--ink-i) }
.blk--light .num--row{ color:var(--acc) }
/* 🔴 Подпись под ценой на тёмном — БЕЛАЯ. Замер по пикселям (qa/_contrast.mjs,
   он гасит сам глиф и берёт фактический цвет градиента под ним):
   --soft-i #9C9CC4 под «врезной» дал 2,14:1 при пороге 4,5 для 11px,
   под «накладной замок» — 3,82, под «за кран» — 4,19. Все три провал.
   Белым на том же месте — 5,6:1. Иерархию держит кегль: 11 против 52. */
.stair__pu{
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.1em;
  text-transform:uppercase; color:var(--ink-i);
}
.blk--light .stair__pu{ color:var(--soft) }

/* ==========================================================================
   13.12 «ЧЕГО РЕТРОФИТ НЕ СДЕЛАЕТ» — липкая колонка и крупная цифра
   ========================================================================== */
/* Обратный порядок колонок: узкая слева, широкая справа */
@media (min-width:900px){
  .split-75--rev{ grid-template-columns:5fr 7fr }
}

.stick{ min-width:0 }
@media (min-width:900px){
  /* Прилипает под шапкой. Ниже 900 колонка короткая, прилипание бессмысленно. */
  .stick{ position:sticky; top:var(--s-6); align-self:start }
}

/* ---------- Крупная цифра фоном ----------
   «Пять вещей» из текста становится объектом. Кегль 2× от --t-num: 168px
   против 15px тела. Контурная, не залитая: залитая такого размера перебьёт
   и заголовок, и список. Обрезается левым краем колонки — приём из
   библиотеки промпта («крупная цифра, обрезанная краем секции»).
   aria-hidden: слово «пять» уже есть в лиде, дублировать цифрой не надо.
   ------------------------------------------------------------------------ */
.stick__n{
  font-family:var(--f-disp); font-weight:500;
  font-size:calc(var(--t-num) * 2.3); line-height:.8; letter-spacing:-.05em;
  margin:var(--s-4) 0 0 -.06em;
  color:transparent;
  -webkit-text-stroke:1.5px var(--line);
  pointer-events:none; user-select:none;
}
.blk--dark .stick__n{ -webkit-text-stroke-color:var(--line-i) }
/* Safari до 15.4 и Firefox без -webkit-text-stroke дали бы невидимый текст */
@supports not (-webkit-text-stroke:1px #000){
  .stick__n{ color:var(--line) }
}

/* Список ограничений плотнее: между пунктами --s-4 вместо --s-5,
   и он больше не делит ширину с пустотой. */
.nlist--tight{ gap:var(--s-4) }
.nlist--tight > li{ padding-top:var(--s-4); border-top:var(--rule) }
.nlist--tight > li:first-child{ padding-top:0; border-top:0 }
.blk--dark .nlist--tight > li{ border-top:var(--rule-i) }

/* ==========================================================================
   13.13 ТРИ ОРИЕНТИРА ПО ЦЕНЕ
   ========================================================================== */
.sec__head--flat{ margin-bottom:0 }

.tiers{ display:grid; gap:var(--sec-gap) var(--col-gap); grid-template-columns:1fr }
@media (min-width:640px){ .tiers{ grid-template-columns:repeat(3,1fr) } }

.tier{ padding-top:var(--s-4); border-top:var(--rule); min-width:0 }
.tier__k{
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.12em;
  text-transform:uppercase; color:var(--soft); margin:0;
}
.tier__n{
  margin:var(--s-3) 0 var(--s-4); color:var(--ink);
  display:flex; align-items:baseline; gap:.14em;
}
.tier__x{ margin:var(--s-3) 0 0; font-size:var(--t-small); line-height:1.5; color:var(--soft) }

/* ---------- Полоса масштаба ----------
   Ширина = число устройств из текста (30 / 50 / 75), приведённое к максимуму.
   Ничего не выдумано: 30/75 = 40 %, 50/75 = 67 %, 75/75 = 100 %.
   Полоса без фонового трека — трек превращает секцию в дашборд.
   ------------------------------------------------------------------------ */
.tier__bar{ margin:0; height:3px; position:relative }
.tier__bar > span{
  display:block; height:100%; width:var(--w);
  background:var(--acc); border-radius:var(--r-pill);
  transform-origin:left center;
}
.blk--dark .tier__bar > span{ background:var(--acc-hi) }
/* стартовое состояние только когда сцены живы */
.has-scenes .tier__bar > span{ transform:scaleX(0) }

/* ==========================================================================
   13.14 СТАТЬИ СМЕТЫ — три колонки
   Было: dt слева, dd справа сплошным абзацем, сумма внутри предложения.
   Колонку сумм нельзя было просмотреть глазом сверху вниз, а на странице
   про цену это первое, что делают.
   ========================================================================== */
.artl{ display:grid; border-top:var(--rule); margin:0 }
/* ТЗ 31, правка 3: разделитель в промежутке, край группы чистый. */
.artl__r{
  display:grid; gap:var(--s-2);
  padding-block:var(--s-5);
}
.artl__r + .artl__r{ border-top:var(--rule) }
@media (min-width:768px){
  .artl__r{ grid-template-columns:5fr 4fr 7fr; gap:var(--col-gap); align-items:start }
}
.artl dt{ font-family:var(--f-body); font-weight:600; font-size:var(--t-body);
          line-height:1.4; color:var(--ink); margin:0 }
.artl dd{ margin:0 }
.artl__v{ display:flex; flex-wrap:wrap; align-items:baseline; gap:var(--s-1) var(--s-2) }
.artl__v .mono{ font-size:var(--t-h4); font-weight:500; color:var(--acc);
                font-variant-numeric:tabular-nums; white-space:nowrap }
.artl__u{ font-size:var(--t-small); color:var(--soft) }
/* «За единицу» — не сумма. В акцентном моноширинном кегле оно читалось как
   цена, хотя цены стоят в соседней колонке. */
.artl__v--txt{ font-size:var(--t-body); color:var(--ink); font-weight:500 }
.blk--dark .artl__v--txt{ color:var(--ink-i) }
.artl__x{ font-size:var(--t-small); line-height:1.6; color:var(--soft) }

/* Четыре цены монтажа — сеткой, а не строкой через запятую внутри абзаца */
.artl__x{ display:flex; flex-wrap:wrap; gap:var(--s-2) var(--s-5) }
.artl__x:not(:has(.unitp)){ display:block }
.unitp{ display:flex; align-items:baseline; gap:var(--s-2); white-space:nowrap }
.unitp b{ font-weight:500; color:var(--acc); font-variant-numeric:tabular-nums }

.blk--dark .artl{ border-top-color:var(--line-i) }
.blk--dark .artl__r + .artl__r{ border-top-color:var(--line-i) }
.blk--dark .artl dt{ color:var(--ink-i) }
.blk--dark .artl__u,
.blk--dark .artl__x{ color:var(--soft-i) }
.blk--dark .artl__v .mono,
.blk--dark .unitp b{ color:var(--acc-hi) }

/* ==========================================================================
   13.15 СРОКИ: ШКАЛА С ЛИНИЕЙ-КОННЕКТОРОМ
   ========================================================================== */
/* Пара итоговых сроков рядом с заголовком. Раньше «4–7 дней» и «6–9 дней»
   лежали внутри абзаца под таблицей — самые полезные числа секции. */
.totals{ display:grid; gap:var(--s-5); align-content:start }
@media (min-width:560px){ .totals{ grid-template-columns:1fr 1fr; gap:var(--col-gap) } }
.totals__i{ margin:0; padding-top:var(--s-3); border-top:var(--rule-i) }
.blk--light .totals__i{ border-top:var(--rule) }
.totals__i .num--row{ display:flex; align-items:baseline; gap:.2em }
.totals__c{
  display:block; margin-top:var(--s-2);
  font-size:var(--t-small); line-height:1.45; color:var(--ink-i); opacity:.78;
}
.blk--light .totals__c{ color:var(--soft); opacity:1 }

/* ---------- Шкала ----------
   Четыре шага в ряд, соединённые линией. Линия — ::before у контейнера,
   а не рамки у ячеек: так она непрерывна и её можно «прорисовать».
   ------------------------------------------------------------------------ */
.road{ display:grid; gap:var(--sec-gap) var(--col-gap); grid-template-columns:1fr;
       position:relative; counter-reset:none }
@media (min-width:768px){
  .road{ grid-template-columns:repeat(4,1fr) }
  .road::before{
    content:""; position:absolute; left:0; right:0; top:5px; height:1px;
    background:var(--line-i); transform-origin:left center;
    /* прогресс прорисовки задаёт js/scenes.js через --road-x.
       Значение по умолчанию 1 — без JS линия просто нарисована целиком. */
    transform:scaleX(var(--road-x,1));
  }
  .blk--light .road::before{ background:var(--line) }
  .has-scenes .road{ --road-x:0 }
}
.road__s{ position:relative; padding-top:var(--s-5); min-width:0;
          display:flex; flex-direction:column; gap:var(--s-2) }
/* на телефоне линия вертикальная — иначе четыре точки висят без связи */
@media (max-width:767px){
  .road__s{ padding-left:var(--s-6); padding-top:0; padding-bottom:var(--s-5);
            border-left:1px solid var(--line-i) }
  .blk--light .road__s{ border-left-color:var(--line) }
  .road__s:last-child{ border-left-color:transparent }
}
/* 🔴 Кольцо box-shadow цветом --bg вокруг точки отделяло её от линии — и на
   пике градиента превращалось в тёмный ореол: у последнего шага «Сдача»
   он читался как дырка в плите. Точка белая и без кольца: белое различимо
   и на дне градиента, и на пике, а линия за ней всё равно тоньше. */
.road__dot{
  position:absolute; top:0; left:0; width:11px; height:11px; border-radius:50%;
  background:var(--ink-i);
}
.blk--light .road__dot{ background:var(--acc) }
@media (max-width:767px){ .road__dot{ left:-6px } }

.road__k{
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.12em;
  text-transform:uppercase; color:var(--ink-i); opacity:.7;
}
.blk--light .road__k{ color:var(--soft); opacity:1 }
.road__d{ font-size:var(--t-h2); font-weight:500; color:var(--ink-i); line-height:1.1;
          font-family:var(--f-disp); letter-spacing:-.02em }
.blk--light .road__d{ color:var(--ink) }
.road__x{ font-size:var(--t-small); line-height:1.6; color:var(--ink-i); opacity:.78;
          margin-top:var(--s-1) }
.blk--light .road__x{ color:var(--soft); opacity:1 }
/* «В день окончания» — не число. В кегле --t-h2 оно занимает три строки
   и ломает ряд, поэтому опускается до кегля карточки. */
.road__d--txt{ font-size:var(--t-h4); font-family:var(--f-body); font-weight:600;
               letter-spacing:0 }

/* ==========================================================================
   13.16 КАРТОЧКА РЕКВИЗИТОВ (вместо пустого слота под портрет)
   ========================================================================== */




/* На тёмном блоке подложку и рамку даёт .plate — здесь только снимаем светлые */












/* ТЗ 31, правка 3: разделитель в промежутке, край группы чистый. */











/* ==========================================================================
   13.17 ПРИЗРАЧНАЯ ЦИФРА
   Тот же приём, что .stick__n в блоке ограничений, вынесенный в общий класс:
   крупная контурная цифра как якорь композиции там, где кроме текста ничего
   нет. Не озвучивается — число уже названо словом в соседнем абзаце.
   ========================================================================== */
.ghost{
  font-family:var(--f-disp); font-weight:500;
  font-size:calc(var(--t-num) * 2.6); line-height:.78; letter-spacing:-.05em;
  margin:0; color:transparent;
  -webkit-text-stroke:1.5px var(--line-i);
  pointer-events:none; user-select:none; align-self:center; justify-self:end;
}
.blk--light .ghost{ -webkit-text-stroke-color:var(--line) }
.ghost--sm{ font-size:calc(var(--t-num) * 1.6); margin:var(--s-4) 0 0;
            justify-self:start; align-self:auto }
@supports not (-webkit-text-stroke:1px #000){
  .ghost{ color:var(--line-i) }
  .blk--light .ghost{ color:var(--line) }
}
@media (max-width:767px){
  .ghost{ font-size:calc(var(--t-num) * 1.8); justify-self:start; margin-top:var(--s-5) }
}

/* ==========================================================================
   13.18 FAQ — плотнее и не свёрнут целиком
   Было: центрированная шапка над списком в одну колонку на всю ширину,
   все семь вопросов свёрнуты. Два типа визуальных сущностей на секцию.
   ========================================================================== */
.faq__item[open] > .faq__q{ color:var(--acc) }
.blk--dark .faq__item[open] > .faq__q{ color:var(--acc-hi) }

/* ==========================================================================
   13.19 ТЕЛЕФОН: СВОЙ РИТМ И СВОЙ МАСШТАБ ЦИФР
   Замер до правки на 390: контраст масштабов ≥2,5 набрался только в 3 секциях
   из 11 при пороге 4. Причина — цифры-факты ужимались до 32px, чтобы влезть
   в три колонки по 100px. Разрыв в кегле, ради которого всё и делалось,
   на телефоне пропадал.
   Лечение — не уменьшать цифру, а поменять раскладку: строка «цифра — подпись»
   вместо трёх узких колонок.
   ========================================================================== */
@media (max-width:559px){
  .facts{ grid-template-columns:1fr; gap:0 }
  .fact{
    display:grid; grid-template-columns:auto 1fr; align-items:baseline;
    gap:var(--s-2) var(--s-4);
    padding-block:var(--s-4);
  }
  .fact__n{ font-size:44px; grid-row:span 2 }      /* 44/15 = 2,9 */
  .fact__u{ display:inline; font-size:var(--t-small) }
  .fact__c{ margin-top:0; align-self:center }
}

@media (max-width:767px){
  /* ---------- Три ориентира: секция вылезала за 1,6 экрана (2,07) ----------
     Основной вес давали четыре статьи сметы: в каждой три ячейки шли стопкой
     по одной строке. Ставим статью и сумму в одну строку — минус ~34px
     на статью, и в блоке из четырёх это заметно. */
  .artl__r{ grid-template-columns:1fr auto; gap:var(--s-2) var(--s-4) }
  .artl dt{ grid-column:1 }
  .artl__v{ grid-column:2; justify-content:flex-end; text-align:right }
  .artl__x{ grid-column:1 / -1 }
  .artl__u{ width:100%; text-align:right }

  .tier{ padding-top:var(--s-3) }
  .tier__n{ margin-block:var(--s-2) var(--s-3) }
  .tier__x{ margin-top:var(--s-2) }

  /* ---------- Карточка реквизитов ----------
     Блок «Объект веду я» шёл 1,66 экрана. Строки реквизитов на телефоне
     идут в две колонки, а не стопкой: значения короткие, места хватает. */



}

/* ---------- 13.20 Правки заполненности двух коротких секций ----------
   Замер: «Сроки» 49,8 % и «Чего не сделает» 53,2 % при пороге 55 %.
   У короткой секции фиксированные --sec-py 64+64 съедают долю тем большую,
   чем она ниже: в полосе 450px это сразу 28 % площади. Лечим не воздухом,
   а тем, что убираем дыры внутри: выравниваем ряд заголовка по низу
   (под двухстрочным h2 висела пустая полоса высотой с третью строку)
   и подтягиваем шкалу ближе к нему.
   ------------------------------------------------------------------------ */
@media (min-width:900px){
  .road-head{ align-items:end }
}
.road-head + .road{ margin-top:var(--s-5) }

/* Призрачная цифра в липкой колонке — крупнее: колонка вдвое короче списка
   рядом, и её нижняя половина оставалась пустой. */
@media (min-width:900px){
  .ghost--sm{ font-size:calc(var(--t-num) * 2.4) }
}
/* Полоса сроков — именно полоса, а не полноценная секция: у неё свой,
   меньший вертикальный отступ. При высоте 450px штатные 64+64 съедали
   28 % площади ещё до того, как в неё что-то положили. */
.blk--band > .sec{ padding-block:var(--s-6) }

/* ==========================================================================
   14. ВНУТРЕННИЕ СТРАНИЦЫ — общий подъём
   Замер до правки (пример /price/, 1440): заполненность героя 35,3 % при
   пороге 45, узкая полоса 360px, ноль типов моушена, на всю страницу
   в 22 экрана — один тип (`reveal`).
   Причина одна и та же на всех восьми страницах: герой — одна колонка
   с `measure` (62ch ≈ 640px) в контейнере 1150px, правые 500px пустые.
   ========================================================================== */

/* ---------- 14.1 Статы героя внутренних страниц ----------
   Тот же приём, что .facts на главной, но на ступень ниже по кеглю:
   52px против 84px. Пик шкалы остаётся у первого экрана главной,
   а внутренние страницы получают разрыв 52/15 = 3,5 вместо 38/15 = 2,53.
   Хайрлайн сверху вместо ничего — ряд перестаёт висеть в воздухе.
   ------------------------------------------------------------------------ */
.hero .stats{
  display:grid; gap:var(--s-5) var(--col-gap);
  grid-template-columns:1fr; justify-content:stretch;
}
@media (min-width:560px){
  .hero .stats{ grid-template-columns:repeat(3,1fr) }
}
.hero .stat{ padding-top:var(--s-4); border-top:var(--rule-i) }
.blk--light .hero .stat{ border-top:var(--rule) }
.hero .stat__num{
  font-size:var(--t-num-sm); line-height:var(--lh-num); letter-spacing:var(--tr-num);
  font-variant-numeric:tabular-nums; color:var(--ink-i);
}
.blk--light .hero .stat__num{ color:var(--ink) }
.hero .stat__cap{
  margin-top:var(--s-3); max-width:30ch;
  font-size:var(--t-small); line-height:1.5; opacity:.8;
}

/* ---------- 14.2 Герой внутренней страницы: 7/5 ----------
   Слева речь, справа — то, что у страницы есть: статы, оглавление или
   карточка. Ставится классом .hero--split, чтобы страницы без правой
   колонки остались одноколоночными и не получили пустоту вместо пустоты.
   ------------------------------------------------------------------------ */
@media (min-width:900px){
  .hero--split .hero__grid{ grid-template-columns:var(--grid-7-5); gap:var(--s-8) }
  /* статы в правой колонке идут стопкой, а не рядом */
  .hero--split .hero__aside .stats{ grid-template-columns:1fr; gap:var(--s-4) }
}

/* ---------- 14.3 Оглавление длинной страницы ----------
   /price/ — 22 экрана. Без навигации по разделам это свиток: до «что
   удорожает работу» надо пролистать восемь экранов прайса, не зная,
   что дальше вообще что-то есть.
   ------------------------------------------------------------------------ */
.toc{ display:flex; flex-direction:column; gap:0 }
.toc__t{
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.12em;
  text-transform:uppercase; color:var(--soft-i); margin:0 0 var(--s-3);
}
.blk--light .toc__t{ color:var(--soft) }
.toc__l{ display:flex; flex-direction:column; border-top:var(--rule-i) }
.blk--light .toc__l{ border-top:var(--rule) }
/* 🔴 ТЗ 31, правка 3. Разделитель принадлежит ПРОМЕЖУТКУ между пунктами,
   а не самому пункту: было border-bottom у каждого li, и нижняя граница
   последнего вставала вплотную к верхней границе соседнего блока, когда
   колонки складывались. Верхняя рамка группы (.toc__l) осталась. */
.toc__l li + li{ border-top:1px solid var(--line-i) }
.blk--light .toc__l li + li{ border-top-color:var(--line) }
.toc__l a{
  display:flex; align-items:baseline; gap:var(--s-3);
  padding-block:var(--s-3); font-size:var(--t-small); line-height:1.4;
  color:var(--ink-i); transition:color var(--d-fast) var(--e-out);
}
.blk--light .toc__l a{ color:var(--ink) }
.toc__n{
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.1em;
  color:var(--acc-hi); flex:0 0 auto;
}
.blk--light .toc__n{ color:var(--acc) }
@media (hover:hover) and (pointer:fine){
  .toc__l a:hover{ color:var(--acc-hi) }
  .blk--light .toc__l a:hover{ color:var(--acc) }
}
@media (pointer:coarse){
  .toc__l a{ min-height:var(--burger); align-items:center }
}

/* ---------- 14.4 Крошки внутри блока ----------
   Были отдельной плитой .blk: 0,09 экрана, заполненность 2,3 %, один тип
   визуальных сущностей — белый огрызок на полотне под тёмным героем.
   Стали первой секцией следующего блока: линия-разделитель уже есть
   у правила .sec + .sec, зазор полотна не тратится.
   ------------------------------------------------------------------------ */
.sec--crumbs{ padding-block:var(--s-4) }
.sec--crumbs + .sec{ border-top:1px solid var(--line) }
.blk--dark .sec--crumbs + .sec{ border-top-color:var(--line-i) }

/* ---------- 14.5 Два бага ряда статов на внутренних страницах ----------
   🔴 Первый. Статы — это <p>, и базовое правило типографики `p + p{margin-top}`
   отодвигало второй и третий вниз на 12px. В прежней флекс-раскладке это было
   незаметно, в сетке с хайрлайнами сверху разъехались сами линии: первая
   на 685px, соседние на 700px.
   🔴 Второй. Карточка контактов наезжает на низ героя и накрывала подпись
   третьего стата — ровно то же, что уже чинили на главной. Резервируем
   под неё колонку.
   ------------------------------------------------------------------------ */
.hero .stats > p + p{ margin-top:0 }

@media (min-width:900px){
  .hero .stats{ padding-right:calc(min(300px,34%) + var(--s-6)) }
}
@media (min-width:768px) and (max-width:899px){
  /* в этом диапазоне карточка возвращается в поток (см. 13.4a), резерв не нужен */
  .hero .stats{ padding-right:0 }
}

/* ==========================================================================
   15. РИСУНКИ ВМЕСТО ЗАГЛУШЕК
   На сайте не было ни одного изображения — семь мест были закрыты пунктирными
   рамками вида `[ фото 4:3 — … ]`. Фотографий объектов нет и до первой съёмки
   не будет, а рамка с текстом в скобках на проде запрещена анти-slop чеком
   фирменного стиля («пустые блоки с рыбой на проде»).
   Каждый рисунок построен на фактах из соседнего текста: числа комплекта —
   из таблицы состава, протоколы — из списка «с чем работаю», перечень замера —
   из абзаца про обследование. Ничего не додумано.
   Пропорции слотов сохранены (3:2, 16:9, 4:3, 4:5) — фотография встанет
   на место рисунка без пересборки сетки.
   ========================================================================== */
.figc{
  margin:0; padding:var(--s-5); border-radius:var(--r-card);
  background-color:var(--bg); background-image:var(--grad-card);
  box-shadow:var(--sh-inset); color:var(--ink-i);
  display:flex; flex-direction:column; justify-content:center;
}
/* 🔴 Только ПРЯМОЙ ребёнок и сцена. Раньше правило было `.figc svg` и
   по специфичности (0,1,1) перебивало `.kit__g` (0,1,0): глифы комплекта
   растягивались во всю ширину карточки. */
.figc > svg,
.figc__stage > svg{ width:100%; height:auto; display:block }
/* Разрез стены во всю ширину секции (1104px) читается как пустая плита:
   три предмета теряются в поле. Ограничиваем и центрируем. */
.figc--wide .figc__stage{ width:100%; max-width:720px; margin-inline:auto }
/* 🔴 width:100% обязателен. .figc — флекс-колонка, и `margin-inline:auto`
   на её ребёнке отменяет растягивание: элемент садится на свою внутреннюю
   ширину, а у <svg> она по умолчанию 300px. Рисунок схлопывался до 300×144
   вместо 720×345. */
.figc figcaption{
  margin-top:var(--s-4); font-size:var(--t-small); line-height:1.5; color:var(--ink-i);
  opacity:.8;
}
/* На тёмном блоке карточка не должна быть второй тёмной поверх первой —
   там она становится плитой --bg-2 (её роль по токенам). */
.blk--dark .figc{ background-image:none; background-color:var(--bg-2);
                  border:1px solid var(--line-i) }

/* Общая типографика подписей внутри рисунков.
   11 единиц viewBox при типичной ширине рисунка — это 10–13px на экране;
   ниже 767 рисунки узкие, поэтому подписи там крупнее. */
.figc text{
  font-family:var(--f-mono); font-size:11px; letter-spacing:.06em;
  text-transform:uppercase; fill:var(--soft-i);
}
@media (max-width:767px){ .figc text{ font-size:15px; letter-spacing:.02em } }

/* ---------- 15.1 Комплект «быстрый старт» ---------- */
.kit__hub{ fill:none; stroke:var(--acc-hi); stroke-width:1.8 }
.kit__dot{ fill:var(--acc-hi) }
.kit__lamp{ fill:none; stroke:var(--ink-i); stroke-width:1.6; opacity:.85 }
.kit__btn{ fill:var(--surf) }
.kit__sock{ fill:none; stroke:var(--ink-i); stroke-width:1.6; opacity:.85 }
.kit__leak{ fill:none; stroke:var(--acc-hi); stroke-width:1.6 }
.kit__pir{ fill:none; stroke:var(--acc-hi); stroke-width:1.6 }
.kit__n{
  fill:var(--ink-i) !important; font-family:var(--f-disp) !important;
  font-size:30px !important; letter-spacing:-.02em !important; text-transform:none !important;
}
.kit__l{ fill:var(--ink-i); opacity:.75 }
.kit__pir-r{ stroke:var(--acc-hi); stroke-width:1.6; fill:none }

/* ---------- 15.2 Стена: выключатель и кнопка ---------- */
.wl__ceil,
.wl__floor{ stroke:var(--line-fig); stroke-width:1.5; fill:none }
.wl__relay rect{ fill:var(--acc-hi) }
.wl__sw rect{ fill:none; stroke:var(--ink-i); stroke-width:1.6; opacity:.8 }
.wl__swk{ stroke:var(--ink-i); stroke-width:2.4; opacity:.8 }
.wl__btn rect{ fill:var(--surf) }
.wl__btn circle{ fill:var(--btn) }
.wl__link{ stroke:var(--acc-hi); stroke-width:1.5; fill:none;
           stroke-dasharray:2 7; stroke-linecap:round; opacity:.6 }
.wl__wave path{ stroke:var(--acc-hi); stroke-width:1.5; fill:none; stroke-linecap:round }
.wl__nochase{ stroke:var(--line-fig); stroke-width:1.5; fill:none; stroke-dasharray:4 6 }
.wl__nochase{ rx:2 }
.wl__l{ fill:var(--ink-i); opacity:.75 }
.wl__l--acc{ fill:var(--acc-hi); opacity:1 }

/* ---------- 15.3 Протоколы → шлюз ---------- */
.pr__src{ fill:none; stroke:var(--line-fig); stroke-width:1.5 }
.pr__src--dashed{ stroke-dasharray:5 5 }
.pr__wire{ stroke:var(--line-fig); stroke-width:1.5; fill:none }
.pr__wire--dashed{ stroke-dasharray:4 6 }
.pr__hub rect{ fill:none; stroke:var(--acc-hi); stroke-width:1.8 }
.pr__hub circle{ fill:var(--acc-hi) }
.pr__home path{ fill:none; stroke:var(--ink-i); stroke-width:1.6; opacity:.85 }
.pr__l{ fill:var(--ink-i); opacity:.85 }
.pr__l--acc{ fill:var(--acc-hi); opacity:1 }
.pr__note{ fill:var(--soft-i); font-size:10px; letter-spacing:.04em }
@media (max-width:767px){ .pr__note{ font-size:13px } }

/* ---------- 15.4 План квартиры: что меряют ---------- */
.pl__rooms rect{ fill:rgba(255,255,255,.05); stroke:var(--line-fig); stroke-width:1.5 }
.pl__pt circle{ fill:var(--acc-hi) }
.pl__grp path{ stroke:var(--acc-hi); stroke-width:1.5; fill:none; opacity:.7 }
.pl__riser rect{ fill:none; stroke:var(--line-fig); stroke-width:1.8 }
.pl__l{ fill:var(--ink-i); opacity:.75 }
.pl__l--acc{ fill:var(--acc-hi); opacity:1 }

/* ==========================================================================
   15.5 🔴 ПОДПИСИ РИСУНКОВ — HTML, А НЕ <text> ВНУТРИ SVG
   Замер, из-за которого пришлось переделать: один и тот же рисунок стоит
   в колонке 400px на /nastroyka/ и во всю ширину 1104px на /kvartira/.
   viewBox у обоих 480, значит масштаб 0,83 и 2,3 — подпись в 11 единиц
   viewBox выходит 9px в узкой колонке и 25px в широкой. Читаемого размера
   она не имеет ни там, ни там, и подобрать его нельзя: он зависит от места,
   куда рисунок поставили.
   Лечение: SVG рисует ТОЛЬКО геометрию, подписи лежат HTML-ом поверх него
   в процентных координатах. Кегль берётся из токенов и не зависит от
   масштаба рисунка; текст при этом остаётся текстом — его можно выделить.
   ========================================================================== */
.figc__stage{ position:relative }
.figc__stage svg{ width:100%; height:auto; display:block }

.figc__tag{
  position:absolute; left:var(--x); top:var(--y);
  transform:translate(-50%,-50%);
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.1em;
  text-transform:uppercase; line-height:1.35; white-space:nowrap;
  color:var(--ink-i); opacity:.82; pointer-events:none;
}
.figc__tag--acc{ color:var(--ink-i); opacity:1 }
/* 🔴 Было color:var(--acc-hi). Замер по реальному пикселю 31.08.2026: подпись
   11px акцентным цветом на синей плите рисунка даёт 2,92–4,00 при пороге 4,5.
   Это тот же случай, что уже разобран у .hero .kicker: акцент читается на дне
   градиента и проваливается на пике. Подпись становится белой, акцент остаётся
   на выноске и точке. Токен --acc-hi не тронут. */
.figc__tag--l{ transform:translate(0,-50%) }          /* прижат влево от точки */
.figc__tag--r{ transform:translate(-100%,-50%) }      /* прижат вправо */
/* 🔴 Цвет подписи задаётся ПОВЕРХНОСТЬЮ РИСУНКА, а не блоком, в котором он
   стоит. Первая версия писала `.blk--light .figc__tag{color:var(--ink))`, и
   на светлой секции /kvartira/ подписи стали тёмными — поверх тёмной
   карточки рисунка. Карточка .figc тёмная всегда: на светлом блоке это
   --grad-card, на тёмном — плита --bg-2. Значит и подпись всегда светлая. */

/* На узком экране подписи поверх рисунка неизбежно сталкиваются:
   рисунок сжимается, а кегль — нет. Там они уходят под рисунок списком. */
@media (max-width:559px){
  .figc__stage{ display:flex; flex-direction:column }
  .figc__tag{
    position:static; transform:none; white-space:normal;
    display:inline-block; margin-right:var(--s-4);
  }
  .figc__tags{ margin-top:var(--s-4); display:flex; flex-wrap:wrap; gap:var(--s-2) var(--s-4) }
}
.figc__tags{ display:contents }
@media (max-width:559px){ .figc__tags{ display:flex } }

/* ---------- 15.6 Комплект — обычный список, а не рисунок с подписями ----------
   Число и слово здесь и так текст; в SVG они попали только потому, что
   рядом стоят глифы. Строим строкой: число — подпись — ряд глифов.
   ------------------------------------------------------------------------ */
.kit{ display:flex; flex-direction:column; gap:var(--s-4); max-width:620px }
/* без ограничения ширины строка растягивается на 1152px и между подписью
   и глифами открывается полоса пустоты в полэкрана */
.kit li{
  display:grid; grid-template-columns:auto 1fr auto; align-items:center;
  gap:var(--s-3) var(--s-4);
  padding-top:var(--s-4); border-top:var(--rule-i);
}
.kit li:first-child{ padding-top:0; border-top:0 }
.kit li:last-child{ padding-bottom:var(--s-2) }
.blk--light .kit li{ border-top:var(--rule) }
/* ТЗ 33, класс C: правило выше (0,2,1) перебивало сброс у первой строки
   (0,1,1), и на светлой секции линия висела на верхнем крае комплекта. */
.blk--light .kit li:first-child{ border-top:0 }
/* 🔴 Цвет — от поверхности рисунка, а не от окружающего блока: карточка
   .figc тёмная всегда, и на светлой секции правило `.blk--light .kit__n`
   делало число тёмным поверх тёмного. Тот же баг был у .figc__tag. */
.kit__n{
  font-family:var(--f-disp); font-weight:500; font-size:var(--t-num-sm);
  line-height:1; letter-spacing:var(--tr-num); font-variant-numeric:tabular-nums;
  color:var(--ink-i); min-width:2.4ch;
}
.kit__l{
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.12em;
  text-transform:uppercase; color:var(--ink-i); opacity:.75;
}
.kit__g{ height:26px; width:auto; max-width:100%; justify-self:end }

/* ---------- ТЗ 33, класс A. Плита комплекта работает во всю ширину ----------
   Замер на 1440: список ограничен 620 px, а плита идёт во всю ширину пояса —
   справа 641 px голого градиента высотой во весь блок. Место справа получает
   содержание, которое в этой фигуре уже было: подпись рисунка. Разрешение Р1
   класса A — ничего не придумано и не добавлено. */
@media (min-width:900px){
  .figc:has(> .kit){
    display:grid;
    /* 620 px — собственный предел .kit: дорожка auto под давлением второй
       колонки сжимала строки комплекта до 360 px и глифы наезжали на подпись */
    grid-template-columns:minmax(0,620px) minmax(0,1fr);
    column-gap:var(--s-8); align-items:center;
  }
  /* подпись стоит по центру списка: прижатая к верху или к низу, она
     оставляла бы под собой или над собой 350 px неработающей полосы */
  .figc:has(> .kit) > figcaption{ margin-top:0 }
}
@media (max-width:559px){
  .kit li{ grid-template-columns:auto 1fr }
  .kit__g{ grid-column:1 / -1; justify-self:start; height:22px; width:auto }
}

/* ---------- 15.7 Счёт из заголовка отдельной цифрой ----------
   Замер, из-за которого это появилось: на внутренних страницах самый крупный
   кегль в секции — h2, а на телефоне это 24px при теле 15px. Контраст
   масштабов 1,6 при пороге 2,5, и одинаково во ВСЕХ секциях: та же болезнь,
   что была на главной. Заголовки вроде «Пять вещей», «Четыре документа»,
   «Пять шагов» уже содержат число — выносим его в объект перед заголовком.
   Контурная, не залитая: залитая в таком кегле перебила бы сам заголовок.
   ------------------------------------------------------------------------ */
.ghost--head{
  font-size:calc(var(--t-num) * 1.15); line-height:.85;
  margin:0 0 var(--s-2); justify-self:start; align-self:auto;
}
@media (max-width:767px){
  .ghost--head{ font-size:calc(var(--t-num) * 1.05); margin-bottom:var(--s-1) }
}

/* ---------- 15.8 Сумма внутри заголовка ----------
   На /bystryy-start/ цена стоит прямо в h1. В кегле заголовка она ничем
   от него не отличается, хотя это главное число страницы.
   Отдельной строкой и в кегле цифры — разрыв 84/15 на десктопе.
   ------------------------------------------------------------------------ */
.h1__num{
  display:block; margin-top:var(--s-3);
  font-size:var(--t-num); line-height:var(--lh-num); letter-spacing:var(--tr-num);
  font-variant-numeric:tabular-nums; color:var(--acc-hi);
}
.blk--light .h1__num{ color:var(--acc) }

/* Статы героя на телефоне: --t-num-sm клампится в 38px, что даёт 2,53 —
   ровно на пороге. Поднимаем до 44, чтобы запас был не в сотых. */
@media (max-width:559px){
  .hero .stat__num{ font-size:44px }
}

/* ==========================================================================
   16. ДЛИННЫЕ СПИСКИ ПУНКТОВ: НА ТЕЛЕФОНЕ СВОРАЧИВАЮТСЯ
   Замер до правки (390): «Пять вещей, за которыми приходят» — 2,47 экрана,
   «Что входит в 48 000 ₽» — 2,55, «Четыре вещи по порядку» — 1,74.
   Потолок — 1,6 экрана, и это ровно та жалоба владельца: «секции не влезают
   в экран». Пять пунктов, у каждого заголовок и абзац на 3–5 строк, в столбик
   дают две с половиной высоты экрана, и сократить их отступами нельзя —
   вес даёт сам текст.
   Промпт даёт выход прямо: секция может быть длиннее полутора экранов только
   «со слайдером или другой механикой листания внутри». Механика здесь —
   раскрытие: на телефоне видны заголовок и цена, абзац открывается нажатием.
   На десктопе всё раскрыто всегда, и никакого взаимодействия не требуется.

   🔴 Раскрытие на десктопе — ЧИСТО НА CSS, без JS. <details> без атрибута
   open прячет содержимое силами браузера, поэтому переопределяем и display
   у детей, и ::details-content. Проверено в прогоне: на 1200 тело пункта
   видно, на 390 — скрыто. Если бы это делал JS, при его падении десктоп
   получил бы свёрнутые пункты.
   ========================================================================== */
.dpt{ min-width:0 }
.dpt > summary{
  display:flex; align-items:baseline; gap:var(--s-3);
  cursor:pointer; list-style:none;
  padding-block:var(--s-3);
}
.dpt > summary::-webkit-details-marker{ display:none }
.dpt > summary::after{
  content:""; margin-left:auto; flex:0 0 auto;
  width:11px; height:11px;
  border-right:1.5px solid var(--soft); border-bottom:1.5px solid var(--soft);
  transform:rotate(45deg) translate(-2px,-2px);
  transition:transform var(--d-base) var(--e-out);
}
.blk--dark .dpt > summary::after{ border-color:var(--soft-i) }
.dpt[open] > summary::after{ transform:rotate(-135deg) translate(-4px,-4px) }
.dpt > summary:focus-visible{ outline:2px solid var(--acc-hi); outline-offset:3px }

.dpt__body{ padding-bottom:var(--s-4) }

@media (min-width:768px){
  /* Раскрыто всегда: механика листания нужна только там, где не хватает высоты */
  .dpt > *:not(summary){ display:block }
  .dpt::details-content{ content-visibility:visible; block-size:auto }
  .dpt > summary{ cursor:default; padding-block:0 }
  .dpt > summary::after{ display:none }
  .dpt__body{ padding-bottom:0 }
}

/* Цена пункта — из абзаца в собственный объект. Она и была главным, что
   ищут в этом списке, но лежала в третьем предложении. */
.dpt__p{
  font-family:var(--f-mono); font-size:var(--t-small); font-weight:500;
  color:var(--acc); white-space:nowrap; flex:0 0 auto;
  font-variant-numeric:tabular-nums;
}
.blk--dark .dpt__p{ color:var(--acc-hi) }
@media (max-width:767px){
  .dpt{ border-top:var(--rule) }
  .blk--dark .dpt{ border-top:var(--rule-i) }
  .dpt:first-child{ border-top:0 }
  .dpt > summary{ min-height:var(--burger) }
}

/* ---------- 16.1 ДЛИННАЯ ТАБЛИЦА НА ТЕЛЕФОНЕ ЛИСТАЕТСЯ ВНУТРИ СЕБЯ ----------
   Замер (390): секция «Двушка в Москве, 50 устройств» — 3,56 экрана, вес
   даёт таблица сметы: на телефоне каждая строка раскладывается в блок
   (§12.1), и пятнадцать строк дают больше двух с половиной экранов.
   Сократить нечего — это данные, их не режут.
   Промпт разрешает секцию длиннее полутора экранов «со слайдером или другой
   механикой листания внутри». Механика здесь — прокрутка самой таблицы:
   она уже так работает по горизонтали (.price-wrap), добавляем вертикаль.
   Порог 8 строк: короткие таблицы в кармане не нуждаются.
   ------------------------------------------------------------------------ */
@media (max-width:767px){
  .price-wrap--tall{
    max-height:76svh; overflow-y:auto; -webkit-overflow-scrolling:touch;
    /* нижняя кромка растворяется — видно, что дальше есть строки */
    -webkit-mask-image:linear-gradient(#000 88%, #0000);
            mask-image:linear-gradient(#000 88%, #0000);
    padding-bottom:var(--s-5);
  }
  /* заголовок таблицы остаётся на месте при прокрутке кармана */
  .price-wrap--tall thead th{ position:sticky; top:0; z-index:1; background:var(--surf) }
  .blk--dark .price-wrap--tall thead th{ background:var(--bg-2) }
}

/* ---------- 16.2 Последний подрез мобильного ритма ----------
   После сворачивания пунктов и кармана у таблиц несколько секций остались
   на 1,61–2,05 экрана при потолке 1,6 — то есть промахивались на считанные
   проценты. Дожимаем тем, что не трогает читаемость: шаг секции, зазор ряда
   и высота кармана таблицы. Кегли и интерлиньяж не меняются.
   ------------------------------------------------------------------------ */
@media (max-width:767px){
  :root{
    --sec-py:28px;    /* было 32 */
    --sec-gap:20px;   /* было 24 */
  }
  .price-wrap--tall{ max-height:66svh }
  .dpt > summary{ padding-block:var(--s-2) }
  .u-mt{ margin-top:var(--s-5) }
  /* 🔴 ТЗ 31, правка 6. На телефоне шапка была рыхлее снаружи, чем внутри:
     кикер → заголовок 16 px, заголовок → лид 24 px, а до следующего блока
     те же 16 px. Замер на 375: 89 шапок на 23 страницах. Сжимаем внутреннее
     и чуть отпускаем внешнее — блок начинает читаться как блок. */
  .sec__head{ margin-bottom:var(--s-5) }
  .sec__head > .kicker{ margin-bottom:var(--s-2) }
  .sec__head > .u-mt-s{ margin-top:var(--s-3) }
}

/* ---------- 16.3 Шаги и карточки тоже сворачиваются на телефоне ---------- */
.dpt--step > summary{ display:grid; grid-template-columns:auto 1fr auto; align-items:start;
                      gap:var(--s-2) var(--s-5) }
.dpt--step .step__t{ font-family:var(--f-disp); font-weight:500; font-size:var(--t-h4);
                     line-height:1.3; display:block }
.dpt--step > summary::after{ align-self:center }
.step .dpt__body{ margin:0 }
@media (min-width:768px){
  .dpt--step > summary{ gap:var(--s-2) var(--s-5) }
  .step .dpt__body{ margin-top:var(--s-2) }
}
@media (max-width:767px){
  /* номер шага и заголовок в одну строку — иначе свёрнутый шаг занимает
     столько же, сколько занимал развёрнутый */
  .dpt--step > summary{ align-items:center }
  .step .dpt__body{ padding-left:calc(38px + var(--s-5)) }
}
[data-card="fold"]{ display:block }
[data-card="fold"] .dpt > summary{ padding-block:0 }

/* ==========================================================================
   17. ДЛИНА СТРОКИ — обязательная пара к расширению контейнера
   Замер после расширения --container до min(92vw,1840px), Range по реальным
   строкам (qa/_measure.mjs): на 1920 главная давала строки в 115 знаков,
   на 2560 — до 115 при норме 45–75 и потолке 90. Читать такое нельзя.
   Расширяется БЛОК и сетки внутри него — карточки, таблицы, ленты, рисунки.
   Текстовая колонка остаётся узкой.
   ========================================================================== */

/* 🔴 ТЗ 33, класс H. --measure (60ch) в реальных строках даёт 78–91 знак:
   в потолок 95 укладывается, а в норму «в среднем не больше 75» — нет.
   Знак «0», по которому считается ch, в Onest заметно шире средней буквы,
   поэтому 60ch ≈ 85 знаков. Там, где строка идёт без переносов до самого
   края ряда — пояснение пункта, вторая колонка списка определений —
   мера на ступень уже. Токен --measure не трогаем: он держит 22 страницы,
   и сужать его целиком значит расширить пустоту справа во всех секциях. */
:root{
  --measure-row: 48ch;   /* ряд во всю ширину: 13-пиксельный текст, ~78 знаков */
  --measure-nl:  42ch;   /* пояснение пункта .nlist набрано крупнее, ~78 знаков */
}

/* Базовое ограничение для всех текстовых потоков. min(100%, …) — чтобы
   в узкой ячейке сетки элемент не пытался стать шире неё. */
main p,
main li,
main dd,
main dt,
/* 🔴 ТЗ 33, класс H. figcaption в базовое правило не входил, и подпись
   под рисунком шла во всю ширину пояса: на /kvartira/ первая строка
   давала 170 знаков при потолке 95, вторая 106, на /bystryy-start/ — 96.
   Семь подписей на сайте, все мимо меры строки. Внутренний <p> подписи
   исключением ниже по-прежнему занимает её целиком — ограничение теперь
   держит сама подпись. */
main figcaption{
  max-width:min(100%, var(--measure));
}

/* Исключения: там, где длину строки задаёт не текст, а раскладка.
   Ячейки таблиц, подписи, метки, пункты навигации и ряды с числами
   должны занимать свою колонку целиком. */
main table p, main table li, main table dd, main table dt,
main figcaption p,
main .toc li, main .crumbs li, main .foot li, main .marquee li,
main .brands li, main .stat, main .fact, main .tier, main .totals__i,
main .artl dt, main .artl dd,
main .reqs dt, main .foot__req dt, main .foot__req dd,
/* 🔴 ТЗ 33, класс H. main .reqs dd из исключений убран: во второй колонке
   реквизитов идёт предложение, а не значение в одну строку, и на /policy/
   и /soglasie/ оно шло в 80 знаков при норме «в среднем 75». Ширину здесь
   задаёт текст, а не раскладка — значит правило меры к нему относится. */
main .stair__p, main .kit li, main .road__s, main .btn-row p,
main .legal li, main .legal dd, main .legal dt,
main .facts li, main .toc__l li{
  max-width:none;
}










   /* ширину держит колонка, не абзац */

/* Заголовки на широком экране тоже нельзя пускать во всю ширину блока.
   Дисплейный кегль читается короткой строкой: 26–30 знаков. */
main h2{ max-width:min(100%, 22ch) }
main h2.measure,
main h2.measure--tight,
main .h--sm,
main .brands-row__t,
main .sec__head h2{ max-width:min(100%, 26ch) }
main .toc h2, main table h2{ max-width:none }

/* ==========================================================================
   18. ШИРОКИЙ ЭКРАН: ОСВОБОДИВШУЮСЯ ШИРИНУ ЗАНИМАЕТ СЕТКА, А НЕ ПУСТОТА
   Расширение блока до 1766px (1920) сделало то, чего и следовало ожидать:
   текстовая колонка осталась на своей мере (60ch ≈ 600px), а её ячейка сетки
   выросла до 1000px. Замер: заполненность секции «Чего ретрофит не сделает»
   упала до 38,7 %, ещё четыре секции ушли ниже 55 %. Справа от списка
   висела полоса в 380px без единого элемента.
   Это ровно та «пустота», от которой лечили в прошлый раз, только пришедшая
   с другой стороны. Лечим не сужением блока обратно, а тем, что отдаём
   ширину содержимому: списки идут в две колонки, ленты и таблицы уже широкие.
   ========================================================================== */
@media (min-width:1600px){

  /* Список ограничений: пять пунктов в две колонки. Мера строки при этом
     не растёт — она задана колонкой, а не элементом. */
  /* 🔴 display:block обязателен. .nlist — это grid, а `columns` к grid- и
     flex-контейнерам не применяется вообще: правило молча не срабатывает.
     Первый заход этого не заметил — список остался в одну колонку. */
/* 🔴 ТЗ 31, правка 5. columns:2 снят и здесь: список ограничений — тоже
   список, и на 1600+ он ложился с тем же перекосом высот. */
  .nlist--tight{ max-width:none }
  .blk--dark .nlist--tight{ column-rule-color:var(--line-i) }
  .nlist--tight > li{ break-inside:avoid; margin-bottom:var(--s-4) }
  .nlist--tight > li:first-child{ padding-top:var(--s-4); border-top:var(--rule) }
  .nlist--tight p{ max-width:none }

  /* Аккордеон вопросов: две колонки. Раскрытие внутри колонки работает
     так же, соседний столбец просто перетекает. */
  .faq{ display:block; columns:2; column-gap:var(--s-7) }   /* .faq — flex, см. выше */
  .faq__item{ break-inside:avoid }
  .faq__a{ max-width:none }

  /* Прилипающая колонка получает больше места — цифра-якорь растёт вместе
     с ней, иначе в широкой колонке она теряется. */
  .ghost--sm{ font-size:calc(var(--t-num) * 3) }

  /* Двухколоночный абзац: третья колонка на очень широком экране.
     Мера строки прежняя, читается тремя короткими столбцами. */

}

/* ==========================================================================
   19. ЕДИНАЯ ВНУТРЕННЯЯ ОБЁРТКА БЛОКА
   Промпт 09, задача 3: «некоторые места кривые, неровные».
   Замер расстояния от верхней кромки секции до её первого элемента (1440):

     hero              96   .hero переопределял padding на --s-9
     бренды            33   .sec--tight ставил свой --s-6
     сроки             47   .blk--band ставил свой --s-6 плюс align-items:end
     вторая .sec       65   +1px от border-top разделителя между .sec
     остальные         64   норма
     подвал           103   своя раскладка

   Шесть разных значений там, где должно быть одно. Это и читается как
   «неровно»: глаз ловит, что заголовки соседних блоков стоят на разной
   высоте от кромки, хотя блоки выглядят одинаковыми.

   Ниже — одна обёртка на все блоки, тёмные и светлые одинаково.
   Все варианты (--tight, --band, .hero) свои отступы больше не задают.
   ========================================================================== */
.sec{
  position:relative;                     /* нужен для разделителя ниже */
  padding:var(--sec-py) var(--pad-blk);
}
.sec--tight,
.blk--band > .sec,
.hero{ padding:var(--sec-py) var(--pad-blk) }

/* 🔴 Разделитель между соседними .sec — линия ::before, а НЕ border-top.
   border добавляет свой пиксель в поток, и первый элемент второй секции
   оказывается на 65px от кромки против 64 у первой. Один пиксель не видно,
   но он делает «допуск 0» недостижимым, а метрика ловит разнобой.
   Абсолютная линия из потока не участвует. */
.sec + .sec,
.sec--crumbs + .sec{ border-top:0 }
.sec + .sec::before,
.sec--crumbs + .sec::before{
  content:""; position:absolute; top:0;
  left:var(--pad-blk); right:var(--pad-blk); height:1px;
  background:var(--line);
}
.blk--dark .sec + .sec::before,
.blk--dark .sec--crumbs + .sec::before{ background:var(--line-i) }

/* Крошки — единственная секция со своим, меньшим шагом: это строка навигации,
   а не содержательная секция. Значение из шкалы. */
.sec--crumbs{ padding-block:var(--s-4) }

/* Полоса сроков высоту больше не подрезает: нижний порог высоты снят
   промптом 09, ужимать её ради него незачем. */
.blk--band > .sec{ padding-block:var(--sec-py) }

/* Ряд заголовка шкалы выравнивается по верху, как везде. align-items:end
   сдвигал h2 вниз на 15px и ломал общую линию верха. */
@media (min-width:900px){
  .road-head{ align-items:start }
}

/* ---------- Зазоры вне шкалы ----------
   Замер: gap 7, 10 и 36px при шкале, кратной 4.
   7 и 10 — это `.14em` и `.2em` между числом и его единицей: типографически
   логично, но в общий ритм не попадает. 36 — это `4vh` в высоте героя.
   Все три приведены к шкале. */
.num--card,
.tier__n,
.totals__i .num--row{ gap:var(--s-2) }

@media (min-width:768px){
  .hero{ gap:var(--s-8) }
}
@media (max-width:767px){
  .hero{ gap:var(--s-6) }
}

/* Подвал — та же внутренняя обёртка, что у секций */
.foot__top{ padding:var(--sec-py) var(--pad-blk) var(--s-6) }

/* ==========================================================================
   20. ШТОРКА РАСТЁТ ВМЕСТЕ С БЛОКОМ
   Замечание владельца после расширения контейнера: «нав бар теперь слишком
   мини смотрится». Подтверждено замером — шторка была задана в абсолютных
   величинах и осталась 653px на любой ширине:

     экран  блок   шторка  доля блока
     1440   1325   653     49 %
     1920   1766   653     37 %
     2560   1840   653     35 %

   До расширения контейнера она занимала 53 % блока. Всё вокруг выросло,
   она — нет, поэтому и читается как мелкая. Лечим пропорцией: с 1600
   и с 2100 шторка получает следующий шаг по высоте, кеглю и полям.
   Ниже 1600 всё остаётся ровно как было: там макет утверждён.
   Радиусы не трогаем — они из утверждённого набора токенов.
   ========================================================================== */
@media (min-width:1600px){
  .notch{ height:52px; padding-inline:var(--s-3); gap:var(--s-2) }
  .notch__link{ font-size:var(--t-lead); padding:var(--s-2) var(--s-4) }
  .logo__mark{ width:32px; height:32px }
  .logo__word{ font-size:var(--t-lead) }
  .city{ font-size:var(--t-small); padding-inline:var(--s-4) }
  .city__ic{ width:32px; height:32px }
  .city__ic svg{ width:16px; height:16px }
  /* 🔴 top у вогнутых стыков НЕ трогать. Они сидят на ВЕРХНЕЙ кромке
     шторки (top:0) и образуют вырез там, где белое встречается с тёмным.
     Первый заход сдвинул их на высоту шторки — из-под неё полезли белые
     язычки. Высота шторки на положение стыков не влияет. */
}

@media (min-width:2100px){
  .notch{ height:58px; padding-inline:var(--s-4) }
  .notch__link{ padding:var(--s-3) var(--s-5) }
}

/* ---------- 20.1 Оглавление сворачивается на телефоне ----------
   Оглавление в герое добавило высоты именно там, где её и так не хватало:
   герой /kak-rabotaem/ с семью пунктами шёл 1,65 экрана при потолке 1,6.
   На десктопе оно раскрыто и работает как навигация, на телефоне —
   строка, которую можно развернуть. Раскрытие на десктопе — тем же
   чисто-CSS приёмом, что у .dpt (без JS). */
.toc__d > summary{
  list-style:none; cursor:pointer;
  display:flex; align-items:center; gap:var(--s-3);
}
.toc__d > summary::-webkit-details-marker{ display:none }
.toc__d > summary::after{
  content:""; margin-left:auto; width:9px; height:9px;
  border-right:1.5px solid currentColor; border-bottom:1.5px solid currentColor;
  transform:rotate(45deg) translate(-2px,-2px);
  transition:transform var(--d-base) var(--e-out); opacity:.6;
}
.toc__d[open] > summary::after{ transform:rotate(-135deg) translate(-4px,-4px) }
@media (max-width:767px){
  .toc__d > summary{ min-height:var(--burger); margin-bottom:0 }
}
@media (min-width:768px){
  .toc__d > *:not(summary){ display:block }
  .toc__d::details-content{ content-visibility:visible; block-size:auto }
  .toc__d > summary{ cursor:default }
  .toc__d > summary::after{ display:none }
}

/* ---------- 20.2 Пункты списка ограничений сворачиваются ---------- */
.dpt--nl > summary{ display:flex; align-items:baseline; gap:var(--s-3) }
.dpt--nl > summary strong{ color:var(--ink) }
.blk--dark .dpt--nl > summary strong{ color:var(--ink-i) }
.dpt--nl .dpt__body{ color:var(--soft); font-size:var(--t-body); line-height:var(--lh-body) }
.blk--dark .dpt--nl .dpt__body{ color:var(--soft-i) }
@media (min-width:768px){
  .dpt--nl > summary{ display:inline }
  .dpt--nl > summary::after{ display:none }
  /* на десктопе пункт читается как единый абзац, а не как заголовок + тело */
  .dpt--nl .dpt__body{ display:inline }
}

/* ---------- 20.3 Список ограничений во всю ширину секции ----------
   Замер /kvartira/ на 1440: секция «Чего ретрофит не делает» — заполненность
   27,8 %, правая половина блока пустая. Причина: каждый <li> упирался
   в меру строки (60ch ≈ 600px), а секция шириной 1237px. Список стоял
   узкой колонкой слева, справа не было ничего.
   Лечение то же, что на главной: ширину занимает содержимое, а не пустота.
   Меру строки при этом держит колонка, а не элемент.
   ------------------------------------------------------------------------ */
/* 🔴 ТЗ 31, правка 5. columns:2 снят: /nastroyka/ раскладывал три пункта
   2 + 1 и колонки расходились на 55 % (222 / 99 px). Ширину секции занимает
   не вторая колонка, а сужённый пояс — правка 4, разрешение Р1. */
@media (min-width:1024px){
  .sec > .nlist:not(.nlist--tight) > li{ max-width:none; margin-bottom:var(--s-5) }
  .sec > .nlist:not(.nlist--tight) p{ max-width:none }
}

/* ---------- 20.4 Карман таблицы и на десктопе ----------
   Замер /kvartira/ на 1440: секция «Двушка в Москве, 50 устройств» — 2,04
   экрана при потолке 1,4. Вес даёт смета на 14 строк. На телефоне у неё
   уже был карман, на десктопе — нет, и таблица шла во всю высоту.
   Потолок «секция влезает в экран» одинаково относится к обеим ширинам.
   Порог 10 строк: короткие таблицы читаются целиком и кармана не требуют.
   ------------------------------------------------------------------------ */
@media (min-width:768px){
  .price-wrap--pocket{
    max-height:72vh; overflow-y:auto;
    -webkit-mask-image:linear-gradient(#000 90%, #0000);
            mask-image:linear-gradient(#000 90%, #0000);
    padding-bottom:var(--s-5);
  }
  .price-wrap--pocket thead th{
    position:sticky; top:0; z-index:1; background:var(--surf);
  }
  .blk--dark .price-wrap--pocket thead th{ background:var(--bg-2) }
}

/* ---------- 20.5 Шаги процесса в две колонки на широком ----------
   /kak-rabotaem/ на 1440: «Шесть этапов» — 1,76 экрана. На десктопе свёртки
   раскрыты всегда, шесть шагов идут столбиком и не влезают в экран.
   Порядок чтения в многоколоночной вёрстке сверху вниз, потом вправо —
   для нумерованной последовательности он верный.
   ------------------------------------------------------------------------ */
/* 🔴 ТЗ 31, правка 5. columns:2 снят: пять шагов ложились 3 + 2 и колонки
   расходились на 35–36 % (/kvartira/ 568 / 363, /dogovor/ 413 / 269).
   У последовательности с нумерацией многоколоночного потока не бывает. */
@media (min-width:1200px){
  .step{ margin-bottom:var(--s-5) }
  /* 🔴 ТЗ 33, класс C. Здесь стояло возвращение верхней границы первому шагу:
     в многоколоночном потоке первый пункт правой колонки без неё выглядел
     оборванным. Поток снят ещё в ТЗ 31, а правило осталось — и линия висела
     на верхнем крае списка, ничего не разделяя (замер на 1440: /kvartira/,
     /nastroyka/, /kak-rabotaem/ — четыре списка). Разделитель принадлежит
     промежутку между шагами; на краю его нет. */
}

/* ---------- 20.6 Сетка пунктов шире на широком экране ---------- */
@media (min-width:1400px){
  .grid--3{ grid-template-columns:repeat(auto-fit,minmax(min(100%,300px),1fr)) }
}

/* Карман на телефоне чуть плотнее: 66svh оставляли секцию за потолком */
@media (max-width:767px){
  .price-wrap--tall{ max-height:56svh }
}

/* ---------- 20.7 Последний подрез мобильных высот ----------
   Замер по составу трёх оставшихся секций выше потолка (390):

     /bystryy-start/ «Что входит»   1608px  = карман 473 + рисунок 681 + текст 248
     /kvartira/      «Смета»        1419px  = шапка 369 + карман 473 + сетка 482
     /nastroyka/     «Считаю»       1692px  = шапка 274 + плита 1222 + кнопка 100

   Самый жирный кусок — рисунок комплекта: на телефоне ряд глифов уезжал
   на свою строку и удваивал высоту каждой из шести строк. Глифы мелкие,
   в строку с числом они помещаются.
   ------------------------------------------------------------------------ */
@media (max-width:559px){
  .kit li{ grid-template-columns:auto 1fr auto }
  .kit__g{ grid-column:auto; justify-self:end; height:18px }
  .kit__l{ font-size:10px; letter-spacing:.08em }
}

@media (max-width:767px){
  /* карман таблицы ещё плотнее: 56svh оставляли три секции за потолком */
  .price-wrap--tall{ max-height:48svh }
  /* строки таблицы на телефоне идут блоками — подрезаем их внутренний шаг */
  .price-wrap--tall .dtable tr,
  .price-wrap--tall .price-table tbody tr{ padding-block:var(--s-3) }
}

/* ---------- 20.8 Свёрнутая врезка-пример ---------- */
.dpt--note > summary strong{ font-weight:600; color:var(--ink) }
.blk--dark .dpt--note > summary strong{ color:var(--ink-i) }
.dpt--note .dpt__body{ margin-top:var(--s-2) }
@media (min-width:768px){
  .dpt--note > summary{ display:inline }
  .dpt--note .dpt__body{ display:inline }
}
/* Карман таблицы на десктопе плотнее: 72vh оставляли секции за 1,4 экрана */
@media (min-width:768px){
  .price-wrap--pocket{ max-height:62vh }
}

/* ---------- 20.9 Текст героя начинается с верхней кромки ----------
   Замер ритма на 1920: у героя расстояние до первого элемента 163px против
   64 у всех остальных секций. Причина — `align-items:center` на .hero__grid:
   правая колонка со схемой выше левой, и текст съезжал к её середине.
   На 1440 разница была невелика и в глаза не бросалась, на 1920 — 99px.
   ------------------------------------------------------------------------ */
.hero__grid{ align-items:start }

/* ---------- 21. КОНТАКТЫ ----------
   Телефон — публикуемый, добавлен 26.08.2026.
   Ставится в четырёх местах и НЕ ставится в шторке навигации: там нет места,
   и прошлый прогон уже ловил дублирование контактов в двух местах одного
   экрана как ошибку вёрстки.
   ------------------------------------------------------------------------ */
.foot__contacts{ display:flex; flex-wrap:wrap; gap:var(--s-2) var(--s-5) }

/* 🔴 Телефон на телефоне — тач-цель, а не текст. По правилу §7.2 ссылки
   внутри предложения из требования 44px исключены, но эта ссылка стоит
   отдельной строкой и нажимается пальцем. */
@media (pointer:coarse){
  /* 🔴 Правило по href, а не по классу. Первый заход перечислял места
     (.foot__contacts, .hero__cc-v) и пропустил телефон в реквизитах подвала —
     прогон нашёл 17px на всех двенадцати страницах. Номер набирают пальцем
     везде, где он стоит, поэтому цель задаётся самой ссылкой. */
  a[href^="tel:"]{ display:inline-flex; align-items:center; min-height:var(--burger) }
  .foot__contacts a,
  .hero__cc-v a{ display:inline-flex; align-items:center; min-height:var(--burger) }
}

/* Телефон крупной строкой — /kontakty/ */
.callout{ margin:var(--s-5) 0 0; display:flex; flex-direction:column; gap:var(--s-2) }
.callout__n{
  font-family:var(--f-disp); font-weight:500; font-size:var(--t-num-sm);
  line-height:var(--lh-num); letter-spacing:var(--tr-num);
  font-variant-numeric:tabular-nums; color:var(--ink); white-space:nowrap;
}
.blk--dark .callout__n{ color:var(--ink-i) }
.callout__x{ font-size:var(--t-small); color:var(--soft) }
.blk--dark .callout__x{ color:var(--soft-i) }
@media (hover:hover) and (pointer:fine){
  .callout__n:hover{ color:var(--acc) }
  .blk--dark .callout__n:hover{ color:var(--acc-hi) }
}

/* ---------- 21.1 Мера строки внутри свёрнутых пунктов ----------
   🔴 Замер Range после ввода .dpt: строки до 103 знаков при потолке 90.
   Причина тонкая: на десктопе тело пункта показывается как `display:inline`
   (чтобы заголовок и текст читались одним абзацем), а `max-width`
   к НЕЗАМЕЩАЕМЫМ СТРОЧНЫМ боксам не применяется вообще — ограничение
   молча не работало. Меру держит блочная обёртка <details>, а не абзац.
   ------------------------------------------------------------------------ */
.dpt{ max-width:min(100%, var(--measure)) }
/* Исключений для многоколоночных раскладок НЕ делаем: `min(100%, --measure)`
   и так берёт меньшее из ширины колонки и меры строки. Первая версия
   выключала ограничение в колонках, рассчитывая, что ширину задаст колонка, —
   и Range тут же показал 103 знака там, где колонка оказалась шире меры. */
.nlist > li,
.steps > li,
[data-card="fold"]{ max-width:min(100%, var(--measure)) }
@media (min-width:1024px){
  /* В многоколоночном списке ширину держит вложенный блок: <details> у пунктов
     с заголовком, обычный <p> — у пунктов без него. Снимать ограничение
     с самого <li>, не поставив его вложенному блоку, нельзя: на /price/
     в списке «если покупаете оборудование сами» пункты идут простыми
     абзацами, и Range показал там 97 знаков. */
  .sec > .nlist:not(.nlist--tight) > li{ max-width:none }
  .sec > .nlist:not(.nlist--tight) > li > p{ max-width:min(100%, var(--measure)) }
}

/* ---------- 21.2 Реквизиты на телефоне ----------
   После публикации телефона и мессенджера в блоке реквизитов стало
   на две строки больше, и секция «Реквизиты» на /kontakty/ ушла
   на 1,79 экрана при потолке 1,6 (замер: плита 1379px из 1514px секции).
   Значения реквизитов короткие — режем шаг строки, а не содержание.
   ------------------------------------------------------------------------ */
@media (max-width:767px){
  .reqs > div{ padding-block:var(--s-2); gap:0 }
  .reqs dt{ margin-bottom:0; line-height:1.35 }
  .reqs dd{ line-height:1.4 }
}

/* ==========================================================================
   22. Меню: панель «Направления»
   ==========================================================================
   Восьмой пункт в пластину не влезает физически, поэтому Квартира, Быстрый
   старт и Настройка уезжают внутрь панели, а снаружи остаются пять пунктов.

   Разметка одна на оба режима — <details>/<summary>. Это принципиально:
   без JS панель раскрывается нативно, и все восемь ссылок остаются доступны.
   JS сверху добавляет задержки наведения, стрелки и Esc — но не является
   условием работоспособности меню.

   На десктопе <details> позиционируется абсолютно от .notch (у него уже
   position:relative) и читается как продолжение пластины: верхние углы
   прямые, нижние — тем же радиусом, что вырез шторки.

   🔴 transform ставится на саму панель, не на .notch и не на .notch__list:
   transform у предка .notch__list ломает position:fixed мобильной шторки
   (та же грабля, что у .blk__glow, см. строку 612).
   ------------------------------------------------------------------------ */
.notch__dir{ position:static; display:flex; align-items:center }

.notch__sum{
  display:flex; align-items:center; gap:var(--s-1);
  font-size:var(--t-small); color:var(--ink);
  padding:var(--s-2) var(--s-3); border-radius:var(--r-pill);
  white-space:nowrap; cursor:pointer; list-style:none;
  transition:background var(--d-fast) var(--e-out), color var(--d-fast) var(--e-out);
}
.notch__sum::-webkit-details-marker{ display:none }
.notch__sum::marker{ content:"" }
.notch__chev{
  width:11px; height:11px; flex:0 0 auto;
  transition:transform var(--d-fast) var(--e-out);
}
.notch__dir[open] > .notch__sum > .notch__chev{ transform:rotate(180deg) }

/* Активное состояние: страница внутри панели подсвечивает пункт так же,
   как подсветился бы прямой пункт шторки. */
.notch__sum[data-in="true"]{ background:var(--surf-2); color:var(--acc) }

.notch__drop{
  /* left:0 вместо центрирования: панель должна продолжать пластину, а не
     висеть под ней шире с обеих сторон. Замер на 1440: шторка 476px —
     580px держит стык слева и уводит расширение только вправо — пластина
     читается как расширяющаяся книзу, а не как чужой слой поверх.
     480px пробовал первым: восемь ссылок ломались на две строки каждая. */
  position:absolute; top:100%; left:0;
  width:max(100%, min(580px, calc(100vw - var(--s-6))));
  background:var(--surf);
  border-radius:0 0 var(--r-notch) var(--r-notch);
  box-shadow:var(--sh-lift);
  padding:var(--s-5) var(--s-5) 0;
  z-index:var(--z-notch);
}
.notch__dgrid{
  display:grid; grid-template-columns:1fr 1fr; gap:0 var(--s-6);
}
.notch__dttl{
  font-family:var(--f-mono); font-size:var(--t-micro); color:var(--soft);
  letter-spacing:var(--tr-mark); text-transform:uppercase;
  padding:0 var(--s-3) var(--s-2);
}
.notch__dlink{
  display:flex; align-items:center; min-height:var(--burger);
  font-size:var(--t-body); color:var(--ink); line-height:var(--lh-head);
  padding:var(--s-2) var(--s-3); border-radius:var(--r-fld);
  transition:background var(--d-fast) var(--e-out), color var(--d-fast) var(--e-out);
}
.notch__dlink[aria-current="page"]{ background:var(--surf-2); color:var(--acc) }

/* Нижняя полоса на всю ширину панели — вне сетки колонок, поэтому
   отрицательные поля возвращают её к краям, а не к колонке. */
.notch__dfoot{
  /* flex со space-between: иначе текст и ссылка идут одной строкой на всю
     ширину панели — замер с раскрытым меню дал 106 знаков при потолке 85. */
  display:flex; flex-wrap:wrap; align-items:baseline;
  gap:var(--s-1) var(--s-4); justify-content:space-between;
  margin:var(--s-4) calc(var(--s-5) * -1) 0;
  padding:var(--s-3) var(--s-5);
  border-top:1px solid var(--line);
  background:var(--surf-2);
  border-radius:0 0 var(--r-notch) var(--r-notch);
  font-size:var(--t-small); color:var(--soft); line-height:var(--lh-body);
}
/* текст полосы — отдельным span: голый текст в flex-контейнере становится
   анонимным элементом, который не переносится и держит строку в 106 знаков */
.notch__dfoot > span{ max-width:42ch }
.notch__dfoot a{ color:var(--acc); text-decoration:underline; text-underline-offset:.18em }

@media (hover:hover){
  .notch__sum:hover{ background:var(--surf-2); color:var(--acc) }
  .notch__dlink:hover{ background:var(--surf-2); color:var(--acc) }
}

/* ---------- 22.1 ≤1139 и ≤1023: панель сужается вместе со шторкой ---------- */
@media (max-width:1139px){
  .notch__sum{ padding-inline:var(--s-2) }
}
@media (max-width:1023px){
  .notch__sum{ padding-inline:var(--s-1); font-size:var(--t-micro);
               font-family:var(--f-body); letter-spacing:0; text-transform:none }
  .notch__drop{ width:max(100%, min(460px, calc(100vw - var(--s-5)))) }
}

/* ---------- 22.2 ≤767: аккордеон в бургере ----------
   Наведения нет — панель раскрывается на месте, в потоке. Остальные пункты
   уезжают вниз и не перекрываются: position:static вместо absolute.
   Содержимое существует в DOM всегда, скрыто только атрибутом open.
   ------------------------------------------------------------------------ */
@media (max-width:767px){
  .notch__dir{ display:block }

  .notch__sum{
    justify-content:space-between; width:100%;
    font-size:var(--t-lead); font-weight:500;
    min-height:var(--burger); padding:var(--s-3) var(--s-4);
    border-radius:var(--r-fld);
  }
  .notch__chev{ width:14px; height:14px }

  .notch__drop{
    position:static; width:auto;
    box-shadow:none; background:transparent;
    border-radius:0; padding:var(--s-1) 0 var(--s-2) var(--s-4);
    margin-left:var(--s-2); border-left:1px solid var(--line);
  }
  .notch__dgrid{ grid-template-columns:1fr; gap:0 }

  .notch__dttl{
    padding:var(--s-4) var(--s-3) var(--s-1);
  }
  .notch__dcol:first-child .notch__dttl{ padding-top:var(--s-2) }

  .notch__dlink{ font-size:var(--t-body); padding:var(--s-3) }

  /* Полоса-подсказка в бургере занимает треть экрана и в вертикальном
     списке из восьми ссылок работает против сканирования. Ссылка на прайс
     в шторке и так есть отдельным пунктом. */
  .notch__dfoot{ display:none }
}

/* ==========================================================================
   23. Подвал: четыре колонки
   ==========================================================================
   Подвал — единственное место, где каждая страница получает входящую
   ссылку. Колонок стало четыре вместо трёх, и в самой длинной семь пунктов,
   поэтому auto-fit по 4 не годится: на 834 он даёт две колонки по семь
   строк и подвал уходит на полтора экрана. Явные три ступени.
   ------------------------------------------------------------------------ */
/* Бренд-колонка и сетка ссылок — две разные сущности, поэтому auto-fit
   на .foot__top больше не годится: он ставил бренд пятой равной колонкой.
   Правило существующее не редактируем — переопределяем каскадом. */
.foot__top{ grid-template-columns:minmax(200px, 1fr) 3fr }

.foot__nav{
  display:grid; grid-template-columns:repeat(4, minmax(0, 1fr));
  gap:var(--s-6) var(--s-5);
}
@media (max-width:1023px){
  .foot__top{ grid-template-columns:1fr }
  .foot__nav{ grid-template-columns:repeat(2, minmax(0, 1fr)); gap:var(--s-5) var(--s-5) }
}
/* На 390 одна колонка давала подвал в 2,10 экрана при потолке 1,6:
   двадцать ссылок по 44px тач-цели складываются в 880px только ссылками.
   Две колонки — по разделу 7.3 «сетка плотнее», а не «убрать пункты». */
@media (max-width:479px){
  .foot__nav{ grid-template-columns:repeat(2, minmax(0, 1fr)); gap:var(--s-5) var(--s-4) }
  .foot__ttl{ margin-bottom:var(--s-2) }
  .foot__list{ gap:0 }
}

/* Реквизиты отдельной строкой, а не хвостом к копирайту: слитой строкой
   они дали 106 знаков на /404.html при потолке 85 — там мало контента,
   и подвальная строка сама попала в 90-й процентиль. */
.foot__req-line{ font-size:var(--t-small) }
@media (max-width:767px){
  .foot__bottom{ align-items:flex-start; flex-direction:column; gap:var(--s-3) }
  .foot__legal{ margin-left:0 }
}

/* ==========================================================================
   24. .verdict — таблица «зона → вердикт → на что смотреть»
   ==========================================================================
   Двенадцать строк правовой карты помещений. Средняя колонка — не текст,
   а метка: читатель сканирует страницу по ней, а не по названиям зон.
   Отсюда три состояния цветом из токенов --ok / --warn / --err и никаких
   новых значений.

   На телефоне таблица из трёх колонок даёт либо горизонтальный скролл, либо
   строку в 12 знаков. Поэтому там она разворачивается в карточки: заголовок
   строки берётся из data-z через ::before, разметка остаётся табличной
   и для робота, и для скринридера.
   ------------------------------------------------------------------------ */
.verdict{ width:100%; border-collapse:collapse; font-size:var(--t-small) }
.verdict th,
.verdict td{ text-align:left; padding:var(--s-3); border-bottom:1px solid var(--line);
             vertical-align:top; line-height:1.5 }
.verdict thead th{
  font-family:var(--f-mono); font-size:var(--t-micro); font-weight:400;
  letter-spacing:.1em; text-transform:uppercase; color:var(--soft);
}
.verdict tbody th{ font-weight:500; color:var(--ink); width:34% }
.verdict td:last-child{ color:var(--soft) }
.verdict tbody tr:last-child th,
.verdict tbody tr:last-child td{ border-bottom:0 }
/* ТЗ 33, класс A. Таблица, у которой во второй колонке одно слово: колонка
   сужается по содержанию, всю остальную ширину забирает первая. Иначе
   восемь ячеек «я» держат 800 px из 1237 (замер на 1440, /partneram/). */
.verdict--who td{ width:1%; white-space:nowrap }

.verdict__m{
  display:inline-block; white-space:nowrap;
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.06em;
  padding:var(--s-1) var(--s-2); border-radius:var(--r-pill);
  border:1px solid currentColor;
}
.verdict__m--yes{ color:var(--ok) }
.verdict__m--may{ color:var(--warn) }
.verdict__m--no{ color:var(--err) }

@media (max-width:767px){
  .verdict,
  .verdict tbody,
  .verdict tr,
  .verdict th,
  .verdict td{ display:block; width:auto }
  .verdict thead{
    position:absolute; width:1px; height:1px; padding:0; margin:-1px;
    overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
  }
  /* схлопываем и сами ячейки шапки: clip-path прячет отрисовку, но ширину
     они продолжают занимать и замер считает это вылетом за правый край */
  .verdict thead th{ display:block; width:1px; height:1px; overflow:hidden; padding:0; border:0 }

  .verdict tbody tr{
    padding:var(--s-4) 0; border-bottom:1px solid var(--line);
  }
  .verdict tbody tr:last-child{ border-bottom:0 }
  .verdict tbody th,
  .verdict td{ border-bottom:0; padding:0 }
  .verdict tbody th{ width:auto; font-size:var(--t-body); margin-bottom:var(--s-2) }
  .verdict td[data-c]{ margin-top:var(--s-2) }
  .verdict td[data-c]::before{
    content:attr(data-c) " — "; color:var(--soft);
  }
}

/* ==========================================================================
   25. .numlist — нумерованный список с крупной цифрой
   ==========================================================================
   «Пять вещей, которые делают камеру незаконной»: цифра работает как якорь
   сканирования, поэтому набрана --t-num-sm против --t-body — отношение
   примерно 3,5, выше порога контраста масштабов 2,5.

   Счётчик CSS не используем: пункты нумеруются <ol>, и номер обязан
   попадать в озвучку скринридера, а сгенерированный content туда не попадает
   надёжно. Цифра рисуется из ::marker выключённого <ol> — <b> в разметке.
   ------------------------------------------------------------------------ */
.numlist{ display:grid; gap:var(--s-5); counter-reset:none; list-style:none; padding:0 }
.numlist > li{
  display:grid; grid-template-columns:auto 1fr; gap:0 var(--s-5);
  align-items:start;
  padding-top:var(--s-5); border-top:1px solid var(--line);
}
.numlist > li:first-child{ padding-top:0; border-top:0 }
.blk--dark .numlist > li{ border-top-color:var(--line-i) }

.numlist__n{
  font-family:var(--f-disp); font-size:var(--t-num-sm); line-height:var(--lh-num);
  letter-spacing:var(--tr-num); color:var(--acc);
  grid-row:span 2;
}
.numlist__t{ font-size:var(--t-h4); font-weight:600; line-height:var(--lh-head);
             margin:0 0 var(--s-2) }
.numlist__x{ color:var(--soft); line-height:var(--lh-body);
             max-width:min(100%, var(--measure)); margin:0 }
/* Срок пункта — в строке заголовка, кеглем подписи. Отдельной частью он был бы
   четвёртой в сетке из трёх и ломал бы отступ (ТЗ 41, заход 8б). */
.numlist__m{ font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.08em;
             color:var(--soft); margin-left:var(--s-3); white-space:nowrap }
.blk--dark .numlist__m{ color:var(--soft-i) }
/* Пояснение из нескольких абзацев остаётся ОДНОЙ частью пункта. */
div.numlist__x > p{ margin:0 }
div.numlist__x > p + p{ margin-top:var(--s-3) }

@media (max-width:767px){
  .numlist{ gap:var(--s-4) }
  .numlist > li{ grid-template-columns:auto 1fr; gap:0 var(--s-4); padding-top:var(--s-4) }
  .numlist__n{ font-size:var(--t-h3) }
}

/* ---------- 24.1 .verdict на телефоне: плотнее ----------
   Замер /videonablyudenie/ 390: секция «Карта помещений» — 2,96 экрана
   при потолке 1,6. Двенадцать строк в развёрнутых карточках давали
   по ~200px каждая. Режем шаг, а не строки: правовая карта неполной
   не бывает, выкидывать зону нельзя.
   ------------------------------------------------------------------------ */
@media (max-width:767px){
  .verdict tbody tr{ padding:var(--s-3) 0 }
  .verdict tbody th{ font-size:var(--t-body); margin-bottom:var(--s-1); line-height:1.35 }
  .verdict td{ line-height:1.45 }
  .verdict td[data-c]{ margin-top:var(--s-1); font-size:var(--t-small) }
  /* префикс «На что смотреть — » повторяется двенадцать раз и на узком
     экране съедает по строке на каждой карточке */
  .verdict td[data-c]::before{ content:none }
  .verdict__m{ font-size:10px; padding:2px var(--s-2) }
}

/* ---------- 24.2 .verdict 390: зона и вердикт в одну строку ----------
   После первого уплотнения секция всё ещё шла 2,47 экрана. Метка на своей
   строке стоила 20px × 12 строк = 240px впустую: она короткая и встаёт
   рядом с названием зоны без ущерба сканированию.
   ------------------------------------------------------------------------ */
@media (max-width:767px){
  .verdict tbody tr{
    display:grid; grid-template-columns:1fr auto; gap:var(--s-1) var(--s-3);
    align-items:baseline; padding:var(--s-3) 0;
  }
  .verdict tbody th{ grid-column:1; margin-bottom:0 }
  .verdict tbody td:first-of-type{ grid-column:2; grid-row:1; justify-self:end }
  .verdict td[data-c]{ grid-column:1 / -1; margin-top:0 }
}

/* ---------- 25.1 .numlist 390 ----------
   Секция «Камера законна почти везде» шла 1,61 при потолке 1,6 — не хватало
   одного шага. Режем отступ между пунктами, цифру и заголовок оставляем.
   ------------------------------------------------------------------------ */
@media (max-width:767px){
  .numlist{ gap:var(--s-3) }
  .numlist > li{ padding-top:var(--s-3) }
  .numlist__t{ font-size:var(--t-body); margin-bottom:var(--s-1) }
}

/* ---------- 26. Плотность на 390: сетка карточек ----------
   Замер /videonablyudenie/: «Чего не делаю» 1445px при потолке 1350.
   Шаг сетки и поля карточки — на токен вниз. Ровно тот ход, который
   раздел 7.3 называет «сетка плотнее», и он же поднимает заполненность.

   🔴 min-width на .price-table здесь НЕ ставить. Проект разворачивает
   прайс-таблицы на телефоне в блоки (правило выше, ~1511), и заданная
   ширина растягивает эти блоки за экран вместо того, чтобы дать прокрутку.
   Проверено: min-width:560px увеличил секцию сметы с 1,65 до 1,93.
   ------------------------------------------------------------------------ */
@media (max-width:767px){
  .grid{ gap:var(--s-4) }
  .card{ padding:var(--s-4) }
}

/* ==========================================================================
   27. .spec — спецификация: позиция, расчёт, сумма, итог отдельной строкой
   ==========================================================================
   Разбор сметы: «Оборудование — закупка 56 400 + 15% — 64 860». Три поля,
   последняя строка — итог.

   Почему не .price-table. На телефоне прайс-таблица разворачивается в блоки
   и каждое значение получает метку над собой отдельной строкой. Для прайса
   это верно: там два поля и метка работает. Для сметы полей три, и разворот
   даёт четыре строки на запись — секция сметы вышла 1,97 экрана при потолке
   1,6. Здесь расчёт и сумма встают в один ряд, а метка идёт инлайн.

   Разметка остаётся <table> с <th scope>: робот и скринридер читают её
   как таблицу, меняется только раскладка.
   ------------------------------------------------------------------------ */
.spec{ width:100%; border-collapse:collapse; font-size:var(--t-small) }
/* 8px по вертикали, не 12: на 1440 десять строк сметы давали секцию
   в 1,50 экрана при потолке 1,4, и весь перебор сидел в воздухе ячеек. */
.spec th,
.spec td{ text-align:left; padding:var(--s-2) var(--s-3); border-bottom:1px solid var(--line);
          vertical-align:top; line-height:1.45 }
.spec thead th{
  font-family:var(--f-mono); font-size:var(--t-micro); font-weight:400;
  letter-spacing:.1em; text-transform:uppercase; color:var(--soft);
}
.spec tbody th{ font-weight:500; color:var(--ink) }
.spec td.calc{ color:var(--soft); white-space:nowrap }
/* 🔴 .num в проекте — акцентная цифра карточки, --t-num-sm (52px). В прайс-таблице
   это осознанно: там пять-восемь строк и цена работает как объект. В спецификации
   строк тринадцать, и 52px дают 92px на строку — смета /skud/ вышла 1,95 экрана
   при потолке 1,4. Здесь цифра табличная, а не акцентная. */
.spec td.num{
  text-align:right; font-family:var(--f-mono); font-weight:400;
  font-size:var(--t-small); line-height:1.45; letter-spacing:0;
  font-variant-numeric:tabular-nums; white-space:nowrap; width:1%;
}
.spec .total td.num{ font-weight:600 }
.spec .total th,
.spec .total td{ border-bottom:0; font-weight:600; color:var(--ink) }
/* 🔴 ТЗ 33, класс C. Разделитель принадлежит промежутку между строками:
   у последней строки его нет. Там, где у таблицы нет итоговой строки
   (.total, которая обнуляет границу сама), нижняя линия висела на краю —
   14 таблиц .spec на 1440, под ней до 540 px пустоты. То же правило
   уже стоит у .price-table и .verdict. */
.spec tbody tr:last-child td,
.spec tbody tr:last-child th{ border-bottom:0 }
.spec .total{ border-top:2px solid var(--ink) }

/* 🔴 ТЗ 33, класс D. Колонка «состав/комментарий» набрана без переносов,
   и на 768–1199 таблица становилась шире пояса: на главной «работы 14 550 ₽
   плюс оборудование» уезжало за правый край на 349 px и обрезалось краем
   секции, на /biznes/sklady/ — на 245 px. Запрет переносов на всю ячейку
   ставился ради коротких «около 30»; после ТЗ 33 каждая сумма защищена
   неразрывностью поштучно, и запрет на всю ячейку больше не нужен. */
@media (max-width:1199px){
  .spec td.calc{ white-space:normal }
  /* заголовок колонки тоже: «На объекте» одной строкой добавлял 19 px */
  .spec thead th{ white-space:normal }
}

.blk--dark .spec th,
.blk--dark .spec td{ border-bottom-color:var(--line-i) }
.blk--dark .spec tbody th,
.blk--dark .spec .total th,
.blk--dark .spec .total td{ color:var(--ink-i) }
.blk--dark .spec .total{ border-top-color:var(--ink-i) }

@media (max-width:767px){
  .spec,
  .spec tbody,
  .spec tr,
  .spec th,
  .spec td{ display:block; width:auto }
  .spec thead{
    position:absolute; width:1px; height:1px; padding:0; margin:-1px;
    overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
  }
  .spec thead th{ display:block; width:1px; height:1px; overflow:hidden; padding:0; border:0 }

  /* позиция на всю ширину, расчёт и суммы — в один ряд под ней.
     flex, а не grid с фиксированными колонками: спецификаций бывает
     и на три поля (позиция, расчёт, сумма), и на четыре (позиция,
     количество, оборудование, работа) — flex укладывает оба случая.
     8px шага, не 12: смета /skud/ из тринадцати строк шла 1,65 экрана. */
  .spec tbody tr{
    display:flex; flex-wrap:wrap; align-items:baseline; gap:0 var(--s-3);
    padding:var(--s-2) 0; border-bottom:1px solid var(--line);
  }
  .spec tbody th{ flex:0 0 100%; padding:0; border:0; margin-bottom:var(--s-1) }
  .spec td{ padding:0; border:0 }
  .spec td.calc{ flex:1 1 auto; white-space:normal }
  .spec td.num{ width:auto; text-align:right }
  /* метка колонки перед значением: в развёрнутой строке два числа подряд
     без подписи не читаются */
  .spec td[data-l]::before{ content:attr(data-l) " "; color:var(--soft) }
  .spec td:empty{ display:none }
  .spec .total{ border-top:2px solid var(--ink); border-bottom:0 }
  .blk--dark .spec tbody tr{ border-bottom-color:var(--line-i) }
}

/* ---------- 26.1 Шаг строки прайс-таблицы на 390 ----------
   Прайс-блок /videonablyudenie/ упирался в 1353px при потолке 1350 —
   не хватало трёх пикселей. Шаг развёрнутой строки 16px сверху и снизу
   при восьми строках даёт 256px чистого воздуха; 12px читаются так же.
   /price/ от этой правки тоже выигрывает: там секций-нарушителей больше.
   ------------------------------------------------------------------------ */
@media (max-width:767px){
  .price-table tr{ padding-block:var(--s-3) }
}

/* ---------- 28. Мера строки для --t-lead ----------
   Замер: .lead.measure на 1440 даёт строки в 94–98 знаков при потолке 85.
   Причина в единице: 56ch считаются по ширине «0», а «0» в Onest —
   табличная цифра и заметно шире средней кириллической буквы. Для тела
   в 15px 56ch выходят ~85 знаков и попадают в норму, для лида в 17px —
   уже ~95.

   Токен --measure не трогаем (он на чтение): сужаем меру там, где кегль
   крупнее тела. Замер после правки — p90 85 → 78 на 1440.
   ------------------------------------------------------------------------ */
.lead.measure{ max-width:50ch }
.numlist__x{ max-width:min(100%, 50ch) }

/* Заголовок карточки, обёрнутый в ссылку: без этого правила тач-цель
   считается по строке текста и даёт 23px вместо 44. */
@media (pointer:coarse){
  .card__title a{ display:inline-flex; align-items:center; min-height:var(--burger) }
}

/* ---------- 27.1 Пара спецификаций на 390 ----------
   Две сметы рядом на десктопе складываются на телефоне в одну колонку
   и дают секцию в 2,1 экрана при потолке 1,6. Разносить их по разным
   секциям нельзя — это одна мысль «однушка против двушки», их и надо
   видеть рядом. Режем шаг внутри спецификации на телефоне.
   ------------------------------------------------------------------------ */
@media (max-width:767px){
  .spec-pair .spec tbody tr{ padding:var(--s-1) 0 }
  .spec-pair .spec tbody th{ margin-bottom:0; line-height:1.25 }
  .spec-pair .spec td{ line-height:1.25 }
  .spec-pair > div + div{ margin-top:var(--s-3) }
  .spec-pair .h--sm{ margin-bottom:var(--s-2) }
}

/* ---------- 28.1 Мера текста в карточке ----------
   Карточка в .grid--2 на 1440 получает ~690px, и .card__text шёл в 107 знаков
   при потолке 85 (/osveshchenie/, p90 91). В .grid--3 колонка сама уже меры,
   поэтому раньше это не всплывало. 52ch по 15px — около 79 знаков кириллицей.
   ------------------------------------------------------------------------ */
.card__text{ max-width:min(100%, 50ch) }

/* ==========================================================================
   29. .tariffs / .tariff — три тарифа с ценой и составом
   ==========================================================================
   Вход, основной и сетевой. Цена — второй по величине объект карточки после
   названия: --t-num-sm против --t-body, отношение примерно 3,5.
   Строка «Кому» отделена линией: это не состав, это адресат.
   ------------------------------------------------------------------------ */
.tariffs{ display:grid; gap:var(--col-gap); grid-template-columns:repeat(auto-fit,minmax(min(100%,280px),1fr)) }

.tariff{
  display:flex; flex-direction:column;
  background:var(--surf); border:1px solid var(--line); border-radius:var(--r-card);
  padding:var(--s-5);
}
.blk--dark .tariff{ background:var(--surf-2); border-color:var(--line) }

.tariff__n{ font-size:var(--t-h4); font-weight:600; line-height:var(--lh-head); margin:0 }
.tariff__p{
  font-family:var(--f-disp); font-weight:500; font-size:var(--t-num-sm);
  line-height:var(--lh-num); letter-spacing:var(--tr-num);
  font-variant-numeric:tabular-nums; color:var(--acc);
  margin:var(--s-3) 0 var(--s-1);
}
.tariff__u{ font-size:var(--t-small); color:var(--soft); font-family:var(--f-body);
            letter-spacing:0; display:block;
            /* ТЗ 33, класс D. Интерлиньяж цифры 0,92 — её строчный бокс ниже
               самого глифа, и подпись формально наезжала на число на 8 px
               (замер на 1440–768, три тарифа). Отбивка разводит их. */
            margin-top:var(--s-2) }
.tariff__x{ color:var(--soft); line-height:var(--lh-body); margin:var(--s-4) 0 0;
            max-width:min(100%, 50ch) }
.tariff__who{
  margin:var(--s-4) 0 0; padding-top:var(--s-4); border-top:1px solid var(--line);
  font-size:var(--t-small); color:var(--ink);
}
.tariff__who b{ font-weight:500; color:var(--soft) }

@media (max-width:767px){
  .tariffs{ gap:var(--s-4) }
  .tariff{ padding:var(--s-4) }
  .tariff__p{ font-size:var(--t-h3) }
  .tariff__x,
  .tariff__who{ margin-top:var(--s-3); padding-top:var(--s-3) }
}

/* ---------- 29.1 Тарифы и длинные формы на 390 ----------
   Замер /biznes/obshchepit/: тарифы 1,615, форма 1,646, блок калибровки 1,612
   при потолке 1,6. Везде перебор в пределах полусотни пикселей — режем шаг,
   а не содержание.
   ------------------------------------------------------------------------ */
@media (max-width:767px){
  .tariff__x{ margin-top:var(--s-2) }
  .tariff__who{ margin-top:var(--s-3); padding-top:var(--s-2) }

  /* форма из семи полей: .form — flex-колонка с gap 16px, семь промежутков
     дают 112px только на воздух. Режем сам gap, а не добавляем margin
     поверх него (первая попытка так и сделала — форма выросла с 1,65 до 1,70). */
  .form{ gap:var(--s-3) }
  .form .field__lbl{ margin-bottom:var(--s-1) }

  /* второй столбец блока про калибровку — самостоятельный подраздел */
  .calib-extra{ margin-top:var(--s-4) }
}

/* ==========================================================================
   30. .hub — сетка хаба: четыре сегмента и три направления
   ==========================================================================
   Две группы карточек с разным весом. Сегмент — это готовый разбор с ценой
   и сроком, направление — работа, которая нужна почти везде. Поэтому сегменты
   идут крупнее и по двое в ряд, направления — по трое и компактнее.

   Цена в карточке сегмента набрана --t-num-sm: она и есть то, за чем сюда
   пришли, а название сегмента читается само.
   ------------------------------------------------------------------------ */
.hub{ display:grid; gap:var(--col-gap); grid-template-columns:repeat(auto-fit,minmax(min(100%,340px),1fr)) }
.hub--3{ grid-template-columns:repeat(auto-fit,minmax(min(100%,260px),1fr)) }

.hub__i{
  display:flex; flex-direction:column;
  background:var(--surf); border:1px solid var(--line); border-radius:var(--r-card);
  padding:var(--s-5);
  transition:border-color var(--d-fast) var(--e-out), box-shadow var(--d-fast) var(--e-out);
}
.blk--dark .hub__i{ background:var(--surf-2) }
@media (hover:hover){
  .hub__i:hover{ border-color:var(--acc); box-shadow:var(--sh-card) }
}

.hub__t{ font-size:var(--t-h4); font-weight:600; line-height:var(--lh-head); margin:0 }
.hub__x{ color:var(--soft); line-height:var(--lh-body); margin:var(--s-3) 0 0;
         max-width:min(100%, 50ch) }
.hub__p{
  font-family:var(--f-mono); font-size:var(--t-small); color:var(--ink);
  font-variant-numeric:tabular-nums;
  margin:var(--s-3) 0 0; padding-top:var(--s-3); border-top:1px solid var(--line);
}
.hub__i .card__more{ margin-top:auto; padding-top:var(--s-4) }

@media (max-width:767px){
  .hub{ gap:var(--s-4) }
  .hub__i{ padding:var(--s-4) }
  .hub__x{ margin-top:var(--s-2) }
  .hub__p{ margin-top:var(--s-2); padding-top:var(--s-2) }
  .hub__i .card__more{ padding-top:var(--s-3) }
}

/* ---------- 31. Мера строки: пересчёт из ch в знаки ----------
   Замер по всему сайту: 56ch у .measure дают 95–104 знака кириллицей
   при потолке 85. Причина та же, что у .lead (раздел 28): ch считается
   по ширине «0», а «0» в Onest — табличная цифра, заметно шире средней
   буквы. Практический коэффициент — примерно 1,75 знака на ch, значит
   потолку 85 соответствуют 48ch.

   Токен --measure не трогаем: он на чтение. Переопределяем сам класс,
   каскадом, в конце файла.
   ------------------------------------------------------------------------ */
.measure{ max-width:48ch }

/* Вторая колонка .verdict на 1440 занимает две трети таблицы и давала
   строки в 122 знака. Ячейка — тот же текст, к ней применяется та же мера. */
.verdict td{ max-width:48ch }

/* Заголовок группы прайса, обёрнутый в ссылку: тач-цель считается по строке
   текста и даёт 24px вместо 44. Тот же случай, что у .card__title a. */
@media (pointer:coarse){
  .h--sm a{ display:inline-flex; align-items:center; min-height:var(--burger) }
}

/* ---------- 32. Числа в прайс-таблицах — табличные, а не карточные ----------
   .num объявлен в разделе 13.8 как «цифра в карточке»: --t-num-sm, 52px,
   чтобы сумма-ориентир стала вторым по величине объектом карточки. В ячейке
   прайс-таблицы тот же класс даёт строку в 110px, и на /price/ это вылилось
   в секции по 1,6–2,1 экрана при потолке 1,4.

   Здесь цена — не ориентир, а строка перечня: семнадцать строк подряд,
   и крупный кегль работает против сканирования, а не за него. Карточную
   цифру (.num--card, .num--row, .stat__num) правило не трогает.
   ------------------------------------------------------------------------ */
.price-table .num{
  /* 🔴 .num задаёт ещё и display:block (нужный ему в карточке). В ячейке
     таблицы это выбивает колонку из сетки: «Единица» и «Цена» вставали
     друг под друга, а строка занимала 110px вместо 40. Возвращаем ячейке
     табличное поведение — на десктопе, где таблица остаётся таблицей. */
  display:table-cell;
  font-family:var(--f-mono); font-weight:400;
  font-size:var(--t-small); line-height:1.45; letter-spacing:0;
}
@media (max-width:767px){
  /* на телефоне таблица развёрнута в блоки — display возвращаем как было */
  .price-table .num{ display:block; font-size:var(--t-body) }
}

/* ---------- 32.1 Развёрнутая строка прайса на 390 ----------
   После разделения группы «Доступ» на СКУД и видеонаблюдение секции вышли
   1,81 и 1,89 экрана при потолке 1,6: в развёрнутом виде каждая запись
   занимала четыре строки — название, метка «Единица», значение, метка «Цена»,
   значение. Единица и цена короткие и встают в один ряд под названием.
   ------------------------------------------------------------------------ */
@media (max-width:767px){
  .price-table tbody tr{
    display:flex; flex-wrap:wrap; align-items:baseline; gap:0 var(--s-4);
  }
  .price-table tbody tr > *:first-child{ flex:0 0 100%; margin-bottom:var(--s-1) }
  .price-table td + td,
  .price-table th[scope="row"] + td{ margin-top:0 }
  .price-table td::before{ display:inline; margin:0 .35em 0 0 }
}

/* ─────────────────────────────────────────────────────────────
   Утилита .u-fill — место под вписывание от руки в шаблоне
   таблички на /kamery-i-zakon/.
   Почему не цепочка «_»: восемь подчёркиваний подряд не переносятся
   и дают горизонтальный скролл на 375. Подчёркнутый inline-block
   тянется по остатку строки и переносится вместе с ней.
   ───────────────────────────────────────────────────────────── */
.u-fill{
  display:inline-block;
  min-width:8ch;
  max-width:100%;
  border-bottom:1px solid currentColor;
  vertical-align:baseline;
}

/* ============================================================================
   ПРАВКИ ПОСЛЕ ЗАМЕРА КОНТРАСТА 27.08.2026
   Замер: qa/_contrast-27.mjs — проба по пикселям под каждым текстом,
   19 страниц, 3 500 элементов. Блок вынесен в конец файла намеренно:
   так его видно целиком и можно снять одним куском, не разбирая середину.
   ============================================================================ */

/* ---------- 1. 🔴 .plate: тёмная подложка без своего цвета текста ----------
   Комментарий к .plate (§9.9) прямо говорит: «подложка --bg-2, контраст
   --ink-i = 18,8:1». Но color компонент не задавал ни разу. В светлой секции
   текст наследовал --ink #0A0A0F и садился на #0C0930 — 1,03:1, не видно
   вообще. Плашка тёмная ВСЕГДА, значит и текст в ней всегда светлый. */
.plate{ color:var(--ink-i) }
.plate .muted,
.plate .lead,
.plate figcaption,
.plate small{ color:var(--soft-i) }
.plate a:not(.btn){ color:var(--acc-hi) }
.plate strong,
.plate b{ color:var(--ink-i) }

/* Факты вместо пустой рамки под фото (главная, блок «Объект веду я»). */
.plate--facts{ display:grid; gap:var(--s-5); align-content:start }
.vfact{ margin:0; padding-top:var(--s-4); border-top:var(--rule-i) }
.vfact:first-child{ padding-top:0; border-top:0 }
.vfact__n{
  font-family:var(--f-disp); font-weight:500;
  font-size:var(--t-num-sm); line-height:var(--lh-num); letter-spacing:var(--tr-num);
  font-variant-numeric:tabular-nums; color:var(--ink-i);
}
.vfact__u{
  font-family:var(--f-body); font-weight:500; font-size:var(--t-lead);
  letter-spacing:0; color:var(--soft-i); margin-left:6px; white-space:nowrap;
}
.vfact__c{
  display:block; margin-top:var(--s-2);
  font-size:var(--t-small); line-height:1.5; color:var(--soft-i); max-width:30ch;
}

/* ---------- 2. 🔴 caption дублирует заголовок секции ----------
   У каждой таблицы стоит и <h2>, и <caption> с тем же текстом — читатель
   видит подпись дважды. caption нужен скринридеру, поэтому не удаляем,
   а убираем с глаз. */
.price-table caption{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
}

/* ---------- 3. Неразрывные пробелы в суммах ----------
   «5 000 ₽» рвалось на две строки. Разряды и знак рубля не переносим. */
.num,.price-table td,.price-table th,.fact__n,.vfact__n,.stat__n{ white-space:nowrap }
.price-table td:first-child,.price-table th:first-child{ white-space:normal }

/* ---------- 4. 🔴 Плотный текст не выживает на ядре градиента ----------
   Замер: на пике #7566FF (правый нижний угол, 88% 94%) даже чистый белый
   даёт 4,12:1, --soft-i — 1,56:1, --acc-hi — 1,00:1. Никаким подбором цвета
   это не лечится: под текстом лежит переменный фон.
   Лечение то же, что уже принято для .plate (§9.9) — сплошная подложка
   --bg-2. На ней --ink-i = 18,8:1, --soft-i = 7,26:1, --acc-hi = 4,65:1,
   и контраст перестаёт зависеть от места в блоке. */
.card--dark{ background-image:none; background-color:var(--bg-2) }

.blk--dark .verdict,
.blk--dark .numlist{
  background:var(--bg-2); border:1px solid var(--line-i);
  border-radius:var(--r-card); padding:var(--s-4);
}
.blk--dark .verdict th,
.blk--dark .verdict td{ border-bottom-color:var(--line-i) }
.blk--dark .verdict tr:last-child th,
.blk--dark .verdict tr:last-child td{ border-bottom:0 }














/* ---------- 5. Компоненты, зашитые под светлый контекст ----------
   Каждый из них задаёт свой цвет жёстко и не имел парного правила
   для .blk--dark, поэтому уходил в невидимость на тёмном блоке. */
.blk--dark .verdict th,
.blk--dark .verdict td{ color:var(--ink-i) }
.blk--dark .verdict thead th,
.blk--dark .verdict td:last-child{ color:var(--soft-i) }
.blk--dark .foot__list a{ color:var(--ink-i) }
.blk--dark .foot__ttl{ color:var(--soft-i) }
.blk--dark .card__more{ color:var(--acc-hi) }
.blk--dark .numlist__x{ color:var(--soft-i) }
.blk--dark .numlist__t{ color:var(--ink-i) }
.blk--dark dt{ color:var(--ink-i) }
.blk--dark dd{ color:var(--soft-i) }

/* Статусные цвета в тёмном контексте: --ok/--warn/--err рассчитаны на белое
   полотно и на тёмном не читаются (--ok даёт 3,85:1 даже на плашке). */
:root{ --ok-i:#7FE3C0; --warn-i:#F2C48C; --err-i:#FF9F94 }
.blk--dark .verdict__m--yes{ color:var(--ok-i) }
.blk--dark .verdict__m--may{ color:var(--warn-i) }
.blk--dark .verdict__m--no{ color:var(--err-i) }

/* ---------- 6. Белая карточка внутри тёмного блока ----------
   Обратный случай: .card залит --surf, и правила выше не должны его задеть.
   Возвращаем светлый контекст внутрь. */
.blk--dark .card:not(.card--dark){ color:var(--ink) }
.blk--dark .card:not(.card--dark) .card__text{ color:var(--soft) }
.blk--dark .card:not(.card--dark) .card__more{ color:var(--acc) }
.blk--dark .card:not(.card--dark) .card__title{ color:var(--ink) }

/* ---------- 7. .btn--line попал на светлый блок ----------
   Вариант нарисован под тёмный фон: фон прозрачный, текст --ink-i.
   На светлом полотне это белым по белому. Базовое правило не трогаем —
   возвращаем светлый контекст там, где блок не тёмный. */
.blk:not(.blk--dark) .btn--line{
  border-color:var(--line); color:var(--ink); background:transparent;
}
.blk:not(.blk--dark) .btn--line .btn__ic{ background:var(--btn); color:var(--ink-i) }

/* ---------- 8. Ссылки прямо на градиенте ----------
   --acc-hi на ядре даёт 1,0:1. Внутри сплошной плашки он проходит (4,65:1),
   а голая ссылка на градиенте — нет. Делаем такие белыми с подчёркиванием:
   цвет там не работает, работает форма. */
.blk--dark .card__more,
.blk--dark > .sec .link,
.blk--dark .sec > .link{
  color:var(--ink-i); text-decoration:underline; text-underline-offset:3px;
  text-decoration-thickness:1px;
}
.blk--dark .card--dark .card__more,
.blk--dark .plate .card__more,
.blk--dark .plate .link{ color:var(--acc-hi); text-decoration:none }

/* ---------- 9. Статусный --warn под норму AA ----------
   #B0682C на белом — 4,32:1. #9C5A22 даёт 5,39:1 на белом и 4,76:1 на полотне. */
:root{ --warn:#9C5A22 }

























































































/* Вариант «цена ошибки» — метка и хайрлайн предупреждающим цветом. */





/* Вариант «так делаю я» — спокойный, зелёный. */










/* Подзаголовок внутри карточки-тезиса: кегль подписи, не заголовка секции. */










/* Карточка-тезис на тёмном блоке садится на градиент — та же болезнь, что
   у .verdict и .numlist. Кладём на сплошную подложку. */







/* ---------- 12. Светлая карточка внутри тёмного блока ----------
   Та же болезнь, что у .card: подложка светлая (--surf-2), а текст
   наследует --ink-i от .blk--dark. Белым по светло-серому — 1,1:1. */
.blk--dark .hub__i{ color:var(--ink) }
.blk--dark .hub__i .hub__x{ color:var(--soft) }
.blk--dark .hub__i .hub__p{ color:var(--ink); border-top-color:var(--line) }
.blk--dark .hub__i .card__more{ color:var(--acc); text-decoration:none }
.blk--dark .hub__i .hub__t{ color:var(--ink) }

/* --ok на светлом полотне давал 4,39:1 при норме 4,5 — метка «что делаю я»
   в карточках-тезисах. #276A54 даёт 5,25:1. */
:root{ --ok:#276A54 }

/* ============================================================================
   33. СПЕЦИФИКАЦИЯ: ЧИСЛОВАЯ КОЛОНКА ОСТАЁТСЯ КОЛОНКОЙ
   Найдено замером qa/_tables-27.mjs 27.08.2026 (промпт 14, хвост D-2).

   Раздел 32 вернул табличное поведение ячейке `.price-table .num`, но
   `.spec` тем же дефектом остался задет: `.num` из раздела 13.8 задаёт
   `display:block`, а `.spec td.num` (раздел 31) ставит `width:1%` и
   `white-space:nowrap` в расчёте на табличную ячейку, где 1% означает
   «по содержимому». В блоке 1% берётся от контейнера — колонка сжимается
   в 24px, а число не переносится и выходит наружу.

   Замер на 1440: /partneram/ — «от 129 000 ₽» в ячейке шириной 24px,
   текст 92px за её правым краем; колонки «Сумма договора» и «Ваши 10%»
   встали друг под друга вместо двух колонок. /osveshchenie/ — колонка «₽»
   шириной 24px, числа пересекают границу колонки «Комментарий».
   Затронуты девять страниц с `table.spec`.

   На ≤767 таблица осознанно развёрнута в блоки (раздел 31, медиазапрос),
   и там `display:block` — правильное поведение. Поэтому правило только
   на десктопе, а мобильную раскладку не трогаем.
   ------------------------------------------------------------------------ */
@media (min-width:768px){
  .spec th.num,
  .spec td.num{ display:table-cell }
}

/* ============================================================================
   34. ПРАЙС-ТАБЛИЦА НА ТЕЛЕФОНЕ — БЛОК ЦЕЛИКОМ, А НЕ ТОЛЬКО СТРОКИ
   Найдено замером qa/_tables-27.mjs 27.08.2026 (промпт 14, хвост D-2).

   Раздел 26 разворачивает `.price-table` на ≤767 в записи: `tr` и `td`
   получают `display:block`, раздел 32.1 переводит `tbody tr` в `flex`.
   Но сама таблица осталась `display:table`, а `tbody` — `table-row-group`.
   Строка, переставшая быть `table-row` внутри группы строк, заворачивается
   браузером в анонимную табличную ячейку, и ширину таблицы начинает
   задавать её max-content, а не `width:100%`.

   Замер на 375, /videonablyudenie/: `.price-table--wide` вышла 516px
   в контейнере 291px. `.price-wrap` на телефоне переведён в
   `overflow-x:visible` (раздел 26), горизонтального скролла у документа
   нет — значит, правые 225px таблицы просто обрезаны предком: «Положение
   о видеонаблюдении с планом-схем…» и весь столбец обоснований читателю
   на телефоне недоступны.

   `.spec` этой болезнью не болеет: её медиазапрос (раздел 31) переводит
   в блок всю цепочку — таблицу, tbody, tr, th, td. Делаем так же.
   thead на телефоне уже `display:none` (раздел 26), подписи колонок
   продублированы в `td::before` из data-l, поэтому семантика не теряется.
   ------------------------------------------------------------------------ */
@media (max-width:767px){
  .price-table,
  .price-table tbody{ display:block; width:100% }
}

/* ============================================================================
   35. NOWRAP В ПРАЙС-ТАБЛИЦЕ ЗАЩИЩАЕТ СУММУ, А НЕ ПРЕДЛОЖЕНИЕ
   Найдено замером qa/_tables-27.mjs 27.08.2026 (промпт 14, хвост D-2),
   продолжение раздела 34.

   Правило «Неразрывные пробелы в суммах» (раздел 3 блока правок 27.08)
   ставит `white-space:nowrap` на все ячейки `.price-table`, кроме первой.
   Задумано было про «5 000 ₽», которое рвалось на две строки. Но
   не-первых ячеек в прайс-таблицах 278, и 90 из них — не суммы, а
   предложения: «Перечень зон наблюдения и прямое указание, где
   наблюдение не ведётся», «В существующие светильники, вместо обычных
   ламп». Похожих на сумму ячеек без класса `.num` — три.

   На десктопе это переживалось: колонка расширялась внутри таблицы.
   На ≤767 строка развёрнута во flex-запись, ячейка становится flex-item,
   а flex-item не сжимается ниже min-content. При nowrap min-content равен
   всей строке: замер на 375 дал ячейку 467px в контейнере 291px, правые
   176px обрезаны предком — читателю телефона текст недоступен.

   Поэтому на телефоне nowrap оставляем ровно там, где он и был нужен —
   на `.num`. Десктоп не трогаем: там правило работает как задумано.
   ------------------------------------------------------------------------ */
@media (max-width:767px){
  .price-table td,
  .price-table th{ white-space:normal }
  .price-table td.num,
  .price-table th.num,
  .price-table .num{ white-space:nowrap }
}

/* ============================================================================
   36. ПЛАШКА-ВЕРДИКТ НЕ ОБРЕЗАЕТСЯ, ДАЖЕ ЕСЛИ В НЕЙ НЕ ЧИСЛО
   Найдено замером qa/_tables-27.mjs 27.08.2026 (промпт 14, хвост D-2).

   `.verdict__m` — пилюля с коротким вердиктом: «2», «Можно», «Нельзя».
   `white-space:nowrap` там уместен: «2–5 дней» рвать по дефису незачем.
   Но в одной ячейке `/zashchita-ot-protechek/` вместо числа стоит
   незакрытый плейсхолдер «[уточнить: по числу вводов и коллекторов]» —
   41 знак. На 375 пилюля выходит за секцию на 61px и обрезается
   `section.blk`: горизонтального скролла нет, текст просто пропадает.

   Содержимое здесь править нечем — число знает владелец, и оно в списке
   `node qa/todo.mjs`. Поэтому лечим деградацию: длинная плашка переносится,
   а не исчезает. Все остальные вердикты на сайте — до 21 знака, им перенос
   не грозит; проверено перебором всех `.verdict__m` в разметке.
   ------------------------------------------------------------------------ */
@media (max-width:767px){
  .verdict__m{ white-space:normal; overflow-wrap:anywhere }
}

/* ============================================================================
   КЛАСС G — ИНТЕРАКТИВНЫЕ СОСТОЯНИЯ, ЕДИНАЯ СИСТЕМА (ТЗ 28)
   ----------------------------------------------------------------------------
   Секция добавлена целиком и снимается одним куском — конвенция проекта.
   Ничего выше не переписано: правила ниже только дополняют каскад.

   Три состояния, одинаковые на всём сайте:
     наведение — акцентная граница/цвет плюс подъём на --lift;
     нажатие   — сжатие до --press за --d-press, быстрее наведения;
     фокус     — одно кольцо --ring-w с отступом --ring-off, видно на белом
                 и на тёмном (#7566FF даёт 4,12:1 на #FFFFFF и 4,83:1 на #080520,
                 порог для нетекстового контраста — 3:1).
   ============================================================================ */

/* ---------- G1. Ховер только там, где есть настоящая мышь ----------
   Три блока выше объявлены под голым @media (hover:hover), без pointer:fine.
   На гибридах и на части Android это истина, и ховер залипает после тапа.
   Гасим ровно эти правила там, где указателя нет или он грубый. */
@media (hover:none), (pointer:coarse){
  .notch__sum:hover,
  .notch__dlink:hover{ background:transparent; color:inherit }
  .hub__i:hover{ border-color:var(--line); box-shadow:none }
  .marquee:hover .marquee__track{ animation-play-state:running }
}

/* ---------- G2. Элементы, которым ховер меняет цвет без перехода ----------
   .callout__n и .burger меняли состояние скачком, остальной сайт — плавно. */
.callout__n{ transition:color var(--d-fast) var(--e-out) }
.burger{ transition:background var(--d-fast) var(--e-out) }

/* ---------- G3. Единый подъём при наведении ----------
   Карточки уже получали акцентную границу и тень, но стояли на месте.
   Подъём делает наведение читаемым без цвета — это же и есть подсказка
   «на меня можно нажать». Только мышь: на тач-устройствах подъёма нет. */
@media (hover:hover) and (pointer:fine){
  .card--link:not([data-card="soon"]),
  .hub__i{ transition:border-color var(--d-fast) var(--e-out),
                     box-shadow   var(--d-fast) var(--e-out),
                     transform    var(--d-fast) var(--e-out) }
  .card--link:not([data-card="soon"]):hover,
  .hub__i:hover{ transform:translateY(calc(-1 * var(--lift))) }

  /* Кнопка поднимается вдвое меньше карточки: она мельче, и полный подъём
     на ней читается как прыжок. */
  .btn{ transition:background var(--d-fast) var(--e-out),
                   color       var(--d-fast) var(--e-out),
                   border-color var(--d-fast) var(--e-out),
                   box-shadow  var(--d-fast) var(--e-out),
                   transform   var(--d-fast) var(--e-out) }
  .btn:hover{ transform:translateY(calc(-0.5 * var(--lift))) }
}

/* ---------- G4. Нажатие ----------
   На сайте не было ни одного :active. Палец и мышь не получали ответа
   в момент нажатия, и на медленной сети это читается как «не сработало». */
.btn,
.card--link:not([data-card="soon"]),
.hub__i,
.burger,
.notch__link,
.notch__dlink{ transition-property:background,color,border-color,box-shadow,transform;
               transition-duration:var(--d-fast);
               transition-timing-function:var(--e-out) }

.btn:active,
.burger:active,
.notch__link:active,
.notch__dlink:active{ transform:scale(var(--press));
                      transition-duration:var(--d-press) }

/* Карточка — ссылка целиком, её сжимаем слабее: большая площадь,
   заметное сжатие выглядит как сбой раскладки. */
.card--link:not([data-card="soon"]):active,
.hub__i:active{ transform:translateY(0) scale(.997);
                transition-duration:var(--d-press) }

/* ---------- G5. Фокус с клавиатуры ----------
   Глобальное кольцо объявлено выше и работает. Здесь закрываем места,
   где оно терялось: элементы с overflow:hidden обрезали outline,
   а у строк оглавления и ссылок в подвале кольцо липло к тексту. */
:focus-visible{ outline-width:var(--ring-w); outline-offset:var(--ring-off) }

.card:focus-visible,
.hub__i:focus-visible,
.faq__item:focus-visible,
.notch__dlink:focus-visible,
.toc__l a:focus-visible,
.doc-toc a:focus-visible,
.foot__list a:focus-visible,
.foot__legal a:focus-visible,
.crumbs a:focus-visible{ outline:var(--ring-w) solid var(--acc-hi);
                         outline-offset:var(--ring-off);
                         border-radius:var(--s-1) }

/* Внутри тёмного блока кольцо остаётся тем же --acc-hi: оно светлее фона.
   На белом фоне --acc-hi тоже проходит порог, поэтому вариантов не два, а один. */

/* ---------- G6. Меньше движения — меньше движения ----------
   Подъём и сжатие отключаются вместе с остальной моторикой. */
@media (prefers-reduced-motion: reduce){
  :root{ --d-press:0s; --lift:0px; --press:1 }
  .card--link:not([data-card="soon"]):hover,
  .hub__i:hover,
  .btn:hover,
  .btn:active,
  .card--link:not([data-card="soon"]):active,
  .hub__i:active,
  .burger:active,
  .notch__link:active,
  .notch__dlink:active{ transform:none }
}

/* ---------- G7 / D1. Секция сужается по содержанию ----------
   Класс D1 из аудита: текст ограничен мерой строки (48ch), а секция занимает
   всю ширину блока — справа пусто. Правило ТЗ: закрывать содержанием, если есть
   чем, иначе сужать секцию. Здесь второй случай — ставить туда нечего.
   width:fit-content, а не жёсткий max-width: секция обнимает самого широкого
   ребёнка и не может стать уже него, поэтому таблица или карточка внутри
   не даст горизонтальной прокрутки. Крошки под правило не попадают. */
.sec--narrow{ width:fit-content; max-width:100%; margin-inline:auto }

/* Три секции, где дети — полноширинные блоки (.plate, .sec__head, .u-flow),
   поэтому fit-content не сжимается: их max-content равен ширине блока.
   Внутри только текст по мере строки, таблиц нет — жёсткий предел безопасен. */
/* ТЗ 41: предел снят с самого блока — он стоял слева и оставлял справа
   пустую треть полосы. Длину строки внутри держит плашка сноски. */
.sec--cap{ max-width:none }

/* caption у .verdict прячем с глаз так же, как у .price-table (см. п. 2 выше):
   видимую подпись несёт заголовок над таблицей, caption остаётся скринридеру. */
.verdict caption{
  position:absolute; width:1px; height:1px; padding:0; margin:-1px;
  overflow:hidden; clip-path:inset(50%); white-space:nowrap; border:0;
}

/* ---------- ТЗ 27, правка 1. Ширина белой пластины навбара ----------
   Владелец разметил на кадре ≈ 56 % ширины тёмного блока. После правки 2
   (три пункта верхнего уровня вместо одного «Направления») пластина
   выросла до 62 % на 1440 и до 81 % на 1024, а зазоры до логотипа и до
   переключателя городов упали до 16 px при норме var(--s-4).
   Добираем не жёсткой шириной — пластина обязана остаться контентной, —
   а горизонтальным отступом пунктов: 12 → 8 px даёт ровно недостающие
   7 × 8 = 56 px. Вертикальный отступ не трогаем: он держит тач-цель. */
@media (min-width:1025px){
  .notch__link,
  .notch__sum{ padding-inline:var(--s-2) }
}

/* ============================================================================
   ТЗ 27 — вёрстка правок 5, 7, 8, 12. Секция добавлена целиком, снимается куском.
   ============================================================================ */

/* ---------- Правка 5. Тёмная плашка «объект»: текст слева, сегменты справа ---------- */
.objgrid{ display:grid; gap:var(--s-6) }
@media (min-width:769px){
  .objgrid{ grid-template-columns:minmax(0,1fr) minmax(0,1fr); gap:var(--s-7); align-items:start }
}


/* 🔴 ТЗ 36, найдено визуальной приёмкой. Плитки были свёрстаны под тёмную
   подложку: --ink-i, --line-i, --soft-i. Пока они лежали в .plate внутри
   тёмного пояса, это было верно. ТЗ 36 сняло плашку ради высоты полосы,
   плитки встали прямо на светлый пояс — и заголовок оказался белым по белому.
   Замер по пикселю: 1,0:1. Цвета берутся по поясу, как у остальных
   компонентов канона: базовые — светлый пояс, инверсные — тёмный и плашка. */




















/* ---------- Правка 7. Разброс цен поставщика + чипы совместимости ---------- */
.spread{ display:grid; gap:var(--s-6) }
@media (min-width:769px){
  .spread{ grid-template-columns:minmax(0,1fr) minmax(0,auto); gap:var(--s-7); align-items:center }
}
.spread__fig{ border:1px solid var(--line); border-radius:var(--r-card); padding:var(--s-5) }
.spread__row{ display:flex; flex-wrap:wrap; align-items:baseline; gap:var(--s-4); margin:0 }
.spread__n{ font-size:var(--t-num-sm); line-height:var(--lh-num); letter-spacing:var(--tr-num); color:var(--ink) }
.spread__n + .spread__n{ color:var(--acc) }
.spread__cap{ font-size:var(--t-small); color:var(--soft); margin:var(--s-3) 0 0; max-width:34ch }

.brands__k{ font-size:var(--t-micro); letter-spacing:.12em; text-transform:uppercase;
            color:var(--soft); margin:var(--s-6) 0 var(--s-3) }
/* Было justify-content:space-between на семи элементах во всю ширину —
   промежутки за двести пикселей, «Яндекс» прилипал к краю. Собираем в чипы. */
.brands{ justify-content:flex-start; gap:var(--s-2); flex-wrap:wrap }
.brands > li{
  border:1px solid var(--line); border-radius:var(--r-pill);
  padding:var(--s-2) var(--s-4); font-size:var(--t-small); color:var(--soft);
}

/* ---------- Правка 8. Кнопки под списком документов — сетка равных ячеек ---------- */
.btn-grid{ display:grid; gap:var(--s-3); grid-template-columns:1fr }
@media (min-width:768px){ .btn-grid{ grid-template-columns:repeat(2,1fr) } }
@media (min-width:1024px){ .btn-grid{ grid-template-columns:repeat(3,1fr) } }
.btn-grid .btn{ width:100%; justify-content:space-between }

/* ---------- Правка 12. Полоса состава сметы ---------- */
.compo{ display:flex; width:100%; border-radius:var(--r-pill); overflow:hidden; margin-top:var(--s-5) }
.compo__p{ height:var(--s-5) }
.compo__p--eq{ background:var(--acc) }
.compo__p--mo{ background:var(--acc-hi) }
.compo__p--pn{ background:var(--line) }
.compo-key{ list-style:none; margin:var(--s-4) 0 0; padding:0;
            display:flex; flex-wrap:wrap; gap:var(--s-3) var(--s-6); font-size:var(--t-small) }
.compo-key li{ display:flex; align-items:center; gap:var(--s-2); color:var(--soft) }
.compo-key i{ width:10px; height:10px; border-radius:2px; flex:0 0 auto }
/* Две цены подряд читались как одно число — разделяем словом, а не отступом. */
.spread__vs{ font-size:var(--t-small); color:var(--soft); text-transform:lowercase }

/* ============================================================================
   ПРАВКИ ПО ЗАМЕЧАНИЯМ ВЛАДЕЛЬЦА 30.08.2026: «куча пустот и кривизны»
   ============================================================================ */

/* ---------- 1. .numlist: текст уезжал от пункта к пункту ----------
   grid-template-columns:auto 1fr задаёт ширину колонки цифры ПО САМОЙ ЦИФРЕ,
   а «1» уже «3» и «7». В двухколоночном блоке это видно сразу: заголовки
   соседних пунктов стоят на разных вертикалях. Фиксируем колонку. */
.numlist > li{ grid-template-columns:1.45em 1fr }
@media (max-width:767px){ .numlist > li{ grid-template-columns:1.3em 1fr } }

/* ---------- 2. .numlist: дыра в последнем ряду ----------
   Семь пунктов в сетке 2×4 оставляли пустую ячейку справа внизу — на главной
   это самая заметная пустота блока. Многоколоночный поток балансирует высоту
   и дыры не оставляет; порядок чтения сверху вниз, потом вправо. */
/* 🔴 ТЗ 31, правка 5. columns:2 СНЯТ. Многоколоночный поток балансирует
   по ВЫСОТЕ, а не по числу пунктов: семь пунктов ложились 4 + 3 и колонки
   расходились на 147 px (главная, 1440). Хуже того, порядок чтения ломался —
   глаз идёт слева направо и читает 1 → 3, — а блок внутри пункта («Скачать
   табличку A5» в пункте 3 на /videonablyudenie/) уезжал в середину чужой
   колонки. Ширина, которая освободилась, закрывается сужением пояса
   (правка 4), а не второй колонкой.
   Разделитель вернулся НАВЕРХ пункта и живёт в промежутке (правка 3):
   у первого пункта линии нет, у последнего нет тоже. */
/* Переопределений на 1024+ у пункта больше нет: базовое правило раздела 24
   (padding-top + border-top, у первого пункта ни того, ни другого) уже
   даёт ровно одну линию в промежутке и чистые края. */

/* ---------- 3. mono в наборном тексте: «3  %» вместо «3 %» ----------
   Моноширинный пробел заметно шире наборного, и число с единицей в строке
   разъезжается: «5  000  ₽». Поджимаем межсловный интервал только внутри
   .mono — цифры и табличное выравнивание не трогаются. */
.mono{ word-spacing:-.18em }
/* 🔴 Компенсация рассчитана на ШИРОКИЙ пробел JetBrains Mono. Там, где класс
   .mono стоит, а шрифт перекрыт дисплейным (.stat__num — Geologica), пробел
   уже узкий, и −0,18em съедает его целиком: «2 года» печаталось как «2года».
   Найдено проходом по всем .mono, у которых вычисленный шрифт не моноширинный. */
.stat__num.mono,
.fact__n.mono,
.numlist__n.mono{ word-spacing:normal }

/* Новые элементы схемы первого экрана: световое пятно на полу и «штроба, которой нет» */
.scheme__pool{ fill:url(#pool) }
.scheme__chase{ fill:none; stroke:rgba(255,255,255,.28); stroke-width:1.5; stroke-dasharray:4 4 }

/* Схема стены на /kvartira/: перекрытие со штриховкой и канал «штробы нет» */
.wl__slab rect{ fill:rgba(255,255,255,.05); stroke:var(--line-fig); stroke-width:1 }
.wl__hatch path{ stroke:rgba(255,255,255,.14); stroke-width:1; fill:none }

/* Подпись «0 метров штробы» стоит на самом светлом участке градиента героя
   и на нём --soft-i даёт 3,35:1 при норме 4,5 (замер по пикселю).
   Для этой подписи берём полный --ink-i: она короткая и несёт цифру оффера. */
.scheme__lbl--strong{ fill:var(--ink-i) }

/* ---------- Одиночная карточка во всю длину при коротком тексте ----------
   Замечание владельца: «куча пустот там, где одна карточка на всю длину».
   Замер на 1440: .plate шириной 1237 px, а содержимым занято 11–39 % —
   текст внутри ограничен мерой строки, остальное пустая коробка.
   Плита, внутри которой только текст, обнимает содержимое. Плиты с таблицей,
   списком определений или сеткой не трогаются: там ширина работает. */
/* ТЗ 41: плашка больше не сжимается по содержимому. `width:fit-content`
   оставлял справа пустую четверть полосы — владелец читает это как «криво».
   Плашка идёт во всю ширину, длину строки держит поле справа. */
.plate{ width:auto; max-width:100% }
@media (min-width:900px){
  .plate > p{ max-width:min(100%, var(--measure)) }
}

/* ---------- Крупные числа рвутся между суммой и знаком рубля ----------
   «5 000 ₽» вставало как «5 000» / «₽» на двух строках: в .stat__num кегль
   крупный, а колонка узкая. Запрещаем перенос внутри самого числа. */
.stat__num,
.fact__n,
.spread__n,
.numlist__n{ white-space:nowrap }

/* ---------- Ряд цифр занимал левую половину плиты ----------
   .stats — flex с центрированием: три показателя жались в середину, справа
   и слева оставалась пустота. Раскладываем равными колонками во всю ширину. */
.stats{ display:grid; gap:var(--s-6) var(--s-7);
        grid-template-columns:repeat(auto-fit,minmax(min(100%,200px),1fr)) }
@media (min-width:768px){
  /* Поровну между показателями. grid-auto-columns:1fr не разворачивался —
     задаём число колонок явно по количеству детей: auto-fit оставлял лишнюю
     пустую колонку, и ряд цифр жался к левому краю плиты. */
  .stats:has(> :nth-child(2):last-child){ grid-template-columns:repeat(2,1fr) }
  .stats:has(> :nth-child(3):last-child){ grid-template-columns:repeat(3,1fr) }
  .stats:has(> :nth-child(4):last-child){ grid-template-columns:repeat(4,1fr) }
  .stats:has(> :nth-child(5):last-child){ grid-template-columns:repeat(5,1fr) }
}
.stats--start{ justify-content:stretch }
.stat__cap{ max-width:22ch }

/* ---------- Пустой хвост плиты под карточкой контактов ----------
   `.hero .stats` резервирует справа полосу под карточку контактов, которая
   наезжает на низ героя (см. 13.4a). Но резерв стоял ВНУТРИ плиты: плита
   тянулась во всю ширину, а её правая треть оставалась пустой — ровно та
   «карточка на всю длину», о которой сказал владелец.
   Резерв переносим на саму плиту: она кончается там, где начинается карточка. */
@media (min-width:900px){
  .hero .plate:has(.stats){ margin-right:calc(min(300px,34%) + var(--s-6)) }
  .hero .plate:has(.stats) .stats{ padding-right:0 }
}

/* Колоночная сетка с одним ребёнком растягивалась во всю ширину, а текст внутри
   ограничен мерой строки — справа пустота. Одна колонка обнимает содержимое. */


/* ============================================================================
   ТЗ 27 — правки 1, 13, 17. Блок добавлен целиком, снимается куском.
   ============================================================================ */

/* ---------- Правка 1. Ширина белой пластины шапки ----------
   Владелец разметил ≈56 % ширины тёмного блока по кадру 2000 px
   (452→1500 при блоке 75→1925). Основной прирост дала правка 2: три пункта
   верхнего уровня вместо одного «Направления» подняли пластину с 34 % до 50 %
   на 1920 и до 58 % на 1440. Добираем остаток ТОЛЬКО на широком экране:
   на 1140–1500 отношение уже 58–65 %, там добор увёл бы пластину на логотип.
   Ширина не задаётся в пикселях — пластина остаётся контентной, растёт только
   поле и зазор, поэтому вогнутые стыки ::before/::after остаются приклеенными. */
@media (min-width:1500px){
  .notch{ padding-inline:clamp(var(--s-2), calc((100vw - 1500px) * .12 + 8px), 72px) }
  .notch__list{ gap:clamp(var(--s-1), calc((100vw - 1500px) / 140 + 4px), var(--s-3)) }
}

/* ---------- Правка 13. Последняя плитка сегментов тянется на весь ряд ----------
   При auto-fit число колонок плавало (3 на широком, 2 около 1024), и правило
   растяжения срабатывало не там. Сначала фиксируем колонки по брейкпоинтам,
   потом растягиваем только там, где колонок ровно три. Селектор
   :last-child:nth-child(3n+1) самокорректируется: при пяти плитках (волна 2)
   ряд 3+2, сироты нет, растяжение не срабатывает.
   🔴 Брейкпоинт 1240, а не 1200: плитки живут в ПРАВОЙ половине .objgrid,
   реальная ширина под них вдвое меньше вьюпорта — точка подобрана замером
   на отрисованной странице (три колонки от 1240, ниже — две). */




@media (min-width:1240px){







  /* Растянутая плитка не должна стать новой пустой половиной: две короткие
     строки в столбик прижались бы к левому краю. Раскладываем в строку. */







}

/* ---------- Правка 17, баг 1. Двузначный номер в .numlist ----------
   Было grid-template-columns:1.45em 1fr — колонка мерилась от кегля li,
   а номер набран --t-num-sm и в разы крупнее. Ширина колонки и ширина
   содержимого не были связаны вообще, и «10» вылезал в текстовую колонку.
   Ширину задаём САМОМУ номеру: 2ch считается от его собственного шрифта. */
.numlist > li{ grid-template-columns:auto 1fr }
/* 🔴 ТЗ 33, класс D. minmax(0,1fr) и min-width:0 обязательны: у элемента
   грида автоминимум равен min-content, и на 375 пункт раздувался шире
   своего списка на 21 px, вылезая за пояс на 5 px (замер, /videonablyudenie/
   и /biznes/obshchepit/ — по шесть пунктов на страницу). */
@media (max-width:767px){
  .numlist > li{ grid-template-columns:auto minmax(0,1fr); min-width:0 }
  .numlist > li > *{ min-width:0 }
}
.numlist__n{
  min-width:2ch;                      /* ровно две цифры от своего кегля */
  text-align:right;
  font-variant-numeric:tabular-nums;  /* «1» и «10» на одной правой вертикали */
}

/* ---------- Правка 17, баг 2. Кнопка без иконки ----------
   Поля .btn асимметричны намеренно: справа маленькое, потому что там стоит
   кружок .btn__ic 26×26 и сам создаёт поле. Без иконки текст прижат к краю.
   Одна строка закрывает и текущие кнопки, и все будущие (правка 8 их добавит). */
.btn:not(:has(.btn__ic)){ padding-inline:var(--s-5) }

/* Правка 1, приёмка «воздух не меньше var(--s-4)»: на 768 между логотипом,
   пластиной и переключателем города оставалось по 8 px. Поле пластины на этой
   ветке уже ничего не держит — снимаем его, пластина ужимается на 16 px,
   воздух становится 16. Вогнутые стыки позиционируются от края пластины
   и остаются приклеенными. */
@media (max-width:1023px){
  .notch{ padding-inline:0 }
}

/* Правка 1 + правка 2, приёмка на 1024. Три пункта верхнего уровня вместо
   одного «Направления» шире прежнего меню, и на 1024 строка перестала
   помещаться: .topbar внутренне переполнялся на 51 px, переключатель города
   срезался правым краем. Компактная ветка меню (микро-кегль, поля пунктов
   в var(--s-1)) до сих пор включалась только с 1023 — то есть ровно на один
   пиксель ниже проверяемой ширины. Поднимаем её границу до 1139.
   Повторяем ТОЛЬКО правила меню: вертикальный ритм и .cols остаются на ≤1023. */
@media (min-width:1024px) and (max-width:1139px){
  .notch__link{ padding-inline:var(--s-1); font-size:var(--t-micro);
                font-family:var(--f-body); letter-spacing:0; text-transform:none }
  .notch__sum{ padding-inline:var(--s-1); font-size:var(--t-micro);
               font-family:var(--f-body); letter-spacing:0; text-transform:none }
  .city{ padding-inline:var(--s-2); gap:var(--s-1) }
}

/* ============================================================================
   ТЗ 27 — правка 16. Секция «Работаю на массовом оборудовании»: три яруса.
   ============================================================================ */

/* Ярус 1. Заголовок ложился в три коротких строки в половину ширины —
   шапка секции наполовину пустая. Ограничение 26ch снимаем ТОЛЬКО здесь,
   глобальное правило (main .sec__head h2) не трогаем: оно живёт на 22 страницах. */
.sec__head--wide h2{ max-width:min(100%, 40ch) }

/* Ярус 2. Было 1fr auto + align-items:center: правая колонка мерилась
   по содержимому и висела в середине короткого абзаца, под ней — полэкрана
   пустоты. Обе колонки одной высоты, правая получает свою долю ширины. */
@media (min-width:769px){
  .spread{ grid-template-columns:minmax(0,1fr) minmax(0,.9fr);
           grid-template-areas:"txt fig" "note fig";
           grid-template-rows:auto 1fr; align-items:start; gap:var(--s-5) var(--s-7) }
  /* 🔴 Область именуется ТОЛЬКО внутри .spread. Без прямого потомка
     grid-area:fig утекал на .spread__fig в других сетках (.argrow),
     где такой области нет, — элемент уезжал в неявный ряд и вторая
     колонка не собиралась. */
  .spread > .spread__txt{  grid-area:txt }
  .spread > .spread__note{ grid-area:note }
  .spread > .spread__fig{  grid-area:fig }
}

/* Плашка сравнения — единственное место секции, которое обязано тянуть взгляд.
   Рамка на полотне этого не делает: сплошная подложка вместо контура.
   На сплошной подложке замер контраста честный, на градиенте — врёт. */
.spread__fig{
  margin:0; border:0; background:var(--bg-2); color:var(--ink-i);
  border-radius:var(--r-card); padding:var(--s-5);
  display:flex; flex-direction:column; gap:var(--s-4); justify-content:center;
}
.spread__figk{ font-size:var(--t-small); color:var(--soft-i); max-width:30ch }
.bars{ display:flex; flex-direction:column; gap:var(--s-3) }
/* Полоса и подпись — две колонки: подпись в своей, поэтому длинная полоса
   ужимается вместе с колонкой и не уезжает в горизонтальный скролл на 375. */
.bar{ display:grid; grid-template-columns:minmax(0,1fr) auto;
      align-items:center; gap:var(--s-3) }
.bar__t{ display:block; height:14px; border-radius:var(--r-pill);
         background:var(--line-i); overflow:hidden }
.bar__f{ display:block; height:100%; border-radius:var(--r-pill);
         background:var(--soft-i) }
.bar__f--hi{ background:var(--acc-hi) }
.bar__v{ font-size:var(--t-h4); font-weight:500; color:var(--ink-i);
         font-variant-numeric:tabular-nums; white-space:nowrap }
.spread__out{ font-size:var(--t-small); color:var(--ink-i); margin:0 }

/* Ярус 3. Чипы полосой во всю ширину под двумя колонками — основание секции. */
.brands__k{ margin-top:var(--s-7) }

@media (max-width:768px){
  /* На узком ярус 2 складывается в одну колонку: абзац, плашка, оговорка
     идут в порядке разметки; чипы остаются последними. */
  .spread__fig{ padding:var(--s-4) }
  .bar__v{ font-size:var(--t-lead) }
}

/* ============================================================================
   ТЗ 27 — правка 20. Сквозной блок вместо формы: выжимка гарантии,
   часы приёма, кнопка в Телеграм. Один и тот же на 19 страницах.
   ============================================================================ */
.tgb{ display:flex; flex-direction:column; gap:var(--s-4) }
.tgb__k{ font-size:var(--t-micro); letter-spacing:.12em; text-transform:uppercase;
         color:var(--soft-i); margin:0 }
/* Определения, а не карточки: срок — величина при термине (ТЗ 29, приём 5).
   Две колонки на широком, одна на узком — длинная правая часть не рвётся. */
.tgb__l{ margin:0; display:grid; gap:var(--s-2) var(--s-4);
         grid-template-columns:1fr; font-size:var(--t-small) }
@media (min-width:480px){
  .tgb__l{ grid-template-columns:minmax(0,auto) minmax(0,1fr);
           gap:var(--s-3) var(--s-5); align-items:baseline }
}
.tgb__l dt{ color:var(--soft-i) }
.tgb__l dd{ margin:0; color:var(--ink-i) }
.tgb__g{ margin:0; font-size:var(--t-small); color:var(--ink-i);
         padding-top:var(--s-4); border-top:var(--rule-i) }
.tgb__h{ margin:0; font-size:var(--t-small); color:var(--soft-i) }
.tgb__b{ margin-top:var(--s-2) }
.tgb__c{ margin:0; font-size:var(--t-micro); color:var(--soft-i) }
.tgb__c a{ color:var(--acc-hi) }

/* 🔴 ТЗ 31, правка 8, вторая половина. Остальные пары «текст + рисунок»
   в .split лечатся не переносом содержимого (переносить нечего: рисунок
   неделим), а тем, что обе колонки занимают всю высоту ряда, а содержимое
   внутри выравнивается по оптическому центру. Пустота перестаёт быть дырой
   у одного края и становится полем с двух сторон.
   Замер до: /nastroyka/ 271 / 459 (41 %), /price/ 601 / 359 на 768 (40 %),
   /o-sebe/ 543 / 395 на 768 (27 %), главная 517 / 701 на 768 (26 %),
   .objgrid главной 235 / 175 на 1200 (26 %). */
@media (min-width:768px){
  .split:not(:has(> .tgb)){ align-items:stretch }
  .split:not(:has(> .tgb)) > *{ display:flex; flex-direction:column; justify-content:center }
  .objgrid{ align-items:stretch }

  /* 🔴 ТЗ 33. Пара «шапка секции + содержание» из-под этого правила выведена.
     Оптический центр хорош для пары «текст + рисунок», где обе колонки
     равноправны. Здесь левая колонка — заголовок, и центрирование отрывало
     его от того, что он называет: на /skud/ заголовок стоял на 277 px ниже
     верха своей колонки, на /price/ и /dogovor/ так же. Шапка держится
     верха ряда. */
  /* специфичность (0,2,0): правило выше объявлено как
     .split:not(:has(> .tgb)) > * и при (0,1,0) перебивало бы это */
  /* 🔴 Колонка шапки узкая — 1fr к 3fr, а не 2fr к 3fr. Замер обеими
     линейками: пара 2fr/3fr лечит пустоту справа и заводит её слева —
     под коротким заголовком оставалось 514–539 px на 260–800 px высоты
     (/skud/, /osveshchenie/, /price/, /kamery-i-zakon/, /dogovor/,
     /kontakty/). При 1fr/3fr колонка шапки 293 px, и полоса под ней
     уже порога в 380 px, а содержание получает 880 px вместо 723. */
  /* 🔴 Пропорция 1 : 2,8 выбрана замером обеими линейками, а не на глаз.
     В поясе 1237 px текстовая колонка держит меру строки (~600 px), значит
     ~640 px воздуха обязаны куда-то деться. При 2fr/3fr весь он собирался
     под шапкой (514–539 px на 260–800 px высоты), при 1fr/3fr — справа
     от содержания (385–425 px). При 1fr/2,8fr колонка шапки 309 px,
     колонка содержания 864 px: обе полосы воздуха становятся меньше
     порога в 380 px почти везде. Что осталось — названо в отчёте. */
  .split.split--40{ grid-template-columns:minmax(0,1fr) minmax(0,2.8fr) }
  .split.split--40,
  .split.split--top{ align-items:start }
  .split.split--40 > *,
  .split.split--top > *{ display:block; justify-content:normal }
  /* Шапка внутри пары уже стоит в своей колонке: делить её ещё раз
     (заголовок слева, цифра справа) незачем — цифра повисала бы в разрыве
     между колонками, а заголовок жался в 26 знаков при колонке в 480 px. */
  .split.split--40 .sec__head > .ghost{ margin:0 0 var(--s-2) }
  .split.split--40 .sec__head h2,
  .split.split--40 .sec__head h3{ max-width:none }
}

/* 🔴 ТЗ 31, правка 8. Блок призыва: слева заголовок и лид, справа карточка
   связи — и правая колонка была выше левой на 162–208 px (разрыв 30–49 %
   на десяти страницах, замер на 1440). Лечение по содержимому, а не растяжкой
   пустой коробки: строка запасных каналов переехала из карточки в левую
   колонку и прижата к её низу. Левая колонка получила работу на всю высоту,
   карточка стала короче ровно на эту строку, обе кромки встали вровень.
   На телефоне колонки складываются в одну, и строка читается сразу
   за лидом — перед карточкой с кнопкой, а не после неё. */
.split:has(> .tgb),
.grid--2:has(> .tgb){ align-items:stretch }
.split:has(> .tgb) > :first-child,
.grid--2:has(> .tgb) > :first-child{ display:flex; flex-direction:column }
/* 🔴 ТЗ 31, ревью. Строка запасных каналов уехала из карточки .tgb наружу,
   а её цвета остались «для тёмного»: на /kontakty/ блок призыва стоит
   на СВЕТЛОЙ секции, и --acc-hi на белом дал 4,12 при пороге 4,5
   (замер по реальному пикселю, 1440). Возвращаем роль по контексту. */
.cta__c{ margin-top:var(--s-5) }
.blk:not(.blk--dark) .cta__c{ color:var(--soft) }
.blk:not(.blk--dark) .cta__c a{ color:var(--acc) }
@media (min-width:768px){
  .cta__c{ margin-top:auto; padding-top:var(--s-6) }
}

/* ТЗ 27, правка 19, п. 3. «Что будет дальше» — список с висящим номером
   (ТЗ 29, приём 5), а не карточки: кликать здесь не по чему. */
.nextlist__k{ margin:var(--s-6) 0 0 }
.nextlist{ list-style:none; margin:var(--s-4) 0 0; padding:0; counter-reset:nx;
           display:flex; flex-direction:column; gap:var(--s-3); max-width:46ch }
/* 🔴 Номер — абсолютом, а не колонкой грида: в гриде КАЖДЫЙ инлайновый ребёнок
   пункта становится ячейкой, и <span class="mono"> с суммой выпадал на свою
   строку — «5 000 ₽» уезжало под текст. Абсолютный маркер терпит любую
   внутреннюю разметку пункта. */
.nextlist li{ counter-increment:nx; position:relative; padding-left:var(--s-7);
              font-size:var(--t-small); line-height:1.5; color:var(--ink-i) }
.nextlist li::before{ content:counter(nx); position:absolute; left:0; top:-.1em;
                      font-family:var(--f-disp); font-size:var(--t-h4);
                      line-height:1.2; color:var(--acc-hi);
                      font-variant-numeric:tabular-nums }

/* 🔴 Номер .numlist на тёмном. Блок обязательств стал тёмным при снятии
   секции «Реле уходит в потолок» (правка 15), и цифра цветом --acc (#3B2BD6)
   на тёмном полотне дала 2,28:1 при пороге 3 — замер по реальному пикселю,
   после проигрывания reveal-сцен. На тёмном берём --acc-hi, он для того
   и заведён («hover на тёмном» в tokens.css). Значение токена не меняется. */
.blk--dark .numlist__n{ color:var(--acc-hi) }

/* ============================================================================
   ТЗ 29, часть 5. Первый экран — ровно в экран устройства.
   ============================================================================
   🔴 Прежнее решение было обратным: в комментарии у .hero записано, что
   min-height:100svh НЕ ставится, потому что лишние пиксели собираются в дыру.
   Это верно для схемы «фиксированная высота + justify-content». ТЗ 29 задаёт
   другую механику — три пояса, где растягивается СРЕДНИЙ: шапка (auto) ·
   содержательный центр (1fr) · нижний пояс фактов (auto). Дыре собраться
   негде: излишек уходит в центр, вокруг заголовка и схемы.
   Эталон приёмки — 375×667 (iPhone SE 2/3): самый узкий и низкий разом.
   svh, а не vh: 100vh на мобильных считается без адресной строки, и низ
   первого экрана уезжает под сгиб. dvh не подходит — он меняется при скролле
   и заставляет блок дышать. */
.hero-wrap{
  /* 🔴 Минус поля полотна: .shell даёт --pad-page сверху и снизу, и блок
     ровно в 100svh уезжал бы на это поле ниже сгиба. Замер: блок занимал
     y 18…918 при экране 900 — «ровно экран» по высоте и мимо экрана
     по положению. */
  min-height:calc(100vh - var(--pad-page) * 2);   /* фолбэк */
  min-height:calc(100svh - var(--pad-page) * 2);  /* кто понимает — перекроет */
  display:grid;
  grid-template-rows:1fr;      /* единственный ребёнок — тёмный блок героя */
  padding-bottom:0;
}
/* 🔴 Три пояса живут в САМОМ тёмном блоке, а не в обёртке: у .hero-wrap
   ребёнок один — <header class="blk blk--dark">. Первая версия ставила
   grid-template-rows:auto 1fr на обёртку, header попадал в первый ряд,
   второй оставался пустым — и под блоком героя на 1440×900 висело 200 px
   голого полотна. Замер «hero-wrap = 100 % экрана» при этом был зелёным:
   обёртка действительно в экран, а блок — нет. Проверять надо блок. */
.hero-wrap > .blk{
  display:grid;
  grid-template-rows:auto 1fr; /* шапка (auto) · содержательный центр (1fr) */
  min-height:0;
}
/* 🔴 min-width:0 обязателен. У элемента грида min-width по умолчанию auto,
   и он отказывается сжиматься уже min-content своего содержимого. На 768,
   где карточка контактов возвращается в поток, .hero из-за этого раздувался
   до 757 px внутри блока шириной 737 и вылезал за правый край на 82 px —
   карточка и «25 600» срезались overflow:hidden. Классический grid blowout:
   документ при этом не прокручивается вбок, и проверка scrollWidth молчит. */
.hero-wrap > .blk > *{ min-width:0 }
.hero__grid,
.hero__say,
.hero .facts{ min-width:0 }
.hero{
  display:grid;
  grid-template-rows:1fr auto; /* центр растягивается · пояс фактов прижат вниз */
  gap:var(--hero-gap);
  padding-block:var(--hero-py);
  min-height:0;                /* иначе содержимое не даёт ряду сжаться */
}
.hero__grid{ align-content:center; min-height:0 }
.hero h1{ font-size:var(--t-hero-vp) }
.hero .lead{ font-size:var(--t-lead-vp) }
.hero .facts{ margin-top:0 }

/* На низком экране пояс фактов ужимается, а не режется: числа мельче,
   подписи короче по мере строки, ряд остаётся тремя колонками. */
@media (max-height:760px){
  .hero .fact__n{ font-size:clamp(20px, 3.4svh, 40px) }
  .hero .fact__c{ font-size:var(--t-micro); max-width:24ch }
  .hero .facts{ gap:var(--s-3) var(--s-5) }
}

/* Низкий и узкий экран одновременно (эталон 375×667). Порядок снятия задан
   ТЗ 29: сначала уходит то, что дублируется, последними — заголовок и кнопка.
   🔴 Карточка контактов в герое — дубль: тот же адрес стоит в сквозном блоке
   заявки ниже и в подвале. Полосу фактов НЕ снимаем, хотя ТЗ ставит её первой:
   она и есть «числовой акцент», единственная цифра на первом экране.
   Вместо снятия — ужимаем, как и требует ТЗ («ужимаются, а не режутся»). */
@media (max-width:899px){
  /* 🔴 .hero__cc не всегда лежит внутри .hero: на /price/, /kvartira/ и
     /kontakty/ это <aside> — сосед <header> внутри .hero-wrap. Селектор
     .hero .hero__cc их не доставал, и карточка продолжала растить первый экран
     на 190 px. Берём от обёртки. */
  .hero-wrap .hero__cc{ display:none }
  .hero .fact{ padding-block:var(--s-3) }
  /* Схема ужимается, а не режется: ТЗ 29 ставит её раньше заголовка
     и кнопки в очереди на снятие, но снимать её не требуется — хватает
     ограничения по высоте. */
  .hero__fig .scheme{ max-height:32svh; width:auto; margin-inline:auto }
  /* Оглавление страницы в герое на телефоне закрыто: это <details>,
     на узком оно и так свёрнуто, но занимает строку. */
  .hero__aside .toc__t{ font-size:var(--t-micro) }
}
/* Самый жёсткий случай — 375×667. Здесь добираем последние пиксели кеглем
   цифры и полем героя; заголовок, лид и кнопка не трогаются. */
@media (max-width:767px) and (max-height:700px){
  .hero{ padding-block:var(--s-3) }
  .hero .fact{ padding-block:var(--s-2) }
  .hero .fact__n{ font-size:34px }
  .hero .fact__c{ font-size:var(--t-micro); line-height:1.3 }
}

/* ============================================================================
   ТЗ 29 — набор приёмов дизайн-прохода. Компоненты добавлены целиком,
   снимаются куском. Ни один токен палитры, шрифта, радиуса и движения
   не изменён: используются только существующие значения.
   ============================================================================ */

/* ---------- Правило A. Шапка секции в две колонки ----------
   `.measure` — ~55 знаков. В полноширинной секции справа от него всегда
   остаётся половина экрана: не иногда, а всегда. Заголовок слева, лид
   справа — пустота закрывается содержанием, а не декором. */
.sec__head--split{ display:grid; gap:var(--s-4) }
@media (min-width:900px){
  .sec__head--split{ grid-template-columns:var(--grid-7-5); gap:var(--s-8);
                     align-items:end }
  .sec__head--split > h2{ margin:0; max-width:none }
  .sec__head--split > .lead{ margin:0 }
}

/* ---------- Приём 3. Таймлайн с соединительной линией ----------
   Для последовательностей: шаг → шаг → шаг. Линия и есть смысл — она
   показывает, что это одна цепочка, а не три независимых блока. */
.flow{ list-style:none; margin:0; padding:0; display:grid; gap:var(--s-6) }
@media (min-width:860px){
  .flow{ grid-template-columns:repeat(3,minmax(0,1fr)); gap:var(--col-gap) }
}
.flow__i{ position:relative; padding-top:var(--s-7) }
/* Линия цепочки: тянется вправо до следующего шага, у последнего обрывается. */
.flow__i::before{
  content:""; position:absolute; left:0; top:11px; height:1px;
  width:calc(100% + var(--col-gap)); background:var(--line);
}
.flow__i:last-child::before{ width:22px }
.blk--dark .flow__i::before{ background:var(--line-i) }
/* Узел цепочки */
.flow__i::after{
  content:""; position:absolute; left:0; top:6px; width:11px; height:11px;
  border-radius:50%; background:var(--acc);
}
.blk--dark .flow__i::after{ background:var(--acc-hi) }
@media (max-width:859px){
  /* На узком цепочка становится вертикальной: линия идёт вниз, а не вправо. */
  .flow__i{ padding:0 0 0 var(--s-6) }
  .flow__i::before{ left:5px; top:11px; width:1px; height:calc(100% + var(--s-6)) }
  .flow__i:last-child::before{ height:22px }
  .flow__i::after{ top:6px; left:0 }
}
.flow__k{ display:block; margin:0 0 var(--s-2);
          font-family:var(--f-mono); font-size:var(--t-micro); font-weight:500;
          letter-spacing:.14em; text-transform:uppercase; color:var(--acc) }
.blk--dark .flow__k{ color:var(--acc-hi) }
.flow__t{ font-family:var(--f-disp); font-weight:500; font-size:var(--t-h4);
          line-height:1.25; margin:0 0 var(--s-3); color:var(--ink) }
.blk--dark .flow__t{ color:var(--ink-i) }
.flow__i p{ margin:0; font-size:var(--t-small); line-height:1.6; color:var(--soft);
            max-width:46ch }
.blk--dark .flow__i p{ color:var(--soft-i) }

/* ---------- Приём 4. Противопоставление в две колонки с общей осью ----------
   Не две карточки рядом, а один спор: общая горизонтальная ось сверху,
   от неё вниз два столбца. Рамок нет — граница между ними и есть ось. */
.versus{ display:grid; gap:var(--s-5); border-top:var(--rule); padding-top:var(--s-5) }
.blk--dark .versus{ border-top:var(--rule-i) }
@media (min-width:820px){
  .versus{ grid-template-columns:1fr 1fr; gap:var(--s-8) }
  .versus > * + *{ border-left:var(--rule); padding-left:var(--s-8) }
  .blk--dark .versus > * + *{ border-left-color:var(--line-i) }
}
.versus__k{ display:block; margin:0 0 var(--s-2);
            font-family:var(--f-mono); font-size:var(--t-micro); font-weight:500;
            letter-spacing:.14em; text-transform:uppercase; color:var(--soft) }
.versus__t{ font-family:var(--f-disp); font-weight:500; font-size:var(--t-h3);
            line-height:1.25; margin:0 0 var(--s-3); color:var(--ink) }
.blk--dark .versus__t{ color:var(--ink-i) }
.versus p{ margin:0; font-size:var(--t-small); line-height:1.6; color:var(--soft);
           max-width:48ch }
.blk--dark .versus p{ color:var(--soft-i) }
.versus p + p{ margin-top:var(--s-3) }
.versus--yes .versus__k{ color:var(--acc) }
.blk--dark .versus--yes .versus__k{ color:var(--acc-hi) }

/* ---------- Приём: определения (термин / объяснение) ----------
   Вместо трёх карточек с одинаковым весом. Термин — короткий, объяснение —
   длинное; это и есть их разница, карточка её стирает. */
.deflist{ margin:0; display:grid; gap:var(--s-5) }
/* 🔴 ТЗ 31, ревью. Правило писалось под строчную разметку (dl > dt + dd),
   но селектор доставал и обёрточную (dl > div > dt + dd) — там в две колонки
   вставали САМИ СТРОКИ, по паре «термин + объяснение» в ряд, и высоты
   расходились на 27–45 % (/price/ 3 пары, /o-sebe/, /kontakty/ на 1440).
   Сужаем до разметки без обёртки; обёрточную обслуживает раздел 11.4. */
@media (min-width:820px){
  .deflist:not(:has(> div)){ grid-template-columns:minmax(0,4fr) minmax(0,8fr);
            gap:var(--s-5) var(--s-8); align-items:baseline }
}
.deflist dt{ font-family:var(--f-disp); font-weight:500; font-size:var(--t-h4);
             line-height:1.25; color:var(--ink) }
.blk--dark .deflist dt{ color:var(--ink-i) }
.deflist dd{ margin:0; font-size:var(--t-small); line-height:1.6; color:var(--soft);
             /* ТЗ 33, класс H: было 60ch — 84–88 знаков в реальной строке
                (замер Range на 1440, /zashchita-ot-protechek/ и /dogovor/). */
             max-width:min(100%, var(--measure-row)) }
.blk--dark .deflist dd{ color:var(--soft-i) }
/* 🔴 ТЗ 31, правка 3. Селектор был потомковый (.deflist dt) и доставал
   dt/dd ВНУТРИ обёртки <div> — у той уже своя линия. Сужен до прямых детей:
   строчную разметку (dl > dt + dd) он обслуживает, обёрточную не трогает.
   У первой строки линии нет — её рисует верхняя рамка группы. */
@media (min-width:820px){
  .deflist > dt{ padding-top:var(--s-4); border-top:var(--rule) }
  .deflist > dd{ padding-top:var(--s-4); border-top:var(--rule) }
  .deflist > dt:first-of-type,
  .deflist > dt:first-of-type + dd{ border-top:0 }
  .blk--dark .deflist > dt,
  .blk--dark .deflist > dd{ border-top-color:var(--line-i) }
}

/* ---------- Приём 9. Врезка-цитата крупной типографикой ----------
   Самая сильная фраза страницы не должна быть набрана как обычный абзац
   в плашке. Кегль h3, мера строки, акцентная черта слева. */
/* ТЗ 41, §4.3: вертикальная кромка снята. Цитату отделяет горизонтальная
   линия — тот же приём, что у остальных блоков сайта. */
.pull{ margin:0; padding-top:var(--s-2); border-top:2px solid var(--acc) }
.blk--dark .pull{ border-top-color:var(--acc-hi) }
.pull p{ margin:0; font-family:var(--f-disp); font-weight:500;
         font-size:var(--t-h3); line-height:1.3; color:var(--ink); max-width:30ch }
.blk--dark .pull p{ color:var(--ink-i) }
.pull__src{ display:block; margin-top:var(--s-4); font-size:var(--t-small);
            line-height:1.6; color:var(--soft); max-width:52ch;
            font-family:var(--f-body); font-weight:400 }
.blk--dark .pull__src{ color:var(--soft-i) }

/* ---------- Приём 7. Липкое оглавление сбоку ----------
   Страницы направлений — по 8–13 секций, читатель теряет, где он.
   🔴 Замер до правки: свободного поля сбоку у полотна почти нет — .sec
   начинается на 58 px от края на 1440 и на 77 px на 1920. Рейка с подписями
   в это поле не помещается и легла бы поверх текста (замер показал наезд
   30–100 px). Поэтому в покое рейка — только штрихи шириной 22 px, они
   в поле помещаются; подписи разворачиваются по наведению и фокусу
   абсолютом поверх, не двигая раскладку. */
.rail{ display:none }
@media (min-width:1200px){
  .rail{
    display:block; position:fixed; left:var(--s-4); top:50%;
    transform:translateY(-50%); z-index:var(--z-notch); width:22px;
  }
  .rail__l{ list-style:none; margin:0; padding:0; display:flex;
            flex-direction:column; gap:var(--s-3) }
  .rail li{ position:relative; height:12px }
  .rail a{
    position:absolute; inset:0 auto 0 0; display:flex; align-items:center;
    text-decoration:none; white-space:nowrap;
  }
  /* Штрих вместо буллета: короткий у неактивного, длинный у текущего. */
  .rail a::before{
    content:""; height:1px; width:10px; background:var(--line-fig-l);
    flex:0 0 auto;
    transition:width var(--d-base) var(--e-out), background var(--d-fast) var(--e-out);
  }
  .rail__t{
    margin-left:var(--s-3); padding:2px var(--s-3);
    border-radius:var(--r-pill); background:var(--surf); color:var(--soft);
    font-size:var(--t-micro); line-height:1.4;
    opacity:0; transform:translateX(-4px); pointer-events:none;
    transition:opacity var(--d-base) var(--e-out), transform var(--d-base) var(--e-out);
  }
  .rail:hover .rail__t,
  .rail:focus-within .rail__t{ opacity:1; transform:none }
  .rail a[aria-current="true"]::before{ width:22px; background:var(--acc) }
  .rail a[aria-current="true"] .rail__t{ opacity:1; transform:none; color:var(--ink) }
  @media (hover:hover){ .rail a:hover .rail__t{ color:var(--ink) } }
  /* Рейка лежит поверх полотна: на тёмной секции её надо перекрасить.
     Класс ставит initRail по IntersectionObserver. */
  .rail.is-dark a::before{ background:var(--line-fig) }
  .rail.is-dark .rail__t{ background:var(--bg-2); color:var(--soft-i) }
  .rail.is-dark a[aria-current="true"]::before{ background:var(--acc-hi) }
  .rail.is-dark a[aria-current="true"] .rail__t{ color:var(--ink-i) }
}
@media (prefers-reduced-motion:reduce){
  .rail__t, .rail a::before{ transition:none }
}

/* ---------- Одна запоминающаяся деталь: вогнутый стык ----------
   Фирменный ход шапки — вогнутый вырез: вырез В блоке, а не панель поверх.
   🔴 Замер до правки: секции сайта НЕ примыкают друг к другу — это
   отдельные блоки с радиусом 26 px и зазором 18 px на полотне. Стыка между
   светлой и тёмной секцией физически нет, и «переход» между ними рисовать
   не на чем. Поэтому мотив развит там, где он применим: тёмный блок,
   идущий за светлым, получает вогнутый укус в верхних углах цветом полотна.
   Читается как продолжение шторки: полотно втекает в блок, а не панель
   ложится сверху. Реализация та же — вырез внутри блока, без наложений,
   поэтому overflow:hidden у .blk ничего не режет. */
/* Верх блока становится прямым: вогнутый укус читается только на прямом
   углу. На скруглённом (--r-blk 26px) вырез радиусом 14 px прячется внутри
   собственного радиуса блока — проверено скриншотом угла. Значение
   --r-blk не тронуто, меняется только применение на этом классе. */
.blk--joint{ border-radius:0 0 var(--r-blk) var(--r-blk) }
.blk--joint::before{
  content:""; position:absolute; inset:0 0 auto 0; height:var(--r-notch);
  pointer-events:none; z-index:1;
  background:
    radial-gradient(circle at var(--r-notch) 100%,
      #0000 calc(var(--r-notch) - .5px), var(--page) var(--r-notch))
      left top / var(--r-notch) 100% no-repeat,
    radial-gradient(circle at 0 100%,
      #0000 calc(var(--r-notch) - .5px), var(--page) var(--r-notch))
      right top / var(--r-notch) 100% no-repeat;
}


/* Подзаголовок внутри колонки противопоставления — тот же уровень, что
   .ncard__sub, компонент новый, правило дублируется, не переиспользуется
   через запятую: .ncard и .versus живут независимо. */
.versus__sub{ font-family:var(--f-disp); font-weight:500; font-size:var(--t-h4);
              line-height:1.25; margin:var(--s-4) 0 var(--s-2); color:var(--ink) }
.blk--dark .versus__sub{ color:var(--ink-i) }

/* Правило A: служебный абзац (оговорка, сноска) в узкой колонке, рядом —
   переход. Один .measure в полноширинной секции — дефект приёмки. */





/* Список внутри колонки противопоставления: подложка и вторая колонка здесь
   лишние. Подложка (.blk--dark .numlist) введена ради читаемости на градиенте,
   но внутри .versus список и так узкий — а рамка возвращает ровно ту карточку,
   которую ТЗ 29 просит убрать. Читаемость проверена замером по пикселю. */
.versus .numlist{ background:none; border:0; border-radius:0; padding:0 }
@media (min-width:1024px){
  /* .numlist на 1024+ раскладывается в две колонки многоколоночным потоком.
     Внутри узкой колонки .versus это лишнее: пять коротких пунктов делятся
     пополам и растягиваются по высоте соседней колонки. Возвращаем один
     поток; шаг между пунктами задаётся своим, а не выравниванием колонок. */
  .versus .numlist{ display:block; columns:auto }
  .versus .numlist > li{ break-inside:auto; padding-block:var(--s-3) }
  .versus .numlist__n{ font-size:var(--t-h3) }
}

/* Подпись рейки показывается только по наведению и фокусу. Текущий раздел
   отмечен длиной и цветом штриха: постоянно висящая подпись у текущего
   пункта наезжала на текст — поля сбоку у полотна почти нет (замер выше). */
@media (min-width:1200px){
  .rail a[aria-current="true"] .rail__t{ opacity:0; transform:translateX(-4px) }
  .rail:hover a[aria-current="true"] .rail__t,
  .rail:focus-within a[aria-current="true"] .rail__t{ opacity:1; transform:none }
}

/* Довод и его доказательство идут парой: слева врезка-цитата, справа
   полоса сравнения. По отдельности каждый оставлял половину секции пустой. */
.argrow{ display:grid; gap:var(--s-6) }
@media (min-width:900px){
  .argrow{ grid-template-columns:minmax(0,1fr) minmax(0,1fr);
           gap:var(--s-8); align-items:center }
}

/* 🔴 Компоненты ТЗ 29 на тёмном блоке. Замер по реальному пикселю
   31.08.2026 на /zashchita-ot-protechek/: метка .versus__k цветом --soft
   давала 2,71–3,18 при пороге 4,5 (11px), абзацы .versus p и .deflist dd
   цветом --soft-i — 3,02 на пике индиго-градиента. Тот же случай, что уже
   разобран у .hero .kicker: тихий цвет читается на дне градиента и
   проваливается на пике. На тёмном берём белила. Токены не тронуты. */
.blk--dark .versus__k{ color:var(--ink-i); opacity:.78 }
.blk--dark .versus p,
.blk--dark .deflist dd{ color:var(--ink-i); opacity:.86 }

/* ---------- ТЗ 33, класс A. Список определений в два столбца пар ----------
   Замер на 1440: строка «термин | расшифровка» занимает 843 px из 1237,
   и справа от каждой расшифровки висит 394 px пустоты — на /o-sebe/ это
   920 px по высоте подряд, на /kontakty/ 680, на /price/ 480.
   Расширять расшифровку нельзя: она уже упирается в меру строки (класс H).
   Поэтому в ряд встают ДВЕ пары, а не одна: каждая пара — целая ячейка,
   текст по колонкам не режется (это не многоколоночный поток), порядок
   чтения слева направо и сверху вниз.
   Линия принадлежит промежутку: у первого РЯДА её нет, а не у первой ячейки. */
@media (min-width:1200px){
  .deflist:has(> div){ grid-template-columns:repeat(2, minmax(0,1fr));
                       column-gap:var(--s-8) }
  .deflist:has(> div) > div{ border-top:0 }
  .deflist:has(> div) > div:nth-child(n+3){ border-top:1px solid var(--line) }
  .blk--dark .deflist:has(> div) > div:nth-child(n+3){ border-top-color:var(--line-i) }
  /* нечётный хвост занимает ряд целиком: одинокая ячейка в половину ряда
     читается как оборванный столбец */
  .deflist:has(> div) > div:nth-child(odd):last-child{ grid-column:1 / -1 }
}

/* ============================================================================
   ТЗ 29 — СИСТЕМНЫЕ ПРАВИЛА. Раскатка на все страницы.
   ТЗ прямо требует чинить причину: «Двадцать семь находок аудита — это
   двадцать семь мест, где сработало одно и то же правило вёрстки.
   Чинить надо правило.» Ниже три правила вместо 254 точечных правок.
   ============================================================================ */

/* ---------- Правило A, системно. Шапка секции с лидом — две колонки ----------
   Замер по 19 страницам: из 134 одиноких `.measure` 82 стоят в одной и той же
   конструкции — `.sec__head` с заголовком и лидом под ним. Лид ограничен мерой
   строки (~55 знаков) в контейнере 1200+ px, значит справа ВСЕГДА половина
   экрана пустая. Заголовок слева, лид справа — пустота закрывается
   содержанием. Ниже 900 конструкция складывается обратно в столбик. */
/* 🔴 ТЗ 31, правка 6. Из-под правила A выведены шапки с призрачной цифрой:
   там автораскладка ставила её ВТОРЫМ ребёнком, то есть в правую колонку,
   и ряд получался «кикер 420 px слева — цифра 52 px справа», 380 px пустоты
   между ними. Замер на 1440: четыре такие шапки — /kvartira/ (две),
   /kak-rabotaem/, /price/; в остальных 183 цифра стоит под кикером
   в той же колонке. Их разбирает отдельный блок ниже. */
@media (min-width:900px){
  .sec__head:not(:has(> .ghost)):has(> h2 + .lead),
  .sec__head:not(:has(> .ghost)):has(> h2 + .measure),
  .sec__head:not(:has(> .ghost)):has(> .kicker + h2 + .lead){
    /* 🔴 ТЗ 31, правка 6. Зазор был --s-8 (64 px) в ОБЕ стороны, и вертикальный
       разгон внутри шапки выходил больше, чем расстояние от шапки до соседнего
       блока (--sec-gap, 32 px): кикер → заголовок 80 px, заголовок → лид 64 px
       при 32 px до следующего блока. Шапка переставала читаться как один блок.
       Колоночный зазор остаётся 64 px, рядовой опускается до 12 px. */
    display:grid; grid-template-columns:var(--grid-7-5); gap:var(--s-3) var(--s-8);
    align-items:end;
  }
  /* 🔴 ТЗ 31, правка 6. Там, где над колонками стоит кикер, выравнивание
     по нижнему краю ряда отрывает заголовок от кикера на высоту разницы
     колонок (замер /price/: 97 и 123 px при 32 px до следующего блока).
     Кикер, заголовок и лид — один блок, поэтому ряд выравнивается по верху:
     заголовок начинается сразу под кикером, зазор фиксированный. */
  .sec__head:not(:has(> .ghost)):has(> .kicker + h2 + .lead){ align-items:start }
  .sec__head:not(:has(> .ghost)):has(> .kicker + h2 + .lead) > .kicker{ grid-column:1 / -1 }
  .sec__head:not(:has(> .ghost)):has(> h2 + .lead) > h2,
  .sec__head:not(:has(> .ghost)):has(> h2 + .measure) > h2,
  .sec__head:not(:has(> .ghost)):has(> .kicker + h2 + .lead) > h2{ max-width:none; margin:0 }
  .sec__head:not(:has(> .ghost)):has(> h2 + .lead) > .lead,
  .sec__head:not(:has(> .ghost)):has(> h2 + .measure) > .measure,
  .sec__head:not(:has(> .ghost)):has(> .kicker + h2 + .lead) > .lead{ margin:0 }
  /* Третий и дальше ребёнок шапки (вторая строка лида, кнопки) уходит
     под обе колонки, а не создаёт третью. */
  /* 🔴 ТЗ 31, правка 6. Было без :not(:has(> .kicker)) — и у шапки
     «кикер + заголовок + лид» лид оказывался ТРЕТЬИМ ребёнком, получал
     grid-column:1 / -1 и уезжал в собственный ряд под заголовок. Правило A
     не срабатывало ровно там, где заводилось: лид стоял узкой лентой
     с пустой правой половиной, а шапка растягивалась на три ряда. */
  .sec__head:not(:has(> .ghost)):not(:has(> .kicker)):has(> h2 + .lead) > :nth-child(n+3),
  .sec__head:not(:has(> .ghost)):has(> .kicker + h2 + .lead) > :nth-child(n+4){ grid-column:1 / -1 }
}

/* ---------- ТЗ 31, правка 6. Шапка с призрачной цифрой ----------
   Кикер, цифра и заголовок — ОДНА колонка и один блок, как в остальных 183
   шапках сайта. Правую колонку занимает лид: это содержательный сосед,
   ради которого двухколоночная шапка и заводилась (правило A ТЗ 29).
   Одинокой цифры в колонке не остаётся, пустоты справа тоже.
   Порядок чтения не меняется: цифра aria-hidden, в озвучку не идёт.
   ------------------------------------------------------------------------ */
@media (min-width:900px){
  .sec__head:has(> .ghost):has(> h2 + .lead){
    display:grid; grid-template-columns:var(--grid-7-5); gap:0 var(--s-8);
    /* 🔴 Ряды объявлены ЯВНО. С неявными рядами grid-row:1 / -1 у лида
       схлопывался в первый ряд: -1 указывает на последнюю линию явной сетки,
       а её не было — и лид вставал вверху колонки вместо низа. */
    grid-template-rows:repeat(3, auto);
  }
  .sec__head:has(> .ghost):has(> h2 + .lead) > .kicker,
  .sec__head:has(> .ghost):has(> h2 + .lead) > .ghost,
  .sec__head:has(> .ghost):has(> h2 + .lead) > h2{ grid-column:1 }
  .sec__head:has(> .ghost):has(> h2 + .lead) > h2{ max-width:none; margin:0 }
  .sec__head:has(> .ghost):has(> h2 + .lead) > .lead{
    grid-column:2; grid-row:1 / -1; align-self:end; margin:0;
  }
}

/* ---------- ТЗ 33, класс A. Шапка с цифрой, но без лида ----------
   Правило выше закрывает правую колонку лидом. Там, где лида нет, колонка
   оставалась пустой на всю высоту шапки: кикер + цифра в 97 px + заголовок
   в две строки дают 300 px, и рядом с ними 385–564 px голого фона
   (замер на 1440: /o-sebe/, /kvartira/, /kontakty/, /nastroyka/).
   Работу получает то, что в шапке уже есть, — сама цифра. Она контурная
   и aria-hidden, порядок чтения не меняется: кикер и заголовок остаются
   первой колонкой и идут подряд, цифра уходит вправо и держит полосу.
   Заодно шапка становится ниже ровно на высоту цифры. */
@media (min-width:900px){
  .sec__head:has(> .ghost):not(:has(.lead)){
    display:grid; grid-template-columns:var(--grid-7-5); gap:0 var(--s-8);
    align-items:center;
  }
  .sec__head:has(> .ghost):not(:has(.lead)) > .kicker,
  .sec__head:has(> .ghost):not(:has(.lead)) > h2{ grid-column:1; max-width:none; margin:0 }
  .sec__head:has(> .ghost):not(:has(.lead)) > .kicker{ margin-bottom:var(--s-3) }
  .sec__head:has(> .ghost):not(:has(.lead)) > .ghost{
    /* к правому краю полосы: цифра работает как колонцифра и закрывает
       полосу до конца, а не оставляет ещё один разрыв в середине */
    grid-column:2; grid-row:1 / -1; justify-self:end; align-self:center; margin:0;
  }
}

/* Шапка внутри пары «шапка + содержание» второй раз не делится: она и так
   стоит в своей колонке. Правило объявлено ПОСЛЕ блока выше — специфичность
   у обоих (0,3,0), и порядок здесь решает. */
@media (min-width:768px){
  /* 🔴 Двойной класс намеренно: системное правило «шапка в две колонки»
     объявлено как .sec__head:not(:has(> .ghost)):has(> h2 + .lead) и имеет
     специфичность (0,3,1). При (0,3,0) оно перебивало это правило, шапка
     внутри пары делилась ещё раз, лид ужимался до 147 px и наезжал
     на правую колонку (замер на 1200, /biznes/obshchepit/). */
  .split.split--40 > .sec__head.sec__head{ display:block }
  /* правило двухколоночной шапки обнуляло поля у заголовка и лида —
     в один столбец им отбивка нужна снова */
  .split.split--40 > .sec__head > .lead,
  .split.split--40 > .sec__head > .measure{ margin-top:var(--s-3) }
  /* цифра встаёт под кикер, а не к правому краю колонки: правило выше
     ставило ей justify-self:end для двухколоночной шапки, здесь колонка одна */
  .split.split--40 > .sec__head > .ghost{
    margin:0 auto var(--s-2) 0; width:fit-content;
  }
}

/* ---------- Правило B СНЯТО решением ТЗ 35, §4.1 ----------
   Правило выбирало облик карточки автоматически, по наличию ссылки внутри
   (`:not(:has(a, button, summary, input, select))`). Замер показал, к чему
   это привело: 210 карточек и восемь разных обликов, из них 69 — «карточка,
   которая перестала быть карточкой», но ховер с неё никто не снял.
   Вид карточки теперь задаётся классом в разметке: card--link, card--fact,
   card--item. Правила — в разделе «КАНОН ТЗ 35» в конце файла. */

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
















/* ---------- Правило A, устройство 2: нижняя полоса секции ----------
   Служебный абзац (оговорка, сноска), за которым ничего не следует, перестаёт
   быть узкой колонкой в полноширинной секции и становится её нижней полосой:
   хайрлайн сверху, ширина контейнера, интерлиньяж под длинную строку.
   Длинный текст режется на две колонки — так строка остаётся читаемой
   длины и правая половина секции не пустует. Приём в проекте не новый:
   .numlist на 1024+ уже раскладывается многоколоночным потоком. */
.sec__note{
  /* 🔴 ТЗ 33, классы F и A. Две колонки сняты. Причина не в суммах, которые
     ТЗ 31 связало неразрывностью, а в самой конструкции: предложение,
     начатое в левой колонке, заканчивается в правой. На /nastroyka/ это
     читалось как «…Со списком можно идти в магазин за | возвратом — многие
     маркетплейсы принимают нераспакованное», на /osveshchenie/ по колонкам
     разъезжалось само число. Колоночный поток не умеет держать фразу
     целиком, поэтому он не чинится, а снимается — как уже снят на списках.
     Пустота справа закрывается не колонкой, а шириной: полоса сужается
     по содержанию (разрешение Р2 класса A), мера строки возвращается
     на место. Замер Range после правки: 61 знак в среднем, 78 максимум. */
  /* 52ch, а не --measure: замер Range на /biznes/obshchepit/ дал при 60ch
     83 знака в строке и 80 в среднем; при 52ch — 72 и 68. */
  max-width:min(100%, 52ch);
  border-top:var(--rule);
  padding-top:var(--s-4);
  margin-top:var(--s-6);
  line-height:1.7;
}
.blk--dark .sec__note{ border-top-color:var(--line-i) }
.sec__note--long{ line-height:1.75 }

/* Врезка-цитата с нормативной формулировкой: кегль ниже врезки-довода —
   это точный текст для договора, его читают, а не считывают. */
.pull--quote p{ font-family:var(--f-body); font-weight:400;
                font-size:var(--t-body); line-height:1.6; max-width:64ch }

/* 🔴 ТЗ 33, класс H. Правило «лид во всю ширину» жило при двухколоночной
   полосе: колонка резала строку пополам, и снятие меры было безопасно.
   Колонок больше нет, и на /bystryy-start/ такой лид шёл одной строкой
   в 108 знаков при потолке 95 (замер Range). Мера возвращается. */
.lead.sec__note{ max-width:min(100%, var(--measure)) }

/* Таймлайн на четыре шага: три колонки под четыре шага дают сироту,
   поэтому у варианта --4 своя сетка — 2×2 на среднем экране, 4 в ряд
   на широком. */
@media (min-width:700px) and (max-width:1199px){
  .flow--4{ grid-template-columns:repeat(2,minmax(0,1fr)) }
  /* линия обрывается на краю ряда, а не тянется в пустоту */
  .flow--4 > .flow__i:nth-child(2n)::before{ width:22px }
}
@media (min-width:1200px){
  .flow--4{ grid-template-columns:repeat(4,minmax(0,1fr)) }
}

/* Таймлайн в узкой колонке — всегда вертикальный: соединительная линия идёт
   вниз, а не вправо. Тот же компонент, другая ось. */
.flow--stack{ grid-template-columns:1fr !important; gap:var(--s-4) }
.flow--stack > .flow__i{ padding:0 0 0 var(--s-6) }
.flow--stack > .flow__i::before{ left:5px; top:11px; width:1px; height:calc(100% + var(--s-4)) }
.flow--stack > .flow__i:last-child::before{ height:18px }
.flow--stack > .flow__i::after{ top:6px; left:0 }
.flow--stack .flow__k{ margin-bottom:var(--s-1) }

/* 🔴 Нижняя полоса секции на тёмном блоке тянется во всю ширину и попадает
   на пик индиго-градиента: --soft-i даёт там 3,51 при пороге 4,5 (замер
   по реальному пикселю, /partneram/). Тот же случай, что у .hero .kicker
   и .versus p. Белила с приглушением — тон служебного текста сохраняется.

   🔴 ТЗ 31, ревью. Приглушения оказалось мало, и цветом это не лечится
   в принципе: на ядре градиента rgb(116,101,251) даже ЧИСТО БЕЛЫЙ даёт 4,22
   при пороге 4,5. Замер по реальному пикселю на /kvartira/ показал 4,21 и 4,46
   на правой колонке полосы. Лечим подложку, а не чернила — тем же приёмом,
   что уже применён к спискам и таблицам на градиенте (§9.9, .plate):
   сплошная --bg-2 под полосой. На ней белила дают 19,2. */
.blk--dark .sec__note,
.blk--dark .sec__note.muted{ color:var(--ink-i); opacity:1 }
.blk--dark .sec__note{
  background:var(--bg-2); border-radius:var(--r-card);
  border-top:0; padding:var(--s-5);
  /* ТЗ 33, класс A. Подложка обнимает содержание: после снятия колонок
     текст ограничен мерой строки, а плита во всю ширину оставляла бы
     справа полосу пустой заливки. Тот же приём, что у .plate с одним
     только текстом внутри. Мера строки при этом остаётся за --measure:
     max-width:100% здесь перебил бы базовое ограничение, и полоса снова
     пошла бы в 131–144 знака (замер Range, /nastroyka/ и /bystryy-start/). */
  width:fit-content; max-width:min(100%, 52ch);
}
@media (max-width:767px){ .blk--dark .sec__note{ padding:var(--s-4) } }
.blk--dark .sec__note + .sec__note{ margin-top:var(--s-3) }

/* ---------- ТЗ 33, класс A. Ряд сносок ----------
   Две-три сноски подряд стояли столбиком: каждая шириной в меру строки,
   справа от каждой пусто, и в сумме участок неработающей полосы доходил
   до 580 px по высоте (замер на 1440: /kvartira/, /bystryy-start/,
   /nastroyka/, /price/, /dogovor/). В ряду сноска остаётся ЦЕЛОЙ в своей
   колонке — это не многоколоночный поток, фразу по колонкам не режет. */
.notes{ display:grid; gap:var(--s-4); margin-top:var(--s-6) }
.notes > .sec__note{ margin-top:0 }
/* 🔴 ТЗ 33, класс E. В ряду у обеих колонок одинаковое оформление: врезка
   получает тот же хайрлайн сверху, что и сноска рядом. Без этого у одной
   колонки пары линия была, у другой нет — ровно то, что класс E запрещает.
   Акцентная черта врезки остаётся: она различает довод и сноску по роли,
   а не по краю ряда. */



/* ---------- ТЗ 33, класс B. Подзаголовок, прикинувшийся текстом ----------
   Два-три абзаца подряд, каждый из которых начинается с жирного фрагмента
   («На работы — 24 месяца», «На оборудование — гарантия производителя»,
   «Почему 15% названы вслух»), читаются как сплошная проза: ранга у этих
   фрагментов не было, кегль и вес почти совпадали с телом абзаца.
   Фрагмент получает свой ранг и отбивку. Слова не менялись — изменено
   только оформление. Юридические страницы под правило не попадают:
   их структура — вопрос к юристу, а не к вёрстке. */
.sec__note > strong:first-child{
  display:block; margin-bottom:var(--s-2);
  font-family:var(--f-disp); font-weight:500;
  font-size:var(--t-h4); line-height:1.3; color:var(--ink);
}
.blk--dark .sec__note > strong:first-child{ color:var(--ink-i) }
@media (min-width:1200px){
  .notes{ grid-template-columns:repeat(2, minmax(0,1fr)); column-gap:var(--s-8) }
  /* нечётный хвост занимает ряд целиком */
  .notes > :nth-child(odd):last-child{ grid-column:1 / -1 }
}
.blk--dark .notes > .sec__note + .sec__note{ margin-top:0 }

/* 🔴 Строка реквизитов набрана моноширинным: там ch — ровно один знак,
   и общая мера 52ch давала 52 знака при норме 75 (колонка 343 px в поясе
   726 px, /price/). Правило стоит ПОСЛЕ .blk--dark .sec__note: у той
   специфичность (0,2,0), и на тёмном блоке она перебивала более раннее. */
.sec__note.req-line,
.blk--dark .sec__note.req-line{ max-width:min(100%, 75ch) }

/* Чем именно закрывается пункт списка — служебная приписка в конце строки:
   моноширинная, тише основного текста, но не серым по серому. */
.numlist__by{ display:inline-block; margin-top:var(--s-1);
              font-family:var(--f-mono); font-size:var(--t-micro);
              color:var(--soft); white-space:nowrap }
/* 🔴 ТЗ 33, класс D. На 375 приписка не помещалась в колонку пункта, а запрет
   переносов не давал ей сжаться: дорожка 1fr раздувалась по min-content,
   и весь пункт вылезал за пояс на 8 px (замер, /biznes/obshchepit/,
   шесть пунктов). Приписка короткая — на телефоне ей перенос не вредит. */
@media (max-width:767px){ .numlist__by{ white-space:normal } }
.blk--dark .numlist__by{ color:var(--ink-i); opacity:.7 }

/* ТЗ 29, часть 5. Очередь снятия на телефоне задана самим ТЗ: «сначала уходит
   нижний пояс фактов, потом подпись схемы, последним — заголовок и кнопка».
   Поддерживающая мелочь в колонке героя стоит в этой очереди раньше фактов:
   она поясняет, а не утверждает. На /kvartira/ две такие врезки занимали
   223 px из 575 доступных — ровно столько, сколько не хватало первому экрану. */
@media (max-width:899px){
  .hero__say .muted{ display:none }
}

/* ТЗ 29, часть 5, очередь снятия на телефоне: «сначала уходит нижний пояс
   фактов». На страницах, где в герое стоит полоса статов (.stats), она и есть
   этот пояс — на 375 она занимает 407 px из 631 доступных. Заголовок, лид
   и кнопка остаются. Сами цифры на странице не пропадают: 5 000 / 400 / 0 ₽
   стоят ниже в таблицах прайса. */
@media (max-width:899px){
  .hero .stats,
  .hero .plate:has(.stats){ display:none }
  /* Кнопки первого экрана в столбик занимали 96–156 px. Ставим по две в ряд:
     на 375 это по 170 px на кнопку — надпись помещается, цель нажатия
     остаётся 44 px по высоте. Три кнопки дают 2 + 1. */
  .hero .btn-row{ flex-wrap:wrap; gap:var(--s-2) }
  .hero .btn-row .btn{ flex:1 1 calc(50% - var(--s-2)); width:auto; min-width:0 }
  .hero .btn-row .btn .btn__ic{ flex:0 0 auto }
}

/* Добор последних пикселей первого экрана на телефоне и планшете.
   Компактный пояс фактов включается по ШИРИНЕ, а не только по низкому экрану:
   на 390×844 и 414×896 порог max-height:700 не срабатывал, а места всё равно
   не хватало. Схема ограничивается и по доле экрана, и потолком в пикселях —
   на высоком телефоне 32svh давало картинку крупнее, чем нужно. */
@media (max-width:899px){
  .hero .fact__n{ font-size:clamp(22px, 3.2svh, 38px) }
  .hero .fact__c{ font-size:var(--t-micro); line-height:1.35 }
  .hero .fact{ padding-block:var(--s-2) }
  .hero__fig .scheme{ max-height:min(30svh, 240px) }
  .hero .lead{ line-height:1.5 }
}

/* Где в герое есть и схема, и пояс фактов, на телефоне не помещается ни то,
   ни другое целиком. По очереди ТЗ 29 первым уходит пояс фактов — схема
   остаётся и ужимается. Там, где схемы нет (страницы направлений), пояс
   фактов остаётся: он и есть единственный числовой акцент первого экрана. */
@media (max-width:899px){
  .hero:has(.hero__fig) .facts{ display:none }
  .hero__fig .scheme{ max-height:min(24svh, 190px) }
}

/* Эталон приёмки 375×667 — самый жёсткий из живых экранов. Здесь добираются
   последние десятки пикселей: пояс фактов идёт плотнее, поля героя минимальны.
   Заголовок, лид и кнопка не снимаются ни при каких условиях. */
@media (max-width:767px) and (max-height:700px){
  .hero .fact{ padding-block:var(--s-1) }
  .hero .fact__n{ font-size:22px }
  .hero .fact__c{ font-size:11px; line-height:1.3 }
  .hero .facts{ gap:var(--s-1) }
}

/* Низкий экран на десктопе — 1280×720, ТЗ 29 называет его вторым по жёсткости
   после 375×667. Ширина там десктопная, а высоты столько же, сколько
   у планшета. Очередь снятия та же: первым уходит пояс статов. */
@media (min-width:900px) and (max-height:760px){
  .hero .stats,
  .hero .plate:has(.stats){ display:none }
}
/* Последние пиксели на 375×667: поля героя и зазор между поясами. */
@media (max-width:767px) and (max-height:700px){
  .hero{ padding-block:var(--s-2); gap:var(--s-3) }
  /* Плашка лида на 375 добирала последние 6 px поверх экрана. */
  .hero .plate{ padding:var(--s-3) }
}

/* 🔴 Таймлайн на тёмном блоке. Замер по реальному пикселю: метка .flow__k
   цветом --acc-hi даёт 2,69–4,04 на пике индиго-градиента при пороге 4,5,
   подзаголовок и абзац на --soft-i — 2,99–3,70. Шестой случай одного и того же:
   тихий цвет читается на дне градиента и проваливается на пике (первым это
   поймали на .hero .kicker). На тёмном — белила с приглушением. Акцентный цвет
   остаётся на узле цепочки: это точка, а не текст. */
.blk--dark .flow__k{ color:var(--ink-i); opacity:.78 }
.blk--dark .flow__t{ color:var(--ink-i) }
.blk--dark .flow__i p{ color:var(--ink-i); opacity:.86 }

/* ---------- Сирота в последнем ряду сетки ----------
   Тот же дефект, который правка 13 ТЗ 27 закрыла для .objtiles, живёт и в
   остальных сетках: четыре элемента в трёх колонках дают 3 + 1, и четвёртый
   висит огрызком под левым краем. Замер по отрисованным страницам: 17 таких
   мест на 1440 и 1024. Лечится числом колонок, а не растяжением: при четырёх
   элементах ряд 2 + 2 сироты не оставляет. Пять элементов дают 3 + 2 — их
   не трогаем, поэтому условие именно на четвёртый-и-последний. */
@media (min-width:900px){
.grid:has(> :nth-child(4):last-child),
.hub:has(> :nth-child(4):last-child){
    grid-template-columns:repeat(2,minmax(0,1fr));
  }
}

/* 🔴 Приёмка правки 2 ТЗ 27: «содержимое коммерции длинное — проверить
   прокрутку на 375 и что кнопка закрытия остаётся достижимой». Не оставалась:
   .notch__close стояла position:absolute ВНУТРИ прокручиваемой шторки и
   уезжала вместе с содержимым. Замер: после раскрытия «Коммерческие объекты»
   панель прокручивается на 391 px, и крест оказывается на 379 px выше экрана.
   Бургер в это время закрыт самой шторкой, остаётся только Esc и узкая полоса
   оверлея. Делаем крест липким: он остаётся в правом верхнем углу панели
   при любой прокрутке. Пункт-обёртка при этом не занимает высоту. */
@media (max-width:767px){
  /* Пункт с крестом переносится в НАЧАЛО потока (order:-1) и делается липким.
     В конце списка sticky бесполезен: элемент прилипает в пределах своего
     места и всё равно уезжает вниз — проверено замером (крест оказывался
     на 579 px, у нижнего края). Порядок чтения с клавиатуры при этом
     не страдает: крест логично идёт первым в шторке. */
  .notch__list > li:has(> .notch__close){
    order:-1; position:sticky; top:0; z-index:2;
    height:0; margin:0; overflow:visible;
  }
  .notch__close{
    position:absolute; right:0;
    top:calc(-1 * (var(--burger) + var(--s-3)));
  }
}

/* 🔴 Цель нажатия у контактных ссылок в перечнях. Замер `qa/_a11y.mjs` на 390:
   почта и телеграм в реквизитах и в таблице контактов давали 17–23 px по высоте
   при норме 44. Правило для .hero__cc-v a уже было — здесь тот же случай,
   просто в других контейнерах: <dd> реквизитов, ячейка таблицы, шаг процесса.
   Ссылка внутри предложения (в <p>) не трогается: там правило не применяется. */
@media (max-width:767px){
  .reqs dd a,
  .step__t a,
  main td > a:only-child,
  main dd > a:only-child{
    display:inline-flex; align-items:center; min-height:var(--burger);
  }
}

/* 🔴 Седьмой случай того же отказа. Метка секции на тёмном блоке набрана
   --acc-hi при 11px: замер по реальному пикселю даёт 4,54 при пороге 4,5 —
   формально проходит, запаса нет, и это худшая проба из 8321 по всему сайту.
   233 пробы сидят в зоне 4,5–5,0, и почти все они — эта метка.
   Соседние метки того же рода (.versus__k, .ncard__k, .flow__k, .figc__tag--acc)
   уже переведены на белила с приглушением по этой же причине; .hero .kicker
   переведён ещё 27.08. Приводим к ним: акцентный цвет остаётся на выносной
   черте перед меткой — это графика, а не текст. Токены не тронуты. */
.blk--dark .kicker{ color:var(--ink-i); opacity:.78 }


/* ==========================================================================
   ТЗ 31, правки 4 и 5. РЯД СПИСКА ВО ВСЮ ШИРИНУ ПОЯСА
   Замер после снятия columns:2: список шёл одной колонкой 599 px внутри
   пояса 1237 px — 638 px пустоты справа, по пять–семь раз на странице.
   Это и есть «текстовая лента» правки 4. Разрешение Р2: место справа
   получает работу — уже имеющийся текст пункта, ничего не придумано.
   Порядок чтения остаётся сверху вниз: пункт 2 под пунктом 1, а не справа.
   Блок внутри пункта (кнопка «Скачать табличку A5» на /videonablyudenie/)
   остаётся внутри своего пункта — это то, чего не умел многоколоночный поток.

   🔴 Блок стоит в КОНЦЕ файла намеренно: и .step, и .numlist > li
   переопределяются точечными правилами разделов 10, 24 и «правок 30.08».
   ========================================================================== */
@media (min-width:1024px){
  /* Пункт .steps — это <details>: номер и название в <summary>, пояснение
     в теле. Ряд делаем на самом <details>: заголовок слева, пояснение
     справа. Меру строки держит тело, а не пункт, поэтому ограничение
     переезжает на .dpt__body — иначе строка вырастет до 97+ знаков
     (эта грабля уже записана в разделе 17.1). */
  .steps > li.step{ max-width:none }
  .steps .dpt--step{
    max-width:none; display:grid;
    grid-template-columns:minmax(0,2fr) minmax(0,3fr); column-gap:var(--s-8);
  }
  .steps .dpt--step > summary{ grid-column:1 }
  .steps .dpt--step .dpt__body{ max-width:min(100%, var(--measure-row)) }
  .steps .step{
    grid-template-columns:auto minmax(0,2fr) minmax(0,3fr);
    align-items:start;
  }
  .steps .step__n{ grid-row:auto }
  .step__x{ max-width:none }

  /* 🔴 main li{max-width:--measure} режет сам пункт — без снятия ограничения
     три колонки складываются внутри 600 px, и правая половина пояса остаётся
     пустой (проверено замером: 189 блоков вместо 101). Меру строки после
     снятия держит пояснение, а не пункт. */
  .numlist > li{
    max-width:none;
    grid-template-columns:auto minmax(0,2fr) minmax(0,3fr);
    gap:0 var(--s-6); align-items:start;
  }
  .numlist__n{ grid-row:auto }
  .numlist__t{ margin:0; max-width:none }
  .numlist__x{ max-width:min(100%, var(--measure-row)) }
  /* 🔴 ТЗ 31, правки 5 и 9. Собственный блок внутри пункта (кнопка
     «Скачать табличку A5» в пункте 3 на /videonablyudenie/) — четвёртый
     ребёнок ряда из трёх колонок: он занимал колонку номера и оставлял
     две пустые ячейки. Отдаём ему весь ряд под номером, названием
     и пояснением: дыры нет, а кнопка читается как часть своего пункта —
     ровно то, чего не умел многоколоночный поток. */
  .numlist > li > :nth-child(n+4){ grid-column:1 / -1 }
  /* 🔴 Пункт без названия (только цифра и абзац) три колонки не заполняет:
     абзац встаёт во вторую, третья остаётся пустой — 8 таких пунктов
     на /price/ и /biznes/posutochnaya-arenda/, замер на 1440. Такому пункту
     ряд из трёх колонок не нужен: возвращаем ему «цифра + текст». */
  .numlist > li:not(:has(> .numlist__t)){ grid-template-columns:auto minmax(0,1fr) }

  /* 🔴 ТЗ 31, правка 5, проверка «секция не выросла выше потолка».
     Одна колонка вместо двух стоит высоты: замер по 23 страницам показал
     +4 809 px суммарно и четыре секции, перешагнувшие потолок 1,4 экрана
     (13 → 17). Часть возвращаем шагом ряда: в ряду во всю ширину пункты
     читаются как строки таблицы, и 24 px между ними избыточны.
     Кегли и интерлиньяж не трогаются. */
  .numlist{ gap:var(--s-4) }
  .numlist > li{ padding-top:var(--s-4) }
  .steps{ gap:var(--s-4) }
  .steps .step{ padding-top:var(--s-4) }
  .sec > .nlist:not(.nlist--tight) > li{ margin-bottom:var(--s-4) }

  /* 🔴 ТЗ 33, класс A. Пункт .nlist — тот же <details>, что .steps и .numlist,
     но ряд во всю ширину ему в ТЗ 31 не достался: название и пояснение
     остались стопкой в колонке 600 px, и справа висело 553 px пустоты
     (замер на 1440: /kvartira/ «Чего ретрофит не делает» и «Что тогда
     делать», /nastroyka/ «Если купленное не дружит между собой»).
     Приводим к тому же приёму: название слева, пояснение справа.
     Меру строки держит пояснение, а не пункт. */
  .nlist .dpt--nl{
    max-width:none; display:grid;
    grid-template-columns:minmax(0,2fr) minmax(0,3fr); column-gap:var(--s-8);
    align-items:start;
  }
  .nlist .dpt--nl > summary{ grid-column:1 }
  /* 🔴 Селектор с .sec намеренно: правило «.sec > .nlist p{max-width:none}»
     из раздела 20.3 имеет специфичность (0,3,1) и без него перебивало меру
     строки — замер Range показывал 85 знаков там, где ждали 78. */
  /* 🔴 display:block обязателен. С 768 пояснение пункта набрано display:inline
     (пункт читается одним абзацем), а строчный элемент max-width игнорирует.
     Блочным его не делает и грид: содержимое <details> лежит в слоте
     ::details-content, и ячейкой ряда становится слот, а не сам абзац.
     Без этой строки замер Range давал 85 знаков при 42ch. */
  .sec .nlist .dpt--nl .dpt__body{
    display:block; max-width:min(100%, var(--measure-nl));
  }
  /* в паре колонок (.nlist--two) ряд уже разрезан по вертикали — там пункт
     остаётся стопкой, иначе внутри 600 px появятся ещё две колонки по 240 */
  .nlist--two .dpt--nl{ display:block }
}

/* ==========================================================================
   ТЗ 31, правка 9. ДЫРА В ПОСЛЕДНЕМ РЯДУ СЕТКИ
   Лечение — не многоколоночный поток (почему — правка 5), а подбор числа
   колонок под число карточек. Замер на 1440: /biznes/pvz/ «Что в эту работу
   не входит» — 5 карточек в 3 колонки, одна ячейка пустая; главная,
   ul.objtiles — 4 плитки в 3 колонки, две ячейки пустые.
   ========================================================================== */
@media (min-width:1024px){
  /* 5 карточек: ряд 3 + ряд 2, ширины внутри ряда равные (сетка из шести
     дорожек: 2+2+2 и 3+3). Разделение осмысленное — по рядам, а не «одна
     карточка вдруг вдвое шире соседей». */
  /* ТЗ 41: частный случай пяти карточек снят — его закрывает общее правило
     «ряд заполняет ряд» ниже по файлу. */

  /* 4 плитки — это 2 × 2, а не «3 и одна». */

}

/* ---------- ТЗ 31, правка 9. Сирота в последнем ряду на средних ширинах ----
   Замер: 24 сетки на 768 и 2 на 1024 — везде один и тот же случай, три
   карточки в двух колонках, третья стоит одна и рядом с ней пустая ячейка.
   На 1440 и 1200 те же сетки трёхколоночные, дыры нет; на 375 они в одну
   колонку. Лечим ровно диапазон, где колонок две: сирота занимает ряд
   целиком. Число карточек не меняется, порядок тоже.
   ------------------------------------------------------------------------ */
@media (min-width:560px) and (max-width:1023px){
.grid--3:has(> :nth-child(odd):last-child) > :last-child,
.grid--2:has(> :nth-child(odd):last-child) > :last-child,
.hub:has(> :nth-child(odd):last-child) > :last-child,
.btn-grid:has(> :nth-child(odd):last-child) > :last-child,
.tariffs:has(> :nth-child(odd):last-child) > :last-child{ grid-column:1 / -1 }
}
/* .tariffs и .grid--2 остаются двухколоночными и выше 1024 — там же случай. */
@media (min-width:1024px) and (max-width:1199px){
  .grid--2:has(> :nth-child(odd):last-child) > :last-child,
  .tariffs:has(> :nth-child(odd):last-child) > :last-child{ grid-column:1 / -1 }
}


/* ==========================================================================
   ТЗ 32 (ответ 1). СМЕТА В ДВЕ РАВНОПРАВНЫЕ КОЛОНКИ ЦЕН
   Комплект от протечек считается двумя способами, и ни один не назначен
   по умолчанию (решение владельца 01.09.2026). Поэтому не «цена и сноска
   про второй вариант», а две колонки одинакового веса: заголовки одинаковые,
   кегль одинаковый, выделения ни у одной нет.
   ========================================================================== */
.spec--two th[scope="col"].num{ width:1%; white-space:nowrap }
.spec--two td.num{ font-variant-numeric:tabular-nums; white-space:nowrap }
@media (max-width:767px){
  /* на телефоне строка спецификации складывается в блок (раздел 27),
     и обе цены обязаны остаться подписанными — иначе непонятно, где какая */
  .spec--two tbody td.num::before{
    content:attr(data-l) " · "; color:var(--soft);
    font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.08em;
    text-transform:uppercase;
  }
  .blk--dark .spec--two tbody td.num::before{ color:var(--soft-i) }
}

/* ==========================================================================
   ТЗ 32. «МОЖНО ДОБАВИТЬ» — резервное питание отдельной опцией
   Решение владельца 01.09.2026: опция НЕ входит в смету и не поднимает
   опорные суммы объектов. Поэтому блок обязан читаться как ДОБАВКА, а не как
   строка сметы: не карточка с рамкой (правило B ТЗ 29 — рамка только там, где
   есть действие), а ruled-список во всю ширину пояса, как .numlist после
   правки 4 ТЗ 31. Ряд: что держит · сколько держит · сколько стоит.
   🔴 Подпись «в смету не входит» стоит у КАЖДОЙ цены, а не один раз в шапке:
   строка может быть прочитана в отрыве от заголовка.
   ========================================================================== */
.addon{ display:grid; gap:var(--s-4); list-style:none; margin:0; padding:0 }
.addon > li{
  display:grid; gap:var(--s-2);
  padding-top:var(--s-4);
}
.addon > li:first-child{ padding-top:0; border-top:0 }

/* 🔴 На тёмном блоке список кладётся на сплошную подложку --bg-2. Это не
   карточка (правило B ТЗ 29 про карточки в силе) — это подложка под
   читаемость, тот же приём, что у .plate вокруг таблиц и списков на
   градиенте. Причина замерена: на живом градиенте служебные цвета падают
   ниже AA — «в смету не входит» (--soft-i) дало 2,88 на /zashchita-ot-protechek/
   и 3,19 на /biznes/pvz/, «Автономность …» (--acc-hi) — 3,22–4,43 на пяти
   пробах. Цветом это не чинится: у --acc-hi на градиенте потолок 4,43.
   На сплошной --bg-2 те же цвета дают 7,2 и 4,65 — числа из раздела 8
   этого файла, проверенные раньше на ссылках внутри плашки. */
.blk--dark .addon{
  background:var(--bg-2); border-radius:var(--r-card); padding:var(--s-5);
}
@media (max-width:767px){ .blk--dark .addon{ padding:var(--s-4) } }
.addon > li + li{ border-top:1px solid var(--line) }
.blk--dark .addon > li + li{ border-top-color:var(--line-i) }

.addon__t{ margin:0; font-family:var(--f-disp); font-weight:500;
           font-size:var(--t-h4); line-height:1.3; color:var(--ink); max-width:none }
.blk--dark .addon__t{ color:var(--ink-i) }
.addon__x{ margin:0; font-size:var(--t-body); line-height:var(--lh-body);
           color:var(--soft); max-width:min(100%, var(--measure)) }
.blk--dark .addon__x{ color:var(--soft-i) }
.addon__d{ margin:0; font-family:var(--f-mono); font-size:var(--t-micro);
           letter-spacing:.1em; text-transform:uppercase; color:var(--acc) }
.blk--dark .addon__d{ color:var(--acc-hi) }
.addon__p{ margin:0; display:flex; flex-wrap:wrap; align-items:baseline; gap:var(--s-1) var(--s-3) }
.addon__n{ font-size:var(--t-micro); color:var(--soft); white-space:nowrap }
.blk--dark .addon__n{ color:var(--soft-i) }
.addon__v{ font-family:var(--f-mono); font-variant-numeric:tabular-nums;
           font-size:var(--t-h4); font-weight:500; color:var(--ink); white-space:nowrap }
.blk--dark .addon__v{ color:var(--ink-i) }

/* Ряд во всю ширину пояса — то же устройство, что у .numlist и .steps
   (правка 4 ТЗ 31): пустого соседа справа у текстовой колонки не остаётся. */
@media (min-width:1024px){
  /* 🔴 main li{max-width:--measure} режет и этот пункт: без снятия ограничения
     ряд складывался внутри 600 px, и справа оставалась пустая половина пояса —
     ровно тот дефект, который лечит правка 4 ТЗ 31. Меру строки держит
     пояснение (.addon__x), а не сам пункт. */
  .addon > li{
    max-width:none;
    grid-template-columns:minmax(0,4fr) minmax(0,5fr) minmax(0,3fr);
    gap:var(--s-2) var(--s-6); align-items:start;
  }
  .addon__t{ grid-column:1 }
  .addon__x{ grid-column:2; grid-row:1 / span 2 }
  .addon__d{ grid-column:1; grid-row:2 }
  .addon__p{ grid-column:3; grid-row:1 / span 2; justify-content:flex-end; text-align:right }
  .addon__p{ display:grid; justify-items:end; gap:var(--s-1) }
}

/* Подпись «в смету не входит» внутри ячейки цены: у .price-table td.num стоит
   white-space:nowrap, и без сброса подпись прилипала к самой цене —
   «11 800 ₽в смету не входит» (замер на 1440, /price/). */
.price-table td.num .addon__n{
  display:block; white-space:normal; margin-top:var(--s-1);
  font-family:var(--f-body); font-size:var(--t-micro); color:var(--soft);
}
.blk--dark .price-table td.num .addon__n{ color:var(--soft-i) }

/* На телефоне блок «Можно добавить» складывается в столбик и вырастает:
   замер на 375 — /price/ 2,19 экрана, /biznes/pvz/ и /biznes/sklady/ 1,82,
   /skud/ 1,65, /biznes/obshchepit/ 1,63 при потолке 1,6. Текст не режем
   (он согласован дословно) и под кнопку не прячем (правило ТЗ 32: скрывать
   можно только то, что читатель пропускает сам). Дожимаем шагом — тем же
   приёмом, что раздел 16.2 применил к секциям на телефоне. */
@media (max-width:767px){
  .addon{ gap:var(--s-3) }
  .addon > li{ padding-top:var(--s-3); gap:var(--s-1) }
  .blk--dark .addon{ padding:var(--s-3) }
  .addon + .sec__note{ margin-top:var(--s-4) }

  section:has(> .sec > .addon) .sec__head,
  section:has(.addon) .sec__head{ margin-bottom:var(--s-4) }
}

/* ==========================================================================
   ТЗ 34, правило 1.1 (продолжение). ШАПКА СБОКУ У ПОЯСА С ШИРОКОЙ ТАБЛИЦЕЙ

   Почему не хватило max-width на ячейке: в автоматической раскладке таблицы
   ограничение ширины ячейки браузером не соблюдается — замер показал 32 → 31,
   то есть правило не сработало. Ширину задаёт таблица целиком.

   Но одно только сужение таблицы в поясе 1237 px открывает справа 397 px
   голого фона — это уже дефект класса A (порог 380×240). Поэтому таблица
   сужается вместе с переводом пояса на общий приём проекта: шапка слева,
   содержание справа (ТЗ 33, .split--40). Освободившееся место занимает
   заголовок, дыры не остаётся, а строка в ячейке приходит в меру.

   Приём собран классом на поясе, а не перестройкой разметки: сетка
   раскладывает уже существующих детей, ни один узел не переезжает.
   ========================================================================== */
@media (min-width:1024px){
  /* 🔴 Двойной класс: приём ставится и поверх .split.split--40 (0,2,0),
     у которого свои пропорции 2fr/3fr. Там колонка шапки выходит 485 px,
     и под коротким заголовком слева открывается 381–409 px пустоты
     на 260–320 px высоты — дефект класса A с левой стороны
     (/price/, /o-sebe/, /dogovor/, /kamery-i-zakon/). Рельса в 290 px
     держит свободное поле в 322 px, ниже порога. */
  .sec--headside.sec--headside{
    display:grid; align-items:start;
    /* 🔴 Ширина колонки шапки задана в пикселях, а не долей. Доля 2fr
       давала слева 509 px пустоты под шапкой на всю высоту таблицы —
       дефект класса A с другой стороны (порог 380). 290 px плюс
       промежуток 32 px дают 322 px, и линейка пустоты молчит
       на всех ширинах от 1024. */
    grid-template-columns:minmax(0,290px) minmax(0,1fr);
    gap:var(--s-4) var(--s-6);
  }
  .sec--headside > *{ min-width:0 }
  /* 🔴 Двойной класс намеренно, причина та же, что у .split--40 (раздел выше):
     системное правило «шапка в две колонки» имеет специфичность (0,3,1)
     и перебивает одинарный класс — лид ужимался до 86 px и наезжал
     на таблицу (замер на 1440, главная, 68 наложений). */
  .sec--headside.sec--headside > .sec__head.sec__head{ grid-column:1; margin-bottom:0; display:block }
  /* та же причина, что строкой выше: системное правило обнуляет поля
     у заголовка и лида ради двухколоночной шапки, здесь колонка одна */
  .sec--headside.sec--headside > .sec__head > .lead,
  .sec--headside.sec--headside > .sec__head > .measure{ margin-top:var(--s-3) }
  .sec--headside.sec--headside > .sec__head > .ghost{ margin:0 auto var(--s-2) 0; width:fit-content }
  /* 🔴 ТЗ 35, §4.4. Было `> *:not(.sec__head){ grid-column:2 }` — и ВСЁ,
     кроме шапки, приколачивалось ко второй колонке навсегда. Правило писали
     под форму «шапка слева + одна таблица справа», а применяли к секциям
     с семью детьми: на главной под шапкой оставалось 957 px пустоты,
     а два заголовка второго уровня стояли на x=424 против x=102 у своего
     же заголовка секции. Правую колонку занимает ПЕРВЫЙ содержательный
     ребёнок — тот, ради которого приём и заводился. Всё, что идёт следом,
     разворачивается на обе колонки: соседа слева у него нет. */
  .sec--headside > *:not(.sec__head){ grid-column:2 }
  .sec--headside > *:nth-child(n+3){ grid-column:1 / -1 }
  /* 🔴 Предел ставится на таблицу, а не на ячейку: только он и работает.
     Величина выведена из замера — при 760 px колонка прозы даёт ~70 знаков
     в строке, и самая длинная ячейка сайта (114 знаков) переносится
     на две строки вместо одной. */
  /* ТЗ 41: предел — на прозаическую колонку, а не на таблицу: сжатая таблица
     оставляла треть полосы пустой. */
  .sec--headside table{ max-width:none }
}

/* ==========================================================================
   ТЗ 34, правило 1.1 (корень). ЗАПРЕТ ПЕРЕНОСОВ ЗАЩИЩАЕТ СУММУ, А НЕ ФРАЗУ

   Настоящая причина длинных строк в ячейках найдена замером: у .price-table
   на десктопе всем ячейкам, кроме первой, стоит white-space:nowrap
   (раздел 3 правок 27.08). Предложение «Доступ в квартиру, работающее
   электричество, свободный подход к светильникам и кранам» физически
   не может перенестись — оно и даёт 85 знаков в строку. По той же причине
   не срабатывал и предел ширины: у таблицы min-content равен сумме
   неразрывных строк, 864 px при пределе 760.

   Раздел 35 снял этот запрет на телефоне со словами «десктоп не трогаем:
   там правило работает как задумано». На десктопе оно работает не как
   задумано: задумано было про «5 000 ₽», а действует на любую фразу.
   ТЗ 33 уже признал это для .spec и снял запрет до 1199 px — «каждая сумма
   защищена неразрывностью поштучно». То же самое, но на всех ширинах:
   защита остаётся у .num и у span.u-nowrap, которые и обнимают суммы.
   ========================================================================== */
.price-table td,
.price-table th,
.spec td.calc,
.spec thead th{ white-space:normal }
.price-table td.num,
.price-table .num,
.spec td.num,
.u-nowrap{ white-space:nowrap }
/* 🔴 Заголовок колонки — не сумма. У .spec th.num в шапке стоит «На объекте»,
   и с запретом переносов он вылезал за пояс на 19 px (замер на 768, главная).
   Прежнее правило ТЗ 33 снимало запрет со всей шапки .spec до 1199 px —
   здесь то же самое, но на всех ширинах. */
.price-table thead th,
.spec thead th{ white-space:normal }

/* ==========================================================================
   ТЗ 34, долг 1.3. РЯД ПУНКТОВ ВМЕСТО СТОЛБИКА
   Четыре пункта обвязки стояли столбиком шириной в меру строки, и справа
   от них оставалось 409 px голого фона на 460 px высоты (класс A, 1440).
   Ряд 2×2 занимает колонку целиком.

   🔴 Без хайрлайнов и без вертикальных черт .cols. Пробовали .cols--top:
   его линия сверху попадает на ПЕРВЫЙ пункт списка, и это дефект класса C —
   «граница на краю списка» (замер: 4 срабатывания на четырёх ширинах).
   Разделять пункты нечем и незачем: их разделяют собственные маркеры.
   ========================================================================== */
.ulist{ list-style:none; margin:0; padding:0 }
.ulist > li{ position:relative; padding-left:var(--s-4) }
.ulist > li::before{
  content:""; position:absolute; left:0; top:.62em;
  width:5px; height:5px; border-radius:50%; background:var(--acc);
}
.blk--dark .ulist > li::before{ background:var(--acc-hi) }













/* ==========================================================================
   ТЗ 34, класс A. ЮРИДИЧЕСКИЙ ДОКУМЕНТ: СОДЕРЖАНИЕ СБОКУ, ТЕКСТ В МЕРЕ
   Замер до правки: /policy/ и /soglasie/ — заполненность пояса 48 % на 1440
   и 59 % на 1200 при высоте пояса 10 875 и 4 331 px. Причина простая:
   текст стоит колонкой в 599 px, а пояс 1237 px, и справа от документа
   больше половины полосы пустует. Содержание при этом стояло отдельным
   поясом выше и уезжало с экрана на первом же развороте.
   Правка: один пояс, две колонки. Мера строки текста не меняется —
   её держит .legal{max-width:60ch}, а не ширина колонки.
   ========================================================================== */
@media (min-width:1024px){
  .sec--legal{
    display:grid; align-items:start;
    grid-template-columns:minmax(0,290px) minmax(0,1fr);
    gap:var(--s-4) var(--s-6);
  }
  .sec--legal > *{ min-width:0 }
  .sec--legal.sec--legal > .sec__head.sec__head{ margin-bottom:0; display:block }
  .legal-toc{ position:sticky; top:var(--s-6) }
  /* 🔴 В рельсе 290 px содержание идёт одной колонкой. Правило выше ставит
     ему две от 768 px — в узкой рельсе пункты рвались на четыре строки
     каждый. Ширина колонки, а не брейкпоинт, решает, сколько колонок. */
  .legal-toc .doc-toc{ grid-template-columns:1fr; column-gap:0;
                       max-height:calc(100svh - var(--s-9)); overflow-y:auto }
}

/* ==========================================================================
   ТЗ 34, класс B (долг ТЗ 33). РАНГ В ЮРИДИЧЕСКОМ ДОКУМЕНТЕ

   Осталось 40 дефектов класса B из 50, и все сорок — на /policy/ и /soglasie/:
   пять «монолитов» (по 5–9 абзацев подряд без единого признака структуры,
   1 040–1 758 знаков) и два места, где подряд идут абзацы, начинающиеся
   с жирного — то есть подзаголовок, прикинувшийся текстом.

   🔴 Текст не тронут ни одним словом: это юридические документы.
   Правится только ранг.

   1. Номер пункта уходит на поле. «1.1.», «4.1.2.» были частью первого
      предложения и никакой роли не играли. Вынесенные в левое поле, они
      становятся тем, чем и являются, — рангом: по документу можно вести
      глазом, не читая. Приём стандартный для нормативных текстов.
   2. Основание обработки и категория cookie («Технические.», «Аналитические.»)
      получают свой кегль и свою строку. Раньше они отличались от текста
      только жирностью — этого мало, чтобы читаться как заголовок.
   ========================================================================== */
/* 🔴 Номер помечен классом .num и НЕ помечен <b>. Оба решения замерены:
   в <b> линейка класса B видит «жирное начало абзаца», то есть подзаголовок,
   прикинувшийся текстом (18 срабатываний), — а это не подзаголовок, а маркер.
   Без .num та же линейка видит «монолит»: пять абзацев подряд без единого
   признака структуры. .num — и есть признак: номер пункта, вынесенный
   на поле, ранг документу и даёт. */
.legal__n{
  font-family:var(--f-mono); font-size:var(--t-micro); font-weight:400;
  color:var(--soft); letter-spacing:.04em;
}
.legal p > .legal__t{
  display:block; font-family:var(--f-body); font-weight:600;
  font-size:var(--t-h4); line-height:var(--lh-head); color:var(--ink);
  margin-bottom:var(--s-2);
}
/* На ширине, где поле есть, номер висит слева от абзаца.
   🔴 Не position:absolute и не inline-block. Вынутый из строки номер
   становится БЛОЧНЫМ ребёнком абзаца, и линейка пустоты перестаёт считать
   сам абзац содержательным листом: в отчёт приходили дыры 510×1180
   и 471×1020 там, где текст идёт до самого края. Висячая строка делается
   отрицательным отступом первой строки — номер остаётся частью строки. */
@media (min-width:1024px){
  /* 🔴 :has(> .legal__n) обязателен. Девять абзацев из 89 идут без номера
     (вводные фразы политики и согласия), и висячий отступ у них выглядел бы
     ошибкой: первая строка левее остальных, а вешать в поле нечего. */
  /* Поле — у ВСЕХ абзацев: девять из 89 идут без номера (вводные фразы
     политики и согласия), и без общего поля их левый край уезжал бы
     на 48 px левее остальных. Висячая строка — только у нумерованных:
     вешать в поле нечего, если номера нет. */
  .legal > p{ padding-left:var(--s-7) }
  .legal > p:has(> .legal__n){ text-indent:calc(var(--s-7) * -1) }
  .legal > p > .legal__n{ padding-right:var(--s-2) }
  .legal > p > .legal__t{ text-indent:0 }
}

/* ==========================================================================
   ТЗ 34, класс A. ПЛИТА С ТАБЛИЧКОЙ A5 ДЕРЖИТ СВОЮ ШИРИНУ
   Табличка на входе — готовый к печати текст: каждая строка в нём короткая
   (самая длинная 64 знака) и заканчивается переносом по смыслу, а не по краю
   колонки. Класс .measure резал плиту по 48ch, и справа от неё оставалось
   396 px голого фона на 660 px высоты. Предел ставится плите, а не строке.
   ========================================================================== */
.plate--a5{ max-width:min(100%,600px) }
.plate--a5 p{ max-width:none }

/* ==========================================================================
   ТЗ 34, долг 1.5. ЕЩЁ ОДИН ПОДРЕЗ МОБИЛЬНОГО РИТМА
   Замер после ТЗ 33: секций выше потолка 1,6 экрана на 375 стало 43 вместо 39.
   Половина из них промахивается на считанные пиксели — 1 444, 1 450, 1 461,
   1 465, 1 466, 1 471, 1 473, 1 482 px при потолке 1 440. Текст не режем
   (правило ТЗ), кегли и интерлиньяж не трогаем — работаем шагом.

   Что меняется на ≤767:
     • поле пояса 28 → 24 px: минус 8 px на каждую секцию;
     • отбивка шапки от содержания 24 → 16 px: шапка есть почти у каждой
       секции, это ещё 8 px;
     • общий шаг .u-mt 24 → 16 px.
   Правило ритма ТЗ 31 (внутренние зазоры меньше внешних) держится:
   внутри шапки кикер → заголовок 8 px, заголовок → лид 12 px, шапка →
   содержание 16 px, между блоками 20 px плюс поля пояса.
   ========================================================================== */
@media (max-width:767px){
  :root{
    --sec-py:24px;    /* ТЗ 33 — 28, база — 32 */
  }
  .sec__head{ margin-bottom:var(--s-4) }
  .u-mt{ margin-top:var(--s-4) }
  /* Ряд сносок отбивался от содержания на 32 px — больше, чем сама секция
     отбивается от соседней. Приводим к общему шагу. */
  .notes{ margin-top:var(--s-5) }
  /* Пункт ряда: между пунктами 16 px вместо 24. Линия-разделитель остаётся,
     она и держит границу пункта — зазор ей в помощь не нужен. */
  .numlist{ gap:var(--s-3) }
  .numlist > li{ padding-top:var(--s-3) }
  .steps{ gap:var(--s-3) }
  .steps .step{ padding-top:var(--s-3) }
}

/* ==========================================================================
   ТЗ 34, задача 3, правило «один акцент на секцию»
   Призрачная цифра в шапке — акцент секции: контурный знак в 97 px при теле
   в 15. Ряд .numlist и .steps несёт СВОИ цифры в 52 px, и в секции, где есть
   и то и другое, глаз тянут два приёма сразу: замер нашёл такие на
   /kvartira/, /o-sebe/ и /dogovor/. Побеждает тот, который несёт смысл:
   номера пунктов — это ранг содержания, призрачная цифра — украшение шапки.
   Разметка не трогается: правило системное и сработает само на любой новой
   секции, где номера появятся.
   ========================================================================== */
section:has(.numlist, .steps) .ghost--head{ display:none }

/* Продолжение того же правила. В секции «Кто приедет» на /o-sebe/ два приёма
   одного веса: номера ряда обязательств (52 px) и «2 года» в карточке
   реквизитов (52 px, .num--row). Оба тянут глаз одинаково, и ни один
   не выигрывает. Цифра гарантии остаётся самым крупным в СВОЕЙ карточке,
   но уходит из акцентного диапазона секции: --t-h3 вместо --t-num-sm.
   Порог акцента в линейке — 2,2 кегля тела (15 px), то есть 33 px;
   --t-h3 даёт 27 px и в него не попадает, оставаясь вдвое крупнее
   текста карточки. Правило точечное, .num--row в сводках (.totals__i)
   не трогается. */


/* ============================================================================
   КАНОН ТЗ 35 (02.09.2026)
   ============================================================================
   Один раздел, в котором живут все виды повторяющихся компонентов сайта.
   Правило раздела: вид выбирается КЛАССОМ В РАЗМЕТКЕ. Автоматический выбор
   облика по содержимому (`:has(a)` и подобное) запрещён — именно он развёл
   восемь обликов карточек из трёх задуманных.

   Замер до канона (qa/_tz35-inv-1440.json): 213 карточек, 8 обликов,
   140 из них не нажимаются, и при этом 69 реагировали на наведение
   подъёмом и тенью при `cursor:auto`.

   КАРТОЧКИ — три вида, больше нет.
     .card--link  карточка-ссылка. Нажимается целиком. Подложка, рамка,
                  скругление, поля. Ховер: подъём, тень, акцентная рамка.
                  cursor:pointer. Цель нажатия — вся площадь, а не текст
                  ссылки внутри. Тот же вид у .hub__i — это её реализация
                  в сетке разделов.
     .card:not(.card--link):not(.card--dark)  карточка-факт. Самостоятельный блок, нажимать нечего.
                  Та же подложка, скругление и поля. Ховера нет вообще.
     .card:not(.card--link):not(.card--dark)  строка-пункт. Элемент перечисления внутри секции: без
                  подложки, одна волосяная линия, отступ по её стороне,
                  ховера нет. Ставится, когда пунктов ≥ 4 и они короткие.
                  Сторона линии — направление перечисления: сверху в столбце,
                  слева в ряду. Слева — это .ncard, тот же вид другой стороной.

   В одном ряду все карточки — одного вида. Поля не нулевые везде, где
   рисуется рамка или тень.
   ========================================================================= */

/* ---------- Общая подложка карточки: один рецепт на оба вида ----------
   Карточка-ссылка и карточка-факт стоят на одной подложке. Разница между
   ними — не в облике покоя, а в поведении: у ссылки есть ховер, курсор
   и цель нажатия во всю площадь, у факта нет ничего из этого. */
.card--link,
.card:not(.card--link):not(.card--dark),
.hub__i{
  background:var(--surf); border:1px solid var(--line);
  border-radius:var(--r-card); padding:var(--s-5);
  background-image:none; box-shadow:none;
}
.blk--dark .card--link,
.blk--dark .card:not(.card--link):not(.card--dark){
  background:var(--bg-2); border-color:var(--line-i); color:var(--ink-i);
}
.blk--dark .card--link .card__text,
.blk--dark .card:not(.card--link):not(.card--dark) .card__text{ color:var(--ink-i); opacity:.86 }
.blk--dark .card--link .card__title,
.blk--dark .card:not(.card--link):not(.card--dark) .card__title{ color:var(--ink-i) }
/* 🔴 Голый <p> внутри карточки на тёмном блоке брал --ink из общего правила
   для абзацев: замер дал 1,03 при пороге 4,5 на пяти карточках, которые
   §4.6 перевёл из ряда колонок в карточки-факты. Цвет ставится карточке
   и её прямым абзацам, а не только именованным .card__text/.card__title. */
.blk--dark .card--link > p,
.blk--dark .card:not(.card--link):not(.card--dark) > p,
.blk--dark .card:not(.card--link):not(.card--dark) > p{ color:var(--ink-i); opacity:.9 }
/* .hub__i на тёмном блоке остаётся светлой карточкой — это единственная
   светлая подложка в тёмном поясе, и цвета текста для неё объявлены выше. */
.blk--dark .hub__i{ background:var(--surf); border-color:var(--line) }
/* Раздел, которого ещё нет: своя подложка ему не нужна, состояние сказано
   словами в .card__soon. */
[data-card="soon"]{ background:var(--surf); cursor:auto }
.blk--dark [data-card="soon"]{ background:var(--bg-2) }

/* ---------- Карточка-ссылка ---------- */
.card--link:not([data-card="soon"]),
.hub__i{ cursor:pointer }

/* Вся площадь — цель нажатия. Растягиваем единственную ссылку карточки
   на её коробку. У карточек с несколькими ссылками растяжку снимает
   [data-card="keep"]: перекрытие съело бы клики по остальным. */
.card--link,
.hub__i{ position:relative }
/* Накладка живёт на ::before, а не на ::after: ::after занят стрелкой.
   Абсолютное позиционирование выводит её из потока, поэтому «до текста»
   на вид не влияет. */
.card--link:not([data-card="keep"]) > .card__more::before,
.hub__i:not([data-card="keep"]) > .card__more::before{
  content:''; position:absolute; inset:0; z-index:1; border-radius:inherit;
}
/* Ссылки, оставшиеся под растяжкой, поднимаются над ней. */
.card--link:not([data-card="keep"]) a:not(.card__more){ position:relative; z-index:2 }

/* ---------- ТЗ 36. Пункт .numlist не разваливается от четвёртого абзаца ----
   🔴 Найдено замером высот. Ряд пункта — сетка из двух колонок, номер занимает
   первую и растянут на две строки (`grid-row:span 2`). Пока частей ровно три
   (номер · название · пояснение), всё встаёт как задумано. Четвёртый ребёнок —
   второй абзац пояснения — уходит в третью строку ПЕРВОЙ колонки, а её ширина
   задана по номеру: 1,3em. Замер /kamery-i-zakon/ на 390: абзац шёл 965 px
   в столбик по одному-два слова, и полоса вырастала до 1 482 px при потолке 844.
   Правило чинит любое число абзацев: текст всегда во второй колонке.
   Только на ≤767: на широком ряд пункта раскладывается в три колонки,
   и там четвёртый ребёнок уже забран правилом `:nth-child(n+4){grid-column:1/-1}`
   в конце файла. */
@media (max-width:767px){
  .numlist > li > :nth-child(n+4){ grid-column:2 }
}

/* ---------- ТЗ 36, п. 4. Нажимаемая карточка видна В ПОКОЕ ----------
   Канон ТЗ 35 развёл карточки по поведению, но не по облику покоя: у
   .card--link и .card:not(.card--link):not(.card--dark) одна подложка, одна рамка, один радиус и одни
   поля. Разница включалась только под курсором. Замер ТЗ 36: 51 нажимаемая
   карточка из 264 — и ни одной приметы до наведения. Человек тыкал в первую
   попавшуюся, ничего не происходило, и это читалось как сломанный сайт.

   Признак — стрелка в верхнем правом углу. Она есть ВСЕГДА: это не эффект
   наведения, а свойство карточки. Глиф тот же «→», что у ссылки-перехода
   (.card__more::after) — новых знаков в языке проекта не заводим.
   Ховер не тронут: стрелка не двигается и не меняет цвет.

   Рисуется в ::before самой карточки. ::after занят зерном у .card--dark,
   ::before у .card__more — накладкой цели нажатия; собственный ::before
   карточки свободен.
   Карточка-факт признака не получает никогда — правила ниже её не касаются. */
/* [data-card="fold"] исключена: внутри неё <details>, и её знак покоя — шеврон
   раскрытия (.dpt > summary::after). Стрелка «→» обещала бы переход. */
.card--link:not([data-card="soon"]):not([data-card="fold"]):not(.card--cover)::before,
.hub__i::before{
  content:"→"; position:absolute; z-index:2; pointer-events:none;
  top:var(--s-5); right:var(--s-5);
  font-size:var(--t-small); line-height:1; color:var(--acc);
}
.blk--dark .card--link:not([data-card="soon"]):not([data-card="fold"]):not(.card--cover)::before{ color:var(--acc-hi) }
/* .hub__i на тёмном поясе остаётся светлой карточкой — акцент прежний. */
.blk--dark .hub__i::before{ color:var(--acc) }
@media (max-width:767px){
  .hub__i::before{ top:var(--s-4); right:var(--s-4) }
}
/* Заголовок не заезжает под стрелку: колонка ужимается на её ширину.
   Абзацы ниже проходят свободно — стрелка стоит только в верхней строке. */
.card--link:not([data-card="soon"]):not([data-card="fold"]):not(.card--cover) > .card__title,
.hub__i > .hub__t{ padding-right:calc(var(--s-5) + 2px) }

/* ---------- Карточка-факт: ховера нет вообще ---------- */
.card:not(.card--link):not(.card--dark){ cursor:auto; transition:none }
.card:not(.card--link):not(.card--dark):hover,
.card:not(.card--link):not(.card--dark):active{ transform:none; box-shadow:none; border-color:var(--line) }
.blk--dark .card:not(.card--link):not(.card--dark):hover,
.blk--dark .card:not(.card--link):not(.card--dark):active{ border-color:var(--line-i) }

/* ---------- ТЗ 41. Строка-пункт снята: один облик карточки на сайт.
   Было: .card--item без подложки с одной волосяной линией сверху —
   десятый вид карточки. Теперь обычная карточка. */






/* ---------- Кнопки: один набор на сайт ----------
   Основная (.btn--primary), вторичная (.btn--line, .btn--ghost, .btn--light)
   и ссылка-стрелка (.card__more, .link--cta). Высота, радиус, поля и
   состояния объявлены в §5.5 и G3–G6. Здесь закрываем только то,
   что реагировало, не будучи нажимаемым. */
.card:not(.card--link):not(.card--dark):focus-visible,
.card:not(.card--link):not(.card--dark):focus-visible{ outline:none }

/* ---------- Канон ТЗ 35, §4.5. Сетка даёт столько колонок, сколько обещает ----
   `.grid--3` был `auto-fit`: на 1440 три колонки, на 1600 и 1728 — четыре,
   на 1920 — пять. Замер на /skud/, шесть карточек: 3+3 ровно, потом 4+2
   с пустой правой половиной второго ряда, потом 5+1 — одна карточка
   и четыре пустые колонки. То же у `.hub` и `.tariffs`.
   От 1024 число колонок фиксируется: имя класса и есть договор.
   Ниже 1024 auto-fit остаётся — там он и работает как задумано. */
@media (min-width:1024px){
  .grid--2{ grid-template-columns:repeat(2,minmax(0,1fr)) }
  /* ---------- ТЗ 41. РЯД ЗАПОЛНЯЕТ РЯД ----------
     Требование владельца 04.09.2026: секция симметрична. Пять карточек — это
     3 сверху и 2 снизу, растянутые на ту же ширину; четыре — 3 и одна во всю
     ширину; две — две половины. Ни дыр в ряду, ни карточек разной ширины.

     Механика одна на все трёхколоночные сетки: шесть дорожек, карточка
     занимает две. Остаток ряда делится поровну — одна берёт шесть дорожек,
     две берут по три. Правило по числу детей, а не по содержимому, поэтому
     работает на любом количестве карточек. */
  .grid--3:not(:has(> :nth-child(4):last-child)), .hub:not(:has(> :nth-child(4):last-child)), .tariffs:not(:has(> :nth-child(4):last-child)){ grid-template-columns:repeat(6,minmax(0,1fr)) }
  .grid--3:not(:has(> :nth-child(4):last-child)) > *, .hub:not(:has(> :nth-child(4):last-child)) > *, .tariffs:not(:has(> :nth-child(4):last-child)) > *{ grid-column:span 2 }

  .grid--3:not(:has(> :nth-child(4):last-child)) > :nth-child(3n+1):last-child,
  .hub:not(:has(> :nth-child(4):last-child))    > :nth-child(3n+1):last-child,
  .tariffs:not(:has(> :nth-child(4):last-child))> :nth-child(3n+1):last-child{ grid-column:span 6 }

  .grid--3:not(:has(> :nth-child(4):last-child)) > :nth-child(3n+2):last-child,
  .grid--3:not(:has(> :nth-child(4):last-child)) > :nth-child(3n+1):nth-last-child(2),
  .hub:not(:has(> :nth-child(4):last-child))    > :nth-child(3n+2):last-child,
  .hub:not(:has(> :nth-child(4):last-child))    > :nth-child(3n+1):nth-last-child(2),
  .tariffs:not(:has(> :nth-child(4):last-child))> :nth-child(3n+2):last-child,
  .tariffs:not(:has(> :nth-child(4):last-child))> :nth-child(3n+1):nth-last-child(2){ grid-column:span 3 }

  /* Четырёхколоночные: двенадцать дорожек, карточка занимает три. */
  .grid--4:not(:has(> :nth-child(4):last-child)), .hub--4:not(:has(> :nth-child(4):last-child)){ grid-template-columns:repeat(12,minmax(0,1fr)) }
  .grid--4:not(:has(> :nth-child(4):last-child)) > *, .hub--4:not(:has(> :nth-child(4):last-child)) > *{ grid-column:span 3 }
  .grid--4:not(:has(> :nth-child(4):last-child)) > :nth-child(4n+1):last-child,
  .hub--4:not(:has(> :nth-child(4):last-child))  > :nth-child(4n+1):last-child{ grid-column:span 12 }
  .grid--4:not(:has(> :nth-child(4):last-child)) > :nth-child(4n+2):last-child,
  .grid--4:not(:has(> :nth-child(4):last-child)) > :nth-child(4n+1):nth-last-child(2),
  .hub--4:not(:has(> :nth-child(4):last-child))  > :nth-child(4n+2):last-child,
  .hub--4:not(:has(> :nth-child(4):last-child))  > :nth-child(4n+1):nth-last-child(2){ grid-column:span 6 }
  .grid--4:not(:has(> :nth-child(4):last-child)) > :nth-child(4n+3):last-child,
  .grid--4:not(:has(> :nth-child(4):last-child)) > :nth-child(4n+2):nth-last-child(2),
  .grid--4:not(:has(> :nth-child(4):last-child)) > :nth-child(4n+1):nth-last-child(3),
  .hub--4:not(:has(> :nth-child(4):last-child))  > :nth-child(4n+3):last-child,
  .hub--4:not(:has(> :nth-child(4):last-child))  > :nth-child(4n+2):nth-last-child(2),
  .hub--4:not(:has(> :nth-child(4):last-child))  > :nth-child(4n+1):nth-last-child(3){ grid-column:span 4 }

  /* Две колонки: нечётный остаток берёт ряд целиком. */
  .grid--2 > :nth-child(2n+1):last-child{ grid-column:span 2 }
}
}

/* ---------- Канон ТЗ 35, §4.3. Линию рисует строка, а не каждая ячейка ------
   Замер до правки: /price/ — 246 линейчатых границ на 15 311 px высоты, и 219
   из них давали ячейки: `th, td { border-bottom }` при двух-трёх ячейках
   в строке. Глазом это одна линия на строку, в разметке — три. Линия
   принадлежит строке: рисуем её на <tr>, ячейки границу не держат.
   На ≤767 таблицы уже разложены в карточки строк и рулятся по <tr> —
   правило действует только там, где таблица остаётся таблицей. */
@media (min-width:768px){
  .price-table th, .price-table td,
  .verdict th,     .verdict td,
  .spec th,        .spec td{ border-bottom:0 }

  .price-table thead tr,
  .verdict thead tr,
  .spec thead tr,
  .price-table tbody tr,
  .verdict tbody tr,
  .spec tbody tr{ border-bottom:1px solid var(--line) }

  .price-table tbody tr:last-child,
  .verdict tbody tr:last-child,
  .spec tbody tr:last-child,
  .spec tbody tr.total{ border-bottom:0 }

  .blk--dark .price-table thead tr, .blk--dark .price-table tbody tr,
  .blk--dark .verdict thead tr,     .blk--dark .verdict tbody tr,
  .blk--dark .spec thead tr,        .blk--dark .spec tbody tr{ border-bottom-color:var(--line-i) }

  /* Подстроки расчёта — части одного итога, а не отдельные строки прайса.
     Линия между ними рисовала сетку там, где её смысл — «это одна статья».
     Остаётся линия перед первой подстрокой и перед итогом. */
  .price-table tbody tr.sub:not(:last-child){ border-bottom:0 }
  .spec tbody tr.sub:not(:last-child){ border-bottom:0 }
}

/* ---------- Канон ТЗ 35, §4.10. Кнопка мобильного сворачивания ----------
   Тот же .clp-more, но во всю ширину колонки: на телефоне это единственное
   действие в блоке, и попасть в него пальцем надо с первого раза. */
.clp-more--m{ width:100%; margin-top:var(--s-4) }
/* 🔴 Найдено визуальной приёмкой: на 1440 кнопка мобильного сворачивания
   оставалась видимой. Скрипт снимает её атрибутом hidden, но `display`
   из класса перебивает UA-правило `[hidden]{display:none}` по специфичности.
   Правило возвращает атрибуту силу. */
.clp-more[hidden]{ display:none }












/* ---------- Канон ТЗ 35, §4.9. Почта — такая же цель, как телефон ----------
   Формы на сайте нет, и почта — один из трёх рабочих каналов наравне
   с телефоном и телеграмом. Замер на 375 с эмуляцией касания: 34 ссылки
   ниже 24 px, из них 31 — info@relaid.ru. Строчная ссылка внутри
   предложения из правила WCAG 2.5.8 исключена, и мы её не трогаем:
   раздувать её — рвать строку. А там, где почта стоит ОТДЕЛЬНЫМ
   контактом — в строке реквизитов подвала, в подписи блока связи,
   в реквизитах на /kontakty/, — она обязана быть целью, а не текстом.
   Строку не рвём: inline-block с полями по вертикали. */
@media (pointer:coarse){
  .foot__req-line a,
  .req-line a,
  .tgb__c a,
  .reqs dd a{
    display:inline-block; padding-block:var(--s-2); min-height:24px;
  }
}

/* ---------- Числовая ячейка, в которой не число ----------
   `.spec td.num` — `white-space:nowrap; width:1%`: колонка ужимается
   по цифре. Если в такой ячейке стоит фраза, она вылезает за свою колонку
   и наезжает на соседнюю (найдено визуальной приёмкой ТЗ 35 на главной:
   «работы 14 550 ₽ плюс оборудование»). Модификатор возвращает ячейке
   перенос и обычную ширину; выключка по правому краю остаётся. */
.spec td.num--text, .spec th.num--text,
.price-table td.num--text, .price-table th.num--text{
  white-space:nowrap; width:auto;
}
@media (max-width:767px){
  .spec td.num--text,
  .price-table td.num--text{ min-width:0 }
}

/* ---------- Канон ТЗ 35. Таблица с прозой держит меру строки ----------
   Приём «шапка слева» (§4.4) снят на трёх поясах, и таблицы, которые
   раньше жили в колонке 760 px, растянулись во всю ширину. Замер Range
   на 1440: ячейка «СанПиН требует…» на /biznes/obshchepit/ и «Замки,
   считыватели, кнопки выхода…» на /biznes/sklady/ пошли в 98 знаков
   одной строкой при норме 45–75. Предел ставится на таблицу — он же
   стоял внутри снятого правила. */
/* ТЗ 41: предел ставится не на таблицу, а на её прозаическую колонку.
   Ограничение ширины самой таблицы оставляло четверть полосы пустой —
   владелец читает это как «криво». Таблица идёт во всю полосу, а строка
   в последней колонке держится в мере полем справа. */
@media (min-width:1024px){
  .verdict, .spec{ max-width:none }
  /* 🔴 Поле справа у ЯЧЕЙКИ считается от ширины таблицы и сжимало колонку
     в полоску: /videonablyudenie/ вырастала с 560 до 2037 px. Меру строки
     в таблице держит сама колонка, отдельного правила ей не нужно. */
}

/* ============================================================================
   КАНОН ТЗ 35 · НАВЕДЕНИЕ, НАЖАТИЕ, ФОКУС
   ============================================================================
   ОДИН ПРИНЦИП НА ВЕСЬ САЙТ: **наведение показывает направление перехода.**
   Акцентная линия прочерчивается слева направо, стрелка уезжает вперёд,
   поверхность приподнимается. Это не новый приём: тем же жестом на сайте
   уже растут полосы тарифов и дорожка этапов (`transform-origin:left center`,
   `scaleX`). Ховер просто говорит на языке, который у проекта уже есть.

   ЖЕСТ ОДИН, МАСШТАБА ТРИ:
     строка   — линия прочерчивается под текстом слева направо;
     стрелка  — уезжает вперёд на --arrow;
     плоскость— приподнимается на --lift, берёт тень и акцентную рамку.
   Карточка-ссылка показывает все три сразу: наведение на любую её точку
   зажигает её собственную стрелку. Карточка и стрелка — одно, а не два.

   ЧЕМ РИСУЕТСЯ ЛИНИЯ. Фоновым градиентом в 1 px, а не псевдоэлементом:
   он работает и на строчной ссылке, которая переносится на вторую строку,
   и не занимает ::before / ::after — они заняты накладкой цели нажатия
   и стрелкой.

   ГРАНИЦЫ. Реагирует только то, что нажимается: у .card:not(.card--link):not(.card--dark), .card:not(.card--link):not(.card--dark)
   и .ncard наведения нет вообще (см. раздел «КАНОН ТЗ 35» выше). Движение —
   только настоящей мышью. Длительность, кривая, подъём и нажатие — из токенов,
   ни одного нового числа, кроме --arrow.
   ========================================================================= */

:root{
  --arrow: 3px;   /* сдвиг стрелки при наведении — единственное новое число */
}

/* ---------- Носитель жеста: линия ----------
   ЛИНИЯ РИСУЕТСЯ ПО ФОРМЕ ЦЕЛИ, и это не две разные системы, а одна:
   линия всегда показывает, что именно нажимается.

   а) ЦЕЛЬ-СТРОКА — пункт меню, вопрос FAQ, строка подвала, крошка.
      Нажимается вся строка, поэтому линия прочерчивается по строке
      слева направо фоновым градиентом в 1 px. Ниже — этот список.

   б) ЦЕЛЬ-ТЕКСТ — ссылка-переход, заголовок-ссылка, ссылка в предложении.
      Нажимается текст, и линия обязана повторять его перенос на вторую
      строку. Прочерчивание тут не работает: у блока фон один, и под
      короткой второй строкой линия уехала бы во всю ширину коробки
      (проверено глазом на карточке «Как устроен датчик протечки воды
      и что к нему нужно»). Поэтому там линия — text-decoration,
      и она наливается цветом. Правила — ниже, в пункте 1-бис. */
.notch__dlink,
.notch__link,
.crumbs a,
.foot__list a,
.foot__legal a,
.foot__brand a,
.doc-toc a,
.toc__l a,
.hero__cc-v a,
.faq__q,
.toc__t,
.dpt > summary,
.card__soon a{
  background-image:linear-gradient(currentColor, currentColor);
  background-position:left calc(100% - .02em);
  background-size:0% 1px;
  background-repeat:no-repeat;
  transition:background-size var(--d-base) var(--e-out),
             color var(--d-fast) var(--e-out);
}

/* 1-бис. ЦЕЛЬ-ТЕКСТ: линия наливается под текстом и повторяет перенос.
   Коробка ужимается по содержанию — иначе кольцо фокуса и фон растянутся
   на всю ширину карточки, а нажимается текст. */
.card__more,
.link--cta{ align-self:start }
.card__more{
  text-decoration:underline; text-decoration-color:transparent;
  text-decoration-thickness:1px; text-underline-offset:.35em;
  /* Заливка подчёркивания идёт за --d-fast, вместе с цветом, подъёмом
     карточки и сдвигом стрелки: у наведения на карточку должен быть один
     срок, иначе части жеста приезжают вразнобой. --d-base остаётся
     прочерчиванию по строке — там путь длиннее и его надо видеть. */
  transition:color var(--d-fast) var(--e-out),
             text-decoration-color var(--d-fast) var(--e-out),
             text-decoration-thickness var(--d-fast) var(--e-out);
}

/* ---------- Стрелка ссылки-перехода ----------
   Была символом в тексте: её нельзя было двигать отдельно и экранный диктор
   читал её вслух. Теперь это оформление, а не содержание. */
.card__more::after,
.link--cta::after{
  content:"→"; display:inline-block; margin-left:.4em;
  transition:transform var(--d-fast) var(--e-out);
}

/* ============================ НАВЕДЕНИЕ ============================ */
@media (hover:hover) and (pointer:fine){

  /* 1. Цель-строка: линия прочерчивается слева направо */
  .notch__dlink:hover,
  .notch__link:hover,
  .crumbs a:hover,
  .foot__list a:hover,
  .foot__legal a:hover,
  .foot__brand a:hover,
  .doc-toc a:hover,
  .toc__l a:hover,
  .hero__cc-v a:hover,
  .faq__q:hover,
  .toc__t:hover,
  .dpt > summary:hover,
  .card__soon a:hover,
  /* и когда мышь стоит на карточке — линия её стрелки прочерчивается тоже */
  .card--link:not([data-card="soon"]):hover .card__more,
  .hub__i:hover .card__more{ background-size:100% 1px }

  /* 1-бис. Цель-текст: линия наливается, следуя за переносом */
  .card__more:hover,
  .card--link:not([data-card="soon"]):hover .card__more,
  .hub__i:hover .card__more{ text-decoration-color:currentColor }
  .link--cta:hover{ text-decoration-thickness:2px }

  /* 2. Стрелка уезжает вперёд */
  .card__more:hover::after,
  .link--cta:hover::after,
  .card--link:not([data-card="soon"]):hover .card__more::after,
  .hub__i:hover .card__more::after,
  .btn:hover .btn__ic{ transform:translateX(var(--arrow)) }

  /* 3. Цвет уходит в акцент. На тёмном — в светлый акцент. */
  .card__more:hover,
  .link--cta:hover,
  .notch__link:hover,
  .crumbs a:hover,
  .foot__list a:hover,
  .foot__legal a:hover,
  .doc-toc a:hover,
  .toc__l a:hover,
  .faq__q:hover,
  .toc__t:hover,
  .dpt > summary:hover,
  .card--link:not([data-card="soon"]):hover .card__more,
  .hub__i:hover .card__more{ color:var(--acc) }

  .blk--dark .card__more:hover,
  .blk--dark .link--cta:hover,
  .blk--dark .foot__list a:hover,
  .blk--dark .foot__legal a:hover,
  .blk--dark .doc-toc a:hover,
  .blk--dark .toc__l a:hover,
  .blk--dark .faq__q:hover,
  .blk--dark .toc__t:hover,
  .blk--dark .dpt > summary:hover,
  .notch__dlink:hover,
  .notch__sum:hover,
  .hero__cc-v a:hover,
  .blk--dark .card--link:not([data-card="soon"]):hover .card__more,
  .blk--dark .card--dark.card--link:hover .card__more{ color:var(--acc-hi) }

  /* 4. Строчная ссылка внутри предложения. Линия у неё уже есть — по WCAG
     1.4.1 ссылка в тексте не может отличаться одним цветом. Поэтому жест
     тот же, но линия не прочерчивается, а наливается: толщина и цвет.
     Двигать здесь нечего — строка не должна дрожать. */
  .link,
  .legal a,
  .cookie__txt a,
  .consent a{ transition:color var(--d-fast) var(--e-out),
                         text-decoration-color var(--d-fast) var(--e-out),
                         text-decoration-thickness var(--d-fast) var(--e-out) }
  .link:hover,
  .legal a:hover,
  .cookie__txt a:hover,
  .consent a:hover{ color:var(--acc-hi); text-decoration-thickness:2px }
  .blk--dark .link:hover{ color:var(--acc-hi) }

  /* 5. Плоскость: карточка-ссылка приподнимается, берёт тень и рамку.
     Подъём объявлен в G3, здесь — вид. */
  .card--link:not([data-card="soon"]):hover,
  .hub__i:hover{ border-color:var(--acc); box-shadow:var(--sh-card) }
  .card--dark.card--link:hover{ border-color:var(--acc-hi); box-shadow:var(--sh-inset) }

  /* 6. Кнопки. Подъём вполовину — в G3. Здесь заливка и иконка. */
  .btn--primary:hover{ background:var(--acc); border-color:var(--acc) }
  .btn--line:hover{ border-color:var(--acc); color:var(--acc) }
  .btn--ghost:hover{ border-color:var(--acc); color:var(--acc) }
  .btn--ghost:hover .btn__ic{ background:var(--acc); color:var(--acc-ink) }
  .btn--light:hover .btn__ic{ background:var(--acc) }
  .clp-more:hover{ border-color:var(--acc); color:var(--acc) }
  .blk--dark .clp-more:hover{ border-color:var(--acc-hi); color:var(--acc-hi) }

  /* 7. Служебные цели: город, бургер, шапка меню. */
  .city:hover{ background:var(--line-i) }
  .burger:hover{ background:var(--surf-2) }
  .notch__link:hover{ background:var(--surf-2) }
  .notch__sum:hover{ background:var(--surf-2) }

  /* 8. Логотип: знак загорается акцентом, слово остаётся. */
  .logo__mark{ transition:color var(--d-fast) var(--e-out) }
  .logo:hover .logo__mark{ color:var(--acc-hi) }

  /* 9. Ссылка без класса — всё равно ссылка.
     Замер по 23 страницам нашёл 72 анкера без класса, до которых система
     не доставала: ссылки-заголовки на /price/, контакты в таблице и в списке
     реквизитов на /kontakty/, подвал выпадающего меню, отдельные ссылки
     в прозе. Дальше они закрыты по месту в разметке, а не по имени класса:
     новая ссылка без класса получит жест сама. */
  .notch__dfoot a:hover{ color:var(--acc-hi); background-size:100% 1px }
  .notch__dfoot a{ background-image:linear-gradient(currentColor, currentColor);
                   background-position:left calc(100% - .02em); background-size:0% 1px;
                   background-repeat:no-repeat;
                   transition:background-size var(--d-base) var(--e-out),
                              color var(--d-fast) var(--e-out) }

  /* заголовок-ссылка — цель-текст: заголовок переносится чаще прочих,
     поэтому линия у него тоже следует за текстом */
  :is(h1,h2,h3,h4,h5) > a[href]{ text-decoration:underline; text-decoration-color:transparent;
                                 text-decoration-thickness:1px; text-underline-offset:.18em;
                                 transition:color var(--d-fast) var(--e-out),
                                            text-decoration-color var(--d-fast) var(--e-out) }
  :is(h1,h2,h3,h4,h5) > a[href]:hover{ text-decoration-color:currentColor; color:var(--acc) }
  .blk--dark :is(h1,h2,h3,h4,h5) > a[href]:hover{ color:var(--acc-hi) }

  /* ссылка в строке текста, таблице или списке реквизитов: подчёркивание
     у неё уже есть — наливается, как у .link */
  main :is(p,li,td,th,dd,dt) > a[href]:not([class]),
  .foot :is(p,li,td,dd) > a[href]:not([class]){
    transition:color var(--d-fast) var(--e-out),
               text-decoration-color var(--d-fast) var(--e-out),
               text-decoration-thickness var(--d-fast) var(--e-out);
  }
  main :is(p,li,td,th,dd,dt) > a[href]:not([class]):hover,
  .foot :is(p,li,td,dd) > a[href]:not([class]):hover{
    color:var(--acc-hi); text-decoration-thickness:2px;
  }

  /* 10. Служебные кнопки шторки и скип-ссылка. */
  .notch__close:hover{ background:var(--surf-2); color:var(--acc) }
  .skip:hover{ background:var(--acc); color:var(--acc-ink) }
}

/* ============================ МЕНЬШЕ ДВИЖЕНИЯ ============================
   Линия и стрелка — движение, и оно снимается вместе с остальным.
   Цвет и толщина остаются: это не движение, а состояние. */
@media (prefers-reduced-motion: reduce){
  .card__more{ transition:color 0s, text-decoration-color 0s }
  .notch__dlink, .notch__link, .crumbs a,
  .foot__list a, .foot__legal a, .foot__brand a, .doc-toc a, .toc__l a,
  .hero__cc-v a, .faq__q, .toc__t, .dpt > summary, .card__soon a{
    background-image:none; transition:color 0s;
  }
  .card__more::after, .link--cta::after{ transition:none }
  .card__more:hover::after,
  .link--cta:hover::after,
  .card--link:hover .card__more::after,
  .hub__i:hover .card__more::after,
  .btn:hover .btn__ic{ transform:none }
}

/* ---------- Канон ТЗ 35. Якорь не приклеивает цель к кромке ----------
   Переход по прямой ссылке вида /kvartira/#primer прокруткой не управляем —
   его делает браузер. scroll-margin-top оставляет над целью тот же воздух,
   что и клик по оглавлению (см. anchorOffset в main.js). */
:where([id]){ scroll-margin-top:var(--anchor-off) }
/* У липкой шапки прайс-таблицы своя кромка — цель не должна уезжать под неё. */
.price-wrap--tall [id]{ scroll-margin-top:calc(var(--anchor-off) + var(--s-7)) }

/* ============================================================================
   КАНОН ТЗ 35 · ДОБОРКА МОТОРИКИ
   ============================================================================
   Замер по 23 страницам: что на сайте меняет состояние и переходит ли оно
   плавно. Ниже — те места, где состояние менялось рывком, а рядом такое же
   меняется плавно. Ни одного нового числа: всё из токенов.
   ========================================================================= */

/* Скип-ссылка выпрыгивает по фокусу с клавиатуры. Прыгала скачком —
   единственный элемент сайта, который так себя вёл. */
.skip{ transition:top var(--d-base) var(--e-out),
                  background var(--d-fast) var(--e-out),
                  color var(--d-fast) var(--e-out) }

/* Крест шторки: ховер объявлен, перехода не было. */
.notch__close{ transition:background var(--d-fast) var(--e-out),
                          color var(--d-fast) var(--e-out) }

/* Переключатель города менял фон плавно, а цвет — скачком. */
.city{ transition:background var(--d-fast) var(--e-out),
                  color var(--d-fast) var(--e-out) }

/* Полоса cookie уезжала вниз без растворения — приём есть на оверлее шторки,
   а здесь его не было. */
.cookie{ transition:transform var(--d-base) var(--e-out),
                    opacity var(--d-base) var(--e-out);
         opacity:0 }
.cookie.is-on{ opacity:1 }

/* ---------- Нажатие: ответ есть у всего, что нажимается ----------
   Было только у кнопок, бургера и пунктов меню: ссылка-стрелка и строки
   списков на нажатие не отвечали никак. Ссылку не сжимаем — она мельче
   кнопки и масштаб на ней читается как дрожь; вместо этого стрелка
   доезжает до места нажатия и остаётся там на время касания. */
@media (hover:hover) and (pointer:fine){
  .card__more:active::after,
  .link--cta:active::after{ transform:translateX(var(--arrow));
                            transition-duration:var(--d-press) }
  .clp-more:active{ transform:scale(var(--press));
                    transition-duration:var(--d-press) }
  .faq__q:active,
  .toc__t:active,
  .dpt > summary:active{ opacity:.72; transition-duration:var(--d-press) }
}

/* Кольцо фокуса намеренно НЕ анимируется. Проба: `:focus-visible{transition:
   outline-offset}` — правило состояния заменяет элементу весь transition
   на время фокуса, и ховер под фокусом перестаёт быть плавным. Плавное
   кольцо не стоит сломанного ховера; outline появляется сразу, как и должен. */

/* ---------- Меньше движения — меньше движения ---------- */
@media (prefers-reduced-motion: reduce){
  .skip, .notch__close, .city, .cookie{ transition:none }
  .card__more:active::after,
  .link--cta:active::after,
  .clp-more:active,
  .faq__q:active,
  .toc__t:active,
  .dpt > summary:active{ transform:none; opacity:1 }
}

/* ---------- Канон ТЗ 35. Маркер раскрывашки идёт вместе с содержимым ----------
   Высоту <details> ведёт скрипт, и атрибут open снимается только в конце
   движения — иначе браузер спрячет содержимое раньше, чем оно доедет.
   Но от open зависят маркеры: шеврон FAQ, плюс во врезке и в оглавлении,
   галка в меню. Без этого правила они поворачивались на целое движение позже
   содержимого. .is-closing ставит скрипт в начале закрытия. */
.faq__item.is-closing > .faq__q::after{ transform:none; background-size:11px 1.5px, 1.5px 11px }
.faq__item.is-closing > .faq__q{ color:var(--ink) }
.blk--dark .faq__item.is-closing > .faq__q{ color:var(--ink-i) }
.dpt.is-closing > summary::after,
.toc__d.is-closing > summary::after{ transform:rotate(45deg) translate(-2px,-2px) }
.notch__dir.is-closing > .notch__sum > .notch__chev{ transform:none }

/* ═══════════════════════════════════════════════════════════════════════
   РАЗДЕЛ 27. БЛОГ: лента /blog/ и страница статьи
   Добавлено 03.09.2026 вместе с разделом статей. Только ДОБАВЛЕНИЯ:
   ни одно значение выше не изменено, новых цветов, шрифтов, радиусов
   и кривых не заведено — всё из tokens.css.

   Канон соблюдается как есть:
     · вид карточки задаётся КЛАССОМ (.card--link у ссылки, .card:not(.card--link):not(.card--dark)
       у факта), выбор облика по :has() не применяется;
     · признак нажимаемости в покое — стрелка «→», её рисует общее правило
       .card--link::before из раздела 26, здесь оно не переопределяется;
     · строка ленты — не карточка: у неё своя примета покоя, стрелка справа,
       и она объявлена ниже явно.
   ═══════════════════════════════════════════════════════════════════════ */

/* ---------- 27.1 Обложка в герое ----------
   Обложка — рисунок, а не фотография: пропорция 1200×630 задана жёстко,
   чтобы og:image и картинка на странице были одним файлом. */
.art__cover{ margin:0; min-width:0 }
.art__cover img{
  display:block; width:100%; height:auto; aspect-ratio:1200/630;
  border-radius:var(--r-card); border:1px solid var(--line-i);
}
.art__meta{ font-size:var(--t-small); color:var(--soft-i) }

/* Карточка контактов лежит в левом нижнем углу героя. У статьи левая колонка
   доходит до низа, поэтому оставляем ей место — так же, как это сделано
   на страницах услуг поясом фактов. */
@media (min-width:768px){
  .hero--art .hero__say{ padding-bottom:calc(var(--s-9) + var(--s-5)) }
}

/* ---------- 27.2 Текст статьи ----------
   Рецепт колонки повторяет .legal (раздел 5): та же мера, тот же кегль,
   те же отступы. Отдельный класс нужен, потому что у статьи есть таблицы
   прайса и врезки, которых в юридическом документе нет. */
/* 🔴 Своя мера, уже общей. Замер длины строки (qa/measure.mjs): при
   --measure (60ch) колонка статьи давала p90 = 74–76 знаков — столько же,
   сколько у /policy/, и выше проектной планки 67. Статью читают подряд,
   а не сверяются с ней, поэтому строка у неё короче документа. 52ch дают
   p90 = 65–67. Токен --measure не трогаем: он держит 22 страницы. */
.art{
  --measure-art:52ch;
  max-width:var(--measure-art);
  font-size:var(--t-body); line-height:var(--lh-body); color:var(--ink);
}
.art > * + *{ margin-top:var(--s-4) }
.art p{ color:var(--soft) }
.art strong{ color:var(--ink); font-weight:600 }
.art h2{
  font-family:var(--f-disp); font-weight:500;
  font-size:var(--t-h3); line-height:var(--lh-head);
  margin-top:var(--s-8); text-wrap:balance; scroll-margin-top:var(--anchor-off);
}
.art h2 + p{ margin-top:var(--s-4) }
.art ul,
.art ol{ display:flex; flex-direction:column; gap:var(--s-2); color:var(--soft);
         padding-left:var(--s-5) }
.art ul{ list-style:disc }
.art ol{ list-style:decimal }
.art li::marker{ color:var(--line) }
.art a{ color:var(--acc); text-decoration:underline; text-underline-offset:.18em }
/* Таблица шире колонки текста: у неё своя мера, иначе цена ломается
   пополам. Мобильный режим записей приходит от .price-table (раздел 12.1). */
.art .price-wrap{ max-width:none; width:100%; margin-top:var(--s-5) }
@media (min-width:1100px){
  .art .price-wrap{ width:min(100%, calc(var(--measure-art) + 10rem)) }
}

/* ---------- 27.3 TL;DR ----------
   Блок, который целиком вытаскивает AI-ответ. Кегль на ступень выше тела:
   это первое, что читают, и часто единственное. */
.art__tldr li{ font-size:var(--t-lead); line-height:1.55; color:var(--ink); max-width:50ch }
.art__tldr li strong{ font-weight:600 }

/* ---------- 27.4 Финальный блок из трёх частей ----------
   Три части идут рядом и читаются как одна мысль, поэтому это карточки-факты:
   нажимать в них нечего, и стрелки покоя они не получают. */
.art__final{ list-style:none; padding:0 }
.art__f{ font-size:var(--t-small); line-height:1.6; color:var(--soft) }
.art__f strong{ color:var(--ink) }
.art__f a{ color:var(--acc); text-decoration:underline; text-underline-offset:.18em }
.art__contacts{ max-width:var(--measure) }

/* ---------- 27.5 Соседние карточки ----------
   Обложка внутри карточки-ссылки. Стрелка покоя приходит от .card--link. */
/* 🔴 Обложка показывается ЦЕЛИКОМ. Прежняя редакция держала высоту полосы
   потолком max-height:230px, и при карточке шире 438 px картинка резалась
   по вертикали: у рисунка срезался низ вместе с логотипом. Резать обложку
   нельзя — она нарисована под кадр 1200×630 и в нём же уходит в og:image.
   Высоту полосы теперь держит не картинка, а ширина ряда: .art__rels ниже.
   object-fit оставлен `contain` как страховка — если однажды в карточку
   попадёт файл другой пропорции, он впишется, а не обрежется. */
.art__relimg{
  display:block; width:100%; height:auto; aspect-ratio:1200/630;
  object-fit:contain;
  border-radius:calc(var(--r-card) - 6px); border:1px solid var(--line);
}
/* 🔴 Потолка ширины у полосы «Рядом» НЕТ, и это третья редакция правила.
   Первая резала обложку по высоте. Вторая ограничила ряд 1100 px — и блок
   встал уже заголовка над ним, то есть съехал вбок, оставив треть полосы
   пустой. Правильное решение простое: ряд занимает полосу целиком, как все
   остальные ряды на сайте, а высоту держит то, что карточек всегда две.
   Замер: 801 px на 1440 и 666 на 390 при потолках 900 и 844 — обе линейки
   канона проходят. На 1920 полоса 894, на 2560 — 913: там она формально выше
   900, но 900 — это высота ЭКРАНА эталонного 1440×900, а у экрана 2560
   высота от 1300, и правило «полоса влезает в экран» соблюдается с запасом.
   Обложка при этом ни на одной ширине не режется (qa/_blog-crop.mjs). */
.art__svc .kicker{ margin-bottom:var(--s-1) }

/* ---------- 27.6 Чипы тем ----------
   Без JS это якоря к группам ленты, с JS — фильтр. Оба состояния описаны
   одним обликом: разница только в aria-pressed, за который отвечает скрипт. */
/* ТЗ 41: .chips снят — фильтр тем встал на .btn-row. */
.btn--chip{
  display:inline-flex; align-items:center; gap:var(--s-2);
  padding:var(--s-2) var(--s-4); min-height:34px;
  border:1px solid var(--line); border-radius:var(--r-pill);
  background:var(--surf); color:var(--ink);
  font-family:var(--f-body); font-size:var(--t-small); line-height:1.2;
  text-decoration:none; cursor:pointer;
  transition:border-color var(--d-fast) var(--e-out), background var(--d-fast) var(--e-out);
}
.btn--chip:hover{ border-color:var(--acc); background:var(--surf-2) }
.btn--chip:active{ transform:scale(var(--press)); transition-duration:var(--d-press) }
.btn__n{
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.06em;
  color:var(--soft);
}
.btn--chip[aria-pressed="true"],
.btn--chip[aria-pressed="true"]{ background:var(--btn); border-color:var(--btn); color:var(--acc-ink) }
.btn--chip[aria-pressed="true"] .btn__n,
.btn--chip[aria-pressed="true"] .btn__n{ color:var(--acc-ink); opacity:.72 }

/* ---------- 27.7 Лента ----------
   Строка, а не карточка: 50 карточек на одной странице дали бы стену
   подложек. У строки одна волосяная линия — тот же приём, что у .card:not(.card--link):not(.card--dark)
   в разделе 26. Примета нажимаемости в покое обязательна и здесь: стрелка
   справа, она есть всегда и при наведении не меняется. */
.feed__g + .feed__g{ margin-top:var(--s-7) }
.feed__h{
  font-family:var(--f-disp); font-weight:500; font-size:var(--t-h4);
  display:flex; align-items:baseline; gap:var(--s-3);
  padding-bottom:var(--s-3); scroll-margin-top:var(--anchor-off);
}
.feed__hn{ font-size:var(--t-micro); color:var(--soft); letter-spacing:.12em }
.feed{ display:flex; flex-direction:column; list-style:none; padding:0 }
.feed__i{ border-top:var(--rule) }
.feed__a{
  display:grid; grid-template-columns:auto 1fr auto; align-items:center;
  gap:var(--s-4); padding:var(--s-4) 0; text-decoration:none; color:inherit;
  transition:opacity var(--d-fast) var(--e-out);
}
/* Высота считается от пропорции, а не задаётся числом: 84×44 — это 1,909
   против 1,905 у обложки, и object-fit:cover срезал бы у неё полпикселя
   сверху и снизу. Мелочь, но обрезанных обложек в разделе нет нигде. */
.feed__img{
  width:84px; height:auto; aspect-ratio:1200/630; flex:0 0 auto;
  border-radius:10px; border:1px solid var(--line);
}
.feed__b{ display:flex; flex-direction:column; gap:var(--s-1); min-width:0 }
.feed__t{ font-family:var(--f-disp); font-weight:500; font-size:var(--t-h4); line-height:1.25; color:var(--ink) }
/* 🔴 68ch при кегле 13 px — это 85 знаков в кириллице (см. --measure):
   строка описания в ленте выходила за верхнюю границу нормы 75. 54ch дают 68. */
.feed__d{ font-size:var(--t-small); line-height:1.5; color:var(--soft); max-width:54ch }
.feed__m{ font-size:var(--t-micro); letter-spacing:.1em; text-transform:uppercase; color:var(--soft) }
.feed__go{ color:var(--acc); font-size:var(--t-body); line-height:1 }
.feed__none{ color:var(--soft); font-size:var(--t-body) }
@media (hover:hover) and (pointer:fine){
  .feed__a:hover .feed__t{ color:var(--acc) }
  .feed__a:hover .feed__img{ border-color:var(--acc) }
}
.feed__a:active{ opacity:.72; transition-duration:var(--d-press) }
@media (max-width:560px){
  .feed__img{ width:64px }
  /* На узком экране в ленте остаются заголовок и тема: описание там —
     третья строка мелким кеглем, которую всё равно не читают. */
  .feed__d{ display:none }
  /* То же правило для карточек «Рядом»: это тот же список ссылок, только
     в другой раскладке, и вести себя он должен одинаково. */
  .art__rel .card__text,
  .art__svc .card__text{ display:none }
}
@media (prefers-reduced-motion: reduce){
  .btn--chip:active,
  .feed__a:active{ transform:none; opacity:1 }
}

/* 🔴 Базовое ограничение `main li{max-width:min(100%,--measure)}` (раздел 20)
   режет строку ленты: пункт — это ряд раскладки, а не текстовый поток, и его
   длину задаёт сетка, а не мера строки. Снимаем ровно у него, как это уже
   сделано для .nlist и пунктов прайса. Текст внутри строки меру сохраняет:
   её держат .feed__t и .feed__d. */
.feed__i{ max-width:none }
.feed__g:first-child{ margin-top:0 }
.feed__t{ max-width:44ch }

/* ---------- 27.8 Правая колонка героя статьи ----------
   Обложка и оглавление стоят друг под другом: колонка героя иначе пустует,
   а длинной статье оглавление нужно так же, как длинной странице услуги —
   там оно уже стоит на этом же месте (.hero__aside .toc). */
.hero--art .hero__say{ padding-bottom:0 }
/* Полоса «Коротко»: слева утверждения, справа оглавление статьи. Пропорция
   та же 7/5, что в герое, — вторая на сайте не заводится. */
.art__top{ display:grid; gap:var(--sec-gap) }
@media (min-width:900px){
  .art__top{ grid-template-columns:var(--grid-7-5); gap:var(--s-8); align-items:start }
}
@media (max-width:899px){
  .art__toc{ display:none }   /* на телефоне оглавление — лишний экран */
}
/* Оглавление тёмное и на светлой полосе: подложка --bg-2 нужна ему всегда,
   см. 27.13 — от неё зависит контраст номеров. */
.art__toc .toc__t{ color:var(--ink-i) }
.art__toc .toc__l a{ color:var(--ink-i) }

/* ---------- 27.9 TL;DR в две колонки ----------
   Пять утверждений в одну колонку оставляли половину полосы пустой.
   column-count, а не grid: порядок чтения сверху вниз сохраняется. */
/* Двухколоночный TL;DR снят: правую колонку полосы занимает оглавление. */

/* ---------- 27.10 Герой статьи не тянется в экран ----------
   🔴 Сознательное отступление от правила «первый экран обязан быть экраном»
   (раздел 13.1) и только для блога. Причина: у страницы услуги низ героя
   занят поясом фактов, у статьи занимать его нечем — заголовок, лид и строка
   даты набирают 560–640 px, и min-height:100svh дописывал снизу 200–260 px
   пустого градиента. ТЗ 36 требует убирать пустоту, а не добывать её.
   Правило высоты полосы при этом соблюдается с запасом: герой становится
   ниже, а не выше. Страницы услуг не затронуты — класс новый. */
.hero-wrap--art{ min-height:0 }
.hero-wrap--art > .blk{ grid-template-rows:auto auto }
@media (min-width:768px){
  .hero--art .hero__aside{ padding-bottom:calc(var(--s-9) + var(--s-5)) }
}

/* ---------- 27.11 Карточка услуги в «Рядом» ----------
   Карточка услуги стоит отдельной широкой строкой над парой соседних статей:
   в общем ряду она осталась бы без обложки и тянулась пустотой на высоту
   соседей. Вид — тот же .card--link, включая стрелку покоя из раздела 26. */
.art__svc{ display:grid; gap:var(--s-1) var(--s-5); align-items:baseline }
@media (min-width:768px){
  .art__svc{ grid-template-columns:auto auto 1fr; padding-right:var(--s-8) }
  .art__svc .card__text{ justify-self:end; text-align:right }
}
.art__f p{ margin:0 }

/* ---------- 27.12 Полосы блога в экран телефона ----------
   Замер 390×844: «Что дальше» шла 940 px (+96), «Рядом» — 1001 px (+157).
   Обе полосы правилу подчиняются (исключение выдано только телу статьи
   и ленте), поэтому ужимаются, а не режутся: текст оффера и обе соседние
   статьи остаются на месте целиком. */
@media (max-width:767px){
  .art__final{ gap:var(--s-2) }
  .art__f{ padding:var(--s-3) var(--s-4) }
  .art__contacts{ margin-top:var(--s-5); font-size:var(--t-small); line-height:1.55 }
  .art__f{ line-height:1.55 }
  .art__tldr li{ font-size:var(--t-body) }
  /* На телефоне обложка не снимается, а становится миниатюрой слева —
     ровно как строка ленты. Полноразмерный рисунок 16:9 дважды подряд давал
     360 px и выталкивал вторую карточку за экран; убирать картинку совсем
     тоже неверно, по ней узнают статью. Миниатюра стоит 50 px и решает оба. */
  /* На телефоне обложка стоит над заголовком, уменьшенной. Полноразмерная
     16:9 дважды подряд добавляла к странице четверть экрана, а вариант
     «миниатюра слева» ужимал заголовок до пятистрочной колонки в 146 px.
     240 px по ширине — компромисс: рисунок узнаётся, заголовок дышит.
     Четверть экрана возвращена ниже, на межабзацном интервале. */
  .art__rel{ padding:var(--s-4) }
  .art__relimg{ width:min(240px, 62%) }
  .art__svc{ padding:var(--s-4) }
  /* Воздух перед подзаголовком на телефоне ужат с 64 до 48 px, между
     абзацами — с 16 до 12. У статьи на 1 967 слов это вместе около 400 px,
     то есть полэкрана, набранное одними отступами. Ни одного слова
     не снято, а строка по-прежнему идёт с интерлиньяжем 1,65. */
  .art h2{ margin-top:var(--s-7) }
  .art > * + *{ margin-top:var(--s-3) }
}

/* ---------- 27.13 Оглавление статьи стоит на своей подложке ----------
   🔴 Найдено замером контраста (qa/_contrast-blog.mjs): номера пунктов
   .toc__n набраны --soft-i и на странице услуги лежат в тёмной части
   градиента. У статьи оглавление длиннее и доезжает до ядра градиента —
   восемь номеров дали от 2,1 до 3,3 при пороге 4,5. Цвет номера не трогаем:
   он общий с страницами услуг. Вместо этого даём оглавлению собственную
   тёмную подложку --bg-2, и фон под текстом перестаёт зависеть от того,
   куда попал блок. */
.art__toc{
  background:var(--bg-2); border:1px solid var(--line-i);
  border-radius:var(--r-card); padding:var(--s-4) var(--s-5) var(--s-5);
}
.art__toc .toc__l{ border-top-color:var(--line-i) }

/* ---------- 27.14 Абзац-предупреждение ----------
   В исходниках проекта «внимание» помечается знаком 🔴 в начале абзаца.
   Регламент статей запрещает эмодзи на сайте, а текст править нельзя,
   поэтому шаблон снимает знак и вешает класс: смысл пометки остаётся,
   эмодзи в тексте не появляется. Кромка — акцентом, новых цветов нет. */





/* ═══════════════════════════════════════════════════════════════════════
   РАЗДЕЛ 28. ШТОРКА ВМЕЩАЕТ ВОСЬМОЙ ПУНКТ
   Добавлено 03.09.2026 вместе с пунктом «Блог». Только ДОБАВЛЕНИЯ:
   ни одно значение выше не изменено, палитра и шрифты не тронуты.

   🔴 Дефект был и до блога. Замер топбара по ширинам: на 834 сумма
   логотипа, шторки и переключателя города уже превышала полосу на 12 px,
   на 768 — на 72. Восьмой пункт добавил к этому ещё 60 и сделал перебор
   очевидным: −49 и −109.

   Лечится не переносом пункта в бургер, а плотностью самой шторки:
   в диапазоне 768–1300 поля пунктов ужимаются с 12 до 8 px, зазоры —
   до нуля. Восемь пунктов дают экономию около 120 px, и полоса снова
   встаёт с запасом. Кегль, цвет и высота пунктов не меняются: строка
   остаётся той же, ужимается воздух вокруг неё.
   ═══════════════════════════════════════════════════════════════════════ */
@media (min-width:768px) and (max-width:1300px){
  .notch{ padding-inline:var(--s-1); gap:0 }
  .notch__list{ gap:0 }
}
/* 🔴 Ниже 1024 ужимается и прямой пункт — ровно так же, как в разделе 22.1
   уже ужат выпадающий: те же 4 px полей и тот же кегль. Иначе половина
   шторки набрана одним размером, половина другим, и «Блог» с «Прайсом»
   выглядят крупнее «Жилых объектов». Раздел 22.1 при этом не трогаем:
   правило добавляется рядом, а не переписывает существующее. */
@media (min-width:768px) and (max-width:1023px){
  .notch__link{ padding-inline:var(--s-1); font-size:var(--t-micro);
                font-family:var(--f-body); letter-spacing:0; text-transform:none }
  .topbar{ padding-inline:var(--s-3); gap:var(--s-2) }
}

/* 🔴 768–899: словесный знак уступает место навигации. Даже после ужатия
   восьми пунктов до 4 px полей полоса на 834 не сходилась — не хватало
   29 px, на 768 — 89. Выбор был между «увести навигацию в бургер уже
   с 900» и «снять на планшете слово RELAID, оставив знак». Взято второе:
   навигация на планшете остаётся открытой, а знак «R» — тот же самый
   фирменный вектор, он стоит и в фавиконе, и на обложках статей.
   С 900 слово возвращается. */
@media (min-width:768px) and (max-width:899px){
  .topbar__logo .logo__word{ display:none }
  .topbar{ padding-inline:var(--s-2); gap:var(--s-1) }
  /* На 768 не хватало ещё 17 px. Переключатель города остаётся кнопкой
     с тем же aria-label и тем же действием — прячется только подпись
     «Мск», знак глобуса на месте. С 900 подпись возвращается. */
  .city .city__cur{ display:none }
}

/* ═══════════════════════════════════════════════════════════════════════
   РАЗДЕЛ 29. ЛЕНТА БЛОГА — КАРТОЧКА-ПОСТЕР
   Добавлено 03.09.2026 по решению владельца: список строк на компьютере
   читался как таблица и не держал внимания. Приём взят с блога Tendo
   (соседний проект той же студии) и переложен в язык RELAID: пропорция
   3:4, обложка во всю карточку, поверх неё рубрика и заголовок, снизу
   время чтения и кнопка перехода.

   Что переложено, а что нет. Взяты: пропорция карточки, обложка фоном,
   два затемнения (сверху под заголовок, снизу под подпись), круглая
   кнопка со стрелкой «наружу», рост картинки при наведении. НЕ взято
   раскрытие описания по наведению: канон ТЗ 35 говорит, что наведение
   показывает НАПРАВЛЕНИЕ перехода, а не открывает спрятанный текст.

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

/* ТЗ 41: .bgrid снят — лента блога встала на общую .grid grid--3. */

.card--cover{
  /* ТЗ 41: постер — состояние одной карточки, а не своя система.
     Поля обнуляются: содержимое лежит поверх рисунка. */
  padding:0;
  position:relative; display:block; overflow:hidden;
  aspect-ratio:3/4; border-radius:var(--r-card);
  background:var(--bg-2); border:1px solid var(--line-i);
  text-decoration:none; color:var(--ink-i);
  transition:transform var(--d-base) var(--e-out), box-shadow var(--d-base) var(--e-out),
             border-color var(--d-fast) var(--e-out);
}
.card__img{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
  transition:transform var(--d-slow) var(--e-mask);
}
/* Два затемнения. Сверху — чтобы белый заголовок читался на светлой части
   рисунка, снизу — под время чтения и кнопку. Рисуются псевдоэлементами:
   отдельные пустые <span> в разметке под градиент заводить незачем. */
.card--cover::before,
.card--cover::after{ content:""; position:absolute; left:0; right:0; pointer-events:none; z-index:1 }
.card--cover::before{
  top:0; height:62%;
  background:linear-gradient(to bottom, rgba(8,5,32,.86), rgba(8,5,32,.24) 56%, transparent);
}
.card--cover::after{
  bottom:0; height:44%;
  background:linear-gradient(to top, rgba(8,5,32,.92), rgba(8,5,32,.18) 62%, transparent);
  transition:background var(--d-base) var(--e-out);
}
.card__cap{ position:absolute; top:var(--s-5); left:var(--s-5); right:var(--s-5); z-index:2 }
.card--cover .card__meta{
  display:inline-block; padding-bottom:3px; border-bottom:2px solid var(--acc-hi);
  font-family:var(--f-mono); font-size:var(--t-micro); letter-spacing:.12em;
  text-transform:uppercase; color:var(--soft-i);
}
.card--cover .card__title{
  display:-webkit-box; -webkit-line-clamp:4; -webkit-box-orient:vertical; overflow:hidden;
  margin-top:var(--s-3);
  font-family:var(--f-disp); font-weight:500; font-size:var(--t-h4); line-height:1.25;
  color:var(--ink-i);
}
.card__note{
  position:absolute; left:var(--s-5); bottom:var(--s-5); z-index:2;
  font-size:var(--t-small); color:var(--ink-i); opacity:.82;
}
.card__go{
  position:absolute; right:var(--s-4); bottom:var(--s-4); z-index:2;
  width:40px; height:40px; border-radius:var(--r-pill);
  display:grid; place-items:center; color:var(--ink-i);
  border:1px solid var(--line-i); background:rgba(255,255,255,.07);
  transition:transform var(--d-base) var(--e-spring), background var(--d-fast) var(--e-out),
             border-color var(--d-fast) var(--e-out);
}
.card__go svg{ width:17px; height:17px }

@media (hover:hover) and (pointer:fine){
  .card--cover:hover{ transform:translateY(calc(var(--lift) * -1)); box-shadow:var(--sh-lift);
                border-color:var(--acc-hi) }
  .card--cover:hover .card__img{ transform:scale(1.05) }
  .card--cover:hover::after{
    background:linear-gradient(to top, rgba(8,5,32,.96), rgba(8,5,32,.44) 66%, transparent);
  }
  .card--cover:hover .card__go{ background:var(--acc); border-color:var(--acc) }
}
.card--cover:active{ transform:scale(var(--press)); transition-duration:var(--d-press) }
@media (prefers-reduced-motion: reduce){
  .card--cover, .card__img, .card__go, .card--cover::after{ transition:none }
  .card--cover:active{ transform:none }
}

/* ---------- 29.1 На телефоне постер разворачивается в строку ----------
   🔴 Пятьдесят постеров в одну колонку — это двадцать пять экранов на 390
   при потолке пятнадцать. На телефоне карточка становится тем же, чем была
   раньше строка ленты: миниатюра слева, заголовок справа. Разметка одна
   и та же; обложка меняется на горизонтальную средствами <picture>,
   поэтому и там и там она показывается целиком, без обрезки. */
@media (max-width:767px){
  .bgrid{ display:flex; flex-direction:column; gap:0 }
  .card--cover{
    aspect-ratio:auto; display:grid; grid-template-columns:84px 1fr auto; gap:var(--s-4);
    align-items:center; padding:var(--s-4) 0;
    background:none; border:0; border-top:var(--rule); border-radius:0;
    color:var(--ink);
  }
  .card--cover::before,
  .card--cover::after{ content:none }
  /* 🔴 Ячейкой сетки становится сам <picture>, а не его содержимое.
     display:contents на нём выпускал в сетку и <source>, и <img> — колонок
     оказывалось на одну больше, чем задано, и строка рассыпалась. */
  .card--cover picture{ display:block; width:84px }
  .card__img{
    position:static; width:100%; height:auto; aspect-ratio:1200/630;
    border-radius:10px; border:1px solid var(--line);
  }
  .card__cap{ position:static; display:flex; flex-direction:column; gap:var(--s-1) }
  .card--cover .card__meta{ order:2; border-bottom:0; padding-bottom:0; color:var(--soft) }
  .card--cover .card__title{ order:1; margin-top:0; color:var(--ink); -webkit-line-clamp:3 }
  .card__note{ position:static; display:none }
  .card__go{
    position:static; width:auto; height:auto; border:0; background:none;
    color:var(--acc); justify-self:end;
  }
  .card__go svg{ width:16px; height:16px }
}

/* ═══════════════════════════════════════════════════════════════════════
   РАЗДЕЛ 28. ТЗ 39 (04.09.2026)
   Правки владельца: одинокий текст без формы · пропавший отступ · движение.
   Только ДОБАВЛЕНИЯ и переопределения ниже по каскаду. Ни одного нового
   цвета, шрифта и радиуса. Все значения — из tokens.css.
   ═══════════════════════════════════════════════════════════════════════ */

/* ────────────────────────────────────────────────────────────────────────
   28.1. ПЛАШКА — одна форма на служебный абзац и подводку
   ────────────────────────────────────────────────────────────────────────
   Правка владельца дословно: «куча таких мест, где такой текст, он смотрится
   как-то отрешённо и одиноко и неоформленно… и так на всём сайте!!!»

   Одна болезнь в двух местах полосы, замер 04.09.2026:
     вид А — сноска под рядом карточек, `.sec__note`: 112 мест
             (62 на страницах услуг, 50 в блоге). Висела под карточками
             на волосяной линии, занимала левую половину, справа — пустота
             во всю высоту абзаца;
     вид Б — подводка справа от заголовка, `.sec__head > .lead`: 57 мест.
             Стояла в правой половине полосы, ни к чему не привязана.
   Итого 169. Два из них сняты на /osveshchenie/ (разд. 3 ТЗ 39), остаётся 167.

   РЕШЕНИЕ ВЛАДЕЛЬЦА: вариант «плашка», выбран по снимкам сравнения.
   ОДИН компонент на оба вида, а не два похожих. Скругление, толщина рамки
   и поля — как у карточки рядом: --r-card, 1px --line, --s-5. Это не новая
   форма, а та же подложка, что у .card--link / .card:not(.card--link):not(.card--dark) (раздел «КАНОН
   ТЗ 35»), поэтому сноска и подводка встают в один ряд с карточками над ними.

   🔴 ОТСТУПЛЕНИЕ ОТ БУКВЫ ТЗ, СОГЛАСОВАНО СО СМЫСЛОМ ПРИЁМКИ. ТЗ просит
   на тёмной полосе «полупрозрачную светлую подложку». Здесь она запрещена
   замером: на пике индиго-градиента rgb(116,101,251) даже ЧИСТО БЕЛЫЙ текст
   даёт 4,22 при пороге 4,5 (замер ТЗ 31, /kvartira/). Осветление подложки
   опускает контраст ещё ниже. Плашка на тёмном остаётся на сплошной --bg-2 —
   ровно та подложка, которой уже пользуются .sec__note на тёмном и .plate:
   на ней белила дают 19,2. Рамка — --line-i, как и просит ТЗ.
   ──────────────────────────────────────────────────────────────────────── */

/* Вид А. Сноска садится на плашку во всю ширину полосы.
   Волосяная линия-разделитель снимается: её работу делает сама плашка. */
.sec__note{
  border-top:0;
  background:var(--surf);
  border:1px solid var(--line);
  border-radius:var(--r-card);
  /* 🔴 Поля 16/24, а не 24 по кругу, как у карточки. Причина — правило
     «секция = один экран», оно старше требования «поля как у карточки»:
     при 24 по кругу плашка добавляла полосе 32 px, и восемь полос сайта
     из 645 выходили за 900 px (замер 04.09.2026). Плашка держит один абзац,
     а не заголовок с текстом и ссылкой, поэтому вертикаль ей нужна меньшая.
     Горизонталь — карточная, и левый край текста стоит на той же вертикали,
     что и в карточках над плашкой. */
  padding:var(--s-4) var(--s-5);
  /* 🔴 Мера строки внутри плашки. Плашка идёт во всю ширину полосы (1290 px
     на 1440) — без внутренней меры строка ушла бы за 130 знаков при норме
     45–75. Ограничение ставится ПОЛЕМ СПРАВА, а не max-width: max-width
     сузил бы саму плашку и вернул пустую правую половину, ради снятия
     которой правка и делается. 52ch сохраняются от ТЗ 33: замер Range
     показал, что 52ch дают 72 знака максимум и 61 в среднем. */
  padding-right:max(var(--s-5), calc(100% - 52ch - var(--s-5)));
  max-width:100%;
}
.blk--dark .sec__note{
  /* подложка остаётся сплошной (см. отступление выше), добавляется рамка */
  background:var(--bg-2);
  border:1px solid var(--line-i);
  width:auto;
  max-width:100%;
  padding:var(--s-4) var(--s-5);
  padding-right:max(var(--s-5), calc(100% - 52ch - var(--s-5)));
}
/* 🔴 ПЛАШКА РИСУЕТСЯ ВОКРУГ ТЕКСТА, А НЕ СДВИГАЕТ ЕГО. Полосы сайта после
   ТЗ 36 стоят вплотную к потолку «секция = один экран»: восемь из 645 были
   в 4–69 px от 900. Плашка с полями 16 px сверху и снизу добавляла ровно
   столько, сколько оставалось, и правило падало.
   Поле по блочной оси компенсируется отрицательным полем той же величины:
   текст остаётся ТОЧНО там же, где стоял, расстояние от последней строки
   до следующего блока прежнее, а рамка выходит на 16 px наружу — в тот воздух,
   который и без неё был пустым. Высота полосы не меняется.
   Снизу компенсация ставится только последнему в секции: там под плашкой
   64 px собственного отступа секции, и вылезать некуда. */
  /* ТЗ 41: компенсация полей плашки снята — плашки на лиде и сноске больше нет. */

/* 🔴 Сноска ВНУТРИ карточки формы не просит: карточка её и есть, а плашка
   в плашке читается как вложенная коробка. Одно такое место на сайте —
   /videonablyudenie/, сноска внутри .ncard (замер 04.09.2026: 111 сносок,
   из них 110 стоят прямо в секции или в ряду .notes и одна внутри карточки).
   Ей остаётся прежний вид: хайрлайн сверху и отступ. */
.card > .sec__note,
.blk--dark .card > .sec__note{
  background:none; border:0; border-top:var(--rule); border-radius:0;
  padding:var(--s-4) 0 0; margin-bottom:0; max-width:min(100%, 52ch);
}
.blk--dark .card > .sec__note{ border-top-color:var(--line-i) }

/* Строка реквизитов набрана моноширинным: там ch — ровно один знак,
   и общей меры 52ch ей мало (правило ТЗ 33 сохраняется в новой форме). */
.sec__note.req-line,
.blk--dark .sec__note.req-line{
  max-width:100%;
  padding-right:max(var(--s-5), calc(100% - 75ch - var(--s-5)));
}
@media (max-width:767px){
  .sec__note,
  .blk--dark .sec__note{ padding:var(--s-4); padding-right:var(--s-4) }
}
/* Две сноски подряд на тёмном стояли через --s-3; у плашек шаг общий. */
.blk--dark .sec__note + .sec__note{ margin-top:var(--s-4) }

/* Вид Б. Подводка садится на плашку в правой половине полосы — там, где
   она стоит сейчас. Ниже 900 двухколоночная шапка складывается в столбик,
   правой половины больше нет, и подводка стоит прямо под заголовком:
   там она ни в какой пустоте не висит, форма ей не нужна. */
@media (min-width:900px){
  .sec__head > .lead{
    background:var(--surf);
    border:1px solid var(--line);
    border-radius:var(--r-card);
    padding:var(--s-4) var(--s-5);   /* та же плотность, что у вида А */
    max-width:100%;
  }
  .blk--dark .sec__head > .lead{
    background:var(--bg-2);
    border-color:var(--line-i);
  }
  /* .blk--light — белая полоса: плашка на ней различается рамкой, ровно так же,
     как на ней различается карточка. Отдельного правила не нужно. */

  /* 🔴 Шапка выравнивалась по нижнему краю ряда (align-items:end). У текста
     это читалось как общая базовая линия, у плашки — как ступенька: коробка
     висела нижним краем на строке заголовка. Ряд с плашкой выравнивается
     по верху, как уже выровнены шапки с кикером. */
  .sec__head:not(:has(> .ghost)):has(> h2 + .lead),
  .sec__head:not(:has(> .ghost)):has(> h2 + .measure){ align-items:start }

  /* Та же компенсация, что у вида А: плашка выходит наружу, а заголовок
     и лид остаются на прежних местах — высота шапки не меняется.

     СНИЗУ компенсируется всегда: под шапкой стоят её собственные 32 px
     (`.sec__head{margin-bottom:var(--sec-gap)}`), выходить есть куда.

     СВЕРХУ — только там, где шапка раскладывается в ДВЕ КОЛОНКИ и лид
     начинается на одной высоте с заголовком. Селекторы взяты дословно
     из правил раскладки выше: правило A ТЗ 29, вариант с призрачной цифрой
     (ТЗ 31, правка 6) и `.sec__head--split`. В шапке-столбике лид стоит
     прямо под заголовком, и подтягивать плашку к нему нельзя — рамка села бы
     на строку заголовка.

     🔴 Двойные классы намеренно. Правила раскладки обнуляют лиду поля
     (`> .lead{ margin:0 }`) со специфичностью (0,4,0). Здесь она поднята
     до той же, а решает порядок: раздел 28 идёт последним в файле. */
  /* ТЗ 41: компенсация полей плашки снята — плашки на лиде и сноске больше нет. */
  /* 🔴 Три контекста ниже по каскаду возвращают шапке display:block, и лид
     в них стоит СТОЛБИКОМ под заголовком, хотя селектор двухколоночного
     правила на них срабатывает: `.split--40 > .sec__head` (ТЗ 33),
     `.sec--headside` и `.sec--legal`. Найдено проверкой раскладки
     (`qa/_tz39/overlap.mjs`): четыре плашки наехали на строку заголовка.
     Верхняя компенсация из этих трёх контекстов выведена — там плашка
     стоит на своём поле, как и в шапке-столбике на узком экране. */
  .sec:not(.sec--headside):not(.sec--legal) > .sec__head:not(:has(> .ghost)):has(> h2 + .lead) > .lead.lead,
  .sec:not(.sec--headside):not(.sec--legal) > .sec__head:not(:has(> .ghost)):has(> .kicker + h2 + .lead) > .lead.lead,
  .sec:not(.sec--headside):not(.sec--legal) > .sec__head:has(> .ghost):has(> h2 + .lead) > .lead.lead,
  /* ТЗ 41: компенсация полей плашки снята — плашки на лиде и сноске больше нет. */

  /* Лид, который одновременно нижняя полоса секции, плашку получает один раз
     по правилу вида А — второй рамки на нём не рисуем. */
  .sec__head > .lead.sec__note{ padding:var(--s-4) var(--s-5) }
}

/* ────────────────────────────────────────────────────────────────────────
   28.2. ШАГ ПЕРЕД БЛОКОМ ЗАЯВКИ — общее правило вместо четырёх заплаток
   ────────────────────────────────────────────────────────────────────────
   Правка владельца дословно: «отступы». Ряд карточек кончался, и на том же
   пикселе начинался заголовок заявки: щель 0 px на /videonablyudenie/,
   /biznes/pvz/, /biznes/sklady/, /partneram/.

   Причина: верхний отступ .split получал не сам, а от margin-bottom шапки
   секции. Там, где перед ним стоит безымянный <div>-обёртка с сеткой
   карточек, отдавать было нечему.

   Норма сайта — 32 px: замер шагов между блоками внутри .sec на семи
   страницах дал 32 px 97 раз, 24 px 14 раз, ноль — только в этом дефекте.
   Шаг берётся из --sec-gap, то есть на узких экранах опускается до 24 px
   вместе со всеми остальными шагами секции.
   ──────────────────────────────────────────────────────────────────────── */
.sec > :not(.sec__head) + .split{ margin-top:var(--sec-gap) }

/* ────────────────────────────────────────────────────────────────────────
   28.3. ДВИЖЕНИЕ: ХОВЕРЫ, КНОПКИ, РАСКРЫТИЕ
   ────────────────────────────────────────────────────────────────────────
   Правка владельца дословно: «очень много неплавности на сайте, ховеры,
   кнопки и т. д. Их надо максимально проработать и придумать что-то
   интересное» и уточнение «интересное не по цветам, а по ховерам».

   ЧТО ПОКАЗАЛ ЗАМЕР 04.09.2026 (девять страниц, 934 интерактивных элемента).
   Переход есть у 99 %, и мёртвым сайт делает не это:
     · правил :hover в файле 137, двигают хоть что-нибудь — 6;
     · 1 706 переходов из 2 185 идут за одну и ту же 0,16 с;
     · @keyframes на весь сайт один — бегущая строка брендов;
     · --e-spring объявлена «для кнопок и чипов», а стоит в двух местах;
     · три облика карточки ведут себя по-разному: .card--cover поднимается с тенью
       и пружиной, .hub__i только поднимается, .card--link не двигается вовсе.

   ЗАМОК С ТОКЕНОВ ДВИЖЕНИЯ СНЯТ решением владельца 04.09.2026. Палитра,
   шрифты и радиусы заперты по-прежнему: ни одного нового цвета, гарнитуры
   и радиуса ниже нет — «интересное» набирается движением.

   ОБРАЗЕЦ НЕ ИЗОБРЕТАЕТСЯ: .card--cover владелец видел и принял. Остальной сайт
   поднимается до неё, а не до чего-то нового.

   ГРАНИЦЫ, которые ниже соблюдены построчно:
     · всё новое — под @media (hover:hover) and (pointer:fine): на тач-экране
       ховер залипает, и это уже принято в проекте;
     · prefers-reduced-motion: reduce гасит всё добавленное;
     · движение только у того, что нажимается: .card:not(.card--link):not(.card--dark), .card:not(.card--link):not(.card--dark)
       и .ncard не реагируют ни на что (канон ТЗ 35);
     · ни одно правило не меняет высоту полосы в покое.
   ──────────────────────────────────────────────────────────────────────── */

:root{
  /* ЧЕТВЁРТАЯ СКОРОСТЬ. Три было мало: раскрытие FAQ и складной карточки
     шло за --d-base (0,3 с) — тем же сроком, что и смена цвета у чипа.
     Путь у раскрытия длиннее в разы, и на общем сроке оно выглядит рывком.
     Иерархия теперь полная и читается словами:
       --d-fast 0,16  наведение — реакция под курсором;
       --d-base 0,3   смена состояния — чип, фильтр, кнопка «свернуть»;
       --d-open 0,45  раскрытие — FAQ, складные блоки, шторка меню;
       --d-slow 0,6   появление полосы при прокрутке. */
  --d-open: .45s;
  /* Сдвиг стрелки внутри кнопки. У строчной ссылки --arrow 3 px: там стрелка
     мелкая и стоит в потоке текста. В кнопке она в своём кружке, и 3 px
     на ней не видно — замер глазом на .btn--primary. */
  --arrow-btn: 6px;
}
@media (prefers-reduced-motion: reduce){
  :root{ --d-open:0s }
}

/* ---------- 28.3.1. ОДИН КАНОН КАРТОЧКИ ----------
   Три облика — .card--link, .hub__i, .card--cover — ведут себя одинаково:
   подъём на --lift, тень --sh-lift, акцентная рамка, пружина на подъёме
   и реакция стрелки. Различие остаётся только там, где различается сама
   карточка: у постера блога дополнительно растёт рисунок и заливается
   круглая кнопка — этого у текстовой карточки просто нет.

   Пружина взята у .card--cover: --e-spring на transform. Токен для того и заведён
   («только кнопки и чипы»), и именно на .card--cover владелец его принял. */
@media (hover:hover) and (pointer:fine){
  .card--link:not([data-card="soon"]),
  .hub__i{
    transition:border-color var(--d-fast) var(--e-out),
               box-shadow   var(--d-fast) var(--e-out),
               transform    var(--d-base) var(--e-spring);
  }
  /* Тень была --sh-card — та же, что у карточки в покое на других поясах.
     Подъём с неизменной тенью читается как дрожь: поверхность едет, а её
     отрыв от фона не меняется. --sh-lift — тень поднятой поверхности,
     ровно она стоит у .card--cover. */
  .card--link:not([data-card="soon"]):hover,
  .hub__i:hover{ box-shadow:var(--sh-lift) }

  /* СТРЕЛКА В УГЛУ КАРТОЧКИ ЕДЕТ ВПЕРЁД. До этого она была неподвижной
     приметой покоя (ТЗ 36, п. 4) и под курсором не отвечала — при том что
     точно такая же стрелка у ссылки-перехода внутри той же карточки уезжала.
     Один знак вёл себя двумя способами в одной карточке. */
  .card--link:not([data-card="soon"]):not([data-card="fold"]):not(.card--cover)::before,
  .hub__i::before{ transition:transform var(--d-base) var(--e-spring) }
  .card--link:not([data-card="soon"]):not([data-card="fold"]):not(.card--cover):hover::before,
  .hub__i:hover::before{ transform:translateX(var(--arrow)) }
}
/* Нажатие у карточки уже объявлено в G4 (сжатие слабее кнопки) — не трогаем. */

/* ---------- 28.3.2. КНОПКА ----------
   Было: смена цвета и подъём вполовину. Стрелка в кружке стояла на месте
   у .btn--primary и .btn--light — двигалась только у .btn--ghost, и то
   заливкой, а не сдвигом. Теперь у кнопки тот же жест, что у карточки
   и у ссылки: показать направление перехода. */
@media (hover:hover) and (pointer:fine){
  .btn{ transition:background var(--d-fast) var(--e-out),
                   color        var(--d-fast) var(--e-out),
                   border-color var(--d-fast) var(--e-out),
                   box-shadow   var(--d-fast) var(--e-out),
                   transform    var(--d-base) var(--e-spring) }
  .btn__ic{ transition:transform var(--d-base) var(--e-spring),
                       background var(--d-fast) var(--e-out),
                       color var(--d-fast) var(--e-out) }
  .btn:hover .btn__ic{ transform:translateX(var(--arrow-btn)) }
  /* Кнопка скачивания едет вниз, а не вбок: направление жеста совпадает
     с направлением действия. Признак берётся из разметки — атрибут download,
     а не из класса и не из текста подписи. На сайте такая кнопка одна
     (табличка A5 на /videonablyudenie/), но правило общее: следующая
     получит жест сама. */
  .btn[download]:hover .btn__ic{ transform:translateY(calc(var(--arrow-btn) / 2)) }
}

/* ---------- 28.3.3. РАСКРЫТИЕ ИДЁТ ЗА СВОЮ СКОРОСТЬ ----------
   Маркеры раскрывашек ходили за --d-base, то есть за срок смены состояния.
   Само содержимое при этом едет 0,45 с (js/collapse.js, morph). Маркер
   приезжал раньше содержимого — это и есть «неплавность» на FAQ. */
.faq__q::after{ transition:transform var(--d-open) var(--e-inout),
                           background-size var(--d-open) var(--e-out) }
.dpt > summary::after,
.toc__d > summary::after,
.notch__chev{ transition:transform var(--d-open) var(--e-inout) }

/* ---------- 28.3.4. МИКРОДВИЖЕНИЕ ТАМ, ГДЕ ЕГО НЕ БЫЛО ----------
   Перечень мест взят из правки владельца: раскрытие FAQ и складных карточек,
   строка прайса, чипы тем, крошки, ссылки подвала, плитки объектов.
   Жест везде один и тот же — тот, что объявлен каноном ТЗ 35: показать
   направление перехода. Ничего нового не придумывается. */
@media (hover:hover) and (pointer:fine){

  /* Строка прайса. Двести строк, ни одна не отвечала на курсор — глазу
     не за что зацепиться, когда ведёшь по таблице пальцем сверху вниз.
     Подсветка строки, а не подъём: строка сидит в таблице, поднимать её
     некуда. Фон — --surf-2, тот же, что у зебры и у ховера бургера. */
  .price-table tbody tr{ transition:background var(--d-fast) var(--e-out) }
  .price-table tbody tr:hover{ background:var(--surf-2) }
  .blk--dark .price-table tbody tr:hover{ background:rgba(255,255,255,.04) }
  .spec tbody tr{ transition:background var(--d-fast) var(--e-out) }
  .spec tbody tr:hover{ background:var(--surf-2) }
  .blk--dark .spec tbody tr:hover{ background:rgba(255,255,255,.04) }

  /* Чип темы. Пружина на нажатии у него уже была, но только на :active —
     под курсором чип менял цвет и стоял. Теперь он чуть приподнимается,
     как и всё остальное нажимаемое. --e-spring здесь по прямому назначению:
     токен объявлен «только кнопки и чипы». */
  .btn--chip{ transition:border-color var(--d-fast) var(--e-out),
                    background   var(--d-fast) var(--e-out),
                    color        var(--d-base) var(--e-out),
                    transform    var(--d-base) var(--e-spring) }
  .btn--chip:hover{ transform:translateY(calc(-1 * var(--lift))) }

  /* Крошка. Линия под ней прочерчивалась (канон ТЗ 35), но сама крошка
     не отвечала ничем: это единственная навигация на странице статьи. */
  .crumbs a{ transition:color var(--d-fast) var(--e-out),
                        background-size var(--d-base) var(--e-out) }

  /* Строка ленты блога. Ховер был — цвет заголовка и рамка миниатюры;
     движения не было. Едет стрелка справа: она у строки уже есть в покое. */
  .feed__go{ display:inline-block;
             transition:transform var(--d-base) var(--e-spring) }
  .feed__a:hover .feed__go{ transform:translateX(var(--arrow)) }

  /* Плитка объекта. 🔴 Ховер у неё был неработающим: белая полупрозрачная
     подложка rgba(255,255,255,.04) рисовалась поверх СВЕТЛОГО пояса —
     белое по белому, ноль разницы. Плитки переехали на светлый пояс в ТЗ 36
     (снятие .plate ради высоты полосы), а правило ховера осталось от тёмного.
     Подложка берётся по поясу, как и цвета самой плитки. */










  /* Ссылка подвала. Линия прочерчивается — это канон; здесь добавлен только
     срок, чтобы прочерчивание шло за ту же 0,3 с, что и остальные смены
     состояния, а не за 0,16 с наведения. */
  .foot__list a,
  .foot__legal a{ transition:color var(--d-fast) var(--e-out),
                             background-size var(--d-base) var(--e-out) }

  /* Логотип — единственный интерактив сайта совсем без перехода (замер).
     Цвет знака уже переходит; сама ссылка не отвечала ничем. */
  .logo{ transition:opacity var(--d-fast) var(--e-out) }
  .logo:hover{ opacity:.86 }
  .logo:active{ transform:scale(var(--press)); transition-duration:var(--d-press) }
}

/* ---------- 28.3.5. МЕНЬШЕ ДВИЖЕНИЯ ----------
   Всё, что добавлено выше, снимается целиком. Цвет и подложка остаются:
   это состояние, а не движение. */
@media (prefers-reduced-motion: reduce){
.card--link:not([data-card="soon"])::before,
.hub__i::before,
.btn__ic,
.btn--chip,
.feed__go,
.faq__q::after,
.dpt > summary::after,
.toc__d > summary::after,
.notch__chev,
.logo{ transition:none }
.card--link:not([data-card="soon"]):hover::before,
.hub__i:hover::before,
.btn:hover .btn__ic,
.btn--chip:hover,
.feed__a:hover .feed__go,
.logo:active{ transform:none }
}

























/* 🔴 ПЛИТКА — ССЫЛКА, А ПРИМЕТЫ НАЖИМАЕМОСТИ У НЕЁ НЕТ.
   ТЗ 36, п. 4 дало стрелку «→» в покое всем нажимаемым карточкам сайта,
   и это стало правилом языка: стрелка есть — нажимается. Плитки объектов
   через проверку проехали (правило адресовало .card--link и .hub__i
   по имени класса), и получилось ровно то, чего правило избегало:
   ссылка без приметы рядом с карточками, у которых примета есть.
   Знак тот же «→», цвет тот же акцентный, новых знаков не заводим.
   Стрелка стоит по правому краю посередине, а не в углу: плитка низкая,
   в две строки, и угол у неё занят первой строкой заголовка. */
















/* У растянутой на ряд плитки строки идут в строку, и стрелка встаёт
   за ними — поле справа ей всё равно нужно, оно уже задано выше. */

/* 🔴 ССЫЛКА В ТЕКСТЕ, КОТОРОЙ НЕ ВИДНО.
   Замер 04.09.2026: в прозе 23 ссылки без класса, у семи из них нет ни
   подчёркивания, ни своего цвета — глобальное `a{color:inherit;
   text-decoration:none}` (строка 77) сняло и то и другое, а класс .link
   этим ссылкам не поставили. Читатель видит обычный текст:
   /biznes/ «прайсу за единицу», «прайсе», «условия целиком здесь»,
   /biznes/obshchepit/ «датчики протечки и приводы кранов»,
   /biznes/posutochnaya-arenda/ «установка замка…», «как работают датчики…»,
   /biznes/sklady/ «установка видеонаблюдения».
   Это же нарушение WCAG 1.4.1: различие только цветом уже недостаточно,
   а здесь нет и цвета.

   Канон ТЗ 35 закрыл такие ссылки ПОВЕДЕНИЕМ («ссылка без класса — всё
   равно ссылка», правило 9), но исходил из того, что подчёркивание у них
   уже есть. У семи его не было. Правило даёт им облик .link — тот же
   акцент и то же подчёркивание, ни одного нового значения.

   Охват — только абзац: `main p > a`. Списки намеренно не тронуты, в них
   живут крошки, оглавления и ленты со своим оформлением. */
main p > a[href]:not([class]){
  color:var(--acc); text-decoration:underline;
  text-underline-offset:.18em; text-decoration-thickness:1px;
}
.blk--dark main p > a[href]:not([class]),
.blk--dark p > a[href]:not([class]){ color:var(--acc-hi) }

/* ════════════════════════════════════════════════════════════════════════
   РАЗДЕЛ 30. ОДИН КАНОН ДВИЖЕНИЯ (04.09.2026)

   Правка владельца: «много где ховеры ещё не плавные, и много где их просто
   нет. Главное, чтобы всё было под один канон».

   🔴 Диагноз замером, а не на глаз. Прогон по 33 типам интерактива на восьми
   страницах (курсор наводится, computed style снимается до и после, каждое
   изменившееся свойство сверяется со списком transition-property):
   у пятнадцати типов часть свойств менялась РЫВКОМ — они менялись по :hover,
   но в список перехода не входили. Это и есть «неплавность»: правило
   перехода вроде бы есть, но написано под другие свойства.

   Типовая картина: `transition:color …, background …`, а ховер меняет ещё
   и border-color, и text-decoration-color, и background-size (подчёркивание
   у нас рисуется градиентом). Три из четырёх свойств едут, четвёртое
   прыгает — глаз читает это как дёрганье.

   ЧТО ЗДЕСЬ СДЕЛАНО. Заведён один набор --t-skin: все «шкурные» свойства,
   какие вообще меняются под курсором на этом сайте, с правильными сроками.
   Дальше каждое семейство интерактива объявляет `var(--t-skin)` плюс своё
   движение. Ни одного нового цвета, шрифта или радиуса; сроки и кривые —
   существующие токены.

   ПОЧЕМУ НЕ ОДНИМ ПРАВИЛОМ НА `a, button`. transition — одно свойство:
   позднее объявление затирает раннее целиком. Общее правило снесло бы
   пружину у карточек и сдвиг иконки у кнопок. Поэтому семейства перечислены
   поимённо, и в каждом --t-skin складывается со своим transform.
   ──────────────────────────────────────────────────────────────────────── */

:root{
  /* Наведение — 0,16 с. Подчёркивание прочерчивается за 0,3 с: путь длиннее,
     и на общем сроке линия успевает раньше, чем цвет. */
  --t-skin:
    color                 var(--d-fast) var(--e-out),
    background-color      var(--d-fast) var(--e-out),
    border-color          var(--d-fast) var(--e-out),
    text-decoration-color var(--d-fast) var(--e-out),
    box-shadow            var(--d-fast) var(--e-out),
    opacity               var(--d-fast) var(--e-out),
    background-size       var(--d-base) var(--e-out);
}

@media (hover:hover) and (pointer:fine){

  /* ---------- 30.1. Навигация: пункты шторки и выпадающих списков ----------
     Самое частое взаимодействие на сайте, и до сих пор оно было самым
     дёрганым: подложка и рамка пункта менялись мгновенно. */
  .notch__link,
  .notch__dlink,
  .notch__sum,
  .nav a{ transition:var(--t-skin) }

  /* ---------- 30.2. Раскрывашки ----------
     Сам маркер уже ходит за --d-open (разд. 28.3.3). Здесь — строка вокруг
     него: у неё менялась рамка, и менялась рывком. */
  .faq__q,
  .toc__t,
  .dpt > summary,
  .notch__d > summary{ transition:var(--t-skin) }

  /* ---------- 30.3. Ссылки ----------
     Строку не двигаем — канон ТЗ 35: текст в потоке не должен дрожать.
     Наливается линия, меняется цвет, и оба — плавно. */
  .link,
  .card__more,
  .link--cta,
  .callout__n,
  .crumbs a,
  .foot__list a,
  .foot__legal a,
  .foot__brand a,
  .notch__dfoot a,
  .doc-toc a,
  .toc__l a,
  main p > a[href]:not([class]){ transition:var(--t-skin) }

  /* ---------- 30.4. Кнопки и «показать всё» ----------
     .btn своё движение уже имеет (разд. 28.3.2) — здесь к нему добавляется
     полный набор шкурных свойств, чтобы подчёркивание и рамка не прыгали. */
  .btn{ transition:var(--t-skin), transform var(--d-base) var(--e-spring) }
  .clp-more{ transition:var(--t-skin), transform var(--d-base) var(--e-spring) }
  .clp-more:hover{ transform:translateY(calc(-0.5 * var(--lift))) }

  /* ---------- 30.5. Карточки, плитки, чипы ----------
     Подъём и пружина остаются как объявлены; добавляется тот же набор кожи. */
.card--link:not([data-card="soon"]),
.hub__i,
.card--cover{ transition:var(--t-skin), transform var(--d-base) var(--e-spring) }
  .btn--chip{ transition:var(--t-skin), transform var(--d-base) var(--e-spring) }

  /* ---------- 30.6. Строка ленты «Статьи по теме» ----------
     🔴 Единственный нажимаемый элемент, который на курсор не отвечал ничем:
     стрелка справа уезжала, а сама строка стояла. Подсветка строки, как
     у строки прайса, — поднимать её некуда, она сидит в списке. */
  .feed__a{ transition:var(--t-skin) }
  .feed__a:hover{ background:var(--surf-2) }
  .blk--dark .feed__a:hover{ background:rgba(255,255,255,.04) }

  /* ---------- 30.7. Логотип ----------
     Правило перехода у него появилось в ТЗ 39, но покрывало только opacity;
     цвет знака при этом менялся рывком. */
  .logo,
  .logo__mark,
  .foot__brand .logo{ transition:var(--t-skin) }
}

/* ---------- 30.8. Меньше движения ----------
   Цвет и подложка остаются: это состояние, а не движение. Снимается только
   то, что едет. --d-fast и --d-base здесь уже равны нулю (tokens.css),
   поэтому --t-skin гасится сам; отдельно снимаем подъём. */
@media (prefers-reduced-motion: reduce){
  .clp-more:hover{ transform:none }
}

/* ════════════════════════════════════════════════════════════════════════
   РАЗДЕЛ 31. ЛЕНТА БЛОГА НА ТЕЛЕФОНЕ (04.09.2026)

   Решение владельца: вариант А — миниатюру убрать, заголовок дать целиком.

   Замер и довод. На 390 миниатюра занимала 84 px по ширине. Обложки
   у нас схематические: один узел линией на градиенте. В 84 px рисунок
   не читается вообще — это цветное пятно, и никакой информации оно
   не несёт. При этом плата за пятно двойная: 84 px ширины плюс зазор
   отнимались у заголовка, и он обрезался многоточием на третьей строке
   («Беспроводная или проводная защита от протечек: что…»). То есть картинка,
   которую нельзя разглядеть, съедала слова, которые нужно прочитать.

   Вариант Б (поднять миниатюру до 120–140 px) отклонён: рисунок стал бы
   различим, но не узнаваем, а заголовку осталось бы ещё меньше места.

   На компьютере лента не меняется: там постер 3:4 работает как задумано.
   ──────────────────────────────────────────────────────────────────────── */
@media (max-width:767px){
  /* Колонка миниатюры уходит из сетки. Ряд остаётся тем же: заголовок
     и стрелка перехода. */
  .card--cover{ grid-template-columns:1fr auto }
  .card--cover picture{ display:none }

  /* Заголовок больше не обрезается: места хватает на любую длину.
     -webkit-line-clamp снимается целиком, вместе со свойствами, которые
     его включают, — иначе браузер оставляет многоточие на последней
     видимой строке даже при большом значении clamp. */
  .card--cover .card__title{
    display:block;
    -webkit-line-clamp:none;
    -webkit-box-orient:horizontal;
    overflow:visible;
  }
}

/* ════════════════════════════════════════════════════════════════════════
   РАЗДЕЛ 32. ЖИВАЯ СМЕТА ПОКАЗЫВАЕТСЯ ЦЕЛИКОМ (04.09.2026)

   Задача владельца: «сделай так, чтобы выглядело приятно для клиента».

   Что было. На `/kvartira/` полоса «Двушка в Москве, 50 устройств, 214 964 ₽»
   держала таблицу сметы в кармане: max-height 62vh, внутренняя прокрутка,
   нижняя кромка растворяется. Из четырнадцати строк на экране было шесть,
   и итог — та самая сумма, ради которой полоса и сделана, — оставался
   за кромкой. Клиент видит обрезанную таблицу и должен догадаться, что
   внутри неё есть своя прокрутка.

   🔴 Почему карман здесь больше не нужен. Его завели в ТЗ 20.4, чтобы полоса
   влезала в экран. Позже ТЗ 36 объявило прайс-таблицу ИСКЛЮЧЕНИЕМ из правила
   высоты — «`.price-table` всегда». То есть основание, ради которого карман
   ставили, отпало, а карман остался. Полоса и без него правилу не нарушает.

   На телефоне карман сохраняется: там каждая строка раскладывается в блок,
   четырнадцать строк дают больше двух с половиной экранов, и прокрутка
   внутри таблицы — меньшее зло, чем страница длиной в лифт.
   ──────────────────────────────────────────────────────────────────────── */
@media (min-width:768px){
  .price-wrap--pocket{
    max-height:none; overflow-y:visible;
    -webkit-mask-image:none; mask-image:none;
    padding-bottom:0;
  }
  /* Липкая шапка нужна была прокрутке внутри кармана. Прокрутки нет —
     шапка едет вместе с таблицей, как у всех остальных таблиц сайта. */
  .price-wrap--pocket thead th{ position:static }
}

/* ---------- 32.1. Короткие таблицы на телефоне тоже показываются целиком ----
   🔴 Замер /price/ на 390: лестница объектов — пять строк — стояла в кармане
   48svh и обрезалась ровно на последней строке, «Быстрый старт в готовую
   квартиру». Клиент видит таблицу цен, у которой отрезан низ; растворяющаяся
   кромка приходится на середину строки и читается как сломанная вёрстка.

   Причина: класс `--tall` вешается с пяти строк, а порог в комментарии
   к правилу назван восьмым. На телефоне строка таблицы раскладывается
   в блок из трёх строк текста, поэтому пять строк = пятнадцать строк
   на экране — и они не влезают в 48svh.

   Правило: карман на телефоне остаётся только у таблиц, которым его дали
   осознанно, то есть у помеченных ещё и `--pocket` (десять строк и больше).
   На сайте такая одна — живая смета двушки на пятнадцать строк. Все
   остальные показываются целиком; полосы с прайс-таблицей и так выведены
   из правила высоты (ТЗ 36, исключение «прайс-таблица»). */
@media (max-width:767px){
  .price-wrap--tall:not(.price-wrap--pocket){
    max-height:none; overflow-y:visible;
    -webkit-mask-image:none; mask-image:none;
    padding-bottom:0;
  }
  .price-wrap--tall:not(.price-wrap--pocket) thead th{ position:static }
}

/* ════════════════════════════════════════════════════════════════════════
   РАЗДЕЛ 33. ПЛАШКА ОБНИМАЕТ СВОЙ ТЕКСТ (04.09.2026)

   🔴 БАГ, ИЗ-ЗА КОТОРОГО АБЗАЦ РАССЫПАЛСЯ ПО ОДНОМУ СЛОВУ В СТРОКУ.

   Разд. 28.1 держал меру строки не через max-width, а полем справа:
       padding-right: max(var(--s-5), calc(100% - 52ch - var(--s-5)))
   Замысел понятен — плашка во всю ширину полосы, а текст внутри в 52ch,
   чтобы справа не было пустой половины. Ошибка в том, что процент
   в padding считается от ширины СОДЕРЖАЩЕГО БЛОКА, а не от ширины самого
   элемента. Пока абзац занимает всю ширину секции, числа совпадают.
   Как только он оказывается уже своего контейнера — а так стоит сноска
   на `/bystryy-start/` — 100% берётся от широкого контейнера, поле справа
   выходит больше самой плашки, и колонка текста схлопывается в два пиксела.

   Замер 04.09.2026, `/bystryy-start/`, 1920: ширина плашки 1108 px,
   padding-right 1082 px, колонка текста 2 px, высота абзаца 525 px
   вместо 60. На экране это столбик из одного слова в строке.

   ЧТО ЗДЕСЬ СДЕЛАНО. Никаких вычислений в полях. Плашка получает обычные
   симметричные поля и max-width по своей мере: 52ch текста плюс два поля.
   Плашка становится ровно такой ширины, какой нужен её тексту, — и это же
   снимает вторую жалобу владельца: плашка больше не растягивается во всю
   полосу с пустой правой половиной внутри.
   ──────────────────────────────────────────────────────────────────────── */
.sec__note,
.blk--dark .sec__note{
  padding:var(--s-4) var(--s-5);
  max-width:min(100%, calc(52ch + var(--s-5) * 2));
}
/* Строка реквизитов набрана моноширинным: там ch — ровно один знак,
   и общей меры ей мало. */
.sec__note.req-line,
.blk--dark .sec__note.req-line{
  padding:var(--s-4) var(--s-5);
  max-width:min(100%, calc(75ch + var(--s-5) * 2));
}
/* Сноска внутри карточки формы не просит — правило разд. 28.1 сохраняется. */
.card > .sec__note,
.blk--dark .card > .sec__note{
  padding:var(--s-4) 0 0; max-width:min(100%, 52ch);
}
@media (max-width:767px){
  .sec__note,
  .blk--dark .sec__note{ padding:var(--s-4); max-width:100% }
}
/* Подводка в шапке секции — та же плашка, та же мера. */
@media (min-width:900px){
  .sec__head > .lead.sec__note,
  .sec__head > .lead{ padding:var(--s-4) var(--s-5) }
}

/* ---------- ТЗ 41. Примечание под блоком: один слот вместо девяти ----------
   Было: .note-b с кромкой слева, .noterow, хвосты .u-mt/.muted — двадцать
   способов сказать «сноска». Стало: .sec__foot, один слот в конце секции.
   Кромок нет: 92 одиночные вертикальные линейки сняты (ТЗ 41, §4.3).
   Значения — только из tokens.css, новых нет. */
.sec__foot{
  margin-top:var(--s-5);
  font-size:var(--t-small); line-height:var(--lh-body); color:var(--soft);
  /* ТЗ 41: сноска — та же плашка, что у подводки и карточки (решение
     владельца в ТЗ 39: «текст смотрится отрешённо и неоформленно»).
     Подложка, рамка, скругление и поля — карточные, чтобы сноска стояла
     в одном ряду с карточками над ней, а не висела серым текстом. */
  background:var(--surf);
  border:1px solid var(--line);
  border-radius:var(--r-card);
  padding:var(--s-4) var(--s-5);
}
.blk--dark .sec__foot{ background:var(--bg-2); border-color:var(--line-i) }
.sec__foot > :first-child{ margin-top:0 }
.sec__foot > :last-child{ margin-bottom:0 }
.sec__foot > p{ margin:0 }
.sec__foot > p + p{ margin-top:var(--s-3) }
.sec__foot strong,
.sec__foot b{ color:var(--ink); font-weight:600 }
.blk--dark .sec__foot{ color:var(--soft-i) }
.blk--dark .sec__foot strong,
.blk--dark .sec__foot b{ color:var(--ink-i) }
/* 🔴 Мера строки вешается на АБЗАЦ хвоста, а не на сам хвост. На хвосте она
   резала всё, что внутри: сетка из пяти плиток на главной оказалась зажата
   в текстовую колонку. Ограничивать надо строку, а не контейнер. */
/* Плашка идёт во всю ширину полосы: иначе справа остаётся пустота, ради снятия
   которой она и делалась. Мера строки ограничивает АБЗАЦ внутри неё, а не саму
   плашку, поэтому строка действий с кнопками стоит в ряд, а не в столбик.
   🔴 Сноска всегда обёртка с абзацем внутри, а не голый <p>: у голого работало
   общее правило меры для main p, и плашка ужималась до трети полосы. */
.sec__foot{ max-width:100% }
@media (min-width:900px){
  .sec__foot > p{ max-width:min(100%, 52ch) }
}

/* Карточка внутри пункта списка тянется на всю ячейку сетки: иначе соседки
   по ряду стоят разной высоты, хотя сами ячейки выровнены. */
.grid > li{ display:grid }
.grid > li > .card{ height:100% }

/* ---------- ТЗ 41, §4 (телефон). Карточка-текст на телефоне без коробки ----
   На 390 поля карточки съедают 48 px — 12 % ширины, и абзац рассыпается
   на лишние строки: замер 04.09.2026 дал семь полос выше экрана при норме
   ноль (правило ТЗ 36). Коробку на телефоне держит только то, что нажимается
   (.card--link) и постер блога (.card--cover). Остальное — блок с линией
   сверху: та же одна система карточек, другое её изложение на узком экране.
   Линия горизонтальная: вертикальных кромок на сайте нет. */
@media (max-width:767px){
  .card:not(.card--link):not(.card--cover){
    background:none; background-image:none; box-shadow:none;
    border:0; border-radius:0;
    border-top:1px solid var(--line);
    padding:var(--s-3) 0 0;
  }
  .blk--dark .card:not(.card--link):not(.card--cover){ border-top-color:var(--line-i) }
  .card:not(.card--link):not(.card--cover):first-child{ border-top:0; padding-top:0 }
  /* Тот же шаг у зазора сетки: карточки уже разделены линией, 24 px между
     ними — второй разделитель поверх первого. */
  .grid{ gap:var(--s-3) }
}

/* ---------- ТЗ 41. Список «термин — значение» внутри карточки ----------
   Карточка сама даёт поля и подложку, поэтому строки списка внутри неё
   идут плотнее и кеглем подписи: иначе на телефоне полоса /o-sebe/
   вырастала до 887 px при потолке 844 (правило ТЗ 36). */
.card .reqs > div{ padding-block:var(--s-1) }
.card .reqs dd{ font-size:var(--t-small) }

/* ══════════════════════════════════════════════════════════════════════════
   ТЗ 41. СИММЕТРИЯ: БЛОК ЗАНИМАЕТ ШИРИНУ СЕКЦИИ ЦЕЛИКОМ
   ══════════════════════════════════════════════════════════════════════════
   Требование владельца 04.09.2026 дословно: «всё должно быть максимально
   симметрично» — и это не только про карточки, но про текст и сноски.

   Приём один: длину строки держит ПОЛЕ СПРАВА, а не сжатие блока. Блок стоит
   во всю ширину полосы, строка внутри него не длиннее меры. Сжатие блока
   давало пустую правую половину — ровно то, что читается как «криво».
   Значения — из tokens.css, новых нет.
   ────────────────────────────────────────────────────────────────────────── */
@media (min-width:560px){
  [data-sec] > .measure,
  [data-sec] > .muted,
  [data-sec] > .lead,
  [data-sec] > .sec__note,
  [data-sec] > .brands__k,
  [data-sec] > p{
    max-width:100%;
    padding-right:max(0px, calc(100% - var(--measure)));
  }
}

/* Таблица занимает полосу целиком: сводка на 980 px в полосе 1237 оставляла
   четверть полосы пустой. */
.verdict, .spec, .price-table, .dtable{ width:100% }

/* Кнопка «показать ещё» — действие, а не блок: её ширина по содержимому,
   и в правило симметрии она не входит. */

/* Ячейка сетки не подчиняется мере строки: ширину ей задаёт ряд, а не текст.
   Без этого безымянный <div> в сетке упирался в --measure (598 px) и ряд
   не заполнялся — замер на 900 и 768 (ТЗ 41). Мера остаётся у текста внутри. */
.grid > *, .hub > *, .feed > *, .tariffs > *, .objgrid > *{ max-width:none }

/* Карточки ряда начинаются на одной линии: собственный верхний отступ
   элемента внутри сетки обнуляется, иначе соседки съезжают на 12 px
   (замер /o-sebe/ 04.09.2026). */
.grid > *, .hub > *, .feed > *, .tariffs > *{ margin-top:0 }

/* ---------- ТЗ 41. Ряд заполняет ряд и на средних ширинах ----------
   От 768 до 1023 сетки двухколоночные. Механика та же, что от 1024:
   четыре дорожки, элемент занимает две, одинокий последний берёт все четыре. */
@media (min-width:768px) and (max-width:1023px){
  .grid--3:not(:has(> :nth-child(4):last-child)), .grid--4:not(:has(> :nth-child(4):last-child)), .hub:not(:has(> :nth-child(4):last-child)), .hub--4:not(:has(> :nth-child(4):last-child)), .tariffs:not(:has(> :nth-child(4):last-child)){ grid-template-columns:repeat(4,minmax(0,1fr)) }
  .grid--3:not(:has(> :nth-child(4):last-child)) > *, .grid--4:not(:has(> :nth-child(4):last-child)) > *, .hub:not(:has(> :nth-child(4):last-child)) > *, .hub--4:not(:has(> :nth-child(4):last-child)) > *, .tariffs:not(:has(> :nth-child(4):last-child)) > *{ grid-column:span 2 }
  .grid--3:not(:has(> :nth-child(4):last-child)) > :nth-child(odd):last-child,
  .grid--4:not(:has(> :nth-child(4):last-child)) > :nth-child(odd):last-child,
  .hub:not(:has(> :nth-child(4):last-child))    > :nth-child(odd):last-child,
  .hub--4:not(:has(> :nth-child(4):last-child)) > :nth-child(odd):last-child,
  .tariffs:not(:has(> :nth-child(4):last-child))> :nth-child(odd):last-child{ grid-column:span 4 }
  .grid--2 > :nth-child(odd):last-child{ grid-column:1 / -1 }
}

/* ---------- ТЗ 41. Число колонок задаётся, а не подбирается ----------
   auto-fit подбирал колонки по ширине карточки, и на средних экранах ряд
   заполнялся частично: два элемента по 227 px в полосе 533 px. Число дорожек
   объявляется на каждом рубеже явно — тогда ряд всегда заполнен.
   Ниже 768 всё в одну колонку. */
@media (max-width:767px){
  .grid, .grid--2, .grid--3, .grid--4, .hub, .hub--4, .tariffs{
    grid-template-columns:minmax(0,1fr);
  }
  .grid > *, .hub > *, .tariffs > *{ grid-column:auto }
}
@media (min-width:768px) and (max-width:1023px){
  .grid--2, .grid--3, .grid--4, .hub, .hub--4, .tariffs{
    grid-template-columns:repeat(4,minmax(0,1fr));
  }
  .grid--2 > *, .grid--3 > *, .grid--4 > *,
  .hub > *, .hub--4 > *, .tariffs > *{ grid-column:span 2 }
  .grid--2 > :nth-child(odd):last-child,
  .grid--3 > :nth-child(odd):last-child,
  .grid--4 > :nth-child(odd):last-child,
  .hub    > :nth-child(odd):last-child,
  .hub--4 > :nth-child(odd):last-child,
  .tariffs> :nth-child(odd):last-child{ grid-column:span 4 }
}

/* ---------- ТЗ 41. Плашка в плашке не рисуется ----------
   Замер /bystryy-start/ 04.09.2026: сноска-обёртка получила подложку, и обе
   вложенные сноски нарисовали свои — рамка в рамке, каждая в треть полосы
   при пустой правой половине. Подложку держит ВНЕШНИЙ блок; вложенные
   становятся обычными абзацами во всю его ширину, а меру строки им задаёт
   он же. Правило общее: на любой глубине рисуется одна плашка. */
.plate .plate,
.plate .card,
.card .card,
.card .plate,
.sec__foot .card,
.sec__foot .sec__foot,
.sec__foot .sec__note,
.sec__foot .plate,
.plate .sec__note,
.plate .sec__foot,
.card .sec__note,
.card .sec__foot{
  background:none; border:0; border-radius:0; padding:0; box-shadow:none;
}
.sec__foot .measure,
.sec__foot .sec__note,
.sec__foot .muted,
.plate .measure,
.plate .sec__note,
.card .measure{ max-width:none }

/* Разделительная линия внутри плашки идёт во всю её ширину: мера строки
   резала её до 519 px в блоке 556 px — короткая линия читается как перекос
   (замер ТЗ 41, 30 страниц). Длину строки держит текст, линию — блок. */
main .tgb__g{ max-width:none }

/* ---------- ТЗ 41. Шапка секции всегда сверху ----------
   Боковой заголовок оставлял под собой пустую колонку во всю высоту
   содержания — от 158 до 1069 px на четырнадцати блоках (замер 04.09.2026).
   Раскладка снята: шапка сверху, содержание под ней во всю полосу.
   .sec__stack — тот же столбик, что и у обычной секции. */
.sec__stack{ display:grid; gap:var(--sec-gap) }
.sec__stack > *{ max-width:100% }

/* Пояснение пункта было ограничено 50ch — оно ставилось, когда список жил
   в узкой колонке рядом с боковым заголовком. Колонки больше нет, список идёт
   во всю полосу, и 50ch оставляли справа две трети пустоты. Верхняя граница
   нормы строки — 75 знаков, её и берём. */
main .numlist__x, main div.numlist__x > p{ max-width:min(100%, 75ch) }

/* ---------- ТЗ 41. Одна мера строки на сайт: 75 знаков ----------
   Было три предела разом — 50ch у пояснения пункта, 52ch у сноски, 56ch
   у общего текста, — и каждый оставлял свою полосу пустоты справа. Норма
   строки 45–75 знаков; берём её верх, одинаково для всех текстовых блоков
   во всю ширину полосы. Колонки и карточки живут по своей ширине. */
main .sec__foot > p,
main [data-sec] > ul:not([class]) > li,
main [data-sec] > ol:not([class]) > li,
main .marks li,
main .ulist li{ max-width:min(100%, 75ch) }
/* У прямых детей полосы предел ставится ПОЛЕМ: сам блок остаётся во всю
   ширину, иначе возвращается пустая правая часть, ради снятия которой
   всё и делалось. */
@media (min-width:560px){
  main [data-sec] > .measure,
  main [data-sec] > .lead,
  main [data-sec] > .muted,
  main [data-sec] > .sec__note,
  main [data-sec] > .brands__k,
  main [data-sec] > p{
    max-width:100%;
    padding-right:max(0px, calc(100% - 75ch));
  }
}
