/* ═══ nosotros.css ═══ */
/* ============================================================
   NOSOTROS

   Dos bloques, tres generaciones.

   1. EL ORIGEN — medio hero: retrato del abuelo a un costado,
      el relato al otro. 5/7, nunca 6/6.
   2. LA FAMILIA — padre e hijo, uno al lado del otro, pero con
      proporciones distintas y desfase vertical. Dos retratos
      idénticos en una grilla perfecta se leen como "página de
      equipo" de plantilla; distintas proporciones y una caída
      desplazada se leen como una publicación.

   Los espacios de foto vacíos NO son cajas grises. Son placas
   con la especificación de lo que va adentro — sirven de brief
   de fotografía y se ven deliberados hasta que llegue la imagen.
   ============================================================ */

.nosotros {
  background: var(--hueso);
  color: var(--tinta);
  padding-block: clamp(72px, 9vw, 132px);
}

/* ---- Bloque 1: el origen ------------------------------------ */
.origen {
  display: grid;
  gap: clamp(32px, 5vw, 72px);
}
@media (min-width: 900px) {
  .origen {
    grid-template-columns: 5fr 7fr;   /* nunca mitades exactas */
    align-items: center;
  }
}

.origen__texto { max-width: 54ch; }

.nosotros__eyebrow {
  margin: 0 0 var(--s4);
  font-size: var(--eyebrow);
  font-weight: 500;
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--piedra);
}

.origen__titulo {
  margin: 0 0 clamp(24px, 3vw, 36px);
  font-size: var(--d2);
  line-height: 1.06;
  letter-spacing: -0.015em;
  max-width: 17ch;
}

.origen__texto p {
  margin: 0 0 1.15em;
  color: var(--piedra);
  font-size: var(--lead);
  line-height: 1.62;
}
.origen__texto p:last-child { margin-bottom: 0; }

/* ---- Bloque 2: la familia ----------------------------------- */
.familia {
  margin-top: clamp(72px, 9vw, 132px);
  padding-top: clamp(48px, 6vw, 80px);
  border-top: 1px solid var(--linea);
  display: grid;
  gap: clamp(48px, 6vw, 80px);
}
@media (min-width: 900px) {
  .familia {
    grid-template-columns: 1fr 1fr;
    gap: clamp(48px, 6vw, 96px);
    align-items: start;
  }
  /* Sin desfase: padre e hijo arrancan a la misma altura.
     El argumento a favor del escalonado era evitar la "grilla de
     equipo", pero rompía la alineación con el resto de la página, y
     una jerarquía visual entre los dos no corresponde. La variedad
     la aportan las proporciones distintas de cada retrato (1:1 y
     3:4), que ya alcanza. */
  .familia { align-items: start; }
}

/* Los tres nombres al mismo tamaño y más grandes: son las personas,
   no un pie de foto. El del abuelo estaba en 11px en versalitas
   —tratamiento de leyenda— mientras padre e hijo iban en display. */
.persona__nombre {
  display: block;
  margin: clamp(20px, 2.4vw, 28px) 0 6px;
  font-family: var(--serif);
  font-variation-settings: var(--fraunces-texto);
  font-weight: 400;
  font-size: clamp(1.75rem, 1.1rem + 2.4vw, 2.75rem);
  line-height: 1.08;
  letter-spacing: -0.015em;
  color: var(--tinta);
}
.persona__pie { display: block; }
.persona__pie .persona__nombre { margin-top: clamp(18px, 2vw, 24px); }

.persona__rol {
  display: block;
  margin: 0 0 clamp(16px, 2vw, 22px);
  font-size: var(--small);
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--laton);          /* ~2.9:1 sobre hueso: por eso va en
                                   versalitas y nunca como texto corrido */
}
.persona__bio {
  margin: 0;
  max-width: 46ch;
  color: var(--piedra);
  line-height: 1.62;
}

/* ---- Retratos ----------------------------------------------- */
.retrato { margin: 0; }
/* Los dos retratos no salen de la misma sesión: el del padre tiene
   fondo gris degradado y el del hijo blanco puro. Sobre hueso, uno
   se recorta y el otro flota. El filete y el fondo blanco común
   hacen que cada uno se lea como lámina montada y la diferencia
   pase a ser fotográfica en vez de accidental.
   Arreglo de fondo: una sesión nueva con los tres retratos —
   mismo fotógrafo, mismo fondo, mismo grado. */
.retrato img {
  display: block;
  width: 100%;
  height: auto;
  object-fit: cover;
  background: #fff;
  border: 1px solid var(--linea);
}

/* Proporciones deliberadamente distintas entre las tres personas */
.retrato--abuelo img,
.retrato--abuelo .retrato__hueco { aspect-ratio: 4 / 5; }
.retrato--padre img,
.retrato--padre .retrato__hueco  { aspect-ratio: 1 / 1; }
.retrato--hijo img,
.retrato--hijo .retrato__hueco   { aspect-ratio: 3 / 4; }

/* El hueco: una placa con la especificación de la foto que falta.
   No es un marcador de posición gris — es un encargo escrito. */
.retrato__hueco {
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 6px;
  padding: clamp(20px, 2.4vw, 28px);
  background: var(--cal);
  border: 1px solid var(--linea);
  color: var(--piedra);
}
.retrato__hueco b {
  display: block;
  font-family: var(--serif);
  font-variation-settings: var(--fraunces-texto);
  font-weight: 400;
  font-size: 1.375rem;
  line-height: 1.2;
  letter-spacing: -0.01em;
  color: var(--tinta);
  margin-bottom: 4px;
}
.retrato__hueco span {
  font-size: var(--micro);
  font-weight: 500;
  letter-spacing: 0.10em;
  text-transform: uppercase;
}

.retrato__pie {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--linea);
  font-size: var(--micro);
  font-weight: 500;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--piedra);
}

/* .muestra y .pendiente se movieron a tokens.css: son
   convención de todo el sitio. */

/* ---- Cuando Nosotros es su propia página ---------------------
   No hay hero que empuje el contenido, así que la sección tiene
   que despejar el header fijo por su cuenta. */
.nosotros--pagina {
  padding-top: calc(var(--header-h) + clamp(56px, 7vw, 104px));
}

/* ═══ proyectos.css ═══ */
/* ============================================================
   PROYECTOS

   La página resuelve un problema que no es de diseño sino de
   negocio: hay DOS cosas distintas que suelen mezclarse en un
   mismo grid y no deberían.

   1. LO QUE SE PUEDE COMPRAR — cuatro proyectos. Pocos, grandes,
      con plano, ficha y una acción. Acá se vende.
   2. LO CONSTRUIDO — veinte viviendas ya entregadas. Nadie puede
      comprarlas: son la prueba de que la empresa cumple.

   Meterlas en una sola grilla de catorce tarjetas hace invisibles
   las cuatro que importan y sugiere que las otras están a la
   venta. Por eso van con tratamientos opuestos: las primeras como
   fichas grandes, las segundas como un ÍNDICE tipográfico.

   El índice no es un recurso de emergencia por falta de fotos: es
   más creíble que una galería, porque cada línea se puede
   verificar. Y ningún competidor de Santa Cruz publica el suyo.
   ============================================================ */

