/* ==========================================================================
   Малый Знаменский переулок, 3/5 стр. 9 — страница объекта
   --------------------------------------------------------------------------
   Основание файла — дизайн-токены GR Platform / Good Realtors, скопированные
   дословно из живой системы заказчика (board/globals.css, канон платформы
   DESIGN-SYSTEM.md, pereustupka-web/globals.css). Значения не подбирались
   заново: палитра, шкала кеглей, отступы, радиусы, тени и длительности взяты
   как есть, чтобы страница читалась как часть того же семейства продуктов.

   Компоненты системы, которых на этой странице нет (чипы, шкала «цена против
   похожих», галерея, модалка, каталожная карточка, поиск-пилюля), удалены:
   мёртвый CSS возить незачем. Токены при этом сохранены ВСЕ, включая роли,
   которые здесь не используются — неполная тема ломает компоненты молча,
   отсутствующий токен превращается в пустую строку, а не в ошибку.

   ПРАВИЛО ФАЙЛА: hex-цветов вне блока :root нет. Любой цвет берётся через
   var(--color-*). Если понадобился оттенок, которого нет, — это повод
   завести ступень в каноне платформы, а не подобрать похожий здесь.
   ========================================================================== */

@import url("https://fonts.googleapis.com/css2?family=Inter:wght@400..700&family=Montserrat:wght@500..800&display=swap");

/* ==========================================================================
   1. ТОКЕНЫ
   ========================================================================== */

