/* ══════════════════════════════════════════
   Reset & Base
══════════════════════════════════════════ */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html, body {
  width: 100%;
  height: 100%;
  overflow: hidden;
  font-family: 'Noto Sans KR', 'Malgun Gothic', sans-serif;
  background: var(--color-bg-dark);
  color: var(--color-text);
}

/* 포커스 인디케이터 — 키보드 접근성 WCAG 2.4.7 */
:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 2px;
}

/* 버튼은 별도 outline-offset 조정 */
button:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

button {
  font-family: inherit;
  cursor: pointer;
  border: none;
  background: none;
}

ul, ol {
  list-style: none;
}

svg {
  display: block;
  flex-shrink: 0;
}

/* ══════════════════════════════════════════
   CSS Custom Properties
══════════════════════════════════════════ */
:root {
  /* ── 색상 ─────────────────────────────────
     표면(surface)은 파노라마 위에 얹히므로 불투명에 가깝게 유지한다 — 씬 전환 중
     캔버스가 무효화될 때 반투명/blur 표면은 매 프레임 재합성돼 떨림의 원인이 된다. */
  --color-bg-dark:       #0a0a0f;
  --color-bg-panel:      rgba(12, 12, 18, 0.94);
  --color-surface:       rgb(12, 12, 18);  /* 파노라마 위 패널(사이드바·평면도) */
  /* 예전엔 rgba(...,0.97)로 살짝 투명했다. 완전 불투명이 아니면 브라우저가 매 프레임
     아래의 파노라마 WebGL 캔버스와 실제로 블렌딩해야 하는데, 모바일에서는 그 캔버스가
     유휴 상태일 때 GPU가 버퍼를 비우거나 레이어를 내렸다 올렸다 하면서 검은 사각형이
     번쩍이며 비쳐 보인다(평면도를 열었을 때의 jitter 원인). 데스크톱 GPU는 이 블렌딩을
     안정적으로 처리해 증상이 전혀 나타나지 않았다. 완전 불투명으로 바꿔 패널이 캔버스를
     완전히 가리게(occlude) 해서 블렌딩 자체를 없앤다. */
  --color-surface-raised: rgba(20, 20, 28, 0.97); /* 표면 위 한 단계 올라온 요소 */
  --color-bg-hover:      rgba(255, 255, 255, 0.07);
  --color-accent:        #c8a96e;
  --color-accent-hover:  #d4b97a;
  --color-accent-dim:    rgba(200, 169, 110, 0.18);
  --color-accent-glow:   rgba(200, 169, 110, 0.45);
  --color-text:          #f0f0f0;
  --color-text-muted:    #808090;
  --color-text-dim:      rgba(240, 240, 240, 0.65);
  --color-text-faint:    rgba(255, 255, 255, 0.6);
  --color-white:         #f8f8f8;
  --color-border:        rgba(255, 255, 255, 0.08);
  --color-border-mid:    rgba(255, 255, 255, 0.12);
  --color-overlay-dark:  rgba(0, 0, 0, 0.88);
  --color-overlay-mid:   rgba(0, 0, 0, 0.72);
  --color-overlay-light: rgba(0, 0, 0, 0.55);
  --color-overlay-pale:  rgba(0, 0, 0, 0.35);

  /* 반투명 컨트롤(파노라마 위 아이콘 버튼 등)의 단계별 채움 */
  --fill-subtle:         rgba(255, 255, 255, 0.08);
  --fill-soft:           rgba(255, 255, 255, 0.12);
  --fill-strong:         rgba(255, 255, 255, 0.22);

  /* ── 레이아웃 ─────────────────────────── */
  --toolbar-height:      50px;
  --sidebar-width:       264px;
  --sidebar-toggle-w:    28px;

  /* ── 라운딩 스케일 ─────────────────────────
     xs: 배지·태그 / sm: 인풋·작은 버튼 / md: 카드·이미지 / lg: 패널 / pill: 알약형 */
  --radius-xs:           3px;
  --radius-sm:           6px;
  --radius-md:           10px;
  --radius-lg:           14px;
  --radius-pill:         999px;
  --panel-radius:        var(--radius-lg);

  /* ── 그림자(elevation) ─────────────────────
     낮을수록 표면에 가깝고, 높을수록 떠 있는 요소(모달·라이트박스) */
  --shadow-sm:           0 1px 3px rgba(0, 0, 0, 0.4);
  --shadow-md:           0 4px 16px rgba(0, 0, 0, 0.45);
  --shadow-lg:           0 12px 36px rgba(0, 0, 0, 0.55);
  --shadow-xl:           0 24px 72px rgba(0, 0, 0, 0.7);

  /* ── 간격 스케일(4px 기반) ───────────────── */
  --space-1:             4px;
  --space-2:             8px;
  --space-3:             12px;
  --space-4:             16px;
  --space-5:             24px;
  --space-6:             32px;

  /* ── 모션 ─────────────────────────────────
     fast: 색·투명도 등 즉각 피드백 / med: 패널 슬라이드 / slow: 화면 전환 */
  --transition-fast:     160ms ease;
  --transition-med:      280ms cubic-bezier(0.4, 0, 0.2, 1);
  --transition-slow:     420ms cubic-bezier(0.4, 0, 0.2, 1);

  /* ── 타이포 ───────────────────────────────
     라벨류(섹션 헤더·오버라인)에 공통으로 쓰는 자간/크기 */
  --label-size:          0.68rem;
  --label-tracking:      0.16em;
}

/* ══════════════════════════════════════════
   Utility
══════════════════════════════════════════ */
.hidden {
  display: none !important;
}

/* ══════════════════════════════════════════
   에러 화면 (JS showError 사용)
══════════════════════════════════════════ */
.error-screen {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--color-bg-dark);
  color: var(--color-text);
  font-family: 'Noto Sans KR', sans-serif;
  text-align: center;
  padding: 2rem;
}

.error-screen h2 {
  font-size: 1.4rem;
  margin-bottom: 1rem;
  color: var(--color-text);
}

.error-screen .error-msg {
  color: var(--color-text-muted);
}

.error-screen .error-hint {
  color: var(--color-text-muted);
  opacity: 0.6;
  font-size: 0.8rem;
  margin-top: 1rem;
}

.error-screen code {
  color: var(--color-accent);
  font-family: monospace;
}

/* ══════════════════════════════════════════
   인트로 화면
══════════════════════════════════════════ */
#intro-screen {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden; /* 블러 배경 레이어의 scale(1.15) 확대분이 밖으로 새지 않도록 */
  transition: opacity 0.7s ease;
}

#intro-screen.fade-out {
  opacity: 0;
  pointer-events: none;
}

/* 인트로 배경 — 두 레이어 구성:
   1) #intro-bg-blur: 같은 이미지를 cover로 화면 전체를 꽉 채우고 크게 블러 처리한 배경.
      실제 이미지(#intro-bg-img)의 종횡비가 화면과 달라 생기는 여백을 이 블러 레이어가
      대신 채워, 레터박스 대신 은은하게 번진 배경으로 보이게 한다.
   2) #intro-bg-img: object-fit: contain으로 잘리지 않고 화면 안에 온전히 들어오는 실제 이미지.
   데스크톱/모바일용 이미지를 자바스크립트가 뷰포트에 맞춰 두 레이어 모두에 교체 적용한다. */
#intro-bg-blur {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  filter: brightness(0.45) saturate(0.85) blur(40px);
  transform: scale(1.15); /* 블러 가장자리가 화면 밖으로 밀려나 경계가 보이지 않도록 여유를 둠 */
  opacity: 0;
  transition: opacity var(--transition-slow);
}

#intro-bg-blur.ready {
  opacity: 1;
}

#intro-bg-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: center;
  /* 원색 그대로 표시 — 어둡게/채도 낮추는 필터 없음. 텍스트 가독성은 #intro-scrim 비네트로 보완 */
  /* JS가 이미지를 완전히 디코딩한 뒤에 .ready를 붙여 한 번에 페이드인시킨다.
     (src를 그대로 걸면 브라우저가 다운로드하며 위에서 아래로 조금씩 그려내
     모바일에서 화면이 여러 번 나뉘어 나타나는 것처럼 보인다.) */
  opacity: 0;
  transition: opacity var(--transition-slow);
}

