/* ============================================================================
   La home

   Salió de styles.css el 2026-09-06 por cobertura, no por orden: son 13,3 KB crudos que las cuatro páginas internas
   bloqueaban sin usar.
   Lo cargan: solo index.html.

   Los tokens (:root), la base y todo lo compartido siguen en styles.css, que
   se carga siempre y va primero. El orden de los bloques acá es el que tenían
   allá, y el <link> va donde corresponde para que la cascada no cambie: ver
   la cabecera de cada página.

   El gate audita este archivo con las mismas reglas de color y tipografía que
   styles.css (HOJAS_CON_TOKENS en tests/gate.mjs).
   ========================================================================= */

/* El descenso del cordón vivía en styles.css y lo pagaban todas las páginas
   en su ruta crítica sin usarlo: solo la home lo dibuja. Se mudó acá el
   2026-09-26, con su bloque responsive, cuando la cinta de la promo entró al
   <header> compartido y como-trabajamos.html quedaba sin holgura de CSS
   (tools/presupuesto.mjs). Va primero para conservar el orden de la cascada. */

/* ============================================================================
   El descenso del cordón
   ========================================================================= */

.descenso {
  position: relative;
  /* La altura ES el largo del recorrido: cuántas pantallas hay que scrollear
     para que el cable termine de bajar.

     Veinte pantallas, por dos clips de diez segundos empalmados en el bloque
     intermedio. El descenso es el espectáculo de la página y necesita durar:
     con doce pantallas sobre trece segundos de metraje llegaba abajo antes de
     que se sintiera un recorrido. A esta altura cada fotograma dura unos 71px
     de scroll, que es lo que hace que se vea continuo y no a saltos.

     La tecla HABLAR fija en el nav existe para que el que no quiera bajar no
     tenga que hacerlo. */
  height: 2000vh;
  background: var(--negro);
}

.descenso__etapa {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr);
  align-items: center;
  gap: calc(var(--u) * 4);
  padding: calc(var(--nav-alto) + var(--u) * 4) calc(var(--u) * 4) calc(var(--u) * 4);
}

/* A sangre a los costados y abajo, y desde el borde inferior del riel hacia
   arriba. El lienzo, el póster y el remate van sin grid-area: un hijo absoluto
   con líneas de grilla toma como caja la celda, que está adentro del relleno,
   y con grid-area auto toma la caja de relleno entera.

   Hasta el 2026-09-17 iban sobre 1 / 1 / -1 / -1 y la foto quedaba enmarcada
   por los 104px de arriba (riel + 32) y los 32 de los costados y de abajo. En
   pantalla eran dos huecos de textura vacía: uno entre la máquina y el
   conmutador, y otro entre el panel final y el pie.

   Arriba NO llega hasta el borde, a propósito. Se probó a sangre completa y la
   foto pasaba por detrás del riel, que es vidrio con backdrop-filter: blur, y
   el blur se recalculaba en cada cuadro del descenso. Medido con
   tools/costura.mjs --fps en 1366x566, cinco rondas: 48,7 fps de mediana
   contra 55,3 de main; con el lienzo debajo del riel, 55,2 contra 53,3. La
   franja del riel queda negra (ver .descenso--fundido .descenso__etapa) y el
   degradado de la costura la tapa en la entrada, así que no se lee como hueco.

   El alto va explícito y no con inset solo: canvas e img son reemplazados, y
   con height auto toman su tamaño intrínseco en vez de estirarse. El texto
   sigue respetando el relleno porque .descenso__mensajes es el hijo en flujo,
   y descenso.js mide todo contra el rectángulo real del lienzo. */
.descenso__lienzo {
  position: absolute;
  inset: var(--nav-alto) 0 0;
  width: 100%;
  height: calc(100% - var(--nav-alto));
  pointer-events: none;
  background: var(--negro);
}

