/* ============================================================
   SPECTRA × MAREA RESIDENCES — Estilos principales (styles.css)
   ============================================================ */

/* ─── FUENTES PROPIAS ─────────────────────────────────────────
   Servidas desde el propio dominio (assets/fonts/), no desde Google Fonts:
   una petición menos a un tercero y el .htaccess ya las cachea 1 año.
   Los .ttf originales (47 KB y 688 KB) se pasaron a WOFF2 con subconjunto
   latino — incluye acentos, ñ, ¿¡ y las flechas del sitio — quedando en
   20 KB y 56 KB. Para regenerarlas:
     python -m fontTools.subset <fuente>.ttf --unicodes=U+0000-00FF,U+0131,
       U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+2000-206F,U+2074,
       U+20AC,U+2122,U+2190-2193,U+2212,U+2215,U+2713,U+FEFF,U+FFFD
       --layout-features='*' --flavor=woff2 --output-file=<salida>.woff2  */
@font-face {
  font-family: "Amerigo BT";
  src: url("assets/fonts/amerigo-bt.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
/* Negrita REAL. Los títulos (h1-h4) piden font-weight:700 y antes solo existía
   la Roman de 400, así que el navegador fabricaba una negrita sintética
   (engrosaba los trazos por su cuenta), que en una fuente de serifas
   acampanadas como esta se ve embarrada. Con el peso de verdad los títulos
   quedan como los dibujó la fundidora. */
@font-face {
  font-family: "Amerigo BT";
  src: url("assets/fonts/amerigo-bt-bold.woff2") format("woff2");
  font-weight: 700;
  font-style: normal;
  font-display: swap;
}
/* Variable (eje wght 100-900) en un solo archivo: cubre todos los pesos que
   usa el sitio sin bajar un archivo por peso. */
@font-face {
  font-family: "Montserrat";
  src: url("assets/fonts/montserrat-var.woff2") format("woff2");
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}
/* Cormorant Garamond: la pedía .hl más abajo desde siempre, pero NUNCA se
   declaró aquí -- así que caía callada a Georgia y las frases destacadas se
   veían con otra letra. Ahora va auto-hospedada (variable, subconjunto
   latino, vía Fontsource) igual que las otras dos. */
@font-face {
  font-family: "Cormorant Garamond";
  src: url("assets/fonts/cormorant-garamond-var.woff2") format("woff2");
  font-weight: 300 700;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Cormorant Garamond";
  src: url("assets/fonts/cormorant-garamond-var-italic.woff2") format("woff2");
  font-weight: 300 700;
  font-style: italic;
  font-display: swap;
}

/* ─── FONDO DE NUBES DEL INDEX ────────────────────────────
   Mismo campo volumétrico que el masterplan (ver assets/nubes.js), aquí como
   fondo de toda la página. Van fijos y en z-index negativo: quedan por detrás
   de absolutamente todo, así que no hace falta tocar ninguna sección.

   El degradado es el respaldo cuando no hay WebGL — y además da el color base
   sobre el que el canvas, que es transparente, dibuja las nubes. */
/* El cielo que queda DETRÁS de las nubes.
   Antes era beige plano de arriba abajo, y por eso la página se leía como
   "recuadros beige" y no como cielo: por donde la nube era fina, lo que
   asomaba era color papel. Ahora arriba entra un azul muy lavado —el aire— y
   abajo se vuelve al hueso cálido de la marca, así que los huecos entre nubes
   se leen como cielo. Sigue siendo suave: no compite con el contenido. */
.idx-sky {
  position: fixed; inset: 0; z-index: -2; pointer-events: none;
  background: linear-gradient(180deg, #DCE6EF 0%, #E8EDF1 26%, #F1F0EC 58%, #F5F3EE 100%);
}
#idxCloudGL {
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  width: 100%; height: 100%; display: block;
}
/* Las secciones que ya traen su propio fondo lo conservan: el de nubes solo se
   ve por donde el contenido es transparente. */
@media (prefers-reduced-motion: reduce) { #idxCloudGL { display: none; } }

/* ─── TOKENS ─────────────────────────────────────────────── */
:root {
  --bg:          #F6F4F0;
  --bg-alt:      #F8F6F3;
  --ink:         #111110;
  --ink-2:       #3A3935;
  --muted:       #888880;
  --line:        #D8D8CC;

  /* Paleta de marca: AZUL MARINO. Sustituye a la terracota #974315 que se
     probó antes (y al dorado Spectra original). Los tres tonos van juntos:
     --accent para iconos, textos de acento y bordes; --accent-ink para el
     estado activo/hover más oscuro; --accent-light como fondo suave.
     Los colores de estatus (--ok/--warn/--sold y --status-*) NO son parte de
     esta paleta: significan Disponible/Reservado/Ocupado y se quedan como
     están. */
  --accent:      #1B3B5F;        /* azul marino */
  --accent-light:#DCE7F3;        /* azul muy claro (fondos de hover) */
  --accent-ink:  #0E2540;        /* azul marino profundo (activo) */

  --card-bg:     #FFFFFF;
  --card-dark:   #2C2C2A;

  --ok:    #1f9d57; --ok-bg:   #e6f4ec;
  --warn:  #d2a52f; --warn-bg: #f6efda;
  --sold:  #cf3d3d; --sold-bg: #f7e6e6;

  /* Colores de estatus de unidad (Disponible/Reservado/Ocupado) -- paleta de
     marca, separada de --ok/--warn/--sold porque esas también las usan cosas
     que no son estatus (el check de "formulario enviado", los bordes de
     campo inválido): cambiar esas por error habría vuelto café el mensaje de
     error del formulario. */
  --status-disponible:    rgb(79,104,21); --status-disponible-bg: rgba(79,104,21,.18);
  --status-reservado:     rgb(233,195,114); --status-reservado-bg:  rgba(233,195,114,.5);
  --status-ocupado:       rgb(117,7,12);  --status-ocupado-bg:    rgba(117,7,12,.18);

  /* --font-d = títulos (h1-h4 y todo lo que sea display) -> Amerigo BT.
     Montserrat va segunda en la cascada a propósito: Amerigo es una fuente
     corta (216 glifos) y no trae la flecha "→" que usan varios botones, así
     que lo que le falte cae en Montserrat y no en un serif del sistema.
     --font-b = estructura (body, UI, controles) -> Montserrat. */
  --font-d: "Amerigo BT", "Montserrat", Georgia, serif;
  --font-b: "Montserrat", system-ui, sans-serif;
  --font-s: "Cormorant Garamond", Georgia, serif;

  --r:      14px;
  --r-lg:   24px;
  --shadow: 0 8px 32px oklch(0.2 0 0 / 0.12), 0 2px 8px oklch(0.2 0 0 / 0.08);
  --shadow-lg: 0 24px 64px oklch(0.15 0 0 / 0.18), 0 8px 24px oklch(0.15 0 0 / 0.1);

  --nav-h: 72px;
}

/* ─── RESET ──────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; font-size: 16px; }
body {
  font-family: var(--font-b);
  /* El color de fondo lo pone ahora .idx-sky (fijo, detrás de todo), para que
     el canvas de nubes se vea a través. Si el body conservara su color opaco
     taparía las nubes en todas las secciones transparentes. El respaldo sigue
     siendo el mismo tono, así que sin WebGL nada cambia respecto a antes. */
  background: transparent;
  color: var(--ink);
  -webkit-font-smoothing: antialiased;
  /* Sin cursor:none aquí: lo pone cursor-marea.css solo cuando su JS ya
     dibuja el anillo, para que la página nunca se quede sin puntero. */
  overflow-x: hidden;
}
a { color: inherit; text-decoration: none; }
img { display: block; max-width: 100%; }
button { font-family: inherit; border: none; background: none; }
select, input, textarea { font-family: inherit; }

/* ─── CURSOR LUXURY ──────────────────────────────────────── */
/* Se mudó a cursor-marea.css (+ cursor-marea.js), que comparten el index, el
   masterplan y el visor de planos. Ahí viven también la pantalla completa
   (el anillo se mete dentro del elemento en vez de volver al puntero
   nativo) y los iframes (el anillo se esconde mientras el puntero está
   encima), que antes se resolvían aquí. */

/* ─── UTILIDADES ─────────────────────────────────────────── */
.wrap { max-width: 1200px; margin: 0 auto; padding: 0 32px; }
.section-pad { padding: 100px 0; }
.eyebrow {
  display: inline-flex; align-items: center; gap: 10px;
  font-family: var(--font-d); font-size: 11px; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted);
}
.eyebrow::before {
  content: ""; display: block; width: 28px; height: 2px;
  background: var(--accent); border-radius: 2px; flex-shrink: 0;
}
.section-head { max-width: 580px; }
.section-head.center { max-width: 620px; margin: 0 auto; text-align: center; }
.section-head.center .eyebrow { justify-content: center; }
.section-head.center .eyebrow::before { display: none; }
.section-head.center::before {
  content: ""; display: block; width: 28px; height: 2px;
  background: var(--accent); border-radius: 2px; margin: 0 auto 16px;
}
.section-head h2 { margin-top: 12px; }
.section-head p { margin-top: 14px; color: var(--muted); font-size: 17px; line-height: 1.65; }

/* ─── TIPOGRAFÍA ─────────────────────────────────────────── */
h1, h2, h3, h4 { font-family: var(--font-d); line-height: 1.1; letter-spacing: -0.02em; font-weight: 700; color: var(--ink); }
h1 { font-size: clamp(2.6rem, 5vw, 4rem); }
h2 { font-size: clamp(1.9rem, 3.5vw, 2.7rem); }
h3 { font-size: 1.2rem; font-weight: 600; }
p { line-height: 1.65; }
.hl {
  display: inline;
  font-family: "Cormorant Garamond", Georgia, serif;
  font-weight: 600;
  font-style: italic;
  letter-spacing: -0.01em;
  color: var(--ink);
}

/* ─── BOTONES ────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-d); font-weight: 600; font-size: 14px;
  padding: 13px 24px; border-radius: 999px;
  text-transform: uppercase; letter-spacing: 0.05em;
  transition: transform 0.18s, box-shadow 0.18s, background 0.18s, color 0.18s;
  white-space: nowrap; cursor: none;
}
.btn:hover { transform: translateY(-2px); }
.btn-primary { background: rgba(255,255,255,0.94); color: var(--accent); border: 1.5px solid rgba(27,59,95,0.18); }
.btn-primary:hover { background: var(--accent-light); box-shadow: 0 8px 24px oklch(0.15 0 0 / 0.15); }
.btn-accent { background: rgba(255,255,255,0.94); color: var(--accent); border: 1.5px solid rgba(27,59,95,0.18); }
.btn-accent:hover { background: var(--accent-light); box-shadow: 0 8px 24px oklch(0.45 0.10 250 / 0.2); }
.btn-ghost { background: rgba(255,255,255,0.94); color: var(--accent); border: 1.5px solid rgba(27,59,95,0.18); }
.btn-ghost:hover { background: var(--accent-light); }
.btn-lg { padding: 15px 30px; font-size: 15px; }
.arrow { display: inline-block; transition: transform 0.18s; }
.btn:hover .arrow { transform: translateX(4px); }
.pulse { animation: pulse 2.4s infinite; }
@keyframes pulse {
  0%, 100% { box-shadow: 0 0 0 0 oklch(0.15 0 0 / 0.18); }
  50% { box-shadow: 0 0 0 10px oklch(0.15 0 0 / 0); }
}

/* ─── ATMÓSFERA FULL-SCREEN ──────────────────────────────── */
.atmos {
  position: fixed; inset: 0; z-index: -1; overflow: hidden;
  pointer-events: none; width: 100vw; height: 100vh;
}
#cloudCanvas { z-index: 1; }
.atmos-sky {
  position: absolute; inset: 0;
  background: linear-gradient(180deg,
    #F1EFEA 0%, #F4F2ED 30%, #F6F4EF 60%, #F5F3EE 100%);
}
.atmos-render {
  display: none;
}
.atmos-haze {
  position: absolute; bottom: 0; left: 0; right: 0; height: 45%;
  background: linear-gradient(180deg, transparent, var(--bg) 85%);
}
/* NUBES — 5 capas con scroll parallax */
.cloud {
  position: absolute; border-radius: 999px;
  background: rgba(255,255,255,0.65);
  filter: blur(0px);
  will-change: transform, opacity;
  transition: opacity 0.6s ease;
}
.cloud::before {
  content: ""; position: absolute;
  border-radius: 50%; background: inherit;
}
/* Nube 1 — grande, lenta */
.cloud-1 {
  width: 420px; height: 120px; top: 6%; left: -5%;
  filter: blur(18px); opacity: 0.55;
  animation: drift1 70s linear infinite;
}
.cloud-1::before { width: 240px; height: 140px; top: -40px; left: 60px; filter: blur(10px); }
/* Nube 2 — mediana */
.cloud-2 {
  width: 280px; height: 85px; top: 12%; left: 30%;
  filter: blur(14px); opacity: 0.4;
  animation: drift2 95s linear infinite; animation-delay: -28s;
}
.cloud-2::before { width: 180px; height: 100px; top: -30px; left: 40px; }
/* Nube 3 — grande derecha */
.cloud-3 {
  width: 500px; height: 140px; top: 3%; right: -8%;
  filter: blur(22px); opacity: 0.45;
  animation: drift3 80s linear infinite; animation-delay: -40s;
}
.cloud-3::before { width: 300px; height: 160px; top: -50px; left: 80px; filter: blur(12px); }
/* Nube 4 — pequeña rápida */
.cloud-4 {
  width: 200px; height: 60px; top: 18%; left: -15%;
  filter: blur(10px); opacity: 0.3;
  animation: drift1 50s linear infinite; animation-delay: -15s;
}
/* Nube 5 — alta y ancha */
.cloud-5 {
  width: 600px; height: 110px; top: 1%; left: 10%;
  filter: blur(28px); opacity: 0.25;
  animation: drift2 110s linear infinite; animation-delay: -50s;
}

.cloud-6 {
  width: 360px; height: 100px; top: 9%; left: 55%;
  filter: blur(16px); opacity: 0.32;
  animation: drift3 88s linear infinite; animation-delay: -20s;
}
.cloud-7 {
  width: 240px; height: 70px; top: 14%; right: 25%;
  filter: blur(12px); opacity: 0.28;
  animation: drift1 60s linear infinite; animation-delay: -8s;
}
@keyframes drift1 {
  0%   { transform: translateX(-120px); }
  100% { transform: translateX(110vw); }
}
@keyframes drift2 {
  0%   { transform: translateX(0); }
  100% { transform: translateX(115vw); }
}
@keyframes drift3 {
  0%   { transform: translateX(20px); }
  100% { transform: translateX(-120vw); }
}
/* Estado scroll: nubes se desvanecen al bajar */
.cloud.faded { opacity: 0 !important; }
.cloud.half-faded { opacity: 0.15 !important; }

/* ─── NAV — oculta sobre el building stage, aparece al bajar al resto del sitio ── */
.nav {
  position: fixed; top: 0; left: 0; right: 0; z-index: 100;
  height: var(--nav-h);
  opacity: 0; transform: translateY(-12px); pointer-events: none;
  transition: background 0.3s, box-shadow 0.3s, opacity 0.4s ease, transform 0.4s ease;
}
.nav.shown { opacity: 1; transform: none; pointer-events: auto; }
.nav.scrolled {
  background: rgba(235,235,228,0.55);
  backdrop-filter: blur(18px);
  -webkit-backdrop-filter: blur(18px);
  box-shadow: 0 1px 0 rgba(0,0,0,0.06);
}
.nav-inner {
  max-width: 1200px; margin: 0 auto; padding: 0 32px;
  height: 100%; display: flex; align-items: center; gap: 40px;
}
.logo {
  display: inline-flex; align-items: center; gap: 10px;
  flex-shrink: 0; text-decoration: none;
}
.logo .iso-wrap { width: 36px; height: 36px; display: grid; place-items: center; }
.logo .iso { width: 100%; height: 100%; object-fit: contain; }
.logo .lock { display: flex; align-items: baseline; gap: 5px; line-height: 1; }
.logo .company { font-family: var(--font-s); font-size: 17px; font-weight: 600; letter-spacing: 0.18em; }
.logo .alli { font-size: 11px; color: var(--muted); }
.logo .project { font-family: var(--font-s); font-size: 17px; font-weight: 700; letter-spacing: 0.1em; }
.nav-links { display: flex; align-items: center; gap: 32px; margin-left: auto; }
.nav-links a {
  font-family: var(--font-d); font-size: 13.5px; font-weight: 500;
  color: var(--ink-2); transition: color 0.18s; letter-spacing: 0.01em;
}
.nav-links a:hover { color: var(--ink); }
.nav-cta { display: flex; align-items: center; gap: 10px; }
.nav-toggle { display: none; flex-direction: column; gap: 5px; width: 32px; padding: 4px; }
.nav-toggle span { display: block; height: 1.5px; background: var(--accent); border-radius: 2px; transition: 0.2s; }

/* ─── HERO ───────────────────────────────────────────────── */
.hero { padding-top: calc(var(--nav-h) + 64px); padding-bottom: 80px; min-height: 100vh; display: flex; align-items: center; }
.hero-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 64px;
  align-items: center;
}
.hero-copy { max-width: 560px; }
.hero-copy .eyebrow { margin-bottom: 18px; }
.hero-copy h1 { line-height: 1.05; }
.hero-copy .lead {
  margin-top: 22px; font-size: 17px; line-height: 1.68;
  color: var(--ink-2); max-width: 480px;
}
.hero-actions { display: flex; gap: 12px; flex-wrap: wrap; margin-top: 32px; }
.hero-trust {
  display: flex; align-items: center; gap: 20px; margin-top: 44px;
  padding-top: 32px; border-top: 1px solid var(--line);
}
.stat { display: flex; flex-direction: column; gap: 2px; }
.stat b { font-family: var(--font-d); font-size: 22px; font-weight: 700; }
.stat span, .stat { font-size: 12px; color: var(--muted); font-weight: 500; }
.sep { width: 1px; height: 36px; background: var(--line); }

/* ─── HERO STACK — copy arriba + building stage abajo ────── */
.hero-stack { display: flex; flex-direction: column; gap: 40px; }
.hero-top { max-width: 720px; }
.hero-top .eyebrow { margin-bottom: 18px; }
.hero-top h1 { line-height: 1.05; }
.hero-top .lead {
  margin-top: 18px; font-size: 17px; line-height: 1.68;
  color: var(--ink-2); max-width: 560px;
}
.hero-top-foot { display: flex; align-items: center; gap: 32px; flex-wrap: wrap; margin-top: 28px; }
.hero-top-foot .hero-actions { display: flex; gap: 12px; flex-wrap: wrap; }
.hero-top-foot .hero-trust { display: flex; align-items: center; gap: 16px; }

/* ─── BUILDING STAGE — pantalla completa, es la página de inicio ─── */
.building-stage {
  position: relative;
  width: 100vw;
  height: 100vh;
  height: 100svh;
  min-height: 480px;
  overflow: hidden;
  background: var(--card-dark);
  isolation: isolate;
}

/* Visual: poster (o canvas de fotogramas a futuro) con Ken Burns sutil */
.stage-visual { position: absolute; inset: 0; z-index: 1; }
.stage-poster {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: center 30%;
  /* No se le pone transform de promoción porque el Ken Burns ya anima transform
     (y .no-motion lo resetea a none): añadirlo chocaría con ambos. */
  opacity: 1; transition: opacity 0.35s ease;
  animation: stageKenBurns 22s ease-in-out infinite alternate;
}
@keyframes stageKenBurns {
  from { transform: scale(1); }
  to   { transform: scale(1.08) translate(-1%, -1%); }
}
/* Fotogramas del recorrido con video: quedan fijos, sin Ken Burns, hasta la siguiente flecha */
.stage-poster.no-motion { animation: none; transform: none; }
#buildingCanvas { position: absolute; inset: 0; width: 100%; height: 100%; }

/* Disponibilidad: pantalla completa PROPIA e independiente del recorrido con
   videos/Ken Burns — su propia imagen, no la del stage del tour. Cubre toda
   la vista por encima de todo lo demás y se cierra con la X. */
.stage-avail-fullscreen {
  position: fixed; inset: 0; z-index: 400; background: #05060a;
  display: flex; align-items: center; justify-content: center;
  /* Vista fija, sin scroll propio -- antes tenía overflow:auto y, si el
     contenido interno llegaba a medir más que el viewport, esa franja se
     desplazaba y se sentía como si "toda la pantalla" bajara. Las tarjetas
     que sí pueden necesitar scroll interno (detalle, ver .stage-avail-
     detailcard) ya traen su propio overflow-y:auto acotado. */
  overflow: hidden;
  overscroll-behavior: contain;
}
.stage-avail-fullscreen[hidden] { display: none; }
/* Imagen a su proporción real (nunca recortada) -> la malla SVG (viewBox
   1360x768, mismas dimensiones naturales del jpg) queda pixel-perfect sobre
   cualquier tamaño de pantalla sin recalcular nada en JS. */
/* aspect-ratio fijo: el fotograma 1 (por defecto) es position:absolute, así
   que no aporta altura por sí solo -- sin esto el contenedor colapsa a 0 en
   cuanto el fotograma 2 (el único hijo en flujo normal) se oculta al
   arrancar en el fotograma 1. */
.stage-avail-media { position: relative; width: 100%; flex: 0 0 auto; aspect-ratio: 1360 / 768; }
.stage-avail-img {
  display: block; width: 100%; height: auto; user-select: none; -webkit-user-drag: none;
  transition: opacity 0.5s ease;
}
/* Desvanecido cruzado entre fotogramas (sin video): ambos quedan montados
   siempre, solo cambia su opacidad -- el que no está activo también pierde
   pointer-events para no robar clicks a los polígonos del que sí se ve. */
.avail-frame-hidden { opacity: 0; pointer-events: none; }
.stage-hotspot { position: relative; cursor: pointer; }
.stage-hotspot-core { pointer-events: none; }
.stage-hotspot .dot {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 14px; height: 14px; border-radius: 50%;
  box-shadow: 0 0 0 4px rgba(255,255,255,0.28), 0 2px 10px rgba(0,0,0,0.45);
  transition: transform 0.18s, box-shadow 0.18s;
}
.stage-hotspot .dot::after {
  content: ""; position: absolute; inset: -9px; border-radius: 50%;
  border: 2px solid currentColor; opacity: 0.6;
  animation: hotspotPulse 2.4s ease-out infinite;
}
.stage-hotspot:hover .dot { transform: translate(-50%, -50%) scale(1.3); }
.stage-hotspot.active .dot { box-shadow: 0 0 0 3px #fff, 0 2px 10px rgba(0,0,0,0.5); }
@keyframes hotspotPulse {
  0%   { transform: scale(0.55); opacity: 0.65; }
  100% { transform: scale(1.7); opacity: 0; }
}
/* Capa superior del fundido cruzado entre fotogramas (VERSIÓN LITE, sin
   videos): misma caja que .stage-poster, arranca invisible y sube a 1 en
   0.9 s -- FADE_MS en site.js debe calzar con esta duración. .instant apaga
   la transición un cuadro para retirarla sin que se note. */
.stage-poster-next {
  opacity: 0; pointer-events: none;
  animation: none;
  transition: opacity 0.9s ease;
  backface-visibility: hidden;
}
.stage-poster-next.active { opacity: 1; }
.stage-poster-next.instant { transition: none; }
/* Galería en reposo (pantalla de "Entrar", tras 1 min sin actividad).
   Va después del poster y los videos y antes de .stage-shade, así que se funde
   encima de la fachada pero sigue por debajo del velo y del gate de nubes. */
.stage-idleshow {
  position: absolute; inset: 0;
  opacity: 0; pointer-events: none;
  transition: opacity 1.1s ease;
}
.stage-idleshow.on { opacity: 1; }

/* Cada foto es una diapositiva con dos capas, igual que el lightbox: una copia
   ampliada y desenfocada rellenando el fondo, y la foto completa (contain)
   encima. Las de galería son verticales (~1083x1280): a pantalla completa con
   `cover` quedarían recortadas a una franja y escaladas por encima de su
   resolución, así que se muestran enteras sobre su propio fondo. */
.idleshow-slide {
  position: absolute; inset: 0;
  opacity: 0; transition: opacity 1.6s ease;
}
.idleshow-slide.on { opacity: 1; }
.idleshow-bg {
  position: absolute; inset: -5%; width: 110%; height: 110%;
  object-fit: cover; object-position: center;
  filter: blur(48px) brightness(0.55) saturate(1.25);
  transform: scale(1.12);
}
.idleshow-fg {
  position: absolute; inset: 0; margin: auto;
  max-width: min(78%, 620px); max-height: 72%;
  width: auto; height: auto; object-fit: contain;
  border-radius: 14px;
  box-shadow: 0 26px 70px rgba(8, 17, 30, 0.5);
  /* Deriva lentísima para que no se sienta una diapositiva congelada */
  animation: idleShowDrift 14s ease-in-out infinite alternate;
}
@keyframes idleShowDrift {
  from { transform: scale(1); }
  to   { transform: scale(1.045) translateY(-1.2%); }
}
@media (prefers-reduced-motion: reduce) {
  .idleshow-fg { animation: none; transform: none; }
}

/* ─── MIENTRAS CORRE UNA TRANSICIÓN ────────────────────────────
   Encima del video hay 8 elementos con backdrop-filter: los dos chevrons, las
   dos píldoras, reiniciar, pantalla completa, el toggle del menú y el cristal
   del hotspot. Cada backdrop-filter obliga al compositor a capturar el fondo,
   desenfocarlo y componerlo EN CADA CUADRO que pinta el video — a 60 fps son
   cientos de pasadas de desenfoque por segundo sobre 1080p, y es lo que hacía
   que el conjunto se sintiera trabado con las dos flechas.

   Durante los ~5 s que dura la transición se cambia el desenfoque por un fondo
   sólido equivalente: se ve prácticamente igual (son botones pequeños sobre
   una imagen en movimiento) y el compositor deja de trabajar. Al terminar
   vuelve el vidrio. Mismo criterio que ya usa el masterplan con body.viz3d. */
.building-stage.transitioning .stage-icon-btn,
.building-stage.transitioning .stage-pill,
.building-stage.transitioning .stage-chevron,
.building-stage.transitioning .stage-drawer,
.building-stage.transitioning .stage-masterplan-cta-glass {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}
.building-stage.transitioning .stage-icon-btn { background: rgba(255,255,255,0.94); }
.building-stage.transitioning .stage-pill     { background: rgba(255,255,255,0.92); }
.building-stage.transitioning .stage-chevron  { background: rgba(255,255,255,0.9); }
.building-stage.transitioning .stage-drawer   { background: rgba(255,255,255,0.96); }

/* Los que faltaban.
   La regla de arriba se escribió cuando encima del video había 8 cristales.
   Después se añadieron la tarjeta de disponibilidad con su cierre y sus dos
   flechas, el botón de acción y la pastilla de Spectra — desenfoques MÁS que
   la regla no alcanzaba, y que por tanto seguían recalculándose en cada
   cuadro del video. Justo en la animación de entrada, que es la que tiene
   que ir perfecta.

   .nav-powered-by va con :has() porque es position:fixed y no cuelga de
   .building-stage: el selector de descendencia no llega a él por mucho que
   flote encima del video. */
.building-stage.transitioning .stage-avail-close,
.building-stage.transitioning .stage-avail-chevron,
.building-stage.transitioning .stage-avail-card,
body:has(.building-stage.transitioning) .nav-powered-by {
  backdrop-filter: none !important;
  -webkit-backdrop-filter: none !important;
}
.building-stage.transitioning .stage-avail-close   { background: rgba(255,255,255,0.94); }
.building-stage.transitioning .stage-avail-chevron { background: rgba(255,255,255,0.9); }
.building-stage.transitioning .stage-avail-card    { background: oklch(0.12 0 0 / 0.9); }

.stage-shade {
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(8,17,30,0.10) 0%, rgba(8,17,30,0.05) 40%, rgba(8,17,30,0.55) 100%);
}


/* Rieles de iconos — estilo galería de referencia (menú / reset / galería · fullscreen) */
.stage-rail {
  position: absolute; top: 20px; z-index: 6;
  display: flex; flex-direction: column; gap: 10px;
  opacity: 0; pointer-events: none;
  transition: opacity 0.4s ease 0.6s;
}
.stage-rail-left  { left: 20px; top: 74px; }
.stage-rail-right { right: 20px; }
.building-stage.revealed .stage-rail { opacity: 1; pointer-events: auto; }
.stage-icon-btn {
  width: 42px; height: 42px; border-radius: 50%;
  background: rgba(255,255,255,0.9); backdrop-filter: blur(8px);
  border: 1px solid rgba(27,59,95,0.18);
  color: var(--accent); display: flex; align-items: center; justify-content: center;
  transition: transform 0.18s, background 0.18s;
}
.stage-icon-btn:hover { background: rgba(255,255,255,0.96); transform: scale(1.08); }

/* Flyout del botón de ubicación: al pasar el mouse (no al hacer clic) se
   despliegan "Ubicación 360" y "Mapa" a la derecha del icono. Cada uno abre
   directo su vista en pantalla completa -- ver stageLocFlyout360/Mapa en
   site.js. Con :focus-within además de :hover para que también se pueda
   llegar por teclado (Tab). */
.stage-loc-trigger { position: relative; }
/* left:100% + padding-left (en vez de left:calc(100% + 10px)) para que el
   propio elemento del flyout cubra ese hueco: si el hueco quedara fuera de
   cualquier elemento, mover el mouse del icono hacia los botones cruzaba una
   franja "muerta" sin :hover y el flyout se cerraba a medio camino. */
.stage-loc-flyout {
  position: absolute; left: 100%; top: 50%; transform: translate(-6px, -50%);
  padding-left: 10px;
  display: flex; gap: 8px; z-index: 6;
  opacity: 0; pointer-events: none;
  transition: opacity 0.2s ease, transform 0.2s ease;
}
.stage-loc-trigger:hover .stage-loc-flyout,
.stage-loc-trigger:focus-within .stage-loc-flyout {
  opacity: 1; pointer-events: auto; transform: translate(0, -50%);
}
.stage-loc-flyout-btn {
  padding: 10px 16px; border-radius: 999px; white-space: nowrap;
  background: rgba(255,255,255,0.94); border: 1px solid rgba(27,59,95,0.18);
  color: var(--accent); font-family: var(--font-d); font-size: 11.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.05em;
  transition: background 0.18s, transform 0.18s;
}
.stage-loc-flyout-btn:hover { background: #fff; transform: translateY(-1px); }

/* Toggle del menú (hamburguesa minimalista) — fuera del riel, siempre por encima del drawer */
.stage-drawer-toggle {
  position: absolute; top: 20px; left: 20px; z-index: 8;
  flex-direction: column; gap: 4px;
  opacity: 0; pointer-events: none;
  transition: opacity 0.4s ease 0.6s, background 0.18s;
}
.building-stage.revealed .stage-drawer-toggle { opacity: 1; pointer-events: auto; }
.stage-drawer-toggle span { width: 16px; height: 1.5px; background: var(--accent); border-radius: 2px; transition: transform 0.22s, opacity 0.22s; }
.stage-drawer-toggle.open span:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
.stage-drawer-toggle.open span:nth-child(2) { opacity: 0; }
.stage-drawer-toggle.open span:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }

/* Píldoras de acceso rápido -- "Vista aérea" arriba-izq (después del menú
   hamburguesa, que ocupa top:20/left:20 con ~42px), "Galería"/"Disponibilidad"
   arriba-der (antes del botón de pantalla completa, top:20/right:20). */
.stage-pills {
  position: absolute; top: 20px; z-index: 6;
  display: flex; gap: 8px;
  opacity: 0; pointer-events: none;
  transition: opacity 0.4s ease 0.75s;
}
.stage-pills-right { right: 72px; }
.building-stage.revealed .stage-pills { opacity: 1; pointer-events: auto; }
.stage-pill {
  padding: 10px 18px; border-radius: 999px;
  background: rgba(255,255,255,0.9); backdrop-filter: blur(8px);
  border: 1px solid rgba(27,59,95,0.18);
  color: var(--accent); font-family: var(--font-d); font-size: 12.5px; font-weight: 600;
  text-transform: uppercase; letter-spacing: 0.05em;
  transition: background 0.18s, transform 0.18s;
}
.stage-pill:hover { background: rgba(255,255,255,0.96); transform: translateY(-1px); }

/* Las píldoras llevan icono ademas del texto (ver index.html). En escritorio
   manda el texto y el icono no se dibuja; en movil se invierte y quedan como
   botones de icono (ver seccion 2d de movil/mobile.css). */
.stage-pill .pill-ico { display: none; }

/* Silueta del edificio (los 4 fotogramas del recorrido): área de hover más
   grande y perdonable debajo del punto circular. z-index menor que el punto
   (6) para que el punto siga arriba, y position/size/points los recalcula
   positionBuildingHotspot en site.js (mismo cover-math que el punto). Sin
   click propio -- fill casi invisible pero pointer-events:all para que SÍ
   dispare :hover incluso sin pintar nada. Se apaga por completo durante
   cualquier transición (ver .hs-transitioning, controlado desde
   hideMasterplanCta/showMasterplanCta en site.js) -- solo debe reaccionar al
   mouse con la cámara ya quieta en un fotograma. */
.stage-building-hotspot { position: absolute; z-index: 5; pointer-events: none; }
.stage-building-hotspot.hs-transitioning { display: none !important; }
.stage-building-hotspot polygon {
  fill: rgba(255, 255, 255, 0.001); pointer-events: all; cursor: pointer;
  stroke: transparent; stroke-width: 0;
  transition: fill 0.25s ease, stroke 0.25s ease, stroke-width 0.25s ease;
}
/* .hs-hover (clase, no :hover) es lo que realmente sincroniza silueta <-> punto
   en ambas direcciones -- se controla desde JS (site.js) porque un simple
   selector de hermanos en CSS solo funciona en un sentido según el orden en
   el DOM, y aquí necesitamos que cualquiera de los dos "encienda" al otro. */
.stage-building-hotspot polygon:hover,
.stage-building-hotspot polygon.hs-hover {
  fill: rgba(255, 255, 255, 0.14);
  stroke: rgba(255, 255, 255, 0.55); stroke-width: 2px;
}

/* Botón circular "Entrar al masterplan" — hotspot anclado en medio del
   edificio, con coordenadas propias por cada uno de los 4 fotogramas del
   recorrido orbital (ver HOTSPOT_POINTS + positionMasterplanCta en site.js).
   Solo visible con la cámara quieta; se oculta apenas arranca cualquier
   video de transición. Abre la ventana de confirmación #masterplanGateModal. */
.stage-masterplan-cta {
  position: absolute; left: 50%; top: 50%; z-index: 6;
  transform: translate(-50%, -50%) scale(0.8);
  width: 72px; height: 72px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: none; border: none; padding: 0;
  opacity: 0; pointer-events: none;
  transition: opacity 0.35s ease, transform 0.35s ease;
}
.stage-masterplan-cta.visible { opacity: 1; pointer-events: auto; transform: translate(-50%, -50%) scale(1); }
.stage-masterplan-cta:hover,
.stage-masterplan-cta.hs-hover.visible {
  transform: translate(-50%, -50%) scale(1.1);
}
.stage-masterplan-cta-img { width: 100%; height: 100%; object-fit: contain; pointer-events: none; }
/* Centro "vidrio líquido": el aro de color y su parpadeo/glow siguen siendo
   el APNG de siempre (sin tocar). Bastante más transparente que el primer
   intento -- ese quedaba casi opaco y tapaba el brillo del centro del apng,
   que es justo donde se nota el parpadeo, por eso se veía "sin animación".
   Con esta opacidad baja el glow de abajo pasa a través y el parpadeo vuelve
   a verse, con solo un velo de vidrio encima (blur + brillo sutil arriba-
   izq). 54% en vez de 58% para dejar un pelín más de margen con el aro. */
.stage-masterplan-cta-glass {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 54%; height: 54%; border-radius: 50%; pointer-events: none; z-index: 1;
  background: radial-gradient(circle at 35% 28%, rgba(255,255,255,0.5), rgba(255,255,255,0.24) 55%, rgba(255,255,255,0.14) 100%);
  backdrop-filter: blur(1.5px) saturate(140%);
  -webkit-backdrop-filter: blur(1.5px) saturate(140%);
  border: 1px solid rgba(255,255,255,0.55);
  box-shadow: inset 0 1px 2px rgba(255,255,255,0.6), inset 0 -1px 2px rgba(0,0,0,0.04);
}

.modal-card--gate { max-width: 380px; align-items: center; text-align: center; }
.modal-card--gate .plans-viewer,
.modal-card--gate p { text-align: center; }

/* Flechas laterales — recorrer partes del edificio */
.stage-chevron {
  position: absolute; top: 50%; z-index: 6;
  transform: translateY(-50%);
  width: 44px; height: 44px; border-radius: 50%;
  background: rgba(255,255,255,0.85); backdrop-filter: blur(6px);
  border: 1px solid rgba(27,59,95,0.16);
  color: var(--accent); display: flex; align-items: center; justify-content: center;
  opacity: 0; pointer-events: none;
  transition: opacity 0.4s ease 0.9s, background 0.18s, transform 0.18s;
}
.building-stage.revealed .stage-chevron { opacity: 1; pointer-events: auto; }
.stage-chevron:hover { background: rgba(255,255,255,0.96); }
.stage-chevron-left  { left: 20px; }
.stage-chevron-right { right: 20px; }

/* Menú lateral desplegable — minimalista (por encima del riel de iconos para no solaparse) */
.stage-drawer {
  position: absolute; inset: 0 auto 0 0; z-index: 7;
  width: 230px;
  background: rgba(255,255,255,0.94); backdrop-filter: blur(16px);
  border-right: 1px solid rgba(27,59,95,0.14);
  padding: 88px 22px 24px;
  display: flex; flex-direction: column; gap: 4px;
  overflow-y: auto;
  transform: translateX(-100%);
  transition: transform 0.38s cubic-bezier(.2,.8,.2,1);
}
.stage-drawer.open { transform: translateX(0); }
/* Logo MAREA RESIDENCES arriba del menú -- el wordmark está pensado para fondo claro
   (gris oscuro + cobre), que es lo que ya trae el drawer, así que la chapa
   solo le suma un borde sutil para separarlo de la lista de abajo. */
.sd-logo { display: flex; justify-content: center; padding-bottom: 14px; margin-bottom: 6px; border-bottom: 1px solid rgba(27,59,95,0.14); }
.sd-logo-chip { display: flex; width: 100%; }
/* El logotipo de MAREA es casi cuadrado (ratio 1.12), no apaisado como el de
   MAREA RESIDENCES (1.78): a ancho completo del cajón se comía media pantalla de alto.
   Se fija el alto y se centra, así la lista de secciones sigue arrancando
   arriba. */
.sd-logo img { height: 118px; width: auto; max-width: 100%; object-fit: contain; display: block; margin: 0 auto; }
.sd-nav { display: flex; flex-direction: column; flex: 1; }
.sd-item {
  display: flex; align-items: center; gap: 12px;
  padding: 13px 4px; border-bottom: 1px solid rgba(27,59,95,0.1);
  font-family: var(--font-d); font-size: 12.5px; font-weight: 500;
  text-transform: uppercase; letter-spacing: 0.04em;
  color: rgba(27,59,95,0.65); text-align: left;
  transition: color 0.18s, padding-left 0.18s;
}
.sd-item:last-child { border-bottom: none; }
.sd-ico { display: flex; align-items: center; justify-content: center; width: 18px; flex-shrink: 0; color: var(--accent); }
.sd-item:hover { color: var(--accent); padding-left: 8px; }
.sd-item.active { color: var(--accent-ink); font-weight: 700; }
.sd-item.active .sd-ico { color: var(--accent-ink); }

/* WhatsApp / Facebook / Correo -- círculos abajo de "Contacto", no más filas
   de la lista. Mismo vidrio que .stage-icon-btn del riel del recorrido, para
   que se sientan del mismo lenguaje visual aunque vivan en el cajón. */
.sd-social {
  display: flex; justify-content: center; gap: 10px; margin-top: 12px; padding-top: 14px;
  border-top: 1px solid rgba(27,59,95,0.1);
}
.sd-social-btn {
  width: 40px; height: 40px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,0.9);
  border: 1px solid rgba(27,59,95,0.18);
  color: var(--accent);
  transition: transform 0.18s, background 0.18s, color 0.18s;
}
.sd-social-btn:hover { background: var(--accent-light); color: var(--accent-ink); transform: translateY(-2px); }

