/* Ferrara Finishes — стили попапа хотспота (#hspop).

   Вынесены из shared.css 2026-09-12 (блок 7 эстимейтов) вместе с логикой
   (assets/hotspot-popover.js): попап понадобился документу эстимейта, а
   его менеджерский вид идёт через портальный каркас, который shared.css не
   подключает вовсе. Копировать три десятка правил во второй файл нельзя —
   именно от такого дублирования владелец просил уходить.

   Правила перенесены БЕЗ ИЗМЕНЕНИЙ, вместе с комментариями и обоими
   медиазапросами (≤560px — bottom sheet, prefers-reduced-motion).
   Подключать ВМЕСТЕ с hotspot-popover.js; сама точка .hotspot осталась в
   shared.css — она есть только на публичных страницах, у документа свой
   свотч (.ev-schema-sw в portal/assets/estimate-view.css). */
/* Подсветка открытой точки — .hspop-hotspot-ghost ниже, не сама .hotspot:
   настоящая точка лежит глубоко внутри карусели, и у некоторых контейнеров
   (#mv-hotspots и т.п., detail-page.css) собственный z-index создаёт свой
   стекинг-контекст — никакой z-index на самой точке не поднял бы её выше
   scrim. */

/* .hspop-hotspot-ghost — "двойник" точки поверх scrim (см. carousel.php) —
   тот же вид, что .hotspot::after, но как независимый элемент на самом
   верхнем уровне стекинга (z-index выше scrim), site.js подставляет его
   точно на координаты настоящей точки на время открытого попапа. Цвет —
   белый, тот же, что и у обычной (неоткрытой) точки — по прямому запросу
   без изменений цвета, только чтобы не темнел под scrim. Раньше была
   скрыта на мобильном (≤560px) — НАЙДЕНО И ИСПРАВЛЕНО: на мобильном точка
   под scrim гасла точно так же, как на десктопе, подсветка нужна там же. */
.hspop-hotspot-ghost {
  position: fixed;
  width: 22px;
  height: 22px;
  margin: -11px 0 0 -11px;
  border-radius: 50%;
  background: var(--text-on-accent);
  color: var(--ink);
  font-size: 14px;
  font-style: italic;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  z-index: 2101;
  opacity: 0;
  transition: opacity 0.15s ease;
  pointer-events: none;
}
.hspop-hotspot-ghost.show {
  opacity: 1;
}

/* Линия-коннектор от точки до попапа (site.js::updateHotspotConnectorLine(),
   вызывается из positionHotspotPopover()). На десктопе — до ближайшего угла
   плавающей карточки. На мобильном (≤560px, bottom sheet) — НАЙДЕНО И
   ИСПРАВЛЕНО, раньше линия была скрыта целиком: теперь видна и там, до
   середины верхней грани .hspop-box (единственная содержательная точка у
   sheet'а, который сам всегда прижат к низу экрана во всю ширину — левой/
   правой/нижней грани, к которым имело бы смысл тянуть линию, у него нет).
   Толщина 3px (была 1px, по прямому запросу — "потолще") — div, повёрнутый
   transform:rotate() на нужный угол, обычный приём для "линии между двумя
   точками" в CSS, без SVG. z-index ВЫШЕ scrim (2099), не 2050, как было —
   иначе scrim, лежащий поверх, приглушал цвет линии (тот же найденный
   баг, что и у подсветки точки выше). Цвет — белый, по прямому запросу
   (был var(--line)), без изменений — только не темнеть под scrim. */
