/* LENRIQUE — landing inmersiva · Diseñado por Operaria
   HTML/CSS/JS plano. Mobile-first. Un tatuaje es un pasaje.
   V2 (17 jul 2026): fotografía real de Enrique reemplaza las evocaciones,
   retrato vivo (foto→video CSS), capítulo de papel frío, itinerario y FAQ.
   V6 (26 jul 2026):
     · el logotipo del hero baja a escala de firma (era titular y se cortaba arriba)
     · fuera el capítulo LA MARCA (logo + foto) — el logo ya vive en el hero
     · EL PASAJE se rehace: un retrato, dos revelados (b/n → color con el scroll)
     · se retira el CSS muerto (chat del agente, itinerario) que ya no tiene DOM */

/* ---------- Tokens ---------- */
:root {
  /* Paleta v2 (16 jul 2026) — SIN sepia. Negro tinta + azul noche + platas + papel frío.
     La tinta negra envejece a azul bajo la piel: el paso del tiempo ES el concepto. */
  --ink-950: oklch(0.10 0.010 255);
  --ink-900: oklch(0.14 0.012 255);
  --ink-850: oklch(0.18 0.014 253);
  --ink-800: oklch(0.23 0.016 250);
  /* Papel FRÍO como MATERIAL (blanco-gris con grano, jamás beige) */
  --paper:   oklch(0.90 0.006 250);
  --paper-2: oklch(0.84 0.008 250);
  /* Azul noche / tinta envejecida: único acento */
  --azul:   oklch(0.66 0.075 250);
  --azul-2: oklch(0.54 0.080 255);
  /* Plata: piel, luz, cicatriz, metal de la aguja */
  --plata:  oklch(0.80 0.010 250);
  /* Texto */
  --on-ink:      oklch(0.94 0.006 250);
  --on-ink-mut:  oklch(0.74 0.010 250);
  --on-paper:    oklch(0.22 0.014 255);
  --on-paper-mut: oklch(0.42 0.018 253);

  /* Una sola voz serif — la del logo (Didot alto contraste; en web Playfair Display) */
  --serif: "Playfair Display", "Didot", "Bodoni 72", "Bodoni MT", Georgia, "Times New Roman", serif;
  --sans: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", "Menlo", "Consolas", monospace;

  --measure: 34rem;
  --ease: cubic-bezier(0.16, 1, 0.3, 1);

  /* z-index semántico */
  --z-inkline: 5;
  --z-content: 6;
  --z-nav: 40;
  --z-modal: 60;
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth;
  scrollbar-color: var(--ink-800) var(--ink-950); scrollbar-width: thin; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

body {
  margin: 0;
  background: var(--ink-950);
  color: var(--on-ink);
  font-family: var(--sans);
  font-size: clamp(1rem, 0.96rem + 0.3vw, 1.125rem);
  line-height: 1.65;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
img, svg, canvas { max-width: 100%; display: block; }

/* Pulido premium: la selección también es de tinta */
::selection { background: var(--azul); color: var(--ink-950); }
.boleto ::selection, .oro ::selection { background: var(--azul-2); color: var(--paper); }
::-webkit-scrollbar { width: 10px; }
::-webkit-scrollbar-track { background: var(--ink-950); }
::-webkit-scrollbar-thumb { background: var(--ink-800); border-radius: 5px; }

/* Foco visible: aguja azul, nunca outline fantasma sobre negro */
:focus-visible { outline: 2px solid var(--azul); outline-offset: 3px; border-radius: inherit; }
.ticket-cta:focus-visible, .btn--solid:focus-visible { outline-color: var(--azul-2); }

/* Textura de grano/papel reutilizable (SVG feTurbulence embebido) */
.grain::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: 0.05; mix-blend-mode: overlay;
}

/* ---------- Tipografía ---------- */
h1, h2, h3 { font-family: var(--serif); font-weight: 500; line-height: 1.02;
  letter-spacing: -0.02em; text-wrap: balance; margin: 0; }
/* UNA escala para los títulos de capítulo (v22). Iban de 51 a 64px según la
   sección —cada capítulo declaraba su propio clamp— y el salto se notaba al
   bajar. El h1 del hero es el único que vive un escalón más arriba. */
.chapter h2 { font-size: clamp(2rem, min(6vw, 6.6vh), 3.6rem); }
p { text-wrap: pretty; }
em, .em { font-family: var(--serif); font-style: italic; }
.mono { font-family: var(--mono); letter-spacing: 0.02em; }

/* ---------- Reveal (solo con JS; sin JS todo visible) ---------- */
.reveal { }
html.js .reveal { opacity: 0.001; transform: translateY(18px);
  transition: opacity 1s var(--ease), transform 1.1s var(--ease); }
html.js .reveal.is-in { opacity: 1; transform: none; }
html.js .reveal.d1 { transition-delay: 0.08s; }
html.js .reveal.d2 { transition-delay: 0.16s; }
html.js .reveal.d3 { transition-delay: 0.24s; }
@media (prefers-reduced-motion: reduce) {
  html.js .reveal { opacity: 0.001; transform: none;
    transition: opacity 0.4s linear; }
  html.js .reveal.is-in { opacity: 1; }
}

/* ---------- Fotografía real — tratamiento editorial unificado ---------- */
.chapter__bg { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; z-index: 0; filter: brightness(0.78) contrast(1.06); }
/* Velo de tinta envejecida: unifica las B/N con la nota azul del hero (una sola campaña) */
.foto-velada { position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: color-mix(in oklab, var(--azul-2) 12%, transparent); mix-blend-mode: multiply; }
/* Scrims de negro tinta para legibilidad (contraste >=4.5:1 sobre el texto) */
.chapter__scrim { position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: color-mix(in oklab, var(--ink-950) 40%, transparent); }

/* Puntos focales de las verticales 1320×2346 (editorial, no accidente) */
.bg--tintas   { object-position: 40% 50%; }
@media (max-width: 700px) {
  .bg--tintas { object-position: 60% 50%; }
}

/* ---------- Línea de tinta (riel del viaje) ---------- */
.inkline { position: fixed; inset: 0; width: 100%; height: 100%;
  z-index: var(--z-inkline); pointer-events: none; }
/* Glow por path duplicado (barato en compositor) — no drop-shadow sobre el trazo animado */
.inkline .ink-glow { fill: none; stroke: var(--azul);
  stroke-width: 4; stroke-linecap: round; stroke-linejoin: round; opacity: 0.14; }
.inkline .ink-main { fill: none; stroke: var(--azul);
  stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; opacity: 0.6; }
.inkline .bleed { fill: var(--azul); opacity: 0; }
.inkline .nib { fill: var(--paper); opacity: 0;
  filter: drop-shadow(0 0 5px var(--azul)); }
@media (prefers-reduced-motion: reduce) {
  .inkline .nib { display: none; }
  .inkline .bleed { opacity: 0.16; }
}

/* ---------- Stack de capítulos ----------
   ARQUITECTURA (v17 · 21 jul): el sticky-stack (mazo de cartas) solo puede
   aplicarse a capítulos que caben SIEMPRE en una pantalla — un sticky top:0
   más alto que el viewport clava su base fuera de la vista y la siguiente
   sección lo tapa: ese era el corte "eterno" de Archivo/Oro/FAQ.
   Regla nueva: TODO fluye (relative). Solo el manifiesto (.pasaje) conserva
   el pin en desktop — es el único capítulo-foto diseñado para el dwell y su
   texto cabe en un viewport por diseño (clamp por vh). El hero maneja su
   propio sticky interno (.hero__sticky). */
.stack { position: relative; z-index: var(--z-content); }
/* v18: los capítulos miden lo que mide su contenido (+aire), no una pantalla
   forzada — el 100dvh obligatorio hacía que la página «corriera de largo»
   con valles vacíos entre secciones cortas. */
.chapter { position: relative; min-height: 0;
  display: grid; align-content: center; overflow: hidden;
  padding: clamp(3.5rem, 9vh, 6rem) max(24px, 6vw);
  isolation: isolate; }
/* Solo los capítulos-FOTO (fondo a sangre) llenan la pantalla: ahí el
   encuadre ES la foto y recortarla la mataría.
   v19: EL PASAJE ya no se pinea a sí mismo (eso le cortaba el último verso
   cuando el Archivo lo tapaba). Ahora es un riel alto con un sticky INTERNO
   —el mismo patrón del hero, el único que nunca se cortó. */
/* v20: no una pantalla entera — con el pie montado encima, el 100dvh dejaba un
   tramo largo de foto sin nada que decir.
   v21: se le devuelve exactamente el alto del logotipo (116px de ancho → 83px
   de alto), que ahora vive dentro del cuadro y no colgando del final. */
.chapter.reserva { min-height: calc(78dvh + 83px); }
/* v21: El oro y el Regalo quedaron en 0,52 y 0,54 dvh —la mitad que sus
   vecinos— después de podarles la figura del umbral y el copy de más. Un
   piso común les devuelve el mismo peso que a los demás capítulos. */
.chapter.oro, .chapter.agente { min-height: 84dvh; }
.chapter > * { position: relative; z-index: 1; }
/* Los lienzos de fondo JAMÁS entran al flujo: absolutos detrás del texto,
   gane quien gane la cascada (fix del valle sin texto, 17 jul) */
.chapter > .chapter__bg, .chapter > .chapter__scrim, .chapter > .foto-velada {
  position: absolute; inset: 0; z-index: 0; }
.wrap { width: 100%; max-width: 68rem; margin-inline: auto; }
.col { max-width: var(--measure); }

/* ---------- HERO (frame-by-frame → retrato vivo) ----------
   La tinta se dibuja con el scroll y al final del recorrido se convierte en él:
   crossfade al retrato real (luz azul, la paleta encarnada). */
/* v18: riel de scrub 180vh (era 220) — la tinta se dibuja igual (el JS usa
   offsetHeight), pero el visitante llega antes al manifiesto. */
/* ---------- EL FRENO DE CADA FRONT (v25) ----------
   Pedido de Francisco: que cada capítulo frene ~0,8 s antes de pasar al
   siguiente. NO se hace secuestrando el scroll (un scroll que no obedece al
   dedo se siente roto, y en trackpad se pelea con la inercia): se hace
   ALARGANDO el riel por la cola. El capítulo ya terminó su efecto y el último
   tramo es puro sostener — uno sigue deslizando y la composición se queda
   quieta. Eso es un freno de verdad.
   --freno = 40vh. A la velocidad de rueda habitual (~50 vh/s) son ~0,8 s de
   cuadro detenido. Es una sola variable: si el freno se siente corto o largo,
   se cambia acá y cambia en los seis capítulos a la vez.
   Los efectos ya cierran antes del final de su riel (ver las ventanas A/B del
   JS), así que la cola es tiempo muerto a propósito, no efecto estirado. */
:root { --freno: 40vh; }

.hero { position: relative; height: calc(180vh + var(--freno)); min-height: 0; padding: 0;
  display: block; align-content: normal; overflow: visible; background: var(--ink-950); }
.hero__sticky { position: sticky; top: 0; height: 100dvh; overflow: hidden;
  display: grid; align-content: center; justify-items: center; isolation: isolate;
  padding: clamp(2rem, 6vw, 6rem) max(24px, 6vw); }
.hero__poster, .hero__canvas { position: absolute; inset: 0; width: 100%; height: 100%; }
.hero__poster { object-fit: cover; z-index: 0; }
.hero__canvas { z-index: 1; }

/* v20: el retrato vivo (Enrique en luz azul al final del hero) se retiró:
   adelantaba su cara y le comía el final al macro. Él entra en el Pasaje. */

.hero__veil { position: absolute; inset: 0; z-index: 3; pointer-events: none;
  background: radial-gradient(120% 90% at 50% 44%,
    color-mix(in oklab, var(--ink-950) 28%, transparent), var(--ink-950) 94%); }
.hero__inner { position: relative; z-index: 4;
  display: flex; flex-direction: column; align-items: center;
  text-align: center; gap: clamp(1.4rem, 4vw, 2.4rem); }
.hero__inner > * { max-width: 100%; }
/* El logotipo firma, no titula (v19): a 300px competía con el claim y se salía
   por arriba del viewport. La marca se lee; el claim es el que habla. */
/* height:auto es obligatorio: el <img> lleva width/height reales (evitan el
   salto de layout al cargar) y sin esto el alto del atributo —653px— manda,
   dejando un boquete vertical que empujaba el ticket fuera de la pantalla. */
.hero__logo { width: min(34vw, 149px); height: auto;
  filter: drop-shadow(0 6px 34px rgba(0,0,0,0.55));
  margin-top: clamp(1rem, 5vh, 3rem); }
/* v20: logo y claim bajan otro 20% juntos — el hero respira y la aguja del
   fondo vuelve a ser el sujeto del cuadro. */
.hero__claim { max-width: 16ch; font-size: clamp(1.92rem, 6.4vw, 4.32rem); }
/* v22: estaba en sans, justo debajo de un claim serif de 69px. Es el segundo
   aliento del claim, no una etiqueta funcional: le corresponde la misma voz. */
.hero__sub { color: var(--on-ink-mut); max-width: min(26rem, 100%);
  font-family: var(--serif); font-size: 1.2rem;
  text-wrap: pretty; overflow-wrap: break-word; margin: 0; }
@media (prefers-reduced-motion: reduce) { .hero { height: 100dvh; } }

/* Boleto perforado (invitación a abordar) */
.ticket-cta { display: inline-flex; align-items: stretch; gap: 0;
  background: var(--paper); color: var(--on-paper); font-family: var(--mono);
  border: none; cursor: pointer; text-decoration: none;
  border-radius: 6px; overflow: hidden; box-shadow: 0 10px 40px rgba(0,0,0,0.5);
  transition: transform 0.5s var(--ease); }
.ticket-cta:hover { transform: translateY(-3px); }
.ticket-cta__stub { display: grid; place-items: center; padding: 0.85rem 0.8rem;
  border-right: 2px dashed var(--on-paper-mut); font-size: 0.68rem; letter-spacing: 0.12em; }
.ticket-cta__main { display: grid; place-items: center; text-align: center; padding: 0.7rem clamp(1rem, 3.5vw, 1.3rem); gap: 0.1rem; }
.ticket-cta__main b { font-family: var(--serif); font-style: italic;
  font-size: clamp(1rem, 3.6vw, 1.15rem); letter-spacing: 0; }
.ticket-cta__main span { font-size: 0.64rem; letter-spacing: 0.08em; color: var(--on-paper-mut);
  white-space: nowrap; }
.scrollhint { color: var(--on-ink-mut); font-size: 0.7rem; letter-spacing: 0.28em;
  text-transform: uppercase; margin: 0; }
.scrollhint span { display: block; width: 1px; height: 34px; margin: 0.7rem auto 0;
  background: linear-gradient(var(--azul), transparent); }

/* ---------- Píldora fija «reserva aquí» (aparece al salir del hero) ---------- */
.pill-reserva { position: fixed; bottom: max(1rem, env(safe-area-inset-bottom));
  right: 1rem; z-index: var(--z-nav);
  display: inline-flex; align-items: center; gap: 0.55rem;
  background: var(--paper); color: var(--on-paper); text-decoration: none;
  font-family: var(--mono); font-size: 0.7rem; letter-spacing: 0.1em;
  padding: 0.7rem 1rem 0.7rem 0.8rem; border-radius: 6px;
  box-shadow: 0 12px 34px rgba(0,0,0,0.55); }
.pill-reserva::before { content: ""; width: 0; height: 1.1em;
  border-left: 2px dashed var(--on-paper-mut); }

/* ---- v24 · La píldora se revela UNA vez, saliendo del humo ----
   Pedido de Francisco: que se sienta viva pero que no dependa del dedo. Antes
   la píldora entraba y salía con el scroll (un toggle de IntersectionObserver)
   y eso la volvía un elemento de interfaz más. Ahora ocurre una sola vez, como
   un sello que se estampa: el humo se abre, y de adentro condensa el boleto.
   El JS pone .is-on y NO la quita nunca — la animación se dispara sola por
   existir la clase, y con fill-mode forwards queda asentada para siempre.
   Nada de esto está ligado a la posición del scroll: el scroll solo da el pie. */
html.js .pill-reserva { opacity: 0; transform: translateY(12px); pointer-events: none;
  transition: opacity .6s var(--ease), transform .6s var(--ease); }
html.js .pill-reserva.is-on { opacity: 1; transform: none; pointer-events: auto; }

/* v26: FUERA el humo. Se intentó de dos formas —una animación de estreno de
   1,5 s y una capa de tres bocanadas movida por el scroll— y ninguna funcionó:
   sobre el negro de la página el humo o no se veía o se leía como una mancha
   clara pegada al borde inferior derecho. La píldora entra y sale limpia.
   Se borró en vez de dejarla en opacity:0 — CSS y JS muertos son la próxima
   confusión de alguien. */

/* Cerca del capítulo Reserva la píldora sobra —el botón grande ya está en
   pantalla—. Se va con un fundido simple: el humo es un estreno, no un
   efecto reutilizable. Y al volver a subir NO se re-estrena. */
html.js .pill-reserva.se-va { opacity: 0; pointer-events: none;
  transition: opacity .45s var(--ease); }

@media (prefers-reduced-motion: reduce) {
  html.js .pill-reserva { transform: none; transition: opacity .3s linear; }
}

/* ---------- EL PASAJE · un retrato, dos revelados (v19) ----------
   El riel mide 230vh y adentro hay UN sticky de 100dvh: el mismo patrón del
   hero. El scroll no cambia de foto — le devuelve el color a la misma. */
.pasaje { background: var(--ink-950); height: calc(230vh + var(--freno)); min-height: 0;
  padding: 0; display: block; align-content: normal; overflow: visible; }
@media (max-width: 700px) { .pasaje { height: 210vh; } }
@media (prefers-reduced-motion: reduce) { .pasaje { height: auto; min-height: 100dvh; } }

.pasaje__sticky { position: sticky; top: 0; height: 100dvh; overflow: hidden;
  isolation: isolate; display: grid; align-content: center;
  padding: clamp(3rem, 9vh, 6rem) max(24px, 6vw); }
@media (prefers-reduced-motion: reduce) { .pasaje__sticky { position: relative; } }

/* Las dos tomas, apiladas al píxel. La de color monta sobre la b/n y sube su
   opacidad con el scroll: fundido en el compositor, cero repintado por frame. */
.pasaje__fotos { position: absolute; inset: 0; z-index: 0; }
.pasaje__foto { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; object-position: 62% 42%; }
.pasaje__foto--bn { filter: brightness(0.92) contrast(1.06); }
/* El color arranca apagado y el JS lo sube. Sin JS: el color ya está puesto
   —el capítulo se ve entero, sólo pierde el viaje. */
.pasaje__foto--color { opacity: 1; filter: brightness(0.94) contrast(1.04) saturate(1.04); }
html.js .pasaje__foto--color { opacity: 0; will-change: opacity; }
@media (prefers-reduced-motion: reduce) { html.js .pasaje__foto--color { opacity: 1; } }
@media (max-width: 700px) { .pasaje__foto { object-position: 66% 34%; } }

/* Velo de entrada: negro tinta sobre la foto mientras el capítulo sube. Sin JS
   no existe (el capítulo entra directo, que es el comportamiento honesto). */
.pasaje__fundido { position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: var(--ink-950); opacity: 0; }
html.js .pasaje__fundido { opacity: 1; will-change: opacity; }
@media (prefers-reduced-motion: reduce) { html.js .pasaje__fundido { opacity: 0; } }

/* Velo de tinta: la izquierda es página para el texto, la derecha es él.
   En vertical el velo sube desde abajo, que es donde caen los versos. */
.pasaje__scrim { position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(100deg,
    color-mix(in oklab, var(--ink-950) 92%, transparent) 6%,
    color-mix(in oklab, var(--ink-950) 72%, transparent) 42%,
    color-mix(in oklab, var(--ink-950) 24%, transparent) 74%,
    transparent 100%); }
@media (max-width: 819px) {
  .pasaje__scrim { background: linear-gradient(to top,
    color-mix(in oklab, var(--ink-950) 94%, transparent) 22%,
    color-mix(in oklab, var(--ink-950) 70%, transparent) 52%,
    color-mix(in oklab, var(--ink-950) 22%, transparent) 82%,
    transparent 100%); }
}

.pasaje__inner { position: relative; z-index: 2; }
@media (max-width: 819px) { .pasaje__sticky { align-content: end;
  padding-bottom: clamp(5rem, 16vh, 8rem); } }

/* Anclaje a la cara (v20) ------------------------------------------------
   El JS calcula dónde caen SUS ojos y SUS labios en pantalla (replica el
   recorte de object-fit:cover) y escribe --y-ojo / --y-labios. El pasado se
   alinea con la vista; el futuro, a la altura de los labios: lo que ya vio y
   lo que todavía no dice. Sin JS o en vertical, los versos vuelven a apilarse
   —el anclaje es un lujo de pantalla ancha, nunca un requisito. */
@media (min-width: 820px) {
  html.js .pasaje__inner { position: absolute; inset: 0;
    padding-inline: max(24px, 6vw); }
  html.js .pasaje__inner .manifiesto { display: block; height: 100%; max-width: none; }
  html.js .manifiesto .verso { position: absolute; margin: 0; }
  html.js .manifiesto .verso--pasado { left: 0;
    top: calc(var(--y-ojo, 32%) - 0.62em); }
  /* v22: el segundo andén se corre ~la mitad del largo de su propia frase
     (unos 110px sobre 220 de línea): queda escalonado bajo el primero, no
     pegado a su misma vertical. */
  html.js .manifiesto .verso--futuro { left: clamp(3rem, 7.6vw, 7rem);
    top: calc(var(--y-labios, 46%) - 0.62em); }
}

/* Los dos versos: el del pasado arriba, el del futuro abajo y desplazado —
   no son una lista, son dos andenes distintos del mismo tablero. */
.manifiesto { display: grid; gap: clamp(2rem, 7vh, 4rem); max-width: 30rem; }
.manifiesto p { font-family: var(--serif);
  font-size: clamp(1.25rem, min(3.1vw, 3.4vh), 2.15rem);
  line-height: 1.28; color: var(--on-ink); letter-spacing: -0.01em; max-width: 22ch;
  margin: 0; text-shadow: 0 2px 24px rgba(6,10,26,0.8); }
/* Especificidad a propósito: `.manifiesto p { margin:0 }` gana por sí solo y
   se comía la sangría del segundo andén. */
.manifiesto .verso--futuro { justify-self: start; margin-left: clamp(0rem, 6vw, 3.5rem); }
/* El inciso es acotación, no continuación: línea aparte y sangrada */
.manifiesto .inciso { display: block; padding-left: 1.1em; margin-top: 0.55em;
  font-size: 0.72em; font-style: italic; line-height: 1.45;
  color: color-mix(in oklab, var(--on-ink) 62%, transparent); }
.manifiesto .inciso::before { content: "— "; }

/* Cada verso entra cuando el riel lo llama (JS). Sin JS: ya están puestos. */
html.js .manifiesto .verso { opacity: 0.001; transform: translateY(16px);
  transition: opacity 1.1s var(--ease), transform 1.2s var(--ease), color 1.4s var(--ease); }
html.js .manifiesto .verso.is-in { opacity: 1; transform: none; }
/* Cuando llega el color, el pasado recula: sigue ahí, ya no manda. */
html.js .manifiesto .verso--pasado.is-past { opacity: 0.5; }
@media (prefers-reduced-motion: reduce) {
  html.js .manifiesto .verso { opacity: 1; transform: none; transition: none; }
  html.js .manifiesto .verso--pasado.is-past { opacity: 1; }
}

/* Pie de boleto: el destino sólo se enciende cuando el color ya llegó */
.pasaje__banda { position: absolute; z-index: 2; left: 0; right: 0; bottom: 0;
  display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap;
  padding: 0.9rem max(24px, 6vw) calc(0.9rem + env(safe-area-inset-bottom));
  border-top: 1px solid color-mix(in oklab, var(--plata) 16%, transparent);
  font-size: 0.6rem; letter-spacing: 0.16em; color: var(--on-ink-mut); }
.pasaje__destino { color: color-mix(in oklab, var(--on-ink-mut) 34%, transparent);
  transition: color 1.2s var(--ease); }
html.js .pasaje__sticky.is-color .pasaje__destino { color: var(--azul); }
/* Sin JS la píldora de reserva está siempre puesta (abajo a la derecha) y se
   comía el destino: ahí, y sólo ahí, la banda le cede el sitio. */
html:not(.js) .pasaje__banda { padding-right: 11rem; }
@media (max-width: 560px) { .pasaje__banda { font-size: 0.54rem; letter-spacing: 0.1em; } }
/* Kicker: UNA sola cadencia deliberada — solo vive en el Archivo (información de boleto real) */
.chapter__kicker { font-family: var(--mono); font-size: 0.72rem; letter-spacing: 0.24em;
  text-transform: uppercase; color: var(--azul); margin-bottom: 1.5rem; display: inline-block; }

/* ---------- SATURNO · el primer pasaje (v21) ----------
   Riel alto con sticky interno —el patrón del hero, el único que nunca se
   cortó—. A la izquierda el relato y el boleto real; a la derecha la cinta:
   el video de la sesión en su formato vertical de historia, dibujado en
   canvas según el scroll. */
.saturno { background: var(--ink-950); height: calc(250vh + var(--freno)); min-height: 0;
  padding: 0; display: block; align-content: normal; overflow: visible; }
@media (max-width: 819px) { .saturno { height: 230vh; } }
@media (prefers-reduced-motion: reduce) { :root { --freno: 0px; }
  .saturno { height: auto; min-height: 100dvh; } }

.saturno__sticky { position: sticky; top: 0; min-height: 100dvh; overflow: hidden;
  display: grid; align-content: center; isolation: isolate;
  padding: clamp(1.5rem, 4.5vh, 4.5rem) max(24px, 6vw); }
@media (prefers-reduced-motion: reduce) { .saturno__sticky { position: relative; } }

/* v24: UNA sola columna, en todos los anchos. Antes eran dos —relato + cinta—
   y ese segundo bloque es el que ya no existe. El boleto queda centrado y
   solo: es la única pieza del capítulo, y ahora puede serlo. */
.saturno__layout { display: grid; gap: clamp(1.2rem, 3vh, 2rem);
  justify-items: center; text-align: center; }
.saturno__relato { display: grid; justify-items: center; width: 100%; }
/* Todo el capítulo mide también por ALTURA: el relato y el boleto tienen que
   caber en el sticky en un portátil de 740px de alto, no solo en un monitor.
   Si desbordan, el sticky se clava más alto que la pantalla y le corta el pie
   —el error estructural que este proyecto ya pagó caro. */
.saturno h2 { margin-bottom: clamp(0.9rem, 2.4vh, 1.6rem); }

/* ---- El pasaje emitido, que gira con el scroll (v22) ----
   Dos caras, un solo objeto: el anverso con la obra y el reverso con la
   historia. El JS escribe --giro (0→180deg) según el riel del capítulo; el
   CSS no anima nada por su cuenta, así que el giro sigue al dedo. */
/* El estado POR DEFECTO es el seguro: las dos caras apiladas y visibles. El
   giro se enciende solo cuando el JS confirma que hay riel que seguir (clase
   .gira). Antes era al revés —giro por defecto, apilado bajo un media query de
   900px— y una ventana de escritorio angosta se quedaba sin giro Y con el
   reverso escondido detrás de una vuelta que nunca ocurría. */
.pasaje-obj { width: 100%; max-width: 44rem; }
.pasaje-obj__giro { position: relative; width: 100%; }
.pasaje-obj__cara { position: relative; margin: 0 0 0.9rem; border-radius: 10px;
  overflow: hidden; box-shadow: 0 26px 70px -24px rgba(0,0,0,0.9); }
.pasaje-obj__cara img { width: 100%; height: auto; }

html.js .pasaje-obj.gira { perspective: 1800px; aspect-ratio: 2000 / 840; }
html.js .pasaje-obj.gira .pasaje-obj__giro { height: 100%;
  transform-style: preserve-3d; transform: rotateY(var(--giro, 0deg));
  will-change: transform; }
html.js .pasaje-obj.gira .pasaje-obj__cara { position: absolute; inset: 0; margin: 0;
  backface-visibility: hidden; -webkit-backface-visibility: hidden; }
html.js .pasaje-obj.gira .pasaje-obj__cara img { height: 100%; object-fit: cover; }
html.js .pasaje-obj.gira .pasaje-obj__cara--rev { transform: rotateY(180deg); }

/* v24: las reglas de .saturno__cinta / .cinta__* y de .saturno .archivo__ig se
   BORRARON junto con su HTML. No se dejan «por si acaso»: CSS que sobrevive a
   su DOM es una de las trampas conocidas de esta página —vuelve a aplicarse
   sobre el primer elemento que reutilice la clase y nadie entiende por qué. */

/* v24: en teléfono AHORA SÍ se pinea, y por eso el boleto también gira ahí.
   El motivo de no pinearlo (v21) era real pero ya no aplica: lo que no cabía
   en una pantalla vertical era relato + CINTA + boleto. Sin la cinta, el
   capítulo es kicker + título + boleto — el boleto mide 2000/840, o sea un
   40% de su ancho, y a 88vw en un teléfono son ~145px de alto. Cabe de sobra.
   El riel se acorta porque hay menos que contar, pero sigue existiendo: es lo
   que el JS necesita para que la vuelta siga al dedo (ver hayRiel()).
   La ley se mantiene: el sticky mide 100dvh, jamás más. */
@media (max-width: 819px) {
  .saturno { height: calc(200vh + var(--freno)); }
  .saturno__sticky { padding-block: clamp(1.5rem, 5vh, 3rem); }
  .pasaje-obj { max-width: none; }
}
.boleto { position: relative; background: var(--paper); color: var(--on-paper);
  border-radius: 8px; padding: 1.3rem 1.3rem 1.4rem; display: grid; gap: 0.6rem;
  box-shadow: 0 14px 40px rgba(0,0,0,0.45); overflow: hidden;
  transition: transform 0.5s var(--ease), box-shadow 0.5s var(--ease); }
.boleto:hover { transform: translateY(-4px); box-shadow: 0 22px 54px rgba(0,0,0,0.55); }
@media (prefers-reduced-motion: reduce) { .boleto:hover { transform: none; } }
.boleto__perf { position: absolute; top: 0; bottom: 0; left: 2.6rem; width: 0;
  border-left: 2px dashed color-mix(in oklab, var(--on-paper) 35%, transparent); }
.boleto::before, .boleto::after { content: ""; position: absolute; left: 2.35rem;
  width: 0.55rem; height: 0.55rem; background: var(--ink-950); border-radius: 50%; }
.boleto::before { top: -0.27rem; }
.boleto::after { bottom: -0.27rem; }
.boleto__serie { font-family: var(--mono); font-size: 0.66rem; letter-spacing: 0.12em;
  color: var(--on-paper-mut); padding-left: 2.1rem; }
.boleto__titulo { font-family: var(--serif); font-style: italic; font-size: 1.55rem;
  padding-left: 2.1rem; }
.boleto__foto { position: relative; aspect-ratio: 3 / 2; border-radius: 5px; overflow: hidden;
  background: var(--ink-850); display: grid; place-items: center; margin-left: 2.1rem; }
.boleto__foto img { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; filter: brightness(0.9) contrast(1.03); }
/* Memoria = azul: leve velo de tinta envejecida sobre las obras del archivo (el pasado) */
.boleto__foto::after { content: ""; position: absolute; inset: 0; pointer-events: none;
  background: color-mix(in oklab, var(--azul-2) 16%, transparent); mix-blend-mode: multiply; }
.boleto__foto .boleto__foto-tag { position: relative; z-index: 1; align-self: end;
  justify-self: start; margin: 0.5rem; padding: 0.25rem 0.45rem;
  background: color-mix(in oklab, var(--ink-950) 72%, transparent);
  color: var(--plata); font-size: 0.62rem; letter-spacing: 0.12em; border-radius: 3px; }
.boleto__foto span { font-family: var(--mono); font-size: 0.62rem; letter-spacing: 0.14em;
  color: var(--on-paper-mut); text-align: center; padding: 0 1rem; line-height: 1.5; }
/* Espacio en blanco deliberado: la promesa (más premium que cualquier evocación) */
.boleto__foto--reservado { background: var(--paper-2);
  outline: 1px dashed color-mix(in oklab, var(--on-paper) 30%, transparent); outline-offset: -8px; }
/* v22: era sans —la única voz ajena dentro del boleto, cuyo serial y banda
   son mono y cuyo título es serif—. Por eso «Un ticket — Un tatuaje — Una
   promesa» se leía como pegada de otra pieza. Ahora habla en la voz de la marca. */
.boleto__texto { padding-left: 2.1rem; font-family: var(--serif);
  font-size: 1rem; line-height: 1.45; letter-spacing: -0.005em;
  color: color-mix(in oklab, var(--on-paper) 90%, transparent); margin: 0; }
.boleto__banda { margin-top: 0.3rem; padding: 0.4rem 0 0 2.1rem; border-top: 1px solid
  color-mix(in oklab, var(--on-paper) 18%, transparent); font-family: var(--mono);
  font-size: 0.62rem; letter-spacing: 0.1em; color: var(--on-paper-mut);
  display: flex; justify-content: space-between; gap: 0.5rem; }
a.boleto { text-decoration: none; cursor: pointer; }

/* El pasaje de regalo (v23): el objeto emitido, sin marco que lo imite.
   height:auto es OBLIGATORIO — el <img> lleva width/height reales y sin esto
   manda el alto del atributo (840px) y la pieza sale estirada. */
.pasaje-regalo { display: block; justify-self: center; width: 100%; max-width: 34rem;
  border-radius: 10px; overflow: hidden; text-decoration: none;
  box-shadow: 0 26px 70px -24px rgba(0,0,0,0.9);
  transition: transform 0.5s var(--ease), box-shadow 0.5s var(--ease); }
/* v26: el boleto de regalo se emitió con el nombre en blanco y su PNG lleva
   zonas translúcidas; sobre el negro del capítulo se veía lavado. Un fondo de
   papel debajo le devuelve el cuerpo sin retocar la pieza emitida. */
.pasaje-regalo { background: var(--paper); }
.pasaje-regalo img { width: 100%; height: auto; }
.pasaje-regalo:hover { transform: translateY(-4px);
  box-shadow: 0 34px 84px -22px rgba(0,0,0,0.95); }
@media (prefers-reduced-motion: reduce) { .pasaje-regalo:hover { transform: none; } }
/* La obra completa vive en Instagram: validación antes de reservar */
.archivo__ig { margin-top: clamp(1.5rem, 4vw, 2.2rem); }
.archivo__ig a { display: inline-flex; align-items: baseline; gap: 0.6rem;
  color: var(--on-ink); text-decoration: none; }
.archivo__ig .em { font-size: clamp(1.05rem, 2.6vw, 1.3rem); }
/* nowrap: en teléfono la flecha se caía sola a la línea de abajo */
.archivo__ig .mono { font-size: 0.7rem; color: var(--azul); letter-spacing: 0.08em;
  white-space: nowrap;
  border-bottom: 1px solid color-mix(in oklab, var(--azul) 45%, transparent); padding-bottom: 2px; }
.archivo__ig a:hover .mono { color: var(--on-ink); }

/* Enlace «reserva aquí» en cuerpo de texto — el patrón de link de la marca
   (nunca dominio crudo). Vivía dentro del bloque del Itinerario; al retirar
   ese capítulo muerto se quedó sin casa y los CTA del FAQ y del regalo
   volvieron al azul de navegador subrayado. Ahora vive por su cuenta. */
.link-reserva { font-family: var(--mono); font-size: 0.74rem; letter-spacing: 0.08em;
  color: var(--azul); text-decoration: none; border-bottom: 1px solid
  color-mix(in oklab, var(--azul) 45%, transparent); padding-bottom: 2px; }
.link-reserva:hover { color: var(--on-ink); }

/* ---------- EL ORO · el papel que envejece a tinta (v23) ----------
   Riel propio con sticky interno. La capa .oro__negativo lleva el papel Y el
   filtro: el JS escribe --neg de 0 a 1 y los claros se van a oscuro. El filtro
   NO vive en el elemento sticky —un filter crea bloque contenedor y no hay por
   qué mezclarlo con el pineado. */
.oro { height: calc(190vh + var(--freno)); min-height: 0; padding: 0; display: block;
  align-content: normal; overflow: visible; background: transparent; }
@media (max-width: 819px) { .oro { height: auto; min-height: 0; } }
@media (prefers-reduced-motion: reduce) { .oro { height: auto; min-height: 0; } }

.oro__sticky { position: sticky; top: 0; min-height: 100dvh; overflow: hidden; }
@media (max-width: 819px), (prefers-reduced-motion: reduce) {
  .oro__sticky { position: relative; min-height: 0; }
}
.oro__negativo { min-height: 100dvh; display: grid; align-content: center;
  padding: clamp(2rem, 6vh, 5rem) max(24px, 6vw); position: relative;
  overflow: hidden;
  /* v25: de papel a tinta. El fondo es el retrato azul a sangre; el color
     base queda debajo por si la foto tarda — nunca un flash blanco. */
  background: var(--ink-950); color: var(--on-ink); }
.oro__negativo > .chapter__bg,
.oro__negativo > .chapter__scrim { position: absolute; inset: 0; z-index: 0; }
/* La foto va espejada: el rostro mira al otro lado y quedó a la DERECHA del
   cuadro. El punto focal se espeja con ella (34% → 66%) — si no, el encuadre
   seguiría buscando la cara donde ya no está y centraría el hombro. */
.bg--perfil { object-position: 66% 28%; filter: brightness(0.86) contrast(1.04); }
/* Degradado y no velo plano: la columna del texto (izquierda) se oscurece y el
   lado del retrato respira. Si se apagara parejo, la foto dejaría de existir.
   El degradado también se dio vuelta: ahora carga a la izquierda. */
.oro__negativo > .chapter__scrim {
  background: linear-gradient(to right,
    color-mix(in oklab, var(--ink-950) 86%, transparent) 0%,
    color-mix(in oklab, var(--ink-950) 66%, transparent) 42%,
    color-mix(in oklab, var(--ink-950) 22%, transparent) 100%); }
.oro__negativo > .wrap { position: relative; z-index: 1; }
@media (max-width: 819px) {
  /* En vertical el texto ocupa todo el ancho: el velo va parejo, y el encuadre
     sube a la cara — con 50% quedaba el hombro y un filo del perfil. */
  .oro__negativo > .chapter__scrim {
    background: linear-gradient(to top,
      color-mix(in oklab, var(--ink-950) 88%, transparent) 0%,
      color-mix(in oklab, var(--ink-950) 72%, transparent) 62%,
      color-mix(in oklab, var(--ink-950) 40%, transparent) 100%); }
  .bg--perfil { object-position: 38% 26%; }
  /* El texto se va al pie: arriba manda el retrato. */
  .oro__negativo { align-content: end; padding-bottom: clamp(2.5rem, 8vh, 4rem); }
}
/* v24: FUERA el pase a negativo. Decisión de Francisco: la inversión del
   capítulo entero era demasiado —daba vuelta el papel, el texto y la foto de
   una sola vez, y lo que había que mostrar era la persona, no el papel—.
   El papel ahora se queda quieto y claro; lo que ocurre con el scroll es que
   la silueta SE FORMA (ver .oro__figura más abajo). */
@media (max-width: 819px) { .oro__negativo { min-height: 100dvh; } }
@media (prefers-reduced-motion: reduce) { .oro__negativo { min-height: 0; } }
.oro__layout { display: grid; gap: clamp(2rem, 5vw, 3.5rem); align-items: center; }
/* v25: una sola columna — la segunda era la figura, que ahora es el fondo. */
@media (min-width: 820px) { .oro__layout { max-width: 46rem; margin-inline: 0; } }
.oro h2 { margin-bottom: 1.2rem; color: var(--on-ink); }
.oro p { max-width: 42ch; color: var(--on-ink-mut); font-size: 1.1rem; }
.oro p strong { color: var(--on-ink); font-weight: 500; }
/* v24: «Tatuador — Psicólogo.» baja DEBAJO del párrafo y se achica (pedido de
   Francisco). Antes iba arriba y en 1.25rem: pesaba como un segundo título y
   competía con el h2. Ahora es lo que debe ser —la firma del oficio al pie de
   lo que se acaba de decir—, en escala de dato, no de titular. */
.oro__oficio { font-family: var(--serif); font-style: italic;
  margin-top: 1.1rem; margin-bottom: 0; }
.oro__oficio strong { font-size: 0.98rem; letter-spacing: 0.02em; }

/* ---- La silueta, QUIETA (v24 · segunda pasada) ----
   Se quitó toda transición: primero fue el pase a negativo, después el
   revelado por máscara con el scroll, y Francisco pidió ninguno de los dos.
   La foto se ve igual siempre.
   Tampoco lleva recuadro: nada de border-radius ni de sombra. El PNG ya trae
   el fondo al color del papel y el borde inferior disuelto, así que no hay
   rectángulo que disimular — si se le pusiera una esquina redondeada, el
   recorte volvería a aparecer justo donde se quiso que desapareciera. */
.oro__figura { margin: 0; justify-self: center; max-width: min(420px, 86vw); }
.oro__figura img { width: 100%; height: auto; }
.oro figcaption { margin-top: 0.7rem; font-family: var(--mono);
  font-size: 0.62rem; letter-spacing: 0.14em; color: var(--on-paper-mut); text-align: center; }
/* Banda de hechos verificables, gramática de boleto */
.oro__banda { display: flex; flex-wrap: wrap; gap: 0.4rem 1.4rem; margin-top: 1.6rem;
  padding-top: 0.9rem; border-top: 1px solid color-mix(in oklab, var(--on-paper) 20%, transparent);
  font-family: var(--mono); font-size: 0.64rem; letter-spacing: 0.12em;
  color: var(--on-paper-mut); }
.oro__igs { display: flex; flex-wrap: wrap; gap: 0.7rem; margin-top: 1.6rem; }
.igtag { font-family: var(--mono); font-size: 0.74rem; letter-spacing: 0.05em;
  color: var(--azul-2); text-decoration: none; padding: 0.5rem 0.9rem;
  border: 1px solid color-mix(in oklab, var(--azul-2) 45%, transparent); border-radius: 999px; }
.igtag:hover { background: color-mix(in oklab, var(--azul-2) 12%, transparent); }
/* (v20: la figura del umbral —los guantes a lo ancho— se retiró del capítulo) */

/* ---------- ANTES DE ABORDAR · el freno (v23) ----------
   Riel corto con sticky interno. OJO: este capítulo fue de los que se cortaban
   en v16 por pinear contenido más alto que la pantalla; por eso su texto mide
   también por vh y el pin solo existe donde el contenido cabe holgado. */
.antes { height: calc(150vh + var(--freno)); min-height: 0; padding: 0; display: block;
  align-content: normal; overflow: visible; background: var(--ink-950); }
/* El cerrojo estaba en 760px de alto y dejaba sin freno a cualquier portátil
   —que es donde se mira esto—. Medido con la primera pregunta ABIERTA, el
   capítulo cabe holgado hasta los 640px; el pin se retira recién bajo 600. */
@media (max-width: 819px), (max-height: 600px), (prefers-reduced-motion: reduce) {
  .antes { height: auto; min-height: 0; }
}
.antes__sticky { position: sticky; top: 0; min-height: 100dvh; overflow: hidden;
  display: grid; align-content: center;
  padding: clamp(1.5rem, 5vh, 4.5rem) max(24px, 6vw); background: var(--ink-950); }
@media (max-width: 819px), (max-height: 600px), (prefers-reduced-motion: reduce) {
  /* v26: se suelta el pin (el capítulo fluye) PERO conserva el alto de una
     pantalla: con foto de fondo, un capítulo de media pantalla se ve como una
     franja con la imagen recortada y aire muerto arriba y abajo.
     min-height, no height: si el contenido crece, crece con él. */
  .antes__sticky { position: relative; min-height: 100dvh; }
}
/* v25: el registro del taller, de fondo. Va dentro del sticky (que es
   position:sticky, o sea posicionado), no como hijo del .chapter — por eso
   necesita sus propias reglas y no le sirven las de .chapter > .chapter__bg.
   El punto focal cae en las manos y la máquina, no en el techo. */
.antes__sticky > .chapter__bg,
.antes__sticky > .chapter__scrim { position: absolute; inset: 0; z-index: 0; }
.bg--taller { object-position: 62% 46%; filter: brightness(0.52) contrast(1.04) saturate(0.7); }
/* Scrim más cargado que el del cierre: acá abajo hay texto largo que LEER,
   no un titular. El contraste manda sobre la foto. */
.antes__sticky > .chapter__scrim {
  background: linear-gradient(to right,
    color-mix(in oklab, var(--ink-950) 88%, transparent) 0%,
    color-mix(in oklab, var(--ink-950) 78%, transparent) 55%,
    color-mix(in oklab, var(--ink-950) 52%, transparent) 100%); }
.antes__sticky > .wrap { position: relative; z-index: 1; }
@media (max-width: 819px) {
  /* En vertical la foto compite con las preguntas: se apaga casi del todo. */
  .antes__sticky > .chapter__scrim {
    background: color-mix(in oklab, var(--ink-950) 90%, transparent); }
}

.antes h2 { margin-bottom: clamp(1.4rem, 4vw, 2.2rem); }
.faq { max-width: 44rem; }
.faq details { border-top: 1px dashed color-mix(in oklab, var(--plata) 26%, transparent); }
.faq details:last-of-type { border-bottom: 1px dashed color-mix(in oklab, var(--plata) 26%, transparent); }
.faq summary { cursor: pointer; list-style: none; display: flex; align-items: baseline;
  justify-content: space-between; gap: 1rem;
  padding: clamp(0.7rem, min(2.5vw, 1.9vh), 1.15rem) 0.2rem;
  font-family: var(--serif); font-style: italic;
  font-size: clamp(1.05rem, min(3vw, 3.1vh), 1.45rem); color: var(--on-ink); }
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: "+"; font-family: var(--mono); font-style: normal;
  font-size: 1rem; color: var(--azul); transition: transform 0.4s var(--ease); }