/* "Powered by Spectra" -- abajo de los círculos, también es enlace (a
   salamspectra.com), mismo isotipo que ya usa el badge del nav superior. */
.sd-powered {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  margin-top: auto; padding-top: 14px;
  font-family: var(--font-d); font-size: 10.5px; letter-spacing: 0.04em;
  color: var(--muted); text-decoration: none;
  transition: color 0.18s;
}
.sd-powered b { color: var(--ink-2); font-weight: 700; transition: color 0.18s; }
.sd-powered:hover { color: var(--accent); }
.sd-powered:hover b { color: var(--accent-ink); }
.sd-powered-iso { width: 16px; height: 16px; object-fit: contain; border-radius: 4px; flex-shrink: 0; }

/* Disponibilidad — pantalla completa: tarjeta de detalle abajo-izquierda,
   leyenda abajo-derecha, píldoras de filtro arriba-derecha (layout de
   referencia del cliente), cerrar arriba-izquierda. */
.stage-avail-close {
  position: absolute; top: 20px; left: 20px; z-index: 10;
  width: 40px; height: 40px; border-radius: 50%;
  background: rgba(255,255,255,0.9); backdrop-filter: blur(8px);
  border: 1px solid rgba(27,59,95,0.18);
  color: var(--accent); display: grid; place-items: center;
  transition: background 0.18s, transform 0.18s;
}
.stage-avail-close:hover { background: rgba(255,255,255,0.96); transform: scale(1.06); }

