/* Общий CSS для страниц одной машины (/machine/<code>) и одной единицы
   навесного оборудования (/attachment/<slug>) — собственный модульный файл,
   не часть монолитного <style> в index.html (Allan, 2026-07-30: "используя
   современные требования построения страниц, использую модули"). Палитра —
   как в index.html (#E63012), а не задрейфовавший #CC2200 из admin/portal.
   Классы с префиксом .mp- общие для обоих типов страниц (топбар, hero,
   секции, карточки); .ap- — специфичные для страницы навесного (таблица
   вариантов/цен, см. attachment_page.py). */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

:root {
  --red:       #E63012;
  --red-dark:  #C42A0F;
  --black:     #111111;
  --dark:      #2A2A2A;
  --gray-700:  #4A4A4A;
  --gray-500:  #8A8A8A;
  --gray-300:  #D1D1D1;
  --gray-100:  #F3F3F3;
  --white:     #FFFFFF;
  --font:      'DM Sans', system-ui, -apple-system, sans-serif;
  --mono:      'Space Mono', 'SF Mono', monospace;
  --ease:      cubic-bezier(0.25, 0.46, 0.45, 0.94);
  --radius:    12px;
  --radius-sm: 8px;
  --shadow-sm: 0 1px 3px rgba(0,0,0,0.06);
  --shadow-md: 0 4px 16px rgba(0,0,0,0.08);
}

/* Allan, 2026-08-06: "сероватая заливка сделать белой" — общий фон для обоих
   типов страниц (машина/навесное, см. докстринг файла), меняем для обеих
   разом, а не только для навесного — иначе переход по ссылке между ними
   заметно "мигал" бы разным фоном без другой причины для расхождения. */
html, body { background: var(--white); color: var(--dark); font-family: var(--font); line-height: 1.5; }

/* ── Шапка — стили лежат в общем frontend/topbar.css, подключённом рядом с
   этим файлом. Здесь остаётся ЕДИНСТВЕННОЕ намеренное отличие страниц товара:
   шапка закреплена. Страница длинная (спеки + до 68 карточек навесного), и без
   sticky кнопки Parts/My Bobcat/Kontakt уезжали вверх насовсем — чтобы
   вернуться к ним, приходилось мотать всё обратно (Allan, 2026-08-04: "при
   скроле шапка уезжает"). На главной этого не нужно: там прокручивается
   внутренняя область сообщений, а шапка и так всегда на экране.
   Всё остальное — общее; дублировать правила здесь нельзя, копии разъезжаются. */
.topbar { position: sticky; top: 0; z-index: 50; }

/* ── Back row — отдельная полоса ПОД настоящей шапкой, не внутри неё, чтобы
   .topbar остался пиксель-в-пиксель как на главной. history.back() вместо
   ссылки на "/" — возвращает не просто на главную, а ровно в то состояние
   каталога (открытые группы, фильтры, скролл), в котором был переход сюда;
   href="/" — запасной вариант, если истории браузера нет (страницу открыли
   напрямую по ссылке, не переходом с сайта). */
/* sticky, top = высота .topbar (52px) — раньше .mp-backrow не был закреплён
   вовсе и уезжал при прокрутке отдельно от шапки над ним: на длинной
   странице (специи + до 68 карточек навесного) кнопка "Back"/"Tagasi"
   пропадала первой же прокруткой (Allan, 2026-08-06: "строка Back... уезжает
   при прокрутке"). z-index на 1 меньше топбара — если когда-нибудь окажутся
   внахлёст, топбар должен остаться поверх. */
.mp-backrow {
  display: flex; align-items: center; gap: 10px; padding: 10px 20px;
  background: var(--white); border-bottom: 1px solid var(--gray-300);
  font-size: 13px; color: var(--gray-500);
  position: sticky; top: 52px; z-index: 49;
}
.mp-back-btn {
  display: flex; align-items: center; gap: 4px; color: var(--black); text-decoration: none;
  font-weight: 700; font-size: 13px;
}
.mp-back-btn:hover { color: var(--red); }
.mp-crumb { text-transform: uppercase; letter-spacing: 0.03em; }
.mp-crumb::before { content: "/"; margin-right: 10px; color: var(--gray-300); }

/* Переключатель языка на страницах машин/навесного (Allan, 2026-08-06:
   "почему нет переключателей языка?"). Здесь язык страницы решает сервер
   (page_i18n.resolve_lang по cookie/Accept-Language — см. main.py), кнопка
   не переключает на лету, а перезагружает страницу с новым cookie (mpSetLang
   в product_page.js). Живёт в .mp-backrow (белый фон), поэтому стиль светлый
   — не тот тёмный "стеклянный" вариант, что на фотобаннере главной, тот
   утонул бы здесь в белом фоне. margin-left:auto прижимает вправо в общем
   flex-ряду с "Back"/крошкой. */
.lang-switch-page {
  display: flex; gap: 4px; margin-left: auto;
}
.lang-switch-page .lang-switch-btn {
  font-family: var(--font); font-size: 11px; font-weight: 700; letter-spacing: 0.02em;
  color: var(--gray-500); background: var(--white);
  border: 1.5px solid var(--gray-300); border-radius: 999px;
  padding: 3px 10px; cursor: pointer;
  transition: border-color 0.15s var(--ease), color 0.15s var(--ease);
}
.lang-switch-page .lang-switch-btn:hover { border-color: var(--red); color: var(--red); }
.lang-switch-page .lang-switch-btn.active {
  color: var(--white); background: var(--red); border-color: var(--red);
}

/* ── Pairing bar — same idea as .catalog-pair-bar on the catalogue (Allan,
   2026-08-03: "баннер спаривания должен быть и на страницах машин"), always
   docked here since the machine slot is fixed by definition (you're already
   on its page) — only the attachment slot is ever empty/fillable. Clicking a
   card in "Compatible Attachments" below fills it (see mpPickAttachment in
   product_page.js); "Open pair" hands the question to the same chat
   auto-send mechanism the homepage already uses (/?q=...). */
