/* ============================================================================
   Sitio Juan Diego Vélez — capa de página
   Base tipográfica y escalas: brand/tokens.css
   Aquí vive la composición: bandas de color, vidrio, movimiento, fotos.
   ============================================================================ */
@import url("./brand/tokens.css?v=12");   /* ver la nota del ?v= en el HTML */

/* ─────────────────────────────────────────────────────────────────────────
   1. TEMA — lo elige la persona, no el sistema operativo
   El atributo data-tema lo fija un script inline en el <head> antes de
   pintar, para que no haya destello del tema equivocado al cargar.
   ───────────────────────────────────────────────────────────────────────── */
:root {
  /* Secuencia de fondos. El recorrido de la página:
     noche → papel → cielo → papel → noche */
  /* La noche subió de 19 % a 27 % de luminosidad. No es un capricho estético:
     el salto que tiene que recorrer la franja pasa de 78 a 70 puntos de L, y
     como además la franja es más larga, el cambio de color por píxel baja
     bastante. Es la mitad del truco para que el degradado se lea suave; la
     otra mitad es la longitud. */
  --f1: oklch(27%   0.042 258);   /* noche  */
  --f2: oklch(97.2% 0.009  85);   /* papel  */
  --f3: oklch(91%   0.048 245);   /* cielo  */
  --f4: var(--f2);
  --f5: var(--f1);

  /* Estos valores estaban COPIADOS de tokens.css en vez de referenciados, así
     que la «única fuente de verdad» tenía en realidad dos copias y cambiar la
     tinta en el brandbook no movía nada en el sitio. Ahora apuntan al token. */
  --tinta-sobre-claro:    var(--ink);
  --tinta-sobre-claro-2:  var(--ink-2);
  --tinta-sobre-claro-3:  var(--ink-3);
  --linea-sobre-claro:    var(--ink-line);
  --link-sobre-claro:     var(--blue-700);

  /* Relleno de avance de los libros en preparación. Va por tema y no con un
     solo tinte: sobre crema el texto es tinta oscura y el relleno puede
     aclarar; sobre banda oscura el texto es beige claro, así que aclarar el
     fondo le QUITA contraste. Con un tinte único el borde vivo caía a 3.44:1
     en oscuro. Medido: nunca baja de 4.5:1 en ninguno de los dos temas. */
  --prog-1: oklch(62% .17 258 / .12);
  --prog-2: oklch(62% .17 250 / .32);
  --prog-3: oklch(70% .15 245 / .50);

  --tinta-sobre-oscuro:   oklch(96% 0.008  85);
  --tinta-sobre-oscuro-2: oklch(78% 0.014 258);
  /* Subió de 64 % a 70 % al aclarar la noche. Con 64 % el antetítulo del hero
     se quedaba en 4.48:1 y no pasaba AA: aclarar el fondo le come contraste al
     texto tenue, así que las dos cosas se mueven juntas. Medido: 5.65:1 sobre
     la noche y 5.63:1 sobre la banda más clara del tema oscuro. */
  --tinta-sobre-oscuro-3: oklch(70% 0.018 258);
  --linea-sobre-oscuro:   oklch(96% 0.008 85 / .16);
  --link-sobre-oscuro:    oklch(82% 0.090 250);
}

/* En tema oscuro la secuencia se conserva como progresión, pero comprimida
   hacia abajo: sigue habiendo respiración clara/oscura, sin quemar la vista. */
html[data-tema="oscuro"] {
  --f1: oklch(18%   0.030 258);   /* también aclarada, por el mismo motivo */
  --f2: oklch(24%   0.024 258);
  --f3: oklch(27%   0.050 250);
  --f4: var(--f2);
  --f5: var(--f1);

  /* Más saturado pero no más claro: es lo que deja ver el avance sin comerse
     el contraste del texto beige. */
  --prog-1: oklch(50% .18 258 / .16);
  --prog-2: oklch(50% .19 252 / .34);
  --prog-3: oklch(54% .20 248 / .42);
}

/* ─────────────────────────────────────────────────────────────────────────
   2. BANDAS
   Cada sección declara sobre qué está apoyada. En tema oscuro todas las
   bandas pasan a texto claro, sin excepción.
   ───────────────────────────────────────────────────────────────────────── */
[data-sobre] { color: var(--txt); }        /* obligatorio: los descendientes
   heredan el color YA CALCULADO; si no se redeclara aquí, cambiar el token
   dentro del ámbito no tiene ningún efecto. */

[data-sobre="claro"] {
  --txt: var(--tinta-sobre-claro); --txt-2: var(--tinta-sobre-claro-2);
  --txt-3: var(--tinta-sobre-claro-3); --linea: var(--linea-sobre-claro);
  --link: var(--link-sobre-claro);
  --pie-foto: var(--tinta-sobre-claro-3);
  /* Estados del formulario. Van aquí y no en :root porque tienen que pasar AA
     sobre el panel de la banda, y ese panel cambia con ella. El rojo estaba
     escrito como literal en dos sitios y daba 3,61:1 sobre el panel claro:
     no pasaba AA para texto pequeño, que es el tamaño al que se usa. */
  --estado-error: oklch(54% .19 25);
  --estado-ok:    oklch(50% .13 155);
  --vidrio: color-mix(in oklch, white 46%, transparent);
  --vidrio-solido: color-mix(in oklch, white 82%, transparent);
  --vidrio-borde: color-mix(in oklch, white 78%, transparent);
  --vidrio-filo: color-mix(in oklch, white 96%, transparent);
}
[data-sobre="oscuro"], html[data-tema="oscuro"] [data-sobre] {
  --txt: var(--tinta-sobre-oscuro); --txt-2: var(--tinta-sobre-oscuro-2);
  --txt-3: var(--tinta-sobre-oscuro-3); --linea: var(--linea-sobre-oscuro);
  --link: var(--link-sobre-oscuro);
  --pie-foto: oklch(89% .03 84);          /* beige cálido, no blanco */
  --estado-error: oklch(68% .15 25);      /* sube la L o se apaga sobre oscuro */
  --estado-ok:    oklch(64% .13 155);
  --vidrio: color-mix(in oklch, white 7%, transparent);
  --vidrio-solido: color-mix(in oklch, oklch(20% .02 258) 72%, transparent);
  --vidrio-borde: color-mix(in oklch, white 15%, transparent);
  --vidrio-filo: color-mix(in oklch, white 26%, transparent);
}

.b1 { background: var(--f1); } .b2 { background: var(--f2); }
.b3 { background: var(--f3); } .b4 { background: var(--f4); }
.b5 { background: var(--f5); }

/* Transiciones: franjas sin texto donde el color cambia. Aquí viven los
   símbolos animados, y por eso ningún párrafo se apoya nunca sobre un
   degradado en movimiento. */
/* Un degradado lineal de dos paradas cambia de color a ritmo constante, así que
   al llegar a la banda plana su pendiente se corta de golpe: el ojo lee esa
   discontinuidad como una raya. Estas paradas siguen una curva suave
   (smoothstep, 3t² − 2t³), cuya pendiente vale cero en los dos extremos, de modo
   que el degradado nace y muere sin costura. La mezcla va en oklab porque en
   sRGB el paso de azul noche a crema se ensucia por el medio.
   Los colores de cada franja se pasan por --a y --b: así el degradado se
   escribe una sola vez. */
/* Longitud: cuanto más recorrido tiene el degradado, menos cambio de color por
   píxel y más suave se lee. Se mide en vh para que en una pantalla corta no se
   coma la página entera de puro aire. */
