: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(245,242,237,0.3);
  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.3);
  box-shadow:0 8px 24px rgba(33,31,27,0.08);
}
.brand{font-family:'Newsreader',serif; font-size:17px; letter-spacing:0.03em; color:#000; font-weight:700; text-transform:uppercase;}
.nav-links{display:flex; gap:36px; font-size:12px; letter-spacing:0.04em; text-transform:uppercase;}
.nav-links a{position:relative; padding-bottom:4px; opacity:0.82; transition:opacity .3s; color:#000; font-weight:400;} /* regular, no bold — Roboto solo tiene 300/400/500/700 cargados */
.nav-links a:hover{opacity:1;}
.nav-cta{
  border:1px solid var(--ink); padding:6px 18px; font-size:11px;
  letter-spacing:0.08em; text-transform:uppercase; border-radius:2px;
  transition:background .3s,color .3s; color:#000; font-weight:700;
}
.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:#000; cursor:pointer;
}

/* drawer full-screen del menú mobile — 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) — mismo diagnóstico ya confirmado en JL Congreso (mismo
   template): .hero es un <section> y hereda section{padding:150px 5%}, un padding-bottom FIJO en
   px que no se achica con el alto real del viewport. Como .hero-content es el único hijo en flujo
   de .hero (display:flex; align-items:flex-end — el resto, .hero-meta / .hero-scroll-text /
   .hero-scroll-arrow / .hero-logo, son position:absolute), queda anclado por su borde inferior a
   (alto de .hero − ese padding), y en viewports bajos ese padding fijo deja muy poco o negativo
   margen entre el contenido y el nav (fixed, por encima). Medido en el navegador (ancho 1400px,
   ANTES del fix): gap eyebrow-top − nav-bottom daba -89px a 636px de alto real, -34px a 700px,
   +28px a 768px, +315px a 1080px — coincide (a menos de 1px) con lo que Congreso midió con su
   propio contenido, confirmando que la geometría del hero es la misma entre los dos proyectos
   (mismo min-height:640px, mismo padding compuesto, mismo font-size del h1 vía clamp(vw)). */
@media (min-width:981px){
  .hero{ padding-bottom: clamp(24px, calc(100vh - 630px), 150px); }
  /* Efecto colateral, medido en el navegador antes de dar el fix de arriba por terminado (no solo
     teórico): al reducirse el padding-bottom de .hero, TODO .hero-content (bloque rígido
     bottom-anchored) se corre hacia abajo — acerca el párrafo (.hero-sub, el último elemento en
     flujo) a .hero-scroll-text/.hero-scroll-arrow, que son position:absolute con un bottom fijo
     INDEPENDIENTE del padding de .hero (no se mueven). Medido: gap párrafo↔scroll-cue = 116px
     SIEMPRE antes del fix de arriba (constante, porque el padding-bottom no cambiaba con la
     altura) — con SOLO ese fix (sin este margin) pasa a depender linealmente del nuevo
     padding-bottom (gap = padding-bottom − 34px, exacto en las 4 alturas medidas) — a 636-654px el
     padding-bottom toca su piso de 24px y el gap da -10px, overlap real del párrafo con el
     scroll-cue, no solo "pegado".
     Compensarlo con un margin-bottom fijo en .hero-sub NO alcanza solo: al inflar la altura total
     de .hero-content (mismo bloque rígido bottom-anchored), empuja el TÍTULO hacia arriba
     exactamente lo mismo que el margin agregado — cancela 1:1 el fix de arriba (confirmado por
     álgebra antes de tocar el CSS: gapTop_nuevo = gapTop_sinMargin − M, gapBottom_nuevo =
     gapBottom_sinMargin + M, la suma de los dos gaps es invariante = 26.97px en el punto más bajo
     — no hay margin que deje a los dos con aire de sobra ahí, es un techo matemático del layout,
     no un problema de calibración). La fórmula de acá reparte ese margen disponible entre los dos
     gaps en vez de resolver uno a costa del otro: clamp(0px, calc(678px − 100vh), 24px) replica la
     MISMA forma que el clamp de arriba (plano en 24px mientras el padding-bottom está en su piso,
     con una rampa lineal que lo lleva a 0 exactamente donde el padding-bottom de arriba llega a
     34px — el punto en que ya no hace falta compensar nada) — un coeficiente distinto (probado
     primero, descartado) generaba un valle en la zona de transición (636-664px) porque su rampa no
     coincidía con la del padding-bottom real.
     Verificado en el navegador, las 4 alturas pedidas + la zona de transición: gap
     párrafo↔scroll-cue queda en un piso constante de 14px entre 636-678px (antes: -10px de overlap
     real en el piso, mismo mecanismo que el gap superior), sin ningún valle en el medio, y sin
     ningún cambio desde 700px hacia arriba (0px de margin ahí, gap-inferior = 36px a 700px, 104px
     a 768px, 116px a 1080px — igual que antes de agregar este margin). El gap superior baja de
     ~+37px a ~+13px en el extremo más bajo (636-650px) por este mismo reparto, pero se mantiene
     siempre positivo (mínimo medido +12.66px a 640px, el piso real por el min-height de arriba) —
     sin overlap con el nav en ningún punto probado. Confirmado con captura de pantalla en 636,
     700, 768 y 1080px: sin solapamiento arriba, sin superposición abajo, 1080px pixel-idéntico al
     estado previo al fix (padding-bottom se mantiene en 150px fijo desde vh=780 hacia arriba, este
     margin da 0px desde vh=678 — ninguno de los dos toca nada por encima de esos umbrales). */
  .hero-sub{ margin-bottom: clamp(0px, calc(678px - 100vh), 24px); }
}
.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:var(--amber); margin-bottom:18px; display:block; /* antes #E7B98A (más claro) — ahora el ámbar del sitio, más oscuro/saturado/marrón */
}
.hero h1{
  font-size:clamp(48px,9vw,120px); line-height:0.98; font-weight:300;
  max-width:900px; color:#fff; /* blanco puro — antes heredaba #F5F2ED de .hero-content */
}
.hero h1 em{font-style:italic; color:#E7B98A; font-weight:300;} /* "barrio." — mismo color que tenía antes de var(--amber), que es el mismo que usa la animación .scroll-cue; el punto quedó adentro del <em> en el HTML para heredar este color también */
.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;}
/* left:9.5% (no 5%) para calzar con el borde izquierdo real del h1: .hero es un <section> y hereda
   el section{padding:150px 5%} genérico ADEMÁS de su propio .hero-content{padding:0 5% 8vh} — dos
   padding-left del 5% en cadena (uno sobre el ancho completo, el otro sobre lo que queda después del
   primero) dan un inset compuesto de 9.5% del ancho del hero, no 5% — confirmado midiendo el h1 real
   en el navegador. Ambos elementos son position:absolute, así que no heredan ese padding compuesto
   solos — el texto necesita el 9.5% explícito para terminar en el mismo punto que el título. */
/* bottom:calc(8vh + 15px) — el borde inferior queda a la misma altura real que el borde inferior de
   los números "9 / 1-2 / 78m²" de .hero-meta: esa caja está en bottom:8vh, pero el número (.num) no
   toca ese borde — abajo suyo hay 15px más (13px de alto de .lbl + 2px de margin-top) hasta el borde
   real del contenedor. calc(8vh + 15px), no un px fijo medido una sola vez, para que el calce siga
   siendo exacto a cualquier alto de ventana (8vh escala, los 15px no porque están en unidades fijas). */
