/* Дизайн-система Ценопульс — раздел 29 итогового документа, токены воспроизведены
   буквально (29.2 палитра, 29.3 типографика, 29.4 сетка/форма, 29.5 карточка сделки).
   Светлая тема ОСНОВНАЯ (не тёмная по умолчанию) — 29.2: "она честнее для товарного
   контента... светлый интерфейс читается при дневном свете на улице". */

:root {
  --canvas: #F7F7F5;
  --paper: #FFFFFF;
  --sunken: #F0F0ED;
  --line: #E4E4E0;
  --line-strong: #D0D1CB;
  --ink-900: #16181C;
  --ink-700: #2F333A;
  --ink-500: #6B7178;
  --ink-400: #8E959D;
  --ink-300: #B7BDC4;

  --deal-700: #056B45;
  --deal-600: #077A4E;
  --deal-500: #0AA86A;
  --deal-400: #2FC98A;
  --deal-100: #C9EEDB;
  --deal-050: #EAF8F1;

  --hot: #D9401F;
  --hot-050: #FDEDE9;
  --warn: #A85E00;
  --warn-icon: #E08A00;
  --danger: #C7261C;
  --info: #1560C4;

  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 14px;
  --radius-xl: 20px;
  --radius-full: 999px;

  --space-1: 4px; --space-2: 8px; --space-3: 12px; --space-4: 16px;
  --space-6: 24px; --space-8: 32px; --space-12: 48px; --space-16: 64px; --space-24: 96px;

  --max-width: 1280px;
  --shadow-overlay: 0 12px 32px rgba(20, 22, 26, .12);

  --ease: cubic-bezier(.2, .8, .2, 1);
}

/* Тёмная тема — переключатель + auto по системной настройке (29.2), НЕ дефолт. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --canvas: #0E0F12; --paper: #16181C; --sunken: #1D2026; --line: #2A2E35; --line-strong: #363B44;
    --ink-900: #F2F4F6; --ink-700: #D6DAE0; --ink-500: #99A0A8; --ink-400: #7A828C; --ink-300: #5C6470;
    --deal-600: #16C97E; --deal-500: #22DC8C; --deal-700: #22DC8C; --deal-050: #102A20; --deal-100: #16382C;
    --hot: #FF6A4D; --hot-050: #3A241E; --danger: #FF5A50;
    /* Р-12 патча №1 — раньше --warn/--warn-icon использовались только в светлой теме
       (нигде в проекте не было потребителя), первый потребитель — .promo-chip. */
    --warn: #E0A030; --warn-icon: #FFB84D;
  }
}
:root[data-theme="dark"] {
  --canvas: #0E0F12; --paper: #16181C; --sunken: #1D2026; --line: #2A2E35; --line-strong: #363B44;
  --ink-900: #F2F4F6; --ink-700: #D6DAE0; --ink-500: #99A0A8; --ink-400: #7A828C; --ink-300: #5C6470;
  --deal-600: #16C97E; --deal-500: #22DC8C; --deal-700: #22DC8C; --deal-050: #102A20; --deal-100: #16382C;
  --hot: #FF6A4D; --hot-050: #3A241E; --danger: #FF5A50;
  --warn: #E0A030; --warn-icon: #FFB84D;
}

* { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--canvas);
  color: var(--ink-700);
  font-family: "Inter", "Inter Variable", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-feature-settings: "tnum" 1, "zero" 1;
  line-height: 1.55;
  font-size: 15px;
}

.container {
  max-width: var(--max-width);
  margin: 0 auto;
  padding: 0 16px;
}
@media (min-width: 768px) { .container { padding: 0 24px; } }
@media (min-width: 1200px) { .container { padding: 0 32px; } }

header.site {
  border-bottom: 1px solid var(--line);
  padding: var(--space-4) 0;
  background: var(--paper);
}
header.site .container { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); }

.brand { display: flex; align-items: center; gap: var(--space-2); text-decoration: none; }
.brand img { width: 28px; height: 28px; border-radius: var(--radius-sm); display: block; }
.brand-word { font-weight: 700; font-size: 20px; color: var(--ink-900); }
.brand-word span { color: var(--deal-700); }

