/* ==========================================================================
   PROYECTOS · hoja propia de /proyectos/ y /proyectos/<slug>/

   Sólo la enlazan esas páginas, después de sections.css y en lugar de
   mockups.css (que acá no se usa: un pedido menos). No toca un solo selector
   de las otras cuatro hojas.

   Lo que reusa, y por eso no está acá: .section .container .eyebrow .h2 .h3
   .lead .display .numeral .rule .btn .tag .chip .ph .ph-badge .checklist
   .seam .glass .pull-quote .footer .nav .mobile-menu, todo el sistema de
   reveals de base.css, y —lo importante— la maquinaria del LIBRO CONTABLE de
   sections.css § 03: la grilla compartida, el rail, el filete de cierre, el
   tramado que se acumula y la doble regla del total. Acá sólo se agregan las
   columnas que el libro de obras necesita de más.

   Los @keyframes obs-libro-* son globales (sections.css), así que las reglas
   de abajo los referencian sin volver a declararlos.
   ========================================================================== */


/* ==========================================================================
   01 · HERO DE PÁGINA — .phero

   Deliberadamente más chico y más tonto que el de la home: 62svh contra
   100svh, sin parallax de puntero, sin diafragma y sin la línea de tiempo de
   siete tiempos. Este hero es una PORTADA, no una apertura de sitio; entra
   con el sistema normal de data-reveal.

   Tampoco hereda html.obs-tl: esa clase la pone el script inline del <head>
   y sólo esconde [data-hero-item], que acá no existe.
   ========================================================================== */
.phero {
  position: relative;
  display: flex;
  align-items: center;
  min-height: 62svh;
  overflow: hidden;
  background: var(--bg-sunk);
  padding: calc(76px + var(--sp-14)) 0 var(--sp-14);
}

.phero__media { position: absolute; inset: 0; z-index: 0; }
.phero__media img {
  width: 100%; height: 100%;
  object-fit: cover;
  /* La figura y el árbol de mid-3 caen a la derecha del encuadre. Con
     `center` el recorte de 62svh se comía la copa del árbol y dejaba el
     valle solo; con 62% el sujeto sobrevive en todos los anchos probados. */
  object-position: 62% 50%;
}

/* EL VELO. No se reusa .hero__veil: el de la home es una elipse CENTRADA,
   calibrada al milímetro (.70, el mínimo que pasa AA en seis anchos) para un
   bloque de texto centrado sobre SU pintura — que es, con el titular acá
   centrado también, casi la misma situación. La diferencia es la pintura:
   ésta tiene su sujeto (montaña, árbol, figura) cargado a la derecha, así que
   en vez de una elipse se usa un degradé horizontal que oscurece el CENTRO —
   donde vive el bloque de texto centrado — y suelta hacia los dos cantos,
   para que el sujeto de la foto conserve algo de luz en los bordes. */
.phero__veil {
  position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background:
    linear-gradient(90deg, rgba(29,28,24,.46) 0%, rgba(29,28,24,.76) 28%, rgba(29,28,24,.76) 72%, rgba(29,28,24,.46) 100%),
    linear-gradient(180deg, rgba(29,28,24,.62) 0%, rgba(29,28,24,.16) 34%, rgba(29,28,24,.30) 70%, rgba(29,28,24,.72) 100%);
}
/* ⚠ SE REFUERZA AL ANGOSTARSE. Bajo 991px el bloque de texto centrado ocupa
   casi todo el ancho de .sangre, así que la banda oscura del centro tiene que
   crecer para seguir cubriéndolo entero — si no, los cantos del titular caen
   sobre la franja más clara y repiten la caída de AA ya medida una vez en
   este hero (3.74:1 contra foto a 768px). */
@media (max-width: 991px) {
  .phero__veil {
    background:
      linear-gradient(90deg, rgba(29,28,24,.62) 0%, rgba(29,28,24,.84) 14%, rgba(29,28,24,.84) 86%, rgba(29,28,24,.62) 100%),
      linear-gradient(180deg, rgba(29,28,24,.64) 0%, rgba(29,28,24,.22) 34%, rgba(29,28,24,.36) 70%, rgba(29,28,24,.74) 100%);
  }
}

/* CENTRADO. El resto de la página va a sangre y sin centralizar a propósito
   (ver la nota de listado.mjs); este hero es la única excepción deliberada:
   es una portada chica y el titular pide leerse como una declaración, no
   como el arranque de una columna. */
.phero__inner {
  position: relative; z-index: 2; width: 100%;
  display: flex; flex-direction: column; align-items: center;
  text-align: center;
}
.phero__inner [data-reveal] { display: flex; flex-direction: column; align-items: center; }

.phero__title {
  color: var(--ink);
  /* em y NO ch, por el mismo motivo medido que el hero de la home: ch mide
     contra la fuente EN USO, así que la caja se movía durante el swap y el
     CLS se iba de .088 a .190. */
  max-width: 13em;
  margin-top: var(--sp-5);
  text-shadow: 0 2px 30px rgba(29,28,24,.55);
}

/* El .eyebrow de base.css pinta con --ink-soft, y acá eso NO alcanza: medido
   sobre el render, 4.36:1 contra la foto — por debajo del 4.5 que pide AA para
   12px. Es la sexta vez que --ink-soft se cae en este proyecto y siempre por lo
   mismo: hay algo abajo que levanta el suelo. Con --ink-mute pasa holgado y no
   cuesta nada. La regla, otra vez: sobre foto, degradé, vidrio o plancha, mute. */
.phero .eyebrow { color: var(--ink-mute); }

.phero__subhead {
  margin-top: var(--sp-6);
  max-width: 58ch;
  font-size: var(--fs-lead);
  line-height: var(--lh-relaxed);
  /* --ink-mute y no --ink-soft: esto es texto sobre una FOTO, que es
     exactamente donde el soft se cayó de AA cinco veces en este proyecto. */
  color: var(--ink-mute);
  text-wrap: pretty;
}


/* ==========================================================================
   03 · EL LIBRO DE OBRAS — .libro--obras

   Mismo objeto gráfico que "Qué solucionamos", con una columna más. La
   diferencia de fondo con aquel: allá las filas son un INVENTARIO DE PÉRDIDAS
   y no llevan a ningún lado; acá cada fila es un link. Todo lo demás —el
   rail, los romanos, el filete, el tramado, el total y la doble regla— es
   literalmente el mismo sistema.

   Orden de columnas: numeral · texto · meta · media. La media va ÚLTIMA y no
   después del numeral a propósito: con la imagen en segunda columna el nombre
   del proyecto —que es lo que se viene a leer— arrancaba a ~380px del canto.
   Así arranca en el mismo sitio que los conceptos del libro de la home.
   ========================================================================== */
.libro--obras {
  --libro-col1: 3.5rem;                                     /* el numeral */
  /* La media crecio con el ancho, y no por gusto: al pasar de .container a
     .marco--ancho la fila gano ~600px que se los llevaba enteros la columna de
     texto, y como el resultado esta capado a 62ch quedaba un hueco muerto de
     medio metro entre el texto y la miniatura. El espacio nuevo va a la imagen,
     que ademas es lo que la pagina viene a mostrar. */
  --obra-media: clamp(200px, 24vw, 420px);
  --obras-cols: var(--libro-col1) minmax(0, 1fr) minmax(8ch, 12ch) var(--obra-media);
  --libro-gap: clamp(1.25rem, 0.9rem + 1.2vw, 2.5rem);
  --libro-sangria: clamp(1.25rem, 0.8rem + 1.6vw, 2.5rem);  /* del rail al texto */
  --libro-aire: clamp(1.75rem, 1.2rem + 1.3vw, 2.5rem);
}

/* Igual que .libro__cuerpo: SIN overflow hidden acá ni en ningún ancestro de
   una fila. Lo volvería contenedor de scroll y animation-timeline: view() se
   ataría a él —que no scrollea nunca— en vez de al viewport. Es la trampa que
   dejó el filete de .seam clavado en scaleX(1) en la 11ª pasada. */
.obras__cuerpo { position: relative; margin-top: clamp(2rem, 1.5rem + 2vw, 3rem); }

.obras__cabecera {
  display: grid;
  grid-template-columns: var(--obras-cols);
  column-gap: var(--libro-gap);
  padding: 0 0 var(--sp-3) var(--libro-sangria);
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-mute);
}
.obras__cabecera span:nth-child(1) { grid-column: 2; }
.obras__cabecera span:nth-child(2) { grid-column: 3; }

.obras__filas { list-style: none; margin: 0; padding: 0; position: relative; }
/* El tope del rail, igual que .libro__filas::before */
.obras__filas::before {
  content: ""; position: absolute; left: 0; top: 0;
  width: 9px; height: 1px; background: var(--line-strong);
}

/* ── la fila ─────────────────────────────────────────────────────────────── */
.obra { position: relative; }

/* UN segmento de rail POR FILA y no un solo elemento alto: view() mide la
   visibilidad de su propio sujeto, y un elemento de ~1200px degenera el rango
   útil. Mismo motivo que en el libro de la home. */
.obra::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 1px; background: var(--line-strong);
  transform: scaleY(1);          /* sin scroll-timeline: ya dibujado */
  transform-origin: top;
}

.obra__link {
  position: relative;
  display: grid;
  grid-template-columns: var(--obras-cols);
  grid-template-areas: "num texto meta media";
  column-gap: var(--libro-gap);
  align-items: start;
  /* El aire va en padding y NUNCA en margin: con margin los segmentos de rail
     quedan separados y la línea deja de leerse continua. El + 12px reserva la
     altura del .obra__marca, que está fuera de flujo. */
  padding: var(--libro-aire) 0 calc(var(--libro-aire) + 12px) var(--libro-sangria);
  color: inherit;
}
/* Sólo cuando la ficha existe. Sin href el cursor nativo se queda: no se
   promete una navegación que todavía no está. Misma lógica que la pastilla
   de la home (módulo 12). */
.obra__link[href] { cursor: none; }

/* Realce de fondo, portado del componente de referencia. Va a -1 para no
   taparle el click a nada y sangra hacia afuera para que no parezca una card. */
.obra__link::after {
  content: ""; position: absolute; z-index: -1;
  inset: calc(var(--libro-aire) * .35) calc(var(--libro-gap) * -.5)
         calc(var(--libro-aire) * .35 + 12px) calc(var(--libro-sangria) * .35);
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--ink) 4%, transparent);
  opacity: 0;
  transition: opacity var(--dur) var(--ease-out);
}
.obra__link:hover::after,
.obra__link:focus-visible::after { opacity: 1; }

.obra__num {
  grid-area: num;
  font-family: var(--font-display);
  font-size: clamp(1.375rem, 1.1rem + 0.9vw, 1.875rem);
  line-height: 1;
  padding-top: .06em;
  color: var(--ink);             /* fallback = estado encendido */
}

.obra__texto { grid-area: texto; min-width: 0; }

/* El nombre y su subrayado animado: el gesto del componente de referencia,
   con la salvedad de que acá el trazo es --accent-2 y no la tinta, porque en
   este sitio el cobre es lo que marca "esto se puede abrir". */
.obra__nombre { display: inline-flex; align-items: center; gap: var(--sp-3); }
.obra__nombre-txt { position: relative; }
.obra__nombre-txt::after {
  content: ""; position: absolute; left: 0; bottom: -.08em;
  height: 1px; width: 0;
  background: var(--accent-2);
  transition: width var(--dur) var(--ease-out);
}
.obra__link:hover .obra__nombre-txt::after,
.obra__link:focus-visible .obra__nombre-txt::after { width: 100%; }

/* La flecha entra en diagonal, tal cual el original: opacidad + translate en
   los dos ejes a la vez. */
.obra__flecha {
  flex: 0 0 auto;
  width: 16px; height: 16px;
  color: var(--ink-mute);
  opacity: 0;
  transform: translate3d(-8px, 8px, 0);
  transition: opacity var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}
.obra__link:hover .obra__flecha,
.obra__link:focus-visible .obra__flecha { opacity: 1; transform: none; }

.obra__result {
  margin-top: var(--sp-3);
  max-width: 62ch;
  color: var(--ink-mute);
  line-height: var(--lh-relaxed);
  text-wrap: pretty;
}

.obra__meta {
  grid-area: meta;
  display: flex; flex-direction: column; align-items: flex-start; gap: var(--sp-2);
  padding-top: .35em;
}
/* .tag pinta con --ink-soft, que en este proyecto tiene cinco caídas de AA
   documentadas. Acá no cuesta nada tomar el margen de entrada. */
.obra__rubro { color: var(--ink-mute); }
.obra__anio {
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  color: var(--ink-mute);
  transition: color var(--dur) var(--ease-out);
}
.obra__link:hover .obra__anio { color: var(--ink); }

/* ── la media de la fila ─────────────────────────────────────────────────── */
.obra__media {
  grid-area: media;
  position: relative;
  aspect-ratio: 16 / 10;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--bg-sunk);
  /* Al hover la miniatura BAJA y el panel que sigue al cursor sube: se pasan
     la posta en vez de competir por la misma atención. */
  transition: opacity var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}
