/* ==========================================================================
   Мобиус — страница каталога: шапка раздела, сетка карточек, пагинация.
   Токены и общие компоненты (.btn, .badge, .container) живут в css/style.css.
   ========================================================================== */

/* --- Шапка раздела -------------------------------------------------------- */
.catalog-intro {
  background:
    radial-gradient(900px 420px at 12% -20%, #e7e9ff 0%, rgba(231, 233, 255, 0) 62%),
    linear-gradient(180deg, var(--color-surface) 0%, var(--color-bg) 100%);
  padding-block: var(--space-xl) var(--space-lg);
}
.catalog-intro__eyebrow { margin-bottom: var(--space-sm); }
.catalog-intro p {
  margin-top: var(--space-md);
  max-width: 60ch;
  color: var(--color-muted);
  font-size: 1.08rem;
}
.catalog-intro__facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-lg);
  margin-top: var(--space-lg);
  padding-top: var(--space-md);
  border-top: 1px solid var(--color-border);
}
.catalog-intro__fact strong { display: block; font-size: 1.35rem; letter-spacing: -0.02em; }
.catalog-intro__fact span { color: var(--color-muted); font-size: var(--fs-small); }

/* --- Что работает только со скриптами ------------------------------------- */
/* Без JS сетка показывает весь ассортимент разом: переключать страницы нечего,
   а отбор по бренду идёт на клиенте — статическая страница вернула бы на
   catalog.html?brand=vertex тот же полный список. Поэтому и пагинация, и панель
   фильтров появляются только при включённых скриптах (класс has-js ставит
   js/config.js в <head>, до первой отрисовки). */
[data-js-only] { display: none; }
.has-js [data-js-only] { display: block; }

/* --- Фильтры -------------------------------------------------------------- */
.has-js .catalog-filters {
  display: block;
  margin-bottom: var(--space-md);
  padding: var(--space-md);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}
.catalog-filters__set {
  display: grid;
  gap: var(--space-xs);
  border: 0;
  padding: 0;
  margin: 0;
  min-width: 0;
}
.catalog-filters__legend {
  padding: 0;
  font-weight: 600;
  font-size: var(--fs-small);
  color: var(--color-muted);
}
.catalog-filters__options {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
}
.catalog-filters__note { color: var(--color-muted); font-size: var(--fs-small); }

/* Пилюля фильтра: обычный input в оболочке. У бренда внутри радиокнопка (бренд
   один), у памяти — галочка (объёмов можно отметить несколько). Ни точку, ни
   галочку не прячем: по ним видно выбранное состояние и без поддержки :has(),
   а форма самого элемента сразу говорит, один вариант выбирают или несколько. */
.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  min-height: 2.75rem;
  padding: 0.35rem 0.85rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  cursor: pointer;
  transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
}
.filter-chip:hover { border-color: var(--color-primary); background: var(--color-primary-soft); }
.filter-chip__input { accent-color: var(--color-primary); margin: 0; cursor: inherit; }
.filter-chip__text { font-weight: 600; font-size: var(--fs-small); }
.filter-chip__count {
  min-width: 1.5rem;
  padding: 0.05rem 0.4rem;
  border-radius: var(--radius-pill);
  background: var(--color-surface-2);
  color: var(--color-muted);
  font-size: 0.8rem;
  text-align: center;
}

/* Выбранный вариант (бренд или объём памяти). Класс ставит отрисовка, :has() —
   подстраховка на случай, когда состояние сменил браузер (кнопка «назад»,
   восстановление формы). */
.filter-chip--on,
.filter-chip:has(.filter-chip__input:checked) {
  border-color: var(--color-primary);
  background: var(--color-primary-soft);
  color: var(--color-primary-dark);
}
.filter-chip--on .filter-chip__count,
.filter-chip:has(.filter-chip__input:checked) .filter-chip__count {
  background: var(--color-primary);
  color: #fff;
}

/* Бренда (или объёма памяти) нет в текущей выдаче — нажимать не на что. */
.filter-chip--empty { opacity: 0.55; cursor: not-allowed; }
.filter-chip--empty:hover { border-color: var(--color-border); background: var(--color-bg); }

