/* ==========================================================================
   V2 APP STYLES — полностью scoped под .yh-app.
   ==========================================================================
   Каждое правило начинается с .yh-app — ни одного голого *, html, body, a,
   button, img селектора. Это то самое архитектурное ограничение из
   Production-ТЗ (п.0/16): модуль не должен влиять на элементы вне себя,
   даже если его смонтируют прямо в DOM Tilda-страницы без iframe.

   z-index — именованная шкала через custom properties на .yh-app, а не
   магические числа россыпью (см. аудит, "z-index / stacking contexts").
   Порядок соответствует Production-ТЗ п.17:
   scene BASE < map/bottom-nav < header/CTA < debug.
   Локальные интерактивы зон (Этап 3+) получат свои токены между
   --yh-z-scene и --yh-z-bottom-nav.
   ========================================================================== */

.yh-app {
  --yh-bg: #f4efe8;
  --yh-ink: #1c1a17;
  --yh-accent: #e0483e;
  --yh-panel-bg: rgba(28, 26, 23, 0.88);
  --yh-panel-fg: #f4efe8;
  --yh-header-h: 64px;

  --yh-z-scene: 1;
  --yh-z-map: 2;
  --yh-z-bubbles: 5;
  --yh-z-local: 10;
  --yh-z-bottom-nav: 40;
  --yh-z-header: 60;
  --yh-z-overlay: 80;
  --yh-z-debug: 999;

  position: relative;
  width: 100%;
  height: 100%;
  overflow: hidden;
  background: var(--yh-bg);
  color: var(--yh-ink);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  line-height: 1.3;
}

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

.yh-app button {
  font-family: inherit;
  cursor: pointer;
}

.yh-app a {
  color: inherit;
  text-decoration: none;
}

.yh-app :focus-visible {
  outline: 3px solid #2f7dee;
  outline-offset: 2px;
}

.yh-app[aria-disabled="true"],
.yh-app a[aria-disabled="true"] {
  opacity: 0.5;
  cursor: default;
}

/* ================= HEADER ================= */

.yh-header {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: var(--yh-header-h);
  z-index: var(--yh-z-header);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 16px;
  background: var(--yh-bg);
  border-bottom: 1px solid rgba(28, 26, 23, 0.08);
}

.yh-header__map-return {
  flex: 0 0 auto;
  border: none;
  background: rgba(28, 26, 23, 0.06);
  color: var(--yh-ink);
  font-size: 0.8rem;
  font-weight: 700;
  padding: 10px 16px;
  border-radius: 999px;
  min-height: 44px;
  cursor: pointer;
}

.yh-header__map-return:hover { background: rgba(28, 26, 23, 0.12); }

.yh-header__actions { flex: 0 0 auto; display: flex; align-items: center; }

/* ---- top content nav — independent of the bottom spatial route; see
   core/global-nav.js header comment. Desktop: a plain inline row,
   centered in the remaining header space between КАРТА ↑ and the CTA.
   Mobile (<=767px, same breakpoint SceneEngine already uses for
   desktop/mobile scene selection): collapses behind a "МЕНЮ" toggle so
   it never crowds the two buttons that must stay visible. ---- */
.yh-header__content-nav {
  flex: 1 1 auto;
  display: flex;
  justify-content: center;
  min-width: 0;
}

.yh-header__content-nav-toggle {
  display: none;
  border: none;
  background: rgba(28, 26, 23, 0.06);
  color: var(--yh-ink);
  font-size: 1.3rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  padding: 8px 16px;
  border-radius: 999px;
  min-height: 44px;
  min-width: 44px;
  line-height: 1;
  cursor: pointer;
}

.yh-header__content-nav-list {
  display: flex;
  align-items: center;
  gap: 20px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.yh-app .yh-header__content-nav-link {
  color: var(--yh-ink);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  white-space: nowrap;
  padding: 6px 2px;
  border-bottom: 2px solid transparent;
}

.yh-app .yh-header__content-nav-link:hover,
.yh-app .yh-header__content-nav-link:focus-visible {
  border-bottom-color: var(--yh-ink);
}

@media (max-width: 767px) {
  /* customer spec: "КАРТА на mobile отсутствует" — the bottom nav already
     covers zone navigation on mobile, so this top-bar "return to map"
     button is redundant there; display:none rather than touching its JS
     (still correct/available on desktop). */
  .yh-header__map-return { display: none; }
  .yh-header__content-nav { flex: 0 0 auto; position: relative; justify-content: flex-start; }
  .yh-header__content-nav-toggle { display: inline-block; }
  .yh-header__content-nav-list {
    display: none;
    position: absolute;
    top: calc(100% + 8px);
    left: 0;
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
    background: var(--yh-bg);
    border: 1px solid rgba(28, 26, 23, 0.12);
    border-radius: 12px;
    padding: 8px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
    min-width: 180px;
  }
  .yh-header__content-nav.is-open .yh-header__content-nav-list { display: flex; }
  .yh-app .yh-header__content-nav-link { padding: 10px 12px; min-height: 44px; display: flex; align-items: center; width: 100%; box-sizing: border-box; border-bottom: none; border-radius: 8px; }
  .yh-app .yh-header__content-nav-link:hover,
  .yh-app .yh-header__content-nav-link:focus-visible { background: rgba(28, 26, 23, 0.06); border-bottom: none; }
}

/* Same rules, driven by JS (core/global-nav.js setMobile(), called from
   mount.js's own isMobile() — the exact source of truth the scene itself
   uses to pick mobile vs desktop assets) instead of the viewport media
   query above. Kept as a second, unconditional copy rather than replacing
   the @media block: belt-and-suspenders against any host page where the
   container's real width (what isMobile() measures) and the window's
   width (what @media sees) can diverge — see core/global-nav.js's
   setMobile() comment for why that's the suspected root cause of this
   nav failing to collapse for the customer even though every environment
   this was tested in showed it collapsing correctly. */
.yh-header--mobile .yh-header__map-return { display: none; }
.yh-header__content-nav--mobile { flex: 0 0 auto; position: relative; justify-content: flex-start; }
.yh-header__content-nav--mobile .yh-header__content-nav-toggle { display: inline-block; }
.yh-header__content-nav--mobile .yh-header__content-nav-list {
  display: none;
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  background: var(--yh-bg);
  border: 1px solid rgba(28, 26, 23, 0.12);
  border-radius: 12px;
  padding: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
  min-width: 180px;
}
.yh-header__content-nav--mobile.is-open .yh-header__content-nav-list { display: flex; }
.yh-app .yh-header__content-nav--mobile .yh-header__content-nav-link { padding: 10px 12px; min-height: 44px; display: flex; align-items: center; width: 100%; box-sizing: border-box; border-bottom: none; border-radius: 8px; }

/* .yh-app .yh-cta (not just .yh-cta): needs specificity ≥ ".yh-app a"
   above, or that rule's color:inherit silently wins over color:#fff here
   despite source order — the exact category of bug documented in the V1
   audit ([hidden] vs. class specificity) and worth guarding against with
   a comment, since it will recur anywhere a scoped a/button reset meets
   a more specific component rule. */
.yh-app .yh-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 44px;
  padding: 10px 20px;
  border-radius: 999px;
  background: var(--yh-ink);
  color: #fff;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  pointer-events: auto;
}

.yh-app .yh-cta:hover { background: var(--yh-accent); }

/* ================= BOTTOM NAV (00-07) ================= */

.yh-bottom-nav {
  position: absolute;
  left: 50%;
  bottom: 18px;
  transform: translateX(-50%);
  z-index: var(--yh-z-bottom-nav);
  max-width: 94%;
  background: var(--yh-panel-bg);
  color: var(--yh-panel-fg);
  border-radius: 999px;
  padding: 8px;
}

.yh-bottom-nav[hidden] { display: none; }

.yh-bottom-nav__rail {
  display: flex;
  gap: 4px;
  overflow-x: auto;
  scrollbar-width: none;
}

.yh-bottom-nav__rail::-webkit-scrollbar { display: none; }

.yh-bottom-nav__item {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  border: none;
  background: transparent;
  color: rgba(244, 239, 232, 0.6);
  padding: 6px 14px;
  border-radius: 18px;
  min-height: 44px;
  font-size: 0.65rem;
  cursor: pointer;
  transition: background 150ms ease, color 150ms ease;
}

/* active zone — real color distinction (customer request: "пусть просто
   отличается по цвету как активная"), not just a subtle white tint.
   Uses that zone's own --yh-zone-color (same per-zone color already used
   for the Zone 00 map dots and the global Map view — one convention, not
   a new one). Dark text throughout: readable against every zone color in
   config/scenes.js, including the lighter ones (yellow, orange). */
.yh-bottom-nav__item.is-active {
  background: var(--yh-zone-color, rgba(244, 239, 232, 0.12));
  color: #16140f;
}

.yh-bottom-nav__num { font-weight: 700; font-size: 0.75rem; }
.yh-bottom-nav__title { white-space: nowrap; }

.yh-bottom-nav__here {
  white-space: nowrap;
  font-size: 0.5rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  margin-top: 1px;
}

.yh-bottom-nav__here[hidden] { display: none; }

/* ================= MAP VIEW ================= */

.yh-map-view {
  position: absolute;
  inset: 0;
  z-index: var(--yh-z-map);
  padding-top: var(--yh-header-h);
  overflow: auto;
}

.yh-map-view[hidden] { display: none; }

.yh-map-stage { position: relative; width: 100%; }

.yh-map-image { display: block; width: 100%; height: auto; }

.yh-map-hotspots { position: absolute; inset: 0; pointer-events: none; }

.yh-map-hotspot {
  position: absolute;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
  border: 2px solid var(--yh-zone-color, var(--yh-accent));
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.35);
  pointer-events: auto;
  cursor: pointer;
  transition: opacity 200ms ease, box-shadow 200ms ease;
}

/* hover/focus one zone -> that zone stays fully visible, the rest dim
   (Production-ТЗ Этап 2: "выделяется только выбранная зона; остальные
   части карты слегка приглушаются"). :has() is well-supported in current
   evergreen browsers (Chromium/Safari/Firefox) — no JS fallback needed. */
.yh-map-hotspots:has(.yh-map-hotspot:hover) .yh-map-hotspot:not(:hover),
.yh-map-hotspots:has(.yh-map-hotspot:focus-visible) .yh-map-hotspot:not(:focus-visible) {
  opacity: 0.4;
}