.hero-scroll-text{
  position:absolute; bottom:calc(8vh + 15px); left:9.5%; z-index:3; color:#F5F2ED;
  font-size:15.18px; 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;}
/* flecha: no al lado del texto sino centrada horizontalmente en la página, al mismo bottom que el
   texto de arriba (misma altura que los números de .hero-meta). Color = el mismo que usa "barrio."
   (.hero h1 em, #E7B98A). Solo la punta (el "palito" vertical se sacó del SVG, ver markup) — el
   ícono quedó 60% más grande respecto al tamaño 1:1 de su propio viewBox (20×12 → 32×19). Hover:
   mismo criterio que el texto (más "peso" visual), acá vía stroke-width en vez de font-weight
   porque es un ícono, no texto. */
.hero-scroll-arrow{
  position:absolute; bottom:calc(8vh + 15px); left:50%; transform:translateX(-50%); z-index:3;
  display:flex; color:#E7B98A; cursor:pointer;
}
/* Animación "latido" (scale 1↔1.25 en loop) — originalmente solo mobile, ahora también en desktop
   a pedido del cliente. Definida acá en scope global (no en el media query) para que un solo
   @keyframes sirva a los dos breakpoints sin duplicar código; el media query mobile de más abajo
   ya no repite ni el @keyframes ni el animation, los hereda de esta regla base. Anima el <svg>
   interno, no el <span> contenedor, para no pisar el transform:translateX(-50%) que centra la
   flecha (mismo motivo documentado originalmente para mobile). No interfiere con el fade a
   opacity:0 de GSAP (propiedad distinta) ni con el hover de stroke-width de abajo. */
@keyframes heroArrowPulse{
  0%, 100%{transform:scale(1);}
  50%{transform:scale(1.25);}
}
.hero-scroll-arrow svg{transition:stroke-width .2s ease; animation:heroArrowPulse 1.6s ease-in-out infinite;}
.hero-scroll-arrow:hover svg{stroke-width:2.6;}
.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-placeholder.jpg') center/cover no-repeat;
  filter:blur(14px);
  transform:scale(1.06); /* evita bordes transparentes que el blur deja al escalar */
  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;}
/* grid fijo de 4 columnas (no flex-wrap) para que las 8 unidades siempre queden en 2 filas de 4 —
   101-301/102-302/103-303/401-701 arriba, 402-702/801/802/901 abajo — sin importar el ancho exacto
   de cada label, que con flex-wrap dependía del espacio disponible y podía romper el orden. */
.unit-info .floor-select{display:grid; grid-template-columns:repeat(4, 1fr); gap:10px; margin-bottom:52px;}
/* botón de unidad: 2 líneas fijas, "Unidad" arriba y el número/rango centrado abajo — mismo
   patrón para las 8, en vez del texto corrido de antes ("Unidad 101 - 301" vs "Unidad 801") que
   quedaba con formato inconsistente entre unidades simples y con rango. */
.floor-select button{
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:3px;
  padding:10px 12px; 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;
  min-width:0; text-align:center;
}
.unit-btn-label{opacity:0.75;}
/* white-space:nowrap — el rango ("101-301") no tiene que cortarse en el guion aunque el botón
   se achique: sin espacios alrededor del guion y en una sola línea, a los 12px/letter-spacing de
   acá entra cómodo en las 4 columnas del grid en 375-428px (confirmado en el navegador); si algún
   día un rango más largo no entrara, quedaría desbordando el botón antes que partirse en dos
   líneas — no hay overflow:hidden acá a propósito, para que un desborde futuro sea visible/obvio
   en vez de recortarse en silencio. */
.unit-btn-num{font-weight:500; white-space:nowrap;}
.floor-select button.active{background:var(--amber-deep); color:#fff; border-color:var(--amber-deep);}
.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;
  /* Ficha PDF/Brochure son <a>, sin chrome nativo que resetear — btn3D es un <button> real (acción
     de JS, no un link), así que hace falta anular el estilo nativo de <button> acá para que los
     tres queden visualmente idénticos. */
  background:none; border:none; font-family:inherit; appearance:none; -webkit-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);}
.btn-disabled{opacity:0.45; pointer-events:none;}

/* UNIDADES — visor 3D: overlay + modal "vidrio líquido" con canvas (secuencia de frames WebP) y
   barra de scrubbing. Armado en JS (mismo patrón que .lightbox-overlay de Galería más abajo:
   un único overlay creado una vez y agregado al final del body) — se agrupa acá porque pertenece
   conceptualmente a Unidades, no a Galería. */
.unit3d-overlay{
  position:fixed; inset:0; z-index:1000;
  background:rgba(20,18,14,0.55);
  /* "Liquid Glass": blur+saturate sobre lo que queda atrás del popup (el resto de la página,
     oscurecida por el rgba de arriba). Ya no hay una segunda tarjeta clara encima (.unit3d-modal
     no lleva fondo propio, ver más abajo) — el efecto vive solo acá, en el overlay. */
  backdrop-filter:blur(18px) saturate(140%); -webkit-backdrop-filter:blur(18px) saturate(140%);
  display:flex; align-items:center; justify-content:center; padding:5%;
  opacity:0; visibility:hidden; transition:opacity .3s ease;
}
.unit3d-overlay.active{opacity:1; visibility:visible;}
/* .unit3d-modal ya no es una "tarjeta" — sin background/border/box-shadow/backdrop-filter propios,
   queda flotando directo sobre el Liquid Glass oscuro del overlay (pedido explícito: sacar el
   panel claro). Solo aporta el ancho máximo y el padding que separa título/canvas/scrubber de los
   bordes del popup. */
.unit3d-modal{
  position:relative; width:min(90vw, 760px);
  padding:32px 32px 28px;
}
.unit3d-title{
  font-family:'Newsreader',serif; font-weight:400; font-size:clamp(20px,2.4vw,26px);
  color:var(--stone); text-align:center; margin-bottom:18px;
  /* fondo ahora oscuro/variable (Liquid Glass sobre contenido de la página) en vez del panel claro
     fijo de antes — la sombra da contraste consistente sin depender de qué haya detrás. */
  text-shadow:0 2px 12px rgba(0,0,0,0.5);
}
.unit3d-canvas-wrap{
  position:relative; width:100%; background:var(--ink); overflow:hidden;
  aspect-ratio:16/9; /* placeholder hasta conocer el aspect real del primer frame — ver main.js,
                         se corrige a la medida real ni bien carga (evita distorsión) */
}
.unit3d-canvas-wrap canvas{display:block; width:100%; height:100%;}
.unit3d-loading{
  position:absolute; inset:0; z-index:2;
  display:flex; align-items:center; justify-content:center;
  background:rgba(33,31,27,0.55); color:var(--stone);
  font-size:12px; letter-spacing:0.08em; text-transform:uppercase;
  opacity:0; visibility:hidden; transition:opacity .25s ease;
}
.unit3d-loading.active{opacity:1; visibility:visible;}
/* .unit3d-scrubber es el área interactiva real (pointerdown/move/up van acá, ver main.js) — el
   padding le da un alto táctil de 20px, bastante más generoso que los 4px visuales de .unit3d-track,
   a propósito: un click/tap que caiga en ese padding (muy fácil con mouse, casi inevitable con el
   dedo) tiene que seguir arrancando el drag. Antes los listeners estaban en .unit3d-track directo
   (4px de alto real) y cualquier click un poco arriba/abajo de esa línea no llegaba a ningún
   listener — bug encontrado y corregido acá. */