/* Обводка фокуса живёт на пилюле, а не на самой точке: так видно, какой бренд
   выбран с клавиатуры. */
.filter-chip:focus-within {
  outline: 3px solid var(--color-primary);
  outline-offset: 2px;
}

/* --- Поиск по каталогу ----------------------------------------------------- */
/* Одно поле во всю ширину панели: поиск отвечает на другой вопрос, чем пилюли
   («какая модель», а не «какие из этих»), и стоит над ними, а не в их ряду.
   Рамка живёт на оболочке, а не на самом input: внутри неё, кроме текста,
   лежат значок и крестик «очистить», и подсвечиваться при фокусе должно всё
   поле целиком. */
.search-filter__field {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding-inline: 0.75rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  transition: border-color 0.15s ease;
}
.search-filter__field:hover { border-color: var(--color-primary); }
.search-filter__field:focus-within {
  border-color: var(--color-primary);
  outline: 3px solid var(--color-primary);
  outline-offset: 2px;
}
.search-filter__icon { font-size: 1rem; line-height: 1; opacity: 0.65; }
.search-filter__input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 2.75rem;
  border: 0;
  padding: 0;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: var(--fs-small);
}
/* Обводка уже нарисована на оболочке — вторая, вокруг самого текста, только
   двоилась бы. */
.search-filter__input:focus { outline: none; }
.search-filter__input::placeholder { color: var(--color-muted); }
/* Родной крестик WebKit убираем: у поля есть свой — одинаковый во всех
   браузерах и с подписью для скринридера. */
.search-filter__input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }

.search-filter__clear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  width: 1.9rem;
  height: 1.9rem;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--color-surface-2);
  color: var(--color-muted);
  font-size: 0.85rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}
.search-filter__clear:hover { background: var(--color-primary-soft); color: var(--color-primary-dark); }
/* Пока поле пустое, очищать нечего: кнопку прячет отрисовка атрибутом hidden,
   а display: inline-flex выше его бы перебил. */
.search-filter__clear[hidden] { display: none; }

.search-filter__hint { color: var(--color-muted); font-size: var(--fs-small); }

/* --- Раскладка панели фильтров -------------------------------------------- */
/* В панели поиск и три фильтра (бренд, память, цена). Поиск стоит сверху и
   виден всегда: он относится ко всей панели, а не к её левой половине, и по
   названию модель ищут чаще, чем перебирают объёмы памяти. Три фильтра под ним
   на телефоне свёрнуты под кнопку «Фильтры»: развёрнутыми они занимают там
   весь экран, и до первой карточки пришлось бы листать мимо всего отбора.
   С 48rem фильтры раскрыты всегда, а кнопка исчезает: место есть, и прятать
   отбор незачем — списки пилюль встают в левую колонку одна под другой,
   а цена в правую во всю высоту: ползунку нужна постоянная ширина, иначе шаг
   в тысячу рублей приходится на разное число пикселей. */
.catalog-filters > * + * { margin-top: var(--space-md); }
.catalog-filters__body {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-md);
}

/* Кнопка «Фильтры» с числом выбранного. Видимостью развёрнутой панели заведует
   класс is-open, а не атрибут hidden: на широком экране панель открыта всегда,
   и hidden пришлось бы снимать при каждой смене ширины окна. */
.catalog-filters__bar { display: flex; }
.catalog-filters__toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  min-height: 2.75rem;
  padding: 0.5rem 0.9rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-pill);
  font-weight: 600;
  cursor: pointer;
}
.catalog-filters__toggle:hover { border-color: var(--color-primary); background: var(--color-primary-soft); }
.catalog-filters__toggle-text { flex: 1 1 auto; text-align: left; }
/* Стрелка справа поворачивается вслед за состоянием кнопки: по ней видно,
   развернётся панель или свернётся. */
.catalog-filters__toggle::after {
  content: "▾";
  flex: 0 0 auto;
  line-height: 1;
  transition: transform 0.15s ease;
}
.catalog-filters__toggle[aria-expanded="true"]::after { transform: rotate(180deg); }
.catalog-filters__badge {
  min-width: 1.5rem;
  padding: 0.05rem 0.4rem;
  border-radius: var(--radius-pill);
  background: var(--color-primary);
  color: #fff;
  font-size: 0.8rem;
  text-align: center;
}