/* OJO al tocar la estructura de .descenso__etapa: es una grilla cuyo único
   hijo EN FLUJO es .descenso__mensajes. De ahí que tenga una sola fila y que
   el bloque de mensajes se centre en ella.

   Agregar un segundo hijo en flujo rompe eso en silencio: la grilla pasa a dos
   filas implícitas. Cuando el lienzo se estiraba con grid-area 1/1/-1/-1 eso
   lo encogía: pasó al envolver el póster en un <picture>, de 676px a 246, la
   secuencia dibujada en un tercio de la pantalla, sin ningún error en consola.
   El check "etapa" de tests/gate.mjs cuenta esos hijos y además verifica que
   las capas absolutas no vuelvan a las líneas de grilla. */
.descenso__poster {
  position: absolute;
  inset: var(--nav-alto) 0 0;
  width: 100%;
  height: calc(100% - var(--nav-alto));
  object-fit: cover;
}

/* La costura con la máquina (movimiento fundido-de-luz de la skill
   motion-web, en su forma más barata).

   La máquina termina en negro con la hélice y el conmutador arranca con luz.
   Sin transición eran un corte con borde. Este degradado vive pegado al borde
   de la sección y se mueve con el scroll nativo: transparente arriba (apaga la
   hélice hacia el negro), negro sólido en la unión y en la franja del riel, y
   transparente hacia abajo a lo largo de 80vh. El conmutador asoma a oscuras,
   se enciende de abajo hacia arriba mientras sube, y el degradado se va de la
   pantalla antes del primer mensaje.

   Por qué un solo degradado estático y no algo animado. La primera versión
   era una capa de dos pantallas que descenso.js subía con translate3d, más una
   máscara degradada sobre el lienzo para suavizar su borde superior. Se veía
   igual y costaba cuadros: medido con tools/costura.mjs --fps en 1440x900,
   51,4 fps de mediana contra 58,6 de main, con cuatro a ocho cuadros trabados
   más. Esto no escribe estilos por cuadro, no crea una superficie de máscara
   sobre un canvas que cambia, y se pinta una sola vez.

   Encima de la etapa pegada de la máquina y de la del descenso porque va
   después en el DOM con z-index 1; debajo del riel. Solo con JS y movimiento:
   descenso.js pone .descenso--fundido. Con movimiento reducido no existe. */
.descenso--fundido::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: -28vh;
  height: 108vh;
  z-index: 1;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    transparent 0,
    var(--negro-lienzo) 28vh,
    var(--negro-lienzo) calc(28vh + var(--nav-alto) + var(--u) * 4),
    transparent 100%);
}

/* La franja detrás del riel: negra y no baquelita, que es el negro con el que
   termina la máquina. Con la misma condición que el degradado. */
.descenso--fundido .descenso__etapa { background: var(--negro-lienzo); }

/* El lienzo arranca transparente y tapa al poster cuando tiene qué dibujar. */
.descenso__lienzo { opacity: 0; transition: opacity 200ms linear; }
.descenso__lienzo.listo { opacity: 1; }

.descenso__mensajes {
  position: relative;
  grid-column: 2;
  display: grid;
  align-items: center;
  min-height: 9em;
  padding: calc(var(--u) * 3);
  transition: translate var(--dur-lenta) var(--ease);
}

/* El remate: el último mensaje se queda en pantalla durante la parada, pero
   del 66% en adelante el panel llena el cuadro y la columna derecha es
   exactamente donde está la leyenda NUNCA MAS UNA CONSULTA SIN RESPONDER.
   El bloque sube a la banda negra de arriba del panel, que en su mitad derecha
   mide 2.1 de luz sobre 255.

   La distancia la calcula descenso.js y la publica en --remate-subida, porque
   depende de cómo el último fotograma quedó recortado contra la pantalla: una
   fracción del archivo no cae en la misma fracción de la etapa. El respaldo en
   vh existe solo para el instante entre que se aplica la clase y el JS mide. */
.descenso__mensajes.arriba {
  translate: 0 var(--remate-subida, -28vh);
  /* La altura mínima existe para que el bloque no cambie de tamaño cada vez
     que se releva un mensaje mientras está centrado. Arriba estorba: la banda
     mide un cuarto del alto del cuadro y el bloque, con sus 9em más el relleno,
     salía por los dos lados. Acá solo vive el tercer mensaje, así que puede
     medir lo que mide su texto. */
  min-height: 0;
  padding-block: var(--u);
}