.faq details[open] summary::after { transform: rotate(45deg); }
@media (prefers-reduced-motion: reduce) { .faq summary::after { transition: none; } }
.faq details p { margin: 0 0 1.1rem; padding: 0 0.2rem; color: var(--on-ink-mut);
  max-width: 58ch; }
.faq details p strong { color: var(--on-ink); font-weight: 500; }
.antes__cta { margin-top: 1.8rem; }

/* ---------- EL PASAJE QUE SE REGALA ---------- */
/* v24: también frena. Es el último gesto antes de la reserva y merece que uno
   se detenga en él, no que pase de largo. Riel corto —su contenido es breve—
   con sticky interno, el patrón que ya usan los demás capítulos. */
.agente { height: calc(150vh + var(--freno)); min-height: 0; padding: 0; display: block;
  align-content: normal; overflow: visible; background: var(--ink-900); }
@media (max-width: 819px), (max-height: 600px), (prefers-reduced-motion: reduce) {
  .agente { height: auto; min-height: 0; }
}
.agente__sticky { position: sticky; top: 0; min-height: 100dvh; overflow: hidden;
  display: grid; align-content: center;
  padding: clamp(1.5rem, 5vh, 4.5rem) max(24px, 6vw); background: var(--ink-900); }
@media (max-width: 819px), (max-height: 600px), (prefers-reduced-motion: reduce) {
  .agente__sticky { position: relative; min-height: 0; }
}
.agente__layout { display: grid; gap: clamp(1.5rem, 5vw, 3rem);
  grid-template-columns: 1fr; align-items: center; }
