/* Витрина СкладСок на дизайн-системе adamSod.
   Здесь нет ни одного «своего» цвета, размера или радиуса — только токены
   из ds/. Каждый блок ниже — это компонент системы, собранный на нашей
   разметке (у нас ванильный JS, а система поставляется в JSX).

   Соответствие: Button, IconButton, Card, Badge, Tag, Icon, Wordmark,
   Input, Select, Switch, NavBar, Tabs, SideRail, Dialog, FactsTable,
   Stat, ProductTile. */

/* ─────────────────────────────────────────────────── поле страницы */
/* «adamSod pages are immersive»: одна насыщенная заливка от края до края,
   поверх неё --field-lift. Никаких белых контейнеров и полос-секций. */

/* display у компонентов перебивает [hidden] — возвращаем атрибуту силу. */
[hidden] { display: none !important; }

/* Пока открыт раздел-страница, каталог за ним не прокручивается: иначе
   в полосу под шапкой видно плитки, и раздел выглядит окном поверх сайта. */
.is-page-open, .is-page-open body { overflow: hidden; }

/* iOS Safari сам увеличивает страницу, когда курсор встаёт в поле со шрифтом
   мельче 16px, и обратно уже не отъезжает. Поэтому на узких экранах все поля
   набраны ровно 16px. Это и есть тот «зум на инпутах»: запрещать масштаб
   через user-scalable не нужно — iOS этот запрет игнорирует с 2016 года,
   а людям со слабым зрением он ломает сайт. */
@media (max-width: 900px) {
  input, select, textarea { font-size: 16px; }
  .input input, .select select, .stepper__input { font-size: 16px; }
}

body {
  background: var(--bg-page);
  background-image: var(--field-lift);
  background-attachment: fixed;
  min-height: 100vh;
}

/* Витрина идёт во всю ширину экрана, а не колонкой по центру: в макете
   поля страницы 96px, а каталог занимает всё остальное. На узком экране
   поля сжимаются, чтобы карточкам оставалось место. */
.shell {
  max-width: none;
  margin: 0 auto;
  padding: 0 clamp(16px, 5vw, 96px);
}

/* ───────────────────────────────────────────────────────── Icon */
/* Глиф из Lucide накладывается маской и всегда красится currentColor —
   иконка физически не может стать двухцветной или «не тем» цветом. */

.ads-icon {
  display: inline-block;
  width: 18px;
  height: 18px;
  flex: 0 0 auto;
  background-color: currentColor;
  -webkit-mask: var(--icon) no-repeat center / contain;
  mask: var(--icon) no-repeat center / contain;
}
.ads-icon--16 { width: 16px; height: 16px; }
.ads-icon--22 { width: 22px; height: 22px; }

/* ─────────────────────────────────────────────────────── Wordmark */
/* Логотипа у бренда нет — марка это набор имени дисплейной гарнитурой,
   вторая половина слова акцентом. */

.wordmark {
  font-family: var(--font-display);
  font-variation-settings: "wdth" var(--display-stretch);
  font-weight: var(--weight-black);
  font-size: 24px;
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--text-title);
  white-space: nowrap;
  border-bottom: none;
}
/* Раньше акцент задавался как .wordmark span — но после того, как марка
   разделилась на знак и имя, под этот селектор попал и сам .wordmark__name
   (он тоже span) и, будучи специфичнее, красил золотым всё слово.
   Акцент живёт в .wordmark__name span ниже: «adam» белым, «Sod» жёлтым. */
.wordmark:hover { color: var(--text-title); }

/* ───────────────────────────────────────────────────────── Button */
/* Всё, что нажимается, — пилюля. Верхний регистр задаётся здесь,
   в разметке текст набран обычным предложением. */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  height: var(--control-h-md);
  padding: 0 var(--control-pad-x-md);
  border-radius: var(--radius-pill);
  border: var(--border-w-strong) solid transparent;
  font-family: var(--font-sans);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  transition: var(--transition-control), transform var(--dur-instant) var(--ease-out);
}
.btn:active { transform: scale(var(--press-scale)); }
.btn:disabled { opacity: .38; cursor: not-allowed; transform: none; }

.btn--sm { height: var(--control-h-sm); padding: 0 var(--control-pad-x-sm); font-size: var(--text-xs); }
.btn--lg { height: var(--control-h-lg); padding: 0 var(--control-pad-x-lg); font-size: var(--text-base); letter-spacing: .1em; }

.btn--primary { background: var(--ink-000); color: var(--cobalt-800); border-color: var(--ink-000); }
.btn--primary:hover:not(:disabled) { background: var(--cobalt-100); border-color: var(--cobalt-100); }

.btn--accent { background: var(--accent); color: var(--text-on-accent); border-color: var(--accent); }
.btn--accent:hover:not(:disabled) { background: var(--sun-400); border-color: var(--sun-400); }

.btn--outline { background: transparent; color: var(--text-title); border-color: var(--border-strong); }
.btn--outline:hover:not(:disabled) { background: var(--white-a08); border-color: var(--ink-000); }

.btn--ghost { background: transparent; color: var(--text-body); }
.btn--ghost:hover:not(:disabled) { background: var(--white-a08); color: var(--text-title); }

/* ───────────────────────────────────────────────────── IconButton */

.icon-btn {
  width: 38px;
  height: 38px;
  display: inline-grid;
  place-items: center;
  flex: 0 0 auto;
  border-radius: var(--radius-circle);
  border: var(--border-w-strong) solid transparent;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  transition: var(--transition-control);
}
.icon-btn:hover { background: var(--white-a08); color: var(--text-title); }
.icon-btn--sm { width: 30px; height: 30px; }

/* ───────────────────────────────────────────────────────── NavBar */
/* 76px, полностью прозрачная. Никакого фростед-стекла: блюр в системе
   разрешён ровно в одном месте — скрим модалки.

   NavBar у системы липкая — на маркетинговой странице
   с разреженным контентом это работает. Над плотной сеткой каталога она
   накладывается на плитки и обе становятся нечитаемыми, а подложку под неё
   класть нельзя: «Nav and rail are fully transparent — no frosted bar».
   Поэтому липкость выключена — у компонента это штатный параметр sticky. */
.nav {
  position: static;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  gap: var(--space-8);
  padding: 0 var(--gutter-page-tight);
  background: transparent;
}
.nav__links { display: flex; align-items: center; gap: var(--space-2); flex: 1; }
.nav__link {
  padding: 8px 16px;
  border-radius: var(--radius-pill);
  border: 0;
  border-bottom: none;
  background: transparent;
  font: var(--type-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
  cursor: pointer;
  transition: var(--transition-control);
}
.nav__link:hover { color: var(--text-title); }
.nav__link[aria-current="true"] { background: var(--white-a12); color: var(--text-title); }

/* ─────────────────────────────────────────────────────── SideRail */
/* Фиксированная вертикальная полоса в левом жёлобе. */

.rail {
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  left: calc((var(--gutter-page-tight) - var(--rail-w)) / 2 + 8px);
  width: var(--rail-w);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-4);
  z-index: 30;
}

/* ──────────────────────────────────────────────── мелочи типографики */

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.stat__unit {
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  margin-left: 4px;
  color: var(--text-body);
}

/* ──────────────────────────────────────────── панель фильтров */

/* Панель поиска и фильтров — Card tone="glass" из системы: белая альфа,
   хайрлайн, радиус 5px. Раньше контролы висели прямо на поле, читались как
   набор случайных элементов и терялись на насыщенной заливке. */
/* Контролы стоят прямо на поле, без «коробки»: панель перетягивала на себя
   внимание с товара и делала первый экран тяжёлым. Границу с шапкой держит
   хайрлайн у .hero снизу. */
.filters {
  display: grid;
  gap: var(--space-4);
  padding: 0;
  margin: var(--space-5) 0 var(--space-7);
  background: none;
  border: 0;
  border-radius: 0;
}
/* Ряд контролов выровнен по центру, а не по нижнему краю: у поля поиска,
   свитча и сегментированных переключателей разная внутренняя высота, и по
   нижней линии они вставали ступенькой. Общая высота --filter-h делает
   верх и низ полосы одной прямой. */
.filters__row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}
/* Контролы справа держатся вместе и прижаты к правому краю полосы. */
.filters__tools {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-left: auto;
  min-width: 0;
}
.filters__foot {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding-top: var(--space-4);
  border-top: var(--border-w-hair) solid var(--border-hairline);
}
/* Счётчик слева, чипы посередине, сброс всегда прижат вправо. */
.filters__foot .toolbar__found { flex: 0 0 auto; }
.filters__foot .tags { flex: 1 1 auto; }
.filters__foot #reset { margin-left: auto; }
.toolbar__found { font: var(--type-body-sm); color: var(--text-muted); }


/* Input — тёмный колодец, 5px, жёлтый фокус */
.input {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  height: var(--control-h-md);
  padding: 0 14px;
  background: var(--surface-inset);
  border: var(--border-w-strong) solid var(--border-hairline);
  border-radius: var(--radius-sm);
  color: var(--text-muted);
  transition: var(--transition-control), box-shadow var(--dur-fast) var(--ease-out);
}
/* Фокус в поле: рамка жёлтая — тем же цветом, что включённый переключатель
   и выбранная половина сегментного контрола. Одно состояние «здесь сейчас
   действие» — один цвет по всей витрине.
   (Старое правило белило рамку; ниже оставлен его след для истории.)
   Жёлтый ореол в 3px читался как подсветка ошибки и держался всё время,
   пока в поле стоит курсор (текстовые поля по спецификации всегда считаются
   «фокусом с клавиатуры», поэтому :focus-visible тут ничего не меняет).
   Белая рамка на кобальтовом поле видна не хуже, а акцент остаётся
   за ценами и кнопкой заявки. */
.input:focus-within { border-color: var(--accent); }
.input input {
  all: unset;
  flex: 1;
  min-width: 0;
  font-family: var(--font-sans);
  font-size: var(--text-base);
  color: var(--text-title);
}
.input input::placeholder { color: var(--text-muted); }

/* Кнопка очистки поиска. Появляется только когда есть что очищать. */
.input__clear {
  width: 26px;
  height: 26px;
  flex: 0 0 auto;
  display: grid;
  place-items: center;
  margin-right: -6px;
  border: 0;
  border-radius: var(--radius-circle);
  background: var(--white-a12);
  color: var(--text-title);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
  transition: var(--transition-control);
}
.input__clear::before { content: "×"; }
.input__clear:hover { background: var(--white-a20); }
.input__clear[hidden] { display: none; }

/* Select — нативный, подогнан под Input */
.select { position: relative; display: inline-block; }
.select::after {
  content: "▾";
  position: absolute;
  right: 14px;
  top: 50%;
  transform: translateY(-50%);
  pointer-events: none;
  color: var(--text-muted);
  font-size: 10px;
}
.select select {
  all: unset;
  box-sizing: border-box;
  width: 100%;
  height: var(--control-h-md);
  padding: 0 38px 0 14px;
  background: var(--surface-inset);
  color: var(--text-title);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  border: var(--border-w-strong) solid var(--border-hairline);
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: var(--transition-control);
}
/* Фокус — белая рамка, без жёлтого кольца. Браузер считает <select> и поля
   ввода «фокусом с клавиатуры» даже при клике мышью, поэтому :focus-visible
   ничего не спасал: жёлтое кольцо загоралось от обычного клика и висело,
   пока не уведёшь фокус. Белая рамка на кобальтовом поле видна не хуже. */
