/* ==========================================================================
   A.P. Torrescusa — sistema de diseño

   TRES MUNDOS. No comparten piel, solo estructura.
     · bosque — Los Portadores del Ojo Verde. Noche, vídeo, ámbar.
     · papel  — El amor del Fénix. Papel, tinta, tipografía. Sin imágenes.
     · neutro — El autor. Ni bosque ni poemario: la casa común.

   REGLAS DE MOVIMIENTO
     · Una sola curva en todo el sitio. Mezclarlas es lo que hace que una web
       parezca cosida a trozos.
     · Nada rota, nada rebota, nada escala más de un 4 %.
     · El texto no aparece con un fundido: sube desde detrás de una máscara.
     · Todo lo que se mueve va atado al scroll. El lector manda sobre el ritmo.

   OJO CON `position: sticky`
     Se rompe si un ancestro tiene `transform`, `will-change: transform`,
     `filter`, `perspective` o `overflow` distinto de `visible`. Por eso el
     body lleva `overflow-x: clip` y no `hidden`: `clip` recorta sin crear
     contenedor de scroll. Costó encontrarlo.
   ========================================================================== */

/* --- Tokens base (mundo neutro) ------------------------------------------ */
:root {
  --negro:   #0A0A0B;
  --hondo:   #101012;
  --plano:   #17171A;
  --linea:   rgba(242, 239, 233, .10);
  --linea2:  rgba(242, 239, 233, .20);
  /* --linea2 vale para separar cajas, pero el borde de un campo de formulario
     es el limite visual de un control y WCAG 1.4.11 le exige 3:1. Al .20 daba
     1,77:1: los campos eran practicamente invisibles hasta recibir el foco. */
  --borde-campo: rgba(242, 239, 233, .42);   /* 3,71:1 sobre --plano */

  --tinta:   #F2EFE9;
  --gris:    #A9A49B;   /* 7,4:1 sobre --plano */
  --tenue:   #8C8880;   /* 5,2:1 sobre --plano · cumple AA */

  /* El ámbar sale de la trufa dorada. Es el complementario del bosque: contra
     tanto verde y teal del material, salta. Un acento verde se perdía dentro
     de las propias imágenes. */
  --ambar:      #F0A93C;
  --ambar-ink:  #241603;
  --ambar-hondo:#C97F1B;

  --display: "Bricolage Grotesque", "Inter", system-ui, sans-serif;
  --lectura: "Newsreader", Georgia, serif;
  --ui:      "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

  --marco:  min(1320px, 100% - 3rem);
  --angosto: min(760px, 100% - 3rem);
  --radio: 4px;

  --curva: cubic-bezier(.16, 1, .3, 1);
}

/* --- Mundo papel: El amor del Fénix --------------------------------------
   Sin imágenes, sin vídeo, sin bosque. Un poemario no los necesita: necesita
   espacio en blanco y una tipografía que respire.
   -------------------------------------------------------------------------- */
.mundo-papel {
  --negro:  #F4F1E9;
  --hondo:  #EDE9DE;
  --plano:  #FFFFFF;
  --linea:  #DCD6C7;
  --linea2: #C3BBA8;
  --borde-campo: #8B8371;                    /* 3,64:1 sobre el blanco */
  --tinta:  #16150F;
  --gris:   #52504A;
  --tenue:  #6B6860;
  /* #A8641A daba 4,13:1 sobre el crema del body y 3,84:1 sobre el fondo
     hondo: por debajo del 4,5:1 que pide la AA para texto normal, y lo usan
     los enlaces a tiendas y los pares anterior/siguiente de los 16 poemas.
     #8A5214 da 5,64 / 5,25 / 6,37 sobre los tres fondos del mundo papel. */
  --ambar:      #8A5214;
  --ambar-ink:  #FFFFFF;
  --ambar-hondo:#7C4810;
}

/* ========================================================================== */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--negro);
  color: var(--tinta);
  font-family: var(--ui);
  font-size: 1rem;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  /* clip, no hidden: hidden convierte el body en contenedor de scroll y anula
     el sticky de los paneles. */
  overflow-x: clip;
}
/* `height: auto` es obligatorio junto a `max-width: 100%`. Sin él, una imagen
   con atributos width/height conserva la altura declarada mientras el ancho se
   comprime, y sale aplastada. Se me había caído del reset y deformaba todas las
   imágenes del sitio. */
img, video, canvas, svg { display: block; max-width: 100%; height: auto; }
/* Excepción: lo que se estira a propósito dentro de un contenedor con posición
   propia y recorte por object-fit. */
.hero__fijo img, .hero__fijo video, .hero__fijo canvas,
.plano__fijo img, .plano__fijo video, .plano__fijo canvas,
.retrato img, .retrato video { height: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }
h1, h2, h3, h4 {
  margin: 0; font-family: var(--display); font-weight: 700;
  line-height: .94; letter-spacing: -.035em;
  font-variation-settings: "opsz" 96;
  text-wrap: balance;
}
p { margin: 0; }
/* Bricolage Grotesque no tiene cursiva: si se pide, el navegador la fabrica
   inclinando las letras y se ve barato. En titulares el énfasis se marca con
   color, no con cursiva. */
h1 em, h2 em, h3 em, .frase em { font-style: normal; color: var(--ambar); }
ul, ol { margin: 0; padding: 0; list-style: none; }
::selection { background: rgba(240, 169, 60, .3); }
:focus-visible { outline: 2px solid var(--ambar); outline-offset: 3px; }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.skip {
  position: absolute; left: 1rem; top: -100px; z-index: 200;
  padding: .7rem 1.1rem; background: var(--ambar); color: var(--ambar-ink);
  font-weight: 600; transition: top .2s var(--curva);
}
.skip:focus { top: 0; }
[id] { scroll-margin-top: 6rem; }

