/*
 * Оформление сайта.
 *
 * Все значения приходят из web/tokens.css: цвета, размеры, отступы, скругления,
 * тени и длительности. В этом файле нет ни палитры, ни своей шкалы — только
 * правила для классов. Если понадобилось новое значение, оно заводится в
 * tokens.css и описывается в docs/18-дизайн-сайта.md, а не пишется здесь числом.
 *
 * Редкие исключения помечены комментарием на месте: это либо геометрия, которой
 * в системе нет (толщина линии SVG, радиус размытия), либо порядок наложения.
 *
 * Правила проекта, которые этот файл соблюдает:
 *  - ни одной внешней загрузки: шрифт системный, значки нарисованы символами
 *    и разметкой, картинок нет. Сайт открывается без интернета;
 *  - тёмная тема — это другие значения переменных, а не второй набор правил;
 *  - честные пометки (демонстрационные данные, «≈», отсутствие планировок,
 *    «подтверждайте у застройщика») набраны не мельче подписи и не приглушены
 *    ниже 4,5:1. Прятать их оформлением нельзя.
 *
 * Переменные подключены и отсюда. Страницы тоже ставят <link> на tokens.css
 * первым — так предписывает docs/18-дизайн-сайта.md, — но правила без значений
 * бессмысленны, и файл стилей не должен зависеть от того, не забыли ли про
 * ссылку в очередной новой странице. Файл лежит рядом, внешней загрузки нет,
 * повторная — из кеша браузера.
 */
@import url("tokens.css");

/* ============================================================================
   ПРОИЗВОДНЫЕ ЗНАЧЕНИЯ
   Не новые значения системы, а то, что из неё вычислено: высота органа
   управления и высота шапки. Оба нужны в нескольких местах сразу.
   ============================================================================ */

:root {
  /* Кнопка, поле ввода, список, кнопка страницы — 40 px: 24 + 16 по ритму.
     Это же нижняя граница зоны нажатия на телефоне. */
  --control-h: calc(var(--sp-5) + var(--sp-4));
  /* Высота липкой шапки: строка названия (--sp-5) плюс поля по --sp-3. */
  --h-header: calc(var(--sp-5) + var(--sp-3) * 2);
}

/* На телефоне поля страницы уже: 20 px вместо 40. Меняется одно значение —
   его берут и шапка, и содержимое, и подвал. */
@media (max-width: 700px) {
  :root { --gutter: var(--gutter-mobile); }
}

/* ============================================================================
   ОСНОВА
   ============================================================================ */

*,
*::before,
*::after { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--c-bg);
  color: var(--c-text);
  font-family: var(--font-ui);
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  letter-spacing: var(--ls-body);
  /* Системный шрифт в крупных заголовках без сглаживания выглядит жирнее,
     чем задумано шкалой. */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* Скрытие атрибутом важнее раскладки: панель фильтров и ряд пилюль прячет
   разметка, а у них здесь задан display. */
[hidden] { display: none !important; }

h1, h2, h3, h4 {
  margin: 0 0 var(--sp-3);
  font-weight: var(--fw-semibold);
  color: var(--c-text);
}

h1 {
  font-size: var(--fs-hero);
  line-height: var(--lh-hero);
  letter-spacing: var(--ls-hero);
}

h2 {
  font-size: var(--fs-title-2);
  line-height: var(--lh-title-2);
  letter-spacing: var(--ls-title-2);
}

h3, h4 {
  font-size: var(--fs-title-3);
  line-height: var(--lh-title-3);
  letter-spacing: var(--ls-title-3);
}

p { margin: 0 0 var(--sp-3); }
p:last-child { margin-bottom: 0; }

/* Связный текст не растягиваем на всю ширину: строка длиннее --measure
   теряется при переходе на следующую. */
main p,
main li { max-width: var(--measure); }

a {
  color: var(--c-accent);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.18em;
  transition: color var(--dur-hover) var(--ease-in-out);
}
a:hover { color: var(--c-accent-hover); }
a:active { color: var(--c-accent-active); }

b, strong { font-weight: var(--fw-semibold); }

code {
  font-family: var(--font-ui);
  background: var(--c-fill);
  border-radius: var(--r-sm);
  padding: 0 var(--sp-1);
}

/* Клавиатурный фокус виден на любой подложке и в обеих темах. */
:focus-visible {
  outline: var(--focus-width) solid var(--c-focus);
  outline-offset: var(--focus-offset);
}
/* Мышью след фокуса не показываем, но и не убираем у тех, кто пришёл с Tab. */
:focus:not(:focus-visible) { outline: none; }

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

/* ============================================================================
   ШАПКА, ПОДВАЛ, ОБЩИЕ ОБОЛОЧКИ
   ============================================================================ */

.site-header {
  position: sticky;
  top: 0;
  /* Числа порядка наложения — не значения оформления: шапка выше липкой
     панели фильтров, панель выше содержимого. */
  z-index: 20;
  background: var(--c-surface);
  border-bottom: 1px solid var(--c-separator);
}

/* Подложка шапки размывается тем, что под ней проезжает. Полупрозрачность
   собирается из того же токена поверхности: отдельного «полупрозрачного»
   цвета в системе нет и заводить его ради одного места не нужно.
   Радиус размытия — величина эффекта, а не ступень шкалы. */
@supports ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-header {
    background: color-mix(in srgb, var(--c-surface) 72%, transparent);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
    backdrop-filter: saturate(180%) blur(20px);
  }
}

.site-header__inner {
  max-width: var(--w-header);
  margin: 0 auto;
  padding: var(--sp-3) var(--gutter);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-5);
}

.site-title {
  margin: 0;
  font-size: var(--fs-body-lg);
  line-height: var(--lh-title-3);
  letter-spacing: var(--ls-title-3);
  font-weight: var(--fw-bold);
  white-space: nowrap;
}
.site-title a {
  color: var(--c-text);
  text-decoration: none;
}
.site-title a:hover { color: var(--c-text); }