#intro-bg-img.ready {
  opacity: 1;
}

#intro-bg-img:not([src]),
#intro-bg-img[src=""] {
  display: none;
}

/* 텍스트 가독성을 위해 이미지 위에 얹는 은은한 하단 비네트 */
#intro-scrim {
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center, transparent 40%, var(--color-overlay-pale) 100%);
  pointer-events: none;
}

#intro-content {
  position: relative;
  text-align: center;
  padding: 2rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
}

/* VR START 버튼/프리로드 상태 — 제목·부제목과 독립적으로 화면 세로 70% 지점에 고정.
   #intro-content(세로 중앙 정렬)의 flex 흐름에서 빠져나와 절대 위치로 배치한다. */
#intro-action-zone {
  position: absolute;
  left: 50%;
  top: 70%;
  transform: translate(-50%, -50%);
  text-align: center;
  padding: 0 2rem;
  width: 100%;
}

#intro-subtitle {
  font-size: clamp(0.75rem, 1.5vw, 0.9rem);
  font-weight: 300;
  letter-spacing: 0.15em;
  color: var(--color-text-dim);
  text-transform: uppercase;
}

#intro-title {
  font-size: clamp(2.5rem, 6vw, 5rem);
  font-weight: 700;
  letter-spacing: 0.06em;
  line-height: 1.1;
  color: var(--color-white);
  text-shadow: 0 4px 32px rgba(0, 0, 0, 0.8);
}

#btn-start {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.85rem 2.8rem;
  font-size: 0.9rem;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--color-bg-dark);
  background: var(--color-accent);
  border-radius: var(--radius-xs);
  transition: transform var(--transition-fast), box-shadow var(--transition-fast), background var(--transition-fast);
}

#btn-start svg {
  width: 16px;
  height: 16px;
  transition: transform var(--transition-fast);
}

#btn-start:hover {
  background: var(--color-accent-hover);
  transform: translateY(-2px);
  box-shadow: 0 8px 28px var(--color-accent-glow);
}

#btn-start:hover svg {
  transform: translateX(3px);
}

#btn-start:active {
  transform: translateY(0);
}

/* ══════════════════════════════════════════
   프리로드 진행 상태 (인트로 화면)
   Matterport/Google Arts & Culture류의 절제된 로딩 UX를 참고 —
   큰 퍼센티지 숫자를 시각적 초점으로, 바는 아주 얇게, 라벨은 최소화.
══════════════════════════════════════════ */
#preload-status {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.1rem;
  width: min(280px, 70vw);
  margin: 0 auto;
}

#preload-percent {
  display: flex;
  align-items: flex-start;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--color-white);
  text-shadow: 0 2px 24px rgba(200, 169, 110, 0.25);
}

#preload-percent-num {
  font-size: clamp(2.4rem, 5vw, 3.2rem);
  font-weight: 200;
  letter-spacing: -0.01em;
}

#preload-percent-sign {
  font-size: 1.1rem;
  font-weight: 300;
  margin-top: 0.4rem;
  margin-left: 0.15rem;
  color: var(--color-accent);
}

#preload-bar-track {
  position: relative;
  width: 100%;
  height: 2px;
  background: var(--fill-soft);
  border-radius: 1px;
  overflow: hidden;
}

#preload-bar-fill {
  position: relative;
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, var(--color-accent-hover), var(--color-accent));
  border-radius: 1px;
  transition: width 280ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* 바 끝단에서 은은하게 흐르는 광택 — 진행 중임을 알리는 미세한 생동감 */
#preload-bar-fill::after {
  content: '';
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: 28px;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.55));
  animation: preload-sheen 1.4s ease-in-out infinite;
}

@keyframes preload-sheen {
  0%, 100% { opacity: 0.3; }
  50%      { opacity: 0.9; }
}

#preload-label {
  font-size: 0.72rem;
  font-weight: 300;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

#btn-skip-preload {
  margin-top: 0.15rem;
  font-size: 0.72rem;
  font-weight: 400;
  letter-spacing: 0.12em;
  color: var(--color-text-muted);
  opacity: 0.7;
  border-bottom: 1px solid transparent;
  transition: color var(--transition-fast), opacity var(--transition-fast), border-color var(--transition-fast);
}

#btn-skip-preload:hover {
  color: var(--color-accent);
  opacity: 1;
  border-color: currentColor;
}

@media (prefers-reduced-motion: reduce) {
  #preload-bar-fill::after { animation: none; opacity: 0.5; }
}

/* ══════════════════════════════════════════
   투어 컨테이너
══════════════════════════════════════════ */
#tour-container {
  position: fixed;
  inset: 0;
  z-index: 1000;
}

/* ── 디버그 오버레이 (?debug=true 전용) — 현재 pitch/yaw/hfov 실시간 표시 ── */
#debug-overlay {
  position: fixed;
  top: 12px;
  left: 12px;
  z-index: 9500; /* 툴바/사이드바보다 위, 인트로 화면(9000)보다도 위 */
  padding: 0.6rem 0.8rem;
  background: rgba(0, 0, 0, 0.75);
  color: #4ade80;
  font-family: 'SFMono-Regular', Consolas, Menlo, monospace;
  font-size: 12px;
  line-height: 1.5;
  white-space: pre;
  border-radius: var(--radius-sm);
  border: 1px solid var(--fill-soft);
  pointer-events: none;
  user-select: none;
}

/* ══════════════════════════════════════════
   Pannellum 뷰어
══════════════════════════════════════════ */
#panorama-viewer {
  position: absolute;
  inset: 0;
  /* 씬 전환 시 .zooming-in이 이 요소 전체에 scale(4.0)을 건다. 확대된 픽셀이 요소
     경계 밖으로 뻗어나가면 위에 떠 있는 평면도 패널·팝업 영역까지 침범해, 파노라마
     조각이 평면도 위에 겹쳐 보인다(모바일에서 확정적으로 재현). overflow: hidden으로
     확대분을 요소 안에서 잘라내고, z-index를 명시해 오버레이(101 이상)보다 확실히
     아래에 깔리도록 스태킹 순서를 고정한다. */
  overflow: hidden;
  z-index: 1;
}

/* Pannellum 기본 컨트롤 숨김 (우리 툴바 사용) */
.pnlm-controls-container {
  display: none !important;
}

/* 씬 전환 시 파란 로딩 화면 숨김 — 자체 페이드 전환으로 대체 */
.pnlm-load-box {
  display: none !important;
}

/* 로드 버튼 스타일 */
.pnlm-load-button {
  background-color: rgba(0, 0, 0, 0.7) !important;
  border: 1px solid rgba(200, 169, 110, 0.4) !important;
}

.pnlm-load-button p {
  color: var(--color-text) !important;
  font-family: 'Noto Sans KR', sans-serif !important;
}

/* ── Pannellum 기본 핫스팟 숨김 (커스텀 엘리먼트 사용) ── */
.pnlm-hotspot-base {
  background: none !important;
  border: none !important;
  overflow: visible !important;
}

/* ══════════════════════════════════════════
   커스텀 핫스팟 엘리먼트
   Pannellum createTooltipFunc으로 주입
══════════════════════════════════════════ */

/* ── 공통 wrapper ── */
.hs-wrap {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  cursor: pointer;
  user-select: none;
  transition: opacity var(--transition-fast);
}

.hs-wrap:hover {
  opacity: 0.85;
}

/* ── 공통 라벨 — 호버 시만 표시 ── */
.hs-label {
  position: absolute;
  bottom: calc(100% + 6px);
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  font-family: 'Noto Sans KR', sans-serif;
  font-size: 11px;
  font-weight: 500;
  color: var(--color-white);
  background: var(--color-overlay-mid);
  padding: 3px 10px;
  border-radius: var(--radius-pill);
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--transition-fast);
  letter-spacing: 0.03em;
}