.yh-map-hotspot:hover,
.yh-map-hotspot:focus-visible {
  background: rgba(255, 255, 255, 0.65);
  /* thin colored glow instead of the generic focus ring — "тонкий
     outline/glow", "focus state визуально понятен" */
  box-shadow: 0 0 0 2px var(--yh-zone-color, var(--yh-accent)), 0 0 10px 2px var(--yh-zone-color, var(--yh-accent));
  outline: none;
}

.yh-map-hotspot--last-visited {
  border-style: dashed;
}

.yh-map-hotspot__label {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 6px);
  transform: translateX(-50%);
  white-space: nowrap;
  background: rgba(28, 26, 23, 0.88);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 700;
  padding: 4px 8px;
  border-radius: 6px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 150ms ease;
}

.yh-map-hotspot:hover .yh-map-hotspot__label,
.yh-map-hotspot:focus-visible .yh-map-hotspot__label {
  opacity: 1;
}

/* ================= GENERIC HOTSPOT LAYER (Zone 01 objects + passage;
   Production-ТЗ: Zone 02/03 reuse this same layer unchanged later) ================= */

.yh-hotspot-layer {
  position: absolute;
  inset: 0;
  z-index: var(--yh-z-local);
  pointer-events: none;
}

.yh-object-hotspot__item,
.yh-passage-hotspot__item {
  position: absolute;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
  border: none;
  background: transparent;
  pointer-events: auto;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* small coded "+" — the approved Zone 01 illustration has no pre-drawn
   marker at these positions (unlike Zone 02/03's baked "+"), so
   Production-ТЗ explicitly allows a small code-drawn one here. Subtle at
   rest, matching "визуальный язык должен оставаться частью архитектурной
   сцены" — not a bold UI chip. */
/* --yh-counter-scale is set from JS (core/hotspot-layer.js, opt-in via
   config.counterScaleDot) to cancel out the scene's own camera zoom —
   customer (2026-09-15): this dot was down to ~2.5px on a real phone.
   Defaults to 1 (no-op) for any layer that doesn't opt in. Exposed as a
   custom property rather than a plain JS-set transform so it composes
   with the :hover scale below instead of an inline style overriding it. */
.yh-object-hotspot__dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.55);
  border: 1.5px solid rgba(28, 26, 23, 0.55);
  transform: scale(var(--yh-counter-scale, 1));
  transition: transform 150ms ease, background 150ms ease;
}

.yh-object-hotspot__item:hover .yh-object-hotspot__dot,
.yh-object-hotspot__item:focus-visible .yh-object-hotspot__dot {
  transform: scale(calc(var(--yh-counter-scale, 1) * 1.6));
  background: #fff;
}

.yh-object-hotspot__item:focus-visible {
  outline: none;
}

.yh-object-hotspot__item:focus-visible .yh-object-hotspot__dot {
  box-shadow: 0 0 0 3px rgba(47, 125, 238, 0.7);
}

/* customer revision: "чёрная pill-кнопка в стилистике той, которая сейчас
   осталась у 05 Столбище... одинаковый размер и положение на всех 5
   карточках... круглые белые кнопки со стрелкой убираем полностью" — the
   separate FAST-PASS circular marker (.yh-material-link__*) is removed
   entirely (see core/zone-01-hall.js); this black pill, already identical
   in size/position across all 5 objects since it's the same hotspot-
   layer markup and CSS for each, is now the only button.
   Follow-up correction: "надпись не должна быть видна постоянно — только
   при наведении на конкретную карточку" — the previous pass made it
   permanently visible (opacity:1 unconditionally), which meant all 5
   showed and overlapped at once; back to hover/focus-gated, one at a
   time, per the object it's attached to. */
.yh-object-hotspot__label {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 4px);
  transform: translateX(-50%);
  white-space: nowrap;
  background: rgba(28, 26, 23, 0.88);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 700;
  padding: 4px 9px;
  border-radius: 6px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 150ms ease;
}

/* customer (2026-09-10): exactly one board's label stays permanently
   visible (see core/zone-01-hall.js) as a discoverability hint on
   touchscreens, where hover never fires — the other four stay
   hover/focus-only, unchanged. */
.yh-object-hotspot__item--always-visible .yh-object-hotspot__label {
  opacity: 1;
}

.yh-object-hotspot__item:hover .yh-object-hotspot__label,
.yh-object-hotspot__item:focus-visible .yh-object-hotspot__label {
  opacity: 1;
}

/* Caption class is derived per-layer in hotspot-layer.js (layerClass +
   "__caption"), not one fixed generic class — a future zone can give its
   own caption a different look here without touching the generic JS.
   Zone 01's object hotspots and the passage happen to want the same
   look today, hence the grouped selector; that's a CSS/data choice, not
   something the generic component decided. */
.yh-object-hotspot__caption,
.yh-passage-hotspot__caption {
  position: absolute;
  transform: translate(-50%, 0);
  background: rgba(28, 26, 23, 0.92);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 600;
  line-height: 1.4;
  text-align: center;
  padding: 6px 10px;
  border-radius: 8px;
  max-width: 180px;
  pointer-events: none;
  white-space: pre-line;
}

.yh-object-hotspot__caption[hidden],
.yh-passage-hotspot__caption[hidden] { display: none; }

/* ================= PASSAGE (spatial nav, not a content hotspot) ================= */

.yh-passage-hotspot__dot {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  border: 1.5px solid rgba(255, 255, 255, 0.7);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.85rem;
  background: rgba(28, 26, 23, 0.25);
  transition: background 150ms ease, transform 150ms ease;
}

.yh-passage-hotspot__dot::before { content: '→'; }

.yh-passage-hotspot__item:hover .yh-passage-hotspot__dot,
.yh-passage-hotspot__item:focus-visible .yh-passage-hotspot__dot {
  background: rgba(28, 26, 23, 0.6);
  transform: scale(1.1);
}

.yh-passage-hotspot__item:focus-visible {
  outline: none;
}

.yh-passage-hotspot__item:focus-visible .yh-passage-hotspot__dot {
  box-shadow: 0 0 0 3px rgba(47, 125, 238, 0.7);
}

.yh-passage-hotspot__label {
  position: absolute;
  right: calc(100% + 8px);
  top: 50%;
  transform: translateY(-50%);
  white-space: nowrap;
  background: rgba(28, 26, 23, 0.88);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 700;
  padding: 4px 9px;
  border-radius: 6px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 150ms ease;
}

.yh-passage-hotspot__item:hover .yh-passage-hotspot__label,
.yh-passage-hotspot__item:focus-visible .yh-passage-hotspot__label {
  opacity: 1;
}

/* ================= ZONE 00 MAP NAVIGATION (in-scene "КАРТА ХАКАТОНА"
   stand, 8 hotspots 00-07 — distinct from the separate full-screen Map
   view in .yh-map-view/.yh-map-hotspot above, which is untouched). Visual
   language matches that existing Map view (visible ring marker, tooltip
   label on hover/focus) rather than inventing a third convention. ================= */

/* UX PASS (customer, 2026-09-15: "более наживным... куда нажимать" —
   more obvious where to click). Found via a CSS audit: every hotspot
   layer here renders its clickable item as a plain <button> (not an
   <a href>, which browsers already show a pointer cursor for by
   default) whenever the item has no outbound url — a bare <button> gets
   the normal arrow cursor from the browser's own default stylesheet, not
   a pointer, unless CSS says so explicitly. That was missing on this and
   four other hotspot types below (Zone 00/01/06/passage, the material-
   link hotspot, and Zone 07's baked CTA) — added cursor:pointer to all
   of them; already-correct hotspot types (Zone 03's AI link, Zone 04's
   drawers, Zone 05's rotate button) are untouched. */
.yh-zone00-hotspot__item {
  position: absolute;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
  border: none;
  background: transparent;
  pointer-events: auto;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  /* customer: "при открытии карточки остальные кружочки все равно на
     переднем плане" — siblings with no z-index stack purely by DOM order,
     so a marker later in the list could sit in front of an earlier one's
     open label. Give every marker the same baseline and let :hover/
     :focus-visible/--current (below) raise just the active one above it. */
  z-index: 1;
}

.yh-zone00-hotspot__item:hover,
.yh-zone00-hotspot__item:focus-visible,
.yh-zone00-hotspot--current {
  z-index: 2;
}

.yh-zone00-hotspot__dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  border: 2px solid var(--yh-zone-color, var(--yh-accent));
  background: rgba(255, 255, 255, 0.35);
  transition: background 150ms ease, box-shadow 150ms ease, transform 150ms ease;
}

.yh-zone00-hotspot__item:hover .yh-zone00-hotspot__dot,
.yh-zone00-hotspot__item:focus-visible .yh-zone00-hotspot__dot {
  background: rgba(255, 255, 255, 0.75);
  box-shadow: 0 0 0 2px var(--yh-zone-color, var(--yh-accent)), 0 0 10px 2px var(--yh-zone-color, var(--yh-accent));
}

.yh-zone00-hotspot__item:focus-visible { outline: none; }

/* current-location marker — the one hotspot meant to be understood at a
   glance, without hovering (a first-time visitor has no reason to hover
   "00" to learn they're already there) */
.yh-zone00-hotspot--current .yh-zone00-hotspot__dot {
  background: var(--yh-zone-color, var(--yh-accent));
  box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.85);
}

/* customer (2026-09-15): "именно на мобильной версии убрать постоянно
   горящую табличку" — the always-on label was permanently occupying
   space right on top of the already-tightly-packed mobile map; kept on
   desktop (plenty of room there, and it's the map's only always-visible
   "you are here" cue) but hidden by default on mobile, where hover/tap
   reveals it like every other marker's label instead. */
@media (min-width: 768px) {
  .yh-zone00-hotspot--current .yh-zone00-hotspot__label {
    opacity: 1;
  }
}

.yh-zone00-hotspot__label {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 11px);
  transform: translateX(-50%);
  white-space: nowrap;
  background: rgba(28, 26, 23, 0.88);
  color: #fff;
  /* Reverted the earlier 1.8x bump ("увеличь масштаб этих кнопок") — on
     the real map the "00" current-zone label is always visible
     (opacity:1, see the --current rule above) sitting right next to the
     other seven markers on a small isometric diagram; at 1.8x its card
     collided with whichever neighbour label was showing on hover.
     Customer follow-up: "слишком большой шрифт на карте и накладываются
     карточки, было не так" — back to the original, smaller size. */
  font-size: 0.68rem;
  font-weight: 700;
  padding: 5px 9px;
  border-radius: 6px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 150ms ease;
  text-align: center;
}