.select select:focus,
.select select:focus-visible { border-color: var(--text-title); box-shadow: none; }
.select option { color: var(--ink-900); }
.select--sm select { height: var(--control-h-sm); font-size: var(--text-sm); }

/* Видимая подпись над каждым контролом — как у Input и Select в системе.
   Плейсхолдер подписью быть не может: он исчезает при вводе. */
.field { display: grid; gap: var(--space-2); }
.field__label { font: var(--type-label); letter-spacing: var(--tracking-label); text-transform: uppercase; color: var(--text-muted); }
.field .select { display: block; min-width: 190px; }
.field .switch { height: var(--control-h-md); }

/* Tag — интерактивный фильтр-чип. Категорий больше, чем влезает в одну
   ленту Tabs, а чипы переносятся по строкам сами. */
.tags { display: flex; gap: var(--space-2); flex-wrap: wrap; }

/* Switch — переключатель мгновенного действия */
.switch { display: inline-flex; align-items: center; gap: var(--space-3); cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch__track {
  width: 42px;
  height: 24px;
  flex: 0 0 auto;
  border-radius: var(--radius-pill);
  padding: 3px;
  display: flex;
  align-items: center;
  background: var(--surface-inset);
  border: var(--border-w-hair) solid var(--border-strong);
  transition: var(--transition-control);
}
.switch__thumb {
  width: 16px;
  height: 16px;
  border-radius: var(--radius-circle);
  background: var(--white-a70);
  transition: transform var(--dur-fast) var(--ease-out), background-color var(--dur-fast) var(--ease-out);
}
.switch input:checked + .switch__track { background: var(--accent); border-color: var(--accent); }
.switch input:checked + .switch__track .switch__thumb { background: var(--cobalt-1000); transform: translateX(18px); }
.switch input:focus-visible + .switch__track { box-shadow: var(--ring-focus); }
.switch__label { font: var(--type-body-sm); color: var(--text-title); }

/* ─────────────────────────────────────────────────────── Badge */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  height: 22px;
  padding: 0 10px;
  border-radius: var(--radius-pill);
  font: var(--type-caption);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  white-space: nowrap;
  background: var(--white-a12);
  color: var(--text-title);
  border: var(--border-w-hair) solid var(--border-hairline);
}
.badge--accent { background: var(--accent); color: var(--text-on-accent); border-color: var(--accent); }
.badge--success { background: rgba(63, 208, 122, .16); color: var(--lime-500); border-color: rgba(63, 208, 122, .4); }
.badge--warning { background: rgba(242, 168, 59, .16); color: var(--amber-500); border-color: rgba(242, 168, 59, .4); }
.badge--danger { background: rgba(244, 97, 76, .16); color: var(--rose-500); border-color: rgba(244, 97, 76, .4); }

/* ───────────────────────────────────────────────────────── Tag */

.tag {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  height: 30px;
  padding: 0 14px;
  border-radius: var(--radius-pill);
  background: var(--white-a04);
  border: var(--border-w-hair) solid var(--border-hairline);
  color: var(--text-body);
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  cursor: pointer;
  transition: var(--transition-control);
}
.tag:hover { background: var(--white-a12); color: var(--text-title); }
/* Выбранная категория — тем же жёлтым, что и остальные «включено».
   Раньше она была белой и спорила по весу с выбранной половиной
   переключателя рядом. */
.tag[aria-pressed="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--text-on-accent);
}
.tag__count { font-family: var(--font-mono); font-variant-numeric: tabular-nums; opacity: .55; }

/* Применённый фильтр — чип с крестиком. В системе у Tag для этого есть
   onRemove, и снять фильтр можно там же, где видно, что он включён. */
.tag--applied { background: var(--white-a12); color: var(--text-title); cursor: default; }
.tag--applied:hover { background: var(--white-a12); }
.tag__remove {
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  margin-right: -6px;
  border: 0;
  border-radius: var(--radius-circle);
  background: transparent;
  color: inherit;
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  opacity: .6;
  transition: var(--transition-control);
}
.tag__remove::before { content: "×"; }
.tag__remove:hover { opacity: 1; background: var(--white-a20); }

/* ──────────────────────────────────────────────── ProductTile */
/* Колодец под фото, под ним центрированная подпись. */

/* Число колонок задаётся явно, а не через auto-fill: покупатель выбирает
   выкладку сам («крупно» или «плотно»), и «две картинки в ряд» должно быть
   ровно двумя, а не «сколько поместится». Горизонтального скролла при этом
   нет: minmax(0, 1fr) снимает неявный минимум min-content у колонки. */
.grid {
  display: grid;
  grid-template-columns: repeat(var(--cols, 4), minmax(0, 1fr));
  /* Ряды разведены сильнее колонок: под карточкой стоит кнопка,
     и без воздуха соседние ряды читаются как одна каша. */
  gap: var(--tile-gap-y, 40px) var(--tile-gap-x, 24px);
}

.tile {
  display: grid;
  gap: var(--space-4);
  justify-items: center;
  cursor: pointer;
  text-align: center;
  transition: transform var(--dur-base) var(--ease-out);
}
.tile:hover { transform: translateY(-6px); }

/* Фотографий товаров пока нет — система запрещает подставлять вместо них
   рисованные банки или сгенерированные картинки. Честная пунктирная
   заглушка, которая заменится снимками из 1С. */
.tile__well {
  position: relative;
  width: 100%;
  aspect-ratio: 1 / 1;
  display: grid;
  place-items: center;
  overflow: hidden;
  border-radius: var(--radius-tile);
  background: var(--surface-card);
  border: var(--border-w-hair) dashed var(--border-hairline);
  transition: var(--transition-control);
}
.tile:hover .tile__well { background: var(--surface-card-hover); }
.tile__well img { width: 100%; height: 100%; object-fit: contain; filter: drop-shadow(var(--shadow-product)); }
.tile__placeholder {
  font: var(--type-caption);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
}
.tile__badge { position: absolute; top: 10px; left: 10px; }
.tile__stock { position: absolute; top: 10px; right: 10px; }

.tile__caption { display: grid; gap: 4px; justify-items: center; }
.tile__name {
  font-family: var(--font-display);
  font-variation-settings: "wdth" var(--display-stretch);
  font-weight: var(--weight-semibold);
  font-size: var(--text-base);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
  color: var(--text-title);
}
/* Две строки под название: иначе цены в одном ряду встают на разной высоте. */
.tile__name { min-height: calc(var(--text-base) * var(--leading-snug) * 2); }
.tile__note { font: var(--type-caption); color: var(--text-muted); }
.tile__price {
  font-family: var(--font-display);
  font-variation-settings: "wdth" var(--display-stretch);
  font-weight: var(--weight-bold);
  font-size: var(--text-lg);
  line-height: 1;
  letter-spacing: var(--tracking-tight);
  color: var(--text-title);
  margin-top: var(--space-1);
}

/* Скелетон на время запроса: сетка не должна прыгать и не должна молчать.
   Единственная непрерывная анимация в проекте — правило системы разрешает
   их только индикаторам загрузки. */
.tile--skeleton { cursor: default; pointer-events: none; }
.tile--skeleton .tile__well { border-style: solid; background: var(--surface-card); }
.sk {
  width: 100%;
  border-radius: var(--radius-sm);
  background: var(--white-a08);
}
.sk--name { height: 13px; }
.sk--note { height: 10px; width: 70%; }
.sk--vol { height: 26px; width: 60%; border-radius: var(--radius-pill); }
.sk--price { height: 18px; width: 45%; margin-top: auto; }
.sk--btn { height: 42px; border-radius: var(--radius-pill); }
.tile--skeleton .tile__well, .sk { animation: skPulse 1.4s var(--ease-in-out) infinite; }
@keyframes skPulse { 0%, 100% { opacity: 1 } 50% { opacity: .45 } }
@media (prefers-reduced-motion: reduce) {
  .tile--skeleton .tile__well, .sk { animation: none; }
}

/* Пустой результат — не тупик: рядом лежит выход из него. */
.empty {
  grid-column: 1 / -1;
  display: grid;
  gap: var(--space-4);
  justify-items: center;
  padding: var(--space-11) var(--space-5);
  font: var(--type-body);
  color: var(--text-body);
  text-align: center;
}
.empty__hint { font: var(--type-body-sm); color: var(--text-muted); max-width: 42ch; }

/* ─────────────────────────────────────────────────────── Pager */

.pager { display: flex; flex-wrap: wrap; gap: var(--space-2); justify-content: center; padding: var(--space-9) 0 var(--space-7); }
.pager .btn { min-width: 42px; }

/* ─────────────────────────────────────────────────────── Dialog */
/* Единственное место в системе, где применяется блюр. */

.dialog {
  width: min(720px, calc(100vw - var(--space-6) * 2));
  max-height: 86vh;
  overflow: auto;
  padding: var(--space-7);
  background: var(--surface-raised);
  border: var(--border-w-hair) solid var(--border-hairline);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-overlay);
  color: var(--text-body);
}
.dialog::backdrop { background: rgba(4, 16, 46, .62); backdrop-filter: var(--blur-glass); }
.dialog__head { display: flex; align-items: flex-start; gap: var(--space-4); margin-bottom: var(--space-5); }
.dialog__title { flex: 1; display: grid; gap: var(--space-2); }
.dialog__title h3 {
  font: var(--type-title);
  font-variation-settings: "wdth" var(--display-stretch);
  letter-spacing: var(--tracking-tight);
  /* «газированный» в дисплейной гарнитуре шире узкого экрана и не переносится */
  overflow-wrap: anywhere;
}
.dialog__sub { font: var(--type-body-sm); color: var(--text-body); }
.dialog__close { margin: -6px -6px 0 0; }
.dialog__body { display: grid; gap: var(--space-6); }

/* Разделы во весь экран: «Корзина», «Опт», «Контакты». Содержимого там
   на страницу, и в узком окне оно превращалось в прокрутку внутри прокрутки.
   Поля и ширина — те же, что у каталога, чтобы переход не сдвигал вёрстку. */
/* Пока открыт раздел, шапка прибита к верху экрана. Она в обычном потоке,
   и на телефоне при появлении адресной строки уезжала вверх — раздел тогда
   начинался под пустотой, а шапка пропадала. Фиксация делает отступ раздела
   и положение шапки согласованными на любом экране. */
.is-page-open .nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 41;
  background: var(--bg-page);
}