:root {
  /* --- Цвет: сырые каналы HSL --------------------------------------------
     Каналы объявлены отдельно от готовых цветов, чтобы прозрачные варианты
     (primary/90, success/12, secondary/72, white/70) собирались из тех же
     чисел, а не подбирались похожими на глаз. */
  --hsl-background: 0 0% 100%;
  --hsl-foreground: 213 50% 15%;
  --hsl-card: 210 20% 97%;
  --hsl-muted: 210 20% 96%;
  --hsl-muted-foreground: 213 22% 43%;
  --hsl-primary: 20 82% 42%;
  --hsl-primary-text: 20 82% 39%;
  --hsl-secondary: 213 50% 15%;
  --hsl-success: 142 65% 40%;
  --hsl-success-text: 142 65% 30%;
  --hsl-warning: 35 90% 42%;
  --hsl-destructive: 0 84.2% 60.2%;
  --hsl-destructive-text: 0 72% 47%;
  --hsl-border: 210 20% 90%;
  --hsl-input: 213 20% 57%;
  --hsl-teal: 175 42% 30%;
  --hsl-teal-soft: 175 35% 92%;
  --hsl-teal-border: 175 30% 80%;

  /* Фон и поверхности */
  --color-background: hsl(var(--hsl-background));           /* #ffffff */
  --color-foreground: hsl(var(--hsl-foreground));           /* #132439 navy */
  --color-card: hsl(var(--hsl-card));                       /* #f6f7f9 */
  --color-card-foreground: hsl(var(--hsl-foreground));      /* #132439 */
  --color-popover: hsl(var(--hsl-background));              /* #ffffff */
  --color-popover-foreground: hsl(var(--hsl-foreground));   /* #132439 */
  --color-muted: hsl(var(--hsl-muted));                     /* #f3f5f7 */

  /* --- Цвет: семантические роли ------------------------------------------
     17 ролей канона платформы. Оттенок вторичного текста затемнён против
     макета: макетный #66798f на подложке карточки давал 4,17:1 при
     требуемых 4,5. Здесь и дальше правее код, а не хендофф. */

  /* Текст */
  --color-muted-foreground: hsl(var(--hsl-muted-foreground));  /* #566b86 — 5,46:1 на белом */

  /* Акцент (CTA, focus-ring). Белый текст на заливке даёт 4,75:1 */
  --color-primary: hsl(var(--hsl-primary));                 /* #c34e13 */
  --color-primary-foreground: hsl(var(--hsl-background));   /* #ffffff */
  --color-accent: hsl(var(--hsl-primary));
  --color-accent-foreground: hsl(var(--hsl-background));
  --color-ring: hsl(var(--hsl-primary));
  /* Тот же оранжевый, НАБРАННЫЙ ТЕКСТОМ: у чернил и у заливки разные пороги,
     поэтому это отдельная ступень одной шкалы, а не «тот же цвет».
     5,38:1 на белом, 5,02:1 на карточке. Именно им набраны ссылки. */
  --color-primary-text: hsl(var(--hsl-primary-text));       /* #b54812 */

  /* Navy как кнопка/подложка */
  --color-secondary: hsl(var(--hsl-secondary));             /* #132439 */
  --color-secondary-foreground: hsl(var(--hsl-background)); /* #ffffff */

  /* Статусы — ЗАЛИВКИ. Порог 3:1: это графика (точки, маркеры, подложки
     с прозрачностью 12%), а не текст. Белым по ним не набирают. */
  --color-success: hsl(var(--hsl-success));                 /* #24a854 */
  --color-success-foreground: hsl(var(--hsl-background));
  --color-warning: hsl(var(--hsl-warning));                 /* #cb7b0b */
  --color-warning-foreground: hsl(var(--hsl-background));
  --color-destructive: hsl(var(--hsl-destructive));         /* #ef4444 */
  --color-destructive-foreground: hsl(210 40% 98%);         /* #f8fafc */

  /* Те же статусы, НАБРАННЫЕ ТЕКСТОМ: тон и насыщенность те же, ниже
     светлота. Заливочный success на белом даёт 3,09:1 при требуемых 4,5,
     поэтому текст поверх подложки берёт ступень -text, а не заливку. */
  --color-success-text: hsl(var(--hsl-success-text));       /* #1b7e3f — 5,12:1 */
  --color-destructive-text: hsl(var(--hsl-destructive-text)); /* #ce2222 — 5,42:1 */

  /* Линии. border — декоративный, порога не имеет и иметь не должен: ошибка
     была бы в обратном, повесить на него информацию. Ровно поэтому маркер
     списка набран токеном input, а не border.
     input — единственный признак «этим можно управлять», поэтому затемнён
     до 3,32:1 по WCAG 1.4.11 (канон платформы на этом элементе свой же
     критерий не проходил). */
  --color-border: hsl(var(--hsl-border));                   /* #e0e6eb */
  --color-input: hsl(var(--hsl-input));                     /* #7b8fa7 */

  /* Класс ЖК / нейтральный «спокойный» акцент. На этой странице не занят. */
  --color-teal: hsl(var(--hsl-teal));                       /* #2c6d67 */
  --color-teal-foreground: hsl(var(--hsl-background));
  --color-teal-soft: hsl(var(--hsl-teal-soft));             /* #e3f2f1 */
  --color-teal-border: hsl(var(--hsl-teal-border));         /* #bddbd9 */

  /* Бренд-оранжевый — это НЕ primary. Он для логотипа, бейджей и акцентов,
     а кнопки и фокус-кольцо берут --color-primary.

     БЕЛЫЙ ТЕКСТ НА НЁМ ЗАПРЕЩЁН КАНОНОМ: #ffffff на #e76f28 даёт 3,13:1
     при требуемых 4,5. Это не придирка и не «почти проходит» — на мелком
     кегле бейджа надпись реально теряется. Поверх бренд-оранжевого идёт
     navy #132439, это 5,00:1, и ровно для этого объявлена роль
     --color-brand-foreground. Если увидите на этой заливке белый текст —
     это регрессия, а не решение.

     Тот же запрет действует для --color-success (белый даёт 3,09:1) и
     --color-warning (3,29:1). Если понадобится белый по зелёной или
     янтарной плашке, брать надо более тёмную ступень того же тона, а не
     заливку. Для янтарного такой ступени в каноне НЕТ: рецепт есть
     (hsl(35 90% 30%) = #915808, 5,83:1 с белым), но токеном он не
     объявлен, потому что вводить неизвестный системе токен значит начать
     вторую палитру. Поэтому предупреждение на этой странице набрано navy
     по янтарной подложке 12%, а не янтарём. */
  --color-brand: #e76f28;
  --color-brand-foreground: hsl(var(--hsl-secondary));

  /* Серии графиков. На этой странице не заняты, но роль сохранена. */
  --color-chart-r1: #e76f28;
  --color-chart-r2: #2e6bc4;
  --color-chart-r3: #0e8d79;
  --color-chart-r4: #9c55b8;
  --color-chart-dev: #c4d3e8;

  /* Мессенджеры — только для своих кнопок */
  --color-whatsapp: #25d366;
  --color-telegram: #2aabee;

  /* Градиенты и служебные подложки.
     --scale-gradient — заливка шкалы «цена против похожих». Она здесь
     объявлена, но НЕ применена, см. комментарий у блока сделки. */
  --hero-gradient: linear-gradient(135deg, hsl(213 50% 15%), hsl(213 50% 22%));
  --catalog-surface: hsl(210 20% 97%);
  --photo-placeholder: linear-gradient(135deg, #dbe2ec, #f1f3f7 55%, #e6eaf1);
  --scale-gradient: linear-gradient(
    90deg,
    hsl(142 65% 40% / 0.32),
    var(--color-border) 48%,
    hsl(0 84% 60% / 0.32)
  );

  /* --- Типографика --------------------------------------------------------
     Заголовки — Montserrat 500-800, H1 700-800 с tracking -0.02em.
     Текст — Inter 400-700. Числа — Inter + tabular-nums.
     Montserrat 800 реально нужен: им набран логотип канона. Если урезать
     диапазон до 700, начертание поедет.
     Кириллица приезжает сама: Google Fonts раздаёт подмножества через
     unicode-range, указывать subset в URL не нужно и вредно.

     Шкала кеглей: значение — desktop, парное --*-m — mobile. */
  --font-heading: "Montserrat", ui-sans-serif, system-ui, -apple-system,
    "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-body: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI",
    Roboto, "Helvetica Neue", Arial, sans-serif;

  --text-h1: 40px;          --text-h1-m: 26px;
  --text-h2: 24px;          --text-h2-m: 18px;
  --text-h3: 18px;          --text-h3-m: 15px;
  --text-section: 16px;
  --text-card-title: 15px;  --text-card-title-m: 13.5px;
  --text-lead: 15px;
  --text-body: 13.5px;
  --text-body-lg: 14px;
  --text-secondary: 12.5px; --text-secondary-m: 11.5px;
  --text-small: 11.5px;
  --text-caption: 11px;

  --text-price: 26px;
  --text-price-panel: 28px;
  --text-price-grid: 20px;

  --leading-none: 1;
  --leading-tight: 1.25;
  --leading-normal: 1.5;
  --leading-relaxed: 1.625;

  --tracking-tight: -0.02em;
  --tracking-normal: 0;
  --tracking-wide: 0.025em;

  --weight-regular: 400;
  --weight-medium: 500;
  --weight-semibold: 600;
  --weight-bold: 700;
  --weight-extrabold: 800;

  /* --- Пространство -------------------------------------------------------
     Базовая единица 0.25rem = 4px, вся шкала кратна ей.
     --space-20 и --space-24 добавлены сверх источника: у доски нет секций
     лендинга, поэтому вертикального ритма такого масштаба в ней не было.
     Это продолжение той же шкалы, а не новые числа. */
  --space-unit: 0.25rem;
  --space-0-5: 2px;
  --space-1: 4px;
  --space-1-5: 6px;
  --space-2: 8px;
  --space-2-5: 10px;
  --space-3: 12px;
  --space-3-5: 14px;
  --space-4: 16px;
  --space-5: 20px;
  --space-6: 24px;
  --space-7: 28px;
  --space-8: 32px;
  --space-9: 36px;
  --space-10: 40px;
  --space-12: 48px;
  --space-14: 56px;
  --space-16: 64px;
  --space-20: 80px;
  --space-24: 96px;

  /* --- Форма --------------------------------------------------------------
     Одна шкала радиусов на всю страницу, отступать от неё нельзя:
     карточки 16px, внутренние блоки и поля ввода 12px, кнопки, чипы и
     бейджи — pill. Круглая кнопка в квадратной раскладке или квадратная
     карточка на странице с pill-кнопками — это сломанная система. */
  --radius-sm: calc(0.75rem - 4px);   /* 8px  */
  --radius-md: calc(0.75rem - 2px);   /* 10px */
  --radius-lg: 0.75rem;               /* 12px */
  --radius-xl: 0.75rem;               /* 12px */
  --radius-2xl: 1rem;                 /* 16px */
  --radius-3xl: 1.5rem;               /* 24px */
  --radius-widget: 20px;
  --radius-full: 9999px;
  --radius-bar: 4px;

  --border-width: 1px;
  --border-width-accent: 2px;

  /* --- Тени ---------------------------------------------------------------
     Только для hover и плавающих элементов. Все тени тонированы в navy,
     чёрных теней на светлом фоне в системе нет. */
  --shadow-card: 0 4px 16px -4px hsl(213 50% 15% / 0.08);
  --shadow-modal: 0 24px 80px -16px hsl(213 50% 15% / 0.35);
  --shadow-dot: 0 2px 6px hsl(213 50% 15% / 0.25);
  --shadow-popover: var(--shadow-card);

  /* --- Движение -----------------------------------------------------------
     150-200 ms, active-кнопки — scale 0.97. Свойства перехода в системе
     всегда перечислены поимённо: transition: all не встречается ни разу.
     Это не педантизм — scale, rotate и translate здесь отдельные
     CSS-свойства, и привычка писать transform их не соберёт. */
  --duration-fast: 150ms;
  --duration: 200ms;
  --duration-slow: 300ms;
  --ease-out-quart: cubic-bezier(0.23, 1, 0.32, 1);
  --ease-default: cubic-bezier(0.4, 0, 0.2, 1);
  --press-scale: 0.97;

  /* --- Раскладка ----------------------------------------------------------
     Брейкпоинты приведены токенами ДЛЯ СПРАВКИ: @media кастомные свойства
     не читает, в правилах ниже стоят те же числа литералами. Если менять
     брейкпоинт — менять в обоих местах. */
  --bp-sm: 40rem;
  --bp-md: 48rem;
  --bp-lg: 64rem;
  --bp-xl: 80rem;
  --bp-2xl: 96rem;

  --container-max: 1280px;
  --container-narrow: 1100px;
  --container-text: 62ch;
  --page-pad-x: 16px;
  --page-pad-x-md: 40px;
  --header-height: 72px;
  --header-height-mobile: 56px;
  --tap-target: 44px;

  /* Тёмной темы в источнике НЕТ ни в каком виде: ни .dark, ни [data-theme],
     ни prefers-color-scheme — проверено по обеим темам и по канону.
     Здесь она намеренно не выдумана: изобретённая тёмная тема это вторая
     система, которой у заказчика нет, и первое же соседство страниц это
     покажет. Если она понадобится, вводить её надо в каноне платформы.

     color-scheme: light — не заглушка «на потом», а явное заявление: без
     него браузер в тёмном режиме подкрасит нативные контролы (поля,
     скроллбары, select) под тёмную схему, и светлая страница получит
     тёмные вкрапления, которых в системе нет. */
  color-scheme: light;
}

/* ==========================================================================
   2. БАЗА
   ========================================================================== */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  /* Поверхности, которые мы не рисовали, тоже принадлежат системе:
     скроллбар, выделение текста и каретка по умолчанию не принадлежат
     никакому дизайну, поэтому они здесь заданы явно. */
  scrollbar-color: var(--color-input) var(--color-muted);
}

body {
  margin: 0;
  background: var(--color-background);
  color: var(--color-foreground);
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: var(--leading-normal);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  /* Длинные слова не должны рвать сетку на 375 px. */
  overflow-wrap: break-word;
}

::selection {
  background: hsl(var(--hsl-primary) / 0.16);
  color: var(--color-foreground);
}

h1,
h2,
h3,
h4 {
  font-family: var(--font-heading);
  text-wrap: balance;
  margin: 0;
}

p,
ul,
ol,
dl,
dd,
figure {
  margin: 0;
}

ul,
ol {
  padding: 0;
  list-style: none;
}

img,
svg,
video {
  display: block;
  max-width: 100%;
}

a {
  color: inherit;
  text-decoration: none;
}

button,
input,
select,
textarea {
  font: inherit;
  color: inherit;
  caret-color: var(--color-primary);
}