.pagina {
  padding-top: calc(var(--header-h) + clamp(48px, 6vw, 88px));
  padding-bottom: clamp(56px, 7vw, 96px);
  background: var(--hueso);
}
.pagina__eyebrow {
  margin: 0 0 var(--s4);
  font-size: var(--eyebrow);
  font-weight: 500;
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--piedra);
}
.pagina__titulo {
  margin: 0 0 var(--s5);
  font-size: var(--d2);
  line-height: 1.04;
  letter-spacing: -0.015em;
  max-width: 16ch;
}
.pagina__entrada {
  margin: 0;
  max-width: 56ch;
  color: var(--piedra);
  font-size: var(--lead);
  line-height: 1.6;
}

/* ============================================================
   En venta
   ============================================================ */
.venta { background: var(--hueso); padding-bottom: clamp(56px, 7vw, 96px); }

.proy {
  padding-top: clamp(40px, 5vw, 64px);
  border-top: 1px solid var(--linea);
}
.proy + .proy { margin-top: clamp(48px, 6vw, 80px); }

.proy__grid { display: grid; gap: clamp(28px, 4vw, 56px); }
@media (min-width: 900px) {
  .proy__grid { grid-template-columns: 7fr 5fr; align-items: start; }
  /* El segundo proyecto invierte el orden: el plano cae al otro
     lado. Dos fichas idénticas seguidas se leen como plantilla. */
  .proy--inv .proy__grid > :first-child { order: 2; }
}

/* La coordenada: dónde queda, en el lenguaje en que se habla la
   ciudad. Es lo primero que un cruceño necesita saber. */
.proy__coord {
  margin: 0 0 var(--s4);
  font-size: var(--micro);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cobalto);
}
.proy__estado::before {
  content: "";
  display: inline-block;
  width: 6px; height: 6px;
  margin-right: 8px;
  border-radius: 999px;
  background: currentColor;
  vertical-align: 0.15em;
}
.proy__nombre {
  margin: 0 0 var(--s4);
  font-size: var(--d3);
  line-height: 1.1;
  letter-spacing: -0.01em;
}
.proy__texto {
  margin: 0 0 clamp(24px, 3vw, 32px);
  max-width: 48ch;
  color: var(--piedra);
  line-height: 1.62;
}

/* Ficha técnica en filetes: se lee como un documento, no como
   una tarjeta de producto. */
.ficha { margin: 0 0 clamp(24px, 3vw, 32px); }
.ficha__fila {
  display: flex;
  justify-content: space-between;
  gap: var(--s5);
  padding-block: 11px;
  border-top: 1px solid var(--linea);
  font-size: var(--small);
}
.ficha__fila:last-child { border-bottom: 1px solid var(--linea); }
.ficha__fila dt { margin: 0; color: var(--piedra); }
.ficha__fila dd {
  margin: 0;
  text-align: right;
  color: var(--tinta);
  font-variant-numeric: lining-nums tabular-nums;
}

.proy__acciones { display: flex; flex-wrap: wrap; gap: 12px; }
/* .btn-linea y .btn-solido se movieron a tokens.css: los usan
   cinco secciones en cuatro páginas, no solo esta. */

/* ---- Los planos ---------------------------------------------
   Los isométricos de Calle 8 tienen canal alfa: se apoyan directo
   sobre la página, sin marco, sin tarjeta, sin sombra. Todas las
   constructoras locales meten sus planos en un rectángulo blanco
   con borde; esto es lo contrario, y es la señal de que el plano
   es una pieza de diseño y no un anexo. */
.planos { display: grid; gap: clamp(16px, 2vw, 28px); }
.plano { margin: 0; }
.plano img { display: block; width: 100%; height: auto; }
.plano--alfa img { background: none; }
.plano--foto img { border-radius: 14px; border: 1px solid var(--linea); }
.plano__pie {
  margin-top: 10px;
  font-size: var(--micro);
  font-weight: 500;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--piedra);
}
@media (min-width: 560px) {
  .planos--par { grid-template-columns: 1fr 1fr; }
}


/* ============================================================
   Trayectoria — el índice
   ============================================================ */
.trayectoria { background: var(--hueso); padding-block: clamp(56px, 7vw, 96px); }

.indice { width: 100%; border-collapse: collapse; }
.indice caption { text-align: left; }

.indice th {
  padding: 0 0 12px;
  border-bottom: 1px solid var(--tinta);
  text-align: left;
  font-size: var(--micro);
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--tinta);
}
.indice td {
  padding-block: 15px;
  border-bottom: 1px solid var(--linea);
  font-size: var(--small);
  color: var(--tinta);
  vertical-align: baseline;
}
.indice td.ref { color: var(--piedra); }
.indice th.num, .indice td.num {
  text-align: right;
  font-variant-numeric: lining-nums tabular-nums;
}
.indice tbody tr:hover td { background: var(--cal); }

.indice .obra-activa td { color: var(--cobalto); font-weight: 500; }
.indice .obra-activa td.ref { color: var(--cobalto); }

.indice tfoot td {
  padding-top: 18px;
  border-bottom: 0;
  border-top: 1px solid var(--tinta);
  font-weight: 600;
  color: var(--tinta);
}

.indice__nota {
  margin: clamp(24px, 3vw, 36px) 0 0;
  max-width: 74ch;
  font-size: 12px;
  line-height: 1.65;
  color: var(--piedra);
}

/* En teléfono la tabla se vuelve fichas apiladas. Nunca un
   scroll horizontal: si el usuario tiene que arrastrar para leer
   una columna, la columna está perdida. */
@media (max-width: 699px) {
  .indice, .indice tbody, .indice tr, .indice td { display: block; width: 100%; }
  .indice thead, .indice tfoot tr td:not(:first-child) { display: none; }
  .indice tr { padding-block: 14px; border-bottom: 1px solid var(--linea); }
  .indice td { padding: 0; border: 0; }
  .indice td.zona { font-weight: 600; margin-bottom: 2px; }
  .indice td.num, .indice td.ref {
    display: inline;
    text-align: left;
    color: var(--piedra);
  }
  .indice td.num::before { content: " · "; }
  .indice td.num[data-u]::after { content: " " attr(data-u); }
  .indice tfoot td { padding-top: 16px; border-top: 1px solid var(--tinta); }
}