/* --- Escala tipográfica --------------------------------------------------- */
.d1 { font-size: clamp(2.9rem, 9.5vw, 9rem);  letter-spacing: -.04em; }
.d2 { font-size: clamp(2.1rem, 5.4vw, 4.2rem); letter-spacing: -.035em; }
.d3 { font-size: clamp(1.4rem, 2.8vw, 2.2rem); letter-spacing: -.025em; }

.tag {
  font-family: var(--ui); font-size: .68rem; font-weight: 500;
  letter-spacing: .2em; text-transform: uppercase; color: var(--tenue);
}
.tag.ambar { color: var(--ambar); }

.lede {
  text-wrap: balance;
  font-family: var(--lectura); font-weight: 300;
  font-size: clamp(1.08rem, 1.6vw, 1.3rem); line-height: 1.68; color: var(--gris);
}
.prosa {
  font-family: var(--lectura); font-weight: 300;
  font-size: 1.08rem; line-height: 1.8; color: var(--gris);
}
.prosa p + p { margin-top: 1.4em; }
.prosa strong { color: var(--tinta); font-weight: 600; }

/* --- Estructura ----------------------------------------------------------- */
.marco   { width: var(--marco);   margin-inline: auto; }
.angosto { width: var(--angosto); margin-inline: auto; }
.bloque  { padding-block: clamp(4.5rem, 9vw, 8rem); position: relative; }
.bloque--hondo { background: var(--hondo); }
.bloque--tight { padding-block: clamp(3rem, 6vw, 5rem); }
.centro { text-align: center; }

/* --- Revelados ------------------------------------------------------------ */
/* La máscara del revelado era más baja que la tinta: con line-height .94 y
   una profundidad de descendente de ~.21em en Bricolage, la j de «mojado»
   salía cortada, y con ella toda g, p, q, y y las comas de cualquier
   titular del sitio. .22em cubre el descendente sin romper la máscara:
   translateY(105%) sigue sacando el texto fuera del recuadro. */
.linea { display: block; overflow: hidden; padding-bottom: .22em; margin-bottom: -.16em; }
.linea > span { display: block; transform: translateY(105%); transition: transform 1.05s var(--curva); }
.visto .linea > span { transform: none; }
.linea:nth-child(2) > span { transition-delay: .08s }
.linea:nth-child(3) > span { transition-delay: .16s }
.linea:nth-child(4) > span { transition-delay: .24s }

.sube { opacity: 0; transform: translateY(22px); transition: opacity .9s var(--curva), transform .9s var(--curva); }
.visto.sube, .visto .sube { opacity: 1; transform: none; }
.sube[data-r="1"] { transition-delay: .1s }
.sube[data-r="2"] { transition-delay: .2s }
.sube[data-r="3"] { transition-delay: .3s }
.sube[data-r="4"] { transition-delay: .4s }

/* --- Cabecera ------------------------------------------------------------- */
.top {
  position: fixed; inset: 0 0 auto 0; z-index: 50;
  display: flex; align-items: center; gap: 1.5rem;
  padding: 1.3rem clamp(1.25rem, 3vw, 2.4rem);
  color: var(--tinta);
  /* Sombra en vez de mix-blend-mode: difference se deshace justo cuando el
     fondo es luminoso, que es cuando más falta hace leer. */
  text-shadow: 0 1px 3px rgba(0,0,0,.55), 0 6px 24px rgba(0,0,0,.45);
  transition: background .35s var(--curva), padding .35s var(--curva);
}
.mundo-papel .top { text-shadow: none; }
.top[data-fijo] {
  background: color-mix(in srgb, var(--negro) 88%, transparent);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 1px solid var(--linea);
  padding-block: .8rem; text-shadow: none;
}
/* El monograma APT es la firma de Ángel: trazo negro sobre transparente. En
   los mundos oscuros se invierte por CSS en vez de mantener dos ficheros. */
.top__marca { display: flex; align-items: center; gap: .7rem;
  font-family: var(--display); font-weight: 600; font-size: 1.05rem; letter-spacing: -.02em; }
.top__marca img { width: 46px; height: auto; filter: invert(1); opacity: .95; }
.mundo-papel .top__marca img { filter: none; }
.pie img { filter: invert(1); opacity: .9; }
.mundo-papel .pie img { filter: none; }
@media (max-width: 420px) { .top__marca span { display: none } .top__marca img { width: 54px } }
.top nav { display: flex; align-items: center; gap: 1.6rem; margin-left: auto; }
.top nav a {
  font-family: var(--ui); font-size: .72rem; font-weight: 500;
  letter-spacing: .16em; text-transform: uppercase;
  color: color-mix(in srgb, var(--tinta) 86%, transparent);
  transition: color .2s var(--curva);
}
.top nav a:hover, .top nav a[aria-current="page"] { color: var(--ambar); }
.top__cta {
  padding: .6rem 1.1rem; background: var(--ambar); color: var(--ambar-ink) !important;
  border-radius: 100px; letter-spacing: .1em !important;
}
.top__cta:hover { filter: brightness(1.08); }
.burger { display: none; margin-left: auto; padding: .5rem; }
.burger span { display: block; width: 22px; height: 2px; background: currentColor; transition: transform .3s var(--curva), opacity .3s; }
.burger span + span { margin-top: 5px; }
.burger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg) }
.burger[aria-expanded="true"] span:nth-child(2) { opacity: 0 }
.burger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg) }