/* Фокус — общий для всего интерактивного: кольцо цвета ring с зазором в цвет
   фона. Эквивалент ring-2 ring-ring ring-offset-2 из канона. focus-visible,
   а не focus: мышью кольцо не показывается, клавиатурой показывается всегда. */
:focus-visible {
  outline: none;
  box-shadow:
    0 0 0 2px var(--color-background),
    0 0 0 4px var(--color-ring);
}

/* Якорь не должен уезжать под липкую шапку. */
[id] {
  scroll-margin-top: calc(var(--header-height-mobile) + var(--space-4));
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   3. ТИПОГРАФИКА
   ========================================================================== */

.h1,
h1 {
  font-family: var(--font-heading);
  font-size: var(--text-h1-m);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
}

.h2,
h2 {
  font-family: var(--font-heading);
  font-size: var(--text-h2-m);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
}

.h3,
h3 {
  font-family: var(--font-heading);
  font-size: var(--text-h3-m);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
}

.section-heading {
  font-family: var(--font-heading);
  font-size: var(--text-section);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
}

.section-subtitle {
  margin-top: var(--space-2);
  color: var(--color-muted-foreground);
  font-size: var(--text-body-lg);
  max-width: var(--container-text);
}

.lead {
  font-size: var(--text-body);
  line-height: var(--leading-relaxed);
  color: var(--color-muted-foreground);
  max-width: var(--container-text);
}

.text-secondary-sm {
  font-size: var(--text-secondary-m);
  font-weight: var(--weight-medium);
  color: var(--color-muted-foreground);
}

.caption {
  font-size: var(--text-caption);
  line-height: var(--leading-relaxed);
  color: var(--color-muted-foreground);
}

/* Все суммы, проценты и счётчики. Без этого цифры «прыгают». */
.tabular {
  font-variant-numeric: tabular-nums;
}

.muted {
  color: var(--color-muted-foreground);
}

.brand-ink {
  color: var(--color-primary-text);
}

@media (min-width: 40rem) {
  .h1,
  h1 {
    font-size: var(--text-h1);
  }

  .h2,
  h2 {
    font-size: var(--text-h2);
  }

  .h3,
  h3 {
    font-size: var(--text-h3);
  }

  .text-secondary-sm {
    font-size: var(--text-secondary);
  }

  .lead {
    font-size: var(--text-lead);
  }
}

/* ==========================================================================
   4. РАСКЛАДКА
   ========================================================================== */

.container-narrow {
  width: 100%;
  max-width: var(--container-narrow);
  margin-inline: auto;
  padding-inline: var(--page-pad-x);
}

.section {
  padding-block: var(--space-12);
}

.surface-catalog {
  background: var(--catalog-surface);
}

/* Шапка секции: заголовок и подводка стоят стопкой, левым краем. Пары
   «большой заголовок слева, мелкий абзац в правом верхнем углу» здесь нет
   намеренно — у секции одно сообщение, а не два, конкурирующих за первый
   взгляд, и висящий в углу абзац ни с чем не выровнен. */
.section-head {
  margin-bottom: var(--space-8);
}

@media (min-width: 48rem) {
  .container-narrow {
    padding-inline: var(--page-pad-x-md);
  }

  .section {
    padding-block: var(--space-20);
  }

  [id] {
    scroll-margin-top: calc(var(--header-height) + var(--space-4));
  }
}

/* ==========================================================================
   5. КНОПКИ
   ==========================================================================
   Правило канона: в одной секции ОДНА primary-кнопка. Если их две, ни одна
   не главная. Hover затемняет заливку примерно на 10%, active даёт
   scale 0.97 — это единственная обратная связь, которая говорит «интерфейс
   тебя услышал», и убирать её нельзя.
   Свойства перехода перечислены поимённо, transition: all не используется. */

.btn-primary,
.btn-outline,
.btn-navy,
.btn-soft {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  border-radius: var(--radius-full);
  padding: var(--space-3) var(--space-6);
  font-family: var(--font-body);
  font-size: 14px;
  font-weight: var(--weight-semibold);
  line-height: var(--leading-normal);
  cursor: pointer;
  border: var(--border-width) solid transparent;
  text-align: center;
  /* Тач-цель: канон 44px, добираем высотой самой кнопки, а не невидимым
     псевдоэлементом — это не компактный чип, кнопке положено быть крупной. */
  min-height: var(--tap-target);
  transition-duration: var(--duration);
  transition-timing-function: var(--ease-default);
}

.btn-primary {
  background: var(--color-primary);
  color: var(--color-primary-foreground);
  transition-property: background-color, scale;
}

.btn-primary:hover {
  background: hsl(var(--hsl-primary) / 0.9);
}

.btn-primary:active {
  scale: var(--press-scale);
}

.btn-primary:disabled,
.btn-primary[aria-disabled="true"] {
  opacity: 0.6;
  cursor: default;
}

.btn-outline {
  background: var(--color-background);
  color: var(--color-foreground);
  border-color: var(--color-input);
  transition-property: background-color, border-color, scale;
}

.btn-outline:hover {
  border-color: hsl(var(--hsl-primary) / 0.4);
}

.btn-outline:active {
  scale: var(--press-scale);
}

.btn-navy {
  background: var(--color-secondary);
  color: var(--color-secondary-foreground);
  transition-property: background-color, scale;
}

.btn-navy:hover {
  background: hsl(var(--hsl-secondary) / 0.9);
}

.btn-navy:active {
  scale: var(--press-scale);
}

.btn-soft {
  background: var(--color-muted);
  color: var(--color-foreground);
  padding-inline: var(--space-4);
  font-size: var(--text-secondary);
  font-weight: var(--weight-medium);
  transition-property: background-color, color, scale;
}

.btn-soft:hover {
  background: var(--color-border);
}

.btn-soft:active {
  scale: var(--press-scale);
}

/* Текстовая ссылка канона: оранжевые чернила без постоянного подчёркивания,
   подчёркивание приходит на hover. Цвет — ступень -text, не заливочный
   primary: 5,38:1 против 4,75:1, и на мелком кегле это заметно. */
.link-primary {
  color: var(--color-primary-text);
  font-weight: var(--weight-semibold);
  text-underline-offset: 3px;
  transition: color var(--duration) var(--ease-default);
}

.link-primary:hover {
  color: var(--color-primary);
  text-decoration: underline;
}

.btn-sm {
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-secondary);
}

.btn-lg {
  padding: var(--space-4) var(--space-6);
}

.btn-block {
  width: 100%;
}

/* ==========================================================================
   6. БЕЙДЖИ
   ========================================================================== */

.pill {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1-5);
  border-radius: var(--radius-full);
  padding: var(--space-1-5) var(--space-3);
  font-size: var(--text-small);
  font-weight: var(--weight-semibold);
  background: var(--color-muted);
  color: var(--color-foreground);
}

/* Тон бейджа — правило дизайн-системы, а не оформление: белый текст на
   бренд-оранжевом даёт 3,13:1 и запрещён каноном, поверх идёт navy — 5,00:1.
   Роль называется --color-brand-foreground именно для этого. */
.pill-brand {
  background: var(--color-brand);
  color: var(--color-brand-foreground);
}

/* ==========================================================================
   7. КАРТОЧКИ
   ==========================================================================
   Карточка в системе — контейнер для того, что действительно сгруппировано,
   а не способ разложить страницу. Ряды одинаковых карточек «иконка плюс
   заголовок плюс текст» как структура страницы каноном не предусмотрены,
   и вложенных карточек в системе не бывает. */

.card-service {
  border-radius: var(--radius-2xl);
  background: var(--color-card);
  padding: var(--space-5);
  overflow: hidden;
}

.card-data {
  border-radius: var(--radius-2xl);
  background: var(--color-background);
  border: var(--border-width) solid var(--color-border);
  padding: var(--space-6);
}

/* Тёмная карточка на hero-gradient. Правило канона: ОДИН такой блок на
   экран, иначе тёмные пятна начинают спорить друг с другом за внимание.
   Здесь он занят блоком сделки — самым важным на странице. */
.card-dark {
  border-radius: var(--radius-2xl);
  background: var(--hero-gradient);
  color: var(--color-secondary-foreground);
  padding: var(--space-6);
}