/* Flechas fotograma 1 <-> 2, ancladas al viewport igual que .stage-chevron
   del recorrido principal (el fullscreen de disponibilidad es position:fixed). */
.stage-avail-chevron {
  position: absolute; top: 50%; z-index: 10; transform: translateY(-50%);
  width: 44px; height: 44px; border-radius: 50%;
  background: rgba(255,255,255,0.85); backdrop-filter: blur(6px);
  border: 1px solid rgba(27,59,95,0.16);
  color: var(--accent); display: flex; align-items: center; justify-content: center;
  cursor: pointer; transition: background 0.18s, transform 0.18s;
}
.stage-avail-chevron:hover { background: rgba(255,255,255,0.96); }
.stage-avail-chevron-left  { left: 20px; }
.stage-avail-chevron-right { right: 20px; }

/* Liquid glass: mismo lenguaje que .stage-avail-close/.stage-avail-chevron
   (vidrio oscuro + blur), aplicado a las tarjetas de detalle y conteo --
   texto claro encima, con overrides puntuales para los elementos internos
   (avail-panel-*, avail-unit-*, botones) que se diseñaron para tarjeta clara. */
.stage-avail-card {
  position: absolute; z-index: 10; color: #fff;
  background: linear-gradient(155deg, rgba(255,255,255,0.16), rgba(255,255,255,0.05) 55%, rgba(255,255,255,0.1));
  border: 1px solid rgba(255,255,255,0.28);
  border-radius: var(--r);
  backdrop-filter: blur(22px) saturate(160%);
  -webkit-backdrop-filter: blur(22px) saturate(160%);
  box-shadow: 0 12px 36px rgba(0,0,0,0.35), inset 0 1px 0 rgba(255,255,255,0.35);
  padding: 20px 22px;
}
.stage-avail-detailcard { left: 24px; bottom: 24px; min-width: 260px; max-width: 320px; max-height: 60vh; overflow-y: auto; }
.stage-avail-legendcard { right: 24px; bottom: 24px; }

