/* =============================================================================
   Этап 30: служебные стили новой вёрстки каталога /s2/objects2/ (site_ver=2).
   Отдельный файл — main.min.css не изменяется (жёсткое ограничение этапа 30).
   Подключается через Asset::getInstance()->addCss в template.php шаблона v2
   (один раз, режим catalog). Наполняется по мере частей 1–5 этапа 30;
   только то, чего нет в main.min.css.
   ============================================================================= */

/* Утилита скрытия (реш. 30.10): в main.min.css нет .d-none. Используется для
   скрытия нулевых значений фильтров (выбранное не скрывается — реш. 8.3),
   пустых секций, кнопки «Показать ещё», числового пагинатора и т.п. */
.js__hidden {
    display: none !important;
}

/* Ограничение высоты списков чекбоксов в dropdown основной строки фильтра
   («Тип объекта», «Посёлок»; правка 2026-08-15): список не выше 300px,
   скролл появляется только когда значений больше (overflow-y: auto).
   Панель «Стоимость» (.filter__panel--price) и dropdown сортировки класс
   filter__dropdown--checkboxes не имеют — не затрагиваются. */
.filter__dropdown--checkboxes > .filter__panel {
    max-height: 300px;
    overflow-y: auto;
    overflow-x: hidden;
}

/* Индикатор загрузки AJAX (ревью 2026-08-16): класс obj-catalog--loading
   ставит objAjaxSetLoading() (static_s2/js/main_temp.js) на обёртку
   div.js__obj_catalog во время apply/more — список, кнопка «Показать ещё» и
   числовой пагинатор приглушаются и не принимают клики; сам фильтр остаётся
   интерактивным. Правило перенесено из v1 (css/style.css,
   .obj-catalog--loading .obj-catalog__list) на классы вёрстки v2: без него
   класс ставился, но визуальной обратной связи и защиты от кликов не было. */
.obj-catalog--loading .catalog__list,
.obj-catalog--loading .catalog__more,
.obj-catalog--loading .obj-pager {
    opacity: .55;
    pointer-events: none;
}

/* --- Сортировка над списком (реш. 30.15, задокументированное добавление
   к макету; правка 2026-08-15 — поднята над p.catalog__found, отступ снизу
   20px). Вместо селекта — выпадающий список в стиле dropdown фильтра
   (div.filter__dropdown + filter__button + filter__panel, как у «Тип
   объекта», без чекбоксов). Базовые стили кнопки/панели/стрелки даёт
   main.min.css (filter__button/filter__panel/filter__button-arrow),
   здесь — только правки под сортировку и пункты списка obj-catalog__sort_opt. */
.obj-catalog__sort {
    display: flex;
    align-items: center;
    gap: 12px;
    margin: 0 0 20px;
}

.obj-catalog__sort_label {
    margin: 0;
    color: var(--color-text-faint);
    font-size: 14px;
    font-weight: 400;
    line-height: 1.4;
    white-space: nowrap;
}

/* Кнопка dropdown сортировки: базовый filter__button (200px, flex) из
   main.min.css — здесь ширина по содержимому (длинные названия сортировок). */
.obj-catalog__sort_button {
    width: auto;
    min-width: 220px;
}

/* Панель-список пунктов: базовый filter__panel (фон, радиус, позиция под
   кнопкой) — из main.min.css; пункты — кнопки на всю ширину панели. */
.obj-catalog__sort_panel {
    padding: 8px;
    gap: 0;
}

.obj-catalog__sort_opt {
    width: 100%;
    padding: 8px 12px;
    color: var(--color-text);
    background-color: transparent;
    border: none;
    border-radius: var(--border-radius-sm);
    font-size: 16px;
    font-weight: 400;
    line-height: 1.4;
    text-align: left;
    white-space: nowrap;
    cursor: pointer;
    transition: background-color .2s linear;
}

.obj-catalog__sort_opt:hover {
    background-color: var(--color-field-background-focus);
}

