/* ══════════════════════════════════════════════════════════════════════
   cascara.css — EL CHROME COMPARTIDO POR TODAS LAS CÁSCARAS

   css/lectura.css gobierna la superficie donde se LEE. Este archivo gobierna
   todo lo que la rodea: la cabecera, el panel de ajustes, los botones, los
   avisos, la barra de estado y la hoja que sube desde abajo en el móvil.

   Existe por una razón concreta y comprobada: el panel de ajustes de
   adaptar.html y el de web.html eran copia literal el uno del otro, y ya
   habían divergido. El campo de dirección de web.html llevaba el
   `font-size: 16px` que evita el zoom de iOS al enfocar; el mismo selector de
   adaptar.html se había quedado en 14,5 px. Nadie decidió eso: se decidió una
   vez y sólo se copió a un lado.

   Regla para saber qué va aquí y qué no: si las DOS cáscaras lo necesitan
   idéntico, va aquí. Lo que es de una sola —la zona de arrastre del PDF, los
   marcadores de página, la tarjeta de aviso del extractor de artículos— se
   queda en el <style> de su propia cáscara.

   Desde el rediseño, index.html (el Laboratorio) también enlaza esta hoja:
   tenía su propia copia de estos tokens y de estos controles, y por eso era
   la única pantalla con rótulos de 10,5 px en gris. Su <style> propio va
   DESPUÉS del enlace, así que lo que de verdad es del Laboratorio sigue
   ganando por orden de cascada.

   Rutas: relativas a css/. Cero red en tiempo de ejecución.
   ══════════════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════════════
   TOKENS

   Importante: estos tokens pintan sólo el CHROME. El fondo y el color del
   texto que se lee salen de --lectura-fondo / --lectura-texto
   (css/lectura.css), que siguen el tinte que la persona eligió y midió: el
   modo oscuro del sistema pinta la aplicación, nunca la lectura.

   ── LA PALETA, Y POR QUÉ ─────────────────────────────────────────────
   Antes eran grises azulados (#eef1f2 sobre #14191b). Un gris azulado frío
   es lo que sale por defecto de cualquier panel de control, y aquí además
   trabaja en contra: el deslumbramiento de un fondo blanco-azulado es la
   queja número uno de quien lee con estrés visual, que es justo por lo que
   esta herramienta ofrece el tinte «crema» para leer. Así que el chrome se
   pinta en papel sin blanquear —cálido, de croma bajo, un 8% por debajo del
   blanco— y la tinta es NEGRO PURO, no un negro teñido: 21:1, que es el
   contraste que la propia herramienta recomienda.

   El verde de la marca se conserva pero se aleja del turquesa: pasa de
   #12564a a un verde de tinta de imprenta, #0b5d3b, que sobre el papel da
   6,3:1 (AA para texto normal, no sólo para titulares).

   En modo oscuro NO se usa negro puro de fondo ni blanco puro de texto: el
   halo que producen sobre un fondo negro absoluto es un problema conocido
   para quien tiene astigmatismo, y aquí hay diez personas leyendo de noche.
   Fondo #191713, texto #f2eee4.
   ══════════════════════════════════════════════════════════════════════ */