.dialog--page {
  position: fixed;
  /* Начинается под шапкой: она остаётся видимой и рабочей.
     Высоту шапки берём измеренную (--nav-real ставит lib.js), а не из токена:
     на узком экране шапка переносится в три строки и занимает вдвое больше,
     чем 76px — верх раздела уезжал под неё и был не виден и не нажимаем. */
  inset: var(--nav-real, var(--nav-h, 64px)) 0 0 0;
  width: 100vw;
  max-width: none;
  /* height: auto, а не единицы экрана: у <dialog> в браузерном стиле стоит
     height: fit-content, и раздел обрезался бы по содержимому, показывая
     каталог под собой. При auto высоту задают сами inset — от шапки до низа
     экрана, без арифметики с dvh, которая в разных браузерах меряет разное. */
  height: auto;
  max-height: none;
  /* Плитки каталога позиционированы (position: relative), и без явного слоя
     их кнопки всплывали поверх раздела. */
  z-index: 40;
  margin: 0;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  overflow: auto;
  /* То же поле, что у каталога: переход между разделами не должен читаться
     как «поверх сайта открылось окно». */
  background: var(--bg-page);
  background-image: var(--field-lift);
  padding: var(--space-7) clamp(16px, 5vw, 96px) var(--space-9);
}
.dialog--page::backdrop { background: var(--bg-page); backdrop-filter: none; }
/* В шапке раздела кнопка «назад» — сосед заголовка, а не угловой крестик:
   её место посередине блока с надстрочной подписью и названием. По верху
   она вставала на 11 px выше центра и читалась съехавшей.
   У обычного окна выравнивание остаётся по верху: там крестик и должен
   стоять в углу. */
.dialog__head--page { margin-bottom: var(--space-7); align-items: center; }
.dialog__head--page .dialog__title { gap: var(--space-1); }
.dialog__head--page h3 { font-size: var(--text-2xl); line-height: 1.02; }
.dialog__back { flex: 0 0 auto; gap: var(--space-2); }

/* Телефон и планшет: раздел занимает весь экран.
   Шапка здесь переносится в три строки, её высота гуляет вместе с адресной
   строкой браузера, и любой отступ под неё оказывается то мал, то велик.
   Поэтому на узком экране шапки под разделом нет вовсе: раздел разворачивается
   на весь экран, а вместо шапки — своя строка с кнопкой «В каталог».
   Она прилипает к верху, так что уйти назад можно с любого места страницы. */
