/* ── СЛОЙ 3: КОМПОНЕНТЫ ─────────────────────────────────────────────────
   Многоразовый интерфейс: шапка и подвал, кнопки, поля и селекты, поле дат,
   степперы, плитки, чипы, карточка объекта, шторка брони.

   Правило: если блок встречается больше чем на одной странице или живёт
   своей жизнью как самостоятельный элемент — он здесь. Страничные
   особенности того же блока лежат в pages.css и перебивают эти правила
   порядком подключения.

   Порядок слоёв и таблица точек перелома — в шапке tokens.css. */

/* Фокус на полях фильтров. Раньше мы гасили дефолтную обводку и рисовали своё
   кольцо (border+box-shadow) — но сборка вёрстки теперь сама задаёт единый
   фирменный фокус (main.css: :focus-visible{outline:2px solid var(--accent);
   outline-offset:2px;border-radius:4px}). Наш старый оверрайд был специфичнее
   и подменял чистую отступную обводку на прижатую к краю синюю рамку, которая
   читалась как «дефолтная браузерная» (жалоба автора 01.09: «фокус сломан»).
   Отдаём фокус сборке — один источник правды, обводка со смещением выглядит
   намеренной, mouse-клик по select фирменного кольца не даёт (Chrome гейтит
   дефолт через :focus-visible), клавиатура и клик по инпутам — дают.

   Сайдбар фильтров — скролл-контейнер (overflow), и раньше обводка со
   смещением +2px резалась его краем (жалоба автора 01.09), поэтому внутри
   фильтров кольцо вдавливали внутрь поля (offset -2px). С 08.09 рейл сам
   даёт кольцу место: внутренний отступ 4px (2px смещение + 2px обводка) на
   всех сторонах, см. .filters-rail ниже. Вдавленный вариант убран — кольцо
   фокуса одно на весь сайт, синее, со смещением (замечание автора 08.09:
   «рамка обрезана», «синим цветом»). */

/* Поле дат с календарём — без кольца фокуса (просьба автора 04.09). Клик по
   текстовому инпуту в Chrome даёт :focus-visible, и поверх открывшегося
   календаря рисовалась ещё и синяя рамка сборки. Индикатор фокуса здесь —
   сам открытый календарь: поле readonly, печатать в него нечего. */
[data-role="date-display"]:focus-visible {
    outline: none;
}

/* Обёртка группы фильтра: подпись над полем, 8px между ними. Один и тот же
   набор утилит стоял в разметке четырежды (рефакторинг 11.09). */
.filter-group {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

/* Колонка «подпись над полем» у минимума и максимума цены: было утилитами
   flex flex-col gap-1 на обоих <label>. */
.price-fields > label {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

/* ── форма фильтра ────────────────────────────────────────────────────── */

/* Единый ритм фильтра (выбор автора 07.09, мокап «Вариант 1»): подпись над
   полем у каждой группы, от подписи до поля 8px (gap-2 группы), между группами
   22px вместо gap-4. Подписи (.filter-cap) — 14px средним весом вторичным
   цветом, без капса (выбор автора 08.09, мокап «Вариант 2»); утилиты в
   разметке, здесь только межстрочное и подписи внутри полей цены. */
[data-role="filter-form"] > div > p {
    margin-bottom: 0;
}

form[data-role="filter-form"] {
    gap: 22px;
    /* было утилитами: flex flex-col (gap-4 тут и так перекрыт) */
    display: flex;
    flex-direction: column;
}

/* Ховер полей как у Airbnb: рамка темнеет до цвета текста. Чекбоксы и
   ползунки не трогаем — у них рамки нет. Селект apple-select ниже задаёт свой
   ховер 35% — здесь перекрываем более специфичным правилом. */
[data-role="filter-form"] :where(input, select) {
    transition: border-color .2s ease;
}

/* ── форма фильтра ────────────────────────────────────────────────────── */

.filter-cap {
    line-height: 1.3;
    /* было утилитами: text-sm font-medium text-fg-muted. Приглушённого
       цвета у подписей нет — его перекрывает правило ниже по файлу;
       letter-spacing: 0 приходил подпиской на .text-sm. */
    font-size: var(--text-sm);
    letter-spacing: 0;
    font-weight: var(--font-weight-medium);
}

/* Удобства сверх превью: в колонке скрыты, в попапе показаны (правка автора
   16.09). Набор плиток один и тот же — site.js переносит его в окно и обратно,
   поэтому «показать остальные» это вопрос места, а не второго списка. */
[data-role="amenities-pool"] > .tile-opt.is-extra {
    display: none;
}

/* В попапе показаны все: плитки лежат в секциях категорий, куда их
   раскладывает site.js. */
[data-role="amenities-bin"] > .tile-opt.is-extra {
    display: flex;
}

/* В окне плитка по содержимому, а не в растяжку: колонка фильтра узкая, и там
   растяжение заполняет строку, а на 860px оно раздувало «Сейф» до трети
   ширины. */
[data-role="amenities-bin"] > .tile-opt {
    flex: 0 0 auto;
    max-width: 100%;
}

/* Текст фильтра — основным цветом, не серым (правка автора 08.09): подписи
   групп, зоны поля дат, подписи цены. Серыми остаются только счётчики
   объектов у плиток и районов (уточнение автора); иконки плиток тоже
   приглушённые. Действует и в мобильной шторке. */
[data-role="filter-form"] .filter-cap,
[data-role="filter-form"] .date-duo-cap,
[data-role="filter-form"] .price-cap {
    color: var(--fg);
}

/* ── фильтр цены ──────────────────────────────────────────────────────── */

.price-cap {
    font-size: 13px;
    line-height: 1.2;
}

/* ── Фильтр цены: кривая распределения и два бегунка ─────────────────────
   Выбор автора 07.09 (мокап «Гистограмма 2»). Кривая — SVG с сервера,
   растянутый по ширине трека (preserveAspectRatio none); серый силуэт под
   всей шкалой и акцентный градиент внутри выбранного диапазона, который
   обрезает clipPath — его x/width двигает site.js вместе с бегунками.

   Два нативных input[type=range] лежат друг над другом поверх одного трека:
   сами инпуты прозрачны и курсор не ловят, ловят только ручки — иначе верхний
   инпут закрывал бы нижний целиком. Это стандартный приём двойного ползунка
   без библиотек: клавиатура, скринридер и тач работают как у обычного range.

   Кривая и трек имеют поля по 11px с боков: центр нативной ручки ходит от
   половины её ширины до ширины минус половина, и без этих полей подсветка на
   кривой расходилась бы с ручками у краёв. */
.price-range {
    position: relative;
    /* было утилитами: flex flex-col */
    display: flex;
    flex-direction: column;
}

/* Подсвеченная часть кривой — чисто декоративна: клики сквозь неё должны
   доходить до ползунков ниже, если ручка заехала на кривую. */
.price-curve-sel {
    pointer-events: none;
}

.price-curve {
    display: block;
    width: calc(100% - 22px);
    height: 44px;
    margin: 0 11px;
    color: var(--accent);
}

.price-curve-base {
    fill: var(--tint);
}

.price-track {
    position: relative;
    height: 4px;
    margin: 10px 11px 0;
    border-radius: 99px;
    background: var(--tint);
}

.price-fill {
    position: absolute;
    top: 0;
    bottom: 0;
    border-radius: 99px;
    background: var(--accent);
}

.price-thumb {
    position: absolute;
    left: -11px;
    right: -11px;
    top: 50%;
    width: calc(100% + 22px);
    height: 22px;
    margin: 0;
    transform: translateY(-50%);
    background: transparent;
    appearance: none;
    -webkit-appearance: none;
    pointer-events: none;
}

.price-thumb::-webkit-slider-runnable-track {
    background: transparent;
    border: 0;
}

.price-thumb::-moz-range-track {
    background: transparent;
    border: 0;
}

.price-thumb::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 22px;
    height: 22px;
    border-radius: 99px;
    background: var(--surface);
    border: 2px solid var(--accent);
    box-shadow: 0 2px 6px rgba(12, 21, 42, .18);
    cursor: grab;
    pointer-events: auto;
}

.price-thumb::-moz-range-thumb {
    width: 18px;
    height: 18px;
    border-radius: 99px;
    background: var(--surface);
    border: 2px solid var(--accent);
    box-shadow: 0 2px 6px rgba(12, 21, 42, .18);
    cursor: grab;
    pointer-events: auto;
}

.price-thumb:active::-webkit-slider-thumb {
    cursor: grabbing;
}

/* Когда обе ручки в правом краю, верхняя (максимум) закрывает нижнюю —
   site.js поднимает минимум классом, чтобы его можно было утащить. */
.price-thumb.is-top {
    z-index: 2;
}

/* Поля «Минимум/Максимум»: было утилитами h-10 w-full rounded-md border
   border-border-strong bg-surface px-3 text-fg (рефакторинг 11.09).

   Класс, а не селектор .price-fields input, и стоит он здесь, а не ниже,
   намеренно. Десктопная рельса фильтра переопределяет фон, радиус и цвет
   рамки правилом .filters-rail :where(input…, select) — у него специфичность
   всего 0,1,0, потому что :where() не добавляет ничего. Прежние утилиты были
   такими же 0,1,0 и стояли раньше по каскаду, то есть рельсе проигрывали.
   Селектор .price-fields input дал бы 0,1,1 и молча перебил бы рельсу: поля
   получили бы белый фон, радиус 12 и видимую рамку вместо подложки без рамки.

   Цвет рамки здесь var(--border), а не var(--border-strong) из разметки:
   безслойное * { border-color: var(--border) } в хвосте main.css бьёт любую
   слоевую утилиту, поэтому border-border-strong не работал никогда. Поля px-3
   перекрывались правилом .price-fields input ниже, где они сужены до 10px. */
.price-input {
    height: 40px;
    width: 100%;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface);
    color: var(--fg);
}

/* Фокус с клавиатуры показываем на ручке, а не рамкой во всю ширину. Кольцо
   такое же, как у всего сайта (2px accent с 2px зазором), только тенью:
   outline у псевдоэлемента ручки Chrome не рисует. Раньше было бледное гало
   accent-subtle — не читалось как фокус (замечание автора 08.09: «синим
   цветом»). */
.price-thumb:focus-visible {
    outline: none;
}

.price-thumb:focus-visible::-webkit-slider-thumb {
    box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent), 0 2px 6px rgba(12, 21, 42, .18);
}

.price-thumb:focus-visible::-moz-range-thumb {
    box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--accent), 0 2px 6px rgba(12, 21, 42, .18);
}

.price-fields {
    display: grid;
    grid-template-columns: 1fr 12px 1fr;
    align-items: end;
    gap: 6px;
    margin-top: 16px;
}

/* Поля «Минимум/Максимум»: плейсхолдер с границей диапазона («฿ 32 000»)
   должен читаться целиком (замечание автора 07.09). В колонке 264px каждому
   полю остаётся около 120px, и нативные стрелки number-инпута срезали хвост —
   стрелки убираем (шаг всё равно задают ползунки), отступы сужаем. */
.price-fields input {
    padding-left: 10px;
    padding-right: 10px;
    -moz-appearance: textfield;
    appearance: textfield;
    /* 14px, как подписи и остальной текст фильтра (просьба автора 09.09): у
       input в сборке font: inherit, то есть 16px от body. Ниже 768px сборка
       жёстко ставит 16px — так и надо, иначе iOS зумит страницу по фокусу. */
    font-size: 14px;
}

.price-fields input::-webkit-outer-spin-button,
.price-fields input::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.price-dash {
    height: 40px;
    display: flex;
    align-items: center;
    justify-content: center;
    /* Тире в один размер с полями, иначе оно крупнее их текста. */
    font-size: 14px;
    color: var(--fg-subtle);
}

/* ── apple-select ─────────────────────────────────────────────────────── */

/* Рамка полей фильтра под курсором. Селекты из перечисления убраны: внутри
   формы фильтра их нет ни одного, а отдельная строка под .apple-select стояла
   там с тех пор, когда сортировка жила в той же форме (рефакторинг 13.09). */
[data-role="filter-form"] :where(input:not([type="checkbox"]):not([type="range"])):hover {
    border-color: var(--fg);
}

/* Дропдауны фильтров в духе apple.com (правка автора 31.08): нативную
   стрелку убираем (appearance:none), рисуем свой тонкий шеврон data-URI
   (цвет #86868b — фирменный серый Apple, читается и на светлом, и на
   тёмном), плавные переходы на ховер/фокус. Разметке достаточно класса
   apple-select — рамка/фон остаются от утилит конкретного места
   (планка главной — прозрачный, сайдбар и сортировка — с рамкой). */