/* Сводные числа проекта — приглушённо: это справка, а не заголовок. */
.site-meta {
  margin-left: auto;
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1) var(--sp-4);
  color: var(--c-text-tertiary);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  font-variant-numeric: var(--numeric-tabular);
}
.site-meta b {
  font-weight: var(--fw-medium);
  color: var(--c-text-secondary);
}

/*
 * Кнопка города рядом с названием сайта (docs/20 §7.1). Это тихая кнопка
 * (.btn--link), но набрана кеглем основного текста: город — главный выбор
 * страницы, а не сноска. Подчёркивания нет — действие выдаёт акцентный цвет
 * и знак «▾», как у выпадающего списка.
 */
.site-city {
  position: relative;
}
/* Через .site-city: правила .btn--link ниже по файлу иначе перебили бы кегль. */
.site-city .site-city__button {
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  text-decoration: none;
}
.site-city .site-city__button:hover { text-decoration: underline; }

/* Список городов всплывает над страницей: приподнятая поверхность и глубокая тень. */
.site-city__list {
  position: absolute;
  top: calc(100% + var(--sp-1));
  left: 0;
  /* Порядок наложения, а не значение оформления: над липкой панелью фильтров. */
  z-index: 30;
  /* 320 px — ширина телефона без полей: «Санкт-Петербург» и «пока нет квартир»
     встают в одну строку и не переносятся по слогам. */
  width: calc(var(--sp-10) * 2 + var(--sp-8));
  max-width: calc(100vw - var(--gutter) * 2);
  padding: var(--sp-2);
  background: var(--c-surface-raised);
  border-radius: var(--r-md);
  box-shadow: var(--sh-3);
}
.site-city__items {
  list-style: none;
  margin: 0;
  padding: 0;
}
.site-city__item {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 0 var(--sp-4);
  align-items: baseline;
  min-height: var(--control-h);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  color: var(--c-text);
  text-decoration: none;
  transition: background var(--dur-hover) var(--ease-in-out);
}
.site-city__item:hover { background: var(--c-fill); }
.site-city__item[aria-current="true"] { background: var(--c-accent-soft); color: var(--c-accent); }
.site-city__name { font-weight: var(--fw-medium); }
/* «с Ленинградской областью» — второй строкой под названием. */
.site-city__note {
  grid-column: 1;
  grid-row: 2;
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: var(--c-text-secondary);
}
.site-city__count {
  grid-column: 2;
  grid-row: 1;
  font-size: var(--fs-caption);
  color: var(--c-text-secondary);
  font-variant-numeric: var(--numeric-tabular);
  white-space: nowrap;
}
/* Город без квартир — внизу списка, приглушён цветом, но не ниже 4,5:1. */
.site-city__item--empty .site-city__name { color: var(--c-text-secondary); }
.site-city__item--empty .site-city__count { color: var(--c-text-tertiary); }

.page {
  max-width: var(--w-content);
  margin: 0 auto;
  padding: var(--sp-7) var(--gutter) var(--sp-8);
}

/* Чем крупнее текст, тем больше вокруг него пустоты. */
.page > h1 { margin-bottom: var(--stack-loose); }

.breadcrumbs {
  margin: 0 0 var(--sp-5);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: var(--c-text-tertiary);
}
.breadcrumbs a { text-decoration: none; }
.breadcrumbs a:hover { text-decoration: underline; }

.site-footer {
  margin-top: var(--sp-8);
  border-top: 1px solid var(--c-separator);
}
.site-footer .page {
  max-width: var(--w-header);
  padding-top: var(--sp-5);
  padding-bottom: var(--sp-7);
}
.site-footer .footnote > div + div { margin-top: var(--sp-1); }

/* ============================================================================
   РАСКЛАДКА ПОИСКА
   ============================================================================ */

.layout {
  display: grid;
  /* Боковая колонка фиксирована (280 px по ритму), выдача забирает остальное. */
  grid-template-columns: calc(var(--sp-10) * 2 + var(--sp-5)) minmax(0, 1fr);
  gap: var(--stack-loose);
  align-items: start;
}

/* ============================================================================
   ПАНЕЛЬ ФИЛЬТРОВ
   Спокойная боковая колонка: ни рамки, ни своей поверхности — только тонкие
   разделители между группами. Панель не должна спорить с выдачей.
   ============================================================================ */

.filters {
  position: sticky;
  top: calc(var(--h-header) + var(--sp-4));
  max-height: calc(100vh - var(--h-header) - var(--sp-6));
  overflow-y: auto;
  /* Полоса прокрутки не должна прижимать подписи к разделителю, а сама она
     тонкая и серая: в спокойной колонке ей незачем быть заметной. */
  padding-right: var(--sp-2);
  margin-right: calc(var(--sp-2) * -1);
  scrollbar-width: thin;
  scrollbar-color: var(--c-separator-strong) transparent;
}

.filters__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  padding-bottom: var(--sp-3);
  border-bottom: 1px solid var(--c-separator);
}
.filters__head h2 {
  margin: 0;
  font-size: var(--fs-title-3);
  line-height: var(--lh-title-3);
  letter-spacing: var(--ls-title-3);
}

.fgroup { border-bottom: 1px solid var(--c-separator); }

.fgroup > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
  padding: var(--sp-3) 0;
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  color: var(--c-text);
  transition: color var(--dur-hover) var(--ease-in-out);
}
.fgroup > summary::-webkit-details-marker { display: none; }
.fgroup > summary:hover { color: var(--c-accent); }