@media (max-width: 900px) {
  .is-page-open .nav { display: none; }

  /* Два класса, а не один: ниже по файлу у обычных окон на телефоне своё
     поле (.dialog), и при равной силе выигрывало бы оно. */
  .dialog.dialog--page {
    --page-pad: var(--space-4);
    inset: 0;
    height: auto;
    /* Верхнего поля нет: его держит прилипшая строка. Нижнее считается
       с запасом под панель браузера на айфоне. */
    padding: 0 var(--page-pad) calc(var(--space-8) + env(safe-area-inset-bottom, 0px));
  }

  .dialog__head--page {
    position: sticky;
    top: 0;
    z-index: 3;
    /* Строка идёт от края до края, поэтому поля раздела гасятся отрицательным
       полем, а возвращаются уже своим внутренним. Значение берётся из той же
       переменной, что и поле раздела, — они не могут разъехаться. */
    margin: 0 calc(var(--page-pad) * -1) var(--space-6);
    padding: var(--space-3) var(--page-pad);
    /* Кнопка стоит первой, за ней название: в DOM порядок обратный, потому
       что на большом экране кнопка справа от заголовка. */
    flex-direction: row-reverse;
    align-items: center;
    gap: var(--space-3);
    background: var(--bg-page);
    border-bottom: var(--border-w-hair) solid var(--border-hairline);
  }
  .dialog__head--page .dialog__title { gap: 0; min-width: 0; }
  /* В строке остаётся только название: надстрочная подпись и пояснение
     повторяют то, что и так видно ниже, а высоту строки задирают. */
  .dialog__head--page .ads-eyebrow,
  .dialog__head--page .dialog__sub { display: none; }
  /* Не одна строка с многоточием: названия напитков длинные, и «Квас Никола
     Хлебный» обрезался бы на середине. Две строки вмещают почти любое,
     а строка остаётся невысокой. */
  /* Через .dialog__title: ниже по файлу у заголовка окна свой размер
     для узкого экрана, и при равной силе выигрывал бы он. */
  .dialog__head--page .dialog__title h3 {
    font-size: var(--text-base);
    line-height: 1.25;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
}

/* Страница товара: слева всё о товаре и покупка, справа фото. */
.product {
  display: grid;
  grid-template-columns: minmax(0, 520px) minmax(0, 1fr);
  gap: var(--space-8);
  align-items: start;
}
.product__main { display: grid; gap: var(--space-6); align-content: start; }
.product__marks { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }
.product__block { display: grid; gap: var(--space-3); }
.product__photo {
  position: sticky;
  top: var(--space-6);
  aspect-ratio: 3 / 4;
  max-height: calc(100dvh - var(--nav-h) - var(--space-9));
  /* Сплошная рамка, как у плитки каталога: пунктир остаётся признаком
     «фото ещё нет» только там, где площадок много и они мелкие. */
  border-style: solid;
}

/* Две цены карточками: розница спокойная, опт обведён акцентом —
   это цена, ради которой сюда пришли. */
.product__prices { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: var(--space-3); }
.price-card {
  display: grid;
  gap: 4px;
  align-content: start;
  padding: var(--space-4) var(--space-5);
  background: var(--surface-inset);
  border: var(--border-w-hair) solid var(--border-hairline);
  border-radius: var(--radius-sm);
}
.price-card--opt { border-color: var(--border-focus); background: var(--white-a04); }
.price-card__head { display: flex; align-items: center; justify-content: space-between; gap: var(--space-3); }
.price-card__label {
  font: var(--type-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
}
.price-card--opt .price-card__label { color: var(--text-accent); }
.price-card__diff {
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
  color: var(--ink-900);
  background: var(--sun-500);
  border-radius: var(--radius-pill);
  padding: 2px 8px;
}
.price-card__value {
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
  font-size: var(--text-xl);
  line-height: 1.1;
  color: var(--text-title);
}
.price-card--opt .price-card__value { color: var(--text-accent); }
.price-card__value small { font-family: var(--font-sans); font-size: var(--text-xs); color: var(--text-muted); margin-left: 4px; font-weight: 400; }
.price-card__note { font: var(--type-caption); color: var(--text-muted); }

.product__buy {
  display: grid;
  gap: var(--space-3);
  padding: var(--space-5);
  background: var(--surface-card);
  border: var(--border-w-hair) solid var(--border-hairline);
  border-radius: var(--radius-sm);
}
.product__buy .cart-slot > .btn {
  width: 100%;
  height: 50px;
  border-radius: var(--radius-pill);
  text-transform: uppercase;
  letter-spacing: .14em;
  font-weight: var(--weight-semibold);
}
.product__hint { font: var(--type-caption); color: var(--text-muted); }

/* Блок покупки. Раньше единицы, счётчик и сумма стояли в одной строке:
   три разных по высоте контрола выравнивались по-разному, а на узком экране
   строка переносилась и сумма оставалась висеть сама по себе.
   Теперь это две подписанные строки «что покупаем» и отбитая чертой строка
   «сколько это стоит» — у каждого элемента есть подпись и место. */
.buy { display: grid; gap: var(--space-5); }
.buy__controls { display: grid; gap: var(--space-3); }
.buy__line {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  /* Не высота контрола, а высота строки: подпись и контрол разной высоты,
     и без общей меры строки шли неровно. */
  min-height: 44px;
}
.buy__label {
  font: var(--type-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
}

/* Контролы здесь крупнее, чем в плитке и в строке корзины: карточка товара —
   то место, где количество действительно набирают. Кнопка 26 px на телефоне
   меньше подушечки пальца (в рекомендациях — 44 pt на iOS, 48 dp на Android),
   и промахи по «−» вместо «+» были делом времени. */
.buy .unit-toggle button { height: 34px; padding: 0 16px; font-size: var(--text-xs); }
.buy .stepper { padding: 4px; }
.buy .stepper__btn { width: 40px; height: 40px; }
.buy .stepper__value { min-width: 72px; font-size: var(--text-base); }
/* Убирает задержку 300 мс перед нажатием на телефоне. */
.buy .unit-toggle button,
.buy .stepper__btn { touch-action: manipulation; }

.buy__price {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: var(--space-2) var(--space-4);
  padding-top: var(--space-4);
  border-top: var(--border-w-hair) solid var(--border-hairline);
}
.buy__total {
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
  font-size: var(--text-xl);
  line-height: 1;
  color: var(--text-accent);
}
/* Под суммой — из чего она сложилась, и метка цены. Метка не жёлтая:
   акцент в этом блоке уже занят суммой и кнопкой. */
.buy__note {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  font: var(--type-caption);
  color: var(--text-muted);
}
/* Две подписи под кнопкой: на узком экране они слипались в одну строку. */
.buy__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

/* Вкладки характеристик — пилюли, активная залита. */
.product__tabs { display: flex; gap: var(--space-2); flex-wrap: wrap; }
.product__tab {
  height: 32px;
  padding: 0 16px;
  border-radius: var(--radius-pill);
  border: var(--border-w-hair) solid var(--border-hairline);
  background: var(--white-a04);
  color: var(--text-body);
  font: var(--type-body-sm);
  cursor: pointer;
  transition: var(--transition-control);
}
.product__tab:hover { color: var(--text-title); }
.product__tab[aria-selected="true"] { background: var(--text-title); border-color: var(--text-title); color: var(--bg-page); }

.spec { display: grid; gap: 0; }
/* Отточие между подписью и значением: строка читается слева направо
   без метаний взглядом по пустому месту. */
.spec__row {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: 9px 0;
  border-bottom: var(--border-w-hair) solid var(--border-hairline);
}
.spec__row dt { color: var(--text-muted); font: var(--type-body-sm); white-space: nowrap; }
/* Псевдоэлемент генерируется последним, поэтому значение отправляем за него
   через order — иначе точки тянулись бы после значения, а не до него. */
.spec__row::after { content: ""; flex: 1; border-bottom: 1px dotted var(--border-hairline); }
.spec__row dd { order: 1; margin: 0; color: var(--text-title); font: var(--type-body-sm); text-align: right; }
.spec__row dd.ads-num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

@media (max-width: 900px) {
  .product { grid-template-columns: minmax(0, 1fr); gap: var(--space-5); }
  /* Фото уходит наверх, но не на весь экран: в пропорции 3/4 оно занимало
     на телефоне 450 px, и до цены с кнопкой нужно было прокручивать целый
     экран. Ландшафтная площадка показывает товар не хуже, а решение
     («сколько стоит» и «в корзину») остаётся в первом экране карточки. */
  /* Через .product: класс .tile__well ниже по файлу задаёт свою пропорцию
     и position: relative, а вместе с ним оживал бы top от липкого фото
     на большом экране — карточка съезжала бы на 24 px под шапку. */
  .product .product__photo {
    position: static;
    top: auto;
    order: -1;
    aspect-ratio: 4 / 3;
    max-height: 42vh;
    margin: 0 auto;
    width: 100%;
  }
}

/* Корзина: слева состав заказа, справа форма — как в макете. */
.cart-page {
  display: grid;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  gap: var(--space-6);
  align-items: start;
}
.cart-page__panel {
  background: var(--surface-card);
  border: var(--border-w-hair) solid var(--border-hairline);
  border-radius: var(--radius-sm);
  padding: var(--space-6);
  display: grid;
  gap: var(--space-5);
}
.cart-page__panel h4 {
  font-family: var(--font-display);
  font-variation-settings: "wdth" var(--display-stretch);
  font-weight: var(--weight-semibold);
  font-size: var(--text-lg);
  letter-spacing: var(--tracking-tight);
  color: var(--text-title);
}
.cart-page__lead { font: var(--type-body-sm); color: var(--text-body); margin-top: 4px; }
.cart-page__actions { display: flex; align-items: center; gap: var(--space-4); }
/* Отправка — главное действие страницы, поэтому во всю ширину колонки. */
.btn--block { width: 100%; justify-content: center; height: var(--control-h-lg, 50px); }

@media (max-width: 900px) {
  .cart-page { grid-template-columns: minmax(0, 1fr); }
  .dialog--page { padding-bottom: var(--space-7); }
}

.detail { display: grid; grid-template-columns: 200px minmax(0, 1fr); gap: var(--space-6); }
.detail__well { aspect-ratio: 3 / 4; }
.detail__lead { display: grid; gap: var(--space-3); align-content: start; }
.detail__price {
  font-family: var(--font-display);
  font-variation-settings: "wdth" var(--display-stretch);
  font-weight: var(--weight-bold);
  font-size: var(--text-2xl);
  line-height: 1;
  letter-spacing: var(--tracking-tight);
  color: var(--text-title);
}
.detail__desc { font: var(--type-body-sm); color: var(--text-body); }

/* SizeScale — выбор размера упаковки.
   Компонент системы из ui_kits/website/ProductScreen.jsx: не силуэт бутылки,
   а пропорциональный столбик (рисованную тару система запрещает) плюс
   моноширинная подпись с объёмом.
   Наше дополнение: банка шире и ниже бутылки — иначе выбор между литровой
   и баночной по одинаковым столбикам не прочитать. */

.sizes { display: flex; align-items: flex-end; gap: var(--space-6); flex-wrap: wrap; }

/* Силуэт бутылки шириной 12–17px — сам по себе он мишень, в которую
   не попасть ни мышью, ни пальцем. Кнопке задан минимум 44×44 и поля,
   поэтому нажимается вся колонка «бутылка + объём», а не тонкая полоска. */
.size {
  display: grid;
  gap: var(--space-2);
  justify-items: center;
  align-content: end;
  min-width: 44px;
  min-height: 44px;
  padding: 6px 8px;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  cursor: pointer;
  transition: var(--transition-control);
}
.size:hover { background: var(--white-a04); }
.size[aria-checked="true"] { background: var(--white-a08); }
.size:focus-visible { outline: none; box-shadow: var(--ring-focus); }
/* Силуэт тары: горлышко и корпус двумя плашками, как в макете.
   Цвет один — currentColor, поэтому выбранное состояние задаётся
   одной строкой, а не вторым набором фигур.
   Размеры приходят из разметки: они зависят от объёма. */
.bottle {
  display: grid;
  align-content: end;
  justify-items: center;
  width: var(--bw);
  height: 44px;
  color: var(--white-a20);
  transition: var(--transition-control);
}
.bottle__neck {
  width: var(--bn);
  height: 5px;
  border-radius: 2px 2px 0 0;
  background: currentColor;
}
.bottle__body {
  width: var(--bw);
  height: var(--bb);
  /* Плечо скруглено по половине ширины, дно — почти прямое: так плашка
     читается бутылкой, а не таблеткой. */
  border-radius: calc(var(--bw) / 2.2) calc(var(--bw) / 2.2) 3px 3px;
  background: currentColor;
}
.size:hover .bottle { color: var(--white-a40); }
/* Разметка ставит aria-checked (role="radio"), а не aria-pressed —
   по старому селектору выбранный объём никогда не подсвечивался. */
.size[aria-checked="true"] .bottle { color: var(--text-title); }

.size__value { font-size: 11px; color: var(--text-muted); transition: var(--transition-control); }
.size[aria-checked="true"] .size__value { font-weight: var(--weight-bold); color: var(--text-title); }
.size__tare { font: var(--type-caption); font-size: var(--text-2xs); color: var(--text-muted); }

.size__price {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-2xs);
  color: var(--text-muted);
}
.size[aria-checked="true"] .size__price { color: var(--text-body); }

/* FactsTable — полоса инсет-ячеек с моноширинными цифрами */
/* Сетка, а не флекс: у флекса ячейка не может стать уже своего содержимого,
   и 13-значный штрихкод растягивал ряд за край окна. */
.facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); gap: var(--space-1); }
.facts__cell {
  min-width: 0;
  background: var(--surface-inset);
  border: var(--border-w-hair) solid var(--border-hairline);
  border-radius: var(--radius-sm);
  display: grid;
  text-align: center;
}
.facts__label { padding: 8px 6px 4px; font: var(--type-caption); color: var(--text-muted); line-height: 1.25; }
.facts__value {
  padding: 0 6px 10px;
  font: var(--type-numeric);
  color: var(--text-title);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

/* Таблицы цен и остатков */
.table { width: 100%; border-collapse: collapse; }
.table caption {
  text-align: left;
  padding-bottom: var(--space-3);
  font: var(--type-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
}
.table th, .table td { padding: 10px 12px; border-bottom: var(--border-w-hair) solid var(--border-hairline); text-align: left; }
.table th { font: var(--type-caption); color: var(--text-muted); font-weight: var(--weight-medium); }
.table td { font: var(--type-body-sm); color: var(--text-title); }
.table td.num, .table th.num { text-align: right; font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.table tr:last-child td { border-bottom: 0; }

.props { display: grid; grid-template-columns: auto 1fr; gap: var(--space-2) var(--space-5); font: var(--type-body-sm); }
.props dt { color: var(--text-muted); }
.props dd { margin: 0; color: var(--text-title); }
.props dd.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* ─────────────────────────────────────────────────────── Footer */

.footer {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  flex-wrap: wrap;
  padding: var(--space-7) 0 var(--space-9);
  font: var(--type-caption);
  color: var(--text-muted);
  border-top: var(--border-w-hair) solid var(--border-hairline);
  margin-top: var(--space-7);
}
.footer__spacer { flex: 1; }

/* ────────────────────────────────────────────────────── адаптив */

@media (max-width: 1180px) {
  .shell { padding: 0 var(--gutter-page-tight); }
  .rail { display: none; }
}

@media (max-width: 900px) {
  .nav {
    height: auto;
    flex-wrap: wrap;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
  }
  .nav__links { order: 3; flex-basis: 100%; overflow-x: auto; scrollbar-width: none; }
  .nav__links::-webkit-scrollbar { display: none; }
  .detail { grid-template-columns: minmax(0, 1fr); }
  .detail__well { max-width: 200px; }
}

@media (max-width: 560px) {
  .shell { padding: 0 var(--space-4); }
  .filters__controls .field { flex: 1 1 100%; }
  .field .select { min-width: 0; }
  .filters__foot #reset { margin-left: 0; width: 100%; }
  .dialog { padding: var(--space-5); }
  .dialog__title h3 { font-size: var(--text-lg); }
  /* Отбивки сетки и поля плитки заданы ниже, в блоке выкладки:
     там они зависят ещё и от выбранного вида. */
  .sizes { gap: var(--space-5); }
}

/* ══════════════════════════════════════ корзина, заявка, разделы ══════ */

/* Крупное поле поиска: оно теперь точка входа в каталог,
   потому что заголовок со сводкой убран. */
.input--hero { height: var(--control-h-lg); padding: 0 18px; }
.input--hero input { font-size: var(--text-md); }
.input--error { border-color: var(--state-danger); }

.field__error {
  font: var(--type-caption);
  color: var(--state-danger);
  min-height: 14px;
}
.field--inline { gap: var(--space-1); }

/* ───────────────────────────────────────────── корзина в шапке */

/* Корзина прижата к правому краю шапки на любой ширине.
   На большом экране её туда отодвигали растянутые разделы (.nav__links {flex:1}),
   но на узком разделы переносятся на свою строку — толкать становится нечему,
   и корзина прилипала к марке слева. Собственное поле не зависит от того,
   что стоит рядом. */
.nav__actions { display: flex; align-items: center; gap: var(--space-4); margin-left: auto; }

.cart-btn {
  position: relative;
  display: inline-grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: var(--radius-circle);
  border: var(--border-w-strong) solid var(--border-hairline);
  background: transparent;
  color: var(--text-title);
  cursor: pointer;
  transition: var(--transition-control);
}
.cart-btn:hover { background: var(--white-a08); border-color: var(--border-strong); }
.cart-btn--filled { border-color: var(--accent); }
.cart-btn__count {
  position: absolute;
  top: -4px;
  right: -4px;
  min-width: 20px;
  height: 20px;
  padding: 0 5px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-pill);
  background: var(--accent);
  color: var(--text-on-accent);
  font-family: var(--font-mono);
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
  font-variant-numeric: tabular-nums;
}
.cart-btn__count:empty { display: none; }

/* ─────────────────────────────────────────────────────── шаговик */

.stepper {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 3px;
  border-radius: var(--radius-pill);
  background: var(--surface-inset);
  border: var(--border-w-hair) solid var(--border-hairline);
}
.stepper__btn {
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  border: 0;
  border-radius: var(--radius-circle);
  background: var(--white-a08);
  color: var(--text-title);
  cursor: pointer;
  transition: var(--transition-control);
}
.stepper__btn:hover { background: var(--white-a20); }
.stepper__value {
  min-width: 62px;
  text-align: center;
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
  color: var(--text-title);
}
.stepper__value small { font-family: var(--font-sans); font-weight: 400; color: var(--text-muted); margin-left: 3px; }

/* Поле ввода количества выглядит как обычная цифра степпера: рамка и фон
   ему не нужны — он уже внутри рамки степпера. Подсветка только по фокусу,
   чтобы было видно, куда попал курсор. */
.stepper__value { display: inline-flex; align-items: baseline; justify-content: center; gap: 0; }
.stepper__input {
  all: unset;
  box-sizing: content-box;
  width: 3ch;
  min-width: 3ch;
  text-align: center;
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
  font-size: inherit;
  color: var(--text-title);
  font-variant-numeric: tabular-nums;
  border-radius: var(--radius-xs, 4px);
  cursor: text;
}
.stepper__input:focus-visible { box-shadow: var(--ring-focus); }
.stepper__input::-webkit-outer-spin-button,
.stepper__input::-webkit-inner-spin-button { appearance: none; margin: 0; }
.stepper--sm { padding: 2px; }
.stepper--sm .stepper__btn { width: 26px; height: 26px; }
.stepper--sm .stepper__value { min-width: 54px; font-size: var(--text-xs); }

/* Плитка: имя товара растягивает свою кликабельную зону на всю карточку,
   а кнопки корзины лежат над ней — вложенных кнопок в кнопке нет. */
.tile { position: relative; }
.tile__name {
  border: 0;
  background: none;
  padding: 0;
  cursor: pointer;
  font-family: var(--font-display);
  font-variation-settings: "wdth" var(--display-stretch);
  font-weight: var(--weight-semibold);
  font-size: var(--text-base);
  line-height: var(--leading-snug);
  letter-spacing: var(--tracking-tight);
  color: var(--text-title);
  text-align: center;
  min-height: calc(var(--text-base) * var(--leading-snug) * 2);
}
/* Прозрачный слой на всю плитку: нажимается любое место, а не только строка
   названия. Он же перехватывал клики по силуэтам объёма и по кнопке корзины —
   поэтому интерактивные части плитки подняты над ним отдельным слоем ниже. */
.tile__name::after { content: ""; position: absolute; inset: 0; border-radius: var(--radius-sm); }
.tile__name:focus-visible { outline: none; }
.tile__name:focus-visible::after { box-shadow: var(--ring-focus); }
.tile .vols,
.tile .cart-slot,
.tile__well { position: relative; z-index: 1; }
/* Отступ сверху здесь был общий на весь сайт — и в строке корзины опускал
   счётчик на 8 px относительно переключателя и суммы: три контрола одной
   высоты стояли по разным уровням. Отступ нужен только в плитке каталога,
   где слот идёт под ценой, — там он и задан (.tile .cart-slot). */
.cart-slot { position: relative; z-index: 1; }

/* ─────────────────────────────────────────── подвал модального окна */

.dialog--wide { width: min(880px, calc(100vw - var(--space-6) * 2)); }
.dialog__foot {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: var(--border-w-hair) solid var(--border-hairline);
}
.dialog__foot-spacer { flex: 1; }
.dialog__foot-note { font: var(--type-caption); color: var(--text-muted); }

/* ────────────────────────────────────────────── таблица корзины */

.table--cart td { vertical-align: middle; }
.cart__name { display: block; font-weight: var(--weight-semibold); color: var(--text-title); }
.cart__total {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: var(--space-4);
  padding: var(--space-4) 0;
}

/* ────────────────────────────────────────────────── форма заявки */

.form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--space-4);
}
#orderForm { display: grid; gap: var(--space-4); margin-top: var(--space-5); }