/* 14.09.2026 (живой баг-репорт владельца, скриншот): поиск сжимался до ~85px на
   обычном десктопе (1920px, .container капается на 1280px). Причина — у flex-детей
   БЕЗ явного min-width автоматический минимум равен ширине их НЕсжимаемого контента
   (min-width:auto по умолчанию); у nav.site 9 пунктов меню (после добавления
   "Telegram-канал" в отдельном раунде) в один ряд без переноса — это ~950px
   "неприкасаемого" контента, и вся давка при недостатке места доставалась ТОЛЬКО
   .search (у него flex:1 с базой 0%, готов сжиматься первым). overflow-x:auto у
   nav.site раньше был только внутри @media(max-width:1024px) — на десктопе внутренний
   скролл не работал ПО ТОЙ ЖЕ причине (min-width:auto не даёт flex-контейнеру сжаться
   настолько, чтобы включился его собственный overflow). min-width:0 — снимает именно
   этот запрет, а не добавляет новое поведение: теперь nav.site при нехватке места
   скроллится сам (тем же паттерном, что уже работал на мобильных), а не отжимает
   поиск. Раньше проверялось только отсутствие ГОРИЗОНТАЛЬНОГО СКРОЛЛА ВСЕЙ СТРАНИЦЫ
   при добавлении 9-го пункта — не то, что происходит с шириной СОСЕДНЕГО элемента
   в той же flex-строке на десктопных ширинах. */
nav.site {
  display: flex; align-items: center; gap: var(--space-4);
  min-width: 0;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
nav.site::-webkit-scrollbar { display: none; }
nav.site a { flex: 0 0 auto; white-space: nowrap; }

/* 14.09.2026 — вне скроллящегося nav.site (см. коммент выше), поэтому свой flex:0 0
   auto нужен и здесь: иначе header-actions мог бы схлопнуться той же логикой
   min-width:auto, что раньше душила .search. */
.header-actions { display: flex; align-items: center; gap: var(--space-3); flex: 0 0 auto; }
nav.site a {
  color: var(--ink-500);
  text-decoration: none;
  font-size: 14px;
  transition: color 140ms var(--ease);
}
nav.site a:hover { color: var(--ink-900); }
/* 14.09.2026 (владелец: "телеграмм надо выделить") — единственная ссылка в шапке,
   ведущая на живой канал с публикациями (не на служебную/юридическую страницу
   сайта, как остальные пункты меню) — тот же принцип выделения "активного"
   элемента, что .hcats a.on / .chip.on ниже (фон deal-050 + текст deal-700),
   не отдельный чужеродный брендовый цвет Telegram. */
.nav-tg {
  color: var(--deal-700);
  background: var(--deal-050);
  padding: 5px 12px;
  border-radius: var(--radius-full);
  font-weight: 600;
  font-size: 14px;
  text-decoration: none;
  transition: background-color 140ms var(--ease);
}
.nav-tg:hover { color: var(--deal-700); background: var(--deal-100); }
/* Та же причина 85px-поиска — на всякий случай ещё и явный пол по ширине, чтобы
   поле поиска никогда не проваливалось ниже читаемого минимума, даже если nav
   когда-нибудь перестанет корректно скроллиться сам. */
.search { min-width: 220px; }

.theme-toggle {
  border: 1px solid var(--line);
  background: var(--sunken);
  color: var(--ink-500);
  border-radius: var(--radius-full);
  width: 32px; height: 32px;
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  font-size: 15px;
  line-height: 1;
}
.theme-toggle:hover { color: var(--ink-900); border-color: var(--line-strong); }

/* Раунд 18 ревью: header.site .container (brand+search+nav) — flex без flex-wrap,
   на узких вьюпортах (телефон) не помещается 8 пунктов меню + поиск, скроллвотч
   документа растягивался до ~970px при вьюпорте 390px (горизонтальный скролл всей
   страницы). Схлопываем в 3 строки на мобильных: бренд, затем поиск на всю ширину,
   затем nav со своим внутренним overflow-x, не раздувая страницу целиком.
   Раунд 34 ревью: брейкпоинт 640px не покрывал планшеты — живой замер (Playwright,
   scrollWidth-clientWidth) показал горизонтальный скролл ВСЕЙ страницы на каждой
   ширине от 641 до 980px включительно (напр. 768px = альбомный/портретный планшет),
   шапка не помещалась и обрезала половину меню без какого-либо намёка на скролл.
   Раздвигаем брейкпоинт до 1024px (замер: фактически нужно >=1000px, берём с
   запасом) — весь планшетный диапазон получает тот же 3-строчный безопасный layout. */
@media (max-width: 1024px) {
  header.site .container { flex-wrap: wrap; row-gap: var(--space-2); }
  .search { order: 3; flex: 1 1 100%; max-width: none; }
  /* overflow-x/scrollbar/nowrap для nav.site теперь в безусловном правиле выше —
     здесь остаётся только мобильная раскладка (перенос на свою строку). */
  nav.site { order: 2; flex: 1 1 100%; }
}

main { padding: var(--space-8) 0 var(--space-16); }

/* Раздел 29.11 ТЗ (Р-14) — живьём найдено (Playwright, Range.getClientRects, 320px,
   /deal/{id}): обычный перенос по словам (white-space:normal) ничего не может
   сделать с ОДНИМ "словом" без пробела внутри (название товара в кавычках-ёлочках,
   напр. «Первоклассник») — если оно само по себе шире доступной ширины, вторая
   строка честно вылезала за край (327.5px против 320px), хотя getBoundingClientRect
   самого h1 ничего не показывал (это была ink-граница строки текста, не CSS-бокса).
   Названия товаров — внешний, не наш текст (партнёрские фиды), длина/пунктуация
   не под нашим контролем — overflow-wrap:break-word единственный надёжный фикс. */
h1 { font-size: 2rem; line-height: 1.15; letter-spacing: -0.02em; margin: 0 0 var(--space-2); color: var(--ink-900); overflow-wrap: break-word; }
h2 { font-size: 1.5rem; line-height: 1.25; letter-spacing: -0.01em; margin: var(--space-6) 0 var(--space-3); color: var(--ink-900); }
h3 { font-size: 1.25rem; line-height: 1.3; margin: var(--space-4) 0 var(--space-2); color: var(--ink-900); }
p.lede { color: var(--ink-500); font-size: 17px; margin: 0 0 var(--space-6); }

.deal-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4);
}
/* Раздел 29.11 ТЗ (Р-14) — тот же класс, что уже чинился у .main-layout выше:
   grid-item по умолчанию min-width:auto, не сжимается ниже min-content своего
   контента. Живьём найдено на карточке с товаром без пробелов в конце названия
   ("...сталь_ДУОэл80х50ПматхрсВОРКун") — .deal-title сам честно клампится
   (-webkit-line-clamp), но алгоритм разметки grid всё равно учитывал его
   некламп-нутую ширину при расчёте минимума колонки, раздувая .deal-card на 3px
   за пределы 320px. min-width:0 на карточке — та же причина, тот же фикс. */