/* Знак раскрытия нарисован символом: значков-картинок на сайте нет. */
.fgroup > summary::after {
  content: "−";
  color: var(--c-text-tertiary);
  font-size: var(--fs-body-lg);
  line-height: 1;
}
.fgroup:not([open]) > summary::after { content: "+"; }

.fgroup__body {
  display: grid;
  gap: var(--sp-4);
  padding-bottom: var(--sp-5);
}

/* Раскрытие — короткое и мягкое; при «меньше движения» длительность 1 мс. */
.fgroup[open] > .fgroup__body {
  animation: fgroup-open var(--dur-expand) var(--ease-out);
}
@keyframes fgroup-open {
  from { opacity: 0; transform: translateY(calc(var(--sp-1) * -1)); }
  to { opacity: 1; transform: none; }
}

.field { display: grid; gap: var(--sp-1); }

.field > label,
.field-label {
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: var(--c-text-secondary);
}

.range {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--sp-2);
}
.range label { display: block; }

/* ---------- Поля ввода и списки ---------- */

.input,
.select,
input[type="number"],
input[type="text"],
input[type="search"],
input[type="password"],
textarea,
select {
  width: 100%;
  min-height: var(--control-h);
  font: inherit;
  font-size: var(--fs-body);
  color: var(--c-text);
  background: var(--c-surface);
  border: 1px solid var(--c-separator-strong);
  border-radius: var(--r-sm);
  padding: var(--sp-2) var(--sp-3);
  transition:
    border-color var(--dur-hover) var(--ease-in-out),
    box-shadow var(--dur-hover) var(--ease-in-out);
}

input[type="number"],
.num input { font-variant-numeric: var(--numeric-tabular); }

textarea { min-height: calc(var(--control-h) * 2); resize: vertical; }

.input:hover,
.select:hover,
input[type="number"]:hover,
input[type="text"]:hover,
input[type="search"]:hover,
input[type="password"]:hover,
select:hover { border-color: var(--c-text-tertiary); }

.input:focus,
.select:focus,
input:focus,
textarea:focus,
select:focus { border-color: var(--c-accent); }

select { cursor: pointer; }

input::placeholder,
textarea::placeholder { color: var(--c-text-tertiary); opacity: 1; }

/* ---------- Флажки ---------- */

.checks { display: grid; gap: var(--sp-1); }

.check {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding: var(--sp-1) 0;
  font-size: var(--fs-body);
  cursor: pointer;
  transition: color var(--dur-hover) var(--ease-in-out);
}
.check:hover { color: var(--c-accent); }
.check input {
  /* 18 px: 16 по ритму плюс половина шага — мельче флажок трудно попасть. */
  width: calc(var(--sp-4) + var(--sp-1) / 2);
  height: calc(var(--sp-4) + var(--sp-1) / 2);
  margin: 0;
  flex: none;
  accent-color: var(--c-accent);
}

/* Счётчик прижат вправо и приглушён: он подсказка, а не часть названия. */
.check__count {
  margin-left: auto;
  color: var(--c-text-tertiary);
  font-size: var(--fs-micro);
  line-height: var(--lh-micro);
  letter-spacing: var(--ls-micro);
  font-variant-numeric: var(--numeric-tabular);
}

/* Значение, под которое нет ни одной квартиры. Приглушаем цветом, а не
   прозрачностью: прозрачность увела бы контраст ниже 4,5:1. */
.check--empty { color: var(--c-text-tertiary); }

/* ============================================================================
   КНОПКИ — три вида, четвёртого нет
   ============================================================================ */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--control-h);
  padding: var(--sp-2) var(--sp-5);
  font: inherit;
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  color: var(--c-text);
  background: var(--c-surface);
  border: 1px solid var(--c-separator-strong);
  border-radius: var(--r-pill);
  cursor: pointer;
  transition:
    background var(--dur-hover) var(--ease-in-out),
    border-color var(--dur-hover) var(--ease-in-out),
    color var(--dur-hover) var(--ease-in-out),
    transform var(--dur-hover) var(--ease-out);
}
.btn:hover { background: var(--c-fill); }
.btn:active { transform: scale(var(--scale-press)); }
.btn:disabled {
  color: var(--c-text-tertiary);
  cursor: default;
  transform: none;
}

.btn--primary {
  background: var(--c-accent);
  border-color: transparent;
  color: var(--c-on-accent);
}
.btn--primary:hover { background: var(--c-accent-hover); }
.btn--primary:active { background: var(--c-accent-active); }

/*
 * Выключенная главная кнопка теряет и заливку, а не только насыщенность
 * текста. Общее правило .btn:disabled приглушает буквы до --c-text-tertiary, и
 * на оставшемся ярком акценте это давало 1,2:1 в тёмной теме и 1,5:1 в
 * светлой — надпись, которую физически не прочитать. Так выглядят «Показать
 * очередь» до ввода токена и «Принять» у уже поглощённого кандидата.
 * Выключенная кнопка должна выглядеть выключенной целиком.
 */
.btn--primary:disabled,
.btn--primary:disabled:hover {
  background: var(--c-surface);
  border-color: var(--c-separator-strong);
  color: var(--c-text-tertiary);
}

.btn--link {
  padding: var(--sp-1) var(--sp-2);
  border: 0;
  background: none;
  color: var(--c-accent);
  font-size: var(--fs-caption);
  text-decoration: underline;
  text-underline-offset: 0.18em;
}
.btn--link:hover {
  background: none;
  color: var(--c-accent-hover);
}

/* Кнопка раскрытия фильтров живёт только на узком экране. */
.filters-toggle { display: none; }

/* ============================================================================
   ВЫДАЧА
   ============================================================================ */

.results__bar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--sp-3) var(--sp-5);
  margin-bottom: var(--stack);
}