.obra__media > * { width: 100%; height: 100%; border-radius: inherit; }
.obra__media img { object-fit: cover; }
/* ⚠ Mismo bug que sections.css § .proyecto__media y, mas abajo en este mismo
   archivo, § .tira__media (parchado recien en la 41ª): <picture> es inline y
   el `> *` de arriba no lo alcanza a poner en caja, así que el <img> de adentro
   queda a su tamaño natural (934px de alto) y el recorte de object-fit sale
   de la ventana visible, no del centro de la imagen. Igual arreglo. */
.obra__media picture { display: block; }
.obra__media picture img { width: 100%; height: 100%; border-radius: inherit; }
.obra__media .ph { background-color: transparent; }
.obra__media .ph::before { top: 10px; left: 10px; }
.obra__media .ph::after  { bottom: 10px; right: 10px; }

@media (hover: hover) and (pointer: fine) {
  .obra__link:hover .obra__media { opacity: .35; transform: scale(.97); }
}

/* ── el filete de cierre y el tramado que se acumula ─────────────────────── */
.obra__marca {
  position: absolute; left: 0; right: 0; bottom: 0;
  height: 12px; pointer-events: none;
}
.obra__marca::before {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0;
  height: 1px; background-color: var(--line-strong);
}
.obra__marca::after {
  content: ""; position: absolute; left: 0; bottom: 2px;
  width: calc(var(--peso, 0) * 100%);
  height: 9px;
  /* Paso de 7px y 50% de tinta: los dos valores salen de la comparación sobre
     render del libro de la home (5-6px "lee como peine gris"; .38 "hay que ir
     a buscarlo"; .64 "compite con los h3"). No se re-eligen acá. */
  background-image: repeating-linear-gradient(90deg,
    color-mix(in srgb, var(--ink) 50%, transparent) 0 1px, transparent 1px 7px);
  clip-path: inset(0 0 0 0);     /* sin scroll-timeline: ya dibujado */
}

/* ── el total de cada tomo ───────────────────────────────────────────────── */
.obras__total {
  position: relative;
  display: grid;
  grid-template-columns: var(--obras-cols);
  column-gap: var(--libro-gap);
  padding: clamp(1.75rem, 1.2rem + 1.4vw, 2.5rem) 0 0 var(--libro-sangria);
  /* sin padding-bottom: el rail tiene que morir exactamente en la doble regla */
}
.obras__total::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0;
  width: 1px; background: var(--line-strong);
  transform: scaleY(1); transform-origin: top;
}
.obras__total-label {
  grid-column: 1; padding-top: .55em;
  font-family: var(--font-ui); font-weight: 700;
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--ink-mute);
}
.obras__total .pull-quote { grid-column: 2 / -1; align-items: flex-start; text-align: left; gap: 0; }
.obras__total .pull-quote p { max-width: 34ch; }
.obras__doble {
  grid-column: 1 / -1;
  margin-left: calc(var(--libro-sangria) * -1);
  margin-top: clamp(1.5rem, 1.1rem + 1.2vw, 2.25rem);
  display: grid; gap: 3px;
}
.obras__doble i {
  display: block; height: 1px; background: var(--ink);
  transform: scaleX(1); transform-origin: left center;
}

/* ── scroll-driven, igual que el libro de la home ────────────────────────── */
/* Todos los rangos son `cover` y ninguno `entry`: la longitud de entry ES la
   altura del sujeto, así que `entry 40%` sobre el .obra__marca de 12px se
   resolvería en ~5px de scroll. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .obra::before,
    .obras__total::before  { transform: scaleY(0); animation: obs-libro-rail linear both;
                             animation-timeline: view(); animation-range: cover 12% cover 42%; }
    .obra__num             { animation: obs-libro-num linear both;
                             animation-timeline: view(); animation-range: cover 32% cover 52%; }
    .obra__marca::before   { animation: obs-libro-filete linear both;
                             animation-timeline: view(); animation-range: cover 30% cover 50%; }
    .obra__marca::after    { clip-path: inset(0 100% 0 0);
                             animation: obs-libro-tramado linear both;
                             animation-timeline: view(); animation-range: cover 40% cover 66%; }
    .obras__doble i        { transform: scaleX(0); animation: obs-libro-doble linear both;
                             animation-timeline: view(); animation-range: cover 30% cover 58%; }
    .obras__doble i:last-child { animation-range: cover 38% cover 66%; }
  }
}


/* ==========================================================================
   04 · LA VISTA — el panel que sigue al cursor

   Es el ProjectShowcase del prompt, portado a vanilla. Lo mueve js/proyectos.js
   con suavizado lerp a 0.15 (el factor del original) sobre requestAnimationFrame.

   Va FUERA de las secciones y position: fixed, por el mismo motivo anotado en
   la pastilla de la home: un fixed dentro de un ancestro recortado es frágil
   según el navegador.

   Y absorbe el trabajo de .cursor-pill —dice "Ver proyecto" o "En preparación"
   en su propio pie— así que estas páginas no incluyen aquel marcado ni ponen
   data-cursor-scope. Un solo objeto persiguiendo el cursor, no dos.
   ========================================================================== */
.vista {
  position: fixed; left: 0; top: 0;
  z-index: var(--z-cursor);
  pointer-events: none;
  will-change: transform;
}
.vista__marco {
  position: relative;
  width: clamp(300px, 26vw, 420px);
  aspect-ratio: 4 / 3;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--bg-sunk);
  box-shadow: var(--shadow-lift);
  /* El desplazamiento respecto del cursor va acá y no en el transform de
     afuera, que lo escribe el JS cuadro a cuadro. --vista-lado lo voltea el
     JS cuando el panel no entra a la derecha del puntero. */
  transform: translate(var(--vista-lado, 28px), -50%) scale(.86);
  opacity: 0;
  transition: opacity var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}
.vista.is-on .vista__marco { opacity: 1; transform: translate(var(--vista-lado, 28px), -50%) scale(1); }

.vista__img {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  opacity: 0;
  transform: scale(1.1);
  filter: blur(10px);
  transition: opacity 500ms var(--ease-out), transform 500ms var(--ease-out), filter 500ms var(--ease-out);
}
.vista__img.is-shown { opacity: 1; transform: scale(1); filter: blur(0); }

/* El hueco cuando la ficha todavía no tiene captura. Sin esto el panel se
   abre en negro y parece un error de carga. */
.vista__hueco {
  position: absolute; inset: 0;
  display: none; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--accent-2) 8%, var(--bg-sunk));
  font-family: var(--font-ui); font-weight: 600;
  font-size: 10px; letter-spacing: .1em; text-transform: uppercase;
  color: color-mix(in srgb, var(--accent-2) 85%, var(--ink));
}
.vista.is-vacia .vista__hueco { display: flex; }

.vista__pie {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: var(--sp-6) var(--sp-4) var(--sp-3);
  display: flex; justify-content: space-between; align-items: flex-end; gap: var(--sp-3);
  background: linear-gradient(180deg, transparent, rgba(29,28,24,.78));
  font-family: var(--font-ui); font-weight: 700;
  font-size: 11px; letter-spacing: .12em; text-transform: uppercase;
  color: var(--brand-crema);
}
.vista__indice { font-variant-numeric: tabular-nums; opacity: .7; }

/* Sin puntero no hay nada que seguir. El JS también se apaga solo, pero la
   regla va igual: si el JS no llega, el panel no puede quedar colgado. */
@media (pointer: coarse), (hover: none) { .vista { display: none; } }
@media (prefers-reduced-motion: reduce) { .vista { display: none; } }


/* ==========================================================================
   05 · EL MAPA — las 24 provincias, con textura de grabado

   REEMPLAZA al dibujo anterior, que era un poligono de 30 vertices trazado a
   mano, monocromo, sin limites internos y clavado en 290px de ancho dentro de
   un contenedor de 1200. O sea: una vineta.

   Ahora la geometria sale de datos publicos (claude/generador/provincias.mjs,
   ver convertir-provincias.mjs) con la proyeccion que le corresponde al pais, y
   la seccion es a sangre con tope de 1920.

   EL RAYADO A BURIL. La textura es un <pattern> de lineas finas en diagonal, o
   sea la tecnica del grabado que es la direccion de arte del proyecto. Se
   descarto clipear assets/plancha.webp: son 264 KB de red para un relleno
   decorativo, y ademas la plancha ya tiene su lugar reservado en el muro del
   pie. El pattern usa currentColor, asi que el color lo pone .mapa__prov y el
   tema lo arrastra solo.
   ========================================================================== */

/* LAS DOS MEDIDAS DEL ESCENARIO, en un solo lugar porque las consume media
   sección y porque cambian juntas en cada breakpoint.

   --mapa-alto es el alto de HOY del dibujo argentino, sin tocar una unidad.
   --mapa-caja es el ancho de la caja donde entran los tres mapas. La cuenta que
   las relaciona está en .mapa__lienzo, más abajo; la regla que no se puede
   romper es que la caja SIEMPRE sea más ancha que 0.484 × el alto, que es lo
   que mide Argentina. Si no, el país se sale de su propia caja. */
.mapa {
  --mapa-alto: clamp(460px, 56vw, 940px);
  --mapa-caja: clamp(280px, 40vw, 680px);
}

/* LA CABECERA ASIMÉTRICA, POR FIN COMPLETA. El comentario de arriba de este
   bloque promete «título grande a la izquierda, texto a la derecha», y la
   derecha estaba vacía desde que se le sacó la bajada: quedó el titular solo,
   capado a 22ch para no estirarse a lo ancho de los 1920 de .marco--ancho.
   Ese hueco es exactamente donde va el cambio de mapa. No se inventó un lugar
   para el control: se le dio el que ya estaba reservado. */
.mapa__head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-5) clamp(1.5rem, 1rem + 2vw, 3rem);
}
.mapa__head .h2 { max-width: 22ch; }
.mapa__titular .lead { margin-top: var(--sp-2); max-width: 40ch; }

/* ── el cambio de mapa ──────────────────────────────────────────────────── */
/* Sin una línea de JS: tres <input type="radio"> escondidos y un par de <label>
   por estado. El porqué del marcado está en claude/generador/mapa.mjs.
   El foco vive en el radio —un <label> no es focusable— así que el aro va en el
   grupo entero, que es lo que la persona está operando.

   Y el position:relative no es decorativo: .visually-hidden posiciona en
   absoluto, así que sin un ancestro posicionado los tres radios se irían a
   buscar el <body> y el navegador scrollearía a cualquier parte al enfocarlos
   con el teclado. Anclados acá, el foco cae donde está el control. */
.mapa__cambio { position: relative; display: flex; align-items: center; }
.mapa__cambio:has(input:focus-visible) {
  outline: 2px solid var(--accent-2);
  outline-offset: 8px;
  border-radius: var(--radius-pill);
}