@media (max-width: 47.99rem) {
  /* Свёрнуто по умолчанию: до первой карточки — поиск и одна кнопка, а не три
     экрана отбора. */
  .catalog-filters__body { display: none; }
  .catalog-filters__body.is-open { display: grid; }
}

@media (min-width: 48rem) {
  .catalog-filters__bar { display: none; }
  .catalog-filters__body {
    grid-template-columns: minmax(0, 1fr) minmax(18rem, 22rem);
    align-items: start;
    column-gap: var(--space-lg);
  }
  .catalog-filters__set { grid-column: 1; }
  .catalog-filters__body .price-filter { grid-column: 2; grid-row: 1 / span 2; }
  .catalog-filters__note { grid-column: 1 / -1; }
}

/* --- Выбранные фильтры ---------------------------------------------------- */
/* Полоса между панелью и сеткой: чем именно сужена выдача прямо сейчас. Чипы
   здесь намеренно не похожи на пилюли панели — это не «выбрать», а «убрать»,
   и одинаковый вид у двух разных действий сбивал бы с толку. Область собирает
   js/catalog-page.js и прячет её целиком, пока ничего не выбрано. */
.active-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-xs);
  margin-bottom: var(--space-md);
}
.active-filters__title {
  color: var(--color-muted);
  font-size: var(--fs-small);
  font-weight: 600;
}
.active-filters__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  min-width: 0;
}
.active-filter {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  min-height: 2.5rem;
  padding: 0.3rem 0.8rem;
  background: var(--color-primary-soft);
  border: 1px solid var(--color-primary);
  border-radius: var(--radius-pill);
  color: var(--color-primary-dark);
  font-size: var(--fs-small);
  font-weight: 600;
  transition: background 0.15s ease, color 0.15s ease;
}
.active-filter:hover {
  background: var(--color-primary);
  color: #fff;
  text-decoration: none;
}
/* Название фильтра — служебная часть подписи, значение важнее: «Бренд:»
   набираем тише, чтобы взгляд цеплялся за «Aurora». */
.active-filter__name { font-weight: 500; opacity: 0.75; }
.active-filter__remove { font-size: 1rem; line-height: 1; }

/* «Сбросить всё» прижимаем к правому краю полосы: это не ещё один чип, а
   выход из всего отбора сразу. На узком экране строка переносится, и кнопка
   встаёт под чипами. */
.active-filters__clear { margin-left: auto; }

/* --- Фильтр по цене ------------------------------------------------------- */

