/* ==========================================================================
   BLUR — Ciudad de Panamá
   Design source: Adobe XD spec (1280px artboards / iPhone 14 Pro Max 430px)
   ========================================================================== */

/* ---- Fonts ---------------------------------------------------------- */
@font-face { font-family: "Jost"; src: url("../assets/fonts/jost-400.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Jost"; src: url("../assets/fonts/jost-500.woff2") format("woff2"); font-weight: 500; font-style: normal; font-display: swap; }
@font-face { font-family: "Jost"; src: url("../assets/fonts/jost-600.woff2") format("woff2"); font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: "Zilla Slab"; src: url("../assets/fonts/zillaslab-400.woff2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Zilla Slab"; src: url("../assets/fonts/zillaslab-500.woff2") format("woff2"); font-weight: 500; font-style: normal; font-display: swap; }

/* ---- Tokens ---------------------------------------------------------- */
:root {
  --bg: #dcd9d1;        /* warm grey — page background (XD) */
  --ink: #544a33;       /* olive — all text, dark sections, logo (XD #544A33) */
  --cream: #fbe2bf;     /* cream — accent, buttons, slider circles (XD #FBE2BF) */
  --blue: #d1dde6;      /* secondary accent */
  --grey: #8c8678;      /* warm neutral secondary text */
  --offwhite: #efede7;  /* very light surfaces */

  --font-sans: "commuters-sans", "Commuters Sans", "Jost", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-slab: "yorkten-slab-normal", "Yorkten Slab", "Zilla Slab", Georgia, "Times New Roman", serif;

  --container: 1184px;
  --gutter: 48px;
}

/* ---- Reset / base ---------------------------------------------------- */
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}
body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-slab);
  font-size: 21px;      /* XD Párrafos: Yorkten Slab Book 21px */
  line-height: 1.524;   /* XD line spacing 32px */
  font-weight: 400;     /* Yorkten "Book" */
  /* Grayscale AA so text renders lighter/sharper on macOS, matching the XD
     mockups. Browser default (subpixel) fattens the strokes. */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
img {
  display: block;
  max-width: 100%;
  /* Los <img> llevan width/height intrínsecos (reserva de espacio pre-carga,
     cero CLS). Este height:auto anula el hint de altura del atributo en
     cualquier img cuyo CSS solo fije el ancho — sin él, esos atributos
     estirarían la imagen a su altura intrínseca en px. */
  height: auto;
}
a {
  color: inherit;
  text-decoration: none;
}
button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

.container {
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
@media (min-width: 1280px) {
  .container {
    padding-inline: 0;
  }
}

/* ---- Header / nav ----------------------------------------------------- */
.site-header {
  max-width: var(--container);
  margin-inline: auto;
  padding: 0 var(--gutter);
  position: relative;
}
@media (min-width: 1280px) {
  .site-header {
    padding-inline: 0;
  }
}
.lang-toggle {
  display: flex;
  justify-content: flex-end;
  gap: 0.35em;
  padding-top: 30px;
  font-family: var(--font-sans);
  font-size: 14px;      /* XD Commuters Sans Regular 14px */
  line-height: 1.714;   /* XD line spacing 24px */
  color: var(--ink);
}
.lang-toggle a,
.lang-toggle-mobile a {
  font-family: inherit;
  font-size: inherit;
  font-weight: 400;
  opacity: 0.55;
}
.lang-toggle a.is-active,
.lang-toggle-mobile a.is-active {
  font-weight: 600;     /* XD SemiBold */
  opacity: 1;
}
.nav-row {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  padding-bottom: 45px; /* logo top y63, hero top y144 */
}
.logo-link img,
.logo-link svg {
  width: 165px;
  height: auto;
  margin-bottom: -18px;
}
/* Header logo draws itself on load: the inline SVG's mask strokes reveal the
   artwork 0→100% (gentle ease-in) while the whole logo fades in. Inline (not
   <img>) so the animation clock starts at first paint and replays on every load. */
.logo-link svg {
  animation: logo-fade 0.6s ease-out both;
}
.logo-draw-line {
  fill: none;
  stroke: #fff;
  stroke-width: 10;
  stroke-linecap: round;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  animation: logo-draw 1.4s cubic-bezier(0.45, 0.05, 0.25, 1) forwards;
}
@keyframes logo-fade {
  from { opacity: 0; }
}
@keyframes logo-draw {
  to { stroke-dashoffset: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .logo-link svg {
    animation: none;
  }
  .logo-draw-line {
    animation: none;
    stroke-dashoffset: 0;
  }
  .carousel-arrow--prev:hover svg,
  .carousel-arrow--prev:focus-visible svg,
  .carousel-arrow--next:hover svg,
  .carousel-arrow--next:focus-visible svg {
    animation: none;
  }
}
.main-nav {
  display: flex;
  align-items: baseline;
  gap: 48px;
}
.main-nav a {
  font-family: var(--font-sans);
  font-size: 18px;        /* XD Commuters Sans Regular 18px */
  font-weight: 400;
  line-height: 1;
  letter-spacing: 0;      /* XD character spacing 0 */
  text-transform: uppercase;
  color: var(--ink);
}
.main-nav a.is-active {
  font-weight: 600;       /* XD SemiBold */
  /* TEMP faux-bold: commuters-sans kit smf8bvc ships no 600 weight yet, so the
     word above doesn't read as SemiBold. Horizontal double-strike thickens the
     glyphs. Remove once the agency authorises the 600 weight. */
  text-shadow: 0.3px 0 0 currentColor, -0.3px 0 0 currentColor;
}
.nav-burger {
  display: none;
}

/* ---- Buttons ---------------------------------------------------------- */
/* Hover interaction: Opción 8 — olive fill wipes left→right (never reverses),
   while cream letters relay in a left→right cascade capped at ~240ms.
   Labels are split into per-letter .btn-pill-cell elements at runtime by main.js
   (aria-label preserves the original text; the split is a no-op when JS is absent). */
.btn-pill {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 224px;
  height: 48px;
  padding: 0 32px;
  border-radius: 24px;
  white-space: nowrap;
  background: var(--cream);
  color: var(--ink);
  font-family: var(--font-sans);
  font-weight: 400;      /* XD Botones: Commuters Sans Regular */
  font-size: 18px;       /* XD Botones 18px */
  line-height: 1;
  text-transform: uppercase; /* buttons are all-caps in the design; markup stays natural case */
  /* lateral wipe via ::before; color transition is a no-JS fallback (pinned
     span colors make it a no-op when JS has split the label into cells) */
  position: relative;
  isolation: isolate;   /* keeps the z-index:-1 wipe inside the pill */
  overflow: hidden;
  transition: color 0.3s ease 0.1s, transform 0.25s ease;
}
.btn-pill::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;           /* above cream background, below the label */
  background: var(--ink);
  transform: scaleX(0);
  transform-origin: 100% 50%; /* resting: wipe enters from left on hover */
  transition: transform 0.45s cubic-bezier(0.65, 0, 0.35, 1);
}
.btn-pill:hover,
.btn-pill:focus-visible {
  color: var(--cream);   /* no-JS fallback; no-op when label is split into cells */
  outline: none;
}
.btn-pill:hover::before,
.btn-pill:focus-visible::before {
  transform: scaleX(1);
  transform-origin: 0 50%; /* hover: wipe exits right on mouse-out (never reverses) */
}
.btn-pill:active {
  transform: scale(0.98);
}

/* Per-letter cells (built by main.js — see initButtonLetters).
   Each cell has two stacked span copies; on hover both translate up by 1.4em
   so the cream (incoming) copy appears as the wipe passes. */
.btn-pill-letters {
  display: flex;
}
.btn-pill-cell {
  display: block;
  height: 1.4em;         /* clearance for uppercase accents: Á, Ó */
  overflow: hidden;
}
.btn-pill-cell span {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 1.4em;
  transition: transform 0.4s cubic-bezier(0.76, 0, 0.24, 1);
  transition-delay: var(--d, 0ms);
}
/* first copy (outgoing) stays ink; second copy (incoming) stays cream —
   pinned so they don't flip instantly when the button's color transitions */
.btn-pill-cell span:first-child { color: var(--ink); }
.btn-pill-cell span:last-child  { color: var(--cream); }
.btn-pill:hover .btn-pill-cell span,
.btn-pill:focus-visible .btn-pill-cell span {
  transform: translateY(-1.4em);
}

/* Reduced motion — disable all animation; instant fill + readable text */
@media (prefers-reduced-motion: reduce) {
  .btn-pill,
  .btn-pill::before,
  .btn-pill-cell span {
    transition: none !important;
  }
  .btn-pill:hover .btn-pill-cell span,
  .btn-pill:focus-visible .btn-pill-cell span {
    transform: none;
  }
  /* with the fill instantaneous, the visible (first) copy must switch to cream */
  .btn-pill:hover .btn-pill-cell span:first-child,
  .btn-pill:focus-visible .btn-pill-cell span:first-child {
    color: var(--cream);
  }
}

/* ---- Hero / image bands ---------------------------------------------- */
.img-band {
  position: relative;
  overflow: hidden; /* frame for the parallax image */
}
.img-band img {
  width: 100%;
  aspect-ratio: 1184 / 656;
  object-fit: cover;
}
.img-band--tall img {
  aspect-ratio: 575 / 704;
}

/* ---- Parallax frames --------------------------------------------------- */
/* Images sit "underneath" their frame and drift slightly slower than the
   scroll. JS sets the translate; the scale gives it headroom to move. */
.frame {
  overflow: hidden;
}
.parallax-img {
  will-change: transform;
  transform: scale(1.16);
}

/* ---- Carousel --------------------------------------------------------- */
.carousel {
  position: relative;
}
.carousel .slides {
  position: relative;
  overflow: hidden;
  display: grid;
}
.carousel .slide {
  grid-area: 1 / 1; /* slides stack; visibility picks the current one */
  width: 100%;
  visibility: hidden;
  position: relative;
  overflow: hidden;
}
.carousel .slide.is-current {
  visibility: visible;
}
.carousel .slide img {
  width: 100%;
  aspect-ratio: 1184 / 656;
  object-fit: cover;
}
.carousel .slide.slide--pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 32px;
}
.carousel .slide.slide--pair img {
  aspect-ratio: 576 / 656;
}
/* curtain reveal (classes driven by main.js): la imagen entrante se revela
   con un clip-path que barre en la dirección del viaje mientras asienta
   debajo; la saliente queda quieta y se atenúa ligeramente */