:root {
  /* — los seis valores base — */
  --papel:      #ebe7e0;   /* el suelo de la aplicación: papel sin blanquear */
  --hoja:       #fbfaf7;   /* lo que se apoya encima: panel, tarjetas */
  --hoja-2:     #f2efe8;   /* lo hundido: campos, botones secundarios */
  --tinta:      #000000;   /* negro puro. 21:1. No hay negro teñido en esta app */
  --tinta-2:    #3b3a35;   /* texto secundario, 9,7:1 */
  --verde:      #0b5d3b;   /* la acción, y lo que es tuyo. 6,3:1 sobre papel */

  --verde-2:    #0a7a4b;
  --linea:      #d6d0c4;
  --linea-suave:#e4dfd6;

  /* — semánticos: todos por encima de 6:1 sobre el papel — */
  --mal:        #9a2417;  --mal-fondo:   #f7e7e2;
  --aviso:      #6e4e00;  --aviso-fondo: #f6eeda;
  --bien:       #0b5d3b;  --bien-fondo:  #e2ede5;

  /* Alias hacia los nombres viejos, para que nada de lo que ya estaba
     escrito con --ink o --accent se quede sin color. Los nombres nuevos son
     los buenos; estos existen para que el rediseño no tuviera que tocar a la
     vez las cuatro pantallas y los cuatro módulos. */
  --bg: var(--papel);
  --panel: var(--hoja);
  --panel-2: var(--hoja-2);
  --ink: var(--tinta);
  --ink-soft: var(--tinta-2);
  --muted: var(--tinta-2);
  --rule: var(--linea);
  --rule-soft: var(--linea-suave);
  --accent: var(--verde);
  --accent-2: var(--verde-2);
  --bad: var(--mal);      --bad-bg: var(--mal-fondo);
  --warn: var(--aviso);   --warn-bg: var(--aviso-fondo);
  --ok: var(--bien);      --ok-bg: var(--bien-fondo);

  /* ── TIPOGRAFÍA ──────────────────────────────────────────────────
     Atkinson Hyperlegible para TODO, también para los botones y los
     rótulos. Dos razones, las dos de esta herramienta y de ninguna otra:
     es la tipografía del perfil medido, y una herramienta que afirma que
     así se lee mejor tiene que estar escrita así. Además no cuesta ninguna
     descarga nueva: ya viaja vendorizada y css/lectura.css la declara.

     Lo que había aquí antes era "IBM Plex Sans", que NO está vendorizada:
     se pedía una fuente que no existe en el proyecto y caía siempre a la
     del sistema. Era una dependencia fantasma.

     El monoespaciado queda SÓLO para lo que es literalmente código (rutas
     de archivo, un comando). Como rótulo de un dato era decoración: los
     números se alinean con font-variant-numeric, que no necesita otra
     familia. */
  --sans: "Atkinson Hyperlegible", system-ui, -apple-system, "Segoe UI", sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, Consolas, monospace;

  /* Escala de cuerpo. El SUELO son 15 px: por debajo de eso no se escribe
     nada que signifique algo. Es la regla más importante de este rediseño y
     la que más cambia el resultado — la pantalla de medición tenía rótulos
     de 10,5 px y notas de 11,5 px en gris, en una aplicación cuya tesis es
     que el cuerpo pequeño cansa. */
  --t-titulo: 21px;   /* el nombre de la pantalla */
  --t-grupo:  19px;   /* el título de un grupo de mandos */
  --t-cuerpo: 16px;   /* todo lo demás: etiquetas, botones, prosa */
  --t-ayuda:  15px;   /* explicaciones y notas. El suelo. */

  /* MEDIDA del chrome: los mismos 60 caracteres por línea del perfil medido.
     Ninguna prosa de esta interfaz pasa de ahí, ni en una pantalla de 27". */
  --medida: 46ch;

  /* ── FORMAS ───────────────────────────────────────────────────────
     Atkinson Hyperlegible existe porque sus letras no se pueden confundir
     unas con otras. El mismo principio, aplicado a la interfaz: cada clase
     de cosa tiene una silueta distinta y sólo suya. Nada de un único
     border-radius repartido por todo.

       · botón     → píldora (999px). Se pulsa.
       · campo     → rectángulo de 6px, hundido. Se escribe dentro.
       · hoja      → 14px. Se apoya: panel, tarjeta, opción elegible.
       · aviso     → sin radio, con barra de color a la izquierda. Se lee.
       · chip      → píldora pequeña. Es una etiqueta de estado. */
  --r-boton: 999px;
  --r-campo: 6px;
  --r-hoja:  14px;
  --r-chip:  999px;

  /* Compatibilidad con lo ya escrito. */
  --r:  var(--r-campo);
  --rs: 4px;

  --s1: 6px; --s2: 10px; --s3: 16px; --s4: 24px; --s5: 40px; --s6: 56px;

  /* Objetivo táctil mínimo (WCAG 2.5.5, y gente leyendo en el móvil):
     nunca menos. */
  --tap: 44px;
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --papel:      #191713;
    --hoja:       #232019;
    --hoja-2:     #2c2820;
    --tinta:      #f2eee4;
    --tinta-2:    #c9c3b5;
    --verde:      #6ecb94;
    --verde-2:    #8adcab;
    --linea:      #3a352b;
    --linea-suave:#2e2a22;

    --mal:        #f09384;  --mal-fondo:   #33201c;
    --aviso:      #e0be72;  --aviso-fondo: #302713;
    --bien:       #6ecb94;  --bien-fondo:  #17251c;
  }
}