/* title + description on the map itself, on hover (see
   core/zone-00-map.js labelText — the bottom-nav no longer carries this
   text, per customer follow-up: "прям на карте при наведении на
   кружочек высвечивается название и описание"). */
.yh-zone00-hotspot__label-title { display: block; }

.yh-zone00-hotspot__label-desc {
  display: block;
  font-weight: 400;
  font-size: 0.9em;
  opacity: 0.85;
  margin-top: 4px;
  white-space: normal;
  max-width: 160px;
}

.yh-zone00-hotspot__item:hover .yh-zone00-hotspot__label,
.yh-zone00-hotspot__item:focus-visible .yh-zone00-hotspot__label {
  opacity: 1;
}

.yh-zone00-hotspot__caption {
  position: absolute;
  transform: translate(-50%, 0);
  background: rgba(28, 26, 23, 0.92);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 600;
  line-height: 1.4;
  text-align: center;
  padding: 6px 10px;
  border-radius: 8px;
  max-width: 180px;
  pointer-events: none;
  white-space: pre-line;
}

.yh-zone00-hotspot__caption[hidden] { display: none; }

/* CTA hotspot — a transparent hit-area over the BASE's already-drawn
   "ПОДАТЬ ЗАЯВКУ →" pill graphic, sized to match it (not the default
   44x44 circle every other Zone 00 hotspot uses). No visible ring at
   rest: the pill is already a complete visual button in the artwork,
   drawing a second marker on top of it would just duplicate it.
   width/height are set inline per-platform in core/zone-00-map.js (the
   desktop and mobile pill graphics are proportioned differently on their
   own BASE images — see config/zone-00-content.js ZONE_00_CTA). */
.yh-zone00-hotspot__item[data-hotspot-id="cta"] {
  border-radius: 999px;
}

.yh-zone00-hotspot__item[data-hotspot-id="cta"] .yh-zone00-hotspot__dot {
  width: 100%;
  height: 100%;
  border: none;
  border-radius: 999px;
  background: transparent;
}

.yh-zone00-hotspot__item[data-hotspot-id="cta"]:hover .yh-zone00-hotspot__dot,
.yh-zone00-hotspot__item[data-hotspot-id="cta"]:focus-visible .yh-zone00-hotspot__dot {
  background: rgba(255, 255, 255, 0.18);
  box-shadow: none;
}

.yh-app--debug .yh-zone00-hotspot__item {
  outline: 1px dashed rgba(0, 120, 255, 0.85);
  outline-offset: -1px;
}

.yh-app--debug .yh-zone00-hotspot__label {
  opacity: 1 !important;
}

/* ================= ZONE 00 CODE-GENERATED SOAP BUBBLES (FAST PASS —
   see core/zone-00-bubbles.js). No raster asset anywhere here — every
   visual layer below is a gradient. Sits BELOW --yh-z-local so a hotspot
   always wins a visual/interaction overlap, never the reverse (spec
   п.12). Lives inside .yh-scene-stage, so it pans/zooms with the BASE
   like everything else in-scene (spec п.13). ================= */

.yh-bubbles-layer {
  position: absolute;
  inset: 0;
  z-index: var(--yh-z-bubbles);
  overflow: visible;
  pointer-events: none;
}

.yh-bubble {
  position: absolute;
  width: 0;
  height: 0;
  transition: transform 0.5s cubic-bezier(0.22, 1, 0.36, 1);
}

.yh-bubble__drift {
  position: absolute;
  left: 0;
  top: 0;
}

.yh-bubble__body {
  position: absolute;
  left: 0;
  top: 0;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  overflow: hidden;
  pointer-events: auto;
  cursor: pointer;
  /* the "thin membrane" itself — near-transparent, a faint bright rim
     from the highlight side, faint dark rim from the shadow side, NOT a
     flat rainbow fill (that's .yh-bubble__iridescence's job, layered on
     top at low opacity). */
  background: radial-gradient(circle at 42% 40%,
    rgba(255, 255, 255, 0.06) 0%,
    rgba(255, 255, 255, 0.04) 55%,
    rgba(255, 255, 255, 0.15) 74%,
    rgba(255, 255, 255, 0.42) 88%,
    rgba(255, 255, 255, 0.13) 100%);
  box-shadow:
    inset 0 0 0 2px rgba(255, 255, 255, 0.48),
    inset 0 8px 14px -8px rgba(255, 255, 255, 0.65),
    inset 0 -8px 14px -8px rgba(40, 30, 20, 0.32);
}

/* only bubble elements opt back into pointer-events (spec п.12) — the
   layer and every purely-decorative child stay none. */
.yh-bubble__iridescence,
.yh-bubble__reflection,
.yh-bubble__specular,
.yh-bubble__droplet {
  pointer-events: none;
}

.yh-bubble__iridescence {
  position: absolute;
  inset: -2%;
  /* background + --rim-angle set per-instance in JS (random colour
     order + rotation — see zone-00-bubbles.js) */
  -webkit-mask: radial-gradient(circle at 50% 50%, transparent 52%, rgba(0, 0, 0, 0.5) 70%, #000 85%, #000 100%);
  mask: radial-gradient(circle at 50% 50%, transparent 52%, rgba(0, 0, 0, 0.5) 70%, #000 85%, #000 100%);
  opacity: var(--iri-opacity, 0.5);
}

.yh-bubble__reflection {
  position: absolute;
  width: 62%;
  height: 40%;
  background: radial-gradient(ellipse at 50% 50%, rgba(255, 255, 255, 0.6) 0%, rgba(255, 255, 255, 0) 72%);
  transform: rotate(var(--refl-angle, -20deg));
}

.yh-bubble__specular {
  position: absolute;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.95) 0%, rgba(255, 255, 255, 0.35) 45%, rgba(255, 255, 255, 0) 75%);
}

.yh-bubble__specular--1 { width: 15%; height: 15%; }
.yh-bubble__specular--2 { width: 8%; height: 8%; }

.yh-bubble__droplet {
  position: absolute;
  left: 50%;
  top: 50%;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.9) 0%, rgba(190, 224, 255, 0.45) 60%, rgba(190, 224, 255, 0) 100%);
  transform: translate(-50%, -50%);
}

.yh-bubble--popped { pointer-events: none; }

/* depth (spec п.7): background reads slightly flatter/duller,
   foreground a touch punchier — both subtle, tuned via a filter rather
   than separate size/opacity constants sprinkled across JS. */
.yh-bubble--background .yh-bubble__body { filter: saturate(0.85) brightness(0.97); }
.yh-bubble--foreground .yh-bubble__body { filter: saturate(1.12) brightness(1.02); }

@media (max-width: 767px) {
  /* larger invisible tap area than the visible membrane (spec п.14),
     without the enlarged area ever fighting a neighbouring control —
     it's a per-bubble ::before, not a layout change, and still sits
     below --yh-z-local. */
  .yh-bubble__body::before {
    content: '';
    position: absolute;
    inset: -10px;
    border-radius: 50%;
  }
}

@media (prefers-reduced-motion: reduce) {
  .yh-bubble { transition: none; }
}

/* ================= MATERIAL HOTSPOTS (Zone 02/03, shared layerClass —
   see core/zone-hotspot-scene.js). The "+" is already baked into BASE
   here (unlike Zone 01), so — unlike .yh-object-hotspot__dot — this dot
   is fully invisible at rest: no new visible marker drawn over the
   image, only a transparent hit-area plus a very soft hover/focus glow
   "underlining" the existing mark. ================= */

.yh-material-hotspot__item {
  position: absolute;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
  border: none;
  background: transparent;
  pointer-events: auto;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.yh-material-hotspot__dot {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: transparent;
  transition: background 200ms ease, box-shadow 200ms ease;
}

.yh-material-hotspot__item:hover .yh-material-hotspot__dot,
.yh-material-hotspot__item:focus-visible .yh-material-hotspot__dot {
  /* soft radial glow, not a hard-edged second "+" */
  background: radial-gradient(circle, rgba(255, 244, 214, 0.55) 0%, rgba(255, 244, 214, 0) 72%);
}

.yh-material-hotspot__item:focus-visible { outline: none; }

.yh-material-hotspot__item:focus-visible .yh-material-hotspot__dot {
  box-shadow: 0 0 0 2px rgba(47, 125, 238, 0.65);
}

.yh-material-hotspot__label {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 4px);
  transform: translateX(-50%);
  white-space: nowrap;
  background: rgba(28, 26, 23, 0.88);
  color: #fff;
  font-size: 0.66rem;
  font-weight: 700;
  padding: 4px 9px;
  border-radius: 6px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 150ms ease;
}

.yh-material-hotspot__item:hover .yh-material-hotspot__label,
.yh-material-hotspot__item:focus-visible .yh-material-hotspot__label {
  opacity: 1;
}

.yh-material-hotspot__caption {
  position: absolute;
  transform: translate(-50%, 0);
  background: rgba(28, 26, 23, 0.92);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 600;
  line-height: 1.4;
  text-align: center;
  padding: 6px 10px;
  border-radius: 8px;
  max-width: 190px;
  pointer-events: none;
  white-space: pre-line;
}

.yh-material-hotspot__caption[hidden] { display: none; }

.yh-app--debug .yh-material-hotspot__item {
  outline: 1px dashed rgba(0, 200, 120, 0.85);
  outline-offset: -1px;
}

.yh-app--debug .yh-material-hotspot__dot {
  background: rgba(0, 200, 120, 0.35);
}

.yh-app--debug .yh-material-hotspot__label {
  opacity: 1 !important;
}

/* ================= WORKSHOP CALLOUTS (Zone 02/03 — see
   core/zone-hotspot-scene.js buildCallouts). Purely decorative,
   pointer-events:none throughout: the real click target stays the
   existing invisible .yh-material-hotspot__item dot, exactly where it
   already was — this overlay just labels it from a nearby, less
   crowded anchor point, connected by a thin line. ================= */

.yh-workshop-callouts {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 12;
}

.yh-workshop-callouts__lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.yh-workshop-callouts__line {
  stroke: var(--yh-callout-color, var(--yh-accent));
  stroke-width: 2;
  opacity: 0.8;
}

.yh-workshop-callouts__badge {
  position: absolute;
  transform: translate(-50%, -50%);
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--yh-callout-color, var(--yh-accent));
  color: #fff;
  font-size: 0.62rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.35);
}

.yh-workshop-callouts__card {
  position: absolute;
  transform: translate(-50%, -100%);
  margin-top: -12px;
  background: rgba(255, 255, 255, 0.96);
  color: #1c1a17;
  border-radius: 10px;
  padding: 8px 13px 9px 14px;
  max-width: 168px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.25);
}