/* Velo propio: el lado derecho de la secuencia es negro, pero un fotograma que
   se corra no puede dejar el texto ilegible.

   Entra y sale CON el texto, no está puesto todo el recorrido. Un velo fijo se
   ve como una mancha rectangular sobre la fotografía en los tramos sin mensaje,
   y sobre todo en el último cuadro, donde el panel encendido tiene que quedar
   limpio. Va en un pseudo-elemento y no en el fondo del contenedor porque el
   fondo no puede llevar su propia opacidad sin arrastrar al texto. */
.descenso__mensajes::before {
  content: '';
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity var(--dur-media) var(--ease);
  background: radial-gradient(
    ellipse at center,
    rgba(0, 0, 0, 0.82) 0%,
    rgba(0, 0, 0, 0.6) 55%,
    rgba(0, 0, 0, 0) 100%);
}

/* El velo sigue al texto sin que el JS tenga que saber que existe: la única
   fuente de verdad sigue siendo la clase activo del mensaje. */
.descenso__mensajes:has(.descenso__mensaje.activo)::before { opacity: 1; }

.descenso__mensaje {
  grid-area: 1 / 1;
  /* Posicionado para pintar por encima del velo: los dos son z-index auto, así
     que decide el orden del árbol y el pseudo-elemento va primero. */
  position: relative;
  margin: 0;
  font-size: var(--t-sub);
  line-height: 1.35;
  color: var(--tinta-suave);
  opacity: 0;
  translate: 0 14px;
  transition:
    opacity var(--dur-media) var(--ease),
    translate var(--dur-media) var(--ease);
}
.descenso__mensaje b { color: var(--tinta); font-weight: 600; }
.descenso__mensaje.activo { opacity: 1; translate: 0; }

@media (max-width: 900px) {

  .descenso__etapa {
    grid-template-columns: minmax(0, 1fr);
    padding-inline: calc(var(--u) * 3);
  }
  .descenso__mensajes { grid-column: 1; }
  .descenso { height: 900vh; }
}

/* ============================================================================
   Materiales del rack

   Las texturas son raster porque baquelita, acero y pintura martillada son
   materiales con luz y profundidad. Un degradado de CSS no es una textura, y
   escribir "CSS" en la columna de medio es cómo un comp lleno de material
   físico termina siendo una página plana con el mismo orden de secciones.
   ========================================================================= */

.material-baquelita {
  background-color: var(--negro);
  background-image: url("../img/textura-baquelita.webp");
  background-size: 420px;
}

/* material-acero, material-hammertone y .costura vivieron acá — el resto de
   la página ya pasó al vidrio HUD (ver --hud-*) y solo el descenso del
   cordón sigue en el mundo físico, con material-baquelita. */

/* Hundido: la luz cae desde arriba, así que el borde superior se oscurece. */
.hundido {
  box-shadow:
    inset 0 4px 14px rgba(0, 0, 0, 0.9),
    inset 0 -1px 0 var(--costura-luz);
}

/* En relieve: la cara superior atrapa el reflejo. */
.relieve {
  box-shadow:
    inset 0 1px 0 var(--costura-luz),
    0 2px 6px rgba(0, 0, 0, 0.55);
}

/* ============================================================================
   Las cuatro áreas — tarjetas de vidrio HUD

   Nacieron en el tablero de beneficios (reemplazado el 2026-09-06 por el hero
   "Entrar a la máquina", ver BRIEF_HERO_MAQUINA.md). Siguen siendo el
   respaldo sin JavaScript de ese hero: las cuatro áreas en dos columnas con su
   escena. Los tokens --tablero-* y --iso-* quedan en :root porque los usan las
   demás secciones y la tarjeta social (tools/og.mjs).
   ========================================================================= */

/* --- Las cuatro áreas --------------------------------------------------- */