.stage-avail-card .avail-panel-eyebrow,
.stage-avail-card .avail-panel-empty,
.stage-avail-card .avail-unit-grid dt,
.stage-avail-card .avail-unit-notes,
.stage-avail-card .acw-word { color: rgba(255,255,255,0.68); }
.stage-avail-card .avail-unit-title,
.stage-avail-card .avail-unit-grid dd,
.stage-avail-card .acw-number { color: #fff; }
.stage-avail-card .avail-unit-grid dt { color: rgba(255,255,255,0.68); }
.stage-avail-card .acw-sep { background: rgba(255,255,255,0.24); }
.stage-avail-card .acw-opt { background: rgba(255,255,255,0.85); border-color: transparent; color: rgba(27,59,95,0.6); }
.stage-avail-card .acw-opt.active { background: rgba(255,255,255,0.94); border-color: var(--accent); color: var(--accent); }
.stage-avail-card .btn-ghost {
  color: var(--accent); background: rgba(255,255,255,0.85); border-color: transparent;
}
.stage-avail-card .btn-ghost:hover { background: rgba(255,255,255,0.94); border-color: var(--accent); }
.stage-avail-card .avail-price-form input {
  background: rgba(255,255,255,0.1); color: #fff; border-color: rgba(255,255,255,0.3);
}
.stage-avail-card .avail-price-form input::placeholder { color: rgba(255,255,255,0.55); }
.stage-avail-card .avail-price-form input:focus { border-color: #fff; }

@media (max-width: 720px) {
  .stage-avail-close { top: 14px; left: 14px; width: 36px; height: 36px; }
  .stage-avail-chevron { width: 38px; height: 38px; }
  .stage-avail-chevron-left  { left: 10px; }
  .stage-avail-chevron-right { right: 10px; }
  /* Widget de conteo: tarjeta chica arriba a la derecha, no estorba al detalle */
  .stage-avail-legendcard {
    top: 14px; right: 14px; left: auto; bottom: auto;
    padding: 10px 14px;
  }
  .acw { gap: 10px; }
  .acw-number { font-size: 20px; }
  .acw-word { font-size: 9.5px; }
  .acw-carousel { width: 150px; height: 28px; }
  .acw-opt { font-size: 11px; padding: 6px 12px; }
  /* Detalle: fijo abajo, ancho completo, con tope de alto para no tapar toda la foto */
  .stage-avail-detailcard {
    left: 14px; right: 14px; bottom: 14px; max-width: none; max-height: 42vh; padding: 16px 18px;
  }
}

/* Cortina de salida: se activa un instante antes de navegar a otra página
   (ver navigateWithFade en site.js, usado por "Ver planta arquitectónica")
   para que el salto de página se sienta como una transición a propósito en
   vez de un frenón/parpadeo en blanco. */
.page-fade-out {
  position: fixed; inset: 0; z-index: 99999; background: #05060a;
  opacity: 0; pointer-events: none; transition: opacity 0.32s ease;
}
.page-fade-out.active { opacity: 1; pointer-events: auto; }

/* Gate de entrada — el hero del mar, cubre el stage hasta que se da Entrar.
   Antes eran dos paneles blancos de nube que se abrían a los lados más un
   campo de motas en canvas; se retiró todo.

   El color base es el turquesa del propio mar, NO el póster de la playa. Es a
   propósito: con el póster de fondo, al cargar se veía la foto de la arena a
   pantalla completa un segundo y luego aparecía el hero encima -- un parpadeo
   feo de dos imágenes. Un color plano del mismo tono no se nota entrar. El
   póster solo se pone si el equipo NO tiene WebGL (ver .cg-hero.marea-hero--fallback). */
.cloud-gate {
  position: absolute; inset: 0; z-index: 8;
  display: flex; align-items: center; justify-content: center; overflow: hidden;
  background-color: #4FA8B4;
  transition: background-color 0.9s ease;
}
/* El CIELO del atardecer, con los tonos muestreados del fotograma 1.

   Ahora es una RED DE SEGURIDAD, no lo que se ve. Cuando la inclinación la
   hacía el CSS, el lienzo se tumbaba y por encima de su borde asomaba este
   degradado; con la cámara de verdad el lienzo no se mueve, lo tapa entero y
   el cielo lo pinta el propio motor (scene.background en hero-beach.js, con
   estos mismos cuatro tonos). Se conserva porque el motor no siempre está:
   sin WebGL se cae al póster, y entre que se pulsa Entrar y el primer cuadro
   del vuelo este fondo es lo que evita un parpadeo. Si se cambian los tonos,
   cámbialos en los dos sitios. */
.cloud-gate.gate-inmersion {
  background-image: linear-gradient(180deg, #8fb6cc 0%, #c9d4d4 40%, #e8d6bd 70%, #f0dcc2 100%);
}
/* Contenedor del hero 3D. */
.cg-hero { position: absolute; inset: 0; z-index: 0; transform-origin: 50% 78%; will-change: transform, filter; }
.cg-hero canvas { display: block; width: 100%; height: 100%; }
.marea-hero__canvas { opacity: 0; transition: opacity 1.1s ease; }
.marea-hero--ready .marea-hero__canvas { opacity: 1; }
/* Respaldo si no hay WebGL: ahí sí la foto, porque no habrá nada más. */
.cg-hero.marea-hero--fallback {
  background: #7fc3c6 url("hero/img/beach-poster.jpg") center/cover no-repeat;
}
/* ── LA TRANSICIÓN DE ENTRADA: EL DRON LEVANTA EL MORRO ────────────────────
   Antes esto era un acercamiento (scale). Se cambió porque un zoom no bastaba,
   y el motivo es concreto: entre las dos imágenes NO cambia la distancia,
   cambia el PUNTO DE VISTA. El hero es una vista cenital -- la cámara mira al
   suelo en vertical -- y el fotograma 1 es una aérea oblicua, con horizonte.
   Acercarse sin inclinarse deja las dos vistas irreconciliables: se ve un
   salto, por suave que sea el zoom.

   Así que la playa se INCLINA. `rotateX` con perspectiva tumba el plano
   cenital hasta que su borde lejano se convierte en horizonte, exactamente el
   gesto de un dron que baja y levanta el morro. Al terminar, el encuadre ya
   coincide en tipo con el del render, y el corte deja de leerse como corte.

   El pivote va abajo (`transform-origin` al 78 %) para que lo que suba sea el
   fondo y no toda la imagen: si el pivote va al centro, la playa se va hacia
   arriba y se ve el vacío por debajo.

   El desenfoque no es decorativo: al inclinar tanto, las últimas filas de
   píxeles se estiran muchísimo y aparece aliasing; medio píxel de blur lo
   disimula y de paso lee como barrido de cámara.

   AJUSTE FINAL — QUE COINCIDA CON EL FOTOGRAMA 1.
   Inclinar solo no bastaba: la playa acababa CRUZANDO la pantalla de lado a
   lado, mientras que en el render la costa se ALEJA hacia el fondo, con el
   Caribe a la izquierda y el canal a la derecha. Faltaba el giro en planta.

   `rotateZ(74deg)` gira la playa sobre el suelo antes de mirarla inclinada:
   con eso la línea de costa deja de ser una banda horizontal y se convierte
   en una diagonal que se pierde arriba a la derecha, igual que en el render.
   El signo importa -- en negativo el mar se iba al lado contrario.

   Medido contra el fotograma 1: su horizonte cae al 19 % de la altura, y con
   68° de inclinación más este encuadre el borde lejano del plano queda ahí
   mismo. Encima del horizonte asoma el cielo del gate (ver .cloud-gate). */
/* ESTO YA NO INCLINA NADA, Y ES EL ARREGLO.

   Aquí vivía `perspective(1400px) rotateX(68deg) rotateZ(74deg) scale(3.2)`:
   la inclinación se le hacía al LIENZO. Y tumbar el lienzo es tumbar una
   lámina ya pintada -- la imagen que lleva encima se dibujó con la cámara
   cenital del reposo, que mira al suelo casi en vertical y no converge. El
   resultado leía como una vista PARALELA puesta en diagonal, un plano sobre
   una mesa, y contra el fotograma 1 (una aérea oblicua de verdad) el empalme
   se notaba.

   El giro, el acercamiento y la apertura del objetivo los hace ahora la
   PerspectiveCamera del motor (VUELO en site.js 5b-2 → setCamera en
   hero-beach.js), así que la perspectiva es de verdad: las líneas convergen,
   el mar se comprime hacia el fondo y el horizonte lo forma el objetivo.

   Lo que sí se queda aquí es el retoque de imagen, que es cosa de CSS y no de
   cámara: un punto más de saturación y luz para acercarse al color del render
   y un desenfoque mínimo que hace de arrastre del movimiento. */
.cloud-gate.gate-inmersion .cg-hero {
  filter: saturate(1.12) brightness(1.05) blur(0.6px);
  transition: filter 1.55s ease-in;
}
/* Velo suave para que el blanco del rótulo se lea sobre agua clara. */
.cg-velo {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  transition: opacity .6s ease;
  background:
    radial-gradient(70% 46% at 50% 50%, rgba(8,40,45,.34) 0%, rgba(8,40,45,0) 72%),
    linear-gradient(180deg, rgba(8,40,45,.22) 0%, rgba(8,40,45,0) 26%, rgba(8,40,45,0) 74%, rgba(8,40,45,.24) 100%);
}
.cloud-gate.gate-inmersion .cg-velo { opacity: 0; }
/* DESTELLO DE HORIZONTE. Sustituye a la espuma blanca que tapaba la pantalla
   entera: eso era una cortina, y cerrar con una cortina es justo lo que hacía
   que el cambio se notara. Ahora, cuando la inclinación ya formó el horizonte,
   se enciende ahí una banda de luz cálida -- el sol entrando en el objetivo --
   que lava esa franja y cubre el relevo entre el hero y el render sin tapar el
   resto de la imagen. */
.cg-espuma {
  position: absolute; left: -10%; right: -10%; top: 0; bottom: 0;
  z-index: 3; pointer-events: none; opacity: 0;
  background:
    radial-gradient(120% 34% at 50% 30%, rgba(255,246,232,.95) 0%, rgba(255,242,222,.55) 38%, rgba(255,240,220,0) 72%),
    linear-gradient(180deg, rgba(255,248,236,0) 0%, rgba(255,248,236,.30) 26%, rgba(255,248,236,0) 52%);
  transition: opacity .55s ease-in;
}
.cg-espuma.on { opacity: 1; }

/* LA LLEGADA. El fotograma 1 no aparece de golpe: entra un punto más grande y
   se asienta. Va sobre .stage-visual y NO sobre .stage-poster porque el poster
   ya tiene su propio Ken Burns animando transform -- ponerle otro encima se
   pelearía con él (ver el comentario junto a .stage-poster). */
.building-stage.entrando .stage-visual {
  animation: llegadaHero 1.15s cubic-bezier(.16,.8,.24,1) both;
}
@keyframes llegadaHero {
  from { transform: scale(1.09); }
  to   { transform: none; }
}

/* Con "reducir movimiento" no hay ni inclinación ni destello: se entra y ya.
   El vuelo de cámara también se salta, pero eso se decide en JS (sinMovimiento
   en site.js 5b-2): desde CSS no se puede parar una cámara de WebGL. */
@media (prefers-reduced-motion: reduce) {
  .cloud-gate.gate-inmersion .cg-hero { transition: none; filter: none; }
  .cg-espuma { display: none; }
  .building-stage.entrando .stage-visual { animation: none; }
}
/* Volver a la entrada desde "Inicio" tiene que ser instantáneo y ya sellado:
   sin esto el fondo y los paneles se re-animarían desde el estado abierto y la
   franja reaparecería durante la vuelta. */
.cloud-gate.gate-instant,
.cloud-gate.gate-instant .cg-hero,
.cloud-gate.gate-instant .cg-content { transition: none !important; }

.cg-content {
  position: relative; z-index: 4;
  display: flex; flex-direction: column; align-items: center; gap: 24px;
  text-shadow: 0 2px 30px rgba(6,35,40,.45);
  transition: opacity 0.5s ease, transform 0.9s cubic-bezier(.5,0,.25,1);
}
.building-stage.revealed .cg-content,
.cloud-gate.gate-inmersion .cg-content { opacity: 0; transform: scale(1.14); pointer-events: none; }
.cg-marca { width: clamp(140px, 15vw, 196px); height: auto; filter: drop-shadow(0 6px 24px rgba(0,20,32,.45)); }
/* Sobre el mar el rótulo va en blanco, no en la tinta clara de antes. */
.cg-brand { font-family: var(--font-s); font-size: 12px; font-weight: 600; letter-spacing: 0.34em; text-transform: uppercase; color: rgba(255,255,255,0.88); padding-left: 0.34em; }
.cg-enter {
  display: flex; align-items: center; gap: 10px;
  padding: 15px 34px; border-radius: 999px;
  background: rgba(255,255,255,0.94); color: var(--accent); border: 1.5px solid rgba(27,59,95,0.18);
  font-family: var(--font-d); font-size: 14px; font-weight: 600; letter-spacing: 0.05em;
  text-transform: uppercase;
  box-shadow: var(--shadow);
  transition: background 0.2s, transform 0.2s;
}
.cg-enter:hover { background: #fff; transform: translateY(-2px); box-shadow: 0 16px 40px rgba(0,24,36,.42); }
/* El gate se retira cuando el edificio ya está debajo. El retraso del fundido
   es corto porque para entonces la espuma ya cubre la pantalla: lo que se ve
   desvanecerse es la espuma, no el gate. */
.building-stage.revealed .cloud-gate {
  opacity: 0;
  background-color: rgba(79, 168, 180, 0);
  transition: opacity 0.6s ease 0.15s, background-color 0.6s ease;
  /* Un elemento con opacity:0 SIGUE recibiendo toques. El gate no se retira del
     DOM hasta los 2.5s, así que entre que acaba de desvanecerse y desaparece
     quedaba medio segundo en el que, ya invisible, se tragaba todos los toques
     y ningún botón respondía. */
  pointer-events: none;
}

/* ─── GALERÍA — lightbox de fotos reales ─────────────────── */
.lightbox {
  position: fixed; inset: 0; z-index: 500;
  background: oklch(0.08 0 0 / 0.94); overflow: hidden;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 14px; padding: 18px 14px;
  opacity: 0; visibility: hidden;
  transition: opacity 0.3s ease;
}
.lightbox.open { opacity: 1; visibility: visible; }
/* La foto activa, agrandada y muy desenfocada, ambienta el color detrás del
   visor (mismo patrón que un reproductor tipo Spotify/Apple Photos) en vez
   de un negro plano -- ver show() en site.js. brightness baja para que el
   vidrio y la imagen nítida de encima sigan leyéndose con contraste. */
.lightbox-backdrop {
  position: absolute; inset: -5%; z-index: 0;
  background-size: cover; background-position: center;
  filter: blur(48px) brightness(0.55) saturate(1.25);
  transform: scale(1.12);
  transition: background-image 0.35s ease;
}
.lightbox-close, .lightbox-body, .lightbox-strip { position: relative; z-index: 1; }
/* La "X" y .lightbox-body comparten z-index:1 dentro de .lightbox: como
   .lightbox-body va DESPUÉS en el marcado, en el empate gana ella y su caja
   (100% de ancho, 88vh de alto, sin fondo visible pero capturando el clic
   igual) tapaba el botón por completo -- por eso la "X" no respondía aunque
   se viera encima. z-index:2 la saca de ese empate sin tocar el resto del
   apilamiento (backdrop en 0, body/caption/strip en 1). */
.lightbox-close {
  position: absolute; top: 24px; right: 24px; z-index: 2;
  width: 42px; height: 42px; border-radius: 50%;
  background: rgba(255,255,255,0.9);
  backdrop-filter: blur(18px) saturate(160%); -webkit-backdrop-filter: blur(18px) saturate(160%);
  border: 1px solid rgba(27,59,95,0.18);
  box-shadow: 0 8px 24px rgba(0,0,0,0.18);
  color: var(--accent); display: grid; place-items: center;
  transition: background 0.18s, transform 0.18s;
}
.lightbox-close:hover { transform: scale(1.06); }
/* ─── CARRETE VERTICAL ────────────────────────────────────────
   La foto activa va nítida al centro; por arriba y por abajo asoman la
   anterior y la siguiente, desenfocadas y encogidas. La tira de miniaturas
   de la derecha se mueve con el mismo criterio. Se navega con la rueda del
   ratón, las flechas ↑/↓ o los botones. El desplazamiento lo calcula
   layoutVerticalGallery() en site.js midiendo el paso real, así que no
   depende de cuadrar números a mano en el CSS. */
.lightbox-body {
  /* La foto es VERTICAL y la diapositiva la limita por altura, así que ensanchar
     el panel no la hacía crecer: solo añadía vacío a los lados. Lo que la
     agranda es darle ALTURA. Ahora la diapositiva ocupa casi toda la pantalla
     de alto y el panel se ciñe al ancho que esa altura necesita.
     La tira de miniaturas sale del flujo y se ancla al borde derecho de la
     pantalla, para no robarle sitio a la foto. */
  position: relative;
  display: flex; align-items: center; justify-content: center;
  width: 100%; max-width: none; height: 88vh;
  padding-right: 104px;              /* hueco reservado para la tira */
  --lb-slide-h: 78vh; --lb-gap: 12px;
}
.lb-stage { position: relative; flex: 1; height: 100%; overflow: hidden; min-width: 0; }
.lb-track {
  position: absolute; left: 0; right: 0; top: 0;
  display: flex; flex-direction: column; row-gap: var(--lb-gap);
  transition: transform 0.44s cubic-bezier(0.22, 0.61, 0.36, 1);
  will-change: transform;
}
.lb-slide {
  height: var(--lb-slide-h); flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  opacity: 0.3; filter: blur(6px); transform: scale(0.88);
  transition: opacity 0.44s ease, filter 0.44s ease, transform 0.44s ease;
}
.lb-slide.act { opacity: 1; filter: none; transform: scale(1); }
/* El tamaño NO puede depender de la resolución del archivo. Cada foto entra
   con la previsualización de 640px y solo la activa y sus dos vecinas suben
   al original (subirACalidadCompleta): con solo max-width/max-height, las que
   seguían en previsualización se pintaban a 640x359 y las demás a pantalla
   -- fotos de distinto tamaño en el mismo carrete. Ahora la caja sale de la
   altura de la diapositiva y la proporción del render (2736x1536, la de todas
   las fotos del proyecto), y el archivo solo decide la nitidez. Si algún día
   entra una foto de otra proporción, `contain` la encaja sin deformarla. */
.lb-slide img {
  width: min(100%, calc(var(--lb-slide-h) * 1.78125));
  height: auto; aspect-ratio: 2736 / 1536;
  max-width: 100%; max-height: 100%; object-fit: contain;
  border-radius: var(--r); box-shadow: var(--shadow-lg);
  user-select: none; -webkit-user-drag: none;
}
.lightbox-nav {
  width: 44px; height: 44px; border-radius: 50%; flex-shrink: 0;
  background: rgba(255,255,255,0.9);
  backdrop-filter: blur(18px) saturate(160%); -webkit-backdrop-filter: blur(18px) saturate(160%);
  border: 1px solid rgba(27,59,95,0.18);
  box-shadow: 0 8px 24px rgba(0,0,0,0.18);
  color: var(--accent); display: grid; place-items: center;
  transition: background 0.18s, transform 0.18s;
}
.lightbox-nav:hover { transform: scale(1.06); }
/* Aquí vivía .lightbox-caption, el pie con el área de la toma ("Amenidades ·
   Lobby", "Depto. Modelo 2 · Cocina"...). Se retiró a pedido del cliente: en
   la galería se ven las fotos y nada más, igual en escritorio que en teléfono.
   El <div> ya no existe en index.html; ver la nota que quedó en su lugar. */
/* Tira de miniaturas — vertical, mismo criterio que el carrete grande:
   la activa nítida al centro, las vecinas desenfocadas arriba y abajo. */
.lightbox-strip {
  /* Fuera del flujo y pegada al borde derecho de la pantalla: así la foto usa
     todo el centro y la tira no le come ancho. */
  position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  flex-shrink: 0;
  width: 84px; height: 82vh; overflow: hidden;
  padding: 10px 0; border-radius: 16px;
  background: linear-gradient(155deg, rgba(255,255,255,0.14), rgba(255,255,255,0.04) 55%, rgba(255,255,255,0.09));
  backdrop-filter: blur(18px) saturate(160%); -webkit-backdrop-filter: blur(18px) saturate(160%);
  border: 1px solid rgba(255,255,255,0.22);
  box-shadow: 0 8px 24px rgba(0,0,0,0.3), inset 0 1px 0 rgba(255,255,255,0.3);
  /* Se difumina hacia los extremos para que las miniaturas entren y salgan
     sin un corte recto contra el borde del panel. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 18%, #000 82%, transparent 100%);
          mask-image: linear-gradient(180deg, transparent 0, #000 18%, #000 82%, transparent 100%);
}
.lb-strip-track {
  position: absolute; left: 0; right: 0; top: 0;
  display: flex; flex-direction: column; align-items: center; row-gap: 8px;
  transition: transform 0.44s cubic-bezier(0.22, 0.61, 0.36, 1);
  will-change: transform;
}
.lightbox-strip img {
  width: 64px; height: 44px; flex-shrink: 0;
  object-fit: cover; border-radius: 6px; cursor: pointer;
  opacity: 0.4; filter: blur(1.6px); transform: scale(0.86);
  outline: 2px solid transparent; outline-offset: 2px;
  transition: opacity 0.32s ease, filter 0.32s ease, transform 0.32s ease, outline-color 0.32s ease;
}
.lightbox-strip img:hover { opacity: 0.8; filter: none; }
.lightbox-strip img.active {
  opacity: 1; filter: none; transform: scale(1); outline-color: var(--accent);
}

/* Los botones pasan a ser ↑/↓ y se apilan con el carrete */
.lightbox-nav.prev, .lightbox-nav.next { align-self: center; }

@media (max-width: 860px) {
  /* En móvil no cabe la tira al lado: se oculta y queda el carrete + rueda/swipe */
  .lightbox-body { --lb-slide-h: 40vh; height: 66vh; gap: 10px; }
  .lightbox-strip { display: none; }
}

/* ─── PLANTAS — modal ─────────────────────────────────────── */
.modal-veil {
  position: fixed; inset: 0; z-index: 500;
  background: oklch(0.08 0 0 / 0.7); backdrop-filter: blur(6px);
  display: flex; align-items: center; justify-content: center; padding: 24px;
  opacity: 0; visibility: hidden;
  transition: opacity 0.3s ease;
}
.modal-veil.open { opacity: 1; visibility: visible; }
.modal-card {
  position: relative; max-width: 420px; width: 100%;
  background: var(--card-bg); border-radius: var(--r-lg);
  padding: 40px 32px 32px; box-shadow: var(--shadow-lg);
  display: flex; flex-direction: column; gap: 10px;
  transform: translateY(12px); transition: transform 0.3s ease;
}
.modal-veil.open .modal-card { transform: none; }
.modal-card h3 { font-size: 1.5rem; }
.modal-card p { color: var(--ink-2); font-size: 14px; line-height: 1.6; }
.modal-close {
  position: absolute; top: 16px; right: 16px;
  width: 34px; height: 34px; border-radius: 50%;
  background: rgba(255,255,255,0.94); color: var(--accent); display: grid; place-items: center;
  border: 1px solid rgba(27,59,95,0.16);
  transition: background 0.18s;
}
.modal-close:hover { background: var(--accent-light); }
.modal-plans-ph {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  padding: 32px 0; margin: 6px 0;
  border: 1.5px dashed var(--line); border-radius: var(--r);
  color: var(--muted); font-family: var(--font-d); font-size: 12.5px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase;
}
.modal-card .btn { align-self: flex-start; margin-top: 4px; }
.modal-card--gate .btn { align-self: center; }

/* SE RETIRÓ el velo claro que usaba el brochure (`oklch(.97 0 0 / .9)` con
   `saturate(140%)`).

   Ese `saturate` era el origen del tinte rosa del que se quejaba el cliente: el
   brochure se abre encima del hero, que es un render a contraluz de atardecer,
   y un velo casi blanco y traslúcido no tapa ese cielo -- lo deja pasar y
   ADEMÁS le sube la saturación un 40 %. El naranja del cielo se volvía rosa y
   teñía todo el marco del documento. Con velo oscuro el problema desaparece
   solo, porque el fondo deja de aportar color.

   El brochure usa ahora el mismo velo oscuro del resto de modales. */
/* ── VIDEO — misma ventana que el análisis por departamento ───────────────
   El bloque gemelo es `#videoOverlay` / `.video-panel` en demo/masterplan.html;
   si se toca uno, tocar el otro. Aquí vivía `.modal-card--video`: una tarjeta
   con encabezado, título y descripción, y el video debajo limitado a 70vh.
   Se retiró — el mismo clip abierto desde el índice y desde un departamento se
   veía como dos cosas distintas, y el texto no aportaba nada que el video no
   dijera ya.

   El panel se ciñe al video: sin relleno, sin fondo de tarjeta y sin sombra
   propia más que la del propio panel. */
.modal-veil--video { background: rgba(8,22,40,.86); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.video-panel {
  position: relative; width: min(1280px, 94vw);
  border-radius: 16px; overflow: hidden;
  box-shadow: 0 24px 70px rgba(8,22,40,.6);
}
.video-panel video {
  display: block; width: 100%; height: auto; max-height: 86vh;
  background: #0B2038;
}
.video-close {
  position: absolute; top: 12px; right: 12px; z-index: 5;
  width: 34px; height: 34px; border-radius: 50%;
  background: rgba(255,255,255,.22); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  border: 1px solid rgba(255,255,255,.35); color: #fff; font-size: 15px; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  transition: background .18s ease;
}
.video-close:hover { background: rgba(255,255,255,.34); }

/* ── BROCHURE — panel de vidrio, IDÉNTICO al del masterplan ───────────────
   Las dos ventanas del brochure (esta y la de demo/masterplan.html) enseñan el
   mismo PDF y ahora también con la misma caja: mismo tamaño de panel, mismo
   vidrio, mismos botones circulares arriba a la derecha y el mismo indicador
   de página. Antes esta era una hoja clara de 1180 px y la otra un panel de
   vidrio oscuro de 860: el mismo documento se veía como dos productos.

   Si se toca una, hay que tocar la otra: el bloque gemelo está en
   `demo/masterplan.html`, buscando `.brochure-panel`. */
.modal-card--brochure {
  width: min(860px, 92vw); max-width: min(860px, 92vw); height: min(880px, 88vh);
  padding: 0; border-radius: 18px; overflow: hidden;
  background: linear-gradient(155deg, rgba(255,255,255,.16), rgba(255,255,255,.05) 55%, rgba(255,255,255,.1));
  backdrop-filter: blur(22px) saturate(160%); -webkit-backdrop-filter: blur(22px) saturate(160%);
  border: 1px solid rgba(255,255,255,.24);
  box-shadow: 0 30px 80px rgba(0,0,0,.55), inset 0 1px 0 rgba(255,255,255,.3);
  gap: 0;
}
/* Contenedor con scroll de las páginas que dibuja PDF.js. La barra se oculta
   a propósito: el gesto es scroll, no una barra de visor -- y por eso existe
   el contador de página, que es lo que devuelve la referencia perdida. */
.brochure-pages {
  width: 100%; height: 100%; overflow-y: auto; overflow-x: hidden;
  padding: 56px 20px 24px;
  scrollbar-width: none; -ms-overflow-style: none;
  scroll-behavior: smooth;
}
.brochure-pages::-webkit-scrollbar { width: 0; height: 0; display: none; }
.brochure-page-holder { margin: 0 auto 16px; max-width: 100%; }
.brochure-page {
  display: block; width: 100%; height: 100%;
  border-radius: 8px; box-shadow: 0 6px 22px rgba(0,0,0,.35);
}
.brochure-status {
  text-align: center; padding: 100px 20px;
  font-size: 14px; color: rgba(255,255,255,.75);
}
/* Botones de acción: círculos de vidrio, como en el masterplan. Sustituyen a
   la píldora "Descargar" con texto que había arriba a la izquierda. */
.brochure-actions { position: absolute; top: 14px; right: 14px; z-index: 5; display: flex; gap: 8px; }
.brochure-icon-btn {
  width: 36px; height: 36px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: rgba(255,255,255,.14); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  border: 1px solid rgba(255,255,255,.28); color: #fff; text-decoration: none;
  transition: background .18s, transform .18s;
}
.brochure-icon-btn:hover { background: rgba(255,255,255,.28); transform: scale(1.06); }

/* Contador de página. Con la barra de desplazamiento oculta y 15 láminas, no
   había forma de saber por dónde vas ni cuánto falta: el documento se sentía
   sin fondo. Se muestra al desplazar y se desvanece al quedarse quieto, para
   no competir con la lámina. Las flechas del teclado saltan de página. */
.brochure-nav {
  position: absolute; left: 50%; bottom: 16px; transform: translateX(-50%);
  z-index: 5; display: flex; align-items: center; gap: 10px;
  height: 32px; padding: 0 6px 0 14px; border-radius: 999px;
  background: rgba(10,20,32,.52); backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border: 1px solid rgba(255,255,255,.18);
  font-family: var(--font-d, inherit); font-size: 12px; font-weight: 600;
  letter-spacing: .08em; color: rgba(255,255,255,.92);
  opacity: 0; transition: opacity .25s ease; pointer-events: none;
}
.brochure-nav.visible { opacity: 1; pointer-events: auto; }
.brochure-nav b { font-variant-numeric: tabular-nums; font-weight: 700; }
.brochure-nav span { opacity: .55; font-weight: 500; }
.brochure-nav button {
  width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center;
  background: rgba(255,255,255,.12); border: 1px solid rgba(255,255,255,.2);
  color: #fff; font-size: 11px; line-height: 1; transition: background .15s;
}
.brochure-nav button:hover:not(:disabled) { background: rgba(255,255,255,.28); }
.brochure-nav button:disabled { opacity: .3; cursor: default; }

.modal-card--plans { max-width: 560px; }
.plans-towers, .plans-styles { display: flex; gap: 8px; flex-wrap: wrap; }
.plans-tower-btn, .plans-style-btn {
  padding: 8px 16px; border-radius: 999px; border: 1px solid rgba(27,59,95,0.18);
  background: rgba(255,255,255,0.94); color: rgba(27,59,95,0.55); font-family: var(--font-d);
  font-size: 12.5px; font-weight: 600; letter-spacing: 0.03em; text-transform: uppercase;
  transition: background 0.18s, color 0.18s, border-color 0.18s;
}
.plans-tower-btn:hover, .plans-style-btn:hover { border-color: var(--accent); color: var(--accent); }
.plans-tower-btn.active, .plans-style-btn.active {
  background: rgba(255,255,255,0.94); color: var(--accent); border-color: var(--accent);
}
.plans-viewer {
  border-radius: var(--r); overflow: hidden; background: var(--bg);
  margin: 4px 0;
}
.plans-viewer img { width: 100%; height: auto; display: block; }

@media (max-width: 720px) {
  .hero-top-foot { flex-direction: column; align-items: flex-start; gap: 20px; }
  .stage-drawer { width: 84vw; max-width: 300px; }
  .idx-brand.shift { left: calc(min(84vw, 300px) + 12px); }
  /* En angosto, el fullscreen ya no deja suficiente margen al lado. */
  .stage-pills-right { top: 64px; right: 14px; }
  .stage-pill { padding: 8px 14px; font-size: 11.5px; }
  .stage-chevron { width: 38px; height: 38px; }
  /* El carrete es vertical: los botones van arriba y abajo, centrados,
     no a los lados (antes .lightbox-main los apilaba en horizontal). */
  .lightbox-nav { position: absolute; left: 50%; transform: translateX(-50%); z-index: 2; }
  .lightbox-nav.prev { top: 10px; } .lightbox-nav.next { bottom: 10px; }
}

/* ─── HERO DEMO CARD ─────────────────────────────────────── */
.hero-demo { position: relative; }
.building-card {
  background: var(--card-dark);
  border-radius: var(--r-lg);
  overflow: hidden;
  box-shadow: var(--shadow-lg);
  min-height: 500px;
  display: flex; flex-direction: column;
  position: relative;
}
.building-card .sky {
  position: absolute; inset: 0;
  background:
    linear-gradient(180deg, rgba(8,17,30,0.08) 0%, rgba(8,17,30,0.28) 55%, rgba(8,17,30,0.80) 100%),
    url('assets/puebla-landscape.webp') center bottom / cover no-repeat;
  opacity: 0.82;
}
.bc-head {
  position: relative; z-index: 2;
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px 20px;
}
.bc-head .tag {
  display: inline-flex; align-items: center; gap: 8px;
  font-family: var(--font-d); font-size: 11.5px; font-weight: 600;
  letter-spacing: 0.06em; color: rgba(255,255,255,0.9);
  background: rgba(255,255,255,0.1); backdrop-filter: blur(8px);
  padding: 5px 12px; border-radius: 999px;
  border: 1px solid rgba(255,255,255,0.15);
}
.live-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: #4ade80;
  animation: livePulse 1.8s ease-in-out infinite;
}
@keyframes livePulse { 0%,100% { opacity: 1; } 50% { opacity: 0.4; } }
.bc-head .dots { display: flex; gap: 6px; }
.bc-head .dots i { width: 10px; height: 10px; border-radius: 50%; background: rgba(255,255,255,0.22); }
.bc-stage {
  position: relative; z-index: 2;
  display: flex; gap: 12px; align-items: center; justify-content: center;
  flex: 1; padding: 12px 16px 0;
}
/* ── Dual-tower hero card layout ─────────────────────── */
.bc-dual-wrap {
  display: flex;
  flex-direction: row;
  align-items: flex-end;
  gap: 0;
  flex: 1;
  justify-content: center;
}
.bc-tower-lbl {
  font-family: var(--font-d);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.45);
  writing-mode: vertical-lr;
  transform: rotate(180deg);
  padding: 4px 2px;
  align-self: center;
}
.bc-dist-col {
  width: 10px;
  align-self: stretch;
  background: linear-gradient(180deg,
    rgba(80,78,72,0.15) 0%,
    rgba(50,50,46,0.35) 100%);
  border-left: 1px solid rgba(255,255,255,0.08);
  border-right: 1px solid rgba(255,255,255,0.08);
  margin: 0 2px;
}
.bc-tower, #heroTower, #heroTowerA, #heroTowerB {
  display: flex; flex-direction: column; gap: 4px;
  width: 78px;
}
.bc-crown {
  width: 55%; margin: 0 auto 2px; height: 12px;
  border-radius: 4px 4px 0 0;
  background: linear-gradient(180deg, rgba(200,200,195,0.9), rgba(160,160,155,0.85));
}
.bc-floor {
  height: 18px; border-radius: 3px;
  background: linear-gradient(180deg, rgba(240,238,232,0.94), rgba(210,208,200,0.9));
  border: 1px solid rgba(255,255,255,0.3);
  position: relative; cursor: none;
  transition: transform 0.25s cubic-bezier(.2,.8,.2,1), background 0.25s;
}
.bc-floor::before {
  content: ""; position: absolute; inset: 3px 6px;
  background: repeating-linear-gradient(90deg, rgba(100,95,85,0.3) 0 5px, transparent 5px 10px);
  border-radius: 1px;
}
.bc-floor:hover { transform: translateX(-5px) scale(1.02); }
.bc-floor.active {
  background: linear-gradient(180deg, rgba(215,210,195,0.98), rgba(190,185,170,0.95));
  border-color: rgba(44,93,143,0.6);
  box-shadow: -3px 3px 16px rgba(44,93,143,0.3);
  transform: translateX(-8px) scale(1.04);
}
.bc-floor.amenity { height: 26px; background: linear-gradient(180deg, rgba(180,175,165,0.9), rgba(155,150,140,0.88)); }
.bc-base {
  height: 32px; border-radius: 3px 3px 6px 6px;
  background: linear-gradient(180deg, rgba(80,78,72,0.9), rgba(50,50,46,0.95));
  box-shadow: 2px 6px 18px rgba(0,0,0,0.45);
}
.bc-rail {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 4px 0;
  max-height: 420px; overflow-y: auto; scrollbar-width: none;
}
.bc-rail::-webkit-scrollbar { display: none; }
.bc-rail-item {
  width: 38px; height: 28px; border-radius: 999px;
  color: rgba(255,255,255,0.75); font-family: var(--font-d); font-size: 12px; font-weight: 600;
  display: grid; place-items: center; transition: 0.16s; flex-shrink: 0;
}
.bc-rail-item:hover { color: #fff; background: rgba(255,255,255,0.12); }
.bc-rail-item.active { background: #fff; color: var(--ink); box-shadow: 0 3px 12px rgba(0,0,0,0.3); }

.bc-readout {
  position: relative; z-index: 2;
  padding: 10px 20px; min-height: 38px;
  font-family: var(--font-d); font-size: 12px; font-weight: 600;
  color: rgba(255,255,255,0.65); letter-spacing: 0.04em;
  text-align: center;
}
.unit-pop {
  position: absolute; bottom: 14px; left: 14px; z-index: 10;
  background: #fff; border-radius: 14px; padding: 14px 16px;
  box-shadow: var(--shadow); min-width: 220px;
  font-family: var(--font-d);
  animation: popIn 0.22s cubic-bezier(.2,.8,.2,1);
}
@keyframes popIn { from { opacity: 0; transform: scale(0.94) translateY(6px); } to { opacity: 1; transform: none; } }
.unit-pop .up-badge {
  font-size: 10px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase;
  padding: 3px 9px; border-radius: 999px; float: right;
}
.unit-pop .up-code { font-size: 22px; font-weight: 700; letter-spacing: -0.02em; margin-top: 4px; }
.unit-pop .up-model { font-size: 12px; color: var(--muted); margin-top: 2px; }
.unit-pop .up-specs {
  display: flex; gap: 12px; margin-top: 10px; padding-top: 10px;
  border-top: 1px solid var(--line); font-size: 12px; color: var(--ink-2);
}
.unit-pop .up-btn {
  display: block; width: 100%; margin-top: 10px;
  background: var(--ink); color: #fff; border-radius: 8px;
  padding: 9px; font-size: 12.5px; font-weight: 600; text-align: center;
  transition: background 0.18s;
}
.unit-pop .up-btn:hover { background: var(--ink-2); }

/* ─── ANIMACIONES REVEAL ─────────────────────────────────── */
.reveal {
  opacity: 0; transform: translateY(24px);
  transition: opacity 0.65s cubic-bezier(.2,.8,.2,1), transform 0.65s cubic-bezier(.2,.8,.2,1);
}
.reveal.in { opacity: 1; transform: none; }
.d1 { transition-delay: 0.1s; }
.d2 { transition-delay: 0.2s; }
.d3 { transition-delay: 0.3s; }

/* ─── DISPONIBILIDAD — malla interactiva (solo vive en #stageAvailFullscreen) ─── */
.availability { background: var(--bg-alt); }
#disponibilidad #availOpenBtn { margin-top: 32px; }
/* Malla SVG: viewBox = dimensiones naturales (1360x768) de la fachada, así que
   escala junto con la imagen sin ningún cálculo de JS ni listener de resize. */
.avail-malla { position: absolute; inset: 0; width: 100%; height: 100%; transition: opacity 0.5s ease; }

/* Fotograma 1: overlay encima del fotograma 2 (misma proporción 1360x768,
   así que object-fit:cover no recorta nada -- el contenedor ya tiene esa
   proporción exacta gracias al aspect-ratio de .stage-avail-media). */
.stage-avail-img-frame1 {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; z-index: 1;
}
.avail-malla-frame1 { z-index: 2; }
.unit-poly {
  cursor: pointer; stroke: rgba(255,255,255,0.55); stroke-width: 1.5px;
  opacity: 0.6; transition: opacity 0.15s, stroke 0.15s, stroke-width 0.15s;
  vector-effect: non-scaling-stroke;
}
.unit-poly:hover { opacity: 0.88; }
.unit-poly.active { opacity: 0.95; stroke: #fff; stroke-width: 2.5px; }
/* Filtro por estatus (carrusel del widget de conteo, ver applyStatusFilter en
   site.js): las unidades que no son del estatus elegido quedan casi
   invisibles y dejan de responder a click, sin desmontarlas del SVG. */
.unit-poly.dimmed { opacity: 0.06; pointer-events: none; }
.unit-poly.dimmed:hover { opacity: 0.06; }

.avail-panel-eyebrow {
  font-family: var(--font-d); font-size: 11px; font-weight: 600;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted);
}
.avail-panel-empty { margin-top: 10px; color: var(--muted); font-size: 14.5px; }
.avail-unit-title { font-family: var(--font-d); font-size: 22px; font-weight: 700; margin-top: 10px; color: var(--ink); }
.avail-unit-status {
  display: inline-flex; align-items: center; gap: 6px; margin-top: 8px;
  font-size: 12.5px; font-weight: 600; padding: 4px 12px; border-radius: 999px;
}
.avail-unit-status.Disponible { color: var(--status-disponible); background: var(--status-disponible-bg); }
/* Reservado (233,195,114) es demasiado claro para usarse como color de TEXTO
   sobre su propio fondo -- aquí el fondo sí es el RGB de marca a máxima
   presencia, pero el texto usa el ink oscuro del sitio para que se lea. */
.avail-unit-status.Reservado  { color: var(--ink); background: var(--status-reservado); }
.avail-unit-status.Ocupado    { color: var(--status-ocupado); background: var(--status-ocupado-bg); }
.avail-unit-grid {
  display: grid; grid-template-columns: 1fr 1fr; gap: 10px 18px;
  margin-top: 18px; font-size: 13.5px; list-style: none; padding: 0;
}
.avail-unit-grid dt { color: var(--muted); margin: 0; }
.avail-unit-grid dd { color: var(--ink); font-weight: 600; margin: 0 0 8px; }
.avail-unit-notes { margin-top: 12px; font-size: 12.5px; color: var(--muted); font-style: italic; }

.avail-plan-btn { width: 100%; justify-content: center; font-size: 13.5px; padding: 12px 16px; margin-top: 18px; }
.avail-price-gate { margin-top: 18px; }
.avail-price-btn { width: 100%; justify-content: center; font-size: 13.5px; padding: 12px 16px; }
.avail-price-btn[hidden] { display: none; }
.avail-price-form { display: flex; flex-direction: column; gap: 8px; }
.avail-price-form[hidden] { display: none; }
.avail-price-form input {
  padding: 11px 14px; border-radius: 10px; border: 1.5px solid var(--line);
  background: var(--bg); font-size: 14px; color: var(--ink); outline: none;
  transition: border-color 0.18s; font-family: inherit;
}
.avail-price-form input:focus { border-color: var(--ink); }
.avail-price-form button { margin-top: 2px; font-size: 13.5px; padding: 11px 16px; justify-content: center; }

/* Widget de conteo (esquina inferior derecha de disponibilidad): dos
   mitades -- número de departamentos a la izquierda, carrusel horizontal de
   estatus (Disponible/Reservado/Ocupado) a la derecha. La opción activa
   queda centrada y grande; las vecinas se asoman más chicas y atenuadas a
   los lados, clipeadas por .acw-carousel, y son clickeables directamente
   para saltar a esa opción -- igual criterio que un carrusel vertical tipo
   "picker", solo que horizontal. Al cambiar, el número se actualiza al
   conteo de esa categoría. */
.acw { display: flex; align-items: center; gap: 14px; }
.acw-count { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; flex: 0 0 auto; }
.acw-number { font-family: var(--font-d); font-size: 28px; font-weight: 700; color: var(--ink); line-height: 1; }
.acw-word { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.06em; }
.acw-sep { width: 1px; align-self: stretch; background: var(--line); }

.acw-carousel { position: relative; width: 210px; height: 32px; overflow: hidden; }
.acw-track {
  position: absolute; top: 0; left: 0; display: flex; align-items: center; gap: 8px;
  transition: transform 0.35s cubic-bezier(0.2, 0.8, 0.2, 1);
  will-change: transform;
}
.acw-opt {
  flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px;
  font-family: var(--font-b); font-size: 11.5px; font-weight: 600; white-space: nowrap;
  text-transform: uppercase; letter-spacing: 0.04em;
  padding: 7px 14px; border-radius: 999px; border: 1px solid rgba(27,59,95,0.18);
  background: rgba(255,255,255,0.94); color: rgba(27,59,95,0.55); cursor: pointer;
  opacity: 0.7; transform: scale(0.86);
  transition: opacity 0.3s ease, transform 0.3s ease, background 0.2s, color 0.2s, border-color 0.2s;
}
.acw-opt .dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.acw-opt.active { opacity: 1; transform: scale(1); color: var(--accent); background: rgba(255,255,255,0.94); border-color: var(--accent); }

/* ─── SERVICIOS ──────────────────────────────────────────── */
.services { background: transparent; }
.svc-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px;
  margin-top: 56px;
}
.svc-card {
  background: rgba(255,255,255,0.52);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  border-radius: var(--r);
  padding: 32px 28px;
  border: 1px solid rgba(255,255,255,0.75);
  transition: transform 0.25s, box-shadow 0.25s, border-color 0.25s, background 0.25s;
  position: relative; overflow: hidden;
}
.svc-card:hover {
  background: rgba(255,255,255,0.72);
}
.svc-card::after {
  content: ""; position: absolute; bottom: 0; left: 0; right: 0; height: 2px;
  background: var(--accent); transform: scaleX(0); transform-origin: left;
  transition: transform 0.35s cubic-bezier(.2,.8,.2,1);
}
.svc-card:hover { transform: translateY(-3px); border-color: transparent; }
.svc-card:hover::after { transform: scaleX(1); }
.svc-card .num {
  font-family: var(--font-d); font-size: 11px; font-weight: 700;
  letter-spacing: 0.1em; color: var(--accent); display: block; margin-bottom: 16px;
}
.svc-card .ico {
  width: 44px; height: 44px; border-radius: 10px;
  background: var(--bg); display: grid; place-items: center;
  margin-bottom: 16px; color: var(--ink-2);
}
.svc-card h3 { font-size: 1.05rem; margin-bottom: 10px; }
.svc-card p { font-size: 14.5px; color: var(--muted); line-height: 1.6; }

/* ─── CLIENTES / LOGOS ───────────────────────────────────── */
.logos {
  display: flex; align-items: center; justify-content: center;
  flex-wrap: wrap; gap: 12px; margin: 48px 0;
}
.logo-ph {
  display: inline-flex; align-items: center; gap: 10px;
  padding: 10px 20px; border-radius: 999px;
  background: var(--card-bg); border: 1px solid var(--line);
  font-family: var(--font-d); font-size: 13px; font-weight: 600; color: var(--ink-2);
  transition: 0.2s;
}
.logo-ph:hover { border-color: var(--ink); color: var(--ink); }
.logo-ph .glyph { width: 24px; height: 24px; display: grid; place-items: center; color: var(--ink-2); }
.logo-ph .glyph svg { width: 100%; height: 100%; }

/* ─── TESTIMONIOS ────────────────────────────────────────── */
.tst-grid {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; margin-top: 56px;
}
.tst-card {
  background: var(--card-bg); border-radius: var(--r);
  padding: 28px; border: 1px solid var(--line);
}
.tst-card .stars { color: var(--accent); font-size: 14px; letter-spacing: 2px; }
.tst-card .quote {
  margin-top: 14px; font-size: 15px; line-height: 1.65;
  color: var(--ink-2); font-style: italic;
}
.tst-who { display: flex; align-items: center; gap: 12px; margin-top: 20px; }
.tst-who .pf {
  width: 38px; height: 38px; border-radius: 50%; flex-shrink: 0;
  background: var(--ink); color: #fff;
  font-family: var(--font-d); font-size: 12px; font-weight: 700;
  display: grid; place-items: center;
}
.tst-who .nm { font-family: var(--font-d); font-size: 13.5px; font-weight: 600; }
.tst-who .rl { font-size: 12px; color: var(--muted); margin-top: 2px; }

/* ─── ABOUT ──────────────────────────────────────────────── */
.about { background: transparent; color: var(--ink); position: relative; }
.about h2, .about h4 { color: var(--ink); }
.about .eyebrow { color: var(--muted); }
.about .eyebrow::before { background: var(--accent); }
.about p { color: var(--ink-2); }
.about-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 80px; align-items: center; }
.about-values { margin-top: 28px; display: flex; flex-direction: column; gap: 14px; }
.about-values .v { display: flex; align-items: flex-start; gap: 12px; }
.about-values .ck {
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--accent-light); color: var(--accent-ink);
  font-size: 12px; font-weight: 700; display: grid; place-items: center; flex-shrink: 0; margin-top: 2px;
}
.stats { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.stats .s {
  background: transparent; border: none;
  border-left: 3px solid var(--accent);
  border-radius: 0; padding: 14px 20px;
  transition: transform 0.25s;
}
.stats .s:hover { transform: translateX(4px); }
.stats .s b {
  font-family: var(--font-d); font-size: 2.6rem; font-weight: 800;
  color: var(--ink); display: block; letter-spacing: -0.03em; line-height: 1;
}
.stats .s b em { font-style: normal; color: var(--accent); }
.stats .s span { font-size: 13px; color: var(--muted); margin-top: 6px; display: block; line-height: 1.4; }

/* ─── MAPA INTERACTIVO ───────────────────────────────────── */
/* Antes tenía dos vistas (análisis urbano 500m con Leaflet -- círculo de
   radio, chips de categoría, marcadores, toggle -- y Google Maps). El
   análisis se quitó por completo; solo queda el iframe de Google Maps,
   ocupando el mismo .imap que antes. */
.contact-grid { display: grid; grid-template-columns: 1.2fr 1fr; gap: 40px; align-items: start; margin-top: 48px; }
.map-wrap { }
.imap {
  position: relative; border-radius: var(--r-lg); overflow: hidden;
  background: var(--card-dark); aspect-ratio: 600/440;
  box-shadow: var(--shadow-lg);
}
.imap-gmaps { position: absolute; inset: 0; }
.imap-gmaps iframe { width: 100%; height: 100%; border: 0; }

/* ─── MAPA CON BUSCADOR Y RUTAS (Google Maps Platform) ──────────────────────
   Solo se ve si config-mapa.js tiene clave; si no, manda el iframe de arriba.
   La caja de controles va DEBAJO del mapa y no encima: superpuesta tapaba
   justo la franja donde caen los resultados más cercanos al predio. */
.imap-live { position: absolute; inset: 0; }
.imap-ui {
  margin-top: 14px; display: flex; flex-direction: column; gap: 10px;
  font-family: var(--font-b);
}
.imap-buscar {
  display: flex; align-items: center; gap: 10px;
  padding: 0 14px; height: 46px; border-radius: 12px;
  background: #fff; border: 1.5px solid var(--line); color: var(--ink-2);
}
.imap-buscar:focus-within { border-color: var(--accent); }
.imap-buscar input {
  flex: 1; min-width: 0; border: 0; outline: 0; background: transparent;
  font-size: 14.5px; color: var(--ink);
}
.imap-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.imap-chip {
  padding: 7px 14px; border-radius: 999px;
  background: #fff; border: 1.5px solid var(--line);
  font-family: var(--font-d); font-size: 12.5px; font-weight: 600; color: var(--ink-2);
  transition: background .16s, border-color .16s, color .16s;
}
.imap-chip:hover { border-color: var(--accent); color: var(--accent); }
.imap-chip.act { background: var(--accent); border-color: var(--accent); color: #fff; }

.imap-aviso { font-size: 13px; color: var(--ink-2); min-height: 0; display: none; }
.imap-aviso.on { display: block; }
.imap-aviso.malo { color: var(--sold); }

.imap-res { display: flex; flex-direction: column; gap: 6px; max-height: 260px; overflow-y: auto; }
.imap-item {
  display: flex; align-items: center; gap: 12px; width: 100%;
  padding: 10px 12px; border-radius: 10px; text-align: left;
  background: #fff; border: 1.5px solid var(--line);
  transition: border-color .16s, background .16s;
}
.imap-item:hover { border-color: var(--accent); background: var(--accent-light); }
.imap-num {
  flex: 0 0 24px; height: 24px; border-radius: 50%;
  background: #3d7ea6; color: #fff;
  display: grid; place-items: center;
  font-family: var(--font-d); font-size: 11.5px; font-weight: 700;
}
.imap-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.imap-txt b { font-size: 14px; font-weight: 600; color: var(--ink); }
.imap-txt i {
  font-style: normal; font-size: 12px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* Distancia y tiempo. Tabulares para que la columna no baile al ir llegando. */
.imap-dato {
  flex: 0 0 auto; font-family: var(--font-d); font-size: 12.5px; font-weight: 600;
  color: var(--accent); font-variant-numeric: tabular-nums;
}

/* ─── INFO DE CONTACTO ───────────────────────────────────── */
.contact-info { display: flex; flex-direction: column; gap: 22px; padding-top: 16px; }
.ci-row { display: flex; align-items: flex-start; gap: 16px; }
.ci-row .ico {
  width: 42px; height: 42px; border-radius: 10px;
  background: var(--card-bg); border: 1px solid var(--line);
  display: grid; place-items: center; flex-shrink: 0; color: var(--ink-2);
}
.ci-row .lbl { font-family: var(--font-d); font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); }
.ci-row .val { margin-top: 3px; font-size: 15px; font-weight: 500; line-height: 1.4; }
.ci-row a:hover { color: var(--accent); }
.ci-socials { display: flex; gap: 10px; margin-top: 6px; }
.ci-socials a {
  width: 40px; height: 40px; border-radius: 50%;
  background: rgba(255,255,255,0.94); border: 1px solid rgba(27,59,95,0.18);
  display: grid; place-items: center; color: var(--accent);
  transition: 0.18s;
}
.ci-socials a:hover { background: var(--accent-light); border-color: var(--accent); }

/* ─── FORMULARIO ─────────────────────────────────────────── */
.form-section { background: transparent; }
.form-card {
  background: transparent; border-radius: 0;
  padding: 40px 0; max-width: 820px; margin: 0 auto;
  box-shadow: none;
}
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; }
.field { display: flex; flex-direction: column; gap: 7px; }
.field.full { grid-column: 1 / -1; }
.field label { font-family: var(--font-d); font-size: 12px; font-weight: 600; letter-spacing: 0.04em; color: var(--ink-2); }
.field .req { color: var(--accent); }
.field input, .field select, .field textarea {
  padding: 12px 16px; border-radius: 10px;
  border: 1.5px solid var(--line); background: var(--bg);
  font-size: 14.5px; color: var(--ink); transition: border-color 0.18s;
  outline: none;
}
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--ink); }
.field.invalid input, .field.invalid select, .field.invalid textarea { border-color: var(--sold); }
.err-msg { font-size: 12px; color: var(--sold); display: none; }
.field.invalid .err-msg { display: block; }
.field textarea { min-height: 100px; resize: vertical; }
.form-foot {
  display: flex; align-items: center; justify-content: space-between;
  margin-top: 24px; gap: 20px; flex-wrap: wrap;
}
.consent { display: flex; align-items: flex-start; gap: 9px; font-size: 13px; color: var(--muted); cursor: none; }
.consent input { margin-top: 2px; accent-color: var(--ink); }
.form-ok {
  display: none; text-align: center; padding: 24px 0;
}
.form-ok.visible { display: block; }
.form-ok .check {
  width: 60px; height: 60px; border-radius: 50%;
  background: var(--ok-bg); color: var(--ok);
  display: grid; place-items: center; margin: 0 auto 16px;
}
.form-ok h3 { font-size: 1.3rem; }
.form-ok p { color: var(--muted); margin-top: 8px; font-size: 14.5px; }

/* ─── FOOTER ─────────────────────────────────────────────── */
.footer { background: var(--ink); color: rgba(255,255,255,0.65); padding: 60px 0 32px; }
.spectrum-bar {
  height: 3px; border-radius: 999px; margin-bottom: 48px;
  background: linear-gradient(90deg, var(--accent) 0%, #7FA8D4 40%, var(--accent) 70%, #3D6E9E 100%);
  opacity: 0.7;
}
.footer .logo.on-dark .company,
.footer .logo.on-dark .project { color: rgba(255,255,255,0.9); }
.footer .logo.on-dark .alli { color: rgba(255,255,255,0.35); }
.footer-blurb { font-size: 13.5px; margin-top: 14px; max-width: 280px; line-height: 1.6; }
.footer-grid {
  display: grid; grid-template-columns: 1.4fr 1fr 1fr 1fr; gap: 40px; padding-bottom: 40px;
}
.footer-col { display: flex; flex-direction: column; gap: 12px; }
.footer-col h4 { font-family: var(--font-d); font-size: 11px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: rgba(255,255,255,0.4); }
.footer-col a, .footer-col p { font-size: 14px; color: rgba(255,255,255,0.6); transition: color 0.18s; line-height: 1.5; }
.footer-col a:hover { color: #fff; }
.footer-bottom {
  display: flex; align-items: center; justify-content: space-between;
  padding-top: 24px; border-top: 1px solid rgba(255,255,255,0.1);
  font-size: 12.5px; gap: 16px; flex-wrap: wrap;
}

/* ─── RESPONSIVE ─────────────────────────────────────────── */
@media (max-width: 1024px) {
  .hero-grid { grid-template-columns: 1fr; gap: 48px; }
  .hero-copy { max-width: 100%; }
  .hero-demo { max-width: 500px; margin: 0 auto; }
  .svc-grid { grid-template-columns: repeat(2, 1fr); }
  .about-grid { grid-template-columns: 1fr; gap: 48px; }
  .contact-grid { grid-template-columns: 1fr; }
  .footer-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 768px) {
  :root { --nav-h: 60px; }
  .wrap { padding: 0 20px; }
  .section-pad { padding: 72px 0; }
  h1 { font-size: 2.2rem; }
  h2 { font-size: 1.75rem; }
  .nav-links { display: none; }
  .nav-toggle { display: flex; }
  .nav-cta .btn-ghost { display: none; }
  .svc-grid { grid-template-columns: 1fr; }
  .tst-grid { grid-template-columns: 1fr; }
  .form-grid { grid-template-columns: 1fr; }
  .form-foot { flex-direction: column; align-items: stretch; }
  .form-card { padding: 28px 20px; }
  .footer-grid { grid-template-columns: 1fr; gap: 28px; }
  .hero-trust { flex-wrap: wrap; gap: 16px; }
  .stats { grid-template-columns: 1fr; }
  /* El cursor NO se apaga por ancho: una ventana de escritorio estrecha
     sigue teniendo ratón. Lo decide el tipo de puntero, en cursor-marea.js. */
}
@media (max-width: 480px) {
  .building-card { min-height: 400px; }
  .hero-actions { flex-direction: column; }
  .hero-actions .btn { justify-content: center; }
}

/* ─── ANIMACIONES EXTRA ──────────────────────────────────── */

/* Números de stats — counter animation */
@keyframes countUp {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}
.stats .s.in b { animation: countUp 0.7s cubic-bezier(.2,.8,.2,1) forwards; }
.stats .s:nth-child(2).in b { animation-delay: 0.1s; }
.stats .s:nth-child(3).in b { animation-delay: 0.2s; }
.stats .s:nth-child(4).in b { animation-delay: 0.3s; }

/* Iconos de servicios — giro suave al hover */
.svc-card .ico {
  transition: transform 0.4s cubic-bezier(.2,.8,.2,1), background 0.3s, color 0.3s;
}
.svc-card:hover .ico {
  transform: rotate(12deg) scale(1.15);
  background: var(--accent); color: #fff;
}

/* Números del hero-trust — ping */
.stat b {
  display: inline-block;
  animation: statPop 0.6s cubic-bezier(.2,.8,.2,1) both;
}
.stat:nth-child(1) b { animation-delay: 0.6s; }
.stat:nth-child(3) b { animation-delay: 0.75s; }
.stat:nth-child(5) b { animation-delay: 0.9s; }
@keyframes statPop {
  0%  { opacity: 0; transform: scale(0.6) translateY(8px); }
  70% { transform: scale(1.08); }
  100%{ opacity: 1; transform: none; }
}

/* CTA buttons — shimmer terracota */
.btn-accent::after, .btn-primary::after {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: linear-gradient(105deg, transparent 40%, rgba(27,59,95,0.12) 50%, transparent 60%);
  transform: translateX(-100%); transition: transform 0.5s ease;
}
.btn-accent, .btn-primary { position: relative; overflow: hidden; }
.btn-accent:hover::after, .btn-primary:hover::after { transform: translateX(100%); }

/* Logo icon — subtle float */
.logo .iso-wrap {
  animation: logoFloat 4s ease-in-out infinite;
}
@keyframes logoFloat {
  0%, 100% { transform: translateY(0); }
  50%       { transform: translateY(-3px); }
}

/* Testimonial stars — stagger */
.tst-card .stars {
  display: inline-block;
  animation: starFade 0.5s ease both;
}
.tst-card.in .stars { animation-play-state: running; }
@keyframes starFade {
  from { opacity: 0; letter-spacing: -4px; }
  to   { opacity: 1; letter-spacing: 2px; }
}

/* Eyebrow line — slide in */
.eyebrow::before {
  transition: width 0.6s cubic-bezier(.2,.8,.2,1);
}
.reveal.in .eyebrow::before { width: 28px; }

/* Scroll indicator (hero bottom) */
@keyframes scrollBounce {
  0%, 100% { transform: translateX(-50%) translateY(0); }
  50%       { transform: translateX(-50%) translateY(6px); }
}

/* Nav links — underline animada */
.nav-links a {
  position: relative;
}
.nav-links a::after {
  content: ""; position: absolute; bottom: -2px; left: 0; right: 0;
  height: 1.5px; background: var(--accent);
  transform: scaleX(0); transform-origin: left;
  transition: transform 0.25s cubic-bezier(.2,.8,.2,1);
}
.nav-links a:hover::after { transform: scaleX(1); }

/* Service cards — número flip */
.svc-card .num {
  transition: color 0.3s;
}
.svc-card:hover .num { color: var(--accent); }

/* Testimonio cards — borde dorado al hover */
.tst-card {
  transition: transform 0.25s, box-shadow 0.25s, border-color 0.25s;
  border-bottom: 3px solid transparent;
}
.tst-card:hover {
  transform: translateY(-4px);
  box-shadow: 0 16px 40px rgba(0,0,0,0.1);
  border-bottom-color: var(--accent);
}

/* ═══════════════════════════════════════════════
   SISTEMA DE APARICIÓN — Entry animations
   Cada elemento aparece con su animación propia
   sin alterar el diseño final
═══════════════════════════════════════════════ */

/* Base: todos los animables comienzan invisibles */
.reveal        { opacity: 0; transform: translateY(28px); }
.reveal-left   { opacity: 0; transform: translateX(-32px); }
.reveal-right  { opacity: 0; transform: translateX(32px); }
.reveal-scale  { opacity: 0; transform: scale(0.88); }
.reveal-fade   { opacity: 0; }

/* Activado por JS al entrar en viewport */
.reveal.in, .reveal-left.in, .reveal-right.in,
.reveal-scale.in, .reveal-fade.in {
  opacity: 1;
  transform: none;
  transition:
    opacity 0.72s cubic-bezier(.2,.8,.2,1),
    transform 0.72s cubic-bezier(.2,.8,.2,1);
}

/* Delays escalonados */
.d1 { transition-delay: 0.08s !important; }
.d2 { transition-delay: 0.16s !important; }
.d3 { transition-delay: 0.24s !important; }
.d4 { transition-delay: 0.32s !important; }
.d5 { transition-delay: 0.40s !important; }
.d6 { transition-delay: 0.48s !important; }

/* HERO COPY — desliza desde izquierda */
.hero-copy { opacity: 0; transform: translateX(-40px); animation: heroLeft 0.9s 0.3s cubic-bezier(.2,.8,.2,1) forwards; }
@keyframes heroLeft {
  to { opacity: 1; transform: none; }
}

/* HERO DEMO CARD — desliza desde derecha */
.hero-demo { opacity: 0; transform: translateX(40px); animation: heroRight 0.9s 0.5s cubic-bezier(.2,.8,.2,1) forwards; }
@keyframes heroRight {
  to { opacity: 1; transform: none; }
}

/* STATS en hero — escalonado */
.hero-trust .stat:nth-child(1) { animation: statRise 0.6s 0.9s both; }
.hero-trust .stat:nth-child(3) { animation: statRise 0.6s 1.0s both; }
.hero-trust .stat:nth-child(5) { animation: statRise 0.6s 1.1s both; }
@keyframes statRise {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: none; }
}

/* SVC-CARD icon — giro de entrada */
.svc-card.in .ico {
  animation: icoSpin 0.5s cubic-bezier(.2,.8,.2,1) both;
}
@keyframes icoSpin {
  from { opacity: 0; transform: rotate(-15deg) scale(0.7); }
  to   { opacity: 1; transform: none; }
}

/* TST-CARD — fade con ligera escala */
.tst-card.in {
  animation: tstIn 0.65s cubic-bezier(.2,.8,.2,1) both;
}
@keyframes tstIn {
  from { opacity: 0; transform: translateY(20px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}

/* ABOUT STATS — zoom conteo */
.stats .s.in {
  animation: statZoom 0.7s cubic-bezier(.2,.8,.2,1) both;
}
@keyframes statZoom {
  from { opacity: 0; transform: translateX(-16px); }
  to   { opacity: 1; transform: none; }
}

/* MAP — aparece con ligero fade */
.map-wrap.in {
  animation: mapIn 0.8s cubic-bezier(.2,.8,.2,1) both;
}
@keyframes mapIn {
  from { opacity: 0; transform: scale(0.97) translateY(12px); }
  to   { opacity: 1; transform: none; }
}

/* CONTACT INFO rows — stagger */
.ci-row { opacity: 0; transform: translateX(20px); }
.ci-row.in {
  opacity: 1; transform: none;
  transition: opacity 0.55s cubic-bezier(.2,.8,.2,1), transform 0.55s cubic-bezier(.2,.8,.2,1);
}

/* LOGOS — aparecen en arco */
.logo-ph { opacity: 0; transform: translateY(14px) scale(0.9); }
.logo-ph.in {
  opacity: 1; transform: none;
  transition: opacity 0.5s cubic-bezier(.2,.8,.2,1), transform 0.5s cubic-bezier(.2,.8,.2,1);
}

/* FORM CARD — desliza arriba */
.form-card.in {
  animation: formIn 0.7s cubic-bezier(.2,.8,.2,1) both;
}
@keyframes formIn {
  from { opacity: 0; transform: translateY(24px); }
  to   { opacity: 1; transform: none; }
}

/* FOOTER — fade desde abajo */
.footer { opacity: 0; animation: footerIn 0.8s 0.1s cubic-bezier(.2,.8,.2,1) forwards; }
@keyframes footerIn {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: none; }
}

/* SPECTRUM BAR en footer */
.spectrum-bar {
  transform-origin: left;
  animation: barGrow 1s 0.3s cubic-bezier(.2,.8,.2,1) both;
}
@keyframes barGrow {
  from { opacity: 0; transform: scaleX(0); }
  to   { opacity: 0.7; transform: scaleX(1); }
}

/* EYEBROW ACCENT LINE */
.section-head.in .eyebrow::before,
.section-head.center.in::before {
  animation: linePop 0.5s cubic-bezier(.2,.8,.2,1) both;
}
@keyframes linePop {
  from { transform: scaleX(0); opacity: 0; }
  to   { transform: scaleX(1); opacity: 1; }
}

/* ═══════════════════════════════════════════════
   APARICIÓN — elementos adicionales
   (logo-ph, stats, ci-row, footer-col, form fields)
═══════════════════════════════════════════════ */

/* Individual logo placeholders */
.logo-ph {
  opacity: 0;
  transform: translateY(14px) scale(0.92);
}
.logo-ph.in {
  opacity: 1;
  transform: none;
  transition: opacity 0.55s cubic-bezier(.2,.8,.2,1),
              transform 0.55s cubic-bezier(.2,.8,.2,1);
}

/* Stats individuales */
.stats .s {
  opacity: 0;
  transform: translateX(-18px);
}
.stats .s.in {
  opacity: 1;
  transform: none;
  transition: opacity 0.6s cubic-bezier(.2,.8,.2,1),
              transform 0.6s cubic-bezier(.2,.8,.2,1);
}

/* Filas de contacto */
.ci-row {
  opacity: 0;
  transform: translateX(22px);
}
.ci-row.in {
  opacity: 1;
  transform: none;
  transition: opacity 0.55s cubic-bezier(.2,.8,.2,1),
              transform 0.55s cubic-bezier(.2,.8,.2,1);
}

/* Columnas del footer */
.footer-col {
  opacity: 0;
  transform: translateY(18px);
}
.footer-col.in {
  opacity: 1;
  transform: none;
  transition: opacity 0.6s cubic-bezier(.2,.8,.2,1),
              transform 0.6s cubic-bezier(.2,.8,.2,1);
}

/* Valores about-values */
.about-values .v {
  opacity: 0;
  transform: translateX(-20px);
}
.about-values .v.in {
  opacity: 1;
  transform: none;
  transition: opacity 0.55s cubic-bezier(.2,.8,.2,1),
              transform 0.55s cubic-bezier(.2,.8,.2,1);
}

/* Campos del formulario */
#leadForm .field {
  opacity: 0;
  transform: translateY(14px);
}
#leadForm .field.in {
  opacity: 1;
  transform: none;
  transition: opacity 0.5s cubic-bezier(.2,.8,.2,1),
              transform 0.5s cubic-bezier(.2,.8,.2,1);
}

/* Footer bottom bar */
.footer-bottom {
  opacity: 0;
  transform: translateY(10px);
}
.footer-bottom.in {
  opacity: 1;
  transform: none;
  transition: opacity 0.55s cubic-bezier(.2,.8,.2,1) 0.3s,
              transform 0.55s cubic-bezier(.2,.8,.2,1) 0.3s;
}

/* Hero actions (botones CTA) */
.hero-actions {
  opacity: 0;
  animation: heroActIn 0.7s 0.65s cubic-bezier(.2,.8,.2,1) forwards;
}
@keyframes heroActIn {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}

/* Eyebrow hero */
.hero-copy .eyebrow {
  opacity: 0;
  animation: eyebrowIn 0.6s 0.25s cubic-bezier(.2,.8,.2,1) forwards;
}
@keyframes eyebrowIn {
  from { opacity: 0; transform: translateX(-14px); }
  to   { opacity: 1; transform: none; }
}

/* H1 hero */
.hero-copy h1 {
  opacity: 0;
  animation: h1In 0.75s 0.35s cubic-bezier(.2,.8,.2,1) forwards;
}
@keyframes h1In {
  from { opacity: 0; transform: translateY(20px); }
  to   { opacity: 1; transform: none; }
}

/* Lead párrafo */
.hero-copy .lead {
  opacity: 0;
  animation: leadIn 0.7s 0.48s cubic-bezier(.2,.8,.2,1) forwards;
}
@keyframes leadIn {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

/* Hero trust divider */
.hero-trust {
  opacity: 0;
  animation: trustIn 0.65s 0.9s cubic-bezier(.2,.8,.2,1) forwards;
}
@keyframes trustIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Form foot (consent + submit) */
.form-foot {
  opacity: 0;
  transform: translateY(12px);
}
.form-foot.in {
  opacity: 1;
  transform: none;
  transition: opacity 0.5s cubic-bezier(.2,.8,.2,1) 0.2s,
              transform 0.5s cubic-bezier(.2,.8,.2,1) 0.2s;
}

/* ═══════════════════════════════════════════════════════════════
   LUXURY SCROLL EXPERIENCE — items 5 & 6
   Animaciones más dinámicas, efecto parallax, scroll journey
═══════════════════════════════════════════════════════════════ */

/* ── SCROLL PROGRESS BAR ──────────────────────────────────── */
#scroll-progress {
  position: fixed; top: 0; left: 0; right: 0; z-index: 200;
  height: 2px;
  background: transparent;
  pointer-events: none;
}
#scroll-progress-fill {
  height: 100%;
  width: 0%;
  background: linear-gradient(90deg, var(--accent) 0%, #7FA8D4 60%, var(--accent) 100%);
  background-size: 200% 100%;
  animation: shimmerBar 2.4s linear infinite;
  transition: width 0.1s linear;
  border-radius: 0 2px 2px 0;
}
@keyframes shimmerBar {
  0%   { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}

/* ── HERO PARALLAX LAYERS ─────────────────────────────────── */
.hero { will-change: transform; }
.hero-top   { will-change: transform, opacity; }
.building-stage { will-change: transform; }
.atmos      { will-change: transform; }

/* ── HERO H1 — texto más dramático ───────────────────────── */
.hero-copy h1 {
  opacity: 0;
  animation: h1LuxIn 0.95s 0.32s cubic-bezier(.06,.9,.1,1) forwards;
}
@keyframes h1LuxIn {
  0%   { opacity: 0; transform: translateY(40px) scale(0.97); filter: blur(4px); }
  60%  { filter: blur(0); }
  100% { opacity: 1; transform: none; filter: blur(0); }
}

/* ── EYEBROW — desliza con acento ─────────────────────────── */
.hero-copy .eyebrow {
  opacity: 0;
  animation: eyebrowLux 0.7s 0.15s cubic-bezier(.06,.9,.1,1) forwards;
}
@keyframes eyebrowLux {
  0%   { opacity: 0; transform: translateX(-20px); letter-spacing: 0.08em; }
  100% { opacity: 1; transform: none; }
}

/* ── LEAD — desenfoque suave ──────────────────────────────── */
.hero-copy .lead {
  opacity: 0;
  animation: leadLux 0.8s 0.5s cubic-bezier(.2,.8,.2,1) forwards;
}
@keyframes leadLux {
  0%   { opacity: 0; transform: translateY(22px); filter: blur(3px); }
  70%  { filter: blur(0); }
  100% { opacity: 1; transform: none; filter: blur(0); }
}

/* ── HERO ACTIONS ─────────────────────────────────────────── */
.hero-actions {
  opacity: 0;
  animation: actionsLux 0.7s 0.68s cubic-bezier(.2,.8,.2,1) forwards;
}
@keyframes actionsLux {
  0%   { opacity: 0; transform: translateY(18px); }
  100% { opacity: 1; transform: none; }
}

/* ── HERO DEMO CARD — entra desde derecha con rebote ─────── */
.hero-demo {
  opacity: 0;
  animation: demoLux 1s 0.55s cubic-bezier(.06,.9,.1,1) forwards;
}
@keyframes demoLux {
  0%   { opacity: 0; transform: translateX(60px) scale(0.95); }
  70%  { transform: translateX(-5px) scale(1.01); }
  100% { opacity: 1; transform: none; }
}

/* ── TRUST STATS — pop escalonado ─────────────────────────── */
.hero-trust {
  opacity: 0;
  animation: trustLux 0.6s 0.95s cubic-bezier(.2,.8,.2,1) forwards;
}
@keyframes trustLux {
  from { opacity: 0; transform: translateY(10px); }
  to   { opacity: 1; transform: none; }
}
.hero-trust .stat b {
  display: inline-block;
  animation: statPopLux 0.7s cubic-bezier(.06,.9,.1,1) both;
}
.hero-trust .stat:nth-child(1) b { animation-delay: 1.1s; }
.hero-trust .stat:nth-child(3) b { animation-delay: 1.25s; }
.hero-trust .stat:nth-child(5) b { animation-delay: 1.4s; }
@keyframes statPopLux {
  0%   { opacity:0; transform: scale(0.5) translateY(12px); }
  65%  { transform: scale(1.12); }
  100% { opacity:1; transform: none; }
}

/* ── REVEAL — easing más dramático ─────────────────────────── */
.reveal, .reveal-left, .reveal-right, .reveal-scale, .reveal-fade {
  transition:
    opacity 0.85s cubic-bezier(.06,.9,.1,1),
    transform 0.85s cubic-bezier(.06,.9,.1,1),
    filter 0.85s cubic-bezier(.06,.9,.1,1);
}
.reveal        { opacity:0; transform:translateY(36px); filter:blur(2px); }
.reveal-left   { opacity:0; transform:translateX(-40px); filter:blur(2px); }
.reveal-right  { opacity:0; transform:translateX(40px);  filter:blur(2px); }
.reveal-scale  { opacity:0; transform:scale(0.9);        filter:blur(2px); }
.reveal-fade   { opacity:0; filter:blur(3px); }

.reveal.in, .reveal-left.in, .reveal-right.in,
.reveal-scale.in, .reveal-fade.in {
  opacity:1; transform:none; filter:blur(0);
}

/* ── SERVICE CARDS — más dramáticas ─────────────────────────── */
.svc-card.in {
  animation: svcLux 0.75s cubic-bezier(.06,.9,.1,1) both;
}
@keyframes svcLux {
  from { opacity:0; transform: translateY(40px) scale(0.95) rotate(-0.5deg); }
  to   { opacity:1; transform: none; }
}

/* ── TESTIMONIAL CARDS ────────────────────────────────────── */
.tst-card.in {
  animation: tstLux 0.75s cubic-bezier(.06,.9,.1,1) both;
}
@keyframes tstLux {
  from { opacity:0; transform: translateY(30px) scale(0.96); filter:blur(2px); }
  to   { opacity:1; transform: none; filter:blur(0); }
}

/* ── STATS — slide desde izquierda con blur ────────────────── */
.stats .s.in {
  animation: statSlide 0.72s cubic-bezier(.06,.9,.1,1) both;
}
@keyframes statSlide {
  from { opacity:0; transform: translateX(-28px); filter:blur(3px); }
  to   { opacity:1; transform: none; filter:blur(0); }
}

/* ── SECTION HEADS — línea de acento animada ────────────────── */
.section-head.in .eyebrow::before,
.section-head.center.in::before {
  animation: lineLux 0.7s cubic-bezier(.06,.9,.1,1) both;
}
@keyframes lineLux {
  from { transform: scaleX(0); opacity: 0; }
  to   { transform: scaleX(1); opacity: 1; }
}
.section-head.in h2 {
  animation: headLux 0.8s 0.1s cubic-bezier(.06,.9,.1,1) both;
}
@keyframes headLux {
  from { opacity:0; transform: translateY(24px); filter:blur(2px); }
  to   { opacity:1; transform: none; filter:blur(0); }
}

/* ── SCROLL INDICATOR HERO ─────────────────────────────────── */
.scroll-indicator {
  position: absolute; bottom: 32px; left: 50%; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  opacity: 0; animation: scrollIndIn 0.6s 1.6s cubic-bezier(.2,.8,.2,1) forwards;
  pointer-events: none;
}
@keyframes scrollIndIn {
  from { opacity:0; transform: translateX(-50%) translateY(12px); }
  to   { opacity:1; transform: translateX(-50%) translateY(0); }
}
.scroll-indicator span {
  font-family: var(--font-d); font-size: 10px; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--muted);
}
.scroll-indicator .scroll-line {
  width: 1px; height: 36px; background: var(--line); position: relative; overflow: hidden;
}
.scroll-indicator .scroll-line::after {
  content: ""; position: absolute; top: -100%; left: 0; right: 0; height: 100%;
  background: var(--accent);
  animation: scrollDrop 1.8s ease-in-out infinite;
}
@keyframes scrollDrop {
  0%   { top: -100%; }
  100% { top: 200%; }
}

/* ── LOGO FLOAT — más suave ────────────────────────────────── */
@keyframes logoFloat {
  0%, 100% { transform: translateY(0); }
  50%       { transform: translateY(-4px); }
}

/* ── BTN SHIMMER terracota ─────────────────────────────────────── */
.btn-accent::after, .btn-primary::after {
  background: linear-gradient(105deg,
    transparent 30%,
    rgba(27,59,95,0.14) 50%,
    transparent 70%);
  transform: translateX(-100%);
  transition: transform 0.55s ease;
}
.btn-accent:hover::after, .btn-primary:hover::after {
  transform: translateX(110%);
}

/* ── MAP ─────────────────────────────────────────────────── */
.map-wrap.in {
  animation: mapLux 0.9s cubic-bezier(.06,.9,.1,1) both;
}
@keyframes mapLux {
  from { opacity:0; transform: translateY(20px) scale(0.97); filter:blur(3px); }
  to   { opacity:1; transform: none; filter:blur(0); }
}

/* ── CONTACT ROWS ───────────────────────────────────────────── */
.ci-row {
  opacity:0; transform: translateX(28px); filter:blur(2px);
}
.ci-row.in {
  opacity:1; transform:none; filter:blur(0);
  transition: opacity 0.6s cubic-bezier(.06,.9,.1,1),
              transform 0.6s cubic-bezier(.06,.9,.1,1),
              filter 0.6s cubic-bezier(.06,.9,.1,1);
}

/* ── FORM CARD ─────────────────────────────────────────────── */
.form-card.in {
  animation: formLux 0.8s cubic-bezier(.06,.9,.1,1) both;
}
@keyframes formLux {
  from { opacity:0; transform: translateY(30px) scale(0.98); filter:blur(3px); }
  to   { opacity:1; transform: none; filter:blur(0); }
}

/* ── LOGO PLACEHOLDERS ─────────────────────────────────────── */
.logo-ph {
  opacity:0; transform: translateY(16px) scale(0.9); filter:blur(2px);
}
.logo-ph.in {
  opacity:1; transform:none; filter:blur(0);
  transition: opacity 0.6s cubic-bezier(.06,.9,.1,1),
              transform 0.6s cubic-bezier(.06,.9,.1,1),
              filter 0.6s cubic-bezier(.06,.9,.1,1);
}

/* ── FOOTER ────────────────────────────────────────────────── */
.footer { opacity:0; animation: footerLux 0.9s 0.1s cubic-bezier(.06,.9,.1,1) forwards; }
@keyframes footerLux {
  from { opacity:0; transform: translateY(24px); filter:blur(3px); }
  to   { opacity:1; transform: none; filter:blur(0); }
}

/* ============================================================
   MAREA RESIDENCES BRANDING & "POWERED BY SPECTRA"
   ============================================================ */

/* ── Logo MAREA RESIDENCES (nav izquierdo) ─────────────────────────── */
.altiva-brand { gap: 10px; }
.altiva-logo-wrap {
  width: 36px; height: 36px;
  display: grid; place-items: center;
  background: var(--ink);
  border-radius: 8px;
  overflow: hidden;
}
.altiva-logo-ph {
  font-family: var(--font-d);
  font-size: 18px; font-weight: 800;
  letter-spacing: -.04em;
  color: var(--accent);
  line-height: 1;
}
/* "MAREA RESIDENCES" son 16 caracteres frente a los 6 de "ALTIVA": con los
   18px y el interletrado de 0.22em de antes el bloque de marca pasaba de
   280px y empujaba los enlaces y los botones de la barra. Se baja el cuerpo
   y se aprieta el interletrado para que quepa sin cambiar la maqueta. */
.altiva-name {
  font-family: var(--font-s);
  font-size: 14px; font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--ink);
  text-transform: uppercase;
  white-space: nowrap;
}
.logo.on-dark .altiva-name { color: rgba(255,255,255,0.92); }
.logo.on-dark .altiva-logo-wrap { background: rgba(255,255,255,0.12); }