.carousel .slide::after {
  content: "";
  position: absolute;
  inset: 0;
  background: #000;
  opacity: 0;
  pointer-events: none;
}
.carousel .slide.from,
.carousel .slide.to {
  visibility: visible;
}
.carousel .slide.to {
  z-index: 2;
}
.carousel.dir-next .slide.to { clip-path: inset(0 0 0 100%); }
.carousel.dir-prev .slide.to { clip-path: inset(0 100% 0 0); }
/* transitions only under .run — start states must commit without animating */
.carousel.run .slide.to {
  clip-path: inset(0 0 0 0);
  transition: clip-path 1s cubic-bezier(0.77, 0, 0.175, 1);
}
.carousel.run .slide.to img {
  transition: transform 1s cubic-bezier(0.77, 0, 0.175, 1);
}
.carousel.run .slide::after {
  transition: opacity 1s cubic-bezier(0.77, 0, 0.175, 1);
}
.carousel.run .slide.from::after {
  opacity: 0.18;
}
/* XD slider button: solid cream circle (~50px) with an olive chevron */
.carousel-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 50px;
  height: 50px;
  border-radius: 50%;
  background: var(--cream);
  color: var(--ink);        /* olive chevron */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  z-index: 2;
  transition: background 0.3s ease, color 0.3s ease;
}
.carousel-arrow svg {
  width: 24px;
  height: 24px;
  display: block;
}
/* Hover: invert to olive circle + cream chevron, and give the chevron a single,
   restrained nudge in the direction it points before settling back. */
.carousel-arrow:hover,
.carousel-arrow:focus-visible {
  background: var(--ink);
  color: var(--cream);
}
.carousel-arrow--prev:hover svg,
.carousel-arrow--prev:focus-visible svg {
  animation: arrow-nudge-prev 0.5s ease;
}
.carousel-arrow--next:hover svg,
.carousel-arrow--next:focus-visible svg {
  animation: arrow-nudge-next 0.5s ease;
}
@keyframes arrow-nudge-prev {
  50% { transform: translateX(-4px); }
}
@keyframes arrow-nudge-next {
  50% { transform: translateX(4px); }
}
.carousel-arrow--prev {
  left: -25px;              /* circle centred on the image edge */
}
.carousel-arrow--next {
  right: -25px;
}

/* Hero carousel (ubicacion). El XD dibujaba el hero SIN flechas en escritorio y
   esta regla las ocultaba — pero era CSS muerto: ninguna página usaba la clase.
   Al estrenarla con dos slides reales resulta que ocultarlas deja la segunda
   imagen INALCANZABLE: el carrusel no tiene autoplay (ver initCarousel en
   js/main.js), las flechas son la única navegación. Así que el hero las enseña
   como cualquier otro carrusel. Si algún día se añade autoplay, se puede
   recuperar el `display: none` de escritorio. */

/* ---- Copy sections ---------------------------------------------------- */
.copy-section {
  text-align: center;
  padding-top: 47px;
}
.copy-section h1,
.copy-section h2 {
  font-family: var(--font-sans);
  font-weight: 400;        /* XD Títulos: Commuters Sans Regular */
  font-size: 40px;         /* XD Títulos 40px */
  line-height: 1.2;        /* XD line spacing 48px */
  letter-spacing: -0.02em; /* XD character spacing -20 */
  color: var(--ink);
}
.copy-section .lead {
  max-width: 1088px;
  margin: 12px auto 0;
  font-size: 21px;         /* XD Párrafos 21px */
  line-height: 1.524;      /* XD line spacing 32px */
  color: var(--ink);
}
.copy-section .btn-pill {
  margin: 46px auto 0;
}
/* ---- Ritmo vertical entre bloques de contenido (CONVENCIÓN) -------------
   El espacio ENTRE bloques de la columna principal lo posee el propio bloque,
   marcado con la clase `.media-block`, anclado a su contenedor padre — NO al
   selector frágil de hermano adyacente `.copy-section + X` (que obligaba a
   enumerar a mano cada tipo que pudiera ir después: si se reordenaba, envolvía
   o añadía un tipo nuevo sin acordarse, el gap desaparecía — el bug recurrente
   del "botón pegado al vídeo"). Cualquier hijo directo de un `.container`
   (no-hero) que lleve `.media-block` recibe su gap superior automáticamente,
   venga después de lo que venga. `:not(:first-child)` evita un gap espurio si el
   bloque abre el contenedor; `.container--hero` queda excluido (su multimedia va
   a tope arriba). Las .copy-section conservan su propio `padding-top` para el gap
   de ARRIBA. Al añadir un bloque multimedia nuevo, NO añadas CSS: dale la clase
   `.media-block` (junto a su clase de tipo) y esta convención lo espacia. */
.container:not(.container--hero) > .media-block:not(:first-child) {
  margin-top: 64px;
}

/* ---- Two-column feature (home bottom) --------------------------------- */
.feature-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 34px;
  row-gap: 0;
  margin-top: 64px;
}
.feature-grid figure {
  overflow: hidden; /* frame for the parallax image */
}
.feature-grid figure img {
  width: 100%;
  aspect-ratio: 573 / 704;
  object-fit: cover;
}
.feature-grid h2 {
  font-family: var(--font-sans);
  font-weight: 400;        /* XD Títulos */
  font-size: 40px;         /* XD 40px */
  line-height: 1.2;        /* XD 48px */
  letter-spacing: -0.02em; /* XD -20 */
  color: var(--ink);
  margin-top: 47px;
}
.feature-grid p {
  margin-top: 12px;
  margin-bottom: 30px;     /* min gap before the button; the rest is absorbed by
                              the button's auto top-margin so both columns align */
  font-size: 21px;         /* XD Párrafos 21px */
  line-height: 1.524;      /* XD 32px */
}
.feature-grid .btn-pill {
  margin-top: auto;        /* pin button to the bottom of the equal-height cell so
                              buttons align even when paragraphs differ in length */
  margin-left: auto;
  display: flex;
}
.feature-cell {
  display: flex;
  flex-direction: column;
}