@media (max-width: 900px) {
  .burger { display: block; }
  .top nav {
    position: fixed; inset: 0 0 auto 0; margin: 0;
    flex-direction: column; align-items: stretch; gap: .3rem;
    padding: 5.5rem 1.5rem 2rem;
    background: color-mix(in srgb, var(--negro) 97%, transparent);
    backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
    border-bottom: 1px solid var(--linea);
    transform: translateY(-102%); transition: transform .45s var(--curva);
    visibility: hidden; text-shadow: none;
  }
  .top nav[data-abierto] { transform: none; visibility: visible; }
  .top nav a { padding: .9rem .5rem; font-size: .85rem; }
  .top__cta { text-align: center; margin-top: .6rem; }
}

/* --- HUD ------------------------------------------------------------------ */
.hud {
  position: fixed; z-index: 40; left: 1.6rem; bottom: 1.6rem;
  display: flex; align-items: center; gap: .7rem;
  font-family: var(--ui); font-size: .66rem; letter-spacing: .18em;
  text-transform: uppercase; color: color-mix(in srgb, var(--tinta) 70%, transparent);
  text-shadow: 0 1px 3px rgba(0,0,0,.6); pointer-events: none;
}
.mundo-papel .hud { text-shadow: none; }
.hud b { color: var(--tinta); font-weight: 500; font-variant-numeric: tabular-nums; }
.hud i { display: block; width: 44px; height: 1px; background: currentColor; opacity: .4; position: relative; }
.hud i::after { content: ""; position: absolute; inset: 0; background: var(--ambar); transform: scaleX(var(--p, 0)); transform-origin: 0 50%; }
@media (max-width: 700px) { .hud { display: none } }

/* --- Botones -------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; gap: .8rem;
  padding: 1.05rem 1.9rem; border-radius: 100px;
  font-family: var(--ui); font-size: .88rem; font-weight: 600; letter-spacing: -.01em;
  border: 1px solid transparent; white-space: nowrap;
  transition: transform .5s var(--curva), background .3s, border-color .3s, color .3s;
  will-change: transform;
}
.btn i { display: block; width: 16px; height: 1px; background: currentColor; transition: width .4s var(--curva); }
.btn:hover i { width: 26px; }
.btn--ambar { background: var(--ambar); color: var(--ambar-ink); }
.btn--ambar:hover { filter: brightness(1.08); }
.btn--claro { background: var(--tinta); color: var(--negro); }
.btn--linea { border-color: var(--linea2); color: var(--tinta); }
.btn--linea:hover { border-color: var(--tinta); }
.fila { display: flex; flex-wrap: wrap; gap: .75rem; }

.enlace {
  display: inline-flex; align-items: center; gap: .5rem;
  font-family: var(--ui); font-size: .88rem; font-weight: 600;
  color: var(--ambar); transition: gap .3s var(--curva);
}
.enlace::after { content: "→"; }
.enlace:hover { gap: .8rem; }

/* ==========================================================================
   HERO con vídeo gobernado por el scroll
   ========================================================================== */
/* 170vh daban 630 px de recorrido para 64 fotogramas. A 150vh son 450 px:
   7,0 px por fotograma, y una pantalla menos de scroll antes del contenido. */