/* ── "Powered by Spectra" badge — nav ───────────────────── */
.nav-powered-by {
  display: flex; align-items: center; gap: 5px;
  flex-shrink: 0;
  padding: 5px 11px 5px 8px;
  border-radius: 999px;
  border: 1px solid var(--line);
  background: oklch(1 0 0 / 0.5);
  backdrop-filter: blur(6px);
  transition: border-color 0.2s, background 0.2s;
}
.nav-powered-by:hover { border-color: var(--accent); }
.pb-label {
  font-family: var(--font-d);
  font-size: 9px; font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--muted);
}
.pb-iso {
  width: 18px; height: 18px;
  object-fit: contain;
  border-radius: 4px;
  flex-shrink: 0;
}
.pb-name {
  font-family: var(--font-s);
  font-size: 14px; font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--ink);
}

/* ── "Powered by" footer wrap ───────────────────────────── */
.footer-pb-wrap {
  display: flex; align-items: center; gap: 5px;
  margin-top: 10px; margin-bottom: 4px;
}
.footer-pb-wrap .pb-label { color: rgba(255,255,255,0.3); }
.footer-pb-wrap .pb-name  { color: rgba(255,255,255,0.55); }

/* ============================================================
   ANIMACIONES LUXURY MEJORADAS
   ============================================================ */