.hs-wrap:hover .hs-label {
  opacity: 1;
}

/* ══════════════════════════════════════════
   1. 관람 동선 — scene-arrow
   위로 향한 ∧ chevron + 원근 입체감 + 바닥 그림자
══════════════════════════════════════════ */
.hs-wrap.hs-type-scene-arrow {
  gap: 2px;
  perspective: 120px;
  perspective-origin: 50% 140%;
}

.hs-scene-arrow {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0;
  /* 바닥으로 기울어진 입체감 */
  transform: rotateX(32deg);
  transform-style: preserve-3d;
  transition: transform var(--transition-med);
}

.hs-scene-arrow svg {
  width: 54px;
  height: 54px;
  color: var(--color-white);
  filter:
    drop-shadow(0 0 12px var(--color-text-dim))
    drop-shadow(0 2px 4px rgba(0,0,0,0.4));
  animation: arrow-bounce 1.6s ease-in-out infinite;
}

/* 바닥 타원 그림자 */
.hs-scene-arrow::after {
  content: '';
  display: block;
  width: 36px;
  height: 8px;
  background: var(--color-overlay-pale);
  border-radius: 50%;
  filter: blur(3px);
  margin-top: -4px;
  animation: shadow-pulse 1.6s ease-in-out infinite;
}

@keyframes arrow-bounce {
  0%   { transform: translateY(0);    opacity: 1;    }
  45%  { transform: translateY(-9px); opacity: 0.75; }
  100% { transform: translateY(0);    opacity: 1;    }
}

@keyframes shadow-pulse {
  0%   { transform: scaleX(1);    opacity: 0.35; }
  45%  { transform: scaleX(0.65); opacity: 0.18; }
  100% { transform: scaleX(1);    opacity: 0.35; }
}

.hs-scene-arrow-label {
  white-space: nowrap;
  font-family: 'Noto Sans KR', sans-serif;
  font-size: 11px;
  font-weight: 600;
  color: var(--color-white);
  background: var(--color-overlay-light);
  padding: 2px 10px;
  border-radius: var(--radius-pill);
  letter-spacing: 0.04em;
  pointer-events: none;
  margin-top: 2px;
  /* perspective 공간 밖으로 꺼내서 납작해지지 않게 */
  transform: rotateX(-32deg);
}

/* ══════════════════════════════════════════
   2. 전시 스팟 — scene-spot
   핀 아이콘 + pulse 링 애니메이션 + 장소명 pill
══════════════════════════════════════════ */
.hs-type-scene-spot {
  gap: 5px;
}

.hs-scene-spot {
  display: flex;
  position: relative;
  width: 36px;
  height: 36px;
  align-items: center;
  justify-content: center;
}

/* pulse 링 — ::before / ::after 두 겹 */
.hs-scene-spot::before,
.hs-scene-spot::after {
  content: '';
  position: absolute;
  inset: 0;
  border-radius: 50%;
  border: 2px solid var(--color-text-dim);
  animation: spot-pulse 2s ease-out infinite;
}

.hs-scene-spot::after {
  animation-delay: 0.9s;
}

@keyframes spot-pulse {
  0%   { transform: scale(1);    opacity: 0.8; }
  70%  { transform: scale(2.2);  opacity: 0;   }
  100% { transform: scale(2.2);  opacity: 0;   }
}

.hs-scene-spot svg {
  width: 22px;
  height: 22px;
  color: var(--color-white);
  position: relative;
  z-index: 1;
  filter: drop-shadow(0 1px 3px rgba(0,0,0,0.5));
}

.hs-spot-name {
  white-space: nowrap;
  font-family: 'Noto Sans KR', sans-serif;
  font-size: 13px;
  font-weight: 600;
  color: var(--color-white);
  background: var(--color-overlay-mid);
  padding: 4px 13px;
  border-radius: var(--radius-pill);
  letter-spacing: 0.02em;
  pointer-events: none;
  backdrop-filter: blur(4px);
  transition: background var(--transition-fast);
}

.hs-type-scene-spot:hover .hs-spot-name {
  background: var(--color-overlay-dark);
}

/* ══════════════════════════════════════════
   3. 리플렛 다운받기 — leaflet
   흰색 반투명 원 + 다운로드 아이콘
══════════════════════════════════════════ */
.hs-leaflet {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.18);
  backdrop-filter: blur(6px);
  border: 1.5px solid rgba(255,255,255,0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background var(--transition-fast);
}

.hs-leaflet svg {
  width: 18px;
  height: 18px;
  color: var(--color-white);
}

.hs-wrap:hover .hs-leaflet {
  background: rgba(255, 255, 255, 0.32);
}

/* ══════════════════════════════════════════
   4. 이미지 팝업 — image-popup
   청록색 원 + 돋보기. 라벨(스팟 타이틀)은 표시하지 않는다.
══════════════════════════════════════════ */
.hs-type-image-popup {
  gap: 5px;
}

.hs-image-popup {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: rgba(0, 190, 200, 0.85);
  backdrop-filter: blur(4px);
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background var(--transition-fast), transform var(--transition-fast);
}

.hs-image-popup svg {
  width: 18px;
  height: 18px;
  color: var(--color-white);
}

.hs-wrap:hover .hs-image-popup {
  background: rgba(0, 210, 220, 1);
  transform: scale(1.1);
}

/* ══════════════════════════════════════════
   5. 동영상 — video
   분홍색 원 + 재생 아이콘, 라벨 pill 항상 표시 (image-popup과 동일 패턴)
══════════════════════════════════════════ */
.hs-type-video {
  gap: 5px;
}

.hs-video {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: rgba(224, 95, 160, 0.85);
  backdrop-filter: blur(4px);
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background var(--transition-fast), transform var(--transition-fast);
}

.hs-video svg {
  width: 16px;
  height: 16px;
  color: var(--color-white);
  /* 재생 삼각형을 살짝 채워서 눈에 잘 띄게 */
  fill: currentColor;
}

.hs-wrap:hover .hs-video {
  background: rgba(230, 110, 170, 1);
  transform: scale(1.1);
}

/* ══════════════════════════════════════════
   7. 외부 링크 — link
   보라색 원 + 외부 링크 아이콘. 라벨은 표시하지 않는다(image-popup과 동일 패턴).
   클릭하면 hs.url을 새 탭으로 연다(뷰어 이탈 없음).
══════════════════════════════════════════ */
.hs-type-link {
  gap: 5px;
}

.hs-link {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: rgba(155, 111, 224, 0.85);
  backdrop-filter: blur(4px);
  border: none;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background var(--transition-fast), transform var(--transition-fast);
}

.hs-link svg {
  width: 17px;
  height: 17px;
  color: var(--color-white);
}

.hs-wrap:hover .hs-link {
  background: rgba(165, 125, 230, 1);
  transform: scale(1.1);
}

.hs-type-video .hs-label {
  position: relative;
  bottom: auto;
  left: auto;
  transform: none;
  opacity: 1;
  font-size: 12px;
  font-weight: 600;
  background: var(--color-overlay-mid);
  padding: 3px 10px;
  border-radius: var(--radius-pill);
}

/* ══════════════════════════════════════════
   (레거시) text-popup / scene-dot
══════════════════════════════════════════ */
.hs-text-popup {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: rgba(60, 180, 120, 0.82);
  border: 1.5px solid rgba(255,255,255,0.4);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: 0 2px 12px var(--color-overlay-pale);
}

.hs-text-popup svg {
  width: 18px;
  height: 18px;
  color: var(--color-white);
}

.hs-wrap:hover .hs-text-popup {
  background: rgba(60, 200, 130, 1);
}

.hs-scene-dot {
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: rgba(60, 140, 220, 0.9);
  border: 2px solid rgba(255, 255, 255, 0.5);
  box-shadow: 0 0 0 0 rgba(60, 140, 220, 0.6);
  animation: hs-pulse 2s ease-out infinite;
}

