/* ═══════════════════════════════════════════════════════════════════
   ezzygrillz · estilos.css
   PASO 1 — paleta, tipografía, marcos de imagen y layout base.
   Todavía SIN animaciones: no hay transitions, keyframes ni transforms
   de scroll. Los tokens de movimiento ya están definidos abajo para que
   el paso 8 no tenga que tocar nada de esto.
   ═══════════════════════════════════════════════════════════════════ */

/* ── 0. FUENTES (auto-hospedadas y subseteadas) ─────────────────────
   Van en /fuentes, no en el CDN de Google: se ahorra el DNS + TLS a un
   tercero, que en 4G es medio segundo.

   Cada familia está cortada en latin y latin-ext. Un visitante que lee
   español baja solo latin: 74 KB en total. El archivo latin-ext existe
   por si algún día entra un carácter fuera del rango, y el navegador lo
   pide solo en ese caso.

   Archivo además está instanciado a los ejes que el sitio usa de verdad
   — peso 400–800 y ancho 75–100 — en vez de los 100–900 / 62–125
   completos. Eso solo bajó de 87 KB a 49 KB. Licencias en la carpeta.

   Si más adelante hace falta otro peso o un ancho más angosto, hay que
   volver a generar el subset: fuera de esos rangos la fuente no varía. */