/* ══════════════════════════════════════════════════════════════════════
   BASE
   ══════════════════════════════════════════════════════════════════════ */
* { box-sizing: border-box; }
html, body { height: 100%; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--papel);
  color: var(--tinta);
  font-family: var(--sans);
  font-size: var(--t-cuerpo);
  line-height: 1.6;
  letter-spacing: .005em;
  -webkit-font-smoothing: antialiased;
}
button, input, select, textarea { font: inherit; color: inherit; letter-spacing: inherit; }
/* En el móvil, un toque sobre un mando no debe esperar a ver si es el primero
   de un doble toque (zoom): `manipulation` quita esa espera sin desactivar el
   zoom de pellizco, que quien lee con dificultad necesita conservar. */
a, button, summary, label, select, input, textarea { touch-action: manipulation; }
[hidden] { display: none !important; }
/* Los números de una tabla o de una medida se alinean por columna sin
   cambiar de familia: no hace falta monoespaciado para eso. */
.tabular, .val, .metric, table.data td.n { font-variant-numeric: tabular-nums; }

/* Respeta «reducir movimiento»: ninguna transición de esta interfaz aporta
   información por sí misma, así que apagarlas no pierde nada. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    transition-duration: .001ms !important;
    animation-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ══════════════════════════════════════════════════════════════════════
   ESQUELETO: cabecera + (panel | escenario) + barra de estado
   ══════════════════════════════════════════════════════════════════════ */
#app { display: flex; flex-direction: column; min-height: 100%; }

header.bar {
  display: flex; align-items: baseline; gap: var(--s3);
  padding: var(--s2) var(--s3);
  min-height: 60px; flex: 0 0 auto;
  background: var(--hoja);
  border-bottom: 1px solid var(--linea);
  flex-wrap: wrap;
  row-gap: var(--s1);
}
/* La marca es la palabra, y además es el camino de vuelta al inicio: desde
   el adaptador, el lector y el Laboratorio no se podía volver a la portada
   sin escribir la dirección a mano. */
.brand {
  font-weight: 700; font-size: var(--t-cuerpo); letter-spacing: -.01em;
  color: var(--tinta); text-decoration: none;
  display: inline-flex; align-items: center; min-height: var(--tap);
  white-space: nowrap;
}
a.brand:hover { color: var(--verde); text-decoration: underline; text-underline-offset: .22em; }
/* El nombre de la pantalla, aparte de la marca: antes iban pegados en un
   solo texto («Adaptar — Readability Lab»). Son dos cosas distintas y ahora
   se ven como dos cosas distintas. */
h1.pagina {
  margin: 0; font-size: var(--t-titulo); font-weight: 700;
  letter-spacing: -.015em; line-height: 1.2;
}
.bar .spacer { flex: 1; }

main { flex: 1; min-height: 0; display: flex; }
@media (max-width: 860px) { main { flex-direction: column; } }

aside {
  width: 372px; flex: 0 0 372px;
  background: var(--hoja);
  border-right: 1px solid var(--linea);
  overflow-y: auto;
  padding: var(--s4);
  display: flex; flex-direction: column; gap: var(--s5);
  max-height: calc(100vh - 60px);
  max-height: calc(100dvh - 60px);
  /* Al llegar al final del panel, el gesto no se «encadena» a la página. */
  overscroll-behavior: contain;
}
aside::-webkit-scrollbar { width: 10px; }
aside::-webkit-scrollbar-thumb { background: var(--linea); border-radius: 9px; }

/* En pantalla ancha el panel se queda pegado arriba mientras el texto pasa.
   Sin esto, un documento de sesenta mil píxeles de alto deja los controles de
   tipografía fuera de la pantalla desde el primer desplazamiento, y para
   mover un deslizador hay que volver al principio. En el móvil no aplica:
   ahí el panel ya es una hoja fija abajo. */