.mp-pair-bar {
  /* top = высота закреплённой .topbar (52px), иначе полка налезала бы на неё */
  position: sticky; top: 52px; z-index: 30;
  display: flex; align-items: center; gap: 16px;
  background: var(--white); border: 2px solid var(--red);
  border-radius: var(--radius-sm);
  padding: 10px 14px; margin: 14px 0 4px;
  box-shadow: 0 4px 16px rgba(0,0,0,0.10);
}
.mp-pair-item {
  display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0;
  border-radius: 6px; padding: 4px 6px; box-sizing: border-box;
  border: 1.5px solid transparent;
}
.mp-pair-item.empty { border-style: dashed; border-color: var(--gray-300); }
.mp-pair-item img, .mp-pair-slot-icon {
  width: 40px; height: 40px; object-fit: contain; background: var(--gray-100);
  border-radius: 4px; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  font-size: 18px; font-weight: 800; color: var(--gray-300); line-height: 1;
}
.mp-pair-slot-label {
  font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--gray-500); line-height: 1.3;
}
.mp-pair-item-title { font-size: 12px; font-weight: 700; letter-spacing: 0.01em; line-height: 1.25; }
.mp-pair-item.empty .mp-pair-item-title { color: var(--gray-500); font-weight: 600; }
.mp-pair-remove {
  margin-left: auto; flex-shrink: 0; width: 22px; height: 22px; padding: 0;
  border: 1.5px solid var(--gray-300); border-radius: 50%;
  background: var(--white); color: var(--black);
  font-size: 15px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.mp-pair-remove:hover { background: var(--red); border-color: var(--red); color: var(--white); }
.mp-pair-plus { font-size: 16px; font-weight: 800; color: var(--red); flex-shrink: 0; }
.mp-pair-btn {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.02em;
  padding: 9px 14px; border-radius: 4px; cursor: pointer; white-space: nowrap;
  border: 1.5px solid var(--red); background: var(--red); color: var(--white);
  flex-shrink: 0;
}
.mp-pair-btn[disabled] {
  background: var(--gray-100); border-color: var(--gray-300); color: var(--gray-500);
  cursor: default;
}
.mp-att-card.selected { outline: 2px solid var(--red); outline-offset: -2px; }
@media (max-width: 640px) {
  .mp-pair-bar { padding: 8px 12px; gap: 8px; flex-wrap: wrap; }
  .mp-pair-btn { width: 100%; }

  /* Allan, 2026-08-06: "переключатель языков стоит несимметрично" — на узком
     экране .mp-backrow сжимал "Back"/крошку до тех пор, пока "← Назад" не
     начинал переноситься посреди себя ("←" на одной строке, "Назад" под
     ним), а lang-switch не сжимался вовсе — разная высота левого/правого
     блока читалась как перекос. Тогдашний фикс — перенос всей строки
     целиком на 2 строки — решал перекос, но проиграл по месту на экране
     (Allan, 2026-08-10: "в 1 строку нужно"). Правильная причина перекоса
     была не "мало места", а то, что "←"+"Назад" ломались ПОСЕРЕДИНЕ слова:
     держим их одним нераздельным блоком (nowrap), а сжимается вместо этого
     крошка категории — с multi-эллипсисом, а не переносом. lang-switch как
     был flex-shrink:0 (не сжимается, всегда читаем), так и остаётся. */
  .mp-back-btn { flex-shrink: 0; white-space: nowrap; }
  .mp-crumb { flex-shrink: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .lang-switch-page { flex-shrink: 0; }
}

/* ── Hero ──────────────────────────────────────────────────────────────── */
.mp-hero {
  display: flex; gap: 32px; max-width: 1200px; margin: 0 auto; padding: 32px 20px;
}
.mp-hero-media { flex: 1 1 50%; min-width: 0; }
.mp-hero-img {
  width: 100%; height: 380px; object-fit: contain; background: var(--white);
  border-radius: var(--radius); box-shadow: var(--shadow-sm);
}
.mp-hero-thumbs { display: flex; gap: 8px; margin-top: 10px; overflow-x: auto; padding-bottom: 4px; }
.mp-hero-thumbs img {
  width: 64px; height: 64px; object-fit: contain; background: var(--white);
  border-radius: var(--radius-sm); border: 1.5px solid var(--gray-300); cursor: pointer; flex-shrink: 0;
}
.mp-hero-info { flex: 1 1 50%; min-width: 0; display: flex; flex-direction: column; gap: 14px; }
.mp-hero-info h1 { font-size: 28px; font-weight: 800; letter-spacing: -0.01em; }
.mp-hero-desc { color: var(--gray-700); font-size: 15px; line-height: 1.6; }
.mp-spec-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.mp-spec-chip {
  display: flex; flex-direction: column; gap: 2px; padding: 8px 12px;
  background: var(--white); border-radius: var(--radius-sm); box-shadow: var(--shadow-sm);
  font-size: 12px; min-width: 90px;
}
.mp-spec-chip span { color: var(--gray-500); text-transform: uppercase; letter-spacing: 0.03em; font-size: 10px; }
.mp-spec-chip b { font-size: 15px; font-weight: 800; }
/* Вторая величина того же параметра — сейчас только лошадиные силы рядом с
   киловаттами. Не отдельная плитка: это одно и то же число, записанное
   дважды, и разносить его по двум плиткам значило бы делать вид, что
   параметров больше, чем есть. */
.mp-spec-sub {
  font-style: normal; font-weight: 600; font-size: 12px;
  color: var(--gray-500); margin-left: 5px;
}
/* Ссылка, а не div (Allan, 2026-08-05: "нажатие создаёт текст запроса при
   помощи иньекции функции контакт") — клик по цене ведёт на тот же
   /?contact=1&about=<модель>, что и кнопка Kontakt в шапке. text-decoration/
   color сброшены, т.к. по умолчанию <a> красит контент синим и подчёркивает —
   .mp-price-label/.mp-price-value сами задают свой цвет, но эти два сброса
   всё равно нужны родителю, иначе рамка вокруг текста и курсор были бы от
   браузерных дефолтов ссылки, а не от карточки. */
a.mp-hero-price {
  display: flex; align-items: baseline; gap: 10px; padding: 16px;
  background: var(--white); border-radius: var(--radius); box-shadow: var(--shadow-sm); margin-top: auto;
  text-decoration: none; color: inherit; transition: box-shadow 0.15s ease;
}
a.mp-hero-price:hover { box-shadow: var(--shadow-md); }
.mp-price-label { font-size: 13px; color: var(--gray-500); }
.mp-price-value { font-size: 26px; font-weight: 800; color: var(--red); }
.mp-price-vat { font-size: 13px; color: var(--gray-500); }

/* "В наличии на складе" — точечная плашка со своей ценой (задача #99, Allan
   2026-08-11: "машина что в наличии имеет свою цену"), не общий элемент
   карточки: показывается только когда machine_page_data отдаёт in_stock для
   этой модели. Зелёный, не красный — не конкурирует с "From"-ценой ниже за
   внимание, читается как отдельный статус-факт с собственным CTA. */
.mp-in-stock-badge {
  display: flex; flex-direction: column; gap: 6px; align-items: flex-start;
  padding: 12px 14px; border-radius: var(--radius-sm);
  background: #f0f9f0; border: 1px solid #cde9cd;
  font-size: 13px; color: var(--dark);
}
.mp-in-stock-address { font-size: 12px; color: var(--gray-700); }
.mp-in-stock-price { font-size: 22px; font-weight: 800; color: var(--red); margin-top: 2px; }
.mp-in-stock-offer-btn {
  margin-top: 6px; padding: 9px 16px; border-radius: 999px;
  background: var(--red); color: var(--white); font-size: 13px; font-weight: 700;
  text-decoration: none; transition: background 0.15s var(--ease);
}
.mp-in-stock-offer-btn:hover { background: var(--red-dark); }

/* ── Generic section ───────────────────────────────────────────────────── */
.mp-section { max-width: 1200px; margin: 0 auto; padding: 0 20px 40px; }
.mp-section h2 { font-size: 22px; font-weight: 800; margin-bottom: 6px; }
.mp-section-sub { color: var(--gray-500); font-size: 13px; margin-bottom: 16px; max-width: 78ch; }

/* Дисклеймер источника данных (US-fallback, см. build_us_enrichment.py) —
   отдельная плашка, а не тихая серая строка: это важное уточнение точности
   данных, должно бросаться в глаза (Allan, 2026-07-30). */
.mp-us-note { padding-bottom: 16px; }
.mp-us-note .mp-section-sub {
  background: #FFF7E6; border: 1px solid #F5D98B; border-radius: var(--radius-sm);
  padding: 10px 14px; color: var(--gray-700); margin-bottom: 0; font-size: 12px;
}

/* ── Protection Plus / BobCARE PM (Allan, 2026-08-11) ─────────────────────
   Карточка того же типа, что .mp-feature-card (белый фон, тень, скругление),
   но двухколоночная: слева таблица покрытия конкретной категории техники +
   логотипы, справа текст про программы и кнопка-вопрос в чат. */
.mp-protection {
  display: flex; gap: 28px; align-items: flex-start; flex-wrap: wrap;
  background: var(--white); border-radius: var(--radius); box-shadow: var(--shadow-sm);
  padding: 24px; margin: 0 auto 40px; max-width: 1200px;
}
.mp-protection-media {
  display: flex; flex-direction: column; gap: 10px; align-items: center;
  flex: 0 0 260px; width: 260px;
}
.mp-protection-logo { width: 180px; max-width: 100%; }
.mp-protection-table { width: 100%; border-radius: var(--radius-sm); border: 1px solid var(--gray-200); }
.mp-protection-bobcare { width: 160px; max-width: 100%; border-radius: var(--radius-sm); }
.mp-protection-text { flex: 1 1 360px; min-width: 280px; }
.mp-protection-text h2 { margin-bottom: 14px; }
.mp-protection-list { list-style: none; margin: 0 0 18px; padding: 0; display: flex; flex-direction: column; gap: 12px; }
.mp-protection-list li { font-size: 14px; line-height: 1.5; color: var(--gray-700); }
.mp-protection-list strong { display: block; color: var(--black); font-size: 15px; margin-bottom: 2px; }
.mp-protection-ask {
  display: inline-block; padding: 10px 18px; border-radius: 999px;
  background: var(--red); color: var(--white); text-decoration: none;
  font-size: 13px; font-weight: 700; transition: background 0.15s var(--ease);
}
.mp-protection-ask:hover { background: var(--red-dark); }
@media (max-width: 640px) {
  .mp-protection { flex-direction: column; padding: 18px; }
  .mp-protection-media { flex: none; width: 100%; }
  .mp-protection-logo, .mp-protection-bobcare { width: 140px; }
}

/* ── Features ──────────────────────────────────────────────────────────── */
/* grid-auto-rows: 1px + dense — "masonry через JS": карточки разной высоты
   (короткое фото+пара строк рядом с текстом+буллетами+галереей) раньше
   тянулись до высоты самой высокой в своей строке, оставляя под короткими
   пустое поле (Allan, 2026-08-28, скриншот — "убрать воздух, левую нижнюю
   поднять наверх"). product_page.js:mpMasonry() после рендера/ресайза
   переводит реальную высоту каждой .mp-feature-card в
   grid-row-end: span N этим же 1px-юнитом — короткие карточки перетекают
   вверх в пустоты сами, без ручной раскладки. */
.mp-feature-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); grid-auto-flow: row dense; grid-auto-rows: 1px; align-items: start; gap: 16px; margin-top: 16px; }
/* Карточка длиннее соседей (текст+буллеты+галерея, напр. "First-class user
   experience") — растянуть на 2 колонки сетки вместо одной, иначе
   непропорционально высокая. На мобильном, где в сетке всего 1 колонка,
   span 2 сам сжимается до неё же (Allan, 2026-08-28). */