@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 400 800;
  font-stretch: 75% 100%;
  font-display: swap;
  src: url('fuentes/archivo-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'Archivo';
  font-style: normal;
  font-weight: 400 800;
  font-stretch: 75% 100%;
  font-display: swap;
  src: url('fuentes/archivo-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fuentes/ibm-plex-mono-400-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('fuentes/ibm-plex-mono-400-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fuentes/ibm-plex-mono-500-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
  font-family: 'IBM Plex Mono';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('fuentes/ibm-plex-mono-500-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ── 1. TOKENS ──────────────────────────────────────────────────── */
:root {

  /* Paleta — negro frío con tinte verde-azulado. Del guion, sección 4. */
  --negro:  #0B0E0D;   /* el negro de la marca. NO se toca. */

  /* ── EL FONDO QUE RESPIRA ──────────────────────────────────────────
     `--negro` es el color DE LA MARCA. `--fondo` es de qué color está la
     página EN ESTE MOMENTO. Son dos cosas distintas y por eso tienen dos
     nombres: el token de marca sigue siendo el mismo de siempre y es el
     valor de referencia, el respaldo y el punto de partida.

     El JS escribe UNA sola propiedad —`--fondo-rgb`, los tres canales
     como números sueltos— en el `<html>`. Todo lo demás sale de acá:
     el color sólido para las seis superficies, y los tres números para
     las viñetas, que necesitan alfa y por eso no pueden usar el color ya
     armado.

     ⚠️ EL RESPALDO DEL `var()` ES EL NEGRO DE MARCA, y eso es lo que hace
     que sin JavaScript —y con `prefers-reduced-motion`, donde el JS no
     escribe nada— el sitio sea exactamente el de siempre, plano y en
     #0B0E0D. No hay una segunda rama que mantener.

     Las paradas y el porqué de cada valor están en el JS, junto al
     mecanismo que las interpola. */
  --fondo-rgb: 11, 14, 13;
  --fondo: rgb(var(--fondo-rgb));
  /* Ya no se usa como fondo en ninguna parte: el sitio es un solo negro
     continuo de arriba a abajo. Se deja definido por si vuelve a hacer
     falta una superficie elevada, pero volver a aplicarlo a una sección
     rompe la continuidad. */
  --acero:  #151C1A;
  /* ── LA LÍNEA ────────────────────────────────────────────────────
     ⚠️ SUBIÓ DE #26302D A #526961, Y NO ES UN CAMBIO DE GUSTO.
     La norma pide 3:1 para bordes y separadores que comunican
     estructura —WCAG 1.4.11, contraste de elementos no textuales— y
     #26302D daba 1,43:1 contra el negro de marca. Estaba a menos de la
     mitad del mínimo.

     📌 EL NÚMERO NO SE ELIGIÓ CONTRA EL NEGRO DE MARCA, sino contra el
     PUNTO MÁS CLARO DE LA RAMPA DEL FONDO, que es el peor caso: cuanto
     más claro el fondo, menor el contraste de una línea clara. Medido
     recorriendo la página, el fondo va de rgb(6,8,7) en el hero a
     rgb(18,21,20) en materiales. Contra ese extremo:

         #26302D  (antes)   1.35:1  ✗
         #51675F  (mínimo)  3.02:1  ✓  ← justo, dos centésimas de margen
         #526961  (éste)    3.11:1  ✓

     Se eligió el segundo escalón y no el primero por dos centésimas de
     aire: con 3.02 cualquier redondeo del navegador deja la línea abajo
     del mínimo.

     📌 Y ES EL MISMO VECTOR DE CROMATICIDAD DE LA MARCA, ×7.48. El negro
     es rgb(11,14,13); esto es rgb(82,105,97), que es (11,14,13) × 7.48
     redondeado. La línea no cambia de familia de color, sólo de
     luminancia — la misma construcción con la que está armada la rampa
     del fondo. El #26302D viejo también estaba sobre ese vector, ×3.45.

     📌 Lo que esto arregla de paso: el subrayado de `.enlace`. Es la
     única señal de que «Ver cómo funciona» en el FAQ es un enlace —el
     texto va del mismo color que el párrafo—, y a 1,42:1 prácticamente
     no se veía. Ahora cumple el 3:1 que pide 1.4.1 para una señal no
     textual. */
  --linea:  #526961;   /* bordes, divisores              */
  --humo:   #6E7A76;   /* gris de marca                  */
  --hueso:  #E8E6DF;   /* texto principal y botones      */

  /* --humo da 4.36:1 sobre negro: pasa AA solo en texto grande.
     Para texto chico se usa esta versión un punto más clara (5.1:1).
     El gris del guion se mantiene intacto para líneas y marcas. */
  --humo-txt: #7A8682;

  /* REGLA DURA: el oro y el cromo solo existen dentro de las fotos.
     Ningún degradé, borde, sombra ni botón dorado en CSS. Nunca. */

  /* Tipografía */
  --f-titular: 'Archivo', 'Helvetica Neue', 'Arial Narrow', sans-serif;
  --f-cuerpo:  'Archivo', 'Helvetica Neue', Arial, sans-serif;
  --f-mono:    'IBM Plex Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  /* Escala de contraste extremo: titulares enormes, mono muy chica.
     Nada de tamaños intermedios tibios. */
  --fs-xxl:  clamp(2.75rem, 9vw, 4.5rem);     /* hero, ancho completo  */
  --fs-hero: clamp(2.75rem, 6vw, 5.5rem);     /* hero partido en dos   */
  --fs-xl:   clamp(2.75rem, 9vw,  7rem);     /* titulares de sección  */
  --fs-l:    clamp(2rem,    5vw,  3.25rem);  /* titulares secundarios */
  --fs-cuerpo:  1.0625rem;                   /* 17px                  */
  --fs-bajada:  clamp(1.0625rem, 2vw, 1.25rem);
  /* Subtítulo: vive ENTRE el titular de su sección y el texto que
     encabeza, y esa es toda su definición. En escritorio queda en 32 px
     entre los 52 del titular y los 20 del cuerpo; en móvil, en 26 px
     entre 44 y 17. Los dos saltos son de ~1.6, así que ninguno de los
     tres niveles se confunde con el de al lado. */
  --fs-sub:     clamp(1.625rem, 2.4vw, 2rem); /* 26 → 32px            */
  /* La pregunta del acordeón. Es su propio escalón y no reusa ninguno de
     los de arriba, por una razón medida: tiene que quedar POR DEBAJO del
     titular de sección y POR ENCIMA de su propia respuesta, y ningún token
     existente cumple las dos.
       · con --fs-sub (26→32) competía con el titular de sección
       · con --fs-bajada (17→20) empataba con la respuesta en móvil, que es
         exactamente el error que el paso 33 documentó al revés
     Queda en 20 → 24px: arriba de los 17 de la respuesta en cualquier
     ancho, y abajo de los 32→52 del titular. Ver el paso 33. */
  --fs-pregunta: clamp(1.25rem, 1.7vw, 1.5rem); /* 20 → 24px          */
  --fs-mono:    0.6875rem;                   /* 11px                  */
  --fs-mono-l:  0.75rem;                     /* 12px                  */

  --tracking-titular: -0.03em;
  --tracking-mono:     0.09em;

  /* Espaciado */
  --e-1: 0.5rem;
  --e-2: 1rem;
  --e-3: 1.5rem;
  --e-4: 2.5rem;
  --e-5: 4rem;
  --e-6: 6rem;
  --e-7: clamp(5rem, 12vh, 9rem);   /* aire entre escenas */

  --ancho-max:  76rem;
  --ancho-texto: 34rem;
  --margen: clamp(1.25rem, 5vw, 4rem);

  /* Alto de la cabecera fija. Lo usan el scroll-margin de las anclas y
     el top de las secciones fijas. Si cambia el padding de la cabecera,
     hay que actualizar esto. */
  --alto-cabecera: 7rem;

  /* Movimiento — definido ahora, usado recién en el paso 8. */
  --dur-corta: 400ms;
  --dur-larga: 800ms;
  --ease: cubic-bezier(.2, .7, .2, 1);

  /* ── LA CASCADA DEL TITULAR DEL HERO ───────────────────────────────
     Los dos números salen de una cuenta, no de una preferencia. El
     titular tarda HOY 930 ms en terminar de aparecer: 130 de retardo
     (`data-retardo="1"`) más los 800 de la transición del bloque. La
     cascada no puede tardar más que eso, así que el presupuesto es:

         130 + (26 − 1) × desfase + duración ≤ 930

     «Los grillz de los artistas» son 26 caracteres contando espacios.
     Con 20 y 300 la cuenta da 930 CLAVADO: el titular termina de
     aparecer en el mismo milisegundo que antes.

     Por qué esos dos y no otro par que también cierre:
       · 16 / 400 → el frente cruza en 400 ms y hay 25 letras animándose
         a la vez sobre 26. Eso vuelve a ser un bloque, o sea el parpadeo
         que había que evitar.
       · 22 / 250 → el frente cruza en 550 ms con 11 letras a la vez. Es
         la cascada más marcada, pero 250 ms por letra queda lejos de la
         banda de 400–800 ms del guion.
       · 20 / 300 → el frente cruza en 500 ms con 15 letras a la vez. Se
         lee como una onda que viaja de izquierda a derecha y las letras
         se superponen de sobra: nunca parece una máquina de escribir.

     📌 Sobre la banda de 400–800 ms de la sección 4 del guion: lo que
     dura 800 ms es EL TITULAR, igual que antes. Los 300 ms son de una
     letra, que no es un elemento de la interfaz sino una parte de él.

     📌 EL TITULAR SIGUE SIENDO «Los grillz de los artistas» y estos números
     siguen valiendo tal cual: 26 posiciones, 130 + 25×20 + 300 = 930 ms.

     Hubo un momento en que el titular se cambió por «Piezas hechas a la
     medida de tu dentadura.» —42 posiciones, 1250 ms medidos en el
     navegador— y esta nota quedó escrita para ese caso. Se revirtió: aquel
     texto era la bajada, no el titular. Queda anotado por si alguna vez
     vuelve a evaluarse un titular más largo, porque el número importa:

     Con 42 posiciones, volver a 930 ms exigiría bajar el desfase a 12,2 ms
     —menos de un cuadro a 60 Hz—, las letras caerían de a dos y de a tres
     en el mismo cuadro y la onda se leería como un bloque otra vez. O sea
     que un titular más largo no se paga acortando el desfase: se paga
     aceptando que la cascada dure más. */
  --dur-letra:     300ms;
  --desfase-letra:  20ms;
  --rev-letra-y: 0.75rem;

  /* ── LA RESPUESTA AL MOUSE ─────────────────────────────────────────
     Cuánto se corre cada capa del hero cuando el cursor llega a un borde
     de la pantalla. Los dos números están topados por la garantía del
     paso 25, no elegidos a ojo:

     Las dos capas se mueven en la MISMA dirección, así que lo que le come
     holgura al texto no es el recorrido de la foto sino la DIFERENCIA
     entre las dos: 6 − 2.5 = 3.5 px en el peor caso, con el cursor pegado
     al borde izquierdo. Medido, la holgura mínima pasa de 36.1 a 32.6 px
     en 2560 × 1440 con el bucle en su punto máximo. Sigue siendo un hueco
     y nunca hay choque.

     Y contra el margen del lettering ni se acerca: en escritorio hay 13.2%
     de aire alrededor de las letras —190 px en una ventana de 1440— y el
     bucle al máximo deja 154. Seis píxeles no lo tocan.

     La viñeta NO lleva ninguno de estos números, y es la misma decisión de
     siempre: está anclada a la pantalla, no a la foto, y que la foto se
     desplace por debajo de una viñeta quieta ES la tercera velocidad. */
  --raton-foto:  6px;
  --raton-texto: 2.5px;

  /* El peso. La foto es la capa del fondo y tarda más en llegar que el
     texto: no es un adorno, es lo que hace que se lean como dos planos a
     distinta profundidad. Las dos duraciones caen dentro de la banda de
     400–800 ms del guion. */
  --dur-raton-foto:  600ms;
  --dur-raton-texto: 450ms;
}

/* ── 2. RESET ───────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body, h1, h2, h3, h4, p, figure, blockquote, dl, dd { margin: 0; }

ul[class], ol[class] { list-style: none; margin: 0; padding: 0; }

img, picture, video, canvas, svg { display: block; max-width: 100%; }

input, button, textarea, select { font: inherit; color: inherit; }

/* ── 3. BASE ────────────────────────────────────────────────────── */
html { scroll-behavior: smooth; }

body {
  background: var(--fondo);
  color: var(--hueso);
  font-family: var(--f-cuerpo);
  font-size: var(--fs-cuerpo);
  font-weight: 400;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

::selection { background: var(--hueso); color: var(--negro); }

/* ── 4. TIPOGRAFÍA ──────────────────────────────────────────────── */

/* Titular: Archivo 800, ancho condensado, tracking muy cerrado.
   La gótica del logo NUNCA se reproduce como fuente web: es imagen. */
.titular {
  font-family: var(--f-titular);
  font-weight: 800;
  font-stretch: 82%;                          /* eje wdth de Archivo */
  font-variation-settings: 'wdth' 82;         /* respaldo explícito   */
  font-size: var(--fs-xl);
  line-height: 0.9;
  letter-spacing: var(--tracking-titular);
  text-transform: uppercase;
  text-wrap: balance;
}

.titular--hero  { font-size: var(--fs-xxl); }
.titular--medio { font-size: var(--fs-l); }

/* ── EL TAMAÑO DE LOS TITULARES DE SECCIÓN ───────────────────────────
   PREGUNTAS y ¿EMPEZAMOS? iban en `--fs-xl` —hasta 112px— mientras que EL
   PROCESO y MATERIALES, que son sus pares, van en `--fs-l`: sus reglas de
   modo fijo (`.es-secuencia`, `.es-fija`) ya los bajaban. O sea que dos
   secciones del mismo nivel se veían al doble que las otras dos, y
   ¿EMPEZAMOS? competía de tamaño con el titular del hero, que es el
   mensaje principal del sitio.

   Ahora los cuatro miden lo mismo, y EL TALLER LLEGA A TI ya estaba en ese
   tamaño por su `titular--medio`. Un solo nivel para todos los titulares
   de sección: 32px en móvil, 52px en escritorio.

   📌 Queda una diferencia conocida: sin JavaScript o con movimiento
   reducido, EL PROCESO y MATERIALES vuelven a `--fs-xl` porque las reglas
   que los bajan cuelgan de las clases de modo. Es de antes de este cambio
   y no se tocó acá para no mover esos dos modos sin pedirlo. */
/* ⚠️ SIN `>` EN EL ÚLTIMO TRAMO, Y SE DEJA ASÍ AUNQUE HOY NO HAGA FALTA.
   Esto decía `.cierre > .cierre__caja > .titular`, hijo directo. Al probar
   el video de TikTok al costado hubo que envolver el texto en un div para
   poder hacer dos columnas, el titular dejó de ser hijo directo, la regla
   dejó de casar y ¿EMPEZAMOS? volvió solo a `--fs-xl` —hasta 112 px— y se
   montó encima de lo que tenía al lado. En el código no se veía nada raro;
   apareció en la captura.

   El envoltorio ya no está, así que el `>` volvería a funcionar. Se deja
   el descendiente suelto igual: lo que importa es que el titular esté
   DENTRO de la caja de la sección, no a qué profundidad, y así el próximo
   envoltorio no vuelve a romperlo. */
.preguntas > .preguntas__caja .titular,
.cierre   > .cierre__caja   .titular { font-size: var(--fs-l); }

/* ── SUBTÍTULOS ──────────────────────────────────────────────────────
   Los nombres de paso y de material son el nivel del medio: encabezan un
   texto y dependen del titular de su sección. Tienen que leerse más
   chicos que el titular y más grandes que el texto que encabezan. Las dos
   cosas, no una.

   Dos errores previos, los dos anotados porque se corrigieron uno con el
   otro:

   1. Llevaban `.titular titular--medio` y en escritorio medían 60 px
      contra los 52 px del titular de su propia sección. El subordinado era
      más grande que el que manda.
   2. Al corregirlo los mandé a rótulo mono de 12 px — y ahí quedaron más
      chicos que su propio párrafo, que en escritorio mide 20 px. Un
      encabezado más chico que lo que encabeza no es jerarquía, es otro
      error del mismo tipo, en el otro sentido.

   Ahora el tamaño sale de sus dos vecinos, no de una preferencia:
       titular 52 · SUBTÍTULO 32 · cuerpo 20      (escritorio)
       titular 44 · SUBTÍTULO 26 · cuerpo 17      (móvil)

   Y el tamaño no trabaja solo. Contra el titular hay otras dos
   diferencias, para que no se lea como un titular chico sino como otro
   nivel: peso 600 contra 800, y ancho normal contra el 82% condensado que
   es la firma de los titulares.

   La jerarquía no se resuelve con color: sigue en hueso, 15.53:1. */
.paso__titulo,
.material__nombre {
  font-family: var(--f-titular);
  font-size: var(--fs-sub);
  font-weight: 600;
  font-stretch: 100%;
  font-variation-settings: 'wdth' 100;
  line-height: 1.05;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  text-wrap: balance;
  color: var(--hueso);
}

.cuerpo { max-width: var(--ancho-texto); }
.cuerpo--ancho { max-width: 44rem; }

.bajada {
  font-size: var(--fs-bajada);
  line-height: 1.45;
  max-width: var(--ancho-texto);
  color: var(--hueso);
}

/* Mono: fichas técnicas, números, etiquetas, pasos, FAQ. */
.mono {
  font-family: var(--f-mono);
  font-size: var(--fs-mono);
  font-weight: 400;
  letter-spacing: var(--tracking-mono);
  text-transform: uppercase;
  line-height: 1.6;
}

.mono--l  { font-size: var(--fs-mono-l); }
.mono--m  { font-weight: 500; }
.mono--nn { text-transform: none; letter-spacing: 0.04em; }

.etiqueta { color: var(--humo-txt); }

.secundario { color: var(--humo-txt); }

/* Número de paso: 01–04. El orden es información, por eso se ve. */
.numero {
  font-family: var(--f-mono);
  font-size: var(--fs-mono-l);
  font-weight: 500;
  letter-spacing: 0.12em;
  color: var(--humo-txt);
}

/* ── 5. BOTONES ─────────────────────────────────────────────────── */

/* Principal: bloque hueso sólido sobre negro. Máximo contraste,
   cero decoración. Sin degradés, sin bordes de color, sin sombra. */
.boton {
  display: inline-block;
  padding: 1.05rem 2rem;
  background: var(--hueso);
  color: var(--negro);
  font-family: var(--f-mono);
  font-size: var(--fs-mono-l);
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  text-decoration: none;
  border: 1px solid var(--hueso);
  border-radius: 0;
}

.boton--secundario {
  background: transparent;
  color: var(--hueso);
  border-color: var(--linea);
}

/* Versión de barra: mismo bloque hueso, menos alto. */
.boton--compacto {
  padding: 0.6rem 1.05rem;
  font-size: var(--fs-mono);
  letter-spacing: 0.1em;
}

/* ── 6. MARCOS DE IMAGEN ────────────────────────────────────────── */
/* Proporción fija + object-fit: cover. Así una foto nueva de otra
   proporción entra sin romper el diseño. Cambiar una imagen = soltar
   el archivo con el mismo nombre en /img. Cero cambios de código. */

.marco {
  position: relative;
  overflow: hidden;
  /* Negro, no acero: es lo que se ve mientras la foto baja, y con acero
     aparecía un rectángulo más claro por un instante en cada `lazy`. */
  background: var(--fondo);
  aspect-ratio: var(--proporcion, 3 / 4);
}

.marco > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: var(--foco, center);
}

.marco--1x1  { --proporcion: 1 / 1; }
.marco--4x5  { --proporcion: 4 / 5; }
.marco--3x4  { --proporcion: 3 / 4; }
.marco--9x16 { --proporcion: 9 / 16; }
.marco--16x9 { --proporcion: 16 / 9; }
.marco--alto { --proporcion: 2 / 3; }

/* Regla de calidad, no negociable: las capturas de redes nunca van a
   pantalla completa. Van chicas o desenfocadas al fondo. */
.marco--captura { max-width: 22rem; }
.marco--fondo   { filter: blur(14px) saturate(0.7); opacity: 0.4; }

/* Grano encima, para que una captura de baja resolución no se lea
   como imagen estirada. Es textura, no animación. */
.marco--grano::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.28;
  mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cfilter id='r'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='3'/%3E%3C/filter%3E%3Crect width='120' height='120' filter='url(%23r)' opacity='0.55'/%3E%3C/svg%3E");
}

.pie-foto {
  margin-top: var(--e-1);
  color: var(--humo-txt);
}

/* ── 7. LAYOUT ──────────────────────────────────────────────────── */
.contenedor {
  width: 100%;
  max-width: var(--ancho-max);
  margin-inline: auto;
  padding-inline: var(--margen);
}

.escena {
  padding: var(--e-7) var(--margen);
  border-top: 1px solid var(--linea);
}

/* .escena--acero se eliminó: no la usaba ningún elemento del HTML y era la
   única otra puerta por la que podía volver a entrar un fondo distinto. */
.escena--silencio { padding-block: calc(var(--e-7) * 1.8); }  /* escena 06 */

/* ── 7b. CABECERA FIJA Y NAV ─────────────────────────────────────────
   Acompaña el scroll. Lo que lo justifica no son los cuatro links: es
   que el botón de agendar quede siempre a un toque de distancia. */
.cabecera {
  position: sticky;
  top: 0;
  z-index: 20;
  /* ⚠️ La cabecera TIENE que seguir al fondo. Pegada en `--negro` fijo se
     convertía en una banda más oscura cruzando el sitio de punta a punta,
     a la vista todo el tiempo. Es la superficie donde más se notaba. */
  background: var(--fondo);
  border-bottom: 1px solid var(--linea);
}

.cabecera__caja {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem var(--e-3);
  width: 100%;
  max-width: var(--ancho-max);
  margin-inline: auto;
  padding: 0.7rem var(--margen);
}

.cabecera__marca { display: block; }

.logo {
  width: clamp(100px, 14vw, 140px);
  height: auto;
}

/* En móvil la nav baja a una segunda fila y el botón queda arriba a la
   derecha, al alcance del pulgar. */
.nav { order: 3; flex-basis: 100%; }

/* Inicio · Proceso · Materiales · Contacto. Sin "Menu", sin "Nosotros". */
.nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  /* El mínimo está calculado para que las cuatro entradas entren en una
     sola fila hasta en 320px. Si se agranda, la cabecera fija pasa a
     tres filas y se come un quinto de la pantalla. */
  gap: clamp(0.55rem, 2.6vw, 2rem);
}

.nav a {
  color: var(--hueso);
  text-decoration: none;
  padding-block: 0.25rem;
}

.nav a:hover,
.nav a:focus-visible { color: var(--humo-txt); }

@media (min-width: 800px) {
  .cabecera__marca { order: 1; }
  .nav            { order: 2; flex-basis: auto; margin-inline: auto; }
  .cabecera__cita { order: 3; }
}

/* Las anclas no pueden quedar tapadas por la cabecera fija. */
:target,
[id] { scroll-margin-top: calc(var(--alto-cabecera) + var(--e-2)); }

/* ── 7b-bis. EL RIEL HORIZONTAL · SOLO ESCRITORIO ────────────────────
   Hero, proceso y materiales, uno al lado del otro. El scroll vertical
   mueve la pantalla de costado entre los tres.

   ⚠️ NACE INERTE. Los tres envoltorios son `display: contents` mientras
   no esté `.es-riel`, así que no existen para el layout y el documento
   es el mismo de antes. Sin JavaScript, con `prefers-reduced-motion` y
   bajo 800 px nunca llega la clase: ahí el sitio es vertical, igual que
   siempre. Es la misma regla que ya cumplen `.es-secuencia` y `.es-fija`.

   LOS DOS MOVIMIENTOS HORIZONTALES NO CONVIVEN. El riel avanza por
   tramos de REPOSO y de TRANSICIÓN. Durante el reposo del proceso, `--x`
   del riel es una constante literal: lo único que se mueve en pantalla
   es la fila de fotos adentro de su ventana, con el titular, el índice y
   el texto clavados. Cuando el riel se mueve, se mueve todo. El ojo los
   distingue por el marco, no por la dirección.

   Entre el final de un reposo y el arranque de la transición va un
   RESPIRO sin nada, que es el mismo papel que cumple `M_SALIDA` en
   materiales: sin él, los dos movimientos se encadenan y se leen como
   uno solo que se agrandó.                                             */

.riel,
.riel__fijo,
.riel__vagon { display: contents; }

/* La pista. Solo existe para dar scroll: su alto es lo que se recorre.
   Los dos números los congela el JS en píxeles la primera vez que mide,
   con el mismo criterio de las otras dos escenas — solo se vuelve a
   medir si cambia el ancho o si el alto cambia más de un 20%. */
.riel.es-riel {
  display: block;
  position: relative;
  height: calc(var(--alto-panel, calc(100svh - var(--alto-cabecera))) + var(--recorrido, 300vh));
}

/* El panel. Recorta y nada más: NO es un contenedor de scroll y el JS no
   le escribe `scrollLeft`, igual que el carril del proceso. */
.riel.es-riel .riel__fijo {
  display: block;
  position: sticky;
  top: var(--alto-cabecera);
  height: var(--alto-panel, calc(100svh - var(--alto-cabecera)));
  overflow: hidden;
}

/* La fila de paneles. Un `transform` compuesto por la GPU, que es lo que
   se comporta igual en todos los navegadores. */
.riel.es-riel .riel__vagon {
  display: flex;
  align-items: stretch;
  height: 100%;
  width: max-content;
  transform: translate3d(var(--x, 0px), 0, 0);
  will-change: transform;
}

/* El ancho lo escribe el JS en píxeles y NO va en `100vw`: `vw` incluye
   la barra de scroll, así que con `100vw` cada panel quedaba ~15 px más
   ancho que la ventana y el error se acumulaba panel a panel. */
.riel.es-riel .riel__vagon > section {
  flex: 0 0 var(--ancho-panel, 100vw);
  width: var(--ancho-panel, 100vw);
  height: 100%;
  min-height: 0;
  overflow: hidden;
  /* El retroceso: el panel que sale se achica y se apaga un punto
     mientras se va, y el que entra hace lo contrario. Es el mismo
     lenguaje de la pila de cartas de materiales, y es lo que hace que la
     transición se lea como pasar de página y no como más paneo.
     En reposo vale exactamente 1 y 1: la composición aprobada queda
     intacta, sin un píxel de escala de por medio. */
  transform: scale(var(--s, 1));
  opacity: var(--o, 1);
  will-change: transform, opacity;
}

/* ── las tres secciones adentro del panel ────────────────────────────
   Cada una pasa de medir lo que mide a medir exactamente el panel. */

/* El hero deja de dimensionarse por el ancho de la foto y se ajusta al
   panel. `object-fit: contain` es lo que sostiene la garantía del paso
   25: la foto entra ENTERA —no se recorta de costado, que es la regla
   dura— y lo que sobra queda en negro, que es el fondo de la propia
   foto y del sitio, así que no se ve dónde termina.

   ⚠️ Y la garantía se sostiene sola, no por casualidad. Con la foto
   centrada y ajustada por alto, la pieza arranca en
   (P − F)/2 + 0.45·F = P/2 − 0.05·F, y como F ≤ P eso nunca es menor que
   0.45·P. O sea: dentro del riel la pieza empieza IGUAL O MÁS A LA
   DERECHA que en el sitio vertical, nunca más a la izquierda. El tope
   del texto en 40vw no hay que tocarlo. Verificado en navegador. */
.riel.es-riel .hero {
  height: 100%;
  padding-bottom: 0;
}

.riel.es-riel .hero__foto { height: 100%; }

/* ⚠️ EL <picture> DEL MEDIO. Sin esta línea el `height: 100%` de la imagen
   no resuelve contra nada: su contenedor directo no es `.hero__foto` sino
   el `<picture>`, que va con alto automático. La imagen caía a `height:
   auto`, medía 864 px de alto en un panel de 788 y el panel le recortaba
   76 px POR ABAJO — justo donde está la pieza. Medido en navegador; la
   cuenta sobre el CSS no lo veía. */
.riel.es-riel .hero__foto picture {
  display: block;
  height: 100%;
}

.riel.es-riel .hero__foto img {
  width: 100%;
  height: 100%;
  max-width: none;
  max-height: none;
  object-fit: contain;
  object-position: center;
}

/* Proceso y materiales pierden su pista y su `sticky` propios: ya están
   adentro de un panel fijo. El avance se lo pasa el reposo del riel en
   vez de salir de `getBoundingClientRect()`. Son las mismas funciones
   puras del scroll; lo único que cambia es quién calcula la entrada. */
.riel.es-riel .proceso.es-secuencia .proceso__pista,
.riel.es-riel .materiales.es-fija .materiales__pista {
  position: static;
  height: 100%;
}

.riel.es-riel .proceso.es-secuencia .proceso__fijo,
.riel.es-riel .materiales.es-fija .materiales__fijo {
  position: static;
  height: 100%;
}

.riel.es-riel .proceso.es-secuencia,
.riel.es-riel .materiales.es-fija {
  position: relative;
  height: 100%;
}

/* ── EL PANEL DEL KIT ────────────────────────────────────────────────
   Es el único panel del riel sin mecanismo interno: cuatro líneas de texto
   en una pantalla entera. En el flujo vertical el bloque arranca arriba y
   está bien, porque abajo sigue la sección siguiente; dentro de un panel
   de alto fijo eso lo deja colgando del borde superior con 600 px de negro
   abajo, que se lee como que falta algo.

   Se centra en vertical y NADA MÁS: el margen izquierdo, el ancho del
   texto y los tamaños son los mismos que en móvil. No se agranda ni se
   centra en horizontal — arranca en la misma vertical que todas las demás
   secciones del sitio. */
.riel.es-riel .kit {
  height: 100%;
  display: grid;
  align-content: center;
  padding-block: 0;
}

/* ⚠️ EL PIE DE MATERIALES NO PUEDE COSTARLE ALTO A LA CARTA. Fuera de la
   pista sumaba 87 px al alto de la sección, y adentro de un panel de alto
   fijo esos 87 px salen de la carta: 58 px de ancho menos, que es
   exactamente lo que los pasos 42 a 45 pelearon por conseguir.

   Se apoya en la esquina inferior izquierda del panel, que es espacio
   muerto medido —la columna de la ficha tiene el titular arriba, la
   ficha al centro y nada abajo—. La carta vive pegada al borde derecho,
   así que no se tocan. */
.riel.es-riel .materiales.es-fija .materiales__pie {
  position: absolute;
  /* Los mismos tres valores que usa `.materiales__fijo` para alinearse:
     así el botón arranca en la misma vertical que el titular y que la
     ficha, y no en el borde de la sección. Es la corrección del paso 45,
     aplicada acá también. */
  left: var(--margen);
  right: var(--margen);
  margin-inline: auto;
  max-width: var(--ancho-max);
  bottom: var(--e-2);
  z-index: 2;
  margin-top: 0;
}

/* ── 7c. HERO (escena 02) ───────────────────────────────────────────
   Foto arriba a sangre completa, sin nada encima, y el bloque de texto
   debajo. Misma lógica en móvil y en escritorio.

   La foto ya dice el nombre de la marca en cromo: ponerle un titular
   encima era taparla. En escritorio el texto caía justo sobre las letras
   y la línea de QUITO · ECUADOR quedaba ilegible sobre el metal.

   Va con width:100% y alto automático, nunca con object-fit: cover.
   Cover la recortaría por los costados en cualquier pantalla más angosta
   que su proporción y se comería la última Z. Como el fondo de la foto es
   negro puro, la banda se funde con el fondo de la página: no se ve dónde
   termina la imagen y dónde empieza el sitio.                          */

.hero {
  position: relative;
  padding-bottom: var(--e-5);
}

.hero__foto {
  position: relative;
  overflow: hidden;
  /* ⚠️ `--bucle-max` VIVE ACÁ Y NO EN LA IMAGEN. La capa del brillo tiene
     que respirar exactamente igual que la foto o la máscara se despega del
     molde, y una hermana no hereda una variable declarada en la otra. Desde
     el contenedor la leen las dos y no pueden desincronizarse.
     `isolation` acota el `mix-blend-mode` del brillo a lo que hay adentro
     de este marco: sin eso, el modo de fusión buscaría el fondo de la
     página y el brillo se vería sobre el negro. */
  --bucle-max: 1.026;
  isolation: isolate;
}

.hero__foto img {
  width: 100%;
  height: auto;
  /* Zoom lento en bucle. Se anima la propiedad `scale`, NO `transform`:
     el parallax en capas ya escribe `transform` sobre esta misma imagen y
     una animación de transform lo pisaría mientras corre. Son propiedades
     distintas y el navegador las compone, así que conviven.

     La amplitud NO está elegida a ojo. Ampliar recorta por los costados,
     que es la regla dura del hero, así que se midió cuánto margen tiene el
     lettering: 13.2% en hero.webp pero solo 3.1% en hero-movil.webp. Manda
     el móvil. A 1.018 se come 0.9% por lado y quedan 2.2% de aire: las
     letras no se tocan en ningún tamaño de pantalla.

     16s y `alternate`. La primera versión iba a 28s con amplitud 1.018 y
     medida daba 2.8 niveles de cambio de luz: no se registraba como nada.
     Ver el paso 19 del registro. */
  animation: respiro-hero 16s ease-in-out infinite alternate;
}

@keyframes respiro-hero {
  from { scale: 1; }
  to   { scale: var(--bucle-max); }
}

@media (min-width: 800px) {
  /* En escritorio hay 13.2% de margen: se puede respirar bastante más.
     A 1.05 se come 2.5% por lado y quedan 10.7% de aire. */
  .hero__foto { --bucle-max: 1.05; }
}

/* ── EL BRILLO QUE RECORRE LA PIEZA ──────────────────────────────────
   Un reflejo angosto que cruza la pieza muy despacio, en bucle. No es un
   degradé sobre el rectángulo de la foto: está recortado por la silueta
   real del molde.

   LA MÁSCARA ES EL PROPIO ARCHIVO. `hero.webp` tiene canal alfa de verdad
   —medido: 68.04% transparente, 31.38% opaco y 0.58% de borde
   antialiasado—, así que el recorte de la pieza ya existe y no hace falta
   inventar nada. Se usa esa misma transparencia como máscara.

   ⚠️ ES EL MISMO ARCHIVO QUE LA IMAGEN, PERO ESO SOLO ES GRATIS SI EL
   SERVIDOR MANDA `Cache-Control`. Medido con `transferSize`: con una
   cabecera de caché larga el pedido de la máscara vale 0 bytes —sale de
   caché—; sin ninguna cabecera, el navegador vuelve a bajar los 87 KB
   enteros. Es un pedido diferido y de baja prioridad, así que no compite
   con el LCP, pero conviene que el sitio publique un `_headers` con caché
   larga para /img/.

   POR QUÉ DOS CAPAS. La de afuera lleva la máscara y se queda quieta
   respecto de la foto; la de adentro es sólo el degradé y es la que se
   mueve. Lo que se desplaza pasa por debajo de un recorte que no se
   desplaza, así que el brillo no puede pintar fuera del contorno ni en el
   cuadro negro que sobra a los costados: ahí la máscara vale cero.

   ⚠️ LOS CUATRO CANALES, Y NINGUNO SE PISA. Sobre la imagen ya escriben
   tres cosas distintas, y la capa de afuera tiene que copiar las tres o la
   máscara se despega del molde en cuanto la foto se mueve un píxel:
       transform → parallax de scroll y salida del riel   (--cap-x, --cap)
       scale     → el bucle lento de zoom                  (--bucle-max)
       translate → la respuesta al mouse                   (--rx, --ry)
   El barrido necesitaba un cuarto canal libre, y por eso vive en la capa
   de ADENTRO, que no copia nada: ahí `transform` está entero disponible.
   Es el mismo argumento que ya resolvió el zoom contra el parallax.

   `mix-blend-mode: color-dodge` y no una capa translúcida: el dodge
   amplifica lo que ya está iluminado y deja lo oscuro donde está, que es
   como se comporta un reflejo especular de verdad. Sobre el cromo levanta
   fuerte y en las sombras entre los dientes no hace nada. Sin color: es
   `--hueso`, el mismo del texto. */
.hero__brillo {
  position: absolute;
  inset: 0;
  /* Sin `z-index`: el orden natural de pintado ya lo deja encima de la
     foto —que es estática— y debajo de la viñeta, que viene después. */
  pointer-events: none;
  overflow: hidden;

  -webkit-mask-image: url('img/hero.webp');
          mask-image: url('img/hero.webp');
  /* `contain` + `center` reproduce exactamente el `object-fit: contain` de
     la imagen dentro del riel, y en el flujo vertical la caja de este
     elemento y la de la foto comparten centro y alto, así que también
     coinciden. Verificado en los dos modos. */
  -webkit-mask-size: contain;      mask-size: contain;
  -webkit-mask-position: center;   mask-position: center;
  -webkit-mask-repeat: no-repeat;  mask-repeat: no-repeat;

  mix-blend-mode: color-dodge;

  /* El espejo de los tres canales de la imagen. */
  transform: translate3d(var(--cap-x, 0px), var(--cap, 0px), 0) scale(var(--aleja, 1));
  animation: respiro-hero 16s ease-in-out infinite alternate;
  will-change: transform;
}

/* En teléfono la foto es otro archivo, así que la máscara también. */
@media (max-width: 799px) {
  .hero__brillo {
    -webkit-mask-image: url('img/hero-movil.webp');
            mask-image: url('img/hero-movil.webp');
  }
}

/* La luz. Angosta a propósito, y el ancho salió de mirarla: la primera
   versión tenía la banda al 7% del eje del degradé —unos 98 px sobre una
   pieza de 710— y eso es el 14% de la pieza: se leía como un velo pasando
   por encima de la foto, no como un reflejo. Al 2.4% son 38 px, o sea el
   5% de la pieza, que ya es un punto de luz.
   El ángulo la inclina para que cruce el lettering en diagonal y no como
   una persiana. */
/* ⚠️ EL ANCHO ES EXACTAMENTE EL DEL PADRE, y esto no es un detalle. Con la
   capa más ancha que el padre, el `translateX(±100%)` se mide sobre SU
   ancho, así que la banda recorría ±60% del padre y se daba vuelta antes
   de llegar al otro lado: medido, entraba, llegaba a la mitad y se iba
   por donde había venido. Con el mismo ancho, el centro de la banda va de
   −50% a +150% y cruza la pieza entera.
   El −20% de arriba y abajo es para que la diagonal cubra las esquinas. */
.hero__brillo-luz {
  position: absolute;
  inset: -20% 0;
  background: linear-gradient(100deg,
    rgba(232, 230, 223, 0)    47.4%,
    rgba(232, 230, 223, 0.42) 50%,
    rgba(232, 230, 223, 0)    52.6%);
  /* El cuarto canal: acá `transform` no lo usa nadie más. */
  transform: translate3d(-100%, 0, 0);
  animation: brillo-hero 15s cubic-bezier(.5, .1, .5, .9) infinite;
  will-change: transform;
}

/* 70% del ciclo es el viaje y el 30% restante es la espera. Sin la espera
   el brillo se lee como un barrido continuo de banner; con ella pasa cada
   tanto, que es lo que hace una pieza girando bajo una luz.
   Son 10.5 s de recorrido, de los cuales unos 5.2 s la banda está sobre la
   pieza, y 4.5 s de nada. */
@keyframes brillo-hero {
  0%   { transform: translate3d(-100%, 0, 0); }
  70%  { transform: translate3d( 100%, 0, 0); }
  100% { transform: translate3d( 100%, 0, 0); }
}

/* Viñeta, mismo criterio que el resto: oscurece el perímetro y asienta la
   foto en el negro de la página. El centro queda limpio, que es donde
   está el cromo. */
.hero__vineta {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(118% 82% at 50% 48%,
    rgba(var(--fondo-rgb, 11, 14, 13), 0) 42%,
    rgba(var(--fondo-rgb, 11, 14, 13), 0.25) 72%,
    rgba(var(--fondo-rgb, 11, 14, 13), 0.78) 100%);
}

.hero__contenido {
  position: relative;
  width: 100%;
  max-width: var(--ancho-max);
  margin-inline: auto;
  padding: var(--e-4) var(--margen) 0;
}

/* ── señal de scroll ─────────────────────────────────────────────────
   Una línea fina y una punta, en el gris de la paleta. Dibujada con
   bordes: no hay SVG, no hay imagen, no hay pedido nuevo.

   Se apaga apenas la persona empieza a bajar — el JS le pone .se-fue
   desde el mismo requestAnimationFrame que ya existe. Una vez que
   cumplió su función, estorba. */
.hero__senal {
  position: absolute;
  /* Sube al aire negro que hay entre la banda de foto y la etiqueta. Sobre
     la foto no se leía: el gris de la paleta contra el yeso claro daba
     1.34:1 medido. Sobre el negro de la página da 4.3:1.
     Absoluta, así no ocupa alto: puesta en el flujo empujaba el titular
     fuera del pliegue. */
  /* Anclada ABAJO y a la izquierda, alineada con el texto. Antes iba al
     tope del bloque de contenido y centrada, que funcionaba cuando el
     texto venía después de la foto. Con la composición superpuesta ese
     punto pasó a estar ARRIBA de la pieza —y centrado, en escritorio caía
     justo encima del molde—. Al pie del hero cumple su función y queda
     sobre negro en los dos tamaños. */
  bottom: var(--e-1);
  left: var(--margen);
  display: grid;
  justify-items: center;
  gap: 0.3rem;
  pointer-events: none;
  opacity: 1;
  transition: opacity var(--dur-corta) var(--ease);
}

.hero__senal.se-fue { opacity: 0; }

.hero__senal-linea {
  width: 1px;
  height: 1.25rem;
  background: linear-gradient(to bottom,
    rgba(110, 122, 118, 0) 0%,
    var(--humo) 100%);
}

/* La punta es un cuadrado al que se le dejan dos bordes y se gira 45°.
   Es la flecha más liviana que existe: cero bytes de imagen. */
.hero__senal-punta {
  width: 0.5rem;
  height: 0.5rem;
  border-right: 1px solid var(--humo);
  border-bottom: 1px solid var(--humo);
  rotate: 45deg;
  animation: senal-baja 3.2s var(--ease) infinite;
}

/* Deriva mínima, 3px. No parpadea ni rebota: solo insinúa la dirección. */
@keyframes senal-baja {
  0%, 100% { translate: 0 0;   opacity: 0.75; }
  50%      { translate: 0 3px; opacity: 1; }
}

/* ── LA PLACA QUE CUBRE LA FOTO · SOLO MÓVIL ─────────────────────────
   La foto se queda quieta y el bloque de texto sube por encima sobre una
   placa negra sólida, tapándola de abajo hacia arriba. Cuando la cubrió
   del todo, la sección se libera y el scroll sigue normal.

   Es `position: sticky` y nada más: cero JavaScript, cero peso, y —lo que
   importa para la restricción— **cero alto agregado**. Sticky no ocupa
   espacio de layout, así que todas las posiciones del documento quedan
   exactamente donde estaban y el botón de AGENDAR CITA llega en el mismo
   punto del scroll que antes.

   ⚠️ NO SE USA NINGUNA UNIDAD DE VIEWPORT acá. El `top` va en rem y los
   altos son los naturales del contenido. Por eso el gesto no salta cuando
   aparece o desaparece la barra del navegador en el teléfono, que es
   justamente lo que rompe este tipo de efecto cuando se arma con vh.

   ⚠️ SOLO MÓVIL, y no por preferencia. Para que la placa cubra la foto del
   todo, el recorrido con la foto fija tiene que ser al menos igual al alto
   de la foto. En móvil hay 455 px disponibles contra 311 necesarios: entra
   con 144 de sobra. En escritorio hay 416 contra 634: faltan 218 px, y
   estirarlo estaba prohibido. Escritorio queda como estaba. */
/* ── COMPOSICIÓN SUPERPUESTA · MÓVIL ─────────────────────────────────
   Acá no hay ancho para poner el texto al costado de la pieza —serían 150
   px de columna—, así que la superposición es vertical: la pieza arriba y
   el texto apoyado sobre el negro que queda debajo, dentro del mismo
   cuadro.

   ⚠️ LA GARANTÍA es la misma idea que en escritorio, en el otro eje. La
   pieza termina en el 85.1% del alto de la foto, y la foto mide 0.8 veces
   el ancho de la ventana: o sea que la pieza termina en el 68.1% del ancho.
   El texto arranca ahí más un margen. Las dos medidas dependen del mismo
   ancho, así que la separación no cambia con el tamaño de pantalla ni con
   la barra del navegador. */
@media (max-width: 799px) {
  .hero {
    /* Dos bandas, y las dos por ANCHO. Nunca por alto: una consulta de
       alto haría saltar toda la composición cuando aparece o desaparece la
       barra del navegador, que es justo lo que no puede pasar.

       Las pantallas angostas son las que menos alto tienen en proporción
       al texto, así que ahí la pieza cede. Manda el botón: si hay que
       elegir entre tamaño de pieza y que AGENDAR CITA entre en la primera
       pantalla, entra el botón. */
    --pieza: 68;
    display: grid;
    /* El relleno de abajo pasa a la placa: si queda en la sección, entre
       el final del texto y el final del hero aparece una franja
       transparente y la foto se ve por ese hueco. Mismo alto total. */
    padding-bottom: 0;
  }

  .hero__foto,
  .hero__contenido { grid-area: 1 / 1; }

  /* align-self: start en la foto, si no el grid la estira al alto de la
     celda —que la marca el texto— y el sticky se comporta raro. */
  .hero__foto      { align-self: start; }

  /* --pieza es el ancho de la foto como porcentaje de la pantalla, y es la
     ÚNICA perilla de esta composición: de ahí sale el tamaño del molde y
     también dónde arranca el texto. Bajarla achica la pieza y sube el
     texto, las dos cosas a la vez y sin desincronizarse.

     El 0.661 no es un número elegido: el marco mide 1500/1400 = 1.0714
     veces su ancho, y el molde termina en el 61.7% del alto del marco.
     1.0714 × 0.617 = 0.661. Cambiando el archivo hay que recalcularlo. */
  .hero__foto img {
    max-width: calc(var(--pieza) * 1%);
    margin-inline: auto;
  }

  .hero__contenido {
    align-self: start;
    padding-top: calc(var(--pieza) * 0.661vw + var(--e-3));
  }

  .hero__foto {
    position: sticky;
    top: var(--alto-cabecera);
    z-index: 0;
  }

  /* La placa. Opaca y del negro del sitio, para que el texto NUNCA quede
     leyéndose sobre el yeso: viaja sobre el fondo, no sobre la imagen.

     ⚠️ background-clip: content-box. Sin eso el fondo pinta TODA la caja,
     relleno superior incluido — y como ese relleno es el que baja el texto
     por debajo de la pieza, la placa tapaba la foto entera desde el primer
     cuadro. Recortado al contenido, el negro arranca justo donde arranca
     el texto y la pieza se ve. */
  .hero__contenido {
    z-index: 1;
    background: var(--fondo);
    background-clip: content-box;
    padding-bottom: var(--e-5);
  }
}

/* Desde 375 px de ancho ya sobra alto para una pieza más grande. */
@media (min-width: 375px) and (max-width: 799px) {
  .hero { --pieza: 86; }
}

.hero__etiqueta { color: var(--humo-txt); }

.hero .titular { margin-top: var(--e-2); font-size: var(--fs-hero); }

.hero .bajada { margin-top: var(--e-3); }

.hero__acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-2);
  margin-top: var(--e-4);
}