@media (min-width: 861px) {
  aside { position: sticky; top: 0; align-self: flex-start; }
}

/* ---- primer uso: todavía no hay documento ----
   Un único bloque tranquilo y centrado con la invitación a empezar. El resto
   del panel sigue oculto hasta que hay algo que leer.
   Sin sombra gris debajo: la tarjeta se separa del suelo porque es más clara
   que él y porque tiene un filete, no porque flote. */
#app.pre-doc main { align-items: center; justify-content: center; padding: var(--s4); }
#app.pre-doc aside {
  width: min(600px, 100%); flex: 0 0 auto; max-height: none;
  border-right: 0; border: 1px solid var(--linea);
  border-radius: var(--r-hoja);
  margin: auto;
}
#app.pre-doc #stage { display: none; }
#app:not(.pre-doc) .lead { display: none; } /* la explicación larga sólo hace falta la primera vez */

footer.status {
  flex: 0 0 auto; display: flex; align-items: center; gap: var(--s3);
  padding: var(--s2) var(--s3); background: var(--hoja); border-top: 1px solid var(--linea);
  font-size: var(--t-ayuda); color: var(--tinta-2); flex-wrap: wrap; row-gap: var(--s1);
}
footer.status .spacer { flex: 1; }
footer.status a { color: var(--verde); }
/* La promesa de privacidad vive aquí, a tamaño legible, y no repetida en
   la cabecera a 12,5 px en gris como estaba. */
.promesa { display: flex; align-items: center; gap: 8px; max-width: var(--medida); }
.promesa::before {
  content: ""; width: 9px; height: 9px; border-radius: 50%;
  background: var(--bien); flex: 0 0 9px;
}

/* ══════════════════════════════════════════════════════════════════════
   CONTROLES — lo que construye js/panel.js, y también los grupos propios
   de cada cáscara: los dos usan estas mismas clases.
   ══════════════════════════════════════════════════════════════════════ */
.group { display: flex; flex-direction: column; gap: var(--s3); }
/* El título de un grupo se distingue por PESO y por tamaño, nunca por
   versalitas ni por tracking: las mayúsculas sostenidas borran la silueta de
   la palabra, que es la pista que más cuesta reconstruir leyendo con
   dislexia. Y nunca en gris: si es un rótulo que hay que leer, va en tinta. */
/* El filete bajo el título no decora: dice dónde empieza un grupo. Sin él,
   «Sesión» (título) y «Quién lee» (etiqueta de campo) son las dos negrita a
   un tamaño parecido, y el panel se lee como una lista plana de doce cosas
   en vez de como cuatro grupos. */
.group > h2 {
  margin: 0; font-size: var(--t-grupo); font-weight: 700; color: var(--tinta);
  letter-spacing: -.015em; text-transform: none;
  padding-bottom: var(--s1); border-bottom: 1px solid var(--linea);
}
.lead, .group > p, .hint {
  margin: 0; font-size: var(--t-ayuda); line-height: 1.6;
  color: var(--tinta-2); max-width: var(--medida);
}