.mp-feature-card-wide { grid-column: span 2; }
@media (max-width: 600px) { .mp-feature-card-wide { grid-column: span 1; } }
/* padding переехал на .mp-feature-text: фото должно доходить до краёв
   карточки, иначе вокруг него остаётся белая рамка и оно смотрится
   вклеенным. Карточки одной строки тянутся на одну высоту (align-items
   grid по умолчанию stretch), поэтому текст прижат к низу через flex. */
.mp-feature-card {
  background: var(--white); border-radius: var(--radius); overflow: hidden;
  box-shadow: var(--shadow-sm); display: flex; flex-direction: column;
  /* span 1000 по умолчанию (rowUnit 1px = запас 1000px) — пока product_page.js
     ещё не отмерил точный span (mpMasonry запускается на window load), карточка
     не сжата в 1px-трек: без этого запаса первый кадр до JS показывает
     обрезанные почти-невидимые карточки. */
  grid-row-end: span 1000;
}
/* contain, НИКОГДА не cover: эти снимки объясняют саму суть характеристики
   (что такое Bob-Tach, куда смотрит камера, как выглядит панель) — обрезать
   их нельзя нигде и ни для какой машины (Allan, 2026-08-04). Высота задана
   через aspect-ratio, а не жёстким height: при фиксированной высоте кадр
   другой пропорции всё равно пришлось бы чем-то заполнять. */
.mp-feature-img {
  width: 100%; aspect-ratio: 3 / 2; object-fit: contain; display: block;
  background: var(--gray-100); flex-shrink: 0; padding: 4px;
}
.mp-feature-text { padding: 16px 18px 18px; }
/* Скрытые до нажатия карточки. display:none, а не max-height — карточки в
   grid разной высоты, схлопывание высотой дало бы рваный обрез. */
.mp-feature-card-extra { display: none; }
.mp-features-expanded .mp-feature-card-extra { display: flex; }
.mp-feature-more {
  display: block; margin: 16px auto 0; padding: 10px 22px;
  font-family: var(--font); font-size: 13px; font-weight: 700;
  color: var(--black); background: var(--white);
  border: 1.5px solid var(--gray-300); border-radius: 999px; cursor: pointer;
  transition: border-color 0.15s var(--ease), background 0.15s var(--ease);
}
.mp-feature-more:hover { border-color: var(--red); background: #fff6f5; }
.mp-feature-card h3 { font-size: 15px; font-weight: 700; margin-bottom: 8px; color: var(--red-dark); }
.mp-feature-card p { font-size: 13px; color: var(--gray-700); line-height: 1.55; }
/* Значки гарантии/BobCARE у brochure_tls_agri "Serviceability & warranty" —
   маленькие, в ряд под текстом, без своей рамки (сами значки уже с фоном). */
.mp-brochure-badges { display: flex; gap: 10px; margin-top: 12px; }
.mp-brochure-badge { height: 44px; width: auto; object-fit: contain; }
/* Доп. крупные планы внутри карточки brochure_tls_agri (кабина/джойстик/
   обзорность, защита днища/реверс вентилятора) — маленькая сетка под
   основным текстом, той же карточки, без отдельной секции. */
.mp-brochure-gallery { display: grid; grid-template-columns: repeat(auto-fit, minmax(90px, 1fr)); gap: 8px; margin-top: 12px; }
.mp-brochure-gallery img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 8px; background: var(--gray-100); }