/* Чекбокс согласия: 3px радиус и жёлтая заливка — как Checkbox в системе. */
.check { display: flex; gap: var(--space-3); align-items: flex-start; cursor: pointer; }
.check input { position: absolute; opacity: 0; width: 0; height: 0; }
.check__box {
  width: 20px;
  height: 20px;
  flex: 0 0 auto;
  margin-top: 1px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-xs);
  background: var(--surface-inset);
  border: var(--border-w-strong) solid var(--border-strong);
  color: var(--text-on-accent);
  font-size: 13px;
  font-weight: 700;
  line-height: 1;
  transition: var(--transition-control);
}
.check input:checked + .check__box { background: var(--accent); border-color: var(--accent); }
.check input:checked + .check__box::before { content: "✓"; }
.check input:focus-visible + .check__box { box-shadow: var(--ring-focus); }
.check__text { font: var(--type-body-sm); color: var(--text-body); }

.link {
  border: 0;
  padding: 0;
  background: none;
  color: var(--text-title);
  font: inherit;
  cursor: pointer;
  border-bottom: 1px solid var(--white-a40);
  transition: var(--transition-control);
}
.link:hover { color: var(--text-accent); border-bottom-color: var(--accent); }

/* ──────────────────────────────────────────── контакты и политика */

.contacts { list-style: none; margin: 0 0 var(--space-6); padding: 0; display: grid; gap: var(--space-3); }
.contacts__row { display: flex; align-items: center; gap: var(--space-3); }
.contacts__icon { color: var(--text-muted); display: flex; }
.contacts__label { font: var(--type-caption); color: var(--text-muted); min-width: 110px; }
.contacts__value { color: var(--text-title); font-size: var(--text-md); border-bottom: 0; }
a.contacts__value:hover { color: var(--text-accent); }

.policy__h {
  margin: var(--space-5) 0 var(--space-2);
  font: var(--type-label);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-title);
}

/* Незаполненные реквизиты видно сразу — и в вёрстке, и глазами. */
.todo { color: var(--state-warning); font-family: var(--font-mono); }
/* Иконка и абзац: выравнивание по верху — иначе значок уезжал бы в середину
   трёх строк текста. Пиксельный сдвиг сажает его на первую строку оптически:
   иконка 16 px, строка 20 px. */
.notice {
  display: flex;
  gap: var(--space-3);
  align-items: flex-start;
  padding: var(--space-4);
  margin-bottom: var(--space-5);
  border-radius: var(--radius-sm);
  background: rgba(242, 168, 59, .12);
  border: var(--border-w-hair) solid rgba(242, 168, 59, .4);
  color: var(--state-warning);
  font: var(--type-body-sm);
}
.notice .ads-icon { flex: 0 0 auto; margin-top: 2px; }
.notice code { font-family: var(--font-mono); }

@media (max-width: 900px) {
  .nav__actions { gap: var(--space-3); }
  .dialog--wide { width: calc(100vw - var(--space-4) * 2); }
  .table--cart thead { display: none; }
  .table--cart tr { display: grid; grid-template-columns: 1fr auto; gap: var(--space-2); padding: var(--space-3) 0; }
  .table--cart td { border: 0; padding: 0; }
  .table--cart td:nth-child(3) { display: none; }
  .table--cart tr { border-bottom: var(--border-w-hair) solid var(--border-hairline); }
}

/* ═══════════════════════════════════════════ статистика заявок ═══════ */

.kpi {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--space-6);
}
.kpi__cell { display: grid; gap: var(--space-2); }
.kpi__value {
  font-family: var(--font-display);
  font-variation-settings: "wdth" var(--display-stretch);
  font-weight: var(--weight-bold);
  font-size: var(--text-xl);
  line-height: 1.1;
  letter-spacing: var(--tracking-tight);
  color: var(--text-title);
}

/* График по дням — обычные блоки, без библиотек. */
.chart {
  display: flex;
  align-items: flex-end;
  gap: 4px;
  height: 180px;
  margin-top: var(--space-4);
  overflow-x: auto;
}
.chart__col {
  flex: 1 0 26px;
  display: grid;
  grid-template-rows: 1fr auto;
  justify-items: center;
  gap: var(--space-2);
  height: 100%;
}
.chart__bar {
  width: 100%;
  max-width: 34px;
  align-self: end;
  border-radius: 3px 3px 2px 2px;
  background: var(--white-a20);
  transition: var(--transition-control);
}
.chart__col:hover .chart__bar { background: var(--accent); }
.chart__label { font-size: var(--text-2xs); color: var(--text-muted); white-space: nowrap; }

.admin-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: var(--space-5);
}
.admin-cols .filters { margin: 0 0 var(--space-7); }

a.tile__note { display: block; border-bottom: 0; color: var(--text-muted); }
a.tile__note:hover { color: var(--text-accent); }

/* ══════════════════ компоненты обновлённой системы ══════════════════ */

/* StockBadge — только факт, без числа. Точка слева читается как индикатор. */
.stock-badge {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  width: fit-content;
  height: 26px;
  padding: 0 11px;
  border-radius: var(--radius-pill);
  font: var(--type-caption);
  font-weight: var(--weight-semibold);
  white-space: nowrap;
}
.stock-badge__dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: 0 0 auto; }
.stock-badge--in { background: rgba(63, 208, 122, .14); border: 1px solid rgba(63, 208, 122, .42); color: var(--state-success); }
.stock-badge--order { background: rgba(242, 168, 59, .14); border: 1px solid rgba(242, 168, 59, .42); color: var(--state-warning); }
.stock-badge--out { background: var(--white-a04); border: 1px solid var(--border-hairline); color: var(--text-muted); }

/* PriceBlock — розница и опт рядом, разница явная. */
.price-block { display: flex; gap: var(--space-2); width: 100%; }
.price-block--compact { flex-direction: column; }

