/* =========================================================
   ДИЗАЙН-СИСТЕМА — белый/синий коммерческий B2B-каталог
   Референс: kazmehanika.kz / kzm.kz / shtabeler.kz + структура zaklepkapro.ru
   Меняй токены здесь — поменяется весь сайт. БЕЗ логотипов/бренд-графики.
   ========================================================= */
:root{
  --bg:        #FFFFFF;   /* фон страницы */
  --surface:   #F4F7FB;   /* светлые секции / подложки */
  --surface-2: #ECF1F8;
  --ink:       #131A26;   /* основной текст */
  --muted:     #5B6677;   /* вторичный текст */
  --line:      #E3E9F1;   /* границы карточек, таблиц, разделителей */
  /* Граница ОРГАНА УПРАВЛЕНИЯ — поля ввода, кнопки-контуры, пагинация,
     переключатель порядка, стрелки карусели. Отдельный токен, потому что
     требование к нему другое: у карточки есть картинка и заголовок, по ним её
     и узнают, а поле ввода узнают только по рамке. Замер 03.08.2026: `--line`
     даёт 1,31…1,43 к фону на всех четырёх палитрах при пороге 3, `--muted` —
     5,14…6,44. Берём существующий токен палитры, новых цветов не выдумываем. */
  --line-control: var(--muted);
  --line-2:    #EEF2F7;

  --brand:     #0B5FD6;   /* фирменный синий — акцент/кнопки */
  --brand-700: #0A4FB0;
  --brand-900: #073A86;   /* тёмная подложка хедера/футера */
  --brand-50:  #EAF1FD;   /* светлая заливка акцента */

  --accent:    #FF6B1A;   /* тёплый акцент: цена/хит/акция */
  --accent-50: #FFF1E7;
  /* Цвет текста НА акцентной заливке. Белое на оранжевом даёт контраст 2.85 —
     ниже порога даже для крупного текста, кнопка «Открыть каталог» читалась
     плохо. Замер по WCAG: тёмный текст на этом же оранжевом — 6.12. */
  --on-accent: var(--ink);
  /* Зелёный «В наличии» на белом давал 3.38 — для мелкой подписи мало.
     #0E7A43 даёт 5.41 при том же смысле цвета. */
  --ok:        #0E7A43;
  --star:      #FFB400;

  --font-display: 'Manrope', system-ui, sans-serif;
  --font-body:    'Inter', system-ui, sans-serif;
  --font-mono:    'IBM Plex Mono', monospace;

  /* Подвал — единственное тёмное место сети, и он был задан цветом напрямую:
     на палитрах «Цех» и «Сталь» оставался синим и спорил со всей страницей.
     Теперь это токены, каждая палитра задаёт свои. */
  --footer-bg:      #0C1626;
  --footer-text:    #9CA7BC;
  --footer-muted:   #7A879E;
  --footer-line:    #1E2A40;
  --footer-bottom:  #7A879E;   /* было #5C6987 — контраст 3.30, ниже порога */
  --footer-mark:    #5B9BFF;

  --radius:    14px;
  --radius-sm: 9px;
  --shadow:    0 8px 30px rgba(16,42,90,.09);
  --shadow-sm: 0 2px 10px rgba(16,42,90,.06);
  --container: 1240px;
}

*{margin:0;padding:0;box-sizing:border-box;}
html{scroll-behavior:smooth;}
body{
  font-family:var(--font-body);
  color:var(--ink);
  background:var(--bg);
  line-height:1.55;
  font-size:1rem;
  /* Длинное слово не должно вылезать за свой блок. Русские составные слова
     («грузоподъёмность», «Транспортными») при крупном шрифте браузера
     перерастали ячейку сетки преимуществ и утаскивали страницу вбок на 3 px.
     `break-word` срабатывает только когда иначе будет перелив, поэтому на
     обычном тексте ничего не меняет. */
  overflow-wrap:break-word;
  -webkit-font-smoothing:antialiased;
}
img{max-width:100%;display:block;}
a{color:inherit;text-decoration:none;}
ul{list-style:none;}
button{font-family:inherit;cursor:pointer;border:none;background:none;}
input,textarea{font-family:inherit;}
.container{max-width:var(--container);margin:0 auto;padding:0 24px;}
.blueprint-bg{background:var(--bg);}

h1,h2,h3,h4,h5{font-family:var(--font-display);letter-spacing:-.02em;line-height:1.12;font-weight:var(--h-weight,800);}

/* -------- eyebrow / служебная подпись -------- */
.eyebrow{
  display:inline-flex;align-items:center;gap:9px;
  font-family:var(--font-mono);font-size:0.75rem;font-weight:600;
  letter-spacing:.14em;text-transform:uppercase;color:var(--brand);
  margin-bottom:14px;
}
.eyebrow::before{content:'';width:22px;height:2px;background:var(--accent);border-radius:2px;}

/* -------- размерный разделитель (тонкая сигнатура) -------- */
.dim-divider{display:flex;align-items:center;gap:16px;margin:0 0 30px;color:var(--muted);
  font-family:var(--font-mono);font-size:0.75rem;letter-spacing:.12em;text-transform:uppercase;}
.dim-divider .tick{width:2px;height:12px;background:var(--accent);flex-shrink:0;border-radius:2px;}
.dim-divider .seg{flex:1;height:1px;background:var(--line);}
.dim-divider .label{white-space:nowrap;font-weight:600;}