@keyframes hs-pulse {
  0%   { box-shadow: 0 0 0 0   rgba(60, 140, 220, 0.6); }
  70%  { box-shadow: 0 0 0 14px rgba(60, 140, 220, 0); }
  100% { box-shadow: 0 0 0 0   rgba(60, 140, 220, 0); }
}

.hs-wrap:hover .hs-scene-dot {
  background: rgba(80, 160, 255, 1);
  animation: none;
  transform: scale(1.15);
}

/* ══════════════════════════════════════════
   씬 전환 트랜지션 오버레이
══════════════════════════════════════════ */
/* 씬 전환: 줌인 */
@keyframes zoom-into-scene {
  0%   { transform: scale(1); }
  100% { transform: scale(4.0); }
}

/* 씬 전환: 새 씬에서 줌아웃 복귀 */
@keyframes zoom-back-to-normal {
  0%   { transform: scale(4.0); }
  100% { transform: scale(1); }
}

#panorama-viewer.zooming-in {
  animation: zoom-into-scene 320ms cubic-bezier(0.4, 0, 1, 1) forwards;
  pointer-events: none;
}

#panorama-viewer.zooming-back {
  animation: zoom-back-to-normal 280ms cubic-bezier(0, 0, 0.2, 1) forwards;
}

/* scene-transition 오버레이 비활성 */
#scene-transition {
  display: none;
}

/* ══════════════════════════════════════════
   Floating Chevron — 관람동선 아이콘과 동일 스타일
   JS가 inline transform으로 입체 방향을 동적 계산
══════════════════════════════════════════ */
#floating-chevron {
  position: absolute;
  pointer-events: none;
  z-index: 50;
  opacity: 0;
  transition: opacity var(--transition-med);
  /* 중심 기준 배치 — JS가 left/top을 마우스 위치로 세팅 */
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  /* perspective는 JS inline style로 부모에 직접 적용 (낮을수록 소실점 왜곡이 강해짐) */
}

#floating-chevron.visible {
  opacity: 1;
}

.chevron-icon {
  position: relative;
  width: 52px;
  height: 100px;
  /* JS가 rotateX/rotateY를 직접 제어 — animation 없음 */
  transform-style: preserve-3d;
  will-change: transform;
  /* JS가 관람자-스팟 거리에 비례해 매 프레임 갱신하는 레이어 간 세로 간격 */
  --fc-gap: 12px;
}

/* 3중 레이어 스택 — 관람자와 가장 가까운(화면 하단) 화살표가 가장 크고 또렷하고,
   목적지(소실점) 방향인 위로 갈수록 작아지고 옅어진다. bottom 오프셋이 --fc-gap의
   배수로 누적되므로, 관람자가 스팟에 가까워질수록(--fc-gap 축소) 세 화살표가
   서로 좁혀지며 한 점으로 수렴하는 원근감을 만든다. */
.chevron-layer {
  position: absolute;
  left: 50%;
  transform-origin: 50% 100%;
  transform: translateX(-50%) translateY(0) scale(var(--fc-scale, 1));
  animation: fc-layer-advance 1.4s cubic-bezier(0.25, 0, 0.3, 1) infinite;
}

.chevron-layer svg {
  display: block;
  width: 52px;
  height: 52px;
  color: var(--color-white);
  /* 진행 방향(near → mid → far → near)으로 밝기가 순차 이동하는 루프.
     세 레이어가 동일 주기를 1/3씩 어긋난 딜레이로 재생해 릴레이처럼 보인다. */
  animation: fc-brightness-relay 1.8s ease-in-out infinite;
}

/* 가장 가까운(아래) 화살표 — 가장 크고 진하고 밝음 */
.chevron-layer-near {
  bottom: 0;
  --fc-scale: 1;
  animation-delay: 0s;
}
.chevron-layer-near svg {
  --fc-dim: 0.75;
  --fc-glow: drop-shadow(0 0 14px var(--fill-subtle)) drop-shadow(0 2px 4px rgba(0,0,0,0.45));
  --fc-glow-peak: drop-shadow(0 0 20px rgba(255,255,255,1)) drop-shadow(0 2px 4px rgba(0,0,0,0.45));
  animation-delay: 0s;
}

/* 중간 화살표 */
.chevron-layer-mid {
  bottom: calc(var(--fc-gap) * 1);
  --fc-scale: 0.76;
  animation-delay: 0.16s;
}
.chevron-layer-mid svg {
  --fc-dim: 0.45;
  --fc-glow: drop-shadow(0 0 8px rgba(255,255,255,0.5));
  --fc-glow-peak: drop-shadow(0 0 16px rgba(255,255,255,0.9));
  animation-delay: 0.6s;
}

/* 가장 먼(위, 소실점 방향) 화살표 — 가장 작고 흐림 */
.chevron-layer-far {
  bottom: calc(var(--fc-gap) * 2);
  --fc-scale: 0.54;
  animation-delay: 0.32s;
}
.chevron-layer-far svg {
  --fc-dim: 0.22;
  --fc-glow: drop-shadow(0 0 5px rgba(255,255,255,0.35));
  --fc-glow-peak: drop-shadow(0 0 12px rgba(255,255,255,0.75));
  animation-delay: 1.2s;
}

/* 바닥 타원 그림자 — near 레이어 기준, 고정 위치 */
.chevron-icon::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: -6px;
  width: 34px;
  height: 8px;
  background: rgba(0, 0, 0, 0.32);
  border-radius: 50%;
  filter: blur(3px);
  transform: translateX(-50%);
  animation: fc-shadow 1.4s cubic-bezier(0.25, 0, 0.3, 1) infinite;
}

/* 화살표가 관람자 쪽에서 솟아나 소실점(위) 방향으로 살짝 나아갔다 돌아오는 위치 애니메이션.
   각 레이어의 기본 scale(--fc-scale)은 유지한 채 translateY만 왕복시킨다. */
@keyframes fc-layer-advance {
  0%   { transform: translateX(-50%) translateY(0)    scale(var(--fc-scale, 1)); }
  50%  { transform: translateX(-50%) translateY(-6px) scale(var(--fc-scale, 1)); }
  100% { transform: translateX(-50%) translateY(0)    scale(var(--fc-scale, 1)); }
}

/* 밝기 릴레이 — 진행 방향(near → mid → far → near)으로 빛이 이어달리듯 순환한다.
   각 레이어는 딜레이만 다를 뿐 동일한 사이클을 타므로, 항상 정확히 한 레이어만
   정점 밝기(opacity 1 + 강한 glow)에 있고 나머지는 각자의 기본 밝기로 가라앉아 있다. */
@keyframes fc-brightness-relay {
  0%   { opacity: var(--fc-dim, 0.35); filter: var(--fc-glow); }
  12%  { opacity: 1;                   filter: var(--fc-glow-peak); }
  34%  { opacity: var(--fc-dim, 0.35); filter: var(--fc-glow); }
  100% { opacity: var(--fc-dim, 0.35); filter: var(--fc-glow); }
}

@keyframes fc-shadow {
  0%   { opacity: 0.32; }
  50%  { opacity: 0.14; }
  100% { opacity: 0.32; }
}

/* 관람동선 안내 화살표는 장식이 아니라 방향 정보 자체이므로(밝기 릴레이로 진행 순서를
   전달) prefers-reduced-motion과 무관하게 항상 애니메이션한다 — 다른 장식적 모션
   (씬 전환 확대 등)만 접근성 설정을 따른다. */

.chevron-label {
  font-family: 'Noto Sans KR', sans-serif;
  font-size: 11px;
  font-weight: 600;
  color: var(--color-white);
  background: var(--color-overlay-light);
  padding: 2px 10px;
  border-radius: var(--radius-pill);
  white-space: nowrap;
  letter-spacing: 0.04em;
  margin-top: 2px;
}

/* ══════════════════════════════════════════
   이미지 라이트박스 (갤러리형 1/N)
══════════════════════════════════════════ */
#lightbox {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
}

#lightbox-overlay {
  position: absolute;
  inset: 0;
  /* 동영상/텍스트 모달과 같은 농도 — 뒤 파노라마가 은은히 비쳐 보이게 한다 */
  background: var(--color-overlay-mid);
  cursor: pointer;
}