.paso { --a: var(--f1); --b: var(--f2);
  block-size: clamp(18rem, 42vh, 36rem); position: relative; overflow: clip;
  display: grid; place-items: center;
  background: linear-gradient(180deg,
    var(--a)                                       0%,
    color-mix(in oklab, var(--b)  2.8%, var(--a)) 10%,
    color-mix(in oklab, var(--b) 10.4%, var(--a)) 20%,
    color-mix(in oklab, var(--b) 21.6%, var(--a)) 30%,
    color-mix(in oklab, var(--b) 35.2%, var(--a)) 40%,
    color-mix(in oklab, var(--b) 50.0%, var(--a)) 50%,
    color-mix(in oklab, var(--b) 64.8%, var(--a)) 60%,
    color-mix(in oklab, var(--b) 78.4%, var(--a)) 70%,
    color-mix(in oklab, var(--b) 89.6%, var(--a)) 80%,
    color-mix(in oklab, var(--b) 97.2%, var(--a)) 90%,
    var(--b)                                     100%); }
.paso-1-2 { --a: var(--f1); --b: var(--f2); }
.paso-2-3 { --a: var(--f2); --b: var(--f3); }
.paso-3-4 { --a: var(--f3); --b: var(--f4); }
.paso-4-5 { --a: var(--f4); --b: var(--f5); }
/* Un degradado largo sobre 8 bits por canal siempre bandea. El grano del papel
   lo rompe: es ruido, que es justo lo que le falta al degradado para dispersar
   el escalón. place-self hace falta porque el padre es grid y centraría la capa
   hasta encogerla a cero. */
.paso::after { content: ""; position: absolute; inset: 0; place-self: stretch;
  pointer-events: none; opacity: .06; mix-blend-mode: overlay;
  background: url("./brand/assets/fondos/papel-1024.avif") 0 0 / 420px;
  /* El grano entra y sale desvanecido. Empezando de golpe en el borde superior
     aclaraba el primer píxel de la franja respecto al último de la banda, y esa
     diferencia mínima se lee como una línea recta de lado a lado. Donde más
     falta hace el grano es en el centro, que es donde el degradado bandea. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 14%, #000 86%, transparent 100%);
          mask-image: linear-gradient(180deg, transparent 0, #000 14%, #000 86%, transparent 100%); }
.paso svg { width: clamp(3rem, 7vw, 5rem); height: auto; opacity: .3; }

body { background: var(--f1); color: var(--tinta-sobre-oscuro);
       transition: background-color var(--dur-base) var(--ease-out); }

/* Grano de papel: solo en tema claro. Sobre fondo oscuro se ve como suciedad. */
html[data-tema="claro"] body::after {
  content: ""; position: fixed; inset: 0; z-index: 9998; pointer-events: none;
  background: url("./brand/assets/fondos/papel-1024.avif") 0 0 / 460px;
  mix-blend-mode: multiply; opacity: .05;
}

/* ─────────────────────────────────────────────────────────────────────────
   3. RETÍCULA
   ───────────────────────────────────────────────────────────────────────── */
.franja { display: grid;
  grid-template-columns:
    [full-start] minmax(var(--gutter),1fr)
    [content-start] min(70rem, 100% - var(--gutter)*2) [content-end]
    minmax(var(--gutter),1fr) [full-end]; }
.franja > * { grid-column: content; }
.franja > .ancho { grid-column: full; }

section.franja { padding-block: var(--section); scroll-margin-block-start: 5.5rem; }

/* ─────────────────────────────────────────────────────────────────────────
   4. CABECERA — vidrio real
   ───────────────────────────────────────────────────────────────────────── */
.top { position: sticky; inset-block-start: 0; z-index: 60;
  display: flex; align-items: center; gap: var(--space-s);
  padding: var(--space-2xs) var(--gutter);
  background: color-mix(in oklch, var(--f1) 72%, transparent);
  border-block-end: 1px solid transparent;
  transition: background-color var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out); }
@supports (backdrop-filter: blur(1px)) {
  .top { backdrop-filter: blur(18px) saturate(180%);
         -webkit-backdrop-filter: blur(18px) saturate(180%); } }
.top.flota { border-block-end-color: var(--linea-sobre-oscuro); }

.marca { display: flex; align-items: center; text-decoration: none;
         color: var(--tinta-sobre-oscuro); flex-shrink: 0; }
.marca svg { width: 44px; height: 44px; }

/* Dos grupos anclados a los extremos: las páginas junto a la marca, las
   secciones de la página actual pegadas a los controles. */
.top nav { display: flex; align-items: center; gap: var(--space-s);
           flex: 1; margin-inline-start: var(--space-m); }
.top nav .grupo { display: flex; align-items: center; gap: var(--space-s); }
.top nav .grupo-sec { margin-inline-start: auto; }
.top nav a { position: relative; font-family: var(--font-tech);
  font-size: var(--step--2); letter-spacing: .08em; text-transform: uppercase;
  text-decoration: none; color: oklch(70% .015 258); padding-block: .35em;
  transition: color var(--dur-fast) var(--ease-out); white-space: nowrap; }
.top nav a::after { content: ""; position: absolute; inset-inline: 0;
  inset-block-end: 0; block-size: 1px; background: currentColor;
  scale: 0 1; transform-origin: left;
  transition: scale var(--dur-base) var(--ease-fluid); }
.top nav a:hover { color: var(--tinta-sobre-oscuro); }
/* Sección activa: se ilumina y subraya. */
.top nav a[aria-current] { color: oklch(88% .10 250); }
.top nav a[aria-current]::after { scale: 1 1; }

/* Controles: idioma y tema */
.ctrl { display: flex; align-items: center; gap: var(--space-3xs); flex-shrink: 0; }
.idioma { display: flex; border: 1px solid var(--linea-sobre-oscuro);
          border-radius: var(--radius-full); overflow: hidden; }
.idioma button, .tema {
  font-family: var(--font-tech); font-size: var(--step--2); letter-spacing: .08em;
  border: 0; background: transparent; cursor: pointer; color: oklch(70% .015 258);
  transition: background var(--dur-fast) var(--ease-out), color var(--dur-fast) var(--ease-out); }
.idioma button { padding: .4em .8em; }
.idioma button[aria-pressed="true"] { background: var(--tinta-sobre-oscuro); color: var(--f1); }
.idioma button:hover:not([aria-pressed="true"]) { color: var(--tinta-sobre-oscuro); }

.tema { inline-size: 2.15rem; block-size: 2.15rem; display: grid; place-items: center;
        border: 1px solid var(--linea-sobre-oscuro); border-radius: var(--radius-full); }
.tema:hover { color: var(--tinta-sobre-oscuro); }
.tema svg { width: 1.05rem; height: 1.05rem; }
/* Solo se muestra el icono del tema al que se va a cambiar. */
html[data-tema="claro"] .tema .luna { display: none; }
html[data-tema="oscuro"] .tema .sol  { display: none; }

/* ─────────────────────────────────────────────────────────────────────────
   5. BILINGÜE — ambos idiomas en el HTML, el CSS oculta el que no toca.
   Sin JS la página sigue leyéndose (muestra los dos), nunca queda vacía.
   ───────────────────────────────────────────────────────────────────────── */
html[data-lang="es"] [lang="en"]:not(.idioma *) { display: none; }
html[data-lang="en"] [lang="es"]:not(.idioma *) { display: none; }

/* ─────────────────────────────────────────────────────────────────────────
   6. TIPOGRAFÍA DE PÁGINA
   ───────────────────────────────────────────────────────────────────────── */
.antetitulo { display: flex; align-items: center; gap: var(--space-2xs);
  font-family: var(--font-tech); font-size: var(--step--2); letter-spacing: .14em;
  text-transform: uppercase; color: var(--txt-3);
  margin: 0 0 var(--space-l); }          /* aire real hasta el titular */
.antetitulo svg { width: 1.15rem; height: 1.15rem; opacity: .8; }
.antetitulo::after { content: ""; flex: 1; height: 1px; background: var(--linea);
                     max-inline-size: 5rem; }

h1, h2, h3, h4 { color: var(--txt); }
h2 { margin-block-end: var(--space-m); }
.tit-ico { display: inline-grid; place-items: center; inline-size: 2.6rem;
  block-size: 2.6rem; border: 1px solid var(--linea); border-radius: var(--radius-m);
  margin-inline-end: var(--space-2xs); vertical-align: -.55em; color: var(--txt-2); }