/* ---- Footer ------------------------------------------------------------ */
.site-footer {
  max-width: var(--container);
  margin: 96px auto 24px;
  border: 1px solid var(--ink);
  padding: 54px 48px 46px;
}
/* Self-drawing border (js/main.js · initFooterBorder). JS overlays an SVG that
   traces the box and hides the static border via .has-border-draw; the stroke
   draws in from the top centre once the box scrolls into view. No-JS /
   reduced-motion keeps the plain 1px border above. */
.site-footer.has-border-draw {
  position: relative;
  border-color: transparent; /* keep the 1px box geometry, hide the line */
}
.footer-border-svg {
  position: absolute;
  top: -1px;                 /* offset by the 1px border so the stroke traces the outline */
  left: -1px;
  overflow: visible;
  pointer-events: none;
}
.footer-border-svg path {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1;
  /* 0.55s delay lets the footer's fade+rise reveal settle before the frame draws */
  transition: stroke-dashoffset 1.4s ease 0.55s;
}
/* Créditos / copyright — bajo la caja del footer, alineados a su contenido */
.footer-copyright {
  max-width: var(--container);
  margin: 0 auto 48px;       /* no lateral padding: text aligns to the footer box edges */
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 24px;
  font-family: var(--font-sans);
  font-size: 12px;
  line-height: 1.4;
  letter-spacing: 0.01em;
  color: color-mix(in srgb, var(--ink) 70%, transparent); /* subtle, a touch more contrast */
}
.footer-copyright a {
  color: var(--ink);         /* links: full ink so they stand out from the credit text */
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
  transition: text-decoration-thickness 0.2s ease;
}
.footer-copyright a:hover,
.footer-copyright a:focus-visible {
  text-decoration-thickness: 2px;
}
.footer-cols {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 48px;
}
.footer-contact {
  font-style: normal;        /* address default is italic */
  font-family: var(--font-sans); /* XD: Commuters Sans Light */
  font-weight: 400;          /* XD Regular */
  font-size: 18px;           /* XD 18px */
  line-height: 1.22;         /* XD line spacing 22px */
  color: var(--ink);
}
.footer-contact p {
  margin-bottom: 0.5rem;
}
/* Teléfono y dirección clicables. El reset global de `a` ya da color: inherit y
   text-decoration: none, así que por defecto NO cambia nada — decisión de Chus;
   el subrayado aparece solo en hover/focus. */
.footer-contact a:hover,
.footer-contact a:focus-visible {
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-thickness: 1px;
}
.footer-address {
  display: block;   /* el ancla envuelve dos <p>: caja de bloque = área táctil completa */
}
.footer-note {
  font-family: var(--font-sans);
  font-weight: 400;          /* XD Commuters Sans Light */
  font-size: 14px;           /* XD 16px */
  line-height: 1.5;         /* XD line spacing 20px */
  color: var(--ink);
  max-width: 510px;
}
.footer-logos {
  display: grid;
  grid-template-columns: 1fr 1fr;   /* mirror .footer-cols so the partner logos sit
                                       in the right column, aligned to the note above */
  column-gap: 48px;
  align-items: end;                 /* brand + partners share one baseline */
  margin-top: 92px;
}
.footer-brand {
  display: flex;
  align-items: center;              /* social icons centred to the Blur logo */
}
/* Los dos iconos sociales van en su propio grupo. En escritorio no cambia nada
   (sigue siendo wordmark + iconos en una fila); existe para que en móvil el
   wordmark pueda separarse y subir al principio del footer mientras los iconos
   se quedan abajo, haciendo de separador. Se queda con el hueco de 90px del XD,
   que antes vivía en .icon-youtube. */
.footer-social {
  display: flex;
  align-items: center;
  margin-left: 90px;                /* XD gap between logo and social icons */
}
.footer-partners {
  display: flex;
  align-items: flex-end;            /* partner logos on a shared baseline */
}
/* Los tres logos de partners van envueltos en un <a>. `flex` elimina la caja de
   línea del ancla: si no, el hueco del descendente (~4px bajo la imagen) los
   descolocaría de la línea base compartida. Sirve para las dos maquetaciones —
   el <a> es ítem flex en escritorio e ítem de rejilla en móvil, y en ambas se
   ajusta al logo. */
.footer-partners a {
  display: flex;
}
.footer-logos .logo-blur {
  width: 120px;              /* XD */
}
.footer-logos .icon-youtube {
  width: 34px;               /* el hueco de 90px lo pone ahora .footer-social */
}
.footer-logos .icon-instagram {
  width: 25px;
  margin-left: 15px;
}
.footer-logos .logo-grupo-vivir {
  width: 97px;
}
.footer-logos .logo-muchagana {
  width: 118px;
}
.footer-logos .logo-mallol {
  width: 82px;
}
/* LOS MÁRGENES VAN EN EL ANCLA, NO EN EL <img>. Desde que los tres socios están
   enlazados, el ítem flex de la fila es el <a>, y un `margin-left: auto` puesto
   en el <img> de dentro NO funciona: el ancla está ajustada al contenido, así que
   dentro no hay espacio libre que absorber y el hueco flexible se colapsa — los
   tres logos se apelotonan a la izquierda. (Hallazgo de Codex en la PR #34: se
   coló al envolver Muchagana, que hasta entonces era el único <img> suelto y por
   eso seguía funcionando.)
   `:has()` en lugar de `:nth-child()` a propósito: así no depende del orden de
   los socios. Si algún día hay que soportar un navegador sin `:has()`, el hueco
   se colapsa pero nada se rompe. Ojo: el bloque de ≤700px tiene que anular estos
   márgenes, porque allí la fila es una rejilla y un `auto` empujaría el logo
   dentro de su columna. */
.footer-partners a:has(.logo-grupo-vivir) {
  margin-left: 0;            /* first partner: left edge aligns to the note above */
}
.footer-partners a:has(.logo-muchagana) {
  margin-left: auto;        /* flexible gap: keeps Muchagana + Mallol grouped at the
                               right of the column and absorbs width as it narrows
                               (≈192px at 1280, shrinks instead of overflowing) */
}
.footer-partners a:has(.logo-mallol) {
  margin-left: 26px;
}
.footer-wrap {
  padding-inline: var(--gutter);
}
@media (min-width: 1280px) {
  .footer-wrap {
    padding-inline: 0;
  }
}

/* ---- Concepto: video -------------------------------------------------- */
.video-block {
  position: relative;
  overflow: hidden; /* frame for the parallax poster */
}
.video-block video,
.video-block .poster {
  width: 100%;
  aspect-ratio: 1184 / 656;
  object-fit: cover;
}
.video-block .video-cta {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}
.video-block .video-cta:focus-visible {
  outline: 2px solid var(--cream);
}

/* ---- Concepto: renders + map ------------------------------------------ */
.render-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 33px;
  margin-top: 46px;
}
.render-pair img {
  width: 100%;
  aspect-ratio: 575 / 704;
  object-fit: cover;
}
.map-band {
  margin-top: 96px;
}
.map-band img {
  width: 100%;
}
.ubicacion-section {
  padding-top: 49px;
}
.ubicacion-section h2 {
  font-family: var(--font-sans);
  font-weight: 400;        /* XD Títulos */
  font-size: 40px;         /* XD 40px */
  line-height: 1.2;        /* XD 48px */
  letter-spacing: -0.02em; /* XD -20 */
  text-align: center;
  color: var(--ink);
}
.ubicacion-section .body {
  margin-top: 40px;
  padding-inline: 16px; /* text margin x64 vs image margin x48 */
}
.ubicacion-section .body p + p {
  margin-top: 32px;
}