.price-filter__body {
  display: grid;
  gap: var(--space-xs);
  /* Проценты закрашенной части дорожки ставит js/catalog-page.js по
     применённому диапазону: считать её ширину скриптом — значит описывать
     раскладку второй раз. */
  --price-from: 0%;
  --price-to: 100%;
}
.price-filter__fields { display: flex; flex-wrap: wrap; gap: var(--space-xs); }
.price-filter__field {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  flex: 1 1 8rem;
  min-height: 2.75rem;
  padding: 0.25rem 0.7rem;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.price-filter__field:focus-within { outline: 3px solid var(--color-primary); outline-offset: 2px; }
.price-filter__field-label { color: var(--color-muted); font-size: var(--fs-small); }
.price-filter__unit { color: var(--color-muted); font-size: var(--fs-small); }
.price-filter__input {
  width: 100%;
  min-width: 4ch;
  border: 0;
  background: transparent;
  font-weight: 600;
  font-size: var(--fs-small);
  /* Стрелки счётчика съедают половину узкого поля, а шаг в тысячу рублей
     удобнее задавать ползунком. */
  appearance: textfield;
  -moz-appearance: textfield;
}
.price-filter__input::-webkit-outer-spin-button,
.price-filter__input::-webkit-inner-spin-button { appearance: none; margin: 0; }
.price-filter__input:focus { outline: none; }

/* Два ползунка лежат друг на друге: вместе они дают диапазон с двумя
   бегунками, которого у обычного input[type=range] нет. Дорожку рисует
   контейнер, поэтому сами ползунки прозрачны и ловят события только
   бегунками — иначе верхний перекрывал бы нижний целиком. */
.price-filter__slider {
  position: relative;
  height: 2.25rem;
  margin-top: var(--space-xs);
}
.price-filter__slider::before,
.price-filter__slider::after {
  content: "";
  position: absolute;
  top: 50%;
  height: 0.35rem;
  border-radius: var(--radius-pill);
  transform: translateY(-50%);
  pointer-events: none;
}
.price-filter__slider::before { left: 0; right: 0; background: var(--color-surface-2); }
.price-filter__slider::after {
  left: var(--price-from);
  right: calc(100% - var(--price-to));
  background: var(--color-primary);
}
.price-filter__range {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  background: transparent;
  appearance: none;
  -webkit-appearance: none;
  pointer-events: none;
}
.price-filter__range::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 1.4rem;
  height: 1.4rem;
  border-radius: 50%;
  border: 2px solid var(--color-primary);
  background: var(--color-bg);
  box-shadow: var(--shadow-sm);
  cursor: grab;
  pointer-events: auto;
}
.price-filter__range::-moz-range-thumb {
  width: 1.4rem;
  height: 1.4rem;
  border-radius: 50%;
  border: 2px solid var(--color-primary);
  background: var(--color-bg);
  box-shadow: var(--shadow-sm);
  cursor: grab;
  pointer-events: auto;
}
.price-filter__range::-moz-range-track { background: transparent; }
.price-filter__range:focus-visible::-webkit-slider-thumb { outline: 3px solid var(--color-primary); outline-offset: 2px; }
.price-filter__range:focus-visible::-moz-range-thumb { outline: 3px solid var(--color-primary); outline-offset: 2px; }

.price-filter__summary { color: var(--color-muted); font-size: var(--fs-small); }

/* Поля ввода на сенсорном экране набираем от 16 пикселей. Меньший кегль Safari
   на iOS считает поводом приблизить страницу: по касанию к поиску или к цене
   каталог уезжает в увеличение, из которого сам уже не возвращается, — и
   дальше покупатель ведёт отбор, разглядывая сетку через лупу и таская её
   вбок. Условие про указатель, а не про ширину окна: планшет в альбомной
   ориентации шире 48rem, а приближает так же. Правило стоит после обоих полей
   — вес у селекторов одинаковый, и раньше по файлу его перебила бы цена. */
@media (pointer: coarse) {
  .search-filter__input,
  .price-filter__input { font-size: 1rem; }
}

/* --- Панель над сеткой ---------------------------------------------------- */
.catalog__toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-sm);
  padding-bottom: var(--space-md);
  border-bottom: 1px solid var(--color-border);
}
.catalog__status { color: var(--color-muted); font-size: var(--fs-small); }

/* --- Сетка карточек ------------------------------------------------------- */
/* Ступени задают не число колонок, а наименьшую ширину плитки, при которой
   фотография остаётся узнаваемой, а цена и наличие читаются: на телефоне
   плитка одна в ряд во всю ширину, дальше их столько, сколько влезает по
   этой мерке — две на крупном телефоне, три-четыре на десктопе. Считает это
   auto-fill: между ступенями карточки растягиваются сами, и пустых полос по
   краям ряда не остаётся. Ниже 30rem колонка одна: две плитки по 10rem — это
   уже не витрина, а список иконок. */
.catalog__grid {
  display: grid;
  gap: var(--space-md);
  margin-top: var(--space-lg);
  grid-template-columns: 1fr;
}
@media (min-width: 30rem) {
  .catalog__grid {
    grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
    gap: var(--space-sm);
  }
}
@media (min-width: 48rem) {
  .catalog__grid {
    grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
    gap: var(--space-md);
  }
}
@media (min-width: 64rem) {
  .catalog__grid { grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); }
}

