/* ════════════════════════════════════════════════════════════════════
   gallery-overlay.css
   Shared between index.html (gallery section) and browse.html (catalog).
   Defines: .overlay backdrop, .overlay-window card, header w/ title +
   QR button + nav buttons, .overlay-stage iframe container, dots+arrows,
   QR modal, .mobile-close X button. Depends on CSS custom properties
   (--bg-dark, --fg, --accent, --muted) being defined by the parent page.
   Extracted from index.html 2026-05-25 (was inlined lines 1149–1498).
   ════════════════════════════════════════════════════════════════════ */

/* overlay window — also dark */
.overlay {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  background: rgba(0, 0, 0, 0.82);
  backdrop-filter: blur(6px);
  opacity: 0; pointer-events: none;
  transition: opacity 0.4s ease;
  /* Prevent over-pulled horizontal swipes from being interpreted by
     the browser as page-back navigation. With the infinite-loop nav
     in place (swipe past piece 20 wraps to piece 1), there's no actual
     boundary to over-pull, but this is defense-in-depth. */
  overscroll-behavior: contain;
  /* Block ALL browser touch behavior on the overlay surface, including
     pinch-zoom. The parent page itself stays normally zoomable (which
     prevents iOS's "user-scalable=no = escalate pinch-out to tab
     switcher" behavior), but while the overlay is active, none of those
     browser-level gestures fire on this region. Crucially, this does
     NOT prevent JS-handled events from dispatching to the iframe inside
     — touch-action only governs the browser's own gesture handling,
     not whether events fire to JS. So the iframe's gesturestart /
     gesturechange handlers still receive the pinch, our camera-z
     handler still runs, and no parent-page zoom or tab-switcher escape
     can happen on top. */
  touch-action: none;
}
.overlay.active { opacity: 1; pointer-events: all; }
.overlay-window {
  width: 86vw; height: 90vh;
  background: var(--bg-dark);
  display: flex; flex-direction: column;
  box-shadow: 0 40px 100px rgba(0, 0, 0, 0.7), 0 8px 24px rgba(0, 0, 0, 0.5);
  border: 1px solid rgba(201, 185, 154, 0.12);
  border-radius: 2px; overflow: hidden;
  transform: translateY(16px) scale(0.97);
  transition: transform 0.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.overlay.active .overlay-window { transform: translateY(0) scale(1); }
.overlay-header {
  padding: 0.6rem 1.5rem;
  display: flex; justify-content: space-between; align-items: center;
  border-bottom: 1px solid rgba(201, 185, 154, 0.15);
  flex-shrink: 0; gap: 1rem;
  background: #0a0a0a;
}
.overlay-title {
  font-family: "Newsreader", serif;
  /* Bigger and brighter for legibility */
  font-size: 1.15rem; font-weight: 400; font-style: italic;
  letter-spacing: 0.04em; flex: 1;
  color: #ffffff;
}
/* Pill-shaped affordance for an interactive button, but with editorial
   typography (Newsreader italic, sentence case) so it doesn't shout. The
   previous treatment used Lato 0.15em UPPERCASE which clashed against the
   serif body voice everywhere around it. The border keeps the "this is
   clickable" affordance; the typography just stops yelling. */
.qr-btn-gallery {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 1rem;
  letter-spacing: 0.01em;
  font-weight: 400;
  color: #ffffff; cursor: pointer;
  background: none;
  border: 1px solid rgba(201, 185, 154, 0.35);
  border-radius: 2rem;
  padding: 0.5rem 1.4rem;
  transition: color 0.2s, border-color 0.2s, background 0.2s;
  flex: 0 0 auto;
}
.qr-btn-gallery:hover {
  color: #ffffff;
  border-color: rgba(201, 185, 154, 0.7);
  background: rgba(201, 185, 154, 0.10);
}
.overlay-nav {
  display: flex; align-items: center; gap: 1.5rem;
  flex: 1;
  justify-content: flex-end;
}
.qr-modal {
  display: none;
  position: fixed; inset: 0;
  z-index: 400;
  background: rgba(10, 8, 6, 0.85);
  backdrop-filter: blur(8px);
  align-items: center;
  justify-content: center;
}
.qr-modal.show { display: flex; }
.qr-card {
  background: #f0ede8;
  padding: 2rem 2.2rem;
  border-radius: 0.6rem;
  text-align: center;
  max-width: 360px;
  font-family: "Newsreader", serif;
  color: #1a1814;
  box-shadow: 0 12px 48px rgba(0,0,0,0.4);
}
.qr-card h2 {
  margin: 0 0 0.4rem 0;
  font-weight: 400;
  font-size: 1.3rem;
  letter-spacing: 0.04em;
  font-style: italic;
}
.qr-card .qr-instr {
  font-family: "Lato", sans-serif;
  font-size: 0.85rem;
  color: #5a544a;
  margin-bottom: 1.2rem;
  line-height: 1.5;
  font-weight: 300;
}
.qr-card .qr-img {
  width: 240px; height: 240px;
  margin: 0 auto 1rem;
  background: white;
  padding: 12px;
  border-radius: 0.4rem;
}
.qr-card .qr-img img {
  width: 100%; height: 100%; display: block;
}
.qr-card .qr-url-text {
  font-family: monospace;
  font-size: 0.7rem;
  color: #7a756c;
  word-break: break-all;
  margin-bottom: 1.2rem;
  padding: 0.5rem;
  background: rgba(0,0,0,0.04);
  border-radius: 0.3rem;
}
/* QR modal close button — matches the overlay nav/close buttons in
   editorial voice (Newsreader italic, sentence-case), since clicking
   the QR modal's Close completes the same chain that started with
   .qr-btn-gallery. Keeps the dark pill background as button affordance. */
.qr-card .qr-close-btn {
  background: #3a3631;
  color: #f0ede8;
  border: none;
  padding: 0.5rem 1.4rem;
  border-radius: 0.3rem;
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 0.95rem;
  letter-spacing: 0.01em;
  font-weight: 400;
  cursor: pointer;
}
/* Overlay navigation — Prev / Next / Close. Sit alongside .qr-btn-gallery
   in the overlay header, so use the same editorial voice (Newsreader
   italic, sentence-case) — just text-only rather than pill-bordered,
   since they're navigation rather than a primary action. */
.overlay .nav-btn,
.overlay .close-btn {
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 1rem;
  letter-spacing: 0.01em;
  font-weight: 400;
  color: #ffffff; cursor: pointer; border: none; background: none;
  padding: 0.45rem 0; transition: color 0.2s, opacity 0.2s;
}
.overlay .nav-btn:hover,
.overlay .close-btn:hover { color: var(--accent); }
.overlay .nav-btn:disabled { opacity: 0.25; cursor: default; }
.overlay .close-btn {
  padding-left: 1.25rem;
  border-left: 1px solid rgba(201, 185, 154, 0.18);
}
/* Swipe stage: container for the two side-by-side iframes during a
   swipe gesture. Only one iframe (#overlay-frame) exists at rest; a
   second (#overlay-frame-next) is spun up on touchstart, sits offscreen
   at translateX(100%) or -100%, and is destroyed after the gesture
   resolves. This keeps memory cost to 1 iframe except during the
   ~300ms window of an active swipe. */
.overlay-stage {
  position: relative;
  flex: 1;
  overflow: hidden;
  /* Background matches the EXACT color the iframe's Three.js scene
     clears to (#d0ccc3, set in template.html via scene.background).
     Previously this was var(--bg-warm) (#d4d0c7) which is the CSS body
     background — but that body background is hidden behind the full-
     viewport WebGL canvas inside the iframe. The visible color inside
     each iframe is the canvas clear color, not the body color. They
     differ subtly (4 vs 0 in red; 2 in green) and that subtle diff
     was readable as a slight color mismatch in the swipe gap. */
  background: #d0ccc3;
  /* touch-action: none — block ALL browser-level touch handling on
     the stage and its descendants. Critical because iOS Safari
     resolves touch-action by walking from the touched element
     outward, taking the innermost declaration as authoritative.
     If iframe or stage had any "pan" allowance, that's the
     declaration iOS reads — even though .overlay (their ancestor)
     says "none." The browser was opting back into pinch
     interpretation via the iframe's permissive touch-action.
     Note: this does NOT block JS-handled events from being
     dispatched. Our gesturestart/touchmove handlers inside the
     iframe still fire and still run camera-z. */
  touch-action: none;
}
.overlay-stage iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  border: none;
  /* Match the Three.js scene clear color exactly — see stage comment. */
  background: #d0ccc3;
  will-change: transform;
  /* Matching touch-action: none — see .overlay-stage comment above. */
  touch-action: none;
}
.overlay-stage iframe.swiping {
  /* No transition while finger is down — preserved CSS in case we
     restore swipe later (the slide animation needs this class). */
  transition: none;
}
.overlay-stage iframe.releasing {
  /* Spring-to-rest after touchend — preserved CSS, see .swiping above. */
  transition: transform 0.28s cubic-bezier(0.22, 1, 0.36, 1);
}