.field { display: flex; flex-direction: column; gap: var(--s1); }
.field > label {
  display: flex; justify-content: space-between; align-items: baseline; gap: var(--s2);
  font-size: var(--t-cuerpo); color: var(--tinta); font-weight: 700;
}
.field .val {
  font-size: var(--t-ayuda); color: var(--tinta-2); font-weight: 400;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.field .help { font-size: var(--t-ayuda); line-height: 1.55; color: var(--tinta-2); }

input[type="range"] {
  width: 100%; accent-color: var(--verde); margin: 0; height: var(--tap);
}
/* 16px como mínimo: por debajo de eso, Safari de iOS hace zoom al enfocar el
   campo y descoloca la página entera.
   El campo es un RECTÁNGULO hundido de 6px, y el botón una píldora: dos
   siluetas que no se pueden confundir. */
select, input[type="text"], input[type="url"], input[type="number"], textarea {
  width: 100%; min-height: var(--tap); padding: 10px 12px;
  background: var(--hoja-2); color: var(--tinta);
  border: 1px solid var(--linea); border-radius: var(--r-campo);
  font-size: 16px;
}
select:hover, input[type="text"]:hover, input[type="url"]:hover { border-color: var(--tinta-2); }
label.chk {
  display: flex; align-items: center; gap: 12px; min-height: var(--tap);
  font-size: var(--t-cuerpo); color: var(--tinta); cursor: pointer;
  max-width: var(--medida);
}
label.chk input { accent-color: var(--verde); width: 22px; height: 22px; flex: 0 0 22px; }
/* Una casilla que además lleva su propia explicación va dentro de un .field, y
   ahí la regla `.field > label` de arriba la repartiría a los extremos: la
   casilla contra el borde izquierdo y su texto contra el derecho, separados
   por medio panel. Esto la vuelve a juntar. */
.field > label.chk { justify-content: flex-start; font-weight: 400; }
:focus-visible { outline: 3px solid var(--verde); outline-offset: 2px; }

/* ---- el botón: píldora, y sólo el botón ---- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  min-height: var(--tap); padding: 10px 20px; cursor: pointer;
  background: var(--hoja-2); color: var(--tinta);
  border: 1.5px solid var(--linea); border-radius: var(--r-boton);
  font-size: var(--t-cuerpo); font-weight: 700;
  text-decoration: none; text-align: center;
}
.btn:hover { border-color: var(--tinta); }
.btn.primary { background: var(--verde); border-color: var(--verde); color: #fff; }
@media (prefers-color-scheme: dark) { :root:not([data-theme="light"]) .btn.primary { color: #191713; } }
.btn.primary:hover { background: var(--verde-2); border-color: var(--verde-2); }
.btn.wide { width: 100%; }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.rowbtns { display: flex; gap: var(--s2); flex-wrap: wrap; }
.rowbtns .btn { flex: 1 1 auto; }

/* Oculto VISUALMENTE, no con display:none ni con tamaño cero: Safari de iOS
   no abre el selector de archivos si el input no está de verdad en la
   maqueta, ni siquiera cuando lo abre un <label for> real. Esta es la única
   forma que funciona en el móvil, y por eso el panel de perfiles usa un
   <label for> y jamás un input.click(). */
.visually-hidden { position: absolute; left: -9999px; top: auto; }

/* ---- el aviso: sin radio y con barra a la izquierda ----
   La silueta cuadrada con barra es distinta de la píldora del botón y de la
   hoja redondeada de la tarjeta: se sabe qué es antes de leerlo. */
.note {
  font-size: var(--t-ayuda); line-height: 1.6; color: var(--tinta-2);
  padding: var(--s3) var(--s3) var(--s3) var(--s4);
  border-radius: 0 var(--r-campo) var(--r-campo) 0;
  background: var(--hoja-2);
  border-left: 5px solid var(--linea);
  max-width: var(--medida);
}
.note.warn { background: var(--aviso-fondo); border-left-color: var(--aviso); color: var(--aviso); }
.note.bad  { background: var(--mal-fondo);   border-left-color: var(--mal);   color: var(--mal); }
.note.ok   { background: var(--bien-fondo);  border-left-color: var(--bien);  color: var(--bien); }
.note.privacy { background: var(--hoja-2); border-left-color: var(--bien); color: var(--tinta-2); }
.note b { font-weight: 700; color: inherit; }
.note p { margin: 0 0 .7em; }
.note p:last-child { margin-bottom: 0; }
.note code { font-family: var(--mono); font-size: .95em; }
.note a { color: inherit; }

/* ---- ajustes avanzados: <details> nativo, plegado por defecto ----
   Se usa el elemento semántico en vez de JavaScript a medida: el teclado, el
   lector de pantalla y el estado abierto/cerrado vienen ya resueltos. */
details.group {
  border: 1px solid var(--linea); border-radius: var(--r-hoja);
  background: var(--hoja-2); padding: 0; gap: 0;
}
details.group > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: var(--s2); flex-wrap: wrap;
  min-height: var(--tap); padding: var(--s3);
  font-size: var(--t-grupo); font-weight: 700; color: var(--tinta);
}
details.group > summary::-webkit-details-marker { display: none; }
details.group > summary::before {
  content: "›"; display: inline-block; font-size: 22px; line-height: 1; color: var(--verde);
  transition: transform .15s ease; transform: rotate(0deg);
}
details.group[open] > summary::before { transform: rotate(90deg); }
details.group > summary:hover { color: var(--verde); }
details.group > .adv-body {
  display: flex; flex-direction: column; gap: var(--s4);
  padding: 0 var(--s3) var(--s3);
}
details.group > summary .hint {
  font-weight: 400; font-size: var(--t-ayuda); color: var(--tinta-2);
}