/* ── COMPOSICIÓN SUPERPUESTA · ESCRITORIO ────────────────────────────
   El texto deja de estar debajo de la foto y comparte la misma pantalla:
   la pieza a la derecha, el texto sobre el negro de la izquierda.

   ⚠️ LA GARANTÍA DE QUE EL TEXTO NUNCA PISA LA PIEZA es geométrica, no
   visual. `hero.webp` está construido con la pieza empezando en el 45%
   del ancho de la imagen, y la imagen se muestra a ancho completo sin
   recorte. El texto está topado en el 38% del ancho del contenedor, que
   siempre es igual o menor que el ancho de la ventana. Las dos medidas son
   fracciones del mismo ancho, así que la separación se mantiene sola en
   cualquier tamaño de pantalla: no hay un punto de quiebre donde se toquen.

   Ninguna cama, veladura ni caja detrás del texto. El texto va sobre el
   negro del sitio y punto: si no entra, se mueve o se achica. */
@media (min-width: 800px) {
  .hero {
    display: grid;
    padding-bottom: 0;
  }

  /* Los dos en la MISMA celda: una sola composición, no dos bloques. */
  .hero__foto,
  .hero__contenido { grid-area: 1 / 1; }

  /* Sin recorte: la proporción del archivo (2400x1080) es exactamente la
     de la caja, así que la pieza entra entera y no la corta el contenedor
     como pasaba con el tope de alto anterior. */
  .hero__foto img {
    max-height: none;
    object-fit: initial;
  }

  .hero__contenido {
    align-self: center;
    padding-block: var(--e-4);
    position: relative;
    z-index: 1;
  }

  /* El tope se calcula contra la VENTANA, no contra el contenedor, y esa
     es la corrección que hace que la garantía se sostenga en pantallas
     anchas. Con un 38% del contenedor, a 2560 px la holgura caía a 3 px:
     el contenedor está topado en 76rem y centrado, así que su borde
     izquierdo se corre a la derecha mientras la pieza sigue arrancando en
     el 45% de la ventana. Acotando el borde derecho del texto al 40% del
     ancho de ventana —descontando el desplazamiento del contenedor— la
     holgura se mantiene arriba de 40 px de 800 a 2560. */
  .hero__contenido > *,
  .hero .bajada {
    max-width: calc(40vw - max(var(--margen), (100vw - var(--ancho-max)) / 2 + var(--margen)));
  }
}