@media (min-width: 820px) { .agente__layout { grid-template-columns: 1fr 1fr; } }
.agente h2 { margin-bottom: 1rem; }
.agente__intro { color: var(--on-ink-mut); max-width: 30ch;
  font-family: var(--serif); font-size: clamp(1.1rem, 2.4vw, 1.35rem);
  line-height: 1.4; }
.agente__regalo-cta { margin-top: 0.7rem; }

/* ---------- RESERVA ---------- */
/* Reserva: pega el contenido al borde superior y hace scroll normal (fuera del
   pin centrado), para que al bajar la pantalla suba y no se quede clavada. */
.reserva { background: var(--ink-950); text-align: center; justify-items: center;
  position: relative; align-content: start; overflow: visible;
  padding-top: clamp(3.5rem, 12vh, 7rem); }
/* La foto de tintas es oscura: scrim liviano para no matarla */
.reserva .chapter__scrim { background: color-mix(in oklab, var(--ink-950) 48%, transparent); }
.reserva h2 { }
/* La línea suelta bajo un título es el segundo aliento del título, no prosa
   explicativa: va en serif, igual que la bajada del hero. La sans queda para
   donde hay párrafo de verdad (el oro, las preguntas). */
.reserva p { color: var(--on-ink-mut); max-width: 36ch; margin: 1rem auto 2rem;
  font-family: var(--serif); font-size: 1.25rem; }