.progressbar { height: 10px; border-radius: var(--r-chip); background: var(--linea-suave); overflow: hidden; }
.progressbar > i { display: block; height: 100%; width: 0%; background: var(--verde); transition: width .15s linear; }
.progresstxt { font-size: var(--t-ayuda); color: var(--tinta-2); font-variant-numeric: tabular-nums; }

/* ---- chip de estado: la única píldora pequeña de la interfaz ---- */
.chip {
  display: inline-block; vertical-align: middle;
  font-size: 14px; font-weight: 700; line-height: 1.2;
  padding: 5px 12px; border-radius: var(--r-chip);
  white-space: nowrap;
}

/* ══════════════════════════════════════════════════════════════════════
   EL ESCENARIO — dónde se coloca la superficie de lectura. La superficie
   misma (.lectura / .lectura-regla / .lectura-contenido) vive en
   css/lectura.css; el relleno y el fondo alrededor son de cada cáscara.
   ══════════════════════════════════════════════════════════════════════ */
#stage { flex: 1; min-width: 0; position: relative; display: flex; flex-direction: column; }
#readerWrap {
  flex: 1; min-height: 0; overflow-y: auto;
  display: flex; flex-direction: column; align-items: center;
  padding: var(--s5) var(--s4) var(--s6);
  position: relative;
}
#reader.sin-documento { display: none; }

/* El estado vacío es una invitación a actuar, no un cartel gris: se lee al
   mismo tamaño que todo lo demás. */
.empty-state {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--tinta-2); font-size: var(--t-cuerpo); text-align: left;
  padding: var(--s4); max-width: var(--medida); margin: 0 auto;
}

/* ══════════════════════════════════════════════════════════════════════
   MÓVIL, con documento ya cargado — el panel de ajustes se convierte en una
   hoja que sube desde abajo, para que el texto sea la superficie principal y
   no compita con doce controles. Antes de tener documento el panel NO se
   pliega: sigue siendo la tarjeta central de entrada, en cualquier ancho.
   ══════════════════════════════════════════════════════════════════════ */