#lightbox-content {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  /* width를 명시해야 한다. #lightbox-img-wrap의 aspect-ratio는 부모 폭을 기준으로
     계산되는데, #lightbox-content가 폭을 자기 자신의(=자식의) 콘텐츠 크기에 맞춰
     정하는 상태(max-width만 있고 width는 auto)이면 "내 폭을 정하려면 자식 높이가
     필요하고, 자식 높이(aspect-ratio)를 정하려면 내 폭이 필요한" 순환이 생긴다.
     브라우저가 이를 매 리플로우마다 다르게 풀면서 컨텐츠가 작은 크기(관찰값 77px)로
     한 프레임 그려졌다가 정상 크기로 튀어, 페이지 전환마다 눈에 띄는 점프로 나타났다. */
  width: min(90vw, 860px);
  /* 상단바·캡션까지 포함해 화면 안에 들어오도록 — 예전엔 상단바를 컨텐츠 바깥(top: -2.6rem)에
     띄워서, 이미지가 큰 경우 닫기 버튼이 뷰포트 위로 잘려나갔다.
     vh 대신 dvh: 브라우저 chrome이 뜬 일반 모드에서 vh가 실시간으로 흔들리는 문제 회피. */
  max-height: 92dvh;
  gap: var(--space-3);
}

/* 상단 바: 카운터 + 닫기 — 컨텐츠 흐름 안에 두어 항상 화면에 보인다 */
#lightbox-topbar {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-shrink: 0;
}

#lightbox-counter {
  font-size: var(--label-size);
  color: var(--color-text-faint);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--label-tracking);
}

#lightbox-close {
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-faint);
  border-radius: 50%;
  background: var(--fill-subtle);
  transition: background var(--transition-fast), color var(--transition-fast);
}

#lightbox-close svg {
  width: 14px;
  height: 14px;
}

#lightbox-close:hover {
  background: var(--fill-strong);
  color: var(--color-white);
}

/* 이미지 영역 — 고정 종횡비 프레임.
   갤러리에 세로·가로 사진이 섞여 있으면 사진 크기에 맞춰 박스가 매번 달라지고(측정값
   500 → 223 → 281px), 세로 중앙 정렬이라 넘길 때마다 사진이 위아래로 튀면서 페이드인과
   겹쳐 떨려 보였다. 프레임을 4:3으로 고정하고 사진은 그 안에서 contain으로 맞춘다 —
   레이아웃이 사진 비율에 반응하지 않으면서, 세로로 꽉 채웠을 때처럼 여백이 과하지도 않다. */
#lightbox-img-wrap {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  aspect-ratio: 4 / 3;
  min-height: 0;
  flex: 0 1 auto;
}

/* 사진이 아니라 프레임을 채워, 레이아웃이 사진 크기에 흔들리지 않게 한다 */
#lightbox-img-wrap > #lightbox-img {
  width: 100%;
  height: 100%;
}

#lightbox-img {
  max-width: 100%;
  max-height: 100%;
  display: block;
  border-radius: var(--radius-md);
  object-fit: contain;
  /* 박스는 프레임 전체를 차지하고 사진만 contain으로 들어가므로, 그림자를 주면 사진이 아닌
     빈 프레임 테두리에 그려진다. 어두운 오버레이 위라 그림자 없이도 충분히 분리돼 보인다. */
  /* 디코딩이 끝나 .ready가 붙기 전에는 감춘다 — src가 없거나 로딩 중인 <img>가
     깨짐 아이콘/빈 칸으로 보이지 않도록. 준비되면 부드럽게 드러난다. */
  opacity: 0;
  transition: opacity var(--transition-fast);
}

#lightbox-img.ready {
  opacity: 1;
}

/* 좌우 화살표 */
.lightbox-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--fill-soft);
  border-radius: var(--radius-pill);
  color: var(--color-text-dim);
  transition: background var(--transition-fast), color var(--transition-fast);
  z-index: 1;
}

.lightbox-nav svg {
  width: 18px;
  height: 18px;
}

.lightbox-nav:hover {
  background: var(--fill-strong);
  color: var(--color-white);
}

.lightbox-nav:disabled {
  opacity: 0.2;
  pointer-events: none;
}

#lightbox-prev { left: -52px; }
#lightbox-next { right: -52px; }

/* 캡션 */
#lightbox-caption {
  text-align: center;
  color: var(--color-text-faint);
  font-size: 0.82rem;
  line-height: 1.5;
  max-width: 600px;
}

/* ── 텍스트 팝업 모달 ── */
#text-modal {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
}

#text-modal-overlay {
  position: absolute;
  inset: 0;
  background: var(--color-overlay-mid);
  cursor: pointer;
}

#text-modal-box {
  position: relative;
  background: var(--color-bg-panel);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 2.5rem 2.5rem 2rem;
  max-width: min(90vw, 560px);
  max-height: 80dvh;
  overflow-y: auto;
  box-shadow: 0 24px 80px rgba(0,0,0,0.7);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
}

#text-modal-close {
  position: absolute;
  top: 1rem;
  right: 1rem;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-muted);
  border-radius: 50%;
  background: var(--color-bg-hover);
  transition: background var(--transition-fast), color var(--transition-fast);
}

#text-modal-close svg {
  width: 13px;
  height: 13px;
}

#text-modal-close:hover {
  background: var(--fill-soft);
  color: var(--color-text);
}

#text-modal-title {
  font-size: 1.4rem;
  font-weight: 700;
  color: var(--color-accent);
  margin-bottom: 1.2rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--color-border);
  letter-spacing: -0.01em;
}

#text-modal-body {
  font-size: 0.9rem;
  line-height: 1.85;
  color: var(--color-text-muted);
  white-space: pre-wrap;
}

/* ══════════════════════════════════════════
   동영상 모달 (유튜브 embed) — text-modal과 동일한
   오버레이/블러/그림자 톤을 유지하되 16:9 폭으로 확장
══════════════════════════════════════════ */
#video-modal {
  position: fixed;
  inset: 0;
  z-index: 2000;
  display: flex;
  align-items: center;
  justify-content: center;
}

#video-modal-overlay {
  position: absolute;
  inset: 0;
  background: var(--color-overlay-mid);
  cursor: pointer;
}

#video-modal-box {
  position: relative;
  background: var(--color-bg-panel);
  color: var(--color-text);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 2.5rem 1.5rem 1.5rem;
  width: min(90vw, 900px);
  box-shadow: 0 24px 80px rgba(0,0,0,0.7);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
}

#video-modal-close {
  position: absolute;
  top: 1rem;
  right: 1rem;
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-muted);
  border-radius: 50%;
  background: var(--color-bg-hover);
  transition: background var(--transition-fast), color var(--transition-fast);
  z-index: 1;
}

#video-modal-close svg {
  width: 13px;
  height: 13px;
}

#video-modal-close:hover {
  background: var(--fill-soft);
  color: var(--color-text);
}

#video-modal-frame-wrap {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: #000;
}

#video-modal-frame-wrap iframe,
#video-modal-frame-wrap video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

#video-modal-caption {
  margin-top: 1rem;
  font-size: 0.85rem;
  line-height: 1.6;
  color: var(--color-text-muted);
}

/* ── 툴팁 (Pannellum 기본 덮어쓰기) ── */
.pnlm-tooltip span {
  background: rgba(10, 10, 15, 0.88) !important;
  color: var(--color-text) !important;
  padding: 4px 10px !important;
  border-radius: var(--radius-sm) !important;
  font-family: 'Noto Sans KR', sans-serif !important;
  font-size: 11px !important;
  white-space: nowrap !important;
  border: 1px solid var(--fill-subtle) !important;
}

