

/* ==========================================================================
   css/pages/product.css
   страничные стили
   ========================================================================== */
/* ===========================================================================
   Карточка товара /product/{slug}/: сцена конфигуратора, панель шагов, смета,
   вкладки и похожие решения. Владелец файла — tools/gen_product.py и
   js/modules/product-page.js; общие компоненты (кнопки, поля, вкладки, шаги,
   сводка) берутся из css/components.css и здесь не переопределяются.

   Цвета — только через var(--…) из tokens.css. Статичная схема ограждения
   красится локальными переменными --art-*, чтобы «ночь» переключалась одним
   атрибутом data-time на контейнере, а не перебором fill у каждой фигуры.
   =========================================================================== */

.product {
  /* Пропорции макета (он нарисован на 1536 с полями 32): сцена 1066, зазор 27,
     панель 380 — то есть панель занимает 25,8 % ширины содержимого. Наш
     контейнер уже (1320 против 1472), поэтому берём ту же долю, но не даём
     панели упасть ниже 344 px: в 300 px «Предварительная стоимость» и сумма
     перестают помещаться в одну строку, а в макете они стоят рядом. */
  --aside-w: 352px;
  --overlay-gap: 12px;       /* отступ плавающих элементов от края сцены, по макету */
}

/* Значок «новинка» — единственный из набора badge, которого нет в
   components.css. Держим его здесь, чтобы карточка не выводила бесцветную
   плашку; когда его добавят в общий файл, строку отсюда убрать. */
.badge--new { background: var(--navy); color: var(--white); }

/* ============================ ШАПКА КАРТОЧКИ ============================ */

/* Крошки и заголовок в макете стоят почти вплотную: между базовой линией
   крошек и верхом H1 всего 23 px, а .section--tight добавляет к 16 px крошек
   ещё 32 сверху. Ужимаем только на этой странице — .section--tight общий. */
.product.section--tight { padding-top: 0; }

/* В макете шапка карточки — одна строка: слева крупный H1, справа шаги, и
   сразу под ними сцена (между низом заголовка и сценой 17 px). Подзаголовок и
   теги оттуда убраны — они переехали в первую вкладку (см. gen_product.py). */
.product__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3) var(--s-7);
  flex-wrap: wrap;
  margin-bottom: var(--s-4);
}
.product__title { min-width: 0; display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; }
/* В макете заголовок карточки 38 px на ширине 1536 — это 35,6 px у нас, тогда
   как общий --fs-h1 на 1440 разворачивается в 40. Локальный потолок 34 держит
   заголовок и строку шагов в одной строке. */
.product__title h1 { margin: 0; font-size: clamp(25px, 2.5vw, 34px); }
.product__flags { display: flex; gap: 6px; }
.product__flags:empty { display: none; }
/* Подзаголовок и теги живут в панели «Описание». */
.product__sub { color: var(--muted); margin-bottom: var(--s-3); }
.product__tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: var(--s-4); }

/* Шаги: в макете это кружок 19 px + подпись 14 px, пары разделены воздухом в
   28 px, соединительных линий нет. Общий .steps-bar в components.css крупнее
   (кружок 26) — уменьшаем только здесь, чтобы не трогать другие страницы. */
.product__steps { gap: 0; }
/* min-height — цель нажатия: без него строка шага 20 px, пальцем не попасть. */
/* Селектор по классу, а не по тегу: до загрузки скрипта шаги напечатаны
   как <span>, после — заменены на <button>. Пока правило ловило только
   button, полоса стояла в 21 px и подпрыгивала до 44, когда JS доходил до
   неё, — CLS 0.063 на пустом месте. */
.product__steps .steps-bar__item {
  background: none; border: 0; cursor: pointer; font: inherit;
  color: var(--ink-2); display: flex; align-items: center; gap: 9px;
  min-height: 44px; padding: 0 13px; font-weight: 600; font-size: var(--fs-sm);
}
.product__steps .steps-bar__item:first-child { padding-left: 0; }
.product__steps .steps-bar__item:last-child { padding-right: 0; }
.product__steps .steps-bar__n {
  width: 18px; height: 18px; font-size: 10.5px; font-weight: 800;
  background: var(--white); color: var(--muted);
  border: 1px solid var(--line-2);
}
.product__steps .is-active { color: var(--ink); font-weight: 800; }
.product__steps .is-active .steps-bar__n { background: var(--orange); border-color: var(--orange); color: var(--white); }
.product__steps .is-done .steps-bar__n { background: var(--sage); border-color: var(--sage); color: var(--white); }
.product__steps .steps-bar__item:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; border-radius: var(--radius-xs); }

/* ============================== СЕТКА ============================== */

.product__layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--aside-w);
  /* Вторая строка гибкая специально: панель конфигуратора занимает обе строки
     и, будь обе строки auto, грид раздал бы её высоту поровну — под сценой
     появлялась дыра в пол-экрана. Гибкий трек забирает весь избыток себе. */
  grid-template-rows: auto 1fr;
  /* По макету: между колонками 27 px, между сценой и вкладками 15. */
  gap: var(--s-4) var(--s-6);
  align-items: start;
}
.product__scene { grid-column: 1; grid-row: 1; min-width: 0; }
/* Панель занимает обе строки правой колонки: смета видна рядом со сценой и
   не уезжает под вкладки. */