.unit3d-scrubber{
  margin-top:24px; /* separado del canvas, no pegado */
  padding:8px 2px;
  cursor:grab; touch-action:none;
}
.unit3d-scrubber.dragging{cursor:grabbing;}
.unit3d-scrubber.disabled{pointer-events:none; opacity:0.5;}
.unit3d-track{
  position:relative; height:4px; border-radius:2px;
  background:rgba(90,86,77,0.22); /* --ink-soft a baja opacidad — "stone/ink-soft" pedido */
}
.unit3d-progress{
  position:absolute; top:0; left:0; height:100%; border-radius:2px;
  background:var(--amber); width:0%;
  pointer-events:none;
}
.unit3d-thumb{
  position:absolute; top:50%; left:0%; transform:translate(-50%,-50%);
  width:16px; height:16px; border-radius:50%;
  background:var(--amber); border:2px solid var(--stone);
  box-shadow:0 2px 6px rgba(0,0,0,0.3);
  pointer-events:none;
}
.unit3d-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;
}
.unit3d-close:hover{background:var(--amber-deep);}
body.unit3d-open{overflow:hidden;} /* bloquea scroll del body mientras el modal está abierto —
                                       mismo mecanismo que body.nav-drawer-open del menú mobile */
@media (max-width:640px){
  .unit3d-modal{padding:22px 18px 20px;}
  .unit3d-close{top:14px; right:14px;}
}

/* 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 */
#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, mismo --ink que tenía la sección originalmente, antes de todas las iteraciones de
   este layout — no --charcoal) 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. */