/* ══════════════════════════════════════════
   오른쪽 사이드바
══════════════════════════════════════════ */
/* backdrop-filter를 쓰지 않는다 — 평면도 패널과 같은 이유. 사이드바가 열린 채로 스팟을
   옮기면 파노라마 전환 시 캔버스가 무효화되는데, 그 위에 blur가 얹히면 매 프레임 뒤
   레이어를 다시 샘플링해야 해서 사이드바가 떨려 보인다(특히 프레임 드랍이 큰 모바일).
   불투명에 가까운 단색 배경으로 대체해 합성 비용을 없앤다. */
#sidebar {
  position: absolute;
  top: 0;
  right: 0;
  width: var(--sidebar-width);
  height: calc(100% - var(--toolbar-height));
  background: var(--color-surface);
  border-left: 1px solid var(--color-border);
  box-shadow: var(--shadow-lg);
  transform: translateX(0);
  transition: transform var(--transition-med);
  z-index: 100;
  display: flex;
  flex-direction: column;
}

#sidebar.sidebar-collapsed {
  transform: translateX(calc(var(--sidebar-width) - var(--sidebar-toggle-w)));
}

/* 접힘 상태에서 노출되는 좁은 스트립에 목록 텍스트가 잘려 보이지 않도록 콘텐츠를 숨기고
   토글 탭(#btn-sidebar-toggle, 사이드바 바깥에 위치)만 보이게 한다 */
#sidebar.sidebar-collapsed #sidebar-inner {
  opacity: 0;
  pointer-events: none;
}

/* 딤 오버레이는 태블릿/모바일(≤768px, hover:none 트리거)에서만 의미가 있다.
   데스크톱에서는 기본적으로 완전히 비활성화해 클릭을 가로채지 않게 한다. */
#sidebar-scrim {
  display: none;
}

/* 토글 탭 버튼 */
#btn-sidebar-toggle {
  position: absolute;
  left: calc(-1 * var(--sidebar-toggle-w));
  top: 50%;
  transform: translateY(-50%);
  width: var(--sidebar-toggle-w);
  height: 64px;
  background: var(--color-surface); /* 사이드바와 동일: 전환 중 blur 재샘플 떨림 방지 */
  border: 1px solid var(--color-border);
  border-right: none;
  border-radius: var(--radius-md) 0 0 var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-muted);
  transition: color var(--transition-fast);
}

#btn-sidebar-toggle:hover {
  color: var(--color-text);
}

#btn-sidebar-toggle svg {
  width: 14px;
  height: 14px;
  transition: transform var(--transition-med);
}

#sidebar:not(.sidebar-collapsed) #btn-sidebar-toggle svg {
  transform: rotate(180deg);
}

/* 사이드바 내부 */
#sidebar-inner {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  display: flex;
  flex-direction: column;
  transition: opacity var(--transition-fast);
}

#sidebar-inner::-webkit-scrollbar {
  width: 4px;
}

#sidebar-inner::-webkit-scrollbar-track {
  background: transparent;
}

#sidebar-inner::-webkit-scrollbar-thumb {
  background: var(--color-border-mid);
  border-radius: var(--radius-xs);
}

#sidebar-header {
  padding: var(--space-5) var(--space-5) var(--space-3);
  border-bottom: 1px solid var(--color-border);
}

/* 라벨류(사이드바 헤더·섹션 헤더)는 같은 오버라인 스타일을 공유해 위계를 통일한다 */
.sidebar-label,
.section-label {
  font-size: var(--label-size);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  font-weight: 500;
}

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

#spot-list {
  padding: var(--space-2) 0 var(--space-4);
}

.section-group + .section-group {
  margin-top: var(--space-2);
}

.section-label {
  color: var(--color-accent);
  padding: var(--space-3) var(--space-5) var(--space-1);
  opacity: 0.9;
}

.spot-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 0.6rem var(--space-4);
  cursor: pointer;
  color: var(--color-text-muted);
  font-size: 0.85rem;
  font-weight: 400;
  transition: background var(--transition-fast), color var(--transition-fast);
  border-radius: var(--radius-sm);
  margin: 0 var(--space-2);
}

.spot-item:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}

.spot-item.active {
  background: var(--color-accent-dim);
  color: var(--color-accent);
  font-weight: 500;
}

/* 활성 항목 좌측 액센트 바 — 배경색만으로는 약한 위계를 또렷하게 만든다 */
.spot-item.active::before {
  content: '';
  position: absolute;
  left: 0;
  top: 50%;
  transform: translateY(-50%);
  width: 2px;
  height: 60%;
  border-radius: var(--radius-pill);
  background: var(--color-accent);
}

.spot-number {
  font-size: 0.68rem;
  font-weight: 300;
  min-width: 22px;
  opacity: 0.45;
  font-variant-numeric: tabular-nums;
}

.spot-item.active .spot-number {
  opacity: 0.7;
}

.spot-name {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.spot-arrow {
  width: 12px;
  height: 12px;
  opacity: 0;
  transition: opacity var(--transition-fast);
  flex-shrink: 0;
}

.spot-item.active .spot-arrow,
.spot-item:hover .spot-arrow {
  opacity: 0.5;
}

/* ══════════════════════════════════════════
   하단 툴바
══════════════════════════════════════════ */
#toolbar {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: var(--toolbar-height);
  background: var(--color-bg-panel);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-top: 1px solid var(--color-border);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 0.5rem;
  z-index: 100;
  gap: 0.5rem;
}

#toolbar-left,
#toolbar-right {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  flex-shrink: 0;
}

#toolbar-center {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-width: 0;
  padding: 0 0.5rem;
}

#toolbar-spot-name {
  font-size: 0.85rem;
  font-weight: 500;
  color: var(--color-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

#toolbar-spot-counter {
  font-size: 0.65rem;
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

.tb-btn {
  width: 36px;
  height: 36px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-muted);
  border-radius: var(--radius-sm);
  transition: background var(--transition-fast), color var(--transition-fast);
  flex-shrink: 0;
}

.tb-btn svg {
  width: 16px;
  height: 16px;
}

.tb-btn:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}

.tb-btn.active {
  color: var(--color-accent);
  background: var(--color-accent-dim);
}

.tb-btn:disabled {
  opacity: 0.25;
  cursor: not-allowed;
  pointer-events: none;
}

.tb-divider {
  width: 1px;
  height: 18px;
  background: var(--color-border-mid);
  margin: 0 var(--space-2);
  flex-shrink: 0;
}

/* ══════════════════════════════════════════
   평면도 패널
══════════════════════════════════════════ */
/* backdrop-filter를 쓰지 않는다 — 평면도가 열린 채로 마커를 눌러 씬을 전환하면
   startRenderWake()가 씬 로드까지 16ms 간격으로 viewer.resize()를 호출해 파노라마
   캔버스 레이어가 계속 무효화되는데, 그 위에 blur가 얹히면 매 프레임 뒤 레이어를
   다시 샘플링해야 해서 패널이 떨려 보인다(특히 폭이 100%가 되는 모바일).
   불투명에 가까운 단색 배경으로 대체해 합성 비용을 없앤다. */
#floorplan-panel {
  position: absolute;
  bottom: var(--toolbar-height);
  left: 0;
  width: clamp(280px, 36vw, 460px);
  background: var(--color-surface);
  border-right: 1px solid var(--color-border);
  border-top: 1px solid var(--color-border);
  border-radius: 0 var(--panel-radius) 0 0;
  box-shadow: var(--shadow-lg);
  transform: translateY(0);
  opacity: 1;
  transition: transform var(--transition-med), opacity var(--transition-med);
  z-index: 101;
  /* 100vh 대신 100dvh: 안드로이드 크롬 등 브라우저 chrome(하단 네비게이션바)이 보이는
     일반 모드에서는 URL 바가 접혔다 펴졌다 할 때마다 100vh 값이 실시간으로 바뀌어
     패널이 매 프레임 리사이즈되며 떨려 보였다(전체화면 모드에서만 안정적이었던 이유 —
     전체화면은 chrome 자체가 없어 뷰포트가 고정되기 때문). dvh는 브라우저가 현재
     보이는 뷰포트에 안정적으로 맞추도록 설계된 단위라 이 흔들림이 없다. */
  max-height: calc(100dvh - var(--toolbar-height) - 40px);
  display: flex;
  flex-direction: column;
}