.product__aside {
  grid-column: 2; grid-row: 1 / span 2;
  display: flex; flex-direction: column; gap: var(--s-3);   /* по макету 13 px */
  min-width: 0;
}
/* Вкладки — под сценой, в той же колонке, как в макете. На всю ширину их
   растягивать нельзя: панель справа занимает обе строки, и вкладки налезли бы
   на неё вторым слоем. */
.product__info { grid-column: 1; grid-row: 2; min-width: 0; }

/* ============================== СЦЕНА ==============================
   Внутренний .scene и его .scene__canvas / .scene__hint / .scene__toolbar /
   .scene__tool описаны в css/pages/scene.css — их владелец модуль сцены, и
   здесь они не переопределяются. Наше — обёртка .scene-box с плавающими
   кнопками: они лежат ЭТАЖОМ ВЫШЕ сцены, потому что SVG-фолбэк при каждой
   перерисовке переписывает содержимое своего контейнера целиком.
   ================================================================== */

/* container-type нужен для правила ниже: ширина сцены задаётся сеткой
   страницы и с шириной окна связана не монотонно, поэтому медиазапрос
   здесь врёт. position: relative уже стоял — контейнер абсолютных
   наложений не меняется. */
.scene-box { position: relative; container-type: inline-size; container-name: scene; }

/* Полноэкранный режим просим у обёртки, а не у сцены: иначе кнопки останутся
   за кадром. Сцена внутри должна забыть про пропорцию 16/10 и занять экран. */
.scene-box:fullscreen { background: var(--bg-2); }
.scene-box:fullscreen .scene {
  aspect-ratio: auto; width: 100%; height: 100%; max-height: none;
  border: 0; border-radius: 0;
}

/* Переменные объявлены на самой схеме, а не на контейнере: тем же рисунком
   живут карточки «Похожих решений», а они лежат вне сцены — с переменными на
   .scene там не находилось ни одного значения и SVG уходил в чёрный. */
.scene-art {
  --art-sky: var(--bg-2);
  --art-ground: var(--line);
  --art-fill: var(--navy-700);
  --art-post: var(--navy);
  --art-line: var(--muted-2);
  --art-dim: var(--muted);
}
.scene-box[data-time="night"] .scene-art {
  --art-sky: var(--navy-900);
  --art-ground: var(--navy);
  --art-fill: var(--navy-700);
  --art-post: var(--navy-900);
  --art-line: var(--navy-700);
  --art-dim: var(--muted-2);
}
.scene-art__sky { fill: var(--art-sky); }
.scene-art__ground { fill: var(--art-ground); }
.scene-art__fill { fill: var(--art-fill); }
.scene-art__post { fill: var(--art-post); }
.scene-art__line { stroke: var(--art-line); stroke-width: 1.5; fill: none; opacity: .55; }
.scene-art__base { stroke: var(--art-line); stroke-width: 2; }
.scene-art__dim { stroke: var(--art-dim); stroke-width: 1.5; fill: none; }
.scene-art__dim text {
  fill: var(--art-dim); stroke: none; font-size: 20px; font-weight: 700;
  font-family: var(--font);
}

/* --- плавающие элементы поверх сцены --- */
.scene-box__try { position: absolute; top: var(--overlay-gap); left: var(--overlay-gap); z-index: 2; min-height: 44px; }

/* Панель инструментов из scene.css — в строку и в правом верхнем углу, как в
   макете: там это одна белая карточка на три пункта, а не три отдельные
   кнопки. Общий вид карточки задан в scene.css, здесь только координаты. */
.scene-box__tools { flex-direction: row; top: var(--overlay-gap); right: var(--overlay-gap); }

/* --- миниатюры ракурсов ---
   В макете это три картинки-превью 106×66 с подписью в нижней полосе;
   активная обведена оранжевым. Фотографий у нас нет, поэтому в плитке
   маленькая схема того же ракурса (см. view_art в gen_product.py). */
.scene-box__views {
  position: absolute; left: 50%; bottom: var(--overlay-gap); transform: translateX(-50%);
  display: flex; gap: var(--s-2); z-index: 2;
}
.scene-box__view {
  display: flex; flex-direction: column; width: 100px; padding: 0; cursor: pointer;
  background: var(--white); color: var(--ink-2); overflow: hidden;
  border: 2px solid var(--white); border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm); font-size: var(--fs-xs); font-weight: 700;
}
.scene-box__view-pic { display: block; height: 42px; }
.scene-box__view-text { display: block; padding: 5px 4px 6px; text-align: center; }
.scene-box__view .view-art { display: block; width: 100%; height: 100%; }
.scene-box__view:hover { color: var(--ink); }
.scene-box__view.is-active { border-color: var(--orange); color: var(--ink); }
.scene-box__view:focus-visible { outline: 3px solid var(--gold); outline-offset: 2px; }

/* Локальные цвета миниатюры: те же роли, что у большой схемы .scene-art. */
.view-art {
  --va-sky: var(--bg-2);
  --va-ground: var(--line);
  --va-solid: var(--navy);
  --va-line: var(--muted-2);
}
.scene-box[data-time="night"] .view-art {
  --va-sky: var(--navy-900);
  --va-ground: var(--navy-700);
  --va-solid: var(--navy);
  --va-line: var(--navy-700);
}
.view-art__sky { fill: var(--va-sky); }
.view-art__ground { fill: var(--va-ground); }
.view-art__solid { fill: var(--va-solid); }
.view-art__line { stroke: var(--va-line); stroke-width: 1.4; fill: none; }
.view-art__post { stroke: var(--va-solid); stroke-width: 3; fill: none; }
.view-art__plan { stroke: var(--va-solid); stroke-width: 3.4; fill: none; stroke-linecap: round; }