.reserva p em { color: var(--on-ink); }
.cta-row { display: flex; flex-wrap: wrap; gap: 0.9rem; justify-content: center; }
.btn { font-family: var(--mono); font-size: 0.82rem; letter-spacing: 0.06em;
  text-decoration: none; padding: 0.95rem 1.6rem; border-radius: 8px; cursor: pointer;
  transition: transform 0.5s var(--ease); border: 1px solid transparent; }
.btn:hover { transform: translateY(-2px); }
.btn--solid { background: var(--paper); color: var(--on-paper); }
/* Línea de des-riesgo bajo los CTA */

/* ---------- CIERRE Y CRÉDITO (v23) ----------
   La marca y las cuentas ya no cuelgan de un pie aparte: viven dentro del
   último cuadro, pegadas al botón —título, línea, botón, marca, cuentas, un
   solo bloque—. Abajo del todo queda una sola línea de crédito. */
.reserva__cierre { display: grid; justify-items: center;
  gap: clamp(0.9rem, 2.4vh, 1.6rem); }
.reserva__cierre .cta-row { margin-top: clamp(0.2rem, 1vh, 0.6rem); }
.reserva__marca { width: 108px; height: auto; opacity: 0.9;
  margin-top: clamp(0.6rem, 2.4vh, 1.6rem);
  filter: drop-shadow(0 2px 14px rgba(0,0,0,0.65)); }