.obj-catalog__sort_opt--current {
    color: var(--color-accent);
}

/* --- Числовой пагинатор под «Показать ещё» (реш. 30.16,
   задокументированное добавление к макету). Собственные классы
   obj-pager__* — Bootstrap-классы v1 (pagination/page-item) не используются. */
.obj-pager {
    display: flex;
    justify-content: center;
    margin-top: 24px;
}

.obj-pager__list {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0;
    padding: 0;
    list-style: none;
}

.obj-pager__link {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 42px;
    height: 42px;
    padding: 0 10px;
    color: var(--color-text);
    background-color: var(--color-field-background-light);
    border-radius: var(--border-radius-sm);
    font-size: 14px;
    line-height: 1.4;
    text-decoration: none;
    transition: background-color .2s linear;
}

a.obj-pager__link:hover {
    background-color: var(--color-field-background-focus);
}

.obj-pager__item--current .obj-pager__link {
    color: var(--color-text-inverse);
    background-color: var(--color-accent);
}

.obj-pager__item--disabled .obj-pager__link {
    opacity: .35;
    pointer-events: none;
}

.obj-pager__item--gap .obj-pager__link {
    min-width: auto;
    padding: 0 4px;
    background-color: transparent;
}

.obj-pager__item--arrow .obj-pager__link {
    font-size: 16px;
}

/* --- Карточка property-card (этап 30, часть 2) --- */

/* Плейсхолдер галереи при отсутствии фото (часть 2): макет не задаёт
   фона пустой галереи — светло-серый фон поверх preview. */
.property-card__gallery--empty {
    /*background-color: var(--color-field-background);*/
    background: var(--color-field-background) url('/local/templates/site/static_s2/img/object_empty_2x.webp') no-repeat center center;
    -webkit-background-size: cover;
    -moz-background-size: cover;
    -o-background-size: cover;
    background-size: cover;
}

/* Пустая выдача списка (часть 2): один li.catalog__item--empty —
   структура макета (li внутри ul.catalog__list) не меняется. */
.catalog__item--empty {
    padding: 40px 0;
    color: var(--color-text-faint);
    font-size: 16px;
    text-align: center;
}

/* Кнопка dropdown без доступных значений (правка 2026-08-17): блок фильтра
   основной строки (TYPE/COMPLEX) не скрывается, но выбирать нечего — кнопка
   приглушена и неактивна (disabled ставит серверный рендер
   control_checkbox.php и клиентский objFilterCountsApply; disabled гасит
   click нативного main.min.js — пустой список не открывается). */
.filter__button--disabled {
    opacity: .5;
    cursor: default;
}

/* --- SEO-текст ЧПУ-страницы v2 (этап 33, реш. 33.8): блок под списком
   объектов на /s2/objects2_<слаг>/ — v2-аналог .obj-sef-text v1. Отступы
   от списка, типографика h2 и цвет — на дизайн-токенах v2; контейнер
   js__sef_text — клиентский objSeoApply обновляет его по AJAX. */
.catalog__sef-text {
    margin-top: 2.5rem;
    padding-top: 1.5rem;
    border-top: 1px solid var(--color-border);
    color: var(--color-text-muted);
}

.catalog__sef-text__head {
    margin: 0 0 .75rem;
    font-family: var(--font-family-heading), sans-serif;
    font-size: 1.25rem;
    font-weight: 600;
    line-height: 1.3;
    color: var(--color-text);
}

.catalog__sef-text__body {
    line-height: 1.65;
}

.catalog__sef-text__body > :last-child {
    margin-bottom: 0;
}

/* --- Посёлки v2 (этап 35, часть 2): страница /s2/villages/ ---
   Сетку .villages-page__list (2 колонки) и карточку .village-card__* даёт
   main.min.css — здесь только то, чего в нём нет:
   плейсхолдер фото карточки и состояние кнопки «Показать ещё». */