.sheet-handle { display: none; }
@media (max-width: 860px) {
  main { flex-direction: column; position: relative; }
  aside { width: auto; flex: 0 0 auto; }

  /* --peek: lo que asoma de la hoja cerrada: el alto de la manija. En
     teléfonos con zona segura inferior, la manija cerrada crece esa zona
     (ver más abajo) para que lo que asoma siga siendo sólo manija: antes
     asomaba además una franja del primer grupo de mandos, cortado a medias. */
  #app.has-doc { --peek: 56px; }
  #app.has-doc aside {
    position: fixed; left: 0; right: 0; bottom: 0; top: auto; z-index: 5;
    width: auto; flex: 0 0 auto;
    max-height: 82vh;
    max-height: 82dvh;  /* dvh: la barra de direcciones del móvil no tapa la hoja */
    border-right: 0; border-top: 1px solid var(--linea);
    border-radius: var(--r-hoja) var(--r-hoja) 0 0;
    /* La única sombra de la aplicación, y significa algo: esta hoja está
       de verdad por encima del texto. Cálida, no gris de plantilla. */
    box-shadow: 0 -8px 26px rgba(25, 23, 19, .22);
    padding: 0 var(--s4) calc(var(--s4) + env(safe-area-inset-bottom, 0px));
    gap: var(--s4);
    transform: translateY(calc(100% - var(--peek) - env(safe-area-inset-bottom, 0px)));
    transition: transform .25s ease;
  }
  #app.has-doc aside.sheet-open {
    transform: translateY(0);
    /* Con la hoja abierta, lo de detrás se atenúa: el sombreado es el propio
       box-shadow de la hoja, extendido más allá de la pantalla. */
    box-shadow: 0 -8px 26px rgba(25, 23, 19, .22), 0 0 0 100vmax rgba(25, 23, 19, .5);
  }
  /* Con la hoja abierta la página de detrás no se desplaza: el gesto sobre la
     hoja desplazaba el documento entero y la hoja se quedaba quieta. */
  html:has(#app.has-doc aside.sheet-open) { overflow: hidden; }
  #app.has-doc .sheet-handle {
    display: flex; align-items: center; justify-content: center; gap: var(--s2);
    position: sticky; top: 0; z-index: 1;
    margin: 0 calc(var(--s4) * -1) 0; padding: 0 var(--s4);
    height: var(--peek); min-height: var(--peek); background: var(--hoja);
    border: 0; border-bottom: 1px solid var(--linea-suave);
    border-radius: var(--r-hoja) var(--r-hoja) 0 0;
    width: calc(100% + var(--s4) * 2);
    font-size: var(--t-grupo); font-weight: 700; color: var(--tinta); cursor: pointer;
  }
  #app.has-doc aside:not(.sheet-open) .sheet-handle {
    height: calc(var(--peek) + env(safe-area-inset-bottom, 0px));
    min-height: calc(var(--peek) + env(safe-area-inset-bottom, 0px));
    padding-bottom: env(safe-area-inset-bottom, 0px);
  }
  #app.has-doc .sheet-handle .chev { transition: transform .2s ease; color: var(--verde); }
  #app.has-doc .sheet-handle[aria-expanded="true"] .chev { transform: rotate(180deg); }
  #app.has-doc #stage { padding-bottom: calc(var(--peek) + env(safe-area-inset-bottom, 0px) + var(--s3)); }
  #readerWrap { padding: var(--s4) var(--s3) var(--s5); }
  h1.pagina { font-size: 19px; }
}

/* ══════════════════════════════════════════════════════════════════════
   MÓVIL APAISADO — un teléfono de lado mide ~360 px de alto. La cabecera, la
   manija y una hoja del 82% se comen la pantalla y dejan tres líneas de
   texto. Ahí la hoja deja de ser hoja: el panel pasa a una columna fija a la
   izquierda, como en escritorio, y el texto ocupa todo el alto.
   ══════════════════════════════════════════════════════════════════════ */
@media (max-width: 1000px) and (max-height: 500px) and (orientation: landscape) {
  header.bar { min-height: 0; padding-block: 0; }
  main { flex-direction: row; }
  aside, #app.has-doc aside {
    position: sticky; top: 0; bottom: auto; left: auto; right: auto;
    align-self: flex-start;
    width: 300px; flex: 0 0 300px;
    /* Descontando la cabecera (la marca mide 44 px): con la página arriba
       del todo, el panel empieza debajo de ella y su final quedaba oculto. */
    max-height: calc(100vh - 44px); max-height: calc(100dvh - 44px);
    transform: none; transition: none;
    border-top: 0; border-right: 1px solid var(--linea); border-radius: 0;
    box-shadow: none;
    padding: var(--s4);
    padding-left: max(var(--s4), env(safe-area-inset-left, 0px));
  }
  #app.has-doc aside.sheet-open { box-shadow: none; }
  html:has(#app.has-doc aside.sheet-open) { overflow: visible; }
  #app.has-doc .sheet-handle { display: none; }
  #app.has-doc #stage { padding-bottom: 0; }
  #app.pre-doc aside { width: min(600px, 100%); flex: 0 0 auto; position: static; max-height: none; }
  #readerWrap { padding-right: max(var(--s3), env(safe-area-inset-right, 0px)); }
}

/* ══════════════════════════════════════════════════════════════════════
   IMPRESIÓN — lo tipográfico lo resuelve css/lectura.css; aquí sólo se
   esconde el chrome. Cada cáscara añade lo suyo en su propio <style>.
   ══════════════════════════════════════════════════════════════════════ */
@media print {
  header.bar, aside, footer.status, .empty-state { display: none !important; }
  body { background: #fff; overflow: visible; }
  main { display: block; }
  #stage { background: #fff !important; }
  #readerWrap { padding: 0; overflow: visible; display: block; }
}