.card-dark .muted,
.on-dark-secondary {
  color: hsl(0 0% 100% / 0.7);
}

@media (min-width: 48rem) {
  .card-dark {
    padding: var(--space-10);
  }
}

/* ==========================================================================
   8. ПОЛЯ ВВОДА
   ==========================================================================
   Подпись всегда стоит НАД полем отдельным label. Плейсхолдер-вместо-подписи
   в системе запрещён: он исчезает, как только человек начал печатать, и
   заполненная форма перестаёт объяснять, что в ней написано. */

.field {
  width: 100%;
  min-height: var(--tap-target);
  border-radius: var(--radius-lg);
  border: var(--border-width) solid var(--color-input);
  background: var(--color-muted);
  padding: var(--space-3-5) var(--space-4);
  font-size: 14px;
  color: var(--color-foreground);
  transition: border-color var(--duration) var(--ease-default),
    background-color var(--duration) var(--ease-default);
}

.field::placeholder {
  /* Подпись всегда над полем, плейсхолдер ничего не подписывает. */
  color: hsl(var(--hsl-muted-foreground) / 0.7);
}

.field:focus-visible {
  border-color: var(--color-primary);
  background: var(--color-background);
}

.field-label {
  display: block;
  margin-bottom: var(--space-1-5);
  font-size: 12px;
  font-weight: var(--weight-medium);
  color: var(--color-muted-foreground);
}

.checkbox {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  accent-color: var(--color-secondary);
}

/* ==========================================================================
   9. ЦЕНА
   ==========================================================================
   Цена — крупная, Montserrat bold, отрицательный трекинг, leading-none и
   ОБЯЗАТЕЛЬНО tabular-nums: без него цифры «прыгают» при подстановке из
   конфига. Форматируется как «600 млн ₽» с УЗКИМИ НЕРАЗРЫВНЫМИ пробелами
   между числом, «млн» и «₽», чтобы строка не разорвалась переносом. Именно
   поэтому white-space: nowrap на этих строках почти везде избыточен —
   собирает их форматтер в index.html, а не CSS. */

.price {
  font-family: var(--font-heading);
  font-size: var(--text-price);
  font-weight: var(--weight-bold);
  line-height: var(--leading-none);
  letter-spacing: var(--tracking-tight);
  font-variant-numeric: tabular-nums;
}

.price-lg {
  font-size: var(--text-price-panel);
}

.price-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: var(--space-2) var(--space-3);
}

.price-note {
  font-size: var(--text-secondary);
  font-weight: var(--weight-medium);
  color: var(--color-muted-foreground);
}

/* ==========================================================================
   10. ХАРАКТЕРИСТИКИ
   ==========================================================================
   Разметка — описательный список: «признак → значение» это ровно та пара,
   для которой в HTML есть dl/dt/dd, и подменять её дивами незачем.

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

.spec-list {
  display: grid;
  gap: var(--space-1) var(--space-10);
}

.spec-row {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--space-2);
  font-size: 13px;
  line-height: var(--leading-relaxed);
}

.spec-row dt {
  display: flex;
  min-width: 0;
  flex: 1 1 auto;
  align-items: baseline;
  gap: var(--space-2);
  color: var(--color-muted-foreground);
}

/* Поводок: без него глаз промахивается на соседнюю строку. Это декорация,
   поэтому псевдоэлемент, а не пустой span девятнадцать раз в разметке. */
.spec-row dt::after {
  content: "";
  height: 1px;
  min-width: 12px;
  flex: 1 1 auto;
  translate: 0 -3px;
  background: var(--color-border);
}

.spec-row dd {
  margin: 0;
  margin-left: auto;
  text-align: right;
  color: var(--color-foreground);
  font-weight: var(--weight-medium);
}

@media (min-width: 40rem) {
  .spec-list {
    grid-template-columns: 1fr 1fr;
  }
}

/* --- «Что важно знать»: утверждения, а не плашки -------------------------
   Маркер — риска, а не буллет, и он набран токеном --color-input, а не
   --color-border: на border информацию вешать нельзя, он декоративный.

   Красного здесь нет сознательно. В этой системе красный занят под «дороже
   похожих» и под удаление. Повесить его на нейтральный факт значит объявить
   аварией то, что аварией не является, а пять красных строк подряд читаются
   как приговор, и человек просто перестаёт читать. */

.claims {
  display: grid;
  gap: var(--space-3);
  max-width: var(--container-text);
}

.claim {
  display: grid;
  grid-template-columns: 14px 1fr;
  align-items: start;
  gap: var(--space-3);
  font-size: var(--text-body-lg);
  line-height: var(--leading-relaxed);
  color: var(--color-foreground);
}

/* Высота ячейки маркера равна межстрочному интервалу строки, поэтому маркер
   стоит по центру ПЕРВОЙ строки и не уезжает вниз при переносе абзаца. */
.claim-marker {
  display: flex;
  height: 1.625em;
  align-items: center;
  justify-content: center;
}

.claim-marker::before {
  content: "";
  display: block;
  height: 2px;
  width: 10px;
  border-radius: var(--radius-full);
  translate: 0 -1px;
  background: var(--color-input);
}

/* ==========================================================================
   11. КАДР
   ==========================================================================
   Фотография объекта РОВНО ОДНА, других не существует. Она подана как один
   большой кадр, а не как галерея с пустыми слотами под будущие снимки:
   пустой слот читается как незагрузившийся интерфейс, то есть как поломка,
   и делает объект беднее, чем он есть.

   Плашек и подписей поверх кадра нет — подпись стоит под ним. .photo даёт
   контейнеру aspect-ratio и placeholder-градиент, картинка внутри лежит
   absolute+cover, поэтому вёрстка не прыгает, пока файл грузится. */

.photo {
  display: block;
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-2xl);
  background: var(--photo-placeholder);
}

.photo > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ==========================================================================
   12. ШАПКА И ПОДВАЛ
   ==========================================================================
   Шапка светлая, 72px на десктопе и 56 на мобильном. Тёмная на длинной
   странице конкурирует с содержимым за внимание; светлая уходит на второй
   план и не спорит с тёмным блоком сделки. */

.site-header {
  position: sticky;
  top: 0;
  z-index: 40;
  border-bottom: var(--border-width) solid var(--color-border);
  background: hsl(var(--hsl-background) / 0.92);
  backdrop-filter: blur(8px);
}

.site-header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: var(--header-height-mobile);
}

.site-nav {
  display: none;
  align-items: center;
  gap: var(--space-7);
}

/* Пункт меню визуально 26px по высоте — подчёркивание активного пункта
   требует, чтобы он оставался компактным. Площадь нажатия добирается до 44px
   псевдоэлементом, а не раздуванием самого пункта: навигация показывается от
   768px, а планшет в этой ширине — это палец, а не мышь. */
.site-nav a {
  position: relative;
  border-bottom: var(--border-width-accent) solid transparent;
  padding-bottom: 3px;
  font-size: 14px;
  font-weight: var(--weight-medium);
  color: var(--color-muted-foreground);
  transition: color var(--duration) var(--ease-default),
    border-color var(--duration) var(--ease-default);
}

.site-nav a::after {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  translate: 0 -50%;
  width: 100%;
  height: var(--tap-target);
}

.site-nav a:hover {
  color: var(--color-foreground);
  border-color: var(--color-border);
}

/* ==========================================================================
   БАННЕР СОГЛАСИЯ
   ==========================================================================
   Встаёт снизу и отдаёт свою высоту переменной --consent-h: пока он на
   экране, липкие элементы могут на неё отступить, а не оказаться под ним.

   Вход снизу, уход — только прозрачностью. Панель, уезжающая вниз, на те же
   миллисекунды накрыла бы то, что под ней. Движение глушится глобальным
   prefers-reduced-motion: это переход, а не WAAPI.
   ========================================================================== */

.consent {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 60;
  border-top: var(--border-width) solid var(--color-border);
  background: var(--color-background);
  box-shadow: var(--shadow-modal);
  transition: translate var(--duration-slow) var(--ease-out-quart),
    opacity var(--duration-slow) var(--ease-out-quart),
    visibility var(--duration-slow) var(--ease-out-quart);
}