.deal-grid > .deal-card { min-width: 0; }
@media (min-width: 768px) { .deal-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1200px) { .deal-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1600px) { .deal-grid { grid-template-columns: repeat(4, 1fr); } }

/* Карточка сделки — раздел 29.5, главный компонент продукта. Иерархия фоном+границей,
   без теней (29.4: "тени только у оверлеев"). */
.deal-card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  text-decoration: none;
  color: var(--ink-700);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  transition: border-color 140ms var(--ease);
  position: relative;
}
.deal-card:hover { border-color: var(--deal-500); }
.deal-card:focus-visible { outline: 2px solid var(--deal-500); outline-offset: 2px; }

.deal-card-top { display: flex; justify-content: space-between; align-items: flex-start; }

/* Бейдж "Рекорд" — редко, только для настоящего исторического минимума (29.2/29.5:
   "если он на каждой карточке, мы превратились в тот самый рыночный развал"). */
.badge-hot {
  display: inline-flex; align-items: center; gap: 4px;
  background: var(--hot-050); color: var(--hot);
  font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.02em;
  padding: 3px 10px; border-radius: var(--radius-full);
  width: fit-content;
}
.badge-fake {
  display: inline-flex; align-items: center; gap: 4px;
  background: var(--hot-050); color: var(--danger);
  font-size: 11px; font-weight: 600;
  padding: 3px 10px; border-radius: var(--radius-full);
  width: fit-content;
}

.deal-shop { color: var(--ink-500); font-size: 13px; text-transform: uppercase; letter-spacing: .04em; }
.deal-title {
  font-size: 15px; font-weight: 600; color: var(--ink-900);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  min-height: 40px;
}