/* ---- Apartamentos: plan viewer ----------------------------------------- */
.plan-viewer-outer {
  position: relative;
  margin-top: 29px;
}
.plan-viewer {
  border: 1px solid var(--ink);
  min-height: 540px; /* Blur plans are landscape — tighter than Haven's 766 portrait frame to cut dead space */
  padding: 0 48px; /* no vertical padding — each column supplies its own; the plan image runs full-height (it carries its own transparent margin) */
  display: grid;
  grid-template-columns: 290px 1fr;
  column-gap: 48px;
  align-items: center;
}
.plan-info {
  display: flex;
  flex-direction: column;
  align-self: stretch; /* fill the row height so the title sits top-left and the locator bottom-left (XD) */
  padding-block: 20px; /* the container has no vertical padding — the text column supplies its own */
}
.plan-title {
  font-family: var(--font-sans); /* XD Commuters Sans */
  font-size: 48px;               /* XD 48px */
  line-height: 1.2;
  color: var(--ink);
}
.plan-title .plan-letter {
  font-weight: 600;              /* XD SemiBold */
}
.plan-title .plan-area {
  font-weight: 400;
}
.plan-title .plan-unit {
  font-size: 0.5em;              /* XD: "M²" unit is 24px against the 48px figure; scales with the title on mobile */
}
.plan-title sup {
  font-size: 0.55em;
}
.plan-details-block {
  margin-top: 26px;              /* gap below the title (XD has no divider rules) */
  margin-bottom: 22px;           /* the locator's guaranteed gap — see .plan-locator */
}
.plan-details {
  list-style: none;
  font-family: var(--font-sans); /* XD Commuters Sans Regular */
  font-size: 16px;               /* XD 16px */
  line-height: 1.6;
  /* Room for the longest programme (seven lines: F, I and Loft C) whatever this
     typology has, so the text column's height stops depending on which one is
     showing. Today the figure column is ~37px taller than even the seven-line
     case, so this changes nothing on screen — it makes the invariant structural
     instead of leaving it resting on that margin. In em so it tracks the font
     size and survives the Typekit-to-Jost fallback. */
  min-height: calc(7 * 1.6em);
}
.plan-details li {
  display: grid;
  grid-template-columns: 14px 1fr;
  column-gap: 10px;              /* indentation between the number and the word */
}
.plan-details li .qty {
  text-align: left;
}
/* ---- Floor-plate locator ------------------------------------------------
   "Dónde queda el apartamento dentro de la torre" — asked for by the client
   once the site was live, and mirroring the deck's own per-type sheet: the
   level range, then a small plan of the floor with this typology filled in.

   Drawn as SVG rather than shipped as thirteen crops so it stays sharp on a
   retina screen and takes the brand palette (--cream on --ink) instead of the
   deck's orange. The plate strokes use vector-effect so they keep their
   hairline weight at any size — without it the whole drawing scales the stroke
   with it and the plan turns muddy on a phone. */
.plan-locator {
  /* Pinned to the bottom of the text column, as in the deck's own sheet and as the
     CTA used to be — the room list reads from the top, the "you are here" anchors
     the foot. The guaranteed gap above it is the details block's margin-bottom;
     `auto` only ever adds to that, so a typology with fewer rooms spreads out
     instead of leaving the plate stranded mid-column. */
  margin-top: auto;
  padding-top: 16px;
  border-top: 1px solid rgba(84, 74, 51, 0.25);  /* the deck's rule above "Niveles" */
  /* 200, not the 250 this started at. The plate is a "you are here", not a plan to
     read — it only has to show which corner lights up, and the narrower it is the
     less the whole module grows to hold it. */
  max-width: 200px;
}
.plan-locator[hidden] {
  display: none;   /* [hidden] alone loses to the figure's display */
}
.plan-locator-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  font-family: var(--font-sans);
  font-size: 15px;
  line-height: 1.2;
  letter-spacing: 0.02em;
  margin-bottom: 12px;
}
.plan-locator-levels {
  font-variant-numeric: tabular-nums;  /* the range must not jitter as types change */
}
/* The street names sit outside the plate on two sides, as in the deck. Grid
   keeps them there without absolute positioning, so a longer translated label
   pushes the layout instead of overlapping the drawing. */
.plan-locator-plate {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas:
    "side plan"
    ".    park";
  align-items: center;
  gap: 8px;
}
.plan-locator-plate svg {
  grid-area: plan;
  width: 100%;
  /* Fixed, not auto. The four plates have different proportions — the level-6
     one is far wider than it is tall — so sizing by width made the loft cards
     ~30px shorter than the apartment ones and the module resized as you paged.
     A fixed box plus preserveAspectRatio letterboxes each plate inside the same
     height instead. */
  height: 128px;
  display: block;
}
.plan-locator-street {
  font-family: var(--font-sans);
  font-size: 12px;
  line-height: 1.2;
  color: var(--grey);
  white-space: nowrap;
}
.plan-locator-street--side {
  grid-area: side;
  writing-mode: vertical-rl;
  transform: rotate(180deg);   /* reads bottom-to-top, like the deck */
}
.plan-locator-street--park {
  grid-area: park;
  text-align: center;
  padding-top: 2px;
}
.locator-plate {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  opacity: 0.55;
}
/* Fill only, no stroke. An L-shaped unit is built from two or three rects, and
   stroking each one would draw a seam through the middle of a single apartment.
   The plate path is painted after this group and already runs along every unit
   boundary, so the fill reads as outlined without inventing internal lines. */
.locator-unit rect {
  fill: var(--cream);
}

/* The Planta / Isométrico toggles sit under the drawing, not in the text column.
   They change the image, so putting them next to the text made you look across
   the module to find what had moved; under the plan the cause and the effect are
   the same glance apart. It also takes ~60px off the text column, which is what
   let the locator fit without the whole module growing. */
.plan-info-actions {
  display: flex;
  /* A ROW, not a column. Lofts show a second group here (Planta Alta / Baja) and
     stacking it added ~49px to this column for three typologies out of thirteen —
     so the frame grew on entering a loft and shrank on leaving it, which is the
     exact jump the fixed detail rows and fixed plate box were added to remove.
     Side by side, one button row is all any typology ever costs. */
  flex-wrap: wrap;             /* the four buttons don't fit one line on a phone */
  justify-content: center;     /* centred under the drawing, which is itself centred */
  align-items: center;
  column-gap: 24px;            /* reads as two groups, not four loose buttons */
}
.plan-figure-col {
  display: flex;
  flex-direction: column;
  align-items: center;   /* image + toggles centered horizontally */
  justify-self: center;  /* center the whole column within its grid cell */
  align-self: center;
  /* Bottom only. The drawing above carries its own transparent margin so the top
     never needs help, but the toggles have no such padding built in and sat flush
     on the frame. Putting this on the viewer instead would pay for it twice —
     once per column — and add 48px to a module we had just shortened. */
  padding-bottom: 24px;
}
.plan-figure {
  margin: 0;
}
.plan-figure img {
  /* 480 rather than 540: the toggles moved under the drawing and need the room,
     and so does the gap between them and the frame.
     The 1.2 zoom still leaves it larger than it was before the labels landed, so
     the measurements didn't lose anything to the move. */
  max-height: 480px;
  width: auto;
  max-width: 100%;
}
/* ---- Zoom into the plan drawing ----------------------------------------
   The architects export every planta on a 1447x1500 canvas and every isométrico
   on 1500x1276, each with a wide transparent margin — measured across all 32
   files at ~164px per side. So the drawing filled only ~72% of the frame and the
   viewer read as mostly empty. That was cosmetic until the plans gained room
   labels; at the old size the measurements were borderline on a phone, so this
   now earns its place.

   Clip the figure and scale the img inside it. The transform doesn't affect
   layout, so the figure keeps shrink-wrapping the *un*scaled image — the frame,
   the grid and the arrows never move. The margin is near-symmetric, so a centred
   scale lands within ~8 source px of a true crop and needs no translate.

   1.254 IS THE HARD CEILING — past it the drawing itself starts getting clipped.
   It is set by loft-b-pa, whose room labels reach closest to the canvas edge;
   apt-c-iso binds the isométricos at 1.273. That ceiling MOVED when the plans
   gained labels (it was 1.28 on the unlabelled exports), which is why
   tools/check-plan-zoom.py now recomputes it from the actual files rather than
   trusting this comment. 1.20 keeps ~4% headroom.

   It MUST stay one uniform factor. All the canvases share a single drawing
   scale, so a 54 m² studio genuinely renders smaller than a 233 m² loft —
   trimming each image to its own content would flatten them to the same size
   and destroy that comparison. */