@starting-style {
  .consent[data-state="open"] {
    translate: 0 100%;
    opacity: 0;
  }
}

/* Выход быстрее входа: закрытие должно ощущаться мгновенным. */
.consent[data-state="closed"] {
  visibility: hidden;
  opacity: 0;
  transition-duration: var(--duration-fast);
}

.consent-inner {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  width: 100%;
  max-width: var(--container-narrow, 1120px);
  margin-inline: auto;
  padding: var(--space-4) var(--page-pad-x) 0;
  /* Кнопки не должны уезжать под домашний индикатор iPhone: на 375px баннер —
     самый нижний интерактив экрана. */
  padding-bottom: max(var(--space-4), env(safe-area-inset-bottom));
}

.consent-body {
  max-width: 68ch;
}

.consent-title {
  font-family: var(--font-heading);
  font-size: 15px;
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  margin: 0;
}

.consent-text {
  margin: var(--space-1-5) 0 0;
  font-size: var(--text-body);
  line-height: var(--leading-relaxed);
  color: var(--color-muted-foreground);
}

.consent-list {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  display: grid;
}

.consent-item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  min-height: var(--tap-target);
  padding-block: var(--space-1);
  font-size: var(--text-body);
  line-height: var(--leading-relaxed);
  color: var(--color-muted-foreground);
  cursor: pointer;
}

.consent-item input {
  margin-top: 2px;
  flex-shrink: 0;
}

.consent-item input:disabled {
  cursor: not-allowed;
}

.consent-item b {
  color: var(--color-foreground);
  font-weight: var(--weight-semibold);
}

.consent-links {
  margin: var(--space-2) 0 0;
  font-size: var(--text-secondary);
  color: var(--color-muted-foreground);
}

.consent-links a {
  color: var(--color-primary-text);
  text-decoration: underline;
  text-decoration-color: var(--color-border);
  text-underline-offset: 4px;
}

.consent-links a:hover {
  text-decoration-color: currentColor;
}

.consent-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2-5);
  flex-shrink: 0;
}

.consent-close {
  position: absolute;
  top: var(--space-2);
  right: var(--page-pad-x);
  display: grid;
  place-items: center;
  width: var(--tap-target);
  height: var(--tap-target);
  border: 0;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-muted-foreground);
  cursor: pointer;
  transition: background-color var(--duration) var(--ease-default),
    color var(--duration) var(--ease-default);
}

.consent-close:hover {
  background: var(--color-muted);
  color: var(--color-foreground);
}

.consent-close[hidden] {
  display: none;
}

/* Кнопка, которая выглядит и ведёт себя как ссылка в тексте. */
.link-inline {
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: var(--color-primary-text);
  text-decoration: underline;
  text-decoration-color: var(--color-border);
  text-underline-offset: 4px;
  cursor: pointer;
}

.link-inline:hover {
  text-decoration-color: currentColor;
}

@media (min-width: 48rem) {
  .consent-inner {
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-10);
    padding-inline: var(--page-pad-x-md);
  }

  .consent-close {
    right: var(--page-pad-x-md);
  }
}

/* Баннера на бумаге нет: он про браузер. */
@media print {
  .consent {
    display: none !important;
  }
}

/* Логотип Good Realtors. Два начертания, и выбор между ними определяется
   шириной, а не вкусом: полный лоқап несёт подпись «агентство недвижимости»
   кеглем, который ниже 768px становится нечитаемым штрихом, поэтому там
   остаётся одна монограмма — она узнаётся и в 20px.

   Размер задан height + width:auto, а пропорция — aspect-ratio, своя для
   каждого начертания. Атрибутами width/height обойтись нельзя: у <picture>
   источники разной формы (564×128 против 255×256), и один набор атрибутов
   зарезервировал бы неверную коробку под второй из них — это сдвиг вёрстки
   в момент загрузки, ровно в шапке, которую видят первой. */
.hdr-brand {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}

.hdr-brand img {
  display: block;
  height: 20px;
  width: auto;
  aspect-ratio: 255 / 256;
}

.hdr-brand:hover {
  opacity: 0.82;
}

/* Черта между «кто продаёт» и «что продаётся». Ниже 768px её нет вместе с
   полным лоқапом: на монограмме разделять уже нечего, а лишний штрих в
   тесной строке читается как дефект вёрстки. */
.hdr-sep {
  display: none;
  flex-shrink: 0;
  width: var(--border-width);
  height: 26px;
  background: var(--color-border);
}

.hdr-left {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  min-width: 0;
}

.site-footer {
  background: var(--color-secondary);
  color: var(--color-secondary-foreground);
}

.site-footer-inner {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  padding-block: var(--space-10);
}

@media (min-width: 48rem) {
  .site-header-inner {
    min-height: var(--header-height);
    gap: var(--space-6);
  }

  .site-nav {
    display: flex;
  }

  /* Здесь <picture> отдаёт полный лоқап — пропорция меняется вместе с ним. */
  .hdr-brand img {
    height: 30px;
    aspect-ratio: 564 / 128;
  }

  .hdr-sep {
    display: block;
  }

  .hdr-left {
    gap: var(--space-4);
  }

  .site-footer-inner {
    flex-direction: row;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-10);
  }
}

/* ==========================================================================
   13. СЛУЖЕБНОЕ
   ========================================================================== */

/* Доводит область нажатия до 44px, НЕ меняя видимого размера элемента.
   Приём канона: визуально компактный элемент обязан остаться компактным,
   поэтому площадь добирается невидимым псевдоэлементом, а не раздуванием
   самого элемента. Проверять его нужно попаданием (elementFromPoint), а не
   getBoundingClientRect — рект элемента псевдоэлемент не учитывает. */
.tap-target {
  position: relative;
}

.tap-target::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  min-width: var(--tap-target);
  min-height: var(--tap-target);
  width: 100%;
  height: 100%;
}

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

/* Ссылка «пропустить навигацию» скрыта до фокуса, но по Tab становится
   полноценной кнопкой 44px. Скрытый элемент, получивший фокус и оставшийся
   размером 1×1, для клавиатуры хуже, чем его отсутствие: человек видит, что
   фокус куда-то ушёл, и не понимает куда. */
.skip-link {
  position: absolute;
  left: var(--space-4);
  top: var(--space-4);
  z-index: 50;
  translate: 0 -200%;
  display: inline-flex;
  align-items: center;
  min-height: var(--tap-target);
  border-radius: var(--radius-full);
  background: var(--color-secondary);
  color: var(--color-secondary-foreground);
  padding-inline: var(--space-5);
  font-size: 14px;
  font-weight: var(--weight-semibold);
  transition: translate var(--duration) var(--ease-out-quart);
}

.skip-link:focus-visible {
  position: fixed;
  translate: 0 0;
}

.divider {
  border: 0;
  border-top: var(--border-width) solid var(--color-border);
  margin: 0;
}

.stack {
  display: grid;
  gap: var(--space-4);
}

.icon {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
}

/* ==========================================================================
   ==========================================================================
   СТРАНИЦА ОБЪЕКТА

   Ниже — раскладки, которых в исходной системе нет: у доски объявлений нет
   ни героя, ни секций лендинга. Всё построено на токенах выше. Новых
   значений цвета, кегля, отступа, радиуса, тени и длительности здесь не
   вводится ни одного — если понадобилось, значит нужна ступень в каноне.
   ==========================================================================
   ========================================================================== */

/* --- Шапка: адрес слева, цена и действие справа --------------------------
   На 375 px навигация уходит, но цена и кнопка остаются: это ровно те два
   элемента, ради которых шапка вообще сделана липкой. Мобильный герой не
   помещается в один экран, и постоянно видимая кнопка «Связаться» — то, что
   это компенсирует. */

.hdr-id {
  display: flex;
  align-items: center;
  gap: var(--space-2-5);
  min-width: 0;
}

.hdr-id-text {
  display: grid;
  min-width: 0;
}

.hdr-id-name {
  font-family: var(--font-heading);
  font-size: 13px;
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  color: var(--color-foreground);
}

.hdr-id-sub {
  font-size: var(--text-caption);
  font-weight: var(--weight-medium);
  line-height: var(--leading-tight);
  color: var(--color-muted-foreground);
  font-variant-numeric: tabular-nums;
}