/* Плейсхолдер фото при отсутствии MORE_PHOTO (реш. 35.4/35.9): заполняет
   .village-card__preview (absolute inset:0), градиент как у заголовка макета,
   название белым внизу. Бейдж/стрелка в разметке идут после плейсхолдера и
   отрисовываются поверх (оба позиционированы, z-index auto → порядок DOM). */
.village-card__placeholder {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: flex-end;
    padding: 30px;
    background: var(--gradient-heading);
    color: var(--color-text-inverse);
    font-family: var(--font-family-heading), sans-serif;
    font-size: 22px;
    font-style: normal;
    font-weight: 500;
    line-height: 1.2;
    letter-spacing: -.02em;
}

.village-card__placeholder span {
    display: block;
}

/* Кнопка «Показать ещё» при загрузке/недоступности (реш. 35.9): :disabled
   ставит JS-модуль objVillageMoreV2_init во время AJAX-запроса («Загрузка...»).
   Базовые стили — .button/.villages-page__more из main.min.css; скрытие после
   последней страницы — класс js__hidden (см. выше). */
.villages-page__more:disabled {
    opacity: .55;
    cursor: default;
    pointer-events: none;
}

/* --- «Читать полностью» на детальной v2 (этап 34, часть 3, реш. 34.6):
   текст «Об объекте» ограничен 6 строками; кнопка js__about_more появляется
   только когда текст обрезается (JS-модуль objAboutMore_init), скрытие —
   утилита js__hidden (см. выше). main.min.css не меняется. */