.hspop-line {
  position: fixed;
  height: 3px;
  background: var(--text-on-accent);
  transform-origin: 0 50%;
  z-index: 2101;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.hspop-line.show {
  opacity: 1;
}

/* ── HOTSPOT POPOVER — canonical, shared between project.php и homepage.php.
   Минималистичный (design-spec-v2.md §6): свотч + название + подзаголовок +
   одна CTA-ссылка, без описания/тегов/списков — данные приходят с сервера
   структурой из hotspot_resolver.php, DOM строит site.js::
   buildHotspotPopoverDom(). Текст переиспользует те же классы, что и
   карточки каталога (.card-eyebrow/.card-name/.card-subtitle/.cta-link,
   см. блок CATALOG CARD выше) — тот же приём типографики, не третий набор
   стилей текста.

   Десктоп — маленькая горизонтальная карточка (.popup) у самой точки
   хотспота, появление/исчезание — fade+scale всего попапа. Мобильный
   (≤560px) — тот же #hspop/.hspop-box становится bottom sheet (fixed внизу
   экрана, slide-up) — .popup прячется, показывается .sheet-row/.sheet-footer,
   см. media-запрос ниже. Оба режима — со scrim (.hspop-scrim) позади,
   гасящим фон, чтобы случайный клик мимо попапа не улетал в то, что под
   ним — тот же .show-класс, что и у .hspop, включает/выключает и его. */
/* pointer-events ВСЕГДА none, включая .show — НАЙДЕНО И ИСПРАВЛЕНО: раньше
   .show включал pointer-events:auto, и scrim (z-index 2099, поверх точки —
   у неё z-index:5) при hit-testing "перекрывал" точку для браузера, даже
   без какого-либо участия JS — сам факт, что scrim физически выше точки в
   стекинге, заставлял браузер слать точке mouseleave, стоило scrim
   появиться, что запускало цикл закрытие→точка снова видна→mouseenter→
   попап открывается заново (бесконечное мигание курсора). Закрытие по
   клику "мимо" не зависит от pointer-events самого scrim — оно уже ловится
   общим document-level обработчиком клика (см. site.js, проверяет
   e.target.closest('.hspop')/.hotspot) независимо от того, что физически
   было под курсором в момент клика. */
.hspop-scrim {
  position: fixed;
  inset: 0;
  z-index: 2099;
  background: color-mix(in srgb, var(--overlay-dark) 45%, transparent);
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 0.15s ease,
    visibility 0s linear 0.15s;
  pointer-events: none;
}
.hspop-scrim.show {
  opacity: 1;
  visibility: visible;
  transition:
    opacity 0.15s ease,
    visibility 0s linear;
}

/* transform:scale() убран из анимации появления (НАЙДЕНО И ИСПРАВЛЕНО —
   повторные жалобы на "рывок" после появления, меньше 10px вверх + пошире
   на 15px под крестик): даже с transform-origin у ближнего к точке края
   (site.js::positionHotspotPopover()) масштаб 0.94→1 делал любую
   неточность в размере/позиции (в т.ч. из-за web-шрифтов, у которых метрики
   могут чуть доуточниться после первого layout) визуально заметной как
   рост/сдвиг. Только opacity — тот же плавный вход, но без риска
   "доразрастания". */
.hspop {
  position: fixed;
  left: 0;
  top: 0;
  z-index: 2100;
  opacity: 0;
  visibility: hidden;
  transition:
    opacity 0.15s ease,
    visibility 0s linear 0.15s;
  pointer-events: none;
}
.hspop.show {
  opacity: 1;
  visibility: visible;
  transition:
    opacity 0.15s ease,
    visibility 0s linear;
  pointer-events: auto;
}

/* Без фиксированного max-width (320px/420px пробовали раньше) — по прямому
   запросу ширина не ограничена, растёт под контент, чтобы название всегда
   помещалось в одну строку (см. .popup-body .card-name{white-space:nowrap}
   ниже). calc(100vw - 24px) — единственный предел, и это не декоративное
   число, а тот же зазор (margin:12px с каждой стороны), которым
   positionHotspotPopover() в site.js зажимает попап в границах окна —
   иначе на узком экране очень длинное название всё равно вытолкнуло бы
   попап за пределы страницы. */
/* border — то же обрамление, что и у карточек (.card, border:1px solid
   var(--line)) — раньше попап держался только на тени, без явной обводки;
   теперь есть свой цвет, который переиспользуют линия-коннектор и подсветка
   хотспота ниже — единая "нить" от точки до попапа одного цвета с его же
   рамкой. */
.hspop-box {
  position: relative;
  max-width: calc(100vw - 24px);
  border: 1px solid var(--line);
  border-radius: var(--r);
  background: var(--bg-card);
  box-shadow: 0 20px 46px color-mix(in srgb, var(--shadow-black) 20%, transparent);
  overflow: hidden;
}
.hspop-close {
  position: absolute;
  top: 8px;
  right: 8px;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  border: 1px solid var(--line-2);
  font-size: 15px;
  line-height: 1;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 3;
}
.hspop-close:hover {
  background: var(--surface);
}

/* Переключение между разными хотпоинтами, пока попап уже открыт — сперва
   гасим текущее содержимое (.switching), затем (см. showHotspotCard() в
   site.js) подменяем DOM и включаем обратно, чтобы старая карточка
   успевала исчезнуть, а не подменялась мгновенно. */
.hspop-mount {
  transition: opacity 0.08s ease;
}
.hspop.switching .hspop-mount {
  opacity: 0;
}

/* ── ДЕСКТОП — .popup: свотч слева + текст справа (CTA прижата к низу
   текстовой колонки через margin-top:auto). ── */
.popup {
  display: flex;
}
.popup[data-clickable] {
  cursor: pointer;
}
.popup-swatch {
  width: 120px;
  height: 120px;
  min-width: 120px;
  aspect-ratio: 1/1;
  overflow: hidden;
  background: var(--bg-3);
}
.popup-swatch img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* Отступы/margin приведены 1:1 к прототипу (redesign-v2.css, .rdv2-popup-
   body/.rdv2-sheet-text) — раньше здесь были другие числа (16px 18px вместо
   5px 10px и т.д.), из-за чего боевой попап визуально отличался от
   dev-preview-cards.php (найденный баг). */
/* padding-right больше остальных — резерв под .hspop-close (26px кнопка +
   8px отступ от края = 34px), иначе длинное однострочное название (см.
   white-space:nowrap ниже) заезжало под крестик. */
.popup-body {
  padding: 5px 34px 5px 10px;
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.popup-body .card-name {
  font-size: 16px;
  margin-bottom: 2px;
  white-space: nowrap;
}
.popup-body .card-subtitle {
  margin: -4px 0 0;
}
/* white-space:nowrap — НАЙДЕНО И ИСПРАВЛЕНО: без него браузер при расчёте
   "естественной" ширины попапа (shrink-to-fit) считал себя вправе перенести
   текст ссылки на 2 строки вместо того, чтобы посчитать её как часть
   максимальной ширины контента — JS-замер (site.js::positionHotspotPopover())
   получал ширину БЕЗ учёта полной ссылки, и уже показанный попап "донашивал"
   недостающую ширину следующим кадром (тот самый рывок/расширение). Теперь
   ссылка всегда учитывается в расчёте с самого начала. */
.popup-body .cta-link {
  margin-top: auto;
  align-self: flex-start;
  display: inline-block;
  white-space: nowrap;
}

/* ── МОБИЛЬНЫЙ — bottom sheet. .sheet-* скрыты по умолчанию (видны только
   ≤560px ниже), .hspop-box на десктопе остаётся обычной плавающей
   карточкой — свою позицию/форму меняет только в мобильном медиа-запросе. ── */
.sheet-handle {
  display: none;
}
.sheet-row {
  display: none;
}
.sheet-footer {
  display: none;
}

@media (max-width: 560px) {
  /* На мобильном анимирует не сам #hspop (fade+scale), а .hspop-box —
     slide-up снизу экрана (transform:translateY), поэтому у #hspop
     scale-анимация здесь отключена, чтобы не дублировать движение. */
  .hspop,
  .hspop.show {
    transform: none;
  }
  .hspop-box {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    max-width: none;
    border-radius: var(--r-panel) var(--r-panel) 0 0;
    padding: 10px 20px 22px;
    transform: translateY(100%);
    transition: transform 0.22s ease;
    touch-action: none; /* см. enableHotspotSheetSwipeClose() в site.js — без этого браузер может начать скроллить страницу под пальцем ДО того, как JS успеет preventDefault() */
  }
  .hspop.show .hspop-box {
    transform: translateY(0);
  }
  .hspop-close {
    top: 12px;
    right: 16px;
  }

  .popup {
    display: none;
  }
  .sheet-handle {
    display: block;
    width: 36px;
    height: 4px;
    background: var(--line);
    border-radius: 2px;
    margin: 0 auto 14px;
  }
  .sheet-row {
    display: flex;
    align-items: center;
    gap: 14px;
  }
  .sheet-row[data-clickable] {
    cursor: pointer;
  }
  .sheet-swatch {
    width: 100px;
    height: 100px;
    min-width: 100px;
    aspect-ratio: 1/1;
    border-radius: var(--r);
    overflow: hidden;
    background: var(--bg-3);
  }
  .sheet-swatch img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
  .sheet-text {
    min-width: 0;
    align-self: center;
  }
  .sheet-footer {
    display: block;
    margin-top: 16px;
    padding-top: 14px;
    border-top: 1px solid var(--line);
  }
}

@media (prefers-reduced-motion: reduce) {
  .hspop,
  .hspop-box,
  .hspop-scrim,
  .hspop-mount {
    transition: none;
  }
}