.reserva__igs { display: flex; gap: clamp(1rem, 4vw, 1.8rem); flex-wrap: wrap;
  justify-content: center; }
.reserva__igs a { color: var(--azul); text-decoration: none;
  font-family: var(--mono); font-size: 0.78rem; }
.reserva__igs a:hover { color: var(--on-ink); }

.footer { position: relative; z-index: var(--z-content); background: var(--ink-950);
  padding: clamp(1.4rem, 3vh, 2rem) 1.5rem
           calc(clamp(1.4rem, 3vh, 2rem) + env(safe-area-inset-bottom));
  text-align: center; }
.footer__cred { color: var(--on-ink-mut); font-size: 0.74rem; margin: 0;
  font-family: var(--mono); letter-spacing: 0.04em; }
.footer__cred a { color: var(--on-ink); text-decoration: none;
  border-bottom: 1px solid color-mix(in oklab, var(--azul) 45%, transparent); }

/* ---------- Ventanillas de boleto · «pasado» / «futuro» ----------
   Tablero de estación: al entrar el verso, las letras giran como paletas
   solari hasta clavar el destino. Sin JS, el verso queda en su serif normal.
   v19: era una pastilla de papel blanco que en móvil se salía del renglón y
   leía como texto resaltado por error. Ahora es tablero de andén —celdas
   oscuras sobre el retrato— y puede quebrar de línea sin romper nada. */