/* Число найденных — главное число этой страницы, потому и набрано крупно. */
.results__count {
  font-size: var(--fs-title-2);
  line-height: var(--lh-title-2);
  letter-spacing: var(--ls-title-2);
  font-weight: var(--fw-semibold);
  font-variant-numeric: var(--numeric-tabular);
}

.results__sort {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
}
.results__sort label {
  font-size: var(--fs-caption);
  color: var(--c-text-secondary);
  white-space: nowrap;
}
.results__sort select {
  width: auto;
  min-width: calc(var(--sp-10) + var(--sp-7));
}
.results__sort .per-page {
  width: auto;
  min-width: calc(var(--sp-8) + var(--sp-2));
}

/* ---------- Применённые фильтры ---------- */

.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  margin-bottom: var(--stack);
}

.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: var(--sp-1) var(--sp-1) var(--sp-1) var(--sp-3);
  background: var(--c-accent-soft);
  color: var(--c-accent);
  border-radius: var(--r-pill);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
}

.chip button,
.chip__remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--sp-5);
  height: var(--sp-5);
  padding: 0;
  border: 0;
  border-radius: var(--r-pill);
  background: none;
  color: inherit;
  font: inherit;
  line-height: 1;
  cursor: pointer;
  transition: background var(--dur-hover) var(--ease-in-out);
}
.chip button:hover,
.chip__remove:hover { background: var(--c-surface); }

/* ============================================================================
   КАРТОЧКА
   Содержимое подаётся как товар: сверху — что это (комнаты и площадь), под
   ним крупно цена, цена за метр рядом мелко, подробности ниже ровным рядом.
   ============================================================================ */

/* Три карточки в ряду — решение заказчика. Число колонок задано явно, а не
   через auto-fill: при автоподборе на широком мониторе вставал бы четвёртый
   столбец, а на среднем окне — всего два. Ступени: одна колонка на телефоне,
   две на планшете, три от 1000 пикселей — там на карточку остаётся не меньше
   220 пикселей даже с открытой панелью фильтров. */
.cards {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--sp-4);
}

@media (min-width: 700px) {
  .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (min-width: 900px) {
  .cards { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* На среднем окне (900–1199) третий столбец помещается только за счёт колонки
   фильтров: сужаем её и убираем лишний промежуток, иначе на карточку осталось
   бы около 190 пикселей и содержимое начало бы ломаться. */
@media (min-width: 900px) and (max-width: 1199px) {
  .layout {
    grid-template-columns: calc(var(--sp-10) + var(--sp-9)) minmax(0, 1fr);
    gap: var(--stack);
  }

  .cards { gap: var(--sp-3); }
  .card { padding: var(--sp-3); }
}

.card {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-4);
  background: var(--c-surface);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
  /* Рамки у карточки нет: её отделяет собственный цвет и тень. */
  transition:
    box-shadow var(--dur-hover) var(--ease-out),
    background var(--dur-hover) var(--ease-out),
    transform var(--dur-hover) var(--ease-out);
}

/* Подъём — только у карточек выдачи. Панели-карточки служебной страницы
   никуда не ведут, и двигаться им незачем. */
.cards .card:hover {
  transform: var(--lift-hover);
  box-shadow: var(--sh-2);
  /* В тёмной теме тень не читается, и приподнятость передаёт поверхность:
     --c-surface-raised там светлее обычной, а в светлой теме совпадает с ней
     и ничего не меняет. */
  background: var(--c-surface-raised);
}

/* Карточка-раскладушка (редкие операции служебной очереди): заголовок должен
   отзываться на курсор, значок раскрытия рисует сам браузер. */
details.card > summary {
  cursor: pointer;
  padding: var(--sp-1) 0;
  border-radius: var(--r-sm);
}

.card__title {
  margin: 0;
  font-size: var(--fs-title-3);
  line-height: var(--lh-title-3);
  letter-spacing: var(--ls-title-3);
  font-weight: var(--fw-semibold);
  font-variant-numeric: var(--numeric-tabular);
}
.card__title a {
  color: var(--c-text);
  text-decoration: none;
}
.card__title a:hover { color: var(--c-accent); }

.card__sub {
  margin: 0;
  max-width: none;
  color: var(--c-text-secondary);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
}

/* ---------- Цена ---------- */

.card__price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--sp-1) var(--sp-3);
}

.price {
  font-size: var(--fs-price-sm);
  line-height: var(--lh-price);
  letter-spacing: var(--ls-price);
  font-weight: var(--fw-price);
  font-variant-numeric: var(--numeric-tabular);
}

/* Цены нет — место остаётся тем же, меняется только голос. */
.price--none {
  font-size: var(--fs-body-lg);
  letter-spacing: var(--ls-body-lg);
  font-weight: var(--fw-medium);
  color: var(--c-text-secondary);
}

.price-per-m2 {
  color: var(--c-text-secondary);
  font-size: var(--fs-caption);
  font-variant-numeric: var(--numeric-tabular);
}

/* ---------- Характеристики строкой ---------- */

.card__facts {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

.fact {
  padding: var(--sp-1) var(--sp-2);
  background: var(--c-fill);
  border-radius: var(--r-sm);
  color: var(--c-text-secondary);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  white-space: nowrap;
}
.fact b {
  color: var(--c-text);
  font-weight: var(--fw-spec);
  font-variant-numeric: var(--numeric-tabular);
}

.card__foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-3);
  margin-top: auto;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--c-separator);
  color: var(--c-text-tertiary);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
}
.card__foot a { font-size: var(--fs-caption); }

/* ============================================================================
   МЕТКИ СТАТУСА
   Всегда со словом: цвет только помогает, читается слово.
   ============================================================================ */

.badge {
  display: inline-flex;
  align-items: center;
  padding: var(--sp-1) var(--sp-3);
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  font-size: var(--fs-micro);
  line-height: var(--lh-micro);
  letter-spacing: var(--ls-micro);
  font-weight: var(--fw-semibold);
  white-space: nowrap;
}