.yh-workshop-callouts__card::before {
  content: '+';
  position: absolute;
  left: -8px;
  bottom: -8px;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: var(--yh-callout-color, var(--yh-accent));
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 18px;
  text-align: center;
  box-shadow: 0 2px 5px rgba(0, 0, 0, 0.3);
}

.yh-workshop-callouts__title {
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  line-height: 1.25;
}

.yh-workshop-callouts__desc {
  margin-top: 3px;
  font-size: 0.56rem;
  line-height: 1.35;
  color: #4a4640;
}

/* mobile: title only, no description line — explicit customer direction
   ("на мобильной версии только названия, без описаний") */
@media (max-width: 767px) {
  .yh-workshop-callouts__desc { display: none; }
  .yh-workshop-callouts__card { max-width: 128px; padding: 6px 10px; }
  .yh-workshop-callouts__badge { width: 22px; height: 22px; font-size: 0.56rem; }
}

/* ================= ZONE 03 ANNOTATIONS (see
   core/zone-03-workshop.js) — "architectural exhibition" look per
   customer spec: saturated blue accent, white label, dark text, small
   radius, no shadow/blur/glassmorphism. Distinct from, and independent
   of, Zone 02's .yh-workshop-callouts above. ================= */

.yh-workshop-annotation-layer {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 12;
}

.yh-workshop-annotation__lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.yh-workshop-annotation__line {
  /* thinner per customer request ("уменьшить выноски") — was 1.5/2.25 */
  stroke: #2f6fed;
  stroke-width: 1;
  opacity: 0.7;
  transition: opacity 150ms ease, stroke-width 150ms ease;
}

/* Zone 03's AI-tools link (core/zone-03-workshop.js) — a real, ALWAYS
   visible "+" sitting directly on the baked-in card, right of its title
   (customer, 2026-09-10). Same visual language as the old annotation-
   card "+" (.yh-workshop-annotation__plus) — blue circle, white "+",
   scale/darken on hover — just built on the generic hotspot-layer
   classes since this is a lone hotspot, not a full annotation card. */