.hero { position: relative; height: 150vh; }
.hero--corto { height: 120vh; }
.hero__fijo { position: sticky; top: 0; height: 100vh; overflow: hidden; display: grid; place-items: center; }
.hero__poster, .hero__video, .hero__lienzo { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.hero__poster { z-index: 1; transition: opacity .6s ease }
.hero__video  { z-index: 2; opacity: 0 }
.hero__lienzo { z-index: 3; opacity: 0; transition: opacity .6s ease }
.hero__fijo[data-video] .hero__video  { opacity: 1 }
.hero__fijo[data-video] .hero__poster { opacity: 0 }
.hero__fijo[data-listo] .hero__lienzo { opacity: 1 }
.hero__fijo[data-listo] .hero__video  { opacity: 0 }
.hero__velo {
  position: absolute; inset: 0; z-index: 4;
  background:
    radial-gradient(58% 42% at 50% 50%, rgba(10,10,11,.72), transparent 72%),
    radial-gradient(120% 85% at 50% 42%, transparent 40%, rgba(10,10,11,.48) 100%),
    linear-gradient(180deg, rgba(10,10,11,.5), transparent 26%, rgba(10,10,11,.24) 66%, var(--negro));
}
.hero__texto {
  position: relative; z-index: 5; width: var(--marco); text-align: center;
  text-shadow: 0 1px 2px rgba(0,0,0,.55), 0 10px 40px rgba(0,0,0,.75);
}
.hero__texto .tag { color: color-mix(in srgb, var(--tinta) 90%, transparent); }
.hero__meta { margin-top: 2rem; display: flex; gap: 2rem; justify-content: center; flex-wrap: wrap; }
.hero__abajo { position: absolute; z-index: 5; left: 50%; bottom: 2rem; translate: -50% 0; display: grid; justify-items: center; gap: .7rem; }
.hero__abajo i { display: block; width: 1px; height: 46px; background: linear-gradient(var(--tinta), transparent); opacity: .8; }

/* --- Cabecera de página sin vídeo ---------------------------------------- */
.cabeza { padding-block: clamp(6.5rem, 11vw, 10rem) clamp(2.5rem, 6vw, 4.5rem); padding-inline: 0; position: relative; }
.cabeza--centro { text-align: center; }

/* ==========================================================================
   Plano a pantalla completa
   ========================================================================== */
/* 175vh por panel eran 1,75 pantallas de scroll para 128-227 caracteres, y
   con cuatro planos seguidos sumaban el 55 % de la página. A 130vh el
   recorrido se reduce a la mitad y, con más fotogramas cacheados, el scrub
   pasa de 22,5 px por fotograma a menos de 5: mucho más suave. */
.plano { position: relative; height: 130vh; }
.plano__fijo { position: sticky; top: 0; height: 100vh; overflow: hidden; display: grid; place-items: center; }
/* Mismo mecanismo que el hero: poster -> video -> canvas, y el scroll manda.
   Tener unos videos en bucle y otros gobernados por el scroll eran dos
   lenguajes distintos en la misma pagina, que es lo que la abarataba. */
.plano__fijo img, .plano__fijo video, .plano__fijo canvas {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover;
}
.plano__fijo img    { z-index: 1; transition: opacity .6s ease }
.plano__fijo video  { z-index: 2; opacity: 0 }
.plano__fijo canvas { z-index: 3; opacity: 0; transition: opacity .6s ease }
.plano__fijo[data-video] video  { opacity: 1 }
.plano__fijo[data-video] img    { opacity: 0 }
.plano__fijo[data-listo] canvas { opacity: 1 }
.plano__fijo[data-listo] video  { opacity: 0 }
.plano__velo {
  position: absolute; inset: 0; z-index: 4;
  /* Cerraba en negro OPACO al 100 %: solo se leía el 44 % del panel, y el
     tercio final del recorrido corría el scrub contra un rectángulo negro.
     Ahora cierra en .72 y el oscurecimiento fuerte se concentra detrás del
     texto, no sobre el plano entero. */
  background:
    radial-gradient(70% 46% at 50% 52%, rgba(10,10,11,.62), transparent 78%),
    linear-gradient(180deg, rgba(10,10,11,.55), rgba(10,10,11,.12) 40%, rgba(10,10,11,.16) 62%, rgba(10,10,11,.72));
}
.plano__texto {
  position: relative; z-index: 5; width: min(760px, 100% - 3rem); text-align: center;
  text-shadow: 0 1px 2px rgba(0,0,0,.5), 0 10px 40px rgba(0,0,0,.6);
}
.plano__texto p { margin-top: 1.6rem; font-family: var(--lectura); font-weight: 300;
  font-size: clamp(1rem, 1.45vw, 1.2rem); line-height: 1.8; color: #D6D3CC; max-width: 46ch; margin-inline: auto; }
/* `.plano__texto p` (0,1,1) le ganaba a `.plano__cita` (0,1,0) y le imponía
   el gris: el único acento de color de cada panel a pantalla completa
   llevaba muerto desde siempre. Se iguala la especificidad. */
.plano__texto p.plano__cita { margin-top: 2.2rem; font-family: var(--lectura); font-style: italic; font-weight: 300;
  font-size: clamp(1.05rem, 1.9vw, 1.5rem); color: var(--ambar); line-height: 1.45; }

/* --- Frase compuesta palabra a palabra ------------------------------------ */
.frase { font-family: var(--display); font-weight: 600; font-size: clamp(1.8rem, 4.2vw, 3.6rem);
  line-height: 1.12; letter-spacing: -.03em; max-width: 22ch; }
.frase w { opacity: .42; transition: opacity .45s ease }
.frase w.on { opacity: 1 }

/* --- Retratos vivos -------------------------------------------------------- */
/* Tarjetas anchas: con columnas de 235px el texto de la ficha se estiraba a
   450px de alto contra 284 de retrato. Más anchas, el texto respira y la
   imagen manda. */
.retratos { display: grid; gap: clamp(.7rem, 1.3vw, 1.2rem);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 330px), 1fr)); }
/* La ficha NO puede ir superpuesta sobre el retrato. Estaba dentro de un
   figcaption absoluto en una tarjeta de proporción 3:4, y el texto medía hasta
   el 143 % de la altura de la tarjeta: tapaba 25 de 27 retratos y en 14 se
   salía fuera. Ahora la tarjeta es una columna: imagen arriba con su
   proporción, texto debajo, y la tarjeta crece lo que haga falta. */
.retrato {
  position: relative; overflow: hidden;
  display: grid; grid-template-rows: auto 1fr;
  background: var(--plano); border: 1px solid var(--linea);
  opacity: 0; transform: translateY(26px) scale(.985);
  transition: opacity 1s var(--curva), transform 1s var(--curva);
}
.retrato__medio { position: relative; aspect-ratio: 4/5; overflow: hidden; }
/* El revelado va POR TARJETA, no por el contenedor. Observando el contenedor,
   en /universo/ a 375 px medía 10.375 px de alto y el umbral del 12 % exigía
   1.245 px visibles cuando la ventana solo ofrece 731: era imposible de
   cumplir, y las quince fichas se quedaban invisibles para siempre. Y aun
   cumpliéndose, las quince se revelaban de golpe —incluidas las que estaban
   nueve mil píxeles más abajo—, así que el escalonado no lo veía nadie. */
.retrato.visto { opacity: 1; transform: none }

/* Retrato doble: Andrés e Iñaki tienen forma humana y forma Sylvánima. Al
   entrar la tarjeta en pantalla el hombre se disuelve en la criatura —cruzar
   la puerta de lluvia, que es lo que les pasa en el libro—, y el puntero
   encima lo devuelve. En táctil, donde no hay puntero, se queda la criatura:
   la nota de la ficha explica cuál es cuál. */