.tit-ico svg { width: 1.4rem; height: 1.4rem; }

.entrada { font-size: var(--step-1); font-weight: 300; color: var(--txt-2);
           line-height: 1.55; max-inline-size: 48ch; }
p { color: var(--txt-2); }
.prosa p { max-inline-size: var(--measure); }
strong, b { font-weight: 600; color: var(--txt); }
em, i { font-style: italic; }
mark { background: none; color: var(--txt);
       border-block-end: 1px solid color-mix(in oklch, var(--link) 55%, transparent);
       padding-block-end: .06em; }
a { color: var(--link); }
:focus-visible { outline: 2px solid var(--link); outline-offset: 3px; }

/* ─────────────────────────────────────────────────────────────────────────
   7. VIDRIO — recetas del brandbook, ahora sensibles a la banda
   ───────────────────────────────────────────────────────────────────────── */
.vidrio { position: relative; isolation: isolate;
  background: var(--vidrio); border: 1px solid var(--vidrio-borde);
  border-radius: var(--radius-l); box-shadow: var(--shadow-m); }
@supports (backdrop-filter: blur(1px)) {
  .vidrio { backdrop-filter: blur(18px) saturate(170%);
            -webkit-backdrop-filter: blur(18px) saturate(170%); } }
@supports not (backdrop-filter: blur(1px)) { .vidrio { background: var(--vidrio-solido); } }
.vidrio::before { content: ""; position: absolute; inset: 0; padding: 1px;
  border-radius: inherit; pointer-events: none;
  background: linear-gradient(155deg, var(--vidrio-filo), transparent 45%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude; }

/* ─────────────────────────────────────────────────────────────────────────
   8. BOTONES — con halo para que no se pierdan sobre fondo oscuro
   ───────────────────────────────────────────────────────────────────────── */
/* Vidrio de verdad, no relleno plano. El primario dejó de ser una píldora
   maciza: ahora es la misma receta de .vidrio con un tinte de azul encima, y
   el texto va en la tinta de la banda en los dos casos. Por eso desapareció la
   regla que forzaba el color en tema oscuro: ya no hay ningún botón con el
   fondo invertido al que compensar. */
.btn { position: relative; display: inline-flex; align-items: center; gap: .55em;
  font-family: var(--font-tech); font-size: var(--step--1); letter-spacing: .05em;
  padding: .85em 1.5em; border-radius: var(--radius-full); text-decoration: none;
  isolation: isolate; color: var(--txt);
  background: color-mix(in oklch, var(--link) 16%, var(--vidrio));
  border: 1px solid color-mix(in oklch, var(--link) 28%, var(--vidrio-borde));
  box-shadow: var(--shadow-m);
  transition: transform var(--dur-base) var(--ease-fluid),
              background var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out); }
@supports (backdrop-filter: blur(1px)) {
  .btn { backdrop-filter: blur(16px) saturate(180%);
         -webkit-backdrop-filter: blur(16px) saturate(180%); } }
@supports not (backdrop-filter: blur(1px)) {
  .btn { background: color-mix(in oklch, var(--link) 16%, var(--vidrio-solido)); } }

/* Filo: la línea de luz del canto superior, lo que hace que el vidrio parezca
   tener grosor. Misma receta que .vidrio::before. */