.yh-ai-link-hotspot__item {
  position: absolute;
  width: 34px;
  height: 34px;
  transform: translate(-50%, -50%);
  border: none;
  background: transparent;
  pointer-events: auto;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.yh-ai-link-hotspot__dot {
  width: 100%;
  height: 100%;
  border-radius: 50%;
  background: #2f6fed;
  border: 2px solid #fff;
  color: #fff;
  font-size: 1.1rem;
  font-weight: 700;
  line-height: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 5px rgba(20, 40, 90, 0.35);
  transition: transform 150ms ease, background 150ms ease;
}

.yh-ai-link-hotspot__dot::after { content: '+'; }

.yh-ai-link-hotspot__item:hover .yh-ai-link-hotspot__dot,
.yh-ai-link-hotspot__item:focus-visible .yh-ai-link-hotspot__dot {
  transform: scale(1.12);
  background: #1c56d6;
}

.yh-ai-link-hotspot__item:focus-visible { outline: none; }
.yh-ai-link-hotspot__item:focus-visible .yh-ai-link-hotspot__dot {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

.yh-workshop-annotation__line.is-emphasized {
  opacity: 1;
  stroke-width: 1.6;
}

/* the real click target, sitting exactly on the physical object */
.yh-workshop-annotation__plus {
  position: absolute;
  transform: translate(-50%, -50%);
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: #2f6fed;
  border: 2px solid #fff;
  color: #fff;
  font-size: 1.1rem;
  line-height: 1;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  pointer-events: auto;
  cursor: pointer;
  box-shadow: 0 1px 5px rgba(20, 40, 90, 0.35);
  transition: transform 150ms ease, background 150ms ease;
}

.yh-workshop-annotation__plus:hover,
.yh-workshop-annotation__plus:focus-visible {
  transform: translate(-50%, -50%) scale(1.12);
  background: #1c56d6;
}

.yh-workshop-annotation__plus:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }

/* the white annotation card — badge attached/overlapping its left edge,
   per the reference ("[ 01 ] [ TITLE / short description ]"). Anchored by
   left/top = its own left-center edge (translateY only, no translateX),
   so the badge sits exactly on that edge via a negative left offset. */
.yh-workshop-annotation__card {
  position: absolute;
  transform: translateY(-50%);
  /* customer follow-up: "огромные карточки... скрывает людей и фон,
     предлагаю их уменьшить... сделать подложку карточки полупрозрачной,
     чтобы было видно фон" — the earlier 0.9-opacity/6px-blur pass (from
     "сделаем полупрозрачными") still read as near-opaque; this is a
     real see-through card now, lighter blur so the scene behind it stays
     legible rather than a frosted wash. */
  background: rgba(255, 255, 255, 0.55);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  color: #16140f;
  border-radius: 11px;
  /* sized back down — the last two size-up passes (1.7x, then +15%) made
     these "огромные" per the same follow-up; back to a compact card. */
  padding: 10px 16px 10px 40px;
  max-width: 230px;
  box-shadow: 0 2px 8px rgba(20, 30, 60, 0.18);
}

/* no numbered badge (e.g. Zone 06 role cards — a person isn't part of a
   numbered sequence) — same card otherwise, just symmetric padding since
   there's no badge notch on the left to clear. */
.yh-workshop-annotation__card--no-badge {
  padding-left: 23px;
}

.yh-workshop-annotation__badge {
  position: absolute;
  left: -21px;
  top: 50%;
  transform: translateY(-50%);
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: #2f6fed;
  border: 2px solid #fff;
  color: #fff;
  font-size: 1rem;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 1px 4px rgba(20, 40, 90, 0.3);
}

/* customer follow-up ("огромный шрифт... предлагаю уменьшить") — title/
   desc sized back down along with the card itself above. */
.yh-workshop-annotation__title {
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  line-height: 1.2;
  /* customer: "исследовательские не вмещается на карточку" — real bug,
     not a positioning illusion this time: measured scrollWidth (203px)
     vs clientWidth (174px) on that exact card. A single long word
     ("ИССЛЕДОВАТЕЛЬСКИЕ", 18 chars) doesn't wrap by default — browsers
     don't break an unhyphenated word unless told to — so it overflowed
     the card's own right edge instead. Any title long enough to hit this
     on a future card gets the same fix. */
  overflow-wrap: break-word;
}

.yh-workshop-annotation__desc {
  margin-top: 6px;
  font-size: 0.88rem;
  line-height: 1.32;
  color: rgba(22, 20, 15, 0.72);
  overflow-wrap: break-word;
}

/* mobile: cards stay ALWAYS VISIBLE here too — but TITLE ONLY, no
   description (customer follow-up: "в мобильной версии нужно чтобы
   оставались только названия, без описания" — reverses the earlier
   "description too" direction for mobile specifically). Compact
   typography/padding (still 1.7x'd from their own prior compact values)
   + a larger "+" hit area (≥44px touch target). */
@media (max-width: 767px) {
  /* bigger jump than desktop's — "нужно увеличить карточки, на телефоне
     особенно" (was 8px 15px 8px 37px / 58vw / 8px). */
  .yh-workshop-annotation__card {
    /* "увеличим карточки, шрифт... на телефоне главное чтобы было видно
       текст" — bumped again, and narrowed (72vw -> 58vw) so the card
       grows taller/vertical instead of wider, per "вытянутыми
       вертикально". */
    padding: 14px 20px 14px 50px;
    max-width: 58vw;
    border-radius: 10px;
  }
  .yh-workshop-annotation__card--no-badge {
    padding-left: 20px;
  }
  .yh-workshop-annotation__badge {
    width: 42px;
    height: 42px;
    left: -21px;
    font-size: 1.05rem;
  }
  .yh-workshop-annotation__title { font-size: 1.18rem; }
  /* default (Zone 02): description STAYS on mobile, just sized down —
     explicit, distinct instruction from Zone 03's mobile ("Do NOT hide
     the description simply because it is mobile"). */
  .yh-workshop-annotation__desc { font-size: 1.02rem; margin-top: 6px; }
  .yh-workshop-annotation__plus {
    /* the visible circle itself IS the ≥44px touch target here — a
       padding-based hit-area trick would draw the white ring/border at
       the padded (larger) box while the blue fill stayed small, leaving
       a visible gap between them, so the honest fix is just sizing the
       whole circle to the real minimum. */
    width: 44px;
    height: 44px;
    font-size: 1.05rem;
  }

  /* Zone 03 opts into titles-only on mobile (its own explicit, separate
     instruction) via this modifier class — see core/zone-03-workshop.js */
  .yh-workshop-annotation-layer--titles-only .yh-workshop-annotation__desc {
    display: none;
  }
  .yh-workshop-annotation-layer--titles-only .yh-workshop-annotation__card {
    max-width: 50vw;
  }

  /* ZONE 06 — MOBILE ONLY (customer spec): "уменьшить ширину карточек
     примерно на 10-15%" — scoped to this zone's own modifier class (see
     core/zone-06-team.js) so Zone 02/03's mobile cards, which share this
     same component, are untouched. 58vw base * ~0.86.
     Narrowed further (50vw -> 34vw) in the mobile re-layout pass: the
     customer's spec keeps two side-by-side columns (left: урбанист/
     архитектор; right: визуализатор/проектировщик/дизайнер) — two 50vw
     columns leave literally 0vw for any gap between them (50+50=100vw),
     so they always touched/overlapped regardless of x position. Also,
     the gap between a fixed-percent-of-image anchor and a vw-sized card
     doesn't shrink/grow at the same rate as the viewport width changes,
     so a gap that's fine at 375px can quietly close by 430px (found via
     Playwright across 360-430px, not just one width) — 34vw plus the
     extra x separation below (see ZONE_06_PEOPLE) keeps a real margin at
     every tested width, not just one. */
  .yh-workshop-annotation-layer--zone06-mobile .yh-workshop-annotation__card {
    max-width: 34vw;
  }
}

/* Unconditional twin of the @media block above, keyed to the JS-driven
   .yh-workshop-annotation-layer--mobile class (core/spatial-
   annotations.js) instead of window width — see that file's comment for
   why. Whichever of the two actually matches wins; when they agree
   (the common case) this is simply redundant. */
.yh-workshop-annotation-layer--mobile .yh-workshop-annotation__card {
  padding: 14px 20px 14px 50px;
  max-width: 58vw;
  border-radius: 10px;
}
.yh-workshop-annotation-layer--mobile .yh-workshop-annotation__card--no-badge {
  padding-left: 20px;
}
.yh-workshop-annotation-layer--mobile .yh-workshop-annotation__badge {
  width: 42px;
  height: 42px;
  left: -21px;
  font-size: 1.05rem;
}
.yh-workshop-annotation-layer--mobile .yh-workshop-annotation__title { font-size: 1.18rem; }
.yh-workshop-annotation-layer--mobile .yh-workshop-annotation__desc { font-size: 1.02rem; margin-top: 6px; }
.yh-workshop-annotation-layer--mobile .yh-workshop-annotation__plus {
  width: 44px;
  height: 44px;
  font-size: 1.05rem;
}
.yh-workshop-annotation-layer--mobile.yh-workshop-annotation-layer--titles-only .yh-workshop-annotation__desc {
  display: none;
}
.yh-workshop-annotation-layer--mobile.yh-workshop-annotation-layer--titles-only .yh-workshop-annotation__card {
  max-width: 50vw;
}
.yh-workshop-annotation-layer--mobile.yh-workshop-annotation-layer--zone06-mobile .yh-workshop-annotation__card {
  max-width: 34vw;
}

.yh-app--debug .yh-workshop-annotation__plus {
  outline: 1px dashed rgba(255, 40, 40, 0.9);
  outline-offset: 2px;
}

.yh-app--debug .yh-workshop-annotation__card {
  outline: 1px dashed rgba(0, 120, 255, 0.9);
}

/* invisible outside ?debug=1 — marks the physical object/target position
   for hotspots that have no real "+" yet (see core/zone-03-workshop.js) */
.yh-workshop-annotation__target-debug { display: none; }

.yh-app--debug .yh-workshop-annotation__target-debug {
  display: block;
  position: absolute;
  transform: translate(-50%, -50%);
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: rgba(255, 40, 40, 0.85);
  outline: 1px dashed rgba(255, 40, 40, 0.9);
  outline-offset: 3px;
}

/* ================= ZONE 04 ARCHIVE DRAWERS ================= */

.yh-archive-hotspot__item {
  position: absolute;
  width: 6%;
  height: 6.5%;
  transform: translate(-50%, -50%);
  border: none;
  background: transparent;
  pointer-events: auto;
  cursor: pointer;
}

.yh-archive-hotspot__dot {
  display: none; /* the drawer plate itself is the visible affordance — no extra dot needed */
}

.yh-archive-hotspot__item:focus-visible {
  outline: 2px solid rgba(47, 125, 238, 0.85);
  outline-offset: 2px;
}

/* desktop hover/focus glow — code-only, no image asset (see
   core/zone-04-archive.js header: the simulated physical pull-out never
   read as realistic, simplified to a plain glow). Lives inside
   .yh-scene-stage (a sibling of .yh-scene-image, NOT the viewport-fixed
   overlayRoot the lightbox uses) so it inherits the same pan/zoom
   transform as the BASE and never screen-centers. Sized to that drawer's
   own row (config peekBoxCoords/peekBoxSize); the radial-gradient itself
   fades to transparent before the box edge, so the glow reads as soft/
   feathered rather than a hard-edged rectangle.
   mix-blend-mode:soft-light — the fix for "looks like a separate object
   on top": blends into the ACTUAL drawer-face pixels underneath instead of
   painting an opaque-ish shape over them, so the face itself reads as lit
   up rather than something floating in front of it. soft-light specifically
   (not screen, which was tried first) — screen pushes already-light pixels
   (the cream number plate, its text) all the way to white, erasing the
   text; soft-light has a much gentler curve on light pixels while still
   visibly brightening the darker wood around them. */
.yh-archive-peek-glow {
  position: absolute;
  z-index: 4;
  background: radial-gradient(ellipse at center, rgba(255, 214, 130, 0.9) 0%, rgba(255, 200, 110, 0.5) 45%, rgba(255, 200, 110, 0) 78%);
  mix-blend-mode: soft-light;
  opacity: 0;
  transition: opacity 220ms ease-out;
  pointer-events: none;
}

.yh-archive-peek-glow.is-active { opacity: 1; }

/* mobile PERMANENT badge glow (core/zone-04-archive.js) — customer:
   "хочется чтобы свечение было всегда, натуральное полупрозрачное".
   Own dedicated class (not a reused/shared element like the desktop peek
   above, since all 5 need to glow at once): same soft-light warm tint
   language as the desktop hover glow, just always on rather than
   opacity-transitioned, and with a gentle breathing pulse so it still
   reads as "alive" rather than a flat static sticker. */
.yh-archive-badge-glow {
  position: absolute;
  z-index: 4;
  pointer-events: none;
  border-radius: 50%;
  background: radial-gradient(ellipse at center, rgba(255, 214, 130, 0.55) 0%, rgba(255, 200, 110, 0.32) 50%, rgba(255, 200, 110, 0) 80%);
  mix-blend-mode: soft-light;
  animation: yh-archive-badge-glow-pulse 3400ms ease-in-out infinite;
}

@keyframes yh-archive-badge-glow-pulse {
  0%, 100% { opacity: 0.65; }
  50% { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .yh-archive-badge-glow { animation: none; opacity: 0.85; }
}

/* cursor-following "ОТКРОЙ" hint — lives in the viewport-fixed
   .yh-overlay-root (not sceneStage), positioned via left/top in px set
   directly from clientX/clientY, so position:fixed resolves against the
   real viewport untouched by the scene's own pan/zoom transform. */
.yh-archive-hover-hint {
  position: fixed;
  z-index: 90;
  transform: translate(0, -50%);
  background: rgba(28, 26, 23, 0.92);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 5px 11px;
  border-radius: 999px;
  pointer-events: none;
  white-space: nowrap;
}

.yh-archive-hover-hint[hidden] { display: none; }

/* .yh-overlay-root (mount.js) — inert wrapper, empty until a zone behavior
   uses it. Fixed to the real viewport (sibling of the transformed scene,
   see mount.js comment), so a lightbox anchored here never inherits the
   scene's pan/zoom. pointer-events:none at rest so an empty overlay layer
   never blocks clicks meant for the page underneath. */
.yh-overlay-root {
  position: fixed;
  inset: 0;
  z-index: var(--yh-z-overlay); /* above header/bottom-nav too — a true lightbox covers everything */
  pointer-events: none;
}

.yh-archive-lightbox {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: auto;
}

.yh-archive-lightbox[hidden] { display: none; }

.yh-archive-lightbox__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(10, 9, 8, 0);
  backdrop-filter: blur(0px);
  -webkit-backdrop-filter: blur(0px);
  transition: background 260ms ease-out, backdrop-filter 260ms ease-out, -webkit-backdrop-filter 260ms ease-out;
}

.yh-archive-lightbox.is-open .yh-archive-lightbox__backdrop {
  background: rgba(10, 9, 8, 0.62);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}

.yh-archive-lightbox__content {
  position: relative;
  z-index: 1;
  max-width: min(88vw, 1100px);
  max-height: 82vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  opacity: 0;
  transform: scale(0.96);
  transition: opacity 220ms ease-out, transform 220ms ease-out;
}

.yh-archive-lightbox.is-open .yh-archive-lightbox__content {
  opacity: 1;
  transform: scale(1);
}

.yh-archive-lightbox__image {
  max-width: 100%;
  max-height: 62vh;
  border-radius: 10px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}

.yh-archive-lightbox__image[hidden] { display: none; }

/* compact architectural signage, not a big white modal card */
.yh-archive-lightbox__panel {
  margin-top: 14px;
  min-width: 220px;
  max-width: 320px;
  background: rgba(250, 247, 240, 0.97);
  color: #1c1a17;
  border-radius: 8px;
  padding: 12px 16px 14px;
  text-align: center;
}

.yh-archive-lightbox__title {
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  margin-bottom: 6px;
}

.yh-archive-lightbox__body { font-size: 0.76rem; line-height: 1.4; }

.yh-archive-lightbox__link {
  color: #1f8f5f;
  font-weight: 700;
  text-decoration: none;
}

.yh-archive-lightbox__link:hover,
.yh-archive-lightbox__link:focus-visible { text-decoration: underline; }

.yh-archive-lightbox__empty {
  color: rgba(28, 26, 23, 0.65);
  font-style: italic;
}

.yh-archive-lightbox__close {
  position: absolute;
  top: -18px;
  right: -18px;
  width: 36px;
  height: 36px;
  border: none;
  border-radius: 50%;
  background: rgba(250, 247, 240, 0.97);
  font-size: 1.3rem;
  line-height: 1;
  color: #1c1a17;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);
}

.yh-archive-lightbox__close:hover,
.yh-archive-lightbox__close:focus-visible { color: #000; }

/* ================= ZONE 05 GALLERY / LIBRARY ================= */

/* static placed objects — always visible, never swapped. object-fit:
   contain so the source PNG's own aspect ratio is preserved inside its
   percent box regardless of viewport, never stretched. */
.yh-gallery-shelf-image,
.yh-gallery-fabric-image {
  position: absolute;
  object-fit: contain;
  pointer-events: none;
}

/* customer request: shelf sits in front of the fabric banner where their
   boxes cross (was relying on DOM order alone — both shared z-index:3 —
   which happened to put the banner on top; now explicit so it doesn't
   flip back if the DOM order ever changes). */
.yh-gallery-fabric-image { z-index: 3; }

/* ZONE 05 shelf rotation spec: "поворот... не должен быть мгновенной
   заменой кадров" — two stacked <img> layers (core/zone-05-gallery.js
   shelfImgA/B) crossfade instead of one <img> swapping its own src.
   --front is the resting/visible frame, --back holds whichever frame is
   about to come forward; setShelfFrame() just swaps which layer has
   which modifier, and this transition does the rest.
   Follow-up correction: "убери тряску... шкаф должен всё время
   оставаться точно на одном месте и в одном размере" — the earlier pass
   added a light settle scale/translateX on top of the fade for a sense
   of motion; on THIS asset (a static front-on render, not a real turntable
   capture) that read as jitter/shake rather than continuity, and directly
   contradicts "same place, same size" — removed, pure opacity crossfade
   only. */
.yh-gallery-shelf-image {
  transition: opacity 400ms ease;
}
.yh-gallery-shelf-image--front {
  opacity: 1;
  z-index: 5;
}
.yh-gallery-shelf-image--back {
  opacity: 0;
  z-index: 4;
}

/* delicate shelf affordance ("небольшим свечением") — a soft glow behind
   the shelf face, shown on desktop cursor-proximity (140-180px, see
   zone-05-gallery.js) and pulsed briefly on every tap/click release
   trigger on mobile. Same soft-light blend approach as Zone 04's drawer
   glow so it reads as the shelf itself lighting up, not an overlay shape
   pasted on top. */
.yh-gallery-shelf-glow {
  position: absolute;
  pointer-events: none;
  z-index: 2;
  background: radial-gradient(ellipse at center, rgba(255, 214, 130, 0.85) 0%, rgba(255, 200, 110, 0.45) 45%, rgba(255, 200, 110, 0) 78%);
  mix-blend-mode: soft-light;
  opacity: 0;
  transition: opacity 260ms ease;
}
.yh-gallery-shelf-glow.is-active { opacity: 1; }

/* shelf rests on the floor -> anchor from the BOTTOM of its box (any
   leftover space from object-fit:contain collapses to the top, which is
   correct — floors don't float). */
.yh-gallery-shelf-image { object-position: center bottom; }

/* fabric hangs from the ceiling -> anchor from the TOP of its box instead.
   With object-position:center bottom (shelf's rule) any leftover vertical
   space collapses to the TOP, which is exactly backwards for something
   hung by its top edge — that leftover gap was the real cause of "floats
   in mid-air with no visible attachment" (real bug, not just an offset to
   nudge: the box was tall enough that ~23% of it sat empty above the
   rendered banner). Anchoring top instead makes the banner start exactly
   at the box's own top edge, at ceiling height, with no arbitrary gap.
   A slight rotation toward the shelf reads as natural sway/perspective
   rather than a perfectly flat rectangle. */
.yh-gallery-fabric-image {
  object-position: center top;
  transform-origin: 50% 0;
  /* customer: "нижний край имеет очень большой угол наклона, создается
     ощущение что полотно кривое" — the real bug was perspective-origin
     defaulting to the element's own CENTER while the rotation axis
     (transform-origin) sits at its TOP: that mismatch makes a perfectly
     flat plane project as a non-linear (bent-looking) trapezoid instead
     of a straight lean, worst at the edge furthest from the axis (the
     bottom). Aligning perspective-origin to the same top point keeps the
     lean linear/straight along the whole height. */
  perspective-origin: 50% 0;
  /* "колыхался словно легкая ткань под ветром" — code-only, no new asset:
     a slow keyframe loop varies the same rotateY (+ a touch of skew/scaleX)
     the static rule below used to fix in place, so the banner keeps its
     3D lean toward the shelf but never holds perfectly still. */
  animation: yh-fabric-wave 6s ease-in-out infinite;
}

@keyframes yh-fabric-wave {
  0%   { transform: perspective(900px) rotateY(18deg) skewX(0deg) scaleX(1); }
  25%  { transform: perspective(900px) rotateY(19.5deg) skewX(0.8deg) scaleX(0.993); }
  50%  { transform: perspective(900px) rotateY(16.5deg) skewX(-0.6deg) scaleX(1.005); }
  75%  { transform: perspective(900px) rotateY(18.8deg) skewX(0.5deg) scaleX(0.996); }
  100% { transform: perspective(900px) rotateY(18deg) skewX(0deg) scaleX(1); }
}

@media (prefers-reduced-motion: reduce) {
  .yh-gallery-fabric-image { animation: none; transform: perspective(900px) rotateY(18deg); }
}

/* "СТАНДАРТ ДЕЯТЕЛЬНОСТИ" book CTA (core/zone-05-gallery.js bookLink) —
   this component had inline left/top/width/height set from JS but NO
   CSS at all, so position:absolute (required for percent left/top to do
   anything), z-index, and the label's own look/visibility were all
   missing — the real reason the plaque never appeared. z-index sits
   above .yh-gallery-shelf-image (4) so it's never covered by the shelf
   raster itself; pointer-events stay off at rest and only turn on via
   .is-active (set in JS exactly when the book is front-facing), so a
   click while the book is mid-rotation still falls through to the
   rotate button beneath instead of hitting a dead/wrong target.
   customer (2026-09-15): "зона 05 периодически вылетает" — this used to
   be sized (via inline width/height) to the WHOLE book rectangle, which
   sits centered inside the rotate button's own much larger hit area, so
   a natural center-of-shelf tap kept winning this link over rotating.
   No longer sized from JS at all — shrink-wraps to its own label pill
   content instead, centered on the book via this transform, leaving the
   rest of the book's area to the rotate button beneath (see
   core/zone-05-gallery.js layoutBookLink). */
.yh-gallery-book-link {
  position: absolute;
  transform: translate(-50%, -50%);
  z-index: 6;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}

.yh-gallery-book-link.is-active { pointer-events: auto; }

.yh-gallery-book-link__label {
  background: rgba(28, 26, 23, 0.88);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  white-space: nowrap;
  padding: 8px 14px;
  border-radius: 999px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 150ms ease;
}

/* desktop: hover/focus reveal (no hover concept on touch) */
@media (min-width: 768px) {
  .yh-gallery-book-link.is-active:hover .yh-gallery-book-link__label,
  .yh-gallery-book-link.is-active:focus-visible .yh-gallery-book-link__label {
    opacity: 1;
  }
}

/* mobile: permanently visible while active — a first-time visitor has no
   hover to discover this is tappable otherwise. */
@media (max-width: 767px) {
  .yh-gallery-book-link.is-active .yh-gallery-book-link__label {
    opacity: 1;
  }
}

/* shelf + fabric hotspots — transparent click targets, no visible dot
   (the shelf/banner graphics are themselves the affordance) */
.yh-gallery-hotspot__item {
  position: absolute;
  transform: translate(-50%, -50%);
  border: none;
  background: transparent;
  pointer-events: auto;
  cursor: pointer;
}

.yh-gallery-hotspot__dot { display: none; }

.yh-gallery-hotspot__item:hover,
.yh-gallery-hotspot__item:focus-visible {
  background: rgba(255, 255, 255, 0.08);
}

.yh-gallery-hotspot__item:focus-visible {
  outline: 2px solid rgba(47, 125, 238, 0.85);
  outline-offset: 2px;
}

.yh-gallery-hotspot__label {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  white-space: nowrap;
  background: rgba(28, 26, 23, 0.88);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  padding: 5px 12px;
  border-radius: 999px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 150ms ease;
}

.yh-gallery-hotspot__item:hover .yh-gallery-hotspot__label,
.yh-gallery-hotspot__item:focus-visible .yh-gallery-hotspot__label {
  opacity: 1;
}

/* mobile: no real hover — permanently visible like the book's own CTA
   (core/zone-05-gallery.js layoutFabricLabel also counter-scales it to
   match that button's true on-screen size, see that function's comment).
   layerClass 'yh-gallery-hotspot' is only ever used for this one fabric
   hotspot, so this is safely scoped. */
@media (max-width: 767px) {
  .yh-gallery-hotspot__label { opacity: 1; }
}

.yh-gallery-hotspot__caption {
  position: absolute;
  transform: translate(-50%, 0);
  background: rgba(28, 26, 23, 0.92);
  color: #fff;
  font-size: 0.7rem;
  font-weight: 600;
  line-height: 1.4;
  text-align: center;
  padding: 6px 10px;
  border-radius: 8px;
  max-width: 180px;
  pointer-events: none;
  white-space: pre-line;
}

.yh-gallery-hotspot__caption[hidden] { display: none; }

/* shelf rotate button — dedicated invisible click target over the shelf's
   real footprint (same size/position convention as the generic hotspot
   layer's items, transform:translate(-50%,-50%) to center on its own
   x/y). No visible dot: the shelf graphic itself is the affordance, same
   spirit as .yh-gallery-hotspot__item. */
.yh-gallery-shelf-rotate-btn {
  position: absolute;
  transform: translate(-50%, -50%);
  border: none;
  background: transparent;
  padding: 0;
  pointer-events: auto;
  cursor: pointer;
  z-index: 4;
}

/* cursor-following "ПОКРУТИ ШКАФ" tooltip — viewport-fixed (appended to
   overlayRoot, tracks raw clientX/clientY via pointermove), positioned
   just above the pointer. Desktop-only (never shown on mobile — no
   listeners attach there, see zone-05-gallery.js). */
.yh-gallery-shelf-tooltip {
  position: fixed;
  transform: translate(-50%, -130%);
  background: rgba(28, 26, 23, 0.88);
  color: #fff;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.03em;
  padding: 5px 12px;
  border-radius: 999px;
  pointer-events: none;
  white-space: nowrap;
  z-index: var(--yh-z-overlay);
}

.yh-gallery-shelf-tooltip[hidden] { display: none; }

/* flying pages — real <button>s so the existing gesture-controller's
   tap-vs-drag arbitration (a scene drag suppresses the synthetic click
   that follows it) applies automatically; no bespoke drag-detection here. */
.yh-gallery-page {
  position: absolute;
  border: none;
  background: transparent;
  padding: 0;
  cursor: pointer;
  width: 4.6%;
  transform-style: preserve-3d;
  will-change: transform, opacity;
  pointer-events: auto;
}

.yh-gallery-page--depth1 { width: 3.7%; opacity: 0.85; }
.yh-gallery-page--depth2 { width: 2.9%; filter: blur(0.4px); opacity: 1; }
/* depth0 (nearest/largest) is the most transparent of the three, depth2
   (farthest/smallest) the most opaque — an inverted-from-usual depth cue
   requested explicitly over the default "near=sharp,far=hazy" convention:
   the big close pages would otherwise dominate/clutter the scene, so they
   recede via transparency instead of via size alone. */
.yh-gallery-page--depth0 { opacity: 0.7; }

.yh-gallery-page__img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 2px;
  box-shadow: 0 10px 26px rgba(0, 0, 0, 0.35);
  pointer-events: none;
}

.yh-gallery-page:focus-visible {
  outline: 2px solid rgba(47, 125, 238, 0.85);
  outline-offset: 3px;
}

.yh-gallery-page.is-hovered {
  filter: none;
  opacity: 1;
}

.yh-gallery-page.is-hovered .yh-gallery-page__img {
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.45);
}