.badge--free { background: var(--c-free-soft); color: var(--c-free); }
.badge--booked { background: var(--c-booked-soft); color: var(--c-booked); }
.badge--sold,
.badge--withdrawn { background: var(--c-sold-soft); color: var(--c-sold); }
.badge--unknown,
.badge--stale { background: var(--c-unknown-soft); color: var(--c-unknown); }

/*
 * «Демонстрационные данные» — пунктирная обводка и графит, без заливки.
 * Пометка обязана быть заметной, но не должна читаться ни как статус лота,
 * ни как акция, поэтому у неё своя форма и свой цвет. Пунктирной пилюли нет
 * больше ни у одного элемента — пометка узнаётся боковым зрением.
 */
.badge--demo {
  background: transparent;
  color: var(--c-demo);
  border-color: var(--c-demo-border);
  border-style: dashed;
  font-weight: var(--fw-medium);
}

/*
 * «Апартаменты» — не статус лота и не пометка данных, а правовой статус
 * помещения: прописаться нельзя, льготная ипотека не действует. Поэтому своя
 * форма — сплошная обводка без заливки основным цветом текста. С заливками
 * статусов и с пунктиром демонстрационных данных она не путается.
 */
.badge--apartment {
  background: transparent;
  color: var(--c-text);
  border-color: var(--c-separator-strong);
}

/* ============================================================================
   МЕТРО И ПРИБЛИЗИТЕЛЬНЫЕ ЗНАЧЕНИЯ
   ============================================================================ */

/* Строка переносится: «≈ 12 мин пешком, ≈ 4 мин на машине» в узкой колонке
   иначе выезжает за карточку. */
.metro {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-1) var(--sp-2);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  color: var(--c-text-secondary);
}

/* Кружок цвета линии: сам цвет приходит данными и ставится в разметке. */
.metro__dot {
  width: var(--sp-2);
  height: var(--sp-2);
  flex: none;
  border-radius: var(--r-pill);
  background: var(--c-text-tertiary);
}

.metro__kind { color: var(--c-text-tertiary); font-size: var(--fs-micro); }
.metro__time { color: var(--c-text-secondary); }

/* Вторая строка выровнена под названием станции, а не под кружком. */
.metro__times {
  width: 100%;
  padding-left: calc(var(--sp-2) + var(--sp-2));
  color: var(--c-text-secondary);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
}

/*
 * Знак «≈» и всё, что посчитано по прямой линии. Точечное подчёркивание —
 * единственная такая форма на сайте: значение приблизительное, и это видно
 * не только из подсказки. Прятать или осветлять нельзя.
 */
.approx {
  color: var(--c-approx);
  border-bottom: 1px dotted var(--c-approx-underline);
  cursor: help;
}

/* ============================================================================
   КАРТОЧКА ЖК И КАРТОЧКА КВАРТИРЫ
   ============================================================================ */

.detail {
  display: grid;
  grid-template-columns: minmax(0, 1fr) calc(var(--sp-10) * 2 + var(--sp-8));
  gap: var(--stack-loose);
  align-items: start;
}

.panel {
  padding: var(--sp-5);
  margin-bottom: var(--stack);
  background: var(--c-surface);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
}
.panel > :last-child { margin-bottom: 0; }

.panel > h1 { margin-bottom: var(--sp-3); }
.panel > h2,
.panel__title {
  margin: 0 0 var(--stack-tight);
  font-size: var(--fs-title-2);
  line-height: var(--lh-title-2);
  letter-spacing: var(--ls-title-2);
  font-weight: var(--fw-semibold);
}

/* ---------- Главная цена ---------- */

.big-price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
  margin: var(--stack) 0 var(--sp-3);
}
.big-price .price { font-size: var(--fs-price-hero); }
.big-price .price-per-m2 { font-size: var(--fs-body); }

/* ---------- Сводка крупными числами ---------- */

.stat-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--stack) var(--stack-loose);
}
.stat { display: grid; gap: var(--sp-1); }
.stat__label {
  color: var(--c-text-secondary);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
}
.stat__value {
  font-size: var(--fs-price);
  line-height: var(--lh-price);
  letter-spacing: var(--ls-price);
  font-weight: var(--fw-price);
  font-variant-numeric: var(--numeric-tabular);
}

/* ---------- Характеристики таблицей ---------- */

.kv {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  column-gap: var(--stack);
  row-gap: 0;
  margin: 0;
}
.kv dt,
.kv dd {
  padding: var(--sp-3) 0;
  border-bottom: 1px solid var(--c-separator);
}
.kv dt {
  color: var(--c-text-secondary);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
}
.kv dd {
  margin: 0;
  font-size: var(--fs-spec);
  font-weight: var(--fw-spec);
  font-variant-numeric: var(--numeric-tabular);
  overflow-wrap: anywhere;
}
/* Первая строка не отбивается сверху, последняя не подчёркивается. */
.kv > dt:first-child,
.kv > dd:nth-child(2) { padding-top: 0; }
.kv > :nth-last-child(-n + 2) { border-bottom: 0; padding-bottom: 0; }

/* В боковой колонке двух столбцов не хватает: номер разрешения рвётся по слогам. */
.kv--narrow { grid-template-columns: minmax(0, 1fr); }
.kv--narrow dt { padding: var(--sp-3) 0 0; border-bottom: 0; }
.kv--narrow dd { padding-top: var(--sp-1); }
.kv--narrow > dt:first-child { padding-top: 0; }

/* ---------- Таблица данных ---------- */

