/* RÜM Cinematic Architecture Engine — reproductor (prototipo Fase 0)
   Estética: A24 / Apple TV+, quiet luxury. La fotografía manda; el chrome desaparece.
   Un solo acento (durazno). Sin negro puro. Motion motivado (Ken Burns + crossfade). */

:root {
  --bg:        #14110d;          /* warm near-black, nunca #000 */
  --bar:       #0e0b08;          /* barras de cine, cálido */
  --ink:       #f3ece0;          /* off-white cálido */
  --muted:     rgba(243, 236, 224, 0.60);
  --accent:    #f3d2b2;          /* DURAZNO — acento único, lockeado */
  --accent-dim: rgba(243, 210, 178, 0.22);
  --scrim:     rgba(12, 9, 6, 0.55);
  --ease:      cubic-bezier(0.16, 1, 0.3, 1);
  --xfade:     900ms;            /* crossfade entre escenas */
  /* Outfit: geométrica editorial — chrome de cine, no system-ui genérico */
  --font: "Outfit", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --rum-font: "Baloo 2", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  /* Marca RÜM: siempre Baloo 2 (aunque el skin cambie --rum-font a serif). */
  --rum-brand-font: "Baloo 2", ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  --ui-tracking: 0.06em;
  --ui-tracking-wide: 0.16em;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}

/* ---------- ETAPA ---------- */
.stage {
  position: fixed;
  inset: 0;
  min-height: 100dvh;
  overflow: hidden;
  cursor: default;
  background: transparent;
}

.frame {
  position: absolute;
  inset: 0;
  overflow: hidden;
  opacity: 0;
  transition: opacity var(--xfade) var(--ease);
}
.frame.is-active { opacity: 1; }

.frame-zoom {
  position: absolute;
  inset: 0;
  transform-origin: center center;
  will-change: transform;
}

.frame-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
  pointer-events: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* Clip de video encima del poster (cuando existe el .mp4) */
.frame-video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 700ms var(--ease);
  pointer-events: none;
}
.frame-video.is-on { opacity: 1; }

/* Canvas: mirror del video cuando force-landscape (iOS no pinta <video> bajo transform) */
.frame-canvas {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  pointer-events: none;
  /* cover vía JS size; el canvas se estira al contenedor */
}
.frame-canvas.is-on { opacity: 1; }
html.force-landscape .frame-video.is-on { opacity: 0 !important; }
html.force-landscape .frame-canvas.is-on { opacity: 1; }

/* Móvil: cover fullscreen (cine edge-to-edge).
   Los hotspots se alinean al rectángulo cover vía syncHotspotLayer() en JS.
   contain dejaba franjas negras + barras anamórficas = layout raro en portrait. */
@media (max-width: 640px) {
  .frame-img,
  .frame-video,
  .frame-canvas {
    object-fit: cover;
    object-position: center center;
  }
  .frame.is-active .frame-zoom.kb {
    animation: none;
  }
}

/* Desktop + proyecto vertical (Casa Tizón): columna 9:16 centrada,
   resto negro. No estirar el metraje móvil a todo el monitor. */
@media (min-width: 641px) {
  html.prefer-portrait,
  html.prefer-portrait body {
    background: #000 !important;
  }
  html.prefer-portrait .stage {
    background: #000 !important;
  }
  html.prefer-portrait .frame {
    inset: auto !important;
    top: 50% !important;
    left: 50% !important;
    right: auto !important;
    bottom: auto !important;
    width: min(100vw, calc(100dvh * 9 / 16)) !important;
    height: min(100dvh, calc(100vw * 16 / 9)) !important;
    max-width: 100vw;
    max-height: 100dvh;
    transform: translate(-50%, -50%);
    background: #000;
  }
  html.prefer-portrait .frame-img,
  html.prefer-portrait .frame-video,
  html.prefer-portrait .frame-canvas {
    object-fit: cover;
    object-position: center center;
  }
  html.prefer-portrait .frame.is-active .frame-zoom.kb {
    animation: none !important;
  }
  html.prefer-portrait .vignette {
    inset: auto !important;
    top: 50% !important;
    left: 50% !important;
    right: auto !important;
    bottom: auto !important;
    width: min(100vw, calc(100dvh * 9 / 16)) !important;
    height: min(100dvh, calc(100vw * 16 / 9)) !important;
    transform: translate(-50%, -50%);
    opacity: 0.4;
  }
  html.prefer-portrait .bar {
    display: none !important;
  }
}

/* Ken Burns — movimiento ambiental sutil, motivado (simula el hero vivo) */
@media (prefers-reduced-motion: no-preference) {
  .frame.is-active .frame-zoom.kb {
    animation: kenburns 26s var(--ease) forwards alternate;
  }
}
@keyframes kenburns {
  from { transform: scale(1.06) translate(1.2%, -0.6%); }
  to   { transform: scale(1.14) translate(-1.4%, 0.8%); }
}

/* Vignette cálida — profundidad filmica, no borde duro */
.vignette {
  position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(120% 90% at 50% 42%, transparent 55%, rgba(10,8,5,0.42) 100%),
    linear-gradient(to bottom, rgba(10,8,5,0.28) 0%, transparent 22%, transparent 62%, rgba(10,8,5,0.55) 100%);
}

/* Barras anamórficas — cálidas, delgadas (no comen la foto). */
:root { --bar-h: 5.2vh; }
.bar {
  position: absolute; left: 0; right: 0; height: var(--bar-h);
  background: var(--bar); pointer-events: none; z-index: 5;
}
.bar--top { top: 0; }
.bar--bottom { bottom: 0; }

/* ---------- APERTURA DE CINE ----------
   Al levantarse la cortina el cuadro se encuadra solo: las barras entran desde
   cero y la imagen cede un pelo de escala, como una cámara que asienta el tiro.
   Una sola pasada — `is-opening` se retira al terminar, y de ahí en adelante el
   cuadro es el de siempre. El recorte lo hace `.stage { overflow: hidden }`, así
   que la escala de más no asoma por los bordes.

   Las barras entran deslizándose desde fuera del cuadro, no creciendo de alto:
   son rectángulos macizos, así que se ve igual, pero animar `height` obliga a
   recalcular y repintar en cada cuadro — encima de un clip de 1080p arrancando,
   eso es justo el tirón que se siente al abrir. */
html.is-opening .bar--top { animation: openingBarTop 1400ms var(--ease) 120ms both; }
html.is-opening .bar--bottom { animation: openingBarBottom 1400ms var(--ease) 120ms both; }
html.is-opening .frame {
  animation: openingFrame 2200ms var(--ease) both;
  /* Sólo durante la apertura: si la capa se promueve a mitad de la animación,
     el salto se ve. */
  will-change: transform;
}
html.is-opening .vignette { animation: openingVignette 1800ms var(--ease) both; }
@keyframes openingBarTop {
  from { transform: translateY(-100%); }
  to   { transform: none; }
}
@keyframes openingBarBottom {
  from { transform: translateY(100%); }
  to   { transform: none; }
}
@keyframes openingFrame {
  from { transform: scale(1.055); }
  to   { transform: none; }
}
@keyframes openingVignette {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  html.is-opening .bar,
  html.is-opening .frame,
  html.is-opening .vignette { animation: none; }
}

/* ---------- GALERÍA / IMAGES: foto de espacio nítida ----------
   El recorrido (video) sigue a `cover`. Las stills de Images no: un poster
   1660×934 en un monitor 1920×1080 con cover se amplía ~16% y se ve blando;
   un vertical 1122×1402 además perdía más de la mitad del encuadre.

   `scale-down` cabe la foto entera y nunca la pinta más grande que sus
   píxeles nativos — se ve más pequeña, pero nítida. El frame se mete dentro
   de la banda anamórfica para que las barras no muerdan la imagen.
   `background-image: none` porque prepareBackBuffer() lo escribe inline sin
   background-size y, sin limpiarlo, mosaicaría junto al <img>.
   La clase la pone applyGalleryFit() al abrir/cambiar la vista. */
.stage.is-gallery.is-gallery-contain .frame,
.stage.is-still-contain .frame {
  top: 5.2vh;
  bottom: 5.2vh;
  background-image: none !important;
}
.stage.is-gallery.is-gallery-contain .frame-img,
.stage.is-still-contain .frame-img {
  object-fit: scale-down;
}

/* Medio que no da los píxeles del cuadro: se encaja, con barras, en vez de
   estirarse. `cover` lo ampliaba —1608 px de clip repartidos en 1900 de
   monitor— y eso no gana encuadre, sólo pierde nitidez. En vertical además
   recortaba cerca del 60% del alto y tiraba la fila de hotspots fuera de
   pantalla. La clase la pone syncHotspotLayer(), que es donde ya se conocen
   las dimensiones naturales. */
.stage.is-fit-media .frame {
  top: 5.2vh;
  bottom: 5.2vh;
  background-image: none !important;
}
.stage.is-fit-media .frame-video,
.stage.is-fit-media .frame-img,
.stage.is-fit-media .frame-canvas {
  /* `contain` aún agranda: un clip de 1660 px en un monitor de 1920 se
     estira y se ve blando. `scale-down` cabe el cuadro entero y no pasa
     de sus píxeles nativos — 100 %, sin pérdida de nitidez. */
  object-fit: scale-down;
}

/* El poster de la toma no se queda debajo del clip mientras el clip corre.
   Casi nunca traen los mismos píxeles nativos —en Cabo, 01.png es 1536×1024 y
   entrance_hero.mp4 1176×784— y con `scale-down` cada uno se pinta a su tamaño:
   en un monitor ancho la foto salía encajada a 1279 px y el video se quedaba en
   sus 1176 nativos, así que la foto asomaba como un marco alrededor del clip y
   el recorrido se leía como dos videos superpuestos. Con el clip encendido la
   still no aporta nada (es el mismo cuadro): se apaga con la misma curva con la
   que él entra, así la transición sigue siendo un fundido y no un corte.

   Fuera de `force-landscape`: ahí el <video> va oculto a propósito y quien pinta
   es .frame-canvas, que puede tardar un cuadro en encenderse; apagar la foto
   antes dejaría el hueco en negro. */
html:not(.force-landscape) .stage.is-fit-media .frame-zoom:has(> .frame-video.is-on) .frame-img {
  opacity: 0;
  transition: opacity 700ms var(--ease);
}

/* ---------- MÓVIL / TABLET: EL CUADRO ES LA PANTALLA ----------
   En escritorio el encaje es la regla buena: el monitor tiene más píxeles que
   el clip y estirarlo sólo lo ablanda. En el teléfono pasa lo contrario —la
   pantalla tiene menos píxeles CSS que el medio— y el encaje deja el
   recorrido en una estampilla: un clip 4:3 dentro de un cuadro de 812×291
   ocupaba el 48 % del ancho, con el resto en negro.

   El recorrido va a sangre (el `cover` por defecto de .frame-img / .frame-video,
   que aquí no amplía nada). Las stills y la galería siguen encajadas —quien
   abre Images quiere la foto entera, no un recorte— pero sin las bandas de
   5.2vh: en móvil esos 84 px salen del lado corto y son casi un tercio del
   cuadro. La clase la pone applyMediaFit() (gate: `pointer: coarse`). */
html.is-media-fill .stage.is-gallery.is-gallery-contain .frame,
html.is-media-fill .stage.is-still-contain .frame {
  top: 0;
  bottom: 0;
}

/* ---------- INTRO TITLE CARD ---------- */
/* Aparece una vez, sobre el hero de entrada (HUD sigue visible debajo). */
.intro {
  position: fixed; inset: 0; z-index: 52;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  text-align: center;
  /* Sobre las losas / huellas del piso en el hero de entrada */
  padding-bottom: clamp(88px, 16vh, 140px);
  pointer-events: none;
  opacity: 1;
  transition: opacity 700ms var(--ease);
}
.intro.is-out { opacity: 0; }

.intro-inner { position: relative; padding: 48px 64px; }
.intro-inner::before {
  content: "";
  position: absolute; inset: -30% -40%;
  z-index: -1;
  background: radial-gradient(58% 58% at 50% 50%, rgba(10, 8, 5, 0.46) 0%, transparent 72%);
  opacity: 0;
  transform: scale(0.92);
  transition: opacity 900ms var(--ease), transform 1.1s var(--ease);
}
.intro.is-in .intro-inner::before {
  opacity: 1;
  transform: scale(1);
}

.intro-line,
.intro-rule,
.intro-by {
  opacity: 0;
  transform: translateY(22px);
  filter: blur(8px);
}

.intro-line {
  font-family: var(--font);
  font-size: 24px;
  font-weight: 500;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--ink);
  text-shadow: 0 2px 20px rgba(0, 0, 0, 0.45);
}
.intro.is-in .intro-line {
  animation: introReveal 1.15s var(--ease) 0.08s forwards;
}

.intro-rule {
  width: 42px; height: 1px; margin: 18px auto;
  background: var(--accent);
  transform-origin: center;
}
.intro.is-in .intro-rule {
  animation: introRuleReveal 1s var(--ease) 0.42s forwards;
}

.intro-by {
  font-family: var(--font);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--muted);
}
.intro.is-in .intro-by {
  animation: introReveal 1s var(--ease) 0.72s forwards;
}
.intro-by strong {
  font-family: var(--rum-brand-font);
  color: var(--accent);
  font-weight: 700;
  letter-spacing: 0.04em;
}

/* Los dos arranques posibles, hombro con hombro: empezar el recorrido, o
   saltar directo a un espacio. */
.intro-actions {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  flex-wrap: wrap;
}
.intro-start {
  display: none;
  margin: 22px 0 0;
  font-family: var(--font);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-ink, #1a1206);
  background: var(--accent);
  border: 0;
  border-radius: 999px;
  padding: 12px 22px;
  cursor: pointer;
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 500ms var(--ease), transform 500ms var(--ease), filter 200ms var(--ease);
}
.intro-start:not([hidden]) { display: inline-flex; align-items: center; justify-content: center; }
.intro.is-in .intro-start {
  opacity: 1;
  transform: translateY(0);
  transition-delay: 520ms;
}
.intro-start:hover { filter: brightness(1.06); }
.intro.is-clickable {
  pointer-events: auto;
  cursor: pointer;
}
.intro.is-clickable .intro-inner { pointer-events: auto; }

.intro.is-out .intro-line,
.intro.is-out .intro-rule,
.intro.is-out .intro-by,
.intro.is-out .intro-start {
  animation: none;
  opacity: 0;
  transform: translateY(-10px);
  filter: blur(4px);
  transition: opacity 520ms var(--ease), transform 520ms var(--ease), filter 520ms var(--ease);
}
.intro.is-out .intro-inner::before {
  opacity: 0;
  transform: scale(1.04);
}

@keyframes introReveal {
  from { opacity: 0; transform: translateY(22px); filter: blur(8px); }
  to   { opacity: 1; transform: translateY(0); filter: blur(0); }
}
@keyframes introRuleReveal {
  from { opacity: 0; transform: translateY(22px) scaleX(0); filter: blur(8px); }
  to   { opacity: 0.85; transform: translateY(0) scaleX(1); filter: blur(0); }
}