.hdr-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-shrink: 0;
}

.hdr-price {
  font-family: var(--font-heading);
  font-size: 15px;
  font-weight: var(--weight-bold);
  line-height: var(--leading-none);
  letter-spacing: var(--tracking-tight);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

@media (min-width: 40rem) {
  .hdr-id-name {
    font-size: 15px;
  }

  .hdr-id-sub {
    font-size: var(--text-secondary);
  }

  .hdr-price {
    font-size: var(--text-price-grid);
  }
}

/* --- Герой ---------------------------------------------------------------
   Асимметричная раскладка: слева заголовок, адрес, четыре числа и действия,
   справа кадр.

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

.hero {
  padding-top: var(--space-8);
  padding-bottom: var(--space-12);
}

.hero-grid {
  display: grid;
  gap: var(--space-6);
}

.hero-address {
  margin-top: var(--space-4);
  font-size: var(--text-body-lg);
  line-height: var(--leading-relaxed);
  color: var(--color-muted-foreground);
  max-width: 46ch;
}

.hero-figure .photo {
  aspect-ratio: 4 / 3;
}

.hero-figure figcaption {
  margin-top: var(--space-2-5);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
}

/* Число колонок связано с ФАКТИЧЕСКОЙ шириной колонки героя, а не с шириной
   окна, и это container query, а не медиазапрос.

   Почему: медиазапрос на окне здесь прямо врёт. При окне 1440 текстовая
   колонка живёт рядом с фотографией и имеет всего ~481px; правило вида
   «от 40rem четыре колонки» давало по 108px на ячейку, а строке
   «600 тыс ₽/м²» кеглем --text-price нужно 174px — три числа из четырёх
   вылезали за свою ячейку. Дефект нашёлся только при замере на десктопе:
   на 375 и на 578 блок в две колонки, и там всё сходилось.

   Порог 47rem = 752px это четыре колонки минимум по 188px, с запасом над
   174. Промежуточного состояния в три колонки не бывает намеренно: четыре
   элемента в три колонки дают висящий одинокий пятый угол. */
.hero-tail {
  container-type: inline-size;
}

.keynums {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-5) var(--space-4);
  margin-top: var(--space-8);
  padding-top: var(--space-6);
  border-top: var(--border-width) solid var(--color-border);
}

@container (min-width: 47rem) {
  .keynums {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }
}

/* Разметка требует dt перед dd, глаз требует числа первым. Переворот делает
   раскладка (column-reverse), а не порядок в документе: экранный чтец
   получает пару «признак, значение» в правильном порядке. */
.keynum {
  display: flex;
  flex-direction: column-reverse;
  align-items: flex-start;
}

.keynum-value {
  display: block;
  font-family: var(--font-heading);
  font-size: 22px;
  font-weight: var(--weight-bold);
  line-height: var(--leading-none);
  letter-spacing: var(--tracking-tight);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.keynum-label {
  display: block;
  margin-top: var(--space-2);
  font-size: var(--text-secondary-m);
  font-weight: var(--weight-medium);
  color: var(--color-muted-foreground);
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-7);
}

@media (min-width: 40rem) {
  .keynum-value {
    font-size: var(--text-price);
  }

  .keynum-label {
    font-size: var(--text-secondary);
  }
}

@media (min-width: 64rem) {
  .hero {
    padding-top: var(--space-12);
    padding-bottom: var(--space-16);
  }

  .hero-grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.02fr);
    grid-template-areas:
      "text figure"
      "tail figure";
    align-items: start;
    column-gap: var(--space-12);
    row-gap: 0;
  }

  .hero-text {
    grid-area: text;
  }

  .hero-figure {
    grid-area: figure;
    align-self: stretch;
    display: flex;
    flex-direction: column;
  }

  .hero-figure .photo {
    aspect-ratio: auto;
    flex: 1 1 auto;
    min-height: 400px;
  }

  .hero-tail {
    grid-area: tail;
  }
}

/* --- Факты-крючки --------------------------------------------------------
   Шесть утверждений, а не шесть одинаковых карточек с иконкой. Карточка
   здесь ничего не сообщает сверх того, что кто-то умеет рисовать карточки,
   а иконка «здание» не добавляет ничего к словам «отдельно стоящее».

   Приём: ведущее слово набрано заголовочным шрифтом, продолжение текстовым.
   Факт читается крупно, но остаётся предложением, а не превращается в
   плитку «большое число, мелкая подпись». */

.facts-grid {
  display: grid;
  gap: var(--space-6) var(--space-10);
}

.fact {
  font-size: var(--text-lead);
  line-height: var(--leading-relaxed);
  color: var(--color-muted-foreground);
  padding-top: var(--space-4);
  border-top: var(--border-width) solid var(--color-border);
}

.fact b {
  display: block;
  font-family: var(--font-heading);
  font-size: var(--text-h3);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  font-variant-numeric: tabular-nums;
  color: var(--color-foreground);
  margin-bottom: var(--space-1-5);
}

@media (min-width: 40rem) {
  .facts-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 64rem) {
  .facts-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .fact b {
    font-size: 21px;
  }
}

/* --- Контекст цены -------------------------------------------------------
   Компактный блок, а не секция во весь экран: его работа — за пять секунд
   показать, относительно чего названа цена.

   Дата в подписи ОБЯЗАТЕЛЬНА и удалять её нельзя: рынок I квартала 2026
   года не вечная величина, а цифра без даты через год станет враньём.
   Там же оговорка «по ценам предложения, а не по ценам сделок» — она
   защищает продавца от претензии не меньше, чем покупателя. */

.market-lead {
  display: grid;
  gap: var(--space-4);
  align-items: baseline;
  padding-bottom: var(--space-6);
  margin-bottom: var(--space-6);
  border-bottom: var(--border-width) solid var(--color-border);
}

.market-pair {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.market-pair-value {
  font-family: var(--font-heading);
  font-size: var(--text-price);
  font-weight: var(--weight-bold);
  line-height: var(--leading-none);
  letter-spacing: var(--tracking-tight);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.market-pair-label {
  font-size: var(--text-secondary);
  font-weight: var(--weight-medium);
  color: var(--color-muted-foreground);
}

.market-tiles {
  display: grid;
  gap: var(--space-5) var(--space-8);
}

/* Без white-space: nowrap намеренно. «в год» имеет полное право уехать на
   вторую строку, а само число с единицей и так не разорвётся — между ними
   стоят узкие неразрывные пробелы. Запрет переноса на всей строке лишил бы
   её единственной безопасной точки разрыва и вытолкнул бы за край колонки.
   Это тот же класс дефекта, что был в числах героя, только лечится
   не колонками, а снятием запрета. */
.market-tile-value {
  display: block;
  font-family: var(--font-heading);
  font-size: var(--text-h3);
  font-weight: var(--weight-bold);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tight);
  font-variant-numeric: tabular-nums;
}

.market-tile-label {
  display: block;
  margin-top: var(--space-1-5);
  font-size: var(--text-secondary);
  color: var(--color-muted-foreground);
}

.market-note {
  margin-top: var(--space-6);
  padding-top: var(--space-4);
  border-top: var(--border-width) solid var(--color-border);
}

/* Тот же приём, что и в герое: число колонок связано с шириной карточки,
   а не окна. Карточка живёт в container-narrow и на планшете заметно уже
   окна, поэтому медиазапрос на окне здесь соврал бы так же. */
.market-card {
  container-type: inline-size;
}

@container (min-width: 32rem) {
  .market-lead {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-6);
  }
}