.table-wrap {
  overflow-x: auto;
  /* Прокручиваемая таблица не должна обрезать след фокуса ячейки. */
  padding-bottom: var(--sp-1);
  scrollbar-width: thin;
  scrollbar-color: var(--c-separator-strong) transparent;
  /*
   * Признак того, что таблица уезжает вбок. На телефоне столбец «Цена от»
   * обрезан краем панели, полосу прокрутки сенсорный браузер не показывает, и
   * без этого признака человек уверен, что видит таблицу целиком.
   *
   * Четыре фона: две «шторки» цвета панели по краям едут вместе с
   * содержимым (attachment: local) и гаснут, когда край достигнут, а две
   * мягкие тени стоят на месте (attachment: scroll) и проступают только из-под
   * уехавшей шторки. Цвет теней собран из токена текста — своей краски тут нет.
   */
  background:
    linear-gradient(to right, var(--c-surface), transparent) left center / var(--sp-5) 100% no-repeat local,
    linear-gradient(to left, var(--c-surface), transparent) right center / var(--sp-5) 100% no-repeat local,
    radial-gradient(farthest-side at 0 50%, color-mix(in srgb, var(--c-text) 14%, transparent), transparent)
      left center / var(--sp-2) 100% no-repeat scroll,
    radial-gradient(farthest-side at 100% 50%, color-mix(in srgb, var(--c-text) 14%, transparent), transparent)
      right center / var(--sp-2) 100% no-repeat scroll;
}

table.grid {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-spec);
}
table.grid th,
table.grid td {
  text-align: left;
  padding: var(--sp-3);
  border-bottom: 1px solid var(--c-separator);
  white-space: nowrap;
}
table.grid th:first-child,
table.grid td:first-child { padding-left: 0; }
table.grid th:last-child,
table.grid td:last-child { padding-right: 0; }
table.grid th {
  color: var(--c-text-secondary);
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
}
table.grid td { font-weight: var(--fw-spec); }
table.grid td.num,
table.grid th.num {
  text-align: right;
  font-variant-numeric: var(--numeric-tabular);
}
table.grid tr:last-child td { border-bottom: 0; }

.list-plain {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--sp-3);
}
.list-plain > li + li {
  padding-top: var(--sp-3);
  border-top: 1px solid var(--c-separator);
}

.source-link {
  display: inline-block;
  margin-top: var(--sp-3);
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
}

/* ============================================================================
   ГРАФИК ИСТОРИИ ЦЕНЫ
   Встроенный SVG: внешних библиотек на сайте нет.
   Толщина линии и прозрачность заливки — геометрия рисунка, не ступени шкалы.
   ============================================================================ */

.chart { display: block; width: 100%; height: auto; }
.chart__line {
  fill: none;
  stroke: var(--c-accent);
  stroke-width: 1.5;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.chart__area { fill: var(--c-accent); opacity: .1; }
.chart__dot { fill: var(--c-surface); stroke: var(--c-accent); stroke-width: 1.5; }
.chart__grid { stroke: var(--c-separator); stroke-width: 1; }
.chart__label {
  fill: var(--c-text-tertiary);
  font-family: var(--font-ui);
  font-size: var(--fs-micro);
}

/* ============================================================================
   ЧЕСТНЫЕ ПОМЕТКИ И ПОЯСНЕНИЯ
   Это не украшение. Они стоят рядом с тем, о чём предупреждают, набраны не
   мельче подписи и не осветлены ниже 4,5:1.
   ============================================================================ */

.footnote {
  color: var(--c-text-tertiary);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
}

.muted { color: var(--c-text-secondary); }

/* «Цену и наличие подтверждайте у застройщика»: не сноска, а условие сделки,
   поэтому цвет второстепенного текста, а не приглушённого. */
.disclaimer {
  padding-left: var(--sp-3);
  border-left: 2px solid var(--c-separator-strong);
  color: var(--c-text-secondary);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
}

/* Место планировки. Мы держим для неё место и объясняем, почему её нет, —
   пустоту нельзя заменить серым прямоугольником без слов. */
.plan-slot {
  padding: var(--sp-5) var(--sp-4);
  background: var(--c-fill);
  border: 1px dashed var(--c-separator-strong);
  border-radius: var(--r-sm);
  color: var(--c-text-secondary);
  font-size: var(--fs-caption);
  line-height: var(--lh-caption);
  text-align: center;
}
.plan-slot--card { padding: var(--sp-3); }
/* Ссылка на планировку у застройщика стоит на месте самой планировки. */
.plan-slot a { font-weight: var(--fw-medium); }

.note {
  padding: var(--sp-5);
  margin-bottom: var(--stack);
  background: var(--c-surface);
  border-radius: var(--r-lg);
  border-left: 2px solid var(--c-accent);
  box-shadow: var(--sh-1);
}
.note--warn { border-left-color: var(--c-booked); }
.note--error { border-left-color: var(--c-sold); }
.note > :first-child { margin-top: 0; }
.note > :last-child { margin-bottom: 0; }

/* ============================================================================
   ПУСТОЕ СОСТОЯНИЕ И СОСТОЯНИЕ ЗАГРУЗКИ
   Не крутилка на весь экран, а спокойная заглушка формы карточки.
   ============================================================================ */

.empty {
  padding: var(--sp-8) var(--sp-5);
  background: var(--c-surface);
  border-radius: var(--r-lg);
  box-shadow: var(--sh-1);
  text-align: center;
  color: var(--c-text-secondary);
}
.empty h2 {
  color: var(--c-text);
  margin-bottom: var(--sp-3);
}
.empty p {
  max-width: var(--measure);
  margin-left: auto;
  margin-right: auto;
}

.skeleton,
.skeleton--card {
  background: var(--c-surface);
  border-radius: var(--r-md);
  box-shadow: var(--sh-1);
  min-height: calc(var(--sp-10) + var(--sp-7));
  animation: skeleton-pulse 1.6s var(--ease-in-out) infinite;
}
/* Заглушки идут подряд одним столбцом — между ними тот же зазор, что в сетке. */
.skeleton + .skeleton,
.skeleton--card + .skeleton--card { margin-top: var(--sp-4); }

.skeleton--line {
  min-height: var(--sp-4);
  border-radius: var(--r-sm);
  box-shadow: none;
  background: var(--c-fill);
}

@keyframes skeleton-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .6; }
}