@media (max-width: 640px) {
  .intro { padding-bottom: clamp(72px, 14vh, 110px); }
  .intro-line { font-size: 18px; letter-spacing: 0.22em; }
  .intro-by { font-size: 11px; letter-spacing: 0.26em; }
  .intro-inner { padding: 32px 24px; }
}
@media (prefers-reduced-motion: reduce) {
  .intro { transition: opacity 200ms linear; }
  .intro-line, .intro-rule, .intro-by { filter: none; transform: none; }
  .intro.is-in .intro-line,
  .intro.is-in .intro-rule,
  .intro.is-in .intro-by { animation: introRevealReduced 400ms linear forwards; }
  .intro.is-in .intro-rule { animation-delay: 120ms; }
  .intro.is-in .intro-by { animation-delay: 240ms; }
  @keyframes introRevealReduced {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
}

/* ---------- HOTSPOTS ---------- */
/* El contenedor es transparente a clics (deja pasar el clic en zona vacía al
   stage → "clic para volver"); sólo los puntos ARMADOS capturan el clic. */
/* inset por defecto; JS ajusta left/top/width/height al rectángulo real
   del media (cover) para que x%/y% del JSON coincidan con la foto. */
.hotspots {
  position: absolute;
  inset: 0;
  z-index: 10;
  pointer-events: none;
  overflow: visible;
}
.hotspots.is-armed .hotspot { pointer-events: auto; }

.hotspot {
  position: absolute;
  transform: translate(-50%, -50%);
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  border: 0;
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  opacity: 0;
  transition: opacity 500ms var(--ease);
}
/* `opacity: 1` es el valor de reposo, no el destino de la animación: si el
   reveal no corre en algún navegador (shorthand + var() dentro de `animation`
   falla en silencio en algunos motores), el botón no puede quedar atorado
   invisible — se ve completo aunque nunca haya animado. `fill-mode: backwards`
   aplica el cuadro `from` solo mientras espera el delay; al terminar, manda
   este valor de reposo, no el `to` de la animación. */
.hotspots.is-armed .hotspot {
  opacity: 1;
  animation-name: hotspotReveal;
  animation-duration: 0.58s;
  animation-timing-function: var(--ease);
  animation-fill-mode: backwards;
}
.hotspots.is-armed .hotspot:nth-child(2) { animation-delay: 0.1s; }
.hotspots.is-armed .hotspot:nth-child(3) { animation-delay: 0.18s; }
.hotspots.is-armed .hotspot:nth-child(4) { animation-delay: 0.26s; }
.hotspots.is-armed .hotspot:nth-child(5) { animation-delay: 0.34s; }

@keyframes hotspotReveal {
  from { opacity: 0; transform: translate(-50%, -50%) scale(0.82); filter: blur(4px); }
  to   { opacity: 1; transform: translate(-50%, -50%) scale(1); filter: blur(0); }
}

.hotspots.is-armed .hotspot .label {
  opacity: 0.92;
  transform: translateY(-50%) translateX(0);
}
.hotspots.is-armed .hotspot[data-side="right"] .label {
  transform: translateY(-50%) translateX(0);
}

.hotspot .dot {
  position: relative;
  width: 13px; height: 13px;
  border-radius: 999px;
  background: var(--accent);
  box-shadow: 0 0 0 6px var(--accent-dim), 0 2px 10px rgba(0,0,0,0.35);
  flex: none;
}
@media (prefers-reduced-motion: no-preference) {
  .hotspot .dot::after {
    content: ""; position: absolute; inset: 0; border-radius: inherit;
    box-shadow: 0 0 0 2px var(--accent);
    animation: pulse 2.6s var(--ease) infinite;
  }
}
@keyframes pulse {
  0%   { transform: scale(1);   opacity: 0.7; }
  70%  { transform: scale(2.6); opacity: 0; }
  100% { transform: scale(2.6); opacity: 0; }
}

.hotspot .label {
  position: absolute;
  top: 50%;
  left: calc(100% + 6px);
  transform: translateY(-50%) translateX(-4px);
  font-family: var(--font);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: var(--ui-tracking);
  line-height: 1.2;
  padding: 7px 13px;
  border-radius: 999px;
  color: var(--ink);
  background: rgba(12, 9, 6, 0.62);
  border: 1px solid rgba(243, 210, 178, 0.22);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.28);
  white-space: nowrap;
  /* La pastilla es el botón visible; el punto es apenas 44-56px. Con
     pointer-events:none aquí, tocar el texto —lo único que se ve claro en
     móvil, donde la etiqueta siempre está abierta— no hacía nada: el toque
     atravesaba hacia el video de abajo. El clic sigue burbujeando al
     <button> padre igual que si se hubiera tocado el punto. */
  pointer-events: auto;
  cursor: pointer;
  opacity: 0;
  transition: opacity 260ms var(--ease), transform 260ms var(--ease);
}
.hotspot:hover .label,
.hotspot:focus-visible .label { opacity: 1; transform: translateY(-50%) translateX(0); }

.hotspot:hover .dot,
.hotspot:focus-visible .dot { transform: scale(1.15); }
.hotspot:focus-visible { outline: none; }
.hotspot:focus-visible .dot { box-shadow: 0 0 0 4px var(--accent), 0 0 0 8px var(--accent-dim); }
.hotspot.is-current .dot { box-shadow: 0 0 0 3px var(--accent); }
.hotspots.is-armed .hotspot.is-current .label { opacity: 1; }

/* Hotspot cerca del borde izquierdo: etiqueta hacia adentro (derecha del dot) */
.hotspot[data-side="right"] .label {
  left: auto;
  right: calc(100% + 6px);
  transform: translateY(-50%) translateX(4px);
}
.hotspot[data-side="right"]:hover .label,
.hotspot[data-side="right"]:focus-visible .label { transform: translateY(-50%) translateX(0); }

/* Hotspot stacked (menú vertical): etiqueta debajo del punto */
.hotspot[data-side="below"] .label {
  left: 50%;
  right: auto;
  top: calc(100% + 6px);
  transform: translateX(-50%) translateY(-4px);
}
.hotspot[data-side="below"]:hover .label,
.hotspot[data-side="below"]:focus-visible .label {
  transform: translateX(-50%) translateY(0);
}
.hotspots.is-armed .hotspot[data-side="below"] .label {
  transform: translateX(-50%) translateY(0);
}

/* ---------- HUD ---------- */
/* absolute (no fixed): en force-landscape / transform del stage los fixed
   se anclan al viewport físico y los botones “desaparecen” o se solapan. */
.hud {
  position: absolute; inset: 0; z-index: 55;
  pointer-events: none;
  transition: opacity 400ms var(--ease);
}
.hud.is-hidden { opacity: 0; }
.hud > * { pointer-events: auto; }

.hud > *:not(.scene-title) {
  opacity: 0;
  transform: translateY(12px);
}
.hud.is-live > *:not([hidden]):not(.scene-title) {
  animation: hudReveal 0.72s var(--ease) forwards;
}
.hud.is-live #playpause { animation-delay: 0.12s; }
.hud.is-live #help { animation-delay: 0.22s; }
.hud.is-live #audio-toggle:not([hidden]) { animation-delay: 0.18s; }
.hud.is-live #back:not([hidden]) { animation-delay: 0.06s; }

.hud.is-live .scene-title {
  animation: hudRevealTitle 0.78s var(--ease) 0.04s forwards;
}

@keyframes hudReveal {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}
@keyframes hudRevealTitle {
  from { opacity: 0; transform: translateX(-50%) translateY(12px); }
  to   { opacity: 1; transform: translateX(-50%) translateY(0); }
}
.hud-pop { animation: hudReveal 0.55s var(--ease) forwards; }

/* Chrome fijo del Showroom (badge + volver al catálogo) —
   SIEMPRE visible: no depende de .is-live ni animaciones.
   Excepción: recorridos privados (html.hide-showroom-back / Nauka). */
/* ---------- REDLINES (marcado sobre el frame) ----------
   La barra vive en la banda negra de arriba: plegada es un solo botón, y abierta
   no tapa el render que se está marcando. El lienzo va por encima de los
   hotspots (z 10) y de las fichas (z 24) —lo que se raya es la imagen completa—
   pero por debajo del HUD (z 55), para no secuestrar sus botones. */
.markup-bar {
  position: absolute;
  z-index: 62;
  top: max(10px, env(safe-area-inset-top));
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 5px;
  padding: 5px;
  border-radius: 999px;
  background: rgba(14, 11, 8, 0.78);
  border: 1px solid rgba(243, 210, 178, 0.2);
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}
.markup-bar button {
  font-family: var(--font);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(243, 236, 224, 0.72);
  background: transparent;
  border: 0;
  border-radius: 999px;
  padding: 8px 13px;
  cursor: pointer;
  white-space: nowrap;
  transition: color 200ms var(--ease), background 200ms var(--ease), box-shadow 200ms var(--ease), filter 200ms var(--ease);
}
.markup-bar button:hover,
.markup-bar button:focus-visible {
  color: var(--ink);
  background: rgba(243, 236, 224, 0.1);
  outline: none;
}
.markup-bar button.is-active {
  color: #1a1206;
  background: var(--accent);
  box-shadow: 0 0 0 1px rgba(243, 210, 178, 0.35);
}
.markup-toggle {
  display: inline-flex !important;
  align-items: center;
  gap: 8px;
  color: #1a1206 !important;
  background: var(--accent) !important;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.18) inset;
  padding: 9px 15px 9px 13px !important;
}
.markup-toggle:hover,
.markup-toggle:focus-visible {
  filter: brightness(1.05);
  color: #1a1206 !important;
  background: var(--accent) !important;
}
.markup-toggle.is-done {
  color: var(--ink) !important;
  background: rgba(243, 236, 224, 0.12) !important;
  box-shadow: none;
  border: 1px solid rgba(243, 236, 224, 0.18);
}
.markup-toggle.is-done:hover,
.markup-toggle.is-done:focus-visible {
  background: rgba(243, 236, 224, 0.18) !important;
  color: var(--ink) !important;
}
.markup-toggle-ico {
  display: inline-flex;
  width: 13px;
  height: 13px;
  color: inherit;
}
.markup-toggle-ico svg { display: block; }
.markup-toggle-label {
  letter-spacing: 0.16em;
}
.markup-bar:not(.is-open) .markup-tool { display: none; }
.markup-sep {
  width: 1px;
  height: 16px;
  background: rgba(243, 210, 178, 0.22);
}
/* Devolver el marcado es LA acción de esta barra: va en durazno, y solo sale
   cuando el recorrido llega dentro de una entrega (hay a qué enlace mandarlo).
   Con el marcado abierto el toggle se vuelve fantasma («Done»), así que este
   es el único botón lleno — no compiten dos primarios. */
.markup-send {
  color: #1a1206 !important;
  background: var(--accent) !important;
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.18) inset;
}
.markup-send:hover,
.markup-send:focus-visible {
  filter: brightness(1.06);
  color: #1a1206 !important;
  background: var(--accent) !important;
}
.markup-canvas {
  position: absolute;
  inset: 0;
  /* Un <canvas> trae tamaño intrínseco (300x150) y `inset: 0` no lo estira: sin
     esto el lienzo se queda en esa esquina y no recibe ni un trazo. */
  width: 100%;
  height: 100%;
  z-index: 30;
  pointer-events: none;
  /* Sin esto el navegador se queda el gesto para hacer scroll y el trazo se corta. */
  touch-action: none;
}
.stage.is-markup .markup-canvas {
  pointer-events: auto;
  cursor: crosshair;
}
/* Marcando, el cine estorba: el recorrido se queda quieto y sin invitaciones. */
.stage.is-markup .hint,
.stage.is-markup .materials,
.stage.is-markup .tap-play {
  opacity: 0 !important;
  pointer-events: none !important;
}
/* Sobre el plano no hay nada que marcar todavía, y la barra caía justo encima
   del encabezado del mapa. `visibility` además de `opacity`: con sólo opacity
   la pastilla seguía contando como algo puesto ahí arriba. */
.stage.is-floorplan .markup-bar {
  opacity: 0 !important;
  visibility: hidden !important;
  pointer-events: none !important;
}
@media (max-width: 640px) {
  .markup-bar button {
    font-size: 10px;
    padding: 7px 10px;
    letter-spacing: 0.1em;
  }
  .markup-toggle {
    padding: 8px 12px 8px 11px !important;
    gap: 7px;
  }
}

/* Nota de markup — modal RÜM (sustituye window.prompt) */
.rum-prompt {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: grid;
  place-items: center;
  padding: max(18px, env(safe-area-inset-top)) 18px max(18px, env(safe-area-inset-bottom));
  background: rgba(12, 9, 6, 0.62);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  opacity: 0;
  transition: opacity 220ms var(--ease);
}
.rum-prompt.is-in { opacity: 1; }
.rum-prompt.is-out { opacity: 0; }
.rum-prompt-card {
  width: min(92vw, 420px);
  padding: 22px 22px 18px;
  border-radius: 18px;
  background: linear-gradient(165deg, rgba(28, 24, 18, 0.96) 0%, rgba(16, 13, 10, 0.98) 100%);
  border: 1px solid rgba(243, 210, 178, 0.22);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.45);
  color: var(--ink);
  transform: translateY(10px) scale(0.98);
  transition: transform 280ms var(--ease);
}
.rum-prompt.is-in .rum-prompt-card { transform: none; }
.rum-prompt-brand {
  margin: 0 0 10px;
  font-family: var(--rum-brand-font);
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--accent);
  line-height: 1;
}
.rum-prompt-title {
  margin: 0 0 6px;
  font-family: var(--font);
  font-size: clamp(1.15rem, 2.6vw, 1.35rem);
  font-weight: 600;
  letter-spacing: -0.015em;
  color: var(--ink);
}
.rum-prompt-hint {
  margin: 0 0 16px;
  font-size: 13px;
  line-height: 1.45;
  color: var(--muted);
}
.rum-prompt-hint:empty,
.rum-prompt-hint[hidden] { display: none; }
.rum-prompt-label {
  display: block;
  margin: 0 0 8px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(243, 210, 178, 0.78);
}
.rum-prompt-input {
  width: 100%;
  margin: 0 0 18px;
  padding: 12px 14px;
  border-radius: 12px;
  border: 1px solid rgba(243, 236, 224, 0.18);
  background: rgba(243, 236, 224, 0.06);
  color: var(--ink);
  font-family: var(--font);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.01em;
  outline: none;
  transition: border-color 200ms var(--ease), background 200ms var(--ease);
}
.rum-prompt-input::placeholder {
  color: rgba(243, 236, 224, 0.38);
  font-weight: 400;
}
.rum-prompt-input:focus {
  border-color: rgba(243, 210, 178, 0.55);
  background: rgba(243, 210, 178, 0.08);
}
.rum-prompt-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
.rum-prompt-btn {
  font-family: var(--font);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  border-radius: 999px;
  padding: 11px 16px;
  cursor: pointer;
  border: 1px solid transparent;
  transition: background 200ms var(--ease), color 200ms var(--ease), border-color 200ms var(--ease);
}
.rum-prompt-btn--ghost {
  color: var(--ink);
  background: transparent;
  border-color: rgba(243, 236, 224, 0.18);
}
.rum-prompt-btn--ghost:hover {
  background: rgba(243, 236, 224, 0.08);
  border-color: rgba(243, 236, 224, 0.3);
}
.rum-prompt-btn--solid {
  color: #1a1206;
  background: var(--accent);
  border-color: var(--accent);
}
.rum-prompt-btn--solid:hover { filter: brightness(1.05); }