#floorplan-panel.panel-hidden {
  transform: translateY(calc(100% + 1px));
  opacity: 0;
  pointer-events: none;
}

/* 데스크톱 — 태블릿/모바일보다 화면이 넓으므로 평면도를 기본 대비 2배 크기로 키워
   가독성을 높인다. vw 비율도 함께 키워 넓은 화면에서 더 크게 보이도록 하되,
   최대 900px로 상한을 둬 초광폭 모니터에서 과도하게 커지지 않게 한다. */
@media (min-width: 769px) {
  #floorplan-panel {
    width: clamp(560px, 60vw, 900px);
  }
}

#floorplan-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  flex-shrink: 0;
}

/* 사이드바 헤더와 같은 오버라인 라벨 스타일 */
#floorplan-header span {
  font-size: var(--label-size);
  letter-spacing: var(--label-tracking);
  text-transform: uppercase;
  font-weight: 500;
  color: var(--color-text-muted);
}

#btn-floorplan-close {
  width: 28px;
  height: 28px;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-text-muted);
  border-radius: var(--radius-sm);
  transition: background var(--transition-fast), color var(--transition-fast);
}

#btn-floorplan-close svg {
  width: 14px;
  height: 14px;
}

#btn-floorplan-close:hover {
  background: var(--color-bg-hover);
  color: var(--color-text);
}

/* 평면도는 스크롤 없이 한눈에 보여야 하므로, 남은 높이 안에 이미지를 맞춘다.
   min-height: 0이 있어야 flex 자식이 콘텐츠 크기 아래로 줄어들 수 있다(그게 없으면
   이미지 높이가 그대로 밀어내 스크롤이 생긴다). */
/* 패딩 대신 안쪽 컨테이너의 margin으로 여백을 준다. box-sizing: border-box 상태에서
   패딩을 쓰면 자식의 max-height: 100%가 패딩까지 포함한 높이를 기준으로 잡혀
   패딩만큼 넘쳐 스크롤이 남는다. */
#floorplan-body {
  overflow: hidden;
  flex: 1;
  min-height: 0;
  display: flex;
}

#floorplan-body::-webkit-scrollbar {
  width: 4px;
}

#floorplan-body::-webkit-scrollbar-thumb {
  background: var(--color-border-mid);
  border-radius: var(--radius-xs);
}

/* 마커(.fp-marker)는 이 컨테이너 기준 % 좌표로 배치되므로, 컨테이너가 이미지와 정확히
   같은 크기여야 마커가 제자리에 놓인다. 그래서 컨테이너를 이미지 크기에 맞추고(fit-content),
   이미지 쪽에서 가용 영역을 넘지 않도록 제한한다. */
#floorplan-map-container {
  position: relative;
  margin: auto;              /* 남는 공간이 있으면 가운데 정렬 */
  background: rgba(255, 255, 255, 0.03);
  border-radius: var(--radius-sm);
  overflow: hidden;
  min-height: 0;
  max-width: calc(100% - 1.5rem);   /* 패널 안쪽 여백 */
  max-height: 100%;
  /* 평면도 이미지 디코딩이 끝나기 전까지는 감춰둔다(JS가 .ready를 붙인다).
     이미지가 없으면 컨테이너 크기가 0이라 % 좌표인 마커들이 한 점에 뭉쳐 보이고,
     alt 텍스트까지 함께 노출돼 "깨진 평면도"처럼 보이기 때문이다. */
  opacity: 0;
  transition: opacity var(--transition-fast);
}

#floorplan-map-container.ready {
  opacity: 1;
}

/* 패널 안에 스크롤이 생기지 않도록 가로·세로 모두 가용 범위 안에서 비율을 유지한다.
   원본보다 크게 늘리지 않아 확대로 인한 화질 저하도 없다.
   세로 제한은 뷰포트에서 직접 계산한다 — 부모가 fit-content라 max-height: 100%가
   참조할 확정 높이를 갖지 못해 제 역할을 하지 못하기 때문이다.
   (패널 max-height = 100vh - 툴바 - 40px, 여기서 헤더 53px과 위아래 여백을 뺀다) */
#floorplan-img {
  display: block;
  max-width: 100%;
  max-height: calc(100dvh - var(--toolbar-height) - 40px - 53px - 1.5rem);
  width: auto;
  height: auto;
  border-radius: var(--radius-sm);
}

/* 평면도 이미지가 없을 때 플레이스홀더 */
#floorplan-img[src=""], #floorplan-img:not([src]) {
  min-height: 200px;
  background: linear-gradient(135deg, rgba(200,169,110,0.05) 0%, rgba(255,255,255,0.03) 100%);
}

/* 평면도 스팟 마커 — 평면도 원본이 밝은 배경(도면·백지 등)인 경우가 많으므로
   라벨은 텍스트 셰도우가 아니라 불투명한 pill 배지로 감싸 배경색과 무관하게
   항상 읽히도록 한다. 도트도 흰 테두리+그림자를 둬 밝은 면 위에서도 윤곽이 산다. */
.fp-marker {
  position: absolute;
  transform: translate(-50%, -50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 5px;
  cursor: pointer;
  background: none;
  border: none;
  padding: 4px;
}

/* 시야각 부채꼴 — 현재 스팟(.active)일 때만 표시. dot과 같은 지점을 중심으로 삼아
   conic-gradient로 개구각(FP_FAN_ANGLE_DEG)만큼 색을 채우고, 마커별 floorplanViewAngle
   만큼 회전시켜 방향을 정한다. 0°는 "위쪽"을 향하도록 -90deg 오프셋을 기준값에 반영. */
.fp-fan {
  --fp-fan-angle: 70deg; /* 부채꼴 개구각(시야각 폭) — 필요시 조정 */
  position: absolute;
  top: 50%;
  left: 50%;
  width: 64px;
  height: 64px;
  border-radius: 50%;
  background: conic-gradient(
    from calc(-90deg - var(--fp-fan-angle) / 2),
    rgba(64, 214, 181, 0.85) 0deg,
    rgba(64, 214, 181, 0.85) var(--fp-fan-angle),
    transparent var(--fp-fan-angle)
  );
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--transition-fast);
  z-index: 0;
}

.fp-marker.active .fp-fan {
  opacity: 1;
}

.fp-dot {
  display: block;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  background: var(--color-text-muted);
  border: 2px solid rgba(255, 255, 255, 0.95);
  box-shadow: 0 1px 4px var(--color-overlay-light), 0 0 0 1px rgba(0, 0, 0, 0.25);
  transition: background var(--transition-fast), transform var(--transition-fast), box-shadow var(--transition-fast);
}

.fp-marker:hover .fp-dot {
  background: var(--color-accent-hover);
  transform: scale(1.4);
}

@keyframes fp-pulse {
  0%   { box-shadow: 0 1px 4px var(--color-overlay-light), 0 0 0 0   rgba(200, 169, 110, 0.55); }
  60%  { box-shadow: 0 1px 4px var(--color-overlay-light), 0 0 0 8px rgba(200, 169, 110, 0); }
  100% { box-shadow: 0 1px 4px var(--color-overlay-light), 0 0 0 0   rgba(200, 169, 110, 0); }
}

.fp-marker.active .fp-dot {
  background: var(--color-accent);
  border-color: rgba(255, 255, 255, 0.95);
  transform: scale(1.5);
  animation: fp-pulse 1.8s ease-out infinite;
}

/* 평면도 없을 때 가이드 텍스트 */
.fp-placeholder {
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 180px;
  color: var(--color-text-muted);
  font-size: 0.8rem;
  text-align: center;
  padding: 1rem;
  line-height: 1.6;
}

/* ══════════════════════════════════════════
   UI 전체 숨김 상태
══════════════════════════════════════════ */
#tour-container.ui-hidden #sidebar,
#tour-container.ui-hidden #toolbar,
#tour-container.ui-hidden #floorplan-panel {
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition-med);
}