/* =========================================================
   КНОПКИ
   ========================================================= */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  padding:13px 24px;border-radius:var(--radius-sm);font-weight:700;font-size:0.9375rem;
  transition:transform .12s, box-shadow .15s, background .15s, color .15s, border-color .15s;
  white-space:nowrap;line-height:1;
}
.btn-primary{background:var(--brand);color:#fff;box-shadow:0 6px 18px var(--brand-shadow, rgba(11,95,214,.28));}
.btn-primary:hover{background:var(--brand-700);transform:translateY(-2px);box-shadow:0 12px 26px var(--brand-shadow, rgba(11,95,214,.34));}
.btn-accent{background:var(--accent);color:var(--on-accent);box-shadow:0 6px 18px var(--accent-glow,rgba(255,107,26,.3));}
.btn-accent:hover{transform:translateY(-2px);box-shadow:0 12px 26px var(--accent-glow,rgba(255,107,26,.36));filter:brightness(1.03);}
.btn-outline{background:#fff;color:var(--ink);border:1.5px solid var(--line-control);}
.btn-outline:hover{border-color:var(--brand);color:var(--brand);}
/* Кнопка-призрак была задумана для тёмного блока: белый текст на белой
   полупрозрачной заливке. На светлой странице она превращалась в невидимку —
   контраст подписи 1,04 при пороге 4,5. Ловилось это на странице 404, где
   так выглядели ОБА пути наружу: «На главную» и «Весь каталог». Страница,
   которая существует ради спасения заблудившегося, не показывала ему выхода.

   Умолчание перевёрнуто: по умолчанию кнопка тёмная и годится на светлом
   фоне, а белый вариант включается там, где под ней действительно темно.
   Так следующее использование на светлом фоне сломается не молча. */
.btn-ghost{background:transparent;color:var(--ink);border:1.5px solid var(--muted);}
.btn-ghost:hover{background:var(--surface);border-color:var(--ink);}
.hero .btn-ghost,
.cta-final .btn-ghost,
footer.site-footer .btn-ghost{
  background:rgba(255,255,255,.14);color:#fff;border-color:rgba(255,255,255,.35);
}
.hero .btn-ghost:hover,
.cta-final .btn-ghost:hover,
footer.site-footer .btn-ghost:hover{background:rgba(255,255,255,.22);border-color:rgba(255,255,255,.55);}
.btn-block{width:100%;}
/* Кнопки с подставляемым текстом. У `.btn` стоит `white-space:nowrap`, и это
   верно, пока подпись задана в шаблоне: «Подробнее» переносить незачем. Но
   там, где внутрь кнопки попадает чужая строка — название раздела, подпись из
   site.yml или _common.yml, телефон, — ширина кнопки становится непредсказуемой
   и страница едет вбок. За три цикла это ловилось трижды поодиночке
   (кнопки первого экрана, кнопки карточки, кнопка «Все позиции раздела»);
   здесь перечислены все такие места разом, найденные разбором шаблонов.
   Правило узкое намеренно: делать перенос у всех кнопок — значит менять вид
   там, где сейчас всё в порядке. */
.hero .cta-row .btn,          /* подписи из site.yml (hero.cta_*) */
.seo-block .btn-ghost,        /* «Все <название раздела> →» */
.lead-form button[type="submit"],  /* подпись из _common.yml */
.pd-actions .btn,             /* телефон сайта */
.pd-context .btn,             /* «Все позиции раздела „…“» */
.to-order__actions .btn{      /* блок «под заказ» */
  white-space:normal;max-width:100%;
}
/* Кнопка на время отправки: помечена aria-disabled, а не disabled (иначе
   теряется фокус). Вид у неё должен быть тот же, что у выключенной. */
.btn[aria-disabled="true"]{opacity:.7;cursor:progress;}
.btn-sm{padding:10px 16px;font-size:0.875rem;}

/* =========================================================
   COOKIE BANNER
   ========================================================= */
.cookie-banner{
  position:fixed;left:24px;right:24px;bottom:24px;z-index:200;max-width:var(--container);margin:0 auto;
  background:var(--ink);color:#fff;padding:16px 22px;border-radius:var(--radius);box-shadow:var(--shadow);
  display:flex;flex-wrap:wrap;gap:16px;align-items:center;justify-content:space-between;font-size:0.875rem;
  transform:translateY(160%);transition:transform .35s cubic-bezier(.2,.8,.2,1);
}
.cookie-banner.is-visible{transform:translateY(0);}
.cookie-banner a{text-decoration:underline;}
.cookie-banner button{background:var(--brand);color:#fff;padding:10px 22px;border-radius:var(--radius-sm);font-weight:700;font-size:0.875rem;flex-shrink:0;}

/* =========================================================
   ШАПКА
   ========================================================= */
.topbar{background:var(--surface);border-bottom:1px solid var(--line);color:var(--muted);font-size:0.8125rem;}
/* min-height, а не height: если строка всё же не помещается, пусть полоса
   вырастет, а не спрячет текст под шапку. */
.topbar .container{display:flex;justify-content:space-between;align-items:center;min-height:40px;flex-wrap:wrap;gap:8px;}
.topbar a{color:var(--ink);}
.topbar a:hover{color:var(--brand);}

header.site-header{background:#fff;border-bottom:1px solid var(--line);position:sticky;top:0;z-index:100;}
.header-main{display:flex;align-items:center;gap:24px;height:80px;}
/* max-width обязателен: у логотипа flex-shrink:0, поэтому его базовая ширина
   равна max-content — то есть подзаголовку в одну строку. У «СверлоПром»
   подзаголовок «Сверлильные станки: настольные, радиальные, координатные»
   занимает 357 px, и на телефоне шапка распирала страницу на всех её
   страницах разом. С max-width подзаголовок переносится. */
.logo{font-family:var(--font-display);font-weight:800;font-size:1.4375rem;letter-spacing:-.03em;flex-shrink:0;max-width:100%;min-width:0;display:flex;flex-direction:column;line-height:1;color:var(--ink);}
.logo b{color:var(--brand);}
/* Подпись под названием. Селектор именно `.logo-text > span`, а не
   `.logo span`: название сайта тоже обёрнуто в span, и по общему селектору
   оно получало стиль подписи — становилось мелким и серым. */
.logo .logo-text > span{font-family:var(--font-body);font-weight:500;font-size:0.6875rem;letter-spacing:.02em;color:var(--muted);text-transform:none;margin-top:4px;}
.logo > span:not(.logo-text){font-family:var(--font-body);font-weight:500;font-size:0.6875rem;letter-spacing:.02em;color:var(--muted);text-transform:none;margin-top:4px;}
/* Знак рядом с названием. Колонка с названием и подписью остаётся прежней —
   поэтому .logo из колонки становится строкой, а прежняя колонка уезжает
   внутрь .logo-text. Иначе подпись встала бы справа от знака отдельным
   столбцом и разъехалась с названием. */
.logo-mark{flex-direction:row;align-items:center;gap:10px;}
.logo-mark img{width:38px;height:38px;flex-shrink:0;border-radius:8px;}
.logo-text{display:flex;flex-direction:column;line-height:1;min-width:0;}
.footer-brand .logo-mark img{border-radius:8px;}

/* поиск в шапке */
.header-search{flex:1;max-width:520px;display:flex;border:1.5px solid var(--line-control);border-radius:var(--radius-sm);overflow:hidden;background:#fff;transition:border-color .15s, box-shadow .15s;}
.header-search:focus-within{border-color:var(--brand);box-shadow:0 0 0 4px var(--brand-50);}
/* Поле забирает остаток строки, кнопка держит свою ширину. min-width:0 на
   поле обязателен: по умолчанию элемент во flex не сжимается уже своего
   содержимого, поле упиралось в собственный размер и выдавливало кнопку за
   край — при overflow:hidden на рамке от неё оставалось «Найт»
   (поймано владельцем 28.08.2026 на мостовых кранах и сверлильных). */
.header-search input{flex:1 1 0;min-width:0;border:none;padding:12px 12px;font-size:0.9062rem;outline:none;background:transparent;}
.header-search button{flex:0 0 auto;white-space:nowrap;background:var(--brand);color:#fff;padding:0 18px;font-weight:700;font-size:0.875rem;}
.header-search button:hover{background:var(--brand-700);}

nav.main-nav{}
nav.main-nav > ul{display:flex;gap:24px;align-items:center;}
nav.main-nav > ul > li > a{font-weight:600;font-size:0.9062rem;color:var(--ink);padding:6px 0;border-bottom:2px solid transparent;transition:color .15s,border-color .15s;}
nav.main-nav > ul > li > a:hover{color:var(--brand);border-color:var(--brand);}

/* Переносим, а не сжимаем. Замер 04.08.2026 на 320 px при кегле 24: подпись
   «WhatsApp» занимает 85 px, и ряд действий вылезал за экран — прокрутка вбок
   на всех четырнадцати проверенных страницах. Уменьшать подпись нельзя: она
   растёт вместе с кеглем не просто так, это настройка человека. Пусть ряд
   переносится на вторую строку — места по вертикали в шапке хватает. */
.header-actions{display:flex;align-items:center;gap:18px;flex-wrap:wrap;
  justify-content:flex-end;min-width:0;}
/* Кнопки мессенджеров: логотип и подпись под ним (владелец, 04.08.2026).
   Появляются, только когда в sites/<slug>/integrations.yml вписаны настоящие
   номера; пока там заглушка, этих элементов в разметке нет вовсе.
   Viber не рисуется ни в каком виде — в нише не используется.

   Цвет значка — фирменный цвет самой службы, и только у значка. Так его
   узнают боковым зрением, не читая подписи; при этом кнопка целиком остаётся
   второстепенной: главное действие шапки — «Заявка», и перетягивать внимание
   мессенджеры не должны. Поэтому фон и рамка нейтральные, а не зелёный с
   синим во всю кнопку. */
.msg-row{display:flex;gap:8px;}
.msg-btn{display:inline-flex;flex-direction:column;align-items:center;gap:3px;
  padding:6px 10px;border:1.5px solid var(--line-control);
  border-radius:var(--radius-sm);color:var(--ink);text-decoration:none;
  line-height:1.1;white-space:nowrap;}
.msg-btn:hover{border-color:var(--brand);}
.msg-btn:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.msg-ico{display:block;}
/* Значок — блочный: у inline-svg внизу остаётся щель под базовую линию, и
   подпись отъезжала на пиксель ниже, чем у соседней кнопки. */
.msg-ico svg{display:block;width:22px;height:22px;fill:currentColor;}
.msg-whatsapp .msg-ico{color:#25D366;}
.msg-telegram .msg-ico{color:#26A5E4;}
.msg-cap{font-size:0.75rem;font-weight:600;letter-spacing:0.01em;}
.msg-lg .msg-btn{padding:12px 18px;gap:6px;}
.msg-lg .msg-ico svg{width:32px;height:32px;}
.msg-lg .msg-cap{font-size:0.875rem;}
.msg-note{margin-top:8px;color:var(--muted);font-size:0.875rem;}
.footer-msg{margin-top:6px;}
/* В подвале фон тёмный, а .msg-btn красит подпись в --ink. Замер 04.08.2026:
   контраст подписи к фону подвала 1,00 — текст был не «плохо виден», а не
   виден вовсе. Берём цвет самого подвала и его же светлую рамку. */
footer.site-footer .msg-btn{color:var(--footer-text);
  border-color:rgba(255,255,255,.30);}
footer.site-footer .msg-btn:hover{border-color:rgba(255,255,255,.65);}
footer.site-footer .msg-cap{color:#fff;}
@media (max-width:600px){
  .msg-btn{padding:5px 8px;}
  .msg-ico svg{width:20px;height:20px;}
  .msg-cap{font-size:0.6875rem;}
}
.header-phone{font-family:var(--font-display);font-weight:800;font-size:1rem;color:var(--ink);}
.header-phone small{display:block;font-family:var(--font-body);font-weight:500;font-size:0.6875rem;color:var(--brand);margin-top:2px;}
.burger{display:none;flex-direction:column;gap:5px;width:28px;}
.burger span{height:2px;background:var(--ink);border-radius:2px;}

/* secondary nav-row (каталог-меню) под основным хедером */
.nav-row{background:#fff;border-bottom:1px solid var(--line);}
/* Меню переносится, а не прокручивается вбок. Горизонтальная прокрутка внутри
   страницы читается как поломка вёрстки, а половина пунктов оказывается за
   краем экрана — на мостовых кранах из пяти разделов видно было три. Перенос
   показывает все пункты сразу и не требует от человека догадываться, что
   строку можно тянуть. */
.nav-row .container{display:flex;flex-wrap:wrap;gap:10px 26px;align-items:center;
  min-height:50px;padding-top:12px;padding-bottom:12px;}
.nav-row a{font-size:0.875rem;font-weight:600;color:var(--muted);white-space:nowrap;}
.nav-row a:hover{color:var(--brand);}

/* =========================================================
   HERO — синий градиент, текстовый, с поиском (как kazmehanika)
   ========================================================= */
.hero{
  position:relative;color:#fff;overflow:hidden;
  /* Градиент героя идёт от токенов бренда, иначе он остаётся синим на всех
     палитрах и спорит с остальной страницей. */
  background:linear-gradient(135deg,var(--brand) 0%,var(--brand-700) 55%,var(--brand-900) 100%);
  padding:74px 0 84px;
}
.hero::before{ /* техническая точечная сетка — атмосфера, не декор */
  content:'';position:absolute;inset:0;opacity:.22;pointer-events:none;
  background-image:radial-gradient(rgba(255,255,255,.5) 1px, transparent 1.4px);
  background-size:26px 26px;
}
.hero::after{ /* мягкое свечение акцента */
  content:'';position:absolute;right:-120px;top:-120px;width:420px;height:420px;border-radius:50%;
  background:radial-gradient(circle,var(--accent-glow,rgba(255,107,26,.35)),transparent 65%);pointer-events:none;
}
.hero .container{position:relative;z-index:1;}
.hero-inner{max-width:820px;}
/* hero с фото станка справа */
.hero-grid{display:grid;grid-template-columns:1.06fr .94fr;gap:48px;align-items:center;}
.hero-grid .hero-inner{max-width:none;}
.hero-photo{background:#fff;border-radius:18px;padding:22px;box-shadow:0 24px 60px rgba(4,28,70,.4);}
.hero-photo img{width:100%;aspect-ratio:4/3;object-fit:contain;border-radius:10px;}
@media (max-width:920px){ .hero-grid{grid-template-columns:1fr;} .hero-photo{display:none;} }
.hero .eyebrow{color:#CFE0FF;}
.hero .eyebrow::before{background:var(--accent);}

/* Палитра «Чертёж» держится на одном цвете: --accent там равен --brand. А
   градиент героя идёт от --brand — значит главная кнопка первого экрана
   оказывалась ровно того же цвета, что фон под ней. Замер 03.08.2026:
   контраст кнопки к фону 1,00 при пороге 3 для границы интерактивного
   элемента. Кнопку выдавала только белая подпись, самой кнопки видно не было.
   То же со штрихом надзаголовка.

   Меняем не палитру, а её поведение на тёмном фоне: белая кнопка с
   тёмно-синей подписью — 11,27 в обе стороны, второго цвета не появляется.
   Замер по остальным палитрам: «Цех» 7,67 — в порядке; «Кобальт» 2,39 и
   «Сталь» 2,91 формально ниже 3, но там кнопка отличается ТОНОМ (медный и
   красный на синем и графитовом), а не только светлотой, и вопрос цвета —
   к владельцу, а не к вёрстке. Правим только случай совпадения цветов.

   Правило узкое — только кнопки в ряду призыва. Первая редакция цикла 90
   ловила ЛЮБУЮ .btn-accent внутри героя, и кнопка «Найти» в поисковой строке
   первого экрана стала белой на белой же плашке: контраст 1,00. Нашла это
   проверка контраста (tools/audit_contrast.py) на первом же прогоне —
   глазами я свою правку принял. */
body.theme-blueprint .hero .cta-row .btn-accent,
/* Тот же случай в нижнем блоке призыва: он залит синим независимо от палитры,
   и тёмно-синяя кнопка «Чертежа» давала на нём 1,94. Нашла проверка контраста
   уже после правки героя — то есть одним местом дело не ограничивалось. */
body.theme-blueprint .cta-final .btn-accent{background:#fff;color:var(--brand);border-color:#fff;}
body.theme-blueprint .hero .cta-row .btn-accent:hover,
body.theme-blueprint .cta-final .btn-accent:hover{background:#EDF1F8;color:var(--brand-900);}
body.theme-blueprint .hero .eyebrow::before{background:#CFE0FF;}
.hero h1{font-size:3.125rem;font-weight:800;margin-bottom:18px;color:#fff;}
.hero p.lead{font-size:1.125rem;color:#D7E4FB;max-width:680px;margin-bottom:28px;}
.hero-search{display:flex;max-width:640px;background:#fff;border-radius:12px;padding:7px;box-shadow:0 18px 40px rgba(4,28,70,.35);margin-bottom:22px;}
.hero-search input{flex:1;border:none;outline:none;padding:14px 18px;font-size:1rem;color:var(--ink);background:transparent;}
.hero-search .btn{flex-shrink:0;}
.hero .cta-row{display:flex;gap:14px;margin-bottom:36px;flex-wrap:wrap;}
.trust-row{display:flex;flex-wrap:wrap;gap:14px;}
.trust-item{background:rgba(255,255,255,.1);border:1px solid rgba(255,255,255,.18);border-radius:12px;padding:14px 18px;min-width:150px;}
.trust-item b{display:block;font-family:var(--font-display);font-weight:800;font-size:1.375rem;color:#fff;margin-bottom:2px;}
.trust-item span{color:#BFD3F5;font-size:0.8125rem;}

/* чипы быстрых категорий под hero */
.hero-cats{margin-top:30px;display:flex;flex-wrap:wrap;gap:10px;}
.hero-cats a{background:rgba(255,255,255,.12);border:1px solid rgba(255,255,255,.22);color:#fff;
  padding:9px 16px;border-radius:999px;font-size:0.8438rem;font-weight:600;transition:background .15s,transform .12s;}
.hero-cats a:hover{background:#fff;color:var(--brand);transform:translateY(-1px);}

/* =========================================================
   ПРЕИМУЩЕСТВА
   ========================================================= */
.advantages{padding:48px 0;}
.advantages .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:20px;}
/* min-width:0 — иначе ячейка сетки держит пол по своему min-content, и при
   крупном шрифте браузера (корень 24 px) блок преимуществ вылезал на 3 px за
   1280. Мелочь, но горизонтальная прокрутка появляется от любого перелива. */
.adv-item{position:relative;display:flex;flex-direction:column;min-width:0;min-height:176px;background:#fff;
  border:1px solid var(--line);border-radius:var(--radius);padding:24px;box-shadow:var(--shadow-sm);
  transition:transform .15s,box-shadow .15s;}
.adv-item:hover{transform:translateY(-3px);box-shadow:var(--shadow);}
.adv-item .adv-ic{position:absolute;top:20px;right:20px;width:46px;height:46px;border-radius:12px;
  background:var(--surface);display:flex;align-items:center;justify-content:center;color:var(--brand);}
.adv-item .adv-ic svg{width:24px;height:24px;}
.adv-item .adv-text{font-size:0.875rem;color:var(--muted);line-height:1.5;padding-right:54px;margin-bottom:20px;}
/* `align-items:flex-start` даёт детям ширину по содержимому, и при крупном
   шрифте браузера крупная строка (1,69rem — это 40 px при корне 24) вылезала
   за ячейку: 244 px содержимого в колонке 172. Перенос слова тут не помогает —
   он включается, только когда ширина блока ограничена. Ограничиваем. */
.adv-stat{margin-top:auto;display:flex;flex-direction:column;align-items:flex-start;gap:7px;}
.adv-stat > *{max-width:100%;}
.adv-stat .adv-tag{font-size:0.6562rem;font-weight:700;text-transform:uppercase;letter-spacing:.5px;
  color:var(--brand);background:var(--brand-50);border-radius:999px;padding:3px 10px;}
.adv-stat b{font-family:var(--font-display);font-weight:800;font-size:1.6875rem;line-height:1;color:var(--ink);}

/* SEO-текст + отзывы-карусель (после товаров, как у kazmehanika) */
.seo-block{padding:64px 0;}
/* minmax(0,…) обязателен и здесь: с «1fr» колонка получает минимум по
   min-content содержимого, и на телефоне блок SEO-текста раздвигал страницу
   до 1126 px при экране 375 — вся главная ехала вбок. */
.seo-grid{display:grid;grid-template-columns:minmax(0,1fr) 380px;gap:48px;align-items:start;}
.seo-grid > *{min-width:0;}
.seo-main .section-head{margin-bottom:16px;}
.seo-main .section-head h2{font-size:1.625rem;}
.seo-collapse{position:relative;overflow:hidden;transition:max-height .4s ease;}
.seo-collapse.is-collapsed{max-height:280px;}
.seo-collapse .seo-fade{display:none;position:absolute;left:0;right:0;bottom:0;height:96px;
  background:linear-gradient(to bottom,rgba(244,247,251,0),var(--surface));pointer-events:none;}
.seo-collapse.is-collapsed .seo-fade{display:block;}
/* Кнопка «Все характеристики» без скрипта ничего не делает — и не должна
   мозолить глаза: текст под ней и так показан целиком. */
.seo-more{display:none;}
.js .seo-more{display:inline-flex;align-items:center;gap:8px;margin-top:14px;background:#fff;border:1px solid var(--line-control);
  border-radius:999px;padding:10px 18px;font-size:0.875rem;font-weight:600;color:var(--ink);cursor:pointer;
  box-shadow:var(--shadow-sm);transition:background .15s,border-color .15s,color .15s;}
.seo-more:hover{border-color:var(--brand);color:var(--brand);}
.seo-more .chev{font-size:0.8125rem;transition:transform .25s;}
.seo-more[aria-expanded="true"] .chev{transform:rotate(180deg);}

.rv-carousel{position:relative;}
.rv-viewport{overflow:hidden;border-radius:18px;}
.rv-track{display:flex;transition:transform .4s ease;}
.rv-slide{position:relative;flex:0 0 100%;min-width:100%;display:block;color:inherit;
  background:#fff;border:1px solid var(--line);border-radius:18px;padding:26px 28px 24px;box-shadow:var(--shadow-sm);}
.rv-slide .rv-company{font-family:var(--font-display);font-weight:800;font-size:1.125rem;margin-bottom:12px;}
.rv-slide .rv-text{font-size:0.9375rem;color:var(--ink);line-height:1.6;margin-bottom:20px;}
.rv-slide .rv-meta{display:flex;flex-direction:column;gap:3px;margin-bottom:14px;}
.rv-slide .rv-date{font-size:0.7812rem;color:var(--muted);}
.rv-slide .who{font-weight:700;font-size:0.875rem;}
.rv-slide .stars{color:var(--star);font-size:0.9375rem;letter-spacing:1px;}
.rv-slide .rv-link{font-size:0.8438rem;font-weight:700;color:var(--brand);}
.rv-nav{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-top:18px;}
/* Точки переключения — по одной на отзыв, 22 px каждая. На узком экране
   (замер: 250 px, главная кран-балок) ряд не помещался и утаскивал вбок всю
   страницу: 274 вместо 250. Перенос ставим на любой ширине — лишним не будет. */
.rv-dots{display:flex;gap:6px;flex-wrap:wrap;}
.rv-dot{width:22px;height:6px;border:0;border-radius:999px;background:var(--line-control);padding:0;cursor:pointer;transition:background .2s;}
.rv-dot.is-active{background:var(--brand);}
.rv-arrows{display:flex;gap:8px;}
.rv-arrow{width:34px;height:34px;border-radius:999px;border:1px solid var(--line-control);background:#fff;cursor:pointer;
  font-size:1.125rem;line-height:1;color:var(--ink);display:flex;align-items:center;justify-content:center;transition:border-color .15s,color .15s;}
.rv-arrow:hover{border-color:var(--brand);color:var(--brand);}

/* характеристики товара — сворачивание по кнопке */
.specs-block{position:relative;}
.specs-collapse{position:relative;overflow:hidden;transition:max-height .4s ease;}
.js .specs-collapse.is-collapsed{max-height:252px;}
.specs-collapse .specs-fade{display:none;position:absolute;left:0;right:0;bottom:0;height:72px;
  background:linear-gradient(to bottom,rgba(255,255,255,0),#fff);pointer-events:none;}
.js .specs-collapse.is-collapsed .specs-fade{display:block;}
.specs-more{margin-top:14px;}

/* -------- SEO-текст: типографика --------
   Общая для всех мест, где он выводится: раздел каталога, главная, страницы
   «О продавце» и статьи блога. Раньше это был голый текст на белом фоне —
   владелец 23.08.2026: «ни оформления, ни выделения, смотрится инородным». */

/* Заголовок помечен той же тонкой вертикальной чертой, что и разделители
   секций на главной (`.dim-divider .tick`) — один приём в двух местах
   читается как система.
   Но цвет другой, и это замер, а не вкус: черта на главной лежит на белом,
   а здесь — на подложке `--surface`, и `--accent` на ней теряется. Контраст
   акцента к подложке по палитрам сети: по умолчанию 2,65, shopfloor 1,81,
   steel 4,09, cobalt 4,24, blueprint 10,02 — то есть на двух палитрах из
   пяти черты почти не видно. У `--brand` на той же подложке 5,40 … 13,86,
   ниже трёх не опускается нигде. */
.seo-text h2{font-family:var(--font-display);font-size:1.375rem;font-weight:800;
  margin:34px 0 14px;line-height:1.25;position:relative;padding-left:16px;}
.seo-text h2::before{content:"";position:absolute;left:0;top:.28em;
  width:3px;height:.9em;background:var(--brand);border-radius:2px;}
.seo-text h3{font-family:var(--font-display);font-size:1.125rem;font-weight:700;margin:26px 0 10px;}
.seo-text h2:first-child,.seo-text h3:first-child{margin-top:0;}

/* Первый абзац — вводный: чуть крупнее остальных. Дальше текст ровный:
   выделять всё значит не выделять ничего. */
.seo-text > p:first-of-type{font-size:1.0625rem;line-height:1.65;color:var(--ink);}

/* Маркеры возвращаем принудительно: глобальное `ul{list-style:none}`
   (сброс для меню и подвала) снимало их и здесь, поэтому список выглядел бы
   набором строк без точек. Цвет маркера — `--brand` по той же причине, что
   у черты заголовка: из токенов палитры только он держит контраст к
   подложке на всех пяти палитрах сети. */
.seo-text ul{list-style:disc;margin:14px 0 18px 20px;}
.seo-text ol{list-style:decimal;margin:14px 0 18px 20px;}
.seo-text li{margin-bottom:8px;padding-left:4px;}
.seo-text li::marker{color:var(--brand);}
/* Числа в перечислениях выстраиваются в столбик: в этих текстах их много
   («Тали — 1150 позиций», «Лебёдки — 267»), и разнобой ширины цифр
   превращает список в частокол. */
.seo-text li{font-variant-numeric:tabular-nums;}

/* Ссылка внутри текста должна быть видна как ссылка. До 23.08.2026 она была
   того же цвета, что текст, и без подчёркивания — то есть неотличима. */
.seo-text a{color:var(--brand);text-decoration:underline;text-underline-offset:.18em;
  text-decoration-thickness:1px;text-decoration-color:color-mix(in srgb,var(--brand) 40%,transparent);}
.seo-text a:hover{text-decoration-color:var(--brand);}

/* -------- SEO-текст раздела: своя рамка --------
   Отделён от списка товаров подложкой и рамкой — теми же, что у карточек
   (`--surface`, `--line`, `--radius`). Ширина строки внутри ограничена: без
   этого строка тянулась на 106 знаков при читаемых 60–75. */
/* Рамка во всю ширину — как список товаров над ней. Узкая рамка с пустотой
   справа выглядела не решением, а недоделкой (владелец, 23.08.2026). Но и
   строка во всю ширину не годится: при 1112 px текста вышло бы 135 знаков
   при читаемых 65–75. Поэтому на широком экране текст идёт в две колонки —
   каждая по ~65 знаков, и место занято по делу. */
.cat-seo-block{margin-top:48px;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius);padding:30px 36px 34px;}
.cat-seo-block .dim-divider{margin-bottom:22px;}
/* Засечки разделителя внутри рамки — тем же `--brand`, что черта заголовка
   и маркер списка. На главной разделитель лежит на белом, здесь на подложке,
   и акцент на ней по двум палитрам из пяти даёт 1,81 и 2,65 — то есть
   практически не виден. Замер тот же, что у заголовка. */
.cat-seo-block .dim-divider .tick{background:var(--brand);}
/* Селектор с родителем не для красоты: `.pd-description` задаёт свои
   `max-width:820px` и нижний отступ ниже по файлу, и одноклассовое правило
   ему проигрывает. */
/* Одна колонка. Двухколоночная вёрстка тут была и не прижилась: вместе с
   таблицами во всю ширину она читалась кашей (владелец, 23.08.2026).

   Ширину держим не на всём блоке, а на текстовых элементах: абзацы,
   заголовки, списки и цитаты идут колонкой в 72 знака по центру рамки, а
   таблица занимает всю ширину. Так и строка остаётся читаемой, и таблица не
   ужимается — в ней сравнивают модели по числам, и узкая колонка ломает
   именно это.

   Пустоты сбоку при этом нет: поля симметричные, как в статье, а не
   полполосы справа, как было у узкой рамки. */
.cat-seo-block .catalog-cat-seo{margin:0;max-width:none;}
/* Текст занимает всю ширину рамки — так же, как таблица (владелец,
   23.08.2026). Узкой колонки по центру больше нет.

   Строка при этом выходит длинной, и чтобы она не превращалась в нитку
   мелкого шрифта, кегль и межстрочный интервал подняты: 1.0625rem против
   0.9688rem и 1.75 против 1.7. Замер на живой рамке 1118 px: было бы
   135 знаков при прежнем кегле, стало 118. */
.cat-seo-block .catalog-cat-seo > *{max-width:none;margin-left:0;margin-right:0;}
.cat-seo-block .catalog-cat-seo :is(p,li){font-size:1.0625rem;line-height:1.75;}
.cat-seo-block .seo-note{margin-top:20px;background:#fff;}

@media (max-width:600px){
  .cat-seo-block{padding:22px 18px 24px;margin-top:34px;}
  .seo-text h2{font-size:1.25rem;}
}

/* =========================================================
   ОБЩИЕ СЕКЦИИ
   ========================================================= */
section{padding:72px 0;}
.section--surface{background:var(--surface);}
.section-head{display:flex;justify-content:space-between;align-items:flex-end;margin-bottom:32px;gap:20px;flex-wrap:wrap;}
/* min-width:0 — по умолчанию элемент флексбокса не сжимается ниже своего
   min-content, и при крупном шрифте браузера заголовок раздела распирал
   строку вместе со ссылкой «все». */
.section-head h2{font-size:2.125rem;font-weight:800;min-width:0;}
.section-head .all-link{font-weight:700;font-size:0.9062rem;color:var(--brand);white-space:nowrap;}
.section-head .all-link:hover{color:var(--brand-700);}

/* -------- КАТЕГОРИИ -------- */
/* align-items:start — чтобы плитка без фото оставалась низкой. Иначе сетка
   растягивает её до высоты соседей с фотографией, и вместо компактной
   карточки получается высокий пустой прямоугольник с рамкой — ровно то,
   от чего уходили, убирая заглушку «фото раздела». */
.categories .grid,.cat-overview-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:20px;align-items:start;}
.cat-card{
  display:block;background:#fff;border:1px solid var(--line);border-radius:var(--radius);
  overflow:hidden;transition:border-color .15s,transform .15s,box-shadow .15s;box-shadow:var(--shadow-sm);
}
.cat-card:hover{border-color:var(--brand);transform:translateY(-4px);box-shadow:var(--shadow);}
.cat-thumb{aspect-ratio:16/10;background:var(--surface);display:flex;align-items:center;justify-content:center;overflow:hidden;border-bottom:1px solid var(--line);}
.cat-thumb img{width:100%;height:100%;object-fit:contain;padding:14px;mix-blend-mode:multiply;}
/* Обёртка <picture> должна занимать ту же коробку, что и сама картинка.
   Без этого правила она — обычный строчный элемент без размеров, и
   `height:100%` у <img> считается от неё, а не от рамки: изображение
   вылезает за край и его срезает `overflow:hidden`. Видно это было только
   там, где есть webp-двойник: на главной tor24 из шести плиток разделов
   в <picture> была ровно одна — «Складская техника», и обрезана была ровно
   она (22.08.2026). Остальные пять отдавались голым <img> и рисовались
   правильно — то есть вид плитки зависел от того, успели ли мы сделать
   webp. Правило распространяется на все коробки с фиксированной формой. */
.cat-thumb picture,
.product-card .thumb picture,
.gallery-main picture{display:block;width:100%;height:100%;}
.hero-photo picture{display:block;width:100%;}

.cat-thumb .ph{color:var(--muted);font-family:var(--font-mono);font-size:0.75rem;}
/* Подпись плитки раздела. Одно правило на оба случая: в каталоге заголовок
   лежит в обёртке .cat-card-body (см. includes/category-card.php), а на
   главной — прямо в .cat-card, своей разметкой. Из-за этого на главной он
   вообще не имел отступа и упирался в рамку. */
.cat-card-body,
.cat-card > h3,
.cat-card > h2{padding:20px 24px 22px;text-align:center;}
.cat-card-body{display:flex;flex-direction:column;align-items:center;gap:6px;}
.cat-card h3,
.cat-card h2{font-size:1.0625rem;font-weight:700;color:var(--ink);transition:color .15s;}
.cat-card-count{font-family:var(--font-mono);font-size:0.75rem;color:var(--muted);}
/* Плитка без фотографии не рисует пустую рамку 16:10, а становится компактной:
   фото есть меньше чем у половины разделов сети, и три серых прямоугольника
   подряд — обычный вид страницы, а не исключение. */
.cat-card-flat{min-height:0;}
.cat-card-flat .cat-card-body{padding:18px 20px 20px;}
.cat-card:hover h3{color:var(--brand);}

/* -------- ТОВАРЫ -------- */
/* minmax(0,1fr), а не 1fr: у «1fr» минимум равен min-content, и одна длинная
   неразрывная строка в названии («…MetalTec CK 36x1500») раздвигала колонку
   шире трека. Сетка вылезала за контейнер на 42 px, и вся страница ехала
   вбок — горизонтальная полоса прокрутки на каждом разделе с товаром. */
.products .grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:20px;}
.products .grid > *{min-width:0;}
.product-card{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:16px;position:relative;display:flex;flex-direction:column;transition:transform .15s,box-shadow .15s,border-color .15s;}
.product-card:hover{transform:translateY(-4px);box-shadow:var(--shadow);border-color:var(--line);}
.product-card .badge{position:absolute;top:14px;left:14px;z-index:2;font-family:var(--font-mono);font-size:0.6562rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;padding:5px 10px;border-radius:6px;background:var(--accent);color:var(--on-accent);}
.product-card .thumb{aspect-ratio:1/.9;background:var(--surface);border-radius:var(--radius-sm);margin-bottom:14px;display:flex;align-items:center;justify-content:center;color:var(--muted);font-family:var(--font-mono);font-size:0.75rem;overflow:hidden;padding:10px;}
.product-card .thumb img{width:100%;height:100%;object-fit:contain;mix-blend-mode:multiply;}
.product-card .sku{font-family:var(--font-mono);font-size:0.7188rem;color:var(--muted);margin-bottom:6px;}
/* Метка «ЗИП / оснастка»: спокойная, без цвета акцента — она поясняет, а не
   привлекает. Контраст muted к surface считается в общей палитре и проходит
   порог для мелкого текста. */
.product-card .pc-tag{display:inline-block;font-size:0.6875rem;font-weight:700;
  text-transform:uppercase;letter-spacing:.04em;color:var(--muted);
  background:var(--surface-2);border-radius:999px;padding:3px 9px;margin-bottom:6px;}
.product-card h3{font-size:0.9375rem;font-weight:600;line-height:1.35;margin-bottom:12px;flex:1;overflow-wrap:anywhere;}
.product-card h3 a:hover{color:var(--brand);}
.product-card .price-row{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:14px;gap:8px;}
.product-card .price{font-family:var(--font-display);font-weight:800;font-size:1.1875rem;color:var(--ink);}
.product-card .price.on-request{font-size:0.9375rem;font-weight:700;color:var(--brand);}
/* `flex:1` без `min-width:0` не даёт кнопке сжаться меньше своего текста: две
   кнопки в карточке вылезали за её край на 7 px, и вся страница получала
   горизонтальную прокрутку. Проявилось, когда «Получить счёт» стало
   «Запросить счёт» — текст длиннее на символ. */
.product-card .pc-actions{display:flex;gap:8px;flex-wrap:wrap;}
.product-card .pc-actions .btn{flex:1 1 auto;min-width:0;}

/* наличие */
.avail{font-size:0.75rem;font-weight:600;display:inline-flex;align-items:center;gap:6px;white-space:nowrap;}
.avail::before{content:'';width:7px;height:7px;border-radius:50%;}
.avail.in{color:var(--ok);}
.avail.in::before{background:var(--ok);}
.avail.back{color:var(--muted);}
.avail.back::before{background:var(--muted);}
.pd-avail{margin-bottom:10px;font-size:0.875rem;}

/* -------- КАК РАБОТАЕМ -------- */
.how-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:24px;position:relative;}
.how-step{position:relative;background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:26px;box-shadow:var(--shadow-sm);}
.how-step .num{width:46px;height:46px;border-radius:12px;background:var(--brand);color:#fff;font-family:var(--font-display);font-weight:800;display:flex;align-items:center;justify-content:center;margin-bottom:16px;font-size:1.0625rem;}
.how-step h4{font-size:1rem;font-weight:700;margin-bottom:8px;}
.how-step p{font-size:0.875rem;color:var(--muted);}

/* -------- ОТЗЫВЫ -------- */
.reviews .grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:20px;}
.reviews .grid > *{min-width:0;}
/* Карточка отзыва несёт название компании и текст письма — оба подставляются.
   При крупном шрифте на экране 320 длинное название распирало карточку до
   299 px в контейнере 272 (нашёл tools/audit_overflow.py). Поля на такой
   ширине тоже великоваты: 28 px по кругу съедают пятую часть экрана. */
.review-card{display:block;background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:28px;box-shadow:var(--shadow-sm);transition:transform .15s,box-shadow .15s,border-color .15s;
  min-width:0;overflow-wrap:anywhere;}
@media (max-width:600px){ .review-card{padding:18px;} }
a.review-card:hover{transform:translateY(-4px);box-shadow:var(--shadow);border-color:var(--brand);}
.review-card .stars{color:var(--star);font-size:0.9375rem;margin-bottom:14px;letter-spacing:2px;}
.review-card .rv-company{font-family:var(--font-display);font-weight:800;font-size:1.125rem;margin-bottom:10px;color:var(--ink);}
.review-card p{font-size:0.9375rem;color:var(--muted);margin-bottom:20px;line-height:1.6;}
.review-card .rv-foot{border-top:1px solid var(--line-2);padding-top:14px;}
.review-card .rv-date{display:block;font-size:0.7812rem;color:var(--muted);margin-bottom:4px;}
.review-card .who{display:block;font-size:0.875rem;color:var(--ink);font-weight:700;}
.review-card .rv-link{display:inline-block;margin-top:10px;font-size:0.8438rem;font-weight:700;color:var(--brand);}
a.review-card:hover .rv-link{text-decoration:underline;}

/* страница отзыва — рекомендательное письмо */
.review-page{max-width:860px;}
.letter{display:grid;grid-template-columns:1fr;gap:24px;}
.letter-paper{background:#fff;border:1px solid var(--line);border-left:4px solid var(--brand);border-radius:var(--radius);padding:32px 34px;box-shadow:var(--shadow-sm);}
.letter-paper :is(p){font-size:1rem;line-height:1.75;color:var(--ink);margin-bottom:14px;}
.letter-sign{margin-top:18px;font-weight:700;color:var(--ink);}
.letter-scan{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:18px;}
.letter-scan img{width:100%;max-width:520px;margin:0 auto;border-radius:8px;}

/* -------- FAQ -------- */
.faq-list{max-width:820px;margin:0 auto;}
.faq-item{background:#fff;border:1px solid var(--line);border-radius:var(--radius);margin-bottom:12px;overflow:hidden;transition:box-shadow .15s,border-color .15s;}
.faq-item.is-open{box-shadow:var(--shadow);border-color:var(--brand);}
.faq-q{width:100%;display:flex;justify-content:space-between;align-items:center;gap:16px;padding:20px 24px;font-size:1.0312rem;font-weight:700;text-align:left;font-family:var(--font-display);}
.faq-q .plus{font-family:var(--font-mono);font-size:1.5rem;color:var(--brand);transition:transform .2s;flex-shrink:0;line-height:1;}
.faq-item.is-open .faq-q .plus{transform:rotate(45deg);}
/* Свёртка — только когда есть скрипт: без него раскрыть нечем. */
.js .faq-a{max-height:0;overflow:hidden;transition:max-height .25s ease;}
.faq-a-inner{padding:0 24px 22px;font-size:0.9375rem;color:var(--muted);line-height:1.6;}

/* -------- ФИНАЛЬНЫЙ CTA -------- */
.cta-final{background:linear-gradient(135deg,#0B5FD6,#073A86);color:#fff;border-radius:0;}
.cta-final .box{display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:24px;
  background:transparent;}
.cta-final h2{font-size:1.875rem;font-weight:800;margin-bottom:8px;color:#fff;}
.cta-final p{color:#CFE0FF;font-size:1rem;}
.cta-final .actions{display:flex;gap:14px;flex-wrap:wrap;}

/* -------- SEO-ТЕКСТ -------- */
.home-seo .seo-text{max-width:920px;}
.seo-text :is(p,ul,ol){margin-bottom:14px;font-size:0.9688rem;color:var(--muted);line-height:1.7;}
.seo-text h2,.seo-text h3{font-family:var(--font-display);font-weight:700;margin:24px 0 12px;color:var(--ink);}
/* -------- таблица в SEO-тексте --------
   Зебра и шапка на тон темнее строк: в этих таблицах сравнивают модели по
   числам, и глаз должен держать строку до конца, не съезжая на соседнюю.
   Цвета — токены палитры, а не новые: шапка `--surface-2`, чётные строки
   `--surface`, нечётные белые. В каждой из пяти палитр сети `--surface-2`
   на шаг темнее `--surface`, так что порядок «шапка темнее» держится
   везде без отдельных правил. */
.seo-text table{width:100%;border-collapse:collapse;margin:20px 0;font-size:0.9062rem;
  background:#fff;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;
  font-variant-numeric:tabular-nums;}
.seo-text table td,.seo-text table th{padding:12px 15px;text-align:left;
  border-bottom:1px solid var(--line-2);}
.seo-text table thead th{background:var(--surface-2);color:var(--ink);
  font-family:var(--font-mono);font-size:0.75rem;font-weight:700;letter-spacing:.05em;
  text-transform:uppercase;border-bottom:1px solid var(--line);}
.seo-text table tbody tr:nth-child(even){background:var(--surface);}
.seo-text table tbody tr:last-child td,
.seo-text table tbody tr:last-child th{border-bottom:none;}
/* Заголовок строки (`th scope="row"`) — обычная ячейка, только полужирная.
   Фон не задаём: пусть просвечивает зебра, иначе в чётной строке он
   выглядел бы белой дырой. */
.seo-text table tbody th{background:none;font-family:inherit;font-size:inherit;
  letter-spacing:normal;text-transform:none;font-weight:600;color:var(--ink);}
/* Обёртку ставит tools/install_texts.py: широкая таблица прокручивается
   внутри себя, а не двигает вбок всю страницу. */
.seo-text .seo-table{overflow-x:auto;margin:20px 0;}
.seo-text .seo-table table{margin:0;min-width:420px;}

/* Номер в нумерованном списке — тем же цветом, что маркеры и черта
   заголовка, и моноширинным: столбик номеров должен стоять ровно. */
.seo-text ol li::marker{color:var(--brand);font-weight:700;font-family:var(--font-mono);}

/* =========================================================
   ФУТЕР
   ========================================================= */
footer.site-footer{background:var(--footer-bg);color:var(--footer-text);padding:64px 0 28px;font-size:0.875rem;}
.footer-grid{display:grid;grid-template-columns:1.5fr .9fr .9fr 1.1fr;gap:40px;margin-bottom:40px;}
.footer-brand .logo{color:#fff;margin-bottom:14px;}
.footer-brand .logo b{color:var(--footer-mark);}
.footer-brand p{max-width:300px;color:var(--footer-muted);line-height:1.6;}
/* Заголовки колонок подвала были h5 при последнем заголовке страницы h2 —
   в обходе по заголовкам это выглядит как пропуск двух уровней. Теперь h2,
   вид тот же (замер 03.08.2026: было 14 px/700, стало столько же). */
.footer-col h2{color:#fff;font-size:0.875rem;font-weight:700;margin-bottom:16px;letter-spacing:0;}
.footer-col ul{display:flex;flex-direction:column;gap:11px;}
.footer-col a:hover{color:#fff;}
.footer-bottom{border-top:1px solid var(--footer-line);padding-top:24px;display:flex;justify-content:space-between;flex-wrap:wrap;gap:10px;font-size:0.7812rem;color:var(--footer-bottom);}

/* =========================================================
   МОДАЛКИ
   ========================================================= */
.modal-overlay{position:fixed;inset:0;background:rgba(10,20,40,.55);z-index:300;display:flex;align-items:center;justify-content:center;padding:20px;opacity:0;visibility:hidden;transition:opacity .2s,visibility .2s;}
.modal-overlay.is-open{opacity:1;visibility:visible;}
/* Страница за открытым диалогом не должна прокручиваться: на телефоне палец
   попадает мимо формы и уезжает содержимое под ней. */
body.modal-open{overflow:hidden;}
/* Закрытая модалка не должна занимать место в раскладке. При visibility:hidden
   она остаётся в потоке, а как fixed-элемент меряется по ширине окна вместе с
   вертикальной полосой прокрутки — на телефоне это давало странице лишние
   16 px горизонтальной прокрутки на страницах без другого широкого содержимого.
   Плавное появление сохраняется: display переключается вместе с классом. */
.modal-overlay:not(.is-open):not(:target){display:none;}
/* Открытие по якорю: без JavaScript кнопка `data-modal-open` ничего не
   делает, и форма заявки становится недоступна совсем. Ссылка на
   #modalOrder открывает её силами одного CSS. */
.modal-overlay:target{opacity:1;visibility:visible;}
.modal{background:#fff;border-radius:var(--radius);padding:34px;max-width:440px;width:100%;position:relative;box-shadow:var(--shadow);}
.modal h3{font-size:1.4375rem;font-weight:800;margin-bottom:6px;}
.modal p.sub{color:var(--muted);font-size:0.9062rem;margin-bottom:22px;}
/* Крестик — ссылка (см. footer.php), поэтому центрируем содержимое сами:
   у кнопки это было по умолчанию. */
.modal .close{position:absolute;top:18px;right:18px;font-size:1.375rem;color:var(--muted);width:32px;height:32px;border-radius:8px;
  display:flex;align-items:center;justify-content:center;line-height:1;text-decoration:none;}
.modal .close:hover{background:var(--surface);}
/* Врезка с формой на «Контактах». Ширину ограничиваем: поле имени во всю
   колонку в 1240 px выглядит как поле для абзаца, а не для двух слов. */
.contact-form{max-width:440px;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--radius);padding:24px;margin-top:8px;}
.contact-form .legal{margin-top:12px;}
.field{margin-bottom:14px;}
.field label{display:block;font-size:0.8438rem;font-weight:600;margin-bottom:6px;}
.field input,.field textarea{width:100%;padding:13px 15px;border:1.5px solid var(--line-control);border-radius:var(--radius-sm);font-size:0.9062rem;transition:border-color .15s,box-shadow .15s;}
.field input:focus,.field textarea:focus{outline:none;border-color:var(--brand);box-shadow:0 0 0 4px var(--brand-50);}
/* Поле, на которое пожаловался сервер. Цветом дело не ограничивается: то же
   поле получает фокус, а текст ошибки читается из живой области рядом. */
.field input[aria-invalid="true"]{border-color:#c02626;box-shadow:0 0 0 4px #fbeaea;}
.modal .legal{font-size:0.7188rem;color:var(--muted);margin-top:14px;}
.modal .legal a{text-decoration:underline;}
/* honeypot — вне потока и недоступно людям, но в DOM для ботов */
.field.hp{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden;}
.form-status{font-size:0.8438rem;margin-top:12px;padding:10px 12px;border-radius:var(--radius-sm);}
/* Пустую область прячем стилем, а не атрибутом hidden: живая область должна
   быть в дереве доступности с самого начала, иначе её появление не объявят. */
.form-status:empty{display:none;}
.form-status.is-ok{color:#0a5b2a;background:#e7f6ec;}
.form-status.is-err{color:#8a1c1c;background:#fbeaea;}

/* =========================================================
   SCROLL TOP
   ========================================================= */
.scroll-top{position:fixed;bottom:24px;right:24px;width:48px;height:48px;border-radius:14px;background:var(--brand);color:#fff;font-size:1.25rem;z-index:150;display:flex;align-items:center;justify-content:center;box-shadow:0 8px 22px rgba(11,95,214,.35);opacity:0;transform:translateY(10px);pointer-events:none;transition:opacity .2s,transform .2s;}
.scroll-top.is-visible{opacity:1;transform:translateY(0);pointer-events:auto;}

a:focus-visible,button:focus-visible,input:focus-visible,textarea:focus-visible{outline:2px solid var(--brand);outline-offset:2px;}
/* На тёмных полосах рамка фокуса цвета бренда исчезает: герой и подвал сами
   выкрашены из тех же токенов. Посчитано 03.08.2026 по всем пяти палитрам —
   контраст рамки к фону героя 1.00…1.34 при пороге 3.0 (WCAG 2.2), то есть
   человек с клавиатуры просто не видит, где он находится. Белая рамка на тех
   же фонах даёт 5.80…18.87. */
.hero a:focus-visible, .hero button:focus-visible, .hero input:focus-visible,
footer.site-footer a:focus-visible, footer.site-footer button:focus-visible,
footer.site-footer input:focus-visible{outline-color:#fff;}
/* Ссылка «к содержимому»: не видна, пока по ней не пришли клавишей. */
.skip-link{position:absolute;left:-9999px;top:0;z-index:400;padding:10px 16px;
  background:var(--brand);color:#fff;font-weight:700;border-radius:0 0 8px 0;}
.skip-link:focus{left:0;}

/* =========================================================
   КАТАЛОГ / КАРТОЧКА ТОВАРА
   ========================================================= */
.breadcrumbs{font-size:0.8438rem;color:var(--muted);margin-bottom:22px;}
/* На телефоне цепочка из шести уровней занимает три строки и 84 px первого
   экрана (замер 03.08.2026 на карточке товара). Прокручиваем её в одну строку
   вместо переноса: путь остаётся целиком — и для человека, и для разметки
   BreadcrumbList, — но перестаёт съедать экран. Полоса прокрутки не рисуется,
   сама прокрутка работает пальцем. */
@media (max-width:600px){
  .breadcrumbs{white-space:nowrap;overflow-x:auto;scrollbar-width:none;
    -webkit-overflow-scrolling:touch;margin-bottom:14px;}
  .breadcrumbs::-webkit-scrollbar{display:none;}
}
.breadcrumbs a{color:var(--brand);}
.breadcrumbs span{margin:0 7px;color:var(--line);}

.page-head{margin-bottom:30px;}
.page-head h1{font-size:2.125rem;font-weight:800;margin-bottom:8px;}
.page-head .count{color:var(--muted);font-size:0.9062rem;}

/* minmax(0,…) — иначе колонка держит пол по своему min-content, и длинное
   название товара при крупном шрифте браузера распирает страницу: замер
   03.08.2026 дал H1 шириной 422 px при экране 375 и корне 24 px. */
.product-detail{display:grid;grid-template-columns:minmax(0,.95fr) minmax(0,1.05fr);gap:48px;margin-bottom:48px;}
.gallery-main{background:#fff;border:1px solid var(--line);border-radius:var(--radius);aspect-ratio:1/.9;overflow:hidden;display:flex;align-items:center;justify-content:center;margin-bottom:12px;padding:24px;}
.gallery-main img{width:100%;height:100%;object-fit:contain;mix-blend-mode:multiply;}
.gallery-thumbs{display:flex;gap:10px;flex-wrap:wrap;}
/* Хвост галереи: кадры сверх двенадцати. Раскрытие штатное, <details>, чтобы
   работало без JS и с клавиатуры. Оформляем как ссылку, а не как кнопку: это
   не действие с товаром, и спорить с «Запросить КП» за внимание не должно. */
.gallery-more{margin-top:10px;}
.gallery-more>summary{cursor:pointer;display:inline-block;padding:6px 2px;
  font-size:14px;color:var(--muted);text-decoration:underline;
  text-underline-offset:3px;}
.gallery-more>summary:hover{color:var(--ink);}
.gallery-more>summary:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
.gallery-more .gallery-thumbs{margin-top:10px;}
/* На телефоне лента миниатюр переносилась на две строки и занимала 146 px —
   столько же, сколько название товара и строка с артикулом вместе. Делаем одну
   прокручиваемую строку: снимки все на месте, высота вдвое меньше. */
@media (max-width:600px){
  .gallery-thumbs{flex-wrap:nowrap;overflow-x:auto;scrollbar-width:none;
    -webkit-overflow-scrolling:touch;padding-bottom:4px;}
  .gallery-thumbs::-webkit-scrollbar{display:none;}
  .gallery-thumbs .gal-thumb{flex:0 0 auto;}
}
.gallery-thumbs .gal-thumb{display:block;padding:0;border:0;background:none;cursor:pointer;border-radius:8px;}
.gallery-thumbs img{width:68px;height:68px;object-fit:contain;border-radius:8px;border:1.5px solid var(--line);cursor:pointer;padding:6px;background:#fff;display:block;}
.gallery-thumbs .gal-thumb:hover img{border-color:var(--brand);}
/* Выбранный снимок видно и глазами, и с клавиатуры. */
.gallery-thumbs .gal-thumb.is-active img{border-color:var(--brand);border-width:2px;}
.no-image{width:100%;height:100%;display:flex;align-items:center;justify-content:center;color:var(--muted);font-family:var(--font-mono);font-size:0.8125rem;}
/* Пустой квадрат «фото пока нет» на телефоне занимал 281 px — столько же,
   сколько настоящий снимок, — и утаскивал название товара с 450 на 732 px,
   то есть за сгиб. Никакой информации в нём нет: это подпись о том, чего нет.
   На телефоне сжимаем его до строки. Замер 03.08.2026, карточка без фото на
   сайте талей; таких позиций в сети 929. */
@media (max-width:600px){
  .gallery-main:has(.no-image){aspect-ratio:auto;padding:14px;margin-bottom:8px;}
  .no-image{height:auto;}
}

.pd-meta{font-family:var(--font-mono);font-size:0.8125rem;color:var(--muted);margin-bottom:14px;display:flex;gap:18px;flex-wrap:wrap;}
/* Метка «ЗИП / оснастка» в строке реквизитов карточки — та же, что в списке. */
.pd-meta .pd-tag{font-weight:700;text-transform:uppercase;letter-spacing:.04em;
  background:var(--surface-2);border-radius:999px;padding:2px 9px;}
.pd-title{font-size:1.875rem;font-weight:800;line-height:1.18;margin-bottom:18px;}
.pd-price-box{background:var(--surface);border:1px solid var(--line);border-radius:var(--radius);padding:24px;margin-bottom:24px;}
.pd-price{font-family:var(--font-display);font-weight:800;font-size:1.75rem;margin-bottom:16px;}
/* Кнопки карточки: `white-space:nowrap` у .btn не даёт им сжаться, и при
   крупном шрифте браузера на экране 320 px номер телефона вылезал за блок —
   страница ехала вбок на 5 px (замер 03.08.2026, корень 24 px). Разрешаем
   элементам ужиматься и переносим строку, когда иначе не помещается. */
.pd-actions{display:flex;gap:12px;flex-wrap:wrap;}
.pd-actions .btn{min-width:0;max-width:100%;}

.specs-table-wrap{overflow-x:auto;border:1px solid var(--line);border-radius:var(--radius);background:#fff;}
table.specs{width:100%;border-collapse:collapse;font-size:0.9062rem;min-width:520px;}
table.specs th{text-align:left;font-family:var(--font-mono);font-size:0.7188rem;letter-spacing:.05em;text-transform:uppercase;color:var(--muted);background:var(--surface);padding:14px 16px;border-bottom:1px solid var(--line);}
table.specs td{padding:13px 16px;border-bottom:1px solid var(--line-2);}
table.specs tr:last-child td{border-bottom:none;}
table.specs td.mono{font-family:var(--font-mono);}
/* Название параметра — заголовок строки (`th scope="row"`), иначе читалка
   произносит «10 т» и не говорит, к чему это относится. Выглядеть он должен
   как обычная ячейка: оформление из правила выше — только для шапки. */
table.specs tbody th{font-family:inherit;font-size:inherit;letter-spacing:normal;
  text-transform:none;color:inherit;background:none;font-weight:400;
  padding:13px 16px;border-bottom:1px solid var(--line-2);text-align:left;}
table.specs tbody tr:last-child th{border-bottom:none;}

/* Подпись только для читалки: заголовок есть в дереве доступности и в обходе
   по заголовкам, на экране его нет. */
.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;}
table.specs tr:hover td{background:var(--surface);}

.pd-description{max-width:820px;margin-bottom:40px;}
.pd-description :is(p,ul,ol){margin-bottom:14px;font-size:0.9688rem;color:var(--ink);line-height:1.7;}
.pd-description li{margin-bottom:6px;margin-left:18px;list-style:disc;}
/* Правило выше вешает точку на КАЖДЫЙ пункт, а у текста раздела оба класса
   сразу (`pd-description seo-text`) — из-за этого нумерованный список
   рисовался точками вместо цифр: тип списка задан на `<ol>`, но перебит
   правилом на самом пункте. Возвращаем тип по родителю. */
.seo-text ul > li{list-style:disc;}
.seo-text ol > li{list-style:decimal;}
/* Подзаголовки внутри описания товара: «Модельный ряд:», «Где применяется?»
   — они в тексте поставщика есть, просто раньше тонули в сплошной портянке.
   Шрифт и вес — по общему канону заголовков, размер на ступень меньше
   заголовка блока, чтобы иерархия читалась. */
.pd-description h3{font-family:var(--font-display);font-weight:700;
  font-size:1.0625rem;line-height:1.3;margin:22px 0 10px;color:var(--ink);}
.pd-description h3:first-child{margin-top:0;}
.pd-description li > strong{color:var(--ink);}
.pd-description table{width:100%;border-collapse:collapse;margin:14px 0;font-size:0.875rem;}
.pd-description table td{padding:9px 13px;border-bottom:1px solid var(--line-2);}

.empty-state{padding:60px 0;text-align:center;color:var(--muted);}
.empty-state a{color:var(--brand);font-weight:600;}
.catalog-cat-seo{margin-top:48px;}

/* Панель порядка списка. Нужна там, где позиций больше страницы: в разделе
   лазерных станков по металлу их 578 на одиннадцати страницах, и до неё
   единственным способом сравнить цены было листать всё подряд. */
/* Ответ на заявку, отправленную без JavaScript. */
.lead-note{padding:14px 0;font-weight:600;}
/* Оговорка под таблицей характеристик снятой с производства модели. Тише
   основного текста, но не настолько, чтобы её нельзя было прочесть. */
.seo-note{margin-top:14px;padding:12px 16px;background:var(--surface);
  border-left:3px solid var(--line);border-radius:var(--radius-sm);
  font-size:0.875rem;color:var(--muted);line-height:1.55;}
.lead-note--ok{background:#E8F5EC;color:#1B5E34;}
.lead-note--err{background:#FDECEC;color:#8A1F1F;}

/* Пояснение на странице отзывов: чьи это письма. */
.reviews-note{max-width:70ch;margin:0 0 26px;padding:14px 16px;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--radius-sm);font-size:0.9375rem;color:var(--ink);}
/* `nowrap` тут стоял, чтобы стрелка не отрывалась от текста ссылки. Но сама
   ссылка «Реквизиты и документы →» при крупном шрифте занимает 299 px в абзаце
   шириной 257 и утаскивает страницу вбок (нашёл tools/audit_overflow.py).
   Держим вместе только последнее слово со стрелкой — перенос по остальным
   пробелам разрешён. */
.reviews-note a{color:var(--brand);font-weight:600;}

/* Указатель разделов на странице каталога: плотный список в несколько колонок,
   с отступом по уровню вложенности — чтобы структура читалась, а страница не
   растянулась на два экрана. */
.cat-index{columns:3;column-gap:32px;list-style:none;margin:0;padding:0;}
.cat-index__item{break-inside:avoid;display:flex;justify-content:space-between;gap:10px;
  padding:5px 0;font-size:0.9375rem;border-bottom:1px solid var(--line-2);}
/* Указатель разделов — единственное место сети, где при «очень крупном»
   шрифте браузера страница ещё ехала вбок (ЧПУ, каталог, 320 px: 349 вместо
   320). Названия разделов длинные («Плоскошлифовальные станки»), строка —
   флексбокс со счётчиком справа, и ссылка не сжималась ниже своего
   min-content. Нужны обе половины: min-width снимает пол, anywhere даёт
   переносить слово. Замер 03.08.2026. */
.cat-index__item a{color:var(--ink);min-width:0;overflow-wrap:anywhere;}
.cat-index__item a:hover{color:var(--brand);}
.cat-index__item span{color:var(--muted);font-size:0.8125rem;font-variant-numeric:tabular-nums;}
.cat-index__lvl3{padding-left:14px;}
.cat-index__lvl4{padding-left:28px;}
@media(max-width:900px){.cat-index{columns:2;}}
@media(max-width:640px){.cat-index{columns:1;}}

/* Блок контекста на карточке без данных: объясняет, что это за класс техники,
   и ведёт в раздел — единственное полезное, что можно показать, не выдумывая
   характеристик. */
.pd-context{max-width:70ch;margin:36px 0 0;padding:22px 24px;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--radius);}
.pd-context h2{font-family:var(--font-display);font-size:1.25rem;margin:0 0 10px;}
.pd-context p{color:var(--ink);margin:0 0 14px;}
.pd-context p:last-child{margin:0;}
/* Кнопка «Все позиции раздела „…“» несёт в себе название раздела, а у .btn
   стоит `white-space:nowrap` — на телефоне она вырастала до 520 px при экране
   375 и утаскивала страницу вбок до 569 (замер 03.08.2026 на карточке без
   характеристик; таких карточек в сети 4961). Разрешаем перенос. */
.pd-context .btn{white-space:normal;max-width:100%;text-align:left;}

/* Строка «область действия» на служебных страницах: чем торгует эта витрина и
   что условия общие для всех витрин продавца. */
.info-scope{max-width:70ch;margin:0 0 22px;padding:12px 16px;background:var(--surface);
  border-left:3px solid var(--brand);border-radius:0 6px 6px 0;font-size:0.9375rem;color:var(--ink);}

.sortbar{display:flex;align-items:center;flex-wrap:wrap;gap:8px;margin:0 0 20px;}
.sortbar__label{font-size:0.875rem;color:var(--muted);margin-right:4px;}
.sortbar__item{display:inline-block;padding:7px 14px;font-size:0.875rem;line-height:1.2;
  border:1px solid var(--line-control);border-radius:999px;color:var(--ink);background:#fff;
  text-decoration:none;transition:border-color .15s,color .15s;}
a.sortbar__item:hover{border-color:var(--brand);color:var(--brand);}
.sortbar__item.is-active{background:var(--brand);border-color:var(--brand);color:var(--on-brand,#fff);}
@media(max-width:640px){
  .sortbar{gap:6px;}
  .sortbar__label{width:100%;margin-bottom:2px;}
  .sortbar__item{padding:6px 12px;font-size:0.8125rem;}
}

/* Раздел без позиций: страница «под заказ».
   Не декоративная заглушка, а рабочий блок — на сайте мостовых кранов такие
   страницы составляют 83 раздела из 93, и через них идёт основной трафик по
   запросам вида «мостовой кран 20 тонн». */
.to-order{margin:8px 0 8px;padding:32px;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--radius);}
.to-order h2{font-size:1.5rem;font-weight:800;margin:0 0 10px;color:var(--ink);}
.to-order p{max-width:62ch;color:var(--muted);margin:0;}
.to-order__actions{display:flex;gap:12px;flex-wrap:wrap;margin-top:20px;}
.to-order__near{margin-top:16px;font-size:0.9375rem;}
.to-order__near a{color:var(--brand);font-weight:600;}
@media(max-width:640px){
  .to-order{padding:22px 18px;}
  .to-order h2{font-size:1.25rem;}
  .to-order__actions .btn{flex:1 1 100%;text-align:center;}
}

/* =========================================================
   БЛОГ — лента статей и страница статьи
   ========================================================= */
.blog-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:24px;}
.post-card{display:flex;flex-direction:column;background:#fff;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow-sm);transition:transform .15s,box-shadow .15s,border-color .15s;}
a.post-card:hover{transform:translateY(-4px);box-shadow:var(--shadow);border-color:var(--brand);}
.post-cover{display:block;aspect-ratio:16/9;background:var(--surface);overflow:hidden;}
.post-cover img{width:100%;height:100%;object-fit:cover;display:block;}
.post-cover .ph{display:flex;align-items:center;justify-content:center;height:100%;font-family:var(--font-mono);font-size:0.75rem;letter-spacing:2px;text-transform:uppercase;color:var(--muted);}
.post-body{display:flex;flex-direction:column;flex:1;padding:22px 24px 24px;}
.post-date{font-family:var(--font-mono);font-size:0.7812rem;color:var(--muted);margin-bottom:10px;}
/* Заголовок карточки статьи — h2 на странице блога и h3 в блоке «Блог» на
   главной (там выше уже есть h2 «Статьи и руководства»). Оформление одно. */
.post-card h2,.post-card h3{font-family:var(--font-display);font-weight:800;font-size:1.1875rem;line-height:1.3;color:var(--ink);margin-bottom:10px;transition:color .15s;}
a.post-card:hover h2,a.post-card:hover h3{color:var(--brand);}
.post-card p{font-size:0.9062rem;color:var(--muted);line-height:1.6;margin-bottom:16px;}
.post-link{margin-top:auto;font-size:0.8438rem;font-weight:700;color:var(--brand);}
a.post-card:hover .post-link{text-decoration:underline;}

/* Ширина колонки статьи. Замер до правки: строка 820 px при шрифте 15,5 px —
   около 106 символов. Глаз теряет начало следующей строки после 75–80; норма
   для длинного текста 60–75. Ставим 68ch и поднимаем кегль до 17 px: 68ch при
   17 px — это те же ~700 px, но читаемые. Заголовок и обложка остаются во всю
   прежнюю ширину, чтобы страница не выглядела зажатой. */
/* Статья идёт во всю ширину контейнера, как и SEO-блок раздела.
   Было: колонка 820 px, а текст внутри неё ещё уже — 68 знаков. Пока в
   статьях были короткие заметки, это читалось; на импортированных
   материалах по полторы тысячи слов с таблицами на семь колонок узкая
   колонка режет таблицу по правому краю — владелец увидел это первым
   (26.08.2026). Ширину задаёт .container, отдельного потолка тут нет.

   Про длину строки. Узкая колонка ставилась ради удобного чтения, и это
   не выдумка: длинная строка читается хуже. Но обрезанная таблица хуже
   длинной строки, а таблицы есть во всех 74 статьях.

   Ограничения в 74 знака на абзацах тоже нет. Сначала я его оставил — и
   получил ту же картину, на которую владелец жаловался: заголовок во всю
   ширину, текст в левой половине, справа пустота. Решение по SEO-текстам
   разделов от 23.08.2026 было ровно таким же — «на всю ширину колонки
   текст растяни, как и таблицу», — и разводить два разных правила для
   двух видов длинного текста незачем. */
.article-page{max-width:none;margin:0;}
.article-page .article-body{max-width:none;}
.article-page .article-body > *{max-width:none;}
/* Селектор с тем же весом, что и общий `.seo-text :is(p,ul,ol)`, но идёт ниже
   по файлу — иначе кегль остаётся 15,5 px, как в SEO-врезках каталога. Там это
   уместно: текст под списком товаров читают по диагонали. Статью читают
   подряд, и для длинного чтения 15,5 px мало. */
.article-page .article-body :is(p,ul,ol){font-size:1.0625rem;line-height:1.75;color:var(--ink);margin-bottom:16px;}
.article-cover{margin:0 0 32px;border-radius:var(--radius);overflow:hidden;border:1px solid var(--line);}
/* Обложка занимала весь первый экран (818×460), и текст начинался за сгибом.
   Ограничиваем высоту: рисунок остаётся, но статья начинается сразу. */
/* Обложка статьи — фотография товара из каталога, а не широкоформатный
   баннер. `object-fit:cover` резал её посередине: снимок тали вертикальный,
   и в полосу высотой 300 px попадала середина корпуса без крюка и без
   цепи. `contain` показывает предмет целиком; фон белый, потому что все
   каталожные снимки сняты на белом и рамка иначе видна швом. */
.article-cover img{width:100%;display:block;height:320px;object-fit:contain;
  background:#fff;padding:14px;}
@media (max-width:640px){.article-cover img{height:220px;padding:8px;}}
/* Сгенерированная обложка 16:9 — фотография на весь кадр, ее не вписывают
   в полосу: на всю ширину колонки, без белых полей (класс ставит
   pages/article.php по пропорциям файла). В полных пропорциях на десктопе
   кадр выходил 670 px и уводил текст за первый экран, поэтому высота
   ограничена: 36% высоты окна, но не больше 380 px (на 1366×768 шапка
   с заголовком занимают 400 px, и текст должен успеть начаться). Лишнее
   срезается сверху и снизу, снизу чуть больше: пол кадра важен меньше,
   чем головы и груз. */
.article-cover--wide img{height:auto;aspect-ratio:16/9;max-height:min(380px,36vh);object-fit:cover;object-position:center 45%;padding:0;background:none;}
/* Картинки внутри текста — тем же порядком: предмет целиком, а не обрезок. */
.article-body figure img{max-height:420px;object-fit:contain;background:#fff;}
.article-body figure figcaption a{color:var(--brand);}
.article-body img{max-width:100%;height:auto;display:block;margin:24px auto;border-radius:var(--radius);border:1px solid var(--line);}
.article-body figure{margin:24px 0;}
.article-body figcaption{font-size:0.8125rem;color:var(--muted);text-align:center;margin-top:8px;}
/* Схемы в статье. Три вида, и каждый требует своего.

   1. Псевдографика (<pre>) — схемы, набранные символами. Держатся на
      моноширинном шрифте и пробелах, поэтому никакого переноса строк:
      узкий экран прокручивает блок вбок, а не ломает рисунок.
   2. Mermaid — рисуется скриптом уже в браузере, до отрисовки в блоке
      лежит исходный текст. Пока он не превратился в схему, показывать
      его читателю незачем: скрываем до появления тега <svg> внутри.
   3. Чертёж (inline <svg>) — вставляется в текст как есть. */
.article-body pre{font-family:ui-monospace,"SFMono-Regular",Menlo,Consolas,monospace;
  font-size:0.8125rem;line-height:1.45;background:#f8fafd;border:1px solid #d6eaf8;
  border-radius:10px;padding:16px;margin:24px 0;overflow-x:auto;white-space:pre;
  color:#2c3e50;}
@media (max-width:640px){.article-body pre{font-size:0.75rem;padding:12px;}}
.article-body .mermaid{background:#f8fafd;border:1px solid #d6eaf8;border-radius:10px;
  padding:20px 16px;margin:28px 0;overflow-x:auto;text-align:center;}
.article-body .mermaid:not(:has(svg)){visibility:hidden;height:0;padding:0;margin:0;border:0;}
.article-body .mermaid svg{max-width:100%;height:auto;margin:auto;}
/* Подпись узла Mermaid рисует не текстом, а разметкой внутри foreignObject —
   и там оказывается обычный <p>. Правило статьи `.article-body p{color:…}`
   перебивало заданный схемой белый цвет, и подпись на тёмно-синем узле шла
   почти чёрной: контраст 1,7:1 при норме 4,5:1 (замер 01.09.2026). Наследуем
   цвет от подписи — у неё он от схемы, и на светлом узле останется тёмным. */
.article-body .mermaid p,
.article-body .mermaid span,
.article-body .mermaid div{color:inherit;margin:0;}
.article-body svg{max-width:100%;height:auto;display:block;margin:28px auto;
  border-radius:8px;}
/* Цитата из документа: в статьях блога и в тексте рекомендательного
   письма на странице отзыва — оформление одно. */
.article-body blockquote,.seo-text blockquote,.letter-paper blockquote{border-left:3px solid var(--brand);padding:6px 0 6px 18px;margin:20px 0;color:var(--ink);font-style:italic;}
/* Цитата в тексте раздела живёт на подложке, а не на белом, и курсивом
   набирать её незачем: это не чужая речь из письма, а выделенная мысль.
   Даём ей белую карточку — тот же приём, что у таблицы. */
.seo-text blockquote{background:#fff;border-radius:0 var(--radius-sm) var(--radius-sm) 0;
  padding:14px 18px;font-style:normal;font-size:1rem;}
.seo-text blockquote p{margin-bottom:0;color:var(--ink);}
.seo-text blockquote p + p{margin-top:10px;}
/* Форма заявки внутри статьи. Владелец 27.08.2026: «сделай вдвое уже и
   отцентрируй, как картинки». Во всю ширину колонки поле ввода имени
   растягивалось на 1100 px — под два слова это выглядит как ошибка
   вёрстки, а не как форма.

   Селектор с двумя классами родителя не для красоты: правилом
   `.article-page .article-body > *{max-width:none}` статья разливается на
   всю ширину, и одиночный `.article-form` оно перебивает по специфичности
   — форма осталась во всю колонку, хотя правило было написано. */
.article-page .article-body > .article-form{max-width:560px;margin:32px auto;}
@media (max-width:640px){
  .article-page .article-body > .article-form{max-width:none;margin:24px 0;}
}

/* Подпись материала. Владелец 27.08.2026 выбрал вариант без имени и лица:
   «Материал подготовлен техническим отделом TOR24». Так же подписывает
   материалы Mazzella — крупнейший в теме такелажа. Вид сдержанный:
   это служебная врезка, а не второй призыв к действию. */
.article-author{margin:40px 0 0;padding:24px 26px;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--radius);}
.article-author h2{font-family:var(--font-display);font-weight:700;
  font-size:1.125rem;margin:0 0 12px;}
.article-author p{font-size:0.9375rem;line-height:1.65;color:var(--ink);
  margin:0 0 10px;max-width:none;}
.article-author p:last-child{margin-bottom:0;}
.article-author a{color:var(--brand);font-weight:600;}
@media (max-width:640px){.article-author{padding:18px 16px;}}

/* «Читайте также» — пять статей с миниатюрами. Владелец 27.08.2026:
   каждая статья должна ссылаться на пять других. Миниатюры те же, что в
   списке блога (assets/blog/<slug>/card.jpg): вторую заготовку под тот же
   размер заводить незачем. */
.article-related{margin:40px 0 8px;padding-top:24px;border-top:1px solid var(--line);}
.article-related h2{font-family:var(--font-display);font-weight:700;
  font-size:1.25rem;margin-bottom:18px;}
/* minmax(0,1fr), а не 1fr: у грид-элемента min-width:auto, и длинное слово
   в подписи распирает колонку — на 320 px при крупном кегле блок «Читайте
   также» уезжал вбок на 42 px (замер tools/audit_overflow.py 01.09.2026). */
.article-related ul{display:grid;grid-template-columns:repeat(5,minmax(0,1fr));
  gap:16px;list-style:none;margin:0;padding:0;}
/* Точку с пункта снимаем селектором той же формы, что у общего правила
   `.seo-text ul > li{list-style:disc}` — иначе оно перебивает по
   специфичности, и слева от каждой карточки висит маркер. Тело статьи
   несёт класс seo-text, так что правило до него достаёт. */
.article-related li,
.seo-text .article-related ul > li,
.pd-description .article-related ul > li{margin:0;list-style:none;}
.article-related a{display:flex;flex-direction:column;gap:8px;
  color:var(--ink);text-decoration:none;}
.article-related img{width:100%;aspect-ratio:16/9;object-fit:contain;
  background:#fff;border:1px solid var(--line);border-radius:var(--radius);
  display:block;margin:0;}
.article-related span{font-size:0.875rem;line-height:1.4;overflow-wrap:anywhere;}
.article-related a:hover span{color:var(--brand);text-decoration:underline;}
@media (max-width:1024px){.article-related ul{grid-template-columns:repeat(3,minmax(0,1fr));}}
@media (max-width:640px){.article-related ul{grid-template-columns:repeat(2,minmax(0,1fr));}}

.article-cat{margin-top:36px;padding-top:20px;border-top:1px solid var(--line-2);font-size:0.9062rem;color:var(--muted);}
.article-cat a{color:var(--brand);font-weight:700;margin-left:6px;}

/* =========================================================
   АДАПТИВ
   ========================================================= */
@media (max-width:1080px){
  .products .grid{grid-template-columns:repeat(3,minmax(0,1fr));}
  .header-search{max-width:340px;}
}
@media (max-width:920px){
  .hero h1{font-size:2.375rem;}
  .advantages .grid,.how-grid{grid-template-columns:repeat(2,1fr);}
  .seo-grid{grid-template-columns:minmax(0,1fr);gap:28px;}
  .categories .grid,.cat-overview-grid,.products .grid,.reviews .grid,.blog-grid{grid-template-columns:repeat(2,minmax(0,1fr));}
  .product-detail{grid-template-columns:minmax(0,1fr);}
  .footer-grid{grid-template-columns:1fr 1fr;}
  /* шапка переносится: логотип+кнопки в строку, поиск на всю ширину снизу */
  .header-main{height:auto;flex-wrap:wrap;padding-top:14px;padding-bottom:14px;gap:14px;}
  .header-search{order:3;flex-basis:100%;max-width:none;}
  .header-phone{display:none;}
}
@media (max-width:600px){
  /* Логотип с подзаголовком занимает 218 px, кнопка «Заявка» — 101, между ними
     зазор 14. В 327 px контентной ширины это не влезает на шесть пикселей, и
     шапка разъезжается на три строки вместо двух — 178 px вместо 126.
     Подзаголовок на телефоне убираем: он дословно повторяет H1 первого экрана. */
  .logo .logo-text > span{display:none;}
  .header-main{gap:10px;padding-top:12px;padding-bottom:12px;}
  /* Адрес склада и почта в верхней полосе — то же самое, что на «Контактах»
     и в подвале. На телефоне они не помещаются и утаскивают полосу на третью
     строку, поэтому убираем их, а пункт выдачи и зону доставки оставляем. */
  .topbar .tb-extra{display:none;}
  /* Перенос разрешаем ВСЕМ кнопкам на мобильной ширине, а не отдельным.
     Причина: при крупном шрифте браузера за экран выходит и кнопка с зашитой
     подписью — «Не нашли нужное — напишите нам» занимает 387 px при экране
     375 и корне 24 px (нашёл tools/audit_overflow.py на первом же прогоне).
     Перечислять такие кнопки по одной бессмысленно: ширина зависит от кегля,
     а не от текста. При обычном кегле кнопки помещаются и так, вид не
     меняется. */
  .btn{white-space:normal;}
  /* Кнопка отправки в форме заявки была 41 px — палец просит 44. Отступ 14
     давал 43, поэтому 15: строка 15 px плюс 15 сверху и снизу — ровно 45,
     как у кнопок первого экрана после цикла 72. */
  .lead-form button[type="submit"]{padding-top:15px;padding-bottom:15px;}
  /* Первый экран телефона. Замер на самом длинном заголовке сети (кран-балки,
     52 символа, три строки): кнопки «Открыть каталог» и «Запросить КП» стояли
     на 758 и 813 px при экране 667 — то есть главное действие человек не видел,
     пока не прокрутит.
     Что поджато: верхний отступ героя 54 → 28, кегль заголовка 30 → 28,
     а поиск внутри героя на телефоне убран совсем — он дословно дублирует поиск
     в шапке, который на 104 px и виден всегда. Экономия 97 + 26 + 12 = 135 px. */
  .hero{padding:28px 0 44px;}
  .hero h1{font-size:1.75rem;}
  .hero-search{display:none;}
  .hero .cta-row{margin-bottom:22px;gap:10px;}
  /* Замер по всем десяти сайтам: обе кнопки не влезали в строку 327 px
     (176 + 157 + 14 = 347) и переносились — вторая кнопка уезжала на 57 px вниз
     и на семи сайтах из десяти оказывалась за сгибом. Горизонтальные отступы
     24 → 12 дают 152 + 133 + 10 = 295, и строка одна. Перенос НЕ отключаю:
     если текст кнопки когда-нибудь удлинится, пусть лучше строка разъедется,
     чем появится горизонтальная прокрутка (цикл 63). Вертикальные отступы
     13 → 14, чтобы палец попадал: высота кнопки была 43 px. */
  .hero .cta-row .btn{padding:14px 12px;}
  /* Подзаголовок героя — шесть строк по 27,9 px, это 167 px первого экрана.
     16/24 вместо 18/27,9 экономит 23 px, отступ снизу — ещё десять.
     Целиться надо в `.lead`, а не в `.hero p`: под общее правило попадает и
     надзаголовок, он от увеличения кегля переносится на вторую строку, и первый
     экран становится на 20 px длиннее вместо короче. Проверено замером. */
  /* Селектор с тем же весом, что базовый `.hero p.lead`: правило `.hero .lead`
     слабее на один элемент и молча не применяется. Тот же случай, что с
     кеглем статьи в цикле 56 — и он снова прошёл незамеченным до замера. */
  .hero p.lead{font-size:1rem;line-height:1.5;margin-bottom:18px;}
  /* Плашка cookie на телефоне занимала 150 px — три строки текста плюс кнопка,
     и закрывала ровно то место, куда мы только что подняли главные кнопки.
     Поджимаем отступы и кегль: смысл тот же, высота около 100 px. Совсем
     убрать перекрытие нельзя — плашка по определению висит снизу, — но после
     первого нажатия «Принять» она больше не появляется. */
  .cookie-banner{left:10px;right:10px;bottom:10px;padding:10px 14px;gap:10px;font-size:0.8125rem;}
  .cookie-banner button{padding:8px 16px;font-size:0.8125rem;}
  .advantages .grid,.how-grid,.categories .grid,.cat-overview-grid,.products .grid,.reviews .grid,.blog-grid{grid-template-columns:1fr;}
  .footer-grid{grid-template-columns:1fr;gap:28px;}
  .section-head{flex-direction:column;align-items:flex-start;}
  section{padding:48px 0;}
  .cta-final .box{flex-direction:column;align-items:flex-start;}
}

/* -------- пагинация листингов -------- */
/* Переключатель страниц по центру (владелец, 27.08.2026). Прижатый влево
   блок под сеткой из трёх колонок читается как продолжение первой из них;
   по центру он виден как отдельный орган управления. */
.pagination{
  display:flex;flex-wrap:wrap;align-items:center;justify-content:center;
  gap:6px;margin:28px 0 8px;
}
.pagination .pg{
  min-width:38px;height:38px;padding:0 11px;
  display:inline-flex;align-items:center;justify-content:center;
  border:1px solid var(--line-control);border-radius:var(--radius-sm);
  font-family:var(--font-mono);font-size:0.8125rem;color:var(--ink);
  background:var(--bg);transition:border-color .15s,background .15s;
}
.pagination a.pg:hover{border-color:var(--brand);color:var(--brand);}
.pagination .pg-cur{background:var(--brand);border-color:var(--brand);color:#fff;font-weight:600;}
.pagination .pg-gap{border:none;background:none;color:var(--muted);min-width:20px;padding:0;}
.pagination .pg-nav{font-family:var(--font-body);font-weight:600;}
@media (max-width:560px){
  .pagination .pg-nav{width:100%;}
}

/* Размер шрифта, заданный в браузере. Кегли переведены в rem (цикл 73), и
   теперь «Крупный» в настройках Chrome (корень 20 px вместо 16) поднимает
   весь текст. Вместе с текстом растут и названия разделов в меню: при корне
   24 px ссылка «Консольные и поворотные кран-балки» занимает 381 px и на
   экране 375 утаскивает страницу вбок (405 вместо 375). Разрешаем перенос на
   всю мобильную ширину: при обычном кегле самая длинная ссылка — 254 px из
   343 доступных, так что правило до поры ничего не меняет. */
@media (max-width:600px){
  .nav-row a{white-space:normal;}
}
/* Одноколоночный герой не должен держать пол по длинному слову. */
@media (max-width:920px){
  .hero-grid{grid-template-columns:minmax(0,1fr);}
}

/* Зум браузера. Увеличение страницы в Chrome — это то же самое, что сужение
   макета: 1280 при 200 % раскладывается как 640 CSS-px, при 400 % — как 320.
   Замер на кран-балках (03.08.2026): до 320 px включительно горизонтальной
   прокрутки нет ни на главной, ни в каталоге, ни на контактах. Ниже 278 px
   страница начинает ехать вбок, и упирается она не в вёрстку, а в длинные
   названия разделов: ссылка «Консольные и поворотные кран-балки» занимает
   254 px и уже не переносится.
   320 px — это признанный порог (WCAG 1.4.10), и он держится. Но и ниже пусть
   лучше рвётся слово, чем уезжает вся страница: горизонтальная прокрутка
   читается как поломка. Правило включаем только на совсем узких ширинах,
   чтобы на обычном телефоне переносов посреди слова не появилось. */
@media (max-width:340px){
  /* Ссылкам меню разрешаем перенос: у них `white-space:nowrap`, и никакой
     `overflow-wrap` против него не работает — правило, поставленное сюда
     первым, не дало ни одного пикселя (замер: как было 278, так и осталось).
     Название раздела в две строки на такой ширине нормально. */
  .nav-row a{white-space:normal;}
  .btn{white-space:normal;}
  h1, h2, h3, p, .nav-row a, .hero-cats a, .breadcrumbs a, .product-card h3{
    overflow-wrap:anywhere;
  }
  /* Колонка `1fr` не сжимается ниже min-content своего содержимого — из-за
     этого герой держал 260 px на экране 250. `minmax(0,1fr)` снимает этот
     пол, а перенос слов выше не даёт тексту вылезти. */
  .hero-grid{grid-template-columns:minmax(0,1fr);}
  /* Поле поиска по умолчанию просит 224 px (size=20). */
  .header-search input{min-width:0;}
  /* Блок преимуществ разложен через `minmax(210px,1fr)` — это жёсткий пол в
     210 px плюс поля, из-за него главная держала 274 px. На такой ширине
     колонка всё равно одна. */
  .advantages .grid{grid-template-columns:1fr;}
  /* Карусель отзывов. Первым делом поджал поля слайда — не дало ни пикселя
     (274 как было, так и осталось). Виноват оказался ряд точек переключения,
     он вынесен в общее правило `.rv-dots`. Поля всё же оставляю: на 250 px
     28 px по бокам съедают восьмую часть ширины. */
  .rv-slide{padding:18px 14px;}
  .rv-slide *{overflow-wrap:anywhere;}
}

/* Широкое содержимое прокручивается внутри себя, а не двигает страницу.
   Таблицы характеристик и реквизитов на телефоне не помещаются: в реквизитах
   IBAN из двадцати символов и адрес в одну строку. Раньше вместе с таблицей
   вбок уезжала вся страница. */
.pd-description table,.seo-text table{display:block;overflow-x:auto;max-width:100%;}
/* В статьях блога таблица — не приложение к тексту, а сам предмет: матрица
   характеристик на семь колонок. `display:block` отбирает у неё табличную
   раскладку, и колонки не тянутся на всю ширину. Возвращаем таблицу,
   прокрутку отдаём обёртке .article-table (её ставит импорт статей). */
.article-body > table{display:table;width:100%;}
.article-table{overflow-x:auto;max-width:100%;margin:20px 0;}
.article-table table{display:table;width:100%;margin:0;}
/* Но не там, где прокруткой занимается обёртка `.seo-table`: `display:block`
   отбирает у таблицы табличную раскладку, и колонки перестают тянуться на
   всю ширину — строки зебры обрывались посередине карточки. Обёртку ставит
   tools/install_texts.py, поэтому здесь возвращаем таблице её поведение. */
.seo-text .seo-table table{display:table;overflow:visible;width:100%;}

/* Таблицы «свойство → значение» (характеристики, реквизиты) на телефоне не
   прокручиваем вбок, а раскладываем в столбик: горизонтальная прокрутка внутри
   страницы читается как поломка, а пара «ИИН / 931021350681» отлично живёт
   двумя строками. Ширина 520 px бралась из самого длинного значения — адреса
   с индексом и номерами офисов. */
@media (max-width:600px){
  /* Селектор именно `table.specs`: правило выше задаёт min-width:520px с той же
     специфичностью, и с одним классом это правило проигрывало молча. */
  table.specs,table.specs tbody,table.specs tr,table.specs th,table.specs td{display:block;width:auto;}
  table.specs{min-width:0;}
  table.specs tr{padding:10px 14px;border-bottom:1px solid var(--line);}
  table.specs tr:last-child{border-bottom:0;}
  table.specs th,table.specs td{border:0;padding:0;word-break:break-word;background:none;}
  table.specs th{font-size:0.7188rem;margin-bottom:2px;}
  table.specs tr:hover td{background:none;}
}

/* Длинное слово не должно распирать страницу. «Политика конфиденциальности»
   в заголовке первого уровня на экране 375 px не помещается в одну строку и
   двигала весь макет вбок на 16 px. break-word рвёт только то, что иначе
   не влезает, — на коротких словах ничего не меняется.

   `anywhere` вместо `break-word` — рисуются они одинаково, но `anywhere`
   ещё и уменьшает min-content заголовка. Это важно там, где родитель меряет
   ширину по содержимому: при крупном шрифте браузера на экране 320 px
   заголовок «Статьи и руководства» распирал блок с `break-word`, а с
   `anywhere` перестал. */
h1,h2,h3,h4,.cat-card h3,.cat-card h2,.product-card h3,.breadcrumbs{overflow-wrap:anywhere;}

/* Карточка без фото, характеристик и описания: одна колонка вместо двух,
   иначе половина экрана остаётся пустой и страница читается как сломанная. */
.product-detail-bare{grid-template-columns:minmax(0,1fr);max-width:720px;}
.pd-nodata{margin-top:18px;color:var(--muted);font-size:0.9375rem;line-height:1.6;}

/* На телефоне меню каталога компактнее: мельче шрифт и меньше зазор, иначе
   восемь пунктов в две-три строки съедают половину первого экрана. */
@media (max-width:600px){
  .nav-row .container{gap:6px 14px;padding-top:8px;padding-bottom:8px;min-height:0;}
  .nav-row a{font-size:0.8125rem;line-height:1.25;}
  /* На телефоне восемь пунктов переносом занимали 209 px — четверть экрана до
     первого блока. Оставляем «Все категории» и четыре раздела, остальное на
     расстоянии одного касания по первой ссылке. */
  .nav-row a:nth-child(n+6){display:none;}
}


/* =========================================================
   ПЛОТНОСТЬ И ФОРМА
   Внутри одной палитры сайты различаются скруглением и весом заголовков.
   Три класса на <body> из поля `shape` в site.yml. Меняются только токены:
   правила компонентов общие, иначе набор стилей размножится.
   ========================================================= */
body.shape-soft{   --radius:16px; --radius-sm:11px; --h-weight:800; }
body.shape-square{ --radius:6px;  --radius-sm:4px;  --h-weight:700; }
body.shape-sharp{  --radius:2px;  --radius-sm:2px;  --h-weight:800; }

/* =========================================================
   ПАЛИТРЫ САЙТОВ
   Четыре направления из дизайн-бандла. Класс ставится на <body> из
   поля `palette` в site.yml: сеть на одном сервере не должна выглядеть
   как десять копий одного шаблона, а перекрашивать движок под каждый
   сайт — значит завести десять копий стилей.
   Переопределяются только токены, ни одного правила компонентов.
   ========================================================= */

/* Чертёж — один цвет вместо двух, бумажный фон */
body.theme-blueprint{
  --bg:#FBFBF9; --surface:#F1F2EE; --surface-2:#E6E8E2;
  --ink:#1A1D21; --muted:#606670; --line:#D5D8D2; --line-2:#E4E6E0;
  --brand:#1B3A6B; --brand-700:#16305A; --brand-900:#122A50; --brand-50:#E8EDF5;
  --accent:#1B3A6B; --accent-50:#E8EDF5; --ok:#2A6B45; --on-accent:#fff;
  --brand-shadow:rgba(27,58,107,.26); --accent-glow:rgba(70,110,170,.32);
  --footer-bg:#122A50; --footer-text:#AFBBD1; --footer-muted:#94A2BC;
  --footer-line:#1F3A64; --footer-bottom:#8494B0; --footer-mark:#9DBBE8;
}
/* Кобальт — глубокий синий с медным акцентом */
body.theme-cobalt{
  --bg:#FFFFFF; --surface:#F5F7FA; --surface-2:#E9EEF4;
  --ink:#101720; --muted:#586374; --line:#DBE2EA; --line-2:#EAEFF4;
  --brand:#123B7A; --brand-700:#0E3266; --brand-900:#0B2A5C; --brand-50:#E7EDF7;
  --accent:#C8500F; --accent-50:#FCEFE6; --ok:#1B6E48; --on-accent:#fff;
  --brand-shadow:rgba(18,59,122,.26); --accent-glow:rgba(200,80,15,.32);
  --footer-bg:#0B2A5C; --footer-text:#AEBCD3; --footer-muted:#92A2BD;
  --footer-line:#173A68; --footer-bottom:#8393B1; --footer-mark:#E9A177;
}
/* Цех — почти чёрный корпус, жёлтый предупреждающий акцент */
body.theme-shopfloor{
  --bg:#FFFFFF; --surface:#F4F2ED; --surface-2:#E9E5DC;
  --ink:#1C1A17; --muted:#635E55; --line:#DCD7CC; --line-2:#EBE7DF;
  --brand:#26241F; --brand-700:#1B1A16; --brand-900:#12110F; --brand-50:#F0EDE5;
  /* Жёлтый акцент «Цеха» с белым текстом даёт 2.02 — нечитаемо.
     Тёмный текст на нём же: 8.59. */
  --accent:#F2A81D; --accent-50:#FDF3DF; --ok:#3F7A3B; --on-accent:#1C1A17;
  --brand-shadow:rgba(38,36,31,.24); --accent-glow:rgba(242,168,29,.34);
  --footer-bg:#12110F; --footer-text:#B6B0A6; --footer-muted:#9A948A;
  --footer-line:#2A2823; --footer-bottom:#8B857B; --footer-mark:#F2A81D;
}
/* Сталь и сигнал — графит как основа, красный только на действии */
body.theme-steel{
  --bg:#FFFFFF; --surface:#F2F4F6; --surface-2:#E7EAEE;
  --ink:#16191D; --muted:#5D646E; --line:#D8DDE3; --line-2:#E9ECEF;
  --brand:#2B3138; --brand-700:#1F2429; --brand-900:#16191D; --brand-50:#EDEFF2;
  /* Сигнальный красный был #E8471F — с белым текстом 3.92, ниже порога.
     #D93E17 даёт 4.51 и на глаз тот же цвет. */
  --accent:#D93E17; --accent-50:#FDEEE9; --ok:#1E7A44; --on-accent:#fff;
  --brand-shadow:rgba(43,49,56,.24); --accent-glow:rgba(232,71,31,.32);
  --footer-bg:#16191D; --footer-text:#B0B7C1; --footer-muted:#949BA6;
  --footer-line:#2A3037; --footer-bottom:#868D98; --footer-mark:#E8815F;
}

/* =========================================================
   РЕЖИМ ВЫСОКОЙ КОНТРАСТНОСТИ WINDOWS
   =========================================================
   В этом режиме браузер подменяет цвета текста и фонов системными, но
   фоновые картинки и градиенты оставляет как есть. Для нашей вёрстки это
   три известных места:

   * герой — белый текст поверх градиента: цвет текста система заменит, а
     градиент останется, и надпись может лечь на светлое;
   * кнопки различаются только заливкой — без рамки они сливаются с текстом;
   * точки карусели и полоски — это цветные прямоугольники без содержимого,
     они пропадают совсем.

   Сам режим включается в настройках Windows, эмулировать его из браузера
   нельзя, поэтому правила написаны по документированному поведению режима,
   а не по замеру. Проверить глазами на машине с включённой высокой
   контрастностью — отдельная задача, она записана в PLAN.md. */
@media (forced-colors: active){
  .hero{background:Canvas;color:CanvasText;}
  .hero .eyebrow, .hero .lead, .hero h1{color:CanvasText;}
  .btn{border:1px solid ButtonText;}
  .rv-dot{border:1px solid ButtonText;}
  .gallery-main img{mix-blend-mode:normal;}
  /* Рамку фокуса система рисует сама, но цвет мы перебиваем белым — вернём
     системный, иначе на светлом фоне её не будет видно. */
  .hero a:focus-visible, .hero button:focus-visible, .hero input:focus-visible,
  footer.site-footer a:focus-visible, footer.site-footer button:focus-visible,
  footer.site-footer input:focus-visible{outline-color:Highlight;}
}

/* =========================================================
   ПЕЧАТЬ И «СОХРАНИТЬ КАК PDF»
   Снабженец печатает три вещи: карточку товара (как основу КП), реквизиты и
   рекомендательное письмо. До этого печаталась вся страница целиком — тёмный
   первый экран, меню, поиск, плашка cookie, футер с колонками ссылок и кнопки,
   на которые в бумаге нажать нельзя. Печатаем только содержимое, чёрным по
   белому, и дописываем к странице контакты продавца — иначе на распечатке
   негде посмотреть, кому звонить.
   ========================================================= */
@media print {
  /* Служебное — вон */
  .topbar, .site-header, .main-nav, .search, .header-search, .sortbar,
  .cookie-banner, .modal-overlay, .scroll-top, .breadcrumbs,
  .site-footer, .cta-final, .blog-home, .home-seo, .faq, .how,
  .advantages, .hero, .products .btn, .pc-actions, .pd-actions, .related,
  .to-order__actions, .pagination, .cat-overview-grid, .article-cat,
  [data-modal-open], [data-copy] { display: none !important; }

  html, body { background: #fff !important; color: #000 !important; }
  body { font-size: 11pt; }
  .container { max-width: none; padding: 0; }
  section { padding: 0 !important; }

  a { color: #000 !important; text-decoration: none; }
  /* Адрес ссылки в бумаге не кликается — печатаем его текстом, но только для
     ссылок внутри содержимого, иначе распечатка тонет в служебных адресах. */
  .article-body a[href^="http"]::after,
  .pd-description a[href^="http"]::after { content: " (" attr(href) ")"; font-size: 9pt; }

  h1 { font-size: 18pt; margin: 0 0 8pt; }
  h2 { font-size: 13pt; margin: 12pt 0 6pt; page-break-after: avoid; }
  table, .specs { width: 100%; border-collapse: collapse; page-break-inside: auto; }
  .specs td, .specs th { border: 1px solid #999; padding: 4pt 6pt; }
  tr, .review-card, .product-card { page-break-inside: avoid; }
  img { max-width: 60mm !important; height: auto !important; }
  .gallery-main, .article-cover { max-width: 80mm; }
  .price, .pd-price { font-size: 14pt; font-weight: 700; }

  /* Контакты продавца в конце распечатки. Значения подставляет шаблон
     (includes/footer.php) из sites/_common.yml — здесь только место под них. */
  .print-contacts { display: block !important; margin-top: 10mm; padding-top: 4mm;
    border-top: 1px solid #999; font-size: 10pt; line-height: 1.5; }
}
.print-contacts { display: none; }

/* =========================================================================
   Карточка товара по ТЗ (03.08.2026): область покупки, доставка по массе,
   бренд, сравнение линейки.
   ========================================================================= */

/* Пометка под ценой: дата актуальности и условия для юрлица. Мелкая — цена
   должна остаться единственным крупным акцентом области покупки (ТЗ 5.4). */
.pd-price-note{margin:2px 0 14px;font-size:.8125rem;color:var(--muted);}

/* Ключевые параметры до кнопки. Человек решает по ним, а не по полной
   таблице ниже: сводку над таблицей даёт лишь 3 % сайтов. */
.pd-key{display:grid;grid-template-columns:minmax(0,auto) minmax(0,1fr);
  gap:6px 16px;margin:0 0 18px;
  padding:14px 0 0;border-top:1px solid var(--line);font-size:.9375rem;}
.pd-key dt{color:var(--muted);}
.pd-key dd{margin:0;font-weight:600;font-family:var(--font-mono);font-size:.875rem;}
/* Колонка `auto` не сжимается ниже самого длинного слова, а название параметра
   бывает в одно длинное слово («Грузоподъёмность»). Замер 04.08.2026 на
   козловых кранах: при кегле 24 список уезжал за экран на 262 px при ширине
   375 и на 207 при 320. minmax(0,…) снимает нижний предел, перенос по слогам
   доламывает то, что всё равно не влезает. */
.pd-key dt, .pd-key dd{overflow-wrap:anywhere;hyphens:auto;}
@media (max-width:420px){
  /* На узком экране пара «название — значение» встаёт в две строки: делить
     390 px на две колонки бессмысленно, значению остаётся полтора слова. */
  .pd-key{grid-template-columns:1fr;gap:2px 0;}
  .pd-key dd{margin-bottom:8px;}
}

/* Четыре обещания под кнопкой. Ведут на страницы-источники: текст условий
   живёт в одном месте и здесь не дублируется (ТЗ 4.12). */
.pd-assure{list-style:none;margin:16px 0 0;padding:14px 0 0;
  border-top:1px solid var(--line);display:grid;gap:7px;font-size:.875rem;}
.pd-assure a{color:var(--ink);text-decoration:none;}
.pd-assure a:hover{color:var(--brand);text-decoration:underline;}

/* Доставка, собранная по массе позиции (ТЗ 4.5). */
.pd-ship,.pd-brand,.pd-line{margin-top:34px;}
.pd-ship h2,.pd-brand h2,.pd-line h2{font-family:var(--font-display);
  font-size:1.375rem;margin-bottom:14px;}
.pd-ship p,.pd-brand p{margin:0 0 10px;max-width:74ch;}
.pd-ship-mass{background:var(--surface);border-left:3px solid var(--brand);
  padding:12px 16px;border-radius:0 var(--radius-sm) var(--radius-sm) 0;}

/* Сравнение линейки: текущая модель подсвечена, иначе таблица читается как
   список чужих товаров. */
.pd-line tr.is-current th,.pd-line tr.is-current td{background:var(--brand-50);}
.pd-line tr.is-current th{font-weight:700;}
.pd-line .muted{color:var(--muted);font-weight:400;font-size:.875rem;}

/* Документы и видео карточки товара. Плитка повторяет карточки документов
   на kazmehanika.kz: квадрат 56×56 слева, подпись и подсказка справа.
   Имена паспортов длинные («Паспорт и руководство по эксплуатации тали
   HHBD 24.10.2025»), поэтому подпись переносится, а не обрезается: обрубок
   в середине слова читается как недоделка. */
.pd-media{margin:28px 0;display:flex;flex-direction:column;gap:10px;align-items:flex-start;}
.pd-media-item{display:flex;gap:12px;align-items:center;width:100%;max-width:520px;
  padding:10px 12px;border:1px solid var(--line);border-radius:10px;
  background:var(--surface);text-align:left;cursor:pointer;text-decoration:none;
  color:inherit;font:inherit;transition:border-color .15s,background .15s;}
.pd-media-item:hover{border-color:var(--accent);background:var(--bg);}
.pd-media-badge{flex:0 0 56px;height:56px;border-radius:8px;display:flex;
  align-items:center;justify-content:center;font-weight:700;font-size:.8125rem;
  letter-spacing:.02em;color:#fff;}
.pd-media-badge--doc{background:none;align-items:flex-start;}
.pd-media-badge--doc svg{width:40px;height:48px;display:block;margin-top:4px;}
.pd-media-badge--video{background:var(--brand);font-size:1.125rem;}
.pd-media-thumb{position:relative;flex:0 0 56px;height:56px;border-radius:8px;overflow:hidden;}
.pd-media-thumb img{width:56px;height:56px;object-fit:cover;display:block;}
.pd-media-play{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;
  background:rgba(0,0,0,.35);color:#fff;font-size:1.125rem;}
.pd-media-text{display:flex;flex-direction:column;gap:2px;min-width:0;}
.pd-media-label{font-weight:600;line-height:1.3;}
.pd-media-hint{font-size:.8125rem;color:var(--muted);}

/* Окно с роликом. Плеер чужой, поэтому грузится ТОЛЬКО по нажатию: iframe
   в разметке тянул бы скрипты RuTube и VK на каждой карточке с видео,
   а таких 345. */
.pd-modal{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;
  justify-content:center;background:rgba(0,0,0,.75);padding:20px;}
.pd-modal[hidden]{display:none;}
.pd-modal-box{position:relative;width:min(960px,100%);aspect-ratio:16/9;background:#000;
  border-radius:12px;overflow:hidden;}
.pd-modal-box iframe{width:100%;height:100%;border:0;}
.pd-modal-close{position:absolute;top:-42px;right:0;background:none;border:0;color:#fff;
  font-size:1.75rem;line-height:1;cursor:pointer;padding:4px 8px;}
@media (max-width:600px){.pd-modal-close{top:auto;bottom:-42px;}}

/* Подбор по грузоподъёмности. Тот же вид, что у переключателя порядка
   (.sortbar): два разных на вид управления списком на одной странице
   читаются как две разные механики, хотя делают одно — сужают выдачу. */
.capfilter{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin:0 0 12px;}
.capfilter__label{font-size:.875rem;color:var(--muted);margin-right:2px;}
.capfilter__item{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;
  border:1px solid var(--line);border-radius:999px;font-size:.875rem;
  color:var(--ink);text-decoration:none;background:var(--bg);transition:border-color .15s,background .15s;}
.capfilter__item:hover{border-color:var(--accent);}
.capfilter__item.is-active{background:var(--brand);color:#fff;border-color:var(--brand);}
.capfilter__n{font-size:.75rem;opacity:.7;font-variant-numeric:tabular-nums;}

/* -------- подборка товаров внутри статьи -------- */
/* Владелец 03.09.2026: в статьях блога нужны карточки товаров из раздела, по
   2-3 штуки, в нескольких местах по ходу текста. Карточка берётся общая
   (includes/product-card.php) — своей вёрстки у блога нет намеренно, иначе она
   разойдётся с каталогом при первой же правке стилей. Здесь только рамка,
   шапка и сетка. */
.article-products{margin:28px 0;padding:18px;background:var(--surface);
  border:1px solid var(--line);border-radius:var(--radius);}
.article-products .ap-head{display:flex;align-items:baseline;justify-content:space-between;
  gap:12px;margin-bottom:14px;flex-wrap:wrap;}
.article-products .ap-head h3{margin:0;font-size:1.0625rem;}
.article-products .ap-all{font-family:var(--font-mono);font-size:0.8125rem;
  white-space:nowrap;color:var(--brand);}
/* Три карточки в ряд на десктопе, две на планшете, одна на телефоне: колонка
   статьи уже сетки каталога, и четыре карточки в ней сминаются. */
.article-products .ap-grid{display:grid;gap:12px;grid-template-columns:repeat(3,minmax(0,1fr));}
.article-products .product-card{padding:12px;}
.article-products .product-card h3{font-size:0.9375rem;}
@media (max-width:900px){.article-products .ap-grid{grid-template-columns:repeat(2,minmax(0,1fr));}}
@media (max-width:560px){.article-products .ap-grid{grid-template-columns:1fr;}}