/* ── 7d. ESCENA 04 · EL PROCESO ──────────────────────────────────────
   Por defecto —móvil, sin JS, o con prefers-reduced-motion— los cuatro
   pasos se leen uno abajo del otro, cada uno con su foto. El modo
   secuencia lo enciende el JS agregando .es-secuencia, y solo en
   escritorio. El marcado es el mismo en los dos casos. */

.proceso {
  padding: var(--e-7) var(--margen);
  border-top: 1px solid var(--linea);
}

/* La costura hero → proceso.

   Hasta el paso 27 entre estas dos secciones vivía el zoom, que era un
   panel a sangre y sin relleno: la sección siguiente podía abrir con todo
   su aire porque venía de una imagen a pantalla completa. Sacado el zoom,
   el relleno de arriba del proceso quedó pegado al relleno de abajo del
   hero y los dos se sumaron. Medido, en móvil daba 121–127 px entre la
   señal de scroll y EL PROCESO, contra 49–72 px en escritorio: el
   teléfono, que es donde menos alto sobra, era el que más negro vacío
   tenía. Se leía como un hueco, no como una separación.

   El selector dice la condición exacta: esto vale cuando el proceso viene
   inmediatamente después del hero, que es el caso que se rompió. Si
   mañana vuelve a haber una escena en el medio, el proceso recupera solo
   su aire completo sin tocar nada.

   Vale en todos los anchos. En escritorio con el modo secuencia encendido
   no cambia nada —`.proceso.es-secuencia` ya pone padding-block: 0 y gana
   por orden—, pero sí arregla los dos casos en los que ese modo no se
   enciende: sin JavaScript y con movimiento reducido, donde la costura
   medía 140 px. */
.hero + .proceso { padding-top: var(--e-4); }

.proceso__intro { margin-bottom: var(--e-5); }

.proceso__pista {
  width: 100%;
  max-width: var(--ancho-max);
  margin-inline: auto;
}

.proceso__rail { display: none; }

.proceso__pasos {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--e-6);
}

.paso {
  display: grid;
  gap: var(--e-3);
  align-content: start;
}

.paso__texto { display: grid; gap: var(--e-1); max-width: 30rem; }

.paso__titulo { margin-block: var(--e-1) var(--e-1); }

.paso .cuerpo { color: var(--hueso); }

/* ── EL CARRIL DE FOTOS ──────────────────────────────────────────────
   Cada paso tiene su fila de fotos. En modo secuencia esa fila cruza de
   lado siguiendo el scroll; sin secuencia —sin JS, con movimiento
   reducido— es una grilla quieta, en orden.

   Altura uniforme, ancho natural. Las fotos no comparten proporción: la
   principal es 2/3 y las de apoyo 3/4. Recortarlas todas a la misma forma
   les comía los bordes, así que en vez de igualar la forma se iguala el
   ALTO y cada una se queda con el ancho que le toca. El carril mide más o
   menos según lo que lleve, y de eso se encarga el recorrido. */
.paso__carril { min-width: 0; }

.paso__vagon {
  list-style: none;
  margin: var(--e-2) 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--e-2);
}

.paso__toma { min-width: 0; }

.paso__toma img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Mientras la foto todavía no se pidió, su <img> lleva `data-src` y no se
   muestra: adentro solo tiene un píxel transparente. El JS le borra el
   atributo al ponerle el `src` de verdad, y ahí aparece.

   Sin JavaScript el atributo no se borra nunca, así que este <img> queda
   escondido para siempre y lo que se ve es la copia del <noscript>, que
   trae la foto real. Sin esta regla se veían las dos: once cajas negras
   vacías y once fotos. */
.paso__toma img[data-src] { display: none; }

@media (min-width: 800px) {
  .paso__vagon { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* ── modo secuencia ──────────────────────────────────────────────────
   Se enciende en TODOS los anchos, no solo en escritorio: el 90% de la
   gente entra por teléfono y el mecanismo tiene que ser el mismo ahí.
   Solo se apaga con prefers-reduced-motion y sin JavaScript.

   El panel queda fijo con el titular, el índice y el texto del paso
   quietos, y la fila de fotos de ese paso cruza de lado siguiendo el
   scroll. Cuando termina de cruzar, la sección libera y entra el paso
   siguiente. El dedo sigue haciendo scroll vertical de toda la vida: no
   hay ni un listener de gesto lateral, ni scroll secuestrado, ni nada que
   impida parar a mitad de camino — la posición es una función pura del
   scroll, así que donde se para, se queda. */

.proceso.es-secuencia { padding-block: 0; }

/* Los dos altos los congela el JS en píxeles la primera vez que mide, y
   solo los vuelve a tomar si cambia el ancho o el alto cambia más de un
   20%. Los valores de respaldo son para el instante entre que se agrega
   la clase y se mide, y para el caso de que el JS falle a mitad. */
.proceso.es-secuencia .proceso__pista {
  position: relative;
  height: calc(var(--alto-panel, calc(100svh - var(--alto-cabecera))) + var(--recorrido, 200vh));
}

.proceso.es-secuencia .proceso__fijo {
  position: sticky;
  top: var(--alto-cabecera);
  height: var(--alto-panel, calc(100svh - var(--alto-cabecera)));
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  row-gap: var(--e-2);
  padding-block: var(--e-2);
}

/* En escritorio sobra alto y el panel puede respirar. En teléfono cada
   píxel vertical que se le saca al relleno se lo lleva la foto, que es
   lo que la persona vino a ver. */
@media (min-width: 800px) {
  .proceso.es-secuencia .proceso__fijo {
    row-gap: var(--e-3);
    padding-block: var(--e-4);
  }
}

.proceso.es-secuencia .proceso__intro {
  margin-bottom: 0;
  padding-bottom: var(--e-3);
  border-bottom: 1px solid var(--linea);
}
.proceso.es-secuencia .proceso__intro .titular { font-size: var(--fs-l); }

.proceso.es-secuencia .proceso__cuerpo {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  column-gap: var(--e-3);
  min-height: 0;
}

@media (min-width: 800px) {
  .proceso.es-secuencia .proceso__cuerpo {
    grid-template-columns: auto minmax(0, 1fr);
  }
}

/* Índice de la secuencia. La marca activa se lee de lejos: hueso lleno,
   un punto más grande y una raya que se estira. Los otros tres quedan
   en el gris de texto, no en el gris de línea: tienen que verse. */
/* El índice, el título del paso y la foto arrancan en la misma línea.
   Alineado al centro, el texto quedaba flotando en el medio con vacío
   arriba y abajo; así el bloque se lee como una sola composición. */
.proceso.es-secuencia .proceso__cuerpo { align-items: start; }

/* El índice lateral solo en escritorio. En móvil ocupaba 90 px de los 351
   que hay de ancho útil, o sea que la ventana del carril se quedaba con
   261: las fotos entraban a un cuarto de pantalla. En su lugar, en móvil
   vuelve el número arriba del subtítulo. */
@media (min-width: 800px) {
.proceso.es-secuencia .proceso__rail {
  display: grid;
  gap: var(--e-2);
  list-style: none;
  margin: var(--e-4) 0 0;
  padding: 0;
}

.proceso.es-secuencia .proceso__rail li {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  color: var(--humo-txt);
  font-size: var(--fs-mono-l);
  line-height: 1;
}

.proceso.es-secuencia .proceso__rail li::after {
  content: '';
  width: 12px;
  height: 1px;
  background: currentColor;
  transition: width var(--dur-corta) var(--ease);
}

.proceso.es-secuencia .proceso__rail li.esta-activo {
  color: var(--hueso);
  font-weight: 500;
}

.proceso.es-secuencia .proceso__rail li.esta-activo::after {
  width: 40px;
  height: 2px;
}
}

.proceso.es-secuencia .proceso__pasos {
  display: grid;
  gap: 0;
  height: 100%;
  min-height: 0;
}

/* En secuencia el número lo lleva el índice de la izquierda: repetirlo
   arriba del título sería decir dos veces lo mismo. */
.proceso.es-secuencia .paso .numero { display: none; }

/* Los tres pasos ocupan la misma celda y se encienden por opacidad.
   No se usa visibility ni display: así siguen en el árbol de
   accesibilidad y un lector de pantalla los lee los tres, en orden.

   El texto arriba y el carril abajo, a lo ancho, en móvil y en escritorio
   igual. Antes en escritorio el texto iba a la izquierda y la foto a la
   derecha; con una fila que cruza, esa composición partía la pantalla en
   dos y el mecanismo se leía distinto según el ancho. */
/* El primer renglón tiene un mínimo para que el bloque de texto ocupe lo
   mismo en los tres pasos. Sin eso, «El acabado» —una sola línea— dejaba
   el carril más alto que en «El molde» —cuatro líneas—, y las fotos
   cambiaban de tamaño al pasar de un paso al otro. Medido: la fila del
   paso 03 daba 1307 px contra 1091 del 01, con las mismas cuatro fotos. */
.proceso.es-secuencia .paso {
  grid-area: 1 / 1;
  display: grid;
  grid-template-rows: minmax(11.5rem, auto) minmax(0, 1fr);
  gap: var(--e-3);
  height: 100%;
  min-height: 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-corta) var(--ease);
}

/* La ventana por la que se ve pasar la fila. Recorta y nada más: NO es un
   contenedor de scroll y el JS no le escribe `scrollLeft`.

   📌 Hubo una versión intermedia que sí lo hacía. La idea era que un
   contenedor de scroll de verdad hiciera funcionar `loading="lazy"`, y no
   funcionó —el navegador se bajaba las once fotos igual— así que la carga
   terminó resolviéndose desde el JS. Con eso, escribir `scrollLeft` dejó
   de comprar nada y quedó costando portabilidad: mover un contenedor con
   `overflow: hidden` por código es de las cosas que Safari en iPhone
   trata distinto que Chrome, y este sitio se mira sobre todo en
   teléfonos. Se volvió a `transform`, que es lo que anda en todos lados
   y además lo compone la GPU. */
/* El alto lo congela el JS en píxeles (--alto-carril) la primera vez que
   mide. Es la pieza que faltaba para que la barra del navegador no rompa
   nada: las fotos se dimensionan por alto, así que si el alto siguiera al
   alto de ventana, al esconderse la barra las fotos se agrandarían, la
   fila mediría distinto y el recorrido guardado dejaría de corresponder.
   Medido: el desliz del paso 01 pasaba de 741 a 292 px. */
.proceso.es-secuencia .paso__carril {
  overflow: hidden;
  height: var(--alto-carril, 100%);
  min-height: 0;
}

/* La fila. Alto 100% y `aspect-ratio` en cada toma: de ahí sale el ancho
   de cada una sin que nadie lo escriba a mano. */
.proceso.es-secuencia .paso__vagon {
  display: flex;
  align-items: stretch;
  gap: var(--e-2);
  height: 100%;
  width: max-content;
  margin: 0;
  transform: translate3d(var(--x, 0px), 0, 0);
  will-change: transform;
}

.proceso.es-secuencia .paso__toma {
  flex: 0 0 auto;
  height: 100%;
  width: auto;
}

/* ── UNA FOTO POR PANTALLA EN TELÉFONO ───────────────────────────────
   En escritorio cada toma se dimensiona por alto y se queda con su propia
   proporción, así que entran dos y media en la ventana y eso está bien:
   hay ancho de sobra. En un teléfono ese mismo criterio metía dos y media
   fotos en 350 px y quedaban en 130 px cada una — miniaturas.

   Acá cada toma ocupa el ancho ENTERO del carril, y el separador termina
   de empujar a la siguiente fuera de la pantalla: se ve una por vez.

   ⚠️ Y por eso mismo la proporción la manda la pantalla, no el archivo:
   una foto vertical a ancho completo no entra de alto en el panel fijo,
   que además tiene que sostener el titular y el texto. Medido en un
   390 × 844: quedan 422 px de alto para 350 de ancho. A 2:3 harían falta
   525 y a 3:4, 467. Así que en teléfono las once se recortan a la misma
   caja, y recortarse todas igual es lo que hace que se lea como una
   decisión y no como un accidente. */
.proceso.es-secuencia .paso.esta-activo {
  opacity: 1;
  pointer-events: auto;
}

/* ⚠️ En escritorio el texto va AL COSTADO, no arriba. Medido: con el texto
   arriba, la ventana del carril mide 1127 px en un 1440 y las cuatro fotos
   entran casi enteras — el paso de tres no tenía literalmente nada que
   desplazar (desliz 0 px) y el mecanismo no ocurría. Con el texto al lado,
   la ventana baja a ~540 px y las fotos son más altas, así que siempre
   sobra fila para cruzar. El comportamiento es el mismo en los dos anchos;
   lo que cambia es dónde se apoya el texto. */
.proceso.es-secuencia .paso__texto {
  max-width: 34rem;
  padding-top: var(--e-2);
}

/* ── TELÉFONO: LA FOTO MANDA ─────────────────────────────────────────
   La foto va a sangre, de borde a borde de la pantalla, y todo lo demás
   se acomoda alrededor. Es la sección que prueba que el taller existe:
   cuando hay que elegir entre que entre el texto cómodo o que la foto se
   vea, gana la foto.

   Qué se le sacó al texto para hacerle lugar, y cuánto:
     · el número y el subtítulo pasan a la misma línea      −27 px
     · el párrafo baja de 17 a 15 px con interlínea 1.35    −20 px
     · el mínimo del renglón de texto baja de 11.5 a 7.5rem −64 px
   Con eso la foto pasa de 350 × 414 a 390 × ~500: de 0.845 de proporción
   a ~0.78, que es prácticamente 3 / 4 — o sea que las ocho fotos de la
   fila, que son 3 / 4 nativas, dejaron de recortarse. */
@media (max-width: 799px) {
  /* A sangre. El carril se sale del relleno lateral de la sección y del
     ancho máximo de la pista con el truco del margen negativo. */
  .proceso.es-secuencia .paso__carril {
    width: 100vw;
    margin-inline: calc(50% - 50vw);
  }

  .proceso.es-secuencia .paso {
    grid-template-rows: minmax(7.5rem, auto) minmax(0, 1fr);
    gap: var(--e-1);
  }

  /* Cada píxel de relleno que se le saca al panel se lo lleva la foto.
     En escritorio esto no cambia: la regla vive dentro del media. */
  .proceso.es-secuencia .proceso__fijo {
    padding-block: var(--e-1);
    row-gap: var(--e-1);
  }

  .proceso.es-secuencia .proceso__intro { padding-bottom: var(--e-1); }

  /* Número y subtítulo en la misma línea: una línea menos de alto. */
  .proceso.es-secuencia .paso__texto {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    column-gap: var(--e-2);
    row-gap: var(--e-1);
    padding-top: 0;
  }

  .proceso.es-secuencia .paso .cuerpo {
    flex: 1 0 100%;
    font-size: 0.9375rem;                 /* 15 px */
    line-height: 1.35;
  }

  .proceso.es-secuencia .paso__toma {
    /* En píxeles y escrito por el JS, no `width: 100%`. Con el porcentaje
       se arma una dependencia circular —la fila mide `max-content`, o sea
       la suma de sus hijos, y los hijos querían medir el 100% de la
       fila— y el navegador la resuelve colapsando: las cuatro fotos del
       paso 01 daban 634 px en total en vez de 1464.

       `flex-basis` además del `width`: sin él, una toma cuya foto todavía
       no se pidió está vacía y mide 0, y la fila salía más corta de lo
       que va a ser. Con la base fija, el hueco ya ocupa su lugar. */
    /* Tope de cuadratura. En un teléfono chico el texto ocupa una línea
       más y al carril le quedan 325 px de alto para 320 de ancho: la caja
       daría 0.985, casi cuadrada, y una foto vertical ahí pierde un
       tercio. Con este `min` la caja nunca pasa de 0.85, y lo que cede es
       el ancho: en un 360 la foto mide 276 de 320 —el 86%— y del borde
       asoma un dedo de la siguiente, que además avisa que hay más. En un
       390 y en un 430 el tope no llega a actuar y la foto va a ancho
       completo. */
    flex: 0 0 auto;
    width: min(var(--ancho-toma, 100%), calc(var(--alto-carril, 100vh) * 0.85));
    aspect-ratio: auto;
  }
}

@media (min-width: 800px) {
  .proceso.es-secuencia .paso {
    grid-template-columns: minmax(0, 22rem) minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr);
    column-gap: var(--e-4);
    align-items: stretch;
  }
  .proceso.es-secuencia .paso__texto { align-self: start; padding-top: var(--e-4); }
}