/* ── Configurator ──────────────────────────────────────────────────────── */
.mp-configurator { display: flex; gap: 24px; margin-top: 16px; align-items: flex-start; }
.mp-opt-groups { flex: 1 1 65%; min-width: 0; display: flex; flex-direction: column; gap: 16px; }
.mp-opt-group { background: var(--white); border-radius: var(--radius); padding: 14px 18px; box-shadow: var(--shadow-sm); }
.mp-opt-group h4 { font-size: 12px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--gray-500); margin-bottom: 8px; }
.mp-opt-row { display: flex; align-items: center; gap: 10px; padding: 7px 0; font-size: 13px; cursor: pointer; border-top: 1px solid var(--gray-100); }
.mp-opt-row:first-of-type { border-top: none; }
.mp-opt-row input { flex-shrink: 0; width: 16px; height: 16px; accent-color: var(--red); }
.mp-opt-row input:disabled { cursor: default; }
.mp-opt-row:has(input:disabled) { cursor: default; }
.mp-opt-desc { flex: 1; }
.mp-opt-price { font-weight: 700; color: var(--red-dark); white-space: nowrap; }
.mp-opt-row:has(input:disabled) .mp-opt-price { color: var(--gray-400, #999); font-weight: 600; }

/* Раздел «Комплектация» — тот же список, но без чекбоксов и цен (базовой
   цены у модели нет, см. machine_page._configurator). Строки короткие, и во
   всю ширину экрана каждая смотрелась потерянной — раскладываем в колонки.
   Разделители при этом горизонтальные, поэтому у первой строки КАЖДОЙ
   колонки её быть не должно, а :first-of-type знает только про первую в
   списке — отсюда общее правило «границы нет» плюс граница сверху у всех,
   кроме первого ряда сетки. */
.mp-opt-group:has(.mp-opt-row-plain) {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  column-gap: 20px; align-items: start;
}
.mp-opt-group:has(.mp-opt-row-plain) h4 { grid-column: 1 / -1; }
.mp-opt-row-plain { cursor: default; padding: 6px 0; border-top: none; }
.mp-config-summary {
  flex: 1 1 30%; min-width: 220px; position: sticky; top: 20px;
  background: var(--white); border-radius: var(--radius); padding: 18px; box-shadow: var(--shadow-md);
}
.mp-config-row { display: flex; justify-content: space-between; padding: 6px 0; font-size: 13px; color: var(--gray-700); }
.mp-config-total { border-top: 2px solid var(--black); margin-top: 6px; padding-top: 10px; font-size: 16px; font-weight: 800; color: var(--black); }
.mp-config-note { font-size: 11px; color: var(--gray-500); margin-top: 10px; line-height: 1.4; }

/* ── Full specifications (accordion) ──────────────────────────────────── */
.mp-specs-list { margin-top: 16px; display: flex; flex-direction: column; gap: 8px; }
.mp-accordion { background: var(--white); border-radius: var(--radius-sm); overflow: hidden; box-shadow: var(--shadow-sm); }
.mp-accordion-head {
  width: 100%; display: flex; justify-content: space-between; align-items: center;
  padding: 12px 16px; background: none; border: none; font-family: var(--font);
  font-size: 14px; font-weight: 700; cursor: pointer; text-align: left;
}
.mp-chevron { transition: transform 0.2s ease; color: var(--gray-500); }
.mp-accordion.open .mp-chevron { transform: rotate(180deg); }
.mp-accordion-body { display: none; padding: 0 16px 12px; }
.mp-accordion.open .mp-accordion-body { display: block; }
.mp-spec-row { display: flex; justify-content: space-between; gap: 12px; padding: 5px 0; font-size: 13px; border-top: 1px solid var(--gray-100); }
.mp-spec-row:first-child { border-top: none; }
.mp-spec-row span { color: var(--gray-500); }
.mp-spec-row b { font-weight: 700; text-align: right; }

/* ── Compatible attachments ────────────────────────────────────────────── */
.mp-count-badge {
  font-size: 12px; font-weight: 800; color: var(--white); background: var(--red);
  border-radius: 999px; padding: 2px 8px; margin-left: 6px;
}
.mp-att-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 12px; margin-top: 16px; }
.mp-att-card {
  background: var(--white); border-radius: var(--radius-sm); padding: 10px; box-shadow: var(--shadow-sm);
  text-align: center; display: block; text-decoration: none; color: inherit; transition: box-shadow 0.15s ease;
}
a.mp-att-card:hover { box-shadow: var(--shadow-md); }
/* ПОСЛЕ .mp-att-card — иначе его display:block перебивает скрытие: у обоих
   правил одинаковая специфичность (один класс), и выигрывает то, что ниже. */
.mp-att-card-extra { display: none; }
.mp-att-expanded .mp-att-card-extra { display: block; }
.mp-att-card img { width: 100%; height: 90px; object-fit: contain; margin-bottom: 6px; }
/* overflow-wrap — длинные составные названия навесного по-русски
   ("САМОВЫРАВНИВАЮЩАЯСЯ", "ПЕСКОСОЛЕРАЗБРАСЫВАТЕЛЬ") в одно слово шире
   колонки. Без переноса такое слово задаёт min-content ширину трека, тот
   раздувается, и вбок уезжает ВСЯ страница (Allan, 2026-08-13, проверка на
   390px: L85 давал scrollWidth 417 при экране 390, правая колонка карточек
   обрезалась). Ни в одном языке ничего не ломает: переносится только то,
   что иначе не влезает. hyphens:auto добавляет дефис в месте переноса —
   <html lang> проставлен на всех трёх языках, поэтому браузер рвёт слово по
   слоговым правилам языка, а не посреди буквосочетания. */
.mp-att-title { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.02em; line-height: 1.3; overflow-wrap: break-word; hyphens: auto; }
.mp-att-price { font-size: 12px; color: var(--red-dark); font-weight: 700; margin-top: 4px; }

/* ── Свойства линейки: не карточки, а спокойная строка чипов. Намеренно
   визуально тише карточек особенностей — это фон, а не аргумент выбора. */
.mp-common-block { margin-top: 26px; padding-top: 18px; border-top: 1px solid var(--gray-300); }
.mp-common-title {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--gray-500); margin-bottom: 10px;
}
/* Плитки, не строка пилюль — см. докстринг в machine_page.py про то, почему
   круглый 24px аватар пришлось выбросить целиком, а не просто увеличить. */
/* Плитки крупные: на 1160px контента помещается 4 в ряд. Мелкие плитки
   бессмысленны — на них не разглядеть ни наклейку Automatic Ride Control, ни
   что показывает кадр вообще (Allan, 2026-08-04: "должны быть больше, чтоб
   было видно"). На мобильном — ровно две в ряд, см. медиазапрос ниже. */
.mp-common-chips {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(265px, 1fr));
  gap: 14px;
}
.mp-common-chip {
  display: flex; flex-direction: column; overflow: hidden;
  font-size: 13px; font-weight: 600; color: var(--gray-700);
  background: var(--white); border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm); text-decoration: none; cursor: default;
}
/* Без фото — плитка не пустует, текст просто центрирован по всей высоте на
   лёгкой заливке вместо белого поля под несуществующую картинку. */