.retrato__otra { position: absolute; inset: 0; width: 100%; object-fit: cover;
  opacity: 0; transition: opacity 1.8s var(--curva); z-index: 2 }

/* La transformacion se dispara cuando el video ESTA CORRIENDO (`data-vivo`), no
   cuando la tarjeta entra en pantalla. Iba con `.visto`, que se pone al asomar,
   asi que en el movil —donde se scrollea rapido y el video tarda en cargar— la
   criatura tapaba al hombre antes de que el video hubiera arrancado: se veia un
   instante de video y de golpe una foto fija. Ahora se ven cuatro segundos de
   hombre y despues cambia. */
.retrato[data-doble][data-vivo] .retrato__otra { opacity: 1; transition-delay: 4s }
/* Respaldo por si el video no llega a cargar: sin esto la ficha se quedaria
   para siempre en la forma humana y la nota hablaria de algo que no se ve. */
.retrato[data-doble].visto .retrato__otra { transition-delay: 7s; opacity: 1 }
.retrato[data-doble][data-vivo] .retrato__otra { transition-delay: 4s }
@media (hover: hover) {
  .retrato[data-doble]:hover .retrato__otra { opacity: 0; transition-delay: 0s;
    transition-duration: .55s }
}
@media (prefers-reduced-motion: reduce) { .retrato__otra { transition: none } }
/* El escalonado se acota a la fila con el resto de la división: así funciona
   con rejillas de 6 y de 15 por igual, en vez de morir en la sexta tarjeta. */
.retrato:nth-child(3n+2) { transition-delay: .07s }
.retrato:nth-child(3n)   { transition-delay: .14s }
.retrato img, .retrato video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.retrato video { opacity: 0; transition: opacity 1s ease }
.retrato[data-vivo] video { opacity: 1 }
.retrato figcaption { padding: 1.2rem 1.15rem 1.4rem; display: grid; gap: .25rem; }
.retrato h3 { font-size: 1.2rem; color: var(--tinta); }
.retrato .tag { color: var(--ambar); font-size: .6rem; }
.retrato__desc { font-size: .84rem; color: var(--gris); line-height: 1.55; margin-top: .4rem; }
.retrato__ficha {
  margin-top: .9rem; padding-top: .9rem; border-top: 1px solid var(--linea);
  display: grid; gap: .35rem; font-size: .76rem; color: var(--tenue); line-height: 1.5;
}
.retrato__ficha b { color: var(--gris); font-weight: 600; }

/* --- Tarjetas -------------------------------------------------------------- */
.rejilla { display: grid; gap: 1.1rem; }
.rejilla--2 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 320px), 1fr)) }
.rejilla--3 { grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr)) }
.tarjeta { padding: 1.7rem; background: var(--plano); border: 1px solid var(--linea);
  transition: border-color .3s, transform .5s var(--curva); }
.tarjeta:hover { border-color: var(--linea2); transform: translateY(-3px) }
.tarjeta h2, .tarjeta h3 { font-size: 1.2rem; margin-bottom: .6rem }
.tarjeta p { font-size: .93rem; color: var(--gris); line-height: 1.65 }
.tarjeta__num { font-family: var(--display); font-weight: 700; font-size: 2.6rem;
  color: var(--ambar); line-height: 1; margin-bottom: .5rem; letter-spacing: -.04em }

/* --- Datos ----------------------------------------------------------------- */
.datos { margin: 0; border-top: 1px solid var(--linea) }
.datos div { display: flex; justify-content: space-between; gap: 2rem; padding: .85rem 0;
  border-bottom: 1px solid var(--linea); font-size: .87rem }
.datos dt { color: var(--tenue) }
.datos dd { margin: 0; font-variant-numeric: tabular-nums }

/* --- Compra ---------------------------------------------------------------- */
.compra { display: grid; gap: clamp(2.5rem, 5vw, 5rem); align-items: center }
@media (min-width: 900px) { .compra { grid-template-columns: .8fr 1.2fr } }
.portada img { width: 100%; box-shadow: 0 50px 90px -40px rgba(0,0,0,.9);
  transform: translateY(18px); transition: transform 1.2s var(--curva) }
.visto .portada img { transform: none }
.precio { font-family: var(--display); font-weight: 700; font-size: clamp(2.4rem, 4.6vw, 3.6rem);
  line-height: 1; letter-spacing: -.03em; font-variant-numeric: tabular-nums }
.precio small { display: block; margin-top: .9rem; font-family: var(--ui); font-size: .7rem;
  font-weight: 500; letter-spacing: .16em; text-transform: uppercase; color: var(--tenue) }
.nota { margin-top: 1.3rem; font-size: .8rem; color: var(--tenue); max-width: 44ch; line-height: 1.6 }

/* --- Poemas ---------------------------------------------------------------- */
.poema { font-family: var(--lectura); font-weight: 300;
  font-size: clamp(1.1rem, 1.6vw, 1.32rem); line-height: 1.95;
  white-space: pre-wrap; color: var(--tinta); }
.poemas { display: grid; gap: 1px; background: var(--linea); border-block: 1px solid var(--linea) }
.poema-fila { display: grid; grid-template-columns: 3rem 1fr auto; gap: 1.5rem; align-items: baseline;
  padding: 1.15rem .4rem; background: var(--negro); transition: background .3s, padding-left .4s var(--curva) }
a.poema-fila:hover { background: var(--hondo); padding-left: 1.1rem }
.poema-fila__n { font-family: var(--ui); font-size: .7rem; color: var(--tenue); font-variant-numeric: tabular-nums }
.poema-fila__t { font-family: var(--display); font-weight: 600; font-size: 1.15rem; letter-spacing: -.02em }
.poema-fila__e { font-family: var(--ui); font-size: .66rem; letter-spacing: .14em;
  text-transform: uppercase; color: var(--tenue) }