/* .overlay-edge / .zones-passthrough CSS removed alongside the swipe
   IIFE. Will be restored when we re-implement swipe (likely with a
   different architecture — two-finger gesture, or a smaller-window
   overlay where the iframe doesn't sit at fullscreen). */

/* Page indicator dots — bottom-center, dim except for the active dot.
   Tappable for direct navigation. Flanked by small arrow buttons for
   users who don't discover the edge-swipe gesture (also reinforces
   that the navigation exists). Lives outside the stage so swipe
   transforms don't carry it. */
.overlay-dots {
  position: absolute;
  /* 2.8rem (+ safe-area inset) — moved up from 1.2rem so dots/arrows
     sit comfortably below the iframe's VIEW ON YOUR WALL button (at
     5.2rem) without being crowded against Safari's bottom status bar
     and home indicator. */
  bottom: calc(3.8rem + env(safe-area-inset-bottom, 0px));
  left: 0;
  right: 0;
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0.55rem;
  z-index: 5;
  pointer-events: none;     /* container is inert; children opt in */
}
.overlay-dots .dot {
  pointer-events: auto;
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  border: 0;
  padding: 0;
  background: rgba(255, 255, 255, 0.28);
  cursor: pointer;
  transition: background 0.25s ease, transform 0.25s ease;
}
.overlay-dots .dot:hover { background: rgba(255, 255, 255, 0.55); }
.overlay-dots .dot.active {
  background: var(--accent);
  transform: scale(1.4);
}
.overlay-dots .dot:focus-visible {
  outline: 1px solid var(--accent);
  outline-offset: 3px;
}
/* Arrow buttons on either side of the dot strip — small affordances
   for users who don't discover the edge-swipe gesture. Sized larger
   than just-visual to give a comfortable tap target on mobile. */