/* Enlace dentro del índice. Va como bloque con alto mínimo: en línea
   dentro de una celda no llegaba a los 44px de área táctil, que fue
   justo lo que detectó la auditoría. */
.indice__enlace {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  color: var(--cobalto);          /* 9.90:1 sobre hueso */
  text-decoration: underline;
  text-decoration-color: var(--linea);
  text-underline-offset: 3px;
  font-weight: 600;
  transition: text-decoration-color 160ms linear;
}
.indice__enlace:hover { text-decoration-color: var(--cobalto); }

/* ---- Registro plegable ---------------------------------------
   La tabla completa pasó de ser un muro de once filas a un
   desplegable. El resumen por zona cuenta la misma verdad en siete
   líneas —"Av. Centenario · 11 viviendas" dice más que cinco
   renglones sueltos— y el detalle queda para quien quiera auditarlo.
   Se usa <details> nativo, igual que en las preguntas frecuentes. */
.registro {
  margin-top: clamp(28px, 3.5vw, 44px);
  border-top: 1px solid var(--linea);
}
.registro summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s5);
  min-height: 56px;
  padding-block: 12px;
  cursor: pointer;
  list-style: none;
  font-size: var(--small);
  font-weight: 600;
  color: var(--cobalto);          /* 9.90:1 */
}
.registro summary::-webkit-details-marker { display: none; }
.registro summary:focus-visible { outline: 2px solid var(--cobalto); outline-offset: 3px; }
.registro[open] summary { border-bottom: 1px solid var(--linea); }
.registro .faq__chevron { stroke: var(--cobalto); }
.registro[open] .faq__chevron { transform: rotate(180deg); }

.registro__cuerpo { padding-top: clamp(24px, 3vw, 36px); }
.registro__cuerpo .indice { margin-bottom: var(--s5); }
.registro__sub {
  margin: clamp(32px, 4vw, 48px) 0 var(--s4);
  font-size: var(--d3);
  line-height: 1.1;
  letter-spacing: -0.01em;
}
.registro[open] .registro__cuerpo { animation: faq-abre 320ms var(--ease) both; }
@media (prefers-reduced-motion: reduce) {
  .registro[open] .registro__cuerpo { animation: none; }
  .registro .faq__chevron { transition: none; }
}

/* ============================================================
   La columna de medios de cada proyecto

   El problema que resuelve: en escritorio, con texto a un lado e
   imágenes al otro, está claro de qué proyecto es cada plano. Al
   apilarse en celular quedaba una lista de texto seguida de una
   lista de imágenes, y a la altura del segundo proyecto ya no se
   sabía a cuál pertenecían.

   Tres cambios:
     · cada proyecto abre con SU fachada — la imagen que nadie
       confunde, y la que ya identifica al proyecto en su página
     · en celular los medios van PRIMERO: cada bloque empieza con
       una foto de fachada, que funciona de separador entre uno y
       otro mucho mejor que un filete
     · el pie de cada imagen nombra el proyecto, no solo la planta
   ============================================================ */
.proy__medios { display: grid; gap: clamp(16px, 2vw, 24px); }

.proy__portada { margin: 0; }
.proy__portada img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 10;
  object-fit: cover;
  border-radius: 14px;
  border: 1px solid var(--linea);
}

@media (max-width: 899px) {
  /* La fachada abre el bloque. Es el mismo orden que la página de
     cada proyecto —imagen, después el nombre— así que el sitio se
     lee igual en los dos lugares. */
  .proy__grid { display: flex; flex-direction: column; }
  .proy__medios { order: -1; }

  /* Y el corte entre proyectos se hace evidente: más aire y un
     filete más marcado que el de las fichas. */
  .proy + .proy { margin-top: clamp(56px, 12vw, 72px); }
  .proy { padding-top: clamp(32px, 7vw, 44px); }

  /* La fachada gana altura en vertical: a 375px un 16:10 queda
     como una franja y no muestra la casa. */
  .proy__portada img { aspect-ratio: 4 / 3; }
}

/* Enlace dentro de un pie de galería. Área táctil propia: la celda
   no la da, y sin esto queda en 18px de alto. */
.gal__enlace {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  color: var(--cobalto);
  text-decoration: underline;
  text-decoration-color: var(--linea);
  text-underline-offset: 3px;
}
.gal__enlace:hover { text-decoration-color: var(--cobalto); }
.entrega__pie .gal__enlace { color: var(--hueso); text-decoration-color: rgba(250,248,245,.4); }
.entrega__pie .gal__enlace:hover { text-decoration-color: var(--hueso); }

/* ============================================================
   EL DIRECTORIO

   Cada proyecto con página propia, en la primera pantalla. Antes
   había que bajar por secciones largas —ocho fotos de calle 11, la
   banda de Casa Alicia— para descubrir que esos proyectos tenían
   su propia página. Con el directorio arriba, la página índice
   vuelve a hacer un solo trabajo: enrutar.

   Las de venta primero, y marcadas: es lo único que se compra.
   ============================================================ */
.directorio { background: var(--hueso); padding-block: clamp(48px, 6vw, 80px); }

.dir {
  display: grid;
  gap: clamp(20px, 2.6vw, 32px);
  margin-top: clamp(28px, 3.5vw, 44px);
}
@media (min-width: 640px)  { .dir { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1050px) { .dir { grid-template-columns: repeat(3, 1fr); } }

.dir__item {
  display: block;
  color: inherit;
  text-decoration: none;
}
.dir__lamina { margin: 0 0 var(--s4); overflow: hidden; border-radius: 16px; }
.dir__lamina img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border: 1px solid var(--linea);
  border-radius: 16px;
  transition: transform 200ms var(--ease);   /* era 420ms: una respuesta a hover tiene que ser inmediata, no deliberada */
}
.dir__item:hover .dir__lamina img { transform: scale(1.03); }

.dir__estado {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  margin: 0 0 var(--s3);
  font-size: var(--micro);
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--piedra);
}
.dir__estado::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}
/* Lo que está en venta se distingue: es lo único accionable. */
.dir__item--venta .dir__estado { color: var(--cobalto); }

.dir__nombre {
  margin: 0 0 4px;
  font-size: clamp(1.25rem, 1.1rem + 0.7vw, 1.625rem);
  line-height: 1.15;
  letter-spacing: -0.015em;
}
.dir__item:hover .dir__nombre { text-decoration: underline; text-underline-offset: 4px; }
.dir__zona  { margin: 0 0 var(--s3); font-size: var(--small); color: var(--piedra); }
.dir__dato  { margin: 0; font-size: var(--small); font-weight: 500; color: var(--tinta); }