/* ── Reveal con split horizontal ───────────────────────── */
.reveal {
  opacity: 0;
  transform: translateY(28px);
  filter: blur(4px);
  transition: opacity 0.7s cubic-bezier(.06,.9,.1,1),
              transform 0.7s cubic-bezier(.06,.9,.1,1),
              filter 0.7s cubic-bezier(.06,.9,.1,1);
}
.reveal.in {
  opacity: 1;
  transform: none;
  filter: blur(0);
}

/* ── Delays escalonados ───────────────────────────────── */
.reveal.d1 { transition-delay: 0.12s; }
.reveal.d2 { transition-delay: 0.22s; }
.reveal.d3 { transition-delay: 0.34s; }

/* ── Hero copy: letras con entrada dramática ────────────── */
.hero-copy .eyebrow.in {
  animation: eyebrowSlide 0.8s 0.05s cubic-bezier(.06,.9,.1,1) both;
}
@keyframes eyebrowSlide {
  from { opacity:0; transform:translateX(-20px); letter-spacing:0.3em; }
  to   { opacity:1; transform:none; letter-spacing:0.14em; }
}
.hero-copy h1.in {
  animation: h1Lux 0.9s 0.15s cubic-bezier(.06,.9,.1,1) both;
}
@keyframes h1Lux {
  from { opacity:0; transform:translateY(34px) scale(0.97); filter:blur(6px); }
  to   { opacity:1; transform:none; filter:blur(0); }
}
.hero-copy .lead.in {
  animation: leadFade 0.9s 0.28s cubic-bezier(.2,.8,.2,1) both;
}
@keyframes leadFade {
  from { opacity:0; transform:translateY(18px); }
  to   { opacity:1; transform:none; }
}