@container (min-width: 38rem) {
  .market-tiles {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

/* --- Характеристики ------------------------------------------------------
   Девятнадцать строк подряд с волосяной линией под каждой — самая ленивая
   раскладка из возможных. Строки сгруппированы по смыслу (здание, инженерия,
   участок, расположение), линия одна на группу, и она отделяет группы,
   а не строки. */

.spec-groups {
  display: grid;
  gap: var(--space-10);
}

.spec-group > .section-heading {
  margin-bottom: var(--space-4);
  padding-bottom: var(--space-3);
  border-bottom: var(--border-width) solid var(--color-border);
}

/* --- Локация -------------------------------------------------------------- */

.loc-grid {
  display: grid;
  gap: var(--space-10);
}

.metro {
  display: grid;
  gap: var(--space-3);
  margin-top: var(--space-6);
}

.metro-row {
  display: flex;
  align-items: baseline;
  gap: var(--space-3);
  font-size: var(--text-body-lg);
}

.metro-row b {
  font-weight: var(--weight-semibold);
}

/* Тоже без nowrap: «414 м, 4 минуты пешком» имеет естественные точки
   разрыва, и в узкой колонке лучше перенести строку, чем вытолкнуть её
   за край. */
.metro-row span:last-child {
  margin-left: auto;
  font-variant-numeric: tabular-nums;
  color: var(--color-muted-foreground);
  text-align: right;
}

/* Iframe карты не встраивается: сторонний скрипт, тормоза и чужой счётчик. */
.mapcard {
  display: block;
  border-radius: var(--radius-2xl);
  border: var(--border-width) solid var(--color-border);
  background: var(--color-card);
  padding: var(--space-5);
  transition: border-color var(--duration) var(--ease-default),
    background-color var(--duration) var(--ease-default);
}

.mapcard:hover {
  border-color: hsl(var(--hsl-primary) / 0.4);
  background: var(--color-background);
}

.mapcard-top {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
}

.mapcard-top .icon {
  color: var(--color-primary-text);
  margin-top: var(--space-0-5);
}

/* Координат на странице НЕТ намеренно, и добавлять их не надо. Открытые
   источники дают по этому владению несколько точек в пределах его же
   габарита, и какая из них строение 9 — по ним не разрешается. Точка,
   поставленная наугад, для покупателя, который поедет смотреть, хуже её
   отсутствия. Ссылка уходит строковым адресом с номером строения: его
   Яндекс разбирает точнее, чем снятую со стороны координату. По той же
   причине в Schema.org нет блока geo. */
.mapcard-foot {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: var(--border-width) solid var(--color-border);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

.loc-figure .photo {
  aspect-ratio: 1 / 1;
}

.loc-figure figcaption {
  margin-top: var(--space-2-5);
}

@media (min-width: 64rem) {
  .loc-grid {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    gap: var(--space-12);
    align-items: start;
  }
}

/* --- Сценарии ------------------------------------------------------------
   Четыре сценария, и у каждого под заголовком стоит не обещание, а перечень
   того, что за него играет, причём каждая строка перечня взята из
   характеристик выше и проверяема.

   Иконок нет: иконка «клиника» ничего не добавляет к строке «85 кВт двумя
   вводами», а ряд одинаковых карточек «иконка, заголовок, текст» — это
   раскладка по умолчанию, а не решение. */

.uses {
  display: grid;
  gap: var(--space-4);
}

.use {
  display: flex;
  flex-direction: column;
  border-radius: var(--radius-2xl);
  background: var(--color-card);
  padding: var(--space-6);
}

.use h3 {
  font-size: var(--text-h3);
}

.use-why {
  margin-top: var(--space-3);
  font-size: var(--text-body-lg);
  line-height: var(--leading-relaxed);
  color: var(--color-muted-foreground);
}

.use-evidence {
  display: grid;
  gap: var(--space-2);
  margin-top: var(--space-5);
  padding-top: var(--space-4);
  border-top: var(--border-width) solid var(--color-border);
}

.use-evidence li {
  display: grid;
  grid-template-columns: 14px 1fr;
  gap: var(--space-3);
  align-items: start;
  font-size: var(--text-body);
  line-height: var(--leading-relaxed);
}

.use-evidence li::before {
  content: "";
  height: 2px;
  width: 10px;
  margin-top: 0.75em;
  border-radius: var(--radius-full);
  background: var(--color-primary);
}

@media (min-width: 48rem) {
  .uses {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-5);
  }
}

/* --- Сделка --------------------------------------------------------------
   Единственный тёмный блок страницы (см. правило одного hero-gradient
   на экран у .card-dark).

   ШКАЛА «ЦЕНА ПРОТИВ ПОХОЖИХ» ЗДЕСЬ НАМЕРЕННО НЕ ПРИМЕНЕНА, хотя она есть
   в системе, есть токен --scale-gradient и она сквозной приём всей витрины.
   Причина: у этой шкалы жёсткий канон — зелёный слева (дешевле), красный
   справа (дороже), и её смысл в том, чтобы вынести вердикт «выгодно или
   дорого». А здесь показано отношение цены к КАДАСТРОВОЙ СТОИМОСТИ, и оно
   вердиктом не является: кадастровая стоимость — это база налога, а не
   рыночная оценка, и «на 49% выше кадастровой» само по себе не значит ни
   «дорого», ни «дёшево». Покрасить это в красный значило бы приписать
   объекту оценку, которой никто не давал, на странице, где каждое
   утверждение должно быть проверяемым фактом.

   Вместо шкалы — две полосы ОДНОГО тона, отличающиеся только длиной. Они
   показывают пропорцию и ничего не утверждают. Если кто-то захочет
   вернуть сюда зелёно-красную шкалу, сначала надо ответить, на основании
   чего выносится вердикт. */

.deal-grid {
  display: grid;
  gap: var(--space-8);
}

.deal-price .price {
  font-size: 34px;
}

.deal-terms {
  display: grid;
  gap: var(--space-5);
}

.deal-term dt {
  font-size: var(--text-secondary);
  font-weight: var(--weight-medium);
  color: hsl(0 0% 100% / 0.7);
}

.deal-term dd {
  margin: 0;
  margin-top: var(--space-1);
  font-size: var(--text-body-lg);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-normal);
}

.bars {
  display: grid;
  gap: var(--space-4);
  margin-top: var(--space-6);
  padding-top: var(--space-6);
  border-top: var(--border-width) solid hsl(0 0% 100% / 0.16);
}

.bar-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: var(--text-secondary);
  margin-bottom: var(--space-2);
}

.bar-head b {
  font-family: var(--font-heading);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  font-variant-numeric: tabular-nums;
  font-size: var(--text-body-lg);
}

.bar-track {
  height: 8px;
  border-radius: var(--radius-bar);
  background: hsl(0 0% 100% / 0.12);
  overflow: hidden;
}

/* Полосы СТАТИЧНЫ, и это тоже решение, а не недоделка.

   Здесь стояла единственная авторская анимация страницы: полосы въезжали
   клипом слева направо по появлению в зоне видимости, показывая отношение
   600 к 403. Убрана после проверки.

   Что выяснилось: IntersectionObserver не выдаёт колбэков во вкладке,
   которая не строит кадры (фоновая вкладка, свёрнутое окно, часть
   headless-окружений). В этом случае полосы оставались в стартовом
   состоянии clip-path: inset(0 100% 0 0), то есть шириной ноль, и данные
   становились НЕ ПРОСТО БЕЗ АНИМАЦИИ, А НЕВИДИМЫМИ.

   Анимация, которая при отказе прячет само число, на витрине с ценой
   недопустима. Подпереть таймером было можно, но сверх статичных полос
   движение здесь ничего не сообщало: пропорцию несёт длина полосы, а не
   то, как она появилась. Отказоустойчивость данных дороже движения.

   Если будете возвращать анимацию — стартовым состоянием должно быть
   КОНЕЧНОЕ, а клип навешиваться только после того, как наблюдатель
   реально подтвердил, что элемент за экраном. */
.bar-fill {
  height: 100%;
  border-radius: var(--radius-bar);
  background: var(--color-background);
}

.bar-fill-muted {
  background: hsl(0 0% 100% / 0.38);
}

@media (min-width: 64rem) {
  .deal-grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.1fr);
    gap: var(--space-12);
    align-items: start;
  }

  .deal-price .price {
    font-size: 44px;
  }
}

/* --- Предупреждение ------------------------------------------------------
   Заметно, но не истерично: это защита покупателя, а не скандал.

   Подложка — янтарный 12%, текст — navy (4,76:1 на этой подложке).
   Набирать сам янтарь ЧЕРНИЛАМИ нельзя: #cb7b0b даёт около 3,3:1 при
   требуемых 4,5, а тёмной ступени янтарного в каноне не объявлено (рецепт
   есть в комментарии у --color-brand, но токена нет, и заводить его здесь
   значило бы начать вторую палитру).

   Красный не взят СОЗНАТЕЛЬНО, хотя соблазн есть. В этой системе красный
   занят под «дороже» и удаление, а здесь не авария, а просьба сверить, с
   кем именно человек разговаривает. Красная плашка на странице продавца
   читается как «у объекта проблема», то есть бьёт по тому, кого защищает. */