.plan-figure {
  overflow: hidden;
}
.plan-figure img {
  display: block;  /* an inline img adds a ~4px descender, which offsets the clip */
  transform: scale(1.2);
}

.plan-viewer-outer .carousel-arrow--prev {
  left: -25px;
}
.plan-viewer-outer .carousel-arrow--next {
  right: -25px;
}

.plan-selector {
  display: grid;
  grid-template-columns: max-content 1fr; /* label column sizes to the wider word ("Apartamentos"), so both button rows start at the same x */
  align-items: center;
  row-gap: 28px;
  column-gap: 28px;
  margin-top: 57px;
}
.selector-row {
  display: contents; /* label + button group become direct grid items, so letters align in columns across rows */
}
.selector-buttons {
  display: flex;
  gap: 14px;
}
.plan-selector .selector-label {
  font-family: var(--font-sans); /* XD Commuters Sans */
  font-size: 24px;               /* XD 24px */
  color: var(--ink);
  white-space: nowrap;
}
.plan-selector button {
  width: 48px;
  height: 48px;
  border-radius: 50%;
  border: 1px solid var(--ink);
  color: var(--ink);
  font-family: var(--font-sans);
  font-weight: 400;      /* XD Commuters Sans Regular */
  font-size: 24px;       /* XD 24px */
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 0.15s ease, color 0.15s ease;
}
.plan-selector button.is-active {
  background: var(--ink);
  color: var(--cream);
}
/* wrap buttons within each row on smaller screens */
@media (max-width: 900px) {
  .selector-buttons {
    flex-wrap: wrap;
    row-gap: 14px;
  }
}

/* ---- Plan view toggle (Planta / Isométrico + Planta Alta / Baja) -------- */
.plan-view-toggle,
.plan-floor-toggle {
  display: flex;
  gap: 8px;
  margin-top: 16px;
}
/* Two independent controls that happen to share a line, so they need a divider:
   without it "Isométrico" and "Planta Alta" sit 24px apart and read as four
   buttons in one group. It hangs off the loft group rather than the parent so
   apartments, which have nothing to its left, never draw a rule into space. */
.plan-floor-toggle {
  border-left: 1px solid rgba(84, 74, 51, 0.3);
  padding-left: 24px;
}
.plan-toggle-btn {
  padding: 6px 14px;
  border-radius: 20px;
  border: 1px solid var(--ink);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: 13px;
  font-weight: 500;
  line-height: 1.5;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  cursor: pointer;
  background: transparent;
  transition: background 0.15s ease, color 0.15s ease;
}
.plan-toggle-btn.is-active {
  background: var(--ink);
  color: var(--cream);
}
.plan-area-detail {
  font-size: 0.42em;
  font-weight: 400;
  opacity: 0.75;
  white-space: nowrap;
}

/* ---- Invierte ----------------------------------------------------------- */
.invierte-hero-copy {
  padding-top: 47px;
  padding-inline: 16px;
}
.invierte-hero-copy h1 {
  font-family: var(--font-sans);
  font-weight: 400;        /* XD Títulos */
  font-size: 40px;         /* XD 40px */
  line-height: 1.2;        /* XD 48px */
  letter-spacing: -0.02em; /* XD -20 */
  color: var(--ink);
}
.lead-form {
  margin-top: 90px;
  padding-inline: 16px;
}
.lead-form .form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 64px;
  row-gap: 50px;
}
.lead-form .field--full {
  grid-column: 1 / -1;
}
.lead-form .field {
  position: relative; /* anchors the floating <label> */
}
.lead-form .field input {
  width: 100%;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--ink);
  border-radius: 0;
  font-family: var(--font-slab);
  font-size: 30px;       /* XD 30px */
  line-height: 1.3;
  color: var(--ink);
  padding: 0 0 10px;
}
/* Placeholder is a single blank space (see invierte.html) — kept invisible so
   only the floating <label> is ever the visible cue. */
.lead-form .field input::placeholder {
  color: transparent;
}
.lead-form .field input:hover,
.lead-form .field input:focus {
  outline: none;
  border-bottom-color: var(--cream); /* cream underline on hover + active */
}
/* Floating label: sits exactly over the input text at rest, then lifts up and
   shrinks on focus — or once the field holds content — leaving room to type.
   Material-inspired, but restrained to match the site's motion north star. */
.lead-form .field label {
  position: absolute;
  left: 0;
  top: 0;                /* aligns with the input text (input has no top padding) */
  font-family: var(--font-slab);
  font-size: 30px;       /* matches the input so it overlays the text at rest */
  line-height: 1.3;
  color: var(--ink);
  white-space: nowrap;
  pointer-events: none;  /* clicks fall through to the input */
  transform-origin: left top;
  transition: transform 0.3s cubic-bezier(0.65, 0, 0.35, 1),
              color 0.3s ease;
}
.lead-form .field input:focus + label,
.lead-form .field input:not(:placeholder-shown) + label {
  transform: translateY(-0.9em) scale(0.55); /* sits just above its own input */
  color: var(--grey); /* steps back to a quiet label once you're typing */
}
/* Honeypot anti-spam field — pushed off-screen instead of display:none so naive
   bots still render and fill it (the JS then drops those submissions before they
   are POSTed). aria-hidden + tabindex=-1 keep it away from humans and AT. */
.lead-form .hp-field {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}
.lead-form .form-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 54px;
}
/* Submit button while a lead is being sent — dim + not-allowed cursor. */
.lead-form button[aria-busy="true"] {
  opacity: 0.6;
  cursor: not-allowed;
}
.lead-form .form-feedback {
  font-size: 20px;
  margin-top: 16px;
  text-align: right;
  min-height: 32px;
}

.invest-section {
  margin-top: 80px;
  padding-inline: 16px;
}
.invest-section .intro-rule {
  border: 0;
  border-top: 1px solid var(--ink);
  margin-bottom: 28px;
}
.invest-section h2 {
  font-family: var(--font-sans);
  font-weight: 400;        /* XD Títulos */
  font-size: 40px;         /* XD 40px */
  line-height: 1.2;        /* XD 48px */
  letter-spacing: -0.04em; /* XD -40 */
  color: var(--ink);
}
.invest-section .intro {
  margin-top: 24px;
  font-size: 30px;         /* XD Yorkten Slab Book 30px */
  line-height: 1.33;        /* XD line spacing 36px */
  letter-spacing: 0.025em; /* XD character spacing +25 */
  color: var(--ink);
  max-width: 1151px;
}
.invest-rows {
  margin-top: 66px;
}
.invest-row {
  display: grid;
  grid-template-columns: 400px 1fr;
  column-gap: 64px;
  border-top: 1px solid var(--ink);
  padding: 34px 0 48px;
}
.invest-row h3 {
  font-family: var(--font-sans);
  font-weight: 400;        /* XD Subtítulos: Commuters Sans Regular */
  font-size: 30px;         /* XD Subtítulos 30px */
  line-height: 1.333;      /* XD line spacing 40px */
  letter-spacing: -0.02em; /* XD -20 */
  color: var(--ink);
}
.invest-row p {
  font-size: 21px;         /* XD Párrafos 21px */
  line-height: 1.524;      /* XD 32px */
  color: var(--ink);
  max-width: 686px;
}