/* reader — same contract as .yh-archive-lightbox (Zone 04): darken+blur
   backdrop, image at its own aspect ratio, close ×/Escape/backdrop */
.yh-gallery-reader {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: auto;
}

.yh-gallery-reader[hidden] { display: none; }

.yh-gallery-reader__backdrop {
  position: absolute;
  inset: 0;
  background: rgba(10, 9, 8, 0);
  backdrop-filter: blur(0px);
  -webkit-backdrop-filter: blur(0px);
  transition: background 260ms ease-out, backdrop-filter 260ms ease-out, -webkit-backdrop-filter 260ms ease-out;
}

.yh-gallery-reader.is-open .yh-gallery-reader__backdrop {
  background: rgba(10, 9, 8, 0.66);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}

.yh-gallery-reader__content {
  position: relative;
  z-index: 1;
  max-width: min(70vw, 720px);
  max-height: 86vh;
  opacity: 0;
  transform: scale(0.96);
  transition: opacity 220ms ease-out, transform 220ms ease-out;
}

.yh-gallery-reader.is-open .yh-gallery-reader__content {
  opacity: 1;
  transform: scale(1);
}

.yh-gallery-reader__image {
  display: block;
  max-width: 100%;
  max-height: 86vh;
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: 4px;
  box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5);
}