.notice {
  display: grid;
  gap: var(--space-4);
  border-radius: var(--radius-2xl);
  background: hsl(var(--hsl-warning) / 0.12);
  padding: var(--space-6);
}

.notice-head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.notice-head .icon {
  color: var(--color-warning);
  width: 22px;
  height: 22px;
}

.notice p {
  font-size: var(--text-body-lg);
  line-height: var(--leading-relaxed);
  max-width: var(--container-text);
}

@media (min-width: 48rem) {
  .notice {
    padding: var(--space-8);
  }
}

/* --- Контакты и форма -----------------------------------------------------
   Кнопки собираются из js/config.js: незаполненное поле не рисует кнопку.
   Кнопка «Написать в Telegram», ведущая в никуда, хуже отсутствующей.
   Форма живёт в <template> и клонируется, только если задан endpoint или
   почта: без JS она не отрендерится вовсе, и страница не покажет поля,
   которые некому принять. */

.contact-grid {
  display: grid;
  gap: var(--space-10);
}

.contact-person {
  display: grid;
  gap: var(--space-1);
  margin-bottom: var(--space-6);
}

.contact-name {
  font-family: var(--font-heading);
  font-size: var(--text-h3);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
}

.contact-actions {
  display: grid;
  gap: var(--space-3);
}

.contact-btn {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--tap-target);
  border-radius: var(--radius-lg);
  border: var(--border-width) solid var(--color-border);
  background: var(--color-background);
  padding: var(--space-3) var(--space-4);
  transition: border-color var(--duration) var(--ease-default),
    background-color var(--duration) var(--ease-default),
    scale var(--duration) var(--ease-default);
}

.contact-btn:hover {
  border-color: hsl(var(--hsl-primary) / 0.4);
  background: var(--color-card);
}

.contact-btn:active {
  scale: var(--press-scale);
}

.contact-btn-label {
  display: block;
  font-size: var(--text-caption);
  font-weight: var(--weight-medium);
  color: var(--color-muted-foreground);
}

.contact-btn-value {
  display: block;
  font-size: var(--text-body-lg);
  font-weight: var(--weight-semibold);
  font-variant-numeric: tabular-nums;
}

.contact-btn .icon {
  color: var(--color-muted-foreground);
}

.contact-btn[data-track="click_phone"] .icon {
  color: var(--color-primary-text);
}

.contact-btn[data-track="click_telegram"] .icon {
  color: var(--color-telegram);
}

.contact-btn[data-track="click_whatsapp"] .icon {
  color: var(--color-whatsapp);
}

.form-grid {
  display: grid;
  gap: var(--space-4);
}

.form-row {
  display: grid;
  gap: var(--space-4);
}

/* Сам чекбокс визуально 18px по канону, и раздувать его нельзя. Тач-целью
   служит вся label: она оборачивает и квадратик, и текст, поэтому нажатие
   в любой её точке переключает согласие.
   Псевдоэлементом здесь площадь не добрать: input — замещаемый элемент и
   ::before/::after не рендерит вовсе. Поэтому до 44px доводится высота
   самой label через min-height. */
.form-consent {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  min-height: var(--tap-target);
  padding-block: var(--space-1);
  cursor: pointer;
  font-size: var(--text-caption);
  line-height: var(--leading-relaxed);
  color: var(--color-muted-foreground);
}

@media (min-width: 40rem) {
  .contact-actions {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .form-row {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (min-width: 64rem) {
  .contact-grid {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--space-12);
    align-items: start;
  }

  .contact-actions {
    grid-template-columns: 1fr;
  }
}

/* --- Подвал --------------------------------------------------------------- */

.footer-legal {
  font-size: var(--text-secondary);
  line-height: var(--leading-relaxed);
  color: hsl(0 0% 100% / 0.7);
  max-width: 68ch;
}

.footer-object {
  font-family: var(--font-heading);
  font-size: var(--text-h3-m);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-tight);
  margin-bottom: var(--space-2);
}

.footer-side {
  display: grid;
  gap: var(--space-4);
  flex-shrink: 0;
}

/* Наведение мышью — только там, где мышь есть. На тач-экране hover
   срабатывает по тапу и оставляет элемент подсвеченным после того, как
   палец убрали: выглядит как залипшая кнопка. */
@media (hover: none) {
  .btn-primary:hover,
  .btn-outline:hover,
  .btn-navy:hover,
  .btn-soft:hover,
  .contact-btn:hover,
  .mapcard:hover {
    background: revert-layer;
    border-color: revert-layer;
  }
}

/* ==========================================================================
   ПЕЧАТЬ
   ==========================================================================
   Это не украшение, а рабочий сценарий: страницу будут печатать и сохранять
   в PDF, чтобы показать руководству. Для объекта такой цены бумажная копия
   доходит до тех, кто ссылку не откроет.

   Липкая шапка, навигация, форма и кнопки-действия на бумаге бесполезны и
   убраны. Тёмный блок сделки выворачивается в белый с navy-текстом: тёмная
   заливка на печати съедает тонер и выходит серым пятном, по которому не
   читается ни цена, ни условия. Полосы пропорции перекрашены в navy и
   input — они должны различаться и в монохроме. Все многоколоночные сетки
   разворачиваются в один поток, break-inside не даёт разрыву страницы
   разрезать блок пополам, а к ссылке на карту допечатывается сам адрес:
   на бумаге ссылку не нажать. */

@media print {
  :root {
    --page-pad-x: 0px;
    --page-pad-x-md: 0px;
  }

  html {
    scroll-behavior: auto;
  }

  body {
    font-size: 10pt;
    line-height: 1.45;
    background: var(--color-background);
  }

  .site-header {
    position: static;
    background: var(--color-background);
    backdrop-filter: none;
  }

  .site-nav,
  .hero-actions,
  .contact-actions .contact-btn .icon,
  .lead-form,
  .footer-side {
    display: none !important;
  }

  .section,
  .hero {
    padding-block: var(--space-6);
  }

  .container-narrow {
    max-width: none;
  }

  /* Тёмный блок на бумаге печатается серым пятном. */
  .card-dark {
    background: var(--color-background);
    color: var(--color-foreground);
    border: var(--border-width) solid var(--color-input);
  }

  .card-dark .muted,
  .on-dark-secondary,
  .deal-term dt {
    color: var(--color-muted-foreground);
  }

  .bar-track {
    background: var(--color-border);
  }

  .bar-fill {
    background: var(--color-foreground);
  }

  .bar-fill-muted {
    background: var(--color-input);
  }

  .bars {
    border-top-color: var(--color-border);
  }

  .site-footer {
    background: var(--color-background);
    color: var(--color-foreground);
  }

  .footer-legal {
    color: var(--color-muted-foreground);
  }

  .notice {
    border: var(--border-width) solid var(--color-input);
    background: var(--color-background);
  }

  .use,
  .card-service,
  .mapcard {
    background: var(--color-background);
    border: var(--border-width) solid var(--color-border);
  }

  .surface-catalog {
    background: var(--color-background);
  }

  /* Разрыв страницы не должен резать блок пополам. */
  section,
  .use,
  .spec-group,
  .notice,
  figure {
    break-inside: avoid;
  }

  h1,
  h2,
  h3 {
    break-after: avoid;
  }

  .hero-grid,
  .loc-grid,
  .deal-grid,
  .contact-grid {
    grid-template-columns: 1fr;
    grid-template-areas: none;
  }

  .hero-figure .photo {
    aspect-ratio: 16 / 9;
    min-height: 0;
  }

  /* На бумаге ссылку не нажать, поэтому адрес печатается рядом. */
  .mapcard::after {
    content: " " attr(href);
    display: block;
    margin-top: var(--space-2);
    font-size: 8pt;
    color: var(--color-muted-foreground);
    word-break: break-all;
  }

  @page {
    margin: 14mm;
  }
}