/* El rótulo NO crece en el modo fijo. Acá estaba el clamp que lo llevaba
   a 60 px, por encima del titular de la sección. */
.proceso.es-secuencia .paso .cuerpo { font-size: var(--fs-bajada); line-height: 1.45; }

/* En móvil el índice lateral no entra, así que el número vuelve arriba del
   subtítulo: sin él no hay forma de saber en qué paso de los tres estás. */
.proceso.es-secuencia .paso .numero { display: block; }

@media (min-width: 800px) {
  .proceso.es-secuencia .paso .numero { display: none; }
}

/* Con las fotos grandes, el grano tiene que sostenerlas, y un punto menos
   de brillo las asienta en el negro de la página. */
.proceso.es-secuencia .marco--grano::after { opacity: 0.48; }

.pie {
  padding: var(--e-5) var(--margen) var(--e-4);
  border-top: 1px solid var(--linea);
  color: var(--humo-txt);
}

hr, .divisor {
  height: 1px;
  border: 0;
  background: var(--linea);
  margin-block: var(--e-4);
}

/* ── 7e. ESCENA 05 · MATERIALES ─────────────────────────────────── */
.materiales {
  /* El negro del sitio, no un tono propio. La sección tenía --acero y era
     el único bloque con fondo distinto de todo el recorrido: se leía como
     una tarjeta apilada en vez de como parte del mismo espacio. */
  background: var(--fondo);
  padding: var(--e-7) var(--margen);
  border-top: 1px solid var(--linea);
}

.materiales__caja,
.preguntas__caja,
.kit__caja,
.cierre__caja,
.pie__caja {
  width: 100%;
  max-width: var(--ancho-max);
  margin-inline: auto;
}

/* Un solo bloque: foto y ficha lado a lado. Con la grilla de tres
   columnas y dos slots vacíos la sección se leía rota. */
.material {
  display: grid;
  gap: var(--e-3);
  margin-top: var(--e-5);
}

.material__foto { --proporcion: 2 / 3; max-width: 22rem; }

/* Viñeta, mismo criterio que el hero: oscurece el perímetro y
   deja limpio el centro, que es donde está la pieza. Va en el marco y no
   en el archivo, así la hereda cualquier foto que entre después
   —cromo/cobalto, esmalte, o el reemplazo de la plata— sin volver a
   procesar nada.

   ⚠️ Y por eso mismo NO se toca cuando entre la foto del esmalte: la
   viñeta es
   neutra, solo baja luminancia. El color del esmalte se queda adentro de
   la foto y la página no lo acompaña con nada.
   El parallax mueve la foto por debajo; la viñeta se queda quieta. */
.material__foto::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(96% 74% at 50% 52%,
    rgba(var(--fondo-rgb, 11, 14, 13), 0) 44%,
    rgba(var(--fondo-rgb, 11, 14, 13), 0.30) 74%,
    rgba(var(--fondo-rgb, 11, 14, 13), 0.82) 100%);
}

/* ── EL LEMA Y EL CUERPO DE CADA CARTA ───────────────────────────────
   El lema es la frase corta que va debajo del nombre —«Resistencia que
   perdura.»—. No es un subtítulo tipográfico nuevo: es la misma `.bajada`
   del resto del sitio, un escalón por debajo, para no inventar un cuarto
   nivel de jerarquía en una sección que ya tiene tres.

   No lo llevan las cinco: cromo/cobalto, plata y los dos oros sí; el
   esmalte no, porque su texto definitivo no trae uno. La carta se arma
   con lo que hay, sin huecos reservados. */
.material__lema {
  margin-top: var(--e-1);
  font-size: var(--fs-bajada);
  line-height: 1.35;
  color: var(--hueso);
  text-wrap: balance;
}

.material__cuerpo { margin-top: var(--e-2); }


/* Ficha técnica en monoespaciada. Ahora lleva UN solo renglón —la pureza—
   y sólo en los tres materiales que tienen ley: plata 925, oro 14K y oro
   18K. Cromo/cobalto y esmalte no la llevan.

   ⚠️ Ya no hay placeholders. Los «[ pendiente de confirmar ]» que había en
   las cuatro cartas se fueron: los datos llegaron. El renglón de «Acabado»
   desapareció entero, no quedó vacío — el texto definitivo no lo trae. */
.material__ficha {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.75rem var(--e-2);
  margin-top: var(--e-2);
  padding-top: var(--e-2);
  border-top: 1px solid var(--linea);
}

/* Sin el fondo acero, la ficha se quedaba sin nada que la separara del
   resto. La estructura vuelve por donde corresponde: una línea fina de la
   paleta bajo cada fila y un poco más de aire entre ellas. Se lee como
   tabla de especificaciones, no como tarjeta — no hay ningún rectángulo
   nuevo, solo horizontales. */
.material__ficha dt,
.material__ficha dd {
  padding-bottom: 0.55rem;
  border-bottom: 1px solid var(--linea);
}

.material__ficha dt { color: var(--humo-txt); }
.material__ficha dd { margin: 0; }

.materiales__pie { margin-top: var(--e-4); }

@media (min-width: 800px) {
  .material {
    grid-template-columns: minmax(0, 1fr) auto;
    column-gap: var(--e-5);
    align-items: center;
    justify-content: start;
  }
  .material__foto { width: 22rem; }
}

/* ── LA PILA DE CARTAS ───────────────────────────────────────────────
   La sección se fija y cada material entra desde abajo y se apoya encima
   del anterior. El de abajo no desaparece: sube un poco y se achica, y se
   sigue viendo. Cuando la cuarta se apoya, la pila libera.

   Se enciende en TODOS los anchos. Solo se apaga con
   prefers-reduced-motion y sin JavaScript, donde los cuatro materiales se
   leen uno debajo del otro.

   TRES DECISIONES, cada una con su porqué:

   1 · LA FICHA NO VIAJA EN LA CARTA. Vive en un bloque fijo —al costado
       en escritorio, arriba en teléfono— que se reemplaza cuando cambia
       la carta de arriba. Si viajara adentro, al quedar tapada se
       perdería. Además deja la carta como foto pura, que es lo que hace
       que una pila de cartas se lea como una pila de fotografías, y evita
       de raíz que una palabra termine apoyada sobre el yeso o el metal.

   2 · LA SEPARACIÓN SALE DEL ESPACIO, NO DE UNA LÍNEA. Cada carta
       enterrada sube 28 px y se achica un 3%: el negro de la página que
       asoma a los costados es la separación. Sin bordes, sin sombras, sin
       rectángulos.

       📌 Y hace falta que sea así, no es estética. Medida la luminancia
       de las cuatro fotos: cromo/cobalto 0.466 · plata 0.361 · oro 0.069
       · esmalte 0.082. El orden pone dos claras seguidas, así que plata
       sobre cromo/cobalto es claro sobre claro y el borde entre fotos
       puede desaparecer. El retroceso de escala garantiza la separación
       pase lo que pase con los tonos.

   3 · LA CARTA NO ES IGUAL EN LOS DOS LADOS. En escritorio la ficha va al
       costado, así el alto entero queda para la pila y la carta se queda
       en 2:3 nativo: cero recorte. En teléfono la ficha va arriba y la
       carta queda en el techo de recorte de teléfono. NO va a sangre:
       a ancho completo el recorte saltaba a 22%, y el margen lateral es
       justo el negro que hace visible el retroceso de la pila.          */

.materiales.es-fija { padding-block: 0; }

/* Los tres altos y el recorrido los congela el JS en píxeles, igual que en
   la escena 04 y por el mismo motivo: la barra del navegador cambia el
   alto de la ventana y no puede mover los límites entre cartas debajo del
   dedo. Los valores de respaldo son para el instante entre que se agrega
   la clase y se mide. */
.materiales.es-fija .materiales__pista {
  position: relative;
  height: calc(var(--alto-panel, calc(100svh - var(--alto-cabecera))) + var(--recorrido, 200vh));
}

/* ⚠️ EL ANCHO DE PÁGINA. Esta sección era la única del sitio que no
   respetaba `--ancho-max`: el panel no vive dentro de un `.contenedor`, así
   que se estiraba hasta el relleno de la escena. Medido en 1920, arrancaba
   288 px más a la izquierda que la cabecera y que el proceso. No era solo
   aire: estaba desalineada. */
.materiales__fijo,
.materiales__pie {
  max-width: var(--ancho-max);
  margin-inline: auto;
}

.materiales.es-fija .materiales__fijo {
  position: sticky;
  top: var(--alto-cabecera);
  height: var(--alto-panel, calc(100svh - var(--alto-cabecera)));
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  row-gap: var(--e-1);
  padding-block: var(--e-1);
}

/* ⚠️ EN ESCRITORIO LA CARTA ESTÁ LIMITADA POR EL ALTO, NUNCA POR EL ANCHO.
   La columna de la foto mide 717 px y la carta usaba 332: sobraban 385 px
   de ancho sin usar. Achicar la ficha de costado no agranda nada. El único
   material del que se puede sacar tamaño es ALTO DE PANEL, y cada píxel
   vertical que se libera devuelve 0,67 px de ancho.

   Por eso el pie deja de ser un renglón propio y pasa a la esquina
   inferior izquierda del cuerpo, debajo de la ficha, donde la columna de
   texto tenía 450 px de alto vacíos. No se saca nada de la pantalla: se
   deja de reservarle un renglón exclusivo a algo que ya tenía sitio. */
@media (min-width: 800px) {
  .materiales.es-fija .materiales__fijo {
    row-gap: var(--e-1);
    padding-block: var(--e-1);
  }
}

/* El pie ya no vive dentro del panel: está fuera de la pista, después de
   toda la pila. El panel se queda con dos renglones y el segundo es entero
   de la carta. */
.materiales.es-fija .materiales__fijo {
  grid-template-rows: auto minmax(0, 1fr);
}

.materiales.es-fija .materiales__intro {
  margin-bottom: 0;
  padding-bottom: var(--e-1);
  border-bottom: 1px solid var(--linea);
}
.materiales.es-fija .materiales__intro .titular { font-size: var(--fs-l); }

@media (min-width: 800px) {
  .materiales.es-fija .materiales__intro { padding-bottom: var(--e-1); }
}

.materiales.es-fija .materiales__cuerpo {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  column-gap: var(--e-3);
  min-height: 0;
}

/* El índice lateral solo en escritorio: en un teléfono se comería el ancho
   que necesita la carta. Ahí el nombre del material, que está en la ficha
   de arriba, ya dice en cuál estás.

   ⚠️ El `display: none` de base va FUERA de la consulta: el <ul> lo escribe
   el JS y, sin esta línea, en teléfono aparecía la lista cruda encima del
   nombre del material. Mismo tratamiento que `.proceso__rail`. */
.materiales__rail { display: none; }

/* El titular deja de ocupar un renglón propio y se muda a la banda
   izquierda, encima de la ficha, donde había 500 px de alto vacíos. Los
   72 px que liberaba ese renglón se los lleva enteros la carta.

   Va desde 1100 y no desde 800 por geometría medida: más abajo la carta
   es tan ancha en relación a la ventana que su esquina superior derecha
   llegaría hasta debajo del titular. A 1100 la carta arranca en x = 512 y
   la banda del titular termina en 464. */
@media (min-width: 1100px) {
  .materiales.es-fija .materiales__fijo {
    grid-template-columns: minmax(0, 29rem) minmax(0, 1fr);
    grid-template-rows: minmax(0, 1fr);
  }

  .materiales.es-fija .materiales__intro {
    grid-area: 1 / 1;
    align-self: start;
  }

  .materiales.es-fija .materiales__cuerpo {
    grid-area: 1 / 1 / 2 / 3;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
  }

  .materiales.es-fija .materiales__rail {
    display: grid;
    gap: var(--e-2);
    list-style: none;
    margin: 0;
    padding: 0;
  }

  /* Sin texto adentro: la marca ES el guion. El ancho va fijo en los 40 px
     que mide el guion activo para que la columna no se mueva mientras uno
     se estira y el otro se encoge — con `auto` el ancho seguía al máximo
     del momento y la carta temblaba de costado en cada cambio. */
  .materiales.es-fija .materiales__rail li {
    display: flex;
    align-items: center;
    width: 40px;
    height: 1em;
    color: var(--humo-txt);
    font-size: var(--fs-mono-l);
    line-height: 1;
  }

  .materiales.es-fija .materiales__rail li::after {
    content: '';
    width: 12px;
    height: 1px;
    background: currentColor;
    transition: width var(--dur-corta) var(--ease);
  }

  .materiales.es-fija .materiales__rail li.esta-activo {
    color: var(--hueso);
    font-weight: 500;
  }

  .materiales.es-fija .materiales__rail li.esta-activo::after {
    width: 40px;
    height: 2px;
  }
}

/* ⚠️ `overflow: hidden` no es cosmética: la carta que todavía no llegó
   está esperando por debajo de la pila, y sin recorte se veía asomar en
   el borde inferior del panel. Recortada arriba tampoco molesta: las
   cartas enterradas suben como mucho lo que sobra entre el tope de la
   pila y el tope de la carta. */