.area {
  position: relative;
  max-width: 32rem;
  padding: calc(var(--u) * 2.5);
  background: var(--hud-vidrio);
  backdrop-filter: blur(20px) saturate(140%);
  -webkit-backdrop-filter: blur(20px) saturate(140%);
  border: 1px solid var(--hud-borde);
  border-radius: var(--radio);
}

/* Cada tarjeta contra el borde exterior de su columna y contra el borde de la
   imagen: es lo que la deja al lado de su escena y no flotando en el medio. */
.area--leads          { grid-area: leads;          justify-self: start; align-self: end; }
.area--conversion     { grid-area: conversion;     justify-self: end;   align-self: end; }
.area--automatizacion { grid-area: automatizacion; justify-self: start; align-self: start; }
.area--redes          { grid-area: redes;          justify-self: end;   align-self: start; }

/* La costura: el hilo de un pixel que ata la tarjeta con su escena. Cae en el
   centro de la tarjeta, que es donde cae el centro de la escena que le toca. */
.area::after {
  content: "";
  position: absolute;
  left: 50%;
  width: 1px;
  height: var(--tablero-costura);
  background: linear-gradient(to bottom, transparent, var(--saffron));
}

.area--leads::after,
.area--conversion::after { top: 100%; }

.area--automatizacion::after,
.area--redes::after {
  bottom: 100%;
  background: linear-gradient(to top, transparent, var(--saffron));
}

.area__eyebrow {
  font: 700 var(--t-placa)/1.3 var(--fuente-titulo);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--saffron);
  margin: 0 0 calc(var(--u) * 1.25);
}

.area__titulo {
  font-size: var(--t-sub);
  margin: 0 0 calc(var(--u) * 1.25);
}

.area__como {
  color: var(--tinta-suave);
  font-size: var(--t-menor);
  margin: 0 0 calc(var(--u) * 2);
}

.area__tools {
  display: flex;
  flex-wrap: wrap;
  gap: var(--u);
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Por debajo de este ancho las cuatro áreas caen en columna. */
@media (max-width: 900px) {
  .area {
    max-width: none;
    justify-self: stretch;
    align-self: auto;
  }

  /* Sin bandas no hay escena arriba ni abajo a la que atar la costura. */
  .area::after { display: none; }
}

/* ============================================================================
   El remate del descenso

   El titular y la tecla se montan sobre la ventana y el hueco iluminados del
   último fotograma. Sus posiciones las calcula descenso.js contra el recorte
   real del lienzo, así que quedan encima a cualquier tamaño de pantalla.

   Van en HTML y no quemados en la foto: el titular se traduce cuando salga la
   versión en inglés, un lector de pantalla lo lee, y la tecla es un botón de
   verdad que puede encenderse al tocarlo.
   ========================================================================= */

.descenso__remate {
  position: absolute;
  /* La misma caja que el lienzo (ver .descenso__lienzo en styles.css): las
     zonas de la tecla se calculan contra su rectángulo. */
  inset: var(--nav-alto) 0 0;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--dur-media) var(--ease);
}
.descenso__remate.conectado { opacity: 1; }


/* El pulsador: un botón real encima de la tapa fotografiada.

   La tapa y su leyenda viven en la foto, porque un texto dibujado en CSS está
   clavado en una posición fija mientras la cámara se mueve, y solo puede calzar
   en el último cuadro. Lo que la foto no puede dar es responder: esto sí se
   puede enfocar con el teclado, y al apretarlo enciende la tapa de abajo. */

.descenso__pulsador {
  position: absolute;
  border: 0;
  padding: 0;
  border-radius: 14%;
  background: transparent;
  cursor: pointer;
  pointer-events: auto;
  transition:
    background var(--dur-rapida) var(--ease),
    box-shadow var(--dur-rapida) var(--ease),
    transform var(--dur-rapida) var(--ease-tecla);
}