.deal-price-row { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; }
.deal-price {
  font-size: 36px; font-weight: 700; line-height: 1; letter-spacing: -0.03em;
  color: var(--ink-900); font-variant-numeric: tabular-nums;
}
.deal-price-before { color: var(--ink-500); text-decoration: line-through; font-size: 15px; }
.deal-discount { color: var(--deal-700); font-weight: 600; font-size: 14px; }

.spark { display: block; width: 100%; height: 40px; }
.spark-fill { fill: var(--deal-100); }
.spark-line { fill: none; stroke: var(--deal-500); stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.spark-dot { fill: var(--deal-400); }

.deal-min-caption { color: var(--ink-500); font-size: 13px; }

/* Раздел 29.5 ТЗ — главная монетизируемая кнопка карточки (клик = партнёрский
   переход), владелец нашёл её недостаточно выделенной. Крупнее/жирнее + честный
   hover-отклик (без тени — раздел 29.4 "тени только у оверлеев", вместо неё
   лёгкое посветление фона). */
.deal-cta {
  margin-top: auto;
  display: inline-block;
  text-align: center;
  background: var(--deal-600);
  color: #fff;
  padding: 12px 18px;
  border-radius: var(--radius-md);
  font-weight: 700;
  font-size: 15px;
  text-decoration: none;
  transition: background-color .15s ease;
}
.deal-cta:hover, .deal-cta:focus-visible { background: var(--deal-500); }

.deal-score { color: var(--ink-400); font-size: 12px; }

.empty-state {
  border: 1px dashed var(--line-strong);
  border-radius: var(--radius-lg);
  padding: var(--space-12) var(--space-6);
  text-align: center;
  color: var(--ink-500);
  background: var(--paper);
}

.deal-detail {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
}
.deal-detail .spark { height: 160px; margin: var(--space-4) 0; }
/* Раздел 29.11 ТЗ (Р-14) — живьём найдено (Playwright, 320px, /deal/{id}):
   .deal-detail добавляет свой var(--space-6) паддинг ПОВЕРХ 16px контейнера
   (доступно ~240px на 320px-вьюпорте), а первая колонка была жёстко "220px" —
   не тот же min-width:auto класс, что раньше, а честно НЕХВАТКА места под
   фиксированную ширину. minmax(0, 220px) разрешает колонке сжаться (dt
   переносится на 2 строки), плюс на действительно узких экранах — вертикальная
   раскладка (dt над dd), читается лучше сжатой в нитку строки. */
.deal-detail dl { display: grid; grid-template-columns: minmax(0, 220px) 1fr; gap: var(--space-2) var(--space-4); margin: var(--space-4) 0; }
@media (max-width: 480px) { .deal-detail dl { grid-template-columns: 1fr; } }
.deal-detail dt { color: var(--ink-500); }
.deal-detail dd { margin: 0; color: var(--ink-900); }
.deal-detail a.buy-link {
  display: inline-block;
  margin-top: var(--space-4);
  background: var(--deal-600);
  color: #fff;
  padding: 12px 24px;
  border-radius: var(--radius-md);
  text-decoration: none;
  font-weight: 600;
}

.score-breakdown { margin-top: var(--space-6); border-top: 1px solid var(--line); padding-top: var(--space-4); }
.score-breakdown summary { cursor: pointer; color: var(--ink-500); font-size: 14px; }
.score-breakdown table { width: 100%; border-collapse: collapse; margin-top: var(--space-3); font-size: 13px; }
.score-breakdown td, .score-breakdown th { padding: 6px 8px; border-bottom: 1px solid var(--line); text-align: left; }
.score-breakdown .na { color: var(--ink-400); font-style: italic; }

article.prose { max-width: 720px; }
article.prose p, article.prose li { color: var(--ink-500); }
article.prose h1, article.prose h2 { color: var(--ink-900); }

footer.site {
  border-top: 1px solid var(--line);
  padding: var(--space-6) 0;
  color: var(--ink-500);
  font-size: 13px;
}
footer.site a { color: var(--ink-500); }
footer.site a:hover { color: var(--ink-900); }

@media (prefers-reduced-motion: reduce) {
  * { transition-duration: 0ms !important; animation-duration: 0ms !important; }
}

/* === Композиция главной — раздел 29.17 ТЗ (патч №1, И-4) === */

.search { flex: 1; max-width: 480px; position: relative; }
.search input {
  width: 100%; height: 38px;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-md);
  background: var(--sunken);
  padding: 0 12px 0 36px;
  font: inherit; font-size: 14px; outline: none;
  color: var(--ink-700);
}
.search input:focus {
  border-color: var(--deal-500);
  background: var(--paper);
  box-shadow: 0 0 0 3px var(--deal-050);
}
/* Раунд 25 ревью: поиск ещё не подключён к бэкенду (раздел 6.2 ТЗ) — визуально
   inert, тот же принцип, что у .btn-block ("скоро" не должно выглядеть как рабочее"). */