html.js .flap { display: inline-flex; flex-wrap: wrap; gap: 2px;
  vertical-align: 0.08em; margin: 0 0.1em; }
html.js .flap b { font-family: var(--mono); font-weight: 400; font-style: normal;
  font-size: 0.5em; line-height: 1.45; text-align: center; display: inline-block;
  min-width: 1.05em; padding: 0.12em 0.1em; border-radius: 2px;
  background: color-mix(in oklab, var(--ink-950) 82%, transparent);
  box-shadow: inset 0 -1px 0 rgba(255,255,255,0.10);
  color: var(--plata); }
html.js .flap--futuro b { color: var(--azul); }
html.js .flap b.flip { animation: flapflip 0.11s linear; }
@keyframes flapflip { from { transform: scaleY(0.2); } to { transform: scaleY(1); } }
@media (prefers-reduced-motion: reduce) { html.js .flap b.flip { animation: none; } }

/* ---------- Afinado móvil ---------- */
@media (max-width: 700px) {
  /* Riel de scrub más corto en teléfono: el pulgar recorre más lento (v18) */
  .hero { height: 150vh; }
  /* Las secciones de lectura respiran a su alto real, sin centrado forzado.
     El pasaje queda fuera: su alto ES el riel del fundido. */
  .chapter:not(.hero):not(.pasaje) { align-content: safe center; overflow: visible; }
}