/* Al pasar por encima, la tapa de la foto se enciende un poco más. */
.descenso__pulsador:hover {
  background: color-mix(in srgb, var(--luz-calida) 18%, transparent);
  box-shadow: 0 0 24px 6px color-mix(in srgb, var(--saffron) 45%, transparent);
}

/* Apretada: baja al ras del vano y el filamento sube. */
.descenso__pulsador:active {
  transform: translateY(3px);
  background: color-mix(in srgb, var(--luz-calida) 34%, transparent);
  box-shadow: 0 0 40px 10px color-mix(in srgb, var(--saffron) 65%, transparent);
}

.descenso__pulsador:focus-visible {
  outline: 3px solid var(--luz-calida);
  outline-offset: 4px;
}

/* --- La tapa del pulsador -------------------------------------------------

   El hueco del bisel viene vacío en la foto, y una tapa iluminada de plástico
   traslúcido es un objeto físico: tiene espesor, brillo especular en la cara
   de arriba, paredes que se oscurecen hacia el borde y luz que la atraviesa
   desde atrás. Se construye en CSS y no en la foto porque tiene que ENCENDERSE
   al apretarla, y una imagen no puede hacer eso.

   Las capas, de atrás hacia adelante: el resplandor que se derrama sobre el
   metal, el cuerpo traslúcido con el núcleo caliente arriba al centro, el
   oscurecimiento de las paredes, el brillo de la cara, y el canto que atrapa
   la luz.

   Hoy la usa una sola pantalla: la tapa de móvil (.descenso__tecla--movil).
   En escritorio el remate es la tapa fotografiada, con .descenso__pulsador
   encima. Se conserva y no se borra porque es la misma pieza del mismo mundo,
   y una tapa de plástico iluminado es lo que corresponde al final del cordón:
   un botón HUD ahí rompería la única sección que sigue siendo física. */

.descenso__tecla {
  display: grid;
  place-items: center;
  text-align: center;
  margin: 0;
  border: 0;
  cursor: pointer;
  border-radius: 14%;
  /* En em, no en porcentaje: el relleno porcentual se calcula contra el
     CONTENEDOR, no contra el botón. Con un contenedor de 1361px, un 8% eran
     109px por lado — más que el ancho entero de la tapa, que se inflaba para
     contenerlos y se derramaba sobre el bisel. */
  padding: 0.7em 0.9em;
  min-width: 0;
  overflow-wrap: anywhere;
  /* El vidrio de la foto ya está iluminado: la leyenda solo se apoya encima,
     oscura, como impresa en él. */
  color: var(--tinta-grabada);
  font-family: var(--fuente-titulo);
  font-weight: 700;
  text-transform: uppercase;
  font-size: clamp(0.6rem, 1.15vw, 1.3rem);
  letter-spacing: 0.04em;
  line-height: 1.15;
  pointer-events: auto;

  background:
    /* brillo de la cara: una banda suave sobre el tercio superior */
    linear-gradient(180deg,
      rgba(255, 255, 255, 0.34) 0%,
      rgba(255, 255, 255, 0.10) 26%,
      rgba(255, 255, 255, 0) 46%),
    /* paredes: el plástico se oscurece hacia los bordes */
    radial-gradient(115% 105% at 50% 38%,
      rgba(0, 0, 0, 0) 52%,
      rgba(0, 0, 0, 0.32) 100%),
    /* cuerpo traslúcido con el núcleo caliente detrás */
    radial-gradient(78% 62% at 50% 30%,
      var(--luz-calida) 0%,
      var(--saffron) 34%,
      var(--ventana-glow) 78%,
      var(--bronce-oscuro) 100%);

  box-shadow:
    /* canto de la tapa atrapando la luz */
    inset 0 2px 0 rgba(255, 255, 255, 0.45),
    inset 0 -3px 6px rgba(0, 0, 0, 0.45),
    /* espesor: la tapa sobresale del vano */
    0 3px 0 var(--bronce-oscuro),
    0 5px 10px rgba(0, 0, 0, 0.6),
    /* resplandor derramándose sobre el metal de alrededor */
    0 0 26px 4px color-mix(in srgb, var(--saffron) 48%, transparent),
    0 0 60px 10px color-mix(in srgb, var(--saffron) 22%, transparent);

  /* La leyenda va impresa sobre la tapa: tapa la luz de atrás, así que es
     oscura, con el ámbar filtrándose alrededor de cada letra. */
  text-shadow:
    0 0 6px color-mix(in srgb, var(--saffron) 80%, transparent),
    0 1px 0 color-mix(in srgb, var(--luz-calida) 60%, transparent);

  transition:
    transform var(--dur-rapida) var(--ease-tecla),
    box-shadow var(--dur-rapida) var(--ease-tecla),
    filter var(--dur-rapida) var(--ease);
}