a.poema-fila:hover .poema-fila__e { color: var(--ambar) }
/* `opacity` atenuaba el elemento entero, texto y fondo a la vez, y dejaba el
   numero en 2,31:1 y el «en el libro» en 1,94:1 sobre el fondo compuesto: la
   mitad del minimo legal, en 57 de las 292 filas. La jerarquia se mantiene
   igual de bien bajando solo el color, sin tocar la opacidad. */
.poema-fila--cerrado .poema-fila__t { color: var(--gris) }        /* 5,56:1 */
.poema-fila--cerrado .poema-fila__n,
.poema-fila--cerrado .poema-fila__e { color: var(--gris) }        /* 5,56:1 */
.poema-fila--cerrado { background: transparent }

/* --- Formularios ----------------------------------------------------------- */
.form { display: grid; gap: .8rem; max-width: 34rem }
.form label { font-size: .82rem; color: var(--gris); display: grid; gap: .4rem }
.form input, .form textarea, .form select {
  width: 100%; padding: .95rem 1.1rem; font: inherit; font-size: .95rem;
  background: color-mix(in srgb, var(--plano) 70%, transparent); color: var(--tinta);
  border: 1px solid var(--borde-campo); border-radius: var(--radio);
  transition: border-color .25s var(--curva);
}
.form input:focus, .form textarea:focus, .form select:focus { border-color: var(--ambar) }
.form input:focus-visible, .form textarea:focus-visible, .form select:focus-visible {
  outline: 2px solid var(--ambar); outline-offset: 2px }
.form ::placeholder { color: var(--tenue) }
.form .consent { display: flex; gap: .6rem; align-items: flex-start; font-size: .78rem;
  color: var(--tenue); line-height: 1.55 }
.form .consent input { width: 15px; height: 15px; margin-top: .2rem; flex: none; accent-color: var(--ambar) }
.form .consent a { color: var(--gris); text-decoration: underline }
.form-nota { font-size: .76rem; color: var(--tenue) }

/* --- Avisos ---------------------------------------------------------------- */
.aviso { padding: 1.1rem 1.3rem; font-size: .87rem; line-height: 1.65;
  background: color-mix(in srgb, var(--ambar) 8%, transparent);
  border-left: 2px solid var(--ambar); color: var(--gris) }
.aviso strong { color: var(--tinta) }

/* --- Migas ----------------------------------------------------------------- */
.migas { font-family: var(--ui); font-size: .72rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--tenue); margin-bottom: 1.6rem }
.migas a:hover { color: var(--ambar) }
.migas span { margin-inline: .5rem; opacity: .5 }

/* --- Pie ------------------------------------------------------------------- */
.pie { padding: 5rem 0 2.5rem; border-top: 1px solid var(--linea); background: var(--hondo) }
.pie__rejilla { display: grid; gap: 2.5rem }
@media (min-width: 760px) { .pie__rejilla { grid-template-columns: 1.5fr 1fr 1fr 1fr } }
.pie h2 { font-family: var(--ui); font-size: .68rem; font-weight: 600; letter-spacing: .18em;
  text-transform: uppercase; color: var(--tenue); margin-bottom: 1rem; line-height: 1.4 }
.pie nav { display: grid; gap: .5rem }
.pie nav a { font-size: .88rem; color: var(--gris); transition: color .2s }
.pie nav a:hover { color: var(--ambar) }
.pie__legal { margin-top: 3rem; padding-top: 1.4rem; border-top: 1px solid var(--linea);
  display: flex; flex-wrap: wrap; gap: 1rem 1.5rem; justify-content: space-between;
  font-size: .76rem; color: var(--tenue) }

/* --- Hero del mundo papel -------------------------------------------------
   El poemario no lleva bosque, pero sí lleva a Ángel: un retrato suyo en
   blanco y negro sobre pared clara es papel, no fantasía, así que no rompe la
   separación de los dos mundos.

   Todo lo demás va al revés que en el bosque. El velo es crema en vez de
   negro y entra desde la izquierda, que es justo donde el plano deja la pared
   vacía; el texto va en tinta, alineado a ese hueco, y sin la sombra dura que
   necesita el texto claro sobre vídeo oscuro —sobre un fondo luminoso esa
   sombra solo ensucia—.
   -------------------------------------------------------------------------- */
.hero--papel .hero__velo {
  background:
    linear-gradient(90deg, var(--negro) 0%, rgba(244,241,233,.94) 30%,
                    rgba(244,241,233,.42) 52%, transparent 70%),
    linear-gradient(180deg, transparent 58%, var(--negro) 100%);
}
.hero--papel .hero__texto {
  text-align: left;
  text-shadow: none;
}
/* El titular no puede crecer hasta los 9rem del bosque: aquí solo dispone de
   la mitad izquierda del cuadro, y si desborda se le echa encima a Ángel. */
.hero--papel .hero__texto .d1 { font-size: clamp(2.5rem, 6.2vw, 5.6rem); max-width: 11ch }
.hero--papel .hero__texto .lede { color: var(--gris); margin-inline: 0 }
.hero--papel .hero__abajo { left: auto; right: 2.5rem; translate: none }

/* El mismo giro para los paneles de dentro: velo crema en vez de negro, tinta
   en vez de tiza, y la cita en el ámbar quemado del mundo papel. */
