:root{
  --stone:#F5F2ED;
  --stone-deep:#EAE5DC;
  --ink:#211F1B;
  --ink-soft:#5A564D;
  --amber:#C97C3E;
  --amber-deep:#A9622C;
  --charcoal:#232821;
  --line: rgba(33,31,27,0.14);
}
*{margin:0;padding:0;box-sizing:border-box;}
html{scroll-behavior:smooth;}
body{
  background:var(--stone);
  color:var(--ink);
  font-family:'Roboto',sans-serif;
  font-weight:300;
  overflow-x:hidden;
}
h1,h2,h3{font-family:'Newsreader',serif; font-weight:400; letter-spacing:0.01em;}
.eyebrow{
  font-family:'Roboto',sans-serif;
  font-size:12px;
  letter-spacing:0.22em;
  text-transform:uppercase;
  color:var(--amber-deep);
  font-weight:500;
}
a{color:inherit; text-decoration:none;}
img{display:block; width:100%; height:100%; object-fit:cover;}

/* NAV */
nav{
  position:fixed; top:0; left:0; right:0; z-index:100;
  display:flex; align-items:center; justify-content:space-between;
  padding:8.8px 5%;
  background:rgba(245,242,237,0);
  transition:background 0.4s ease, padding 0.4s ease, box-shadow .4s ease;
}
nav.scrolled{
  background:rgba(33,31,27,0.55);
  backdrop-filter:blur(20px) saturate(180%);
  -webkit-backdrop-filter:blur(20px) saturate(180%);
  padding:5.6px 5%;
  border-bottom:1px solid rgba(255,255,255,0.12);
  box-shadow:0 8px 24px rgba(33,31,27,0.16);
}
.brand{font-family:'Newsreader',serif; font-size:17px; font-weight:700; letter-spacing:0.03em; color:#FFFFFF; text-transform:uppercase; cursor:pointer;}
.nav-links{display:flex; gap:36px; font-size:12px; font-weight:400; letter-spacing:0.04em; text-transform:uppercase; color:#FFFFFF;}
.nav-links a{position:relative; padding-bottom:4px; opacity:0.82; transition:opacity .3s;}
.nav-links a:hover{opacity:1;}
.nav-cta{
  border:1px solid #FFFFFF; padding:6px 18px; font-size:11px; font-weight:700;
  letter-spacing:0.08em; text-transform:uppercase; border-radius:2px; color:#FFFFFF;
  transition:background .3s,color .3s;
}
.nav-cta:hover{background:var(--ink); color:var(--stone);}

/* hamburguesa — oculta por default, se muestra solo en el media query ≤980px de más abajo,
   que es el mismo breakpoint donde .nav-links pasa a display:none */
.nav-hamburger{
  display:none; background:none; border:none; padding:6px; margin:-6px 0; color:#FFFFFF; cursor:pointer;
}

/* drawer full-screen del menú mobile/tablet — mismo mecanismo de overlay que .lightbox-overlay
   (opacity+visibility con transition, en vez de display:none, para poder animar la entrada/salida) */
.nav-drawer{
  position:fixed; inset:0; z-index:300; /* por encima de nav(100) y wsp-float(200) */
  background:var(--charcoal); color:var(--stone);
  display:flex; align-items:center; justify-content:center;
  opacity:0; visibility:hidden; transform:translateY(-14px);
  transition:opacity .35s ease, visibility .35s ease, transform .35s ease;
}
.nav-drawer.active{opacity:1; visibility:visible; transform:translateY(0);}
.nav-drawer-close{
  position:absolute; top:20px; right:5%; z-index:301;
  width:44px; height:44px; border-radius:50%; border:none;
  background:rgba(245,242,237,0.1); color:var(--stone);
  display:flex; align-items:center; justify-content:center; cursor:pointer;
  transition:background .3s ease;
}
.nav-drawer-close:hover{background:rgba(245,242,237,0.2);}
.nav-drawer-links{display:flex; flex-direction:column; align-items:center; gap:28px;}
.nav-drawer-links a{
  font-family:'Newsreader',serif; font-style:italic; font-weight:400;
  font-size:clamp(26px,7vw,38px); color:var(--stone); transition:color .25s ease;
}
.nav-drawer-links a:hover{color:#E7B98A;}
.nav-drawer-links a.nav-drawer-cta{
  margin-top:10px; font-family:'Roboto',sans-serif; font-style:normal; font-weight:500;
  font-size:13px; letter-spacing:0.1em; text-transform:uppercase; color:var(--ink);
  background:#E7B98A; padding:14px 36px; border-radius:2px; transition:background .3s ease, color .3s ease;
}
.nav-drawer-links a.nav-drawer-cta:hover{background:var(--stone); color:var(--ink);}
/* bloquea el scroll de fondo mientras el drawer está abierto — toggleado por JS */
body.nav-drawer-open{overflow:hidden;}

/* HERO */
.hero{
  position:relative; height:100vh; min-height:640px; overflow:hidden;
  display:flex; align-items:flex-end;
}
/* ⚠ Bug de notebooks de 15" (~1366×768, alto de viewport real bastante menor por el chrome del
   navegador) — el título tapaba el nav. Causa real, confirmada con getBoundingClientRect() antes de
   tocar nada: `.hero` es un <section> y NUNCA resetea `padding` (arriba), así que hereda el
   `section{padding:150px 5%}` genérico del sitio — un padding-bottom FIJO en px que no se achica con
   el alto real del viewport. Como `.hero-content` es flex item de `.hero` (align-items:flex-end,
   el único hijo en flujo — el resto son position:absolute) queda anclado por su borde INFERIOR a
   (alto de .hero − ese padding-bottom de 150px); su borde superior = esa línea − su propia altura
   (~535-545px a los anchos de escritorio donde el h1 llega al tope del clamp de font-size). El
   padding-top NO es la causa (no afecta la posición del item mientras entre en la caja) — es
   específicamente el padding-bottom fijo el que, en un viewport bajo, deja muy poco margen entre
   el borde superior del contenido y el navbar (fixed, z-index:100, por encima del hero). Medido en
   el navegador (ancho 1436px, con y sin el fix): a 636px de alto real (el `min-height:640px` de
   arriba) el gap eyebrow-top − nav-bottom daba -89px (bien tapado); a 696px daba -37.8px; a 764px
   ya daba +24.7px (sin solapar, pero con poco margen); a 1076px daba +311.8px (sin ningún problema).
   Fix — SOLO desktop (`min-width:981px`, ver la media query de abajo): se le da a `.hero` un
   `padding-bottom` propio (pisa el heredado de `section`, ganando por especificidad de clase) que
   se ACHICA a partir de cierto alto de viewport y queda EXACTO en 150px arriba de ese punto —
   `clamp(24px, calc(100vh - 630px), 150px)`: el término `calc(100vh - 630px)` cruza los 150px
   exactos en vh=780px (100vh-630=150 ⟺ vh=780), así que para cualquier alto ≥780px (incluye los
   1080px pedidos para verificar) el `clamp` devuelve 150px siempre — CERO cambio de layout ahí,
   confirmado con ScrollTrigger.getAll()[...].end idéntico antes/después (2283px a 1080px de alto).
   Por debajo de 780px el valor baja progresivamente (768px→~138px, 696px→~66px) hasta tocar el piso
   de 24px (alcanzado recién en viewports muy bajos, ~654px o menos) — con esto el gap medido pasa a
   ~+41px a 768px, ~+46px a 696px y ~+37px incluso en el mínimo absoluto de 636px (`min-height:640px`
   de arriba), siempre sin superponerse al nav. `padding-top`/horizontal no se tocan (siguen 150px/5%
   heredados de `section`, no forman parte del bug). No toca `heroEl.offsetHeight` (la timeline de
   GSAP mide el alto TOTAL de `.hero`, fijo por `height:100vh`+`min-height:640px` — el padding vive
   ADENTRO de esa altura ya fija, así que `heroPxPerUnit`/`heroEndPx` quedan exactamente iguales,
   confirmado). Ámbito `min-width:981px` a propósito: `.hero` en mobile/tablet (≤980px) hereda en
   cambio `section{padding:90px 6%}` (ver ese mismo `@media` más abajo) — sin este scope, la regla de
   acá (más específica por clase) pisaría también ese padding-bottom mobile sin querer. */
@media (min-width:981px){
  .hero{
    /* Cuánto le restó el fix de arriba al padding-bottom original de 150px, a este alto de
       viewport — 0 en cualquier alto ≥780px (el clamp da 150 ahí, sin cambios), creciendo hasta
       126px en el piso de 640px. La usan .hero-meta/.hero-scroll-arrow/.hero-scroll-text más abajo
       para devolver parte de ese espacio (ver comentario ahí — efecto colateral de este mismo fix
       sobre el gap párrafo↔"Descubrí el proyecto"), sin que ninguna de las dos cosas necesite
       repetir la cuenta por separado si el 630/150/24 de arriba cambia en el futuro. */
    --hero-pad-shrink: calc(150px - clamp(24px, calc(100vh - 630px), 150px));
    padding-bottom: clamp(24px, calc(100vh - 630px), 150px);
  }
}
.hero-img{position:absolute; inset:0; z-index:1;}
.hero-img img{transform:scale(1.12);}
.hero-scrim{
  position:absolute; inset:0; z-index:2;
  background:linear-gradient(180deg, rgba(20,18,14,0.15) 0%, rgba(20,18,14,0.08) 35%, rgba(18,16,12,0.78) 100%);
}
.hero-content{position:relative; z-index:3; padding:0 5% 8vh; color:#F5F2ED; width:100%;}
.hero-eyebrow{
  font-size:12px; letter-spacing:0.28em; text-transform:uppercase;
  color:#E7B98A; margin-bottom:18px; display:block;
}
.hero h1{
  font-size:clamp(48px,9vw,120px); line-height:0.98; font-weight:300;
  max-width:900px;
}
.hero h1 em{font-style:italic; color:#E7B98A; font-weight:300;}
.hero-sub{
  margin-top:22px; font-size:16px; font-weight:300; max-width:420px;
  line-height:1.6; color:rgba(245,242,237,0.82);
}
.hero-meta{
  position:absolute; bottom:8vh; right:5%; z-index:3;
  display:flex; gap:38px; color:#F5F2ED;
}
.hero-meta div{text-align:right;}
.hero-meta .num{font-family:'Newsreader',serif; font-size:26px; font-style:italic; color:#E7B98A;}
.hero-meta .lbl{font-size:10px; letter-spacing:0.14em; text-transform:uppercase; opacity:0.7; margin-top:2px;}
/* Flecha de scroll-down — elemento nuevo, no forma parte de .scroll-cue (que queda intacto).
   Centrada en toda la página (no relativa a .hero-meta, que está alineado a la derecha) y a la
   misma altura que los números de las stats: .hero-meta{bottom:8vh} es el borde inferior de esa
   fila (donde cae el label chico), así que +15px sube el centro del ícono hasta la altura del
   número — mismo offset que usa Mosconi, válido acá porque .hero-meta es idéntico en ambos
   proyectos. Color = el mismo que .hero-meta .num (#E7B98A). El tamaño real del ícono (32×19,
   viewBox 20×12) es el mismo que usa Mosconi, visualmente comparable a los 26px de .num. */
.hero-scroll-arrow{
  position:absolute; bottom:calc(8vh + 15px); left:50%; transform:translateX(-50%); z-index:3;
  display:flex; color:#E7B98A; cursor:pointer;
}
/* Latido (scale loop) reusando el @keyframes heroArrowPulse ya definido para .scroll-cue .line
   (ver más abajo) — sin restricción a desktop, a diferencia de esa otra regla: acá no hay
   equivalente mobile que preservar (.hero-meta se ve en todos los anchos en este sitio, así que
   la flecha también). Anima el <svg> interno, no el <span>, para no pisar el
   transform:translateX(-50%) que centra el ícono. */
.hero-scroll-arrow svg{animation:heroArrowPulse 1.6s ease-in-out infinite;}
/* "Latido" (scale loop continuo) — mismo keyframe que ya usa Mosconi para la flecha SVG de su
   scroll-cue (heroArrowPulse, mismo nombre por consistencia entre ambos sitios). */
@keyframes heroArrowPulse{0%,100%{transform:scale(1);} 50%{transform:scale(1.25);}}
/* "Descubrí el proyecto" — reemplaza por completo al viejo .scroll-cue (texto + línea con
   highlight deslizante, ya eliminado del HTML y del CSS). En la misma fila que la flecha
   y las stats: comparte el bottom:calc(8vh + 15px) exacto de .hero-scroll-arrow — eso es lo que
   realmente define "misma fila" (el viejo .scroll-cue usaba bottom:36px fijo, una base distinta
   a la de .hero-meta/.hero-scroll-arrow que podía desalinearse en otras alturas de pantalla).
   ⚠ left:9.5%, NO 5% (corregido — el 5% de una versión anterior de esta regla parecía alineado
   con el título pero no lo estaba, verificado con getBoundingClientRect: la caja de
   .hero-content arranca en 5%, pero el texto real del h1 arranca 85.7px más adentro, porque
   .hero es un <section> y hereda el section{padding:150px 5%} genérico DEL SITIO, además de su
   propio .hero-content{padding:0 5%} — los dos 5% se acumulan (5% + 5% del 90% restante = 9.5%
   del ancho de .hero), mientras que .hero-scroll-text es position:absolute y su left:% se mide
   contra el ancho completo de .hero, sin heredar ese padding. 9.5% dio exactos los mismos px que
   el h1 en el navegador. Mismo valor que ya usa Mosconi para este mismo caso (mismo bug
   estructural, mismo fix). Tamaño: 13.86px = -30% sobre los 19.8px anteriores. */
.hero-scroll-text{
  position:absolute; bottom:calc(8vh + 15px); left:9.5%; z-index:3; color:#F5F2ED;
  font-size:13.86px; letter-spacing:0.12em; text-transform:uppercase;
  opacity:0.75; font-weight:400; cursor:pointer;
  transition:font-weight .2s ease;
}
.hero-scroll-text:hover{font-weight:700;}
/* ⚠ Efecto colateral del fix de solapamiento navbar/título (sesión posterior, ver el comentario
   junto a `.hero{padding-bottom}` más arriba) — corregido acá, sin tocar nada de ese fix. Al
   achicarse el padding-bottom de `.hero` en viewports bajos, `.hero-content` (bottom-anchored por
   `align-items:flex-end`) se corre hacia ABAJO, más cerca de "Descubrí el proyecto" — que es
   `position:absolute` con `bottom` medido contra el borde de `.hero` (NO contra el padding-bottom
   de `.hero`, así que el fix de arriba no lo mueve un solo px por sí solo). Resultado: el párrafo
   quedó "pegado" a "Descubrí el proyecto" en los mismos viewports bajos donde se ganó espacio
   arriba (confirmado en el navegador: a 700px de alto, el gap párrafo↔"Descubrí el proyecto" bajó
   de 119px a 35px — la MISMA cantidad, 84px, que el fix de arriba le restó al padding-bottom).
   ⚠ Probado primero y descartado: agregar `margin-bottom` al párrafo (o a `.hero-content`) NO
   sirve — por el mismo `align-items:flex-end`, cualquier alto extra agregado ADENTRO de
   `.hero-content` empuja TODO el bloque (eyebrow+h1+párrafo) hacia ARRIBA en la misma medida
   (confirmado en el navegador: +40px de margin-bottom en `.hero-sub` movió el `eyebrow-top` -40px,
   comiéndose case todo el margen ganado con el navbar: gap nav→eyebrow bajó de 46px a 6px) — habría
   deshecho el fix anterior en los mismos viewports que lo necesitan. Tampoco es un `gap` de flex
   (`.hero-scroll-text`/`.hero-scroll-arrow` no son hijos de ningún contenedor flex compartido con
   el párrafo, son absolutos, hermanos sueltos de `.hero-content` dentro de `.hero`) — ninguna de
   las dos opciones típicas aplica tal cual acá.
   Fix real: mover "Descubrí el proyecto" (+ la flecha, + las stats, para no romper la alineación
   "misma fila" ya documentada arriba) hacia ABAJO — un lever totalmente independiente del `.hero`
   de arriba (no toca su padding-bottom ni el de `.hero-content`, así que no le devuelve nada al
   navbar). La cantidad a devolver es `min(--hero-pad-shrink, 25.6px)` — el mismo `--hero-pad-shrink`
   que ya calcula el fix de arriba (0 en ≥780px, sin cambios ahí — confirmado: gap idéntico al
   original a 1080px), tope en 25.6px = el `line-height` real de `.hero-sub` (16px × 1.6, el pedido
   era "una línea de ese párrafo") para no devolver de más en viewports muy bajos. Con esto el gap
   pasa a ~+41px a 768px (era ~ya bueno ahí, +16 de compensación), ~+61px a 696px (+25.6, tope) y
   ~+61px también en el piso de 636px (mismo tope) — siempre por encima del original ajustado, nunca
   por debajo. Ámbito `min-width:981px`, igual que el fix de arriba — en mobile/tablet no aplica
   ningún `--hero-pad-shrink` (la variable ni se define fuera de esa media query, así que `var()`
   cae a 0 por el `min(0, 25.6px)`... en realidad ni hace falta: fuera de `min-width:981px` esta
   regla entera no existe, `.hero-meta`/`.hero-scroll-arrow`/`.hero-scroll-text` quedan con su
   `bottom` de la regla base, sin tocar). */
@media (min-width:981px){
  .hero-meta{ bottom: calc(8vh - min(var(--hero-pad-shrink), 25.6px)); }
  .hero-scroll-arrow, .hero-scroll-text{ bottom: calc(8vh + 15px - min(var(--hero-pad-shrink), 25.6px)); }
}
.hero-logo{
  position:absolute; inset:0; z-index:3;
  display:flex; align-items:center; justify-content:center;
  font-family:'Newsreader',serif; font-weight:600; color:#F5F2ED;
  font-size:clamp(32px,6vw,72px); letter-spacing:0.05em;
  opacity:0; pointer-events:none;
}

section{padding:150px 5%; position:relative;}
.narrow{max-width:1180px; margin:0 auto;}

/* INTRO / POR QUE INVERTIR */
#invertir{position:relative; overflow:hidden;}
.invertir-video{position:absolute; left:0; right:0; z-index:1; width:100%; object-fit:cover; will-change:transform;}
.invertir-overlay{position:absolute; inset:0; z-index:2; background:rgba(20,18,14,0.55);}
#invertir > .narrow{position:relative; z-index:3;}
#invertir .eyebrow{color:#E7B98A;}
.intro-grid{
  display:grid; grid-template-columns:0.9fr 1.1fr; gap:80px; align-items:start;
}
.intro-grid h2{font-size:clamp(30px,4vw,46px); line-height:1.15; color:var(--stone);}
.intro-copy{font-size:16px; line-height:1.8; color:rgba(245,242,237,0.75); font-weight:300;}
.reasons{margin-top:46px; display:flex; flex-direction:column;}
.reason{
  display:grid; grid-template-columns:70px 1fr; gap:24px;
  padding:26px 0; border-top:1px solid rgba(245,242,237,0.18);
}
.reason:last-child{border-bottom:1px solid rgba(245,242,237,0.18);}
.reason .mark{font-family:'Newsreader',serif; font-style:italic; font-size:22px; color:#E7B98A;}
.reason h3{font-size:18px; font-weight:500; font-family:'Roboto'; margin-bottom:6px; color:var(--stone); transition:color .25s;}
.reason p{font-size:14.5px; color:rgba(245,242,237,0.75); line-height:1.65; font-weight:300; max-width:520px;}
.reason-link{cursor:pointer;}
.reason-link:hover h3{color:#E7B98A;}

/* SPLIT IMAGE SECTIONS (parallax) */
.split{
  display:grid; grid-template-columns:1fr 1fr; min-height:92vh; align-items:center;
}
.split.reverse .split-img{order:2;}
.split.reverse .split-copy{order:1;}
.split-img{height:100%; overflow:hidden; position:relative;}
.split-img .parallax-layer{height:130%; width:100%; position:relative; top:-15%;}
.split-copy{padding:0 90px;}
.split-copy .eyebrow{margin-bottom:20px;}
.split-copy h2{font-size:clamp(28px,3.6vw,42px); margin-bottom:22px; line-height:1.15;}
.split-copy p{font-size:15.5px; line-height:1.8; color:var(--ink-soft); font-weight:300; max-width:420px;}
.split-copy .stat-row{display:flex; gap:44px; margin-top:36px;}
.split-copy .stat .num{font-family:'Newsreader',serif; font-style:italic; font-size:32px; color:var(--amber-deep);}
.split-copy .stat .lbl{font-size:11px; letter-spacing:0.08em; text-transform:uppercase; color:var(--ink-soft); margin-top:4px;}

/* UNIDADES */
.unidades{
  background:var(--stone-deep); overflow:hidden;
  padding-top:75px; padding-bottom:75px;
}
.unidades-bg{
  position:absolute; left:0; right:0; z-index:1;
  background:url('../assets/planos/fondo.jpg') center/cover no-repeat;
  will-change:transform;
}
.unidades-overlay{position:absolute; inset:0; z-index:2; background:rgba(20,18,14,0.55);}
.unidades > .narrow{position:relative; z-index:3;}
.unidades .eyebrow{color:#E7B98A;}
.unidades-head{display:flex; justify-content:space-between; align-items:flex-end; margin-bottom:40px; flex-wrap:wrap; gap:24px;}
.unidades-head h2{font-size:clamp(28px,3.6vw,42px); color:var(--stone);}
.unit-panel{
  display:grid; grid-template-columns:1.1fr 1fr; gap:90px;
}
.unit-visual{
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:26px;
  position:relative; min-height:700px;
}
.unit-visual img{width:auto; max-width:100%; height:auto; max-height:720px; object-fit:contain; transition:opacity .55s ease, transform .55s ease;}
.dim-toggle{
  background:none; border:none; padding:10.5px 17.5px; cursor:pointer;
  font-family:'Roboto'; font-size:19.25px; letter-spacing:0.1em; text-transform:uppercase;
  color:#E7B98A; font-weight:400;
}
.dim-toggle.active{font-weight:700;}
.unit-info{display:flex; flex-direction:column; justify-content:center;}
.unit-info .floor-select{display:flex; flex-wrap:wrap; gap:10px; margin-bottom:52px;}
.floor-select button{
  padding:10px 18px; border:1px solid rgba(245,242,237,0.3); background:transparent; color:var(--stone);
  font-family:'Roboto'; font-size:12px; letter-spacing:0.03em; cursor:pointer; transition:all .25s;
}
.floor-select button.active{background:var(--amber-deep); color:#fff; border-color:var(--amber-deep);}
/* .unit-btn-full/.unit-btn-label/.unit-btn-num — los 3 spans conviven siempre en el DOM (mismo
   markup en todos los breakpoints), pero por default (desktop/tablet) solo se ve .unit-btn-full
   ("Unidad 101 - 701", idéntico texto/formato de siempre) — .unit-btn-label/.unit-btn-num (el
   formato de 2 líneas "Unidad" + "101-701" sin espacios en el guion, mismo criterio que Mosconi)
   quedan ocultos acá y solo se activan en el rediseño mobile (ver @media max-width:640px, más abajo
   en el archivo) para no tocar en absoluto el aspecto de desktop/tablet. */
.unit-btn-label, .unit-btn-num{display:none;}
.unit-details{transition:opacity .55s ease, transform .55s ease;}
.unit-info h3{font-size:28px; font-family:'Newsreader',serif; font-style:italic; margin-bottom:4px; color:var(--stone);}
.unit-subtitle{font-size:13px; letter-spacing:0.06em; text-transform:uppercase; line-height:1.6; color:rgba(245,242,237,0.75);}
.unit-espacios{font-size:15px; line-height:1.7; color:rgba(245,242,237,0.75); font-weight:300; margin-top:26px; max-width:420px;}
.unit-specs{display:flex; gap:30px; margin:36px 0 44px; padding-top:30px; border-top:1px solid rgba(245,242,237,0.18);}
.unit-specs div .num{font-family:'Roboto',sans-serif; font-weight:500; font-size:26px; color:#E7B98A;}
.unit-specs div .lbl{font-size:10.5px; letter-spacing:0.06em; text-transform:uppercase; color:rgba(245,242,237,0.65); margin-top:2px;}
.unit-visual.unit-hidden img{opacity:0; transform:translateX(-28px);}
.unit-details.unit-hidden{opacity:0; transform:translateX(-28px);}
.unidades .btn-outline{border-color:rgba(255,255,255,0.6); color:var(--stone);}
.unidades .btn-outline:hover{background:var(--stone); color:var(--ink); border-color:var(--stone);}
.btn-row{display:flex; gap:14px; margin-top:8px; flex-wrap:wrap;}
.btn{
  padding:13px 24px; font-size:12px; letter-spacing:0.06em; text-transform:uppercase;
  display:inline-flex; align-items:center; gap:8px; transition:all .3s; cursor:pointer;
  background:none; border:none; font-family:inherit; -webkit-appearance:none; appearance:none;
}
.btn-primary{background:var(--ink); color:var(--stone);}
.btn-primary:hover{background:var(--amber-deep);}
.btn-outline{border:1px solid var(--ink); color:var(--ink);}
.btn-outline:hover{background:var(--ink); color:var(--stone);}

/* AMENITIES */
#amenities{
  position:relative; overflow:hidden;
  min-height:80vh; display:flex; align-items:center;
}
.amenities-video{
  position:absolute; inset:0; z-index:1;
  width:100%; height:100%; object-fit:cover;
}
.amenities-overlay{position:absolute; inset:0; z-index:2; background:rgba(0,0,0,0.35);}
.amenities-content{position:relative; z-index:3;}
.amenities-content .eyebrow{color:#E7B98A;}
.amenities-content h2{color:var(--stone);}
.amenities-content p{
  margin-top:20px; font-size:15.5px; max-width:420px; line-height:1.7;
  color:rgba(245,242,237,0.85);
}

/* DIA A DIA — video de fondo entre "Los dormitorios" y "Unidades", mismo patrón que AMENITIES.
   Sección portada 1:1 desde JL Mosconi (mismo template, ya confirmada y funcionando ahí) — sin
   equivalente previo en Congreso. Sin override mobile: se apoya en las reglas genéricas de
   `section{padding:...}` del sitio, igual que en Mosconi (no tiene ningún @media propio ahí
   tampoco). */
#dia-a-dia{
  position:relative; overflow:hidden;
  min-height:80vh; display:flex; align-items:center;
}
.dia-a-dia-video{
  position:absolute; inset:0; z-index:1;
  width:100%; height:100%; object-fit:cover;
}
.dia-a-dia-overlay{position:absolute; inset:0; z-index:2; background:rgba(0,0,0,0.35);}
.dia-a-dia-content{position:relative; z-index:3;}
.dia-a-dia-content .eyebrow{color:#E7B98A;}
.dia-a-dia-content h2{color:var(--stone);}
.dia-a-dia-content p{
  margin-top:20px; font-size:15.5px; max-width:420px; line-height:1.7;
  color:rgba(245,242,237,0.85);
}

/* GALERIA — el carrusel (.galeria-wrap y todo lo de adentro) va full-bleed, sin padding de sección
   ni gap entre cards (eso no se toca). Arriba y abajo de las imágenes hay dos franjas de fondo
   sólido (.galeria-strip, var(--ink) — NO --charcoal, NO blur/fondo desenfocado, versión
   descartada) en relación 2:1 (arriba el doble de alta que abajo), sincronizadas por JS
   (sizeGaleriaStrips() en main.js: mide .galeria-strip-top y fija .galeria-strip-bottom a la
   mitad) para que la proporción se mantenga exacta en cualquier breakpoint, incluso si el título
   llega a wrapear a 2 líneas. El título vive pegado al borde inferior de la franja de arriba
   (justify-content:flex-end), no centrado ni arriba de todo — el aire libre queda por encima del
   texto, no alrededor. Ya no necesita ningún scrim: está sobre el fondo sólido de la banda, no
   sobre las fotos (el viejo .galeria-scrim se eliminó por completo, HTML y CSS).
   A diferencia de Mosconi, acá NO se usa el breakout width:100vw/margin:calc(50%-50vw) en
   .galeria-wrap para el full-bleed — .galeria{padding:0} sobre una <section> que ya ocupa el
   100% del ancho alcanza solo, mismo resultado visual con el mecanismo que ya usa el resto de
   Congreso para secciones full-bleed. */
.galeria{background:var(--ink); position:relative; overflow:hidden; padding:0;}
.galeria .eyebrow{color:#E7B98A;}
.galeria h2{color:var(--stone);}
.galeria-strip{position:relative; background:var(--ink); overflow:hidden;}
.galeria-strip-top{
  min-height:279px;
  padding:0 5%; /* el padding-bottom (espacio título↔imágenes) lo fija sizeGaleriaStrips() en JS,
                   a partir del alto real del h2 (Newsreader) — así queda siempre en 2x ese alto,
                   sea cual sea el font-size que resuelva el clamp() del h2 en cada breakpoint */
  display:flex; flex-direction:column; justify-content:flex-end;
}
/* .narrow es flex item acá (columna) — como flex item se encoge a su contenido en vez de ocupar
   el ancho disponible hasta max-width:1180px, y margin:0 auto termina centrando esa caja angosta
   en vez de dejarla pegada a la izquierda del mismo margen que usa el resto del sitio. width:100%
   fuerza el ancho completo (recortado por max-width:1180 como en cualquier otra sección), así el
   margin:auto centra la MISMA caja de 1180px que usan las demás — mismo margen que el título de
   "Por qué invertir", no un padding inventado nuevo. */
.galeria-strip-top .narrow{width:100%; margin:0 auto;}
.galeria-wrap{position:relative;}
.galeria-track{
  overflow:hidden; touch-action:pan-y; cursor:grab;
}
.galeria-track.dragging{cursor:grabbing; user-select:none;}
.galeria-track-inner{display:flex; gap:0; will-change:transform;}
.galeria-card{
  flex:0 0 auto; height:clamp(195px,27vw,386.25px); aspect-ratio:4/3;
  overflow:hidden; cursor:pointer;
}
.galeria-card img{width:100%; height:100%; object-fit:cover; -webkit-user-drag:none; user-select:none; pointer-events:none;}
.galeria-arrow{
  position:absolute; top:50%; transform:translateY(-50%); z-index:5;
  width:46px; height:46px; border-radius:50%; border:none;
  background:var(--stone); color:var(--ink);
  display:flex; align-items:center; justify-content:center;
  cursor:pointer; transition:background .3s ease, color .3s ease;
}
.galeria-arrow:hover{background:var(--amber-deep); color:var(--stone);}
.galeria-prev{left:14px;}
.galeria-next{right:14px;}

/* LIGHTBOX */
.lightbox-overlay{
  position:fixed; inset:0; z-index:1000; background:rgba(20,18,14,0.94);
  display:flex; align-items:center; justify-content:center;
  opacity:0; visibility:hidden; transition:opacity .3s ease;
}
.lightbox-overlay.active{opacity:1; visibility:visible;}
.lightbox-img-wrap{
  width:86vw; height:84vh;
  display:flex; align-items:center; justify-content:center;
  overflow:hidden; cursor:zoom-in;
}
.lightbox-img-wrap.zoomed{cursor:grab;}
.lightbox-img-wrap.zoomed.panning{cursor:grabbing;}
.lightbox-img-wrap img{
  max-width:100%; max-height:100%; width:auto; height:auto;
  display:block; object-fit:contain; border:1px solid rgba(245,242,237,0.14);
  user-select:none; -webkit-user-drag:none;
}
.lightbox-img-wrap.zoomed img{max-width:none; max-height:none;}
.lightbox-close{
  position:fixed; top:28px; right:28px; z-index:1001;
  width:46px; height:46px; border-radius:50%; border:none;
  background:var(--ink); color:var(--stone);
  display:flex; align-items:center; justify-content:center;
  cursor:pointer; transition:background .3s ease;
}
.lightbox-close:hover{background:var(--amber-deep);}
.lightbox-arrow{
  position:fixed; top:50%; transform:translateY(-50%); z-index:1001;
  width:52px; height:52px; border-radius:50%; border:none;
  background:var(--ink); color:var(--stone);
  display:flex; align-items:center; justify-content:center;
  cursor:pointer; transition:background .3s ease;
}
.lightbox-arrow:hover{background:var(--amber-deep);}
.lightbox-prev{left:28px;}
.lightbox-next{right:28px;}
@media (max-width:640px){
  .lightbox-prev{left:10px;}
  .lightbox-next{right:10px;}
  .lightbox-close{top:14px; right:14px;}

  /* HERO mobile — se sacan las stats (Pisos/Ambientes/Terraza común) y el texto "Descubrí el
     proyecto", dejando sola la flecha de scroll-cue, centrada, con su mismo latido (heroArrowPulse,
     regla base sin cambios). GSAP sigue targeteando .hero-meta/.hero-scroll-text en el fade de fase 1
     del pin (heroPinTl, main.js) sin ningún problema: un elemento display:none no se rompe porque algo
     le anime opacity, simplemente no se ve. .hero-scroll-arrow no se toca (ya queda centrada por su
     propio left:50%+transform de la regla base, válida en todos los anchos) — el listener de click
     (scrollToHeroEnd) tampoco cambia, sigue enganchado al mismo elemento. */
  .hero-meta{display:none;}
  .hero-scroll-text{display:none;}

  /* UBICACION mobile — #mapa-proyecto (puntos/íconos/letras) se oculta acá: su contenido ya viene
     fundido en assets/img/mapa/base-mobile.png, que reemplaza a #mapa-veredas vía <picture><source>
     en este mismo breakpoint (ver HTML) — sin ocultarlo, quedaría duplicado (el mapa base mobile ya
     trae el pin+texto+plaza+subte+tren propios, dibujados más grandes). El resto de las capas
     (mapa-calles, hover-*) no se ocultan: sus <picture> ya resuelven al asset mobile correspondiente
     en este ancho, mismo <img> de siempre (mismo id, misma clase), así que toda la lógica de
     opacity/toggle/hover en JS sigue funcionando sin tocar nada ahí. */
  #mapa-proyecto{display:none;}

  /* GALERÍA mobile — franjas de fondo sólido arriba/abajo del carrusel, achicadas a la mitad de su
     alto (mismo var(--ink), mismo mecanismo 2:1 vía sizeGaleriaStrips() en main.js — la de abajo
     sigue derivándose como la mitad de la de arriba, así que solo hace falta tocar el min-height de
     arriba: 279px → 139.5px, la de abajo la sigue automáticamente a 69.75px).
     El título (eyebrow+h2) se reacomoda para conservar la misma relación aire-arriba:espacio-abajo
     que tenía antes (162:56 medido en el navegador — el título queda más cerca de los renders que
     del borde superior), no exactamente el mismo *porcentaje sobre el total* (eso daría un
     padding-bottom negativo: el bloque del título mide 61px fijos — no se achica, no se pidió — y
     61px solos ya son el 44% de los 139.5px nuevos, más que el 58% que ocupaba el aire-arriba sobre
     el total viejo). En vez de eso, se reparte el espacio que queda tras descontar el título
     (139.5-61=78.5px) en la misma proporción 162:56 que tenían aire-arriba y espacio-abajo entre sí
     — el multiplicador de padding-bottom (GALERIA_TITLE_PAD_MULT en main.js) pasa de 2×alto-del-h2 a
     ~0.72×alto-del-h2 específicamente en este breakpoint (ver el chequeo de matchMedia en
     sizeGaleriaStrips()), dejando el resto como aire arriba del título vía justify-content:flex-end
     — mismo mecanismo que ya existía, solo con proporciones más chicas. */
  .galeria-strip-top{min-height:139.5px;}
}

/* MODAL 3D — canvas + secuencia de frames WebP, scrub por scroll dentro del propio modal */
#btn3D{font-weight:700;}
.modal3d-overlay{
  position:fixed; inset:0; z-index:1000;
  /* mismo efecto "Liquid Glass" que nav.scrolled (sección NAV, más arriba) — blur+saturate del
     contenido de fondo real en vez de un overlay oscuro plano */
  background:rgba(245,242,237,0.3);
  backdrop-filter:blur(20px) saturate(180%);
  -webkit-backdrop-filter:blur(20px) saturate(180%);
  opacity:0; visibility:hidden; transition:opacity .3s ease;
  display:flex; align-items:center; justify-content:center;
}
.modal3d-overlay.active{opacity:1; visibility:visible;}
.modal3d-content{
  /* ancho real del popup: 60% del viewport, recortado por la proporción exacta de los frames
     (1400×930) para que el canvas se vea completo, sin croppear */
  --modal3d-w: min(60vw, calc(60vh * 1400 / 930));
  --modal3d-h: calc(var(--modal3d-w) * 930 / 1400);
  display:flex; flex-direction:column; align-items:center; gap:20px;
}
.modal3d-header{width:var(--modal3d-w); display:flex; align-items:center; justify-content:space-between; gap:20px;}
.modal3d-title{font-family:'Newsreader',serif; font-style:italic; font-weight:400; font-size:22px; color:var(--stone);}
.modal3d-window{
  position:relative; width:var(--modal3d-w); height:var(--modal3d-h);
  box-shadow:0 20px 60px rgba(0,0,0,0.5);
}
.modal3d-close{
  flex-shrink:0; width:40px; height:40px; border-radius:50%; border:none;
  background:var(--ink); color:var(--stone);
  display:flex; align-items:center; justify-content:center;
  cursor:pointer; transition:background .3s ease;
}
.modal3d-close:hover{background:var(--amber-deep);}
.modal3d-canvas{display:block; width:100%; height:100%;}
.modal3d-loading{
  position:absolute; inset:0; z-index:1;
  display:flex; align-items:center; justify-content:center;
  background:rgba(20,18,14,0.85); color:var(--stone);
  font-size:12px; letter-spacing:0.1em; text-transform:uppercase;
}
/* barra de scrubbing horizontal — reemplaza el scroll interno. Track en tono stone/ink-soft,
   progreso y thumb en ámbar (misma paleta que el resto del sitio) */
.modal3d-slider{width:var(--modal3d-w);}
.modal3d-slider-track{
  position:relative; width:100%; height:4px; border-radius:2px;
  background:rgba(245,242,237,0.25);
}
.modal3d-slider-fill{
  position:absolute; top:0; left:0; height:100%; width:0%; border-radius:2px;
  background:var(--amber-deep); pointer-events:none;
}
.modal3d-slider-thumb{
  position:absolute; top:50%; left:0%; transform:translate(-50%,-50%);
  width:18px; height:18px; border-radius:50%;
  background:var(--amber-deep); border:2px solid var(--stone);
  box-shadow:0 2px 6px rgba(0,0,0,0.4);
  cursor:grab; touch-action:none; /* evita que el navegador interprete el drag táctil como scroll */
}
.modal3d-slider-thumb.dragging{cursor:grabbing;}

/* UBICACION */
.ubicacion{
  display:grid; grid-template-columns:1fr 1fr; min-height:88vh; align-items:stretch;
}
.ubicacion-map{position:relative; overflow:hidden;}
.mapa-layer{position:absolute; inset:0; opacity:0; transition:opacity .3s ease;}
#mapa-veredas, #mapa-proyecto{opacity:1;} /* base fija y quieta, sin scroll-driven reveal */
#mapa-calles.active{opacity:1;} /* prende junto con el resto de los overlays, ver setMapaActive() en main.js */

/* capas de highlight (marcador del proyecto y POIs) — siempre arriba de todo, ocultas por
   defecto. Encienden/apagan TODAS JUNTAS (incluida #mapa-calles, arriba) ante hover (desktop) o
   tap (mobile) en cualquier parte del mapa (ver setMapaActive() en main.js) — ya no hay zonas
   individuales por punto ni toggle independiente de "Calles". */
.mapa-hover-layer{position:absolute; inset:0; opacity:0; transition:opacity .2s ease; pointer-events:none; z-index:2;}
.mapa-hover-layer.active{opacity:1;}

/* mismo mecanismo que .unidades-bg/.unidades-overlay (imagen+parallax por aspect ratio real,
   ver sizeUbicacionCopyBg() en main.js) pero escoped a .ubicacion-copy en vez de a toda la
   sección — el mapa (.ubicacion-map, hermano de este div) no participa, sigue con su fondo propio
   sin cambios. overflow:hidden acá es necesario: .ubicacion-copy-bg se dimensiona más alto que el
   contenedor a propósito (margen para el parallax), igual que .unidades{overflow:hidden}. */
.ubicacion-copy{
  position:relative; overflow:hidden;
  padding:110px 90px; display:flex; flex-direction:column; justify-content:center;
  background:var(--charcoal); /* fallback antes de que JS calcule el alto de .ubicacion-copy-bg */
  color:var(--stone);
}
.ubicacion-copy-bg{
  position:absolute; left:0; right:0; z-index:0;
  background:url('../assets/planos/fondo.jpg') center/cover no-repeat;
  will-change:transform;
}
.ubicacion-copy-overlay{position:absolute; inset:0; z-index:1; background:rgba(20,18,14,0.55);}
.ubicacion-copy-content{position:relative; z-index:2; display:flex; flex-direction:column;}
.ubicacion-copy .eyebrow{color:#E7B98A;}
.ubicacion-copy h2{font-size:clamp(28px,3.6vw,42px); margin:20px 0 24px; color:var(--stone);}
.ubicacion-copy p{color:rgba(245,242,237,0.72); font-size:15.5px; line-height:1.8; max-width:420px; margin-bottom:36px;}
.poi-list{display:flex; flex-direction:column; gap:0;}
.poi{display:flex; justify-content:space-between; padding:16px 0; border-top:1px solid rgba(245,242,237,0.14); font-size:14px;}
.poi .d{color:#E7B98A; font-family:'Newsreader',serif; font-style:italic;}
/* la lista está desconectada del mapa (ver main.js) — el único efecto es este hover puro en CSS,
   sin ninguna clase ni listener compartido con el mapa. */
.poi[data-poi]:hover span:first-child{font-weight:700;}

/* CONTACTO / FOOTER */
footer{
  position:relative; overflow:hidden;
  background:var(--ink); color:var(--stone);
  min-height:100vh; display:flex; flex-direction:column;
  padding:110px 5% 80px;
}
.footer-video{position:absolute; left:0; right:0; z-index:1; width:100%; object-fit:cover; will-change:transform;}
.footer-overlay{position:absolute; inset:0; z-index:2; background:rgba(20,18,14,0.55);}
.footer-top{
  position:relative; z-index:3; flex:1 1 auto; display:grid; grid-template-columns:1.2fr 1fr; gap:80px; align-content:center;
  max-width:1180px; width:100%; margin-left:auto; margin-right:auto;
}
footer h2{font-size:clamp(32px,4.4vw,54px); color:var(--stone); line-height:1.1; max-width:520px;}
footer h2 em{color:#E7B98A; font-style:italic;}
.footer-form{display:flex; flex-direction:column; gap:16px;}
.footer-form input{
  background:transparent; border:none; border-bottom:1px solid rgba(245,242,237,0.28);
  color:var(--stone); font-family:'Roboto'; font-size:15px; padding:12px 2px; font-weight:300;
}
.footer-form input::placeholder{color:rgba(245,242,237,0.88);}
.footer-form .btn-primary{background:#E7B98A; color:var(--ink); align-self:flex-start; margin-top:10px;}
.footer-form .btn-primary:hover{background:var(--stone);}
.footer-form .btn-primary:disabled{background:#E7B98A; opacity:0.6; cursor:not-allowed;}
.footer-form .btn-primary:disabled:hover{background:#E7B98A;}
/* Formulario de contacto conectado a Web3Forms (fetch, sin recargar la página) — ver main.js
   `contactForm`. `.form-msg` es el mensaje de error inline (queda dentro del propio <form>, debajo
   del botón, no molesta el layout cuando está vacío por el `min-height` — sin eso el texto que
   aparece empujaría al resto hacia abajo de golpe). `.form-success` es un bloque aparte, hermano del
   `<form>` dentro de `.footer-form-col` (el nuevo contenedor que ocupa el lugar que antes tenía
   `.footer-form` directamente como hijo de `.footer-top` — mismo ancho de columna del grid, sin
   tocar `.footer-top`): al confirmar el envío el JS oculta el `<form>` (`display:none`, sin
   crossfade — más simple y sin riesgo de layout, ver CLAUDE.md) y hace aparecer este bloque con el
   mismo mecanismo de fade que ya usa el sitio en otros lados (`.reveal`/`.unit-hidden`: opacity+
   translateY con transition, clase agregada recién después de forzar reflow). Tipografía y color
   calcados de los demás encabezados de sección sobre fondo oscuro (`.eyebrow` ámbar + `h3` en
   Newsreader itálica, mismo tratamiento que `.unit-info h3`). */
.form-msg{font-size:12px; margin-top:2px; min-height:14px; color:#E2897A; opacity:0; transition:opacity .25s ease;}
.form-msg.in{opacity:1;}
.form-success{
  display:none; flex-direction:column; gap:10px; max-width:420px;
  opacity:0; transform:translateY(16px); transition:opacity .6s ease, transform .6s ease;
}
.form-success.in{opacity:1; transform:translateY(0);}
.form-success h3{font-size:22px; font-style:italic; color:var(--stone); line-height:1.3;}
.footer-bottom{
  position:relative; z-index:3;
  display:flex; justify-content:space-between; align-items:flex-end; padding-top:32px;
  font-size:12px; color:rgba(245,242,237,0.5); flex-wrap:wrap; gap:16px;
  max-width:1180px; width:100%; margin-left:auto; margin-right:auto;
} /* align-items:flex-end (no center, ver Mosconi): .footer-brand-col es más alta que .social-row
     (tiene el cross-link arriba del logo) — flex-end alinea por el borde inferior (el del logo,
     último elemento de la columna) con el de WhatsApp/Instagram/LinkedIn, en vez de centrar el
     bloque entero y desalinear el social-row. */
/* ⚠ El colchón anti-choque con .wsp-float (ver más abajo, junto al reordenado de esta sesión) vive
   ahora en `.footer-brand` directamente (margin-right), NO en `.footer-bottom` — un padding-right
   en .footer-bottom le resta ancho de contenido a SU grid sin restárselo también al de .footer-top,
   así que las columnas de ambas cajas (que tienen que calzar exactas) quedaban desalineadas
   (verificado: con el padding acá, .social-row quedaba ~55px a la izquierda de donde arranca
   .footer-form). Un margin en el elemento no afecta el cálculo de los tracks del grid, solo
   empuja a ESE elemento dentro de su propia celda — mismo resultado visual, sin descuadrar nada. */
.social-row{display:flex; gap:22px; align-items:baseline;}
.social-row a{font-size:15px; letter-spacing:0.06em; text-transform:uppercase; color:rgba(245,242,237,0.75); border-bottom:1px solid transparent;}
.social-row a:hover{border-color:#E7B98A; color:#E7B98A;}
/* ⚠ REVERTIDO en una sesión posterior — acá hubo un intento (letter-spacing por link con
   [href*=…], WhatsApp/Instagram/LinkedIn estirados a 500px cada uno para igualar el ancho de la
   línea separadora de .footer-form) que en su momento se dio por confirmado para escritorio, pero
   resultó estar mal: a 3 palabras de 500px + gaps (1544px) no entran en los 1180px de
   .footer-bottom, así que .footer-bottom (flex-wrap:wrap de base) las manda a su propia fila,
   empujando el cross-link "JL Mosconi" + el logo de JL Desarrollos a una fila aparte más abajo —
   confirmado roto contra CONTACTOS LAYOUT ROTO.jpg (WEB JL CONGRESO EDITABLES/), pedido explícito
   de restaurar el estado de antes de ese cambio contra CONTACTOS LAYOUT.jpg (misma carpeta). Fix:
   volver a `letter-spacing:0.06em` UNIFICADO en `.social-row a` (no por link) — es exactamente el
   valor que ya traía el primer commit del repo (`git show 1f7ee26:css/style.css`, antes de
   cualquier ajuste de esta sección), verificado ahí mismo. Con esto los 3 links vuelven a caber en
   una sola fila junto con el cross-link/logo, sin ningún ancho forzado. El ajuste de ancho tipo
   "cada palabra = ancho del input" sigue vivo SOLO en mobile (ver
   `@media(max-width:980px)` más abajo — `.social-row{width:100%; justify-content:space-between}`,
   sin tocar acá), que es donde se pidió específicamente y donde sí tiene sentido (una sola línea
   corta, container mucho más angosto). */

/* Cross-link "Descubrí también / JL Mosconi" — estructura idéntica a Mosconi (donde apunta a
   JL Congreso): ya no va inline en .social-row, es su propio bloque apilado con el logo de la
   desarrolladora dentro de .footer-brand-col. Ambos (cross-link y logo) centrados horizontalmente
   dentro de .footer-brand-col (antes align-items:flex-end, pegados al borde derecho) — a pedido
   explícito, ya no calzan con el borde derecho del social-row. */
.footer-brand-col{display:flex; flex-direction:column; align-items:center;}
.cross-link{color:rgba(245,242,237,0.55); margin-bottom:80px; display:flex; flex-direction:column; align-items:center; gap:6px;}
.cross-link-label{
  font-size:16.25px; font-family:'Roboto',sans-serif; font-weight:500; line-height:normal;
  color:#E7B98A;
}
.cross-link a{
  font-size:21.84px;
  font-weight:700; font-family:'Newsreader',serif; color:var(--stone);
  text-transform:uppercase; transition:color .25s;
}
.cross-link a:hover{color:#E7B98A;}

/* Logo de la desarrolladora (JL Desarrollos) — el mismo en Congreso y en Mosconi, con crossfade
   blanco/color en hover. Asset real (LOGO JL BLANCO/COLOR.png, 2078×397 en ambos proyectos),
   no texto estilizado — el nombre del proyecto actual no debe aparecer acá, solo en el cross-link
   de arriba y en el brand del nav. Altura 64.35px = 49.5px × 1.3 (+30%, a pedido explícito; era
   "mismo valor que Mosconi", ya no — Mosconi no se tocó), ancho vía aspect-ratio real del PNG.
   `transform:translateY()` (no margin-top) para bajarlo de posición sin mover nada más: este es el
   último hijo de .footer-brand-col (flex-column, alineada al fondo de .footer-bottom vía
   align-items:flex-end en .footer-bottom) — un margin-top achicaría el espacio de arriba pero NO
   baja el logo (el borde inferior de .footer-brand-col queda fijo contra el fondo del footer,
   agrandar el margin superior solo estira la columna hacia arriba); translateY corre el logo en
   sí sin alterar el flujo, dejando intacta esa alineación de fondo con el social-row. Ambas
   imágenes apiladas con position:absolute dentro de una caja de tamaño fijo (height + aspect-ratio)
   — sin esto, alternar solo la opacity de una imagen se ve como un fundido a negro en vez de un
   cambio real de blanco a color. Sin cambios en el override mobile (≤980px, ver más abajo:
   height:32px + margin-bottom:-42px) — no se tocó, es un mecanismo aparte para ese breakpoint. */
.footer-brand{position:relative; display:inline-block; height:64.35px; aspect-ratio:2078/397; transform:translateY(20px);}
.footer-brand img{position:absolute; inset:0; width:100%; height:100%; display:block; transition:opacity .25s;}
.footer-brand-hover{opacity:0;}
.footer-brand:hover .footer-brand-default{opacity:0;}
.footer-brand:hover .footer-brand-hover{opacity:1;}

/* Reordenado en una sesión posterior, a pedido explícito (con referencia visual): WhatsApp/
   Instagram/LinkedIn tienen que quedar INMEDIATAMENTE debajo del form (misma columna, la
   derecha) y el cross-link "Descubrí también/JL Mosconi" más abajo, alineado a la izquierda
   (misma columna que el título) — con el logo compartiendo esa fila a la derecha. .footer-top y
   .footer-bottom son cajas HERMANAS independientes (no anidadas), que solo comparten el mismo
   max-width:1180px centrado — nunca hubo relación real entre "columna del form" y dónde caía
   .social-row, que quedaba a la izquierda por ser el primer hijo de un
   justify-content:space-between (ver comentario viejo en la regla base de .footer-bottom).
   Swap de roles entre .footer-top/.footer-bottom (esto es también lo que resuelve "el espacio
   vertical excesivo entre el form y el resto"): antes .footer-top crecía (flex:1 1 auto) y
   centraba su propio contenido (align-content:center) absorbiendo el espacio sobrante del
   min-height:100vh del footer — eso dejaba un hueco variable (según el alto del viewport) entre
   el form y .footer-bottom. Ahora .footer-top queda con altura NATURAL (flex:0 0 auto) y es
   .footer-bottom la que crece (flex:1 1 auto) — adentro, una fila `1fr` (grid-template-rows)
   entre el social-row (fila 1) y el cross-link/logo (fila 3) absorbe ese mismo espacio sobrante,
   empujando el cross-link/logo al fondo real de la página (igual que siempre) mientras el
   social-row queda pegado arriba de esa caja, a solo `padding-top:32px` del form (el mismo valor
   que ya tenía .footer-bottom de la regla base — no es un número nuevo).
   .footer-bottom pasa de flex a grid con las MISMAS columnas que .footer-top (1.2fr/1fr) para
   que las columnas calcen exactas entre ambas cajas. .footer-brand-col pasa a `display:contents`
   (mismo mecanismo que ya usa este archivo en `.unit-info`, sección Unidades) — sin tocar el
   HTML: se "disuelve" el wrapper para que `.cross-link` y `.footer-brand` sean ítems directos
   del grid de `.footer-bottom`, cada uno en su columna.
   ⚠ Este bloque tiene que ir DESPUÉS de las reglas base de .footer-top/.footer-bottom/.social-row/
   .footer-brand-col/.cross-link/.footer-brand en el archivo (no importa que ya esté dentro de un
   @media — a igual especificidad, en CSS gana la regla que aparece último en el archivo, sea o no
   condicional; un primer intento puso este mismo bloque ANTES de la regla base de
   `.footer-brand-col` y esa regla base, sin ningún @media, le ganaba igual por venir después en
   el archivo — el `display:contents` nunca se aplicaba pese a que el media query matcheaba).
   Todo esto vive en min-width:981px — @media(max-width:980px) no se tocó, ni una línea. */
@media (min-width:981px){
  /* margin-top:118px — corrección de una sesión posterior: el pedido anterior de "triplicar el
     espacio entre el social-row y el form" (ver padding-top:96px más abajo) se resolvió subiendo
     todo el bloque de arriba (título+form) en vez de bajar el social-row, dejando el conjunto
     pegado arriba de la sección. Ahora se empuja el BLOQUE ENTERO hacia abajo (título + form +
     social-row juntos, sin tocar el espaciado interno entre ellos) hasta que el título quede a la
     altura donde antes estaba "¿Qué ambiente te interesa?" (228.09px − 110.09px = 118px).
     No hace falta envolver los 3 elementos en un contenedor nuevo: `.footer-top` (título+form) y
     `.footer-bottom` (que contiene a `.social-row` en su fila 1) son cajas HERMANAS dentro de
     `footer{display:flex; flex-direction:column}`, una pegada a la otra sin ningún gap entre
     medio — así que un margin-top en `.footer-top` empuja automáticamente a `.footer-bottom`
     entera (con `.social-row` adentro) la misma distancia, preservando el `padding-top:96px`
     interno intacto. `.cross-link`/`.footer-brand` NO se mueven por este cambio: su posición
     depende del fondo real del viewport (vía el `1fr` espaciador dentro de `.footer-bottom`), no
     del punto donde arranca `.footer-top` — verificado en el navegador. */
  .footer-top{flex:0 0 auto; margin-top:118px;}
  .footer-bottom{
    flex:1 1 auto;
    display:grid; grid-template-columns:1.2fr 1fr; grid-template-rows:auto 1fr auto;
    column-gap:80px; row-gap:0;
    /* padding-top:20px — corregido en una sesión posterior, reemplaza el 96px (= 32px original × 3)
       de arriba. Bug reportado: en notebooks de 15" (viewport bajo) la fila WHATSAPP/INSTAGRAM/
       LINKEDIN quedaba pegada al logo de JL Desarrollos de abajo. Causa: `.footer-bottom` es un
       grid `auto/1fr/auto` (fila 1 = social-row, fila 3 = cross-link+logo) dentro de un `footer`
       con `min-height:100vh` — la fila `1fr` del medio absorbe lo que sobra, y en viewports bajos
       eso "sobrante" se reduce mucho (medido a 700px de alto: gap social→logo de solo 20px).
       Aumentar el padding-top de acá (como se había hecho con el 96px) empeora esto — le resta
       MÁS presupuesto vertical a esa misma fila `1fr`. El pedido (medir el gap botón "ENVIAR
       CONSULTA"↔línea de "¿Qué ambiente te interesa?" — 26px, `16px` de `gap` en `.footer-form` +
       `10px` de `margin-top` propio del botón — y aplicar el DOBLE, 52px, como nuevo gap botón↔fila
       social) se resuelve bajando este padding-top, no subiéndolo: verificado en el navegador que
       `padding-top:20px` da un gap botón↔social-row de exactamente 52.0px — y, como consecuencia
       directa de liberarle presupuesto a la fila `1fr`, el gap social→logo sube de 20px a 45.7px a
       700px de alto (más del doble), resolviendo el bug reportado sin haber tocado nada de esa fila
       directamente. Se pisa acá (no en la regla base) para no tocar mobile, que sigue heredando el
       32px original de la regla base. */
    padding-top:20px;
  }
  /* gap:125.4609px reemplaza el gap:22px de la regla base — es lo que hace que las 3 palabras
     JUNTAS (con su tracking normal de 0.06em intacto, sin tocar letter-spacing) ocupen los mismos
     500px que la línea del input. Calculado igual que otras veces en esta sección: se mide el
     ancho de .social-row con gap:0 y con gap:10px, la pendiente (2, porque son 2 huecos entre 3
     palabras) resuelve el gap exacto contra el target de 500px — verificado en el navegador que
     da 500.000px exacto con este valor. */
  .social-row{grid-column:2; grid-row:1; justify-self:start; gap:125.4609px;}
  .footer-brand-col{display:contents;}
  /* align-items:flex-start (pisa el `center` de la regla base, que sigue aplicando en mobile) —
     a pedido explícito: "Descubrí también"/"JL MOSCONI" quedan alineados a la izquierda. La CAJA
     de .cross-link ya arrancaba en el mismo left que el título (ambos son columna 1 de sus
     respectivos grids) — lo que estaba corrido a la derecha era el TEXTO adentro, centrado por
     align-items:center. Con flex-start el texto queda pegado al borde izquierdo de la caja, que
     ya coincide exacto con el margen del título — sin necesidad de ningún margin/left adicional. */
  .cross-link{grid-column:1; grid-row:3; align-self:end; margin-bottom:0; align-items:flex-start;}
  /* +60% sobre los 21.84px de la regla base (21.84 × 1.6 = 34.944px), a pedido explícito — el
     "Descubrí también" (.cross-link-label) no se toca, mantiene su tamaño de siempre. */
  .cross-link a{font-size:34.944px;}
  /* margin-right:100px = el mismo colchón anti-choque con .wsp-float que antes vivía como
     padding-right en .footer-bottom (ver comentario arriba de este bloque, por qué se movió acá). */
  .footer-brand{grid-column:2; grid-row:3; justify-self:end; align-self:end; margin-right:100px;}
}

/* Oculto (y corrido 120px hacia abajo) hasta que el pin del hero libera el scroll — el
   opacity/transform de acá abajo son solo el estado de reposo antes de que arranque esa tween
   (coinciden con su valor "from" para que no haya salto cuando GSAP toma el control); la
   animación real es 100% la tween scrubbeada al final de heroPinTl en main.js (último 10% del
   progress), no una transition CSS — por eso NO lleva `transition` acá: si la tuviera, el
   navegador intentaría suavizar cada actualización de estilo inline que hace GSAP frame a
   frame durante el scrub, compitiendo con esa interpolación y generando lag/rebote.
   .in solo controla pointer-events — togglea en onLeave/onEnterBack del mismo ScrollTrigger
   (ver main.js): el scrub no tiene un umbral natural para decidir cuándo el botón empieza a
   ser clickeable, así que esa parte sigue siendo binaria, atada al mismo instante exacto en
   que el pin libera/recaptura el scroll (mismo instante que nav.scrolled). */
.wsp-float{
  position:fixed; bottom:56px; right:56px; z-index:200;
  width:56px; height:56px; border-radius:50%; background:var(--ink);
  display:flex; align-items:center; justify-content:center; color:#fff;
  overflow:hidden;
  box-shadow:10px 14px 30px rgba(0,0,0,0.3); cursor:pointer;
  opacity:0; transform:translateY(120px); pointer-events:none;
}
.wsp-float.in{pointer-events:auto;}
@media (min-width:981px){
  .wsp-float{width:70px; height:70px;}
}
/* WSP-FLOAT mobile — distancia al borde inferior/lateral reducida a la mitad (56px→28px, ambos
   ejes) respecto a la regla base (pensada para pantallas más grandes). Tamaño sin cambios (56px en
   mobile ya es el valor base — el +25% a 70px es exclusivo de desktop, min-width:981px, no se toca
   acá). Este bloque tiene que venir DESPUÉS de la regla base .wsp-float (arriba) para ganarle en el
   cascade — misma especificidad en ambos casos, así que el orden de aparición en el archivo decide.
   No existe en Congreso ningún mecanismo de "near-footer-brand" que ocultara este botón cerca del
   footer (se buscó explícitamente, ver grep — esa lógica solo existió en Mosconi, y ya se sacó de
   ahí a pedido del cliente), así que no hay nada que este cambio pueda romper en ese sentido; el
   único ajuste real para que no choque con el logo del footer en mobile es el margin-bottom
   negativo de .footer-brand (ver esa sección más abajo), que no depende de la posición de
   .wsp-float y sigue funcionando igual. */
@media (max-width:640px){
  .wsp-float{bottom:28px; right:28px;}
}

.reveal{opacity:0; transform:translateY(36px); transition:opacity .9s ease, transform .9s ease;}
.reveal.in{opacity:1; transform:translateY(0);}

@media (max-width:980px){
  .nav-links{display:none;}
  .nav-hamburger{display:flex; align-items:center; justify-content:center;}
  .intro-grid, .split, .ubicacion, .footer-top{grid-template-columns:1fr; gap:40px;}
  /* .ubicacion, no: mapa y copy tienen que quedar pegados (como en desktop, sin gap entre columnas)
     — el gap:40px compartido de arriba dejaba una franja del --stone de fondo de la página entre
     el mapa (claro) y .ubicacion-copy (oscuro), que se veía como una franja blanca suelta debajo
     del mapa, ni parte del mapa ni de ningún fondo real. Encontrado al revisar el recorte del mapa
     en un viewport mobile real (no estaba relacionado al recorte en sí, es un gap compartido con
     .intro-grid/.split/.footer-top que nunca debió aplicarse acá). */
  .ubicacion{gap:0;}
  .split.reverse .split-img, .split.reverse .split-copy{order:0;}
  .split-copy{padding:60px 6%;}
  .unit-panel{grid-template-columns:1fr;}
  .galeria-prev{left:8px;}
  .galeria-next{right:8px;}
  .ubicacion-copy{padding:70px 6%;}
  section{padding:90px 6%;}

  /* Footer en mobile: la fila social + el link "JL Mosconi" (agrandados arriba) ya no entran
     en una sola línea junto al logo sin desbordar el viewport ni pisar el botón de WhatsApp
     fijo (que cae justo en esa esquina) — se pasa a columna, cada bloque en su propia fila,
     y el social-row puede wrappear sus propios links si hiciera falta. */
  .footer-bottom{flex-direction:column; align-items:flex-start; gap:28px;}
  /* WhatsApp/Instagram/LinkedIn en mobile — REESCRITO otra vez en una sesión posterior: el intento
     anterior (tracking normal + `gap:35.6655px` fijo, calculado contra los 320.41px medidos a
     375px) asumía que `.footer-form input` medía siempre eso — pero ese ancho es FLUIDO (escala
     con el viewport, sin `max-width` que lo fije, a diferencia de escritorio con
     `.footer-top{max-width:1180px}`), así que en un celular real que no mide 375px exactos el gap
     fijo dejaba el renglón desalineado: confirmado a mano en dos anchos de prueba (360px → input
     306.91px pero fila fija en 320.39px, SE PASA por 13px; 414px → input 355.5px, fila fija sigue
     en 320.39px, QUEDA CORTA por 35px) — el signo del desfasaje cambia según el ancho, prueba de
     que el problema es el valor fijo en sí, no un cálculo puntual mal hecho.
     **Fix real: que .social-row tome el ancho real del contenedor en vez de un px calculado.**
     `.footer-bottom` (padre directo de `.social-row`) resulta ser el contenedor correcto — no por
     nesting directo con `.footer-form` (son ramas separadas: `.footer-top > .footer-form` vs
     `.footer-bottom`, ambos hijos de `<footer>`), sino porque `.footer-top` Y `.footer-bottom`
     tienen CADA UNO su propio `max-width:1180px; margin:0 auto` sobre el mismo `<footer>` con el
     mismo padding — así que renderizan siempre al mismo ancho exacto, y `.footer-form` a su vez
     ocupa el 100% de `.footer-top` en este breakpoint (`grid-template-columns:1fr`, columna
     única). Verificado en el navegador midiendo los 3 en simultáneo a 360px Y a 414px:
     `.footer-top`/`.footer-form`/`.footer-bottom` dan exactamente el mismo ancho entre sí en
     ambos casos (`306.91px` y `355.5px` respectivamente) — confirma que `.footer-bottom` es una
     referencia válida para cualquier viewport, no solo para el que se testeó.
     Con `.social-row{width:100%}` (100% de `.footer-bottom`) + `justify-content:space-between` en
     vez de un `gap` fijo, el espacio entre las 3 palabras se recalcula solo en cada ancho — sin
     JS, sin recalcular nada a mano. `flex-wrap:nowrap` se mantiene (sigue siendo lo que garantiza
     que nunca se apilen, tracking de cada palabra sigue en `0.06em` normal, sin cambios ahí). */
  .social-row{width:100%; justify-content:space-between; flex-wrap:nowrap;}
  .social-row a[href*="wa.me"], .social-row a[href*="instagram.com"], .social-row a[href*="linkedin.com"]{
    letter-spacing:0.06em; white-space:nowrap;
  }
  /* Más aire entre el contenido y el borde inferior de la pantalla. */
  footer{padding-bottom:110px;}
  /* Sin mouse no hay :hover — se fuerza la versión color del logo y el blanco del cross-link
     (mismo criterio que Mosconi) en vez de depender de un estado que en mobile no se dispara
     de forma confiable entre dispositivos. */
  .footer-brand-default{opacity:0; transition:none;}
  .footer-brand-hover{opacity:1; transition:none;}
  .cross-link a{color:var(--stone); transition:none;}
  /* El logo a 49.5px (tamaño desktop) es más ancho que el hueco entre el margen izquierdo y el
     botón de WhatsApp fijo — se achica en mobile para que entre al lado sin superponerse.
     ⚠ margin-top NO sirve para bajarlo: al ser el último elemento del footer, su borde
     inferior queda siempre a exactamente footer{padding-bottom} del final real de la página
     (que coincide con el borde inferior del viewport cuando se llega al fondo) — cualquier
     margin-top que se le sume sólo alarga el documento por arriba (scrollea más, no se ve
     nada distinto una vez abajo del todo). El único lever que efectivamente lo acerca al
     borde inferior — y por lo tanto al botón de WhatsApp fijo — es un margin-bottom negativo,
     que reduce esa distancia real al final del documento. Valores calibrados a ojo contra el
     centro del botón (56px, fijo a bottom:56px), ver CLAUDE.md. */
  .footer-brand{height:32px; margin-bottom:-42px;}
  .cross-link{margin-bottom:20px;}
  /* Centrado horizontal del bloque "Descubrí también/JL Mosconi" + logo, solo mobile — .footer-bottom
     de acá arriba tiene align-items:flex-start (necesario para .social-row, que ya es width:100% y
     no se ve afectado por esto), así que .footer-brand-col (que internamente ya centra cross-link y
     logo entre sí, ver regla base) queda pegada a la izquierda como caja — align-self la centra a
     ELLA en el eje transversal (horizontal) sin tocar su alto/ancho ni la posición vertical (el
     orden en el documento no cambia), ni ningún otro elemento del footer. */
  .footer-brand-col{align-self:center;}
}

/* UNIDADES mobile — rediseño completo, replicado del mismo rediseño ya hecho en Mosconi (mismo
   template), adaptado a que acá son 4 unidades en vez de 8. @media(max-width:640px) NUEVO y aparte
   (no agregado al bloque de 640px de más arriba, el de lightbox/hero) a propósito: tiene que venir
   DESPUÉS del @media(max-width:980px) de arriba en el archivo para ganarle por orden de aparición a
   .unit-panel{grid-template-columns:1fr} (misma especificidad — si este bloque quedara antes que el
   de 980px, esa regla de acá perdería el cascade).
   Objetivo: que la sección quepa en una pantalla sin scrollear, replicando la experiencia desktop
   (todo visible de una) en vez del layout apilado en 1 columna que imponía el breakpoint de 980px
   (pensado para tablet, no para esto). */
@media (max-width:640px){
  /* Título de sección — menos aire arriba/abajo/entre el título y el panel. */
  .unidades{padding-top:40px; padding-bottom:40px;}
  .unidades-head{margin-bottom:20px;}

  /* .unit-panel vuelve a 2 columnas (plano | botones) en vez del apilado que imponía el breakpoint
     de 980px — con grid-template-areas para que .unit-details (el bloque de texto) quede en su
     propia fila ABAJO ocupando todo el ancho, no comprimido junto a los botones.
     La columna de botones es un ancho FIJO (53.82px = 46.8px +15%, pedido explícito para agrandar los
     botones tras la reducción anterior de -40%) — con 4 unidades (no 8 como en Mosconi) hay bastante
     menos altura total de botones, así que conviene un ancho fijo con aspect-ratio:1 en el botón (ver
     abajo) para que cada uno sea un cuadrado real en vez de aproximarlo con padding/font-size. Como la
     columna del plano es 1fr, todo el ancho que resta tras agrandar la columna de botones lo absorbe
     automáticamente el plano, sin tocar nada más acá (el max-height del plano sí se recalculó, ver
     más abajo, para que siga usando ese ancho al 100% en vez de quedar limitado por altura).
     .unit-info pasa a display:contents: "aplana" ese wrapper para que sus dos hijos directos
     (.floor-select y .unit-details) puedan ser items del grid de .unit-panel cada uno con su propio
     grid-area, sin tener que tocar el HTML. */
  .unit-panel{
    display:grid;
    grid-template-columns:1fr 53.82px;
    grid-template-areas:"plano botones" "detalles detalles";
    column-gap:12px; row-gap:20px;
  }
  /* min-height:auto — la regla base (.unit-visual{min-height:700px}, pensada para la columna angosta
     de desktop) forzaba a 700px la fila entera "plano" del grid de acá abajo (el grid item más alto
     de la fila define su altura), dejando un hueco enorme entre el plano/botones (~330-340px reales)
     y el resto de la sección. Con min-height:auto la fila se ajusta al contenido real. */
  .unit-visual{grid-area:plano; min-height:auto;}
  .unit-info{display:contents;}
  /* align-content:start — sin esto, con solo 4 botones (mucha menos altura total que el plano de al
     lado), el grid estiraría las filas auto-sizadas para llenar el alto disponible y los cuadrados
     dejarían de ser cuadrados (se agrandarían verticalmente). Con start quedan pegados arriba, del
     mismo alto entre sí, y el sobrante de altura queda como aire debajo del último botón.
     padding-top:63.82px (= un "slot" completo: 53.82px del botón +10px de gap) — pedido explícito:
     el primer botón tiene que quedar a la altura donde antes estaba el 2do, corriendo los 4 hacia
     abajo un slot completo en vez de arrancar pegados al borde superior del plano. */
  .unit-info .floor-select{grid-area:botones; grid-template-columns:1fr; gap:10px; margin-bottom:0; align-content:start; padding-top:63.82px;}
  .unit-details{grid-area:detalles;}
  /* botón de unidad — cuadrado real (aspect-ratio:1 sobre el ancho fijo de la columna, 78px), con
     el texto en 2 líneas fijas: "Unidad" arriba (label) y el número/rango centrado abajo (num) — ver
     spans .unit-btn-full/.unit-btn-label/.unit-btn-num en el HTML y su regla base (oculta label/num,
     regla justo arriba de este bloque en el archivo). Acá se invierte: se oculta full y se muestran
     label/num, en 2 líneas (flex-direction:column). */
  .floor-select button{
    display:flex; flex-direction:column; align-items:center; justify-content:center; gap:2.3px;
    aspect-ratio:1; width:100%; padding:3.45px; font-size:10.35px;
  }
  .unit-btn-full{display:none;}
  .unit-btn-label, .unit-btn-num{display:block;}
  .unit-btn-label{opacity:0.75;}
  /* white-space:nowrap — el rango ("101-701") no tiene que cortarse en el guion aunque el botón sea
     angosto: sin espacios alrededor del guion, a este font-size entra cómodo en las 4 columnas de
     78px (confirmado en el navegador). */
  .unit-btn-num{font-weight:500; white-space:nowrap;}

  /* Botón "Dimensiones" afuera — se reemplaza por tap directo sobre el plano (planoImg también
     escucha 'click' con el mismo toggleDimensiones() que usa este botón, ver main.js). */
  .dim-toggle{display:none;}
  /* max-height subido de 340px a 380px: con la columna de botones más angosta (46.8px, ver arriba),
     el ancho disponible para el plano creció (de 240px a ~271px), pero max-width:100% (regla base)
     por sí solo no lo aprovechaba porque max-height:340px seguía siendo la restricción que ataba —
     a la proporción real del plano (1568×2144), 340px de alto sólo permite ~249px de ancho, menos
     que el espacio ya disponible. 380px alcanza (a esa proporción, un ancho de 271px pide ~371px de
     alto) para que el ancho vuelva a ser la restricción real y el plano ocupe todo el espacio que se
     liberó al achicar los botones, en vez de quedar más chico de lo que el layout ya permite. */
  .unit-visual img{cursor:pointer; max-height:380px;}

  /* unit-details compacto — menos margen/padding entre título, subtítulo, ambientes, specs y
     botones, para que el bloque completo entre en pantalla junto con el plano y los botones de
     arriba. Tamaños de letra más chicos pero todavía legibles, no se aplana la jerarquía (el título
     sigue siendo claramente el elemento más grande del bloque). */
  .unit-info h3{font-size:21px; margin-bottom:2px;}
  .unit-subtitle{font-size:10.5px;}
  .unit-espacios{font-size:12.5px; line-height:1.45; margin-top:10px; max-width:none;}
  .unit-specs{gap:20px; margin:14px 0; padding-top:12px;}
  .unit-specs div .num{font-size:19px;}
  .unit-specs div .lbl{font-size:9.5px;}

  /* Ficha PDF / Brochure completo / 3D en una sola fila — .btn-row ya era display:flex, lo que
     faltaba entrar lado a lado era espacio: con el padding/font-size de la regla base los 3 botones
     no entraban en el ancho de mobile y flex-wrap los mandaba a varias filas. flex:1 en vez de ancho
     intrínseco (se reparten el ancho disponible en partes iguales) + padding/font-size más chicos +
     white-space:nowrap (para que el texto de CADA botón no rompa a una 2da línea). */
  .btn-row{flex-wrap:nowrap; gap:8px; margin-top:10px;}
  .unidades .btn-row .btn{flex:1 1 0; padding:10px 6px; font-size:10px; letter-spacing:0.02em; justify-content:center; white-space:nowrap;}
}

/* SPLIT ("El living" / "Los dormitorios") — SOLO mobile real (≤640px), sesión posterior a pedido
   explícito: no tocar tablet (641-980px, que sigue con el layout apilado de 1 columna del
   @media(max-width:980px) de arriba, min-height:92vh y padding:60px 6% sin cambios). Este bloque
   tiene que ir DESPUÉS de ese @media(max-width:980px) en el archivo para ganarle por orden de
   aparición al `.split-copy{padding:60px 6%}` de ahí — misma especificidad, mismo gotcha ya
   documentado arriba para el bloque de Unidades.
   Investigación antes de tocar nada: se aisló el efecto real de min-height:92vh sacándolo
   temporalmente (en un iframe de 375×808 real) — el contenido SIN min-height ya mide 698.83px,
   apenas 44.5px menos que con el mínimo forzado (743.36px = 92vh exacto). Osea que min-height NO
   estaba generando el "espacio muerto" por sí solo — el contenido (imagen + texto) ya ocupa casi
   toda esa altura. Bajar min-height solo (probado en el navegador a 59.8vh = 65% de 92vh) no
   achica nada por debajo de esos 698.83px naturales, porque el contenido ya excede ese piso.
   Para acercarse de verdad al 65% pedido (≈483px de un total de 743px) haría falta achicar también
   la imagen (259px hoy) — **confirmado explícitamente con el usuario que la imagen NO se toca**,
   solo se compacta el espaciado interno del texto. Con eso, el resultado queda por encima del 65%
   original (imagen sin cambios + texto compactado al mínimo razonable), no en el número exacto —
   aceptado a propósito, ver conversación.
   `min-height` igual se ajusta a 59.8vh (65% de 92vh, el mismo cálculo pedido) porque en viewports
   más altos que este de prueba SÍ puede volver a ser la restricción activa (en un mobile más alto,
   92vh podría superar al contenido compactado y forzar espacio vacío de nuevo) — bajarlo evita
   reintroducir ese mismo problema en otros altos de pantalla, aunque acá (375×808) no sea el valor
   que termina mandando. */
@media (max-width:640px){
  .split{min-height:59.8vh;}
  .split-copy{padding:36px 6%;}
  .split-copy .eyebrow{margin-bottom:12px;}
  .split-copy h2{margin-bottom:14px;}
  .split-copy .stat-row{margin-top:20px;}
}