/* ---------- ACABADOS (esferas de material) ----------
   Catálogo por escena, a la derecha y sobre el pause: la ficha de habitación vive
   abajo a la izquierda y los hotspots del pase no pasan de x=50, así que esta
   esquina está libre.

   La esfera es la textura plana más dos gradientes: un brillo especular arriba a
   la izquierda y la caída de sombra abajo a la derecha. `background-size: 100%`
   (cover en un círculo cuadrado) muestra la muestra a escala real — ampliarla
   (p. ej. 180%) pixelaba el grano de la textura. */
.materials {
  position: absolute;
  z-index: 24;
  right: max(3.5vw, 20px);
  bottom: max(16vh, 96px);
  display: flex;
  align-items: center;
  gap: 11px;
  opacity: 0;
  transition: opacity 500ms var(--ease);
  pointer-events: none;
}
.materials.is-live {
  opacity: 1;
  pointer-events: auto;
}
.material-orb {
  position: relative;
  width: 42px;
  height: 42px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  cursor: pointer;
  background-color: #2a2622;
  background-size: 118%;
  background-position: 35% 40%;
  background-repeat: no-repeat;
  image-rendering: auto;
  box-shadow:
    0 7px 16px rgba(0, 0, 0, 0.45),
    inset 0 0 0 1px rgba(243, 236, 224, 0.2);
  transition: transform 260ms var(--ease), box-shadow 260ms var(--ease);
  animation: finish-orb-turn 18s ease-in-out infinite;
}
.material-orb::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background:
    radial-gradient(circle at 32% 26%, rgba(255, 255, 255, 0.5) 0%, rgba(255, 255, 255, 0.1) 20%, rgba(255, 255, 255, 0) 42%),
    radial-gradient(circle at 70% 76%, rgba(0, 0, 0, 0.42) 0%, rgba(0, 0, 0, 0.1) 48%, rgba(0, 0, 0, 0) 72%);
  animation: finish-orb-glint 12s ease-in-out infinite;
}
.material-orb:hover,
.material-orb:focus-visible,
.material-orb.is-active {
  transform: translateY(-2px) scale(1.08);
  box-shadow:
    0 10px 20px rgba(0, 0, 0, 0.5),
    inset 0 0 0 1px var(--accent);
  outline: none;
}
.material-orb .label {
  position: absolute;
  bottom: calc(100% + 8px);
  right: 0;
  white-space: nowrap;
  font-family: var(--font);
  font-size: 11px;
  font-weight: 500;
  letter-spacing: var(--ui-tracking);
  padding: 6px 11px;
  border-radius: 999px;
  color: var(--ink);
  background: rgba(12, 9, 6, 0.72);
  border: 1px solid rgba(243, 210, 178, 0.22);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  opacity: 0;
  transform: translateY(4px);
  transition: opacity 200ms var(--ease), transform 200ms var(--ease);
  pointer-events: none;
}
.material-orb:hover .label,
.material-orb:focus-visible .label {
  opacity: 1;
  transform: translateY(0);
}
/* En detalle o galería el recorrido cede la pantalla a lo que se está mirando. */
.stage.is-detail .materials,
.stage.is-gallery .materials {
  opacity: 0 !important;
  pointer-events: none !important;
}
@media (max-width: 640px) {
  .materials {
    right: max(3vw, 12px);
    bottom: max(13vh, 74px);
    gap: 8px;
  }
  .material-orb {
    width: 34px;
    height: 34px;
  }
}

/* El sello. Los mockups del Showroom lo posicionan en su propio HTML y esas
   reglas ganan (su <style> va después de este archivo); esto es lo que viste una
   entrega directa, que antes se quedaba sin sello ninguno.
   Va arriba a la izquierda: ahí no está el botón `i` —con el que el badge del
   Showroom sí choca en móvil— y en un proyecto anamórfico cae dentro de la banda
   negra, sin tapar el render. En recorridos privados el botón Showroom está
   oculto, así que la esquina está libre. */
/* Índice de espacios. Caminar de puerta en puerta funciona con tres cuartos y
   se vuelve un laberinto con quince: esto es el atajo, y va discreto en la
   esquina porque el recorrido sigue siendo la forma principal de verlo. */
/* Bajo la firma, arriba a la izquierda. Es la única esquina libre: el
   marcado se queda en el centro, la ayuda y el audio a la derecha, y Back
   abajo a la izquierda. */
.rooms {
  position: fixed;
  top: calc(max(12px, env(safe-area-inset-top)) + 32px);
  left: max(16px, env(safe-area-inset-left));
  z-index: 61;
  /* Fila: el índice y el keymap son las dos formas de saltar de espacio, así
     que viven juntos. */
  display: inline-flex;
  align-items: flex-start;
  gap: 8px;
  opacity: 0;
  transition: opacity 400ms var(--ease);
  pointer-events: none;
}
.rooms.is-live { opacity: 1; pointer-events: auto; }
.rooms[hidden] { display: none; }
.rooms-toggle {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 7px 13px;
  border-radius: 999px;
  border: 1px solid rgba(233,230,223,0.18);
  background: rgba(11,13,23,0.5);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: rgba(233,230,223,0.9);
  font-family: var(--font);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 200ms var(--ease), border-color 200ms var(--ease);
}
.rooms-toggle:hover,
.rooms-toggle[aria-expanded="true"] {
  color: var(--accent);
  border-color: rgba(243,210,178,0.45);
}
.rooms-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.rooms-toggle-dots { display: inline-flex; flex-direction: column; gap: 2px; }
.rooms-toggle-dots i {
  width: 11px;
  height: 1.5px;
  border-radius: 1px;
  background: currentColor;
}
/* Keymap: mismo botón que Rooms, glifo de retícula en vez de las tres barras —
   el plano es la otra forma de elegir espacio, no otra clase de control. */
.keymap-toggle[hidden] { display: none; }
.keymap-icon {
  display: grid;
  grid-template-columns: repeat(2, 4.5px);
  grid-template-rows: repeat(2, 4.5px);
  gap: 2px;
}
.keymap-icon i {
  width: 4.5px;
  height: 4.5px;
  border-radius: 1px;
  background: currentColor;
}
/* Portada: no hay fila de hotspots con la que alinearse, así que el par deja de
   ser un control de esquina — es la puerta al recorrido y se planta centrado
   bajo la casa, a tamaño de verse desde el sofá. */
.rooms.is-standalone {
  top: auto;
  bottom: clamp(74px, 17%, 190px);
  left: 50%;
  transform: translateX(-50%);
  gap: 14px;
}
.rooms.is-standalone .rooms-toggle {
  padding: 13px 26px;
  gap: 11px;
  font-size: 12px;
  letter-spacing: 0.2em;
  background: rgba(11,13,23,0.58);
}
.rooms.is-standalone .rooms-toggle-dots i { width: 14px; height: 2px; }
.rooms.is-standalone .keymap-icon {
  grid-template-columns: repeat(2, 6px);
  grid-template-rows: repeat(2, 6px);
}
.rooms.is-standalone .keymap-icon i { width: 6px; height: 6px; }
/* En un cuadro estrecho el par a tamaño de portada llega a los bordes. */
@media (max-width: 420px) {
  .rooms.is-standalone { gap: 10px; }
  .rooms.is-standalone .rooms-toggle {
    padding: 12px 18px;
    font-size: 11px;
    letter-spacing: 0.16em;
  }
}
/* Desde abajo el índice se despliega hacia arriba: hacia abajo no cabe. */
.rooms.is-standalone .rooms-list {
  top: auto;
  bottom: calc(100% + 10px);
  left: 50%;
  right: auto;
  transform: translateX(-50%);
  animation-name: rooms-in-up;
}
.rooms-list {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  min-width: 176px;
  padding: 6px;
  border-radius: 12px;
  border: 1px solid rgba(233,230,223,0.14);
  background: rgba(11,13,23,0.9);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 18px 44px rgba(0,0,0,0.55);
  animation: rooms-in 180ms var(--ease) both;
  /* Con la casa entera dentro esto pasa de siete renglones a treinta, y en un
     teléfono acostado no cabrían. */
  max-height: min(60vh, 420px);
  overflow-y: auto;
}
.rooms-list[hidden] { display: none; }
@keyframes rooms-in { from { opacity: 0; transform: translateY(-6px); } to { opacity: 1; transform: none; } }
/* ---------- SELECTOR DE PLANTA ----------
   Entre la portada y el recorrido: el visitante ve dónde está cada cuarto
   antes de entrar, en vez de aterrizar en uno arbitrario y descubrir la casa
   a base de botones. Vive por encima de la card del intro (52). */
.floorplan {
  position: fixed;
  inset: 0;
  z-index: 56;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(12px, 2.4vh, 22px);
  padding: clamp(16px, 4vh, 42px) clamp(16px, 4vw, 48px);
  background: rgba(11, 9, 6, 0.92);
  backdrop-filter: blur(16px);
  -webkit-backdrop-filter: blur(16px);
  animation: floorplan-in 300ms var(--ease) both;
  /* Con leyenda y plano juntos en pantalla corta el contenido puede pasarse:
     mejor que se pueda arrastrar que perder el botón de entrar. */
  overflow-y: auto;
  overscroll-behavior: contain;
}
.floorplan[hidden] { display: none; }
@keyframes floorplan-in { from { opacity: 0; } to { opacity: 1; } }

.floorplan-head { display: grid; justify-items: center; gap: 10px; }
.floorplan-kicker {
  margin: 0;
  font-family: var(--font);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent);
}
/* Un solo riel con las plantas dentro, no dos pastillas sueltas: son las dos
   caras de una misma pregunta, y el riel las presenta como un interruptor —
   se ve de un golpe cuál está puesta y que hay otra. */
.floorplan-levels {
  display: inline-flex;
  flex-wrap: nowrap;
  gap: 2px;
  padding: 3px;
  border-radius: 999px;
  border: 1px solid rgba(233, 230, 223, 0.14);
  background: rgba(255, 255, 255, 0.045);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}
.floorplan-levels[hidden] { display: none; }
.floorplan-level {
  appearance: none;
  padding: 8px 16px;
  border-radius: 999px;
  border: 0;
  background: transparent;
  color: rgba(233, 230, 223, 0.6);
  font-family: var(--font);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color 180ms var(--ease), background 180ms var(--ease), box-shadow 180ms var(--ease);
}
.floorplan-level:hover { color: var(--ink); background: rgba(255, 255, 255, 0.06); }
.floorplan-level:focus-visible { outline: none; color: var(--ink); box-shadow: inset 0 0 0 1px var(--accent); }
.floorplan-level.is-current {
  color: var(--accent-ink, #1a1206);
  background: var(--accent);
  box-shadow: 0 2px 12px rgba(243, 210, 178, 0.26);
}

/* El plano y los pines comparten caja: los pines se posicionan en % de esta,
   así siguen a su cuarto cuando la imagen se reescala. `width:max-content`
   la ciñe a la imagen ya escalada, no a la columna. */
/* Sin hoja blanca: el plano se dibuja sobre el mismo negro del recorrido, en
   trazo claro, y así entra en la misma película en vez de parecer un PDF pegado
   encima. Sin padding a propósito: los pines se sitúan en % de esta caja, así
   que tiene que coincidir con la imagen. */
.floorplan-stage {
  position: relative;
  width: max-content;
  max-width: 100%;
  background: transparent;
  /* Antes recortaba: con la hoja blanca hacía falta, ahora sólo cortaría la
     etiqueta de un pin pegado al borde. */
  overflow: visible;
  /* Sin esto la columna la encogía —157px de caja para una imagen de 265— y los
     pines, que se sitúan en % de la caja, se apelotonaban en el tercio de arriba
     lejos de su cuarto. La caja tiene que medir exactamente lo que la imagen. */
  flex: none;
}
.floorplan-img {
  display: block;
  /* Contenido, no fondo: a sangre el plano competía con el recorrido que hay
     debajo. Se lee entero de un vistazo sin barrer la pantalla, y el aire
     alrededor deja claro que es una consulta de la que se sale.
     La segunda cota es para pantallas bajas (un teléfono acostado son 390px de
     alto): sin ella el plano no deja sitio a la leyenda ni al botón de entrar.
     Se pide la altura en vez de acotarla porque los planos vienen recortados a
     su dibujo y son más chicos que el hueco: `max-height` sólo achica, así que
     se quedaban a tamaño natural en medio del cuadro. El ancho sale del ratio, y
     si se pasa, `max-width` lo recorta y la altura le sigue. */
  max-width: min(100%, 880px);
  height: min(54vh, calc(100dvh - 240px));
  width: auto;
  /* El plano llega en gris y sobre este fondo café leía a plomo, ajeno al hueso
     y al durazno de todo lo demás. `sepia` lo tuerce a amarillo, así que el
     giro de tono lo trae al beige de la casa; los dos planos comparten el
     tinte para que cambiar de nivel no cambie de papel. */
  --plan-warm: sepia(0.45) saturate(1.5) hue-rotate(-12deg);
  /* El plano viene en negro sobre blanco. A gris y del revés: muros y cotas en
     claro, papel en negro. `screen` deja pasar ese negro, que se funde con el
     fondo y no queda recuadro. Un negativo a color habría dado la alberca en
     naranja y el jardín en magenta. */
  filter: grayscale(1) invert(1) contrast(1.08) brightness(1.04) var(--plan-warm);
  mix-blend-mode: screen;
  opacity: 0.96;
}
/* Planos que ya vienen claros sobre oscuro: sólo se les quita el color. */
.floorplan-img.is-inverted {
  filter: grayscale(1) contrast(1.08) var(--plan-warm);
}
.floorplan-pins { position: absolute; inset: 0; }
/* El botón es exactamente el punto, y la etiqueta cuelga en absoluto: si la
   etiqueta ocupara sitio en el flujo, el centrado dejaría el punto por encima
   de la coordenada y ninguna x/y del JSON caería donde dice. */
.floorplan-pin {
  position: absolute;
  transform: translate(-50%, -50%);
  appearance: none;
  border: 0;
  background: transparent;
  padding: 0;
  line-height: 0;
  cursor: pointer;
  color: #f7f3ec;
  font-family: var(--font);
  z-index: 1;
  -webkit-tap-highlight-color: transparent;
}
/* Área de dedo de ~44px sin agrandar el punto: el pin dibujado mide 16px y
   en táctil eso se falla más de lo que se acierta. */
.floorplan-pin::before {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
}
/* 14px y no 16: el plano ahora se dibuja más chico y el mismo punto de antes
   engordaba sobre él —en el sótano, con tres cuartos apilados cada 5.5% de
   alto, los aros llegaban a tocarse. */
.floorplan-pin-dot {
  min-width: 14px;
  height: 14px;
  padding: 0 3px;
  box-sizing: border-box;
  border-radius: 999px;
  background: var(--accent);
  color: #1a1206;
  font: 700 9px/1 var(--font);
  letter-spacing: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  /* Aro oscuro que abre un claro en el trazo del plano, y detrás el halo del
     acento: el mismo punto que marca los hotspots del recorrido. */
  border: 0;
  /* El halo va más marcado que en el recorrido: el punto es durazno y el papel
     del plano es beige, así que sobre la foto bastaba con insinuarlo y aquí se
     perdía contra el piso. */
  box-shadow:
    0 0 0 3px rgba(11, 9, 6, 0.9),
    0 0 0 5px rgba(243, 210, 178, 0.42),
    0 2px 10px rgba(0, 0, 0, 0.5);
  transition: transform 180ms var(--ease), box-shadow 180ms var(--ease);
}
/* El plano ya trae el nombre de cada cuarto impreso: una pastilla permanente
   encima lo tapaba y repetía. Aparece al apuntar, como confirmación de que ese
   punto lleva a ese espacio. */
.floorplan-pin-label {
  position: absolute;
  left: 50%;
  bottom: calc(100% + 8px);
  transform: translate(-50%, 3px);
  font-size: 11px;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: 0.05em;
  white-space: nowrap;
  padding: 5px 10px;
  border-radius: 999px;
  color: #f7f3ec;
  background: rgba(20, 15, 10, 0.96);
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.35);
  pointer-events: none;
  opacity: 0;
  transition: opacity 160ms var(--ease), transform 160ms var(--ease);
}
.floorplan-pin:hover .floorplan-pin-dot { transform: scale(1.25); }
.floorplan-pin:hover .floorplan-pin-label,
.floorplan-pin:focus-visible .floorplan-pin-label,
.floorplan-pin.is-cued .floorplan-pin-label {
  opacity: 1;
  transform: translate(-50%, 0);
}
.floorplan-pin:focus-visible { outline: none; }
.floorplan-pin:focus-visible .floorplan-pin-dot {
  box-shadow: 0 0 0 2.5px rgba(11, 9, 6, 0.9), 0 0 0 5px var(--accent);
}
/* Pin señalado desde la leyenda. */
.floorplan-pin.is-cued { z-index: 2; }
.floorplan-pin.is-cued .floorplan-pin-dot {
  transform: scale(1.4);
  box-shadow: 0 0 0 3px rgba(11, 9, 6, 0.9), 0 0 0 6px var(--accent);
}