.price-cell {
  flex: 1;
  min-width: 0;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  background: var(--surface-inset);
  border: var(--border-w-hair) solid var(--border-hairline);
}
.price-cell--opt { background: rgba(255, 197, 61, .10); border-color: var(--accent); }
.price-cell__head { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.price-cell__label {
  font: var(--type-caption);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  color: var(--text-muted);
}
.price-cell--opt .price-cell__label { color: var(--text-accent); }
.price-cell__diff {
  font: var(--type-caption);
  font-weight: var(--weight-bold);
  color: var(--text-on-accent);
  background: var(--accent);
  border-radius: var(--radius-pill);
  padding: 2px 7px;
}
.price-cell__value {
  display: block;
  margin-top: 5px;
  font-weight: var(--weight-bold);
  font-size: var(--text-lg);
  color: var(--text-title);
}
.price-cell__value small { font-family: var(--font-sans); font-size: var(--text-2xs); font-weight: 400; color: var(--text-muted); margin-left: 3px; }
.price-cell__note { display: block; margin-top: 4px; font: var(--type-caption); color: var(--text-muted); }

/* SpecList — подпись, волосок, значение. */
.spec { margin: var(--space-6) 0 0; display: grid; }
.spec__row { display: flex; align-items: baseline; gap: var(--space-4); padding: 10px 0; }
.spec__row + .spec__row { border-top: var(--border-w-hair) solid var(--border-hairline); }
.spec dt { font: var(--type-body-sm); color: var(--text-muted); flex: 0 0 auto; min-width: 150px; }
.spec__rule { flex: 1; height: 1px; background: var(--white-a04); }
.spec dd { margin: 0; font: var(--type-body-sm); color: var(--text-title); text-align: right; }

/* UnitToggle — сегментированный контрол на две опции.

   Половины лежат заподлицо: серая дорожка идёт от края до края, а выбранная
   половина закрывает ровно свою сторону. Раньше у дорожки было поле в 3 px
   и зазор между половинами — обе читались двумя пилюлями, сдвинутыми
   к середине, а не одним переключателем. Скругление даёт overflow: hidden
   у дорожки, поэтому у самих кнопок радиуса нет и стыка посередине не видно. */
.unit-toggle {
  display: inline-flex;
  gap: 0;
  padding: 0;
  overflow: hidden;
  width: fit-content;
  background: var(--surface-inset);
  border: var(--border-w-hair) solid var(--border-hairline);
  border-radius: var(--radius-pill);
}
/* Кольцо фокуса — на дорожке: у кнопки внутри его срезало бы overflow. */
.unit-toggle:has(button:focus-visible) { box-shadow: var(--ring-focus); }
.unit-toggle button {
  border: 0;
  border-radius: 0;
  cursor: pointer;
  height: 32px;
  padding: 0 14px;
  background: transparent;
  /* Невыбранная половина набрана основным цветом текста, а не приглушённым:
     на сером она и так спокойнее выбранной, а приглушать её ещё раз значило
     сделать вид, что нажимается только одна сторона. */
  color: var(--text-body);
  font-family: var(--font-sans);
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
  transition: var(--transition-control);
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.unit-toggle button:hover:not([aria-checked="true"]) { background: var(--white-a08); color: var(--text-title); }
/* Выбранная половина — фирменный жёлтый: та же роль, что у включённого
   переключателя «В наличии». Одно значение — один цвет по всей витрине. */
.unit-toggle button[aria-checked="true"] { background: var(--accent); color: var(--text-on-accent); }
.unit-toggle button:focus-visible { outline: none; }

/* Строки корзины — CartPanel. */
/* Строка отвечает на ширину списка, а не экрана: на большом мониторе состав
   заказа всё равно стоит в колонке около 630 px, и «широкая» раскладка там
   не помещалась — строка молча ломалась, а крестик уезжал под неё. */
.cart-rows { display: grid; container: cartrows / inline-size; }

/* Базовая раскладка — узкая: название с крестиком сверху, под ними одна
   строка управления (единицы слева, счётчик посередине, сумма справа).
   Раньше строка разъезжалась на четыре яруса, и одна позиция занимала
   полэкрана телефона. */
.cart-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: center;
  padding: var(--space-4) 0;
}
/* Основа 0, а не auto: с auto длинное название занимало всю строку
   собственной шириной и сталкивало крестик на следующую. */
.cart-row__main { flex: 1 1 0; min-width: 0; }
.cart-row .icon-btn { order: 1; }
.cart-row__ctl {
  order: 2;
  flex: 1 1 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

/* Список шире 660 px — всё встаёт в одну строку, как в макете. Порог взят
   по сумме собственных ширин: контролы 348, крестик 34, название от 180
   и три отбивки по 16. */
@container cartrows (min-width: 660px) {
  .cart-row { gap: var(--space-4); }
  .cart-row__main { flex: 1 1 180px; }
  .cart-row__ctl { order: 1; flex: 0 0 auto; gap: var(--space-4); }
  .cart-row .icon-btn { order: 2; }
}
.cart-row + .cart-row { border-top: var(--border-w-hair) solid var(--border-hairline); }
/* Все контролы строки — одной высоты. Раньше переключатель единиц был 34 px,
   счётчик 32, а крестик 30: три соседних элемента в одном ряду стояли
   ступенькой, и строка читалась неровной. */
.cart-row .unit-toggle,
.cart-row .stepper,
.cart-row .icon-btn { height: 34px; }
.cart-row .icon-btn { width: 34px; }
.cart-row .stepper--sm { padding: 3px; }
/* Кнопка «В корзину» в строке появляется, когда количество обнулили
   до нуля, — она тоже должна быть ростом со строку. */
.cart-row .btn--sm { height: 34px; }
.cart-row__main { display: grid; gap: 3px; min-width: 0; }
.cart-row__name {
  font-family: var(--font-display);
  font-variation-settings: "wdth" var(--display-stretch);
  font-weight: var(--weight-semibold);
  font-size: var(--text-base);
  letter-spacing: var(--tracking-tight);
  color: var(--text-title);
  line-height: 1.15;
}
.cart-row__note { font: var(--type-caption); color: var(--text-muted); }
.cart-row__opt { color: var(--text-accent); font-weight: var(--weight-semibold); }
.cart-row__derived { font-size: var(--text-2xs); color: var(--text-body); }
.cart-row__sum {
  min-width: 96px;
  text-align: right;
  font-weight: var(--weight-bold);
  font-size: var(--text-base);
  color: var(--text-title);
}
.cart-row__sum--opt { color: var(--text-accent); }

.cart-total {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-5);
  padding: var(--space-4);
  background: var(--surface-inset);
  border: var(--border-w-hair) solid var(--border-hairline);
  border-radius: var(--radius-sm);
}
.cart-total__row { display: flex; justify-content: space-between; gap: var(--space-4); font: var(--type-body-sm); color: var(--text-muted); }
.cart-total__row--save { color: var(--text-accent); }
.cart-total__row--sum {
  padding-top: var(--space-2);
  border-top: var(--border-w-hair) solid var(--border-hairline);
  align-items: baseline;
}
.cart-total__row--sum span:first-child {
  font-family: var(--font-display);
  font-variation-settings: "wdth" var(--display-stretch);
  font-weight: var(--weight-bold);
  font-size: var(--text-md);
  color: var(--text-title);
  letter-spacing: var(--tracking-tight);
}
.cart-total__row--sum span:last-child { font-weight: var(--weight-bold); font-size: var(--text-lg); color: var(--text-title); }

.field__hint { font: var(--type-caption); color: var(--text-muted); }
.detail__pack { font-size: var(--text-sm); color: var(--text-body); }

/* Счётчик: знаки минус и плюс текстом, как в QuantityStepper системы. */
.stepper__btn { font-size: 18px; font-weight: 500; line-height: 1; }
.stepper--sm .stepper__btn { font-size: 16px; }

@media (max-width: 900px) {
  .price-block { flex-direction: column; }
}

@media (max-width: 560px) {
  /* Поля панели уже: иначе строка контролов не встаёт на 360 px. */
  .cart-page__panel { padding: var(--space-4); }
  .cart-row .unit-toggle button { padding: 0 10px; }
  .cart-row .stepper__value { min-width: 44px; }
  .cart-row__sum { min-width: 0; }
}

/* ═════════════════ шапка, первый экран и плитка из UI-кита ═════════ */

/* Знак «изгиб течения» — маска, залитая акцентом: цвет знака нельзя
   задать мимо палитры. Правило системы: слева от знака ничего не ставим. */
.wordmark { display: inline-flex; align-items: center; gap: 9px; }
.wordmark__mark {
  width: 27px;
  height: 27px;
  flex: 0 0 auto;
  background: var(--accent);
  -webkit-mask: url("/assets/logo/mark-dnipro.svg") no-repeat center / contain;
  mask: url("/assets/logo/mark-dnipro.svg") no-repeat center / contain;
}
.wordmark__name {
  font-family: var(--font-display);
  font-variation-settings: "wdth" var(--display-stretch);
  font-weight: var(--weight-black);
  font-size: 26px;
  line-height: 1;
  letter-spacing: -.03em;
  color: var(--text-title);
}
.wordmark__name span { color: var(--accent); }

/* Корзина в шапке: пустая — контурная, с товаром — жёлтая с суммой. */
.cart-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  height: var(--control-h-md);
  padding: 0 6px 0 16px;
  width: auto;
  border-radius: var(--radius-pill);
  background: transparent;
  border: var(--border-w-strong) solid var(--border-strong);
  color: var(--text-title);
  cursor: pointer;
  transition: var(--transition-control);
}
.cart-btn:hover { background: var(--white-a08); border-color: var(--ink-000); }
.cart-btn__label {
  font-family: var(--font-sans);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-label);
  text-transform: uppercase;
  white-space: nowrap;
}
.cart-btn--filled {
  background: var(--accent);
  border-color: transparent;
  color: var(--text-on-accent);
}
.cart-btn--filled:hover { background: var(--sun-400); border-color: transparent; }
.cart-btn__sum {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 30px;
  padding: 0 12px;
  border-radius: var(--radius-pill);
  background: var(--cobalt-1000);
  color: var(--ink-000);
  font-family: var(--font-mono);
  font-weight: var(--weight-bold);
  font-size: var(--text-xs);
  font-variant-numeric: tabular-nums;
}
.cart-btn__sum small { opacity: .6; font-weight: 400; }
.cart-btn:not(.cart-btn--filled) { padding-right: 16px; }

/* Первый экран каталога: заголовок слева, «опт дешевле» справа.
   По центру, а не по нижнему краю: цифра стояла на уровне последней строки
   абзаца и читалась приклеенной ко дну — рядом с двухстрочным заголовком
   это выглядело как сбитая вёрстка, а не как приём. */
.hero {
  display: flex;
  align-items: center;
  gap: var(--space-8);
  flex-wrap: wrap;
  padding: var(--space-5) 0;
  border-bottom: var(--border-w-hair) solid var(--border-hairline);
}
.hero__text { min-width: 0; flex: 1 1 420px; }
.hero h1 { font-size: var(--text-2xl); max-width: 24ch; }
.hero p { margin-top: var(--space-3); font: var(--type-body); color: var(--text-body); max-width: 56ch; }

/* «Опт дешевле — до N %»: это главный довод витрины, поэтому цифра набрана
   в размер заголовка, а не строчкой рядом с ним. */
.hero .stat { display: grid; gap: 2px; justify-items: end; text-align: right; }
.hero .stat__value {
  font-family: var(--font-display);
  font-variation-settings: "wdth" var(--display-stretch);
  font-weight: var(--weight-bold);
  font-size: var(--text-2xl);
  line-height: 1;
  letter-spacing: var(--tracking-tight);
}
.stat--accent .stat__value { color: var(--text-accent); }

/* Полоса фильтров: контролы-пилюли в одну строку. */
/* Пилюля фильтра — размеры из макета: 50px высоты, волосяная рамка. */
.select--pill select {
  border-radius: var(--radius-pill);
  height: 50px;
  padding: 0 36px 0 16px;
  border-width: var(--border-w-hair);
}
.select--pill::after { right: 16px; }
/* Поиск — та же пилюля, что и сортировка: 50px, волосяная рамка.
   Не растягивается на всю строку, иначе он один занимает полосу,
   а сортировка и переключатель цен уезжают к правому краю. */
.input--search {
  flex: 1 1 240px;
  max-width: 320px;
  min-width: 180px;
  height: 50px;
  padding: 0 8px 0 16px;
  gap: 10px;
  border-width: var(--border-w-hair);
  border-radius: var(--radius-pill);
}
.input--search input { min-width: 0; }
/* Лупа — жёлтая: поле поиска на насыщенной заливке иначе ничем не помечено,
   а это главный вход в каталог. */
.input--search > .ads-icon { color: var(--accent); }
/* Крестик очистки — цель нажатия 26px мала для пальца, добавляем поле вокруг. */
.input--search .input__clear { width: 30px; height: 30px; margin-right: 0; }
.unit-scope { display: inline-flex; align-items: center; gap: var(--space-2); white-space: nowrap; }
.filters__volumes { display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap; }

/* ═══════════════════════════════════════════ полоса фильтров: пилюли ═══ */

/* Все контролы полосы — пилюли одной высоты с одной волосяной рамкой.
   Высота держится переменной, а не числом в трёх местах: на телефоне она
   уменьшается один раз, и весь ряд уменьшается вместе с ней. */
:root { --filter-h: 50px; }

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  height: var(--filter-h);
  padding: 0 6px 0 var(--space-4);
  border-radius: var(--radius-pill);
  border: var(--border-w-hair) solid var(--border-hairline);
  background: var(--surface-inset);
  white-space: nowrap;
  transition: var(--transition-control);
}
.chip:hover { border-color: var(--border-strong); }
.chip__label { color: var(--text-muted); }

/* У свитча справа нет вложенного контрола, поэтому поле симметричное. */
.chip.switch { padding: 0 var(--space-4); cursor: pointer; }
.chip.switch:has(input:checked) { border-color: var(--border-focus); }
.chip.switch .switch__label { white-space: nowrap; }

/* Сегментированный переключатель внутри пилюли: своя рамка ему не нужна —
   она бы читалась вторым контуром внутри первого. */
/* Дорожка внутри пилюли своей рамки не носит: она читалась бы вторым
   контуром внутри первого. Заливка чуть светлее пилюли — чтобы серое поле
   переключателя было видно от края до края. */
.chip .unit-toggle { border-color: transparent; background: var(--white-a12); }
.chip .unit-toggle button { height: 34px; }

/* Кнопки выкладки — только глиф: подпись «крупно/плотно» рядом с иконкой
   удлиняет полосу вдвое, а сам знак читается без слов. */
.unit-toggle--icons button {
  width: 42px;
  padding: 0;
  display: inline-grid;
  place-items: center;
  color: var(--text-body);
}
.unit-toggle--icons button[aria-checked="true"] { color: var(--text-on-accent); }

/* Поиск занимает свободное место полосы и не уезжает вправо: он — вход
   в каталог, а не такой же контрол, как переключатель единиц. */
.input--search {
  flex: 1 1 260px;
  max-width: 420px;
  min-width: 200px;
  height: var(--filter-h);
}

/* ══════════════════════════════════════════════ выкладка каталога ══════ */

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