/* «День / Ночь»: в макете это белая таблетка с тонким разделителем; активную
   половину отмечает не тёмная заливка, а оранжевая иконка и жирная подпись.
   Заливкой не выделяем — она в макете белая у обеих половин. */
.scene-box__time {
  position: absolute; right: var(--overlay-gap); bottom: var(--overlay-gap);
  box-shadow: var(--shadow-sm); z-index: 2;
  padding: 3px; gap: 0;
}
.scene-box__time button {
  display: flex; align-items: center; gap: 8px; min-height: 40px;
  padding: 0 18px; color: var(--ink-2);
}
.scene-box__time button + button { border-left: 1px solid var(--line); }
.scene-box__time svg { width: 18px; height: 18px; color: var(--muted-2); }
.scene-box__time button[aria-pressed="true"],
.scene-box__time button.is-active { background: var(--bg-2); color: var(--ink); }
.scene-box__time button[aria-pressed="true"] svg { color: var(--orange); }

/* Своё сообщение о состоянии сцены — под кнопкой «Примерить», потому что низ
   занят ракурсами. Оно живёт всю жизнь кадра и говорит на каждом этапе своё:
   до запуска — что в кадре фотография и что сцена включится по нажатию, во
   время загрузки — что модель собирается, после запуска — чем её крутить
   (класс is-controls на .scene-box, ставит product-page.js). Собственную
   подсказку модуля сцены к этому моменту модуль уже не рисует: инструкция на
   кадре одна. */
.scene-box__msg {
  position: absolute; left: var(--overlay-gap); top: calc(var(--overlay-gap) + 48px);
  max-width: 44%; padding: 8px 12px; z-index: 2;
  background: var(--white); border-radius: var(--radius-xs); box-shadow: var(--shadow-sm);
  font-size: var(--fs-xs); color: var(--muted);
}
.scene-box__msg:empty, .scene-box__msg[hidden] { display: none; }

/* Подсказку про управление рисует модуль сцены. В макете низ кадра занят
   миниатюрами ракурсов, а они начинаются с 34 % ширины — ужимаем плашку, чтобы
   она до них не доставала. Убрать совсем нельзя: сцену надо тянуть мышью, и без
   подсказки об этом не догадаться; живёт она до первого движения (класс
   is-quiet ставит сам модуль). Сам компонент не трогаем: правило живёт в
   контексте .scene-box. */
.scene-box .scene__hint { max-width: min(30ch, 32%); font-size: 11px; }

/* ============================ ПАНЕЛЬ ШАГОВ ============================ */

.cfg { padding: var(--s-5); }
.cfg__loading { font-size: var(--fs-sm); color: var(--muted); margin-bottom: var(--s-3); }
.cfg__skeleton { height: 42px; margin-bottom: var(--s-3); }
.cfg__noscript .note { margin-bottom: var(--s-4); }

/* На широком экране видны все четыре группы подряд, как в макете; в режиме
   мастера (класс cfg__fields--wizard, до 900 px) enhanceSteps прячет все,
   кроме текущей, атрибутом hidden. */
/* scroll-margin здесь не нужен: отступ под липкую шапку при прокрутке к якорю
   уже задан один раз в base.css (html { scroll-padding-top }), и второй такой
   же сдвигал заголовок группы на сотню пикселей ниже, чем следует. */
.cfg__step { border: 0; padding: 0; margin: 0; }
.cfg__step[hidden] { display: none; }
/* Отбивка между группами — линия во всю ширину карточки. Первой группе она не
   нужна: над ней край карточки. */
.cfg__step + .cfg__step {
  margin-top: var(--s-4); padding-top: var(--s-4);
  border-top: 1px solid var(--line);
}

/* Заголовок группы: номер в кружке и название — те же, что в полосе шагов над
   панелью, чтобы полоса и панель читались как один указатель. */
.cfg__legend { padding: 0; margin-bottom: var(--s-3); width: 100%; }
.cfg__legend-box {
  display: flex; align-items: center; gap: 8px;
  font-size: var(--fs-sm); font-weight: 800; color: var(--ink);
}
.cfg__legend-n {
  display: grid; place-items: center; width: 18px; height: 18px; flex: none;
  border-radius: 50%; background: var(--bg-2); color: var(--muted);
  border: 1px solid var(--line-2);
  font-size: 10.5px; font-weight: 800;
}
/* В мастере имя шага уже написано в подсвеченной кнопке полосы — повторять его
   внутри карточки незачем, но из разметки убирать нельзя: fieldset без legend
   для скринридера безымянная группа полей. */
.cfg__fields--wizard .cfg__legend {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%);
}
.cfg__fields--wizard .cfg__step + .cfg__step { margin-top: 0; padding-top: 0; border-top: 0; }