/* Leyenda numerada: la lista legible y tocable. En escritorio las etiquetas
   sobre el plano ya nombran cada cuarto, así que sólo aparece en pantalla
   chica — donde no hay hover y las etiquetas se encabalgaban. */
.floorplan-legend { display: none; }
.floorplan-legend-item {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 7px;
  min-height: 36px;
  padding: 6px 12px 6px 7px;
  border-radius: 999px;
  border: 1px solid rgba(233, 230, 223, 0.22);
  background: rgba(255, 255, 255, 0.04);
  color: #f7f3ec;
  font-family: var(--font);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color 180ms var(--ease), border-color 180ms var(--ease), background 180ms var(--ease);
}
.floorplan-legend-num {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 20px;
  height: 20px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--accent);
  color: #1a1206;
  font: 700 10px/1 var(--font);
}
.floorplan-legend-name {
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  white-space: nowrap;
}
.floorplan-legend-item:hover,
.floorplan-legend-item:focus-visible {
  outline: none;
  color: var(--accent);
  border-color: rgba(243, 210, 178, 0.6);
  background: rgba(243, 210, 178, 0.08);
}
.floorplan-legend-item:active { transform: scale(0.98); }

/* Era texto suelto y se leía como un pie de página; ahora es la salida del
   mapa, con la misma pastilla que las plantas para que se vea que se pulsa. La
   flecha dice hacia dónde lleva —y con el mapa abierto desde el recorrido, donde
   sólo cierra, pasa a ser una cruz. */
.floorplan-skip {
  appearance: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 18px;
  border-radius: 999px;
  border: 1px solid rgba(233, 230, 223, 0.14);
  background: transparent;
  color: rgba(233, 230, 223, 0.58);
  font-family: var(--font);
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color 180ms var(--ease), border-color 180ms var(--ease), background 180ms var(--ease);
}
.floorplan-skip::after {
  content: "→";
  font-size: 12px;
  letter-spacing: 0;
  transition: transform 180ms var(--ease);
}
.floorplan-skip.is-close::after { content: "<svg xmlns="http://www.w3.org/2000/svg" class="rum-ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>"; font-size: 10px; }
.floorplan-skip:hover,
.floorplan-skip:focus-visible {
  outline: none;
  color: var(--accent);
  border-color: rgba(243, 210, 178, 0.45);
  background: rgba(243, 210, 178, 0.06);
}
.floorplan-skip:hover::after { transform: translateX(3px); }
.floorplan-skip.is-close:hover::after { transform: none; }

/* Sin puntero no hay hover que revele el nombre del pin, y los puntos son un
   blanco de 16px para un dedo: la leyenda numerada pasa a ser la lista, con
   nombre y área de toque. Por táctil y no sólo por ancho — una tableta tiene
   ancho de sobra y tampoco tiene hover. */
@media (max-width: 720px), (hover: none) and (pointer: coarse) {
  .floorplan-legend {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 6px;
    max-width: 100%;
  }
  .floorplan-pin-label { display: none; }
  /* El plano cabe en un tercio de la pantalla y los puntos no se encogen con
     él: dos cuartos contiguos quedaban con los aros montados. Aquí el punto sólo
     tiene que decir dónde cae el número —el nombre lo pone la lista de abajo. */
  .floorplan-pin-dot {
    min-width: 12px;
    height: 12px;
    padding: 0 2px;
    font-size: 8px;
    box-shadow:
      0 0 0 2px rgba(11, 9, 6, 0.9),
      0 0 0 3.5px rgba(243, 210, 178, 0.42),
      0 2px 8px rgba(0, 0, 0, 0.5);
  }
}
@media (max-width: 720px) {
  .floorplan { gap: 10px; padding: 12px 14px; }
  .floorplan-head { gap: 8px; }
  /* En el teléfono el plano no se encoge más: ya iba justo, y aquí la lista de
     abajo es la que se toca —el plano sólo dice dónde cae cada número. */
  .floorplan-img { height: min(46vh, calc(100dvh - 205px)); }
  .floorplan-level { padding: 10px 16px; min-height: 40px; }
  .floorplan-legend-item { min-height: 34px; padding: 5px 10px 5px 6px; }
  .floorplan-legend-name { font-size: 10px; }
  .floorplan-skip { min-height: 44px; font-size: 10px; padding: 9px 16px; }
}
@media (prefers-reduced-motion: reduce) {
  .floorplan { animation: none; }
}

/* Ripple al pulsar. La onda nace donde cayó el dedo, así que el botón acusa el
   toque exacto y no un estado genérico — en táctil, donde no hay hover, es la
   única confirmación de que se pulsó lo que se quería pulsar. */
.rum-ripple {
  position: absolute;
  border-radius: 50%;
  transform: scale(0);
  /* Durazno y no el color del texto: sobre una pastilla de 25 px de alto una
     onda crema al 28% se leía como un parpadeo. El acento la vuelve un gesto
     de la marca en vez de un destello. */
  background: var(--accent);
  opacity: 0.55;
  pointer-events: none;
  animation: rum-rippling 520ms var(--ease) forwards;
}
@keyframes rum-rippling {
  to { transform: scale(2.4); opacity: 0; }
}
/* La pastilla acusa el toque además de la onda: en táctil no hay hover, y el
   hundido es lo que dice "sí, ésta". Sin la etiqueta del hotspot: esa usa
   `transform` para centrarse bajo el punto, y escalarla la descolocaría. */
.gallery-toggle,
.rooms-item,
.rooms-level-summary,
.intro-start {
  transition: transform 120ms var(--ease), background 160ms var(--ease), color 160ms var(--ease);
}
.gallery-toggle:active,
.rooms-item:active,
.rooms-level-summary:active,
.intro-start:active {
  transform: scale(0.97);
}
@media (prefers-reduced-motion: reduce) {
  .rum-ripple { display: none; }
}
/* La onda se recorta al contorno de la pastilla; sin esto sale del borde. */
.gallery-toggle,
.rooms-item,
.rooms-level-summary,
.intro-start { position: relative; overflow: hidden; }
/* Estas dos cuelgan en absoluto de su punto. Forzarles `position: relative` para
   contener el ripple las devolvía al flujo del botón, y el punto —que se centra
   en una rejilla de 44px— subía media etiqueta: Images y Finishes quedaban 15px
   por encima de Rooms y Keymap en vez de en la misma línea. Para el ripple basta
   el recorte; la caja ya la contienen ellas. */
.hotspot .label,
.hotspots > .rooms .rooms-toggle-label { overflow: hidden; }

/* Niveles plegados. Con la casa entera son treinta y tantos nombres, y una
   columna de treinta no se lee: se elige el nivel y luego el cuarto. */
.rooms-level + .rooms-level { margin-top: 2px; }
.rooms-level-entry {
  appearance: none;
  width: 100%;
  border: 0;
  background: transparent;
  font-family: inherit;
  text-align: left;
}
.rooms-level-entry::before { display: none; }
.rooms-level-entry.is-current { background: rgba(243, 210, 178, 0.1); }
.rooms-level + .rooms-level-entry,
.rooms-level-entry + .rooms-level { margin-top: 2px; }
.rooms-level-summary {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 11px;
  border-radius: 8px;
  cursor: pointer;
  list-style: none;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
  transition: background 160ms var(--ease);
}
.rooms-level-summary::-webkit-details-marker { display: none; }
.rooms-level-summary:hover { background: rgba(233, 230, 223, 0.06); }
.rooms-level-summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* Galón propio: el triángulo nativo no se puede colorear ni alinear. */
.rooms-level-summary::before {
  content: "";
  width: 5px;
  height: 5px;
  flex: none;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg) translate(-1px, -1px);
  transition: transform 180ms var(--ease);
}
.rooms-level[open] > .rooms-level-summary::before {
  transform: rotate(45deg) translate(-1px, -1px);
}
.rooms-level-count {
  margin-left: auto;
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: rgba(233, 230, 223, 0.4);
}
.rooms-level-items { padding-bottom: 4px; }
.rooms-item {
  appearance: none;
  display: block;
  width: 100%;
  text-align: left;
  padding: 8px 11px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: rgba(233,230,223,0.82);
  font-family: var(--font);
  font-size: 12.5px;
  line-height: 1.25;
  cursor: pointer;
  transition: background 160ms var(--ease), color 160ms var(--ease);
}
.rooms-item:hover { background: rgba(233,230,223,0.08); color: var(--ink); }
.rooms-item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.rooms-item.is-current { color: var(--accent); font-weight: 600; }
.rooms-item.is-current::after { content: " ·"; }
@media (prefers-reduced-motion: reduce) {
  .rooms-list { animation: none; }
}

/* En la fila el índice es un destino más, así que se dibuja como uno: punto de
   acento centrado en `y` y la etiqueta en pastilla debajo, con los mismos
   valores que `.hotspot`. Un botón con otra forma en medio de la fila se lee
   como que pertenece a otra cosa. */
.hotspots > .rooms {
  position: absolute;
  top: var(--rooms-y, 70%);
  left: var(--rooms-x, 88%);
  transform: translate(-50%, -50%);
  /* En la fila cada botón lleva su pastilla debajo: con el gap de la esquina
     («Rooms» y «Keymap» a 8px) las dos etiquetas se montaban una sobre otra. */
  gap: clamp(38px, 4vw, 56px);
  opacity: 1;
  pointer-events: auto;
}
.hotspots > .rooms .rooms-toggle {
  position: relative;
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  padding: 0;
  gap: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  /* `.hotspots` no recibe punteros; el índice sí, o no se podría pulsar entre
     escena y escena mientras los hotspots están desarmados. */
  pointer-events: auto;
}
/* Las tres barras del menú se vuelven el punto. */
.hotspots > .rooms .rooms-toggle-dots {
  display: block;
  width: 13px;
  height: 13px;
  border-radius: 999px;
  background: var(--accent);
  box-shadow: 0 0 0 6px var(--accent-dim), 0 2px 10px rgba(0, 0, 0, 0.35);
  transition: transform 200ms var(--ease);
}
.hotspots > .rooms .rooms-toggle-dots i { display: none; }
.hotspots > .rooms .rooms-toggle:hover .rooms-toggle-dots,
.hotspots > .rooms .rooms-toggle[aria-expanded="true"] .rooms-toggle-dots {
  transform: scale(1.15);
}
.hotspots > .rooms .rooms-toggle-label {
  position: absolute;
  left: 50%;
  top: calc(100% + 6px);
  transform: translateX(-50%);
  font-family: var(--font);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: var(--ui-tracking);
  text-transform: none;
  line-height: 1.2;
  padding: 7px 13px;
  border-radius: 999px;
  color: var(--ink);
  background: rgba(12, 9, 6, 0.62);
  border: 1px solid rgba(243, 210, 178, 0.22);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.28);
  white-space: nowrap;
  opacity: 0.92;
}
.hotspots > .rooms .rooms-toggle:hover .rooms-toggle-label,
.hotspots > .rooms .rooms-toggle[aria-expanded="true"] .rooms-toggle-label {
  opacity: 1;
}
.hotspots > .rooms .rooms-toggle:focus-visible { outline: none; }
.hotspots > .rooms .rooms-toggle:focus-visible .rooms-toggle-dots {
  box-shadow: 0 0 0 4px var(--accent), 0 0 0 8px var(--accent-dim);
}
.hotspots > .rooms .rooms-list {
  top: auto;
  bottom: calc(100% + 8px);
  left: 50%;
  right: auto;
  transform: translateX(-50%);
  animation: rooms-in-up 180ms var(--ease) both;
}

@keyframes rooms-in-up {
  from { opacity: 0; transform: translate(-50%, 6px); }
  to { opacity: 1; transform: translate(-50%, 0); }
}
/* En la portada y en el keymap el índice no aparece: ahí el cuarto se elige
   sobre el plano, y ofrecer dos caminos a la vez sólo los pone a competir. */
.stage.is-intro .rooms,
.stage.is-floorplan .rooms,
/* Con el muro de acabados, una galería o el marcado encima, el índice estorba. */
.stage.is-finishes .rooms,
.stage.is-gallery .rooms,
.stage.is-detail .rooms,
.stage.is-markup .rooms { opacity: 0 !important; pointer-events: none !important; }

/* Aviso breve del player (descarga retenida, por ahora). Va por encima de la
   barra de markup —que es desde donde se dispara— y se retira solo. */
.player-notice {
  position: fixed;
  left: 50%;
  top: 74px;
  transform: translate(-50%, -8px);
  z-index: 92;
  max-width: min(560px, calc(100vw - 32px));
  padding: 12px 18px;
  border-radius: 14px;
  border: 1px solid rgba(243, 210, 178, 0.34);
  background: rgba(20, 17, 13, 0.94);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: #f3ece0;
  font: 600 13px/1.5 "Baloo 2", ui-sans-serif, system-ui, sans-serif;
  text-align: center;
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.42);
  opacity: 0;
  pointer-events: none;
  transition: opacity 240ms var(--ease, ease), transform 240ms var(--ease, ease);
}
.player-notice.is-live { opacity: 1; transform: translate(-50%, 0); }