.descenso__tecla:hover { filter: brightness(1.14) saturate(1.06); }

/* Apretada: baja al ras del vano, pierde el espesor y el filamento sube. */
.descenso__tecla:active {
  transform: translateY(3px);
  filter: brightness(1.3) saturate(1.1);
  box-shadow:
    inset 0 2px 0 rgba(255, 255, 255, 0.3),
    inset 0 -2px 8px rgba(0, 0, 0, 0.55),
    0 0 0 var(--bronce-oscuro),
    0 1px 4px rgba(0, 0, 0, 0.7),
    0 0 40px 8px color-mix(in srgb, var(--saffron) 70%, transparent);
}

.descenso__tecla:focus-visible {
  outline: 3px solid var(--luz-calida);
  outline-offset: 4px;
}

/* --- La tapa de móvil -----------------------------------------------------

   En escritorio no existe: ahí la tapa está fotografiada en el último cuadro y
   .descenso__pulsador se monta encima. En un teléfono ese cuadro se recorta al
   27% de su ancho y la tapa cae en left: -258px, o sea fuera de la pantalla
   —ver el comentario en index.html—, así que el pulsador sobre la foto se
   apaga y en su lugar aparece esta tapa, en el eje del cuadro y a la altura
   del pulgar.

   Vive adentro de .descenso__remate a propósito: hereda el mismo inert y la
   misma revelación con .conectado, así que aparece cuando el plug entra y no
   antes, igual que en escritorio. */

.descenso__tecla--movil { display: none; }

@media (max-width: 900px) {
  /* El pulsador apunta a un hueco de la foto que en esta pantalla no se ve. */
  .descenso__pulsador { display: none; }

  .descenso__tecla--movil {
    position: absolute;
    display: grid;
    left: 50%;
    translate: -50% 0;
    z-index: 1;
    /* Sobre el borde inferior de la etapa, despejando la barra del navegador
       y el área segura del teléfono. */
    bottom: max(calc(var(--u) * 4), calc(env(safe-area-inset-bottom) + var(--u) * 3));
    min-height: calc(var(--u) * 6);   /* 48px: por encima del mínimo táctil */
    min-width: min(78vw, 20rem);
    padding: calc(var(--u) * 1.5) calc(var(--u) * 2.5);
    font-size: var(--t-menor);
    letter-spacing: 0.08em;
  }
}

/* En un teléfono en vertical la secuencia es la variante recortada, donde la
   tapa fotografiada VUELVE a estar en cuadro y ocupa casi la mitad del ancho.
   Ahí no hace falta el paliativo: se monta el pulsador sobre la foto, que es lo
   que hace desde el principio en escritorio, y la tapa se enciende al tocarla.

   Ver ZONAS_VERTICAL en descenso.js y tools/vertical.mjs. La franja de 641 a
   900px se queda con el paliativo: ahí la caja es demasiado ancha para el
   recorte vertical y se sirve la secuencia apaisada. */