/* Колонка значения тянется, а не стоит на жёстких 150 px. Замер по ширинам
   320–1600: в 150 px не помещались «Velur X матовый», «Без укрепления»,
   «100×100×3 мм», «Небольшой уклон» и ещё десяток значений — выбранное нельзя
   было прочитать. Раньше это лечили уменьшением кегля до 13,5, но поля мельче
   16 px заставляют iPhone увеличивать страницу при фокусе, так что кегль
   вернулся к общему, а место должна дать сама сетка. Подпись слева ужимается
   первой: «Заполнение» в две строки читается, обрезанное значение — нет. */
.cfg__row {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(150px, 1.35fr);
  align-items: center; gap: var(--s-3) var(--s-3);
  margin-bottom: var(--s-3);
}
/* В панели поля мельче основного текста: в 150 px колонки «Бетонирование»
   базовым кеглем не помещается, а системный <select> режет строку без
   многоточия — человек видит обрубок. По той же причине ужат отступ под
   стрелку: с общими 36 px «Бетонирование» не влезало и в мелкий кегль. */
/* Кегль полей не опускаем ниже 16 px (см. --fs-field): iPhone зумит форму.
   Компактность держим отступами. */
.cfg__row .select, .cfg__row .input, .cfg__row .stepper input { padding-top: 8px; padding-bottom: 8px; }
.cfg__row .select { padding-right: 29px; background-position: right 9px center; }
.cfg__row:last-child { margin-bottom: 0; }
.cfg__row[hidden] { display: none; }
/* Свотчи, чекбоксы и подсказки занимают всю ширину: в колонку 158px они не
   помещаются, а перенос делает панель рваной. */
.cfg__row--wide { grid-template-columns: 1fr; gap: var(--s-2); }
.cfg__label {
  display: flex; align-items: center; gap: 6px;
  font-size: var(--fs-sm); font-weight: 600; color: var(--ink-2);
}
.cfg__tip {
  position: relative;
  width: 17px; height: 17px; flex: none; padding: 0; cursor: pointer;
  border: 1px solid var(--line-2); border-radius: 50%; background: var(--white);
  color: var(--muted); font-size: 11px; font-weight: 800; line-height: 1;
}
/* Кружок «i» по макету 17 px, но пальцем в него не попасть: зону нажатия
   расширяем прозрачным псевдоэлементом до 44×44, вид при этом прежний. */
.cfg__tip::after {
  content: ''; position: absolute; top: 50%; left: 50%;
  width: 44px; height: 44px; transform: translate(-50%, -50%);
}
.cfg__tip:hover, .cfg__tip[aria-expanded="true"] { border-color: var(--gold); color: var(--navy); }
.cfg__hint {
  grid-column: 1 / -1; margin-top: -4px;
  font-size: var(--fs-xs); color: var(--muted);
}
.cfg__hint[hidden] { display: none; }

/* Поле с единицей измерения: «42 | м» одной рамкой, как в макете. */
.cfg__unit { display: flex; align-items: stretch; }
.cfg__unit .input { border-radius: var(--radius-sm) 0 0 var(--radius-sm); border-right: 0; text-align: left; }
.cfg__unit-label {
  display: grid; place-items: center; min-width: 40px; padding: 0 10px;
  background: var(--bg-2); color: var(--muted);
  border: 1.5px solid var(--line-2); border-left: 0;
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: var(--fs-sm); font-weight: 700;
}
.cfg__row .stepper { justify-self: stretch; }
.cfg__row .stepper input { width: 100%; }
.cfg__row .select, .cfg__row .input { width: 100%; }
.cfg__static { font-weight: 700; text-align: right; }

.cfg__check-line { display: flex; align-items: center; gap: 8px; }
/* --- форма участка: где будет забор ---
   Плитки-планы вместо числа сторон. Радиогруппа: выбран всегда ровно один
   вариант, стрелки переводят и выбирают, выключенные варианты приглушены —
   по ним видно, что дальше дробить периметр некуда. */
.cfg__shapes {
  grid-column: 1 / -1;
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-2);
}
.cfg__shape {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  padding: 10px 6px; min-height: 74px;
  background: var(--white); border: 1px solid var(--line); border-radius: var(--radius-sm);
  color: var(--ink-2); font-size: var(--fs-xs); font-weight: 700; line-height: 1.15;
  text-align: center; cursor: pointer;
}
.cfg__shape:hover { border-color: var(--muted-2); color: var(--ink); }
.cfg__shape[aria-checked="true"] {
  border-color: var(--gold); box-shadow: inset 0 0 0 1px var(--gold);
  background: var(--gold-100); color: var(--navy);
}
.cfg__shape[aria-disabled="true"] { opacity: .42; cursor: not-allowed; }
/* Кадр чуть выше линии чертежа: при 26 px крайние штрихи упирались в край. */
.cfg__shape-plan { width: 100%; height: 30px; }
.cfg__shape-plan path { fill: none; stroke: currentColor; stroke-width: 3; stroke-linejoin: round; stroke-linecap: round; }
.cfg__shapes-note {
  grid-column: 1 / -1; margin: 0; color: var(--muted); font-size: var(--fs-xs); line-height: 1.35;
}
.cfg__shapes-note[hidden] { display: none; }

.cfg__checks { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-2) var(--s-4); }
.cfg__checks .check { font-size: var(--fs-sm); font-weight: 600; }
.cfg__swatch-name { font-size: var(--fs-xs); color: var(--muted); }