.materiales.es-fija .materiales__lista {
  display: grid;
  gap: 0;
  height: 100%;
  min-height: 0;
  overflow: hidden;
}

/* Los cuatro materiales ocupan la MISMA celda. Las fichas se superponen en
   su renglón y las fotos en el suyo: eso es la pila.

   A diferencia del modo anterior, acá el bloque NO se apaga por opacidad
   —si no, las cartas enterradas desaparecerían—. Lo que se apaga es solo
   la ficha del material que no está arriba. */
.materiales.es-fija .material {
  grid-area: 1 / 1;
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  gap: var(--e-1);
  height: 100%;
  min-height: 0;
  margin-top: 0;
  opacity: 1;
}

.materiales.es-fija .material__texto {
  max-width: 30rem;
  opacity: 0;
  transition: opacity var(--dur-corta) var(--ease);
}

.materiales.es-fija .material.esta-activo .material__texto { opacity: 1; }

/* La carta. El alto lo escribe el JS: es el de la pila menos lo que se
   llevan los desfases de las que quedan debajo. El ancho sale del alto,
   nunca al revés: mandan los píxeles verticales, que son los que faltan.

   En teléfono la proporción llega hasta 1.15 y no 2:3, porque el texto va
   arriba y el renglón que le queda a la carta no da para el nativo. El
   número y el porqué están en `M_PROP_MAX_TELEFONO`, en el JS. Lo que sobra a los costados no es un descuido: es el
   negro que deja ver el retroceso de las cartas de abajo. */
.materiales.es-fija .material__foto {
  align-self: end;
  justify-self: center;
  height: var(--alto-carta, 100%);
  width: var(--ancho-carta, 100%);
  max-width: 100%;
  max-height: none;
  margin: 0;
  aspect-ratio: auto;
  transform: translate3d(0, var(--y, 0px), 0) scale(var(--s, 1));
  /* ⚠️ EL ORIGEN VA ARRIBA, Y ESTO NO ES UN DETALLE. Con el origen abajo,
     achicar la carta le BAJA el borde superior (1 − s) × alto, y eso se
     come el asomo antes de que se vea. Medido con los valores viejos: el
     asomo declarado era de 28 px y el real era de 9,3 px en escritorio y
     de 4,4 px en teléfono. Con el origen arriba, el borde de arriba no se
     mueve al escalar y el asomo real es EXACTAMENTE el declarado. */
  transform-origin: 50% 0%;
  will-change: transform;
}

.materiales.es-fija .material__foto img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (min-width: 800px) {
  /* ⚠️ La columna de la ficha es FLUIDA, no 22rem fijos. Con 22rem + 4rem de
     separación, en una ventana de 800 px a la carta le quedaban 124 px de
     columna y salía como una tira de 125 × 635. Ahora la ficha cede ancho
     cuando la ventana es chica, que es exactamente donde hace falta.

     Y la columna de la FOTO es `auto`, no `1fr`: se achica al ancho real de
     la carta en vez de estirarse. Antes la columna medía 829 px y la carta
     478, así que entre el texto de la ficha y el borde de la carta quedaban
     395 px de los cuales 352 no eran de nadie. Ahora la única separación
     entre las dos es el gap declarado, y el sobrante se va a los costados
     del par con `justify-content: center`. */
  /* ⚠️ EL PAR YA NO VA CENTRADO, Y ESTO CORRIGE AL PASO 45.
     Aquel paso puso la columna de la foto en `auto` y centró el par para
     que no quedara un hueco entre la ficha y la carta. El efecto lateral
     —que en su momento no se midió— es que el sobrante se repartía a los
     DOS lados, así que la ficha se despegaba del margen: medido en 1440,
     MATERIALES arrancaba en 112 y el nombre del material en 347, con 235
     px de diferencia entre el titular de la sección y su propio texto. Y
     además quedaban 171 px de negro muerto entre la carta y el borde
     derecho, o sea que el hueco que se quería evitar existía igual, sólo
     que corrido de lugar.

     Con la columna de la foto en `1fr` y la carta pegada a su borde
     derecho —el `justify-self: end` que ya estaba— la sección queda como
     el proceso: el texto arranca en el margen y la imagen termina en el
     borde opuesto. El aire queda ENTRE los dos, que es donde se lee como
     composición y no como un descuido. */
  /* ⚠️ LA COLUMNA DE LA FOTO PASÓ A `auto` Y LA DEL TEXTO A `1fr`. Es al
     revés de como estaba, y resuelve dos cosas de una.

     Antes: el texto tenía una columna FIJA de 20vw —288 px en 1440— y la
     foto se quedaba con todo el resto. Como la carta está limitada por el
     ALTO y no por el ancho, sobraba columna: medido en 1440, la carta
     ocupaba 462 de 821 px y quedaban 180 px de negro muerto a la derecha
     de la carta, más 223 en el medio. O sea 403 px que no eran de nadie,
     mientras el párrafo se apretaba en 7 renglones de 38 caracteres. Eso
     es lo que se leía saturado: no era falta de aire vertical, era una
     columna de texto demasiado angosta al lado de un hueco grande.

     Ahora la foto reclama exactamente su ancho (`auto` resuelve a
     `--ancho-carta`, que escribe el JS) y el texto se queda con TODO el
     resto. La carta no cambia de tamaño ni un píxel —se le da su ancho,
     no se le quita—, y el negro muerto de la derecha desaparece porque la
     carta queda apoyada en el margen opuesto.

     📌 Y no reabre el problema del paso 45. Aquel intento también puso la
     foto en `auto`, pero además centraba el par con `justify-content`, y
     eso despegaba el texto del margen izquierdo. Acá no hay centrado: con
     una columna en `1fr` la fila se llena sola y el texto arranca donde
     arranca el titular de la sección.

     `--col-ficha` sobrevive pero SÓLO lo usa la regla de medición de acá
     abajo, que necesita una columna de ancho conocido para que el `1fr` de
     la foto no se mida a sí mismo.

     ⚠️ Y VALE 22rem, NO 34rem, aunque el texto llegue a 34. Es el número
     que decide EL TAMAÑO DE LA CARTA, no el del texto: cuanto más grande,
     menos ancho ve el JS y más chica calcula la carta. Con 34rem —probado y
     medido— la carta perdía 121 px de ancho y 182 de alto en 2560, un 18%,
     que es exactamente la calibración que no hay que reabrir. Con 22rem la
     carta sale idéntica a la de siempre en todos los anchos, y el texto
     igual se queda con lo que sobra, que es bastante más que antes.

     La prioridad está escrita en ese número: primero la carta, después el
     texto. Donde no entren los dos, el que cede es el texto. */
  /* ⚠️ HAY UNA TERCERA COLUMNA Y NO TIENE NADA ADENTRO. Es a propósito:
     es el aire de la derecha, y existe para despegar la carta del borde.

     Antes la fila eran dos columnas y la carta quedaba apoyada contra el
     margen derecho —medido en 1440, su borde caía en 1326 de una fila que
     termina en 1328—. Se leía pegada, y toda la holgura que sobraba estaba
     del lado del texto, así que la carta no tenía margen de maniobra.

     ⚠️ EL ORDEN DE LOS TRES TIPOS DE COLUMNA ES LO QUE HACE QUE ESTO
     FUNCIONE, y hubo un intento anterior que lo tenía mal:

       `minmax(0,1fr) auto minmax(0,0.4fr)`  ← MAL. Las dos columnas
       flexibles se repartían el sobrante en proporción, así que el texto
       no llegaba nunca a su tope: caía a 428 px en 1440 y el párrafo
       volvía a 5 y 8 renglones. Medido.

       `minmax(0,34rem) auto minmax(0,1fr)`  ← BIEN. El texto NO es
       flexible: se sirve primero, hasta su tope de 34rem. La carta es
       `auto`, o sea que pide exactamente su ancho. Y recién lo que queda
       después de esos dos va a la tercera columna, que es el aire.

     Con ese orden la prioridad queda escrita en la estructura: primero la
     carta, después el texto, y el aire de la derecha es lo único que cede
     — se achica solo hasta desaparecer cuando la fila se llena, sin
     tocarle un píxel a ninguno de los dos.

     ⚠️ Y SON CUATRO COLUMNAS, NO TRES: LAS DOS DE AIRE SON IGUALES.
     Con tres —texto, carta, aire— la separación de la izquierda era un
     `margin` fijo y la de la derecha se quedaba con todo el sobrante, así
     que la carta no quedaba centrada entre las dos cosas sino corrida
     hacia el borde: medido en 1440, 43 px contra el texto y 101 contra el
     margen derecho.

     Ahora el aire va en DOS tracks `1fr`, uno de cada lado de la carta.
     Dos `1fr` se reparten el sobrante en partes iguales por definición, o
     sea que la carta queda centrada entre el texto y el borde sola, en
     cualquier ancho, sin un número escrito a mano. En 1440 quedan 73 y 73.

     El `min` del clamp existe para que la separación nunca se cierre del
     todo cuando la fila se llena; el `1fr` es lo que la hace crecer
     parejo cuando sobra.

     `column-gap: 0` porque la separación YA son estas dos columnas: un
     `gap` encima sumaría un tercer espacio y saldría del texto. */
  .materiales.es-fija .material {
    --col-ficha: 22rem;
    grid-template-columns:
      minmax(0, 34rem)
      minmax(clamp(var(--e-2), 2.5vw, var(--e-4)), 1fr)
      auto
      minmax(clamp(var(--e-2), 2.5vw, var(--e-4)), 1fr);
    grid-template-rows: minmax(0, 1fr);
    column-gap: 0;
  }

  /* La carta va en la tercera columna: las de aire quedan vacías. */
  .materiales.es-fija .material__texto { grid-column: 1; }
  .materiales.es-fija .material__foto  { grid-column: 3; }

  /* El tope sube de 30rem a 34rem. Con los textos definitivos —párrafo de
     cuatro o cinco renglones más lema y pureza— 30rem daba 7 renglones de
     38 caracteres, por debajo de la banda legible de 45–75. En 34rem el
     renglón queda en ~66 caracteres y el párrafo baja a 4.

     Es un TOPE, no un ancho: donde la ventana no da para tanto, la columna
     `1fr` manda y el texto se achica con ella. */
  .materiales.es-fija .material__texto { max-width: 34rem; }

  /* Esta regla existía porque la columna de la foto era `auto`: con `auto`
     y la foto en `width: 100%`, el ancho se definía a sí mismo y colapsaba
     a cero —la misma dependencia circular que ya rompió una vez la fila
     del proceso—, así que durante la medición había que forzarla a `1fr`.
     Ahora la base YA es `1fr` y esta regla declara lo mismo. Se deja como
     red: si alguna vez la base vuelve a `auto`, la medición sigue sana. */
  /* ⚠️ ESTA REGLA VOLVIÓ A SER IMPRESCINDIBLE. Dejó de ser una red el día
     que la columna de la foto pasó a `auto`: con `auto` y la foto en
     `width: var(--ancho-carta)` —que es justo lo que el JS está a punto de
     calcular— el ancho se definiría a sí mismo y colapsaría a cero. Durante
     la medición la foto vuelve a `1fr` contra una columna de texto de ancho
     conocido, que es la única forma de leer cuánto espacio hay de verdad. */
  .materiales.es-fija.esta-midiendo .material {
    /* Dos columnas y no cuatro mientras se mide: lo que se está midiendo
       es CUÁNTO ancho hay disponible para la carta, no cómo queda
       repartido después. Con las cuatro, las dos columnas de aire ya se
       habrían quedado con su parte y la medición saldría corta. */
    grid-template-columns: var(--col-ficha) minmax(0, 1fr);
    column-gap: clamp(var(--e-2), 3vw, var(--e-5));
    justify-content: normal;
  }

  .materiales.es-fija.esta-midiendo .material__texto { grid-column: 1; }
  .materiales.es-fija.esta-midiendo .material__foto  { grid-column: 2; }
  .materiales.es-fija .material__texto { align-self: center; }

  /* ⚠️ CENTRADA EN SU COLUMNA, NI PEGADA A UN BORDE NI AL OTRO. Estuvo en
     los dos extremos y los dos se ven mal, medido a 1440:

       justify-self: end    → 408 px de hueco entre la ficha y la carta y
                              0 de aire a la derecha. Un agujero negro en
                              el medio de la sección.
       justify-self: start  →  65 px de hueco pero 343 de aire a la
                              derecha. El mismo agujero, del otro lado.
       justify-self: center →  236 y 171. Repartido.

     Y hay una coincidencia que confirma el número: centrada, la carta cae
     en 679–1157, que es exactamente donde estaba antes de que el texto se
     mudara al margen. O sea que el texto se corrió a la izquierda y la
     imagen se quedó donde siempre estuvo — que es lo que había que
     conseguir, y no mover las dos cosas juntas.

     La carta no puede crecer para llenar la columna: está limitada por el
     alto, no por el ancho (paso 42). El sobrante existe sí o sí; lo único
     que se elige es dónde ponerlo. */
  .materiales.es-fija .material__foto { justify-self: center; }
}

/* Afuera de la pista: cierra la sección después de que se apoyó la cuarta
   carta, con el mismo aire que cualquier otro cierre de sección. */
.materiales.es-fija .materiales__pie { margin-top: var(--e-4); }

/* ⚠️ EN TELÉFONO PASA LO CONTRARIO: manda el ancho. La carta no puede
   pasar del ancho del contenedor sin comerse el margen lateral, así que
   por encima de cierto alto deja de crecer a lo ancho y solo crece a lo
   alto — y eso RECORTA MENOS, no más. El 13% acordado es el techo del
   recorte, no una medida fija.

   Este bloque va DESPUÉS de las reglas base a propósito: ya hubo un bug
   de cascada en el proceso por escribir el bloque de teléfono antes.

   Lo que se aprieta es el AIRE de la ficha, nunca el tamaño de letra: los
   11 px de la monoespaciada no se tocan porque el texto tiene que seguir
   legible cuando le toca. */
@media (max-width: 799px) {
  .materiales.es-fija .materiales__fijo { padding-block: 4px; }
  .materiales.es-fija .materiales__intro { padding-bottom: 4px; }
  .materiales.es-fija .material { gap: 6px; }

  /* La pila se sale del relleno lateral de la sección con el mismo truco
     del margen negativo que usa el carril del proceso. No es para ir a
     sangre: es para poder declarar el margen en 12 px en vez de heredar
     los 20 del contenedor. El `overflow: hidden` de la lista obliga a que
     sea la lista la que se salga, no la carta: si se saliera la carta,
     la lista se la recortaría. */
  .materiales.es-fija .materiales__lista {
    width: 100vw;
    margin-inline: calc(50% - 50vw);
  }

  /* El texto NO se va a sangre con la lista: recupera el relleno de la
     sección para no quedar pegado al borde de la pantalla. */
  .materiales.es-fija .material__texto { padding-inline: var(--margen); }

  /* 12 px por lado. Es lo único que separa a la carta del borde, y lo que
     el escalón lateral del 5% necesita para leerse. */
  .materiales.es-fija .material__foto { max-width: calc(100vw - 24px); }

  /* La ficha comprimida a dos renglones justos. Se aprieta el aire, nunca
     la letra: siguen siendo 11 px con contraste 5,13:1. */
  .materiales.es-fija .material__ficha {
    gap: 0.15rem var(--e-2);
    margin-top: 0.35rem;
    padding-top: 0.35rem;
  }

  .materiales.es-fija .material__ficha dt,
  .materiales.es-fija .material__ficha dd {
    padding-bottom: 0.2rem;
    line-height: 1.35;
  }
}

/* ── 7f. ESCENA 08 · PREGUNTAS ───────────────────────────────────────
   Acordeón nativo con <details>. Funciona sin JavaScript, se navega con
   teclado y no pesa nada. El guion pide simple y sin animación pesada. */
.preguntas {
  padding: var(--e-7) var(--margen);
  border-top: 1px solid var(--linea);
}

.acordeon { margin-top: var(--e-5); border-top: 1px solid var(--linea); }

.pregunta { border-bottom: 1px solid var(--linea); }