/* Длительность пульсации задана временем, а не переменной движения, поэтому
   её отключаем отдельно. */
@media (prefers-reduced-motion: reduce) {
  .skeleton,
  .skeleton--card { animation: none; }
}

/* ============================================================================
   ПОСТРАНИЧНАЯ ВЫДАЧА
   ============================================================================ */

.pager {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  margin-top: var(--stack-loose);
}
.pager button {
  min-width: var(--control-h);
  min-height: var(--control-h);
  padding: 0 var(--sp-3);
  font: inherit;
  font-size: var(--fs-body);
  font-weight: var(--fw-medium);
  color: var(--c-text);
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--r-pill);
  cursor: pointer;
  font-variant-numeric: var(--numeric-tabular);
  transition:
    background var(--dur-hover) var(--ease-in-out),
    color var(--dur-hover) var(--ease-in-out);
}
.pager button:hover:not(:disabled) { background: var(--c-fill); }
.pager button[aria-current="page"] {
  background: var(--c-accent);
  color: var(--c-on-accent);
  font-weight: var(--fw-semibold);
}
.pager button:disabled { color: var(--c-text-tertiary); cursor: default; }
.pager__gap { color: var(--c-text-tertiary); padding: 0 var(--sp-1); }

/* ============================================================================
   СЛУЖЕБНАЯ ОЧЕРЕДЬ ПРОВЕРКИ (review.html)
   Страница пользуется общими карточками, кнопками и метками; здесь только то,
   чего в остальном сайте нет. Свои правила страницы лежат в её разметке —
   этот раздел повторяет их значениями системы, чтобы страница не разъезжалась
   с сайтом, когда разметка от собственного блока стилей избавится.
   ============================================================================ */

.rv-bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-3);
  align-items: flex-end;
  margin-bottom: var(--stack-tight);
}
.rv-bar .field { min-width: calc(var(--sp-10) + var(--sp-8)); flex: 1 1 auto; }
.rv-bar .btn { flex: none; }

.rv-queue { display: grid; gap: var(--stack); }
.rv-group { display: grid; gap: var(--sp-3); }
.rv-group__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-3);
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--c-separator);
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
}

.rv-review { gap: var(--sp-3); }
.rv-review__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--sp-2) var(--sp-3);
}
.rv-facts { display: flex; flex-wrap: wrap; gap: var(--sp-2); }

.rv-cands { display: grid; gap: var(--sp-2); margin: 0; padding: 0; list-style: none; }
.rv-cand {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  background: var(--c-fill);
  border-radius: var(--r-sm);
}
.rv-cand__name { font-weight: var(--fw-semibold); }
.rv-cand__ev {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-1) var(--sp-3);
  font-size: var(--fs-caption);
  color: var(--c-text-secondary);
}
.rv-cand__act { margin-left: auto; display: flex; gap: var(--sp-2); }

.rv-actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); align-items: flex-end; }
.rv-actions .field { flex: 1 1 auto; }

.rv-forms { display: grid; gap: var(--stack-tight); }
.rv-form { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: flex-end; }
.rv-form .field { flex: 1 1 auto; }
.rv-form__title {
  flex-basis: 100%;
  margin: 0;
  font-size: var(--fs-body);
  font-weight: var(--fw-semibold);
}

.rv-num { font-variant-numeric: var(--numeric-tabular); }

/* ============================================================================
   ЗОНА НАЖАТИЯ ПАЛЬЦЕМ
   Мышь попадает в 40 px, палец — нет: подушечка накрывает около 10 мм, и
   промах по фильтру на телефоне читается как «сайт не работает». Поэтому там,
   где указатель грубый (сенсор) или экран узкий, орган управления вырастает
   до 44 px. Это не новая ступень шкалы, а то же 24 + 16 плюс шаг ритма.
   Размер снаружи не виден: у пилюли фильтра и у ссылок растёт только область
   попадания, а рисунок остаётся прежним — воздух страницы не страдает.
   ============================================================================ */

@media (pointer: coarse), (max-width: 900px) {
  :root { --control-h: calc(var(--sp-5) + var(--sp-4) + var(--sp-1)); }

  /* Строка флажка: сам квадрат остаётся 18 px, палец бьёт по всей строке. */
  .check { min-height: var(--control-h); }

  /* Текстовая кнопка («Сбросить») и ссылка в подвале карточки — те же 44 px
     по высоте, набор и подчёркивание не меняются. */
  .btn--link,
  .card__foot a,
  .plan-slot a,
  .source-link,
  .breadcrumbs a,
  .site-title a {
    display: inline-flex;
    align-items: center;
    min-height: var(--control-h);
  }
  .source-link { margin-top: var(--sp-2); }

  /* Заголовок карточки — главная ссылка выдачи, по ней и попадают в квартиру.
     Строка в 21 px даёт 27 px высоты: добавляем отступ сверху и снизу и тут же
     снимаем его отрицательным полем. Область попадания вырастает до 51 px,
     а рисунок карточки не сдвигается ни на пиксель. */
  .card__title a {
    display: inline-block;
    padding-block: var(--sp-3);
    margin-block: calc(var(--sp-3) * -1);
  }

  /* Пилюля включённого фильтра и её крестик. Кнопка прозрачная, поэтому
     видно по-прежнему только знак «×» — вырастает область попадания, а не
     рисунок. Нимб держим внутри пилюли: вылези он за её край, на соседнюю
     пилюлю, и палец снимал бы не тот фильтр. */
  .chip { min-height: var(--control-h); padding-block: 0; }
  .chip button,
  .chip__remove {
    width: var(--control-h);
    height: var(--control-h);
  }
}