@media (max-width: 640px) and (orientation: portrait) {
  .descenso__pulsador { display: block; }
  .descenso__tecla--movil { display: none; }

  /* Y acá el bloque de mensajes no se centra ni viaja: vive arriba desde el
     principio.

     En escritorio el bloque ocupa la columna derecha y sube al final para
     despejar la leyenda del panel; el viaje son 900ms de transición. En un
     teléfono no hay columna derecha —el bloque ocupa todo el ancho— y la tapa
     fotografiada está en el medio del cuadro, así que ese viaje lo hace por
     encima del botón: durante casi un segundo el cartel tapa la única acción
     del final de la página. Anclado arriba no cruza nunca, y el relleno
     superior de la etapa ya despeja el riel.

     align-self y no un translate: el translate lo sigue calculando el JS para
     escritorio, y dos mecanismos peleando por la misma posición es cómo
     aparecen los -30px de la nota de abajo. */
  .descenso__mensajes { align-self: start; }
  .descenso__mensajes.arriba { translate: 0; }
}

/* El agente vive en assets/css/agente.css: no bloquea el primer pintado.
   Ver la cabecera de ese archivo. */

/* ============================================================================
   El descenso con movimiento reducido

   Vivía en styles.css hasta el 2026-09-17; se mudó acá porque solo lo usa la
   home y las otras páginas lo cargaban en su ruta crítica (tools/presupuesto.mjs
   lo marcó: como-trabajamos pasó el tope de CSS por 404 bytes). Va al final
   para ganarles por orden a las reglas base del descenso, en styles.css y más
   arriba en este archivo.
   ========================================================================= */

/* Movimiento reducido: la etapa deja de ser fija, el cordón se dibuja entero
   y el remate está visible desde el arranque. Se pierde el espectáculo, no la
   información ni la acción. */
@media (prefers-reduced-motion: reduce) {
  /* Se lee en orden, sin nada encima de nada: primero los tres mensajes sobre
     negro, abajo el panel final entero con la tecla que funciona.

     Antes la etapa seguía siendo una pantalla con la foto de fondo y los tres
     mensajes en columna encima, así que tapaban el panel y su leyenda. Y como
     el lienzo nunca recibía .listo, la foto que se veía era el primer cuadro.

     La foto va en el relleno inferior de la etapa, con el alto exacto de su
     proporción (--foto-alto): sin recorte, las zonas de la tecla que calcula
     descenso.js caen donde tienen que caer. El lienzo, el póster y el remate
     se anclan abajo, los tres con la misma caja. */
  /* cqi y no vw: 100vw incluye la barra de scroll de Windows y la foto
     quedaba unos píxeles más alta que su proporción. */
  .descenso {
    container-type: inline-size;
    --foto-alto: calc(100cqi * 714 / 1280);
    height: auto;
  }
  /* Con .descenso adelante para ganarle a .material-baquelita, que está más
     arriba en este archivo con la misma especificidad: los mensajes van sobre
     el negro de la máquina, no sobre textura. */
  .descenso .descenso__etapa {
    position: relative;
    display: block;
    height: auto;
    /* Arriba, el riel más aire: el riel es sticky y pasa por encima del
       contenido que fluye, y en un teléfono mide 104px. */
    padding: calc(var(--nav-alto) + var(--u) * 4) calc(var(--u) * 4) var(--foto-alto);
    background: var(--negro-lienzo);
  }
  /* El velo existe para leer sobre la foto. Acá el texto ya está sobre negro,
     y el velo solo dibujaba un rectángulo más claro que la textura de atrás. */
  .descenso__mensajes::before { display: none; }
  .descenso__mensaje { opacity: 1; translate: 0; position: static; grid-area: auto; }
  .descenso__mensajes {
    display: flex;
    flex-direction: column;
    gap: calc(var(--u) * 2);
    max-width: 44rem;
    min-height: 0;
    margin: 0 auto calc(var(--u) * 6);
  }
  .descenso__lienzo,
  .descenso__poster,
  .descenso__remate { top: auto; bottom: 0; height: var(--foto-alto); }
}

/* La variante vertical (516x1076) tiene otra proporción. Misma condición que
   esVertical en descenso.js. */
@media (prefers-reduced-motion: reduce) and (max-width: 640px) and (orientation: portrait) {
  .descenso { --foto-alto: calc(100cqi * 1076 / 516); }
  .descenso .descenso__etapa { padding-inline: calc(var(--u) * 3); }
}