.yh-gallery-reader__close {
  position: absolute;
  top: -18px;
  right: -18px;
  width: 36px;
  height: 36px;
  border: none;
  border-radius: 50%;
  background: rgba(250, 247, 240, 0.97);
  font-size: 1.3rem;
  line-height: 1;
  color: #1c1a17;
  cursor: pointer;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.3);
  z-index: 2;
}

.yh-gallery-reader__close:hover,
.yh-gallery-reader__close:focus-visible { color: #000; }

/* ================= ZONE 06 TEAM / CHAIR ================= */

.yh-team-chair-image {
  position: absolute;
  object-fit: contain;
  object-position: center bottom;
  pointer-events: none;
  z-index: 3;
  transition: transform 260ms ease;
  transform-origin: 50% 100%;
}

/* "выдвигается" on hover/focus of the chair hotspot (see
   core/zone-06-team.js) — a code-only pulled-toward-viewer nudge, same
   spirit as Zone 04's code-only drawer glow (no new asset needed). */
.yh-team-chair-image.is-pulled {
  transform: translateY(4%) scale(1.06);
}

/* same generic dot/label/caption look as Zone 01's object hotspots —
   deliberately reused, not reinvented, per "минимальный интерфейс". */
.yh-team-hotspot__item {
  position: absolute;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
  border: none;
  background: transparent;
  pointer-events: auto;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.yh-team-hotspot__dot {
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.55);
  border: 1.5px solid rgba(28, 26, 23, 0.55);
  transition: transform 150ms ease, background 150ms ease;
}

.yh-team-hotspot__item:hover .yh-team-hotspot__dot,
.yh-team-hotspot__item:focus-visible .yh-team-hotspot__dot {
  transform: scale(1.6);
  background: #fff;
}

.yh-team-hotspot__item:focus-visible { outline: none; }

.yh-team-hotspot__item:focus-visible .yh-team-hotspot__dot {
  box-shadow: 0 0 0 3px rgba(47, 125, 238, 0.7);
}

.yh-team-hotspot__label {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 4px);
  transform: translateX(-50%);
  white-space: nowrap;
  background: rgba(28, 26, 23, 0.88);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 700;
  padding: 4px 9px;
  border-radius: 6px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 150ms ease;
}

.yh-team-hotspot__item:hover .yh-team-hotspot__label,
.yh-team-hotspot__item:focus-visible .yh-team-hotspot__label {
  opacity: 1;
}

/* the chair's hotspot box is tall (covers its whole footprint, not a small
   dot — see ZONE_06_CHAIR.hotspotDesktopSize), so the generic
   bottom:calc(100% + 4px) label rule above anchors "ПОДАТЬ ЗАЯВКУ" to the
   TOP of that tall box — far above the chair itself, near the people's
   heads. Override just for the chair: anchor it lower, over the chair's
   own backrest area, and size it up slightly per customer request. */
.yh-team-hotspot__item[data-hotspot-id="chair"] .yh-team-hotspot__label {
  left: 50%;
  top: 64%;
  bottom: auto;
  transform: translate(-50%, -50%);
  font-size: 0.8rem;
  padding: 7px 14px;
}

.yh-team-hotspot__caption {
  position: absolute;
  transform: translate(-50%, 0);
  background: rgba(28, 26, 23, 0.92);
  color: #fff;
  font-size: 0.68rem;
  font-weight: 600;
  line-height: 1.4;
  text-align: center;
  padding: 6px 10px;
  border-radius: 8px;
  max-width: 180px;
  pointer-events: none;
  white-space: pre-line;
}

.yh-team-hotspot__caption[hidden] { display: none; }

/* ================= ZONE 07 APPLY CTA ================= */

.yh-apply-hotspot__item {
  position: absolute;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
  border: none;
  background: transparent;
  pointer-events: auto;
  cursor: pointer;
}

.yh-apply-hotspot__item:hover,
.yh-apply-hotspot__item:focus-visible {
  background: rgba(255, 255, 255, 0.1);
  border-radius: 8px;
}

.yh-apply-hotspot__item:focus-visible {
  outline: 2px solid rgba(47, 125, 238, 0.85);
  outline-offset: 2px;
}

/* ---- application / contact banner ----
   Lives inside .yh-scene-stage (percent box, set inline from
   config/zone-07-content.js) — it is NOT viewport-fixed and scales/pans
   with the camera like any other scene object, by design (a real poster
   would too). Opaque solid panel (no glass/blur/white-SaaS-card look),
   sharp corners, thin rules — reads as a printed exhibition panel rather
   than a web card. Font stack is the project's existing system stack
   (see :root) — see core/zone-07-cta.js header for why "Druk Cond Cy
   Super"/"Apercu Pro" aren't used (neither exists anywhere in this repo,
   no license/files delivered — not introduced here either). */
/* LIGHT theme (customer revision, replaces the earlier dark-panel pass):
   same "printed poster, not a web card" sharp-corner/thin-rule language,
   just inverted to a light surface — dark ink text on the project's cream
   background instead of the reverse. Centered on screen (both axes,
   within the space that keeps the right Zone 07 wall/board/speaker
   clear — see config/zone-07-content.js for the exact box), primary CTA
   block enlarged, secondary contact-form block shrunk, and the same
   proportions/typography used on both desktop and mobile (only the
   outer box's position/size is calibrated per breakpoint). */
.yh-apply-banner {
  position: absolute;
  display: flex;
  flex-direction: column;
  /* customer revision: "reduce empty space, look at how designers
     usually do it" — content sizes to itself (see the two sections
     below, both flex:0 0 auto now) and this centers that compact group
     vertically in the fixed box, rather than force-stretching arbitrary
     proportions into it and leaving a dead gap in the middle. */
  justify-content: center;
  /* semi-transparent — customer revision: the scene behind should still
     read through the panel, not a fully opaque surface. */
  background: rgba(244, 239, 232, 0.86);
  color: var(--yh-ink);
  /* REAL BUG (found via computed-style inspection, not a guess): percent
     padding always resolves against the CONTAINING BLOCK's width (the
     scene stage, which is huge) for an absolutely-positioned element —
     never against the padded element's own box, even for top/bottom.
     At this banner's old, much larger size that mistake was invisible
     (the resulting px value was still small relative to the box); at
     this shrunk size the same percentages computed to ~157px of
     left+right padding on a ~316px-wide box, leaving ~2px for content
     and collapsing every child to 0 width. Values below are sized
     against the STAGE width for the CURRENT banner box, not a percent
     of the box itself. */
  padding: 0.7% 0.9%;
  box-sizing: border-box;
  overflow: hidden;
  border: 1px solid rgba(28, 26, 23, 0.12);
}

.yh-apply-banner__section { display: flex; flex-direction: column; }
/* both sections now content-sized (flex:0 0 auto) instead of forcing a
   fixed 50/50-ish split — the outer .yh-apply-banner's justify-content:
   center takes care of placing that compact group in the box. */
.yh-apply-banner__section--primary { flex: 0 0 auto; min-height: 0; }
.yh-apply-banner__section--secondary {
  flex: 0 0 auto;
  min-height: 0;
}

/* customer follow-up: "просила увеличить шрифт... но это супер огромный,
   это плохо" — the last few rounds compounded (1.7x-ish base bump, then
   another ~1.5x desktop-only multiplier on top) into sizes far past
   comfortable reading size. Reset to a size actually meant for a small
   on-screen card, tuned for real reading distance on both phone and
   desktop rather than "as large as will fit".
   SECOND follow-up (2026-09-15): these BASE values are what mobile
   actually renders (desktop gets its own separate, later ~1.5x bump via
   the @media block below) — customer found mobile "очень мелкий текст",
   now that the banner itself counter-scales to a constant real size
   (core/zone-07-cta.js) instead of shrinking further with the camera.
   First mobile-only bump (~1.25x) still read as "мелко" on her real
   device — pushed further here (~1.5x total vs. the original reset),
   verified against the mobile banner box's own real content-vs-box
   height so it still doesn't clip (see config/zone-07-content.js
   ZONE_07_BANNER_MOBILE). */
/* customer (2026-09-15): "текст ... крупнее и карточка не была такой
   пустой ... хочется увеличить пространство текста в полтора-два раза"
   — ~1.7x on every mobile text size below (base rules — desktop keeps
   its own separate @media bump, untouched, per "остальное оставляем").
   Bigger text naturally fills more of the fixed banner box too (still
   vertically centered — see .yh-apply-banner's justify-content), and
   ZONE_07_BANNER_MOBILE's own height was grown to match in config/
   zone-07-content.js so the extra content isn't clipped by this box's
   overflow:hidden. */
.yh-apply-banner__eyebrow {
  font-size: 1.8rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(28, 26, 23, 0.55);
  margin: 0 0 6px;
}

.yh-apply-banner__heading {
  font-size: 3.9rem;
  font-weight: 800;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  line-height: 1.1;
  margin: 0 0 9px;
}

.yh-apply-banner__lead {
  font-size: 2.1rem;
  line-height: 1.3;
  color: rgba(28, 26, 23, 0.75);
  margin: 0 0 16px;
}

/* .yh-app .yh-apply-banner__cta (not just .yh-apply-banner__cta): the
   broad ".yh-app a { color: inherit }" reset (see HEADER section, same
   fix already applied to .yh-cta) otherwise wins on specificity — this
   keeps the button's own colors regardless of that reset. Solid ink
   button (same language as the header's .yh-cta pill) for a strong,
   enlarged primary action against the now-light panel. */