/* ---- Scroll-reveal ------------------------------------------------------ */
/* .reveal is added by JS (progressive enhancement) so content is never
   hidden when scripts don't run. */
.reveal {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity 0.7s ease, transform 0.7s ease;
}
.reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* ---- Desenfoque de imagen en reveal (Opción 6 · Aproximación) ----------- */
/* Las imágenes dentro de secciones reveal pasan de blur(16px) a nítidas en
   1.6s, solapándose con el fade+rise de la sección.
   CRÍTICO — en ESCRITORIO esta transición es SOLO FILTER, nunca `all` ni
   `transform`: initParallax (js/main.js) escribe
   style.transform="scale(1.16) translateY(…)" en cada frame de scroll sobre
   estas mismas imgs; transicionar transform aquí suavizaría el parallax y
   produciría movimiento borroso / descontrolado.
   El scale(1.16) del parallax, combinado con overflow:hidden en los contenedores,
   ya recorta el halo de desenfoque en los bordes — no se necesita escala extra.
   EN MÓVIL (≤700px) el contrato se invierte: el parallax está desactivado, así
   que nadie escribe transform y nadie tapa el halo — allí SÍ se transiciona
   transform, con una escala mínima que aterriza en scale(1). Ver la regla en
   el bloque @media (max-width: 700px). */
.carousel.reveal .slide img,
.render-pair .frame.reveal img,
.feature-cell.reveal figure img {
  filter: blur(16px);
  transition: filter 1.6s ease;
}
.carousel.reveal.is-visible .slide img,
.render-pair .frame.reveal.is-visible img,
.feature-cell.reveal.is-visible figure img {
  filter: blur(0);
}
/* En pares (slide--pair), la segunda imagen se retrasa levemente */
.carousel.reveal .slide--pair img + img {
  transition-delay: 0.12s;
}

/* ---- Sticky nav (appears on scroll-up) ---------------------------------- */
.sticky-nav {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 20;
  background: var(--bg);
  border-bottom: 1px solid rgba(84, 74, 51, 0.25); /* olive --ink @25% */
  transform: translateY(-100%);
  visibility: hidden;
  transition: transform 0.3s ease, visibility 0.3s;
}
.sticky-nav.is-visible {
  transform: translateY(0);
  visibility: visible;
}
.sticky-nav-inner {
  max-width: var(--container);
  margin-inline: auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 10px var(--gutter);
}
@media (min-width: 1280px) {
  .sticky-nav-inner {
    padding-inline: 0;
  }
}
.sticky-nav .sticky-logo img {
  width: 85px;
  height: auto;
}
.sticky-nav nav {
  display: flex;
  align-items: baseline;
  gap: 36px;
}
.sticky-nav nav a {
  font-family: var(--font-sans);
  font-size: 14px;         /* XD Commuters Sans Regular 14px */
  font-weight: 400;
  line-height: 1;
  letter-spacing: 0;       /* XD character spacing 0 */
  text-transform: uppercase;
  color: var(--ink);
}
.sticky-nav nav a.is-active {
  font-weight: 600;        /* XD SemiBold */
  text-shadow: 0.4px 0 0 currentColor, -0.4px 0 0 currentColor; /* TEMP faux-bold — see .main-nav a.is-active */
}
.sticky-nav .nav-burger {
  padding: 2px 0;
}

/* ---- Mobile menu overlay ------------------------------------------------ */
.mobile-menu {
  display: none;
}

/* ---- Mobile menu open/close veil (echoes the page-transition veil) -------
   Cream + olive panels rise from below to cover, then lift to reveal — the
   same two-tone choreography as css/transitions.css, minus the parallax logo.
   The .menu-veil layer is injected into <body> once by initMenu() in
   js/main.js and driven by the same is-armed / is-cover / is-reveal flips.
   Shares --vt-ease with a literal fallback so this file doesn't hard-depend on
   transitions.css. Durations mirror the COVER_MS / REVEAL_MS in js/main.js. */
.menu-veil {
  position: fixed;
  inset: 0;
  z-index: 9000;           /* over the menu (30) + header; under the nav veil (9999) */
  display: none;
  pointer-events: none;
}
.menu-veil.is-armed { display: block; }
.menu-veil.is-cover,
.menu-veil.is-reveal { pointer-events: auto; }  /* block leak-through while active */

.menu-veil-panel {
  position: absolute;
  inset: 0;
  transform: translateY(101vh);   /* waits just below the viewport */
}
.menu-veil-cream { z-index: 1; background: var(--cream); }
.menu-veil-olive { z-index: 2; background: var(--ink); }  /* olive sits over cream */

/* cover: rise from below to cover; cream leads (a little faster) */
.menu-veil.is-cover .menu-veil-olive { transform: translateY(0); transition: transform 0.64s var(--vt-ease, cubic-bezier(0.77, 0, 0.175, 1)); }
.menu-veil.is-cover .menu-veil-cream { transform: translateY(0); transition: transform 0.48s var(--vt-ease, cubic-bezier(0.77, 0, 0.175, 1)); }
/* reveal: olive lifts first, cream lingers then follows. Declared AFTER cover
   so it wins source-order while is-cover is still applied during the reveal. */
.menu-veil.is-reveal .menu-veil-olive { transform: translateY(-101vh); transition: transform 0.64s var(--vt-ease, cubic-bezier(0.77, 0, 0.175, 1)); }
.menu-veil.is-reveal .menu-veil-cream { transform: translateY(-101vh); transition: transform 0.81s var(--vt-ease, cubic-bezier(0.77, 0, 0.175, 1)); }

/* Reduced motion: initMenu() skips the veil (instant toggle); belt-and-braces
   so no panel or menu link ever animates. */
@media (prefers-reduced-motion: reduce) {
  .menu-veil-panel { transition: none !important; }
  .mobile-menu.is-open > a,
  .mobile-menu.is-open .lang-toggle-mobile { transition: none !important; }
}

/* ==========================================================================
   Responsive — per iPhone 14 Pro Max artboard (430×932)
   ========================================================================== */
@media (max-width: 900px) {
  .copy-section h1,
  .copy-section h2,
  .feature-grid h2,
  .ubicacion-section h2,
  .invierte-hero-copy h1,
  .invest-section h2 {
    font-size: 36px; /* desktop XD 40px, scaled down for tablet */
  }
  .invest-row {
    grid-template-columns: 1fr;
    row-gap: 18px;
  }
  .invest-row h3 {
    font-size: 28px; /* desktop XD 30px */
  }
  .plan-viewer {
    grid-template-columns: 1fr;
    row-gap: 32px;
    min-height: 0;
  }
  /* Half-width column can't fit all three partner logos — stack the Blur/social
     group over the partners so each gets the full width. */
  .footer-logos {
    grid-template-columns: 1fr;
    row-gap: 32px;
  }
}

@media (max-width: 1279px) {
  .main-nav {
    gap: 32px;
  }
}