.search input:disabled {
  cursor: not-allowed;
  opacity: .65;
}
.search svg { position: absolute; left: 11px; top: 10px; color: var(--ink-400); pointer-events: none; }

.hcats {
  display: flex; gap: 4px; overflow-x: auto;
  margin: var(--space-2) 0 0;
  padding-bottom: 2px;
}
.hcats a {
  font-size: 14px; color: var(--ink-500);
  padding: 6px 10px; border-radius: var(--radius-sm);
  white-space: nowrap; text-decoration: none;
}
.hcats a.on { color: var(--deal-700); font-weight: 600; background: var(--deal-050); }
.hcats a:hover { background: var(--sunken); }

.pulse-strip {
  background: var(--ink-900); color: #fff;
  display: flex; gap: var(--space-6); align-items: center;
  height: 42px; padding: 0 16px; border-radius: var(--radius-md);
  font-size: 13px; overflow-x: auto; margin: var(--space-4) 0;
}
.pulse-strip b { color: var(--deal-400); font-weight: 600; }
.pulse-strip span { color: #B9BFC6; white-space: nowrap; }
.pulse-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--deal-400); display: inline-block;
  animation: pulse-blink 3s infinite;
}
@keyframes pulse-blink { 0%,100%{opacity:.35;transform:scale(.8)} 50%{opacity:1;transform:scale(1)} }

.main-layout { display: grid; grid-template-columns: 1fr 316px; gap: var(--space-6); }
/* Раздел 29.11 ТЗ (Р-14) — живьём найдено (Playwright, 320px, scrollWidth 398 против
   clientWidth 320): grid-колонка "1fr" по умолчанию не сжимается ниже min-content
   своего содержимого (браузерное default min-width:auto на grid-item) — .tabs/.chips
   внутри неё честно умеют переноситься (flex-wrap:wrap), но САМА колонка отказывалась
   сжиматься до их наличной ширины и раздувала всю страницу на 78px. Тот же класс
   граблей, что и у flexbox min-width:auto, просто на grid-стороне. min-width:0 —
   стандартный фикс, разрешает колонке сжаться до ширины контейнера, перенос строк
   внутри .tabs/.chips делает остальную работу. */
.main-layout > div { min-width: 0; }
@media (max-width: 1100px) { .main-layout { grid-template-columns: 1fr; } .aside { display: none; } }

/* Раздел 29.11 ТЗ (Р-14) — живьём найдено (Playwright, 320-390px): 4 вкладки
   (Горячее/Новое/Рекорды цены/Промокоды) в один ряд без переноса/скролла шире
   доступных 288px — крайняя ссылка "Промокоды" вылезала за пределы своего же,
   уже честно сжатого до 288px контейнера (min-width:0 у .main-layout > div выше
   исправил ГРАНИЦУ контейнера, но не резиновость самого содержимого). Тот же
   контур, что уже применён к nav.site/.hcats/.pulse-strip — скролл внутри своего
   блока, не раздувание всей страницы. */