.mp-common-chip:not(.mp-common-chip-media) {
  align-items: center; justify-content: center; text-align: center;
  background: var(--gray-100); border-style: dashed; padding: 12px 10px; min-height: 180px;
}
/* contain, НЕ cover: у фич разный кадр (горизонтальные фото, круглый значок
   HVO, вертикальная инфографика), и cover резал их по центру так, что от
   сути оставался случайный кусок (Allan, 2026-08-04: "все изображения
   обрезаны"). contain показывает кадр целиком, поля заполняет фон. */
.mp-common-chip-img {
  width: 100%; aspect-ratio: 4 / 3; object-fit: contain;
  background: var(--gray-100); display: block; padding: 4px;
}
.mp-common-chip-video { width: 100%; aspect-ratio: 16 / 9; border: 0; display: block; background: #000; }
.mp-common-chip-label { padding: 8px 10px; line-height: 1.3; }
a.mp-common-chip { cursor: pointer; transition: border-color 0.15s var(--ease), box-shadow 0.15s var(--ease); }
a.mp-common-chip:hover { border-color: var(--red); box-shadow: var(--shadow-sm); }
.mp-common-chip-play {
  display: inline-flex; align-items: center; justify-content: center;
  width: 15px; height: 15px; border-radius: 50%; flex-shrink: 0;
  background: var(--red); color: var(--white); font-size: 7px; padding-left: 1px;
  margin-right: 4px; vertical-align: -2px;
}

/* ── "In action": рабочее фото из печатного каталога + ссылка на ролик ──
   Фото каталога ограничено 406x272 в исходнике (оригинал 203x134, нарезка
   отрендерена x2) — поэтому фиксируем разумную высоту и не растягиваем на
   всю ширину, иначе апскейл видно. Для hero эти файлы не годятся, там
   остаются студийные снимки с Cloudinary. */
.ap-action-grid { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 4px; }
/* contain, не cover — рабочее фото показывает, ЧТО навесное делает, резать
   его нельзя (Allan, 2026-08-04). width:auto при фиксированной высоте и так
   сохраняет пропорцию, но contain страхует на случай узких кадров. */
.ap-action-shot {
  height: 190px; width: auto; max-width: 100%;
  object-fit: contain; border-radius: var(--radius-sm);
  background: var(--gray-100); box-shadow: var(--shadow-sm);
}
/* Встроенный плеер вместо ссылки (Allan, 2026-08-06: "видео можно на
   страницах показывать") — youtube-nocookie.com + autoplay=0, см.
   attachment_page._in_action(). aspect-ratio держит 16:9 при любой
   ширине колонки без классического padding-hack'а. */
.ap-video-embed {
  position: relative; width: 100%; max-width: 480px; aspect-ratio: 16 / 9;
  margin-top: 14px; border-radius: var(--radius-sm); overflow: hidden;
  box-shadow: var(--shadow-sm); background: var(--gray-100);
}
.ap-video-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: none; }
/* Страница печатного каталога 2026/2027 (Allan, 2026-08-06: "что мешает
   это делать") — полноразмерная, не ужимаем в мелкий превью-кадр, там
   таблица размеров и пиктограммы, которые как раз должны читаться. */
.ap-catalogue-page {
  width: 100%; max-width: 700px; height: auto; display: block;
  border-radius: var(--radius-sm); box-shadow: var(--shadow-sm);
  border: 1px solid var(--gray-300);
}
@media (max-width: 640px) {
  .ap-action-shot { height: 150px; }
  .ap-video-embed { max-width: 100%; }
}

/* ── Attachment page (.ap-) specifics ──────────────────────────────────── */
.ap-app-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.ap-app-chip {
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.02em;
  padding: 4px 10px; border-radius: 999px; background: var(--gray-100); color: var(--gray-700);
}
.ap-feature-list { list-style: none; display: flex; flex-direction: column; gap: 10px; margin-top: 16px; }
.ap-feature-list li {
  position: relative; padding-left: 20px; font-size: 14px; line-height: 1.5; color: var(--gray-700);
}
.ap-feature-list li::before { content: "\2022"; position: absolute; left: 4px; color: var(--red); font-weight: 800; }
.ap-compat-cat { font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.02em; margin: 20px 0 4px; color: var(--gray-500); }
.ap-compat-cat:first-of-type { margin-top: 16px; }
.ap-table-wrap { overflow-x: auto; margin-top: 16px; border-radius: var(--radius-sm); box-shadow: var(--shadow-sm); }
.ap-variants-table { width: 100%; border-collapse: collapse; background: var(--white); font-size: 13px; white-space: nowrap; }
.ap-variants-table th {
  text-align: left; padding: 10px 14px; background: var(--black); color: var(--white);
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.02em; position: sticky; top: 0;
}
/* white-space:normal тут перебивало white-space:nowrap со всей таблицы
   (строка выше) — у длинных названий вариантов ("Man Platform 2.2 m 300 kg
   Rotating QT") получалось 5 строк текста в одной ячейке вместо того, чтобы
   воспользоваться уже готовым горизонтальным скроллом .ap-table-wrap
   (Allan, 2026-08-10: "в 1 строку нужно"). Только у первого столбца
   (название варианта) — у него самый длинный текст; артикул/цена и так
   nowrap своими классами (.ap-pn/.ap-price-cell). */
.ap-variants-table td { padding: 9px 14px; border-top: 1px solid var(--gray-100); white-space: normal; }
.ap-variants-table td:first-child { white-space: nowrap; }
.ap-variants-table tr:hover td { background: var(--gray-100); }
.ap-pn { font-family: var(--mono); font-size: 11px; color: var(--gray-500); white-space: nowrap; }
.ap-price-cell { font-weight: 800; color: var(--red-dark); white-space: nowrap; }
.mp-more-note { font-size: 12px; color: var(--gray-500); margin-top: 10px; }

/* ── Why Bobcat (battlecard) ───────────────────────────────────────────── */
.mp-adv-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 16px; margin-top: 16px; }
.mp-adv-card { background: var(--white); border-radius: var(--radius); box-shadow: var(--shadow-sm); overflow: hidden; }
/* .mp-adv-card без battlecard-строк (AGRI PACKS tiers, brochure_tls_agri) —
   тому же контейнеру просто нужен padding для заголовка/текста. */
.mp-adv-card-body { padding: 18px 20px; }
.mp-adv-card-body h3 { font-size: 16px; font-weight: 800; color: var(--red-dark); margin-bottom: 8px; }
.mp-adv-card-body p { font-size: 13px; color: var(--gray-700); line-height: 1.55; }

/* ── Option Book Stage V: комплектации, опции, шины ─────────────────────
   Аудитория страниц — профессионалы, которые читают спеки (Allan,
   2026-08-28: "страница должна быть атрактивна именно для профессионалов
   ... информация в техническом и профессиональном виде"). Отсюда плотная
   табличная подача вместо крупных маркетинговых плиток: статус
   штатно/опция читается сразу, ограничения по моделям не спрятаны. */