.overlay-dots .arrow {
  pointer-events: auto;
  background: none;
  border: 0;
  color: rgba(255, 255, 255, 0.75);
  font-family: "Newsreader", serif;
  font-weight: 200;
  font-size: 2.6rem;
  line-height: 1;
  padding: 0.4rem 1.2rem;
  cursor: pointer;
  transition: color 0.25s ease, transform 0.18s ease;
  margin: 0 0.6rem;
}
.overlay-dots .arrow:hover { color: rgba(255, 255, 255, 1); }
.overlay-dots .arrow:active { transform: scale(0.92); }
.overlay-dots .arrow:disabled {
  opacity: 0.25;
  cursor: default;
}
.overlay-dots .arrow:focus-visible {
  outline: 1px solid var(--accent);
  outline-offset: 3px;
  color: rgba(255, 255, 255, 1);
}
/* Position counter — replaces the dot strip when assets[] is larger
   than DOT_THRESHOLD in gallery-overlay.js (currently 25). Catalog
   browse can have 282 visible pieces; a dot per piece would overflow.
   The counter sits centered between the arrows, matching the dot row's
   editorial voice (Newsreader italic, muted). */
.overlay-dots .overlay-position {
  pointer-events: none;
  color: rgba(255, 255, 255, 0.85);
  font-family: "Newsreader", serif;
  font-style: italic;
  font-size: 1.05rem;
  letter-spacing: 0.05em;
  padding: 0 0.4rem;
  min-width: 6rem;
  text-align: center;
}

/* On small mobile screens (with 20 dots), shrink the row so it fits and
   doesn't crowd the bottom edge. */
@media (max-width: 640px) {
  .overlay-dots { gap: 0.35rem; bottom: calc(3.4rem + env(safe-area-inset-bottom, 0px)); }
  .overlay-dots .dot { width: 0.42rem; height: 0.42rem; }
  .overlay-dots .arrow { font-size: 1.8rem; margin: 0 0.35rem; padding: 0.35rem 0.85rem; }
}

.overlay-window iframe {
  flex: 1; border: none; width: 100%;
  background: var(--bg-dark);
}
.mobile-close {
  display: none;
  position: fixed; top: 1rem; right: 1rem; z-index: 300;
  width: 2.4rem; height: 2.4rem;
  background: rgba(20, 18, 16, 0.92);
  color: var(--fg);
  border: 1px solid rgba(201, 185, 154, 0.25);
  border-radius: 50%;
  font-size: 1.2rem; line-height: 1;
  cursor: pointer;
  align-items: center; justify-content: center;
}
@media (max-width: 768px) {
  .overlay-window { width: 100vw; height: 100vh; border-radius: 0; box-shadow: none; }
  .overlay-header { display: none; }
  .qr-btn-gallery { display: none; }
  .mobile-close { display: flex; }
}