/* Образцы RAL: в макете это один ряд из семи цветов и кнопки «+». Восемь
   штук по 34 px в панель не помещались и переносились на вторую строку —
   в макете образец 30 px, берём его. Для пальца 30 мало: зону нажатия
   расширяем псевдоэлементом до 44×44, промежуток 9 px, чтобы зоны соседей
   стыковались, а не наезжали друг на друга. */
.product__aside .swatches { gap: 9px; flex-wrap: nowrap; }
.product__aside .swatch { position: relative; width: 30px; height: 30px; flex: none; }
.product__aside .swatch::after {
  content: ''; position: absolute; top: 50%; left: 50%;
  width: 44px; height: 44px; transform: translate(-50%, -50%);
}

/* ============================== СМЕТА ============================== */

/* Поля карточки 20 px, а не 24 из .card--pad: в макете обе карточки правой
   колонки имеют одинаковые поля в 19 px, и с 24 контрольная колонка перестаёт
   помещаться. */
.price-card { display: flex; flex-direction: column; gap: var(--s-3); padding: var(--s-5); }

/* Цена липнет к верху экрана на широких экранах. Причина: когда параметры
   перестали прятаться в мастер (как в макете), панель выросла примерно до
   1400 px, и человек, меняя высоту или ворота внизу списка, переставал видеть
   сумму — то есть результат своего действия. В макете цена стоит рядом со
   сценой и видна всегда; sticky даёт ровно это, не ломая порядок разметки.
   На узких экранах не нужно: там своя липкая полоса с итогом внизу. */
@media (min-width: 961px) {
  .price-card {
    position: sticky;
    top: calc(var(--header-h) + var(--s-3));
    /* Если панель цены сама выше экрана, sticky превратился бы в ловушку:
       низ карточки стал бы недостижим. Ограничиваем и разрешаем прокрутку. */
    max-height: calc(100vh - var(--header-h) - var(--s-6));
    overflow: auto;
  }
}
/* Подпись и сумма стоят в одну строку, как в макете. nowrap на подписи —
   потому что перенос «Предварительная / стоимость» ломает эту строку надвое и
   карточка перестаёт быть похожей на макет; при нехватке места сумма ужимается
   не она, а кегль подписи (см. clamp ниже). */
.price-card__top { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-2); }
.price-card__label { font-size: var(--fs-sm); font-weight: 700; color: var(--ink); white-space: nowrap; }
/* 24 px = 26 px макета, пересчитанные с 1536 на 1440. */
.price-card__total {
  font-size: 24px; font-weight: 800; letter-spacing: -.02em; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.price-card__per { font-size: var(--fs-xs); color: var(--muted); margin-top: calc(-1 * var(--s-3)); }
.price-card__demo { align-self: flex-start; }
/* Строки разбивки: в макете и подпись, и сумма — обычным начертанием, суммы
   выровнены по правому краю. Жирный там только у итога. */
.price-card__groups { gap: 7px; }
.price-card__groups .summary__row span { color: var(--ink-2); }
.price-card__groups .summary__row b { font-weight: 600; color: var(--ink-2); }
/* Вертикальные поля — цель нажатия: строка «Показать смету построчно» сама по
   себе 21 px. Даём padding, а не min-height с флексом: display у <summary>
   менять нельзя, вместе с ним пропадает треугольник раскрытия. */
.price-card__spec > summary {
  cursor: pointer; font-size: var(--fs-sm); font-weight: 700; color: var(--navy);
  padding: 12px 0;
}
.price-card__spec[hidden] { display: none; }
.price-card__warnings { display: grid; gap: var(--s-2); }
.price-card__warnings:empty { display: none; }
/* Колонку задаём явно. У грида без grid-template-columns единственная колонка
   auto, а её минимум — min-content; у .btn стоит white-space: nowrap, поэтому
   «Бесплатный замер и точная смета» требовала 356 px при карточке в 310 —
   кнопки вылезали за карточку, а документ прокручивался вбок на 37 px.
   minmax(0, 1fr) запрещает колонке расти по содержимому, а перенос строки
   даёт длинной подписи уместиться в двух строках вместо обрезки. */
.price-card__actions { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--s-2); }
/* В макете кнопки 38 px высотой; ниже 44 не опускаемся — это минимальная цель
   нажатия, — но лишние 13 px вертикальных полей .btn убираем. */
.price-card__actions > .btn { white-space: normal; padding-top: 0; padding-bottom: 0; min-height: 44px; }
/* Пара вторичных ссылок в одну строку тоже была шире карточки — на 6 px даже
   на десктопе. .btn--sm добавляет ссылке-кнопке боковые поля, которых у
   .btn--link нет; убираем их (ширина сходится), а цель нажатия добираем
   высотой. Перенос строк оставляем на случай совсем узкой карточки. */
.price-card__minor { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 0 var(--s-3); padding: 2px 0; }
.price-card__minor .btn { min-width: 0; min-height: 44px; padding-left: 0; padding-right: 0; white-space: normal; }

/* «Модель пересчитана» — в макете не плашка, а строка зелёным с кружком-
   галочкой, отделённая от кнопок линией во всю ширину карточки. Карточка
   имеет боковые поля, поэтому линию растягиваем отрицательными полями. */