.ob-packs { display: grid; grid-template-columns: repeat(auto-fit, minmax(215px, 1fr)); gap: 14px; margin-top: 16px; align-items: start; }
.ob-pack {
  background: var(--white); border: 1px solid var(--gray-200); border-radius: var(--radius);
  box-shadow: var(--shadow-sm); padding: 16px 18px 18px; display: flex; flex-direction: column; gap: 7px;
}
.ob-pack-stars { font-size: 15px; letter-spacing: 2px; color: var(--red); line-height: 1; }
.ob-star-off { color: var(--gray-200); }
.ob-pack h3 { font-size: 14px; font-weight: 800; letter-spacing: .01em; }
.ob-pack p { font-size: 12.5px; line-height: 1.55; color: var(--gray-700); }
.ob-note { margin-top: 14px; font-size: 12.5px; line-height: 1.5; color: var(--gray-500); border-left: 2px solid var(--gray-300); padding-left: 12px; max-width: 82ch; }

/* Штатное оснащение — чипами, тот же вид, что у применимости навесного */
.ob-std-block { margin-top: 16px; padding: 16px 18px; background: var(--gray-100); border-radius: var(--radius-sm); }
.ob-std-block h3 { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; color: var(--gray-500); margin-bottom: 10px; }

.ob-cats { margin-top: 16px; }
.ob-count { display: inline-block; margin-left: 7px; font-size: 11px; font-weight: 700; color: var(--gray-500); }
/* ВАЖНО: .ob-list не должен задавать display напрямую — это тот же
   одноклассовый вес, что и у .mp-accordion-body { display: none }, и,
   стоя ниже по файлу, он перебивал скрытие: категории были раскрыты
   всегда, а клик по заголовку визуально ничего не менял. Раскладку
   включаем только в открытом состоянии. */
.mp-accordion.open .ob-list { display: flex; flex-direction: column; gap: 0; }
/* Строка опции: миниатюра слева, текст справа. Ряды, а не карточки —
   46 позиций карточками не просматриваются, а рядами сканируются. */
.ob-row { display: grid; grid-template-columns: 92px 1fr; gap: 14px; padding: 13px 0; border-top: 1px solid var(--gray-100); }
.ob-row:first-child { border-top: none; }
.ob-thumb { width: 92px; height: 74px; object-fit: cover; border-radius: var(--radius-sm); background: var(--gray-100); display: block; }
.ob-body { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.ob-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 10px; }
.ob-head h3 { font-size: 13.5px; font-weight: 700; color: var(--black); }
.ob-body p { font-size: 12.5px; line-height: 1.55; color: var(--gray-700); }
.ob-state {
  font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
  padding: 2px 7px; border-radius: 999px; white-space: nowrap; flex-shrink: 0;
}
.ob-std { color: var(--white); background: var(--red-dark); }
.ob-opt { color: var(--gray-700); background: var(--gray-100); border: 1px solid var(--gray-300); }
.ob-meta { display: flex; flex-direction: column; gap: 3px; margin-top: 2px; }
.ob-app { font-size: 11.5px; color: var(--gray-500); }
/* Ограничение по моделям/рынку — не прячем мелким серым: для профессионала
   это часть ответа "подойдёт ли мне", а не сноска. */
.ob-extra { font-size: 11.5px; line-height: 1.45; color: var(--red-dark); }
.ob-tire-state { width: 1%; white-space: nowrap; }
/* Пояснение к шине — вторая строка, приглушённая: обозначение должно
   читаться первым, пояснение не должно с ним спорить. */