/* customer revision: "the apply button should be bigger and more native"
   — switched from the flat sharp-corner "printed poster" rectangle to the
   same pill language already established for .yh-cta (header button)
   elsewhere in this app: rounded-full, generous min-height/padding — a
   real native-feeling tap target rather than a thin flat strip. */
.yh-app .yh-apply-banner__cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  align-self: flex-start;
  min-height: 98px;
  font-size: 2.4rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--yh-bg);
  background: var(--yh-ink);
  padding: 14px 30px;
  border-radius: 999px;
  box-sizing: border-box;
}

.yh-app .yh-apply-banner__cta:hover { background: #000; }
.yh-app .yh-apply-banner__cta[aria-disabled='true'] { opacity: 0.5; cursor: default; }

.yh-apply-banner__rule {
  height: 1px;
  background: rgba(28, 26, 23, 0.15);
  margin: 4px 0;
  flex: 0 0 auto;
}

/* eyebrow + subheading share one line ("ЕСТЬ ВОПРОС?" / "НАПИШИ НАМ") —
   both copy strings kept, just laid out side by side rather than
   stacked. Customer revision: both halves now the SAME font size/weight
   (were two different sizes/weights before) and pushed a bit further
   below the button/rule with extra top margin. */
.yh-apply-banner__subheading-row {
  display: flex;
  align-items: baseline;
  gap: 7px;
  margin: 6px 0 5px;
}
.yh-apply-banner__subheading-row .yh-apply-banner__eyebrow {
  margin: 0;
  font-size: 2rem;
  font-weight: 700;
  color: rgba(28, 26, 23, 0.85);
}

.yh-apply-banner__subheading {
  font-size: 2rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  text-transform: uppercase;
  margin: 0;
  color: rgba(28, 26, 23, 0.85);
}

.yh-apply-banner__form {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* name + email share a row on anything wide enough to fit two fields —
   pure spatial economy, still stacks on narrow mobile banners. */
.yh-apply-banner__form-row {
  display: flex;
  flex-wrap: wrap;
  gap: 2px 6px;
}

.yh-apply-banner__form-row .yh-apply-banner__field { flex: 1 1 70px; }

/* message + submit share a row instead of the button stacking below —
   saves a full row's height; button aligns to the textarea's baseline.
   Still wraps to its own stacked rows below when the panel is too
   narrow for both side by side. */
.yh-apply-banner__form-row--message { align-items: flex-end; }
.yh-apply-banner__form-row--message .yh-apply-banner__field { flex: 1 1 60px; min-width: 0; }

.yh-apply-banner__field {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.yh-apply-banner__field-label {
  font-size: 1.8rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(28, 26, 23, 0.5);
}

/* minimal input styling — a single underline rule, no border-radius, no
   box-shadow, no generic "form card" chrome. */
.yh-apply-banner__field-input {
  background: transparent;
  border: none;
  border-bottom: 1px solid rgba(28, 26, 23, 0.25);
  border-radius: 0;
  color: var(--yh-ink);
  font-size: 2.2rem;
  font-family: inherit;
  padding: 2px 3px;
  outline: none;
  resize: none;
  width: 100%;
  box-sizing: border-box;
}

.yh-apply-banner__field-input::placeholder { color: rgba(28, 26, 23, 0.3); }

.yh-apply-banner__field-input:focus {
  border-bottom-color: var(--yh-ink);
}

/* customer (2026-09-14): "поле несоразмерно" — a bare 1em min-height
   undercut the textarea's own rows="2" attribute (see core/zone-07-
   cta.js), reading as one thin single-line strip rather than a real
   multi-line field. */
textarea.yh-apply-banner__field-input { min-height: 2.4em; }

.yh-apply-banner__submit {
  flex: 0 0 auto;
  align-self: flex-end;
  white-space: nowrap;
  background: transparent;
  border: 1px solid var(--yh-ink);
  border-radius: 0;
  color: var(--yh-ink);
  font-size: 2rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  padding: 8px 16px;
  cursor: pointer;
}

.yh-apply-banner__submit:hover { background: var(--yh-ink); color: var(--yh-bg); }
.yh-apply-banner__submit:disabled { opacity: 0.5; cursor: default; }

.yh-apply-banner__status {
  font-size: 2rem;
  line-height: 1.3;
  min-height: 1em;
  margin-top: 3px;
}

.yh-apply-banner__status--error { color: #c0392b; }
.yh-apply-banner__status--success { color: #2e7d32; }
.yh-apply-banner__status--info { color: rgba(28, 26, 23, 0.65); }

/* customer follow-up: "супер огромный, это плохо... подумай какой самый
   удобный размер шрифта" — replaced the old ×1.5-on-top-of-an-already-
   bumped-base multiplier with a modest, comfortable desktop bump (roughly
   ×1.2-1.3 over the base above) instead of stacking multipliers. */
/* customer (2026-09-14): "текст хочется в 1,5 раза больше, само поле
   несоразмерно" — now that the banner counter-scales to a constant real
   size (see core/zone-07-cta.js layoutBanner) instead of shrinking with
   the camera, there's room for genuinely bigger text without it eating
   the whole scene at some zoom levels the way it used to. Every desktop
   size below ×1.5 vs. the previous pass; the message textarea also gets
   an explicit min-height (was relying on the HTML rows=2 attribute alone
   plus a 1em CSS min-height that undercut it, reading as a single thin
   line) so it looks like a real multi-line field, not a stray underline.
   The banner box itself is sized up to match in config/zone-07-content.js
   (ZONE_07_BANNER_DESKTOP/MOBILE) — same real-px targets, both changes
   verified together. */
@media (min-width: 768px) {
  .yh-apply-banner__eyebrow { font-size: 1.35rem; }
  .yh-apply-banner__heading { font-size: 3rem; }
  .yh-apply-banner__lead { font-size: 1.65rem; }
  .yh-app .yh-apply-banner__cta {
    min-height: 76px;
    font-size: 1.7rem;
    padding: 18px 36px;
  }
  .yh-apply-banner__subheading-row .yh-apply-banner__eyebrow,
  .yh-apply-banner__subheading { font-size: 1.4rem; }
  .yh-apply-banner__field-label { font-size: 1.15rem; }
  .yh-apply-banner__field-input { font-size: 1.5rem; }
  .yh-apply-banner__field-input.yh-apply-banner__field-input { min-height: 2.6em; }
  .yh-apply-banner__submit {
    font-size: 1.35rem;
    padding: 12px 24px;
  }
  .yh-apply-banner__status { font-size: 1.5rem; }
}

/* ZONE 07 — APPLICATION CARD / MOBILE ONLY (customer spec): only the
   upper "meaning" block (07 АМФИТЕАТР / ПОДАТЬ ЗАЯВКУ heading+lead / the
   CTA / ЕСТЬ ВОПРОС subheading-row) centers — scoped to
   .yh-apply-banner__section--primary specifically, not the shared
   .yh-apply-banner__section both sections use, so the form below
   (labels/inputs/ОТПРАВИТЬ) is untouched and stays left-aligned, per this
   revision's own explicit "форму лучше оставить выровненными по левому
   краю" instruction — an earlier pass had centered the field labels and
   the message+submit row too, which this reverts. subheading-row keeps
   its own justify-content:center below regardless of the outer section's
   alignment (it's a full-width flex item that centers ITS OWN children),
   so "ЕСТЬ ВОПРОС? НАПИШИ НАМ" still centers even though it technically
   lives in the (no-longer-centered) secondary/form section. */
@media (max-width: 767px) {
  .yh-apply-banner__section--primary { align-items: center; }
  .yh-apply-banner__eyebrow,
  .yh-apply-banner__heading,
  .yh-apply-banner__lead { text-align: center; }
  .yh-app .yh-apply-banner__cta { align-self: center; }
  .yh-apply-banner__subheading-row { justify-content: center; }
}

/* ================= SCENE VIEW ================= */

.yh-scene-view {
  position: absolute;
  inset: 0;
  z-index: var(--yh-z-scene);
  background: #0d0c0b;
}

.yh-scene-view[hidden] { display: none; }

.yh-scene-viewport {
  position: absolute;
  inset: 0;
  overflow: hidden;
  cursor: grab;
  touch-action: none; /* панорама полностью управляется Pointer Events/JS */
}

.yh-scene-viewport.is-dragging { cursor: grabbing; }

.yh-scene-stage {
  position: absolute;
  top: 0;
  left: 0;
  transform-origin: 0 0;
  will-change: transform;
}

.yh-scene-image {
  display: block;
  max-width: none;
  user-select: none;
  -webkit-user-drag: none;
  transition: opacity 260ms ease;
}

/* customer (2026-09-15): "фон подгружается прошлый" — applied by
   mount.js's setSceneImage while the NEXT zone's bitmap is still
   downloading in the background (see the long comment there); dims the
   still-showing PREVIOUS image as an explicit "loading" cue instead of
   it silently sitting at full opacity looking stuck. */
.yh-scene-image--loading { opacity: 0.4; }

/* ================= DEBUG HUD (?debug=1 / config.debug) ================= */

.yh-debug-hud {
  position: absolute;
  left: 12px;
  bottom: 80px;
  z-index: var(--yh-z-debug);
  background: rgba(0, 0, 0, 0.75);
  color: #7CFC9A;
  font-family: 'SF Mono', 'Consolas', monospace;
  font-size: 0.7rem;
  line-height: 1.5;
  padding: 10px 12px;
  border-radius: 8px;
  white-space: pre;
  pointer-events: none;
}

.yh-debug-hud[hidden] { display: none; }

/* ================= DEBUG: hotspot bounds (?debug=1 / config.debug) =================
   Production-ТЗ: "debug mode ?debug=1 для дальнейшей настройки координат
   hotspots" — visible bounding boxes + always-on labels, so exact
   placement can be judged and adjusted against the real BASE artwork. */

.yh-app--debug .yh-object-hotspot__item,
.yh-app--debug .yh-passage-hotspot__item {
  outline: 1px dashed rgba(255, 0, 60, 0.85);
  outline-offset: -1px;
}

.yh-app--debug .yh-object-hotspot__dot {
  width: 16px;
  height: 16px;
  background: rgba(255, 0, 60, 0.55);
  border-color: rgba(255, 0, 60, 0.9);
}

.yh-app--debug .yh-object-hotspot__label,
.yh-app--debug .yh-passage-hotspot__label,
.yh-app--debug .yh-map-hotspot__label {
  opacity: 1 !important;
}

@media (prefers-reduced-motion: reduce) {
  .yh-app * { animation-duration: 0.001ms !important; transition-duration: 0.001ms !important; }
}