/* ── Sección heads: h2 con línea que se extiende ────────── */
.section-head h2 {
  position: relative;
  overflow: hidden;
}
.section-head.reveal.in h2 {
  animation: h2Sweep 0.85s 0.1s cubic-bezier(.06,.9,.1,1) both;
}
@keyframes h2Sweep {
  from { opacity:0; transform:translateY(22px); clip-path:inset(0 0 100% 0); }
  to   { opacity:1; transform:none; clip-path:inset(0 0 0% 0); }
}

/* ── Service cards: escalonado con rotación sutil ────────── */
.svc-card {
  opacity: 0;
  transform: translateY(20px) rotate(-0.5deg);
  filter: blur(3px);
  transition: opacity 0.65s cubic-bezier(.06,.9,.1,1),
              transform 0.65s cubic-bezier(.06,.9,.1,1),
              filter 0.5s cubic-bezier(.06,.9,.1,1),
              box-shadow 0.25s, background 0.25s, border-color 0.25s;
}
.svc-card.in {
  opacity: 1;
  transform: none;
  filter: blur(0);
}

/* ── Stats: pop con escala ───────────────────────────────── */
.stats .s {
  opacity: 0;
  transform: translateY(14px) scale(0.95);
  transition: opacity 0.55s cubic-bezier(.06,.9,.1,1),
              transform 0.55s cubic-bezier(.06,.9,.1,1);
}
.stats .s.in {
  opacity: 1;
  transform: none;
}

/* ── Testimonios: entrada con deslizamiento lateral ─────── */
.tst-card {
  opacity: 0;
  transform: translateX(24px);
  filter: blur(2px);
  transition: opacity 0.6s cubic-bezier(.06,.9,.1,1),
              transform 0.6s cubic-bezier(.06,.9,.1,1),
              filter 0.5s;
}
.tst-card.in {
  opacity: 1;
  transform: none;
  filter: blur(0);
}
.tst-card:nth-child(odd).in  { transform: none; }
.tst-card:nth-child(even)    { transform: translateX(-24px); }
.tst-card:nth-child(even).in { transform: none; }

/* ── About values: slide desde izquierda ────────────────── */
.about-values .v {
  opacity: 0;
  transform: translateX(-18px);
  transition: opacity 0.55s cubic-bezier(.06,.9,.1,1),
              transform 0.55s cubic-bezier(.06,.9,.1,1);
}
.about-values .v.in { opacity:1; transform:none; }

/* ============================================================
   SCROLL STORYTELLING — EXPERIENCIA LUXURY DE SCROLL
   ============================================================ */

/* ── Línea vertical de progreso de sección ───────────────── */
.scroll-story-line {
  position: fixed;
  top: 50%; left: 24px;
  transform: translateY(-50%);
  display: flex; flex-direction: column; gap: 8px;
  z-index: 90;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.4s;
}
.scroll-story-line.visible { opacity: 1; }
.ssl-dot {
  width: 6px; height: 6px; border-radius: 50%;
  background: var(--line);
  transition: background 0.3s, transform 0.3s;
  pointer-events: auto;
  cursor: pointer;
}
.ssl-dot.active {
  background: var(--accent);
  transform: scale(1.5);
}

/* ── Secciones con separador luxury ─────────────────────── */
.section-divider {
  width: 1px;
  height: 60px;
  background: linear-gradient(180deg, transparent, var(--accent), transparent);
  margin: 0 auto;
  opacity: 0.5;
}

