/* home-base.css — стили главной, ОДНИ на обе версии дизайна.
 *
 * Перенесено 2026-08-29 из <style> внутри pages/home/index.html без единой правки.
 * Здесь живёт в том числе весь мобильный вид (шторка фильтров, меню-бургер, карточки):
 * по решению владельца телефон в новой версии выглядит так же, как сегодня, поэтому
 * новая версия подключает ЭТОТ файл первым и лишь надстраивает над ним десктоп.
 */

  /* Tailwind CDN's .flex/.block utilities have the same specificity as base [hidden]{display:none},
     so a node with class="flex" + the hidden attribute stayed VISIBLE (e.g. the account-menu links,
     incl. "Сайт"). Force [hidden] to win so JS `el.hidden = true` actually hides. (matches /pms, /admin) */
  [hidden] { display: none !important; }
  body {
    background-color: #fcf9f8;
    color: #1c1b1b;
    font-family: 'Manrope', sans-serif;
    /* ERR-018 guard: NO overflow-x:hidden on html/body — it breaks position:fixed on Android.
       Horizontal bleed is contained per-section instead. */
  }
  /* D-06 MAJOR fix: contain any horizontal bleed on the IN-FLOW content containers only (never
     html/body — ERR-018). #filter-drawer/#filter-overlay/#cc-widget stay position:fixed siblings
     of these, so clipping header/main/footer does not affect them. */
  /* overflow-y stays explicit "visible" — setting only overflow-x would otherwise force the
     browser to promote overflow-y to "auto" (CSS spec: mismatched visible/non-visible pairs are
     not allowed), which would turn main into its own scroll container and break the descendant
     sticky booking-form/filters-rail panels (position:sticky needs the document as scroller). */
  #site-header, main, footer { overflow-x: clip; overflow-y: visible; max-width: 100vw; }
  /* Belt-and-braces: the off-canvas drawer must never contribute to scrollWidth even while
     translated off-screen, independent of the cause above. */
  #filter-drawer { max-width: 100vw; }
  /* iconify icons inherit color + size; default optical sizing for crisp render */
  iconify-icon { display: inline-block; vertical-align: -0.125em; }
  .price-num { font-variant-numeric: tabular-nums; }

  /* ===== Module-5 effects (mechanics 1:1 from Hotel DEMO 2, palette recolored to light B/W) ===== */
  /* Hotel tile photos: whole image appears at once on load (no top-down streaming), placeholder bg until then */
  .tile-photo {
    opacity: 0;
    /* Мягкий наезд: одна transition на 450ms в обе стороны, без переопределения
       длительности в :hover (решение владельца 31.08.2026). */
    transition: opacity .2s ease, transform 450ms cubic-bezier(0.25,0.46,0.45,0.94);
    backface-visibility: hidden;
  }
  .tile-photo.is-loaded { opacity: 1; }
  /* Наезд фото — только там, где есть настоящее наведение мышью. На телефоне первое касание
     считается наведением, и фото оставалось увеличенным навсегда. */
  @media (hover: hover) and (pointer: fine) {
    #hotels-grid .group:hover .tile-photo {
      transform: scale(1.04) translateZ(0);
    }
  }
  @media (prefers-reduced-motion: reduce) {
    #hotels-grid .group:hover .tile-photo { transform: none; }
  }

  /* E7 scroll-reveal fade-up + cascade. Fallback: .js-ready not set => content visible (no-aos safety). */
  .reveal { opacity: 1; transform: none; }
  html.js-ready .reveal { opacity: 0; transform: translateY(24px); transition: opacity .35s cubic-bezier(0.23,1,0.32,1), transform .45s cubic-bezier(0.23,1,0.32,1); will-change: opacity, transform; }
  html.js-ready .reveal.is-in { opacity: 1; transform: none; }
  .reveal.delay-1 { transition-delay: .06s; }
  .reveal.delay-2 { transition-delay: .12s; }
  .reveal.delay-3 { transition-delay: .18s; }
  .reveal.delay-4 { transition-delay: .24s; }

  /* E1 sticky header: transparent over hero → blurred panel on scroll */
  #site-header { transition: background-color .3s ease, box-shadow .3s ease, backdrop-filter .3s ease; }
  #site-header.scrolled {
    background-color: rgba(252, 249, 248, 0.82);
    backdrop-filter: saturate(140%) blur(12px);
    -webkit-backdrop-filter: saturate(140%) blur(12px);
    box-shadow: 0 1px 0 0 rgba(0, 0, 0, 0.06), 0 8px 24px -16px rgba(0, 0, 0, 0.18);
  }

  /* D-06 CRIT fix: mobile nav drawer (burger). Mirrors the desktop nav + account actions below md.
     Reuses the SAME open/close/outside-click/Escape JS pattern as #acct-menu (see the account-menu
     module script), just a second independent trigger/panel pair — not a new interaction mechanism. */
  #mobile-menu {
    position: fixed; inset: 0; z-index: 65;
    background-color: rgba(28,27,27,0);
    transition: background-color .2s ease;
  }
  #mobile-menu.open { background-color: rgba(28,27,27,0.45); }
  #mobile-menu-panel {
    position: absolute; top: 0; right: 0; bottom: 0; width: min(84vw, 340px);
    background: #fcf9f8; box-shadow: -16px 0 48px -24px rgba(0,0,0,0.45);
    padding: 20px; display: flex; flex-direction: column; gap: 4px;
    overflow-y: auto;
    /* Шторка приезжает и уезжает, а не мигает: тот же приём, что у шторки фильтров —
       переход (не keyframes), поэтому быстрый повторный тап разворачивает движение. */
    transform: translateX(100%); transition: transform .28s cubic-bezier(.32,.72,0,1);
  }
  #mobile-menu.open #mobile-menu-panel { transform: translateX(0); }
  #mobile-menu:not(.open) { pointer-events: none; }
  /* Кружок консьержа лежал поверх подложки бургер-меню — тот же приём, что у шторки
     фильтров и шторки партнёров ниже. */
  #mobile-menu:not([hidden]) ~ #cc-widget { opacity: 0; pointer-events: none; }
  @media (prefers-reduced-motion: reduce) {
    #mobile-menu, #mobile-menu-panel { transition: none; }
  }

  /* E4 scroll-spy active nav underline */
  .nav-link { position: relative; }
  .nav-link.active { color: #000; }
  /* F-13 Подчёркивание активного пункта возвращено по решению владельца 30.08.2026:
     волосяная чёрная линия под тем разделом, на котором человек сейчас стоит. Только шапка
     широкого экрана — у пунктов шторки бургера своя линия снизу, вторая там лишняя.
     Наведение НЕ подчёркивается: линия говорит «вы здесь», а не «сюда можно нажать»
     (на телефоне тап оставлял бы её висеть). Цвет — тот же #000, что и у текста. */
  #site-header nav .nav-link::after {
    content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1px;
    background: #000; transform: scaleX(0); transform-origin: left;
    transition: transform 180ms ease-out;
  }
  #site-header nav .nav-link.active::after { transform: scaleX(1); }
  @media (prefers-reduced-motion: reduce) {
    #site-header nav .nav-link::after { transition: none; }
  }
  /* Меню аккаунта — всплывашка у своей кнопки: растёт из правого верхнего угла, а не
     возникает из ниоткуда (никогда не scale(0)). */
  #acct-menu {
    transform-origin: top right;
    opacity: 0; transform: scale(.97) translateY(-4px);
    transition: opacity 150ms cubic-bezier(0.23,1,0.32,1), transform 150ms cubic-bezier(0.23,1,0.32,1);
  }
  #acct-menu.is-open { opacity: 1; transform: none; }
  /* Пока меню гаснет (150 мс), оно всё ещё в потоке: без этого тап сразу после закрытия
     проваливался в прозрачное меню. То же у шторки бургера, там подложка во весь экран. */
  #acct-menu:not(.is-open) { pointer-events: none; }
  @media (prefers-reduced-motion: reduce) {
    #acct-menu { transition: none; }
  }

  /* E15 back-to-top spring slide-in */
  /* Sits ABOVE the floating concierge avatar (which occupies right:20/bottom:20, ~60px tall). */
  /* Кружок консьержа шириной 60px стоит по right:20px, значит его центр — в 50px от края.
     «Наверх» шириной 48px должен встать по той же оси: 50 − 24 = 26px. */
  #backToTop {
    position: fixed; right: 26px; bottom: 96px; z-index: 40;
    width: 48px; height: 48px; border-radius: 9999px;
    display: flex; align-items: center; justify-content: center;
    background: #000; color: #fff;
    opacity: 0; transform: translateY(16px) scale(.9); pointer-events: none;
    transition: opacity .3s ease, transform .4s cubic-bezier(.34,1.56,.64,1);
    box-shadow: 0 8px 24px -8px rgba(0, 0, 0, 0.4);
  }
  #backToTop.show { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
  /* Пока открыт консьерж, «Наверх» уходит своим переходом — правило `#backToTop.cc-away`
     и весь блок виджета переехали 31.08.2026 в общий `styles/concierge.css`: тот же
     консьерж теперь стоит на карточке отеля, карточке номера и на партнёрах.
     Страница подключает concierge.css ПОСЛЕ этого файла. */

  /* E21 micro: focus-visible rings (accessibility, ui-ux-pro-max) */
  a:focus-visible, button:focus-visible, input:focus-visible {
    outline: 2px solid #000; outline-offset: 2px; border-radius: 4px;
  }

  /* Reduced motion: kill all decorative motion, content always visible */
  @media (prefers-reduced-motion: reduce) {
    html.js-ready .reveal { opacity: 1 !important; transform: none !important; transition: none !important; }
    #site-header, #backToTop, .reveal { transition: none !important; }
    #hotels-grid img { transition: none !important; transform: none !important; }
    /* Пульсирующий скелет ожидания — тоже украшение: серые плашки остаются, мигание уходит.
       Класс приходит из Tailwind, поэтому гасим сам класс, а не каждое место. */
    .animate-pulse { animation: none !important; }
  }

  /* Партнёры: смена категории подменяет содержимое целиком — короткое затухание вместо
     рывка. Правило безвредно для остальных страниц: узла с таким id у них нет. */
  #partners-body { transition: opacity 120ms ease; }
  @media (prefers-reduced-motion: reduce) {
    #partners-body { transition: none; }
  }

  /* ===== S-06 filter bar + mobile drawer (E16) ===== */
  /* Filter "pill" buttons (stars / meals / distance presets) — selected state = filled primary. */
  .f-opt {
    border: 1px solid #c4c7c7; border-radius: 9999px;
    padding: 6px 14px; font-size: 14px; line-height: 1; color: #444748;
    background: #ffffff; cursor: pointer; white-space: nowrap;
    transition: color .2s ease, border-color .2s ease, background-color .2s ease, scale 160ms cubic-bezier(0.23,1,0.32,1);
  }
  .f-opt:hover { border-color: #000; color: #000; }
  .f-opt[aria-pressed="true"], .f-opt.is-active {
    background: #000; color: #fff; border-color: #000;
  }
  /* Мёртвый вариант выбора («скоро»): серый, не реагирует ни на курсор, ни на нажатие.
     Раньше такая строка была просто серой поверх .f-opt — и чернела под мышью, обещая
     нажатие, которого не будет. Серый затемнён до #6b6f6f: #a9acac на фоне #fcf9f8 давал
     около 2.4:1 и не читался, #767a7a — около 4.3:1 на белом, то есть всё ещё ниже нормы
     4.5:1 для мелкого текста; #6b6f6f даёт ≈5.1:1. Правило идёт ПОСЛЕ .f-opt:hover — при равном весе
     побеждает последнее, поэтому наведение гасится и на фишке `class="f-opt f-opt-off"`. */
  .f-opt-off { color: #6b6f6f; cursor: default; }
  /* `scale: none` — общее вдавливание press.css сюда не распространяется: мёртвая строка
     не имеет права отзываться на палец. */
  .f-opt-off:hover, .f-opt-off:active { color: #6b6f6f; border-color: #c4c7c7; transform: none; scale: none; }
  .f-group-label { font-size: 12px; letter-spacing: .05em; text-transform: uppercase; color: #444748; font-weight: 600; }
  /* U-18 G6: city dropdown — pill-shaped to match the filter aesthetic; scales to many cities without
     spilling rows of buttons. Custom chevron via background SVG (no native arrow restyle headaches). */
  .f-select {
    border: 1px solid #c4c7c7; border-radius: 9999px;
    padding: 8px 38px 8px 14px; font-size: 14px; line-height: 1.2; color: #1c1b1b;
    background-color: #fff; cursor: pointer; width: 100%; -webkit-appearance: none; appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 24 24' fill='none' stroke='%23444748' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat; background-position: right 12px center;
    transition: border-color .2s ease;
  }
  .f-select:hover, .f-select:focus { border-color: #000; outline: none; }
  /* U-18 G6a: price range slider, tinted to the primary (black) accent of the project. */
  .price-range { -webkit-appearance: none; appearance: none; width: 100%; height: 4px; border-radius: 9999px;
    background: #c4c7c7; outline: none; cursor: pointer; accent-color: #000; }
  .price-range::-webkit-slider-thumb { -webkit-appearance: none; appearance: none; width: 20px; height: 20px;
    border-radius: 9999px; background: #000; border: 2px solid #fcf9f8; box-shadow: 0 1px 4px rgba(0,0,0,0.3); cursor: pointer; }
  .price-range::-moz-range-thumb { width: 20px; height: 20px; border-radius: 9999px; background: #000;
    border: 2px solid #fcf9f8; box-shadow: 0 1px 4px rgba(0,0,0,0.3); cursor: pointer; }
  /* Отклик на нажатие: кнопка обязана «проседать» под пальцем. Само вдавливание с
     05.09.2026 одно на весь сайт (/styles/press.css) — здесь остаётся его плавность,
     дописанная К СПИСКУ того, что у кнопки уже плавно менялось: короткая запись
     transition стёрла бы цвет и прозрачность. */
  #filter-apply, #filter-apply-drawer, #ccy-note-ok, #publish-note-ok,
  #partners-sheet-close {
    transition: opacity .2s ease, scale 160ms cubic-bezier(0.23,1,0.32,1);
  }
  #filter-reset-drawer {
    transition: color .2s ease, border-color .2s ease, scale 160ms cubic-bezier(0.23,1,0.32,1);
  }
  #cc-launcher { transition: scale 160ms cubic-bezier(0.23,1,0.32,1); }
  @media (prefers-reduced-motion: reduce) {
    .f-opt, #filter-apply, #filter-apply-drawer, #filter-reset-drawer,
    #ccy-note-ok, #publish-note-ok, #partners-sheet-close, #cc-launcher { transition: none; }
  }

  /* Сердечко: белый кружок остался 32px, а нажимаемая кнопка вокруг него — 44px.
     Отрицательный отступ (top/right 1.5 вместо 3) держит кружок ровно там, где он был. */
  .card-fav:hover .card-fav-dot { background-color: #fff; }
  /* Отмеченный отель: сердце ГОРИТ КРАСНЫМ и остаётся таким после перезагрузки — владелец
     04.09.2026: «должно просто сердечко загораться красным и оставаться». Цвет держится на
     `aria-pressed`, а не на классе в разметке: у статической копии каталога для краулеров и у
     живой плитки разметка обязана совпадать до класса (tests/test_favourites.py), а состояние
     приходит уже в браузере. Класс+атрибут перебивают одиночный `.text-primary` тайлвинда. */
  .card-fav[aria-pressed="true"] { color: #ba1a1a; }

  /* Mobile drawer: off-canvas via transform (NO overflow-x:hidden on html/body — ERR-018). */
  #filter-drawer {
    position: fixed; inset: 0 0 0 auto; width: min(86vw, 360px); z-index: 60;
    background: #fcf9f8; box-shadow: -16px 0 48px -24px rgba(0,0,0,0.45);
    transform: translateX(100%); transition: transform .32s cubic-bezier(.22,.61,.36,1);
    display: flex; flex-direction: column;
    overscroll-behavior: contain; /* contain scroll inside drawer, not on body */
  }
  #filter-drawer.open { transform: translateX(0); }
  #filter-overlay {
    position: fixed; inset: 0; z-index: 55; background: rgba(0,0,0,0.4);
    opacity: 0; pointer-events: none; transition: opacity .3s ease;
  }
  #filter-overlay.open { opacity: 1; pointer-events: auto; }
  /* D-06 MAJOR fix: while the filter drawer is open, hide the chat FAB so it never overlaps the
     drawer's sticky "Показать" footer button (Pattern 4 option a — lower risk than repositioning). */
  #filter-drawer.open ~ #cc-widget { opacity: 0; pointer-events: none; }
  @media (prefers-reduced-motion: reduce) {
    #filter-drawer { transition: none; }
    #filter-overlay { transition: none; }
  }
  /* Touch targets ≥44px on mobile (success-criteria E2): bump pill + mobile trigger height.
     Desktop keeps the compact inline bar. lg breakpoint = 1024px. */
  @media (max-width: 1023px) {
    .f-opt { min-height: 44px; padding-top: 10px; padding-bottom: 10px; }
    #filter-open { min-height: 44px; }
    /* In the drawer, pills span full row for big, thumb-friendly hit areas. */
    #filter-drawer-body .f-opt { display: inline-flex; align-items: center; justify-content: center; }
  }
  /* U-18 G6c: the inline «Применить» belongs to the desktop rail; on mobile the drawer footer «Показать»
     commits instead, so hide the inline copy when #filter-controls is moved into the drawer. */
  #filter-drawer-body #filter-apply { display: none; }
  /* Reference-price notice: fade overlay + spring-up card */
  #ccy-note.is-open { opacity: 1; }
  #ccy-note.is-open #ccy-note-card { transform: translateY(0) scale(1); }
  @media (prefers-reduced-motion: reduce) {
    #ccy-note, #ccy-note #ccy-note-card { transition: none; }
  }
  /* «Только с компьютера» — окно кнопки «Разместить свой отель». Появляется и уходит так же,
     как справка о ценах; само НЕ исчезает, поэтому никакого таймера у него нет. */
  #publish-note.is-open { opacity: 1; }
  #publish-note.is-open #publish-note-card { transform: translateY(0) scale(1); }
  @media (prefers-reduced-motion: reduce) {
    #publish-note, #publish-note #publish-note-card { transition: none; }
  }

  /* Партнёры на телефоне: шторка снизу. Тот же приём, что у шторки фильтров — съезд
     transform'ом, без overflow-x на html/body (ERR-018). */
  #partners-sheet {
    position: fixed; inset: auto 0 0 0; z-index: 60;
    max-height: 80vh; background: #fcf9f8;
    border-radius: 24px 24px 0 0;
    box-shadow: 0 -16px 48px -24px rgba(0,0,0,0.45);
    transform: translateY(100%); transition: transform .32s cubic-bezier(.22,.61,.36,1);
    display: flex; flex-direction: column;
    overscroll-behavior: contain; /* прокрутка остаётся внутри шторки, не уезжает на страницу */
  }
  #partners-sheet.open { transform: translateY(0); }
  #partners-sheet-overlay {
    position: fixed; inset: 0; z-index: 55; background: rgba(0,0,0,0.4);
    opacity: 0; pointer-events: none; transition: opacity .3s ease;
  }
  #partners-sheet-overlay.open { opacity: 1; pointer-events: auto; }
  /* Пока шторка открыта, кружок консьержа прячется — иначе он лежит поверх «Закрыть». */
  #partners-sheet.open ~ #cc-widget { opacity: 0; pointer-events: none; }
  @media (prefers-reduced-motion: reduce) {
    #partners-sheet { transition: none; }
    #partners-sheet-overlay { transition: none; }
  }

  /* 260930, макет C (владелец: «Вариант С»). На телефоне пилюля «Налог включён в стоимость»
     уезжала под кнопку «Забронировать»: обе неразрывны, а в ряду колонки на двоих не хватает.
     Ниже md (там же сетка плиток становится одноколоночной) отметка — тихая серая строка без
     рамки, которой разрешён перенос; кнопка — обычным регистром и чуть уже, высота прежняя.
     Разметка не меняется (статика и живая плитка совпадают), десктоп не трогается.
     Фон кнопки здесь не задаётся — наведение (hover:bg-on-surface-variant) остаётся как было.
     Сторож — tests/test_home_price_tile.py (браузерная половина меряет пересечение на 360 px). */
  @media (max-width: 767.98px) {
    #hotels-grid [data-tax-badge] {
      height: auto; padding: 0; border: 0; border-radius: 0; background: transparent;
      align-items: flex-start; white-space: normal; font-size: 12px; line-height: 16px;
    }
    #hotels-grid [data-tax-badge] > svg { margin-top: 1px; }
    #hotels-grid [data-book] {
      text-transform: none; letter-spacing: 0; font-size: 14px;
      padding-left: 18px; padding-right: 18px;
    }
  }

  /* 30.09.2026 (владелец: «да чини»). Тот же наезд на компьютере: колонка плитки уже 372 px
     на 768–847 px и 1024–~1209 px (от 298 px), а пилюля «Налог не включён в стоимость» и
     «ЗАБРОНИРОВАТЬ» вместе требуют ~372. Вид компьютера (пилюля, заглавные) остаётся; ряду с
     отметкой разрешён перенос — кнопка уходит под неё вправо, только когда рядом не помещается.
     Разметка не меняется: ряд находится по кнопке внутри него. Сторож — tests/test_home_price_tile.py. */
  @media (min-width: 768px) {
    #hotels-grid div:has(> [data-book]):has([data-tax-badge]) { flex-wrap: wrap; }
    #hotels-grid div:has([data-tax-badge]) > [data-book] { margin-left: auto; }
  }