.price-card__status {
  display: flex; align-items: center; gap: 9px; margin: 0;
  padding-top: var(--s-4);
  margin-inline: calc(-1 * var(--s-5));
  padding-inline: var(--s-5);
  border-top: 1px solid var(--line);
  font-size: var(--fs-sm); font-weight: 700; color: var(--sage);
}
.price-card__status svg { width: 18px; height: 18px; flex: none; }
.price-card__status[hidden] { display: none; }

/* ============================== ВКЛАДКИ ============================== */

.product-tabs { padding: var(--s-5); }
.tabpanel__title { font-size: var(--fs-h3); margin-bottom: var(--s-4); }
/* Без JS панели стоят стопкой и каждая подписана заголовком; когда скрипт
   включает вкладки, заголовки становятся лишними. */
.product-tabs.is-enhanced .tabpanel__title { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.product-tabs .tabpanel + .tabpanel { border-top: 1px solid var(--line); margin-top: var(--s-5); }
.product-tabs.is-enhanced .tabpanel + .tabpanel { border-top: 0; margin-top: 0; }

.spec-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 0 var(--s-8); }
/* Две равные колонки внутри строки, значение прижато влево — так таблица
   характеристик нарисована в макете: «Высота … 2,0 м» читается как пара, а не
   как две крайности строки. */
.spec-grid__item {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  align-items: baseline; gap: var(--s-4);
  padding: 10px 0; border-bottom: 1px solid var(--line);
}
.spec-grid__item dt { color: var(--muted); font-size: var(--fs-sm); }
.spec-grid__item dd { font-weight: 700; }
.spec-grid + .note { margin-top: var(--s-4); }
/* Сноска под таблицей характеристик: в макете это обычная серая строка со
   значком «i», а не жёлтая плашка. Плашка в общем .note--info нужна там, где
   предупреждение должно останавливать взгляд; здесь — примечание. */
.product-tabs .note--info {
  background: none; border-color: transparent; color: var(--muted);
  padding: var(--s-3) 0 0; font-size: var(--fs-xs);
}
.product-tabs .note--info svg { color: var(--muted-2); }

.kit-list { list-style: none; display: grid; gap: var(--s-2); margin-bottom: var(--s-5); }
.kit-list__item {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-3) var(--s-4); background: var(--bg-2); border-radius: var(--radius-sm);
}
.kit-list__name { display: flex; flex-direction: column; }
.kit-list__name a { font-weight: 700; text-decoration: underline; text-underline-offset: 3px; }
.kit-list__name small { color: var(--muted); }
.kit-list__price { font-weight: 800; white-space: nowrap; text-align: right; }
.kit-list__price small { display: block; font-weight: 600; color: var(--muted); }

/* ============================ ПОХОЖИЕ ============================ */

.related-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s-5); }
/* В карточках «Похожих» лежит либо схема, либо фотография в <picture>. Размеры
   и object-fit картинке задаёт .product-card__media img в css/components.css;
   здесь — только сам <picture>: он инлайновый, и без display:block картинка
   внутри меряет высоту от строки текста, а не от рамки. */
.related-grid .product-card__media picture { display: block; width: 100%; height: 100%; }

/* ============================== АДАПТИВ ============================== */