/* ── Hero scroll indicator actualizado ──────────────────── */
.scroll-indicator {
  position: absolute;
  bottom: 32px; left: 50%;
  transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  color: var(--muted);
  font-family: var(--font-d); font-size: 10px; font-weight: 600;
  letter-spacing: 0.12em; text-transform: uppercase;
  animation: indicatorBob 2.6s ease-in-out infinite;
  pointer-events: none;
}
@keyframes indicatorBob {
  0%,100% { transform: translateX(-50%) translateY(0); opacity:0.6; }
  50%      { transform: translateX(-50%) translateY(6px); opacity:1; }
}
.scroll-line {
  width: 1px; height: 36px;
  background: linear-gradient(180deg, var(--accent), transparent);
  animation: scrollPulse 2.6s ease-in-out infinite;
}
@keyframes scrollPulse {
  0%,100% { transform: scaleY(1); opacity:0.6; }
  50%      { transform: scaleY(1.3); opacity:1; }
}

/* ── Parallax en sections al scroll ─────────────────────── */
.parallax-section {
  will-change: transform;
  transition: transform 0.1s linear;
}

/* ── "Reveal from below" para section-heads más dramático ── */
.section-head.reveal {
  position: relative;
}
.section-head.reveal::before {
  content: "";
  position: absolute;
  left: 0; bottom: -12px;
  width: 0; height: 2px;
  background: linear-gradient(90deg, var(--accent), transparent);
  transition: width 0.9s 0.3s cubic-bezier(.06,.9,.1,1);
  border-radius: 2px;
}
.section-head.reveal.in::before { width: 80px; }
.section-head.center.reveal::before { left: 50%; transform: translateX(-50%); }

/* ── Efecto "frosted glass" en cards al hacer scroll ──────── */
@supports (backdrop-filter: blur(1px)) {
  .svc-card {
    backdrop-filter: blur(0px);
    transition: opacity 0.65s cubic-bezier(.06,.9,.1,1),
                transform 0.65s cubic-bezier(.06,.9,.1,1),
                filter 0.5s, backdrop-filter 0.3s,
                box-shadow 0.25s, background 0.25s, border-color 0.25s;
  }
}

/* ── Número de servicio: pop dorado al revelar ──────────── */
.svc-card.in .num {
  animation: numPop 0.5s 0.25s cubic-bezier(.06,.9,.1,1) both;
}
@keyframes numPop {
  from { opacity:0; transform:scale(0.5); color: var(--muted); }
  50%  { transform:scale(1.2); }
  to   { opacity:1; transform:scale(1); color: var(--accent); }
}

/* ── Testimonio stars: aparecen letra a letra ──────────────── */
.tst-card.in .stars {
  animation: starsFly 0.6s 0.2s cubic-bezier(.06,.9,.1,1) both;
}
@keyframes starsFly {
  from { opacity:0; transform: translateX(-10px); letter-spacing:-.2em; }
  to   { opacity:1; transform:none; letter-spacing:normal; }
}

/* ── Hero trust bar: entrada suave ──────────────────────── */
.hero-trust .stat {
  opacity: 0;
  transform: translateY(10px);
  transition: opacity 0.5s cubic-bezier(.06,.9,.1,1),
              transform 0.5s cubic-bezier(.06,.9,.1,1);
}
.hero-trust .stat.in { opacity:1; transform:none; }
.hero-trust .stat:nth-child(1) { transition-delay: 0.1s; }
.hero-trust .stat:nth-child(3) { transition-delay: 0.2s; }
.hero-trust .stat:nth-child(5) { transition-delay: 0.3s; }

/* ── Scroll progress dorado ─────────────────────────────── */
#scroll-progress {
  position: fixed; top: 0; left: 0; right: 0; z-index: 200;
  height: 2px;
  background: transparent;
}
#scroll-progress-fill {
  height: 100%; width: 0;
  background: linear-gradient(90deg, var(--accent), #7FA8D4, var(--accent));
  background-size: 200% 100%;
  animation: shimmerBar 2s linear infinite;
  border-radius: 0 2px 2px 0;
  transition: width 0.15s linear;
}
@keyframes shimmerBar {
  0%   { background-position: 100% 50%; }
  100% { background-position: -100% 50%; }
}

/* ── Efecto "entering" en las secciones visibles ─────────── */
.services.in-view .svc-card:nth-child(1)  { transition-delay: 0s; }
.services.in-view .svc-card:nth-child(2)  { transition-delay: 0.08s; }
.services.in-view .svc-card:nth-child(3)  { transition-delay: 0.16s; }
.services.in-view .svc-card:nth-child(4)  { transition-delay: 0.24s; }
.services.in-view .svc-card:nth-child(5)  { transition-delay: 0.32s; }
.services.in-view .svc-card:nth-child(6)  { transition-delay: 0.40s; }

/* ── Mobile: nav-powered-by adaptado ────────────────────── */
@media (max-width: 768px) {
  .nav-powered-by { display: none; }
  .altiva-name { font-size: 15px; }
}


/* ── ABOUT VALUES ─────────────────


/* ═══════════════════════════════════════════════════════════════
   FONDO DE NUBES VISIBLE — SUPERFICIES EN VIDRIO
   ═══════════════════════════════════════════════════════════════
   El canvas de nubes va detrás de toda la página (.idx-sky / #idxCloudGL),
   pero no se veía: las secciones y tarjetas llevaban fondos crema OPACOS
   (var(--bg), var(--bg-alt), var(--card-bg), #fff) que lo tapaban por
   completo. Daba igual lo bien que dibujara el shader.

   Aquí esas superficies pasan a vidrio: mismo tono, pero translúcido y con
   desenfoque detrás. Las nubes se ven a través y el texto sigue legible — es
   el mismo acabado que ya usan los paneles del masterplan.

   IMPORTANTE: esto NO toca el masterplan. Sus nubes y sus paneles se quedan
   exactamente como están; aquí solo se replica el fondo en el index.

   Cada regla deja primero un color sólido de respaldo: los navegadores sin
   backdrop-filter se quedan con él y ven lo mismo de antes.
   Aplica a escritorio y a móvil por igual. */

/* ── Secciones de página completa ──────────────────────────── */
.availability {
  background: var(--bg-alt);
  background: rgba(248, 246, 243, 0.55);
  backdrop-filter: blur(18px) saturate(1.15);
  -webkit-backdrop-filter: blur(18px) saturate(1.15);
}
/* La sección de bienvenida y la de servicios ya eran transparentes: por ahí
   las nubes se ven directas, sin nada que atravesar. */

/* ── Tarjetas y paneles ────────────────────────────────────── */
.svc-card, .tst-card, .plan-card, .amenity-card, .modal-card, .plans-viewer {
  background: var(--card-bg);
  background: rgba(255, 255, 255, 0.55);
  backdrop-filter: blur(15px) saturate(1.12);
  -webkit-backdrop-filter: blur(15px) saturate(1.12);
  border: 1px solid rgba(255, 255, 255, 0.5);
}
.plans-viewer {
  background: rgb(246, 244, 240);
  background: rgba(246, 244, 240, 0.5);
}

/* ── Piezas pequeñas: iconos, chips, campos ────────────────────
   Estas SÍ son translúcidas, pero deliberadamente SIN backdrop-filter, y la
   razón es de fluidez, no de estética:

   En esta página hay ~10 .ico, 6 .logo-ph, los sociales y 9 campos de
   formulario. Si cada uno lleva su propio desenfoque son ~29 capas de
   compositor extra, y cada una obliga al navegador a capturar su trozo de
   fondo y volver a desenfocarlo cada vez que ese fondo cambia — o sea, en
   cada scroll. Ese era un coste real por cero ganancia visual: son piezas
   pequeñas apoyadas sobre un fondo de nubes que YA es suave, así que el
   desenfoque no se distingue. La translucidez sola da el mismo acabado y
   es prácticamente gratis (no crea capa ni pase de blur).

   El desenfoque se reserva para las superficies grandes de abajo, donde sí
   se nota: la sección de disponibilidad y las tarjetas. */
.ico, .ci-row .ico, .logo-ph, .ci-socials a {
  background: var(--bg);
  background: rgba(246, 244, 240, 0.72);
  border: 1px solid rgba(255, 255, 255, 0.45);
}
/* Los campos del formulario: translúcidos pero algo más sólidos, que ahí se
   escribe y el texto tiene que leerse sin esfuerzo. */
.field input, .field textarea, .field select {
  background: var(--bg);
  background: rgba(255, 255, 255, 0.78);
}

/* El hero y el pie se quedan opacos a propósito: el hero tiene su propia
   imagen a sangre y el pie es la banda oscura de cierre. Translúcidos
   romperían su lectura. */

/* Sin soporte de desenfoque se recupera el sólido: nunca debe quedar texto
   flotando sobre nubes en movimiento. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .availability { background: var(--bg-alt); }
  .svc-card, .tst-card, .plan-card, .amenity-card, .modal-card { background: var(--card-bg); }
  .plans-viewer { background: rgb(246, 244, 240); }
  .ico, .ci-row .ico, .logo-ph, .ci-socials a { background: var(--bg); }
  .field input, .field textarea, .field select { background: var(--bg); }
}

/* ═══════════════════════════════════════════════════════════════
   INDEX REDUCIDO — UBICACIÓN/MAPA, CONTACTO Y MARCA
   ═══════════════════════════════════════════════════════════════
   El index se quedó solo con el recorrido de imágenes y transiciones. Lo
   único que sobrevive del antiguo cuerpo es el mapa, que ahora vive dentro
   de una capa a demanda, y el formulario, que vive en una ventana emergente.

   Criterio de rendimiento, igual que en el resto del sitio: NADA de esto se
   monta ni descarga hasta que se abre. Mientras están cerradas, estas capas
   llevan [hidden] / display:none, así que no hay capas de compositor, ni
   desenfoques recalculándose, ni iframes bajando datos por detrás. El
   recorrido del edificio conserva toda la GPU para sí. */

/* ── Capa Ubicación / Mapa ─────────────────────────────────────────
   Sin pestañas: cada botón del flyout de ubicación (ver .stage-loc-flyout)
   entra directo a una de las dos vistas, siempre a pantalla completa. La
   barra superior no ocupa su propia fila -- flota sobre el contenido y solo
   lleva el botón de cerrar. */
.stage-loc-fullscreen{position:fixed;inset:0;z-index:60;
  background:rgba(20,20,19,.55);
  backdrop-filter:blur(22px) saturate(1.2);-webkit-backdrop-filter:blur(22px) saturate(1.2)}
.stage-loc-fullscreen[hidden]{display:none}


.loc-topbar{position:absolute;top:0;left:0;right:0;z-index:2;
  display:flex;justify-content:flex-end;padding:16px;
  background:linear-gradient(rgba(0,0,0,.35),transparent)}
.loc-close{width:36px;height:36px;border-radius:50%;display:grid;place-items:center;cursor:pointer;
  border:1px solid rgba(27,59,95,.18);color:var(--accent);background:rgba(255,255,255,.9);
  backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}
.loc-close:hover{background:rgba(255,255,255,0.96)}

.loc-panel{position:absolute;inset:0;display:flex}
.loc-panel[hidden]{display:none}
/* El mapa hereda su propio estilo (.map-wrap/.imap); aquí solo se le da el
   hueco, sin el borde redondeado que .imap trae por defecto -- edge to edge,
   igual que el recorrido 360. */
.loc-panel .map-wrap{flex:1;min-height:0;margin:0;width:100%}
.loc-panel .imap{width:100%;height:100%;min-height:0;border-radius:0}

.loc-tour{position:relative;flex:1;min-height:0;background:rgba(0,0,0,.35)}
.loc-tour iframe{position:absolute;inset:0;width:100%;height:100%;border:0;display:block}

/* Tapa el "Loading virtual tour..." propio del reproductor 3DVista (texto
   plano dentro del iframe, no se puede restylear) con un loader de marca --
   mismo patrón que #ugTourLoading en demo/masterplan.html, para que las
   distintas vistas 360 del sitio se vean consistentes. Se quita en cuanto
   showLocTourLoading() detecta, leyendo el DOM del iframe, que ya salió de
   ese estado (ver site.js). */
.tour-loading{position:absolute;inset:0;z-index:2;
  background:linear-gradient(180deg,#F1EFEA 0%,#F4F2ED 30%,#F6F4EF 60%,#F5F3EE 100%);
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:16px;
  transition:opacity .5s ease}
.tour-loading.hide{opacity:0;pointer-events:none}
.tour-loading-logo img{height:46px;object-fit:contain;animation:tour-loader-breathe 1.8s ease-in-out infinite}
@keyframes tour-loader-breathe{0%,100%{opacity:.62;transform:scale(.97)}50%{opacity:1;transform:scale(1)}}
.tour-loading-txt{font-size:11.5px;color:rgba(17,17,16,.45);letter-spacing:.08em}
@media (prefers-reduced-motion:reduce){.tour-loading-logo img{animation:none}}

/* ── Ventana emergente de contacto ─────────────────────────────── */
.contact-modal-card{max-width:760px;width:min(94vw,760px);max-height:88vh;overflow:auto;
  padding:30px 30px 26px;position:relative;
  /* El resto de .modal-card usa vidrio esmerilado (55% blanco, ver la regla
     compartida con .svc-card/.plan-card/etc.), pero aquí es un formulario
     que hay que poder leer sobre una foto de fondo con mucho contraste --
     mejor un blanco casi sólido que gris translúcido con letras que se
     pierden. */
  background: rgba(255, 255, 255, 0.97);
  backdrop-filter: blur(15px) saturate(1.12);
  -webkit-backdrop-filter: blur(15px) saturate(1.12);
}
.contact-modal-card .cm-head{text-align:center;margin-bottom:22px}
/* Viñeta "Aparta tu departamento": isotipo del proyecto en vez de la rayita
   que trae .eyebrow::before por defecto.
   El isotipo viejo de MAREA RESIDENCES traía su fondo crema horneado en la imagen y
   hacía falta mix-blend-mode:multiply para fundirlo con el blanco de la
   tarjeta. marea-badge-iso.webp ya viene con transparencia real, así que el
   multiply sobra: se quitó porque además oscurecía el escudo al mezclarlo
   con el fondo. */
.eyebrow--iso::before{display:none}
.eyebrow-iso-img{width:22px;height:22px;object-fit:contain;flex-shrink:0}
.contact-modal-card .cm-head h2{margin:6px 0 8px}
.contact-modal-card .cm-head p{margin:0;font-size:14px;line-height:1.55;color:var(--ink-soft)}
.contact-modal-card .form-card{background:transparent;border:0;backdrop-filter:none;
  -webkit-backdrop-filter:none;padding:0;box-shadow:none}
.modal-x{position:absolute;right:14px;top:14px;width:34px;height:34px;border-radius:50%;
  display:grid;place-items:center;cursor:pointer;border:1px solid rgba(27,59,95,.16);
  color:var(--accent);background:rgba(255,255,255,.9)}
.modal-x:hover{background:rgba(255,255,255,0.96)}

/* ── Marca en la esquina inferior izquierda ────────────────────────
   Wordmark completo sobre una chapa SÓLIDA -- el .webp del wordmark es
   transparente (a diferencia del isotipo, que ya traía su propio fondo
   crema horneado en la imagen), así que aquí se le pone ese mismo crema
   como fondo de la píldora en vez del vidrio translúcido de antes: sobre
   foto/video el vidrio dejaba pasar demasiado color de fondo y el logo
   se perdía. #F6EFE6 = color exacto muestreado del fondo de
   marea-badge-iso.webp. */
/* Oculto en la pantalla de "Entrar": el gate de nubes vive en z-index:8,
   muy por debajo de esta marca (z-index:40), así que sin este opacity:0 se
   veía encima del gate desde el primer instante. Aparece recién cuando se
   pulsa "Entrar" (.building-stage.revealed), con el mismo retraso con el que
   el propio gate termina de desvanecerse (1.5s + 0.5s, ver
   .building-stage.revealed .cloud-gate) para que no aparezca de golpe sobre
   las nubes que todavía se están yendo.
   Combinador de HERMANOS (~), no descendiente: #idxBrand vive fuera de
   <header id="inicio" class="building-stage">, como hermano suyo a nivel de
   <body> (mismo motivo que .stage-drawer/.idx-brand ya se cablean por JS y
   no por anidamiento -- ver setDrawer en site.js). */
.idx-brand{position:fixed;left:18px;bottom:18px;z-index:40;pointer-events:none;
  width:max-content;padding:8px;border-radius:14px;
  background:#F6EFE6;
  box-shadow:0 8px 24px rgba(0,0,0,.16);
  opacity:0;
  transition:left .38s cubic-bezier(.2,.8,.2,1), opacity .5s ease}
.building-stage.revealed ~ .idx-brand{opacity:1;transition:left .38s cubic-bezier(.2,.8,.2,1), opacity .6s ease 1.6s}
@media (prefers-reduced-motion: reduce){
  .building-stage.revealed ~ .idx-brand{transition:left .38s cubic-bezier(.2,.8,.2,1), opacity 0s}
}
/* Menú lateral abierto (ver setDrawer en site.js): la marca se corre lo que
   mide el drawer + su margen, para no quedar tapada por él. Vuelve sola al
   cerrar, con la misma curva que la propia animación del drawer. */
.idx-brand.shift{left:calc(230px + 18px)}
/* 68px y no los 44px de antes: el logotipo de MAREA RESIDENCES era apaisado (ratio
   1.78) y a 44px de alto su palabra se leía sin problema. El de MAREA es un
   escudo CON el texto debajo (ratio 1.12), así que a 44px la línea
   "MAREA RESIDENCES" bajaba a ~10px y no se leía. */
.idx-brand img{display:block;height:68px;width:auto}

/* Disponibilidad (body.avail-open, ver openStageAvail/closeStageAvail en
   site.js): las tarjetas de detalle (abajo-izq) y conteo (abajo-der) viven
   justo en las mismas esquinas que esta marca -- se encimaba con la de
   detalle y tapaba el texto. Se reubica arriba-derecha, la única esquina
   libre en esta vista (la "X" de cerrar vive arriba-izq), y más chica.
   Este mismo hook (body.avail-open) ya reposicionaba un botón de sonido que
   ya no existe; se reutiliza para la marca en su lugar. */
body.avail-open .idx-brand{
  left:auto; right:18px; top:18px; bottom:auto;
  transform:scale(.82); transform-origin:top right;
}
body.avail-open .idx-brand.shift{ left:auto; } /* no debería coincidir con el cajón abierto, pero por si acaso */

/* ── Móvil ─────────────────────────────────────────────────────── */
@media (max-width: 860px){
  .loc-topbar{padding:10px 12px}
  .loc-close{width:32px;height:32px}
  .contact-modal-card{padding:24px 18px 20px;max-height:92vh}
  .idx-brand{left:12px;bottom:12px;padding:6px;border-radius:12px}
  .idx-brand.shift{left:calc(230px + 12px)}
  .idx-brand img{height:52px}
  /* La tarjeta de conteo pasa a ser una barra de ancho completo pegada
     arriba (top:64px, ver .stage-avail-legendcard en mobile.css) -- la
     marca se sube más y se encoge más para no tocarla. */
  body.avail-open .idx-brand{ right:10px; top:10px; transform:scale(.7); }
}

/* La ficha de contacto se desplaza por dentro (el formulario es largo), pero
   sin barra a la vista: mismo criterio que el resto del sitio. */
.contact-modal-card{scrollbar-width:none;-ms-overflow-style:none}
.contact-modal-card::-webkit-scrollbar{width:0;height:0;display:none}