/* El área táctil la da la tarjeta entera, muy por encima de 44px. */
.dir__item:focus-visible {
  outline: 2px solid var(--cobalto);
  outline-offset: 6px;
  border-radius: 18px;
}

@media (prefers-reduced-motion: reduce) {
  .dir__lamina img, .dir__item:hover .dir__lamina img { transition: none; transform: none; }
}

/* Las zonas que todavía no tienen página propia van como registro,
   no como enlace: un <div>, no un <a>. Un enlace que no lleva a
   ningún lado es peor que ningún enlace. La foto sí se puede ampliar
   con el visor, que es lo único que hay para ver.

   Quedan un punto más quietas —sin el subrayado al pasar, el punto
   de estado en gris piedra— para que se distinga de un vistazo qué
   se puede abrir y qué no. */
.dir__item--registro .dir__lamina img { cursor: zoom-in; }
.dir__item--registro:hover .dir__lamina img { transform: none; }
.dir__item--registro .dir__nombre { font-size: clamp(1.125rem, 1.05rem + 0.5vw, 1.375rem); }

.dir__nota {
  margin: clamp(28px, 3.5vw, 40px) 0 0;
  max-width: 62ch;
  font-size: var(--small);
  line-height: 1.6;
  color: var(--piedra);
}

/* Los lotes en preventa no tienen proyecto todavía, así que no tienen
   imagen. En vez de una caja gris, una placa que dice qué es: es el
   mismo criterio que el resto del sitio usa para lo que aún no existe
   —decir la ausencia en vez de disimularla. */
.dir__hueco {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 4 / 3;
  margin-bottom: var(--s4);
  border: 1px dashed var(--linea);
  border-radius: 16px;
  background: var(--cal);
}
.dir__hueco span {
  font-size: var(--micro);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--piedra);
}
.dir__item--placa .dir__estado { color: var(--laton); }

/* Con las tres obras nuevas el directorio pasó a 17 tarjetas, y 17 es
   impar: a dos columnas la última quedaba sola llenando el 48%, que se
   lee como una tarjeta que falta. Que ocupe la fila.

   La última fila con DOS de tres (63%) se deja como está: es la misma
   decisión que ya se tomó con 14 tarjetas — dos tarjetas juntas no se
   leen como un hueco, una sola sí.

   Al final del archivo a propósito: con igual especificidad gana la
   última regla. */
@media (min-width: 640px) and (max-width: 1049px) {
  .dir__item:last-child:nth-child(odd) { grid-column: span 2; }
}

/* ═══ contacto.css ═══ */
/* ============================================================
   CONTACTO

   Formulario a un lado, contacto directo al otro. 7/5.

   Decisión de fondo: el formulario NO manda un correo. Compone el
   mensaje y lo abre en WhatsApp con todo ya escrito.

   Por qué: no hay servidor en este borrador, y un formulario que
   dice "enviado" sin enviar nada es la peor falla posible en una
   página de contacto — el visitante cree que lo van a llamar y
   nadie lo llama. Además, en Santa Cruz la conversación de compra
   ocurre en WhatsApp de todos modos. El comprador ve exactamente
   el mensaje que sale, y llega con las respuestas ya cargadas, así
   que quien atiende sabe de entrada qué busca.

   Cuando exista el endpoint del formulario, este mismo marcado
   sirve: se cambia el `submit` y se mantiene todo lo demás.
   ============================================================ */

.contacto__grid {
  display: grid;
  gap: clamp(40px, 5vw, 72px);
  align-items: start;
  padding-bottom: clamp(56px, 7vw, 96px);
}
@media (min-width: 900px) {
  .contacto__grid { grid-template-columns: 7fr 5fr; }   /* nunca mitades */
}
.contacto__grid > * { min-width: 0; }   /* el textarea impone su ancho mínimo */

/* ---- Formulario --------------------------------------------- */
.forma { display: grid; gap: clamp(28px, 3.5vw, 40px); }

.grupo { border: 0; padding: 0; margin: 0; }
.grupo legend {
  padding: 0 0 var(--s4);
  font-size: var(--micro);
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: var(--piedra);
}

/* Opciones como píldoras: la misma forma que el nav y el CTA.
   Son <input type="radio"> reales — el teclado y los lectores de
   pantalla los recorren con flechas, como corresponde. */
.opciones { display: flex; flex-wrap: wrap; gap: 10px; }
.opcion { position: relative; display: inline-flex; }
.opcion input {
  position: absolute;
  inset: 0;
  margin: 0;            /* el margen del UA encogía la caja a 119x41 */
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
}
.opcion span {
  display: flex;
  align-items: center;
  min-height: var(--tap);
  padding-inline: 20px;
  border: 1px solid var(--linea);
  border-radius: var(--radio-pildora);
  font-size: var(--small);
  color: var(--tinta);
  transition: border-color 160ms linear, background-color 160ms linear;
}
.opcion:hover span { border-color: var(--piedra); }
.opcion input:checked + span {
  border-color: var(--cobalto);
  background: var(--cobalto);
  color: var(--hueso);          /* 10.49:1 */
}
.opcion input:focus-visible + span {
  outline: 2px solid var(--cobalto);
  outline-offset: 3px;
}

/* Los estilos de .campo se mudaron a tokens.css: los usan el
   formulario de contacto y el de cada landing. */


.forma__dos { display: grid; gap: clamp(20px, 2.5vw, 28px); }
@media (min-width: 620px) { .forma__dos { grid-template-columns: 1fr 1fr; } }

.forma__pie { display: flex; flex-wrap: wrap; align-items: center; gap: 16px 20px; }
.forma__nota {
  margin: 0;
  max-width: 42ch;
  font-size: 12px;
  line-height: 1.6;
  color: var(--piedra);
}

/* ---- Contacto directo --------------------------------------- */
.directo {
  padding: clamp(28px, 3.5vw, 40px);
  background: var(--cobalto-hondo);
  color: var(--hueso);
  border-radius: 18px;
}
.directo h2 {
  margin: 0 0 clamp(20px, 2.5vw, 28px);
  font-size: var(--d3);
  line-height: 1.1;
  letter-spacing: -0.01em;
}
.directo__lista { margin: 0; padding: 0; list-style: none; }
.directo__lista li + li {
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid rgba(250, 248, 245, 0.20);
}
.directo__et {
  display: block;
  margin-bottom: 4px;
  font-size: var(--micro);
  font-weight: 600;
  letter-spacing: 0.10em;
  text-transform: uppercase;
  color: rgba(250, 248, 245, 0.62);   /* 6.9:1 */
}
.directo a,
.directo__dato {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  color: var(--hueso);
  text-decoration: none;
  font-size: var(--lead);
  border-bottom: 1px solid transparent;
  transition: border-color 160ms linear;
}
.directo a:hover { border-bottom-color: var(--laton); }
.directo__dato { border-bottom: 0; }
.directo :focus-visible { outline-color: var(--hueso); }