.plano--papel .plano__velo {
  background: linear-gradient(180deg, var(--negro) 0%, rgba(244,241,233,.55) 26%,
                              rgba(244,241,233,.62) 64%, var(--negro) 100%);
}
.plano--papel .plano__texto { text-shadow: none }
.plano--papel .plano__texto p { color: var(--gris) }

/* --- Sin scrub, sin recorrido ---------------------------------------------
   El JS no monta el vídeo atado al scroll por debajo de 900 px de ancho, pero
   las alturas de 170-175vh seguían aplicándose igual. Resultado: en tableta o
   en una ventana estrecha había que recorrer casi dos pantallas por panel
   —cinco paneles en Los Portadores, casi nueve pantallas— por delante de una
   imagen quieta sin que pasara absolutamente nada. Mismo tratamiento que ya
   recibía «movimiento reducido»: si no hay movimiento, no hay recorrido.
   -------------------------------------------------------------------------- */
@media (max-width: 899px) {
  /* El scrub también corre en móvil. Antes no: aquí se ponía `height: auto` y
     `position: static`, y sin recorrido de scroll `progreso()` devolvía 0
     siempre, así que aunque el JS se hubiera activado no habría avanzado un
     solo fotograma. Eran dos bloqueos, no uno.

     En `svh` y no en `vh`: la barra del navegador móvil aparece y desaparece
     al scrollear, y con `vh` la altura de la sección cambiaría a mitad de
     gesto — el vídeo daría un salto justo mientras se mira.

     El recorrido es proporcionalmente el mismo que en escritorio (unos 28svh
     por plano), así que con menos fotogramas se mantiene el escalón por debajo
     de los 8 px. Los fotogramas y la resolución los baja el JS. */
  .hero  { height: 145vh; height: 145svh }
  .plano { height: 128vh; height: 128svh }
  .hero__fijo, .plano__fijo { height: 100vh; height: 100svh }
}

/* --- Movimiento reducido --------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important }
  .linea > span { transform: none !important }
  .sube, .retrato { opacity: 1 !important; transform: none !important }
  .frase w { opacity: 1 !important }
  .hero, .plano { height: auto !important }
  .hero__fijo, .plano__fijo { position: static; height: 80vh }
}

/* --- Precios de los firmados ----------------------------------------------
   Sustituye al desplegable del formulario que se retiró: la misma información,
   pero legible de un vistazo y sin pedir nada a cambio.
   -------------------------------------------------------------------------- */
.precios-firmados { display: grid; gap: 0 }
.precios-firmados li { display: flex; justify-content: space-between; align-items: baseline;
  gap: 1.5rem; padding: .85rem 0; border-bottom: 1px solid var(--linea) }
.precios-firmados li:first-child { border-top: 1px solid var(--linea) }
.precios-firmados span { color: var(--gris) }
.precios-firmados b { font-family: var(--display); font-weight: 600; font-size: 1.1rem;
  font-variant-numeric: tabular-nums; white-space: nowrap }

/* --- Planos en pantalla estrecha ------------------------------------------
   Fuente 16:9 dentro de una caja de 375x812 (0,46): `cover` deja ver 332 px de
   los 1280, o sea el 26 % del ancho del fotograma. Cualquier composicion que
   tuvieran estos planos desaparecia y quedaban como texturas centradas. Con la
   altura ya recortada y el encuadre subido al tercio superior —que es donde
   suele estar el sujeto— se recupera bastante.
   -------------------------------------------------------------------------- */
@media (max-width: 700px) {
  /* A esta anchura la caja es casi el doble de alta que ancha y el video es
     16:9, asi que solo se ve una franja estrecha del centro. `--foco` deja que
     cada plano diga por donde recortarse; sin el, el sujeto se sale. */
  .plano__fijo img, .plano__fijo video, .plano__fijo canvas,
  .hero__fijo img, .hero__fijo video, .hero__fijo canvas { object-position: var(--foco, 50% 38%) }
  /* Aquí había un `height: 62vh` que iba con el apilado de antes, cuando el
     bloque del vídeo era estático y el texto caía debajo. Ahora que vuelve a
     ser sticky y a pantalla completa, esos 62vh ganaban por ir después y
     dejaban el panel a 503 px de 812: el texto se salía y el recorrido de
     scroll no cuadraba con el vídeo. */
}

/* --- Libro en tres dimensiones --------------------------------------------
   Sin librerias: tres caras y una perspectiva. No se dibuja la contraportada
   porque con el giro en reposo nunca se ve, y cada cara que se anade es una
   capa mas que componer en cada fotograma.

   El grosor sale de las paginas reales del libro: 225 en la novela y 173 en el
   poemario, a unos 0,062 mm por hoja mas las tapas. Un lomo inventado se nota.
   -------------------------------------------------------------------------- */
.libro3d { perspective: 1500px; width: 100%; max-width: var(--ancho, 220px) }
.libro3d__cuerpo {
  position: relative; width: 100%; aspect-ratio: var(--ratio, 155 / 235);
  transform-style: preserve-3d;
  transform: rotateX(var(--rx, 3deg)) rotateY(var(--ry, -21deg));
  transition: transform .55s var(--curva);
  will-change: transform;
}
.libro3d__cara { position: absolute; inset: 0; backface-visibility: hidden }
.libro3d__frente { transform: translateZ(calc(var(--grosor) / 2)) }
.libro3d__trasera { transform: rotateY(180deg) translateZ(calc(var(--grosor) / 2)) }
.libro3d__frente img, .libro3d__trasera img {
  width: 100%; height: 100%; object-fit: cover;
  box-shadow: 0 0 0 1px rgba(0,0,0,.35);
  -webkit-user-drag: none; user-select: none;
}
/* Se puede coger y girar. El cursor lo dice antes de que nadie lo intente. */
.libro3d { cursor: grab; touch-action: pan-y }
.libro3d[data-cogido] { cursor: grabbing }