.tabs { display: flex; gap: 2px; border-bottom: 1px solid var(--line); margin-bottom: var(--space-4);
  overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
.tabs::-webkit-scrollbar { display: none; }
.tabs a {
  padding: 9px 14px; font-weight: 600; font-size: 15px;
  color: var(--ink-500); text-decoration: none; white-space: nowrap; flex: 0 0 auto;
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.tabs a.on, .tabs span.on { color: var(--ink-900); border-bottom-color: var(--deal-500); }

.chips { display: flex; gap: 7px; flex-wrap: wrap; margin-bottom: var(--space-4); }
.chip {
  font-size: 13px; padding: 5px 11px;
  border: 1px solid var(--line-strong); border-radius: var(--radius-full);
  background: var(--paper); color: var(--ink-500);
  text-decoration: none; display: inline-block;
}
a.chip:hover { border-color: var(--deal-500); color: var(--ink-900); }
/* Р-8 патча №1 — активный фильтр-чип (routers/pages.py::index, query-параметр chip) */
.chip.on { background: var(--deal-500); color: #fff; border-color: var(--deal-500); }

/* Раздел 29.5 ТЗ (патч №1, Р-12) — промокод-чип магазина на карточке сделки, копирование
   в буфер через data-copy-code (base.html, делегированный обработчик — см. докстринг там же
   про то, почему не onclick с интерполяцией). Отдельный (не зелёный) акцент — это купон
   МАГАЗИНА, не оценка сделки, не должен читаться как ещё один "честностный" бейдж. */
.promo-chip {
  font-size: 12px; font-weight: 600; padding: 4px 9px; margin-top: 6px;
  border: 1px dashed var(--warn-icon); border-radius: var(--radius-full);
  background: transparent; color: var(--warn); display: inline-flex; align-items: center; gap: 4px;
  cursor: pointer; font-family: inherit;
}
button.promo-chip:hover { background: color-mix(in srgb, var(--warn-icon) 12%, transparent); }
.promo-chip-auto { cursor: default; }
.promo-chip-auto:hover { background: transparent; }

.thumb {
  position: relative; aspect-ratio: 4/3;
  background: var(--sunken); border-radius: var(--radius-md);
  display: grid; place-items: center; color: var(--ink-400);
  margin-bottom: var(--space-2);
}
.thumb .badge-hot, .thumb .badge-fake { position: absolute; top: 9px; left: 9px; }

.deal-discount-m { color: var(--ink-400); font-weight: 400; }

.deal-detail-hero { display: flex; gap: var(--space-6); align-items: flex-start; flex-wrap: wrap; }
.deal-detail-thumb { width: 180px; aspect-ratio: 1; margin-bottom: 0; flex: none; }
/* Раздел 29.11 ТЗ (Р-14) — живьём найдено (Playwright, ровно 320px, /deal/{id}):
   .deal-detail добавляет свой var(--space-6) паддинг с обеих сторон, оставляя этому
   блоку в самом узком поддерживаемом вьюпорте всего ~238px — на 2px меньше жёсткого
   min-width:240px, даже когда блок уже честно перенесён на свою строку (flex-wrap
   выше отработал верно, дело не в переносе). min(240px, 100%) — тот же минимум на
   более широких экранах, но не давит через край родителя, когда доступно меньше. */
.deal-detail-hero > div:last-child { flex: 1; min-width: min(240px, 100%); }

.aside section {
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: var(--space-4); margin-bottom: var(--space-4);
}
.aside h3 { margin: 0 0 var(--space-3); font-size: 14px; }
.alertbox { background: linear-gradient(180deg, var(--deal-050), var(--paper)); border-color: var(--deal-100); }
.alertbox p { font-size: 13px; color: var(--ink-500); margin: 0 0 var(--space-3); }
.btn-block {
  width: 100%; background: var(--ink-300); color: #fff; border: 0;
  padding: 10px; border-radius: var(--radius-md); font-weight: 600; cursor: not-allowed;
}
.mini { display: flex; gap: 10px; padding: 9px 0; border-top: 1px solid var(--line); }
.mini:first-of-type { border-top: 0; padding-top: 0; }
.m-th { width: 44px; height: 44px; border-radius: var(--radius-sm); background: var(--sunken); flex: none; display: grid; place-items: center; color: var(--ink-400); }
.m-t { font-size: 13px; color: var(--ink-900); line-height: 1.3; }
.m-p { font-size: 13px; font-weight: 700; color: var(--ink-900); margin-top: 3px; }
.m-p s { color: var(--ink-400); font-weight: 400; font-size: 11.5px; margin-left: 5px; }
.shops-row { display: flex; justify-content: space-between; font-size: 13px; padding: 6px 8px; border-radius: var(--radius-sm); }
.shops-row:hover { background: var(--sunken); }
.shops-pct { color: var(--ink-400); }

.empty-state h3 { margin: 0 0 6px; }
.empty-state p { margin: 0; font-size: 14px; }

.method-table { width: 100%; border-collapse: collapse; margin: var(--space-4) 0; font-size: 14px; }
.method-table td { padding: 8px 4px; border-bottom: 1px solid var(--line); }
.method-table td:last-child { text-align: right; font-weight: 600; color: var(--ink-900); }
.note {
  background: var(--deal-050); border: 1px solid var(--deal-100);
  border-radius: var(--radius-md); padding: 12px 14px;
  font-size: 13.5px; color: var(--deal-700); margin: var(--space-4) 0;
}

/* /categories (14.09.2026) — та же карточная иерархия, что .deal-card (фон+граница,
   без теней — 29.4 "тени только у оверлеев"), но без цены/картинки, только имя+счётчики. */
.category-grid {
  display: grid; grid-template-columns: 1fr; gap: var(--space-3);
  margin: var(--space-4) 0;
}
@media (min-width: 640px) { .category-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) { .category-grid { grid-template-columns: repeat(3, 1fr); } }
.category-card {
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius-lg);
  padding: var(--space-4); text-decoration: none; color: var(--ink-700);
  display: flex; flex-direction: column; gap: 6px;
  transition: border-color 140ms var(--ease);
}
.category-card:hover, .category-card:focus-visible { border-color: var(--deal-500); }
.category-card-name { font-weight: 600; font-size: 16px; color: var(--ink-900); }
.category-card-stats { display: flex; gap: var(--space-3); font-size: 13px; color: var(--ink-500); }
.category-card-deals { color: var(--deal-600); font-weight: 600; }

/* реальное фото товара из фида (приоритет над иконкой-заглушкой) */
.thumb img { width: 100%; height: 100%; object-fit: contain; padding: 8px; background: #fff; }
.m-th img { width: 100%; height: 100%; object-fit: contain; border-radius: var(--radius-sm); background: #fff; }
.deal-detail-thumb img { width: 100%; height: 100%; object-fit: contain; background: #fff; }

/* --- Р-15: страницы товара/магазина/истории/промокодов/алертов --- */
.crumbs { font-size: 13px; color: var(--ink-400); margin: 16px 0 0; }
.crumbs a:hover { color: var(--deal-700); }
.panel { background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius-lg); padding: 18px; margin-bottom: 16px; }
.panel h2, .panel h3 { margin: 0 0 10px; color: var(--ink-900); font-size: 15px; }
.cols { display: grid; grid-template-columns: 1.3fr .7fr; gap: 20px; padding-top: 12px; }
@media (max-width: 980px) { .cols { grid-template-columns: 1fr; } }
.kpi { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 16px; }
.kpi div { background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius-md); padding: 12px 14px; }
.kpi b { display: block; font-size: 22px; color: var(--ink-900); letter-spacing: -.02em; margin-bottom: 2px; font-variant-numeric: tabular-nums; }
.kpi span { font-size: 12px; color: var(--ink-400); }
table.data { width: 100%; border-collapse: collapse; font-size: 13.5px; }
table.data th { text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-400); font-weight: 600; padding: 8px 10px; border-bottom: 1px solid var(--line); }
table.data td { padding: 10px; border-bottom: 1px solid var(--line); }
table.data tr:last-child td { border-bottom: 0; }
.tag { font-size: 11.5px; padding: 3px 8px; border-radius: 999px; background: var(--sunken); color: var(--ink-500); font-weight: 600; white-space: nowrap; }
.tag.r { background: var(--hot-050); color: var(--danger); }
.tag.g { background: var(--deal-050); color: var(--deal-700); }
.period-chips { display: flex; gap: 6px; margin-top: 12px; }
.shop-hero { display: flex; gap: 20px; align-items: center; flex-wrap: wrap; }
.shop-badge { width: 74px; height: 74px; border-radius: var(--radius-md); background: var(--sunken); display: grid; place-items: center; font-weight: 800; color: var(--ink-500); font-size: 22px; }
.h1-page { font-size: 28px; font-weight: 800; letter-spacing: -.02em; color: var(--ink-900); margin: 12px 0 4px; }
.sub-lede { color: var(--ink-500); margin: 0 0 16px; font-size: 14px; }
.alert-form { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }
.alert-form input { flex: 1; min-width: 240px; height: 40px; border: 1px solid var(--line-strong); border-radius: var(--radius-md); padding: 0 12px; font: inherit; background: var(--paper); color: var(--ink-900); }
.flash-note { background: var(--deal-050); border: 1px solid var(--deal-100); color: var(--deal-700); padding: 11px 13px; border-radius: var(--radius-md); font-size: 13px; margin-bottom: 14px; }