/* --- Карточка товара ------------------------------------------------------ */
.product-card { display: flex; }
.product-card__inner {
  display: flex;
  flex-direction: column;
  width: 100%;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, transform 0.15s ease;
}
.product-card__inner:hover {
  border-color: var(--color-primary);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}

.product-card__media {
  position: relative;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
}
/* Фотография ещё едет (US-009): её место мерцает, а не стоит пустой рамкой.
   Метку снимает js/catalog-page.js по событию load — и по error тоже, потому
   что вместо непришедшего снимка встаёт готовая заглушка. */
.product-card__media--loading {
  background: var(--color-surface-2);
  overflow: hidden;
}
.product-card__media--loading::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.7) 50%,
    rgba(255, 255, 255, 0) 100%);
  animation: skeleton-sweep 1.4s ease-in-out infinite;
  pointer-events: none;
}
@media (prefers-reduced-motion: reduce) {
  .product-card__media--loading::after { animation: none; content: none; }
}
.product-card__image {
  width: 100%;
  /* Соотношение задано и в разметке (width/height), и здесь: место под фото
     занято до загрузки, поэтому сетка не прыгает. */
  aspect-ratio: 1 / 1;
  object-fit: cover;
}
.product-card__discount {
  position: absolute;
  top: var(--space-xs);
  right: var(--space-xs);
  background: var(--color-primary);
  color: #fff;
}

.product-card__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-xs);
  padding: var(--space-md);
  flex: 1 1 auto;
}
.product-card__brand {
  font-size: var(--fs-small);
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-muted);
}
.product-card__title { font-size: 1.05rem; line-height: 1.35; }
.product-card__link { color: var(--color-text); }
.product-card__link:hover { color: var(--color-primary); text-decoration: none; }

.product-card__rating {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  font-size: var(--fs-small);
  color: var(--color-muted);
}
.product-card__rating-value { font-weight: 700; color: var(--color-text); }
.product-card__star { color: var(--color-star); }

.product-card__summary { font-size: var(--fs-small); color: var(--color-muted); }

/* Ключевые характеристики (US-002): экран, ОЗУ, память, камера.
   Ровно две колонки, а не auto-fit: строки соседних плиток должны стоять на
   одних и тех же местах — тогда сетку каталога можно сравнивать взглядом, не
   вчитываясь. Набор бывает и неполным (у записи нет строки про ОЗУ) — блок
   от этого не ломается, просто становится короче. */
.product-card__specs {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 0.4rem 0.5rem;
  margin-top: 0.15rem;
  padding: var(--space-xs);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
}
.product-card__spec {
  /* minmax(0, 1fr) сверху + min-width здесь: без них длинное значение
     растягивает колонку и обрезание многоточием не срабатывает. */
  min-width: 0;
  padding-left: 0.5rem;
  border-left: 2px solid var(--color-primary);
}
.product-card__spec-label {
  font-size: 0.7rem;
  line-height: 1.3;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-muted);
}
.product-card__spec-value {
  font-size: var(--fs-small);
  font-weight: 600;
  line-height: 1.35;
  color: var(--color-text);
  /* Значение приходит уже сокращённым, но на узком экране «6,7″ AMOLED» всё
     же может не поместиться. Обрезаем многоточием, а не переносим: перенос
     сделал бы соседние плитки в ряду разной высоты. Полная формулировка
     остаётся в подсказке (title) и в таблице на карточке товара. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.product-card__price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: auto;
  padding-top: var(--space-xs);
}
.product-card__now { font-size: 1.3rem; font-weight: 700; letter-spacing: -0.02em; }
.product-card__old {
  font-size: var(--fs-small);
  color: var(--color-muted);
  text-decoration: line-through;
}

.product-card__stock { display: flex; }
.product-card__note { font-size: var(--fs-small); color: var(--color-muted); }

/* Действия плитки: «Подробнее» ведёт на страницу товара, «Быстрый просмотр»
   открывает окно с полными характеристиками, не уводя из сетки (US-003).
   Второй кнопки нет в статической разметке — она появляется только вместе со
   скриптами, которые её и обслуживают. */
.product-card__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-xs);
  margin-top: var(--space-xs);
}
.product-card__actions .btn { flex: 1 1 auto; }