@media (max-width: 1024px) {
  /* header: replace text nav with burger */
  .nav-row {
    align-items: center;
  }
  .sticky-nav nav {
    display: none;
  }
  .main-nav {
    display: none;
  }
  .nav-burger {
    display: block;
    width: 30px;
    padding: 6px 0;
  }
  .nav-burger span {
    display: block;
    height: 2px;
    background: var(--ink);
    margin: 7px 0;
  }

  .mobile-menu.is-open {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 36px;
    position: fixed;
    inset: 0;
    background: var(--bg);
    z-index: 30;
  }
  .mobile-menu > a {
    font-size: 24px;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink);
  }
  .mobile-menu > a.is-active {
    font-weight: 600;
    text-shadow: 0.4px 0 0 currentColor, -0.4px 0 0 currentColor; /* TEMP faux-bold — see .main-nav a.is-active */
  }
  .mobile-menu .lang-toggle-mobile {
    font-family: var(--font-sans);
    font-size: 14px;
    display: flex;
    gap: 0.35em;
  }
  .mobile-menu .menu-close {
    position: absolute;
    top: 20px;
    right: 24px;
    font-size: 32px;
    line-height: 1;
    color: var(--ink);
  }

  /* Links cascade in as the veil clears (staggered fade + rise). The hidden
     base state applies only while open; .links-in (added by initMenu() once
     the reveal starts) relays them in. The × stays visible so it's reachable
     immediately. */
  /* Direct-child scoped: the language toggle's ES/EN are <a> too, but they live
     inside .lang-toggle-mobile and must NOT pick up nav sizing, the faux-bold, or
     the per-link stagger — as nth-of-type children of their own wrapper they would
     otherwise fire at 0.05s/0.11s, ahead of the 0.35s their container animates on. */
  .mobile-menu.is-open > a,
  .mobile-menu.is-open .lang-toggle-mobile {
    opacity: 0;
    transform: translateY(12px);
    transition: opacity 0.5s var(--vt-ease, cubic-bezier(0.77, 0, 0.175, 1)),
                transform 0.5s var(--vt-ease, cubic-bezier(0.77, 0, 0.175, 1));
  }
  .mobile-menu.is-open.links-in > a,
  .mobile-menu.is-open.links-in .lang-toggle-mobile {
    opacity: 1;
    transform: translateY(0);
  }
  .mobile-menu.is-open.links-in > a:nth-of-type(1) { transition-delay: 0.05s; }
  .mobile-menu.is-open.links-in > a:nth-of-type(2) { transition-delay: 0.11s; }
  .mobile-menu.is-open.links-in > a:nth-of-type(3) { transition-delay: 0.17s; }
  .mobile-menu.is-open.links-in > a:nth-of-type(4) { transition-delay: 0.23s; }
  .mobile-menu.is-open.links-in > a:nth-of-type(5) { transition-delay: 0.29s; }
  .mobile-menu.is-open.links-in .lang-toggle-mobile { transition-delay: 0.35s; }
}

@media (max-width: 700px) {
  :root {
    --gutter: 24px;
  }
  body {
    font-size: 18px;
    line-height: 1.55;
  }

  /* header: compact mobile sizing */
  .lang-toggle {
    display: none;
  }
  .sticky-nav .sticky-logo img {
    width: 80px;   /* wider to track the enlarged header logo (was 57px) */
  }
  .sticky-nav-inner {
    padding: 8px var(--gutter);
  }
  .nav-row {
    padding-top: 18px;
    padding-bottom: 18px;
  }
  .logo-link img,
  .logo-link svg {
    width: 90px;
    margin-bottom: -14px;
  }

  /* hero: full bleed per mobile artboard */
  .container--hero {
    padding-inline: 0;
  }
  /* DOS RATIOS MÓVILES, deliberadamente (CONVENCIÓN) — el 1184/656 de
     escritorio es el ancho del artboard de 1280 y en el móvil no significa
     nada: dejaba los bloques en ~212px de alto, un sello de correos justo
     donde va el material más caro. Se sustituye por:

       HERO      430/500 vertical (≈0.86) — como lo dibujó el XD. Es lo
                 primero que se ve y llena la pantalla; que sea el bloque
                 más vertical del móvil es una decisión, no un descuido.
       CONTENIDO 500/430 apaisado (≈1.16) — la misma proporción girada,
                 a sangre (ver el full-bleed abajo). 430×370.

     El apaisado no es solo estética: los renders de recursos/ son TODOS
     16:9, así que cada punto de verticalidad se paga tirando ancho con
     object-fit: cover. El 430/500 solo dejaba ver el 48% del render; el
     500/430 a sangre enseña el 65%.
     Los pares (.slide--pair, .render-pair) y el feature-grid quedan FUERA:
     ya se apilan a 1 columna y traen su propio ratio vertical (~0.82). */
  .img-band--hero img,
  .container--hero .carousel .slide img,
  .container--hero .video-block video,
  .container--hero .video-block .poster {
    aspect-ratio: 430 / 500;
  }
  .media-block.carousel .slide:not(.slide--pair) img,
  .media-block.img-band img,
  .media-block.video-block video,
  .media-block.video-block .poster {
    aspect-ratio: 500 / 430;
  }
  /* A SANGRE — los bloques de media de contenido se comen el gutter del
     .container y tocan los bordes de la pantalla, mientras el texto
     conserva el suyo. No es un patrón nuevo: el hero ya sangra (ver
     .container--hero arriba); esto lo extiende al resto del media.
     Se usa margin negativo y no width:100vw a propósito — 100vw incluye
     la barra de scroll en algunos navegadores y provoca scroll horizontal.
     El margen negativo compensa exactamente el padding del contenedor. */
  .container:not(.container--hero) > .media-block {
    margin-inline: calc(var(--gutter) * -1);
  }
  /* Halo del blur sin parallax que lo tape — el parallax está desactivado por
     debajo de este breakpoint (ver initParallax en js/main.js), y era su
     scale(1.16) lo que dejaba fuera de cuadro el borde difuminado del
     blur(16px) del reveal. Se sustituye por una escala mínima que SOLO existe
     mientras la imagen está borrosa y aterriza en scale(1): el halo queda
     fuera al entrar y la imagen acaba entera, que es justamente lo que
     buscábamos al quitar el parallax.
     Transicionar `transform` aquí es seguro PORQUE en móvil el JS ya no lo
     escribe. En escritorio seguiría siendo un error — ver la nota CRÍTICA en
     la sección de reveal. */
  .carousel.reveal .slide img,
  .render-pair .frame.reveal img,
  .feature-cell.reveal figure img {
    transform: scale(1.06);
    transition: filter 1.6s ease, transform 1.6s ease;
  }
  .carousel.reveal.is-visible .slide img,
  .render-pair .frame.reveal.is-visible img,
  .feature-cell.reveal.is-visible figure img {
    transform: scale(1);
  }
  /* (el override que reponía las flechas del hero aquí ya no hace falta: desde
     que el hero de ubicacion es un carrusel real, nunca se ocultan — ver la
     nota junto a .carousel--hero arriba.) */
  .carousel-arrow {
    width: 44px;
    height: 44px;
  }
  .carousel-arrow svg {
    width: 20px;
    height: 20px;
  }
  .carousel-arrow--prev {
    left: 8px;
  }
  .carousel-arrow--next {
    right: 8px;
  }

  .copy-section {
    padding-top: 32px;
  }
  .copy-section h1,
  .copy-section h2,
  .feature-grid h2,
  .ubicacion-section h2,
  .invierte-hero-copy h1,
  .invest-section h2 {
    font-size: 32px; /* Commuters Sans, scaled from XD 40px for the 430px artboard */
    line-height: 1.2;
  }
  .copy-section .lead {
    margin-top: 16px;
  }
  .copy-section .btn-pill {
    margin-top: 32px;
  }
  /* Ritmo vertical (CONVENCIÓN) — versión móvil; ver nota en la regla desktop. */
  .container:not(.container--hero) > .media-block:not(:first-child) {
    margin-top: 40px;
  }

  .carousel .slide.slide--pair {
    grid-template-columns: 1fr;
    gap: 24px;
  }
  .feature-grid {
    grid-template-columns: 1fr;
    row-gap: 48px;
    margin-top: 40px;
  }
  .feature-grid .btn-pill {
    margin-left: 0;
    align-self: flex-start;
  }
  .render-pair {
    grid-template-columns: 1fr;
    gap: 24px;
  }
  .map-band {
    margin-top: 48px;
  }
  /* A SANGRE — el mapa se come el gutter del .container y toca ambos bordes de
     la pantalla, igual que los .media-block. El enlace .map-fallback es hermano
     de .map-embed, así que conserva su margen. */
  .map-band .map-embed {
    margin-inline: calc(var(--gutter) * -1);
  }
  .ubicacion-section .body,
  .invierte-hero-copy,
  .lead-form,
  .invest-section {
    padding-inline: 0;
  }

  .lead-form .form-grid {
    grid-template-columns: 1fr;
    row-gap: 40px;
  }
  .lead-form .field--bare {
    display: none; /* desktop-only second underline from the XD layout */
  }
  .lead-form .field input,
  .lead-form .field label {
    font-size: 24px; /* label matches input so it stays aligned (float is em-based) */
  }
  .invest-section .intro {
    font-size: 22px;
  }
  .invest-row p {
    font-size: 18px;
  }

  .plan-viewer {
    padding: 24px;
  }
  .plan-info {
    padding-block: 0; /* the container supplies the padding when the viewer stacks */
  }
  .plan-floor-toggle {
    /* The four buttons wrap here, so each group already has its own line — the
       rule would just hang off the left edge of the second one. */
    border-left: 0;
    padding-left: 0;
  }
  .plan-title {
    font-size: 36px;
  }
  .plan-selector {
    grid-template-columns: 1fr; /* stack label above its buttons on small screens */
    gap: 12px;
  }
  .selector-buttons {
    gap: 12px;
  }
  .plan-selector button {
    width: 48px;
    height: 48px;
    font-size: 20px;
  }
  .plan-viewer-outer .carousel-arrow--prev {
    left: 8px;
  }
  .plan-viewer-outer .carousel-arrow--next {
    right: 8px;
  }

  .site-footer {
    /* no lateral margin: the .footer-wrap gutter already insets the box, so it
       lines up with the container/images instead of doubling the inset */
    margin: 64px 0 16px;
    padding: 32px 24px;
    /* ORDEN MÓVIL DEL FOOTER (decisión de Chus): el wordmark BLUR abre el footer,
       encima de los teléfonos, y los iconos sociales se quedan abajo haciendo de
       separador entre el texto y los logos de partners. Para poder reordenar hay
       que aplanar la jerarquía: el footer pasa a ser la rejilla y sus nietos
       suben a ser ítems suyos (ver `display: contents` más abajo). El SVG del
       borde autodibujado NO se ve afectado — es position:absolute, o sea que
       está fuera de flujo y nunca llega a ser ítem de la rejilla. */
    display: grid;
    row-gap: 28px;
  }
  /* Se disuelven los dos wrappers para que .logo-blur, .footer-social y
     .footer-partners pasen a ser hermanos de .footer-cols y `order` pueda
     colocarlos. Son <div> sin semántica, así que `display: contents` no quita
     nada al árbol de accesibilidad. Los selectores `.footer-logos .x` siguen
     funcionando: contents saca la caja, no el nodo del DOM. */
  .footer-logos,
  .footer-brand {
    display: contents;
  }
  .footer-copyright {
    margin: 0 0 24px;   /* aligns to the mobile footer box edges */
    flex-direction: column;    /* stack so the two credits don't collide */
    align-items: flex-start;
    gap: 6px;
    font-size: 11px;
  }
  .footer-cols {
    grid-template-columns: 1fr;
    row-gap: 28px;
  }
  .footer-contact {
    font-size: 18px;
    /* Los datos de contacto van DEBAJO de la descripción de BLUR (decisión de
       Chus). Solo aplica aquí: a partir de 700px .footer-cols vuelve a ser de
       dos columnas (contacto izquierda / nota derecha) y no hay "debajo".
       .footer-cols conserva su propia rejilla — NO lleva `display: contents` —
       así que basta con reordenar sus dos hijos, sin tocar el aplanado del
       resto del footer. La nota no es interactiva, así que el orden de foco no
       se resiente: los únicos enlaces de la zona están dentro de este bloque y
       entre ellos mantienen su orden. */
    order: 1;
  }
  .footer-note {
    font-size: 17px;
  }
  /* El wordmark abre el footer. Es el único que necesita `order`: una vez
     aplanada la jerarquía el resto ya queda en el orden correcto por DOM
     (.footer-cols -> .footer-social -> .footer-partners). */
  .footer-logos .logo-blur {
    order: -1;
    width: 100%;
    max-width: 140px;       /* se encoge por debajo de 140 si el ancho aprieta */
  }
  /* Ya no va pegado al wordmark (que se ha ido arriba), así que se le quita el
     hueco lateral del XD y se le da aire por encima: hace de separador entre el
     párrafo y los logos de partners. */
  .footer-social {
    margin-left: 0;
    margin-top: 20px;
  }
  .footer-logos .icon-instagram {
    margin-left: 15px;
  }
  .footer-logos .icon-youtube,
  .footer-logos .icon-instagram {
    flex: none;             /* los iconos no se encogen; el wordmark sí */
  }
  /* Rejilla proporcional: 97fr/118fr/82fr SON los anchos del XD, así que los tres
     escalan por el mismo factor (se conserva la proporción del diseño) y siempre
     caben, hasta 320px. `end` mantiene la misma línea base inferior que en
     escritorio (.footer-partners usa align-items: flex-end). */
  .footer-partners {
    display: grid;
    grid-template-columns: 97fr 118fr 82fr;
    column-gap: 20px;
    align-items: end;
  }
  /* Aquí la fila es una rejilla y el hueco lo da `column-gap`, así que hay que
     anular los márgenes de escritorio: el `margin-left: auto` de Muchagana
     empujaría su logo al borde derecho DE SU COLUMNA, y los 26px de Mallol le
     comerían ancho. Misma especificidad (0,2,1) que las reglas `:has()` de
     arriba, pero van después, así que ganan. */
  .footer-logos .footer-partners a {
    margin: 0;
  }
  .footer-logos .footer-partners img {
    width: 100%;            /* especificidad 0,2,1 — tiene que ganar a las reglas
                               .footer-logos .logo-* (0,2,0) del bloque desktop */
    height: auto;
    margin: 0;
  }
}