/* Los horarios en cifras tabulares: dos rangos alineados leen como
   un dato, no como una frase. */
.horario {
  margin: 2px 0 0;
  font-size: var(--lead);
  line-height: 1.4;
  color: var(--hueso);
  font-variant-numeric: lining-nums tabular-nums;
}

/* ═══ mapa.css ═══ */
/* ============================================================
   MAPA DE LA OFICINA

   El mapa embebido de Google pesa cerca de 800 kB y deja cookies de
   terceros en CUALQUIER visita, aunque nadie lo mire. Por eso acá va
   detrás de una fachada: se carga con un clic, y el botón avisa de
   dónde viene el contenido.

   No es una limitación disfrazada: la dirección, los horarios y el
   enlace a Google Maps están visibles sin cargar nada, así que quien
   solo quiere la dirección la tiene de una.
   ============================================================ */

.mapa { background: var(--cal); padding-block: clamp(56px, 7vw, 96px); }
.mapa__titulo {
  margin: 0 0 clamp(28px, 3.5vw, 44px);
  font-size: var(--d3);
  line-height: 1.1;
  letter-spacing: -0.01em;
  max-width: 20ch;
}

.mapa__marco {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: 18px;
  overflow: hidden;
  border: 1px solid var(--linea);
  background: var(--hueso);
}
@media (max-width: 700px) { .mapa__marco { aspect-ratio: 4 / 5; } }

.mapa__marco iframe {
  display: block;
  width: 100%;
  height: 100%;
  border: 0;
}

.mapa__fachada {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: clamp(18px, 2.4vw, 26px);
  padding: clamp(24px, 3vw, 40px);
  text-align: center;              /* check-bans-ignore: es una fachada
                                      de un elemento centrado, no una
                                      sección de contenido */
  /* Trama de puntos: sugiere un plano sin fingir un mapa que no está. */
  background-image: radial-gradient(var(--linea) 1px, transparent 1px);
  background-size: 22px 22px;
}
.mapa__dir {
  margin: 0;
  font-size: var(--lead);
  line-height: 1.5;
  color: var(--piedra);
}
.mapa__dir b {
  display: block;
  margin-bottom: 4px;
  font-family: var(--serif);
  font-variation-settings: var(--fraunces-texto);
  font-weight: 400;
  font-size: 1.5rem;
  color: var(--tinta);
}
.mapa__acciones { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; }
.mapa__aviso { margin: 0; font-size: 12px; color: var(--piedra); }

.mapa__datos {
  margin: clamp(28px, 3.5vw, 40px) 0 0;
  padding: 0;
  list-style: none;
  border-top: 1px solid var(--linea);
}
@media (min-width: 820px) {
  .mapa__datos { display: grid; grid-template-columns: repeat(3, 1fr); column-gap: clamp(24px, 3vw, 48px); }
}
.mapa__datos li {
  display: grid;
  gap: 2px;
  padding-block: 14px;
  border-bottom: 1px solid var(--linea);
  font-size: var(--small);
}
.mapa__datos b { font-weight: 600; color: var(--tinta); }
.mapa__datos span { color: var(--piedra); }
.mapa__datos a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  color: var(--cobalto);
  text-decoration: underline;
  text-decoration-color: var(--linea);
  text-underline-offset: 3px;
}
.mapa__datos a:hover { text-decoration-color: var(--cobalto); }

/* ═══ visor.css ═══ */
/* ============================================================
   VISOR DE IMÁGENES

   Fondo casi opaco en el azul de la marca —no negro— para que
   ampliar una foto siga sintiéndose parte del sitio y no una
   ventana del navegador.

   Los tres controles son círculos de 48px: es el único lugar del
   sitio donde hay botones redondos, y se justifica porque flotan
   sobre una fotografía, donde una píldora se leería como una
   calcomanía pegada.
   ============================================================ */

.visor {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(16px, 4vw, 48px);
  background: rgba(9, 20, 42, 0.985);   /* medido: el 0,96 dejaba pasar el texto de atrás */
  animation: visor-entra 180ms var(--ease) both;
}
@keyframes visor-entra { from { opacity: 0; } to { opacity: 1; } }

/* El fondo no se desplaza mientras el visor está abierto. */
.visor-abierto { overflow: hidden; }

.visor__marco {
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: clamp(12px, 2vw, 18px);
  max-width: min(100%, 1500px);
  max-height: 100%;
}

.visor__img {
  display: block;
  max-width: 100%;
  /* Se reserva el alto del pie y de la barra de controles para que
     una imagen alta no empuje la leyenda fuera de la pantalla. */
  max-height: calc(100svh - clamp(120px, 20vh, 190px));
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: 6px;
}
/* La entrada es una TRANSICIÓN, no un keyframe, y esto se midió.
   Con `animation` reactivada por clase, avanzar dos fotos con 60ms de
   diferencia devolvía la opacidad a 0,00 y rehacía los 260ms enteros:
   un keyframe reinicia desde cero, no retoma. Una transición
   interpola desde el valor actual, así que pasar rápido se ve
   continuo en vez de parpadear.

   Además desaparece el problema de cascada que obligó a la clase: una
   propiedad animada le gana a cualquier declaración normal, pero una
   transición no — solo interpola entre valores calculados. Por eso
   `.visor--cargando` ya puede usar `opacity` y no `visibility`, y el
   JS se ahorra el baile de sacar la clase, forzar reflow y reponerla.

   Nota deliberada: cuando la foto ya está precargada —lo habitual,
   porque se piden las dos vecinas— no hay transición ninguna, porque
   `.visor--cargando` nunca llega a aplicarse. Es lo correcto: avanzar
   con la flecha es una acción de teclado, y esas no llevan animación.
   El fundido queda solo para cuando de verdad hubo que esperar. */
.visor__img {
  transition: opacity 160ms var(--ease), transform 160ms var(--ease);
}

.visor__pie {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 6px 16px;
  max-width: 62ch;
  text-align: center;
  font-size: var(--small);
  line-height: 1.5;
  color: rgba(250, 248, 245, 0.92);    /* 13,9:1 sobre el fondo */
}
.visor__cuenta {
  font-size: var(--micro);
  font-weight: 600;
  letter-spacing: 0.12em;
  color: rgba(250, 248, 245, 0.62);
  font-variant-numeric: lining-nums tabular-nums;
}