.ob-tire-note { display: block; font-size: 12px; color: #6b6b6b; margin-top: 2px; }
@media (max-width: 520px) {
  .ob-row { grid-template-columns: 64px 1fr; gap: 11px; }
  .ob-thumb { width: 64px; height: 52px; }
}
.mp-adv-header { display: flex; justify-content: space-between; padding: 12px 16px; background: var(--black); color: var(--white); font-size: 13px; font-weight: 700; }
.mp-adv-rows-head { display: grid; grid-template-columns: 1.4fr 1fr 1fr; gap: 8px; padding: 8px 16px; font-size: 11px; text-transform: uppercase; color: var(--gray-500); }
.mp-adv-row { display: grid; grid-template-columns: 1.4fr 1fr 1fr; gap: 8px; padding: 7px 16px; font-size: 13px; border-top: 1px solid var(--gray-100); }
.mp-adv-row span { color: var(--gray-700); }
.mp-adv-us { color: var(--red-dark); font-weight: 800; }
.mp-adv-them { color: var(--gray-500); font-weight: 600; }

/* ── Footer ────────────────────────────────────────────────────────────── */
.mp-footer-section { text-align: center; padding-top: 20px; padding-bottom: 90px; }

/* Строка ввода вопроса — закреплена внизу экрана, тот же принцип, что у
   .input-area на главной (Allan, 2026-08-11: "у нас же чат а тут чата нет").
   padding-bottom у .mp-footer-section выше увеличен на её высоту, чтобы
   не перекрывать текст брошюры/источника в самом низу страницы. */
.mp-ask-bar {
  position: fixed; bottom: 0; left: 0; right: 0; z-index: 100;
  display: flex; align-items: center; gap: 8px;
  padding: 10px 16px; padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
  background: rgba(255,255,255,0.92); backdrop-filter: blur(6px);
  border-top: 1px solid var(--gray-300);
}
.mp-ask-input {
  flex: 1; min-width: 0; padding: 12px 16px; border-radius: 22px;
  border: 1.5px solid var(--gray-300); background: var(--gray-100);
  font-family: var(--font); font-size: 15px; outline: none;
  transition: border-color 0.15s var(--ease);
}
.mp-ask-input:focus { border-color: var(--red); background: var(--white); }
.mp-ask-send {
  flex-shrink: 0; width: 44px; height: 44px; border-radius: 50%; border: none;
  background: var(--red); color: var(--white); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  transition: background 0.15s var(--ease);
}
.mp-ask-send:hover { background: var(--red-dark); }
.mp-ask-send svg { width: 18px; height: 18px; fill: currentColor; }
.mp-brochure-link {
  display: inline-block; padding: 10px 20px; background: var(--black); color: var(--white);
  border-radius: 999px; font-size: 13px; font-weight: 700; text-decoration: none; margin-bottom: 14px;
}
.mp-source-note { font-size: 11px; color: var(--gray-500); }

/* ── Mobile: no side-by-side squeeze, full-width stacking throughout
   (lesson learned 2026-07-30 fixing the main catalogue cards on mobile —
   image+text sharing one narrow row causes text wrap/overlap; full-width
   rows top-to-bottom instead). ─────────────────────────────────────────── */
@media (max-width: 800px) {
  .mp-hero { flex-direction: column; padding: 16px; gap: 16px; }
  .mp-hero-img { height: 240px; }
  .mp-hero-info h1 { font-size: 22px; }
  .mp-configurator { flex-direction: column; }
  .mp-config-summary { position: static; width: 100%; }
  /* minmax(0, 1fr), а не 1fr: у "1fr" минимум трека — min-content, поэтому
     одно длинное слово в карточке расширяло колонку и выталкивало страницу
     за экран. Нулевой минимум разрешает колонке сжиматься до своей доли. */
  .mp-att-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Ровно две в ряд, а не auto-fill: при minmax(265px) на экране 390px
     влезала бы одна, и плитки растягивались бы во всю ширину (Allan,
     2026-08-04: "на мобильном два рядом"). */
  .mp-common-chips { grid-template-columns: repeat(2, 1fr); gap: 10px; }
  .mp-common-chip { font-size: 12px; }
  .mp-common-chip:not(.mp-common-chip-media) { min-height: 120px; }
  .mp-adv-grid { grid-template-columns: 1fr; }
  .mp-adv-row, .mp-adv-rows-head { grid-template-columns: 1.2fr 1fr 1fr; font-size: 12px; }
}

/* ── Гусеничные погрузчики (CTL) ─────────────────────────────────────── */
/* Сравнение по ряду. Подсветка текущей модели — не декоративная: без неё
   в таблице из трёх одинаковых колонок непонятно, про какую машину
   страница. */
.ctl-headline th[scope="row"] { white-space: nowrap; font-weight: 700; }
.ctl-headline td { font-variant-numeric: tabular-nums; }
.ctl-row.ctl-cur { background: #FDF0ED; }
.ctl-row.ctl-cur th[scope="row"] { color: var(--red); }
.ctl-cur-badge {
  display: inline-block; margin-left: 8px; padding: 1px 7px;
  border-radius: var(--radius-sm); background: var(--red); color: var(--white);
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; vertical-align: middle;
}
.ctl-pos {
  margin: 0 0 18px; padding: 14px 16px; border-left: 3px solid var(--red);
  background: var(--gray-100); border-radius: 0 8px 8px 0;
}
.ctl-pos-tag {
  font-size: 12px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--red); margin-bottom: 6px;
}
.ctl-pos p { margin: 0; font-size: 15px; line-height: 1.55; max-width: 74ch; }

.ctl-spec-table th[scope="row"] { text-align: left; font-weight: 500; }
.ctl-spec-table td { font-variant-numeric: tabular-nums; text-align: center; }
.ctl-spec-table .ctl-cur { background: #FDF0ED; font-weight: 700; }
.ctl-spec-table thead .ctl-cur { color: var(--red); }
.ctl-unit { color: var(--gray-500); font-size: 12px; white-space: nowrap; }

/* ВАЖНО: как и у .ob-list, display задаётся только внутри .open —
   у .mp-accordion-body { display:none } тот же одноклассовый вес. */
.ctl-cats { display: flex; flex-direction: column; gap: 10px; }
.mp-accordion.open .ctl-eq-list { display: grid; }
.ctl-eq-list {
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 2px 20px; list-style: none; margin: 0; padding: 0;
}
.ctl-eq {
  display: flex; align-items: baseline; gap: 9px;
  padding: 7px 0; border-bottom: 1px solid var(--gray-300);
}
.ctl-eq-name { font-size: 14px; line-height: 1.4; }
.ctl-eq-no .ctl-eq-name { color: var(--gray-500); text-decoration: line-through; }
.ob-state.ctl-no { background: var(--gray-100); color: var(--gray-500); }
.ctl-eq .ob-state { flex: 0 0 auto; min-width: 86px; text-align: center; }

/* Ходовая: одна карточка ведущая (торсион / Land Management), остальные
   равные — иерархия читается без заголовков. */
.ctl-uc-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 14px; margin-bottom: 14px;
}
.ctl-uc-card {
  padding: 16px 18px; border: 1px solid var(--gray-300); border-radius: var(--radius-sm);
  background: var(--white);
}
.ctl-uc-card h3 { margin: 0 0 8px; font-size: 15px; }
.ctl-uc-card p { margin: 0; font-size: 14px; line-height: 1.55; color: var(--gray-700); max-width: 76ch; }
.ctl-uc-hero {
  grid-column: 1 / -1; border-color: var(--red); background: #FDF4F2;
}
.ctl-uc-hero h3 { color: var(--red); }

/* Отрасли: этапы пронумерованы, потому что это действительно
   последовательность работ, а не список равнозначных пунктов. */
.ctl-app-lead {
  margin: 0 0 16px; font-size: 15px; line-height: 1.6; color: var(--dark);
  max-width: 68ch;
}
.ctl-stages { display: flex; flex-direction: column; gap: 4px; }
.ctl-stage { padding: 14px 0; border-top: 1px solid var(--gray-300); }
.ctl-stage:first-child { border-top: none; padding-top: 0; }
.ctl-stage-head { display: flex; align-items: center; gap: 10px; margin-bottom: 6px; }
.ctl-stage-n {
  flex: 0 0 auto; width: 24px; height: 24px; border-radius: 50%;
  background: var(--red); color: var(--white); font-size: 12px; font-weight: 700;
  display: inline-flex; align-items: center; justify-content: center;
  font-variant-numeric: tabular-nums;
}
.ctl-stage h4 { margin: 0; font-size: 15px; }
.ctl-stage p {
  margin: 0 0 10px 34px; font-size: 14px; line-height: 1.55; color: var(--gray-700);
  max-width: 72ch;
}
.ctl-atts { display: flex; flex-wrap: wrap; gap: 6px; margin-left: 34px; }
.ctl-att {
  display: inline-block; padding: 4px 10px; border-radius: 14px;
  background: var(--gray-100); border: 1px solid var(--gray-300);
  font-size: 12.5px; color: var(--dark);
}
.ctl-fit {
  display: inline-block; margin-left: 8px; padding: 2px 8px;
  border-radius: var(--radius-sm); background: #1F7A3D; color: var(--white);
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em;
}

.ctl-sub-h { margin: 24px 0 12px; font-size: 15px; }
.ctl-why-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px;
}
.ctl-why { padding: 14px 16px; border-radius: var(--radius-sm); background: var(--gray-100); }
.ctl-why h4 { margin: 0 0 6px; font-size: 14px; color: var(--red); }
.ctl-why p { margin: 0; font-size: 13.5px; line-height: 1.5; color: var(--gray-700); }
.ctl-kit { margin-top: 22px; }
.ctl-kit h3 { margin: 0 0 10px; font-size: 15px; }
.ctl-kit-list {
  margin: 0; padding: 0; list-style: none;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 2px 20px;
}
.ctl-kit-list li {
  padding: 6px 0 6px 18px; font-size: 14px; line-height: 1.45;
  border-bottom: 1px solid var(--gray-300); position: relative;
}
.ctl-kit-list li::before {
  content: "\2713"; position: absolute; left: 0; color: #1F7A3D; font-weight: 700;
}

@media (max-width: 640px) {
  .ctl-stage p, .ctl-atts { margin-left: 0; }
  .ctl-eq-list { grid-template-columns: 1fr; }
}

/* Фотографии в блоках CTL. Этап без проверенного кадра остаётся текстовым —
   раскладка на это рассчитана и не оставляет дыру на месте картинки. */