.pregunta__titulo {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--e-3);
  padding: var(--e-3) 0;
  /* Misma corrección que los subtítulos de proceso y materiales, y por el
     mismo motivo: la pregunta encabeza a la respuesta, así que no puede
     ser más chica que ella. Estaba en 12 px mono contra una respuesta de
     17 px.

     Va con el mismo `--fs-sub` que los otros subtítulos, pero SIN
     versalitas: «EL MOLDE» son dos palabras y una pregunta entera en
     mayúsculas es un muro. La caja alta se cambia por peso 600, que hace
     el mismo trabajo de separarla del texto de la respuesta.

     ⚠️ BAJÓ DE `--fs-sub` A `--fs-pregunta`. En 26→32px la pregunta pesaba
     como un titular de sección y el acordeón se leía como cinco secciones
     apiladas. Ahora va en 20→24px, que es la parte alta de la escala de
     texto de cuerpo del sitio.
     Lo que NO se hizo fue mandarla al tamaño de la respuesta: la pregunta
     ENCABEZA a la respuesta, así que tiene que seguir siendo más grande
     que ella. Medido, los tres niveles quedan monótonos:
         escritorio   titular 52 · PREGUNTA 24 · respuesta 17
         móvil        titular 32 · PREGUNTA 20 · respuesta 17
     y encima el peso (600 contra 400) hace el resto del trabajo. */
  font-family: var(--f-titular);
  font-size: var(--fs-pregunta);
  font-weight: 600;
  font-stretch: 100%;
  font-variation-settings: 'wdth' 100;
  line-height: 1.15;
  letter-spacing: -0.01em;
  text-wrap: balance;
  cursor: pointer;
  list-style: none;
}

.pregunta__titulo::-webkit-details-marker { display: none; }

/* En `em` y no en `rem`: la marca acompaña al tamaño de la pregunta en vez
   de quedarse en los 24px fijos de antes, que ahora la habrían dejado más
   grande que el texto al que pertenece en móvil. */
.pregunta__titulo::after {
  content: '+';
  flex: 0 0 auto;
  font-size: 1em;
  line-height: 1;
  color: var(--humo-txt);
}

.pregunta[open] .pregunta__titulo::after { content: '−'; }

.pregunta__titulo:hover { color: var(--humo-txt); }

.pregunta__cuerpo {
  max-width: var(--ancho-texto);
  padding-bottom: var(--e-3);
}

.pregunta__cuerpo p + p { margin-top: var(--e-2); }

/* Las respuestas están vacías a propósito: las tiene que escribir Ezzy.
   Ver el aviso del index. */
/* Se usaba para el cartel «Pendiente de que Ezzy responda» de las tres
   respuestas que faltaban. Ya no queda ninguna: las seis están contestadas
   y el marcado no tiene un solo `.pregunta__pendiente`. La regla se queda
   porque el mecanismo sirve —si mañana entra una pregunta nueva sin
   respuesta, se declara el hueco en vez de inventarla— y es una línea. */
.pregunta__pendiente { color: var(--humo-txt); }

/* ── 7g. ESCENA 09 · EL TALLER LLEGA A TI ────────────────────────────
   Antes era «kit a domicilio». El `id` y las clases siguen diciendo `kit`
   porque son el nombre con el que el riel y la rampa del fondo conocen a
   este panel; renombrarlos tocaría el mecanismo de scroll. */
.kit {
  padding: var(--e-6) var(--margen);
  border-top: 1px solid var(--linea);
}

/* La regla de `.kit__caja` se eliminó entera. Era el segundo fondo distinto
   del sitio —acero, con borde completo— o sea una tarjeta dibujada encima
   del negro. Al sacarle el fondo y el borde, el ancho de 34rem y el
   centrado quedaban sin nada que los justificara: el bloque flotaba en el
   medio sin alinearse con ninguna otra sección. Ahora hereda el contenedor
   compartido y arranca en el mismo margen izquierdo que todo lo demás.
   El párrafo sigue acotado por `.cuerpo`, que ya lo limita a 34rem. */

.kit__marca { color: var(--humo-txt); }

.kit .titular { margin-block: var(--e-2); }

/* ── EL RELATO ───────────────────────────────────────────────────────
   La respuesta de Ezzy, entera, en un solo párrafo. Va separada de la
   bajada por más aire del que hay entre párrafos normales: la bajada es
   la promesa corta y esto es la explicación, y el salto marca que son
   dos registros distintos.

   Hubo antes una lista `.kit__pasos` con tres ítems numerados. Se quitó
   junto con su CSS: partir la frase le sacaba la conjunción «y» y el
   texto dejaba de ser literal. Si alguna vez vuelve, el patrón de
   número + línea está resuelto en la escena 04 del proceso. */
.kit__relato { margin-top: var(--e-5); }

.kit__accion { margin-top: var(--e-4); }

/* ── LA FOTO DEL ENVÍO ───────────────────────────────────────────────
   En teléfono va DEBAJO del texto, en el orden del marcado, con el ancho
   de la columna. Arriba no: el rótulo, el titular y la promesa corta son
   lo que tiene que entrar primero, y una foto de 3/4 antes del titular
   empuja todo eso fuera del pliegue.

   El tope de 22rem es el mismo de las cartas de materiales — es la única
   otra foto vertical suelta del sitio y no hay motivo para que ésta tenga
   su propia medida. */
.kit__foto {
  margin: var(--e-5) 0 0;
  width: 100%;
  max-width: 22rem;
}

/* La misma viñeta que las cartas de materiales, con los mismos números.
   No es decoración: el fondo de estudio de esta foto es gris claro y sin
   esto el cuadro se lee como una tarjeta pegada encima del negro, con sus
   cuatro bordes marcados. La viñeta le disuelve el perímetro y la deja
   apoyada en la página en vez de encima.

   ⚠️ Es NEUTRA: sólo baja luminancia con el propio color de fondo, no
   agrega ni un tono. Y usa `--fondo-rgb`, así que acompaña a la rampa del
   fondo y no se despega cuando el negro cambia de luz. */
.kit__foto::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: radial-gradient(96% 74% at 50% 52%,
    rgba(var(--fondo-rgb, 11, 14, 13), 0) 44%,
    rgba(var(--fondo-rgb, 11, 14, 13), 0.30) 74%,
    rgba(var(--fondo-rgb, 11, 14, 13), 0.82) 100%);
}

@media (min-width: 800px) {
  /* Texto a la izquierda, foto a la derecha, con el mismo reparto que
     materiales: el texto se queda con lo que sobra y la foto reclama su
     ancho. Así las dos secciones del riel que tienen foto al costado se
     componen igual y no parecen dos criterios distintos.

     ⚠️ `align-items: center` y no `start`. Esta sección es un panel del
     riel: el alto lo manda el panel, no el contenido, y con `start` el
     bloque de texto quedaba colgado del borde superior mientras la foto
     bajaba sola. Centrados, el par se lee como una composición.

     ⚠️ TRES COLUMNAS, LA TERCERA VACÍA. Es la misma estructura que la fila
     de materiales y por el mismo motivo: con dos columnas la foto quedaba
     apoyada contra el margen derecho. El texto va primero y NO es flexible
     —se sirve hasta su tope—, la foto pide su ancho, y el aire de la
     derecha se queda con lo que sobre y se achica solo cuando no sobra.
     La separación va como `margin-left` de la foto y el `gap` en 0, para
     que la tercera columna no cobre un segundo espacio a costa del texto. */
  .kit__caja {
    display: grid;
    grid-template-columns:
      minmax(0, 34rem)
      minmax(clamp(var(--e-2), 2.5vw, var(--e-4)), 1fr)
      auto
      minmax(clamp(var(--e-2), 2.5vw, var(--e-4)), 1fr);
    column-gap: 0;
    align-items: center;
  }

  .kit__texto { grid-column: 1; }
  .kit__foto  { grid-column: 3; }

  /* El alto manda: la foto se acota contra el panel para no empujarlo, y
     de ese alto sale el ancho por la proporción 3/4. Es el mismo criterio
     que las cartas de materiales, en una línea en vez de en JavaScript,
     porque acá hay una sola foto y no hay pila que calibrar. */
  /* ⚠️ 68vh Y NO 58, Y EL MOTIVO ES EL HUECO. Con 58 la foto salía en
     360 px de ancho y entre el texto y ella quedaban 312 px de negro
     muerto en 1440 — el mismo agujero que se acaba de cerrar en
     materiales. Subiéndola a 68vh el aire baja a ~144 px, que es
     exactamente el que quedó en materiales: las dos secciones respiran
     igual. El tope en `rem` frena el crecimiento en pantallas muy altas.

     Y el alto sigue mandando: de él sale el ancho por la proporción, así
     la foto nunca empuja el panel del riel. */
  .kit__foto {
    /* Sin `margin-left`: la separación ya es la segunda columna. */
    margin: 0;
    align-self: center;
    height: min(38rem, 68vh);
    width: auto;
    max-width: none;
  }

  .kit__foto > img { width: auto; height: 100%; }
}

/* ── 7h. ESCENA 10 · CIERRE ──────────────────────────────────────── */
.cierre {
  padding: var(--e-7) var(--margen);
  border-top: 1px solid var(--linea);
}

.cierre .bajada { margin-top: var(--e-3); }

.cierre__accion { margin-top: var(--e-4); }

/* ── LOS ACCESOS A LAS REDES ─────────────────────────────────────────
   Debajo de AGENDAR CITA y en secundario: la jerarquía del cierre no se
   toca. El rótulo va en el gris de texto y los botones en la misma caja
   transparente con borde que ya usa «Consultar materiales y precios», así
   que no entra ni un tratamiento nuevo al sitio. */
.cierre__marca-redes {
  margin-top: var(--e-5);
  color: var(--humo-txt);
}

.cierre__redes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-2);
  margin-top: var(--e-2);
}


/* El ícono y la palabra adentro del mismo botón, alineados por el centro
   de la caja y no por la línea base. */
.boton--red {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
}

/* ── EL COLOR DE CADA RED ────────────────────────────────────────────
   ⚠️ ESTO CONTRADICE UNA NOTA ESCRITA MÁS ARRIBA EN ESTE MISMO ARCHIVO,
   y conviene que quede dicho en vez de que alguien lo descubra. La nota de
   los accesos a redes decía: «van en trazo y con `currentColor`, sin el
   color de marca de cada red — en este sitio el color vive dentro de las
   fotos». Ese criterio venía de la sección 4 del guion: la página va fría
   para que el metal de las fotos sea lo único cálido en pantalla.

   Anthony pidió el color de cada red, y es su decisión. Lo que sigue es
   cómo se hizo para que cueste lo menos posible.

   📌 SIGUEN SIENDO `--compacto`: 11 px de letra contra los 12 de AGENDAR
   CITA, y la mitad de relleno. Es lo único que mantiene la jerarquía
   ahora que los tres botones del cierre están rellenos. Si alguna vez
   alguien les saca el `--compacto`, la llamada a la acción del sitio pasa
   a ser una de tres y no la primera. */

/* TIKTOK · blanco puro. No es `--hueso` (#E8E6DF, que tira a crema): el
   blanco de TikTok es blanco. Al lado de AGENDAR CITA se nota que son dos
   blancos distintos, y está bien que se note — uno es el botón del sitio y
   el otro es una marca ajena. */
.boton--tiktok {
  background: #FFFFFF;
  border-color: #FFFFFF;
  color: var(--negro);
}

/* INSTAGRAM · el degradado del logo, en su construcción real: NO es lineal,
   es un radial que nace en la esquina inferior izquierda —el amarillo—, pasa
   por el naranja y el magenta y termina en el azul violeta de la esquina
   opuesta. Los cinco topes son los del isotipo.

   ⚠️ Y ACÁ ESTÁ EL PROBLEMA, MEDIDO Y NO SUPUESTO: un degradado que va del
   amarillo #FDF497 al azul #285AEB no puede llevar texto chico de UN solo
   color y cumplir AA. Blanco sobre el amarillo da 1.4:1 y sobre el azul
   5.3:1; negro da lo inverso, 15:1 sobre el amarillo y 4.1:1 sobre el azul.
   No hay color de letra que pase en los dos extremos.

   La salida NO fue cambiar los colores del logo ni recortar el degradado a
   la mitad fría: fue CORRER EL ORIGEN DEL RADIAL fuera de la caja, muy por
   debajo y a la izquierda. La secuencia sigue siendo la del isotipo entera
   —amarillo, naranja, magenta, violeta, azul violeta— pero el botón sólo
   muestra el tramo del naranja/magenta en adelante, y del amarillo entra
   apenas el calor de la esquina. La letra cae siempre sobre magenta y
   violeta, que son los tramos que sí sostienen blanco.

   El origen se buscó midiendo, no a ojo. Cada candidato se pintó, se le
   escondió el texto, se leyó el PNG y se buscó el píxel más claro debajo
   de cada letra:

       origen              peor bajo el texto   peor bajo el ícono
       12% 118%   (real)        1.80:1 ✗             1.49:1 ✗
       -12% 190%               3.46:1 ✗             2.72:1 ✗
       -25% 260%               4.71:1 ✓             4.29:1 ✓
       -32% 300%  ← éste       5.07:1 ✓             4.76:1 ✓

   Se eligió el último: pasa 4.5 con medio punto de margen en vez de con
   dos décimas, y todavía se ve el calor en la esquina. */
.boton--instagram {
  background: radial-gradient(circle at -32% 300%,
    #FDF497 0%, #FD5949 18%, #D6249F 38%, #A02FBF 63%, #6A38D9 100%);
  border-color: transparent;
  color: #FFFFFF;
}

/* Los dos íconos heredan `currentColor`, así que se resuelven solos: negro
   sobre el blanco de TikTok, blanco sobre el degradado de Instagram. */

/* El foco de teclado se dibuja con el hueso del sitio, y sobre un botón
   blanco o sobre el amarillo del degradado se perdería. Se le agrega un
   halo oscuro por fuera: el anillo se ve sobre cualquiera de los dos. */
.boton--tiktok:focus-visible,
.boton--instagram:focus-visible {
  outline-color: var(--negro);
  box-shadow: 0 0 0 4px var(--hueso);
}

/* 1.35em sobre los 11 px de la monoespaciada son 15 px. A 1.15em el ícono
   medía 13 y se leía como una mancha al lado del texto. El `translate`
   corrige que el trazo se apoye un pelo alto respecto de la mayúscula. */
.icono-red {
  width: 1.35em;
  height: 1.35em;
  flex: none;
  translate: 0 -0.04em;
}

/* En el pie siguen siendo enlaces de texto. El relleno agranda el área
   que se puede tocar sin mover nada de sitio: el margen negativo lo
   devuelve. Medido, el enlace pasa de 18 px de alto a 34, que en un
   teléfono es la diferencia entre acertarle y no. */
.pie__red {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  padding-block: 0.5rem;
  margin-block: -0.5rem;
}

/* ── 7i. PIE ─────────────────────────────────────────────────────── */
.pie {
  padding: var(--e-5) var(--margen) var(--e-4);
  border-top: 1px solid var(--linea);
}

.pie__lugar { color: var(--humo-txt); }

/* El número, visible y grande: en Ecuador verlo escrito genera más
   confianza que un botón solo. */
.pie__numero {
  margin-top: var(--e-2);
  font-size: var(--fs-bajada);
}

.pie__redes {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-1) var(--e-3);
  margin-top: var(--e-4);
}

.pie__marca { margin-top: var(--e-4); }

/* Enlaces de texto: subrayado fino, sin color de marca. */
.enlace {
  color: var(--hueso);
  text-decoration: underline;
  text-underline-offset: 0.25em;
  text-decoration-color: var(--linea);
}

.enlace:hover,
.enlace:focus-visible { text-decoration-color: var(--hueso); }

/* ── 7j. ESCENA 06 · LA PIEZA DEL DEDO ───────────────────────────────
   El HTML está comentado (fase 2), pero el espaciado vive acá resuelto:
   cuando llegue el texto de Ezzy se descomenta y ya está.

   Acá se detiene todo. Sin animación, sin viñeta, sin parallax. Fondo
   limpio, una sola imagen, texto corto y mucho aire — el doble de aire
   que cualquier otra sección. Después de cinco secciones de metal, el
   silencio es el efecto.                                              */
.dedo {
  padding: calc(var(--e-7) * 1.8) var(--margen);
  border-top: 1px solid var(--linea);
}

.dedo__caja {
  display: grid;
  gap: var(--e-6);
  width: 100%;
  max-width: 54rem;
  margin-inline: auto;
}