.mapa__switch { display: none; align-items: center; gap: var(--sp-2); }
.mapa:has(#mapa-r-ar:checked) .mapa__switch--ar,
.mapa:has(#mapa-r-na:checked) .mapa__switch--na,
.mapa:has(#mapa-r-eu:checked) .mapa__switch--eu { display: flex; }

/* 44px de lado por el objetivo táctil de WCAG 2.2, no por estética: el chevron
   dibujado adentro mide 18. */
.mapa__flecha {
  display: inline-flex; align-items: center; justify-content: center;
  width: 44px; height: 44px;
  border-radius: 50%;
  cursor: pointer;
  color: var(--ink);
  box-shadow: inset 0 0 0 1px var(--line);
  transition: color var(--dur) var(--ease-out),
              box-shadow var(--dur) var(--ease-out),
              transform var(--dur) var(--ease-spring);
}
.mapa__flecha svg { width: 18px; height: 18px; }
.mapa__flecha:hover { color: var(--accent-2); box-shadow: inset 0 0 0 1px var(--accent-2); }
.mapa__flecha:active { transform: scale(.94); }

/* min-width, y en ch: los tres nombres miden distinto («Argentina»,
   «Norteamérica», «Europa») y sin un piso las dos flechas se corrían de lugar
   en cada cambio — que es justo lo que un control de este tipo no puede hacer. */
.mapa__switch-txt { display: grid; justify-items: center; gap: 2px; min-width: 13ch; }
.mapa__switch-nombre {
  font-family: var(--font-ui); font-weight: 700;
  font-size: var(--fs-sm);
  line-height: 1;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink);
  white-space: nowrap;
}
.mapa__switch-indice {
  font-family: var(--font-display);
  font-size: var(--fs-xs);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--accent-2);
}

/* Las tramas izadas no ocupan lugar: es un <svg> de 0x0 con los tres <pattern>
   que comparten los mapas. Ver la nota del marcado. */
.mapa__tramas { position: absolute; width: 0; height: 0; overflow: hidden; }

/* .mapa__grid YA NO ES LA GRILLA: es el contenedor de los tres paneles, de los
   que se ve uno solo. La grilla de dos columnas se mudó entera a .mapa__panel,
   sin cambiarle un valor. */
.mapa__grid { margin-top: clamp(2.5rem, 1.8rem + 2.4vw, 4rem); }

.mapa__panel {
  display: none;
  /* `auto` y no una fraccion: dentro de su caja los tres mapas son mas altos
     que anchos. Con dos columnas fraccionarias el dibujo quedaba flotando en el
     medio de una columna el doble de ancha que el, con espacio muerto a los dos
     lados, y la lista desaprovechaba el resto.
     Dejando que la columna mida lo que mide la caja, todo lo que sobra se lo
     lleva la lista y la seccion ocupa el ancho de verdad. */
  grid-template-columns: auto minmax(0, 1fr);
  gap: clamp(2rem, 1.4rem + 4vw, 6rem);
  /* ⚠ start Y NO center, Y ES LA REGLA QUE SOSTIENE EL DESPLEGABLE.
     Con center, la fila de la grilla mide lo que la columna más alta y el
     dibujo se acomoda en el medio de lo que sobre. Mientras la lista fue más
     baja que el mapa daba igual — pero al abrir Buenos Aires entran ocho filas
     más y la lista pasa a ser la más alta: la fila crece y el dibujo BAJA unos
     60px. Justo lo que la nota de .mapa__lienzo existe para impedir ("la lista
     no se corre ni un píxel al cambiar de mapa").

     Y no cambia nada de lo que ya estaba: se midió a 2560 y 1440 —los dos
     anchos donde el panel tiene dos columnas— y con la lista cerrada manda
     siempre el dibujo, así que start y center caen en el mismo píxel. De 900
     para abajo el panel se apila y cada columna es su propia fila, con lo que
     la alineación vertical ya no participa. */
  align-items: start;
}
/* Se ve el panel del radio marcado, y nada más. */
.mapa:has(#mapa-r-ar:checked) .mapa__panel--ar,
.mapa:has(#mapa-r-na:checked) .mapa__panel--na,
.mapa:has(#mapa-r-eu:checked) .mapa__panel--eu { display: grid; }

/* EL PANEL QUE APARECE ENTRA CON UN DESVANECIDO, y no hace falta ni JS ni una
   clase: como pasa de display:none a grid, la animación arranca sola. Corta a
   propósito —280 ms— porque la primera vez que se muestra un panel compite con
   el reveal de sus propios ítems. */
@media (prefers-reduced-motion: no-preference) {
  .mapa__panel { animation: obs-mapa-entra .28s var(--ease-out) both; }
}
@keyframes obs-mapa-entra { from { opacity: 0; } to { opacity: 1; } }

/* EL ESCENARIO — una caja de dimensiones fijas donde entran los tres mapas.

   ⚠ EXISTE PORQUE LOS TRES RATIOS SON INCOMPATIBLES, y ahí está toda la
   dificultad de esta sección. Argentina mide 0.484 —angosta y muy alta—;
   Norteamérica 0.945 y Europa 0.975, o sea casi cuadrados. Con la regla vieja
   (alto fijo, ancho libre) un continente salía de 916 px de ancho contra los
   455 de Argentina: el doble de columna, la lista aplastada, y la sección
   cambiando de forma en cada click.

   Con la caja fija pasa lo contrario, que es lo que se pidió: la sección mide
   lo mismo con cualquiera de los tres y la lista no se corre ni un píxel al
   cambiar de mapa. A Argentina la sigue limitando el ALTO, así que se dibuja
   EXACTAMENTE del mismo tamaño que antes de que esta caja existiera, centrada
   en ella. A los continentes los limita el ANCHO.

   ⚠ Y LOS 680 px DE TOPE NO SON UN NÚMERO SUELTO: son el ANCHO_RENDER de
   claude/generador/convertir-continentes.mjs, de donde sale la tolerancia con
   que se simplificó la geometría de los dos continentes. Si cambia este, hay
   que cambiar aquel y regenerar continentes.mjs. */
.mapa__lienzo {
  justify-self: center;
  display: grid;
  width: var(--mapa-caja);
  min-height: var(--mapa-alto);
  /* ⚠ start Y NO center EN EL EJE HORIZONTAL, y esto se midió: centrado, el
     dibujo argentino —que es más angosto que la caja— se corría 50px a la
     derecha respecto de donde estaba antes de que la caja existiera. El tamaño
     daba idéntico y la posición no, que es igual de grave. Anclado al arranque
     queda en su x de siempre y el ancho que sobra se va al canal que ya había
     entre el mapa y la lista.
     Los continentes llenan la caja, así que a ellos no les cambia nada. */
  align-items: center;
  justify-items: start;
}
/* Argentina: el ALTO manda y el ancho sale del ratio. Al reves —fijando el
   ancho— o el pais quedaba enano o la seccion medía tres pantallas. */
.mapa__svg {
  display: block;
  height: var(--mapa-alto);
  width: auto;
  overflow: visible;
}
/* Los continentes, al revés: manda el ancho de la caja y el alto sale del
   ratio. El preserveAspectRatio de SVG —que por defecto ya es «meet»— hace el
   resto, así que no hay una segunda cuenta que mantener sincronizada. */
.mapa__svg--cont { width: 100%; height: auto; }

/* ── las provincias ─────────────────────────────────────────────────────── */
/* color pone el currentColor que consume el <pattern>; fill lo usa. */
.mapa__prov {
  color: var(--ink);
  fill: url(#buril);
  stroke: var(--line-strong);
  /* 15 unidades = 0,9px en pantalla. Ver la nota de escala en mapa.mjs: acá
     un stroke-width de 6 sería un tercio de pixel, o sea nada. */
  stroke-width: 15;
  stroke-linejoin: round;
  paint-order: fill stroke;
  transition: color var(--dur) var(--ease-out);
}
/* Las siete con trabajo: trama mas densa y en cobre. El "no monocromo" del
   pedido se resuelve con DENSIDAD y un solo acento, no con una paleta: un mapa
   de doce colores seria un grafico, y esto es un grabado. */
.mapa__prov--activa {
  color: var(--accent-2);
  fill: url(#buril-denso);
  stroke: color-mix(in srgb, var(--accent-2) 55%, transparent);
  stroke-width: 20;
}
/* NO hay un contorno de pais aparte, y no es un olvido: unir 24 poligonos para
   sacar el borde exterior es un problema de geometria que no paga lo que da.
   El canto de afuera ya se lee solo porque del otro lado no hay trama, mientras
   que los limites internos separan dos tramas iguales. La jerarquia sale del
   contraste con el fondo, no de un trazo mas grueso. */
.mapa__formas { stroke-linejoin: round; }

/* ── las Malvinas y su bandera ──────────────────────────────────────────── */
/* Las islas heredan .mapa__prov: mismo filete, misma tinta, mismo grabado — no
   pueden leerse como una calcomanía pegada encima.

   Lo único que cambia es la DENSIDAD de la trama, y es obligatorio: con el
   rayado de las provincias (paso de 8,5 px) una isla de 26 px de alto se lleva
   tres líneas y se ve como un garabato roto, no como tierra. Ver la nota del
   pattern en mapa.mjs. */
.mapa__isla { fill: url(#buril-isla); stroke-width: 12; }

/* EL ASTA. Un pelo más marcada que un límite de provincia para que se lea como
   objeto y no como frontera. */
.mapa__asta {
  stroke: var(--ink);
  stroke-width: 22;
  stroke-linecap: round;
}

/* EL PAÑO. El celeste sale del token de acento de la casa y no de un celeste
   patrio literal: es el mismo azul que ya usa el sitio, así que el detalle
   entra en la paleta en vez de pelearse con ella.

   El blanco va literal —una bandera con la banda del color del papel no tiene
   banda— y por eso el filo de tinta alrededor: sin él, sobre el crema, el paño
   se desfonda por el medio. */
.mapa__pano-celeste { fill: var(--brand-celeste-lt); }
.mapa__pano-blanco  { fill: #F7F4EE; }
.mapa__sol          { fill: var(--accent-2); }
.mapa__pano-filo    { fill: none; stroke: var(--ink); stroke-width: 14; }

/* EL FLAMEO. Muy corto de amplitud a propósito: a 33 px de ancho, cualquier
   deformación que se note de verdad convierte el paño en una gelatina. Es un
   respiro, no una animación de bandera.
   El origen va en el asta (izquierda) porque es de donde cuelga. */
@media (prefers-reduced-motion: no-preference) {
  .mapa__pano {
    transform-box: fill-box;
    transform-origin: left center;
    animation: obs-flamear 4.5s var(--ease-out) infinite;
  }
}
@keyframes obs-flamear {
  0%, 100% { transform: scaleX(1)    skewY(0deg); }
  35%      { transform: scaleX(.955) skewY(1.1deg); }
  70%      { transform: scaleX(.985) skewY(-.7deg); }
}

.mapa__punto { fill: var(--accent-2); transition: r var(--dur) var(--ease-out); }
.mapa__halo {
  fill: none;
  stroke: var(--accent-2);
  stroke-width: 20;
  opacity: 0;
  transform-box: fill-box;
  transform-origin: center;
}

/* ── la lista ───────────────────────────────────────────────────────────── */
.mapa__lista { list-style: none; margin: 0; padding: 0; }

/* EL ITEM YA NO ES LA FILA: ES LA FILA MÁS SU DESPLEGABLE.
   La grilla de tres columnas que vivía acá se mudó tal cual al <button> de
   abajo, sin cambiarle un valor. Lo que queda en el <li> es lo que sigue
   describiendo al ítem entero: su filete de arriba y el de abajo del último.

   Los dos filetes los pone también .accordion__item, con el mismo valor y el
   mismo :last-child — no es una duplicación que haya que resolver, es la prueba
   de que el acordeón de la casa y esta lista ya estaban dibujados igual. Se
   dejan escritos acá para que la lista se entienda sin ir a components.css. */
.mapa__item { border-top: 1px solid var(--line); }
.mapa__item:last-child { border-bottom: 1px solid var(--line); }

/* LA FILA QUE ABRE. Hereda de .accordion__trigger el ancho, el padding, la
   alineación del texto y el peso; lo único que le cambia es el display —de flex
   a grid— porque estas cuatro columnas tienen que caer en la misma retícula en
   las siete filas, y con flex el número y el lugar bailaban según el largo del
   nombre.
   La cuarta columna son los 20px de .accordion__sym, y están reservados
   SIEMPRE: si aparecieran con el hover, la crucecita empujaría la cuenta al
   entrar el puntero. */
.mapa__abrir {
  display: grid;
  grid-template-columns: 3.25rem minmax(0, 1fr) auto 20px;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-6) 0;
  min-height: 48px;
  font-size: inherit;
}
/* El :hover de .accordion__trigger pinta el BOTÓN, y el nombre tiene color
   propio, así que sin esta línea el hover de la fila no se ve. */
.mapa__abrir:hover .mapa__item-lugar { color: var(--accent); }
.mapa__item-num {
  font-family: var(--font-display);
  font-size: 1.25rem; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--accent-2);
}
/* Mas grande que antes: la lista ahora ocupa dos tercios del ancho, y con
   texto de 15px al lado de un mapa de casi mil pixeles quedaba desbalanceada. */
.mapa__item-lugar {
  font-family: var(--font-ui); font-weight: 700;
  font-size: var(--fs-h3);
  line-height: var(--lh-snug);
  letter-spacing: -0.005em;
  color: var(--ink);
}
.mapa__item-rubro {
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-mute);
}
.mapa__item-num { align-self: start; padding-top: .35em; }

/* LA CRUCECITA, QUE APARECE CON EL HOVER — que es literalmente lo pedido: "al
   hacer hover debe aparecer la opción de desplegar". En reposo la fila se ve
   como se vio siempre; el signo entra cuando llega el puntero.

   Se enciende en TRES estados y los tres hacen falta: hover (el pedido), foco
   de teclado (si no, quien navega con Tab opera un control invisible) y abierto
   (una fila abierta tiene que poder cerrarse sin ir a buscarla con el puntero).

   ⚠ Y EN TOUCH VA SIEMPRE VISIBLE. En un teléfono no hay hover: sin la regla de
   abajo el desplegable sería, sencillamente, invisible. Es el mismo criterio
   por el que .no-js abre los paneles más abajo — el gesto que revela la puerta
   no puede ser el único que la abre. */
.mapa__item-sym { opacity: 0; transition: opacity var(--dur) var(--ease-out); }
.mapa__item:hover .mapa__item-sym,
.mapa__abrir:focus-visible .mapa__item-sym,
.mapa__item.is-open .mapa__item-sym { opacity: 1; }
@media (hover: none) { .mapa__item-sym { opacity: 1; } }

/* La cuenta ocupa el lugar que tenía el rubro y usa su misma tipografía de
   etiqueta, así que la columna derecha se ve igual que siempre. Dice "8 sitios"
   y no un rubro porque una ubicación con ocho sitios no TIENE un rubro: cada
   uno trae el suyo, adentro. */
.mapa__item-cuenta {
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-mute);
  font-variant-numeric: tabular-nums;
}
.mapa__item.is-open .mapa__item-cuenta { color: var(--accent-2); }

/* ── el desplegable ─────────────────────────────────────────────────────── */
/* La altura la anima .accordion__panel con grid-template-rows 0fr→1fr, que es
   la técnica de la casa y la única que anima de verdad sin medir alturas en JS.
   Acá va sólo lo de adentro.

   Sangrado hasta debajo del NOMBRE y no del número: las filas de sitio cuelgan
   de la ubicación, y arrancarlas contra el canto izquierdo las pondría al mismo
   nivel jerárquico que ella. La cuenta es la primera columna del trigger más su
   gap, o sea la misma que ya separa el número del lugar. */
.mapa__webs {
  list-style: none;
  margin: 0;
  padding: 0 0 var(--sp-5) calc(3.25rem + var(--sp-4));
}
.mapa__web {
  display: grid;
  /* Cuatro columnas, y la ultima casi siempre vacia: solo cinco de los veinte
     sitios llevan nota. Va en `auto` —no reservada— justamente por eso: una
     columna fija de ancho de chip abriria un canal muerto en las quince filas
     que no la tienen. */
  grid-template-columns: 20px minmax(0, 1fr) auto auto;
  align-items: center;
  gap: var(--sp-3);
  min-height: 40px;
  padding: var(--sp-2) 0;
  /* Un filete MÁS CLARO que el de las ubicaciones. Con --line a secas, las ocho
     filas de Buenos Aires se leían como ocho ubicaciones más y el desplegable
     dejaba de verse como un adentro. */
  border-top: 1px solid color-mix(in srgb, var(--line) 55%, transparent);
}

/* El favicon, a 20px. Chico a propósito: la fila tiene que barrerse de un
   vistazo y un logo grande convertiría el desplegable en una galería.
   object-fit: contain porque los favicons vienen en cualquier proporción. */
.mapa__favicon {
  width: 20px; height: 20px;
  border-radius: 3px;
  object-fit: contain;
}
/* EL MONOGRAMA — para un sitio que no publica un favicon usable. La inicial en
   la display dentro de una caja con filete: la misma mano del grabado, así que
   una fila sin favicon no se lee como una fila rota. */
.mapa__favicon--mono {
  display: grid; place-items: center;
  font-family: var(--font-display);
  font-size: 12px; line-height: 1;
  color: var(--accent-2);
  box-shadow: inset 0 0 0 1px var(--line-strong);
}
/* EL HUECO: la caja .ph de la casa —su tinte de acento— achicada a 20px y
   REDONDA. .ph viene con padding y radio de bloque grande, que a este tamaño la
   borran; las escuadras ya las saca .ph--plain desde el marcado.

   ⚠ Y SE LE SACA EL ARO, que es lo unico que no se hereda tal cual. Con el aro
   inset de .ph, un cuadradito vacio repetido ocho veces en columna deja de
   leerse como "acá va un favicon" y se lee como OCHO CASILLAS DE TILDAR: un
   control que no existe, en una lista donde el unico control es la fila. Redondo
   y macizo se lee como lo que es —una marca pendiente— y ademas rima con el
   punto del mapa, que es el mismo gesto. El aviso de hueco no se pierde: lo dan
   el tinte de acento y la tipografia de .ph__label de MARCA_n / RUBRO. */
.mapa__favicon.ph {
  padding: 0;
  border-radius: 50%;
  box-shadow: none;
  background-color: color-mix(in srgb, var(--accent-2) 22%, transparent);
}

.mapa__web-nombre {
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  color: var(--ink);
  transition: color var(--dur) var(--ease-out);
}
a.mapa__web-nombre:hover {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.mapa__web-rubro {
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-mute);
}
/* LA NOTA — "Aliado" / "con Blacklead". Es .chip de la casa, con el padding y
   el cuerpo bajados a la escala de la fila: entero (0.4/0.8rem, --fs-xs) pesaba
   mas que el nombre del sitio, que es lo que la fila tiene que decir primero.
   Se queda con lo que hace al chip un chip: la pildora y el filete.

   El acento va en el FILETE y no en el relleno. Con fondo pleno, cinco chips
   repartidos entre veinte filas se leian como cinco alertas; con el filete se
   leen como lo que son, una precision al margen. */
.mapa__web-nota {
  padding: .15rem .5rem;
  font-family: var(--font-ui); font-weight: 600;
  font-size: 10px;
  letter-spacing: .06em;
  white-space: nowrap;
  color: color-mix(in srgb, var(--accent-2) 90%, var(--ink));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--accent-2) 40%, transparent);
}

/* ⚠ UN PANEL CERRADO TIENE QUE SALIR DEL ORDEN DE TABULACION, Y overflow NO LO
   HACE. El acordeón de la casa cierra con grid-template-rows: 0fr más un
   overflow: hidden en el hijo. Eso lo esconde a la vista y nada más: overflow no
   saca del recorrido del teclado. Sólo lo hacen display:none, visibility:hidden,
   content-visibility:hidden, inert o el atributo hidden.

   Mientras adentro hubo huecos sin enlace daba igual. Con los veinte sitios
   cargados, tabular por esta sección se metía en los enlaces de LOS CATORCE
   paneles cerrados: veinte destinos invisibles antes de salir de la lista.
   Medido tabulando de verdad, no mirando: claude/piezas/mapa-a11y.mjs.

   visibility y no display porque display mata la animación de altura, que es
   toda la gracia del acordeón. El truco es el retardo: al ABRIR, visibility
   cambia en 0s (el contenido tiene que estar ahí mientras el panel crece); al
   CERRAR, espera --dur-slow, o sea que el panel termina de plegarse y recién
   ahí desaparece. Sin ese retardo el contenido se borra de golpe y el pliegue
   se ve vacío.

   ⚠ Y ES UN ARREGLO LOCAL A ESTA LISTA. El .accordion__panel de components.css
   sigue igual: hoy no le pasa porque el único que lo usa —el de preguntas de la
   home— tiene sólo <p> adentro. El día que una respuesta lleve un enlace, hay
   que subir estas dos reglas allá. */
.mapa__sitios {
  visibility: hidden;
  transition: grid-template-rows var(--dur-slow) var(--ease-expo),
              visibility 0s linear var(--dur-slow);
}
.accordion__item.is-open .mapa__sitios {
  visibility: visible;
  transition: grid-template-rows var(--dur-slow) var(--ease-expo),
              visibility 0s;
}

/* SIN JS LOS PANELES QUEDAN ABIERTOS. El toggle es el módulo 8 de main.js: sin
   él, grid-template-rows se queda en 0fr para siempre y los enlaces de adentro
   son inalcanzables — no invisibles, INALCANZABLES, también para el teclado.
   Mismo criterio que .no-js .obra__flecha, más abajo. */
.no-js .mapa__sitios { grid-template-rows: 1fr; visibility: visible; }
.no-js .mapa__item-sym { display: none; }

/* El puente lista → mapa sin una linea de JS. :has() ya es tecnica aprobada en
   esta casa (base.css lo usa para el ritmo vertical). Son siete pares porque
   CSS no tiene bucles; la alternativa era JS para algo que tiene que seguir
   funcionando sin JS.

   ⚠ EL ÁMBITO ES .mapa__panel Y NO .mapa__grid, y ese cambio es lo que impide
   un bug nuevo: con tres listas en la misma página, el ítem 01 de Europa habría
   encendido también el punto 01 de Argentina. Acotado al panel, cada lista sólo
   alcanza a su propio mapa. Siguen siendo siete pares porque siete es el mapa
   más poblado; los otros dos tienen tres puntos y uno. */
.mapa__panel:has(.mapa__item[data-p="1"]:hover) .mapa__punto[data-p="1"],
.mapa__panel:has(.mapa__item[data-p="2"]:hover) .mapa__punto[data-p="2"],
.mapa__panel:has(.mapa__item[data-p="3"]:hover) .mapa__punto[data-p="3"],
.mapa__panel:has(.mapa__item[data-p="4"]:hover) .mapa__punto[data-p="4"],
.mapa__panel:has(.mapa__item[data-p="5"]:hover) .mapa__punto[data-p="5"],
.mapa__panel:has(.mapa__item[data-p="6"]:hover) .mapa__punto[data-p="6"],
.mapa__panel:has(.mapa__item[data-p="7"]:hover) .mapa__punto[data-p="7"] { r: 190; }

.mapa__panel:has(.mapa__item[data-p="1"]:hover) .mapa__halo[data-p="1"],
.mapa__panel:has(.mapa__item[data-p="2"]:hover) .mapa__halo[data-p="2"],
.mapa__panel:has(.mapa__item[data-p="3"]:hover) .mapa__halo[data-p="3"],
.mapa__panel:has(.mapa__item[data-p="4"]:hover) .mapa__halo[data-p="4"],
.mapa__panel:has(.mapa__item[data-p="5"]:hover) .mapa__halo[data-p="5"],
.mapa__panel:has(.mapa__item[data-p="6"]:hover) .mapa__halo[data-p="6"],
.mapa__panel:has(.mapa__item[data-p="7"]:hover) .mapa__halo[data-p="7"] { opacity: .6; }

.mapa__pie {
  margin-top: var(--sp-6);
  font-size: var(--fs-sm);
  color: var(--ink-mute);
  text-wrap: pretty;
}

/* El pulso va sobre el HALO y nunca sobre el punto: escalar el punto lo
   correria respecto de su coordenada real, que es lo que este dibujo tiene que
   decir bien. */
@media (prefers-reduced-motion: no-preference) {
  .mapa__halo { animation: obs-mapa-pulso 3.2s var(--ease-out) infinite; animation-delay: var(--d, 0s); }
}
@keyframes obs-mapa-pulso {
  0%   { opacity: .55; transform: scale(.6); }
  70%  { opacity: 0;   transform: scale(2.4); }
  100% { opacity: 0;   transform: scale(2.4); }
}


/* ==========================================================================
   06 · CIERRE DE PÁGINA — el panel de "Nuestra Esencia" de /nosotros/,
   reutilizado como CTA de cierre. Es una copia deliberada de
   css/nosotros.css § 02 (.nesencia__panel / __trama / __vidrio), bajo su
   propio nombre porque esta hoja no importa nosotros.css: mismo fondo,
   misma retícula de puntos sin un byte de red, mismo radio y el mismo vidrio
   de 40rem, y el mismo rastro de imágenes detrás del cursor. La única
   diferencia es de copy: acá la tarjeta cierra con un CTA en vez de presentar.
   ========================================================================== */

/* ⚠ LA SECCIÓN DECLARA EL TEMA Y NO PINTA. `base.css` § 187 dice
   `[data-theme] { background: var(--bg) }`, o sea que toda sección temada
   pinta su fondo. El `data-theme="dark"` tiene que estar en la <section>
   —main.js saca de ahí las zonas para voltear el nav— pero si además pinta, el
   negro se come el filo de 12px del embutido y aparecen DOS FRANJAS NEGRAS
   arriba y abajo del panel: deja de ser un panel y pasa a ser una banda a
   sangre. El negro lo pone el panel; la sección sólo dice qué hay debajo.
   Es la misma corrección que ya tiene anotada nosotros.css para .nesencia. */
.pcierre { background: transparent; }

.pcierre__panel {
  position: relative;
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 90svh;
  padding: var(--sp-6);
  border-radius: var(--radius-xl);
  overflow: clip;
  color: var(--ink);
  background:
    radial-gradient(115% 80% at 50% 0%, var(--bg-elev), transparent 64%),
    var(--bg-sunk);
}

/* La misma retícula que nosotros.css § 02: un cuadrado del alto del panel,
   centrado, apagándose hacia el borde. Ver la nota larga de ahí para la
   cuenta del radio — acá sólo se copia el resultado. */
.pcierre__trama {
  position: absolute;
  top: 50%; left: 50%;
  height: 100%;
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  z-index: 1;
  pointer-events: none;
  --paso: 4.0364%;
  background-image: radial-gradient(circle at 50% 50%,
    color-mix(in srgb, var(--ink) 40%, transparent) 0 6.84%, transparent 6.94%);
  background-size: var(--paso) var(--paso);
  -webkit-mask-image: radial-gradient(closest-side at 50% 50%, #000, transparent);
          mask-image: radial-gradient(closest-side at 50% 50%, #000, transparent);
}

/* LA CAPA DEL RASTRO — entre la retícula y la tarjeta.

   ⚠ VA POR DETRÁS DE LA TARJETA. Así el `backdrop-filter` del vidrio difumina
   las imágenes que pasan, que es exactamente para lo que existe el vidrio, y
   la tarjeta nunca pierde legibilidad. No necesita `overflow`: el panel ya
   recorta con `clip`. */
.pcierre__rastro { position: absolute; inset: 0; z-index: 2; pointer-events: none; }

/* El precalentado: el <template> clonado a un rincón de 1px para que las siete
   estén en caché cuando el cursor se mueva. Cada <picture> elige por `media` y
   no por `sizes`, así que este tamaño no cambia qué archivo baja. */
.pcierre__precarga {
  position: absolute; top: 0; left: 0;
  width: 1px; height: 1px;
  overflow: clip; opacity: 0; pointer-events: none;
}

/* UN CLON. Mismos tres tiempos medidos que en /nosotros/ (ver nosotros.css
   § 02): nace en el punto del clon anterior, viaja hasta el cursor con una
   salida larga, aparece en ~200 ms y a los ~515 se apaga encogiéndose. El
   encogido va en el <picture> y no en la <figure> para que el traslado y la
   muerte no se peleen por la misma propiedad `transform`. */
.prastro {
  position: absolute; top: 0; left: 0;
  width: min(36rem, 40vw);
  aspect-ratio: 2 / 2.7;
  margin: 0;
  border-radius: var(--radius-sm);
  overflow: clip;
  will-change: transform, opacity;
  animation:
    obs-prastro-mov var(--rastro-vida, 700ms) cubic-bezier(.16, 1, .3, 1) both,
    obs-prastro-luz var(--rastro-vida, 700ms) cubic-bezier(.33, 1, .68, 1) both;
}
.prastro > picture {
  display: block; width: 100%; height: 100%;
  animation: obs-prastro-fin var(--rastro-vida, 700ms) linear both;
}
.prastro img { display: block; width: 100%; height: 100%; object-fit: cover; }

@keyframes obs-prastro-mov {
  from    { transform: translate3d(var(--x0), var(--y0), 0) translate(-50%, -50%); }
  73%, to { transform: translate3d(var(--x1), var(--y1), 0) translate(-50%, -50%); }
}
@keyframes obs-prastro-luz {
  from    { opacity: 0; }
  29%, to { opacity: 1; }
}
@keyframes obs-prastro-fin {
  from, 73% { opacity: 1; transform: scale(1); }
  to        { opacity: 0; transform: scale(.6); }
}

.pcierre__vidrio {
  position: relative;
  z-index: 3;
  width: 100%;
  max-width: 40rem;
  padding: var(--sp-12);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-6);
  text-align: center;
  border-radius: var(--radius);
  background: rgba(15, 14, 12, .70);
  backdrop-filter: blur(50px);
  -webkit-backdrop-filter: blur(50px);
  box-shadow: inset 0 0 0 var(--hairline) rgba(234, 227, 214, .10);
}
.pcierre__titulo {
  margin: 0;
  font-family: var(--font-ui); font-weight: 700;
  font-size: var(--fs-h2);
  line-height: var(--lh-snug);
  letter-spacing: var(--track-snug);
  color: var(--ink);
}
.pcierre__titulo em { font-family: var(--font-display); font-style: italic; font-weight: 400; }
.pcierre__sub {
  margin: 0;
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-mute);
}
.pcierre__cita {
  margin: 0;
  max-width: 42ch;
  font-family: var(--font-body);
  font-size: var(--fs-lead);
  line-height: 1.5;
  color: var(--ink-mute);
  text-wrap: pretty;
}
@media (max-width: 640px) {
  .pcierre__panel  { min-height: auto; padding-block: var(--sp-14); }
  .pcierre__vidrio { padding: var(--sp-8); }
}


/* ==========================================================================
   07 · PÁGINAS DE CASO — /proyectos/<slug>/
   ========================================================================== */
.caso-head {
  position: relative;
  padding: calc(76px + var(--sp-12)) 0 0;
  background: var(--bg-sunk);
}
.caso-head__volver {
  display: inline-flex; align-items: center; gap: var(--sp-3);
  min-height: 44px;
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-sm);
  color: var(--ink-mute);
  transition: color var(--dur) var(--ease-out);
}
.caso-head__volver:hover { color: var(--ink); }
.caso-head__volver i { font-style: normal; transition: transform var(--dur) var(--ease-out); }
.caso-head__volver:hover i { transform: translateX(-4px); }

.caso-head__barra {
  display: flex; justify-content: space-between; align-items: center; gap: var(--sp-4);
  padding-bottom: var(--sp-6);
  border-bottom: 1px solid var(--line);
}
.caso-head__indice {
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--track-label);
  font-variant-numeric: tabular-nums;
  color: var(--ink-mute);
}

/* LOS DOS ROTULOS EN LINEA, arriba del titular. Es lo que la referencia pone
   ahi: el nombre del proyecto y, al lado, de que trabajo se trata. Reemplazan a
   la tabla de datos de seis campos, que la referencia no tiene y que empujaba
   la portada media pantalla mas abajo. */
.caso-tags {
  display: flex; flex-wrap: wrap; align-items: baseline;
  gap: var(--sp-4) var(--sp-8);
  margin: 0;
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
}
.caso-tags__nombre   { color: var(--ink); }
.caso-tags__servicio { color: var(--ink-mute); }

/* El titular. El tamaño lo pone la clase .h2 del marcado, que en esta escala
   llega a 48px — medido contra la referencia, que ahi tiene ~48px tambien.

   ⚠ NO PONER `font-size: var(--fs-h1)`. Se probó y salió un titular de 14px:
   en esta casa NO EXISTE --fs-h1 ni la clase .h1. La escala va --fs-display
   (40→88) para el hero del sitio y --fs-h2 (28→48) para los titulos de
   seccion, sin nada en el medio (tokens.css § escala fluida). Una custom
   property que no existe no cae al valor de al lado: invalida la declaracion
   entera y el elemento se queda con el tamaño heredado, sin un solo aviso en
   consola. Es la falla mas silenciosa de todo el archivo.

   El tope de ancho va en `em` para que escale con el cuerpo y no se estire a
   los 1200 del contenedor: un titular a ancho completo se lee como banner. */
.caso-head__titulo {
  margin-top: clamp(1.25rem, 1rem + 1.2vw, 2rem);
  max-width: 15em;
}

/* LA PORTADA. Sobresale hacia abajo y cose la cabecera oscura con la seccion
   clara que sigue: es la bisagra entre los dos temas, el mismo trabajo que hace
   un .seam pero con la imagen haciendolo.

   ⚠ EL margin-bottom NEGATIVO ES LA BISAGRA, NO UN AJUSTE DE AIRE. Entre
   .caso-head y .caso-cuerpo no hay ningun .seam: si se saca este voladizo, los
   dos temas quedan pegados con un corte recto y la pagina pierde el unico gesto
   que une su mitad oscura con su mitad clara. (Se perdio una vez, en la 31ª, al
   reescribir el bloque: la imagen quedo sin radio y sin voladizo y el corte se
   veia como un error de maqueta.) */
.caso-head__portada {
  position: relative; z-index: 2;
  margin-top: clamp(2.5rem, 1.8rem + 2.4vw, 4rem);
  margin-bottom: calc(clamp(2.5rem, 1.8rem + 2.4vw, 4rem) * -1);
  aspect-ratio: var(--ratio, 21 / 9);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.caso-head__portada > * { width: 100%; height: 100%; border-radius: inherit; }
.caso-head__portada img { width: 100%; height: 100%; object-fit: cover; }

/* ── 02 · LA SOLUCION: TEXTO PEGADO CONTRA LA PILA ───────────────────────── */
/* ESTA ES LA PIEZA QUE DEFINE LA PAGINA DE REFERENCIA, y la que la version
   anterior no tenia: el texto queda quieto a la izquierda mientras las imagenes
   pasan por la derecha. La diferencia con una galeria en grilla no es de gusto
   — con la grilla el visitante lee el texto, lo deja atras y despues mira
   fotos; asi, lee el caso MIENTRAS mira el trabajo.

   La proporcion es 38 / 62 y no 50/50: la columna de texto tiene que quedar en
   ancho de lectura (unos 46ch) y la de imagen tiene que ser claramente la
   protagonista. */
.caso-sol {
  display: grid;
  grid-template-columns: minmax(0, 38fr) minmax(0, 62fr);
  gap: clamp(2rem, 1.4rem + 3vw, 5rem);
  align-items: start;
}

/* ⚠ align-self: start ES OBLIGATORIO Y NO ES REDUNDANTE CON align-items. Un
   item de grid estira por defecto (`stretch`), y un elemento estirado a la
   altura de su fila NO PUEDE pegarse: sticky necesita que la caja sea mas corta
   que su contenedor para tener recorrido. Con stretch la columna mide lo mismo
   que la pila y el sticky no hace absolutamente nada — sin error, sin aviso, se
   ve como si la propiedad no existiera. Es la misma clase de trampa que ya
   costo una pasada con overflow y animation-timeline.

   El `top` despeja el nav flotante, que mide 76px mas su aire. */
.caso-sol__texto {
  position: sticky;
  top: clamp(96px, 12vh, 140px);
  align-self: start;
  max-width: 46ch;
}
.caso-sol__texto > .h2 { margin: var(--sp-3) 0 clamp(1.5rem, 1.2rem + 1.4vw, 2.5rem); }
.caso-sol__texto p { color: var(--ink-mute); line-height: var(--lh-relaxed); text-wrap: pretty; }
.caso-sol__texto p + p { margin-top: var(--sp-4); }

.caso-sol__par { margin-bottom: clamp(1.5rem, 1.2rem + 1.4vw, 2.25rem); }
.caso-sol__par > .tag,
.caso-sol__entrega > .tag { display: block; margin-bottom: var(--sp-3); color: var(--ink-mute); }

/* La entrega va a UNA columna acá y no a dos. En el bloque de ancho completo
   que tenia antes, dos columnas evitaban un medio renglon vacio; adentro de una
   columna de 46ch, dos columnas darian celdas de 20 caracteres. */
.caso-sol__entrega { margin-top: var(--sp-8); padding-top: var(--sp-6); border-top: 1px solid var(--line); }
.caso-sol__entrega .checklist { display: grid; grid-template-columns: 1fr; gap: var(--sp-4); }

/* La variante sin pila, para las fichas pendientes: no hay imagenes contra las
   que pegarse, asi que no se pega. */
.caso-sol__texto--solo { position: static; max-width: 58ch; }
.caso-sol__aviso {
  display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap;
  margin-top: var(--sp-8); padding-top: var(--sp-6);
  border-top: 1px solid var(--line);
  font-size: var(--fs-sm);
}

/* LA PILA. Una imagen debajo de la otra, cada una con su proporcion desde los
   datos. Sin grilla y sin columnas: es una columna. */
.caso-pila { display: flex; flex-direction: column; gap: clamp(1rem, .8rem + 1vw, 2rem); }
.caso-pila__img {
  aspect-ratio: var(--ratio, 16 / 9);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.caso-pila__img > * { width: 100%; height: 100%; border-radius: inherit; }
.caso-pila__img img { width: 100%; height: 100%; object-fit: cover; }

/* ── 03 · RESULTADOS ─────────────────────────────────────────────────────── */
/* Titulo a la izquierda, parrafo a la derecha, y debajo una sola imagen grande.
   El parrafo arranca ARRIBA (align-items: start) y no centrado: en la
   referencia las dos columnas comparten la linea de tope, que es lo que las
   hace leer como una sola unidad y no como dos bloques sueltos. */
.caso-remate__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2rem, 1.4rem + 3vw, 5rem);
  align-items: start;
  margin-bottom: clamp(2.5rem, 1.8rem + 2.4vw, 4rem);
}
.caso-remate__head .h2 { margin-top: var(--sp-3); max-width: 12em; }
.caso-remate__texto {
  max-width: 52ch;
  color: var(--ink-mute);
  line-height: var(--lh-relaxed);
  text-wrap: pretty;
}
.caso-remate__texto .ph-badge { margin-right: var(--sp-2); }
.caso-remate__img {
  aspect-ratio: var(--ratio, 16 / 9);
  border-radius: var(--radius-lg);
  overflow: hidden;
}
.caso-remate__img > * { width: 100%; height: 100%; border-radius: inherit; }
.caso-remate__img img { width: 100%; height: 100%; object-fit: cover; }

/* ── ver el sitio en vivo ────────────────────────────────────────────────── */
/* Casi nunca se renderiza: el bloque solo existe si el proyecto declara
   `sitio`, y hoy no lo declara ninguno. Ver la nota larga en caso.mjs — el
   manual no deja revelar al cliente, y un link al sitio en vivo lo revela
   entero de un click. */
.caso-sitio__link {
  display: inline-flex; align-items: center; gap: var(--sp-3);
  min-height: 44px;
  margin-top: var(--sp-8);
  padding-bottom: var(--sp-2);
  border-bottom: 1px solid var(--line-strong);
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--ink);
  transition: border-color var(--dur) var(--ease-out);
}
.caso-sitio__link:hover { border-color: var(--accent-2); }
.caso-sitio__flecha { transition: transform var(--dur) var(--ease-out); }
.caso-sitio__link:hover .caso-sitio__flecha { transform: translateX(6px); }