/* Marca de agua tramada sobre el cuadro entero. Discreta a propósito: tiene
   que sobrevivir a una captura de pantalla sin competir con el render que el
   cliente está juzgando.
   La intensidad la puede subir el proyecto con `chrome.watermarkOpacity`
   cuando el sello deja de ser autoría y pasa a ser advertencia —un entregable
   pendiente de pago, por ejemplo—: ahí sí tiene que verse. */
.watermark {
  position: fixed;
  inset: 0;
  z-index: 59;
  background-repeat: repeat;
  opacity: var(--watermark-opacity, 0.06);
  mix-blend-mode: screen;
  pointer-events: none;
  user-select: none;
}
/* Firma de la casa. Baloo 2 es la tipografía de marca de RÜM; la diéresis va
   en durazno como en el resto de la plataforma. */
.brand-mark {
  position: fixed;
  top: max(12px, env(safe-area-inset-top));
  left: max(16px, env(safe-area-inset-left));
  z-index: 60;
  font-family: "Baloo 2", var(--font);
  font-weight: 700;
  font-size: 21px;
  line-height: 1;
  letter-spacing: 0.02em;
  color: rgba(243, 236, 224, 0.94);
  text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6);
  pointer-events: none;
  user-select: none;
  opacity: 1 !important;
  visibility: visible !important;
  display: inline-flex !important;
  align-items: center;
  transform: none;
}
.brand-mark-u { color: var(--accent, #f3d2b2); }
@media (max-width: 640px) {
  .brand-mark { font-size: 18px; }
  /* Menos densa en pantalla chica: la misma trama en 375 px de ancho tapa
     más superficie de la que sella. */
  .watermark { background-size: 210px 133px; opacity: calc(var(--watermark-opacity, 0.06) * 0.85); }
}
html.hide-showroom-back .showroom-back {
  display: none !important;
  visibility: hidden !important;
  opacity: 0 !important;
  pointer-events: none !important;
}
.showroom-back {
  position: fixed !important;
  top: max(12px, env(safe-area-inset-top)) !important;
  left: max(12px, env(safe-area-inset-left)) !important;
  z-index: 2147483646 !important;
  display: inline-flex !important;
  align-items: center;
  gap: 8px;
  opacity: 1 !important;
  visibility: visible !important;
  pointer-events: auto !important;
  transform: none !important;
  font: 700 11px/1 var(--font);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  color: #2f6df6 !important;
  background: rgba(255, 255, 255, 0.95) !important;
  border: 1px solid rgba(47, 109, 246, 0.3);
  padding: 10px 14px;
  border-radius: 999px;
  box-shadow: 0 6px 18px rgba(15, 23, 42, 0.22);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
}
.showroom-back-icon { font-size: 13px; line-height: 1; opacity: 0.85; }

@keyframes chromeReveal {
  from { opacity: 0; transform: translateY(-10px); }
  to   { opacity: 1; transform: translateY(0); }
}

.btn-ghost {
  position: absolute; left: max(3.5vw, 20px); bottom: max(7vh, 34px);
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--font);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: var(--ui-tracking-wide);
  text-transform: uppercase;
  color: var(--ink);
  background: transparent;
  border: 1px solid rgba(243,236,224,0.28);
  border-radius: 999px;
  padding: 10px 18px 10px 14px;
  cursor: pointer;
  transition: border-color 240ms var(--ease), background 240ms var(--ease), transform 120ms var(--ease);
}
.btn-ghost:hover { border-color: var(--accent); background: rgba(243,210,178,0.08); }
.btn-ghost:active { transform: translateY(1px); }
.btn-ghost .chev { color: var(--accent); font-size: 15px; line-height: 0; font-weight: 400; }

.scene-title {
  position: absolute; left: 50%; bottom: max(6.6vh, 30px);
  transform: translateX(-50%) translateY(12px);
  opacity: 0;
  font-family: var(--font);
  font-size: 12px;
  font-weight: 500;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--muted);
  text-shadow: 0 1px 14px rgba(0,0,0,0.5);
  transition: opacity 360ms var(--ease);
  white-space: nowrap;
  /* Sólo es una etiqueta; nunca debe capturar clics de los botones cercanos. */
  pointer-events: none;
}
.scene-title.is-dim { opacity: 0; }
.scene-title b { color: var(--ink); font-weight: 600; letter-spacing: 0.28em; }
.scene-title.title-pop {
  animation: hudRevealTitle 0.72s var(--ease) forwards;
}

.btn-help {
  position: absolute; right: max(3.5vw, 20px); top: max(3vh, 18px);
  width: 34px; height: 34px; border-radius: 999px;
  display: grid; place-items: center;
  font-size: 13px; font-style: italic; font-weight: 600;
  color: var(--muted);
  background: var(--scrim);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(243,236,224,0.16);
  cursor: pointer;
  transition: color 200ms var(--ease), border-color 200ms var(--ease);
}
.btn-help:hover { color: var(--accent); border-color: var(--accent-dim); }

/* Vuelta al arranque del recorrido. Esquina superior derecha: la izquierda la
   tiene la firma y el centro el marcado. Se retira mientras se está ya en la
   escena de arranque — un botón que no lleva a ningún sitio nuevo. */
.btn-home {
  position: absolute; right: max(3.5vw, 20px); top: max(3vh, 18px);
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 15px;
  border-radius: 999px;
  border: 1px solid rgba(243, 236, 224, 0.22);
  background: var(--scrim);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  color: rgba(243, 236, 224, 0.9);
  font-family: var(--font);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  cursor: pointer;
  transition: color 200ms var(--ease), border-color 200ms var(--ease), background 200ms var(--ease);
}
.btn-home[hidden] { display: none; }
.btn-home:hover { color: var(--accent); border-color: var(--accent-dim); background: rgba(243, 210, 178, 0.08); }
.btn-home:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn-home-icon { flex: none; color: var(--accent); }
/* Con Restart puesto, la ayuda y el audio corren a su izquierda. */
.hud.has-home .btn-help { right: calc(max(3.5vw, 20px) + 118px); }
.hud.has-home .btn-audio { right: calc(max(3.5vw, 20px) + 164px); }
/* Sobre la portada, el plano o una ficha no hay recorrido del que volver. */
.stage.is-intro .btn-home,
.stage.is-floorplan .btn-home,
.stage.is-detail .btn-home,
.stage.is-gallery .btn-home,
.stage.is-finishes .btn-home,
.stage.is-markup .btn-home {
  opacity: 0 !important;
  pointer-events: none !important;
}

/* Toggle de audio ambiental (a la izquierda del botón de ayuda) */
.btn-audio {
  position: absolute; right: max(calc(3.5vw + 46px), 66px); top: max(3vh, 18px);
  width: 34px; height: 34px; border-radius: 999px;
  display: grid; place-items: center;
  color: var(--muted);
  background: var(--scrim);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(243, 236, 224, 0.16);
  cursor: pointer;
  transition: color 200ms var(--ease), border-color 200ms var(--ease);
}
.btn-audio:hover { color: var(--accent); border-color: var(--accent-dim); }
.btn-audio .wave { opacity: 0; transition: opacity 200ms var(--ease); }
.btn-audio .mute-slash { opacity: 1; transition: opacity 200ms var(--ease); }
.btn-audio.is-on .wave { opacity: 1; }
.btn-audio.is-on .mute-slash { opacity: 0; }

/* Pausa/reanuda el shot activo (video o Ken Burns del poster) */
.btn-playpause {
  position: absolute; right: max(3.5vw, 20px); bottom: max(7vh, 34px);
  width: 38px; height: 38px; border-radius: 999px;
  display: grid; place-items: center;
  background: transparent;
  border: 1px solid rgba(243, 236, 224, 0.28);
  cursor: pointer;
  transition: border-color 240ms var(--ease), background 240ms var(--ease), transform 120ms var(--ease);
}
.btn-playpause:hover { border-color: var(--accent); background: rgba(243, 210, 178, 0.08); }
.btn-playpause:active { transform: translateY(1px); }

.btn-playpause .icon { position: relative; width: 13px; height: 14px; }
.btn-playpause .icon::before,
.btn-playpause .icon::after {
  content: ""; position: absolute; top: 0; width: 4px; height: 14px;
  background: var(--ink); border-radius: 1px;
}
.btn-playpause .icon::before { left: 0; }
.btn-playpause .icon::after { right: 0; }

/* Pausado → icono cambia a triángulo de "reanudar" */
.btn-playpause.is-paused .icon::before,
.btn-playpause.is-paused .icon::after { display: none; }
.btn-playpause.is-paused .icon {
  width: 0; height: 0; margin-left: 2px;
  border-top: 7px solid transparent;
  border-bottom: 7px solid transparent;
  border-left: 12px solid var(--ink);
}

/* Congela el Ken Burns del poster mientras está pausado (si aún no hay video) */
.stage.is-paused .frame.kb { animation-play-state: paused; }

/* Volver de detalle */
.detail-close {
  position: absolute; z-index: 22;
  left: 50%; bottom: max(11vh, 60px); transform: translateX(-50%);
  display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--font);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: var(--ui-tracking-wide);
  text-transform: uppercase;
  color: var(--ink);
  background: var(--scrim);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(243,236,224,0.22);
  border-radius: 999px; padding: 11px 20px 11px 15px; cursor: pointer;
  opacity: 0; transition: opacity 400ms var(--ease);
}
.detail-close:not([hidden]) { opacity: 1 !important; visibility: visible !important; z-index: 58; }
.detail-close .chev { color: var(--accent); font-size: 15px; line-height: 0; }

/* Ficha de acabado / material en detalle de objeto */
/* Rótulo, no ficha. Sólo lleva nivel y nombre, así que se dimensiona a eso:
   con el ancho de antes quedaba medio recuadro vacío al lado del texto. */
.material-card {
  position: absolute; z-index: 24;
  left: max(3.5vw, 20px);
  bottom: max(18vh, 110px);
  max-width: min(300px, 70vw);
  width: max-content;
  padding: 12px 16px 13px;
  color: var(--ink);
  background: rgba(12, 9, 6, 0.72);
  border: 1px solid rgba(243, 236, 224, 0.18);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  pointer-events: none;
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 420ms var(--ease), transform 420ms var(--ease);
}
.material-card:not([hidden]) {
  opacity: 1;
  transform: translateY(0);
}
/* En ventana angosta la card llegaba a 86vw y se le montaba a la fila de
   hotspots, que vive al 70% de alto. No estorbaba el clic —la card no recibe
   punteros— pero el último botón quedaba leyéndose sobre ella. Se estrecha y
   sube por encima de la fila. */
@media (max-width: 820px) {
  .material-card {
    max-width: min(340px, 62vw);
    bottom: max(30vh, 200px);
  }
}
.material-card-kicker {
  margin: 0 0 5px;
  font-family: var(--font);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent);
}
.material-card-kicker:empty,
.material-card-sub:empty,
.material-card-body:empty { display: none; }
.material-card-title {
  margin: 0;
  font-family: var(--rum-font);
  font-size: clamp(1rem, 1.9vw, 1.2rem);
  font-weight: 500;
  letter-spacing: 0.02em;
  line-height: 1.2;
  color: var(--ink);
}
.material-card-sub {
  margin: 8px 0 0;
  font-family: var(--font);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
}
.material-card-body {
  margin: 12px 0 0;
  padding-top: 12px;
  border-top: 1px solid rgba(243, 236, 224, 0.14);
  font-family: var(--font);
  font-size: 13px;
  font-weight: 400;
  letter-spacing: 0.01em;
  line-height: 1.55;
  color: rgba(243, 236, 224, 0.82);
}
.material-card-body:empty,
.material-card-sub:empty,
.material-card-kicker:empty { display: none; }