/* ==========================================================================
   Chrome — disabled nav link (Brokers)
   ========================================================================== */
.nav-link--disabled { opacity: .45; cursor: default; pointer-events: none; }

/* ---- Ubicación: Google Maps embed -------------------------------------- */
.map-embed { position: relative; aspect-ratio: 16 / 9; background: var(--offwhite); }
.map-embed iframe,
.map-embed .map-canvas { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
/* Calm on-brand surface while the Maps script loads (and if the key is missing/blocked). */
.map-canvas { background: var(--offwhite); }
.map-canvas--error { display: grid; place-items: center; padding: 24px; text-align: center; }
.map-canvas--error::after {
  content: "El mapa no se pudo cargar. Usa el enlace de abajo para ver la ubicación en Google Maps.";
  max-width: 34ch; font-family: var(--font-sans); font-size: 15px; line-height: 1.4; color: var(--grey);
}
/* The failure text above is Spanish; /en/ pages declare lang="en" and get this instead.
   Only CSS-generated copy on the site — everything else lives in markup or js/main.js. */
html[lang="en"] .map-canvas--error::after {
  content: "The map couldn’t load. Use the link below to view the location on Google Maps.";
}
.map-fallback { margin-top: 12px; font-family: var(--font-sans); font-size: 16px; text-align: center; }
.map-fallback a { text-decoration: underline; }
@media (max-width: 700px) { .map-embed { aspect-ratio: 4 / 3; } }

/* ==========================================================================
   Reduced motion — disable decorative animation, keep everything usable
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .reveal {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  /* belt-and-braces: JS no añade .reveal con reducedMotion=true, pero si
     alguna imagen llegara a tener el estado inicial de blur, lo cancelamos.
     `transform` entra aquí desde que el móvil usa scale(1.06) como tapadera
     del halo: sin esta línea, una imagen que llegara al estado inicial se
     quedaría congelada al 106% para siempre. */
  .carousel.reveal .slide img,
  .render-pair .frame.reveal img,
  .feature-cell.reveal figure img {
    filter: none !important;
    transform: none !important;
    transition: none !important;
  }
  .parallax-img {
    transform: none !important;
  }
  .carousel.run .slide.to,
  .carousel.run .slide.to img,
  .carousel.run .slide::after {
    transition: none !important;
  }
  .sticky-nav {
    transition: none;
  }
  .lead-form .field label {
    transition: none !important; /* label still floats, just no animation */
  }
}

/* Ensure the hidden attribute always wins over component display rules
   (plan view/floor toggles + details block toggle their `hidden` attr in JS). */
[hidden] { display: none !important; }