/* ── 04 · el link al siguiente proyecto ──────────────────────────────────── */
/* CENTRADO, como la referencia. Era una barra de ancho completo entre dos
   filetes, con el nombre a la izquierda y la flecha a la derecha: la misma
   informacion, puesta de una forma que la referencia no usa. Ahora es rotulo +
   chip con el nombre, los dos en el eje, y debajo la miniatura ancha.

   Sin filetes arriba y abajo a proposito: los filetes hacian de la barra un
   renglon de tabla, y este bloque no cierra una lista — abre lo siguiente. */
.siguiente__link {
  display: flex; flex-direction: column; align-items: center;
  gap: clamp(1.5rem, 1.2rem + 1.4vw, 2.5rem);
  padding: clamp(1rem, .8rem + 1vw, 2rem) 0 0;
  color: var(--ink);
}
.siguiente__fila {
  display: flex; align-items: center; flex-wrap: wrap; justify-content: center;
  gap: var(--sp-4);
}
.siguiente__eyebrow {
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--track-eyebrow); text-transform: uppercase;
  color: var(--ink-mute);
}
/* El chip. Es el gesto de la referencia: el nombre del proximo proyecto adentro
   de una pastilla, no como titular. Deja que el rotulo y el nombre se lean en
   un solo renglon y le saca peso al bloque, que es un pie de pagina y no una
   seccion. */