html.skin-tizon .material-card {
  border-radius: 0;
  border-color: var(--tizon-line);
  border-left: 2px solid var(--accent);
  background: rgba(10, 8, 6, 0.78);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
html.skin-tizon .material-card-title {
  font-family: var(--rum-font);
  letter-spacing: 0.04em;
}

/* En portrait desktop: ficha en la franja negra izquierda (como Exterior). */
@media (min-width: 900px) {
  html.prefer-portrait .material-card {
    left: max(16px, calc(50vw - min(100vw, 100dvh * 9 / 16) / 2 - 280px));
    right: auto;
    bottom: max(16vh, 96px);
    max-width: 260px;
    z-index: 30;
  }
}

/* CRÍTICO: varios overlays declaran display:inline-flex/flex/grid, que GANA al
   atributo `hidden` (cuyo default es display:none). Sin esto, botones y docks
   "ocultos" siguen ocupando su lugar a pantalla/centro y se comen los clics de
   los demás botones (Volver, back, hotspots…). Forzar que `hidden` oculte de
   verdad arregla toda la clase de bugs de golpe. */
[hidden] { display: none !important; }

/* Galería día/noche (detalle estático) — dock unificado */
.gallery-dock {
  position: absolute; z-index: 24;
  left: 50%; bottom: max(10vh, 56px);
  transform: translateX(-50%);
  display: inline-flex; align-items: center; gap: 10px;
  padding: 6px 8px 6px 6px;
  border-radius: 999px;
  background: var(--scrim);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(243, 236, 224, 0.22);
  opacity: 0;
  transition: opacity 400ms var(--ease);
}
.gallery-dock:not([hidden]) { opacity: 1 !important; visibility: visible !important; z-index: 58; }

.gallery-dock-back {
  display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: var(--ui-tracking-wide);
  text-transform: uppercase;
  color: var(--ink);
  background: rgba(243, 210, 178, 0.12);
  border: 1px solid rgba(243, 210, 178, 0.28);
  border-radius: 999px;
  padding: 9px 14px 9px 11px;
  cursor: pointer;
  transition: background 200ms var(--ease), border-color 200ms var(--ease);
}
.gallery-dock-back:hover {
  background: rgba(243, 210, 178, 0.2);
  border-color: rgba(243, 210, 178, 0.45);
}
.gallery-dock-back .chev { color: var(--accent); font-size: 14px; line-height: 0; }

.gallery-dock-toggles {
  display: inline-flex; align-items: center; gap: 4px;
  padding-left: 2px;
  border-left: 1px solid rgba(243, 236, 224, 0.16);
  flex-wrap: wrap;
  justify-content: center;
  max-width: min(92vw, 520px);
}

/* Acabados · esferas de material (Nauka / Fase 3) */
/* Muestrario y textura, lado a lado desde que abre. Sin estado intermedio:
   la fila de esferas sin textura era una pantalla que no resolvía nada, y
   obligaba a un clic extra para llegar a lo que se venía a ver. */
.finishes-dock {
  position: absolute;
  inset: 0;
  z-index: 26;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-areas:
    "orbs img"
    "orbs card";
  align-content: center;
  justify-content: center;
  gap: 14px clamp(20px, 3.5vw, 40px);
  padding: 18px 18px max(8vh, 48px);
  background: linear-gradient(180deg, rgba(11,13,23,0.15) 0%, rgba(11,13,23,0.55) 55%, rgba(11,13,23,0.78) 100%);
  opacity: 0;
  transition: opacity 400ms var(--ease);
  pointer-events: none;
}
.finishes-dock:not([hidden]) {
  opacity: 1 !important;
  visibility: visible !important;
  z-index: 58;
  pointer-events: auto;
}
.finishes-dock > .gallery-dock-back {
  position: absolute;
  left: max(18px, env(safe-area-inset-left));
  top: max(18px, env(safe-area-inset-top));
}
.finishes-spheres {
  grid-area: orbs;
  display: flex;
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: center;
  gap: clamp(10px, 1.8vh, 20px);
}
.finish-sphere {
  appearance: none;
  border: 0;
  background: transparent;
  padding: 0;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  width: clamp(56px, 8vw, 76px);
  color: var(--ink);
  font-family: var(--font);
}
.finish-sphere-label { font-size: 10px; }
.finish-sphere-orb {
  position: relative;
  width: clamp(46px, 6.5vw, 64px);
  height: clamp(46px, 6.5vw, 64px);
  border-radius: 50%;
  /* Over 100% so the grain has somewhere to travel. The idle drift only
     needs a sliver of overflow, but dragging reads as a dead sphere unless
     the texture actually moves under the finger, and background-position
     can only pan across what overflows. 150% buys that range at a grain
     size that still shows the material rather than a crop of it. */
  background-size: 150%;
  background-position: var(--orb-x, 35%) var(--orb-y, 40%);
  background-repeat: no-repeat;
  cursor: grab;
  /* Sin esto el gesto de girar se lo queda el scroll del navegador en táctil
     y `pointermove` nunca llega. */
  touch-action: none;
  image-rendering: auto;
  box-shadow:
    inset -10px -14px 22px rgba(0,0,0,0.45),
    inset 8px 10px 18px rgba(255,255,255,0.35),
    0 10px 28px rgba(0,0,0,0.45);
  transform: translateZ(0);
  transition: transform 280ms var(--ease), box-shadow 280ms var(--ease);
  animation: finish-orb-turn 16s ease-in-out infinite;
}
.finish-sphere-orb::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: radial-gradient(circle at 32% 28%, rgba(255,255,255,0.45) 0%, rgba(255,255,255,0.08) 28%, rgba(0,0,0,0) 55%);
  pointer-events: none;
  animation: finish-orb-glint 10s ease-in-out infinite;
}
.finish-sphere-label {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-align: center;
  line-height: 1.25;
  text-shadow: 0 1px 8px rgba(0,0,0,0.65);
  max-width: 100%;
}
.finish-sphere:hover .finish-sphere-orb,
.finish-sphere.is-active .finish-sphere-orb {
  transform: translateY(-4px) scale(1.06);
  box-shadow:
    inset -10px -14px 22px rgba(0,0,0,0.45),
    inset 8px 10px 18px rgba(255,255,255,0.4),
    0 0 0 2px var(--accent),
    0 14px 32px rgba(0,0,0,0.5);
  animation-duration: 9s;
}
.finish-sphere:hover .finish-sphere-orb::after,
.finish-sphere.is-active .finish-sphere-orb::after {
  animation-duration: 6s;
}
.finish-sphere:focus-visible .finish-sphere-orb {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
/* Turning by hand takes the sphere off its idle loop. It stays where it was
   left instead of snapping back: the drift resuming under a finger that just
   stopped moving reads as the page fighting the visitor. */
.finish-sphere-orb.is-turning {
  animation: none;
  transition: none;
  cursor: grabbing;
}
.finish-sphere-orb.is-turned { animation: none; }
@keyframes finish-orb-turn {
  0%   { background-position: 28% 38%; }
  25%  { background-position: 68% 42%; }
  50%  { background-position: 62% 62%; }
  75%  { background-position: 32% 58%; }
  100% { background-position: 28% 38%; }
}
@keyframes finish-orb-glint {
  0%, 100% { transform: translate(0, 0); opacity: 1; }
  40% { transform: translate(10%, 6%); opacity: 0.85; }
  70% { transform: translate(-6%, 10%); opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  .finish-sphere-orb,
  .finish-sphere-orb::after,
  .finish-sphere:hover .finish-sphere-orb,
  .finish-sphere.is-active .finish-sphere-orb,
  .material-orb,
  .material-orb::after {
    animation: none !important;
  }
}
.finishes-card {
  grid-area: card;
  max-width: min(70vw, 520px);
  text-align: left;
  color: var(--ink);
  padding: 0;
}
.finishes-card-kicker {
  margin: 0 0 4px;
  font-size: 10px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent);
  font-weight: 600;
}
.finishes-card-title {
  margin: 0 0 6px;
  font-family: var(--font-display, var(--font));
  font-size: clamp(1.05rem, 2.4vw, 1.35rem);
  font-weight: 560;
  letter-spacing: -0.01em;
}
.finishes-card-body {
  margin: 0;
  font-size: 13px;
  line-height: 1.45;
  color: rgba(233,230,223,0.78);
}
.finishes-card-kicker:empty,
.finishes-card-body:empty { display: none; }
/* Texture panel — vive dentro del muro de acabados, no encima de él.
   Abrirla como modal tapaba la habitación, que es contra lo que se está
   comparando el material: el cliente pierde justo la referencia que hace
   útil la muestra. Aquí la escena queda a la vista todo el tiempo. */
.swatch {
  grid-area: img;
  display: flex;
  align-items: center;
  justify-content: flex-start;
  min-width: 0;
  animation: swatch-in 220ms var(--ease) both;
}
.swatch[hidden] { display: none; }
@keyframes swatch-in { from { opacity: 0; transform: translateX(-8px); } to { opacity: 1; transform: none; } }
.swatch-img {
  /* `contain` y nunca por encima de sus píxeles: quien compara dos
     travertinos está leyendo la veta, y una veta reescalada ya no es la
     veta que se va a instalar. */
  max-width: 100%;
  max-height: min(52vh, 420px);
  object-fit: contain;
  border-radius: 10px;
  box-shadow: 0 18px 44px rgba(0,0,0,0.55);
}

/* En vertical no hay "de lado" que valga: la textura va arriba y las
   esferas se quedan en su fila, que es como ya se leen con el pulgar. */
@media (max-width: 720px), (orientation: portrait) {
  .finishes-dock {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      "img"
      "orbs"
      "card";
    justify-items: center;
  }
  .finishes-spheres {
    flex-direction: row;
    flex-wrap: wrap;
    gap: clamp(12px, 4vw, 24px);
  }
  .finishes-card { text-align: center; max-width: 92vw; }
  .swatch { justify-content: center; }
  .swatch-img { max-height: 34vh; }
}
@media (prefers-reduced-motion: reduce) {
  .swatch { animation: none; }
}

.stage.is-finishes .scene-title,
.stage.is-finishes .btn-ghost,
.stage.is-finishes .btn-playpause,
.stage.is-finishes .tap-play,
.stage.is-finishes .hotspots,
.stage.is-finishes .material-card {
  opacity: 0 !important;
  visibility: hidden !important;
  pointer-events: none !important;
}
.stage.is-finishes .tap-play { display: none !important; }

.stage.is-gallery .scene-title { opacity: 0 !important; }
.stage.is-detail .btn-ghost,
.stage.is-detail .btn-playpause,
.stage.is-gallery .btn-ghost,
.stage.is-gallery .btn-playpause { opacity: 0 !important; pointer-events: none !important; }

/* Detalle/galería: nunca el overlay «Toca para reproducir» — solo «Volver». */
.stage.is-detail .tap-play,
.stage.is-gallery .tap-play {
  display: none !important;
  pointer-events: none !important;
}

/* Intro / tap-to-play / pre-unlock: un solo foco — sin hotspot ni pause duplicado. */
.stage.is-intro .hotspots,
.stage.is-tap-play .hotspots,
.stage.is-awaiting-play .hotspots {
  opacity: 0 !important;
  visibility: hidden !important;
  pointer-events: none !important;
}
.stage.is-intro .scene-title {
  opacity: 0 !important;
  visibility: hidden !important;
  pointer-events: none !important;
}
.stage.is-intro #playpause,
.stage.is-tap-play #playpause,
.stage.is-awaiting-play #playpause,
.stage.is-intro .hud #playpause,
.stage.is-tap-play .hud #playpause,
.stage.is-awaiting-play .hud #playpause {
  display: none !important;
  opacity: 0 !important;
  visibility: hidden !important;
  pointer-events: none !important;
}

.gallery-toggle {
  font-family: var(--font);
  font-size: 11.5px;
  font-weight: 500;
  letter-spacing: var(--ui-tracking-wide);
  text-transform: uppercase;
  color: var(--muted);
  background: transparent;
  border: 0;
  border-radius: 999px;
  padding: 9px 16px;
  cursor: pointer;
  transition: color 200ms var(--ease), background 200ms var(--ease);
}
.gallery-toggle:hover { color: var(--ink); }
.gallery-toggle.is-active {
  color: var(--ink);
  background: rgba(243, 210, 178, 0.18);
  box-shadow: inset 0 0 0 1px rgba(243, 210, 178, 0.35);
}
.gallery-toggle:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--accent);
}

/* Respuesta táctil: sin retardo de 300ms, sin zoom por doble-tap, sin flash gris */
.btn-ghost, .btn-playpause, .btn-help, .btn-audio,
.detail-close, .gallery-dock-back, .gallery-toggle, .hotspot {
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}

/* Hint */
.hint {
  position: absolute; z-index: 22; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  font-family: var(--font);
  font-size: 12.5px;
  font-weight: 500;
  letter-spacing: var(--ui-tracking-wide);
  text-transform: uppercase;
  color: var(--ink);
  background: var(--scrim);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(243,236,224,0.18);
  border-radius: 999px; padding: 12px 22px;
  transition: opacity 600ms var(--ease);
}
.hint.is-out { opacity: 0; }

/* ---------- SPLASH ---------- */
.splash {
  position: fixed; inset: 0; z-index: 50;
  display: grid; place-content: center; justify-items: center; gap: 14px;
  background: rgba(20, 17, 13, 0.42);
  transition: opacity 700ms var(--ease);
}
.splash.is-out { opacity: 0; pointer-events: none; }
.wordmark {
  font-family: var(--rum-brand-font);
  font-size: 40px;
  letter-spacing: 0.06em;
  font-weight: 700;
  padding-left: 0.06em;
}
.splash .sub { font-family: var(--font); font-size: 11px; font-weight: 500; letter-spacing: 0.34em; text-transform: uppercase; color: var(--muted); }
.loader { margin-top: 18px; width: 140px; height: 2px; background: rgba(243,236,224,0.14); border-radius: 2px; overflow: hidden; }
.loader span { display: block; height: 100%; width: 40%; background: var(--accent); border-radius: 2px; animation: slide 1.3s var(--ease) infinite; }
@keyframes slide { 0% { transform: translateX(-120%); } 100% { transform: translateX(360%); } }
.splash-status {
  margin: 10px 0 0; font-family: var(--font); font-size: 11.5px; font-weight: 500;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted);
}

/* ---------- FATAL ---------- */
.fatal {
  position: fixed; inset: 0; z-index: 60; display: grid; place-content: center;
  text-align: center; padding: 8vw; color: var(--muted); font-size: 14px; line-height: 1.7;
  background: var(--bg);
}
.fatal b { color: var(--ink); }

/* ---------- ROTATE GATE (móvil portrait → landscape) ---------- */
.rotate-gate {
  position: fixed; inset: 0; z-index: 80;
  display: none; place-content: center; justify-items: center;
  padding: max(24px, env(safe-area-inset-top)) max(24px, env(safe-area-inset-right))
    max(24px, env(safe-area-inset-bottom)) max(24px, env(safe-area-inset-left));
  background:
    radial-gradient(120% 80% at 50% 30%, rgba(243, 210, 178, 0.12), transparent 55%),
    rgba(20, 17, 13, 0.96);
  color: var(--ink);
  text-align: center;
}
.rotate-gate:not([hidden]) { display: grid; }
.rotate-gate-inner {
  display: grid; justify-items: center; gap: 10px; max-width: 280px;
  pointer-events: auto; position: relative; z-index: 1;
}
.rotate-icon {
  color: var(--accent);
  margin-bottom: 6px;
  animation: rotate-phone 1.8s var(--ease) infinite;
}
@keyframes rotate-phone {
  0%, 100% { transform: rotate(0deg); }
  35% { transform: rotate(90deg); }
  65% { transform: rotate(90deg); }
}
.rotate-title {
  margin: 0; font-family: var(--rum-font); font-size: 22px; font-weight: 700;
  letter-spacing: 0.04em;
}
.rotate-sub {
  margin: 0; font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--muted); line-height: 1.5;
}
.rotate-lock {
  margin-top: 14px; appearance: none; cursor: pointer;
  font: 700 12px/1 var(--font); letter-spacing: 0.14em; text-transform: uppercase;
  color: #1a1510; background: var(--accent); border: 0; border-radius: 999px;
  padding: 14px 22px;
  position: relative; z-index: 2;
  pointer-events: auto;
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
  min-height: 48px;
  min-width: 200px;
}
.rotate-lock:active { transform: scale(0.98); }

/*
  Layout horizontal en portrait: rota el stage 90°.
  El <video> se oculta y se pinta en canvas (ver player.js) porque iOS
  no compone video dentro de un ancestro con transform.
*/
html.force-landscape {
  overflow: hidden;
}
html.force-landscape body {
  position: fixed; inset: 0; width: 100%; height: 100%; overflow: hidden;
  background: var(--bg);
}
html.force-landscape .stage {
  visibility: visible !important;
  pointer-events: auto !important;
  /* Anula .stage { min-height: 100dvh }: si no, el stage queda cuadrado
     (100vh×100vh) al rotar y HUD / «Volver» salen del viewport físico. */
  inset: auto;
  width: 100vh;
  height: 100vw;
  min-width: 0;
  min-height: 0;
  max-width: 100vh;
  max-height: 100vw;
  top: 50%;
  left: 50%;
  right: auto;
  bottom: auto;
  transform: translate(-50%, -50%) rotate(90deg);
  transform-origin: center center;
}
html.force-landscape .splash,
html.force-landscape #boot-poster {
  inset: auto;
  width: 100vh;
  height: 100vw;
  min-width: 0;
  min-height: 0;
  max-width: 100vh;
  max-height: 100vw;
  top: 50%;
  left: 50%;
  right: auto;
  bottom: auto;
  transform: translate(-50%, -50%) rotate(90deg);
  transform-origin: center center;
}
html.force-landscape .rotate-gate { display: none !important; }

/* El stage va rotado 90°, así que dentro de él `vh` mide el lado largo del
   teléfono —el ancho de la caja rotada, no su alto—. Las alturas del selector
   de planta se pasaban del stage y el plano salía cortado: aquí van en vw, que
   es el alto real de la caja rotada. */
html.force-landscape .floorplan {
  gap: clamp(6px, 1.6vw, 14px);
  padding: clamp(8px, 2.6vw, 20px) clamp(14px, 3vh, 36px);
}
html.force-landscape .floorplan-img { height: min(44vw, calc(100vw - 205px)); }
html.force-landscape .floorplan-legend { max-height: 22vw; overflow-y: auto; }