/* ---- Los tres controles ---- */
.visor__cerrar,
.visor__nav {
  position: absolute;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  padding: 0;
  border: 1px solid rgba(250, 248, 245, 0.28);
  border-radius: 50%;
  background: rgba(9, 20, 42, 0.55);
  color: var(--hueso);
  cursor: pointer;
  transition: background-color 160ms linear, border-color 160ms linear;
}
.visor__cerrar:hover,
.visor__nav:hover {
  background: rgba(250, 248, 245, 0.16);
  border-color: rgba(250, 248, 245, 0.55);
}
.visor__cerrar:focus-visible,
.visor__nav:focus-visible {
  outline: 2px solid var(--hueso);
  outline-offset: 3px;
}
.visor__cerrar svg,
.visor__nav svg {
  width: 22px;
  height: 22px;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.visor__cerrar { top: clamp(12px, 3vw, 24px); right: clamp(12px, 3vw, 24px); }
.visor__nav--antes { left:  clamp(8px, 2vw, 24px); top: 50%; transform: translateY(-50%); }
.visor__nav--luego { right: clamp(8px, 2vw, 24px); top: 50%; transform: translateY(-50%); }

/* En el teléfono las flechas bajan al pie: a los costados tapan la
   foto, y el pulgar llega mucho mejor abajo que al centro del borde. */
@media (max-width: 559px) {
  .visor__nav {
    top: auto;
    bottom: clamp(12px, 4vw, 20px);
    transform: none;
  }
  .visor__nav--antes { left: 50%; margin-left: -60px; }
  .visor__nav--luego { right: 50%; margin-right: -60px; }
  .visor__img { max-height: calc(100svh - 210px); }
  .visor__pie { padding-bottom: 56px; }
}

/* ---- La imagen ampliable, en la página ---- */
.visor-listo img { cursor: zoom-in; }
.visor-listo img:focus-visible {
  outline: 3px solid var(--cobalto);
  outline-offset: 3px;
}

@media (prefers-reduced-motion: reduce) {
  .visor { animation: none; }
}

/* ============================================================
   INDICADOR DE CARGA

   Aparece solo cuando de verdad hay espera. El truco está en el
   `transition-delay` de 160 ms: si la imagen ya estaba en caché —que
   con la precarga de vecinas es el caso habitual— la clase se pone y
   se saca dentro de ese margen y el círculo nunca llega a verse. Un
   indicador que parpadea en cada toque hace sentir la interfaz MÁS
   lenta, no menos, porque anuncia una espera que no existió.

   Mientras carga, la imagen se oculta del todo. Es a propósito: antes
   se veía la foto anterior con el rótulo de la nueva, y un marco
   vacío con un indicador es honesto donde eso era falso.
   ============================================================ */
.visor__cargador {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 38px;
  height: 38px;
  margin: -19px 0 0 -19px;
  border: 2px solid rgba(250, 248, 245, 0.22);
  border-top-color: var(--hueso);
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
  transition: opacity 140ms linear 160ms;
  animation: visor-gira 720ms linear infinite;
}
.visor--cargando .visor__cargador { opacity: 1; }

@keyframes visor-gira { to { transform: rotate(360deg); } }

/* Ya puede ser `opacity` y no `visibility`: sin animación de por medio
   la declaración se aplica, y encima es lo que la transición necesita
   para tener desde dónde interpolar. */
.visor--cargando .visor__img { opacity: 0; transform: scale(0.985); }

@media (prefers-reduced-motion: reduce) {
  .visor__cargador { animation: none; border-top-color: rgba(250, 248, 245, 0.75); }
  /* El fundido se queda —no marea a nadie y avisa que la foto llegó—;
     lo que se va es el desplazamiento. */
  .visor__img { transition: opacity 160ms linear; }
  .visor--cargando .visor__img { transform: none; }
}

/* ═══ footer.css ═══ */
/* ============================================================
   PIE DE PÁGINA

   Cobalto profundo, el mismo suelo que la banda de cifras — así
   el sitio cierra con el color con el que abre.

   Columnas DESIGUALES (5/2/2/3): la marca se queda con el ancho,
   los índices ocupan lo que necesitan. Cuatro columnas idénticas
   son el pie de plantilla.

   La línea inferior nombra al grupo — constructora y los dos
   colegios. Eso establece hoy la estructura multi-negocio, así
   que sumarla en serio más adelante es un cambio de contenido y
   no un rediseño.
   ============================================================ */

.pie {
  background: var(--cobalto-hondo);
  color: rgba(250, 248, 245, 0.78);   /* 8.0:1 sobre #102A70 */
  padding-block: clamp(56px, 7vw, 96px) clamp(28px, 3vw, 40px);
  font-size: var(--small);
}

.pie__grid {
  display: grid;
  gap: clamp(40px, 5vw, 64px);
}
@media (min-width: 900px) {
  .pie__grid {
    grid-template-columns: 5fr 2fr 2fr 3fr;
    gap: clamp(32px, 4vw, 72px);
  }
}

/* ---- Columna de marca ---- */
.pie__marca { max-width: 34ch; }
.pie__logo {
  display: inline-flex;
  align-items: center;
  color: var(--hueso);
  padding-block: 10px;      /* 24 + 10 + 10 = 44px de área táctil */
}
.pie__logo svg { display: block; width: 190px; height: 24px; }

.pie__nota {
  margin: clamp(20px, 2.4vw, 28px) 0 0;
  line-height: 1.6;
}

.pie__contacto {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 2px;
  margin-top: clamp(20px, 2.4vw, 28px);
}
.pie__contacto a {
  display: inline-flex;
  align-items: center;
  min-height: var(--tap);
  color: var(--hueso);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: border-color 160ms linear;
}
.pie__contacto a:hover { border-bottom-color: var(--laton); }

/* ---- Columnas de enlaces ---- */
.pie__titulo {
  margin: 0 0 var(--s4);
  font-size: var(--eyebrow);
  font-weight: 500;
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: rgba(250, 248, 245, 0.60);
}

.pie__lista { margin: 0; padding: 0; list-style: none; }
.pie__lista a {
  display: flex;
  align-items: center;
  min-height: var(--tap);          /* 44px sin necesidad de rellenos */
  color: rgba(250, 248, 245, 0.86);
  text-decoration: none;
  transition: color 160ms linear;
}
.pie__lista a:hover { color: var(--hueso); }
.pie__lista a[aria-current="page"] { color: var(--hueso); }

/* Los enlaces externos llevan una marca discreta: se sale del
   sitio, y conviene decirlo antes del clic y no después. */
.pie__lista a[target="_blank"]::after {
  content: "↗";
  margin-left: 8px;
  font-size: 0.85em;
  color: rgba(250, 248, 245, 0.50);
}

/* ---- Barra inferior ---- */
.pie__base {
  margin-top: clamp(48px, 6vw, 80px);
  padding-top: 20px;
  border-top: 1px solid rgba(250, 248, 245, 0.20);
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 10px var(--s5);
  font-size: 12px;
  color: rgba(250, 248, 245, 0.66);
}
.pie__grupo { display: flex; flex-wrap: wrap; gap: 6px 14px; }
.pie__grupo span:not(:last-child)::after {
  content: " ·";
  margin-left: 8px;
  color: rgba(250, 248, 245, 0.40);
}

.pie :focus-visible { outline-color: var(--hueso); }


/* ============================================================
   Pie en celular

   Cuatro columnas apiladas dan un pie larguísimo: en un teléfono
   son casi dos pantallas de desplazamiento para llegar al final.
   Se compacta en tres pasos:
     · las dos listas de enlaces van a dos columnas, no a una
     · la descripción de marca se oculta: ya está en Nosotros y en
       el inicio, y acá solo estira
     · la línea del grupo del pie se oculta: repite la columna
       "El grupo" que está justo arriba

   Los 44px de área táctil NO se tocan: es la regla del sitio y
   ahorrar 4px por enlace no justifica romperla.
   ============================================================ */
@media (max-width: 767px) {
  .pie { padding-block: clamp(40px, 8vw, 56px) 24px; }
  .pie__grid { gap: 32px; }
  .pie__nota { display: none; }
  /* El logo se achica, pero el área táctil no: 20 + 12 + 12 = 44. */
  .pie__logo svg { width: 158px; height: 20px; }
  .pie__logo { padding-block: 12px; }

  /* Navegación y El grupo, lado a lado */
  .pie__grid > :nth-child(2),
  .pie__grid > :nth-child(3) {
    grid-column: span 1;
  }
  .pie__grid {
    grid-template-columns: 1fr 1fr;
  }
  .pie__grid > :first-child,
  .pie__grid > :last-child { grid-column: 1 / -1; }

  .pie__contacto { margin-top: 16px; }

  .pie__base {
    margin-top: 28px;
    padding-top: 16px;
    font-size: var(--micro);
  }
  /* La línea del grupo se repite con la columna "El grupo": sobra. */
  .pie__grupo { display: none; }
}

/* ═══ motion.css ═══ */
/* ============================================================
   MOVIMIENTO — un sistema, no efectos sueltos

   Reglas que sostienen todo lo de abajo:

   1. UNA SOLA CURVA. Todo usa --ease. Cuando cada elemento tiene
      su propio easing, el sitio se lee escrito por varias manos.
   2. DISTANCIAS CORTAS. 14px, no 40. Lo caro se mueve poco: un
      desplazamiento grande grita "animación", uno chico solo se
      siente.
   3. UNA SOLA VEZ. Nada se re-anima al volver a pasar. Repetir
      convierte el movimiento en ruido durante el resto de la
      sesión.
   4. NADA SE MUEVE ANTES DE ESTAR EN PANTALLA. El hero anima al
      cargar; el resto espera a entrar en cuadro.
   5. EL CONTENIDO EXISTE SIN JS. Si el script falla, todo queda
      visible: las clases de partida solo se aplican cuando el JS
      marca el documento con `js-mov`.
   ============================================================ */

:root {
  --mov-dist: 14px;
  --mov-dur: 700ms;
  --mov-paso: 80ms;      /* desfase entre hermanos de un grupo */

  /* Estas eran cifras fijas repartidas por el archivo. Son variables
     porque el celular necesita las mismas animaciones más cortas, y
     eso no se puede hacer si el valor está escrito en cada regla. */
  --mov-img-op:   900ms;   /* la imagen sube su opacidad          */
  --mov-img-tr:   600ms;   /* y cede la escala, algo más lenta    */
  --mov-hero-bg: 1800ms;   /* el fondo del hero                   */
  --mov-hero-tx:  900ms;   /* cada línea del hero                 */
  --mov-carga:    760ms;   /* cascada de entrada de página        */
  --mov-lamina:  1100ms;   /* la portada de las páginas de obra   */
  --mov-hero-d1:  180ms;
  --mov-hero-d2:  300ms;
  --mov-hero-d3:  520ms;
  --mov-hero-d4:  640ms;
}

/* ---- Aparición base ----------------------------------------- */
/* El estado de partida solo aplica si el JS está vivo. Sin JS no
   hay `js-mov` en el <html> y el contenido se ve normal. */
.js-mov .revelar {
  opacity: 0;
  transform: translateY(var(--mov-dist));
  transition: opacity var(--mov-dur) var(--ease),
              transform var(--mov-dur) var(--ease);
  transition-delay: var(--mov-retraso, 0ms);
}
.js-mov .revelar.esta-visible {
  opacity: 1;
  transform: none;
}

/* ---- Imágenes: se destapan, no aparecen ---------------------- */
/* La figura recorta y la imagen entra desde una escala levemente
   mayor. Da la sensación de que la foto ya estaba ahí y se
   descubre, en vez de materializarse. */
.js-mov .revelar-img {
  overflow: hidden;
}
.js-mov .revelar-img > img,
.js-mov .revelar-img > picture > img {
  opacity: 0;
  transform: scale(1.04);
  transition: opacity var(--mov-img-op) var(--ease), transform var(--mov-img-tr) var(--ease);
  transition-delay: var(--mov-retraso, 0ms);
}
.js-mov .revelar-img.esta-visible > img,
.js-mov .revelar-img.esta-visible > picture > img {
  opacity: 1;
  transform: none;
}

/* ---- Hero: el único momento orquestado ----------------------- */
/* Ocurre al cargar, no al hacer scroll: ya está en pantalla.
   La imagen cede escala mientras el texto sube en cascada, así
   que las dos cosas terminan juntas y se lee como un solo gesto. */
.js-mov .hero__fondo img {
  animation: hero-fondo var(--mov-hero-bg) var(--ease) both;
}
@keyframes hero-fondo {
  from { transform: scale(1.06); }
  to   { transform: scale(1); }
}

.js-mov .hero__eyebrow,
.js-mov .hero__titulo,
.js-mov .hero__filete,
.js-mov .hero__pie {
  animation: hero-sube var(--mov-hero-tx) var(--ease) both;
}
.js-mov .hero__eyebrow { animation-delay: var(--mov-hero-d1); }
.js-mov .hero__titulo  { animation-delay: var(--mov-hero-d2); }
.js-mov .hero__filete  { animation-delay: var(--mov-hero-d3); }
.js-mov .hero__pie     { animation-delay: var(--mov-hero-d4); }

@keyframes hero-sube {
  from { opacity: 0; transform: translateY(18px); }
  to   { opacity: 1; transform: none; }
}

/* El filete además se dibuja hacia abajo en vez de aparecer. */
.js-mov .hero__filete {
  transform-origin: top;
  animation: hero-filete var(--mov-hero-tx) var(--ease) var(--mov-hero-d3) both;
}
@keyframes hero-filete {
  from { opacity: 0; transform: scaleY(0); }
  to   { opacity: 1; transform: scaleY(1); }
}

/* ---- Cifras: los números cuentan ----------------------------- */
/* `tabular-nums` es obligatorio acá: sin él el ancho del número
   cambia en cada cuadro y la etiqueta de al lado tiembla. */
.cifra__valor { font-variant-numeric: lining-nums tabular-nums; }

/* ---- Micro-interacciones ------------------------------------ */
/* Los enlaces del pie ya suben su filete; se le agrega el
   desplazamiento mínimo que indica "esto responde". */
.pie__lista a { transition: color 160ms linear, transform 220ms var(--ease); }
.pie__lista a:hover { transform: translateX(3px); }

/* El muro no se detiene nunca, así que un hover que levante la
   tarjeta obligaría a perseguir un objeto en movimiento. Solo
   cambia el borde: se nota sin pedirle puntería a nadie. */
.testimonio { transition: border-color 320ms var(--ease); }
.testimonio:hover { border-color: var(--piedra); }

/* ============================================================
   Sin movimiento: TODO se apaga.
   No es una versión degradada — es el sitio completo, quieto.
   ============================================================ */
@media (prefers-reduced-motion: reduce) {
  .js-mov .revelar,
  .js-mov .revelar-img > img,
  .js-mov .revelar-img > picture > img {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .js-mov .hero__fondo img,
  .js-mov .hero__eyebrow,
  .js-mov .hero__titulo,
  .js-mov .hero__filete,
  .js-mov .hero__pie {
    animation: none !important;
    opacity: 1;
    transform: none;
  }
  .pie__lista a:hover { transform: none; }
}

/* ============================================================
   TRANSICIÓN ENTRE PÁGINAS

   El sitio son archivos HTML separados, así que cada clic en el nav
   provocaba un parpadeo blanco y un salto seco. Con la API de View
   Transitions el navegador toma una instantánea de la página que se
   va, otra de la que llega, y las cruza — sin JavaScript y sin
   convertir el sitio en una SPA.

   Donde el navegador no la soporta, la navegación ocurre como antes:
   no hay degradación, simplemente no hay transición. Por eso se
   puede usar hoy sin condicionar nada.

   El armazón (header y pie) lleva `view-transition-name` propio, así
   que NO se desvanece con el resto: se queda quieto mientras cambia
   el contenido. Eso es lo que hace que se sienta una sola aplicación
   en vez de siete documentos.
   ============================================================ */
@view-transition { navigation: auto; }

::view-transition-old(root) {
  animation: salir 220ms var(--flip-ease) both;
}
::view-transition-new(root) {
  animation: entrar 220ms var(--ease) both;   /* era 380ms: navegar es lo más frecuente que hay */
}
@keyframes salir {
  to { opacity: 0; transform: translateY(-8px); }
}
@keyframes entrar {
  from { opacity: 0; transform: translateY(12px); }
}

/* El header y el pie no se cruzan: permanecen. */
.hdr { view-transition-name: armazon-header; }
.pie { view-transition-name: armazon-pie; }
::view-transition-group(armazon-header),
::view-transition-group(armazon-pie) { animation-duration: 0ms; }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-old(root),
  ::view-transition-new(root) { animation: none; }
}

/* ============================================================
   Entrada al cargar — la portada de cada página

   El hero del inicio tenía su cascada al cargar, pero las demás
   páginas aparecían de golpe: su encabezado está sobre el pliegue,
   así que el observador de scroll se dispara antes de que se vea
   nada y la entrada se pierde. Es el mismo motivo por el que el
   hero se anima al cargar y no al entrar en cuadro.

   No se lista qué páginas ni qué secciones. `motion.js` toma la
   PRIMERA sección de <main> —sea cual sea— y a lo que hay adentro
   le pone `revelar--carga` en vez de observarlo. Una página nueva
   queda cubierta sin tocar nada.
   ============================================================ */
.js-mov .revelar--carga {
  opacity: 0;
  transform: translateY(var(--mov-dist, 14px));
  animation: mov-carga var(--mov-carga) var(--ease) both;
  animation-delay: var(--mov-retraso, 0ms);
}
@keyframes mov-carga {
  to { opacity: 1; transform: none; }
}

/* La imagen de portada de un proyecto entra antes que su título:
   se lee "primero la casa, después su nombre".

   ⚠️ La escala va en la IMAGEN dentro de una figura que recorta, no en
   la figura. Cuando la escala estaba en la figura, un 1,03 sobre 320 px
   son 329,6: durante el segundo que dura la entrada la página se podía
   arrastrar 10 px de costado en un teléfono angosto. Un transform NO se
   recorta a sí mismo — desborda a su padre —, así que ponerle overflow
   a la figura solo sirve si lo que se mueve es su contenido. Es además
   el mismo patrón que ya usa `.revelar-img`. */
.js-mov .pr-hero__lamina { overflow: hidden; }
.js-mov .pr-hero__lamina img {
  animation: pr-lamina var(--mov-lamina) var(--ease) both;
}
@keyframes pr-lamina {
  from { opacity: 0; transform: scale(1.03); }
  to   { opacity: 1; transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .js-mov .revelar--carga,
  .js-mov .pr-hero__lamina img {
    animation: none !important;
    opacity: 1;
    transform: none;
  }
}

/* ============================================================
   PRESUPUESTO DE MOVIMIENTO EN CELULAR

   Medido: el scroll va a 60 fps con la CPU frenada 4×, con un salto
   máximo de 20 ms entre fotogramas. O sea que no era falta de
   fluidez — era duración. En un teléfono se hace scroll rápido y con
   el pulgar: un revelado de 1.200 ms sigue corriendo cuando el
   elemento ya salió de pantalla, y lo que se percibe no es una
   animación elegante sino contenido que llega tarde.

   Las distancias no cambian (14 px sigue siendo 14 px). Solo el
   tiempo. La misma coreografía, tocada más rápido.

   Va al final del archivo a propósito: con igual especificidad gana
   la última regla, y en este proyecto ya se perdieron tres tardes
   por poner un override antes de su base.
   ============================================================ */
@media (max-width: 860px) {
  :root {
    --mov-dur:      420ms;
    --mov-paso:      45ms;
    --mov-img-op:   520ms;
    --mov-img-tr:   700ms;
    --mov-hero-bg: 1100ms;
    --mov-hero-tx:  560ms;
    --mov-carga:    480ms;
    --mov-lamina:   650ms;
    --mov-hero-d1:   90ms;
    --mov-hero-d2:  160ms;
    --mov-hero-d3:  280ms;
    --mov-hero-d4:  350ms;
  }
}