.siguiente__chip {
  display: inline-flex; align-items: center;
  min-height: 34px;
  padding: 0 var(--sp-5);
  border-radius: 999px;
  border: 1px solid var(--line-strong);
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--track-label); text-transform: uppercase;
  color: var(--ink);
  transition: border-color var(--dur) var(--ease-out), background-color var(--dur) var(--ease-out);
}
.siguiente__link:hover .siguiente__chip {
  border-color: var(--accent-2);
  background: color-mix(in srgb, var(--accent-2) 12%, transparent);
}

/* LA MINIATURA. Ancha y centrada, no una estampilla al costado: en la
   referencia es lo ultimo que se ve de la pagina y ocupa casi todo el ancho del
   contenedor. Se apaga un poco y se enciende al pasar, para que el chip siga
   siendo lo primero que se lee. */
.siguiente__miniatura {
  display: block;
  width: 100%;
  max-width: 900px;
  aspect-ratio: 21 / 9;
  border-radius: var(--radius-lg);
  overflow: hidden;
  opacity: .68;
  transition: opacity var(--dur) var(--ease-out), transform var(--dur) var(--ease-out);
}
.siguiente__miniatura > * { width: 100%; height: 100%; border-radius: inherit; }
.siguiente__miniatura img { width: 100%; height: 100%; object-fit: cover; }
.siguiente__link:hover .siguiente__miniatura { opacity: 1; transform: scale(1.015); }