@media (max-width: 1180px) {
  .product { --aside-w: 340px; }
  .scene-box__view { width: 78px; }
  .related-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 960px) {
  /* Одна колонка: сцена → конфигуратор со сметой → вкладки. Порядок совпадает
     с порядком в разметке, поэтому переставлять ничего не нужно. */
  .product__layout { grid-template-columns: minmax(0, 1fr); }
  .product__scene, .product__aside, .product__info { grid-column: 1; grid-row: auto; }
  .product__head { flex-direction: column; align-items: stretch; }
  .product__steps { margin-top: 0; }
  .scene-box__msg { max-width: none; right: var(--overlay-gap); }
  /* Панель на всю ширину — полю можно дать больше места. */
  .cfg__row { grid-template-columns: minmax(0, 1fr) 220px; }
  /* Правило выше по весу равно .cfg__row--wide, но лежит ниже по файлу и
     перекрывало его: образцы RAL (они не переносятся) попадали в колонку 220 px
     и высовывали страницу за край окна на полсотни пикселей. Повторяем
     широкую строку здесь. */
  .cfg__row--wide { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
  .scene-box { --overlay-gap: var(--s-2); }
  /* «Примерить на участке» и кнопки сцены делят верхний край холста: на 360 px
     подпись в одну строку налезала на инструменты. Ограничиваем ширину и
     разрешаем перенос — обрезать подпись нельзя, она же и есть название. */
  .scene-box__try { max-width: 46%; white-space: normal; justify-content: flex-start; text-align: left; }
  /* Плитки ракурсов ужимаем, но подпись оставляем: без неё три картинки 50×30
     не читаются вообще — непонятно, чем «Участок» отличается от «Сверху». */
  .scene-box__view { width: 64px; }
  .scene-box__view-pic { height: 30px; }
  .scene-box__view-text { padding: 3px 2px 4px; font-size: 10.5px; }
  /* «День/Ночь» переезжает под панель инструментов в правый верхний угол: внизу
     на 320 px он и плитки ракурсов встречаются посередине и налезают друг на
     друга. Сверху справа они разведены по вертикали. */
  .scene-box__time { bottom: auto; top: calc(var(--overlay-gap) + 52px); padding: 2px; }
  .scene-box__time button { padding: 0 12px; min-height: 38px; font-size: var(--fs-xs); }
  /* Стартовая подпись («фотография похожего ограждения, нажмите на кадр»)
     в 320 px разваливается в столбик и лезет на плитки ракурсов — прячем.
     А вот строка управления после запуска короткая («Тяните — поворот, два
     пальца — масштаб») и нужна как раз на телефоне: собственную подсказку
     модуля здесь не видно. */
  .scene-box__msg { display: none; }
  /* Наверху места нет: слева «Примерить» в две строки, справа инструменты и
     под ними «День/Ночь». Свободна только полоса над миниатюрами ракурсов —
     туда строку и кладём, во всю ширину кадра. Ракурсы на 320 px высотой
     около 50 px, отсюда отступ снизу. */
  .scene-box.is-controls .scene-box__msg {
    display: block;
    top: auto;
    bottom: calc(var(--overlay-gap) + 58px);
    text-align: center;
  }
  /* Подсказка модуля на телефоне и неверна (там пальцем, а не мышью), и негде
     её положить: в 320 px она либо лезет на плитки, либо разваливается в
     столбик из шести строк. */
  .scene-box .scene__hint { display: none; }
  /* На телефоне подпись встаёт над полем: в две колонки на 320 px и подпись,
     и значение обрезаются одновременно. */
  .cfg__row { grid-template-columns: 1fr; gap: 6px; }
  .cfg__hint { margin-top: 0; }
  .cfg__checks { grid-template-columns: 1fr; }
  .related-grid { grid-template-columns: minmax(0, 1fr); }
  .price-card__total { font-size: 24px; }
  .product-tabs { padding: var(--s-4); }

  /* ---------------- ЛИПКАЯ ПОЛОСА С ИТОГОМ (только телефон) ----------------
     На телефоне карточка сметы стоит ПОД панелью конфигуратора: до цены две
     тысячи пикселей прокрутки, а «В корзину» ещё ниже. Приклеиваем итог и
     кнопку к низу экрана.

     Липкий элемент держится внутри своего содержащего блока, поэтому полоса
     должна принадлежать не карточке сметы (она сама начинается только после
     панели), а всей колонке .product__aside. Отсюда display: contents на
     карточке: она перестаёт быть боксом, её содержимое поднимается на уровень
     колонки, и полоса живёт от первого поля конфигуратора до последней строки
     сметы. Оформление карточки при этом переезжает на саму колонку — панель и
     смета на телефоне читаются как одна карточка «конфигуратор и смета». */
  .product__aside {
    flex-flow: row wrap;
    align-items: flex-start;
    row-gap: var(--s-3);
    column-gap: 0;
    background: var(--white);
    border: 1px solid var(--line);
    border-radius: var(--radius);
    padding: var(--s-4);
  }
  /* На телефоне настройка идёт ПЕРЕД сметой.
     На широком экране смета стоит вровень с фотографией — так и задумано. Но в
     одну колонку это превращается в «фото → цена → кнопка „В корзину“ → и
     только на 1150-м пикселе первое поле»: человек читает кнопку как «настройка
     закончена» и покупает комплектацию по умолчанию, не увидев ни одного поля.
     Сумма при этом никуда не девается — она липкой полосой внизу экрана
     (order: 90 ниже), то есть видна всё время настройки. */
  .cfg { background: none; border: 0; border-radius: 0; padding: 0; order: -1; }
  .price-card, .price-card__actions { display: contents; }
  /* Поля колонки на телефоне 16 px, а не 20 — линию над «Модель пересчитана»
     растягиваем ровно на них, иначе она вылезает за карточку на 4 px. */
  .price-card__status { margin-inline: calc(-1 * var(--s-4)); padding-inline: var(--s-4); }
  .product__aside > *, .price-card > *, .price-card__actions > * { flex: 1 1 100%; min-width: 0; }
  /* Значок «предварительно» шириной по тексту, иначе плашка растянется на всю
     строку и станет похожа на кнопку. */
  .price-card__demo { flex: 0 0 auto; }
  /* Отрицательный отступ в поля колонки: полоса идёт от края до края. */
  .price-card__top {
    order: 90;                       /* в потоке — в самом низу колонки */
    /* Базис 0, а не auto: перенос строк во flex считается по базисам ДО сжатия,
       и при auto подпись «Предварительная стоимость» уводила кнопку на
       следующую строку — две липкие полосы наезжали друг на друга. */
    flex: 1 1 0;
    align-self: stretch;
    position: sticky; bottom: 0; z-index: 3;
    flex-direction: column; align-items: flex-start; gap: 0;
    margin-left: calc(-1 * var(--s-4));
    padding: var(--s-2) var(--s-3) var(--s-2) var(--s-4);
    background: var(--white);
    box-shadow: 0 -1px 0 var(--line);
    line-height: 1.15;               /* полоса не должна съедать пол-экрана */
  }
  .price-card__label { font-size: var(--fs-xs); color: var(--muted); }
  /* Отступ «за пог. м» подтягивал строку к итогу, который уехал в полосу. */
  .price-card__per { margin-top: 0; }
  .price-card__actions > [data-act="cart"] {
    order: 90;                       /* тот же order — встаёт в одну строку с итогом */
    flex: 0 0 auto;
    /* .btn--block задаёт width: 100%, а flex-basis: auto берёт ширину из него —
       кнопка требовала всю строку и итог уезжал на строку выше. */
    width: auto;
    align-self: stretch;
    position: sticky; bottom: 0; z-index: 3;
    margin-right: calc(-1 * var(--s-4));
    border-radius: 0;
    white-space: nowrap;
    box-shadow: 0 -1px 0 var(--line);
  }
}

/* Раскрывающийся блок «Ещё параметры» внутри группы. Второстепенные поля
   (заглубление, сечение столба, грунт, рельеф) не должны отодвигать карточку
   цены за первый экран, но и прятать их совсем нельзя: на них держится смета. */
.cfg__more { grid-column: 1 / -1; margin-top: var(--s-2); }
.cfg__more > summary {
  cursor: pointer; list-style: none; padding: 8px 0;
  font-size: var(--fs-sm); font-weight: 700; color: var(--navy);
  display: flex; align-items: center; gap: 7px;
}
.cfg__more > summary::-webkit-details-marker { display: none; }
.cfg__more > summary::before {
  content: ''; width: 8px; height: 8px; flex: none;
  border-right: 2px solid currentColor; border-bottom: 2px solid currentColor;
  transform: rotate(-45deg); transition: transform var(--dur);
}
.cfg__more[open] > summary::before { transform: rotate(45deg); }
.cfg__more > summary:hover { color: var(--gold-600); }
.cfg__more-box { display: grid; gap: var(--s-3); padding-top: var(--s-2); }

/* На телефоне четыре плитки в ряд дают 70 px на плитку — подпись «С трёх
   сторон» в них не читается. Кладём в две строки по две. */
@media (max-width: 520px) {
  .cfg__shapes { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* --- миниатюра «Сверху»: показываем ровно столько сторон, сколько считается ---
   В плитке лежат все четыре плана; видимым остаётся тот, чей data-n совпал с
   data-sides на кнопке. Так число сторон не приходится дублировать в Python и
   в JS: генератор печатает геометрию, скрипт пишет одно число. */
.view-art__plan[data-n] { display: none; }
[data-sides="1"] .view-art__plan[data-n="1"],
[data-sides="2"] .view-art__plan[data-n="2"],
[data-sides="3"] .view-art__plan[data-n="3"],
[data-sides="4"] .view-art__plan[data-n="4"] { display: block; }
/* Дом в кадре плана уместен только у замкнутого контура: у прямой линии
   квадратик посреди пустоты читается ошибкой рисунка. */
[data-sides="1"] .scene-box__view-pic .view-art__solid,
[data-sides="2"] .scene-box__view-pic .view-art__solid { display: none; }

/* Сообщение об ошибке под полем конфигуратора: появляется, только когда есть
   что сказать. Цвет — токен ошибки, он проверен на контраст. */
.cfg__error {
  grid-column: 1 / -1; margin: 4px 0 0; color: var(--danger);
  font-size: var(--fs-xs); font-weight: 700; line-height: 1.35;
}
.cfg__error[hidden] { display: none; }


/* ====================== правый верхний угол кадра ======================

   Плитки ракурсов центрированы по низу кадра, таблетка «День/Ночь» прижата к
   правому нижнему углу. Пока кадр широкий, они расходятся; на узком таблетка
   наезжает на четвёртую плитку, и «Сверху» перестаёт нажиматься — а это
   единственный ракурс, который показывает раскладку по участку.

   Замер шагом 20 px: наложение на 660–800 и 980–1260, центр «Сверху» перехвачен
   на 660–720 и 980–1080. Диапазона два, а не один, потому что ширина кадра
   зависит от сетки страницы и с шириной окна связана не монотонно: 628 → 860
   (окно 660–900), падение до 576 на 980, когда включается вторая колонка, и
   рост до 904. Поэтому условие ставим на КОНТЕЙНЕР — медиазапрос здесь врёт.

   Порог считается, а не подбирается: плитки центрированы, столкновение
   начинается, когда ширина кадра меньше, чем плитки + два зазора + две ширины
   таблетки = 424 + 24 + 402 = 850. Берём 860; фактический максимум кадра 904.

   Блок стоит в конце файла намеренно: ни @media, ни @container не добавляют
   специфичности, и на прежнем месте его перебивали базовое правило таблетки
   и медиазапрос 960 px для подписи. */
@container scene (max-width: 860px) {
  /* Отступ 70, а не 52 как в мобильном медиазапросе: там панель инструментов
     ниже (подписи мельче), а здесь она 62 px — на 52 таблетка задевала её
     углом на 10 px. */
  .scene-box__time { bottom: auto; top: calc(var(--overlay-gap) + 70px); }
  /* Подпись на кадре в этой полосе тянется во всю ширину (медиазапрос 960 px),
     а таблетка теперь стоит сверху справа. Освобождаем ей место: без этого
     строка уезжала под таблетку — замерено до 147 px скрытого текста на 680 px.
     213 = ширина таблетки 201 + зазор 12.

     Нижняя граница обязательна: на телефоне 320–414 px после вычета 213 от
     подписи остаётся узкая колонка, и строка управления встаёт столбиком
     поверх забора. Там таблетка и подпись и так разведены по вертикали
     медиазапросом 640 px, освобождать место не нужно. */
  .scene-box__msg { right: calc(var(--overlay-gap) + 213px); }
}
@container scene (max-width: 560px) {
  .scene-box__msg { right: var(--overlay-gap); }
}
