/*
 * threed.css — chrome for gallery3d.js and viewer3d.js.
 * Owner: threed.
 *
 * Brand tokens are read via var(--bmd-*, <locked fallback>) so this file
 * is correct standalone (as in gallerylab.html/viewerlab.html, which don't
 * load tokens.css) and automatically inherits the real tokens once
 * tokens.css (type lane) is present on the production pages — no edits
 * needed here either way. Fallback values are the exact locked hex/rgba
 * values from docs/BRIEF.md, scoped to these components only (not :root),
 * so this file can never fight tokens.css for the global cascade.
 */

.gallery3d-root, .viewer3d-root {
  --_void: var(--bmd-void, #08090a);
  --_carbon: var(--bmd-carbon, #0e1012);
  --_graphite: var(--bmd-graphite, #16191c);
  --_ash: var(--bmd-ash, #2a2f34);
  --_hairline: var(--bmd-hairline, rgba(255,255,255,0.09));
  --_steel: var(--bmd-steel, #6b7278);
  --_mist: var(--bmd-mist, #a8afb5);
  --_white: var(--bmd-white, #f2f4f5);
  --_red: var(--bmd-red, #ed1c24);
  --_red-deep: var(--bmd-red-deep, #b4141a);
  --_red-glow: var(--bmd-red-glow, rgba(237,28,36,0.35));
  --_mono: var(--bmd-font-mono, ui-monospace, 'SFMono-Regular', Menlo, Consolas, monospace);
  --_sans: var(--bmd-font-sans, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif);

  position: relative;
  width: 100%;
  /* No `height` here on purpose. The host page (.mount3d / .mount3d--full
     in components.css) is what sets real sizing (100svh, a clamp(), etc.),
     and this selector has the exact same specificity as those class rules —
     this file loading after components.css in every page's <head> would
     otherwise let a stray `height: 100%` here win the cascade tie and
     collapse the mount to whatever "auto" resolves to (the <canvas>'s own
     intrinsic 300x150 aspect ratio stretched to full width, clamped by
     min-height below), which is exactly what was happening: ~720px on a
     1440-wide desktop mount3d--full instead of the intended full 100svh,
     and the bare 320px min-height floor on a 375px phone instead of 812px.
     The standalone lab pages (gallerylab.html/viewerlab.html) still size
     correctly without this: their mount is `#stage`, an ID selector that
     already out-specifies anything here. */
  min-height: 320px;
  background: var(--_void);
  overflow: hidden;
  color: var(--_white);
  font-family: var(--_sans);
  -webkit-tap-highlight-color: transparent;
}
.gallery3d-root:focus-visible, .viewer3d-root:focus-visible {
  outline: 1px solid var(--_red);
  outline-offset: -1px;
}

.gallery3d-canvas, .viewer3d-canvas {
  display: block;
  width: 100%;
  height: 100%;
  touch-action: none;
  cursor: grab;
}
.gallery3d-canvas:active { cursor: grabbing; }

.gallery3d-overlay, .viewer3d-overlay {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.gallery3d-overlay > *, .viewer3d-overlay > * { pointer-events: auto; }

/* ------------------------------------------------------------- sr-only */
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0,0,0,0);
  white-space: nowrap;
  border: 0;
}

/* ------------------------------------------------------------ loading */
.gallery3d-loading, .viewer3d-loading {
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 2px;
  background: var(--_hairline);
  pointer-events: none;
  overflow: hidden;
}
.gallery3d-loading span, .viewer3d-loading span {
  position: absolute;
  inset: 0;
  width: 40%;
  background: linear-gradient(90deg, transparent, var(--_red), transparent);
  animation: bmd3d-scan 1.1s cubic-bezier(0.16,1,0.3,1) infinite;
}
@keyframes bmd3d-scan {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(350%); }
}
.viewer3d-loading.is-error { background: var(--_red-deep); height: auto; padding: 0.6em 1em; font: 11px/1.4 var(--_mono); text-transform: uppercase; letter-spacing: 0.06em; }

/* -------------------------------------------------------- hover caption */
.gallery3d-hover-caption {
  position: absolute;
  left: 0; top: 0;
  display: flex;
  align-items: center;
  gap: 0.6em;
  transform: translate(-9999px,-9999px);
  margin-top: 14px;
  padding: 0.4em 0.7em;
  background: rgba(8,9,10,0.72);
  border: 1px solid var(--_hairline);
  backdrop-filter: blur(6px);
  font-size: 11px;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  opacity: 0;
  transition: opacity 0.25s cubic-bezier(0.16,1,0.3,1);
  white-space: nowrap;
}
.gallery3d-hover-caption.is-visible { opacity: 1; }
.gallery3d-hover-caption .tag {
  font-family: var(--_mono);
  font-style: normal;
  color: var(--_red);
  font-size: 10px;
}
.gallery3d-hover-caption .text { font-weight: 600; color: var(--_white); }

/* --------------------------------------------------------- detail view */
/* [hidden] must win over the `display: flex` below, or this full-viewport
   (inset:0) panel stays hit-testable (opacity:0 doesn't remove it from
   hit-testing) and silently eats every pointerdown meant for the canvas
   underneath, at all times, not just while a tile is open — nothing in
   this codebase sets a global `[hidden]{display:none}` reset, so this
   author rule was the one unconditionally overriding the browser default.
   Same reasoning applies to .gallery3d-fallback-lightbox further down. */
.gallery3d-detail[hidden] { display: none; }
.gallery3d-detail {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  padding: 0 20px 28px;
  /* Keep the middle clear: the opened photo sits there, and a 35% black veil
     over it made the one image the user asked to see the dimmest thing on
     screen. The surrounding wall is dimmed in the 3D scene instead. */
  background: radial-gradient(ellipse at center, rgba(8,9,10,0) 0%, rgba(8,9,10,0) 52%, rgba(8,9,10,0.62) 78%, rgba(8,9,10,0.92) 100%);
  opacity: 0;
  transition: opacity 0.5s cubic-bezier(0.16,1,0.3,1);
}
.gallery3d-detail.is-open { opacity: 1; }
.gallery3d-detail-frame { flex: 1; width: 100%; }
.gallery3d-detail-info {
  display: flex;
  align-items: baseline;
  gap: 1em;
  max-width: 640px;
  border-top: 1px solid var(--_hairline);
  padding-top: 0.8em;
  transform: translateY(10px);
  opacity: 0;
  transition: transform 0.6s cubic-bezier(0.16,1,0.3,1) 0.15s, opacity 0.6s cubic-bezier(0.16,1,0.3,1) 0.15s;
}
.gallery3d-detail.is-open .gallery3d-detail-info { transform: translateY(0); opacity: 1; }
.gallery3d-detail-info .tag { font-family: var(--_mono); color: var(--_red); font-size: 11px; font-style: normal; flex: none; }
.gallery3d-detail-info .caption { margin: 0; font-size: 13px; color: var(--_mist); letter-spacing: 0.01em; }
.gallery3d-close {
  position: absolute;
  top: 16px; right: 16px;
  width: 38px; height: 38px;
  display: grid; place-items: center;
  background: transparent;
  border: 1px solid var(--_hairline);
  color: var(--_white);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.2s, color 0.2s, transform 0.2s;
}
.gallery3d-close:hover { border-color: var(--_red); color: var(--_red); }
.gallery3d-close:focus-visible { outline: 1px solid var(--_red); outline-offset: 2px; }

/* -------------------------------------------------------- fallback grid */
.gallery3d-fallback-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 2px;
  width: 100%;
  height: 100%;
  overflow-y: auto;
  padding: 2px;
}
.gallery3d-fallback-item { margin: 0; position: relative; background: var(--_graphite); }
.gallery3d-fallback-btn {
  display: block;
  width: 100%; padding: 0; border: 0; background: none; cursor: pointer;
  aspect-ratio: 4 / 3;
  overflow: hidden;
}
.gallery3d-fallback-btn img { width: 100%; height: 100%; object-fit: cover; display: block; filter: grayscale(0.1); transition: transform 0.6s cubic-bezier(0.16,1,0.3,1), filter 0.6s; }
.gallery3d-fallback-btn:hover img, .gallery3d-fallback-btn:focus-visible img { transform: scale(1.04); filter: grayscale(0); }
.gallery3d-fallback-btn:focus-visible { outline: 1px solid var(--_red); outline-offset: -2px; }
.gallery3d-fallback-item figcaption {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 0.5em 0.6em;
  font-size: 10.5px; letter-spacing: 0.03em; text-transform: uppercase;
  background: linear-gradient(0deg, rgba(0,0,0,0.75), transparent);
  color: var(--_mist);
  pointer-events: none;
}
.gallery3d-fallback-lightbox[hidden] { display: none; }
.gallery3d-fallback-lightbox {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: rgba(5,6,7,0.92);
  padding: 40px 20px;
}
.gallery3d-fallback-lightbox figure { margin: 0; max-width: 100%; max-height: 100%; text-align: center; }
.gallery3d-fallback-lightbox img { max-width: 100%; max-height: 70vh; display: block; margin: 0 auto; }
.gallery3d-fallback-lightbox figcaption { margin-top: 12px; font-size: 12px; color: var(--_mist); text-transform: uppercase; letter-spacing: 0.04em; }

/* ===================================================== viewer3d specific */

.viewer3d-hotspots { position: absolute; inset: 0; pointer-events: none; }
.viewer3d-hotspot {
  position: absolute; left: 0; top: 0;
  transition: opacity 0.2s ease;
}
.viewer3d-hotspot.is-hidden, .viewer3d-hotspot.is-occluded { opacity: 0; pointer-events: none; }
.viewer3d-hotspot-dot {
  position: absolute; left: -6px; top: -6px;
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--_void);
  border: 1.5px solid var(--_red);
  box-shadow: 0 0 0 3px var(--_red-glow);
  cursor: pointer;
  pointer-events: auto;
  padding: 0;
}
.viewer3d-hotspot-dot::after {
  content: ''; position: absolute; inset: 3px; border-radius: 50%; background: var(--_red);
}
.viewer3d-hotspot.is-active .viewer3d-hotspot-dot { box-shadow: 0 0 0 5px var(--_red-glow); }
.viewer3d-hotspot-leader {
  position: absolute; left: 5px; top: -18px;
  width: 1px; height: 24px;
  background: var(--_hairline);
  transform: rotate(35deg);
  transform-origin: top;
}
.viewer3d-hotspot-label {
  position: absolute; left: 20px; top: -40px;
  padding: 0.3em 0.6em;
  background: rgba(8,9,10,0.78);
  border: 1px solid var(--_hairline);
  font-family: var(--_mono);
  font-size: 10px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  white-space: nowrap;
  pointer-events: auto;
  cursor: pointer;
}
.viewer3d-hotspot.is-active .viewer3d-hotspot-label { border-color: var(--_red); color: var(--_white); }

.viewer3d-spec-panel {
  position: absolute;
  left: 16px; bottom: 16px;
  max-width: min(320px, calc(100% - 32px));
  padding: 1em 1.1em;
  background: rgba(14,16,18,0.88);
  border: 1px solid var(--_hairline);
  backdrop-filter: blur(8px);
}
.viewer3d-spec-panel .tag { display: block; font-family: var(--_mono); font-style: normal; color: var(--_red); font-size: 10px; letter-spacing: 0.08em; margin-bottom: 0.4em; }
.viewer3d-spec-panel .title { margin: 0 0 0.3em; font-size: 15px; letter-spacing: 0.01em; }
.viewer3d-spec-panel .material { margin: 0 0 0.35em; font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--_mist); }
.viewer3d-spec-panel .note { margin: 0; font-size: 12px; line-height: 1.4; color: var(--_steel); }
.viewer3d-spec-close {
  position: absolute; top: 8px; right: 8px;
  width: 22px; height: 22px; display: grid; place-items: center;
  background: none; border: 0; color: var(--_steel); font-size: 16px; cursor: pointer;
}
.viewer3d-spec-close:hover { color: var(--_red); }

.viewer3d-controls {
  position: absolute;
  right: 16px; top: 16px;
  display: flex;
  flex-direction: column;
  gap: 0.7em;
  padding: 0.9em 1em;
  background: rgba(14,16,18,0.72);
  border: 1px solid var(--_hairline);
  backdrop-filter: blur(8px);
  min-width: 180px;
  font-family: var(--_mono);
  font-size: 10.5px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.viewer3d-layers { display: flex; flex-wrap: wrap; gap: 0.4em; }
.viewer3d-layer-chip {
  padding: 0.35em 0.6em;
  background: transparent;
  border: 1px solid var(--_ash);
  color: var(--_steel);
  cursor: pointer;
  font: inherit;
  transition: border-color 0.15s, color 0.15s;
}
.viewer3d-layer-chip.is-active { border-color: var(--_red); color: var(--_white); }
.viewer3d-slider-row { display: flex; align-items: center; justify-content: space-between; gap: 0.8em; color: var(--_mist); }
.viewer3d-slider-row input[type="range"] {
  flex: 1;
  accent-color: var(--_red);
  height: 2px;
  background: var(--_ash);
}
.viewer3d-toggle-row { display: flex; gap: 0.4em; flex-wrap: wrap; }
.viewer3d-btn {
  flex: 1;
  padding: 0.5em 0.5em;
  background: transparent;
  border: 1px solid var(--_ash);
  color: var(--_mist);
  cursor: pointer;
  font: inherit;
  white-space: nowrap;
  transition: border-color 0.15s, color 0.15s;
}
.viewer3d-btn:hover { color: var(--_white); }
.viewer3d-btn.is-active { border-color: var(--_red); color: var(--_red); }

/* ------------------------------------------------------- viewer fallback */
.viewer3d-fallback-panel {
  position: absolute; inset: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 0.6em;
  padding: 32px 20px;
  text-align: center;
}
.viewer3d-fallback-art { position: relative; width: 140px; height: 140px; margin-bottom: 0.6em; }
.viewer3d-fallback-art .ring { position: absolute; inset: 0; border-radius: 50%; border: 2px solid var(--_ash); }
.viewer3d-fallback-art .ring-b { inset: 22px; border-color: var(--_red-deep); }
.viewer3d-fallback-art .hub { position: absolute; inset: 58px; border-radius: 50%; background: var(--_graphite); border: 1px solid var(--_hairline); }
.viewer3d-fallback-panel .tag { font-family: var(--_mono); color: var(--_red); font-size: 11px; letter-spacing: 0.08em; }
.viewer3d-fallback-panel h3 { margin: 0; font-size: 20px; }
.viewer3d-fallback-panel .note { margin: 0; font-size: 12px; color: var(--_steel); text-transform: uppercase; letter-spacing: 0.04em; }
.viewer3d-fallback-list {
  list-style: none;
  margin: 0.8em 0 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 1px;
  background: var(--_hairline);
  max-width: 780px;
  width: 100%;
  text-align: left;
}
.viewer3d-fallback-list li { background: var(--_carbon); padding: 0.8em 1em; }
.viewer3d-fallback-list b { display: block; font-size: 12px; margin-bottom: 0.2em; }
.viewer3d-fallback-list span { display: block; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--_mist); margin-bottom: 0.3em; }
.viewer3d-fallback-list p { margin: 0; font-size: 11.5px; line-height: 1.4; color: var(--_steel); }

/* ------------------------------------------------------------ responsive */
@media (max-width: 480px) {
  .viewer3d-controls { left: 12px; right: 12px; top: auto; bottom: 12px; min-width: 0; }
  .gallery3d-detail { padding: 0 12px 16px; }
  .viewer3d-spec-panel { left: 12px; right: 12px; max-width: none; bottom: 100px; }
}

/* -------------------------------------------------- reduced motion, global */
@media (prefers-reduced-motion: reduce) {
  .gallery3d-loading span, .viewer3d-loading span { animation: none; }
  .gallery3d-detail, .gallery3d-detail-info, .gallery3d-hover-caption,
  .gallery3d-fallback-btn img { transition-duration: 0.001s !important; }
}