/* Ступени написаны от телефона вверх: базовые значения — самый узкий экран,
   каждый следующий запрос добавляет колонку. Числа подобраны так, чтобы
   карточка держалась в пределах 210–310 px на любой ширине: шире — фото
   раздувается на пол-экрана, уже — не читается название. */

[data-tiles="cozy"] .grid { --cols: 2; --tile-gap-y: 24px; --tile-gap-x: 12px; }
[data-tiles="dense"] .grid { --cols: 3; --tile-gap-y: 20px; --tile-gap-x: 8px; }

@media (min-width: 701px) {
  [data-tiles="cozy"] .grid { --cols: 3; --tile-gap-y: 28px; --tile-gap-x: 16px; }
  [data-tiles="dense"] .grid { --cols: 4; --tile-gap-y: 24px; --tile-gap-x: 12px; }
}
@media (min-width: 1001px) {
  [data-tiles="cozy"] .grid { --cols: 4; --tile-gap-y: 36px; --tile-gap-x: 20px; }
  [data-tiles="dense"] .grid { --cols: 5; --tile-gap-y: 28px; --tile-gap-x: 14px; }
}
@media (min-width: 1201px) {
  [data-tiles="cozy"] .grid { --cols: 4; --tile-gap-y: 40px; --tile-gap-x: 24px; }
  [data-tiles="dense"] .grid { --cols: 6; --tile-gap-y: 32px; --tile-gap-x: 16px; }
}
@media (min-width: 1501px) {
  [data-tiles="cozy"] .grid { --cols: 5; }
  [data-tiles="dense"] .grid { --cols: 7; }
}
@media (min-width: 1900px) {
  [data-tiles="cozy"] .grid { --cols: 6; }
  [data-tiles="dense"] .grid { --cols: 8; }
}

/* ═══════════════════════════════════════════════ плитка каталога ═══════ */

/* Плитка отвечает на свою ширину, а не на ширину экрана: одна и та же
   карточка стоит и в две колонки на телефоне, и в шесть на мониторе.
   Медиазапрос про экран об этом ничего не знает — контейнерный запрос знает. */
.tile {
  container: tile / inline-size;
  display: flex;
  flex-direction: column;
  gap: 10px;
  text-align: left;
}

/* Площадка под фото. Квадрат, а не 3/4: при 3/4 в одну колонку на телефоне
   фото забирало больше половины экрана, и до цены нужно было прокручивать
   каждую позицию. Квадрат — та же картинка, но карточка короче на четверть.
   На телефоне площадка ещё ниже — см. блок адаптива в конце файла. */
/* Селектор через .tile, а не просто .tile__well: тот же класс носит фото
   на странице товара, и квадрат каталожной плитки не должен подменять
   его собственную пропорцию. */
.tile > .tile__well {
  aspect-ratio: 1 / 1;
  cursor: pointer;
  border-style: solid;
  border-radius: var(--radius-tile);
}
/* Значок наличия — в углу площадки, а не отдельной строкой под фото:
   строка добавляла карточке высоты ради двух слов. */
.tile .tile__stock { position: absolute; top: 8px; left: 8px; bottom: auto; right: auto; }
.tile .tile__badge { top: 8px; right: 8px; left: auto; }
/* Нет в наличии — площадка гаснет: это видно раньше, чем прочитан значок. */
.tile > .tile__well:has(.stock-badge--out) { opacity: .62; }

/* Подпись: название и производитель. Производитель — служебная строка,
   в плотной выкладке её нет. */
.tile__caption { display: flex; flex-direction: column; gap: 2px; text-align: left; }
.tile__name {
  font-size: var(--text-base);
  line-height: 1.2;
  text-align: left;
  /* Ровно две строки: длинные названия обрезаются, короткие занимают
     столько же места. Иначе пилюли объёма в соседних плитках стояли бы
     на разной высоте — ряд читался бы как несколько разных списков. */
  min-height: 2.4em;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.tile__note {
  text-align: left;
  font-size: var(--text-xs);
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Цена. Крупная — та, которую платят за показанную единицу; рядом зачёркнута
   розничная, чтобы выгода читалась без счёта в уме. */
.tile__prices {
  display: flex;
  /* По центру, а не по базовой линии: в ряду стоят не только цифры, но и
     залитая пилюля «−34%», и по базовой линии она вставала на 2 px выше. */
  align-items: center;
  flex-wrap: wrap;
  gap: 4px var(--space-2);
  margin-top: auto;
  padding-top: var(--space-1);
}
.tile__price {
  font-family: var(--font-mono);
  font-variation-settings: normal;
  font-weight: var(--weight-bold);
  font-size: var(--text-lg);
  line-height: 1.1;
  letter-spacing: 0;
  color: var(--text-title);
  margin-top: 0;
}
.tile__price-old {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-xs);
  color: var(--text-muted);
  text-decoration: line-through;
}
.tile__diff {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-2xs);
  font-weight: var(--weight-bold);
  color: var(--text-on-accent);
  background: var(--accent);
  border-radius: var(--radius-pill);
  padding: 2px 7px;
  line-height: 1.4;
}
.tile__unit {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-2xs);
  color: var(--text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Кнопка карточки — пилюля во всю ширину. Ниже, чем была: 50 px под каждой
   из шести плиток в ряду перевешивали сам товар. */
.tile .cart-slot { display: block; margin-top: var(--space-1); }
.tile .cart-slot > .btn {
  width: 100%;
  height: 42px;
  padding: 0 var(--space-3);
  border-radius: var(--radius-pill);
  border-color: var(--white-a40);
  color: var(--text-title);
  text-transform: uppercase;
  letter-spacing: .1em;
  font-size: var(--text-2xs);
  font-weight: var(--weight-semibold);
}
.tile .cart-slot > .btn:hover:not(:disabled) { border-color: var(--text-title); background: var(--white-a08); }
/* «Нет в наличии» на общих 38 % читалась хуже, чем нужно: это не выключенная
   кнопка, а сообщение о складе, и прочесть его человек должен с первого раза. */
.tile .cart-slot > .btn:disabled { opacity: .55; }

/* Счётчик встаёт на место кнопки «В корзину» и повторяет её след: та же
   ширина, та же высота, тот же радиус. Иначе плитка на глазах «схлопывалась». */
.tile .cart-slot > .stepper {
  display: flex;
  width: 100%;
  height: 42px;
  justify-content: space-between;
  align-items: center;
  padding: 3px;
}
.tile .cart-slot .stepper__btn { width: 36px; height: 36px; }
.tile .cart-slot .stepper__value { flex: 1; min-width: 0; font-size: var(--text-sm); }
/* Убирает задержку 300 мс перед нажатием на телефоне. */
.tile .cart-slot .stepper__btn { touch-action: manipulation; }

/* ───────────────────────────────────────────── объём в плитке каталога */

/* В каталоге объём — маленькая пилюля с числом, а не силуэт бутылки:
   силуэты честно показывают пропорции, но занимают 44 px высоты в каждой
   плитке и в ряду из шести превращаются в частокол. Полноразмерный выбор
   силуэтами живёт на странице товара — там на него есть и место, и повод. */
.vols {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  min-height: 26px;
}
.vol {
  display: inline-flex;
  align-items: center;
  height: 26px;
  padding: 0 9px;
  border-radius: var(--radius-pill);
  border: var(--border-w-hair) solid var(--border-hairline);
  background: transparent;
  color: var(--text-muted);
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-2xs);
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
  transition: var(--transition-control);
}
.vol:hover { border-color: var(--border-strong); color: var(--text-title); }
/* Единственный объём не выбирают — его сообщают: ни курсора-руки,
   ни отклика на наведение у такой пилюли быть не должно. */
.vol--static { cursor: default; }
.vol--static:hover { border-color: var(--border-hairline); color: var(--text-muted); }
/* Выбранный объём — жёлтый, как всё остальное «выбрано» на витрине.
   Белым он спорил по весу с ценой под ним, и ряд пилюль читался как подпись,
   а не как выбор, который можно сделать прямо в списке. */
.vol[aria-checked="true"] {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--text-on-accent);
  font-weight: var(--weight-bold);
}
.vol:focus-visible { outline: none; box-shadow: var(--ring-focus); }
.vol[data-out] { opacity: .4; }
/* «ещё 2» — не объём, а вход в карточку: пунктирная рамка отличает его
   от выбираемых пилюль. */
.vol--more { border-style: dashed; }

/* ─────────────────────────────────────── плитка отвечает на свою ширину */

/* Узкая плитка (плотная выкладка на планшете, две колонки на телефоне):
   строка производителя и подпись под ценой уходят — остаётся то, ради чего
   карточка нужна: фото, название, объём, цена, кнопка. */
@container tile (max-width: 200px) {
  .tile { gap: var(--space-2); }
  .tile__note { display: none; }
  .tile__unit { display: none; }
  .tile__name { font-size: var(--text-sm); }
  .tile__price { font-size: var(--text-base); }
  .tile .cart-slot > .btn,
  .tile .cart-slot > .stepper { height: 38px; }
  .tile .cart-slot .stepper__btn { width: 32px; height: 32px; }
}

/* Совсем узкая плитка (три колонки на телефоне): подпись кнопки не влезает
   ни в какую вёрстку, поэтому кнопка становится знаком «+», а зачёркнутая
   розница уступает место оптовой цене. */
@container tile (max-width: 140px) {
  /* Название набирается основной гарнитурой и в три строки: дисплейная
     широкая на 12 px нечитаема, а две строки на 105 px давали «Вода
     минеральн…» у трёх соседних позиций подряд — различить их было нельзя. */
  .tile__name {
    font-family: var(--font-sans);
    font-variation-settings: normal;
    font-size: 12px;
    line-height: 1.25;
    letter-spacing: 0;
    -webkit-line-clamp: 3;
    min-height: 3.75em;
  }
  .tile__prices { gap: 2px 6px; }
  .tile__price { font-size: var(--text-sm); }
  .tile__price-old { display: none; }
  .vols { min-height: 0; }
  .vol { height: 22px; padding: 0 7px; }
  .tile .cart-slot > .btn {
    font-size: 0;
    letter-spacing: 0;
    height: 34px;
  }
  .tile .cart-slot > .btn::after {
    content: "+";
    font-family: var(--font-sans);
    font-size: var(--text-lg);
    line-height: 1;
  }
  /* Кнопки нет в наличии — «плюс» обещал бы то, чего склад не даст. */
  .tile .cart-slot > .btn:disabled::after { content: "—"; }
  .tile .cart-slot > .stepper { height: 34px; padding: 2px; }
  .tile .cart-slot .stepper__btn { width: 28px; height: 28px; }
  .tile .cart-slot .stepper__value small { display: none; }
  /* Подпись «В наличии» в плитку шириной 105 px не встаёт ни при каком
     кегле — остаётся точка. Смысл не теряется: рядом гаснет вся площадка,
     если товара нет. */
  .tile__stock .stock-badge {
    font-size: 0;
    gap: 0;
    width: 16px;
    height: 16px;
    padding: 0;
    display: inline-grid;
    place-items: center;
    border-radius: var(--radius-circle);
  }
}

/* Наведение поднимает плитку только там, где есть мышь: на телефоне
   «подъём» срабатывал от касания и карточка дёргалась под пальцем. */
.tile:hover { transform: none; }
@media (hover: hover) and (pointer: fine) {
  .tile:hover { transform: translateY(-4px); }
}