/* На узком экране плитка идёт во всю ширину, и внутренние поля можно ужать:
   лишние полсантиметра по краям — это ещё одна строка характеристик, видная
   без прокрутки. Сама плитка при этом не меняет строения: сравнивать модели
   взглядом покупатель должен одинаково на любом экране. */
@media (max-width: 47.99rem) {
  .product-card__body { padding: var(--space-sm); gap: 0.4rem; }
  .product-card__now { font-size: 1.2rem; }
}

/* Плитка, на которую фокус поставили скриптом (первая из дописанных кнопкой
   «Показать ещё»): обводка обходит её целиком со скруглением карточки, а не
   прямоугольником общего правила — иначе она смотрится обрезком. */
.product-card[tabindex]:focus-visible {
  outline: 3px solid var(--color-primary);
  outline-offset: 3px;
  border-radius: var(--radius-lg);
}

/* --- Плитка-заглушка на время ожидания (US-009) --------------------------- */
/* Скелет повторяет строение карточки: квадрат под фото и шесть строк текста
   в тех же местах и тех же размеров. Поэтому, когда приедут настоящие товары,
   сетка не подпрыгнет и не сдвинет то, во что покупатель уже целился.
   Наводить на заглушку нечего — подсветку и подъём карточки отключаем. */
.product-card--skeleton .product-card__inner:hover {
  border-color: var(--color-border);
  box-shadow: var(--shadow-sm);
  transform: none;
}
.product-card--skeleton .product-card__media {
  /* Место под фото занимает столько же, сколько заняла бы фотография: её
     соотношение сторон живёт на .product-card__image, которого в заглушке нет. */
  aspect-ratio: 1 / 1;
  background: var(--color-surface-2);
  border-radius: 0;
}
.product-card--skeleton .product-card__body {
  gap: var(--space-sm);
  min-height: 15rem;
}

/* --- Пустая выдача -------------------------------------------------------- */
/* Сам блок — общий компонент пустого состояния (.empty-state в css/style.css):
   тем же оформлены «товар не найден» и «отзывов пока нет» на карточке товара.
   Здесь остаётся только его место в потоке страницы. Выходов из пустой выдачи
   до трёх — снять один фильтр, сбросить все, позвонить; на узком экране кнопки
   встают одна под другой, на широком — рядом (.empty-state__actions). */
.catalog__empty { margin-top: var(--space-lg); }

/* --- «Показать ещё» ------------------------------------------------------- */
/* Продолжение списка вниз (US-010). Кнопка во всю ширину колонки: на телефоне
   в неё попадают большим пальцем не целясь, а на широком экране она всё равно
   ограничена шириной сетки. Под ней — «Показано 6 из 9»: без этой строки
   непонятно, сколько ещё нажимать и не проще ли сузить фильтры. */
/* Селектор с .has-js — не украшение: правило [data-js-only] выше делает
   помеченные блоки display: block, и без той же силы кнопка съехала бы к
   левому краю. */
.has-js .catalog-more {
  display: grid;
  justify-items: center;
  gap: var(--space-xs);
  margin-top: var(--space-lg);
}
.catalog-more__button { width: min(100%, 22rem); }
.catalog-more__counter { color: var(--color-muted); font-size: var(--fs-small); }

/* Пагинация каталога — общий компонент, он же под историей заказов.
   Живёт в css/style.css рядом с остальными общими элементами. */

/* --- Полоса доверия под каталогом ---------------------------------------- */
.catalog-trust {
  display: grid;
  gap: var(--space-md);
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  margin-top: var(--space-lg);
}
.catalog-trust__item {
  padding: var(--space-md);
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.catalog-trust__item h3 { margin-bottom: 0.35rem; }
.catalog-trust__item p { color: var(--color-muted); font-size: var(--fs-small); }
.catalog-trust__icon {
  width: 2.6rem;
  height: 2.6rem;
  display: grid;
  place-items: center;
  border-radius: var(--radius-sm);
  background: var(--color-primary-soft);
  color: var(--color-primary);
  font-size: 1.25rem;
  margin-bottom: var(--space-sm);
}