/* ============================================================================
   УЗКИЕ ЭКРАНЫ
   Переломы: 1000 px — боковая колонка карточки уходит вниз; 900 px — фильтры
   прячутся под кнопку; 420 px — парные поля и характеристики в один столбец.
   ============================================================================ */

/*
 * Страховка на широком экране: панель фильтров осталась свёрнутой.
 *
 * Свернуть её может только телефонная кнопка, и на широком экране скрипт
 * разворачивает панель обратно. Но событие смены ширины приходит не во всех
 * браузерах и не во всех режимах, а свёрнутая панель — это display: none: из
 * двух колонок раскладки выдача занимала первую, узкую, и карточки сжимались
 * в полосу в 256 px рядом с пустой половиной страницы. Тут раскладка
 * становится одностолбцовой, а кнопка «Фильтры» возвращается — иначе развернуть
 * панель обратно нечем. Браузеры без :has() просто не увидят этих двух правил,
 * и всё останется как было.
 */
@media (min-width: 901px) {
  .layout:has(> .filters[hidden]) { grid-template-columns: minmax(0, 1fr); }
  body:has(.filters[hidden]) .filters-toggle {
    display: inline-flex;
    margin-bottom: var(--stack-tight);
  }
}

/*
 * В городе без квартир фильтровать нечего (docs/20 §7.5): панель и кнопка
 * «Фильтры» убираются, пустое состояние занимает всю ширину. Классом, а не
 * атрибутом hidden у панели: hidden у панели означает «свёрнута на телефоне»,
 * и правило страховки выше вернуло бы кнопку «Фильтры».
 */
.is-bare .filters,
.is-bare .filters-toggle,
.is-bare .results__sort { display: none; }
.is-bare .layout { grid-template-columns: minmax(0, 1fr); }

/* На планшете и узком ноутбуке колонка фильтров сужается до 256 px, а зазор —
   до --stack: иначе выдаче не хватает ширины на два столбца карточек и она
   рассыпается в один столбец во всю ширину. */
@media (max-width: 1100px) {
  .layout {
    grid-template-columns: calc(var(--sp-10) * 2) minmax(0, 1fr);
    gap: var(--stack);
  }
}

@media (max-width: 1000px) {
  /* Колонки встают друг под друга, и зазор сетки обнуляется: расстояние между
     панелями держит их собственный нижний отступ, иначе на стыке колонок
     получается двойная дыра. */
  .detail {
    grid-template-columns: minmax(0, 1fr);
    gap: 0;
  }
}

@media (max-width: 900px) {
  .page { padding-top: var(--sp-6); padding-bottom: var(--sp-7); }
  .page > h1 { margin-bottom: var(--stack); }

  .layout {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--stack);
  }

  /* Фильтры сворачиваются в раскрывающуюся панель: кнопка во всю ширину,
     сама панель поднимается над содержимым собственной поверхностью. */
  .filters-toggle {
    display: flex;
    justify-content: space-between;
    width: 100%;
    margin-bottom: var(--stack-tight);
  }
  .filters {
    position: static;
    max-height: none;
    overflow: visible;
    padding: var(--sp-2) var(--sp-4) var(--sp-4);
    margin-right: 0;
    background: var(--c-surface);
    border-radius: var(--r-lg);
    box-shadow: var(--sh-3);
  }
  .fgroup:last-of-type { border-bottom: 0; }

  .site-meta { margin-left: 0; width: 100%; }
  /* Список городов на телефоне — во всю ширину под шапкой: от кнопки вправо он
     вылезал бы за край экрана. Опорой служит сама шапка (position: relative ниже). */
  .site-city { position: static; }
  .site-city__list {
    top: 100%;
    left: var(--gutter);
    right: var(--gutter);
    width: auto;
    max-width: none;
  }
  /* На телефоне сводка переносится на вторую строку, и липкая шапка съедает
     треть экрана. Сводка важнее липкости: шапка перестаёт быть липкой.
     relative, а не static: список городов из шапки обязан лечь поверх страницы,
     а порядок наложения работает только у позиционированной шапки. */
  .site-header { position: relative; }

  .results__sort { margin-left: 0; width: 100%; }
  .results__sort select { flex: 1 1 auto; min-width: 0; }
  /* Выбор числа карточек — короткий список: ему хватит узкого поля. */
  .results__sort .per-page { flex: 0 0 calc(var(--sp-8) + var(--sp-5)); }

  .cards { grid-template-columns: minmax(0, 1fr); }
}

/* Узкий телефон: крупным числам сводки и строке «найдено / сортировка» нужен
   тесный ритм, иначе четыре показателя растягиваются на пол-экрана. */
@media (max-width: 560px) {
  .results__bar { gap: var(--sp-3); }
  .stat-row { gap: var(--stack-tight) var(--stack); }
}

@media (max-width: 420px) {
  /* Пара «от / до» остаётся в два столбца и на 360 px: каждому полю достаётся
     около 140 px, а это шире самого длинного числа витрины. */

  .kv { grid-template-columns: minmax(0, 1fr); }
  .kv dt { padding: var(--sp-3) 0 0; border-bottom: 0; }
  .kv dd { padding-top: var(--sp-1); }
  .kv > dt:first-child { padding-top: 0; }

  .card__facts { gap: var(--sp-1); }
  .rv-cand__act { margin-left: 0; }
}

/* ============================================================================
   ПЕЧАТЬ
   ============================================================================ */

@media print {
  .filters,
  .filters-toggle,
  .pager,
  .site-meta { display: none; }

  body { background: var(--c-surface); }
  .card,
  .panel { box-shadow: none; }
}