/* Tap-to-play: iOS / Low Power — gesto discreto, la foto manda */
.tap-play {
  position: absolute; inset: 0; z-index: 40;
  display: none; place-content: center; justify-items: center;
  background: rgba(12, 9, 6, 0.22);
  border: 0; cursor: pointer; color: var(--ink);
  font-family: var(--font);
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  gap: 14px;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
}
.tap-play:not([hidden]) { display: grid; }
.tap-play-icon {
  width: 48px; height: 48px; border-radius: 999px;
  display: grid; place-items: center;
  background: rgba(12, 9, 6, 0.28);
  color: var(--ink);
  border: 1px solid rgba(243, 236, 224, 0.42);
  box-shadow: none;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: border-color 240ms var(--ease), background 240ms var(--ease), transform 120ms var(--ease);
}
.tap-play:active .tap-play-icon {
  transform: scale(0.96);
  border-color: var(--accent);
  background: rgba(12, 9, 6, 0.4);
}
.tap-play-icon svg {
  width: 16px; height: 16px;
  margin-left: 2px;
  opacity: 0.92;
}
.tap-play-label {
  color: rgba(243, 236, 224, 0.7);
  text-shadow: 0 1px 18px rgba(0, 0, 0, 0.55);
  font-weight: 500;
}
@media (prefers-reduced-motion: no-preference) {
  .tap-play:not([hidden]) .tap-play-icon {
    animation: tapPlayBreath 3.2s var(--ease) infinite;
  }
}
@keyframes tapPlayBreath {
  0%, 100% { border-color: rgba(243, 236, 224, 0.32); }
  50% { border-color: color-mix(in srgb, var(--accent) 65%, rgba(243, 236, 224, 0.4)); }
}

@media (max-width: 640px) {
  :root { --bar-h: 3.4vh; }
  .scene-title {
    font-size: 10.5px;
    font-weight: 500;
    letter-spacing: 0.16em;
    /* Entre Volver (izq) y play (der); sin esto el tracking largo desborda el viewport. */
    max-width: calc(100vw - 11.5rem);
    overflow: hidden;
    text-overflow: ellipsis;
    text-align: center;
    bottom: max(7.4vh, calc(env(safe-area-inset-bottom) + 28px));
  }
  .scene-title b { letter-spacing: 0.14em; }

  /* Etiquetas de hotspot: tipografía de cine, no system genérica.
     El índice va incluido: en la fila se dibuja como un hotspot más y tiene
     que encoger con ellos, o queda una pastilla de otro tamaño en la fila. */
  .hotspot .label,
  .hotspots > .rooms .rooms-toggle-label {
    font-size: 11.5px;
    font-weight: 500;
    letter-spacing: 0.05em;
    padding: 8px 12px;
    max-width: min(44vw, 168px);
    white-space: normal;
    text-align: left;
    line-height: 1.3;
  }

  /* Sin hover en touch: etiquetas siempre visibles cuando el hotspot está armado */
  .hotspots.is-armed .hotspot .label {
    opacity: 1;
    transform: translateY(-50%) translateX(0);
  }
  .hotspots.is-armed .hotspot[data-side="right"] .label {
    transform: translateY(-50%) translateX(0);
  }
  .hotspots.is-armed .hotspot[data-side="below"] .label {
    transform: translateX(-50%) translateY(0);
    text-align: center;
    max-width: min(56vw, 200px);
  }

  /* HUD nunca se esconde; botones siempre opacos (no dependen de la animación).
     Importante: NO resetear transform en .scene-title — vive en left:50% +
     translateX(-50%). Sin eso el título arranca a mitad de pantalla y se corta. */
  .hud.is-hidden { opacity: 1; }
  .hud.is-live > *:not([hidden]):not(.scene-title) {
    opacity: 1 !important;
    transform: none !important;
    animation: none !important;
  }
  .hud.is-live .scene-title {
    opacity: 1 !important;
    transform: translateX(-50%) translateY(0) !important;
    animation: none !important;
  }

  /* Targets táctiles cómodos (≥44px) y anclados fuera del notch / barra de inicio
     (viewport-fit=cover): así los botones dejan de "esconderse" bajo el hardware. */
  .btn-help,
  .btn-audio {
    width: 44px; height: 44px; font-size: 15px;
    top: max(3vh, calc(env(safe-area-inset-top) + 12px));
    z-index: 58;
  }
  .btn-help { right: max(3.5vw, calc(env(safe-area-inset-right) + 16px)); }
  .btn-audio { right: max(calc(3.5vw + 54px), calc(env(safe-area-inset-right) + 70px)); }

  .btn-home {
    top: max(3vh, calc(env(safe-area-inset-top) + 12px));
    right: max(3.5vw, calc(env(safe-area-inset-right) + 16px));
    min-height: 40px;
    padding: 9px 14px;
    z-index: 58;
  }
  .hud.has-home .btn-help { right: calc(max(3.5vw, calc(env(safe-area-inset-right) + 16px)) + 114px); }
  .hud.has-home .btn-audio { right: calc(max(3.5vw, calc(env(safe-area-inset-right) + 16px)) + 168px); }

  .btn-playpause {
    width: 48px; height: 48px;
    right: max(3.5vw, calc(env(safe-area-inset-right) + 16px));
    bottom: max(7vh, calc(env(safe-area-inset-bottom) + 20px));
    z-index: 58;
  }
  /* Refuerzo móvil: el HUD !important no debe resucitar el pause junto al tap. */
  .stage.is-intro #playpause,
  .stage.is-tap-play #playpause,
  .stage.is-awaiting-play #playpause,
  .stage.is-intro .hud #playpause,
  .stage.is-tap-play .hud #playpause,
  .stage.is-awaiting-play .hud #playpause {
    display: none !important;
    opacity: 0 !important;
    visibility: hidden !important;
    pointer-events: none !important;
  }

  .btn-ghost {
    left: max(3.5vw, calc(env(safe-area-inset-left) + 16px));
    bottom: max(7vh, calc(env(safe-area-inset-bottom) + 22px));
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.14em;
    padding: 13px 20px 13px 16px;
    z-index: 58;
    /* Fondo sólido: sin esto el borde ghost se pierde en suelos/techos oscuros. */
    background: var(--scrim);
    backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
    border-color: rgba(243, 236, 224, 0.28);
  }
  .btn-lean {
    left: max(3.5vw, calc(env(safe-area-inset-left) + 16px));
    bottom: calc(max(7vh, calc(env(safe-area-inset-bottom) + 22px)) + 50px);
  }

  .detail-close {
    padding: 14px 22px 14px 17px;
    font-size: 12px;
    font-weight: 500;
    letter-spacing: 0.14em;
    bottom: max(11vh, calc(env(safe-area-inset-bottom) + 64px));
    z-index: 60;
  }

  .material-card {
    /* En móvil las fichas tapan hotspots / Volver — solo desktop (gutter). */
    display: none !important;
    visibility: hidden !important;
    opacity: 0 !important;
    pointer-events: none !important;
  }
  .material-card-title { font-size: 1.1rem; }
  .material-card-body { font-size: 12px; line-height: 1.5; }

  /* Por si el HUD móvil fuerza opacity:1 — el pause no debe tapar el detalle. */
  .stage.is-detail .hud #playpause,
  .stage.is-gallery .hud #playpause,
  .stage.is-detail .hud #back,
  .stage.is-gallery .hud #back {
    opacity: 0 !important;
    pointer-events: none !important;
    visibility: hidden !important;
  }

  .gallery-dock {
    bottom: max(10vh, calc(env(safe-area-inset-bottom) + 56px));
    gap: 8px;
    z-index: 58;
  }
  .gallery-dock-back {
    padding: 12px 15px 12px 12px;
    font-size: 11.5px;
    letter-spacing: 0.12em;
  }
  .gallery-toggle {
    padding: 12px 18px;
    font-size: 11.5px;
    letter-spacing: 0.14em;
  }

  /* Punto de interés: área de toque más grande y punto ligeramente mayor */
  .hotspot,
  .hotspots > .rooms .rooms-toggle { width: 56px; height: 56px; }
  .hotspot .dot,
  .hotspots > .rooms .rooms-toggle-dots { width: 15px; height: 15px; }

  .hint {
    font-size: 11.5px;
    letter-spacing: 0.14em;
    padding: 13px 20px;
  }
}

/* Un teléfono acostado mide 844×390: ancho de tablet y alto de nada. El trato
   móvil de arriba va por ancho, así que a esos 390 px de alto le caía la
   pastilla de escritorio y cinco de ellas se comían la banda de abajo del
   cuadro —justo encima del rótulo de la escena. La segunda cota es el mismo
   teléfono en vertical con el escenario rotado, donde el alto útil es el ancho
   del viewport. */
@media (hover: none) and (pointer: coarse) and (max-height: 540px),
       (hover: none) and (pointer: coarse) and (max-width: 540px) {
  .hotspot .label,
  .hotspots > .rooms .rooms-toggle-label {
    font-size: 10px;
    letter-spacing: 0.04em;
    padding: 6px 10px;
    max-width: min(34vw, 132px);
  }
  /* 44px, el mínimo de dedo, en vez de los 56 del teléfono en vertical: aquí lo
     que falta es alto, y la fila entera cabe en la banda baja. */
  .hotspot,
  .hotspots > .rooms .rooms-toggle { width: 44px; height: 44px; }
  .hotspot .dot,
  .hotspots > .rooms .rooms-toggle-dots { width: 11px; height: 11px; }

  /* La banda de abajo no da para las dos cosas: el rótulo quedaba detrás de las
     pastillas, asomando a trozos entre una y otra. Le cede el sitio a la fila y
     vuelve en cuanto se pliega, que es justo cuando hay hueco para leerlo — el
     nombre del cuarto sigue puesto en la ficha mientras tanto. */
  .stage:not(.is-lean) .hotspots.is-armed ~ .hud .scene-title {
    opacity: 0 !important;
  }
}

/* Modo limpio. En táctil el chrome no se esconde solo —la gente creía que se
   había roto— pero en un teléfono acostado la fila de nombres tapa el mueble que
   se está enseñando. Esto lo pone en manos del visitante: un toque retira los
   nombres, otro los devuelve. Sólo en táctil: en escritorio sobra el sitio y el
   cine-idle ya hace este trabajo sin pedir nada. */
.btn-lean {
  position: absolute;
  left: max(3.5vw, 20px);
  /* Mismo borde izquierdo que #back (.btn-ghost): apilado arriba de él, no
     encima — comparten esquina en móvil, que es donde ambos existen a la vez. */
  bottom: calc(max(7vh, 34px) + 50px);
  width: 38px;
  height: 38px;
  border-radius: 999px;
  display: none;
  place-items: center;
  color: var(--muted);
  background: var(--scrim);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(243, 236, 224, 0.22);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: color 200ms var(--ease), border-color 200ms var(--ease);
}
/* Fuera del baile de entrada del HUD: aparece cuando se arma la fila, que es
   después de que esa animación ya corrió, y con opacity heredada se quedaba
   invisible aunque ocupara su sitio. */
.hud > .btn-lean { opacity: 1; transform: none; }
.btn-lean[aria-pressed="true"] { color: var(--accent); border-color: var(--accent-dim); }
.btn-lean .lean-slash { opacity: 0; transition: opacity 200ms var(--ease); }
.btn-lean[aria-pressed="true"] .lean-slash { opacity: 1; }
/* Visible en cualquier momento del recorrido, no sólo con la fila de hotspots
   armada: en galería (`is-gallery`) los hotspots se desarman a propósito al
   entrar, pero el riel de vistas (Main/Rear/Side...) sigue ahí encima de la
   foto y también hay que poder plegarlo. Los modos sin nada que plegar quedan
   fuera por la lista de abajo, no por depender de `is-armed`. */
@media (hover: none) and (pointer: coarse) {
  .hud .btn-lean { display: grid; }
}
/* Sobre la portada, el plano o marcando no hay fila que plegar (o plegarla
   sería quitarle al visitante el control que está usando). La galería de
   fotos (`is-gallery`) sí tiene su propio riel de botones sobre la imagen
   — Main/Rear/Side/etc — así que se pliega igual que la fila de hotspots. */
.stage.is-intro .btn-lean,
.stage.is-floorplan .btn-lean,
.stage.is-detail .btn-lean,
.stage.is-finishes .btn-lean,
.stage.is-markup .btn-lean {
  display: none !important;
}
/* Plegado: se van los nombres, el par de navegación y —en galería— el riel de
   vistas. Los puntos se quedan — siguen diciendo dónde hay algo y siguen
   llevando a su sitio, así que el cuadro queda limpio sin volverse una foto
   muerta. */
.stage.is-lean .hotspots .hotspot .label {
  opacity: 0;
  pointer-events: none;
}
.stage.is-lean .hotspots > .rooms {
  opacity: 0;
  pointer-events: none;
}
.stage.is-lean .gallery-dock {
  /* !important porque `.gallery-dock:not([hidden])` ya fuerza opacity:1
     !important para no perderse contra su propia animación de entrada. */
  opacity: 0 !important;
  pointer-events: none;
}

/* ═══════════════════════════════════════════════════════════
   SKIN · Casa Tizón — refugio volcánico / madera oscura
   Distinto de Cabo (peach pills + Outfit + letterbox).
   ═══════════════════════════════════════════════════════════ */
html.skin-tizon {
  --bg: #100e0c;
  --bar: #0a0908;
  --ink: #f2ebe1;
  --muted: rgba(242, 235, 225, 0.58);
  --accent: #C08E52;
  --accent-dim: rgba(192, 142, 82, 0.2);
  --scrim: rgba(10, 8, 6, 0.62);
  --font: "DM Sans", ui-sans-serif, system-ui, sans-serif;
  --rum-font: "Fraunces", Georgia, "Times New Roman", serif;
  --ui-tracking: 0.08em;
  --ui-tracking-wide: 0.18em;
  --tizon-line: rgba(242, 235, 225, 0.18);
  --xfade: 720ms;
}

html.skin-tizon .vignette {
  background:
    radial-gradient(120% 90% at 50% 40%, transparent 35%, rgba(8, 7, 6, 0.55) 100%),
    linear-gradient(180deg, rgba(8, 7, 6, 0.28) 0%, transparent 18%, transparent 72%, rgba(8, 7, 6, 0.5) 100%);
}

html.skin-tizon .hotspot .dot {
  width: 11px;
  height: 11px;
  border-radius: 1px;
  background: transparent;
  border: 1.5px solid var(--accent);
  box-shadow: none;
  transform: rotate(45deg);
}
html.skin-tizon .hotspot .dot::before {
  content: "";
  position: absolute;
  inset: 2.5px;
  background: var(--accent);
  border-radius: 0.5px;
}
html.skin-tizon .hotspot .dot::after {
  inset: -5px;
  border-radius: 1px;
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--accent) 55%, transparent);
  animation: tizonPulse 3.4s var(--ease) infinite;
}
@keyframes tizonPulse {
  0%   { transform: scale(1); opacity: 0.55; }
  70%  { transform: scale(1.85); opacity: 0; }
  100% { transform: scale(1.85); opacity: 0; }
}
html.skin-tizon .hotspot:hover .dot,
html.skin-tizon .hotspot:focus-visible .dot {
  transform: rotate(45deg) scale(1.12);
  background: transparent;
  box-shadow: none;
}
html.skin-tizon .hotspot:focus-visible .dot {
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 35%, transparent);
}