.btn::before { content: ""; position: absolute; inset: 0; padding: 1px;
  border-radius: inherit; pointer-events: none;
  background: linear-gradient(150deg, var(--vidrio-filo), transparent 55%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
  -webkit-mask-composite: xor; mask-composite: exclude; }

/* El halo: capa desenfocada por debajo, más abierta y más tenue que antes. */
.btn::after { content: ""; position: absolute; inset: -45%; z-index: -1;
  border-radius: inherit; opacity: .38; filter: blur(20px);
  background: radial-gradient(closest-side,
    oklch(70% .17 255 / .7), oklch(70% .17 255 / .2) 62%, transparent);
  transition: opacity var(--dur-base) var(--ease-out); }
.btn:hover { transform: translateY(-2px);
  background: color-mix(in oklch, var(--link) 24%, var(--vidrio));
  border-color: color-mix(in oklch, var(--link) 45%, var(--vidrio-borde));
  box-shadow: var(--shadow-l); }
.btn:hover::after { opacity: .85; }

/* El secundario es el mismo vidrio sin el tinte azul: se distingue por peso,
   no por forma. */
.btn.sec { background: var(--vidrio); border-color: var(--vidrio-borde); }
.btn.sec::after { opacity: .16; }
.btn.sec:hover { background: color-mix(in oklch, var(--link) 12%, var(--vidrio));
  border-color: color-mix(in oklch, var(--link) 34%, var(--vidrio-borde)); }
.acciones { display: flex; gap: var(--space-xs); flex-wrap: wrap; margin-block-start: var(--space-l); }

.chip { font-family: var(--font-tech); font-size: var(--step--2); letter-spacing: .05em;
  padding: .45em .9em; border-radius: var(--radius-full); border: 1px solid var(--linea);
  text-decoration: none; color: var(--txt-2); display: inline-flex; align-items: center;
  gap: .45em; transition: border-color var(--dur-fast) var(--ease-out),
  color var(--dur-fast) var(--ease-out); }
.chip:hover { border-color: var(--link); color: var(--link); }
.enlaces { display: flex; flex-wrap: wrap; gap: var(--space-2xs); }

/* ─────────────────────────────────────────────────────────────────────────
   9. FOTOS — montadas sobre vidrio, nunca a sangre
   ───────────────────────────────────────────────────────────────────────── */
.foto { position: relative; border-radius: var(--radius-xl); overflow: hidden;
  border: 1px solid var(--vidrio-borde); box-shadow: var(--shadow-l);
  background: var(--vidrio); isolation: isolate; }
.foto img { display: block; inline-size: 100%; block-size: auto; border-radius: inherit; }
/* El rótulo sobre vidrio dentro de la foto se retiró: ahora todos los pies van
   fuera del marco (.foto-pie). Se quitaron también sus reglas, que quedaban
   sin ningún elemento al que aplicarse. */
/* Halo detrás del retrato: es lo que hace que el vidrio tenga algo que refractar. */
.foto-halo { position: relative; isolation: isolate; }
.foto-halo::before { content: ""; position: absolute; inset: -12%; z-index: -1;
  border-radius: 50%; filter: blur(16px);
  background: radial-gradient(closest-side,
    oklch(62% .17 262 / .5), oklch(62% .17 262 / .12) 58%, transparent); }

/* ─────────────────────────────────────────────────────────────────────────
   10. PORTADA
   ───────────────────────────────────────────────────────────────────────── */
/* El hero NO usa la retícula .franja: tiene su propia rejilla de dos columnas.
   Mezclar ambas hacía que los hijos buscaran una línea «content» inexistente y
   la portada se apilaba. El ancho se limita con padding para que el fondo de
   la banda siga sangrando a todo lo ancho. */
/* Altura mínima. Sin ella, la portada de Contacto —que tiene mucho menos
   contenido y ni foto— medía 391 px contra los 750 px de Inicio, y como la
   franja de transición mide lo mismo en las dos (va en vh), allí el degradado
   ocupaba casi tanto como la banda y se leía como si el azul estuviera
   cortado. El degradado era idéntico; lo que no lo era, la banda. */
.hero { position: relative; overflow: clip; isolation: isolate;
  min-block-size: min(68vh, 44rem);
  padding-block: clamp(3rem, 9vh, 6rem) var(--section);
  padding-inline: max(var(--gutter), (100% - 70rem) / 2);
  display: grid; gap: clamp(var(--space-l), 5vw, var(--space-3xl));
  grid-template-columns: repeat(auto-fit, minmax(min(19rem,100%), 1fr));
  align-items: center; }
.hero h1 { font-size: var(--step-5); font-weight: 250; margin-block-end: var(--space-m); }
.hero .antetitulo { margin-block-end: var(--space-m); }
/* La figura ya no se corta: se contiene entera y se apoya en su propio halo.
   Baja de opacidad respecto al original porque ahora comparte sitio con la
   trama de fórmulas y las dos juntas ensuciaban el fondo. */
.hero-fig { position: absolute; z-index: -2; inset-inline-end: -6%;
  inset-block-start: 50%; translate: 0 -50%;
  inline-size: min(34rem, 52%); opacity: .3; pointer-events: none; }
.hero-halo { position: absolute; z-index: -3; inline-size: min(38rem, 62%);
  aspect-ratio: 1; inset-inline-end: -10%; inset-block-start: 50%; translate: 0 -50%;
  background: var(--glow-blue); filter: blur(14px); pointer-events: none; }

/* Trama de fórmulas: se concentra en la mitad derecha, detrás del retrato, y
   se apaga antes de llegar al texto para no competir con la lectura. */
.hero-trama { position: absolute; inset: 0; z-index: -2;
  inline-size: 100%; block-size: 100%; opacity: .18; pointer-events: none;
  /* Dos máscaras que se cruzan (mask-composite: intersect):
     · La horizontal deja limpia la columna de texto por construcción. Una
       radial centrada en la foto no servía: escondía las fórmulas justo detrás
       de ella y dejaba escapar las de la izquierda sobre el texto.
     · La vertical apaga la trama ANTES del canto inferior del hero. Sin ella la
       capa moría de golpe justo donde empieza la franja de transición, y ese
       corte se veía como una raya de lado a lado —era la mitad de la «división»
       del degradado que se notaba; la otra mitad era el grano del .paso. */
  -webkit-mask-image: linear-gradient(90deg, transparent 42%, #000 70%),
                      linear-gradient(180deg, #000 55%, transparent 94%);
          mask-image: linear-gradient(90deg, transparent 42%, #000 70%),
                      linear-gradient(180deg, #000 55%, transparent 94%);
  -webkit-mask-composite: source-in;
          mask-composite: intersect; }
/* Plegado a una columna no hay foto a la derecha detrás de la cual esconderse:
   la trama cae justo bajo el texto, así que baja hasta ser solo textura. */
@media (max-width: 60rem) { .hero-trama { opacity: .07; } }

/* ─────────────────────────────────────────────────────────────────────────
   11. FICHAS Y REJILLAS
   ───────────────────────────────────────────────────────────────────────── */
.rejilla { display: grid; gap: var(--space-m);
  grid-template-columns: repeat(auto-fit, minmax(min(17rem,100%), 1fr)); }
.ficha { padding: var(--space-m); display: flex; flex-direction: column;
         gap: var(--space-2xs); }
.ficha h3 { margin: 0; font-size: var(--step-1); }
.ficha p { margin: 0; font-size: var(--step--1); }
.ficha .ico { inline-size: 2rem; block-size: 2rem; color: var(--txt-3);
              margin-block-end: var(--space-2xs); }
.ico { inline-size: 1.5em; block-size: 1.5em; flex-shrink: 0; }

/* ─────────────────────────────────────────────────────────────────────────
   12. LÍNEA DE TIEMPO — se dibuja sola conforme se baja
   ───────────────────────────────────────────────────────────────────────── */
.linea-t { position: relative; padding-inline-start: var(--space-xl);
           list-style: none; margin: 0; }
.linea-t::before { content: ""; position: absolute; inset-block: 0;
  inset-inline-start: 6px; inline-size: 1px; background: var(--linea); }
/* El trazo que avanza. scroll-driven: corre en el compositor, sin listeners. */
.linea-t::after { content: ""; position: absolute; inset-block-start: 0;
  inset-inline-start: 6px; inline-size: 1px; block-size: 100%;
  background: linear-gradient(180deg, var(--link), color-mix(in oklch, var(--link) 20%, transparent));
  transform-origin: top; scale: 1 0; }
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .linea-t::after { animation: crecer linear both;
      animation-timeline: view(); animation-range: entry 85% exit 15%; }
    @keyframes crecer { to { scale: 1 1; } }
  }
}
@supports not (animation-timeline: view()) { .linea-t::after { scale: 1 1; } }

.linea-t li { position: relative; padding-block: var(--space-s); }
.linea-t li::before { content: ""; position: absolute; inset-inline-start: calc(-1 * var(--space-xl) + 2px);
  inset-block-start: calc(var(--space-s) + .55em); inline-size: 9px; block-size: 9px;
  border-radius: 50%; background: var(--f2); border: 1px solid var(--link); }
html[data-tema="oscuro"] .linea-t li::before, [data-sobre="oscuro"] .linea-t li::before {
  background: var(--f1); }
.linea-t .anio { font-family: var(--font-tech); font-size: var(--step--2);
  color: var(--txt-3); letter-spacing: .08em; display: block; margin-block-end: .25em; }
.linea-t .t { font-size: var(--step-0); color: var(--txt); margin: 0 0 .25em; }
.linea-t .r { font-family: var(--font-tech); font-size: var(--step--2);
  color: var(--txt-2); margin: 0; line-height: 1.55; }

/* ─────────────────────────────────────────────────────────────────────────
   13. LISTAS DE DOCUMENTOS
   ───────────────────────────────────────────────────────────────────────── */
details.docs { border: 1px solid var(--linea); border-radius: var(--radius-m);
  padding: var(--space-3xs) var(--space-s); margin-block-end: var(--space-2xs); }
details.docs > summary { cursor: pointer; font-family: var(--font-tech);
  font-size: var(--step--1); padding-block: var(--space-2xs); list-style: none;
  display: flex; align-items: center; gap: var(--space-2xs); color: var(--txt); }
details.docs > summary::-webkit-details-marker { display: none; }
details.docs > summary::before { content: "+"; color: var(--link); inline-size: 1em; }
details.docs[open] > summary::before { content: "–"; }
details.docs .cuenta { margin-inline-start: auto; color: var(--txt-3); font-size: var(--step--2); }
.docs ol { list-style: none; margin: var(--space-2xs) 0 var(--space-s); padding: 0;
           border-block-start: 1px solid var(--linea); }
.docs li { border-block-end: 1px solid var(--linea); }
.docs li a { display: flex; gap: var(--space-s); align-items: baseline;
  padding-block: .6rem; text-decoration: none; color: var(--txt); }
.docs li a:hover { color: var(--link); }
.docs li .f { margin-inline-start: auto; font-family: var(--font-tech);
  font-size: var(--step--2); color: var(--txt-3); white-space: nowrap;
  font-variant-numeric: tabular-nums; }

/* ─────────────────────────────────────────────────────────────────────────
   14. LISTAS SIMPLES
   ───────────────────────────────────────────────────────────────────────── */
.lista { list-style: none; padding: 0; margin: 0; }
.lista li { padding-block: var(--space-xs); border-block-end: 1px solid var(--linea); }
.lista .n { font-size: var(--step-0); color: var(--txt); }
.lista .t { font-family: var(--font-tech); font-size: var(--step--2);
  color: var(--txt-2); display: block; margin-block-start: .2em; line-height: 1.5; }

/* ─────────────────────────────────────────────────────────────────────────
   15. FORMULARIO
   ───────────────────────────────────────────────────────────────────────── */
.campo { display: grid; gap: var(--space-3xs); margin-block-end: var(--space-s); }
.campo label { font-family: var(--font-tech); font-size: var(--step--2);
  letter-spacing: .08em; text-transform: uppercase; color: var(--txt-3); }
.campo input, .campo textarea {
  font-family: var(--font-body); font-size: var(--step-0); color: var(--txt);
  background: var(--vidrio); border: 1px solid var(--linea);
  border-radius: var(--radius-m); padding: .7em .9em; inline-size: 100%;
  transition: border-color var(--dur-fast) var(--ease-out); }
.campo input:focus, .campo textarea:focus { border-color: var(--link); }
.campo textarea { resize: vertical; min-block-size: 7rem; field-sizing: content; }
.campo .error { font-family: var(--font-tech); font-size: var(--step--2);
                color: var(--estado-error); }
[aria-invalid="true"] { border-color: var(--estado-error) !important; }
.oculto { position: absolute; inset-inline-start: -9999px; }
.estado { font-family: var(--font-tech); font-size: var(--step--1); color: var(--txt-2); }
.estado:empty { display: none; }          /* sin mensaje no reserva aire */
.estado.ok  { color: var(--estado-ok); }
.estado.mal { color: var(--estado-error); }
/* El botón deshabilitado mientras se envía. Sin cursor de "prohibido": no está
   prohibido, está ocupado. */
.btn[aria-busy="true"], .btn:disabled { opacity: .62; cursor: default; }

/* ─────────────────────────────────────────────────────────────────────────
   16. VÍDEO Y AUDIO
   ───────────────────────────────────────────────────────────────────────── */
.video { position: relative; aspect-ratio: 16/9; border-radius: var(--radius-l);
  overflow: hidden; border: 1px solid var(--linea); }
.video iframe { position: absolute; inset: 0; inline-size: 100%; block-size: 100%; border: 0; }

/* Portada del video: lo que se ve antes de que haya ningún contacto con Google.
   Fondo local, nunca la miniatura de YouTube —pedirla ya sería la petición a un
   tercero que esta portada existe para evitar. */
.video-portada { position: absolute; inset: 0; inline-size: 100%; block-size: 100%;
  display: grid; place-content: center; justify-items: center; gap: var(--space-2xs);
  border: 0; cursor: pointer; padding: var(--space-l); text-align: center;
  color: var(--tinta-sobre-oscuro);
  background: linear-gradient(180deg,
                color-mix(in oklab, var(--f1) 82%, transparent),
                color-mix(in oklab, var(--f1) 94%, transparent)),
              url("./brand/assets/fondos/caustica-1920.avif") center / cover;
  transition: background var(--dur-base) var(--ease-out); }
.video-portada:hover { background: linear-gradient(180deg,
                color-mix(in oklab, var(--f1) 72%, transparent),
                color-mix(in oklab, var(--f1) 88%, transparent)),
              url("./brand/assets/fondos/caustica-1920.avif") center / cover; }
.video-play { inline-size: clamp(3rem, 8vw, 4.2rem); block-size: auto;
  color: var(--tinta-sobre-oscuro); opacity: .85;
  transition: transform var(--dur-base) var(--ease-fluid), opacity var(--dur-fast); }
.video-portada:hover .video-play { transform: scale(1.06); opacity: 1; }
.video-txt { font-family: var(--font-display); font-size: var(--step-1);
  font-weight: 300; line-height: 1.25; }
.video-nota { font-family: var(--font-tech); font-size: var(--step--2);
  line-height: 1.5; color: var(--tinta-sobre-oscuro-3);
  max-inline-size: 40ch; }
.pistas { display: grid; gap: var(--space-2xs);
  grid-template-columns: repeat(auto-fit, minmax(min(14rem,100%), 1fr)); }
.pista { display: flex; align-items: center; gap: var(--space-2xs);
  font-family: var(--font-tech); font-size: var(--step--2);
  padding: var(--space-2xs) var(--space-xs); border: 1px solid var(--linea);
  border-radius: var(--radius-m); text-decoration: none; color: var(--txt); }
.pista .ico { inline-size: 1.3rem; block-size: 1.3rem; color: var(--txt-3); }
.pista:hover { border-color: var(--link); color: var(--link); }

/* ─────────────────────────────────────────────────────────────────────────
   17. MOVIMIENTO — aparición única, no repetida
   El bloqueo lo hace el script: al aparecer añade .visto y deja de observar.
   ───────────────────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: no-preference) {
  .surge { opacity: 0; translate: 0 1.4rem;
    transition: opacity var(--dur-slow) var(--ease-fluid),
                translate var(--dur-slow) var(--ease-fluid); }
  .surge.visto { opacity: 1; translate: 0 0; }
  /* Escalonado dentro de un grupo */
  .surge[style*="--i"] { transition-delay: calc(var(--i) * 70ms); }
}
/* Sin animación disponible, el contenido está visible. Nunca al revés. */
@media (prefers-reduced-motion: reduce) { .surge { opacity: 1; translate: none; } }

/* Fotos: entran con un fundido fotográfico ————————————————————————
   .revela sustituye el fundido corto de .surge por uno más largo y con
   desenfoque, que es lo que hace que una foto parezca «revelarse» en vez de
   simplemente aparecer. Se apoya en el mismo observador, así que también
   ocurre una sola vez y queda fija.
   Sin transform a propósito: .foto ya usa transform para el gesto de hover y
   para el filo, y encadenar los dos en el mismo elemento obliga a pelear con
   la especificidad cada vez que se toca uno. Con opacidad y desenfoque basta. */
@media (prefers-reduced-motion: no-preference) {
  .surge.revela { opacity: 0; translate: none; filter: blur(14px);
    transition: opacity 950ms var(--ease-fluid),
                filter  950ms var(--ease-fluid); }
  .surge.revela.visto { opacity: 1; filter: blur(0); }
}

/* Titulares que se arman palabra a palabra ————————————————————————
   El bloque deja de desvanecerse como un todo: quien aparece son las palabras.
   Si el titular siguiera además con el fundido de .surge, se verían las dos
   animaciones encima y el arranque quedaría turbio. */
@media (prefers-reduced-motion: no-preference) {
  .armar.surge { opacity: 1; translate: none; }
  .armar .pal { display: inline-block; opacity: 0;
    translate: 0 .42em; filter: blur(7px);
    transition: opacity 620ms var(--ease-fluid),
                translate 620ms var(--ease-fluid),
                filter 620ms var(--ease-fluid);
    transition-delay: calc(var(--w) * 58ms); }
  .armar.visto .pal { opacity: 1; translate: 0 0; filter: blur(0); }
}

/* Símbolos de las franjas de transición */
@media (prefers-reduced-motion: no-preference) {
  .paso svg { animation: girar 46s linear infinite; }
  .paso.inverso svg { animation-direction: reverse; }
  @keyframes girar { to { rotate: 360deg; } }
}

/* ─────────────────────────────────────────────────────────────────────────
   18. FÓRMULAS AL TOCAR
   ───────────────────────────────────────────────────────────────────────── */
#formulas { position: fixed; inset: 0; z-index: 9999; pointer-events: none;
            overflow: hidden; }
.formula { position: absolute; font-family: var(--font-display); font-style: italic;
  font-size: clamp(1.1rem, 2.4vw, 1.7rem); white-space: nowrap;
  color: var(--link); opacity: 0; will-change: transform, opacity; }

/* Halo cian al tocar. Sin mix-blend-mode a propósito: en «screen» se vería
   sobre las bandas oscuras y desaparecería sobre el crema, y el efecto tiene
   que funcionar en las cinco bandas. Con alfa baja se comporta igual en todas. */
.toque { position: absolute; inline-size: 20rem; block-size: 20rem;
  border-radius: 50%; pointer-events: none; opacity: 0;
  will-change: transform, opacity; filter: blur(5px);
  background: radial-gradient(closest-side,
    oklch(88% .12 205 / .40),
    oklch(80% .14 210 / .16) 46%,
    transparent 72%); }

/* ─────────────────────────────────────────────────────────────────────────
   19. PIE
   ───────────────────────────────────────────────────────────────────────── */
footer.pie { padding-block: var(--space-2xl) var(--space-xl);
  border-block-start: 1px solid var(--linea); display: grid; gap: var(--space-m);
  grid-template-columns: repeat(auto-fit, minmax(min(15rem,100%), 1fr)); }
footer.pie p { font-family: var(--font-tech); font-size: var(--step--2);
               line-height: 1.7; margin: 0; }

.saltar { position: absolute; inset-inline-start: -999px; background: var(--txt);
  color: var(--f1); padding: var(--space-2xs) var(--space-s);
  border-radius: var(--radius-m); font-family: var(--font-tech);
  font-size: var(--step--1); z-index: 100; }
.saltar:focus { inset-inline-start: var(--space-s); inset-block-start: var(--space-s); }

/* ─────────────────────────────────────────────────────────────────────────
   20. MÓVIL — decisión de página, por eso @media y no @container
   ───────────────────────────────────────────────────────────────────────── */
@media (max-width: 60rem) {
  .top { flex-wrap: wrap; row-gap: var(--space-3xs); }
  .ctrl { margin-inline-start: auto; }
  /* flex: none es obligatorio. El flex: 1 del menú en escritorio trae
     flex-basis: 0, que en el eje principal gana al inline-size: 100% y dejaba
     el menú incrustado en la fila de la marca en vez de bajarlo. */
  .top nav { order: 3; flex: none; inline-size: 100%; margin-inline-start: 0;
    flex-wrap: nowrap; overflow-x: auto; scrollbar-width: none;
    padding-block-end: var(--space-3xs); }
  .top nav::-webkit-scrollbar { display: none; }
  /* Plegado en una sola fila que se desliza: los dos grupos vuelven a estar
     juntos, así que el margen automático sobra y el separador sí hace falta. */
  .top nav .grupo-sec { margin-inline-start: 0; }
  .top nav .sep { display: block; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   21. REFINAMIENTOS
   Van al final a propósito: son ajustes sobre el sistema base, y al ir después
   ganan por orden de cascada sin subir la especificidad.
   ═══════════════════════════════════════════════════════════════════════════ */

/* — Separador del menú. Con los dos grupos en extremos opuestos no separa nada,
   así que solo aparece cuando el menú se pliega en móvil y vuelven a quedar
   pegados (ver el bloque de móvil). */
.top nav .sep { display: none; inline-size: 1px; block-size: 1.1em; align-self: center;
                background: var(--linea-sobre-oscuro); margin-inline: var(--space-3xs); }

/* — Profundidad en las bandas oscuras ————————————————————————————————
   El azul plano se leía como un bloque. Dos resplandores muy abiertos le dan
   volumen sin ensuciar el texto: van por debajo del contenido y no se animan. */
.b1, .b5 { position: relative; isolation: isolate; }
.b1::before, .b5::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background:
    radial-gradient(110% 70% at 78% 0%,  oklch(48% .12 258 / .38), transparent 62%),
    radial-gradient(90%  60% at 8%  100%, oklch(40% .10 268 / .30), transparent 65%);
  /* El segundo resplandor está centrado justo en el borde inferior, así que
     llegaba a la franja de transición a plena intensidad y ahí se cortaba en
     seco: media raya del corte que se veía venía de aquí, no del degradado.
     La máscara lo apaga antes de tocar el canto. */
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 16%, #000 84%, transparent 100%);
          mask-image: linear-gradient(180deg, transparent 0, #000 16%, #000 84%, transparent 100%);
}
html[data-tema="oscuro"] .b1::before, html[data-tema="oscuro"] .b5::before {
  background:
    radial-gradient(110% 70% at 78% 0%,  oklch(38% .10 258 / .34), transparent 62%),
    radial-gradient(90%  60% at 8%  100%, oklch(30% .09 268 / .30), transparent 65%);
}

/* — Marcador de sección ————————————————————————————————————————————
   La caja cuadrada junto al h2 flotaba desalineada. Ahora es un disco menor
   apoyado en el color de acento.

   Alinearlo con vertical-align no funcionaba: el desplazamiento en em depende
   del cuerpo de cada titular, así que lo que cuadraba en el h2 de sección se
   descolgaba en el h3 de las fichas. Con el titular en flex el disco se centra
   contra la PRIMERA línea usando la unidad lh —la altura de línea real del
   titular—, y así queda igual de bien en cualquier cuerpo y aunque el texto
   pase a dos líneas. */
h2:has(> .tit-ico), h3:has(> .tit-ico) {
  display: flex; align-items: flex-start; gap: var(--space-2xs); }
.tit-ico { inline-size: 2.1rem; block-size: 2.1rem; border-radius: 50%;
  border: 1px solid color-mix(in oklch, var(--link) 40%, transparent);
  background: color-mix(in oklch, var(--link) 9%, transparent);
  color: var(--link); flex: none; vertical-align: baseline;
  margin-inline-end: 0; margin-block-start: calc((1lh - 2.1rem) / 2); }
.tit-ico svg { inline-size: 1.1rem; block-size: 1.1rem; }

/* — Antetítulo: la regla de la derecha crece con la sección ——————————— */
.antetitulo::after { max-inline-size: 8rem; opacity: .8; }

/* — Titulares con más aplomo ——————————————————————————————————————— */
.hero h1 { letter-spacing: -.028em; line-height: 1.02; text-wrap: balance; }
h2 { letter-spacing: -.022em; text-wrap: balance; }

/* — Vidrio: reacción al puntero ————————————————————————————————————
   Solo transform y opacidad del borde: nada que fuerce repintado del blur. */
.vidrio, .ficha, .foto {
  transition: transform var(--dur-base) var(--ease-fluid),
              border-color var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out); }
.vidrio:hover, .ficha.vidrio:hover {
  transform: translateY(-3px);
  border-color: color-mix(in oklch, var(--link) 45%, var(--vidrio-borde));
  box-shadow: var(--shadow-l); }
/* El formulario no se mueve al pasar por encima: escribir sobre algo que salta
   es incómodo. */
form.vidrio:hover { transform: none; }

/* — El formulario tiene que leerse como una superficie ————————————————
   Con el vidrio normal (blanco al 46 %) sobre la banda crema el panel casi
   desaparecía: se veía el borde y poco más. Pasa a la receta sólida, que ya
   existía en el sistema, y los campos se hunden un punto respecto al panel.
   Ese contraste entre panel y campo es lo que hace que un campo parezca un
   campo y no un rectángulo dibujado. Va con tokens de banda, así que funciona
   igual en tema claro y en oscuro. */
form.vidrio { background: var(--vidrio-solido);
  border-color: color-mix(in oklch, var(--txt) 14%, transparent);
  box-shadow: var(--shadow-l); }
form.vidrio .campo input, form.vidrio .campo textarea {
  background: color-mix(in oklch, var(--txt) 5%, transparent);
  border-color: color-mix(in oklch, var(--txt) 20%, transparent); }
form.vidrio .campo input:focus, form.vidrio .campo textarea:focus {
  background: color-mix(in oklch, var(--txt) 2%, transparent);
  border-color: var(--link); }

/* — Fotos: filo interior que remata el canto del vidrio ————————————— */
.foto::after { content: ""; position: absolute; inset: 0; border-radius: inherit;
  pointer-events: none; box-shadow: inset 0 1px 0 color-mix(in oklch, white 34%, transparent),
  inset 0 0 0 1px color-mix(in oklch, white 10%, transparent); }
.foto:hover { transform: translateY(-3px) scale(1.004); }

/* — Chips más legibles ————————————————————————————————————————————— */
.chip { background: color-mix(in oklch, var(--txt) 4%, transparent);
        color: var(--txt-2); }
.chip:hover { background: color-mix(in oklch, var(--link) 12%, transparent);
              color: var(--link); }

/* — Línea de tiempo: el nodo se enciende cuando su entrada aparece ————— */
.linea-t li::before { transition: background var(--dur-base) var(--ease-out),
  box-shadow var(--dur-base) var(--ease-out); }
.linea-t li.visto::before { background: var(--link);
  box-shadow: 0 0 0 4px color-mix(in oklch, var(--link) 18%, transparent); }

/* — Símbolos de las franjas de transición ————————————————————————— */
.paso svg { opacity: .42; }
@media (prefers-reduced-motion: no-preference) {
  .paso svg { animation: girar 60s linear infinite, flotar 9s var(--ease-in-out) infinite; }
  @keyframes flotar { 50% { translate: 0 -8px; } }
}

/* — Detalles plegables: el sumario se comporta como control ——————————— */
details.docs { transition: border-color var(--dur-fast) var(--ease-out); }
details.docs:hover { border-color: color-mix(in oklch, var(--link) 35%, var(--linea)); }
details.docs[open] { background: color-mix(in oklch, var(--txt) 3%, transparent); }

/* — Selección y barra de desplazamiento a juego ————————————————————— */
::selection { background: color-mix(in oklch, var(--link) 28%, transparent); color: var(--txt); }
/* — Barra de desplazamiento de vidrio ————————————————————————————————
   Firefox solo entiende scrollbar-color/scrollbar-width, así que se le deja su
   versión. Chrome y Safari usan los pseudoelementos, que permiten el pulgar
   redondeado y translúcido.
   El color es un gris azulado neutro y NO una variable de banda: la barra vive
   en el borde de la ventana, donde detrás puede haber crema o noche según por
   dónde se vaya, así que tiene que funcionar sobre las dos.
   El truco del borde transparente con background-clip: content-box es lo que
   deja aire alrededor del pulgar y hace que flote en vez de tocar el canto. */
html { scrollbar-color: oklch(62% .03 258 / .45) transparent;
       scrollbar-width: thin; }
::-webkit-scrollbar { inline-size: 13px; block-size: 13px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  border-radius: var(--radius-full);
  border: 3px solid transparent;
  background: oklch(62% .03 258 / .38);
  background-clip: content-box;
  transition: background var(--dur-base) var(--ease-out); }
::-webkit-scrollbar-thumb:hover {
  background: oklch(68% .04 250 / .62); background-clip: content-box; }
::-webkit-scrollbar-corner { background: transparent; }

/* — Foco visible también sobre fondo oscuro ————————————————————————— */
:focus-visible { outline: 2px solid var(--link); outline-offset: 3px;
  border-radius: var(--radius-s); }
[data-sobre="oscuro"] :focus-visible { outline-color: oklch(88% .10 250); }

/* — Enlaces dentro de prosa ————————————————————————————————————————— */
.prosa a, .lista a, .docs a { text-underline-offset: .2em;
  text-decoration-thickness: 1px; }

/* — Rendimiento: diferir el pintado de lo que está fuera de pantalla ————
   Solo en bloques bajo el pliegue y con tamaño estimado, porque sin
   contain-intrinsic-size el elemento colapsa a 0 y el scroll da saltos.
   NO se aplica al hero ni a la primera sección: ahí retrasaría el pintado. */
#notas, #divulgacion, #musica, #tesis,
#cursos > article:nth-of-type(n+3) {
  content-visibility: auto;
  contain-intrinsic-size: auto none auto 640px;
}

/* ═══════════════════════════════════════════════════════════════════════════
   22. PIEZAS NUEVAS
   ═══════════════════════════════════════════════════════════════════════════ */

/* — Pie de foto fuera del marco ————————————————————————————————————
   El rótulo deja de apoyarse sobre la imagen y baja debajo. El color lo pone
   la banda: beige sobre fondo oscuro, tinta apagada sobre fondo claro. Un
   beige fijo desaparecería sobre el crema. */
.foto-pie { margin: 0; display: grid; gap: var(--space-xs); }
/* En cursiva y con la Newsreader, no con la mono: de la mono no hay itálica
   entre las fuentes autohospedadas y el navegador la inclinaría por su cuenta,
   que es una falsificación geométrica y se nota. La Newsreader itálica sí está
   cargada (newsreader-italic-400) y es una itálica dibujada. */
.foto-pie > figcaption, .hero-media > figcaption {
  font-family: var(--font-display); font-style: italic;
  font-size: var(--step--1); line-height: 1.55; color: var(--pie-foto);
  padding-inline-start: var(--space-3xs); }
/* Itálica dentro de itálica: se invierte, que es como se ha resuelto siempre
   en tipografía. Si no, «Física y Matemáticas» no se distinguiría del resto. */
.foto-pie > figcaption em, .hero-media > figcaption em { font-style: normal; }

/* — Portada de Académico ————————————————————————————————————————————
   La foto sale del marco de vidrio y se disuelve hacia la izquierda contra la
   banda. Al no tener canto puede crecer y acercarse al texto sin encajonarlo. */
.hero-media { margin: 0; display: grid; gap: var(--space-xs);
              margin-inline-end: calc(-1 * var(--gutter)); }
.hero-media img { display: block; inline-size: 100%; block-size: auto;
  border-radius: var(--radius-l);
  -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 40%);
          mask-image: linear-gradient(90deg, transparent 0, #000 40%); }
.hero-media > figcaption { padding-inline-start: 40%; }   /* bajo la parte opaca */

/* La rejilla de dos columnas solo por encima del punto de plegado. Con
   grid-template-columns fijo y min(20rem,100%) las dos columnas valdrían 100%
   en móvil y la página se saldría a lo ancho. */
@media (min-width: 60rem) {
  .hero.hero-foto { grid-template-columns: 1fr 1.3fr; }
}
@media (max-width: 60rem) {
  .hero-media { margin-inline-end: 0; }
  .hero-media img { -webkit-mask-image: none; mask-image: none; }
  .hero-media > figcaption { padding-inline-start: var(--space-3xs); }
}

/* — Bloque plegable con «Ver más» ——————————————————————————————————
   overflow: clip y no hidden a propósito. hidden convierte la caja en
   contenedor de desplazamiento, y entonces el animation-timeline: view() de la
   línea de tiempo se resuelve contra ella —que nunca se desplaza— y el trazo
   se queda congelado. clip recorta sin crear ese contenedor.
   La altura la anima el script en píxeles; aquí solo vive el estado cerrado. */
.plegado-caja { position: relative; overflow: clip;
  max-block-size: var(--alto-plegado, 34rem);
  transition: max-block-size var(--dur-slow) var(--ease-fluid); }
.plegado-caja::after { content: ""; position: absolute; inset-inline: 0;
  inset-block-end: 0; block-size: 12rem; pointer-events: none;
  transition: opacity var(--dur-base) var(--ease-out);
  background: linear-gradient(180deg,
    transparent                                                    0%,
    color-mix(in oklab, var(--fondo-plegado, var(--f2))  10%, transparent) 22%,
    color-mix(in oklab, var(--fondo-plegado, var(--f2))  35%, transparent) 45%,
    color-mix(in oklab, var(--fondo-plegado, var(--f2))  72%, transparent) 72%,
    var(--fondo-plegado, var(--f2))                              100%); }
.plegado[data-abierto] .plegado-caja::after { opacity: 0; }
/* En el flujo normal, es decir pegado al margen izquierdo del contenido. */
.plegado-btn { margin-block-start: var(--space-m); }
.plegado-btn .menos { display: none; }
.plegado[data-abierto] .plegado-btn .mas   { display: none; }
.plegado[data-abierto] .plegado-btn .menos { display: inline; }

/* — Retrato de aula ————————————————————————————————————————————————
   Acompaña a la columna de cursos mientras se baja, pero solo cuando hay dos
   columnas. Plegado en una sola, quedarse fija significaba verla asomar por
   detrás de las tarjetas durante todo el recorrido. */
.foto-curso { align-self: start; }
@media (min-width: 60rem) {
  .foto-curso { position: sticky; inset-block-start: 6rem; }
}

/* — Libros ————————————————————————————————————————————————————————
   Título arriba, portada 4:5 pequeña a la izquierda y los datos a su lado. */
#libros { grid-template-columns: repeat(auto-fit, minmax(min(23rem, 100%), 1fr)); }
.libro { display: grid; gap: var(--space-s); align-content: start; }
.libro h3 { margin: 0; font-size: var(--step-1); }
.libro-cuerpo { display: grid; grid-template-columns: 6.5rem 1fr;
                gap: var(--space-m); align-items: start; }
.libro-portada { display: block; inline-size: 100%; aspect-ratio: 4 / 5;
  block-size: auto; border-radius: var(--radius-s);
  border: 1px solid var(--vidrio-borde); box-shadow: var(--shadow-m); }
.libro p { margin: 0; font-size: var(--step--1); }

/* — Libros en preparación: el avance asoma al acercarse —————————————
   El relleno entra por ::before con z-index negativo dentro del propio
   contexto de apilamiento del chip (isolation: isolate). Así se pinta encima
   del fondo del chip pero por debajo del texto, sin tener que subir a mano el
   z-index de cada rótulo. El overflow: hidden lo recorta a la píldora. */
.chip-prog { position: relative; overflow: hidden; isolation: isolate;
             cursor: default; }
.chip-prog::before { content: ""; position: absolute; z-index: -1;
  inset-block: 0; inset-inline-start: 0; inline-size: 0;
  transition: inline-size var(--dur-slow) var(--ease-fluid);
  /* Tintes y no color pleno: el texto queda a caballo entre la parte llena y
     la vacía, así que su color no puede cambiar y tiene que seguir leyéndose
     sobre las dos. Los valores viven en --prog-1..3, que cambian por tema.
     El tramo final, más vivo, es lo que marca dónde va el avance. */
  background: linear-gradient(90deg,
    var(--prog-1)   0%,
    var(--prog-2)  92%,
    var(--prog-3) 100%); }
.chip-prog:hover::before, .chip-prog:focus-visible::before { inline-size: var(--p); }
.chip-prog .chip-n { font-variant-numeric: tabular-nums; color: var(--link);
  max-inline-size: 0; overflow: hidden; opacity: 0; white-space: nowrap;
  transition: max-inline-size var(--dur-base) var(--ease-fluid),
              margin-inline-start var(--dur-base) var(--ease-fluid),
              opacity var(--dur-fast) var(--ease-out); }
.chip-prog:hover .chip-n, .chip-prog:focus-visible .chip-n {
  max-inline-size: 5ch; margin-inline-start: .5em; opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .chip-prog .chip-n, .chip-prog::before, .plegado-caja { transition: none; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   23. CONSENTIMIENTO Y DIÁLOGO
   ═══════════════════════════════════════════════════════════════════════════ */

/* La casilla no se disfraza ni se esconde: la ley pide un acto expreso, y un
   control que no parece un control no es un acto expreso. */
.campo-consentimiento { margin-block: var(--space-m) var(--space-s); }
.consentimiento { display: flex; align-items: flex-start; gap: var(--space-2xs);
  cursor: pointer; font-family: var(--font-body); font-size: var(--step--1);
  line-height: 1.55; color: var(--txt-2); text-transform: none; letter-spacing: 0; }
.consentimiento input[type="checkbox"] {
  inline-size: 1.15rem; block-size: 1.15rem; flex: none; margin-block-start: .18em;
  accent-color: var(--link); cursor: pointer; }
.enlace-auth { display: inline; background: none; border: 0; padding: 0;
  font: inherit; color: var(--link); cursor: pointer;
  text-decoration: underline; text-underline-offset: .2em; }

/* Diálogo modal. Vidrio, como el resto del sistema. */
dialog.modal { max-inline-size: min(38rem, calc(100% - 2rem)); inline-size: 100%;
  border: 1px solid var(--vidrio-borde); border-radius: var(--radius-l);
  background: var(--f2); color: var(--tinta-sobre-claro);
  padding: var(--space-l); box-shadow: var(--shadow-l); }
html[data-tema="oscuro"] dialog.modal { color: var(--tinta-sobre-oscuro); }
dialog.modal::backdrop { background: color-mix(in oklch, oklch(16% .02 258) 62%, transparent); }
@supports (backdrop-filter: blur(1px)) {
  dialog.modal::backdrop { backdrop-filter: blur(6px); } }
dialog.modal h2 { font-size: var(--step-2); margin: 0 0 var(--space-s); }
dialog.modal .modal-cuerpo { max-block-size: min(52vh, 26rem); overflow-y: auto;
  font-size: var(--step--1); line-height: 1.65; }
dialog.modal .modal-cuerpo p { max-inline-size: none; }
dialog.modal .modal-pie { display: flex; gap: var(--space-2xs); flex-wrap: wrap;
  justify-content: flex-end; margin-block-start: var(--space-m);
  padding-block-start: var(--space-s); border-block-start: 1px solid var(--linea); }

/* ═══════════════════════════════════════════════════════════════════════════
   24. TÁCTIL Y MÓVIL
   ═══════════════════════════════════════════════════════════════════════════ */

/* La consulta correcta aquí es hover, NO el ancho: hay tabletas anchas sin
   puntero fino y portátiles estrechos con ratón. Decidir por píxeles se
   equivoca en los dos casos. */
@media (hover: none) {
  /* Sin hover el avance de los libros no se vería nunca. Se muestra fijo: más
     vale enseñarlo siempre que esconderlo tras un gesto que no existe. */
  .chip-prog::before { inline-size: var(--p); }
  .chip-prog .chip-n { max-inline-size: 5ch; margin-inline-start: .5em; opacity: 1; }
  /* Los desplazamientos de hover se disparan al tocar y dejan la tarjeta
     levantada hasta que tocas otra cosa. Fuera. */
  .vidrio:hover, .ficha.vidrio:hover, .foto:hover { transform: none; }
  /* El rectángulo gris de iOS/Android al tocar se come el vidrio. */
  a, button, summary, .chip { -webkit-tap-highlight-color: transparent; }
}

@media (max-width: 60rem) {
  /* Blancos táctiles de 44 px. Se fija con min-block-size y no con padding en
     em: el padding en em depende del cuerpo de letra, así que con un texto de
     12 px hacían falta cifras distintas en cada control y ninguno llegaba.
     Con min-block-size el blanco mide 44 px y punto, sea cual sea la letra.
     (El mínimo que exige WCAG 2.5.8 es 24 px y ya se cumplía; 44 es el cómodo
     de verdad para un dedo, y sale gratis.)
     Los enlaces dentro de párrafos NO se tocan: están exentos por norma y
     estirarlos rompería el interlineado del texto. */
  .top nav a,
  .idioma button,
  .chip,
  .docs li a       { min-block-size: 44px; display: inline-flex; align-items: center; }
  .docs li a       { display: flex; }          /* conserva su fila con la fecha */
  .idioma button   { justify-content: center; padding-inline: 1em; }
  .tema            { inline-size: 44px; block-size: 44px; }
  .btn             { min-block-size: 48px; padding: .95em 1.5em; }
  details.docs > summary { min-block-size: 44px; }
  .campo input     { min-block-size: 44px; }
  /* El cuerpo de los campos ya pasa de 16 px por la escala fluida, que es lo
     que evita que Safari en iOS haga zoom al enfocarlos. Comprobado: 16.19 px.
     Si alguien baja esa escala, el formulario empezará a dar saltos en iPhone. */

  /* El encabezado plegado ocupa dos filas, así que un ancla saltaba a un punto
     que quedaba debajo de él. */
  section.franja   { scroll-margin-block-start: 7.5rem; }

  /* En una pantalla corta, 34rem plegados son casi toda la altura visible. */
  .plegado-caja    { --alto-plegado: 24rem; }

  /* Los botones de acción ocupan el ancho: es más fácil acertar y se lee
     mejor que dos píldoras partidas. */
  .acciones .btn   { flex: 1 1 auto; justify-content: center; }

  /* La portada de 6.5rem dejaba unos 100 px para el texto a 320 px de ancho. */
  .libro-cuerpo    { grid-template-columns: 5rem 1fr; gap: var(--space-s); }
}