.object__about-text--clamped {
    display: -webkit-box;
    -webkit-line-clamp: 6;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* =============================================================================
   Поиск v2 (этап 37, часть 2). Панель .header__search, форма .search-form,
   контейнер подсказок .search-form__suggestions и их базовые элементы уже
   стилизованы в main.min.css — здесь только то, чего в нём нет: подписи
   групп подсказок, состояние «Загрузка…», имя/подпись строки подсказки
   (JS-модуль objSearchV2_init, main_temp.js) и блоки страницы поиска
   /s2/search/ (s2/search/index.php, шаблон search.results/v2,
   классы search-page-v2__*).
   ============================================================================= */

/* Подпись группы подсказок («Объекты» / «Посёлки») — li внутри
   .search-form__suggestions-list, рендерит objSearchV2Render. */
.search-form__suggestions-group {
    color: var(--color-text-faint);
    font-size: 13px;
    font-weight: 500;
    line-height: 1.4;
    letter-spacing: .04em;
    text-transform: uppercase;
}

.search-form__suggestions-group:not(:first-child) {
    margin-top: 8px;
}

/* Состояние «Загрузка…» между вводом и ответом quick (единственный li
   списка на время запроса). */
.search-form__suggestions-loading {
    color: var(--color-text-muted);
    font-size: 16px;
    line-height: 1.4;
}

/* Строка подсказки: имя + подпись (посёлок · тип) в две строки. Базовый
   цвет/размер ссылки — .search-form__suggestion из main.min.css. */
.search-form__suggestion {
    display: block;
}

.search-form__suggestion-name {
    display: block;
}

.search-form__suggestion-sub {
    display: block;
    color: var(--color-text-faint);
    font-size: 13px;
    line-height: 1.4;
}

/* --- Страница поиска v2 (s2/search/index.php, шаблон search.results/v2) --- */

.search-page-v2 {
    margin-top: 30px;
}

/* Форма поиска: вид как у .search-form макета (поле + кнопка «Найти»). */
.search-page-v2__form {
    display: flex;
    gap: 12px;
    align-items: stretch;
}

.search-page-v2__input {
    flex: 1;
    min-width: 0;
    height: 60px;
    padding: 0 20px;
    color: var(--color-text);
    background-color: var(--color-field-background-light);
    border: 1px solid var(--color-border);
    border-radius: var(--border-radius-sm);
    font-size: 16px;
    font-weight: 400;
    line-height: 1.4;
    outline: none;
    transition: background-color .2s linear;
}

.search-page-v2__input::placeholder {
    color: var(--color-text-faint);
    opacity: 1;
}

.search-page-v2__input:focus {
    background-color: var(--color-field-background-focus);
}

.search-page-v2__submit {
    flex: none;
}

/* Итоговая строка «Найдено N результатов» / «Ничего не найдено». */
.search-page-v2__total {
    margin: 28px 0 0;
    color: var(--color-text-muted);
    font-size: 16px;
    line-height: 1.4;
}

/* Пустые состояния: нет запроса / короткий запрос / ничего не найдено. */
.search-page-v2__empty {
    margin-top: 28px;
}

.search-page-v2__empty-text {
    margin: 0;
    max-width: 640px;
    color: var(--color-text);
    font-size: 18px;
    line-height: 1.5;
}

.search-page-v2__empty-hint {
    margin: 10px 0 0;
    color: var(--color-text-muted);
    font-size: 16px;
    line-height: 1.4;
}

.search-page-v2__catalog-link {
    margin-top: 24px;
}

/* Группы результатов (Объекты / Посёлки) и строки имя + подпись + ссылка. */
.search-page-v2__group {
    margin-top: 32px;
}

.search-page-v2__group-title {
    margin: 0 0 16px;
    font-family: var(--font-family-heading), sans-serif;
    font-size: 24px;
    font-weight: 500;
    line-height: 1.2;
    letter-spacing: -.02em;
    color: var(--color-text);
}

.search-page-v2__list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.search-page-v2__item + .search-page-v2__item {
    border-top: 1px solid var(--color-border);
}

.search-page-v2__item:first-child {
    border-top: 1px solid var(--color-border);
}

.search-page-v2__link {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 14px;
    padding: 14px 0;
    text-decoration: none;
}

.search-page-v2__name {
    color: var(--color-text);
    font-size: 18px;
    font-weight: 500;
    line-height: 1.4;
    transition: color .2s linear;
}

.search-page-v2__link:hover .search-page-v2__name {
    color: var(--color-accent-light);
}

.search-page-v2__sub {
    color: var(--color-text-faint);
    font-size: 14px;
    line-height: 1.4;
}

/* Пагинация — классы obj-pager__* (стилизуются секцией этапа 30 выше). */

/* ==========================================================================
   Блог v2 (этап 38; план s2/docs/plan_dev/stages/stage-38.md, реш. 38.12).
   Только дополнения, которых нет в main.min.css. Аудит части 2: стили
   .blog__*, .article-card__*, .articles__* полностью в main.min.css;
   утилита js__hidden объявлена выше — для СПИСКА /s2/blog/ дополнений
   не потребовалось. Детальная — см. ниже (часть 3).
   ========================================================================== */

/* Зум-курсор обложки детальной: клик открывает Fancybox (objBlogV2_init,
   реш. 38.9/38.12) — в main.min.css курсора нет. */
.article__cover[data-fancybox] {
    cursor: zoom-in;
}

/* ==========================================================================
   Избранное v2 (этап 40; план s2/docs/plan_dev/stages/stage-40.md,
   реш. 40.7/40.8). Страница /s2/favorites/ — шаблон favorites/v2.
   Основную стилизацию favorites__title/clear/list, property-card и
   like-button даёт main.min.css (макет) — здесь только добавления к макету:
   пустое состояние (js__fav_empty) и заглушка MISSING (favorites__stub).
   Сетка списка .favorites__list (grid 2 колонки) — из main.min.css,
   li.favorites__item — обычный grid-элемент (стили не нужны).
   ========================================================================== */

/* Пустое состояние «В избранном нет объектов» (реш. 40.8): показано при
   TOTAL===0 && MISSING===0 (сервер) и после удаления последней карточки
   (JS). Сквозная утилита js__hidden объявлена выше. */
.favorites__empty {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 20px;
    margin: 0 0 100px;
    padding: 60px 20px;
    text-align: center;
    color: var(--color-text-muted);
    border: 1px dashed var(--color-border);
    border-radius: var(--border-radius-md);
}

.favorites__empty-text {
    margin: 0;
    font-size: 18px;
    line-height: 1.5;
}

/* Заглушка MISSING «Объект удалён» (реш. 40.7): карточка-призрак сетки —
   li.favorites__item из list.php, внутри dashed-блок на всю высоту ячейки
   (как v1 .obj-fav__stub, только на токенах v2). */
.favorites__stub {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 12px;
    height: 100%;
    min-height: 300px;
    padding: 30px 20px;
    text-align: center;
    color: var(--color-text-muted);
    border: 1px dashed var(--color-border);
    border-radius: var(--border-radius-md);
}

.favorites__stub-text {
    margin: 0;
    font-size: 18px;
    font-weight: 500;
    line-height: 1.5;
    color: var(--color-text);
}

/* Ссылка «Удалить» у заглушки (js__fav_stub_remove, ?remove=<ID>). */
.favorites__stub-remove {
    color: var(--color-accent);
    text-decoration: underline;
    cursor: pointer;
}

.favorites__stub-remove:hover {
    color: var(--color-accent-light);
}

.favorites__clear {
    text-align: right;
}

/* --- Политика обработки персональных данных (/s2/def/): модификатор
   снимает ограничение ширины базового .article__content (max-width: 766px) —
   текст блока тянется на всю ширину контейнера страницы. --- */
.article__content--policy {
    max-width: none;
}

/* Модификатор той же страницы: базовый .about__intro — сетка в 2 колонки,
   здесь интро выводится одним блоком (без колонок и фото). */
.about__intro--policy {
    display: block;
}

/* --- Адрес в футере (footer.php, .footer__contact--address): на экранах
   ≤1279.98px .footer__contacts становится гридом, и вторая дорожка объявлена
   как max-content, то есть не сжимается — адрес выводится одной строкой и
   выходит за пределы блока. Меняем дорожку на minmax(0, 1fr): текст переносится
   по словам. Три правила обязательны — каждый брейкпоинт макета заново
   объявляет ОБЕ дорожки, иначе max-content вернётся на 991.98px/767.98px.
   На телефонах (≤767.98px) возвращаем вертикальную раскладку макета: display
   обнуляет grid-свойства, flex-direction/align-items/gap берутся из базового
   правила .footer__contacts. Правило идёт последним — при равенстве
   специфичности побеждает порядок в файле. main.min.css не изменяется. --- */
@media (width <= 1279.98px) {
    .footer__contacts {
        grid-template-columns: calc(20% - 16px) minmax(0, 1fr);
    }
}

@media (width <= 991.98px) {
    .footer__contacts {
        grid-template-columns: calc(33.3333% - 13.3333px) minmax(0, 1fr);
    }
}

@media (width <= 767.98px) {
    .footer__contacts {
        display: flex;
    }
}

/* Страховка от длинного слова/названия без пробелов в адресе (ИБ 14,
   элемент footer_contacts_address). */
.footer__contact--address {
    overflow-wrap: break-word;
}

/* Галереи детальных страниц (.object__gallery.swiper — объекты и посёлки):
   смена фото без анимации прозрачности. Ни main.min.css, ни нативный
   main.min.js fade не задают (effect не передан, .swiper-fade нет, opacity
   в правилах .swiper* / .object__gallery* отсутствует) — остаётся штатный
   сдвиг слайдов по transform. Правило фиксирует требование явно, чтобы
   появление картинки при смене слайда не читалось как проявление из
   прозрачности. */
.object__gallery-image {
    opacity: 1;
    transition: none;
}

/* --- Политика обработки персональных данных (/s2/def/): модификатор
   снимает ограничение ширины базового .article__content (max-width: 766px) —
   текст блока тянется на всю ширину контейнера страницы. --- */
.article__content--policy {
    max-width: none;
}

/* Модификатор той же страницы: базовый .about__intro — сетка в 2 колонки,
   здесь интро выводится одним блоком (без колонок и фото). */
.about__intro--policy {
    display: block;
}