html.skin-tizon .hotspot .label {
  border-radius: 0;
  padding: 8px 12px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  background: rgba(12, 10, 8, 0.78);
  border: 1px solid var(--tizon-line);
  border-left: 2px solid var(--accent);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35);
}

html.skin-tizon .btn-ghost,
html.skin-tizon .detail-close,
html.skin-tizon .gallery-dock-back,
html.skin-tizon .gallery-toggle,
html.skin-tizon .btn-help,
html.skin-tizon .btn-audio,
html.skin-tizon .btn-playpause {
  border-radius: 0;
  letter-spacing: 0.16em;
}
html.skin-tizon .btn-ghost {
  border-color: var(--tizon-line);
  background: rgba(12, 10, 8, 0.45);
  padding: 11px 16px 11px 12px;
}
html.skin-tizon .btn-ghost:hover {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
}
html.skin-tizon .scene-title {
  font-family: var(--rum-font);
  font-weight: 500;
  letter-spacing: 0.22em;
  font-size: 13px;
  color: rgba(242, 235, 225, 0.72);
}
html.skin-tizon .btn-help,
html.skin-tizon .btn-audio,
html.skin-tizon .btn-playpause {
  border-color: var(--tizon-line);
  background: rgba(12, 10, 8, 0.4);
}
html.skin-tizon .btn-help:hover,
html.skin-tizon .btn-audio:hover,
html.skin-tizon .btn-playpause:hover {
  border-color: var(--accent);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--ink);
}

html.skin-tizon .intro-inner {
  border: 1px solid var(--tizon-line);
  background: rgba(10, 8, 6, 0.55);
  padding: 28px 32px;
  border-radius: 0;
}
html.skin-tizon .intro-line {
  font-family: var(--rum-font);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: none;
  font-size: clamp(1.35rem, 3.5vw, 1.85rem);
}
html.skin-tizon .intro-rule {
  width: 42px;
  height: 1px;
  background: var(--accent);
  margin: 14px auto;
  opacity: 0.85;
}
html.skin-tizon .intro-by {
  letter-spacing: 0.2em;
  text-transform: uppercase;
  font-size: 11px;
  color: var(--muted);
}
html.skin-tizon .intro-by strong {
  font-family: var(--rum-brand-font);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: none;
}

html.skin-tizon .splash {
  background:
    radial-gradient(90% 70% at 50% 20%, rgba(192, 142, 82, 0.1), transparent 55%),
    var(--bg);
}
html.skin-tizon .wordmark {
  font-family: var(--rum-brand-font);
  font-weight: 700;
  letter-spacing: 0.06em;
  font-size: 34px;
}
html.skin-tizon .splash .sub {
  letter-spacing: 0.28em;
}
html.skin-tizon .loader,
html.skin-tizon .loader span {
  border-radius: 0;
}

html.skin-tizon .detail-close {
  border-color: var(--tizon-line);
  background: rgba(12, 10, 8, 0.55);
}
html.skin-tizon .gallery-dock {
  border-radius: 0;
  border: 1px solid var(--tizon-line);
  background: rgba(12, 10, 8, 0.72);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
html.skin-tizon .gallery-toggle.is-active {
  background: var(--accent);
  color: #1a1510;
  border-radius: 0;
}
html.skin-tizon .tap-play {
  background: rgba(8, 7, 6, 0.26);
  border: 0;
}
html.skin-tizon .tap-play-icon {
  border-radius: 0;
  border: 1px solid color-mix(in srgb, var(--accent) 50%, transparent);
  background: rgba(10, 8, 6, 0.32);
  color: var(--ink);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
html.skin-tizon .tap-play:active .tap-play-icon {
  border-color: var(--accent);
  background: rgba(10, 8, 6, 0.48);
}
html.skin-tizon .tap-play-label {
  letter-spacing: 0.22em;
  color: rgba(242, 235, 225, 0.68);
}
html.skin-tizon .hint {
  border-radius: 0;
  letter-spacing: 0.18em;
}

@media (max-width: 640px) {
  html.skin-tizon .hotspot .dot { width: 12px; height: 12px; }
  html.skin-tizon .scene-title {
    font-size: 11px;
    letter-spacing: 0.14em;
    max-width: calc(100vw - 11.5rem);
  }
  html.skin-tizon .scene-title b { letter-spacing: 0.12em; }
  html.skin-tizon .material-card {
    display: none !important;
    visibility: hidden !important;
    opacity: 0 !important;
    pointer-events: none !important;
  }
}

/* Portrait projects (Casa Tizón): never allow CSS landscape rotate. */
html.prefer-portrait.force-landscape .stage,
html.prefer-portrait.force-landscape .splash,
html.prefer-portrait.force-landscape #boot-poster {
  inset: 0 !important;
  top: 0 !important;
  left: 0 !important;
  right: 0 !important;
  bottom: 0 !important;
  width: 100% !important;
  height: 100% !important;
  min-width: 0 !important;
  min-height: 100dvh !important;
  max-width: none !important;
  max-height: none !important;
  transform: none !important;
}
html.prefer-portrait .rotate-gate { display: none !important; }
html.prefer-portrait.force-landscape .frame-video.is-on { opacity: 1 !important; }
html.prefer-portrait.force-landscape .frame-canvas.is-on { opacity: 0 !important; }

/* ---------- KEYMAP DE BOLSILLO ----------
   El plano completo es una consulta que interrumpe: se abre, se lee, se cierra.
   Esta versión chica va puesta sobre cada toma para responder sin salir de ella
   las dos preguntas que se hacen solas —dónde estoy y hacia dónde miro—, y al
   pulsarla abre el plano grande, que es donde se elige a dónde ir.

   Cuelga de la capa de hotspots (no del stage) para ceñirse al rectángulo
   pintado: con `scale-down` el cuadro no llega a los bordes y anclada al
   viewport quedaría flotando sobre la banda negra. El desplazamiento superior
   deja libre la firma RÜM, que sí va fija al viewport y en móvil cae encima. */
.minimap {
  position: absolute;
  z-index: 26;
  /* En la columna de la ficha del espacio, no sobre el render: comparte su
     margen izquierdo y se apila justo encima, así que «dónde estoy» y «qué
     espacio es» se leen como una sola cosa y la toma queda despejada. El JS
     mide la ficha y pone `--minimap-bottom`; la cota de reserva es para las
     escenas que no traen ficha. */
  left: max(3.5vw, 20px);
  bottom: var(--minimap-bottom, calc(max(18vh, 110px) + 96px));
  /* El ancho lo pone el JS con la banda negra que hay de verdad hasta el
     cuadro: escrito a mano, o se queda chico en un monitor o se sube al
     render en una ventana angosta. Los conos se miden contra ese ancho para
     que crezcan con el plano y no se vuelvan garrapatas en la versión grande. */
  width: var(--minimap-w, 156px);
  --cone-w: calc(var(--minimap-w, 156px) * 0.3);
  --cone-h: calc(var(--minimap-w, 156px) * 0.26);
  margin: 0;
  padding: 9px 9px 10px;
  display: block;
  text-align: left;
  border: 1px solid rgba(243, 236, 224, 0.16);
  border-radius: 4px;
  background: rgba(12, 9, 6, 0.66);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  color: var(--ink);
  font-family: var(--font);
  pointer-events: auto;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 460ms var(--ease), transform 460ms var(--ease),
    border-color 200ms var(--ease);
}
.minimap[hidden] { display: none; }
.minimap.is-live { opacity: 1; transform: translateY(0); }
.minimap:hover { border-color: var(--accent-dim, rgba(243, 210, 178, 0.45)); }
/* Abrir el plano grande: capa transparente sobre el papel y por debajo de los
   puntos, para que cada clic vaya a lo que se pulsó. */
.minimap-open {
  position: absolute;
  inset: 0;
  z-index: 1;
  appearance: none;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: zoom-in;
}
.minimap-open:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.minimap-level {
  display: block;
  margin: 0 0 6px;
  font-size: 8px;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--accent);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.minimap-level:empty { display: none; }

.minimap-stage {
  position: relative;
  display: block;
  line-height: 0;
}
.minimap-img {
  display: block;
  width: 100%;
  height: auto;
  /* Mismo revelado que el plano grande (.floorplan-img): gris, negativo cuando
     el plano viene en negro sobre blanco, y el mismo tinte cálido — cambiar de
     tamaño no debe cambiar de papel. La clase is-inverted marca los que ya vienen
     claros sobre oscuro y sólo piden que se les quite el color. */
  --plan-warm: sepia(0.45) saturate(1.5) hue-rotate(-12deg);
  filter: grayscale(1) invert(1) contrast(1.08) brightness(1.04) var(--plan-warm);
  mix-blend-mode: screen;
  opacity: 0.94;
}
.minimap-img.is-inverted {
  filter: grayscale(1) contrast(1.08) var(--plan-warm);
}

/* Los demás espacios de la planta, sin nombre: dan escala y contexto sin
   competir con el punto que importa. Cada uno lleva su cono en sombra cuando
   se le midió el rumbo, así que de un vistazo se ve hacia dónde mira cada toma
   del nivel y no sólo dónde está. En tinta y no en durazno: el durazno es del
   punto en el que estás parado, y con treinta conos iguales no se distinguiría. */
.minimap-dots { position: absolute; inset: 0; z-index: 2; pointer-events: none; }
/* Cada pin es un botón que lleva a ese espacio. La caja mide 22px aunque el
   punto mida 4: en táctil el dedo no acierta un punto de 4px, y agrandar el
   punto para que se acierte ensucia el plano. Caja de dedo grande, marca chica. */
.minimap-pin {
  position: absolute;
  width: 22px;
  height: 22px;
  margin: -11px 0 0 -11px;
  appearance: none;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
  pointer-events: auto;
}
.minimap-pin:focus-visible { outline: 2px solid var(--accent); outline-offset: 0; border-radius: 999px; }
.minimap-pin-dot {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 4px;
  height: 4px;
  margin: -2px 0 0 -2px;
  border-radius: 999px;
  /* El plano revelado es papel claro: un punto hueso encima desaparecía. */
  background: rgba(26, 18, 10, 0.62);
  box-shadow: 0 0 0 1px rgba(243, 236, 224, 0.32);
  transition: background 160ms var(--ease), box-shadow 160ms var(--ease),
    width 160ms var(--ease), height 160ms var(--ease), margin 160ms var(--ease);
}
.minimap-pin:hover .minimap-pin-dot,
.minimap-pin:focus-visible .minimap-pin-dot {
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  background: var(--accent);
  box-shadow: 0 0 0 2px rgba(12, 9, 6, 0.8), 0 0 10px rgba(243, 210, 178, 0.55);
}
.minimap-pin-cone {
  position: absolute;
  left: 50%;
  bottom: 50%;
  width: calc(var(--cone-w) * 0.66);
  height: calc(var(--cone-h) * 0.66);
  margin-left: calc(var(--cone-w) * -0.33);
  transform-origin: 50% 100%;
  transform: rotate(var(--dir, 0deg));
  pointer-events: none;
  clip-path: polygon(50% 100%, 4% 6%, 22% 0%, 78% 0%, 96% 6%);
  background: linear-gradient(
    to top,
    rgba(26, 18, 10, 0.45),
    rgba(26, 18, 10, 0.12) 58%,
    rgba(26, 18, 10, 0)
  );
}

.minimap-here {
  position: absolute;
  z-index: 3;
  /* Es una marca, no un destino: no debe robarle el clic a la capa que abre el
     plano grande ni a los pines de al lado. */
  pointer-events: none;
  width: 0;
  height: 0;
  /* left/top los pone el JS en % del plano */
}
.minimap-dot {
  position: absolute;
  left: 0;
  top: 0;
  width: 7px;
  height: 7px;
  margin: -3.5px 0 0 -3.5px;
  border-radius: 999px;
  background: var(--accent);
  box-shadow: 0 0 0 2px rgba(12, 9, 6, 0.75), 0 0 10px rgba(243, 210, 178, 0.6);
}
/* Cono de visión: vértice en el punto, abriendo hacia donde mira la toma.
   0° = arriba en el plano; el JS gira en sentido horario. Sin rumbo en el pin
   no se pinta: media casa apuntando al norte por defecto mentiría. */
.minimap-cone {
  position: absolute;
  left: 0;
  bottom: 0;
  width: var(--cone-w);
  height: var(--cone-h);
  margin-left: calc(var(--cone-w) / -2);
  transform-origin: 50% 100%;
  transform: rotate(var(--dir, 0deg));
  clip-path: polygon(50% 100%, 4% 6%, 22% 0%, 78% 0%, 96% 6%);
  background: linear-gradient(
    to top,
    rgba(243, 210, 178, 0.92),
    rgba(243, 210, 178, 0.34) 58%,
    rgba(243, 210, 178, 0)
  );
  /* El durazno sobre papel claro se perdía: el contorno oscuro le da borde sin
     meterle una línea al dibujo. */
  filter: drop-shadow(0 0 2.5px rgba(24, 17, 10, 0.65));
  pointer-events: none;
}
.minimap:not(.has-dir) .minimap-cone { display: none; }
/* Sin cono, el punto solo pulsa: se lee igual como “estás aquí”. */
.minimap:not(.has-dir) .minimap-dot { animation: minimapPulse 2.4s var(--ease) infinite; }
@keyframes minimapPulse {
  0%, 100% { box-shadow: 0 0 0 2px rgba(12, 9, 6, 0.75), 0 0 0 0 rgba(243, 210, 178, 0.5); }
  55% { box-shadow: 0 0 0 2px rgba(12, 9, 6, 0.75), 0 0 0 7px rgba(243, 210, 178, 0); }
}

/* Modo de trazado de rumbos (?dirs=1): arrastrar sobre el plano apunta el cono
   de la escena actual y C copia el JSON. No es chrome de cliente — es la forma
   de medir 29 rumbos sin escribirlos a ciegas en el index.json. */
.minimap-readout {
  display: block;
  margin-top: 7px;
  font-size: 9px;
  letter-spacing: 0.08em;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.minimap-readout[hidden] { display: none; }
.minimap.is-dirs { cursor: crosshair; width: 184px; }

@media (max-width: 820px) {
  .minimap {
    width: var(--minimap-w, 104px);
    --cone-w: calc(var(--minimap-w, 104px) * 0.32);
    --cone-h: calc(var(--minimap-w, 104px) * 0.28);
    left: max(3.5vw, 16px);
    bottom: var(--minimap-bottom, calc(max(30vh, 200px) + 84px));
    padding: 7px 7px 8px;
  }
  .minimap-dot { width: 6px; height: 6px; margin: -3px 0 0 -3px; }
  .minimap-pin { width: 26px; height: 26px; margin: -13px 0 0 -13px; }
}

/* Se retira con el resto del chrome: modo limpio, portada, plano grande, fichas
   y marcado. En marcado además estorbaría justo lo que se está rayando. */
.stage.is-lean .minimap,
.stage.is-intro .minimap,
.stage.is-floorplan .minimap,
.stage.is-detail .minimap,
.stage.is-gallery .minimap,
.stage.is-finishes .minimap,
.stage.is-markup .minimap {
  opacity: 0;
  pointer-events: none;
}