/* --------------------------------------------------------------------------
   EL CIERRE — el CTA que remata la ficha

   ⚠ ESTAS TRES CLASES NO TENÍAN UNA SOLA REGLA EN TODO EL REPO hasta la 45ª, y
   están en las NUEVE fichas. `grep -rn "cierre" css/` sólo devolvía .pcierre__*,
   .tira__cierre y .nacred__cierre, que son otra cosa. O sea que el llamado a la
   acción que remata cada caso —el punto de conversión de la página, en un sitio
   cuya acción es «escribir por WhatsApp»— se renderizaba en flujo de bloque:
   pregunta, botón y nota los tres a 16px de IBM Plex, pegados al canto
   izquierdo, sin un milímetro de aire entre ellos. No era un problema móvil:
   estaba así en los ocho anchos.

   Centrado y no alineado a la izquierda porque es lo que hace .siguiente__link
   justo arriba (align-items: center): el cierre es el pie de esa misma
   composición, no un bloque nuevo. Y el filete de arriba lo separa de la
   miniatura, que si no se lee como su epígrafe.

   ⚠ La medida va en `em` y NO en `ch`. Es la trampa que este proyecto ya pagó
   dos veces: `ch` se mide contra la fuente EN USO, así que durante el swap la
   CAJA cambia de ancho junto con el texto y eso es CLS. En `em` la caja no
   depende de la fuente. Acá pesa doble porque .cierre__text va en la display,
   que es justo la que llega tarde.
   -------------------------------------------------------------------------- */
.cierre {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: clamp(1.25rem, 1rem + 1vw, 2rem);
  margin-top: clamp(3.5rem, 2.6rem + 3.4vw, 6rem);
  padding-top: clamp(2.5rem, 2rem + 2vw, 4rem);
  border-top: 1px solid var(--line);
}
.cierre__text {
  font-family: var(--font-display);
  font-size: var(--fs-h2);
  line-height: var(--lh-snug);
  letter-spacing: var(--track-snug);
  color: var(--ink);
  max-width: 14em;
  text-wrap: balance;
}
/* --ink-mute y no --ink-soft: es la quinta vez que --ink-soft no llega a AA en
   este proyecto (placeholder del formulario, .servicios__total, el pie de la
   bobina y la pista de fases). Para texto chico sobre el negro de marca, mute. */
.cierre__nota {
  font-size: var(--fs-sm);
  line-height: var(--lh-normal);
  color: var(--ink-mute);
  max-width: 34em;
  text-wrap: pretty;
}
@media (max-width: 480px) {
  /* El botón a ancho completo, igual que los CTA del hero bajo 640: en el
     pulgar, una píldora centrada de 200px es un blanco chico al final de un
     scroll largo. */
  .cierre .btn { width: 100%; }
}


/* ==========================================================================
   08 · RESPONSIVE

   La rotura que importa es la de 768: la media SALE de la fila y pasa arriba
   del texto. No es una degradación — en táctil no hay hover, así que esa
   miniatura es la única imagen que el visitante va a ver, y el móvil termina
   siendo la versión MÁS visual de las dos.
   ========================================================================== */
/* ⚠ EL PEGADO SE APAGA A 1200 Y NO A 900, Y ESTO SE MIDIO. La columna de texto
   sigue teniendo dos columnas hasta 900, pero el PIN necesita algo mas que dos
   columnas: necesita que la columna de texto sea MAS CORTA que la pila, porque
   la diferencia entre las dos ES el recorrido del pegado.

   Medido en la ficha de Cohen, que es la de texto mas largo de las ocho:

     1440px   texto 1110 · pila 1811  -> 700px de recorrido, se nota
     1100px   texto 1259 · pila 1353  ->  94px, un tiron que parece un glitch
     1024px   texto 1280 · pila 1251  -> NEGATIVO: el pin no engancha nunca

   Al angostarse, la columna de texto CRECE (mas renglones por parrafo) y la
   pila se ACHICA (las imagenes bajan de alto proporcionalmente): las dos curvas
   se cruzan cerca de 1050. Un pin sin recorrido no es "menos elegante", es una
   propiedad que no hace nada y que la proxima persona va a intentar arreglar.
   Por eso se apaga con margen, antes del cruce, y no en el limite. */
@media (max-width: 1200px) {
  .caso-sol__texto { position: static; }
}

@media (max-width: 1100px) {
  .libro--obras {
    --obra-media: clamp(150px, 20vw, 260px);
    --obras-cols: var(--libro-col1) minmax(0, 1fr) minmax(6ch, 9ch) var(--obra-media);
  }
}

@media (max-width: 900px) {
  .mapa__panel { grid-template-columns: 1fr; gap: clamp(2rem, 1.6rem + 2vw, 3rem); }
  /* ⚠ ACA SE DESPEGA EL TEXTO, Y HAY QUE APAGAR LAS DOS COSAS. Con la grilla en
     una sola columna, un `position: sticky` que sobrevive deja el texto flotando
     sobre las imagenes mientras se scrollea la pila: no es "menos elegante", es
     que el texto tapa la imagen. `position: static` es lo que lo corta; el
     max-width se suelta para que no quede una columna de 46ch centrada con aire
     muerto a la derecha. */
  .caso-sol { grid-template-columns: 1fr; gap: clamp(2rem, 1.6rem + 2vw, 3rem); }
  .caso-sol__texto { position: static; max-width: 62ch; }
  .caso-remate__head { grid-template-columns: 1fr; }
}

@media (max-width: 768px) {
  .libro--obras { --libro-col1: 2.5rem; }
  /* Se esconde la cabecera de columnas; el prefijo .visually-hidden de cada
     fila conserva el contexto para el lector de pantalla. */
  .obras__cabecera { display: none; }

  .obra__link {
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas:
      "num   media"
      "texto texto"
      "meta  meta";
    row-gap: var(--sp-5);
    column-gap: var(--sp-4);
  }
  .obra__meta { flex-direction: row; align-items: center; gap: var(--sp-4); padding-top: 0; }
  /* Sin hover, la flecha y el subrayado tienen que estar de entrada. */
  .obra__flecha { opacity: 1; transform: none; }
  .obra__nombre-txt::after { width: 100%; }

  .obras__total { grid-template-columns: auto minmax(0, 1fr); }
  .obras__total .pull-quote { grid-column: 2; }

}

@media (max-width: 640px) {
  .phero { min-height: 74svh; padding-top: calc(64px + var(--sp-10)); }
  .caso-head { padding-top: calc(64px + var(--sp-10)); }
  .mapa__abrir { grid-template-columns: 1.75rem minmax(0, 1fr) 20px; }
  .mapa__item-cuenta { grid-column: 2; }
  .mapa__item-sym { grid-column: 3; grid-row: 1; }
  /* Apilado, la miniatura de 120px quedaba de estampilla huerfana contra el
     canto izquierdo. A ancho completo y en 21:9 se lee como lo que es: la cara
     del proximo proyecto. */
  /* La miniatura pasa a 16:9: a 21:9 y con 340px de ancho quedaba una franja de
     140px de alto, que no muestra nada del proyecto que anuncia. */
  .siguiente__miniatura { aspect-ratio: 16 / 9; }
  .caso-sitio__link { width: 100%; justify-content: space-between; }
}


/* ==========================================================================
   09 · MOVIMIENTO REDUCIDO Y SIN JS

   El interruptor global de base.css lleva las duraciones a 1ms, pero no toca
   las animaciones infinitas ni las propiedades de layout declaradas acá, así
   que cada una se apaga explícitamente. Todo queda en su estado final, nunca
   oculto.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .mapa__halo { animation: none; opacity: .28; }
  .mapa__contorno { stroke-dashoffset: 0; }
  .obra__flecha { opacity: 1; transform: none; }
  .obra__media { opacity: 1 !important; transform: none !important; }
}

.no-js .vista { display: none; }
.no-js .obra__flecha { opacity: 1; transform: none; }


/* ==========================================================================
   10 · DOS AJUSTES AL SHELL QUE SOLO APLICAN ACA
   ========================================================================== */

/* 10a · LA COSTURA A NEGRO PLANO SE MUDO A components.css § 13.
   Nacio aca en la 19a pasada, pero es una VARIANTE DE .seam y su familia
   (--a, --b, --c) vive alla. Cuando /nosotros/ la necesito tambien, quedaban
   dos salidas malas: cargar esta hoja entera (38 KB) para usar cuatro
   renglones, o duplicar la regla en dos hojas — que es exactamente la clase de
   bug que ya mordio dos veces en este proyecto (el mismo valor en dos lugares).
   Se mudo. Ver components.css. */

/* 10b · LA SECCION ACTUAL EN EL NAV.
   El modulo 3 de main.js solo junta a[href^="#"] (main.js:109), asi que un
   link a otra pagina nunca entra en su lista y la pildora no lo alcanza —
   correcto, porque la pildora mide secciones de ESTA pagina. Pero entonces
   "Proyectos" quedaba igual que los demas estando parado en Proyectos.
   El aria-current="page" del marcado sobrevive: pintarActivo() solo limpia el
   atributo de los links que junto, y este no es uno. */
.nav__links a[aria-current="page"],
.mobile-menu__links a[aria-current="page"] { color: var(--ink); }
.nav__links a[aria-current="page"]::after {
  content: ""; position: absolute; left: 50%; bottom: .1em;
  width: 3px; height: 3px; margin-left: -1.5px;
  border-radius: 50%;
  background: var(--accent-2);
}
.nav__links a[aria-current="page"] { position: relative; }


/* ==========================================================================
   11 · RUBROS — la tabla de dónde ya entré y dónde no

   La referencia es una tabla de búsquedas laborales: lo cerrado apagado, lo
   abierto encendido con su "Apply Now". Acá la lógica se INVIERTE, y ésa es
   toda la idea de la sección: lo apagado es lo que ya está hecho y lo
   encendido es donde todavía no entré — que es donde hay precio de entrada.

   La fila apagada dice "conseguido". La encendida dice "disponible".
   ========================================================================== */
.rubros__grid {
  display: grid;
  /* Muy desigual a propósito: el titular ocupa un tercio y la tabla se lleva
     el resto. Es el gesto de la referencia y lo que rompe el centrado. */
  grid-template-columns: minmax(0, 0.62fr) minmax(0, 1fr);
  gap: clamp(2rem, 1.2rem + 4vw, 6rem);
  align-items: start;
}
.rubros__head { position: sticky; top: calc(76px + var(--sp-8)); }
.rubros__head .h2 { margin-top: var(--sp-4); max-width: 12ch; }