.dedo__foto {
  --proporcion: 2 / 3;
  max-width: 26rem;
  margin-inline: auto;
}

/* Sin viñeta: es la única sección donde la foto no lleva tratamiento. */
.dedo .marco::before { display: none; }

.dedo__texto { text-align: center; }

.dedo__texto .cuerpo { margin-top: var(--e-3); margin-inline: auto; }

/* ── 7k. ESCENAS 00 Y 01 · CARGA Y APERTURA ──────────────────────────
   Nace oculta. La enciende el JS con .con-intro en el <html>: sin
   JavaScript no aparece nada y la página se ve directamente.          */
.intro { display: none; }

.con-intro .intro {
  display: block;
  position: fixed;
  inset: 0;
  z-index: 100;
}

/* Mientras dura, la página no se mueve. */
.con-intro { overflow: hidden; }
.con-intro body { overflow: hidden; }

/* Las dos placas: media pantalla cada una. Al abrirse, una sube y la
   otra baja. Es el gesto de firma — la página se abre como una boca. */
.intro__placa {
  position: absolute;
  left: 0;
  right: 0;
  height: 50.5%;                 /* medio punto de más: sin costura al centro */
  background: var(--fondo);
  transition: transform var(--dur-larga) var(--ease);
}

.intro__placa--arriba { top: 0; }
.intro__placa--abajo  { bottom: 0; }

.intro.esta-abriendo .intro__placa--arriba { transform: translateY(-100%); }
.intro.esta-abriendo .intro__placa--abajo  { transform: translateY(100%); }

.intro__centro {
  position: absolute;
  inset: 0;
  display: grid;
  place-content: center;
  justify-items: center;
  gap: var(--e-4);
  transition: opacity var(--dur-corta) var(--ease);
}

.intro.esta-abriendo .intro__centro { opacity: 0; }

.intro__logo { width: clamp(160px, 34vw, 260px); height: auto; }

/* La barra que se llena. El "barrido metálico" del guion se resuelve con
   un degradé en hueso, no en dorado ni en cromo: la regla dura del
   proyecto es que el metal solo existe dentro de las fotos. */
.intro__barra {
  width: clamp(140px, 30vw, 240px);
  height: 2px;
  background: var(--linea);
  overflow: hidden;
}

.intro__relleno {
  display: block;
  width: 0;
  height: 100%;
  background: linear-gradient(to right,
    var(--linea) 0%,
    var(--humo) 45%,
    var(--hueso) 100%);
  transition: width var(--dur-corta) linear;
}

/* ═══════════════════════════════════════════════════════════════════
   8. APARICIONES POR SCROLL Y PARALLAX
   La capa de movimiento que el guion pedía en la sección 4 y que había
   quedado sin construir.

   NACE APAGADA. El estado oculto existe SOLO bajo .con-revelado, que
   agrega el JS. Sin JavaScript el contenido se ve tal cual, sin ninguna
   regla de opacidad encima: nada puede quedar invisible por un error de
   script. Con prefers-reduced-motion el JS tampoco agrega la clase.
   ═══════════════════════════════════════════════════════════════════ */

.con-revelado [data-revela] {
  opacity: 0;
  transform: translate3d(var(--rev-x, 0), var(--rev-y, 1.75rem), 0);
  transition:
    opacity   var(--dur-larga) var(--ease) var(--retardo, 0ms),
    transform var(--dur-larga) var(--ease) var(--retardo, 0ms);
}

/* Entradas laterales: es la maquinaria de la referencia —los textos que
   entran desde los costados— traducida a esta paleta. El gesto se copia;
   la estética no. */
.con-revelado [data-revela="izq"] { --rev-x: -3.5rem; --rev-y: 0; }
.con-revelado [data-revela="der"] { --rev-x:  3.5rem; --rev-y: 0; }

.con-revelado [data-revela].se-revelo {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* ── EL TITULAR DEL HERO, LETRA POR LETRA ────────────────────────────
   Mismo lenguaje que la entrada de bloque —opacidad y un desplazamiento
   chico hacia arriba— pero aplicado a cada letra, con un desfase corto
   entre una y la siguiente. Los números y su porqué están arriba, en los
   tokens.

   QUIÉN HACE QUÉ. El JavaScript solo parte el texto en `<span>` al cargar
   y le escribe a cada uno su índice en `--i`. La animación entera es
   CSS: no hay temporizadores, ni bucle de cuadros, ni una sola línea de
   JS corriendo mientras la cascada ocurre.

   EL DISPARADOR NO CAMBIA. El `h1` conserva su `data-revela`, así que lo
   sigue encendiendo el mismo IntersectionObserver de siempre, en el mismo
   momento. Lo único que cambia es a qué nivel se aplica el efecto.

   ⚠️ LAS PALABRAS SE ENVUELVEN APARTE, y no es decorativo. Con cada letra
   en un `inline-block` suelto, el navegador puede cortar la línea entre
   dos letras cualesquiera y el titular se parte a mitad de palabra. Cada
   palabra va dentro de su propio `inline-block`, que es atómico para el
   salto de línea; los espacios quedan como texto de verdad entre palabras,
   que es donde el corte sí tiene que poder ocurrir.

   SIN JAVASCRIPT no hay spans: el `h1` es texto plano y entra como
   bloque, igual que cualquier otro `data-revela`. Con
   `prefers-reduced-motion` el JS ni siquiera parte el texto y `.con-revelado`
   nunca se agrega, así que el titular aparece completo y quieto. */

.titular--hero .palabra {
  display: inline-block;
  white-space: nowrap;
}

.titular--hero .letra {
  display: inline-block;
  /* Sin esto, una letra a la que se le anima `transform` puede quedar
     recortada arriba o abajo por su propia caja en algunos navegadores. */
  will-change: transform, opacity;
}

/* El bloque deja de animarse cuando sus letras lo hacen: si no, el
   titular entero se desvanecería ADEMÁS de la cascada y se verían los dos
   efectos multiplicados. La clase la pone el JS al partir el texto. */
.con-revelado [data-revela].tiene-letras {
  opacity: 1;
  transform: none;
  transition: none;
}

.con-revelado .tiene-letras .letra {
  opacity: 0;
  transform: translate3d(0, var(--rev-letra-y), 0);
  transition:
    opacity   var(--dur-letra) var(--ease) var(--retardo-letra, 0ms),
    transform var(--dur-letra) var(--ease) var(--retardo-letra, 0ms);
}

/* `--i` lo escribe el JS en cada span. El retardo arranca en los mismos
   130 ms que tenía el bloque —el `data-retardo="1"` del titular— y de ahí
   suma el desfase por posición. */
.con-revelado .tiene-letras .letra {
  --retardo-letra: calc(130ms + var(--i, 0) * var(--desfase-letra));
}

.con-revelado .tiene-letras.se-revelo .letra {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

/* Escalonado leve: el ojo sigue el orden de lectura sin que la sección
   se sienta lenta. */
/* El desfase estaba en 80/160/240 y no se leía como secuencia: contra una
   duración de 800ms, tres elementos separados por 80ms entran prácticamente
   juntos. Subido a 130/260/390, el ojo alcanza a seguir el orden de lectura
   sin que la entrada se haga lenta: el último arranca a 390ms. */
.con-revelado [data-retardo="1"] { --retardo: 130ms; }
.con-revelado [data-retardo="2"] { --retardo: 260ms; }
.con-revelado [data-retardo="3"] { --retardo: 390ms; }

/* En móvil, a la mitad. Un recorrido de 3.5rem en una pantalla de 390px
   se lee como un salto, no como una entrada. */
@media (max-width: 799px) {
  .con-revelado [data-revela]       { --rev-y: 1rem; }
  .con-revelado [data-revela="izq"] { --rev-x: -1.5rem; }
  .con-revelado [data-revela="der"] { --rev-x:  1.5rem; }
  /* Misma proporción que el bloque, que en móvil baja a la mitad: la
     letra mide 44 px en vez de 86 y un recorrido de 12 px se leería como
     un salto en vez de como una entrada. Los tiempos NO cambian. */
  :root { --rev-letra-y: 0.5rem; }
}

/* ── PARALLAX ────────────────────────────────────────────────────────
   La foto se mueve más lento que la página dentro de su marco. El marco
   ya tiene overflow:hidden y la imagen va a object-fit:cover, así que
   solo hace falta darle holgura: scale(1.14) deja un 7% de alto de
   margen para desplazar sin que asome el borde.

   El desplazamiento lo escribe el JS en --par desde el mismo
   requestAnimationFrame que ya mueve el proceso y los materiales. No se
   agrega un segundo listener de scroll. */
.con-revelado [data-parallax] > img {
  transform: translate3d(0, var(--par, 0px), 0) scale(1.14);
  will-change: transform;
}

/* ── PARALLAX EN CAPAS ───────────────────────────────────────────────
   Lo mismo, pero entre elementos distintos de una misma escena: la foto,
   la viñeta y el texto se mueven a velocidades distintas y eso es lo que
   da profundidad. El desplazamiento lo escribe el JS en --cap, desde el
   mismo requestAnimationFrame. Sigue habiendo un solo listener.

   Las viñetas NO se mueven, y es una decisión, no un olvido: en este
   proyecto la viñeta va pegada al borde de la pantalla, no a la foto —
   por eso sobrevive cuando se reemplaza la imagen. Que la foto se
   desplace por debajo de una viñeta quieta ES la tercera velocidad. */
.con-revelado [data-capa] {
  /* Tres cosas se multiplican acá y NINGUNA pisa a la otra, que es todo el
     truco: `transform` lleva el parallax y el alejamiento por scroll, y la
     propiedad `scale` —aparte, en la regla del hero— lleva el bucle lento.
     Son canales distintos y el navegador los compone.

     --aleja lo escribe el JS SOLO en móvil y SOLO hacia abajo de 1: la
     pieza se aleja al bajar, nunca se agranda. Agrandarla en móvil es lo
     que recortaba el lettering, y por eso no puede pasar ni por error: el
     valor está topado en 1. */
  /* --cap-x es el mismo parallax en capas, montado sobre la transición
     del riel en vez de sobre el scroll vertical. Dentro del riel el
     panel del hero está frenado durante todo su reposo, así que mover
     --cap ahí sería un temblor —lo mismo que `enPanelFijo()` ya evita en
     las otras dos escenas—. Cuando el panel se va de costado, la foto
     sale un poco más lento que el texto: es el mismo gesto de
     profundidad aprobado, rotado al eje del viaje.
     Vale 0 en reposo y 0 en móvil, donde no hay riel: ahí manda --cap. */
  transform: translate3d(var(--cap-x, 0px), var(--cap, 0px), 0) scale(var(--aleja, 1));
  will-change: transform;
}

/* ── EL HERO RESPONDE AL MOUSE · SOLO ESCRITORIO ─────────────────────
   Las mismas dos capas que ya tienen parallax de scroll se corren unos
   pocos píxeles siguiendo al cursor, con retardo. La foto —que es el
   fondo— se mueve más y tarda más; el texto, menos y más rápido.

   ⚠️ SE USA LA PROPIEDAD `translate`, NO `transform`, Y ESA ES TODA LA
   CLAVE. Sobre estos dos elementos ya escriben otras dos cosas:
     · `transform` lleva el parallax de scroll y la salida del riel
     · la propiedad `scale`, aparte, lleva el bucle lento de la foto
   Si el mouse escribiera `transform`, habría que ponerle una transición
   a `transform` — y con eso el parallax de scroll dejaría de ser
   instantáneo: pasaría a arrastrarse medio segundo detrás de la rueda y
   dejaría de ser una función pura del scroll.
   `translate` es un canal libre, el navegador compone los tres, y la
   transición cuelga SOLO de él. Es el mismo argumento que el guion ya
   dejó escrito para el bucle del hero.

   ⚠️ LA CONSULTA DE MEDIOS ES LA PRIMERA DEFENSA, no el JavaScript. Todo
   este bloque vive dentro de `(min-width: 800px)` y de `(hover: hover) and
   (pointer: fine)`: en un teléfono estas reglas no existen, así que móvil
   no puede cambiar ni por un error de script. El JS agrega la segunda
   defensa —`pointerType === 'mouse'`— y la tercera es la clase
   `.sigue-al-raton`, que solo está mientras el hero está frenado en su
   reposo dentro del riel.

   Con `prefers-reduced-motion` el JS nunca agrega la clase ni escribe
   `--rx`/`--ry`, en ningún ancho. */
@media (min-width: 800px) and (hover: hover) and (pointer: fine) {

  /* Sin la clase, `translate` no se declara y vale `none`. Al quitarla
     —cuando el riel arranca la transición— las capas vuelven al centro
     con la misma transición, en vez de saltar. */
  .hero__foto img,
  .hero__foto .hero__brillo,
  .hero__contenido {
    transition: translate var(--dur-raton-texto) var(--ease);
  }

  .hero__foto img,
  .hero__foto .hero__brillo { transition-duration: var(--dur-raton-foto); }

  /* `--rx` y `--ry` los escribe el JS sobre `.hero`, ya normalizados a
     −1 → 1. Las amplitudes viven acá, que es donde se pueden ajustar sin
     tocar una línea de JavaScript. */
  /* La capa del brillo copia el MISMO desplazamiento que la foto. Si no,
     al correrse la foto 6 px la máscara se queda y el reflejo asoma fuera
     del molde justo del lado hacia el que se movió. */
  .hero.sigue-al-raton .hero__foto img,
  .hero.sigue-al-raton .hero__foto .hero__brillo {
    translate: calc(var(--rx, 0) * var(--raton-foto))
               calc(var(--ry, 0) * var(--raton-foto));
  }

  .hero.sigue-al-raton .hero__contenido {
    translate: calc(var(--rx, 0) * var(--raton-texto))
               calc(var(--ry, 0) * var(--raton-texto));
  }
}

/* ── 9. ACCESIBILIDAD ───────────────────────────────────────────── */
:focus-visible {
  outline: 2px solid var(--hueso);
  outline-offset: 3px;
}

.salto-contenido {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 100;
  padding: var(--e-2) var(--e-3);
  background: var(--hueso);
  color: var(--negro);
  font-family: var(--f-mono);
  font-size: var(--fs-mono-l);
  text-decoration: none;
}
.salto-contenido:focus { left: var(--margen); top: var(--e-2); }

.visualmente-oculto {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Zoom, parallax y apariciones se desactivan acá. El modo secuencia de
   la escena 04 además ni se enciende: el JS lo consulta antes. */
@media (prefers-reduced-motion: reduce) {
  /* Explícito, no por el 0.01ms global de abajo: ese cortaría el bucle en
     su fotograma final y dejaría el hero congelado un 3% ampliado. Acá se
     apaga y vuelve a escala 1. La señal queda estática, sin deriva. */
  /* La superposición SE MANTIENE. Se probó deshacerla y era peor: el texto
     volvía a apilarse debajo de la foto y los botones se iban de scrollY
     251 a 407, o sea que quien pide menos movimiento terminaba con más
     scroll que el resto. Y no hace falta: superponer es layout, no
     movimiento — no hay una sola animación en la composición.
     Lo que sí se apaga es todo lo que se mueve. */
  .hero__foto          { position: static; }   /* no se fija: flujo normal */
  .hero__foto img      { animation: none; scale: 1; }
  /* El brillo se saca entero y no se deja quieto en algún punto: apagado
     por la regla global de abajo quedaría clavado donde caiga el último
     fotograma, que es un estado que nadie eligió. */
  .hero__brillo        { display: none; }
  .hero__senal-punta   { animation: none; translate: 0 0; opacity: 1; }

  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ═══════════════════════════════════════════════════════════════════
   10. ANDAMIO PROVISIONAL — se borra cuando se construyan las escenas.
   Solo sirve para que el esqueleto se lea mientras se aprueba el paso 1.
   ═══════════════════════════════════════════════════════════════════ */
.escena[data-paso] > .etiqueta { margin-bottom: var(--e-2); }

.escena[data-paso] > .titular { color: var(--linea); }

.pendiente {
  margin-top: var(--e-2);
  max-width: var(--ancho-texto);
  font-size: 0.9375rem;
  color: var(--humo-txt);
}

.pendiente code {
  font-family: var(--f-mono);
  font-size: 0.8125rem;
  color: var(--hueso);
}

.marca-pendiente { color: var(--humo-txt); }