select.apple-select {
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' viewBox='0 0 24 24' fill='none' stroke='%2386868b' stroke-width='2.25' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
    background-size: 14px 14px;
    padding-right: 32px;
    padding-top: 6.4px;
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

/* Высокий вариант: селект в колонке полей h-11, сейчас это форма брони.
   Нативный select текст по вертикали сам не центрует (в отличие от input), а
   padding-top базового правила подогнан под компактный селект сортировки.
   Центруем явно: line-height = высота минус рамки, паддинги в ноль. Раньше
   правило цеплялось за форму снаружи (рефакторинг 13.09). */
select.apple-select--tall {
    padding-top: 0;
    padding-bottom: 0;
    line-height: calc(44px - 2px);
    /* Коробка была утилитами h-11 rounded-md border border-border-strong
       bg-surface px-3 (рефакторинг 13.09). Цвет рамки пишем тот, что
       рисовался: border-border-strong в сборке мёртв, его гасит безслойное
       * { border-color: var(--border) }. Правое поле не трогаем — под шеврон
       его задаёт базовое правило. */
    height: 44px;
    border-radius: 12px;
    border: 1px solid var(--border);
    background-color: var(--surface);
    padding-left: 12px;
    cursor: pointer;
}

select.apple-select:hover {
    border-color: color-mix(in srgb, var(--fg, #1d1d1f) 35%, transparent);
}

/* IE/старый Edge не в поддержке, но ::-ms-expand дешёвый — пусть будет. */
select.apple-select::-ms-expand {
    display: none;
}

/* Безрамочный вариант: селект в поисковой планке главной. Прозрачный и без
   рамки — шеврон прижимаем к краю, чтобы не распирать ячейку. Раньше это
   правило цеплялось за планку снаружи, [data-role="hero-search"] select, то
   есть вид селекта дописывался из страничного слоя (рефакторинг 13.09).

   Вертикальные паддинги в ноль и явная line-height (замечание автора 04.09:
   «Любой район», «2» и «Заезд → Выезд» стояли на разных уровнях). Причина —
   appearance: base-select добавляет кнопке свой padding-block, и текст селекта
   уезжал на 6px ниже текста date-инпута рядом. Теперь у всех трёх ячеек
   строка ровно 24px и одна базовая линия; фиксированной высоты нет, поэтому
   в браузерах без base-select ничего не меняется. */
select.apple-select--bare {
    background-position: right 0 center;
    padding-right: 24px;
    padding-top: 0;
    padding-bottom: 0;
    line-height: 24px;
}

/* Раскрытый список — тоже в стиле apple.com (правка автора 01.09, финальный
   мокап): белая панель со скруглением и тенью, галочка у выбранного, ховер
   строк фирменным tint. Новый стандарт appearance:base-select — понимает
   Chromium; Firefox/Safari весь блок игнорируют (@supports) и раскрывают
   нативный список ОС, закрытое состояние у них прежнее. Ноль JS. */
@supports selector(::picker(select)) {
    select.apple-select,
    select.apple-select::picker(select) {
        appearance: base-select;
    }

    /* Свой шеврон уже нарисован фоном — системную иконку кнопки убираем. */
    select.apple-select::picker-icon {
        display: none;
    }

    /* В base-select кнопка — обычный блок, и при фиксированной высоте
       (h-10/h-11 из утилит) текст прилипал к верху (замечание автора
       01.09). Центрируем содержимое кнопки по вертикали. */
    select.apple-select {
        align-content: center;
    }

    /* В base-select кнопка получает UA-рамку; безрамочному варианту
       возвращаем как было. Сортировке рамку задают её утилиты, они сильнее
       UA-стилей. */
    select.apple-select--bare {
        border: none;
    }

    select.apple-select::picker(select) {
        border: none;
        border-radius: 14px;
        background: var(--surface);
        box-shadow: var(--sh-modal);
        padding: 8px;
        margin-top: 6px;
        min-width: 230px;
    }

    /* ВАЖНО: все стили строк скоупим на ::picker(select) — раскрытый список.
       Без этого padding/флекс/галочка «протекают» в ЗАКРЫТУЮ кнопку (её
       содержимое — клон выбранного option через ::selectedcontent), и
       плейсхолдер съезжает вправо-вниз на ширину гуттера ✓ и паддинги
       строки (баг автора 01.09). В кнопке текст должен идти от px-3 разметки. */
    select.apple-select::picker(select) option {
        display: flex;
        align-items: center;
        padding: 11px 18px 11px 10px;
        border-radius: 8px;
        font-size: 14px;
        font-weight: 500;
        color: var(--fg);
        cursor: pointer;
    }

    select.apple-select::picker(select) option:hover {
        background: var(--surface-hover);
    }

    /* Галочка своя: гуттер под неё зарезервирован у ВСЕХ строк, текст не
       прыгает (у системной ::checkmark место резервируется только у
       выбранной). */
    select.apple-select::picker(select) option::checkmark {
        display: none;
    }

    select.apple-select::picker(select) option::before {
        content: "";
        width: 24px;
        flex: 0 0 auto;
    }

    select.apple-select::picker(select) option:checked {
        font-weight: 600;
    }

    select.apple-select::picker(select) option:checked::before {
        content: "✓";
    }
}

/* ── поле дат ─────────────────────────────────────────────────────────── */

/* Пустое input[type=date] показывает системную маску mm/dd/yyyy — она зависит
   от локали браузера и ничего не говорит пользователю. Пока значение не
   выбрано, прячем маску и рисуем свою подпись из data-ph. */
/* ВАЖНО: только у ПУСТОГО поля. Без [data-empty="1"] правило прятало текст
   у всех незафокушенных полей, и выбранная дата не отображалась вовсе —
   поле выглядело пустым, хотя значение в нём было (найдено 27.08).

   Цвет гасим у САМОГО поля, а не у ::-webkit-datetime-edit: до 15.09 правило
   висело на псевдоэлементе и не срабатывало — маска mm/dd/yyyy оставалась
   видна и налезала на нашу подпись (замечание автора 15.09). Гасить поле
   целиком безопасно: правило живёт только при пустом значении, прятать
   нечего, а подпись рисуется ::before со своим цветом. */
input.date-ph[data-empty="1"]:not(:focus) { color: transparent; }

input.date-ph:not(:focus) { position: relative; }

input.date-ph[data-empty="1"]:not(:focus)::before {
    content: attr(data-ph);
    position: absolute;
    left: 12px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--fg-subtle, #8a8f98);
    pointer-events: none;
}

input.date-ph[data-empty="1"]:not(:focus)::-webkit-calendar-picker-indicator { opacity: .5; }

/* Даты в фильтре каталога одним полем (выбор автора 07.09, мокап «Вариант 2»):
   одна рамка, две подписанные зоны «Заезд | Выезд» через черту, как у Airbnb.
   Внутри — те же нативные date-инпуты без собственных рамок. Радиус как у
   rounded-md сборки (12px). Ховер и фокус — рамкой, без кольца: индикатором
   служит открытый календарь. */
.date-duo {
    display: grid;
    grid-template-columns: 1fr 1fr;
    border: 1px solid var(--border-strong);
    border-radius: 12px;
    background: var(--surface);
    transition: border-color .2s ease, box-shadow .2s ease;
}

.date-duo:hover { border-color: var(--fg); }

.date-duo:focus-within { border-color: var(--accent); }

.date-duo-cell {
    position: relative;
    display: flex;
    align-items: center;
    min-width: 0;
    height: 44px;
    padding: 0 12px;
    cursor: pointer;
}

.date-duo-cell + .date-duo-cell { border-left: 1px solid var(--border); }

/* Подпись зоны лежит поверх инпута; когда дата выбрана — прячется, и её
   место занимает сама дата (правка автора 08.09: «Дата» как плейсхолдер убрана). */
/* Строчными, 13px, вторичным цветом — как остальные внутренние подписи
   фильтра (выбор автора 08.09, мокап «Вариант 2»: капс снят везде). */
.date-duo-cap {
    position: absolute;
    left: 12px;
    top: 50%;
    transform: translateY(-50%);
    font-size: 14px; /* 14px — правка автора 08.09 */
    font-weight: 400;
    line-height: 1.2;
    color: var(--fg-muted);
    pointer-events: none;
}

.date-duo-cell:has(.date-duo-input[data-empty="0"]) .date-duo-cap {
    display: none;
}

.date-duo-input {
    width: 100%;
    min-width: 0;
    height: 24px;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--fg);
    font: inherit;
    font-size: 14px;
    outline: none;
}

/* Пустой инпут невидим (его маску «дд.мм.гггг» и подсветку сегментов гасим),
   кликабельным остаётся: клик по зоне открывает календарь. Нативную иконку
   календаря прячем, только когда построен наш (.rp ставит site.js). */
.date-duo-input[data-empty="1"]::-webkit-datetime-edit { color: transparent; }

/* ── календарь дат ────────────────────────────────────────────────────── */

.rp .date-duo-input::-webkit-calendar-picker-indicator { display: none; }

.rp input.date-duo-input:focus::-webkit-datetime-edit-day-field,
.rp input.date-duo-input:focus::-webkit-datetime-edit-month-field,
.rp input.date-duo-input:focus::-webkit-datetime-edit-year-field { background: transparent; }

.rp input.date-duo-input[data-empty="0"]:focus::-webkit-datetime-edit-day-field,
.rp input.date-duo-input[data-empty="0"]:focus::-webkit-datetime-edit-month-field,
.rp input.date-duo-input[data-empty="0"]:focus::-webkit-datetime-edit-year-field { color: var(--fg); }

/* Выбор диапазона дат на карточке объекта.

   Нативный input[type=date] умеет только min/max — исключить отдельные дни
   внутри диапазона ему нечем, и гость выбирал занятый период, узнавая об этом
   только после расчёта. Календарь рисует site.js поверх тех же инпутов:
   без JS остаётся прежнее поведение, поэтому классы нужны только когда
   календарь построен.

   Живёт здесь, а не в main.css: тот заморожен и пересборке не подлежит. */
.rp {
    position: relative;
}

/* Стрелки стоят в одной строке с названиями месяцев (замечание автора 04.09:
   «место месяцов между стрелочками»). Строка навигации ложится поверх строки
   заголовков: сама она кликов не ловит, только её кнопки. Заголовок месяца
   центрирован в своей колонке и по высоте равен кнопке, поэтому «‹ Сентябрь ·
   Октябрь ›» читается одной строкой; при одном месяце в колонке (узкий экран)
   стрелки остаются у первого заголовка. */
.rp-head {
    position: absolute;
    top: 12px;
    left: 12px;
    right: 12px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    pointer-events: none;
}

.rp-head .rp-nav { pointer-events: auto; }

.rp-title {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 32px;
    font-weight: 600;
    font-size: 14px;
}

.rp-nav {
    width: 32px;
    height: 32px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: inherit;
    cursor: pointer;
    font-size: 16px;
    line-height: 1;
}

.rp-nav:hover:not(:disabled) { background: var(--surface-hover, rgba(0, 0, 0, .06)); }

.rp-nav:disabled { opacity: .35; cursor: default; }

.rp-week, .rp-days {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 2px;
}

.rp-wd {
    text-align: center;
    font-size: 11px;
    color: var(--fg-muted, #71717a);
    padding-bottom: 4px;
}

.rp-day {
    /* 36px — нижняя граница удобного нажатия пальцем. */
    min-height: 36px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: inherit;
    font-size: 13px;
    cursor: pointer;
}

.rp-day:hover:not(:disabled) { background: var(--surface-hover, rgba(0, 0, 0, .06)); }

.rp-day:disabled {
    /* Недоступный день (занят или прошёл) — просто бледный, БЕЗ зачёркивания
       (правка автора 01.09): перечёркивание читалось как «занято» даже для
       прошедших дат. Приглушаем цветом и прозрачностью, ничего не черкаем. */
    color: var(--fg-subtle, #a1a1aa);
    opacity: .5;
    cursor: not-allowed;
}

.rp-day.is-empty { visibility: hidden; }

.rp-day.is-edge { background: var(--accent, #2563eb); color: #fff; }

.rp-day.is-inside { background: color-mix(in srgb, var(--accent, #2563eb) 18%, transparent); }

.rp-hint {
    margin-top: 8px;
    font-size: 12px;
    color: var(--fg-muted, #71717a);
}

@media (min-width: 480px) {
    /* Два месяца рядом: выбирая неделю на стыке, не надо листать туда-сюда. */
    .rp-months { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
}

/* Слайдшоу галереи объекта.

   Разметка — эталонная (demo /en/object), но библиотеки Swiper у нас нет: в
   собранном main.css от неё лежат только правила запасного вида, где без JS
   виден первый кадр. Раскладку ленты держим сами и здесь, а не в main.css —
   тот заморожен и пересборке не подлежит.

   Правила включаются классом swiper-initialized, который ставит site.js: до
   него работает запасной вид из main.css, и при отключённом JS галерея не
   разъезжается в вертикальный столбец. */
/* Обёртка и слайд — во всю высоту контейнера с aspect-ratio: иначе h-full и
   size-full у кадра не срабатывают, картинка рисуется в своей пропорции и в
   контейнере остаются пустые поля (на мобильном снизу; замечание автора 08.09),
   а на десктопе кадр вылезал за 16:9 и обрезался снизу вместо центрирования. */
[data-role="gallery"] .swiper-wrapper,
[data-role="gallery"] .swiper-slide {
    height: 100%;
}

/* ── swiper ───────────────────────────────────────────────────────────── */

/* Смена кадра — растворением, а не сдвигом ленты (выбор автора 16.09, мокап
   «Вариант 1»). Кадры лежат друг на друге: сдвиг требовал ширины ленты в N
   экранов и «дёргал» соседние фотографии на границе жеста, а растворение
   ничего не двигает — на странице объекта работает фотография, а не движение. */
.swiper.swiper-initialized .swiper-wrapper {
    display: block;
    position: relative;
    height: 100%;
}

.swiper.swiper-initialized .swiper-slide {
    position: absolute;
    inset: 0;
    width: 100%;
    margin: 0;
    opacity: 0;
    transition: opacity .5s ease;
}

.swiper.swiper-initialized .swiper-slide.is-shown {
    opacity: 1;
}

/* Приближения у кадров галереи НЕТ (просьба автора 17.09).
   Пробовали ken-burns как у героя главной и отказались: на главной кадр один и стоит
   фоном, а здесь гость разглядывает жильё и сравнивает снимки между собой — подъезжающий
   кадр этому мешает. Класс is-browsed и пауза анимации ушли вместе с ней. */

/* ── Управление галереей: деления слева, стрелки справа ────────────────
   Раскладка с образца автора (16.09, мокап «Вариант 2»): деления как в сторис
   в левом нижнем углу кадра, стрелки в правом нижнем. Счётчика «1 / 3» больше
   нет — деления говорят и сколько кадров, и сколько уже просмотрено. */
.gal-bar {
    position: absolute;
    inset: auto 16px 16px 16px;
    z-index: 2;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 20px;
}

/* Подложка под рядом: белое на светлом небе или белой стене пропадает, а
   фотографии у объектов и дневные, и вечерние. Градиент почти не виден как
   приём, зато держит читаемость на любом кадре. */
.gal-scrim {
    position: absolute;
    inset: auto 0 0 0;
    z-index: 1;
    height: 96px;
    pointer-events: none;
    background: linear-gradient(to top, rgba(0, 0, 0, .45), transparent);
}

.gal-segs {
    display: flex;
    align-items: center;
    gap: 6px;
}

/* Деление — полоска, а не точка: пройденные заливаются целиком, и «просмотрено
   две трети» читается быстрее, чем «шестая из девяти». */
/* Заливку делаем фоном-градиентом, а не вложенным элементом: у вложенного
   пришлось бы обрезать выход за края (overflow: hidden), а он заодно обрезал
   бы и накладку, расширяющую область нажатия. */
.gal-segs button {
    position: relative;
    width: 24px;
    height: 2px;
    padding: 0;
    border: 0;
    cursor: pointer;
    background-image: linear-gradient(to right, #fff 50%, rgba(255, 255, 255, .35) 50%);
    background-size: 200% 100%;
    background-position: right center;
    transition: background-position .45s ease;
}

.gal-segs button.is-shown,
.gal-segs button.is-done {
    background-position: left center;
}

/* Четыре деления на телефоне, шесть от планшета (просьба автора 16.09).
   Число задаётся здесь и только здесь — site.js его замеряет, а не повторяет.
   Разметка всегда отдаёт шесть кнопок; лишние на узком экране прячет это
   правило, а в неполном последнем круге — атрибут hidden из js. */
.gal-segs button:nth-child(n+5) {
    display: none;
}

@media (min-width: 768px) {
    .gal-segs button:nth-child(n+5) {
        display: block;
    }
}

/* Правило выше перебивает [hidden] браузера — возвращаем ему силу. */
.gal-segs button[hidden] {
    display: none;
}

/* Смена круга: деления гаснут разом и БЕЗ перехода. С переходом шесть полосок
   одновременно «отматывались» назад, и та единственная, что должна была
   залиться заново, терялась в этой ряби. */
.gal-segs.is-turning button {
    transition: none;
}

/* Область нажатия больше самой полоски: два пикселя высоты пальцем и курсором
   не поймать, а промах уходил в кадр и открывал окно вместо листания
   (замечание автора 16.09). Невидимая накладка расширяет цель до 28 px по
   высоте, не трогая вид. */
.gal-segs button::before {
    content: "";
    position: absolute;
    inset: -13px -2px;
}

.gal-segs button:focus-visible {
    outline: 2px solid #fff;
    outline-offset: 3px;
}

.gal-arrows {
    display: inline-flex;
    gap: 12px;
}

/* Весь кадр — кнопка «все фото». Прозрачная и без рамки: видимая кнопка поверх
   фотографии спорила бы с делениями, а курсор-лупа и так объясняет, что будет. */
.gal-open {
    position: absolute;
    inset: 0;
    z-index: 1;
    border: 0;
    padding: 0;
    background: none;
    cursor: zoom-in;
}

.gal-open:focus-visible {
    outline: 2px solid #fff;
    outline-offset: -4px;
}

/* Все фотографии в окне — «тетрисом» (выбор автора 16.09, мокап «Вариант 11»):
   два кадра в ряд, под ними один во всю ширину, и снова пара. Колонка одинаковых
   кадров читалась как лента без начала и конца; смена ритма даёт глазу опору и
   показывает больше на одном экране.

   Пропорции задаёт сетка, кадр под неё обрезается по центру. Это осознанный
   размен: ровные ряды важнее, чем сохранить оригинальные пропорции каждого
   снимка (у объектов они всё равно разные — от 3:2 до 16:9). */
.gal-mosaic {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/* Ряды не сжимаются: у флекс-элемента shrink по умолчанию единица, и вместо
   прокрутки окно ужимало сами фотографии до высоты окна — прокручивать было
   нечего (замечание автора 16.09). */
.gal-mosaic > * {
    flex: 0 0 auto;
}

.gal-mosaic-row {
    display: grid;
    grid-template-columns: 1fr;
    gap: 10px;
}

@media (min-width: 640px) {
    .gal-mosaic-row {
        grid-template-columns: 1fr 1fr;
    }
}

.gal-mosaic figure {
    overflow: hidden;
    border-radius: 10px;
    aspect-ratio: 4 / 3;
}

.gal-mosaic .gal-mosaic-wide {
    aspect-ratio: 16 / 7;
}

.gal-mosaic img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Ролик в окне галереи (19.09).

   contain, а не cover как у снимков: кадр видео обрезать нельзя — вместе с
   краями уедут и органы управления браузера, до кнопки «во весь экран» будет
   не дотянуться. Свободное место по краям закрашиваем чёрным, как в
   кинозале, — так ролик любой пропорции выглядит намеренно, а не сломанно. */
.gal-mosaic video {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: contain;
    background: #0b0e12;
}

/* Плитка ролика выше снимка: 16/7 режет вертикальные ролики с телефона
   почти целиком, а 16/9 — обычная пропорция съёмки. */
.gal-mosaic .gal-mosaic-wide:has(video) {
    aspect-ratio: 16 / 9;
}

/* Уважаем системную настройку «меньше движения»: листание остаётся, исчезает
   только его анимация. */
@media (prefers-reduced-motion: reduce) {
    .swiper.swiper-initialized .swiper-slide,
    .gal-thumb { transition: none; }
}

/* Список районов. Мобайл: строки во всю ширину (В5). */
.district-chips {
    display: flex;
    flex-direction: column;
    margin: 0;
    padding: 0;
    list-style: none;
}

.district-chip {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    min-height: 44px;
    padding: 8px 4px;
    border-bottom: 1px solid var(--border);
    font-weight: 600;
    color: var(--fg);
    transition: color .3s ease, border-color .3s ease, box-shadow .3s ease, background-color .3s ease;
}

.district-chip-n {
    font-size: 13px;
    font-weight: 600;
    color: var(--accent);
    background: var(--accent-subtle);
    border-radius: 99px;
    padding: 2px 9px;
}

/* ── Вторичная кнопка ─────────────────────────────────────────────────
   Контурная кнопка рядом с фирменной: «WhatsApp»/«Telegram» на странице
   объекта и «Сбросить фильтры» в пустом списке каталога. Набор из тринадцати
   утилит стоял трижды (рефакторинг 11.09).

   Цвет рамки здесь var(--border), а не var(--border-strong) из разметки:
   border-border-strong не работал никогда — его бьёт безслойное
   * { border-color: var(--border) } в хвосте main.css.

   Высота, поля и размер текста остались утилитами — по той же причине, что и
   у .btn: у кнопок они разные, а font-size вдобавок несёт подписку
   letter-spacing. */
.btn-ghost {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--border);
    border-radius: 12px;
    font-weight: var(--font-weight-medium);
    /* Размер по умолчанию: h-11 text-sm. Полей нет, их добавляют
       варианты: у кнопок в сетке страницы объекта ширина от сетки. */
    height: 44px;
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
    color: var(--fg);
    transition-property: var(--tr-colors);
    transition-duration: var(--tr-duration);
    transition-timing-function: var(--tr-ease);
}

.btn-ghost:hover {
    background-color: var(--surface-hover);
}

/* ── Стрелки галереи на странице объекта ──────────────────────────────
   Светлые скруглённые квадраты в правом нижнем углу кадра (образец автора
   16.09). Раньше это были две крупные круглые кнопки по краям кадра поверх
   фотографии; они перекрывали сюжет и на телефоне прятались вовсе. Теперь обе
   стоят рядом в общем ряду управления и видны на всех экранах: листать пальцем
   по-прежнему можно, но кнопка больше не исчезает. */
.gal-nav {
    position: relative;
    display: grid;
    place-items: center;
    width: 32px;
    height: 32px;
    border: 0;
    border-radius: 7px;
    background: rgba(255, 255, 255, .88);
    color: #1b1f2b;
    font-size: 14px;
    line-height: 1;
    cursor: pointer;
    -webkit-backdrop-filter: blur(var(--blur-sm));
    backdrop-filter: blur(var(--blur-sm));
    transition-property: var(--tr-colors);
    transition-duration: var(--tr-duration);
    transition-timing-function: var(--tr-ease);
}

/* То же расширение цели, что у делений: промах по стрелке попадал в кадр и
   открывал окно вместо листания. Видимый размер прежний, цель — 52×44. */
.gal-nav::before {
    content: "";
    position: absolute;
    inset: -6px -10px;
}

.gal-nav:hover {
    background: #fff;
}

/* ── Заголовки блоков и лид-абзац ─────────────────────────────────────
   Типографика секций: заголовок страницы, заголовок секции и абзац под ним.
   Наборы стояли в разметке двенадцать, пять и четыре раза (рефакторинг 11.09).
   Значения из сборки один в один.

   Про межбуквенное у .sec-title: у утилиты .text-h2 своё -.025em, но рядом в
   разметке всегда стояла tracking-[-0.03em] и его перебивала — заголовки
   секций намеренно плотнее, чем по умолчанию. Здесь записано то, что
   получается на экране. */
/* Заголовок поверх кадра: «Контакты». Белый независимо от темы — под ним
   фотография с тинтом. Было утилитой text-white в разметке. */
.page-title--on-photo {
    color: #fff;
}

.page-title {
    font-size: clamp(32px, 4vw, 48px);
    line-height: 1.1;
    letter-spacing: -.03em;
    font-weight: var(--font-weight-bold);
}

.sec-title {
    font-size: clamp(24px, 3vw, 36px);
    line-height: 1.15;
    letter-spacing: -.03em;
    font-weight: var(--font-weight-bold);
    /* Было утилитой text-balance в восьми местах из девяти. Заголовок
       секции всегда балансируется по строкам. */
    text-wrap: balance;
}

.sec-lead {
    margin-top: 16px;
    max-width: 672px;
    font-size: var(--text-lg);
    line-height: var(--text-lg--line-height);
    color: var(--fg-muted);
}

/* Моноширинные цифры. Отдельным классом, потому что стоят на суммах в разных
   блоках, а смысла сверх выравнивания колонки у них нет. */
.num {
    font-variant-numeric: tabular-nums;
}

/* Карточка на подложке с мягким скруглением: отзывы на «О компании», блоки
   «Инвестиций». bg-tint остаётся в разметке — у него подписка на рамку в
   тёмной теме. */
.soft-card {
    border-radius: 28px;
    padding: 24px;
}

@media (min-width: 768px) {
    .soft-card {
        padding: 28px;
    }
}

/* Приглушённый текст: подписи, пояснения, вторичные строки. Голым классом
   встречался в десяти местах разных шаблонов (рефакторинг 13.09). Размерные
   варианты отдельно, потому что кегль у них разный. */
.note-muted-sm {
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
    color: var(--fg-muted);
}

.note-muted-xs {
    font-size: var(--text-xs);
    line-height: var(--text-xs--line-height);
    letter-spacing: 0;
    color: var(--fg-muted);
}

/* Ссылка-сброс: «Сбросить всё» в фильтре, «Показать все» на объекте. */
/* Ссылка «Сбросить всё» в мобильной шторке фильтра стоит во всю ширину, и
   текст в ней по центру. Было утилитой text-center. */
.filters-reset {
    text-align: center;
}

.link-reset {
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
    text-decoration-line: underline;
    cursor: pointer;
}

.link-reset--muted {
    color: var(--fg-muted);
}

.link-reset:hover {
    color: var(--fg);
}

/* Размеры значков. Имена по размеру, а не по месту: значок один и тот же в
   разных блоках, и смысла сверх размера у него нет. */
.icon-xs {
    width: 16px;
    height: 16px;
}

.icon-sm {
    width: 20px;
    height: 20px;
}

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

/* Картинка или видео во всю площадь рамки. */
.media-fill {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Ссылка «Смотреть все» рядом с заголовком раздела. Цвет ссылочный, а не
   акцентный: в разметке стояло text-accent, но site-wide правило
   a.text-accent переводит его в --link. Замер это подтвердил. */
.more-link {
    align-items: center;
    gap: 6px;
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
    font-weight: var(--font-weight-medium);
    color: var(--link);
}

/* Вариант заголовка секции, у которого кегль приходит от родителя: заголовок
   каталога в шапке страницы. В разметке у него стояли font-bold и
   tracking-[-0.03em] без text-h2, то есть всё то же, кроме размера. */
.sec-title--inherit {
    letter-spacing: -.03em;
    font-weight: var(--font-weight-bold);
    text-wrap: balance;
}

/* Подзаголовок внутри блока: шаги на главной, пункты «Инвестиций». */
.sub-title {
    font-size: var(--text-xl);
    line-height: var(--text-xl--line-height);
    letter-spacing: -.02em;
    font-weight: var(--font-weight-semibold);
}

/* Приглушённая подпись под заголовком или пунктом. */
.note-muted {
    color: var(--fg-muted);
}

/* Строка согласия на обработку данных с чекбоксом. */
/* Ссылка на политику внутри строки согласия — подчёркиванием. Было утилитой
   underline в партиале. */
.consent-row a {
    text-decoration-line: underline;
}

.consent-row {
    display: flex;
    min-height: 44px;
    align-items: center;
    gap: 10px;
    cursor: pointer;
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
}

/* ── Поле формы ───────────────────────────────────────────────────────
   Общее ядро полей ввода: формы заявок на «Контактах», лид-форма, форма брони
   на странице объекта, поля в модалках — двадцать одно место (рефакторинг
   11.09).

   Цвет текста остался утилитой намеренно: у двух селектов он задан выражением
   Blade, и color в классе перебил бы приглушённый вариант. Высоты и отступы
   тоже остались утилитами — их три и четыре набора соответственно.

   Цвет рамки здесь var(--border), а не var(--border-strong), как написано в
   разметке: безслойное * { border-color: var(--border) } в хвосте main.css
   бьёт любую слоевую утилиту, поэтому border-border-strong не работал никогда.

   Правило стоит выше блока рельсы фильтра намеренно: та переопределяет фон и
   радиус селектором .filters-rail :where(input…, select) со специфичностью
   0,1,0 (:where() не добавляет ничего), и прежние утилиты ей проигрывали по
   порядку. Перенеси .field ниже — и поля фильтра получат белый фон вместо
   подложки. */
.field {
    width: 100%;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface);
    /* было утилитой text-fg на каждом поле */
    color: var(--fg);
}

/* Строка формы: подпись, поле и сообщение под ним. Один и тот же набор стоял
   восемь раз в формах «Контактов» и в лид-форме (рефакторинг 11.09). */
.form-row {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.form-label {
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
    font-weight: var(--font-weight-medium);
}

/* Сообщение под конкретным полем — не то же, что [data-role="form-error"]
   ниже: то про форму целиком, и цвет у него другой. Не сводим. */
[data-role="field-error"] {
    font-size: var(--text-xs);
    line-height: var(--text-xs--line-height);
    letter-spacing: 0;
    color: var(--error);
}

/* Размерные варианты поля. Два размера и многострочный вариант, каждый
   повторяется в разметке несколько раз (рефакторинг 13.09).

   Вместе с размерами сняты две мёртвые утилиты фокуса: focus:border-accent
   перебивает безслойное * { border-color: var(--border) }, а focus:outline-none
   гасится безслойным же :focus-visible из хвоста сборки. Оба правила вне
   @layer и выигрывают у слоевых независимо от специфичности. Замер до правки:
   рамка не меняется ни в покое, ни на клике, ни с клавиатуры, а обводка с
   клавиатуры появляется вопреки focus:outline-none. */
.field--sm {
    height: 44px;
    padding-inline: 12px;
}

.field--md {
    height: 48px;
    padding-inline: 14px;
}

/* Цвет подсказки в пустом поле. Эта утилита, в отличие от двух снятых, живая:
   её правило тоже рукописное в хвосте сборки и работает. */
.field--md::placeholder {
    color: var(--fg-subtle);
}

/* Многострочное поле: высота по содержимому, поля сверху и снизу. Идёт после
   размерных вариантов, чтобы снять их фиксированную высоту. */
.field--area {
    height: auto;
    padding-block: 8px;
}

.field--md.field--area {
    padding-block: 10px;
}

/* Заголовок формы: набор text-h3 font-semibold стоял в лид-форме и в модалке
   успеха. */
.form-title {
    font-size: 24px;
    line-height: 1.25;
    letter-spacing: -.02em;
    font-weight: var(--font-weight-semibold);
}

/* Пояснение под заголовком формы и текст в модалке. */
.form-hint {
    color: var(--fg-muted);
}

/* Звёздочка обязательного поля. */
.form-req {
    color: var(--error);
}

/* Сообщение об ошибке под полем: было утилитой text-sm в двенадцати местах.
   Цвет не трогаем — половина сообщений приглушённая, половина наследует, и
   сводить их к одному это решение про дизайн, а не про раскладку. */
[data-role="form-error"] {
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
}

/* ── Верхняя строка каталога ──────────────────────────────────────────
   Счётчик найденного, форма сортировки, чипы активных фильтров и заголовок
   колонки фильтров. Переехало из утилит разметки (рефакторинг 13.09). */
[data-role="results-label"] {
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
    font-weight: var(--font-weight-semibold);
    color: var(--fg);
}

.catalog-sort {
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
    color: var(--fg-muted);
}

/* Селект сортировки.

   Фон --field-bg, тот же, что у полей фильтра (правка автора 09.09). В
   разметке стояло bg-surface, но его перекрывало правило по data-role в
   base.css: своего класса у селекта не было. Теперь есть, и перекрытие
   убрано.

   Цвет рамки --border, а не --border-strong из разметки: утилиты цвета рамки
   в этой сборке мертвы, их бьёт безслойное * { border-color: var(--border) }.
   Рамка тут нужна: селект стоит не в колонке фильтров, а над выдачей, и без
   неё терял бы границы. */
/* Подпись «Сортировка» слева от селекта. Было утилитой whitespace-nowrap:
   перенос ломал строку, потому что подпись и селект стоят в одной ячейке. */
.sort-label {
    white-space: nowrap;
}

.sort-select {
    height: 40px;
    width: auto;
    min-width: 176px;
    border: 1px solid var(--border);
    border-radius: 12px;
    padding-inline: 12px;
    background-color: var(--field-bg);
    color: var(--fg);
    cursor: pointer;
}

@media (min-width: 768px) {
    .sort-select {
        height: 36px;
    }
}

/* Кнопка «ОК» рядом с сортировкой: видна только без JS. */
.sort-ok {
    height: 36px;
    border: 1px solid var(--border);
    border-radius: 12px;
    padding-inline: 12px;
}

/* Заголовок колонки фильтров: прилипает к верху при прокрутке колонки. */
.filters-rail-title {
    position: sticky;
    top: 0;
    z-index: 10;
    padding-top: 4px;
    padding-bottom: 20px;
    background: var(--bg);
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
    font-weight: var(--font-weight-semibold);
}

/* Чипы активных фильтров. Цвет текста ссылочный, а не акцентный: в разметке
   стояло text-accent, но site-wide правило a.text-accent переводит его в
   --link. Про это же говорит комментарий автора у ховера чипа ниже. */
.chips-label {
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
    color: var(--fg-muted);
}

.filter-chip {
    display: inline-flex;
    align-items: center;
    min-height: 36px;
    gap: 6px;
    padding-inline: 14px;
    border-radius: 3.40282e38px;
    background: var(--accent-subtle);
    color: var(--link);
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
    font-weight: var(--font-weight-medium);
    cursor: pointer;
}

/* В тёмной теме текст чипа белый, а не ссылочный: так его красит правило
   [data-theme="dark"] .bg-accent-subtle.text-accent в base.css. Оно служит ещё
   трём элементам (бейдж типа на странице объекта и заметки на служебных
   страницах), поэтому остаётся там, а чипу цвет задаём здесь. */
[data-theme="dark"] .filter-chip {
    color: var(--accent-fg);
}

.chips-reset {
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
    color: var(--fg-muted);
    text-decoration-line: underline;
    cursor: pointer;
}

.chips-reset:hover {
    color: var(--fg);
}

/* ── Мобильная шторка фильтров ────────────────────────────────────────
   Раскрывается той же кнопкой, что и показывает. Подложка на весь экран,
   панель прижата к низу. Было восемь и семь утилит соответственно
   (рефакторинг 13.09).

   Раскрытие раньше шло вариантом group-open/f: из сборки; переписано на
   состояние самого блока, читается без знания приёмов сборки. */
.filters-veil {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: none;
    flex-direction: column;
    justify-content: flex-end;
    background: var(--scrim);
}

[data-role="filter-sheet"][open] .filters-veil {
    display: flex;
}

.filters-sheet {
    max-height: 85dvh;
    overflow-y: auto;
    overscroll-behavior: contain;
    border-top-left-radius: 28px;
    border-top-right-radius: 28px;
    padding: 20px;
    background: var(--bg);
    box-shadow: var(--sh-modal);
}

/* Иконка в плавающей кнопке. */
.filters-fab svg {
    width: 16px;
    height: 16px;
}

/* ── Плашка блока ─────────────────────────────────────────────────────
   Крупный блок со скруглением и большими полями: нижняя плашка «не нашли
   подходящее», тёмный блок контактов, форма заявки. Один и тот же набор
   утилит стоял трижды (рефакторинг 13.09). */
.panel {
    border-radius: 40px;
    padding-inline: 28px;
    padding-block: 48px;
}

@media (min-width: 768px) {
    .panel {
        padding-inline: 56px;
    }
}

/* Заливка контрастом: плашка CTA и блок контактов. */
.panel--contrast {
    background: var(--contrast);
    color: var(--on-contrast);
}

@media (min-width: 768px) {
    .panel--contrast {
        padding-block: 64px;
    }
}

/* Обводка вместо заливки: форма заявки под текстом страницы. */
.panel--outline {
    border: 1px solid var(--border);
}

/* Подзаголовок на контрастной плашке, тише основного текста. */
.panel-lead {
    opacity: .75;
}

/* Контурная кнопка на контрастной плашке.

   Цвета переехали сюда из костыля в base.css. Утилита border-on-contrast/30
   мертва, как и все утилиты цвета рамки: их бьёт безслойное
   * { border-color: var(--border) } в хвосте сборки. Автор 08.09 обошёл это
   правилом [data-role="cta"] a.border, потому что своего класса у кнопки не
   было. Теперь есть, и костыль убран.

   Смешение в srgb, а не в oklab: у живой утилиты hover:bg-on-contrast/10
   смешение oklab, но правило из base.css её перекрывало, и на экране сейчас
   вариант srgb. Записываем то, что видно. */
.btn-ghost--on-contrast {
    border-color: color-mix(in srgb, var(--on-contrast) 30%, transparent);
    color: var(--on-contrast);
    transition-property: var(--tr-colors);
    transition-duration: var(--tr-duration);
    transition-timing-function: var(--tr-ease);
}

.btn-ghost--on-contrast:hover {
    background: color-mix(in srgb, var(--on-contrast) 10%, transparent);
}

/* Текст и список контактов в тёмном блоке. */
.contact-note {
    font-size: var(--text-sm);
    line-height: 1.625;
    letter-spacing: 0;
    color: var(--fg-muted);
}

.contact-list {
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
}

.contact-list a:hover {
    text-decoration-line: underline;
}

/* ── Модалка успеха ───────────────────────────────────────────────────
   Нативный <dialog>, показывает site.js после отправки любой формы. Было
   набором из семи утилит на самом диалоге (рефакторинг 13.09). */
[data-role="modal"] {
    width: min(92vw, 480px);
    border-radius: 28px;
    padding: 24px;
    background: var(--surface);
    color: var(--fg);
    box-shadow: var(--sh-modal);
}

[data-role="modal"]::backdrop {
    background-color: var(--scrim);
}

/* ── Широкий попап со своей прокруткой ────────────────────────────────
   «Все удобства» на странице объекта (выбор автора 16.09, мокап «Вариант 2»,
   образец Airbnb). Обычная модалка 480px растягивалась по списку в высоту, и
   прокручивалась СТРАНИЦА под ней: полоса оказывалась снаружи окна, а само
   окно — узким в две строки на пункт.

   Здесь высота ограничена, а прокрутка живёт в теле окна; шапка с заголовком
   и крестиком остаётся на месте. Полей у самого <dialog> нет — их держат
   шапка и тело, иначе прокрутка отрезала бы правое поле. */
[data-role="modal"].modal--wide {
    width: min(94vw, 860px);
    max-height: min(86vh, 760px);
    border-radius: 20px;
    padding: 0;
    overflow: hidden;
}

/* Окно галереи — по ширине шапки сайта (правка автора 16.09): фотографии в нём
   главное, и 860px им тесно, а за край макета выходить незачем — страница и
   окно должны стоять по одной сетке. */
[data-role="modal"].modal--gallery {
    width: min(96vw, var(--shell-max));
    /* Высота — во весь свободный экран, а не 760px как у остального широкого
       окна (просьба автора 17.09): фотографий в галерее много, и потолок в
       760px заставлял прокручивать там, где место на экране было.

       Поля сверху и снизу равны и получаются сами: у <dialog> margin: auto,
       поэтому окно стоит по центру, а вычитание 2 × 40px оставляет ровно по
       40px с каждой стороны. Высота при этом не фиксируется: max-height —
       потолок, и короткая галерея из трёх кадров сожмётся по содержимому.

       dvh, а не vh: на телефоне адресная строка то есть, то нет, и по vh
       нижний край окна уезжал бы под неё. */
    max-height: calc(100vh - 80px);
    max-height: calc(100dvh - 80px);
}

/* Крестик один и стоит справа. Селектор с [data-role], а не просто
   .modal--gallery: правило .modal--wide .modal-head ниже по файлу иначе
   перебивало бы его своим space-between, и единственная кнопка уезжала влево. */
[data-role="modal"].modal--gallery .modal-head {
    justify-content: flex-end;
    padding-bottom: 8px;
}

/* Вкладки «Фото / Видео» в шапке окна (выбор автора 19.09, мокап «Вариант A»).

   Шапка галереи выровнена вправо (правило выше), поэтому вкладкам нужен
   space-between: слева переключатель, справа крестик. */
[data-role="modal"].modal--gallery .modal-head:has(.gal-tabs) {
    justify-content: space-between;
    gap: 16px;
}

.gal-tabs {
    display: flex;
    gap: 8px;
    min-width: 0;
}

.gal-tab {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 40px;
    padding: 0 18px;
    border: 1px solid var(--border-strong);
    border-radius: 999px;
    background: var(--surface);
    color: var(--fg);
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    font-weight: var(--font-weight-medium);
    white-space: nowrap;
    cursor: pointer;
    transition: var(--tr-colors) var(--tr-duration) var(--tr-ease);
}

.gal-tab:hover {
    background: var(--surface-hover);
}

/* Выбранная вкладка — заливкой цвета текста: в окне и так много кадров, и
   цветная заливка спорила бы с ними. */
.gal-tab.is-on {
    background: var(--contrast);
    border-color: var(--contrast);
    color: var(--on-contrast);
}

/* Число рядом с названием — тише самой подписи: это справка, а не действие. */
.gal-tab span {
    font-size: var(--text-xs);
    opacity: .6;
}

/* Крестик крупнее обычного (правка автора 16.09): в окне на весь экран кнопка
   40×40 терялась. Поворот на ховере — единственное движение в окне, и оно
   подсказывает, что это кнопка, а не значок. */
.modal--gallery .modal-head .icon-btn {
    width: 48px;
    height: 48px;
    color: var(--fg);
    transition: background-color .2s var(--tr-ease, ease), transform .25s var(--tr-ease, ease);
}

.modal--gallery .modal-head .icon-btn svg {
    width: 20px;
    height: 20px;
}

.modal--gallery .modal-head .icon-btn:hover {
    transform: rotate(90deg);
}

@media (prefers-reduced-motion: reduce) {
    .modal--gallery .modal-head .icon-btn {
        transition: background-color .2s ease;
    }

    .modal--gallery .modal-head .icon-btn:hover {
        transform: none;
    }
}

/* Колонкой — только у ОТКРЫТОГО окна. Без [open] display: flex перебивал
   собственный display: none у <dialog>, и оба попапа висели на странице
   развёрнутыми (замечание автора 16.09). */
[data-role="modal"].modal--wide[open] {
    display: flex;
    flex-direction: column;
}

.modal--wide .modal-head {
    display: flex;
    flex: none;
    align-items: center;
    /* Крестик прижат к правому краю: утилиты grow в замороженной сборке нет,
       а разводить их по краям всё равно надо. */
    justify-content: space-between;
    gap: 16px;
    padding: 22px 28px 14px;
}

.modal--wide .modal-body {
    overflow-y: auto;
    /* Докрутив список до конца, дальше листать страницу под окном не начинаем. */
    overscroll-behavior: contain;
    padding: 0 28px 28px;
}

/* Полоса прокрутки в попапе — тонкая и с полем сверху и снизу, как в колонке
   фильтров (правка автора 16.09): во всю высоту окна она читалась как рамка и
   упиралась в скруглённые углы. 4px, дорожка прозрачная, бегунок полутоном.
   В Chrome/Safari это только ::-webkit-scrollbar — при заданном scrollbar-width
   он игнорируется, поэтому стандартное свойство ставим лишь там, где
   webkit-псевдоэлементов нет (Firefox). */
.modal--wide .modal-body {
    scrollbar-width: auto;
    scrollbar-color: auto;
}

.modal--wide .modal-body::-webkit-scrollbar {
    width: 4px;
}

.modal--wide .modal-body::-webkit-scrollbar-track {
    background: transparent;
    /* Поле сверху и снизу: полоса не доезжает до краёв окна. */
    margin-block: 10px;
}

.modal--wide .modal-body::-webkit-scrollbar-thumb {
    border-radius: 999px;
    background: var(--border-strong);
}

.modal--wide .modal-body:hover::-webkit-scrollbar-thumb {
    background: var(--fg-subtle);
}

@supports not selector(::-webkit-scrollbar) {
    .modal--wide .modal-body {
        scrollbar-width: thin;
        scrollbar-color: var(--border-strong) transparent;
    }
}

/* Попап «О жилье»: текста много, окно уже удобств, а заголовок едет вместе с
   текстом (мокап «Вариант 1»). Крестик один в шапке, поэтому шапку прижимаем
   вправо, а заголовок живёт первой строкой тела. */
.modal--read {
    width: min(94vw, 760px);
}

.modal--read .modal-head {
    justify-content: flex-end;
    padding-bottom: 0;
}

.modal--read .modal-body > .form-title {
    margin-bottom: 14px;
}

/* Категория и её удобства. Отступ сверху у всех, кроме первой: между группами
   нужен воздух, а под шапкой — нет. */
.modal--wide .amen-sec + .amen-sec {
    margin-top: 26px;
}

/* Список в две колонки с линией под каждым пунктом. Вертикального зазора нет
   намеренно: линии соседних пунктов должны идти одна за другой, как в списке,
   а не висеть в воздухе. На узком экране колонка одна. */
.modal--wide .amen-list {
    display: grid;
    grid-template-columns: 1fr;
    column-gap: 34px;
    margin-top: 4px;
}

@media (min-width: 640px) {
    .modal--wide .amen-list {
        grid-template-columns: 1fr 1fr;
    }
}

.modal--wide .amen-list > .obj-amenity {
    padding-block: 14px;
    border-bottom: 1px solid var(--border);
}

/* ── Главная кнопка сайта ─────────────────────────────────────────────
   Фирменная кнопка-действие: формы заявок, фильтр, CTA, страницы ошибок,
   модалка успеха. Тот же набор из девяти утилит стоял в четырнадцати местах
   двенадцати шаблонов (рефакторинг 11.09).

   Здесь только то, что одинаково во всех четырнадцати: форма, цвет, ховер и
   выключка. Размеры остались утилитами в разметке намеренно — три высоты и
   три поля стоят в почти случайных сочетаниях, системы из них нет, а
   font-size вдобавок несёт подписку letter-spacing, которая у <a> и <button>
   разная. Анимация блика — своя, .btn-shine ниже. */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 12px;
    background: var(--accent);
    color: var(--accent-fg);
    font-weight: var(--font-weight-medium);
    cursor: pointer;
    /* Размер по умолчанию: h-12 px-5 text-sm в разметке, семь мест.
       Межбуквенное ноль: у <button> оно приходило от элемента, у <a>
       от класса .text-sm, то есть у обеих разновидностей было нулевым. */
    height: 48px;
    padding-inline: 20px;
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
    transition-property: var(--tr-colors);
    transition-duration: var(--tr-duration);
    transition-timing-function: var(--tr-ease);
}

.btn:hover {
    background: var(--accent-hover);
}

/* Размерные варианты кнопок.

   Поля задаются здесь, а не утилитами в разметке: правила компонента
   безслойные и выигрывают у слоевых утилит, поэтому px-6 рядом с .btn просто
   проиграл бы. Разные поля это варианты.

   Про межбуквенное у крупного варианта. Подписка letter-spacing: 0 из хвоста
   сборки цепляется к элементам button, input, select, label и к классу
   .text-sm, но не к .text-base. Поэтому у крупной кнопки оно разное: у
   <button> ноль от элемента, у <a> наследуется от body и равно -0.08px.
   Ноль стоит в .btn, а ссылкам крупного варианта наследование возвращается
   отдельным правилом. Выровнять их значит изменить набор строки. */
.btn--sm {
    height: 44px;
}

.btn--wide {
    padding-inline: 24px;
}

.btn--lg {
    height: 52px;
    padding-inline: 28px;
    font-size: var(--text-base);
    line-height: var(--text-base--line-height);
}

a.btn--lg {
    letter-spacing: inherit;
}

/* Одна кнопка на странице объекта идёт без перехода цвета. Так в исходной
   вёрстке: у неё не было утилиты transition-colors, в отличие от остальных
   тринадцати. Выравнивать значит менять поведение, поэтому возвращаем
   начальные значения всех трёх свойств, а не ставим none: вычисленное
   значение должно совпасть с прежним. */
.btn--flat {
    transition-property: all;
    transition-duration: 0s;
    transition-timing-function: ease;
}

/* Красный вариант кнопки: «Оставить заявку» в нижней плашке. Красный из
   палитры отведён CTA-кнопкам, синий акцент — кнопке поиска и формам: в
   таблице цветов accent значится как «кнопки», cta как «CTA-кнопки».

   Раньше этот цвет приезжал снаружи, правилом [data-role="cta"] .btn: сначала
   потому, что своего имени у кнопки не было вовсе, потом по инерции. Теперь
   это модификатор, и блок CTA больше не красит кнопку у себя внутри
   (рефакторинг 13.09). */
.btn--cta {
    background: var(--cta);
    color: var(--cta-fg);
}

.btn--cta:hover {
    background: var(--cta-hover);
    color: var(--cta-fg);
}

.btn-ghost--lg {
    height: 52px;
    padding-inline: 28px;
    font-size: var(--text-base);
    line-height: var(--text-base--line-height);
}

a.btn-ghost--lg {
    letter-spacing: inherit;
}

/* Кнопка сброса в пустой выдаче каталога: единственная контурная кнопка на
   заливке. Перехода цвета у неё нет: в разметке не было утилиты
   transition-colors, в отличие от двух других контурных. Возвращаем начальные
   значения всех трёх свойств. */
.btn-ghost--filled {
    padding-inline: 20px;
    background: var(--surface);
    transition-property: all;
    transition-duration: 0s;
    transition-timing-function: ease;
}

/* ── шапка сайта ──────────────────────────────────────────────────────── */
/* Геометрия и поведение шапки: переехало из утилит разметки (рефакторинг
   11.09). Значения из сборки один в один: у bg-bg/85 прозрачность появляется
   только там, где браузер умеет color-mix, — обе ветки повторены ниже. */
[data-role="site-header"] {
    position: sticky;
    top: 0;
    z-index: 50;
    /* Линия под шапкой видна ВСЕГДА, а не только при скролле: в хвосте
       main.css, вне всяких @layer, лежит * { border-color: var(--border) },
       а безслойное правило бьёт любое слоевое независимо от специфичности —
       значит border-transparent в разметке не работал никогда. Повторяем
       фактическое поведение, а не намерение разметки. */
    border-bottom: 1px solid var(--border);
    background-color: var(--bg);
    -webkit-backdrop-filter: blur(var(--blur-md));
    backdrop-filter: blur(var(--blur-md));
    transition-property: border-color, box-shadow;
    transition-duration: .5s;
    transition-timing-function: var(--ease-out-expo);
}

@supports (color: color-mix(in lab, red, red)) {
    [data-role="site-header"] {
        background-color: color-mix(in oklab, var(--bg) 85%, transparent);
    }
}

/* Прокрученное состояние ставит main.js через data-scrolled. Меняется
   только тень: цвет линии и до правки был один и тот же в обоих
   состояниях (см. выше про безслойное правило). */
[data-role="site-header"][data-scrolled="true"] {
    box-shadow: var(--sh-card);
}

.hdr-nav {
    margin-left: auto;
    display: none;
    align-items: center;
    gap: 4px;
}

/* Полный бар — только с 1024 (правка автора 20.09, п. 12): на планшете
   768–1023 меню, CTA и инструменты в сумме шире экрана — кнопка «Подобрать
   жильё» и переключатель темы обрезались правым краем. До 1024 планшет живёт
   мобильной шапкой с бургером, как телефон. */
@media (min-width: 1024px) {
    .hdr-nav {
        display: flex;
    }
}

/* Бургер — зеркало полного бара: виден, пока бар скрыт. */
@media (min-width: 1024px) {
    .hdr-burger {
        display: none;
    }
}

.hdr-tools {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 4px;
}

@media (min-width: 1024px) {
    .hdr-tools {
        margin-left: 0;
    }
}

/* Пункт меню и его вариант-раскрывашка. */
.nav-link {
    border-radius: 12px;
    padding-inline: 12px;
    padding-block: 8px;
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
    font-weight: var(--font-weight-medium);
    color: var(--fg);
    /* было утилитой transition-colors в разметке */
    transition-property: var(--tr-colors);
    transition-duration: var(--tr-duration);
    transition-timing-function: var(--tr-ease);
}

.nav-link:hover {
    background-color: var(--surface-hover);
}

.nav-link--group {
    display: flex;
    align-items: center;
    gap: 4px;
    cursor: pointer;
    list-style: none;
}

.nav-link--group::-webkit-details-marker {
    display: none;
}

/* Выпадашка подменю: раскрывает её group-open/menu:block из сборки. */
.nav-dropdown {
    position: absolute;
    top: 100%;
    z-index: 40;
    display: none;
    width: 256px;
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 8px;
    background: var(--surface);
    box-shadow: var(--sh-card);
}

.nav-drop-link {
    display: block;
    border-radius: 12px;
    padding-inline: 12px;
    padding-block: 8px;
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
    font-weight: var(--font-weight-medium);
    color: var(--fg);
    /* было утилитой transition-colors в разметке */
    transition-property: var(--tr-colors);
    transition-duration: var(--tr-duration);
    transition-timing-function: var(--tr-ease);
}

.nav-drop-link--child {
    font-weight: inherit;
    color: var(--fg-muted);
}

.nav-drop-link:hover {
    background-color: var(--surface-hover);
    color: var(--fg);
}

/* Иконки в кнопках шапки. Переключение пар (солнце и луна, бургер и крестик)
   было вариантами dark: и group-open/menu: в разметке — это состояния
   компонента, а не локальная настройка, поэтому они здесь. */
.hdr-icon--night,
.hdr-icon--close {
    display: none;
}

[data-theme="dark"] .hdr-icon--day {
    display: none;
}

[data-theme="dark"] .hdr-icon--night {
    display: block;
}

/* Раскрытое мобильное меню меняет бургер на крестик. */
[data-role="nav-mobile"][open] .hdr-icon--menu {
    display: none;
}

[data-role="nav-mobile"][open] .hdr-icon--close {
    display: block;
}

/* Стрелка у пункта с подменю. */
.nav-chev {
    width: 16px;
    height: 16px;
}

/* Кнопка-иконка: тумблер темы и бургер в шапке, закрытие модалки успеха.
   Один и тот же набор из девяти утилит стоял в трёх местах. */
.icon-btn {
    display: flex;
    width: 40px;
    height: 40px;
    align-items: center;
    justify-content: center;
    border-radius: 12px;
    color: var(--fg-muted);
    cursor: pointer;
    list-style: none;
}

.icon-btn:hover {
    background-color: var(--surface-hover);
}

.icon-btn::-webkit-details-marker {
    display: none;
}

.icon-btn svg {
    width: 20px;
    height: 20px;
}

/* Крупнее на 4px и без приглушённого цвета: кнопка закрытия шторки фильтров.
   Цвет наследуется, как было в разметке, где утилиты цвета у неё не стояло. */
.icon-btn--lg {
    width: 44px;
    height: 44px;
    color: inherit;
}

/* Кнопки шапки анимируют смену цвета, кнопка закрытия модалки нет. Так в
   исходной вёрстке; выравнивать их значит менять вид, а не раскладку. */
.hdr-icon-btn {
    transition-property: var(--tr-colors);
    transition-duration: var(--tr-duration);
    transition-timing-function: var(--tr-ease);
}

/* Пункты мобильного меню. */
.hdr-sheet-link {
    display: flex;
    min-height: 56px;
    align-items: center;
    border-bottom: 1px solid var(--border);
    font-size: var(--text-lg);
    line-height: var(--text-lg--line-height);
    font-weight: var(--font-weight-medium);
    color: var(--fg);
    /* было утилитой transition-colors в разметке */
    transition-property: var(--tr-colors);
    transition-duration: var(--tr-duration);
    transition-timing-function: var(--tr-ease);
}

.hdr-sheet-link--child {
    gap: 8px;
    font-size: var(--text-base);
    line-height: var(--text-base--line-height);
    font-weight: inherit;
    color: var(--fg-muted);
}

.hdr-sheet-link:active {
    color: var(--accent);
}


/* ── Шапка сайта: на два тона светлее (выбор автора 10.09, мокап «Вариант 2»)
   В разметке шапка стоит на bg-bg/85 с backdrop-blur-md — фон страницы на
   85% с размытием. Сплошной белый автору не подошёл (пробовали, откатили),
   из десяти шагов между прежним и белым сначала взяли самый лёгкий (белого
   15%), затем следующий: тот же фон страницы, но смешанный с белым на 30%,
   те же 85% и то же размытие. Над серым фоном это #F5F5F7 против прежнего
   #F1F2F4, светлота L* 96,6 против 95,5. Только светлая тема — в тёмной
   шапка остаётся на своём --bg. Размытие и линия при скролле остаются от
   разметки. */
[data-theme="light"] [data-role="site-header"] {
    background: color-mix(in srgb, color-mix(in srgb, var(--bg), white 30%) 85%, transparent);
}

[data-role="site-header"][data-scrolled="true"] [data-role="logo-link"] svg {
    height: 26px;
    width: auto;
}

/* ── шапка сайта ──────────────────────────────────────────────────────── */

/* ── Шапка: компактная ТОЛЬКО в стики-состоянии ───────────────────────
   Уточнение автора 01.09: в покое шапка прежняя (72px, лого как было),
   а при скролле (data-scrolled="true", ставит main.js) плавно ужимается
   до 60px с меньшим лого — «Вариант A» относится именно к стики-версии.
   Высоты — свои классы: в замороженном билде нет h-[60px] и
   calc-высоты мобильного шита. */
.hdr-bar {
    height: var(--hdr-h);
    transition: height .4s var(--ease-out-expo, cubic-bezier(.16, 1, .3, 1));
    /* было утилитами: mx-auto flex w-full max-w-[1280px] items-center
       gap-6 px-5 md:px-8 */
    display: flex;
    width: 100%;
    max-width: var(--shell-max);
    margin-inline: auto;
    align-items: center;
    gap: 24px;
    padding-inline: 20px;
}

[data-role="site-header"][data-scrolled="true"] .hdr-bar {
    height: 60px;
}

@media (prefers-reduced-motion: reduce) {
    .hdr-bar,
    [data-role="logo-link"] svg {
        transition: none;
    }
}

/* Мобильный шит меню — на всю высоту вьюпорта под шапкой (--hdr-h в покое;
   в ужатом состоянии низ чуть недотягивает — шит и так скроллится). */
.hdr-sheet {
    height: calc(100dvh - var(--hdr-h));
    /* Список разделов по центру экрана (правка автора 08.09): распорки
       ::before/::after вместо justify-content: center — при переполнении
       они сжимаются в ноль и верх списка не обрезается. Нижняя распорка
       больше на высоту шапки, чтобы центр совпал с центром вьюпорта. */
    display: flex;
    flex-direction: column;
    /* было утилитами: absolute inset-x-0 top-full z-40 overflow-y-auto
       overscroll-contain border-t border-border bg-bg px-5 pt-2 */
    position: absolute;
    inset-inline: 0;
    top: 100%;
    z-index: 40;
    overflow-y: auto;
    overscroll-behavior: contain;
    border-top: 1px solid var(--border);
    background: var(--bg);
    padding-inline: 20px;
    padding-top: 8px;
}

.hdr-sheet::before,
.hdr-sheet::after {
    content: "";
    flex: 1 1 0;
}

.hdr-sheet::after {
    flex-basis: var(--hdr-h);
}

/* Строки разделов выше (64px вместо 56), между ними воздуха больше. Пункты
   по центру строки (правка автора 09.09): лист и так центрирован по вертикали,
   и с выключкой по центру список читается одним блоком, а не прижатым к краю.
   Подпункты попадают под то же правило — они такие же прямые дети листа. */
.hdr-sheet > a:not(.hdr-sheet-cta) {
    min-height: 64px;
    justify-content: center;
    text-align: center;
}

/* CTA «Подобрать жильё» в шапке (только «О компании»; мокап Х5 «пилюля»):
   сплошная фирменная пилюля с иконкой поиска и мягкой тенью. */
.hdr-cta {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    height: 36px;
    padding: 0 16px;
    margin: 0 6px; /* воздух с обеих сторон: между языком и тумблером темы */
    border-radius: 99px;
    background: var(--cta);
    color: var(--cta-fg);
    font-size: 13.5px;
    font-weight: 600;
    white-space: nowrap;
    /* Тень в тон кнопки, вчетверо прозрачнее исходной (правки автора 04.09:
       30% читались как подсветка, 15% ещё светились, остановились на 8%) —
       едва заметный отрыв от шапки. Цвет — от токена CTA, чтобы тень
       следовала за палитрой страницы. */
    box-shadow: 0 4px 14px color-mix(in srgb, var(--cta) 8%, transparent);
    transition: background-color .25s ease, box-shadow .25s ease;
}

.hdr-cta:hover {
    background: var(--cta-hover);
    color: var(--cta-fg);
}

.hdr-cta svg {
    width: 15px;
    height: 15px;
    stroke: currentColor;
    fill: none;
    stroke-width: 2;
    stroke-linecap: round;
}

/* На телефоне пилюлю из бара убираем — она переезжает в бургер-меню
   (.hdr-sheet-cta ниже), чтобы не тесниться с логотипом и бургером. */
@media (max-width: 1023px) {
    .hdr-cta {
        display: none;
    }
}

/* CTA «Подобрать жильё» внутри мобильного листа: полноширинная фирменная
   пилюля под списком разделов (правка автора 04.09: сначала стояла сверху,
   и меню начиналось с кнопки, а не с разделов). Живёт только в .hdr-sheet
   (сам лист скрыт на десктопе), поэтому своего медиазапроса не требует. */
.hdr-sheet-cta {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    width: 100%;
    height: 48px;
    margin: 16px 0 8px;
    border-radius: 99px;
    background: var(--cta);
    color: var(--cta-fg);
    font-size: 15px;
    font-weight: 600;
    box-shadow: 0 4px 14px color-mix(in srgb, var(--cta) 8%, transparent); /* как у пилюли в шапке */
    transition: background-color .25s ease;
}

.hdr-sheet-cta:hover,
.hdr-sheet-cta:active {
    background: var(--cta-hover);
    color: var(--cta-fg);
}

.hdr-sheet-cta svg {
    width: 17px;
    height: 17px;
    stroke: currentColor;
    fill: none;
    stroke-width: 2;
    stroke-linecap: round;
}

[data-role="logo-link"] svg {
    transition: height .4s var(--ease-out-expo, cubic-bezier(.16, 1, .3, 1));
}

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

/* Плавающая кнопка «Показать фильтры» (правка автора 09.09): синяя, по
   центру над контентом. Высота 48px и радиус в полукруг — палец попадает
   мимо только по краям. Внизу учитываем safe-area, иначе на iPhone кнопка
   лежит на домашней полоске.
   Открытый шит кнопку убирает: подложка шита лежит выше по z-index, и синее
   пятно просвечивало бы через неё; закрыть шит есть чем — крестик в его
   шапке. */
.filters-fab {
    position: fixed;
    left: 50%;
    bottom: calc(16px + env(safe-area-inset-bottom));
    transform: translateX(-50%);
    z-index: 40;
    height: 48px;
    padding: 0 22px;
    border-radius: 99px;
    background: var(--accent);
    color: var(--accent-fg);
    box-shadow: 0 6px 20px rgba(12, 21, 42, .22);
    white-space: nowrap;
    transition: background-color .2s ease, opacity .2s ease, transform .2s ease,
        visibility .2s ease;
    /* Было утилитами: flex cursor-pointer list-none items-center
       justify-center gap-2 text-sm font-semibold. Вид кнопки был собран
       пополам, часть тут, часть в разметке. */
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    cursor: pointer;
    list-style: none;
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
    font-weight: var(--font-weight-semibold);
}

.filters-fab::-webkit-details-marker {
    display: none;
}

.filters-fab:hover {
    background: var(--accent-hover);
}

/* У футера кнопка уезжает (правка автора 09.09): иначе она навсегда лежит
   поверх ссылок футера. Класс ставит hideFabAtFooter в site.js — по
   IntersectionObserver, порог сдвинут на высоту самой кнопки, поэтому она
   исчезает ровно тогда, когда футер доходит до её верхнего края.
   visibility, а не только opacity: прозрачная кнопка осталась бы в
   табуляции. */
.filters-fab.is-away {
    opacity: 0;
    visibility: hidden;
    transform: translateX(-50%) translateY(12px);
    pointer-events: none;
}

[data-role="filter-sheet"][open] > .filters-fab {
    display: none;
}

/* Ширина колонки фильтров: было утилитой w-64. На десктопе её перекрывает
   правило ниже: там ширина на 4px больше, чтобы обводка фокуса у крайних
   полей не срезалась скролл-контейнером. */
.filters-rail {
    width: 256px;
}

/* ── Сайдбар фильтров каталога: по высоте вьюпорта, свой скролл ───────
   Правка автора 01.09: раньше до низа фильтра приходилось доскролливать
   весь листинг. Утилиты lg:sticky/lg:top-20/lg:max-h-[calc(100vh-6rem)]/
   lg:overflow-y-auto стояли в разметке, но в замороженном билде их НЕТ —
   молча не работали. Теперь честным CSS: липнет под шапкой, высота — по
   вьюпорту, скролл внутренний (тонкий — scroll-slim из билда),
   overscroll не дёргает страницу на краях. */
@media (min-width: 1024px) {
    /* Тонкий кастомный скролл (правка автора 08.09): 4px — вдвое тоньше
       браузерного thin, дорожка прозрачная, бегунок полутоном, при наведении
       темнее. В Chrome/Safari это только ::-webkit-scrollbar: при заданном
       scrollbar-width он игнорируется, поэтому стандартное свойство ставим
       лишь там, где webkit-псевдоэлементов нет (Firefox). Без transition по
       цвету: интерполяция давала желтоватую вспышку на тёмной теме. */
    .filters-rail {
        scrollbar-width: auto;
        scrollbar-color: auto;
    }

    .filters-rail::-webkit-scrollbar {
        width: 4px;
    }

    .filters-rail::-webkit-scrollbar-track {
        background: transparent;
    }

    .filters-rail::-webkit-scrollbar-thumb {
        border-radius: 999px;
        background: var(--border-strong);
    }

    .filters-rail:hover::-webkit-scrollbar-thumb {
        background: var(--fg-subtle);
    }

    @supports not selector(::-webkit-scrollbar) {
        .filters-rail {
            scrollbar-width: thin;
            scrollbar-color: var(--border-strong) transparent;
        }
    }

    .filters-rail {
        position: sticky;
        /* sticky создаёт свой stacking context, и без z-index листинг, идущий
           в DOM позже, рисовался поверх календаря дат, который выезжает из
           колонки (замечание автора 08.09). Выше ссылки-оверлея карточки
           (z-10), ниже шапки и шторок (z-50). */
        z-index: 20;
        top: 80px; /* шапка 72px (60 в скролле) + воздух */
        max-height: calc(100vh - 96px);
        overflow-y: auto;
        overscroll-behavior: contain;
        /* Скролл-контейнер режет всё за своим краем, в том числе кольцо
           фокуса (2px смещение + 2px обводка) у полей и кнопок во всю ширину
           колонки — слева и у последнего элемента снизу (замечание автора
           08.09: «Ещё 7 районов» с обрезанной рамкой). Даём 4px внутреннего
           поля слева, сверху и снизу, а колонку на те же 4px сдвигаем и
           расширяем, чтобы содержимое осталось на прежнем месте. Справа
           место уже есть (pr-6 — воздух до карточек), повторяем его здесь,
           потому что шорткат padding перекрыл бы утилиту. */
        width: calc(256px + 4px);
        margin: -4px 0 0 -4px;
        padding: 4px 24px 4px 4px;
    }

    /* Стики-заголовок «Фильтры» липнет к краю содержимого, а не к краю
       рейла, и в 4px поля сверху и слева просвечивали прокрученные поля
       (замечание автора 08.09: «видимая полоса над шапкой»). Сдвигаем
       заголовок на эти 4px и возвращаем текст на место внутренним полем. */
    .filters-rail > .filters-rail-title {
        top: -4px;
        margin-left: -4px;
        padding-left: 4px;
    }
}

/* ── Десктоп: спокойный фильтр на подложке (выбор автора 08.09, мокап
   «Вариант 2»; подложка — светлее фона на 50%, токен --field-bg, 08.09)
   Белые поля и плитки с рамкой на сером фоне «светились» и спорили с
   карточками выдачи. В колонке каталога (.filters-rail — только десктоп,
   мобильная шторка прежняя) поля, плитки, степперы, поле дат и список
   районов лежат на подложке без рамки, скругление 10; разделители внутри
   составных полей полутоном. Выбранная плитка и ховер — рамкой цвета текста,
   фокус поля дат — акцентом, как и раньше. Тёмная тема берёт свой tint. */
.filters-rail :where(input:not([type="checkbox"]):not([type="range"]):not(.date-duo-input):not(.stepper-value), select),
.filters-rail .date-duo,
.filters-rail .stepper-box,
.filters-rail .tile-opt {
    background: var(--field-bg);
    border-color: transparent;
    border-radius: 10px;
}

/* Выбранный район и раскрывашка списка — той же подложкой, что поля. */
.filters-rail .dist-row.is-on,
.filters-rail .dist-toggle {
    background: var(--field-bg);
}

.filters-rail .tile-opt:has(.tile-opt-input:checked),
.filters-rail .tile-opt:hover,
.filters-rail .stepper-box:hover,
.filters-rail .date-duo:hover,
.filters-rail :where(input:not([type="checkbox"]):not([type="range"]):not(.date-duo-input):not(.stepper-value), select):hover {
    border-color: var(--fg);
}

.filters-rail .date-duo:focus-within {
    border-color: var(--accent);
}

.filters-rail .date-duo-cell + .date-duo-cell,
.filters-rail .stepper + .stepper {
    border-color: var(--border-strong);
}

/* Кружки степперов остаются белыми: на подложке они читаются как кнопки. */
.filters-rail .stepper-btn {
    border-color: transparent;
}

.filters-rail .stepper-btn:hover:not(:disabled) {
    border-color: var(--fg);
}

.filters-rail .stepper-btn:disabled {
    background: transparent;
    border-color: var(--border-strong);
}

/* ── lang-switch ──────────────────────────────────────────────────────── */

/* ── Переключатель языка: сегмент-контрол ─────────────────────────────
   Редизайн 01.09 по мокапу Я1 (выбор автора): оба языка в общем сером
   треке, активный — на светлой «таблетке» с тенью, iOS-паттерн. Цвета
   явные: в тёмной теме трек темнеет, таблетка — приподнятый серый. */
[data-role="lang-switch"] {
    gap: 2px;
    padding: 3px;
    border-radius: 99px;
    background: var(--tint); /* дорожка — токен, а не бежевый из прежней палитры (правка автора 04.09) */
    /* было утилитами: flex items-center (gap-1 тут и так перекрыт) */
    display: flex;
    align-items: center;
}

[data-theme="dark"] [data-role="lang-switch"] {
    background: var(--tint);
}

/* ── переключатель языка ──────────────────────────────────────────────── */

.lang-chip {
    display: flex;
    min-width: 38px;
    height: 28px; /* под шапку 60px (вариант A) */
    padding: 0 10px;
    align-items: center;
    justify-content: center;
    border-radius: 99px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--fg-muted);
    transition: color .25s ease;
}

a.lang-chip:hover {
    color: var(--fg);
}

.lang-chip.is-on {
    background: var(--surface);
    color: var(--fg);
    box-shadow: 0 1px 3px rgba(34, 39, 44, .14);
}

[data-theme="dark"] .lang-chip.is-on {
    background: color-mix(in srgb, var(--fg) 16%, var(--bg));
    color: var(--fg);
    box-shadow: none;
}

/* ── подвал ───────────────────────────────────────────────────────────── */

/* ── Футер: кнопки мессенджеров и плашка графика работы ───────────────
   Редизайн 31.08 по макету Ф1C: WhatsApp сплошной фирменный, Telegram
   контуром, график работы — контурная карточка без заливки. Токены —
   из main.css, тёмная тема подхватывается сама. */
/* Оболочка и колонки подвала: переехало из утилит разметки (рефакторинг
   11.09). Цвета здесь фактические, а не те, что подразумевала разметка: внутри
   подвала text-fg-muted перекрыт в base.css на var(--fg), а hover:text-fg — на
   var(--link). Утилита tracking-wide в заголовках была мертва: безслойная
   подписка .text-xs,.text-sm,… { letter-spacing: 0 } бьёт слоевую утилиту.
   bg-tint остаётся в разметке — у него подписка на рамку в тёмной теме. */
[data-role="site-footer"] {
    /* Отступ до подвала — как между секциями (просьба автора 17.09). Секции
       несут только нижний отступ (pb-16/md:pb-24 = 64/96px), так что ритм
       между ними — 64/96, и последняя секция почти всё даёт сама. Подвалу
       добавлено немного: замер 17.09 показал, что на «Контактах» до линии
       подвала выходило ~78px против 96 между секциями, а с 48/64px (первая
       попытка) — уже 142, заметно больше ритма. Раньше стояло margin-top:
       auto, но подвал и так внизу: <main> растянут flex-1. */
    margin-top: 16px;
    border-top: 1px solid var(--border);
    padding-block: 48px;
}

@media (min-width: 768px) {
    [data-role="site-footer"] {
        margin-top: 24px;
    }
}

.footer-cols {
    display: flex;
    flex-direction: column;
    gap: 40px;
}

@media (min-width: 768px) {
    .footer-cols {
        flex-direction: row;
        justify-content: space-between;
    }
}

/* Заголовок колонки: один и тот же набор стоял в разметке трижды. */
/* Подпись под логотипом в подвале. Кегль был утилитой text-xs, цвет —
   утилитой text-fg-muted, которую правило подвала тут же перекрашивало в
   полный текст. Утилита и перебивка убраны, цвет объявлен один раз
   (рефакторинг 13.09). Имя не .footer-about: так уже называется обёртка
   колонки с логотипом, внутри которой эта подпись и стоит. */
.footer-blurb {
    font-size: var(--text-xs);
    line-height: var(--text-xs--line-height);
    letter-spacing: 0;
    color: var(--fg);
}

.footer-col-title {
    margin-bottom: 12px;
    font-size: var(--text-xs);
    line-height: var(--text-xs--line-height);
    letter-spacing: 0;
    font-weight: var(--font-weight-semibold);
    text-transform: uppercase;
    color: var(--fg);
}

/* Заголовок раздела бывает ссылкой: ховер цветом ссылок. В разметке стояла
   утилита hover:text-fg, а правило подвала перекрашивало её в --link, потому
   что после смены базового цвета ховер перестал бы читаться. Оставляем ровно
   то, что рисовалось. */
.footer-col-title a:hover {
    color: var(--link);
}

.footer-links {
    display: flex;
    flex-direction: column;
    gap: 12px;
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
    color: var(--fg);
}

/* Ссылка подвала: зона нажатия 40px, отрицательные поля гасят её прирост,
   чтобы строки остались на прежних местах. */
.footer-link {
    display: inline-flex;
    align-items: center;
    min-height: 40px;
    margin-block: -8px;
}

.footer-link:hover {
    color: var(--link);
}

/* Телефон и почта в колонке «Связаться»: та же геометрия, свои цвета.
   Акцентного цвета у почты никогда не было, хотя в разметке стояло
   text-accent: site-wide правило a.text-accent, a.hover\:text-accent:hover
   ниже в этом же файле переводит и то и другое в var(--link). Поэтому у
   почты ссылочный цвет, а телефону свой ховер не нужен — его даёт общее
   .footer-link:hover, и это тот же var(--link), что был до правки. */
.footer-link--phone {
    gap: 8px;
    font-weight: var(--font-weight-semibold);
    color: var(--fg);
}

.footer-link--mail {
    gap: 8px;
    color: var(--link);
}

/* Словесный логотип в подвале: было утилитами block text-[var(--logo-color)]
   на обёртке и h-7 w-auto на самом знаке. */
.footer-logo {
    display: block;
    color: var(--logo-color);
}

.footer-logo svg {
    height: 28px;
    width: auto;
}

/* Колонка «о компании»: было max-w-xs. Ниже md ограничение снимает правило в
   мобильном блоке сразу за этим — поэтому базовое обязано стоять раньше. */
.footer-about {
    max-width: 320px;
}

/* Нижняя строка: было утилитами mt-10 flex flex-col gap-2 border-t
   border-border pt-6 text-xs text-fg-muted md:flex-row md:justify-between.
   Своего правила у неё до сих пор не было — только мобильное. */
.footer-bottom {
    margin-top: 40px;
    display: flex;
    flex-direction: column;
    gap: 8px;
    border-top: 1px solid var(--border);
    padding-top: 24px;
    font-size: var(--text-xs);
    line-height: var(--text-xs--line-height);
    letter-spacing: 0;
    color: var(--fg);
}

@media (min-width: 768px) {
    .footer-bottom {
        flex-direction: row;
        justify-content: space-between;
    }
}

/* Ссылка на политику в нижней строке. Ховер был утилитой hover:text-fg, но
   цвет ей давал не он: патч в base.css перекрашивал такие ссылки внутри
   подвала в цвет ссылок. Записываем то, что видно на экране. */
.footer-bottom a:hover {
    color: var(--link);
}

/* Подвал на мобильном (выбор автора 08.09, мокап «Вариант 1»): всё по центру —
   логотип, описание, мессенджеры, меню одной колонкой, нижняя строка; секция
   «Связаться» двухколоночная: слева телефон и почта, справа плашка «на связи»
   в нынешнем виде. Колонка «о компании» без ограничения ширины (md:max-w-xs в
   сборке нет). Десктоп не меняется. */
@media (max-width: 767px) {
    .footer-about {
        max-width: none;
    }

    [data-role="site-footer"] {
        text-align: center;
    }

    [data-role="site-footer"] .footer-about > span {
        display: flex;
        justify-content: center;
    }

    [data-role="site-footer"] .footer-msgrs {
        justify-content: center;
    }

    [data-role="site-footer"] nav ul {
        align-items: center;
    }

    [data-role="footer-contacts"] {
        display: grid;
        grid-template-columns: 1fr 1.15fr;
        gap: 12px;
        align-items: start;
        text-align: left;
    }

    [data-role="footer-contacts"] > p {
        grid-column: 1 / -1;
        text-align: center;
    }

    [data-role="footer-contacts"] > ul {
        padding-top: 4px;
    }

    [data-role="footer-contacts"] .hours-badge {
        margin-top: 0;
        max-width: none;
    }

    .footer-bottom {
        align-items: center;
    }

    .footer-bottom > span {
        justify-content: center;
    }

    /* Адрес и год на телефоне встают в две строки, и точка между ними
       оставалась одна на строке (замечание автора 17.09). */
    .footer-dot {
        display: none;
    }
}

.footer-msgrs {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    margin-top: 18px;
}

.footer-msgr {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    min-height: 42px;
    padding: 0 16px;
    border-radius: 99px;
    font-size: 13.5px;
    font-weight: 600;
    transition: background-color .25s ease, box-shadow .25s ease;
}

.footer-msgr svg {
    width: 17px;
    height: 17px;
    stroke: currentColor;
    fill: none;
    stroke-width: 1.8;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.footer-msgr--solid {
    background: var(--accent);
    color: var(--accent-fg);
}

.footer-msgr--solid:hover {
    background: var(--accent-hover);
    color: var(--accent-fg);
    box-shadow: 0 8px 20px color-mix(in srgb, var(--accent) 28%, transparent);
}

.footer-msgr--ghost {
    border: 1.5px solid var(--accent);
    color: var(--accent);
}

.footer-msgr--ghost:hover {
    background: var(--accent-subtle);
    color: var(--accent-hover);
}

/* Подвал на тёмной теме: всё синее — красным (решение автора 07.09):
   сплошная кнопка WhatsApp, контурная Telegram; почта уже красная через
   text-accent выше. */
[data-theme="dark"] .footer-msgr--solid {
    background: var(--cta);
    color: var(--cta-fg);
}

[data-theme="dark"] .footer-msgr--solid:hover {
    background: var(--cta-hover);
    color: var(--cta-fg);
    box-shadow: 0 8px 20px color-mix(in srgb, var(--cta) 28%, transparent);
}

[data-theme="dark"] .footer-msgr--ghost {
    border-color: var(--cta);
    color: var(--link);
}

[data-theme="dark"] .footer-msgr--ghost:hover {
    background: color-mix(in srgb, var(--cta) 16%, transparent);
    color: var(--link-hover);
}

/* ── график работы ────────────────────────────────────────────────────── */

.hours-badge {
    display: flex;
    gap: 10px;
    align-items: flex-start;
    max-width: 250px;
    margin-top: 14px;
    padding: 14px 16px;
    border: 1.5px solid var(--accent);
    border-radius: 14px;
}

/* В каналах на «Контактах» плашка идёт по ширине колонки и без верхнего
   отступа: расстояние до списка задаёт разметка. */
.hours-badge--wide {
    max-width: none;
    margin-top: 0;
    padding: 16px 20px;
}

.hours-badge-dot {
    width: 9px;
    height: 9px;
    border-radius: 99px;
    background: var(--warning);
    margin-top: 5px;
    flex: 0 0 auto;
}

.hours-badge-dot.is-online {
    background: var(--success);
}

.hours-badge b {
    display: block;
    font-size: 13.5px;
    font-weight: 700;
    color: var(--fg);
}

.hours-badge > span:last-child > span {
    display: block;
    margin-top: 2px;
    font-size: 12.5px;
    color: var(--fg-muted);
    line-height: 1.45;
}

[data-theme="dark"] [data-role="site-footer"] .hours-badge {
    border-color: var(--cta);
}

/* ── object-card ──────────────────────────────────────────────────────── */

/* ── Карточка объекта: «табличные факты» ──────────────────────────────
   Редизайн 01.09 по одобренному мокапу CardFinal, уплотнена по мокапу
   CardCompact (правка автора 01.09: лайв вышел выше мокапа): рамка-бокс,
   факты мини-таблицей в одну строку («4 спальни»), низ — «от ฿ЦЕНА / ночь»
   + стрелка «Подробнее». Фото 4:3 не тронуто. Работает и в 3 колонки
   (каталог), и в 4 (главная). Ховер — мягкий подъём, как у шагов. */
[data-role="object-card"] {
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--surface);
    overflow: hidden;
    transition: transform .45s cubic-bezier(.16, 1, .3, 1), box-shadow .45s cubic-bezier(.16, 1, .3, 1);
    /* было утилитами: relative flex h-full flex-col */
    position: relative;
    display: flex;
    height: 100%;
    flex-direction: column;
}

[data-role="object-card"]:hover {
    transform: translateY(-4px);
    box-shadow: var(--sh-hover);
}

@media (prefers-reduced-motion: reduce) {
    [data-role="object-card"],
    [data-role="object-card"]:hover {
        transform: none;
        transition: none;
    }
}

/* ── absolute ─────────────────────────────────────────────────────────── */

/* Ссылка-оверлей растянута на всю карточку, а карточка режет overflow ради
   скругления фото — кольцо фокуса на ссылке резалось со всех сторон
   (замечание автора 08.09). Кольцо рисуем на самой карточке: снаружи её
   ничто не режет, скругление то же. У ссылки своё кольцо гасим. */
[data-role="object-card"] > a.absolute:focus-visible {
    outline: none;
}

[data-role="object-card"]:has(> a.absolute:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Вид карточки: переехало из pages.css (рефакторинг 11.09) — карточка
   рисуется одним партиалом на каталоге, главной и странице объекта, значит
   это компонент, а не страничный блок. Раскладка списка (subgrid для
   выравнивания рядов) осталась страничной. */
/* Фото: прежний ратио 4:3. Скругление своё, на все четыре угла (правка автора
   02.09): верхние и раньше срезала карточка (overflow), нижние теперь тоже —
   фото читается тем же боксом, что и сама карточка. Радиус равен карточному. */
.obj-photo {
    aspect-ratio: 4 / 3;
    border-radius: 12px;
    /* было утилитами: relative overflow-hidden. Утилита bg-tint осталась в
       разметке намеренно: в тёмной теме сборка вешает на .bg-tint волосяную
       рамку, и это общесайтовая договорённость, а не свойство карточки. */
    position: relative;
    overflow: hidden;
}

.obj-body {
    padding: 9px 13px 10px;
    /* было утилитами: flex flex-1 flex-col */
    display: flex;
    flex: 1;
    flex-direction: column;
}

/* Заголовок без резерва высоты под две строки: кламп остаётся в разметке
   (line-clamp-2), но короткое название не оставляет пустоты. */
.obj-title {
    font-size: 16px;
    line-height: 1.25;
    /* было утилитами: line-clamp-2 font-semibold tracking-[-0.02em] */
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    overflow: hidden;
    font-weight: var(--font-weight-semibold);
    letter-spacing: -.02em;
}

.obj-sub {
    margin-top: 1px; /* вплотную к названию (правка автора 01.09) */
    font-size: 12.5px;
    line-height: 1.35;
    color: var(--fg-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Факты одной строкой через «·» (мокап 6). Таблица с рамками занимала
   на ~20px больше и в три колонки резала слова. margin-top: auto —
   прижимает строку к низу тела, чтобы карточки в ряду совпадали. */
.obj-facts {
    margin-top: auto;
    padding-top: 5px;
    font-size: 12px;
    line-height: 1.4;
    color: var(--fg-muted);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.obj-facts b {
    font-weight: 600;
    color: var(--fg);
    font-variant-numeric: tabular-nums;
}

.obj-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    border-top: 1px solid var(--border);
    padding: 7px 13px 10px;
    margin-top: auto;
    font-size: 12px;
}

.obj-price {
    font-size: 14.5px;
    /* было утилитами: font-semibold tabular-nums */
    font-weight: var(--font-weight-semibold);
    font-variant-numeric: tabular-nums;
}

.obj-more {
    color: var(--accent);
    font-weight: 700;
    font-size: 12px;
    white-space: nowrap;
}

/* Геометрия и поведение, переехавшие из утилит разметки (рефакторинг
   11.09). Значения взяты из сборки один в один: ссылка-оверлей на всю
   карточку, кадр с зумом на ховере через свойство scale (именно scale, а не
   transform — так его объявляет утилита group-hover:scale-[1.05]),
   длительность .9s и кривая --ease-out-expo от transition-transform. */
.obj-link {
    position: absolute;
    inset: 0;
    z-index: 10;
}

.obj-photo > img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition-property: transform, translate, scale, rotate;
    transition-duration: .9s;
    transition-timing-function: var(--ease-out-expo);
}

[data-role="object-card"]:hover .obj-photo > img {
    scale: 1.05;
}

/* Бейдж типа жилья поверх кадра. */
.obj-badge {
    position: absolute;
    top: calc(var(--spacing) * 3);
    left: calc(var(--spacing) * 3);
    border-radius: 3.40282e38px;
    padding-inline: calc(var(--spacing) * 3);
    padding-block: var(--spacing);
    background: var(--accent);
    color: var(--accent-fg);
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    /* Сборка гасит межбуквенное у .text-xs/.text-sm/кнопок/полей; бейдж
       был на .text-sm, поэтому повторяем явно, иначе он унаследовал бы
       -.005em от body. */
    letter-spacing: 0;
    font-weight: var(--font-weight-medium);
}

/* Заглушка, когда фото ещё не загрузили. */
.obj-ph {
    display: flex;
    height: 100%;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: calc(var(--spacing) * 2);
    color: var(--fg-subtle);
}


/* ── Хлебные крошки ───────────────────────────────────────────────────
   Мелкий приглушённый список над заголовком страницы. Кегль, цвет и ховер
   были утилитами в партиале (рефакторинг 13.09). Межбуквенное ноль: у класса
   .text-sm в сборке оно нулевое, и на этом же держался прежний набор строки.
   Текущий пункт полным цветом, ссылки — цветом ссылок на ховере. */
.crumbs {
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
    color: var(--fg-muted);
}

.crumbs a:hover {
    color: var(--fg);
}

.crumbs-current {
    color: var(--fg);
}

/* Заглушка карточки, когда фото ещё нет: размер значка и подпись под ним. */
.obj-ph-icon {
    width: 32px;
    height: 32px;
}

.obj-ph-text {
    font-size: var(--text-sm);
    line-height: var(--text-sm--line-height);
    letter-spacing: 0;
}

/* ── btn-shine ────────────────────────────────────────────────────────── */

/* ── Блик на ховере главных кнопок ────────────────────────────────────
   Выбор автора 04.09 (мокап «Ховер 5»): по кнопке один раз пробегает узкая
   светлая полоса. Приём сайту не чужой — такой же блик у скелетонов загрузки.

   Полоса — ::before с отрицательным z-index: внутри своего контекста наложения
   (isolation: isolate) она рисуется поверх фона кнопки, но под текстом, и
   тексту не нужен ни лишний span, ни свой z-index. overflow: hidden обрезает
   полосу по скруглению.

   Анимация одноразовая и висит на :hover, поэтому повторяется при каждом
   новом наведении. Только там, где есть настоящий курсор: на тапе ховер
   «залипает», и блик остался бы стоять посреди кнопки. При
   prefers-reduced-motion общий сброс в билде вёрстки гасит её сам.

   Класс стоит на трёх кнопках. Пилюля в шапке — самая заметная кнопка сайта;
   у неё скругление 99px, обрезка работает так же. Сплошная кнопка WhatsApp в
   подвале — единственная синяя кнопка, которая оставалась без блика (правка
   автора 04.09). Раньше обе были дописаны сюда селекторами, то есть часть их
   вида объявлялась в чужом блоке; теперь эффект приходит классом в разметке
   (рефакторинг 13.09). */
.btn-shine {
    position: relative;
    overflow: hidden;
    isolation: isolate;
}

.btn-shine::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    width: 45%;
    z-index: -1;
    background: linear-gradient(100deg, rgba(255, 255, 255, 0), rgba(255, 255, 255, .35), rgba(255, 255, 255, 0));
    transform: translateX(-160%) skewX(-12deg);
    pointer-events: none;
}

@media (hover: hover) {
    .btn-shine:hover::before {
        animation: btn-shine .7s cubic-bezier(.25, 1, .5, 1);
    }
}

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

/* ── «Район» в фильтре списком с чекбоксами ─────────────────────────────────
   Выбор автора 08.09 (мокап «Вариант 3»): строки «чекбокс · название ·
   счётчик», выбранная — на подложке tint и полужирным, стоит первой; видны
   первые пять, остальные скрыты, пока не нажали плитку-раскрывашку «Ещё N
   районов». Свёрнутость ставит site.js через data-collapsed (без JS список
   полный). Заменил аккордеон details/summary. */
.dist-list {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.dist-row {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 40px;
    /* Без отрицательных полей: строка выезжала за левый край колонки, и её
       скролл-контейнер обрезал левое скругление подложки (замечание автора 08.09). */
    padding: 0 10px;
    border-radius: 10px;
    color: var(--fg);
    cursor: pointer;
    transition: background-color .2s ease;
}

.dist-row:hover {
    background: color-mix(in srgb, var(--tint) 60%, transparent);
}

.dist-row.is-on {
    background: var(--tint);
    font-weight: 600;
}

.dist-row-name {
    flex: 1;
    min-width: 0;
    font-size: 14px;
}

.dist-row-n {
    font-size: 13px;
    color: var(--fg-subtle);
}

.dist-list[data-collapsed="1"] .dist-row--more {
    display: none;
}

.dist-toggle {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    min-height: 40px;
    margin-top: 6px;
    border: 0;
    border-radius: 10px;
    background: var(--tint);
    color: var(--fg);
    /* Отдельными свойствами: в шорткате font ключевое слово inherit делало всё
       объявление невалидным, и кнопка оставалась 16px (замечание автора 08.09). */
    font-family: inherit;
    font-size: 14px;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    transition: background-color .2s ease;
}

.dist-toggle:hover {
    background: color-mix(in srgb, var(--tint) 70%, var(--border-strong));
}

.dist-toggle-chev {
    transition: transform .18s ease;
}

.dist-list[data-collapsed="0"] .dist-toggle-chev {
    transform: rotate(180deg);
}

[data-theme="dark"] .dist-row.is-on,
[data-theme="dark"] .filters-rail .dist-row.is-on {
    background: var(--accent);
    color: var(--accent-fg);
}

[data-theme="dark"] .dist-row.is-on .dist-row-n {
    color: color-mix(in srgb, var(--accent-fg) 80%, transparent);
}

/* ── степперы фильтра ─────────────────────────────────────────────────── */

/* ── Степперы «Гостей» и «Спальни» в фильтре ──────────────────────────────
   Выбор автора 08.09 (мокап «Вариант 2», образец Airbnb): две строки в одной
   рамке через волосяную линию, подпись слева, «− значение +» справа, кнопки
   кружками 30px. Значение — number-инпут без стрелок и рамки; пустое поле
   показывает плейсхолдер «Неважно» цветом текста, как обычное значение.
   Ховер рамки — как у остальных полей колонки. */
.stepper-box {
    border: 1px solid var(--border-strong);
    border-radius: 12px;
    background: var(--surface);
    transition: border-color .2s ease;
}

.stepper-box:hover {
    border-color: var(--fg);
}

.stepper {
    display: flex;
    align-items: center;
    gap: 6px;
    min-height: 50px;
    padding: 0 10px 0 12px;
}

.stepper + .stepper {
    border-top: 1px solid var(--border);
}

.stepper-label {
    flex: 1;
    min-width: 0;
    font-size: 14px;
    color: var(--fg);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.stepper-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 30px;
    height: 30px;
    padding: 0;
    border: 1px solid var(--border-strong);
    border-radius: 99px;
    background: var(--surface);
    color: var(--fg);
    cursor: pointer;
    transition: border-color .2s ease, color .2s ease;
}

.stepper-btn svg {
    width: 12px;
    height: 12px;
}

.stepper-btn:hover:not(:disabled) {
    border-color: var(--fg);
}

.stepper-btn:disabled {
    border-color: var(--border);
    color: var(--fg-subtle);
    cursor: default;
}

/* Значение не сжимается флексом: на узком экране «Неважно» обрезалось
   (замечание автора 08.09); 68px хватает на слово при 14px. */
.stepper-value {
    flex: 0 0 60px;
    width: 60px;
    height: 30px;
    padding: 0;
    border: 0;
    background: transparent;
    text-align: center;
    font: inherit;
    font-size: 13px; /* «Неважно» ужато, чтобы подпись не наезжала на минус (правка автора 08.09) */
    color: var(--fg);
    -moz-appearance: textfield;
    appearance: textfield;
}

/* На узких экранах main.css держит инпуты 16px (против зума iOS при фокусе),
   и «Неважно» в 68px обрезалось; в мобильной шторке места больше — 84px. */
@media (max-width: 767px) {
    .stepper-value {
        flex-basis: 84px;
        width: 84px;
    }
}

/* Мобильный кегль значения степпера и сортировки — 14px (просьба автора
   09.09). В сборке ниже 768px стоит input, select, textarea { font-size:
   16px !important }, поэтому «Неважно» и «Цена: по возрастанию» были на
   два пункта крупнее своих подписей. Перебиваем только эти два контрола,
   остальные поля фильтра остаются на 16px.
   Цена правки: на iPhone Safari увеличивает страницу по фокусу, если у
   контрола кегль меньше 16px. У степпера значение обычно набирают кнопками
   «−/+», у сортировки открывается нативный список, но зум по тапу в само
   поле теперь возможен. */
@media (max-width: 767px) {
    .stepper-value,
    [data-role="sort-select"] {
        font-size: 14px !important;
    }
}

.stepper-value::-webkit-outer-spin-button,
.stepper-value::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

.stepper-value::placeholder {
    color: var(--fg);
    opacity: 1;
}

/* ── плитки фильтра ───────────────────────────────────────────────────── */

/* ── Плитки «Тип жилья» и «Удобства» ────────────────────────────────────────
   Выбор автора 08.09 (мокап «Вариант 2», образец Airbnb): плитка 44px с
   радиусом 12 как поля колонки, иконка слева серым, название, счётчик
   объектов у правого края. Плитки переносятся и растягиваются по строке: в
   колонке 264px жёсткая сетка резала «Апартаменты». Выбранная — та же рамка
   1px, но цвета текста; фон прежний.
   Чекбокс внутри label остаётся в форме и доступен с клавиатуры, рисуем
   только рамку вокруг него. */
.tile-opts {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

/* Плотнее мокапа (отступы 8, зазор 6, 13.5px, иконка 16): в колонке 232px
   контента пары «Студия + Таунхаус», «Любой + Вилла» иначе не влезали в
   строку на несколько пикселей и каждая плитка вставала отдельно. */
.tile-opt {
    position: relative;
    display: flex;
    flex: 1 1 auto;
    align-items: center;
    gap: 6px;
    min-width: 0;
    min-height: 44px;
    padding: 0 8px;
    border: 1px solid var(--border-strong);
    border-radius: 12px;
    background: var(--surface);
    color: var(--fg);
    font-size: 13.5px;
    font-weight: 500;
    cursor: pointer;
    transition: border-color .2s ease;
}

.tile-opt:hover {
    border-color: var(--fg);
}

.tile-opt-input {
    position: absolute;
    width: 1px;
    height: 1px;
    opacity: 0;
    pointer-events: none;
}

.tile-opt-icon {
    flex: 0 0 auto;
    width: 16px;
    height: 16px;
    color: var(--fg-muted);
}

.tile-opt-name {
    flex: 1;
    min-width: 0;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.tile-opt::after {
    content: attr(data-count);
    font-size: 13px;
    font-weight: 400;
    color: var(--fg-subtle);
}

.tile-opt[data-count=""]::after {
    content: none;
}

/* Выбранная: только цвет рамки серая → цвета текста; толщина 1px и фон те
   же, что у невыбранной (правка автора 08.09). */
.tile-opt:has(.tile-opt-input:checked) {
    border-color: var(--fg);
}

.tile-opt:has(.tile-opt-input:checked)::after {
    color: var(--fg);
}

/* У выбранной плитки вместо иконки — акцентная галочка (правка автора 08.09).
   Маска с заливкой токеном, а не картинка: цвет акцента меняется с темой. */
.tile-opt:has(.tile-opt-input:checked) .tile-opt-icon {
    display: none;
}

.tile-opt:has(.tile-opt-input:checked)::before {
    content: "";
    flex: 0 0 auto;
    width: 16px;
    height: 16px;
    background: var(--accent);
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12.5 4.5 4.5L19 7'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m5 12.5 4.5 4.5L19 7'/%3E%3C/svg%3E") center / contain no-repeat;
}

.tile-opt:has(.tile-opt-input:focus-visible) {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
}

/* Тёмная тема: выбранные пилюли фильтра синим без рамки (выбор автора 08.09,
   мокап «Вариант 1»): плитки типов и удобств и выбранный район заливаются
   акцентом, текст, галочка и счётчик белые. Светлая тема — рамкой цвета текста. */
[data-theme="dark"] .tile-opt:has(.tile-opt-input:checked) {
    border-color: transparent;
    background: var(--accent);
    color: var(--accent-fg);
}

[data-theme="dark"] .tile-opt:has(.tile-opt-input:checked)::before {
    background: var(--accent-fg);
}

[data-theme="dark"] .tile-opt:has(.tile-opt-input:checked)::after {
    color: color-mix(in srgb, var(--accent-fg) 80%, transparent);
}

/* ── шторка брони ─────────────────────────────────────────────────────── */

/* ── Шторка брони на мобильном ───────────────────────────────────────────────
   Выбор автора 08.09 (мокап «Вариант 2»): ниже lg блок #booking по кнопке бара
   становится полноэкранной шторкой (95% высоты, скруглённый верх, хэндл,
   заголовок и крестик), фон затемнён, страница под ней не скроллится. Это тот
   же блок, что справа на десктопе: содержимое и формы не дублируются. Шапка
   шторки и затемнение в обычном потоке скрыты. */
.bk-head,
.bk-scrim {
    display: none;
}

@media (max-width: 1023px) {
    html.bk-open,
    html.bk-open body {
        overflow: hidden;
    }

    html.bk-open .bk-scrim {
        display: block;
        position: fixed;
        inset: 0;
        z-index: 55;
        background: rgba(14, 15, 17, .45);
    }

    html.bk-open [data-role="booking-sheet"] {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        top: 5vh;
        z-index: 60;
        overflow-y: auto;
        overscroll-behavior: contain;
        padding: 0 16px calc(24px + env(safe-area-inset-bottom));
        border-radius: 20px 20px 0 0;
        background: var(--bg);
        box-shadow: 0 -12px 40px rgba(14, 15, 17, .2);
    }

    /* Сам бокс внутри шторки — без рамки и тени, это уже не карточка. */
    html.bk-open [data-role="booking-sheet"] [data-role="booking-box"] {
        border: 0;
        border-radius: 0;
        box-shadow: none;
        background: transparent;
        padding: 0;
    }

    html.bk-open .bk-head {
        display: block;
        position: sticky;
        top: 0;
        z-index: 1;
        margin: 0 -16px 12px;
        padding: 0 16px 10px;
        background: var(--bg);
    }

    .bk-handle {
        display: block;
        width: 36px;
        height: 4px;
        margin: 8px auto 6px;
        border-radius: 99px;
        background: var(--border-strong);
    }

    .bk-head-row {
        display: flex;
        align-items: center;
        gap: 12px;
        min-height: 44px;
    }

    .bk-title {
        flex: 1;
        font-size: 17px;
        font-weight: 600;
        color: var(--fg);
    }

    .bk-close {
        display: flex;
        align-items: center;
        justify-content: center;
        width: 36px;
        height: 36px;
        border: 0;
        border-radius: 99px;
        background: var(--tint);
        color: var(--fg);
        cursor: pointer;
    }

    /* Кнопка отправки заявки липнет к низу шторки: итог и кнопка видны, пока
       гость дочитывает форму. */
    html.bk-open [data-role="booking-inquiry"] button[type="submit"] {
        position: sticky;
        bottom: 8px;
        z-index: 1;
        box-shadow: 0 8px 24px rgba(14, 15, 17, .18);
    }
}

/* Чип активного фильтра: ховер тише (правка автора 09.09). Утилиты
   hover:bg-accent и hover:text-accent-fg давали сплошную заливку акцентом,
   но цвет текста оставался ссылочным (--link перебивает text-accent у
   ссылки), и надпись на заливке пропадала: 1,3:1. Теперь подложка на ховере
   просто плотнее — с 10% акцента до 22%, текст не меняется. */
[data-role="filter-chip"]:hover {
    background: color-mix(in srgb, var(--accent) 22%, transparent);
}

/* Поля бара на планшете и шире — отдельным правилом, как было утилитой
   md:px-8 (рефакторинг 11.09). */
@media (min-width: 768px) {
    .hdr-bar {
        padding-inline: 32px;
    }
}

/* ── Кегли для широкой колонки (решения автора 18.09) ─────────────────
   Колонка стала 1480 вместо 1280, и прежние кегли на ней читались мельче.
   Подняты точечно, по таблице автора, — и ТОЛЬКО от 1280px: ниже колонка
   упирается во вьюпорт, телефон и планшет остаются как в main (уточнение
   автора 18.09). Шкала на десктопе: 12 примечания · 14 подписи · 16 текст и
   всё нажимаемое · 18 лиды и заголовки карточек; дробных кеглей нет.
   Блок стоит последним в файле нарочно: при равной специфичности побеждает
   он. */
@media (min-width: 1280px) {
    /* Шапка: меню 14→16, «Подобрать жильё» 13.5→16, язык 12.5→14. Стики-шапка
       (data-scrolled ставит main.js) ужимается с 74 до 60px — текст в ней на
       2px мельче (просьба автора 18.09): меню и CTA 16→14, язык 14→12. Кегль
       анимируется с той же кривой и за то же время, что высота бара, иначе
       текст прыгал бы посреди плавного сжатия. Только десктоп: на телефоне
       меню в бургере, бар живёт по своим правилам. */
    .nav-link {
        font-size: var(--text-base);
        line-height: var(--text-base--line-height);
        transition-property: var(--tr-colors), font-size;
        transition-duration: var(--tr-duration), .4s;
        transition-timing-function: var(--tr-ease), var(--ease-out-expo, cubic-bezier(.16, 1, .3, 1));
    }

    .hdr-cta {
        font-size: 16px;
        transition: background-color .25s ease, box-shadow .25s ease, font-size .4s var(--ease-out-expo, cubic-bezier(.16, 1, .3, 1));
    }

    .lang-chip {
        font-size: 14px;
        transition: color .25s ease, font-size .4s var(--ease-out-expo, cubic-bezier(.16, 1, .3, 1));
    }

    [data-role="site-header"][data-scrolled="true"] .nav-link {
        font-size: var(--text-sm);
        line-height: var(--text-sm--line-height);
    }

    [data-role="site-header"][data-scrolled="true"] .hdr-cta {
        font-size: 14px;
    }

    [data-role="site-header"][data-scrolled="true"] .lang-chip {
        font-size: 12px;
    }

    /* Кнопки и ссылки-кнопки 14→16 (крупные .btn--lg и так 16). */
    .btn,
    .btn-ghost,
    .more-link {
        font-size: var(--text-base);
        line-height: var(--text-base--line-height);
    }

    /* Крошки, счётчик «Найдено» и подпись сортировки — в ряду с меню. Сам
       селект остаётся 14 (правка автора 18.09): это поле, как в фильтрах. */
    .crumbs,
    .catalog-topbar [data-role="results-label"],
    .sort-label {
        font-size: var(--text-base);
        line-height: var(--text-base--line-height);
    }

    /* Фильтры каталога: рабочий текст 16, вторичные счётчики 14. */
    .filters-rail-title,
    .filter-cap {
        font-size: var(--text-base);
        line-height: var(--text-base--line-height);
    }

    /* Поля, значения с чекбоксами и CTA в колонке фильтров — 14 (правка
       автора 18.09: на 2px меньше, чем вышло после первого подъёма):
       подписи в полях дат, «Гостей», названия районов, плитки типа и удобств.
       Заголовок «Фильтры» и подписи над полями остаются 16. */
    .date-duo-cap,
    .stepper-label,
    .dist-row-name,
    .tile-opt,
    .dist-row-n,
    .price-cap {
        font-size: 14px;
    }

    .filters-rail .btn,
    .filters-rail .btn-ghost {
        font-size: var(--text-sm);
        line-height: var(--text-sm--line-height);
    }

    /* H1 на всех страницах одного размера — 64px (решение автора 18.09).
       До этого было четыре разных: герой главной 60, «О компании» 56,
       страницы и объект 48. Заголовок объекта .page-title и H1
       посадочных/ошибок h1.sec-title — здесь; герои — в pages.css, там их
       правила специфичнее. Исключение — компактный H1 каталога (36) и H2
       .sec-title (36). */
    .page-title,
    h1.sec-title {
        font-size: 64px;
        line-height: 1.05;
        letter-spacing: -.03em;
    }

    /* Страница объекта — исключение (правка автора 18.09): заголовок объекта
       остаётся 48, как было. Названия объектов длинные и стоят не на фото, а
       над кадром и блоком бронирования — 64 съедал бы первый экран. */
    .page-title[data-role="object-title"] {
        font-size: 48px;
        line-height: 1.1;
    }

    /* Карточка каталога: название 16→18, район 12.5→14, факты и низ 12→14,
       цена 14.5→16. Бейдж типа на фото остаётся 14. */
    .obj-title {
        font-size: 18px;
    }

    .obj-sub,
    .obj-facts,
    .obj-foot,
    .obj-more {
        font-size: 14px;
    }

    .obj-price {
        font-size: 16px;
    }

    /* Чип района на главной: число 13→14 (сам чип — в pages.css, он там
       переопределён). */
    .district-chip-n {
        font-size: 14px;
    }

    /* Подвал: мессенджеры 13.5→16, заголовки колонок 12→14, плашка «на
       связи» 13.5→14 и подпись 12.5→14. */
    .footer-msgr {
        font-size: 16px;
    }

    .footer-col-title {
        font-size: var(--text-sm);
        line-height: var(--text-sm--line-height);
    }

    .hours-badge b,
    .hours-badge > span:last-child > span {
        font-size: 14px;
    }

    /* Ряд управления галереей отходит от краёв кадра вдвое дальше — 16 → 32
       (просьба автора 19.09): на широком кадре деления и стрелки были прижаты
       к самому углу. Только десктоп: на телефоне 32px с каждой стороны съели
       бы шестую часть ширины, и стрелки уехали бы на деления. */
    .gal-bar {
        inset: auto 32px 32px 32px;
    }
}


/* ── Настоящая карта поверх статичной заглушки (решение автора 07.09) ────
   Хост — блок, в котором уже лежит картинка (фото офиса, фотокарта района).
   Карта добавляется слоем сверху и держится прозрачной, пока site.js не
   убедится, что тайлы отрисованы: класс .geo-live ставится по событию idle.
   Значит при медленной сети, упавшем сервисе тайлов или без WebGL посетитель
   видит прежнюю картинку, а не серый бокс. */
.geo-host {
    position: relative;
}

/* Селектор с родителем — не косметика: maplibre-gl.css подключается позже
   нашего (его добавляет site.js в момент загрузки карты) и ставит контейнеру
   .maplibregl-map position: relative. При одинаковой специфичности победил бы
   он, слой встал бы в поток под фотографией, и карта оказалась бы за краем
   блока с overflow: hidden — ровно то, что автор и увидел 07.09. */
.geo-host > .geo-map {
    position: absolute;
    inset: 0;
    z-index: 1;
    opacity: 0;
    pointer-events: none;
    transition: opacity .4s ease;
}

.geo-host.geo-live > .geo-map {
    opacity: 1;
    pointer-events: auto;
}

/* Скругление хоста карта наследует сама: у неё свой канвас, который иначе
   торчит углами из-под рамки. */
.geo-host,
.geo-host > .geo-map,
.geo-map .maplibregl-canvas-container,
.geo-map canvas {
    border-radius: inherit;
}

/* Маркер: точка со счётчиком или пином и подпись с белым ореолом — как у
   пинов фотокарты, чтобы карты выглядели родственно. */
.geo-mk {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    text-decoration: none;
    cursor: pointer;
}

.geo-mk__dot {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    height: 30px;
    border-radius: 99px;
    border: 2px solid #fff;
    background: var(--accent);
    box-shadow: 0 2px 8px rgba(12, 21, 42, .28);
    color: #fff;
    font-size: 12.5px;
    font-weight: 600;
    transition: transform .2s cubic-bezier(.25, 1, .5, 1);
}

/* Одна точка без счётчика — кружок с «дыркой», читается как пин. */
.geo-mk__dot--pin::after {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: 99px;
    background: #fff;
}

.geo-mk:hover .geo-mk__dot {
    transform: scale(1.28);
}

.geo-mk__name {
    font-size: 11.5px;
    font-weight: 600;
    color: var(--fg);
    white-space: nowrap;
    text-shadow: 0 1px 0 #fff, 0 -1px 0 #fff, 1px 0 0 #fff, -1px 0 0 #fff, 0 0 6px #fff;
}

/* Тёмная тема: фото под картой светлое, но сама карта светлая всегда —
   подпись маркера держим тёмной, иначе она исчезнет на светлых тайлах. */
[data-theme="dark"] .geo-mk__name {
    color: #1b1f2b;
}