/* EL SUBRAYADO DE "sale menos" — mismo trazo que .hero__title-key en la home
   (sections.css): un hairline en --accent-2 pegado al pie del fragmento, y no
   un subrayado nativo, que en Instrument Serif itálica cruza los descendentes
   de la "g". Se dibuja con la misma familia de reveal que el resto de la
   página: por defecto va TRAZADO ENTERO (visible sin JS y en no-js), y sólo
   cuando el h2 recibe .is-visible se re-arma desde 0 con obs-draw-x — igual
   que .rule en base.css. */
.rubros__clave { position: relative; display: inline-block; }
.rubros__clave::after {
  content: ""; position: absolute;
  left: 0; right: 0; bottom: .04em; height: 1px;
  background: var(--accent-2);
  transform-origin: left center;
}
#rubros-title.is-visible .rubros__clave::after {
  animation: obs-draw-x 520ms var(--ease-expo) 180ms both;
}
.rubros__lead {
  margin-top: var(--sp-6);
  max-width: 44ch;
  color: var(--ink-mute);
  line-height: var(--lh-relaxed);
  text-wrap: pretty;
}
.rubros__cuenta {
  margin-top: var(--sp-8);
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-mute);
}
.rubros__cuenta span {
  font-family: var(--font-display);
  font-size: 1.5rem;
  letter-spacing: 0;
  font-variant-numeric: tabular-nums;
  color: var(--accent-2);
}

/* ⚠ ES UNA <table> DE VERDAD y no una grilla de <div>. Son datos tabulares: un
   lector de pantalla tiene que poder recorrerlos por columna y anunciar el
   encabezado de cada celda. Mismo criterio que la tabla de /nosotros/.
   table-layout: fixed da las columnas sin que el contenido las negocie. */
.rubros__tabla { width: 100%; border-collapse: collapse; table-layout: fixed; }
.rubros__tabla thead th {
  padding: 0 0 var(--sp-4);
  text-align: left;
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-mute);
  border-bottom: 1px solid var(--line-strong);
}
.rubros__c { padding: var(--sp-4) var(--sp-4) var(--sp-4) 0; vertical-align: baseline; }
.rubros__c:last-child { padding-right: 0; text-align: right; }
.rubros__tabla thead th:last-child { text-align: right; }
.rubros__c--rubro { width: 42%; }
.rubros__c--estado { width: 26%; }

.rubros__fila { border-bottom: 1px solid var(--line); }
.rubros__fila .rubros__c--rubro {
  font-family: var(--font-ui); font-weight: 700;
  font-size: var(--fs-h3);
  line-height: var(--lh-snug);
  letter-spacing: -0.005em;
  color: var(--ink);
}
.rubros__fila .rubros__c--tipo {
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-mute);
}

/* LA FILA HECHA. Se apaga entera, pero con --ink-mute y no con opacity: bajar
   la opacidad de un renglón entero lo saca de AA sin avisar, y acá hay texto de
   12px. Con el token el contraste sigue siendo medible. */
.rubros__fila--hecho .rubros__c { color: var(--ink-mute); }
.rubros__fila--hecho .rubros__c--rubro { font-weight: 600; color: var(--ink-mute); }
.rubros__hecho {
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-xs);
  /* --ink-mute y NO --ink-faint. El faint da 2.19:1 sobre la crema, medido:
     menos de la mitad del 4,5 que pide AA para 13px. La fila hecha tiene que
     leerse APAGADA, no ilegible — apagar es una jerarquia, no un obstaculo. */
  color: var(--ink-mute);
}

/* LA FILA ABIERTA. El link ocupa toda la celda para que el objetivo táctil sea
   la celda y no las once letras. */
.rubros__abrir {
  display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: 44px;
  font-family: var(--font-ui); font-weight: 700;
  font-size: var(--fs-sm);
  color: var(--ink);
  transition: color var(--dur) var(--ease-out);
}
.rubros__abrir span { color: var(--accent-2); transition: transform var(--dur) var(--ease-out); }
.rubros__abrir:hover { color: var(--accent-2); }
.rubros__abrir:hover span { transform: translateX(4px); }

/* El realce de fila al pasar por encima, sólo en las abiertas: la hecha no
   ofrece nada, prometer interacción ahí sería mentir. */
@media (hover: hover) and (pointer: fine) {
  .rubros__fila:not(.rubros__fila--hecho):hover .rubros__c--rubro { color: var(--accent-2); }
}


/* ==========================================================================
   12 · LA PISTA DEL TOMO II — la misma mecánica que "Fases de trabajo"

   ⚠ ESTA SECCIÓN SE REHIZO DOS VECES Y LA SEGUNDA ES LA QUE VALE. Vale la pena
   el resumen, porque el error de la primera no era de código:

   La versión anterior era un scroller horizontal nativo. El razonamiento para
   elegirlo era bueno —la home ya tiene tres mecánicas de pin y una cuarta
   parecía demasiado— pero el resultado no funcionaba, y por un motivo que sólo
   se ve MIDIENDO A ANCHOS GRANDES: con cuatro piezas de 560px de tope, a partir
   de ~2400px de viewport LAS CUATRO ENTRAN ENTERAS. Sin desborde no hay nada
   que deslizar, así que la sección quedaba completamente estática, el rótulo
   prometía "deslizá para ver los cuatro" y el pulgar de la barra se quedaba
   clavado en su primer cuarto. Se leía como un loader colgado, y con razón.

   La lección, que vale para cualquier carrusel del sitio: UN CARRUSEL CUYO
   CONTENIDO ENTRA EN PANTALLA ES UN CARRUSEL ROTO, y eso no aparece probando a
   1440 — aparece arriba de 2400.

   Con el pin no puede pasar: el movimiento lo maneja el scroll VERTICAL, que
   siempre existe. Y el riel se mueve solo mientras uno baja, así que no hay que
   rotular ninguna interacción escondida — que era la otra mitad del problema.

   La forma está acá; el desplazamiento lo escribe el módulo 14 de main.js, que
   engancha por data-pista* y sirve a las dos pistas del sitio.

   ⚠ NADA DE animation-timeline ACÁ ADENTRO. Con el pin puesto el elemento deja
   de moverse dentro del scrollport y cualquier progreso de view() se congela.
   Es el mismo motivo anotado en sections.css § 08 y en el módulo 14. El foco de
   cada ficha sale de --focus, que escribe el JS cuadro a cuadro.
   ========================================================================== */
/* ⚠ EL MODO POR DEFECTO ES EL SWIPE, Y EL PIN ES LA MEJORA — al revés que en
   .fases de la home, que da el pin por sentado y lo apaga en un @media.

   El motivo es concreto: el pin no sirve de nada sin JS (el track lo desplaza
   el módulo 14). Con el orden de la home, un navegador sin JS a 1440px se
   queda con el pin pegado, el track quieto y las fichas al 42% de opacidad,
   porque --focus nunca se escribe. Declarando el swipe como base y encendiendo
   el pin sólo bajo `html:not(.no-js)` + ancho + movimiento normal, la
   degradación es correcta por construcción y no por un override que hay que
   acordarse de mantener.

   Y evita una pelea de especificidad: si el pin fuera la base y el apagado
   viviera en un @media, la regla de encendido `html:not(.no-js) .tira__pin`
   (0,2,1) le ganaría al apagado `.tira__pin` (0,1,0) aunque el @media
   coincidiera — las media queries no suman especificidad. */
.tira__runway { height: auto; }

.tira__pin {
  display: flex;
  flex-direction: column;
}

.tira__head {
  flex-shrink: 0;
  display: grid;
  grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
  gap: clamp(1.5rem, 1rem + 2.5vw, 4rem);
  align-items: end;
}
.tira__head .lead { max-width: 46ch; margin: 0; }

/* ── el riel de progreso ─────────────────────────────────────────────────── */
/* Reemplaza a la barra de la versión anterior. La diferencia que importa no es
   de forma sino de honestidad: aquella anunciaba un gesto horizontal que a
   anchos grandes no existía; ésta refleja un recorrido que siempre ocurre. */
.tira__riel {
  display: none;                 /* sólo existe con el pin encendido */
  position: relative;
  margin-top: clamp(1.25rem, 3.4vh, 2.75rem);
  height: 1px;
  background: var(--line);
}
.tira__riel-fill {
  position: absolute; inset: 0;
  background: var(--accent-2);
  transform: scaleX(var(--p, 0));
  transform-origin: left center;
}
.tira__tick {
  position: absolute; top: 50%;
  left: calc(var(--t) * 100%);
  width: 7px; height: 7px;
  margin: -3.5px 0 0 -3.5px;
  border-radius: 50%;
  background: var(--bg);
  box-shadow: inset 0 0 0 1px var(--line-strong);
  transition: box-shadow var(--dur) var(--ease-out), transform var(--dur) var(--ease-spring);
}
.tira__tick.is-lit {
  background: var(--accent-2);
  box-shadow: inset 0 0 0 1px var(--accent-2);
  transform: scale(1.35);
}

/* ── la pista ────────────────────────────────────────────────────────────── */
.tira__viewport {
  display: block;
  overflow-x: auto;
  overflow-y: hidden;
  /* ⚠ contain: paint NO ES DECORATIVO, ARREGLA UN DESBORDE REAL — y es la
     MISMA trampa que ya había costado una vuelta en la 24ª, que volvió a picar
     acá por no trasladarla al reescribir la sección.

     Sin esto la página entera se corre en horizontal: medido a 390px, el
     documento daba 1031 de ancho contra 390 de viewport (y 1290 contra 768).
     Lo desconcertante es que el scroller RECORTA BIEN —su caja mide lo que
     tiene que medir y las fichas se ven cortadas— y aun así el ancho se le
     escapa al ancestro. En la 24ª se probó y descartó: overflow:hidden en los
     dos ejes, overflow-y:clip, max-width, width y min-width. contain: paint es
     lo único que lo corta, y además es lo correcto: le garantiza al navegador
     que nada de adentro pinta afuera de la caja. */
  contain: paint;
  /* ⚠ SÓLO -X. El shorthand contendría también el eje vertical y trabaría el
     scroll de la página con el puntero encima: es exactamente el bug de la
     25ª, y acá hay un scroller horizontal otra vez, así que vuelve a aplicar. */
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
  margin-top: clamp(2rem, 1.5rem + 2vw, 3rem);
  padding-bottom: var(--sp-4);
}
.tira__pista {
  display: flex;
  align-items: stretch;
  gap: var(--gap-grid);
  margin: 0;
  padding: 0;
  list-style: none;
  height: auto;
  padding-inline: var(--borde);
}
.tira-sec { --tira-card: min(82vw, 400px); }
.tira__pieza { scroll-snap-align: center; }
.tira__media { min-height: min(58vh, 480px); }

.tira__pieza {
  flex: 0 0 var(--tira-card);
  display: flex;
}
.tira__link {
  display: flex;
  flex-direction: column;
  width: 100%;
  color: inherit;
  transition: opacity var(--dur) linear, transform var(--dur) var(--ease-out);
}
.tira__link[href] { cursor: pointer; }

.tira__media {
  position: relative;
  flex: 1;
  min-height: 0;
  border-radius: var(--radius);
  overflow: clip;
  background: var(--bg-sunk);
}
.tira__media > * { width: 100%; height: 100%; border-radius: inherit; }
.tira__media img { object-fit: cover; transition: transform var(--dur-slow) var(--ease-expo); }
/* ⚠ MISMO BUG QUE .obra__media (mas arriba, § 283) Y sections.css §
   .proyecto__media: <picture> es `display:inline` y el `> *` de arriba no lo
   alcanza a poner en caja, asi que el <img> de adentro queda a su tamaño
   natural —432x540, el atributo HTML— y no al de `.tira__media`. En las otras
   dos esto ya estaba parchado; aca no, y se noto recien en la 41ª porque
   `--tira-card` (clamp 280px a 560px, linea 1628) recien supera los 432px
   fijos en viewports grandes: mas abajo de eso el atributo y la caja
   coinciden de casualidad y no se ve nada raro. A partir de ahi la foto queda
   mas chica que su marco y el fondo de `.tira__media` asoma como una franja
   al costado — con una foto de fondo claro (Serotoninn) se ve, con una mas
   oscura se disimula, pero las CUATRO tarjetas del tomo tenian el mismo hueco
   (medido, no a ojo: los rects de las cuatro a 2560px daban `img` mas angosto
   que `media`). Mismo arreglo que las otras dos. */
.tira__media picture { display: block; }
.tira__media picture img { width: 100%; height: 100%; border-radius: inherit; }
@media (hover: hover) and (pointer: fine) {
  .tira__link:hover .tira__media img { transform: scale(1.04); }
}