/* El lomo: gira sobre su canto izquierdo y se centra en el eje Z. */
.libro3d__lomo {
  width: var(--grosor); left: 0; right: auto;
  transform-origin: 0 50%;
  transform: rotateY(-90deg) translateX(calc(var(--grosor) / -2));
  /* El lomo real, recortado del despliegue de imprenta. El degradado encima es
     el redondeo: sin el, el lomo se lee como una pegatina plana. */
  background:
    linear-gradient(90deg, rgba(0,0,0,.5), rgba(0,0,0,0) 42%, rgba(0,0,0,.55)),
    var(--lomo-img) center / 100% 100% no-repeat,
    #23201c;
}
/* El canto de las hojas. Las lineas finas son lo que hace que parezca papel y
   no un bloque de color: sin ellas el libro se lee como una caja. */
.libro3d__hojas {
  width: var(--grosor); left: auto; right: 0;
  transform-origin: 100% 50%;
  transform: rotateY(90deg) translateX(calc(var(--grosor) / 2));
  background:
    repeating-linear-gradient(90deg,
      #efe9dc 0 1px, #d9d2c2 1px 2px, #f4efe4 2px 3px);
}
.libro3d__sombra {
  position: absolute; left: 6%; right: -6%; bottom: -7%; height: 12%;
  background: radial-gradient(50% 50% at 50% 50%, rgba(0,0,0,.6), transparent 70%);
  filter: blur(9px); transform: translateZ(-1px);
  transition: opacity .55s var(--curva); opacity: .85;
}
.libro3d:hover .libro3d__sombra { opacity: 1 }

@media (prefers-reduced-motion: reduce) {
  .libro3d__cuerpo { transition: none; transform: rotateY(-14deg) }
}

/* --- Ramas al fondo de las frases ------------------------------------------
   Primera version: hojas sueltas que aparecian y se desvanecian mientras
   bajaban. Angel lo vio y dijo que era pobre, y lo era: un fundido no es
   viento. Nada se sujetaba a nada.

   Ahora son ramas. Cada hoja cuelga de su peciolo y ondea sobre EL PUNTO DONDE
   SE UNE AL TALLO —`transform-origin` en el nacimiento, no en el centro—, que
   es lo que hace que parezca una hoja y no un adorno flotando. La rama entera
   se mece mas despacio y con menos amplitud que sus hojas, como pasa de
   verdad: la masa grande manda el compas y las pequenas lo adornan.

   La colocacion va en `transform` y el meneo en `rotate`, que son propiedades
   independientes: si el meneo fuera tambien un transform, borraria la
   colocacion en cada fotograma.
   -------------------------------------------------------------------------- */
.frase-caja { position: relative; z-index: 0 }
.ambiente {
  position: absolute; inset: -26% 0 -12% 0; z-index: -1;
  pointer-events: none; overflow: hidden;
  color: #6E8F5E;                 /* verde de fronda, no verde de semaforo */
}
.mundo-papel .ambiente { color: #B08050 }   /* rama seca, ceniza tibia */

/* La cortina cuelga del borde de arriba y se derrama por detras del texto.
   `slice` en vez de `meet`: preferimos que se salga por abajo, donde el
   contenedor la recorta, a que encoja y deje la fronda flotando. */
.ambiente svg.cortina {
  position: absolute; left: 0; top: 0; width: 100%; height: 118%;
  opacity: .17;
}
.mundo-papel .ambiente svg.cortina { opacity: .13 }
.ambiente .rama    { transform-box: view-box;
                     animation: mecer var(--vaiven, 11s) ease-in-out infinite; }
.ambiente .hoja    { transform-box: fill-box; transform-origin: 0 50%;
                     animation: ondear var(--sopla, 4s) ease-in-out infinite;
                     animation-delay: var(--esp, 0s); }
.ambiente .tallo   { fill: none; stroke: currentColor; stroke-linecap: round }
.ambiente .lamina  { fill: currentColor }
.ambiente .nervio  { fill: none; stroke: #0A0A0B; stroke-width: .8; opacity: .25 }

/* Cuando el JS toma el mando apaga las keyframes: si no, la animacion y el
   muelle se pelean por la misma propiedad y la hoja tiembla. */
.ambiente[data-vivo] .hoja { animation: none }

@keyframes mecer  { 0%, 100% { rotate: -2.2deg } 50% { rotate: 2.6deg } }
@keyframes ondear { 0%, 100% { rotate: -4deg }   50% { rotate: 5.5deg } }

/* Con movimiento reducido no se mueve nada, y aqui ni se dibuja. */
@media (prefers-reduced-motion: reduce) {
  .ambiente { display: none }
}

/* En movil SI se ve: estaba apagada por debajo de 700 px y Angel la echaba en
   falta con razon, porque el fondo detras de los textos se quedaba en un negro
   plano. Lo que se recorta es la carga, no la presencia: en pantallas estrechas
   se ocultan las hojas alternas —la mitad de elementos que animar— y se sube un
   punto la opacidad, porque a ese tamano la cortina se lee menos. */
@media (max-width: 700px) {
  .ambiente .rama:nth-child(even) { display: none }
  .ambiente svg.cortina { opacity: .22 }
  .mundo-papel .ambiente svg.cortina { opacity: .16 }
}