.galeria{background:var(--ink); padding:0; overflow:hidden;}
.galeria .eyebrow{color:#E7B98A;}
.galeria h2{color:var(--stone);}
.galeria-strip{position:relative; background:var(--ink); overflow:hidden;}
.galeria-strip-top{
  min-height:279px; /* 186px + 50% */
  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 (~477px) 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" (`#invertir .narrow`, con el padding:150px 5% default de section + su propio
   centrado), no un padding inventado nuevo. */
.galeria-strip-top .narrow{width:100%; margin:0 auto;}
.galeria-wrap{
  position:relative;
  width:100vw; margin-left:calc(50% - 50vw); margin-right:calc(50% - 50vw);
}
.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{
  /* 75% del alto que tenía antes (clamp(260px,36vw,515px)) */
  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 — .hero-meta / .hero-scroll-text / .hero-scroll-arrow son position:absolute con
     posiciones en % pensadas para un hero ancho (desktop). El layout de 2 filas apiladas que hubo
     acá antes (stats arriba, "Descubrí el proyecto"+flecha abajo, para resolver un overlap entre
     las tres cajas por debajo de ~500-560px) se simplificó: ahora .hero-content se centra
     verticalmente en el hero, se sacan las stats y el texto del scroll-cue, y queda solo la flecha
     — pulsando — en la altura donde antes estaban las stats. Reusa el breakpoint 640px que ya
     existía acá para el lightbox, no se agrega uno nuevo. */

  /* 1) Bloque de texto (eyebrow + h1 + copy) centrado verticalmente en el hero — antes quedaba
     pegado abajo (align-items:flex-end en la regla base de .hero, pensada para dejar lugar a las
     stats/scroll-cue debajo). padding-bottom:0 acá además: con align-items:center, el padding-
     bottom:8vh de .hero-content (asimétrico respecto al padding-top:0) corría el centrado visual
     del texto varios px hacia arriba del centro real del viewport — sacándolo, el centrado queda
     exacto. */
  .hero{align-items:center;}
  .hero-content{padding-bottom:0;}
  /* .hero-scrim mobile — el degradé base (rgba(20,18,14,0.15) 0% → 0.08 35% → 0.78 100%) está
     calibrado para un hero con el texto pegado ABAJO (donde el degradé ya es bien oscuro, 0.78).
     Con .hero-content centrado, .hero-content mide de 33.9% a 66.1% del alto del hero (medido en
     el navegador) — justo la zona MÁS CLARA del degradé base (~0.08-0.24 interpolado ahí). No es
     un problema de opacity/alfa en el texto en sí: se probó el mismo texto en negro sólido
     (color + -webkit-text-fill-color) y se ve perfecto, sin ningún sangrado del fondo — es que ahí
     el fondo (la fachada del edificio, un patrón claro y con mucho detalle) no está lo bastante
     oscurecido como para dar contraste contra colores claros/medios como el ámbar o el blanco. Acá
     se oscurece un tramo ancho del degradé (0.68 ya al 30%, sostenido en ~0.7 hasta el 68%, un
     poco antes de que arranque el texto y hasta un poco después de que termina) en vez de un solo
     punto medio, para que TODO el bloque de texto (no solo su centro exacto) tenga contraste
     parejo — sin tocar la regla base (afecta solo mobile, desktop sigue con el degradé original
     de 3 paradas). */
  .hero-scrim{background:linear-gradient(180deg, rgba(20,18,14,0.22) 0%, rgba(20,18,14,0.68) 30%, rgba(18,16,12,0.72) 68%, rgba(18,16,12,0.85) 100%);}

  /* 2) "Villa Pueyrredón — Av. Mosconi" al mismo color que "barrio." (.hero h1 em, #E7B98A — no
     var(--amber), que es un tono más oscuro/marrón) y de light (300, heredado) a regular (400).
     letter-spacing:0.16em (ya estaba, ver historial): a 375px, con el 0.28em de la regla base
     (pensado para una línea ancha de desktop) el texto no entraba y cortaba justo antes de
     "MOSCONI" — este ajuste es independiente del color/peso de acá, se mantiene igual.
     font-weight subido un escalón más acá (400→500, ver bloque de pesos más abajo): quedó
     "regular" en el fix anterior, este pedido es "el que ya esté en regular pasa al siguiente
     peso disponible" — 500 (Roboto medium) está cargado (ver <link> de Google Fonts en el head). */
  .hero-eyebrow{letter-spacing:0.16em; color:#E7B98A; font-weight:500;}

  /* Opacidad y grosor de TODOS los textos del hero en mobile — un escalón más de peso cada uno,
     manteniendo la jerarquía relativa (el que estaba en light pasa a regular, nunca se aplanan
     todos al mismo peso), y ninguno queda con opacity/alfa menor a 1.
     - .hero-sub tenía color:rgba(245,242,237,0.82) — alfa <1, texto no del todo opaco. Pasa al
       mismo color pero con alfa 1 (#F5F2ED, el mismo que usa .hero-content). No interfiere con el
       fade a opacity:0 de .hero-content durante el pin del hero: GSAP anima la opacity del
       CONTENEDOR completo (heroPinTl.to('.hero-content', {opacity:0,...})), no la de este elemento
       en particular — sacarle su propia semitransparencia solo cambia qué tan nítido se ve ANTES
       de que arranque ese fade, no toca la animación en sí (confirmado corriendo el timeline a
       mano vía ScrollTrigger, ver notas de verificación).
     - .hero h1 pasa de light (300, regla base) a regular (400) acá.
     - .hero h1 em ("barrio.") también pasa de 300 a 400 — con una salvedad: Newsreader itálica
       tiene UN SOLO peso cargado en este sitio (400, ver <link> del head), así que aunque la regla
       base dijera font-weight:300, el navegador ya venía renderizando "barrio." con la única cara
       itálica disponible (400) por fallback de fuentes (confirmado con document.fonts: cero caras
       itálicas de otro peso "loaded"). Este cambio deja la declaración CSS honesta/consistente con
       lo que ya se estaba viendo, no es un cambio visual nuevo en sí — pero corresponde igual
       porque "el que esté en light pasa a regular" ya se cumplía de hecho acá antes de tocar nada.
     - .hero-scroll-text queda con opacity:1 (antes 0.75, la "opacity separada del resto" que ya
       documentaba Funcionalidades implementadas) por prolijidad — sigue con display:none en
       mobile (fix anterior), así que este cambio no tiene efecto visible hoy, pero saca un rezago
       de opacity que podía confundir a futuro si el elemento se llega a mostrar de nuevo. No se le
       tocó el font-weight: al estar oculto, no es un "texto visible del hero" en este breakpoint
       hoy — si se reactiva en el futuro, conviene revisar su peso en ese momento.
     - .hero-meta .lbl ("PISOS"/"AMBIENTES"/"TERRAZA COMÚN", las etiquetas chicas bajo cada número)
       tenía opacity:0.7 propio en la regla base — mismo caso que .hero-scroll-text: .hero-meta
       está en display:none en mobile (fix anterior), así que hoy no se ve, pero se saca igual acá
       para que ninguna letra del hero quede con alfa <1 en el CSS, se muestre o no.
     - .hero-logo ("JL MOSCONI", el logo que aparece centrado durante el pin del hero) es la
       EXCEPCIÓN a propósito — se deja con su opacity:0 de la regla base sin tocar. No es texto
       "atenuado": es el estado de ARRANQUE de una animación (GSAP lo anima de 0 a 1 mientras el
       usuario scrollea y hace zoom-out del header, ver heroPinTl.fromTo('.hero-logo', {opacity:0,
       scale:0.9}, {opacity:1,...}) en main.js) — si se pisa ese opacity:0 acá, "JL MOSCONI" quedaría
       visible todo el tiempo desde la carga de la página, superpuesto al resto del contenido del
       hero, y en el mejor de los casos GSAP lo volvería a poner en 0 con un flash visible al
       arrancar el timeline. Este es exactamente el tipo de bug de opacity/reveal que se pidió
       explícitamente no reintroducir — acá no se toca. */
  .hero h1{font-weight:400;}
  .hero h1 em{font-weight:400;}
  .hero-sub{font-weight:400; color:#F5F2ED;}
  .hero-scroll-text{opacity:1;}
  .hero-meta .lbl{opacity:1;}

  /* 3) Stats (Pisos/Ambientes/Terraza común) — afuera en mobile. GSAP sigue targeteando
     .hero-meta en el timeline del pin (fade a opacity:0 en fase 1) sin ningún problema: un
     elemento display:none no se rompe porque algo le anime opacity, simplemente no se ve. */
  .hero-meta{display:none;}

  /* 5) "Descubrí el proyecto" — afuera en mobile, mismo criterio que las stats (GSAP le sigue
     animando opacity sin efecto visible, no hace falta sacarlo del timeline). */
  .hero-scroll-text{display:none;}

  /* 4) La flecha pasa a ocupar sola la altura donde estaban las stats (bottom:74px, mismo valor
     que tenía .hero-meta acá) — no la altura donde estaba antes ella misma junto al texto
     (bottom:22px, ese lugar queda vacío ahora que no hay texto ni stats). Vuelve a quedar
     centrada horizontalmente (left:50% + transform, como en desktop) en vez de pegada a la
     derecha — ese right:5% solo tenía sentido cuando compartía la fila con el texto a la
     izquierda. La animación "latido" (@keyframes heroArrowPulse) ya no se define acá — vive en la
     regla base .hero-scroll-arrow svg (scope global, fuera de este media query) y se hereda sin
     repetirla, desde que se extendió también a desktop. */
  .hero-scroll-arrow{left:50%; right:auto; bottom:74px; transform:translateX(-50%);}

  /* hero h1 — el piso del clamp (48px) combinado con el <br> fijo del HTML (que en desktop separa
     "Vivir en la ciudad" de "al ritmo del barrio." como una composición deliberada de 2 líneas)
     daba 4 líneas acá: "Vivir en la ciudad" ya no entra en una sola línea a 48px/375px, y el
     segundo tramo tampoco. Achicando el font-size a 36px (barrido en el navegador: es el mayor
     tamaño que sigue dando 2 líneas) y ocultando el <br> fijo (.hero-break, ver HTML) para que el
     texto completo wrapee solo, el resultado es "Vivir en la ciudad al" / "ritmo del barrio." — un
     corte natural en un buen lugar, no el forzado de antes. No se apunta a "2 líneas siempre" a
     rajatabla, es lo que dio este texto/ancho/tamaño en el navegador — si el copy cambia en el
     futuro convendría re-barrer el font-size en vez de asumir que 36px sigue siendo el óptimo. */
  .hero h1{font-size:36px;}
  .hero-break{display:none;}

  /* wsp-float mobile — antes se ocultaba cerca del final de la página (se superponía con el logo
     "JL Desarrollos" del footer). A pedido del cliente el botón queda visible siempre, incluso al
     fondo — se sacó la regla ".wsp-float.in.near-footer-brand{opacity:0;...}" y el toggle en
     main.js que la togglaba (updateWspFooterClearance). Ninguna otra propiedad del botón (bottom/
     right, tamaño, sombra, animación) se tocó. */

  /* botones de unidad — a 375px con la regla base (padding:10px 12px) quedan ~80×55px, justo en
     el piso recomendado de 44px de objetivo táctil. Más padding vertical (10→16px) para que se
     sientan más cómodos al tocar; el ancho no se toca (sigue siendo 1fr de 4 columnas, así que
     siempre entran los 4 por fila sin importar el padding) — solo crece el alto de cada fila. */
  .floor-select button{padding:16px 12px;}

  /* AMENITIES (Rooftop) mobile — el video de fondo (1440×1048, ~4:3 apaisado) con object-fit:cover
     dentro de una caja angosta y muy alta (min-height:80vh sobre un viewport mobile) recorta
     demasiado a los costados: se termina viendo solo una tira vertical muy zoomeada del video. Acá
     se fuerza la proporción visible del contenedor a 0.8 (ancho = 80% del alto) en vez de dejar que
     80vh decida el alto — bastante menos extrema que la que resultaba antes, así que el recorte
     lateral se nota mucho menos. min-height:340px acá reemplaza por completo (misma especificidad,
     más abajo en el archivo) al min-height:80vh de la regla base, que si no seguiría ganando por
     dar un piso más alto que el que resulta de aspect-ratio:0.8 en anchos mobile típicos — el valor
     nuevo es chico a propósito, solo como red de contención mínima si el contenido (eyebrow +
     título + copy) llegara a necesitar más lugar del que da 0.8 en un viewport muy angosto. */
  #amenities{aspect-ratio:0.8; min-height:340px;}
}

/* UBICACION */
.ubicacion{
  display:grid; grid-template-columns:1fr 1fr; align-items:stretch;
}
/* alto real calculado en JS a partir del aspect ratio de veredas mosconi.png (sizeUbicacionMapa() en
   main.js) — a propósito sin min-height:88vh acá: esa unidad viewport-relative + object-fit:cover
   en la imagen recortaba franjas arriba/abajo cada vez que el chrome del navegador le comía alto a
   la ventana (menos vh, mismo ancho → contenedor más ancho que la imagen). min-height:320px es solo
   un piso para el instante antes de que corra el JS (o si llegara a fallar), no compite con el alto
   que fija sizeUbicacionMapa() en uso normal. */
.ubicacion-map{position:relative; overflow:hidden; background:var(--charcoal); min-height:320px;}
.mapa-layer{position:absolute; inset:0; opacity:0; transition:opacity .3s ease;}
#mapa-veredas{opacity:1;} /* base fija y quieta, sin scroll-driven reveal — Mosconi no tiene capa "proyecto" separada como Congreso */
#mapa-calles.active{opacity:1;} /* prende junto con el resto de los overlays, ver setMapaActive() en main.js */

/* capas de highlight (marcador del terreno y los 3 POI) — 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" (sistema anterior, sacado; mismo
   criterio ya aplicado en JL Congreso). */
.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 +
   blur/scale porque fondo-placeholder.jpg no viene pre-blureado, ver sizeUbicacionCopyBg() en
   main.js) pero escoped a .ubicacion-copy en vez de a toda la sección — el mapa (.ubicacion-map,
   columna hermana dentro de .ubicacion) 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-placeholder.jpg') center/cover no-repeat;
  filter:blur(14px);
  transform:scale(1.06); /* evita bordes transparentes que el blur deja al escalar, igual que .unidades-bg */
  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/.footer-bottom son flex items de footer{display:flex;flex-direction:column} — igual
   que .narrow dentro de .galeria-strip-top (ver esa sección más arriba), un max-width+margin:auto
   ahí encogería la caja a su contenido en vez de centrar hasta 1180px, por eso el width:100% acá.
   El resultado alinea el título/copy del footer al mismo margen que usa "Por qué invertir" (misma
   cuenta: padding de sección 5% + centrado hasta max-width:1180px). */
.footer-top, .footer-bottom{width:100%; max-width:1180px; margin-left:auto; margin-right:auto;}
/* translateY(-26px): arranca de los -146px del ajuste anterior + 120px hacia abajo (distancia real
   medida entre el top de "Nombre" y el top de "¿Qué ambiente te interesa?" — 2 inputs con su
   gap:16px de por medio) — todo en unidades fijas, no vh/clamp, no cambia con el ancho de pantalla. */
.footer-top{position:relative; z-index:3; flex:1 1 auto; display:grid; grid-template-columns:1.2fr 1fr; gap:80px; align-content:center; transform:translateY(-26px);}
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:2px 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`, mismo patrón que JL Congreso. `.form-msg` es el mensaje de error inline (queda
   dentro del propio <form>, debajo del botón, con `min-height` fijo para no empujar el resto del
   form de golpe cuando aparece). `.form-success` es un bloque aparte, hermano del `<form>` dentro de
   `.footer-form-col` (el wrapper 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) y hace aparecer este bloque con el
   mismo mecanismo de fade que ya usa el sitio en otros lados (`.reveal`: 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). */
.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;
} /* align-items:flex-end (no center): .footer-brand-col ahora es más alta que .social-row (tiene el
     cross-link arriba del logo), así que centrarlos por el medio del bloque entero desalineaba el
     social-row respecto al logo. flex-end alinea por el borde inferior — que es el borde del logo,
     porque es el último elemento de la columna — con el borde inferior de WhatsApp/Instagram/LinkedIn. */
/* .social-row es el primer ítem de .footer-bottom (justify-content:space-between), así que ya queda
   pegado a la izquierda sin importar el tamaño — agrandar el font-size acá crece "hacia la derecha"
   solo, sin desalinear el borde izquierdo respecto al resto de la fila (mismo criterio que ya usamos
   con .footer-brand creciendo hacia la izquierda por ser el último ítem). */
/* align-items:baseline — sin esto (default "normal"/stretch) el texto más grande de .cross-link
   (26px Newsreader vs 15px Roboto de los otros 3) queda con la caja estirada y el texto "colgando"
   más abajo en vez de compartir la línea de base con WhatsApp/Instagram/LinkedIn. */
.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;}
/* "Mirá también JL Congreso" ya no va al lado de WhatsApp/Instagram/LinkedIn — se movió arriba del
   logo de JL Desarrollos, alineado a la derecha (mismo borde que el logo) con un margin-bottom para
   que no quede pegado. */
.footer-brand-col{display:flex; flex-direction:column; align-items:flex-end;}
/* "Descubrí también" ahora va apilado arriba de "JL Congreso" (antes iba en la misma línea) —
   display:flex en columna, alineado a la derecha igual que .footer-brand-col. */
.cross-link{color:rgba(245,242,237,0.55); margin-bottom:80px; display:flex; flex-direction:column; align-items:flex-end; gap:6px;}
/* mismo color/tipografía/peso/interlineado que usan los títulos de sección tipo "UBICACIÓN" o
   "GALERÍA" (.eyebrow: font-family Roboto, font-weight:500, line-height normal, color #E7B98A) —
   se mantiene el font-size propio (16.25px), no el 12px de .eyebrow, porque no se pidió igualar eso. */
.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; /* 18.2px + 20% */
  font-weight:700; font-family:'Newsreader',serif; color:var(--stone);
  text-transform:uppercase; transition:color .25s;
}
/* hover: mismo criterio de color que el resto de los links del footer (.social-row a:hover),
   que es lo que "JL Congreso" heredaba de ahí antes de salir de .social-row */
.cross-link a:hover{color:#E7B98A;}
/* el texto "JL Desarrollos" (27px, 33px de alto renderizado) se reemplazó por el logo real —
   49.5px de alto = 33px × 1.5 ("50% más de tamaño que el texto"), ancho según el aspect ratio real
   de los PNG (2078×397, mismo aspect ratio en BLANCO y COLOR). .footer-bottom usa
   justify-content:space-between, así que el borde derecho de este ítem (el último de la fila)
   ya queda pegado al borde derecho del contenedor sin CSS extra — el ancho de más que agrega el
   logo se reparte solo hacia la izquierda, sin desalinear nada de lo que está arriba.
   Logo fijo (BLANCO) + logo de hover (COLOR) apilados con crossfade: la caja del link tiene el
   tamaño fijo (height + aspect-ratio) y ambas imágenes se superponen con position:absolute — sin
   esto, alternar solo la opacity de una imagen se ve como un fundido a negro/transparente en vez de
   un cambio real de blanco a color. */
.footer-brand{position:relative; display:inline-block; height:49.5px; aspect-ratio:2078/397;}
.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;}

/* Reorganización del footer en escritorio (pedido explícito) — "Descubrí también/JL Congreso" pasa
   al lugar donde estaba WhatsApp/Instagram/LinkedIn (alineado con el h2), WSP/INST/LKN pasa a vivir
   debajo del botón "Enviar consulta" (misma columna que el form), y el logo JL Desarrollos se mueve
   a la izquierda (misma columna que el botón) y crece 20%. Ámbito min-width:981px a propósito y sin
   tocar ninguna línea de los @media de mobile: el CSS mobile (más abajo) depende de que .social-row
   y .cross-link sigan exactamente donde están hoy en el HTML (.footer-bottom{flex-wrap} los apila en
   2 filas, .cross-link{margin-bottom} está calibrado a mano contra esa posición) — moverlos de
   verdad rompía mobile sin tocar una sola regla suya. Solución: `.cross-link-desktop` y
   `.social-row-desktop` son copias en el HTML (ver index.html) — el original de cada uno queda 100%
   intacto en su lugar de siempre (lo sigue usando mobile sin ningún cambio), la copia se muestra
   solo acá y el original se oculta solo acá (mobile no matchea este media query, así que nunca ve
   ninguna de las dos reglas de `display` de abajo). */
.cross-link-desktop{display:none;}
.social-row-desktop{display:none;}
@media (min-width:981px){
  /* mismo grid 1.2fr/1fr/gap:80px que .footer-top, sobre el mismo contenedor (max-width:1180px,
     centrado) — así la columna 2 de acá cae exacto en el mismo x que la columna 2 de .footer-top
     (donde vive el botón), sin necesidad de medir nada a mano. */
  .footer-bottom{display:grid; grid-template-columns:1.2fr 1fr; gap:80px; align-items:end;}
  .footer-bottom > .social-row{display:none;} /* el original — la copia de abajo ocupa su lugar visual */
  .footer-brand-col > .cross-link{display:none;} /* el original — cross-link-desktop lo reemplaza */

  .cross-link-desktop{
    display:flex; flex-direction:column; align-items:flex-start; gap:6px;
    grid-column:1; margin-bottom:0; color:rgba(245,242,237,0.55);
  }
  /* "JL CONGRESO" +50% (pedido explícito, solo esta palabra — "Descubrí también"/.cross-link-label
     no se toca). `.cross-link-desktop a` tiene la MISMA especificidad que la regla base
     `.cross-link a{font-size:21.84px}` (1 clase + 1 tag en ambas) — gana por venir después en el
     archivo, y solo dentro de este @media(min-width:981px), así que la copia original de mobile
     (que usa `.cross-link a` sin la clase extra) sigue en 21.84px, intacta. 21.84px × 1.5 = 32.76px. */
  .cross-link-desktop a{font-size:32.76px;}
  /* footer-brand-col pasa a la columna 2 (la del botón) — align-items flex-end→flex-start para que
     el logo (único hijo visible, el cross-link de acá adentro está oculto arriba) quede pegado al
     borde IZQUIERDO de esa columna en vez del derecho. */
  .footer-brand-col{grid-column:2; align-items:flex-start;}
  .footer-brand{height:59.4px;} /* 49.5px + 20% */

  /* Gap medido en el navegador entre el botón "Enviar consulta" y la línea (border-bottom) del
     input de arriba: 26px. Separación nueva pedida = el doble = 52px, entre el botón y esta fila. */
  .social-row-desktop{display:flex; gap:22px; align-items:baseline; margin-top:20px;}
}

/* oculto (y corrido 120px hacia abajo) hasta que termina la sección #invertir — ver el
   ScrollTrigger dedicado en main.js. pointer-events:none mientras está oculto para que no sea
   clickeable/hover-able estando invisible. A diferencia de una revelación de una sola vez, esto es
   reversible: si el usuario vuelve a scrollear hacia arriba más allá de ese mismo punto, se oculta
   de nuevo con la misma transición — "rebobina" igual que la animación de scroll del hero. */
.wsp-float{
  position:fixed; bottom:56px; right:56px; z-index:200; /* 40px + 40% */
  width:70px; height:70px; border-radius:50%; overflow:hidden; /* 56px + 25%, solo desktop — ver override mobile en @media max-width:640px */
  display:flex; align-items:center; justify-content:center; color:#fff;
  /* sombra proyectada hacia abajo-derecha, difusa (blur generoso) y traslúcida — offset positivo
     en X e Y en vez del "0 8px" centrado que tenía antes, para que se note la direccionalidad. */
  box-shadow:8px 10px 28px rgba(0,0,0,0.3); cursor:pointer;
  opacity:0; transform:translateY(120px); pointer-events:none;
  transition:opacity .6s ease, transform .6s ease;
} /* sin background propio: el PNG ya trae su círculo completo, el fondo oscuro era remanente del ícono SVG anterior y generaba un anillo visible en el borde del círculo */
.wsp-float.in{opacity:1; transform:translateY(0); pointer-events:auto;}
/* LOGO WSP 100x100.png ya trae su propio círculo verde con las esquinas transparentes — el
   overflow:hidden de arriba recorta esas esquinas contra el borde circular del contenedor */
.wsp-float img{width:100%; height:100%; object-fit:cover;}

.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 lleva el gap:40px de la regla compartida de arriba: en desktop (2 columnas) esa
     regla ni siquiera aplica gap (.ubicacion no tenía gap propio ahí), pero acá, apilado en una
     columna, ese gap dejaba una franja del fondo claro del body (--stone) entre el mapa y el panel
     oscuro — ninguno de los dos es un color "de diseño" ahí, es el hueco transparente del grid. El
     mapa y el panel tienen que quedar pegados, sin separación, igual que en desktop. */
  .ubicacion{gap:0;}
  .split.reverse .split-img, .split.reverse .split-copy{order:0;}
  .split-copy{padding:60px 6%;}
  /* min-height:92vh de la regla base no tiene override acá — cuando el grid pasa a 1 columna
     (arriba), la imagen sigue ocupando su alto natural/intrínseco (ese cálculo no depende de
     min-height, así que no cambia), pero el sobrante hasta completar 92vh se lo queda la fila de
     texto, que con align-items:center (heredado, tampoco tocado) queda centrada dentro de esa fila
     inflada — ~100px de aire de más arriba y abajo del texto real. min-height:0 saca el piso
     forzado: cada fila pasa a medir lo que su contenido necesita y listo, mismo criterio en las
     dos variantes de .split (normal y reverse, misma estructura). */
  .split{min-height:0;}
  .unit-panel{grid-template-columns:1fr;}
  /* .unit-visual min-height:700px (regla base) está pensado para la columna angosta de desktop,
     donde el plano necesita alto propio para no quedar aplastado al lado de la columna de texto.
     Apilado a ancho completo en mobile, el plano (con max-width:100%; height:auto, conserva su
     aspect ratio real) casi siempre necesita bastante menos que 700px — ese piso fijo dejaba aire
     de sobra debajo del botón "Dimensiones". min-height:auto deja que el flex container (ya es
     column + align-items:center + justify-content:center) se ajuste al alto real de imagen+botón. */
  .unit-visual{min-height:auto;}
  /* max-height:720px (regla base, en .unit-visual img) es el verdadero motivo por el que la
     columna del plano seguía siendo enorme en mobile incluso después de sacar min-height:700px de
     arriba: midiendo las 8 imágenes reales, TODAS son recortes muy angostos/altos (ratio 0.17-0.46)
     — a max-width:100% del contenedor (~360px) todas piden entre 780px y 2100px de alto por su
     propio aspect ratio, así que en la práctica siempre terminan topando el límite de 720px de
     alto (no el de ancho) — el contenido real YA superaba los 700px de min-height en las 8
     unidades, por eso ese fix por sí solo no cambiaba nada visible. Achicar el tope a 480px acá sí
     reduce el alto real de la columna en mobile a algo bastante menos desproporcionado, sin
     recortar contenido (object-fit:contain, solo escala más chico). */
  .unit-visual img{max-height:480px;}
  .galeria-prev{left:8px;}
  .galeria-next{right:8px;}
  .ubicacion-copy{padding:70px 6%;}
  section{padding:90px 6%;}
}

/* UNIDADES mobile — rediseño completo, en un @media(max-width:640px) NUEVO y aparte (no agregado
   al bloque de 640px de más arriba) 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}
   y a .unit-visual img{max-height:480px} (misma especificidad en los tres casos — si este bloque
   quedara antes que el de 980px, esas dos reglas de acá perderían el cascade). El resto de las
   reglas de acá no compite con nada del bloque de 980px, pero se agrupan todas juntas por prolijidad
   en vez de partir el rediseño en dos lugares distintos del archivo.
   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){
  /* 5) 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;}

  /* 1) .unit-panel vuelve a 2 columnas (plano | botones), como en desktop, en vez del apilado que
     imponía el breakpoint de 980px — pero con grid-template-areas en vez del grid-template-columns
     simple de desktop, para que .unit-details (el bloque de texto) pueda quedar en una fila propia
     ABAJO ocupando todo el ancho, no comprimido dentro de la columna angosta de la derecha junto a
     los botones (a este ancho, unit-details con su propio max-width/font-size no entraría cómodo
     en la mitad de un panel de 375-428px).
     La columna de botones es un ancho FIJO (92px, no una fracción del panel) — con 1fr/1fr o
     similar quedaba casi tan ancha como el plano, y como .floor-select en 1 columna hace que cada
     botón ocupe el 100% del ancho de su columna, terminaban siendo tiras rectangulares anchas en
     vez de cuadrados. Con un ancho fijo angosto, el plano (1fr, se lleva todo el resto) recupera
     el espacio que antes perdía, y el ancho de cada botón queda acotado a algo parecido a lo que
     medían en el grid de 4 columnas (~72-83px) en vez de a todo el ancho disponible.
     .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-info deja de generar caja propia, pero
     select-ores como ".unit-info .floor-select" lo siguen matcheando igual — display:contents no
     saca el elemento del árbol del DOM, solo de la generación de cajas). */
  .unit-panel{
    display:grid;
    grid-template-columns:1fr 66px;
    grid-template-areas:"plano botones" "detalles detalles";
    column-gap:12px; row-gap:20px;
  }
  .unit-visual{grid-area:plano;}
  .unit-info{display:contents;}
  .unit-info .floor-select{grid-area:botones; grid-template-columns:1fr; gap:5px; margin-bottom:0;}
  .unit-details{grid-area:detalles;}
  /* botones de unidad en esta lista vertical de 8 — la columna de botones se achicó a 66px (antes
     92px, que ya había sido una primera corrección sobre el 1fr/1fr original: en cualquiera de
     los dos casos anteriores, con .floor-select en 1 columna cada botón ocupa el 100% del ancho
     de su columna, así que terminaban siendo tiras rectangulares anchas). Padding horizontal y
     font-size también se achicaron (6px→4px, 12px→10px de letra) para que el número de cada
     unidad (incluidos los rangos con guion, "401-701") siga entrando en una sola línea a este
     ancho — confirmado en el navegador, no a ojo. Resultado: 66×43px por botón (ratio ~1.5:1),
     bastante más parecido a un cuadrado que la tira ancha de antes, sin empujar la columna de 8
     botones fuera del alto de una pantalla (unidadesHeight sigue entrando en el viewport, medido
     en el navegador). */
  .unit-info .floor-select button{padding:6px 4px; font-size:10px;}

  /* 2) 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). max-height
     más chico que el de la regla de 980px (480px→380px): con solo ~0.85fr de una columna angosta
     de por sí (antes el plano tenía casi todo el ancho de la sección para él solo), conviene un
     tope de alto menor para que el panel completo (plano + botones + detalles) entre en pantalla. */
  .dim-toggle{display:none;}
  .unit-visual img{cursor:pointer; max-height:380px;}

  /* 3) 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;}

  /* 4) Ficha PDF / Brochure completo en una 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 (13px 24px / 12px)
     los dos botones no entraban en el ancho de mobile y flex-wrap los mandaba a 2 filas. flex:1
     en vez de ancho intrínseco (se reparten el ancho disponible mitad y mitad en vez de que cada
     uno mida lo que su propio texto pide) + padding/font-size más chicos + white-space:nowrap
     (para que el texto de CADA botón no rompa a una 2da línea, que es el otro "2 líneas feo" que
     había que evitar además del wrap de la fila entera). */
  .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;}

  /* SPLITS ("El living" / "Los dormitorios") — el espacio en blanco arriba del bloque de texto en
     mobile YA NO viene de .split{min-height:92vh} (ese min-height se sacó para ≤980px hace tiempo
     — confirmado en el navegador: acá da min-height:0px, no es la causa). La causa real hoy es
     .split-copy{padding:60px 6%} (regla de ≤980px, compartida con tablet): 60px de borde de
     sección a eyebrow, confirmado midiendo el hueco en el navegador (64px, calza con el padding
     declarado). Acá se reduce SOLO el padding-top a la mitad (60px→30px) — el padding-bottom
     queda en su valor original (pedido explícito: el espacio de ABAJO, después del último texto,
     no se toca en este ajuste — se había reducido también en un primer intento y se revirtió
     acá), el horizontal (6%) tampoco se toca, y ningún espacio INTERNO (el gap título↔texto lo
     dan .split-copy .eyebrow{margin-bottom:20px} y .split-copy h2{margin-bottom:22px}, ninguno de
     los dos tocado). Se pisa solo padding-top puntual (no el shorthand completo) para no tener
     que repetir ni el horizontal ni el padding-bottom de la regla de ≤980px. Mismo override para
     las dos variantes de .split (normal y reverse): comparten exactamente la misma estructura y
     el mismo .split-copy, no hay ninguna diferencia entre "El living" y "Los dormitorios" que
     pida valores distintos — confirmado revisando las dos en el navegador. */
  .split-copy{padding-top:30px;}

  /* WSP-FLOAT — distancia al borde inferior/lateral reducida a la mitad (56px→28px, ambos ejes)
     respecto a la regla base (pensada para una pantalla más grande, donde 56px se veía proporcionado).
     Tamaño: se vuelve a pisar a 56px acá (la base ahora es 70px, +25% pedido solo para desktop) —
     mobile queda con el tamaño original, sin el aumento. */
  .wsp-float{bottom:28px; right:28px; width:56px; height:56px;}

  /* LOGO "JL Desarrollos" del footer — en mobile no hay mouse, así que el :hover de la regla base
     (crossfade blanco→color) nunca se dispara solo: queda siempre en blanco, salvo que el
     navegador simule un "sticky hover" tras un tap, lo cual es inconsistente entre dispositivos.
     Acá se fuerza directamente la versión de color, sin depender de ningún estado de hover, y se
     saca la transition (no hay nada que animar si el estado es siempre el mismo). */
  .footer-brand-default{opacity:0; transition:none;}
  .footer-brand-hover{opacity:1; transition:none;}

  /* "JL Congreso" — sin mouse, el :hover de la regla base (que lo pone en ámbar) nunca se dispara
     solo, así que se fuerza el color acá sin depender de ningún estado — el link ya apunta a la
     URL real (ver index.html) en todos los anchos, no solo acá, esto es puramente el tratamiento
     visual del hover. El color forzado es var(--stone) (blanco, el mismo que el estado normal/no-
     hover de la regla base) — NO #E7B98A/ámbar: una primera versión de este fix se equivocó y
     forzó el color de hover en vez del normal, corregido más abajo. */
  .cross-link a{transition:none;}

  /* GALERÍA — franjas de fondo sólido arriba/abajo del carrusel, reducidas a 1/3 de su alto
     actual cada una (no 1/3 del total): antes 279px arriba / 139.5px abajo (medido en el
     navegador), ahora 93px / 46.5px — la de abajo sigue derivándose como la mitad de la de arriba
     en sizeGaleriaStrips() (ver main.js), así que reducir la de arriba ya arrastra a la de abajo
     al valor correcto sin tocar esa fórmula.
     El título (eyebrow+h2) se reacomoda dentro de la franja de arriba para conservar la misma
     proporción relativa que tenía antes (arriba del título : título : abajo del título ≈
     162:61:56 sobre un total de 279 → ahora escalado sobre un total de 93 en vez de quedar pegado
     a un extremo) — el multiplicador que usa sizeGaleriaStrips() para el padding-bottom (espacio
     título↔imágenes) pasa de 2×alto-del-h2 a ~0.3×alto-del-h2 específicamente en este breakpoint
     (ver el chequeo de matchMedia en main.js), 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:93px;}

  /* "Cada ambiente, en detalle." (título de Galería) — revisado: sigue presente en el HTML y
     renderiza bien en 320/375/428px (con overflow:hidden en .galeria-strip-top, pero min-height
     es un piso, no un techo: si el título wrapea a 2 líneas en un ancho más angosto, la franja
     crece sola para no cortarlo — confirmado en el navegador). No se encontró ningún estado en el
     que falte, así que no se tocó nada de la Galería para esto.

     "Descubrí también / JL Congreso" — .cross-link-label ("Descubrí también") no se toca, ya
     estaba bien: tiene su propio color ámbar fijo en la regla base, sin depender de hover en
     ningún momento. El color de "JL Congreso" (el `<a>`) se fija acá en var(--stone) — mismo
     blanco que su estado normal/no-hover de la regla base, así queda igual con o sin mouse. */
  .cross-link a{color:var(--stone);}

  /* CONTACTO — footer-bottom pasa a 2 filas apiladas en mobile (flex-wrap:wrap ya existía, y a
     este ancho social-row + footer-brand-col no entran lado a lado): arriba WSP/INST/LKN, abajo
     el bloque "Descubrí también / JL Congreso" + logo JL Desarrollos.
     1) Ambos logos ya comparten el mismo borde derecho de por sí (.footer-brand-col{align-items:
        flex-end} ya alinea tanto .cross-link como .footer-brand por ese lado) — no hizo falta
        tocar nada para este punto, confirmado midiendo los dos en el navegador (277.09px de
        borde derecho, exacto en ambos).
     2) "JL Congreso" centrado a mitad de camino entre la fila de redes y el logo: medido en el
        navegador, el hueco de arriba (fin de WSP/INST/LKN → inicio de "JL Congreso") daba 41px,
        el de abajo (fin de "JL Congreso" → inicio del logo, antes margin-bottom:80px de la regla
        base) daba 80px — bien lejos de ser equidistante. Se achica el margin-bottom acá a 40px
        (redondeando el valor medido de 41px) para igualarlo al de arriba, en vez de agrandar el
        de arriba para igualar el de abajo — más compacto, en línea con el resto de los ajustes de
        esta sesión. */
  .cross-link{margin-bottom:40px;}

  /* 3) WSP/INST/LKN — antes con gap:22px fijo, el grupo de los 3 quedaba mucho más angosto que el
     ancho disponible (293px de 324px totales, ~31px sin usar a la derecha). width:100% para que
     el flex item deje de encogerse a su contenido (mismo gotcha de flex+ancho que ya se documentó
     varias veces en este archivo — .narrow dentro de .galeria-strip-top, el bloque del footer,
     etc.) y justify-content:space-between para que los 3 se repartan parejo todo el ancho — el
     gap:22px de la regla base queda como piso mínimo (acá el espacio real que da space-between es
     mayor, ~37px, así que gana él, no el gap). */
  .social-row{width:100%; justify-content:space-between;}

  /* 4) Más aire entre la fila WSP/INST/LKN y el bloque de abajo (JL Congreso + logo JL Desarrollos)
     — quedaban muy pegados (16px, el "gap" de la regla base de .footer-bottom, pensado para
     desktop donde ambos bloques van lado a lado, no apilados). Se sube solo el row-gap (no el
     shorthand completo, para no tocar el column-gap de 16px que sigue existiendo por si algún
     ancho intermedio todavía no alcanza a apilar) a 72px — al momento de este cambio había sobra
     de espacio hasta el final de la página (footer{padding-bottom:80px} de la regla base), así que
     empujar el bloque de abajo no generaba scroll extra ni lo sacaba de pantalla. Nota: el punto 5
     de más abajo reduce ese padding-bottom a 31px para bajar el logo — con el row-gap ya puesto acá
     primero, sigue sin recortar nada (verificado junto con el punto 5). */
  .footer-bottom{row-gap:72px;}

  /* "Descubrí también / JL CONGRESO" + logo "JL Desarrollos" — centrado horizontal en la página
     (pedido explícito, solo mobile, sin tocar la posición vertical de ninguno de los dos).
     .footer-brand-col NO ocupaba el ancho completo de la fila: al quedar solo en su línea del
     flex-wrap (.footer-bottom es flex row + wrap, .social-row de arriba ya es width:100%), con
     justify-content:space-between y un único ítem en esa línea el box queda pegado al borde
     IZQUIERDO en vez de centrado o a la derecha (confirmado midiendo con getBoundingClientRect:
     .footer-brand-col ocupaba solo hasta la mitad del ancho disponible) — mismo gotcha de
     flex+ancho ya documentado varias veces en este archivo (.narrow dentro de .galeria-strip-top,
     el bloque del footer en desktop): un ítem que no llega a width:100% se encoge a su propio
     contenido en vez de estirarse, y ahí "centrar" solo centraría dentro de esa caja angosta, no
     en la página real. width:100% + align-items:center acá resuelve los dos casos a la vez (el
     logo y el cross-link son los dos únicos hijos directos de .footer-brand-col). No se tocó
     row-gap/margin-bottom/padding-bottom de acá arriba/abajo — nada cambia en el eje vertical. */
  .footer-brand-col{width:100%; align-items:center;}
  /* el cross-link en sí (display:flex; flex-direction:column) seguía alineando sus 2 líneas
     ("Descubrí también" / "JL CONGRESO") a la derecha entre sí (align-items:flex-end, regla base) —
     se pasa a center para que además queden centradas una respecto de la otra, no solo el bloque
     centrado en la página. Ya estaba encolumnado (flex-direction:column, sin cambios ahí). */
  .footer-brand-col > .cross-link{align-items:center; text-align:center;}

  /* logo "JL Desarrollos" — 40% más chico que el tamaño mobile actual (pedido explícito, solo
     mobile): 49.5px × 0.6 = 29.7px de alto; el ancho sigue el mismo aspect-ratio real del PNG
     (2078/397, ya declarado en la regla base), no hace falta tocarlo acá. */
  .footer-brand{height:29.7px;}

  /* 5) Logo "JL Desarrollos" alineado verticalmente con el botón flotante de WhatsApp.
     Primer intento (descartado): margin-top en .footer-brand. No funcionó — footer{min-height:
     100vh} + .footer-top{flex:1 1 auto} hace que .footer-top absorba cualquier margen agregado
     DENTRO de .footer-bottom encogiéndose para compensar (mientras le quede aire hasta su propio
     min-content), así que la distancia entre el logo y el borde real de abajo de la página queda
     prácticamente invariante — confirmado midiendo en el navegador: con margin-top:49px puesto,
     .footer-brand-col crecía 49px hacia ARRIBA (su borde inferior, el del logo, no se movía un
     píxel). El lever que sí mueve el logo es el padding-bottom del propio <footer> — eso sí reduce
     directamente la distancia fija entre .footer-bottom y el borde de abajo de la página.
     Medido en el navegador: el centro vertical de .wsp-float (56px, bottom:28px en mobile) cae en
     y:752px; con footer{padding-bottom:31px} (regla base: 80px) el centro del logo (49.5px de
     alto) cae en y:752.06px — alineados. El row-gap:72px de arriba entre .social-row y
     .footer-brand-col no se toca, así que ambos bloques bajan juntos y mantienen su separación
     entre sí; footer-top/footer-form no se ven afectados (siguen intactos arriba). */
  footer{padding-bottom:31px;}
}