.tira__pie { padding-top: var(--sp-5); flex-shrink: 0; }
.tira__num {
  display: block;
  font-family: var(--font-display);
  font-size: 1.125rem; line-height: 1;
  color: var(--accent-2);
  margin-bottom: var(--sp-3);
}
.tira__nombre { display: inline-flex; align-items: center; gap: var(--sp-3); }
.tira__nombre-txt { position: relative; }
.tira__nombre-txt::after {
  content: ""; position: absolute; left: 0; bottom: -.08em;
  height: 1px; width: 0;
  background: var(--accent-2);
  transition: width var(--dur) var(--ease-out);
}
.tira__link:hover .tira__nombre-txt::after,
.tira__link:focus-visible .tira__nombre-txt::after { width: 100%; }
.tira__link .obra__flecha { opacity: 0; transform: translate3d(-8px, 8px, 0); }
.tira__link:hover .obra__flecha,
.tira__link:focus-visible .obra__flecha { opacity: 1; transform: none; }

.tira__result {
  margin-top: var(--sp-3);
  color: var(--ink-mute);
  line-height: var(--lh-relaxed);
  font-size: var(--fs-sm);
  text-wrap: pretty;
}
.tira__meta {
  margin-top: var(--sp-4);
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3);
}
.tira__rubro { color: var(--ink-mute); }
.tira__anio {
  font-family: var(--font-ui); font-weight: 600;
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  color: var(--ink-mute);
}

/* El aviso de deslizar SÓLO existe en el modo swipe, y ahí sí es cierto: las
   fichas miden 82vw, o sea que siempre hay desborde. Ése era el pecado de la
   versión anterior — rotular un gesto que a ciertos anchos no existía. */
.tira__hint {
  margin-top: var(--sp-5);
  font-size: var(--fs-xs);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  /* --ink-soft y no --ink-faint: éste último no llega a AA y ya se cayó siete
     veces en el proyecto. */
  color: var(--ink-soft);
}

/* El cierre del tomo. Va FUERA del runway: se lee cuando el pin ya se soltó,
   que es el remate natural del recorrido. No lleva la doble regla del libro a
   propósito — esa marca es del libro contable y repetirla acá volvería a
   igualar los dos tomos, que es justo lo que esta sección viene a deshacer. */
.tira__cierre {
  display: grid;
  grid-template-columns: 4rem minmax(0, 1fr);
  gap: var(--sp-4);
  align-items: start;
  margin-top: clamp(2.5rem, 1.8rem + 2.4vw, 4rem);
  padding-top: clamp(1.75rem, 1.2rem + 1.4vw, 2.5rem);
  border-top: 1px solid var(--line);
}
.tira__cierre-label {
  font-family: var(--font-ui); font-weight: 700;
  font-size: var(--fs-eyebrow);
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--ink-mute);
  padding-top: .35em;
}
.tira__cierre .pull-quote { align-items: flex-start; text-align: left; gap: 0; }
.tira__cierre .pull-quote p { max-width: 34ch; }

/* --------------------------------------------------------------------------
   EL PIN — la mejora, no la base.

   Se enciende sólo cuando las tres condiciones se cumplen: hay JS (el track lo
   desplaza el módulo 14), hay ancho para que la pista tenga sentido, y no hay
   una preferencia de movimiento reducido. En cualquier otro caso queda lo de
   arriba, que es un carrusel de swipe nativo con snap y su rótulo.
   -------------------------------------------------------------------------- */
@media (min-width: 1025px) and (prefers-reduced-motion: no-preference) {
  html:not(.no-js) .tira-sec {
    /* El tope de 560 y no 460, y esto se midió: con fichas de 460 a 2560px de
       viewport las cuatro suman 1960 sobre 2560, así que el recorrido del track
       caía a 443px y quedaba un vacío enorme a la derecha durante todo el pase
       — la misma sensación de desbalance que tenía la versión estática. Con 560
       el recorrido sube a ~840 y el conjunto llena la pantalla. */
    --tira-card: clamp(280px, 30vw, 560px);
    padding-top: 0;              /* el aire de arriba lo pone el pin */

    /* ⚠ EL INSET DE LA PISTA TIENE QUE SER EL DEL TITULAR, Y `--borde` SOLO NO
       LO ES. La cabecera vive en `.sangre > .marco--ancho`: hasta 2016px el
       marco no toca su tope y arranca en --borde, pero de ahi para arriba se
       centra dentro de 120rem y su canto izquierdo se corre a (100% - 120rem)/2
       —el --borde se cancela con el de la derecha—. La pista, en cambio, va a
       sangre completa y arrancaba SIEMPRE en --borde: a 2423px el titular
       empezaba en 251px y la primera ficha en 48. Doscientos pixeles de
       desalineacion justo en el gesto que la seccion venia a hacer.

       Ademas le da aire al recorrido: con el inset chico, la SEGUNDA ficha
       nacia ya centrada y no tenia de donde acercarse (ver el modulo 14). */
    --tira-inset: max(var(--borde), calc((100% - 120rem) / 2));
  }

  /* Tunable: más runway = pase más lento por el mismo recorrido. Eran 4 fichas
     y heredaban la calibración de .fases; al sumar Bruma son 5, así que el
     runway escala con ellas (300 × 5/4) para que cada ficha se lleve el mismo
     scroll que antes. Verificado en render: al final del runway la última
     ficha entra entera y sobran 15px de track, igual que con cuatro. */
  html:not(.no-js) .tira__runway { height: 375vh; }

  html:not(.no-js) .tira__pin {
    position: sticky;
    top: 0;
    height: 100vh;
    height: 100svh;
    overflow: hidden;
    padding-top: clamp(84px, 12vh, 132px);   /* despeja el nav fijo */
    padding-bottom: var(--sp-8);
  }

  html:not(.no-js) .tira__riel { display: block; }

  html:not(.no-js) .tira__viewport {
    flex: 1;
    display: flex;
    align-items: center;
    overflow: hidden;
    min-height: 0;               /* si no, el flex item no deja encoger la pista */
    scroll-snap-type: none;
    margin-top: 0;
    padding-bottom: 0;
  }

  html:not(.no-js) .tira__pista {
    height: min(100%, 620px);
    will-change: transform;
    /* Inset asimétrico, igual que .fases y por el mismo motivo. A la izquierda,
       el borde de la página, para que la primera ficha arranque alineada con el
       titular. A la derecha, medio viewport, para que la ÚLTIMA llegue a
       centrarse en vez de quedar estacionada contra el canto sin haber tenido
       su momento. */
    padding-left: var(--tira-inset);
    padding-right: max(var(--tira-inset), calc(50% - var(--tira-card) / 2));
  }

  html:not(.no-js) .tira__pieza { scroll-snap-align: none; }
  html:not(.no-js) .tira__media { min-height: 0; }

  /* --focus lo escribe el JS cuadro a cuadro: 0 lejos del centro, 1 encima. De
     acá salen la escala y el velo, y nada más — la ficha nunca desaparece, sólo
     se retira. */
  html:not(.no-js) .tira__link {
    opacity: calc(.42 + var(--focus, 0) * .58);
    transform: scale(calc(.94 + var(--focus, 0) * .06));
  }

  html:not(.no-js) .tira__hint { display: none; }
}


/* ==========================================================================
   13 · RESPONSIVE DE LO NUEVO
   ========================================================================== */
@media (max-width: 1100px) {
  /* Las cabeceras asimétricas que quedan se apilan antes que el resto: un
     titular grande en media columna a 1000px ya rompe en tres renglones feos. */
  .rubros__grid, .tira__head, .mapa__panel { grid-template-columns: 1fr; }
  .rubros__head { position: static; }
  .rubros__head .h2 { max-width: 18ch; }
  .tira__head .lead { max-width: 62ch; }
  .mapa__panel { gap: clamp(2rem, 1.6rem + 2vw, 3rem); }
  /* La caja acompaña al alto en cada breakpoint. El piso de la relación es
     siempre el mismo: caja > 0.484 × alto, o Argentina se sale de su caja. Acá
     el alto baja a 620 (Argentina: 300px de ancho) y la caja a 470. */
  .mapa { --mapa-alto: clamp(380px, 62vw, 620px); --mapa-caja: clamp(280px, 42vw, 470px); }
}

/* ⚠ ACÁ SE SUELTA LA CAJA, Y ESTE BLOQUE VA DESPUÉS DEL DE 1100 A PROPÓSITO.
   Hay otro @media de 900px más arriba en la hoja; a 820px los dos aplican, y
   con la misma especificidad gana el último que se lee. Puesto arriba, el 42vw
   del bloque de 1100 lo pisaba y la caja se quedaba en 344px justo donde ya no
   hacía falta.

   Y hace falta soltarla porque a 900px el panel se apila: el mapa deja de
   competir con la lista por el ancho, así que encerrar un continente en lo que
   le alcanzaba a Argentina lo dejaría a ~29 unidades de viewBox por píxel y el
   buril caería a 3,9px de paso. Con la caja suelta quedan 4,9. */
@media (max-width: 900px) {
  .mapa { --mapa-caja: min(100%, 440px); }
  /* Apilado no hay canal a la derecha al que mandar el sobrante, así que el
     dibujo vuelve al centro — que es donde estaba antes en estos anchos. */
  .mapa__lienzo { justify-items: center; }
}

@media (max-width: 768px) {
  /* LA TABLA SE APILA. thead se esconde y cada celda lleva su rótulo desde
     data-col, así que no se pierde ninguna relación de la tabla. Mismo patrón
     que la tabla de /nosotros/. */
  .rubros__tabla, .rubros__tabla tbody, .rubros__fila, .rubros__c {
    display: block; width: 100%;
  }
  .rubros__tabla thead {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%);
  }
  .rubros__fila { padding: var(--sp-5) 0; }
  .rubros__c { padding: 0; text-align: left; }
  .rubros__c:last-child { text-align: left; }
  .rubros__c--tipo { margin-top: var(--sp-3); }
  .rubros__c--estado { margin-top: var(--sp-1); }
  /* SIN el prefijo attr(data-col) en la celda del tipo. Se probó y ocupaba dos
     renglones —"QUÉ SE AUTOMATIZA ·"— con lo que el rótulo pesaba más que el
     dato y cada fila medía el doble. El valor se explica solo ("ATENCIÓN Y
     VENTAS" no necesita que le digan que es la columna de qué se automatiza), y
     la relación de tabla NO se pierde: el thead sigue en el árbol de
     accesibilidad (position absolute + clip-path es la técnica de
     .visually-hidden, no display:none), así que un lector de pantalla anuncia
     el encabezado igual. */

  .tira__cierre { grid-template-columns: 1fr; }
  /* Sólo el alto: la caja ya la soltó el bloque de 900px, que es donde el
     panel se apila. Ver la nota de ahí. */
  .mapa { --mapa-alto: clamp(340px, 78vw, 520px); }
  /* Apilado, la cuenta baja debajo del nombre y la crucecita se queda en su
     columna cruzando las dos filas: es el único control de la fila y tiene que
     quedar donde el pulgar lo busca. */
  .mapa__abrir { grid-template-columns: 2.25rem minmax(0, 1fr) 20px; }
  .mapa__item-cuenta { grid-column: 2; }
  .mapa__item-sym { grid-column: 3; grid-row: 1 / -1; }
  /* Menos sangría: a 390px, 2.25rem + 1rem se comen un cuarto del ancho. */
  .mapa__webs { padding-left: calc(2.25rem + var(--sp-3)); }
  /* LA FILA DE SITIO SE APILA. En 390px quedan ~300px utiles y las cuatro
     columnas no entran: "REPARACIÓN DE ASCENSORES" sola ya se come la fila, y
     con un chip al lado el nombre se partia en dos renglones. Apilado, el
     favicon cruza las dos filas —ancla la fila entera— y rubro y nota bajan
     juntos debajo del nombre. */
  .mapa__web {
    grid-template-columns: 20px minmax(0, 1fr);
    align-items: start;
    row-gap: var(--sp-1);
    padding: var(--sp-3) 0;
  }
  .mapa__favicon { grid-row: 1 / -1; margin-top: .15em; }
  .mapa__web-rubro { grid-column: 2; }
  .mapa__web-nota  { grid-column: 2; justify-self: start; }
}

@media (max-width: 640px) {
  /* El pulso de siete halos a la vez en una pantalla chica es ruido. */
  .mapa__halo { animation-duration: 4.4s; }
}

/* Sin movimiento: la tira NO se desactiva —es navegación, no adorno— pero el
   snap se suelta, porque un salto duro con reduced-motion es exactamente lo
   que la preferencia pide evitar. */
@media (prefers-reduced-motion: reduce) {
  .tira__link .obra__flecha { opacity: 1; transform: none; }
  .tira__nombre-txt::after { width: 100%; }
}

/* Sin JS todo lo que dependía del hover se muestra de entrada. */
.no-js .tira__link .obra__flecha { opacity: 1; transform: none; }
.no-js .tira__nombre-txt::after { width: 100%; }