/* ═══════════════════════════════════ корзина, прилипшая к краю ═════════ */

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

   Полупилюля, приклеенная к правому краю, а не круг посреди экрана: так она
   закрывает угол одной плитки, а не её середину, и читается как край
   интерфейса, а не как объект поверх товара. */
.cart-fab { display: none; }

@media (max-width: 900px) {
  .cart-fab {
    position: fixed;
    /* Пятая часть экрана сверху: выше — под шапкой, ниже — под большим
       пальцем в момент прокрутки. */
    top: 20vh;
    right: 0;
    z-index: 35;

    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    /* Ширина по содержимому: пустая корзина — один знак, полная — знак
       и сумма. 52 px высоты — минимум, за который уверенно попадает палец
       (44 pt по рекомендациям iOS). */
    height: 52px;
    padding: 0 12px 0 15px;
    border: var(--border-w-hair) solid var(--border-strong);
    border-right: 0;
    border-radius: var(--radius-pill) 0 0 var(--radius-pill);
    background: var(--cobalt-1000);
    color: var(--text-title);
    box-shadow: var(--shadow-overlay);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;

    /* Появляется, когда кнопка в шапке ушла за экран; до этого её нет
       ни для мыши, ни для клавиатуры, ни для голосового доступа. */
    opacity: 0;
    visibility: hidden;
    transform: translateX(8px);
    transition: opacity var(--dur-base) var(--ease-out),
                transform var(--dur-base) var(--ease-out),
                visibility 0s linear var(--dur-base);
  }
  .cart-fab.is-on {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition-delay: 0s;
  }
  .cart-fab:active { transform: scale(var(--press-scale)); }
  .cart-fab:focus-visible { outline: none; box-shadow: var(--ring-focus); }

  /* Непустая корзина — акцент: та же роль, что у кнопки в шапке. */
  .cart-fab--filled {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--text-on-accent);
  }

  /* Сумма набранного — то, ради чего на кнопку смотрят с середины списка.
     Моноширинным: разряды не пляшут, когда сумма растёт на ходу. */
  .cart-fab__sum {
    font-family: var(--font-mono);
    font-variant-numeric: tabular-nums;
    font-weight: var(--weight-bold);
    font-size: var(--text-sm);
    line-height: 1;
    white-space: nowrap;
  }
  .cart-fab__sum[hidden] { display: none; }
  /* Пустая корзина — только знак, и поля вокруг него ровные. */
  .cart-fab:not(.cart-fab--filled) { padding: 0 14px; }
}

/* Пока открыт раздел — корзина, товар, «Опт» — кнопки нет: она вела бы
   туда, где человек уже стоит, и висела бы поверх содержимого раздела. */
.is-page-open .cart-fab { display: none; }

@media (prefers-reduced-motion: reduce) {
  .cart-fab { transition: none; }
}

/* ═══════════════════════════════════════════════════════ наверх ═══════ */

/* Стрелка возврата к началу списка. На большом экране она стоит в рельсе
   слева; ниже 1180 px рельс убран — здесь его работу берёт эта кнопка.

   Единственное место, кроме скрима модалки, где применяется блюр. Правило
   системы держит блюр под запретом ради полос во всю ширину: фростед-бар
   поверх насыщенной заливки убивает и её, и себя. Кнопка 44 px — не полоса:
   стекло здесь делает ровно то, что нужно, — уводит её на второй план,
   оставляя читаемой. Это осознанное расширение правила с одного места
   до двух, а не его отмена.

   Форма нарочно не такая, как у корзины: корзина — жёлтая пилюля, вваренная
   в край экрана, за ней идут. Эта — стеклянный кружок, отставший от края:
   он ничего не обещает, он помогает вернуться. */
.to-top { display: none; }

@media (max-width: 1180px) {
  .to-top {
    position: fixed;
    right: max(var(--space-3), env(safe-area-inset-right));
    bottom: calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
    z-index: 34;

    display: inline-grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border-radius: var(--radius-circle);
    border: var(--border-w-hair) solid var(--white-a20);
    background: var(--white-a08);
    -webkit-backdrop-filter: var(--blur-glass);
    backdrop-filter: var(--blur-glass);
    color: var(--text-body);
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    touch-action: manipulation;

    opacity: 0;
    visibility: hidden;
    transform: translateY(8px);
    transition: opacity var(--dur-base) var(--ease-out),
                transform var(--dur-base) var(--ease-out),
                background-color var(--dur-fast) var(--ease-out),
                visibility 0s linear var(--dur-base);
  }
  .to-top.is-on {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition-delay: 0s;
  }
  .to-top:hover { background: var(--white-a20); color: var(--text-title); }
  .to-top:active { transform: scale(var(--press-scale)); }
  .to-top:focus-visible { outline: none; box-shadow: var(--ring-focus); }
}

/* Пока открыт раздел — своя прокрутка и своя кнопка возврата. */
.is-page-open .to-top { display: none; }

@media (prefers-reduced-motion: reduce) {
  .to-top { transition: none; }
}

/* ══════════════════════════════════════════════════════ адаптив ════════ */

@media (max-width: 900px) {
  .nav__actions .btn--outline { display: none; }
  .hero h1 { font-size: var(--text-xl); }
}

/* Шапка телефона — одна строка со знаком и корзиной, под ней разделы.
   С непустой корзиной кнопка вырастала до 229 px, марка занимала 192,
   и шапка разъезжалась на три строки высотой 145 px: четверть экрана
   уходила на навигацию ещё до первого товара. */
@media (max-width: 560px) {
  .nav { gap: var(--space-2); padding: var(--space-3) var(--space-4) var(--space-2); }
  .wordmark { font-size: 20px; gap: 7px; }
  .wordmark__mark { width: 22px; height: 22px; }
  /* В кнопке остаются знак и сумма: слово «корзина» рядом с иконкой сумки
     и ценником не сообщает ничего нового, а забирает 90 px строки.
     Число штук уходит следом: «1 121 ₽ 18 шт» разносило шапку на три строки
     на большом заказе, а сколько там штук, видно в самой корзине. Сумма
     остаётся — она и есть повод туда заглянуть. */
  .cart-btn__label { display: none; }
  .cart-btn__sum small { display: none; }
  .cart-btn { padding-left: 12px; }
  .cart-btn:not(.cart-btn--filled) { padding-right: 12px; }
  .nav__link { padding: 7px 14px; }
}

/* Планшет и телефон: полоса фильтров встаёт в две строки — поиск во всю
   ширину, под ним контролы. Контролы при этом остаются одной линией:
   ряд не переносится, а прокручивается, если не помещается. */
@media (max-width: 760px) {
  :root { --filter-h: 44px; }
  .filters__row { gap: var(--space-3); }
  .input--search { flex: 1 1 100%; max-width: none; order: -1; }
  .filters__tools {
    margin-left: 0;
    flex: 1 1 100%;
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    /* Тень обрезания не нужна: полоса и так короткая, а полоса прокрутки
       на телефоне съедала бы высоту ряда. */
    padding-bottom: 1px;
  }
  .filters__tools::-webkit-scrollbar { display: none; }
  .filters__tools > * { flex: 0 0 auto; }
  /* Надстрочные подписи в узкой полосе не нужны: «шт/упак» и два знака
     выкладки понятны без них, а места они занимают больше самих контролов. */
  .chip__label { display: none; }
  .chip { padding: 0 5px 0 var(--space-3); gap: var(--space-2); }
  .chip.switch { padding: 0 var(--space-4); }

  /* Подписи «Считать в» и «Вид» на узком экране скрыты, а поле и рамка
     пилюли вокруг переключателя оставались: вокруг дорожки шёл пустой
     тёмный ободок, и получалось два скруглённых контура один в другом.
     Обёртка здесь не нужна — переключатель сам становится пилюлей
     нужного роста. */
  .unit-scope,
  .view-scope {
    padding: 0;
    border: 0;
    background: none;
    height: auto;
  }
  .unit-scope .unit-toggle,
  .view-scope .unit-toggle {
    height: var(--filter-h);
    background: var(--surface-inset);
    border-color: var(--border-hairline);
  }
  /* Сегменты во всю высоту дорожки: 30 px по вертикали — меньше подушечки
     пальца, а по знаку выкладки промахнуться проще всего. */
  .chip .unit-toggle button { height: 100%; padding: 0 14px; }
  .unit-toggle--icons button { width: 44px; }
}

/* Телефон: три контрола в 343 px помещаются только без дорожки свитча.
   «Только в наличии» становится такой же нажимаемой пилюлей, как чипы
   категорий строкой ниже: включено — залита, выключено — контур.
   Это тот же смысл теми же средствами, что и у соседей по экрану,
   и полоса остаётся одной строкой, а не уползает за край. */
@media (max-width: 620px) {
  .chip.switch .switch__track { display: none; }
  .chip.switch {
    gap: 0;
    cursor: pointer;
  }
  .chip.switch:has(input:checked) {
    background: var(--accent);
    border-color: var(--accent);
  }
  .chip.switch:has(input:checked) .switch__label {
    color: var(--text-on-accent);
    font-weight: var(--weight-semibold);
  }
  /* Фокус с клавиатуры виден на самой пилюле: дорожки, на которой он
     показывался, здесь больше нет. */
  .chip.switch:has(input:focus-visible) { box-shadow: var(--ring-focus); }

  /* Категории — одна прокручиваемая строка, а не три ряда чипов: в три ряда
     они съедали треть экрана телефона, и первый товар начинался за краем.
     Строкой их видно столько же, сколько успевают прочитать, а остальные
     в одном движении пальца. */
  #cats {
    flex-wrap: nowrap;
    overflow-x: auto;
    scrollbar-width: none;
    /* Поля страницы гасятся, чтобы строка шла от края до края: обрезанный
       чип у самого края читается как «здесь есть продолжение». */
    margin: 0 calc(var(--space-4) * -1);
    padding: 0 var(--space-4);
  }
  #cats::-webkit-scrollbar { display: none; }
  #cats > * { flex: 0 0 auto; }
}

/* Телефон: заголовок и «опт дешевле» встают колонкой, поля страницы уже. */
/* Телефон: площадка под фото ниже квадрата. Бутылку видно и в такой рамке
   (снимок вписывается по высоте), а на экран помещается на треть больше
   позиций — ради этого на витрину и заходят с телефона. */
@media (max-width: 620px) {
  .tile > .tile__well { aspect-ratio: 4 / 3; }
}

@media (max-width: 560px) {
  /* Заголовок и «опт дешевле» встают в строку, а не колонкой: вместе они
     занимают меньше экрана, и первый ряд товара начинается выше — иначе
     весь первый экран телефона уходил на обещание, а не на товар. */
  .hero {
    gap: var(--space-4);
    padding: var(--space-3) 0 var(--space-4);
  }
  .hero__text { flex: 1 1 100%; }
  .hero h1 { font-size: var(--text-lg); max-width: none; }
  .hero p { margin-top: var(--space-2); font: var(--type-body-sm); }
  .hero .stat { justify-items: start; text-align: left; }
  .hero .stat__value { font-size: var(--text-lg); }
  .filters { margin: var(--space-4) 0 var(--space-5); padding: 0; gap: var(--space-3); }
  .cart-page__panel { padding: var(--space-5); }
}