/* ══════════════════════════════════════════
   평면도 열림 중 핫스팟 애니메이션 정지
══════════════════════════════════════════ */
/* 관람동선(hs-scene-arrow)·전시스팟(hs-scene-spot) 등의 무한 반복 애니메이션은
   filter: drop-shadow / blur를 동반해 매 프레임 리페인트를 일으킨다. 평면도 패널이
   그 위에 겹쳐 있으면 겹친 영역의 패널까지 매번 다시 합성해야 해서, 핫스팟이 패널
   뒤에 가려지는 화각에서만 패널이 떨려 보였다(가려져 보이지도 않는 요소가 원인).
   패널이 열려 있는 동안에는 애니메이션을 멈춰 리페인트 자체를 없앤다.
   주의: arrow-bounce는 .hs-scene-arrow가 아니라 그 자식 svg에 걸려 있다. 부모만
   겨냥하면 정지되지 않으므로 실제 애니메이션이 선언된 요소를 그대로 나열한다. */
#tour-container.floorplan-open .hs-scene-arrow svg,
#tour-container.floorplan-open .hs-scene-arrow::after,
#tour-container.floorplan-open .hs-scene-spot::before,
#tour-container.floorplan-open .hs-scene-spot::after,
#tour-container.floorplan-open .hs-scene-dot {
  animation: none;
}

/* 애니메이션을 멈춰도 drop-shadow/blur 필터가 남아 있으면 합성 비용이 계속 들고,
   패널에 가려 어차피 보이지 않는다. 필터와 3D 컨텍스트까지 걷어내 레이어를 단순화한다. */
#tour-container.floorplan-open .hs-scene-arrow svg,
#tour-container.floorplan-open .hs-scene-arrow::after {
  filter: none;
}

#tour-container.floorplan-open .hs-wrap.hs-type-scene-arrow {
  perspective: none;
}

/* ══════════════════════════════════════════
   반응형 (태블릿 / 모바일)
   터치 입력을 1차 입력수단으로 가정해 재설계:
   - 터치 타겟은 44×44px(WCAG 권장) 이상을 유지
   - hover 전용 UI(라벨, floating chevron)는 터치 디바이스에서 대체 표시
   - 모달류(라이트박스 등)는 화면 폭 안에서 완결되도록 재배치
══════════════════════════════════════════ */

/* hover가 없는 입력장치(터치) 공통 — 데스크톱 마우스에는 영향 없음 */
@media (hover: none) {
  /* hover로만 노출되던 핫스팟 라벨을 상시 표시 — 탭 전에 무엇인지 알 수 있어야 함.
     단, 관람동선(scene-arrow)은 화살표 자체에 "관람동선" 라벨(.hs-scene-arrow-label)이
     이미 항상 붙어 있으므로 이 범용 hover 라벨은 터치에서 절대 노출하지 않는다. */
  .hs-label {
    opacity: 1;
    transform: translateY(0);
  }

  .hs-type-scene-arrow .hs-label {
    display: none;
  }

  /* mousemove 전용인 floating chevron은 터치에서 절대 나타나지 않으므로 렌더링 자체를 끔 */
  #floating-chevron {
    display: none;
  }

  /* 터치 디바이스는 핀치 제스처로 확대/축소가 가능하므로 버튼을 숨겨 툴바 폭을 확보 */
  #btn-zoom-in,
  #btn-zoom-out {
    display: none;
  }
}

/* 태블릿 — 사이드바 자체의 펼침/접힘 규칙은 데스크톱과 동일한 의미(.sidebar-collapsed = 접힘)를
   그대로 쓴다. 기본 접힘 여부는 app.js의 initSidebarDefault()가 뷰포트를 보고 결정한다. */
@media (max-width: 768px) {
  :root {
    /* 스팟 이름을 한 줄로 읽을 수 있는 실용 최소폭 — 화면을 불필요하게 가리지 않도록
       vw 비율 대신 고정폭에 가까운 상한을 둔다 */
    --sidebar-width: min(72vw, 280px);
  }

  /* 토글 탭의 터치 히트영역 확대(시각 크기는 유지, 히트영역만 확장) */
  #btn-sidebar-toggle {
    width: calc(var(--sidebar-toggle-w) + 10px);
  }

  /* 사이드바 스팟 리스트 항목 — 터치 타겟 44px 이상 확보 */
  .spot-item {
    min-height: 44px;
  }

  /* 사이드바 펼침 시 뒤 파노라마를 어둡게 하고 탭하면 닫히게 유도(드로어 패턴) */
  #sidebar-scrim {
    display: block;
    position: absolute;
    inset: 0;
    background: rgba(0, 0, 0, 0.4);
    z-index: 90;
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--transition-med);
  }

  #sidebar:not(.sidebar-collapsed) ~ #sidebar-scrim,
  #sidebar-scrim.sidebar-scrim-visible {
    opacity: 1;
    pointer-events: auto;
  }
}

/* 모바일 */
@media (max-width: 600px) {
  :root {
    /* 태블릿보다도 화면이 좁으므로 더 타이트한 실용 최소폭을 사용 —
       "전시 소개" 등 5~8자 스팟 이름이 한 줄에 들어가는 최소 기준 */
    --sidebar-width: min(66vw, 240px);
    --toolbar-height: 56px;
  }

  #toolbar-center {
    display: none;
  }

  #toolbar-left,
  #toolbar-right {
    gap: 2px;
  }

  /* 터치 타겟 44×44px 확보 (WCAG 권장치) — 기본 데스크톱 크기(36px)보다 오히려 키움 */
  .tb-btn {
    width: 44px;
    height: 44px;
  }

  #floorplan-panel {
    width: 100%;
    border-right: none;
    border-radius: 0;
  }

  /* 라이트박스: 좌우 화살표를 화면 안쪽으로 당기고 이미지 위에 오버레이.
     width(고정값)에 얹는 max-width였는데, #lightbox-img-wrap의 aspect-ratio가
     부모 폭을 참조하는 상황에서 width와 max-width가 서로 다른 값(96vw vs 90vw)을
     가지면 aspect-ratio 계산이 매 리플로우마다 불안정해져 페이지 전환 시 미세한
     레이아웃 점프(jitter)를 만들었다. width 자체를 96vw로 덮어써 하나의 값만
     남긴다. */
  #lightbox-content {
    width: 96vw;
  }

  #lightbox-prev { left: 4px; }
  #lightbox-next { right: 4px; }

  .lightbox-nav {
    width: 44px;
    height: 44px;
    background: var(--color-overlay-light);
  }

  /* 텍스트 모달: 좌우 여백 최소화, 세로 스크롤은 내부에서 처리 */
  #text-modal-box {
    width: 92vw;
    max-height: 82dvh;
    padding: 1.5rem 1.25rem;
  }

  /* 동영상 모달: 좌우 여백 최소화 (16:9 비율은 aspect-ratio가 자동으로 유지) */
  #video-modal-box {
    width: 94vw;
    padding: 2rem 0.75rem 1rem;
  }

  /* 핫스팟 클릭/탭 히트영역을 넓혀 오탭을 줄임 (시각 크기는 아이콘 그대로 유지) */
  .hs-wrap {
    padding: 10px;
    margin: -10px;
  }
}

@media (max-width: 400px) {
  /* 44px 터치 타겟은 유지하고, 좁은 폭에서는 버튼 사이 여백/툴바 좌우 패딩·간격을 줄여 공간을 확보 */
  #toolbar {
    padding: 0 0.15rem;
    gap: 0;
  }

  #toolbar-left,
  #toolbar-right {
    gap: 0;
  }

  .tb-divider {
    margin: 0 1px;
  }

  .tb-btn svg {
    width: 15px;
    height: 15px;
  }
}

/* 모션 감소 선호 — UI 전환만 줄임, 핵심 UX 애니메이션(씬 전환·핫스팟)은 유지 */
@media (prefers-reduced-motion: reduce) {
  #intro-screen,
  #sidebar,
  #floorplan-panel,
  #floating-chevron,
  .tb-btn,
  .spot-item,
  #btn-start {
    transition-duration: 0.01ms !important;
  }
}