.ctl-app-hero {
  display: block; width: 100%; height: auto; aspect-ratio: 1200 / 520;
  object-fit: cover; border-radius: var(--radius-sm); margin-bottom: 16px;
}
.ctl-stage-body { display: flex; gap: 16px; align-items: flex-start; margin-left: 34px; }
.ctl-stage-text { min-width: 0; flex: 1; }
.ctl-stage-shot .ctl-stage p, .ctl-stage-shot .ctl-atts { margin-left: 0; }
.ctl-stage-body p, .ctl-stage-body .ctl-atts { margin-left: 0; }
.ctl-stage-img {
  flex: 0 0 236px; width: 236px; aspect-ratio: 760 / 500;
  object-fit: cover; border-radius: var(--radius-sm);
}
.ctl-uc-shot, .ctl-fo-shot {
  display: block; width: 100%; max-width: 760px; height: auto;
  border-radius: var(--radius-sm); margin: 0 0 16px;
}
.ctl-uc-shot { max-width: 100%; background: var(--gray-100); }
@media (max-width: 720px) {
  .ctl-stage-body { flex-direction: column; margin-left: 0; gap: 10px; }
  .ctl-stage-img { flex: none; width: 100%; }
}

/* Пакеты комплектации: те же карточки, что у AGRI-пакетов, но вместо звёзд —
   название ступени, а под описанием состав. Ступени идут по возрастанию,
   поэтому порядок в разметке несёт смысл и менять его нельзя. */
.pkg-packs .ob-pack { display: flex; flex-direction: column; }
.pkg-name {
  font-size: 13px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--red); margin-bottom: 8px;
}
.pkg-blocks {
  margin-top: auto; padding-top: 10px; border-top: 1px solid var(--gray-300);
  font-size: 12.5px; line-height: 1.45; color: var(--gray-500);
}
/* Ссылка на соседнее поколение — приглушённая: это подсказка, а не призыв
   уйти со страницы. */
.ctl-gen-link { color: var(--red); text-decoration: none; font-weight: 700; }
.ctl-gen-link:hover { text-decoration: underline; }

/* У экскаватора карточек всего две, и ведущая занимает всю ширину. При
   auto-fit вторая падала в первую из четырёх колонок и вытягивалась в
   узкий столбик — здесь колонка одна, обе карточки одной ширины. */
.mex-tech-grid { grid-template-columns: 1fr; }

/* Список гидроконтуров мини-экскаватора живёт вне аккордеона, поэтому
   grid ему надо включить отдельно: .ctl-eq-list раскрывается правилом
   .mp-accordion.open, которого здесь нет. */
.mex-aux-list { display: grid; margin-bottom: 14px; }
.mex-aux-list .ob-state { min-width: 60px; }

/* Шины. Шесть семейств — сетка по три; ограничение семейства пишем
   отдельной строкой, чтобы его не читали как продолжение достоинств. */
.tire-grid { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
.tire-card h3 { color: var(--red-dark); }
.tire-limit {
  margin: 8px 0 0 !important; padding-top: 8px;
  border-top: 1px solid var(--gray-300);
  font-size: 13px !important; color: var(--gray-500) !important;
}
.tire-gen-intro { margin: 22px 0 10px; font-size: 14px; color: var(--dark); }
.tire-gen-table th[scope="row"] { font-weight: 600; }
.tire-cycles { font-weight: 700; font-variant-numeric: tabular-nums; }
.tire-delta { color: var(--gray-500); font-size: 13px; }
.tire-spec-table { margin-top: 10px; font-variant-numeric: tabular-nums; }
.tire-sub { font-weight: 400; color: var(--gray-500); }


/* ── Длина строки ────────────────────────────────────────────────────────
   Замер на живой странице 2026-09-01: подписи секций шли по 127 знаков в
   строке, примечания по 132, карточки техрешений по 115 — при комфортных
   45–90. Глаз на такой длине теряет начало следующей строки.
   Ограничиваем ТОЛЬКО сплошной текст: таблицам, спискам комплектации и
   галереям широкая строка как раз нужна, там глаз идёт по колонкам. */
.mp-section p, .pkg-text, .ctl-why p, .ctl-stage-text p, .ssl-new-text {
  max-width: 76ch;
}
.mp-section .ap-table-wrap p,
.mp-section .ctl-eq-name,
.mp-section figcaption { max-width: none; }

/* Исполнения строительных телескопов: два шага вместо лесенки пакетов.
   Базовая комплектация и пакеты живут внутри одного аккордеона, поэтому
   у них свои подзаголовки — h3 тут был бы вровень с заголовком секции. */
.tls-line-text { margin: 0 0 14px; font-size: 14.5px; color: var(--dark); max-width: 74ch; }
.tls-sub {
  margin: 18px 0 8px; font-size: 11px; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--gray-500);
}
.tls-base { margin: 0; padding-left: 18px; font-size: 14px; color: var(--gray-700); }
.tls-base li { margin-bottom: 3px; }
.tls-packs {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 12px;
}
.tls-pack {
  padding: 14px 16px; border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm); background: var(--white);
}
.tls-pack ul { margin: 8px 0 0; padding-left: 16px; font-size: 13.5px; line-height: 1.5; color: var(--gray-700); }
.tls-pack li { margin-bottom: 5px; }

/* Свойства линейки, у которых нет фото. Раньше они занимали такую же
   плитку 180 px с пунктирной рамкой — на странице это выглядело как
   семнадцать сломанных картинок подряд (Allan, 2026-09-01). Факт «это
   есть» остаётся, место под несуществующий кадр — нет. */
.mp-common-plains {
  display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px;
}
.mp-common-plain {
  font-size: 12.5px; font-weight: 600; color: var(--gray-700);
  background: var(--gray-100); border: 1px solid var(--gray-300);
  border-radius: 999px; padding: 5px 11px; line-height: 1.25;
}

/* Цена не опубликована — блок превращается в кнопку «узнать цену».
   Раньше печаталось «От —», и прочерк читался как сломанная страница
   (Allan, 2026-09-01: цены не показываем — значит и место под них не
   должно выглядеть пустым). */
.mp-hero-price-ask .mp-price-value {
  font-size: 15px; font-weight: 700; color: var(--bobcat-red, #d0271d);
}

/* ── Подвал сайта — реквизиты на КАЖДОЙ странице ───────────────────────
   До 2026-09-02 подвала не было ни на одной серверной странице: у машины
   в нём стояли только ссылка на брошюру и строка про источник данных, ни
   названия компании, ни адреса, ни телефона. Это требование политики
   посадочных страниц Google Ads и Merchant Center: с объявления человек
   должен видеть, кто продаёт и как связаться. */
.site-footer {
  max-width: 1200px; margin: 40px auto 0; padding: 24px 20px 32px;
  border-top: 1px solid var(--gray-300);
  font-size: 13px; line-height: 1.7; color: var(--gray-500);
}
.site-footer .sf-name { font-weight: 800; color: var(--black); font-size: 14px; }
.site-footer a { color: var(--gray-700); }
.site-footer a:hover { color: var(--red); }
