/* ============================================================================
   FACTIBLE · CORREO CORPORATIVO — SISTEMA DE DISEÑO "REGISTRO"
   app.css · capa 1: componentes compartidos.
   Depende de tokens.css. Nunca define colores literales: solo usa tokens.
   Orden: base · utilidades · armazón · barra · carpetas · REGISTRO ·
          lectura · botones · campos · distintivos · menús · diálogos ·
          paleta · esqueletos · avisos · vacíos · navegación del prototipo ·
          transiciones de vista · adaptable.
   ========================================================================= */

/* ==========================================================================
   1. BASE
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

/* El `display:none` del navegador para [hidden] tiene la misma especificidad que
   cualquier regla de componente, así que perdía contra todo lo que declara su
   propio display (.btn, .vacio, .banda, las ramas del árbol de carpetas): el
   atributo se ponía y el elemento se seguía viendo. Es un defecto de corrección,
   no de estilo — ocultar es una promesa del HTML y aquí se cumple.           */
[hidden] { display: none !important; }

html {
  /* Nunca se bloquea el zoom: sin user-scalable=no, sin max-scale. */
  -webkit-text-size-adjust: 100%;
  color-scheme: dark;
}

body {
  margin: 0;
  min-height: 100dvh;
  background: var(--lienzo);
  color: var(--txt-2);
  font-family: var(--fuente-texto);
  font-size: var(--txt-sm);
  font-weight: var(--peso-normal);
  line-height: var(--interlinea-normal);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3, h4 {
  margin: 0;
  color: var(--txt-1);
  font-weight: var(--peso-semi);
  line-height: var(--interlinea-apretada);
  text-wrap: balance;
}
p { margin: 0; text-wrap: pretty; }
ul, ol { margin: 0; padding: 0; list-style: none; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: var(--marca-texto); text-underline-offset: 0.2em; }
a:hover { color: var(--marca-texto-vivo); }
:where(img, svg) { display: block; max-width: 100%; }

/* UN TÍTULO CON GLIFO ES UNA FILA, NO DOS.
   DEFECTO CORREGIDO. La regla de arriba pone `display:block` a todo SVG —lo
   correcto para un icono que vive dentro de un contenedor flex, que es el 99 %
   del producto—, pero un `<h2>` normal no es flex: ahí el glifo se convertía en
   un bloque propio y caía JUSTO ENCIMA del texto, sin una sola separación. Se
   veía en la tarjeta «Por dónde salir» de los estados (404, 403, 500): medido,
   el icono y la primera línea compartían la misma x y quedaban a 15 px, pegados.
   Se arregla en el sistema y no en la vista porque el que rompe es el sistema.
   Especificidad (0,0,2): cualquier regla de clase de una hoja de vista gana. */
:is(h1, h2, h3, h4):has(> svg) {
  display: flex;
  align-items: center;
  gap: var(--e-2);
}

/* --- FOCO: un solo anillo, siempre visible, 9.4:1 contra el lienzo ------- */
:focus-visible {
  outline: var(--foco-anillo) solid var(--foco);
  outline-offset: var(--foco-separacion);
  border-radius: var(--radio-1);
}
/* Nunca se elimina el foco; solo se le da forma. */
:focus:not(:focus-visible) { outline: none; }

::selection {
  background: color-mix(in oklab, var(--marca-500) 45%, transparent);
  color: var(--txt-1);
}

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--regla-media);
  border: 3px solid var(--lienzo);
  border-radius: var(--radio-pleno);
}
::-webkit-scrollbar-thumb:hover { background: var(--regla-fuerte); }

/* ==========================================================================
   2. UTILIDADES
   ========================================================================== */

/* .cifra — OBLIGATORIA en toda cifra: horas, tamaños, conteos, folios, cuota.
   Factible Cifra con figuras tabulares para que las columnas cuadren de verdad. */
.cifra {
  font-family: var(--fuente-cifra);
  font-variant-numeric: var(--cifras-tabulares);
  font-feature-settings: "tnum" 1;
  letter-spacing: -0.01em;
}
.cifra--suave { color: var(--txt-3); }
.cifra--fuerte { color: var(--txt-1); font-weight: var(--peso-medio); }

/* .rotulo — etiqueta de una región. LA VERSALITA SE RACIONA A UNA POR REGIÓN.
   ------------------------------------------------------------------------
   Se midieron 177 versalitas en diez pantallas —45 sólo en Contactos— sobre
   157 usos de esta clase en 46 plantillas. Una versalita destaca porque es
   RARA; a veinte por pantalla forman un muro gris de 11 px, y eso es lo que se
   está llamando «tipografía agresiva»: no es la fuente, es dónde se aplica.

   Así que en reposo `.rotulo` es texto tranquilo: caja de frase, peso normal,
   13 px, --txt-3. La versalita la conserva UN rótulo por región —el del
   membrete de un panel o de una sección—, que es donde de verdad ordena.
   Ni una palabra se pierde: sólo deja de gritarlas el tipo. */
.rotulo {
  font-size: var(--txt-2xs);
  font-weight: var(--peso-normal);
  letter-spacing: 0;
  text-transform: none;
  color: var(--txt-3);
}
/* El rótulo que SÍ sigue siendo versalita: el del membrete (§6.1) y el de la
   cabecera de un panel. Uno por región y ninguno más. */
:is(.panel__cabecera, .membrete, .dia__titulo, .cinta, .seccion__cabecera,
    .bloque__titulo, .adjuntos__titulo, .adjuntos__membrete) .rotulo {
  font-size: var(--txt-3xs);
  font-weight: var(--peso-medio);
  letter-spacing: var(--espaciado-versalita);
  text-transform: uppercase;
}

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

.truncar {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* .herramientas — grupo compacto de botones icónicos en un membrete. */
.herramientas { display: flex; align-items: center; gap: var(--e-1); }

.pila { display: flex; flex-direction: column; gap: var(--e-2); }
.fila { display: flex; align-items: center; gap: var(--e-2); }
.fila--separada { justify-content: space-between; }
.empuje { margin-inline-start: auto; }

/* .regla — la línea horizontal que separa secciones del registro. */
.regla { height: 1px; background: var(--regla-fina); border: 0; margin: 0; }

/* ==========================================================================
   3. ARMAZÓN DE LA APLICACIÓN
   ========================================================================== */

.app {
  display: grid;
  grid-template-columns: var(--ancho-carpetas) var(--ancho-lista) minmax(0, 1fr);
  grid-template-rows: var(--altura-barra) minmax(0, 1fr);
  grid-template-areas:
    "barra   barra  barra"
    "carpetas lista lectura";
  height: 100dvh;
  overflow: hidden;
}

.app__barra    { grid-area: barra; }
.app__carpetas { grid-area: carpetas; min-height: 0; }
.app__lista    { grid-area: lista; min-height: 0; }
.app__lectura  { grid-area: lectura; min-height: 0; }

/* ►► EL REGISTRO SE LLEVA EL ANCHO ◄◄
   DISENO-SPEC §10: «la lista se lleva el ancho, no la lectura: el registro es
   la tesis». Lo que había era lo contrario, y medido: con la bandeja recién
   abierta el panel de lectura VACÍO ocupaba 800 px de 1600 —la mitad exacta de
   la ventana— contra 576 del registro, y en esos 800 px no había más que un
   sobre dibujado, un título y tres teclas explicando cómo elegir un renglón.
   Media pantalla dedicada a decir que no has elegido nada.

   Mientras no hay mensaje abierto, la tercera pista vale 0 y el panel no se
   dibuja: el registro pasa de 576 a 1 376 px. El `<article>` sigue en el DOM
   porque es el destino de htmx (`#panelLectura`), y en cuanto el servidor
   devuelve un mensaje la clase se va con el intercambio y la pista vuelve.

   NO SE ANIMA, a propósito: el registro cuelga del subgrid de
   `--registro-columnas`, así que animar el ancho pondría a bailar sesenta
   renglones y sus seis columnas a la vez. El cambio es instantáneo, que es
   además lo que se siente rápido.

   Sólo en escritorio: por debajo de 50rem el armazón ya enseña un panel a la
   vez (§19) y esta pista no existe. */
@media (min-width: 50.0625rem) {
  .app:has(> .app__lectura.panel--lectura-cerrada) {
    grid-template-columns: var(--ancho-carpetas) minmax(0, 1fr) 0;
  }
  .app__lectura.panel--lectura-cerrada { display: none; }
}

/* .panel — la caja base de cada región. Contenedor de consulta: sus hijos
   se adaptan al ancho DEL PANEL, no al de la ventana. */
.panel {
  container-type: inline-size;
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--superficie-1);
  border-inline-start: 1px solid var(--regla-fina);
  overflow: hidden;
}
.panel--carpetas { container-name: carpetas; background: var(--lienzo); border-inline-start: 0; }
.panel--lista    { container-name: lista; }
.panel--lectura  { container-name: lectura; background: var(--superficie-1); }

.panel__cabecera {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--e-3);
  min-height: 2.75rem;
  padding: var(--e-2) var(--e-4);
  border-bottom: 1px solid var(--regla-fina);
}
.panel__cuerpo { flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
/* Sólo el cuerpo cede alto. Cualquier OTRA banda de un panel —cabecera, pie,
   pestañas, saltos de sección, banda de lote— conserva su alto natural.
   PROMOVIDO EN INTEGRACIÓN: sin esta regla una banda sin `flex` propio se
   comprime contra el cuerpo y se le corta el texto. Le pasaba a la barra de
   saltos de Administración a 390 px. */
.panel > :not(.panel__cuerpo) { flex: 0 0 auto; }
.panel__pie {
  flex: 0 0 auto;
  padding: var(--e-3) var(--e-4);
  border-top: 1px solid var(--regla-fina);
}

/* ==========================================================================
   4. BARRA SUPERIOR
   ========================================================================== */

.barra {
  display: flex;
  align-items: center;
  gap: var(--e-4);
  height: var(--altura-barra);
  padding-inline: var(--e-4);
  background: var(--lienzo);
  border-bottom: 1px solid var(--regla-fina);
  z-index: var(--z-barra);
}

.barra__marca {
  display: flex;
  align-items: baseline;
  gap: var(--e-2);
  flex: 0 0 auto;
  min-width: calc(var(--ancho-carpetas) - var(--e-4));
}
.barra__logo {
  font-size: var(--txt-md);
  font-weight: var(--peso-bold, 700);
  letter-spacing: -0.015em;
  color: var(--txt-1);
}
.barra__logo b {
  font-weight: var(--peso-fuerte);
  /* El azul de la marca, no un degradado decorativo. */
  color: var(--marca-texto);
}
.barra__sufijo {
  font-size: var(--txt-3xs);
  letter-spacing: var(--espaciado-versalita);
  text-transform: uppercase;
  color: var(--txt-3);
}

.barra__centro { flex: 1 1 auto; display: flex; justify-content: center; min-width: 0; }
.barra__fin { flex: 0 0 auto; display: flex; align-items: center; gap: var(--e-2); }

/* .buscador — disparador de la búsqueda. Muestra su atajo, no lo esconde. */
.buscador {
  display: flex;
  align-items: center;
  gap: var(--e-2);
  width: min(34rem, 100%);
  min-height: 2rem;
  padding: var(--e-1) var(--e-2) var(--e-1) var(--e-3);
  background: var(--superficie-hundida);
  border: 1px solid var(--regla-fina);
  border-radius: var(--radio-2);
  color: var(--txt-3);
  text-align: start;
  cursor: text;
  transition: border-color var(--dur-2) var(--curva-estandar),
              background var(--dur-2) var(--curva-estandar);
}
.buscador:hover { border-color: var(--regla-media); background: var(--superficie-1); }
.buscador__texto { flex: 1 1 auto; font-size: var(--txt-sm); }

/* .tecla — representación de una tecla física. Siempre en Factible Cifra. */
.tecla {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5rem;
  height: 1.375rem;
  padding-inline: var(--e-1);
  background: var(--superficie-2);
  border: 1px solid var(--regla-media);
  border-bottom-width: 2px;
  border-radius: var(--radio-1);
  font-family: var(--fuente-cifra);
  font-size: var(--txt-3xs);
  line-height: 1;
  color: var(--txt-3);
}

/* .avatar — iniciales, nunca fotografías inventadas. */
.avatar {
  display: grid;
  place-items: center;
  inline-size: 1.75rem;
  block-size: 1.75rem;
  border-radius: var(--radio-pleno);
  background: var(--marca-fondo);
  border: 1px solid var(--marca-regla);
  color: var(--marca-texto-vivo);
  font-size: var(--txt-2xs);
  font-weight: var(--peso-semi);
  letter-spacing: 0.01em;
  flex: 0 0 auto;
}
.avatar--grande { inline-size: 2.5rem; block-size: 2.5rem; font-size: var(--txt-sm); }
.avatar--sistema {
  background: var(--superficie-2);
  border-color: var(--regla-media);
  color: var(--txt-3);
  border-radius: var(--radio-1);
}

/* ==========================================================================
   5. BARRA LATERAL DE CARPETAS
   ========================================================================== */

.carpetas { padding: var(--e-3) var(--e-2) var(--e-6); }
.carpetas__grupo { margin-block-end: var(--e-5); }
.carpetas__titulo { padding: var(--e-1) var(--e-3) var(--e-2); }
/* Cabecera del rail: el botón Redactar. PROMOVIDO EN INTEGRACIÓN — estaba
   duplicado idéntico en cinco view-*.css. */
.carpetas__accion { padding: 0 var(--e-2) var(--e-4); }

/* .carpeta — renglón de navegación. También es un registro: nombre a la
   izquierda, conteo en Factible Cifra a la derecha, alineado entre carpetas. */
.carpeta {
  display: grid;
  grid-template-columns: 1.125rem minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--e-3);
  width: 100%;
  min-height: 2rem;
  padding: var(--e-1) var(--e-3);
  background: none;
  border: 0;
  border-radius: var(--radio-2);
  color: var(--txt-2);
  font-size: var(--txt-sm);
  text-align: start;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--dur-1) var(--curva-estandar),
              color var(--dur-1) var(--curva-estandar);
}
.carpeta:hover { background: var(--hover-fila); color: var(--txt-1); }
.carpeta__icono { color: var(--txt-3); flex: 0 0 auto; }
.carpeta__nombre { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.carpeta__conteo {
  font-family: var(--fuente-cifra);
  font-variant-numeric: var(--cifras-tabulares);
  font-size: var(--txt-2xs);
  color: var(--txt-3);
}
/* Estado activo sin clase extra en JS: lo dice aria-current. */
.carpeta[aria-current="page"] {
  background: var(--seleccion);
  color: var(--txt-1);
  font-weight: var(--peso-medio);
}
.carpeta[aria-current="page"] .carpeta__icono { color: var(--marca-texto); }
.carpeta[aria-current="page"] .carpeta__conteo { color: var(--marca-texto-vivo); font-weight: var(--peso-medio); }
/* Carpeta con pendientes: el conteo se enciende, no el nombre. */
.carpeta:has(.carpeta__conteo[data-sin-leer]) .carpeta__conteo { color: var(--txt-1); }

/* .cuota — medidor de almacenamiento. Cifras en Factible Cifra. */
.cuota { display: flex; flex-direction: column; gap: var(--e-2); }

/* LA BARRA FINA DE MEDICIÓN — una sola forma para todo el producto.
   PROMOVIDO EN INTEGRACIÓN: tres vistas la habían escrito con tres alturas
   (4 px, 5 px) y dos acabados (con y sin filo). Es siempre 4 px y de cápsula.
   `.cuota__barra` cuota del buzón · `.uso__barra` uso por buzón en
   administración · `.medidor__barra` puntuación de spam en cuarentena.
   La barra SEGMENTADA de 12 px del panel de cuenta es otra pieza distinta y
   se define en view-panel.css: ahí la barra es el desglose, no un indicador. */
.cuota__barra,
.uso__barra,
.medidor__barra {
  display: block;
  height: 4px;
  background: var(--superficie-hundida);
  border-radius: var(--radio-pleno);
  overflow: hidden;
}
.cuota__relleno {
  height: 100%;
  width: var(--cuota, 0%);
  background: var(--marca-500);
  border-radius: inherit;
}
.cuota--alta .cuota__relleno { background: var(--aviso); }
.cuota--critica .cuota__relleno { background: var(--riesgo); }
.cuota__texto { font-size: var(--txt-2xs); color: var(--txt-3); }

/* ==========================================================================
   6. EL REGISTRO  —  la lista de mensajes
   --------------------------------------------------------------------------
   Tesis: la bandeja se lee como un libro de registro, no como una lista.
   Las columnas se declaran UNA vez en .registro; los grupos de día y los
   renglones las heredan con subgrid, así el remitente de un martes alinea
   con el remitente de un jueves. Sin subgrid cada renglón inventaría su
   propio ancho y la alineación sería una ilusión.
   ========================================================================== */

.registro {
  /* Juego de columnas por defecto. El "tamaño" NO está: en un panel de
     lista angosto roba ancho al asunto. Reaparece solo si el panel da para
     ello (ver la consulta de contenedor al final del archivo).            */
  --registro-columnas:
    var(--col-regla) var(--col-marca) var(--col-remitente)
    var(--col-asunto) var(--col-senales) var(--col-hora);
  display: grid;
  grid-template-columns: var(--registro-columnas);
  column-gap: var(--e-2);
  align-content: start;
}

/* Encabezado de columnas: el membrete del registro. Pegajoso.
   Su regla inferior sólo aparece cuando el registro ya se desplazó
   (animation-timeline: scroll()), así el panel arranca limpio.            */
.registro__cabecera {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  align-items: center;
  padding-block: var(--e-2);
  background: var(--superficie-1);
  border-bottom: 1px solid transparent;
}
/* Pegajosa SÓLO dentro de un panel con desplazamiento propio. Suelta era
   `position: sticky` incondicional, así que en un documento normal (el catálogo
   de estados, una lámina de muestras) se quedaba flotando encima de la página.
   Pegarse es una relación con el contenedor, no una propiedad del membrete. */
.panel__cuerpo > .registro > .registro__cabecera,
.panel__cuerpo .registro > .registro__cabecera {
  position: sticky;
  top: 0;
  z-index: var(--z-pegajoso);
}
.registro__cabecera > * { padding-block: 0; }
/* Las celdas del membrete se alinean igual que las del renglón: si la
   cabecera no cuadra con la columna, el registro deja de ser un registro. */
.registro__cabecera :is(.col-cifra, .col-hora) { text-align: end; }
.registro__cabecera .col-cifra { display: none; }
.registro__cabecera .col-hora { padding-inline-end: var(--e-4); }
.registro__cabecera .col-marca { display: flex; justify-content: center; }
@supports (animation-timeline: scroll()) {
  /* Misma condición que el pegado: fuera de un panel desplazable la línea se
     dibujaría siguiendo el scroll de la página, que no es lo que mide.      */
  .panel__cuerpo .registro > .registro__cabecera {
    animation: cabecera-al-desplazar linear both;
    animation-timeline: scroll(nearest block);
    animation-range: 0 4rem;
  }
  @keyframes cabecera-al-desplazar {
    to { border-bottom-color: var(--regla-fina); }
  }
}
@supports not (animation-timeline: scroll()) {
  .registro__cabecera { border-bottom-color: var(--regla-fina); }
}
/* Movimiento reducido: la regla del membrete se queda fija en vez de
   aparecer al desplazar. Se anula ESTA animación, no todas. */
@media (prefers-reduced-motion: reduce) {
  .registro__cabecera {
    animation: none !important;
    border-bottom-color: var(--regla-fina);
  }
}

/* --- Grupo de día ------------------------------------------------------- */
.dia {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
}
/* Sólo posición y relleno: display/align/gap vienen de .membrete (§6.1). */
.dia__titulo {
  grid-column: 1 / -1;
  padding: var(--e-5) var(--e-4) var(--e-2) 0;
  /* La fecha empieza en el mismo filo que la regla de margen. */
  padding-inline-start: calc(var(--col-regla) + var(--e-3));
}
/* EL CORTE DE DÍA, EN CAJA DE FRASE. Era la tercera fuente de versalita del
   producto —una por grupo, en las ocho secciones que agrupan por día—. Un
   corte de día ya se distingue por la regla que lo cruza y por el aire de
   arriba; no necesita además gritar la fecha. */
.dia__fecha {
  min-width: 0;
  font-size: var(--txt-2xs);
  font-weight: var(--peso-semi);
  letter-spacing: 0;
  text-transform: none;
  color: var(--txt-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
/* La regla que cruza: separa la sección igual que en un estado de cuenta.
   Ver .membrete (§6.1): esta línea es UNA sola regla compartida por todas las
   vistas; .dia__linea es sólo el nombre que usa el registro. */
/* LA MONOESPACIADA ES DE LOS DÍGITOS, NO DE LA PROSA QUE LOS ACOMPAÑA.
   Esta regla forzaba Factible Cifra sobre «4 mensajes · 1 sin leer» ENTERO: siete
   palabras de prosa leyéndose como código en cada corte de día. El spec
   siempre dijo «toda CIFRA en mono», no «todo valor». La familia baja al texto
   y sólo la sube un `.cifra` interno; `font-variant-numeric` se queda porque
   alinear dígitos no cuesta nada y no cambia la letra. */
.dia__conteo {
  flex: 0 0 auto;
  font-variant-numeric: var(--cifras-tabulares);
  font-size: var(--txt-2xs);
  color: var(--txt-3);
  white-space: nowrap;
}
.dia__conteo .cifra { font-family: var(--fuente-cifra); }
/* El encabezado de día se abrevia sin perder el conteo: en un panel angosto
   caen el día de la semana y la palabra "mensajes", nunca las cifras.      */
@container lista (max-width: 30rem) {
  .dia__fecha-detalle, .dia__unidad { display: none; }
}
.dia__renglones {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
}

/* ==========================================================================
   6.1 EL MEMBRETE  —  rótulo · regla que cruza · cifra
   --------------------------------------------------------------------------
   PROMOVIDO EN INTEGRACIÓN. Este patrón es el encabezado de sección de todo
   el producto: en la bandeja titula un día, en administración un bloque de
   servicios, en el panel una sección de ajustes, en lectura la lista de
   adjuntos y en la lámina de acceso la cinta de membrete. Siete vistas lo
   habían escrito por separado, con dos alineaciones (baseline / center), dos
   separaciones (--e-2 / --e-3) y cinco copias de la misma línea de 1px.
   Aquí hay UNA definición y las vistas sólo aportan su relleno.

   Contrato: contenedor flex · rótulo que no se parte · línea que se estira ·
   cifra pegada al filo derecho. La línea es la que da el aire, no el padding.
   ========================================================================== */

.membrete,
.dia__titulo,
.cinta,
.seccion__cabecera,
.bloque__titulo,
.adjuntos__titulo,
.adjuntos__membrete {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  margin: 0;
}

/* La regla que cruza. Una sola verdad para todo el producto.
   `min-width` no es opcional: sin él la línea se colapsa a 0 en paneles
   angostos y la sección pierde el filo que la separa de la anterior. */
.membrete__linea,
.dia__linea,
.cinta__linea,
.seccion__linea,
.bloque__linea,
.adjuntos__linea {
  flex: 1 1 var(--e-4);
  min-width: var(--e-4);
  height: 1px;
  background: var(--regla-fina);
  border: 0;
}

/* El rótulo de la izquierda: nunca se parte, se recorta. */
.membrete__rotulo {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--txt-1);
}

/* La cifra de la derecha: pegada al filo, en Factible Cifra, nunca se parte. */
.membrete__cifra,
.cinta__meta,
.seccion__meta,
.adjuntos__total {
  flex: 0 0 auto;
  font-size: var(--txt-2xs);
  color: var(--txt-3);
  white-space: nowrap;
}

/* --- EL RENGLÓN --------------------------------------------------------- */
/* Sin padding lateral y sin padding vertical propios: el relleno vive en las
   celdas. Así la REGLA DE MARGEN cubre el alto completo del renglón y el
   realce de hover llega a los filos del panel.                             */
.renglon {
  position: relative;
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  align-items: center;
  cursor: default;
  border-bottom: 1px solid color-mix(in oklab, var(--regla-fina) 55%, transparent);
  transition: background var(--dur-1) var(--curva-estandar);
}
.renglon > * { padding-block: var(--renglon-aire); }
.renglon:hover { background: var(--hover-fila); }

/* ►►► LA REGLA DE MARGEN ◄◄◄
   2px verticales al filo izquierdo. Encendida = sin leer. Apagada = leída.
   Sirve para UNA sola cosa. Adjuntos, etiquetas y prioridad van en otra
   columna. Con 167 de 187 sin leer, poner en negritas el 90% no jerarquiza
   nada; una regla encendida sí se puede barrer con la vista.               */
/* EL CARRIL Y LA TINTA. El filo son DOS capas, y hay una razón concreta:
   el carril (`background`) es el trazo apagado y no se mueve nunca; la tinta
   viva vive en el `::after` y entra escribiéndose de arriba abajo.
   Si el color vivo se pintara en el `background` —como se hacía— el trazo
   sería invisible: estaría dibujándose encima de un filo que YA está azul.

   Que la tinta esté en una capa aparte tiene un segundo efecto que conviene
   no perder: las otras secciones (administración pinta su filo de atención,
   contactos lo apaga) siguen escribiendo `background` sobre el carril y no se
   enteran de nada. */
.renglon__regla {
  align-self: stretch;
  position: relative;
  background: var(--regla-margen-muerta);
  padding-block: 0;
}
.renglon__regla::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--regla-margen-viva);
  transform: scaleY(0);
  transform-origin: top;
}
.renglon[data-sin-leer="true"] .renglon__regla::after { transform: scaleY(1); }

/* EL ELEMENTO FIRMA — el trazo de 240 ms.
   Sólo lo dibuja el renglón que ACABAS DE TOCAR. El servidor estampa
   `data-trazo="recien"` únicamente en `parciales/respuesta_fila.html`, que
   devuelve UN renglón; el registro completo y los grupos de día no lo llevan
   nunca. Sin esa disciplina, cambiar de filtro dispararía sesenta trazos a la
   vez y lo que es un acuse se convertiría en un espectáculo.

   `--curva-tinta` es de un solo uso y este es el uso: velocidad pareja, como
   una pluma que no acelera. `@starting-style` es lo que da el «de dónde
   viene» a un elemento que htmx acaba de insertar. */
.renglon[data-trazo="recien"] .renglon__regla::after {
  transition: transform var(--dur-trazo) var(--curva-tinta);
}
/* Al apagarse se borra por el otro extremo: la pluma levanta por donde acabó. */
.renglon[data-trazo="recien"][data-sin-leer="false"] .renglon__regla::after {
  transform-origin: bottom;
}
@starting-style {
  .renglon[data-trazo="recien"][data-sin-leer="true"]  .renglon__regla::after { transform: scaleY(0); }
  .renglon[data-trazo="recien"][data-sin-leer="false"] .renglon__regla::after { transform: scaleY(1); }
}

/* Único refuerzo permitido del no-leído: el asunto sube a --txt-1.
   El peso NO cambia. Nada de negritas masivas.                             */
.renglon__asunto { color: var(--txt-3); }
.renglon[data-sin-leer="true"] .renglon__asunto { color: var(--txt-1); }
.renglon[data-sin-leer="true"] .renglon__remitente { color: var(--txt-1); }

/* Celda de marca: casilla de selección / estrella. */
.renglon__marca {
  display: flex;
  align-items: center;
  justify-content: center;
}

.renglon__remitente {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--txt-sm);
  color: var(--txt-2);
}
/* Remitente automatizado: se apaga sin desaparecer. Es el ruido DMARC. */
.renglon[data-tipo="automatico"] .renglon__remitente {
  color: var(--txt-3);
  font-family: var(--fuente-cifra);
  font-size: var(--txt-2xs);
}

/* Asunto + vista previa del cuerpo en la misma celda: el sistema actual
   recorta la vista previa; aquí es parte del renglón.                      */
/* Asunto arriba, vista previa del cuerpo debajo. En una sola línea ambos
   se truncarían y ninguno serviría; apilados, los dos se leen y las demás
   columnas siguen alineadas por subgrid.                                  */
.renglon__cuerpo { min-width: 0; display: grid; gap: 1px; align-content: center; }
.renglon__asunto {
  font-size: var(--txt-md);
  font-weight: var(--peso-normal);
  line-height: 1.3;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.renglon__previa {
  min-width: 0;
  font-size: var(--txt-xs);
  line-height: 1.3;
  color: var(--txt-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Columna de señales: adjuntos, hilo, etiqueta. NO estado de lectura. */
/* Columna de señales: SOLO iconos y puntos. Nada de etiquetas de texto —
   repetir "Automático" doce veces es el ruido que este rediseño elimina.  */
.renglon__senales {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--e-2);
  color: var(--txt-3);
}

.renglon__cifra,
.renglon__hora {
  font-family: var(--fuente-cifra);
  font-variant-numeric: var(--cifras-tabulares);
  font-size: var(--txt-2xs);
  color: var(--txt-3);
  text-align: end;
  white-space: nowrap;
}
.renglon__cifra { display: none; }
.renglon__hora { padding-inline-end: var(--e-4); }
.renglon[data-sin-leer="true"] .renglon__hora { color: var(--txt-2); }

/* Renglón seleccionado / abierto. */
.renglon[aria-selected="true"] { background: var(--seleccion); }
.renglon[aria-selected="true"] .renglon__asunto { color: var(--txt-1); }
/* Cursor de teclado del recorrido j/k. No se confía sólo en :focus-visible:
   en una lista con tabindex móvil el navegador no siempre lo considera
   interacción de teclado. Mientras el foco esté DENTRO del registro, el
   renglón activo lleva anillo visible (9.4:1 contra su fondo).            */
.renglon:focus-visible,
.registro:focus-within .renglon[aria-selected="true"] {
  outline: none;
  background: var(--realce-medio);
  box-shadow: inset 0 0 0 2px var(--foco);
}
/* Seleccionado con casilla: sin clase extra, lo dice :has(). */
.renglon:has(.casilla:checked) { background: var(--realce-suave); }

/* --- Acciones al pasar el cursor ---------------------------------------- */
/* Comportamiento BASE, el de los registros de UNA sola línea: administración,
   contactos, la cola de salida. Ahí la tira flota sobre las últimas columnas
   —que son cifras— y esas cifras se apagan mientras está a la vista.
   EL REGISTRO DE MENSAJES YA NO USA ESTO: desde §16.2 su renglón es de dos
   líneas y la tira baja a la rejilla, a la franja que la primera línea deja
   vacía. El porqué está escrito ahí.
   Aparece con hover o con foco dentro del renglón (teclado incluido).      */
.renglon__acciones {
  position: absolute;
  inset-inline-end: var(--e-3);
  inset-block: var(--e-1);
  display: flex;
  align-items: center;
  gap: var(--e-1);
  padding: 0 var(--e-1);
  padding-block: 0;
  background: color-mix(in oklab, var(--superficie-2) 92%, transparent);
  border: 1px solid var(--regla-fina);
  border-radius: var(--radio-2);
  box-shadow: var(--sombra-1);
  opacity: 0;
  visibility: hidden;
  transform: translateX(var(--e-1));
  transition: opacity var(--dur-1) var(--curva-estandar),
              transform var(--dur-1) var(--curva-salida),
              visibility var(--dur-1);
}
.renglon:hover .renglon__acciones,
.renglon:focus-within .renglon__acciones,
.renglon:focus-visible .renglon__acciones {
  opacity: 1;
  visibility: visible;
  transform: none;
}
/* Cuando asoman las acciones, las cifras se apagan en vez de estorbar. Sólo
   donde la tira de verdad las tapa: en el renglón de mensaje ya no las tapa,
   y apagar un dato que se sigue leyendo es la peor de las dos opciones.    */
.renglon:not(:has(> .renglon__cuerpo)):hover
  :is(.renglon__cifra, .renglon__hora) { opacity: 0.25; }

/* .accion-fila — botón icónico dentro del renglón. */
.accion-fila {
  display: grid;
  place-items: center;
  inline-size: 1.75rem;
  block-size: 1.75rem;
  padding: 0;
  background: none;
  border: 0;
  border-radius: var(--radio-1);
  color: var(--txt-3);
  cursor: pointer;
  transition: background var(--dur-1) var(--curva-estandar),
              color var(--dur-1) var(--curva-estandar);
}
.accion-fila:hover { background: var(--superficie-3); color: var(--txt-1); }
.accion-fila--peligro:hover { background: var(--riesgo-fondo); color: var(--riesgo); }

/* ==========================================================================
   6.2 EL COLOR DEL VERBO  —  una acción se enciende con lo que significa
   --------------------------------------------------------------------------
   Sólo «Borrar» tenía señal propia al pasar el cursor; «Archivar» y las dos
   marcas se encendían las dos con el mismo gris (--superficie-3 + --txt-1),
   así que la tira de tres botones daba UNA señal y tres botones. Ahora cada
   verbo tiene la suya, y no hay que inventar ninguna paleta: el producto ya
   tiene exactamente tres semánticos y las tres acciones caen una en cada uno,
   con el significado que el spec §2 ya les había dado.

     ARCHIVAR   --exito   «entregado, verificado, programado» → asunto cerrado,
                          fuera de la bandeja. Es el verde de lo que se
                          despachó bien, que es literalmente lo que se hace.
     MARCAR     --aviso   «requiere atención o respuesta». Destacar y marcar
                          sin leer son las dos formas de decir «vuelvo a esto»:
                          es el ámbar del pendiente, y es además el color con
                          el que el ojo lleva toda la vida buscando un
                          marcador.
     BORRAR     --riesgo  el spec lo dice con esa palabra: «falla, rechazo,
                          borrar».

   NO se usa el azul y no es por gusto: el azul es de la regla de margen y del
   único enlace de marca. Un botón que se pone azul al tocarlo compite con el
   único gesto que el producto se permitió gastar.

   El enganche es `data-accion`, que las plantillas YA escriben para el
   teclado de `base.html`: el verbo lo declara el servidor una vez y el color
   se lo pone el sistema. Ninguna plantilla tiene que aprender una clase nueva,
   y el mismo botón se ve igual en el renglón, en el panel de lectura y en el
   menú, que es donde antes se desincronizaban.
   ========================================================================== */
:is(.accion-fila, .btn)[data-accion="archivar"]:is(:hover, :focus-visible) {
  background: var(--exito-fondo);
  border-color: var(--exito-regla);
  color: var(--exito);
}
:is(.accion-fila, .btn):is([data-accion="destacar"], [data-accion="leido"]):is(:hover, :focus-visible) {
  background: var(--aviso-fondo);
  border-color: var(--aviso-regla);
  color: var(--aviso);
}
:is(.accion-fila, .btn)[data-accion="borrar"]:is(:hover, :focus-visible) {
  background: var(--riesgo-fondo);
  border-color: var(--riesgo-regla);
  color: var(--riesgo);
}

/* La casilla NO compite con la regla de margen: aparece al pasar el cursor,
   al recibir foco o cuando está marcada. El filo izquierdo del registro es
   de la regla y de nadie más. */
.renglon .casilla { opacity: 0; transition: opacity var(--dur-1) var(--curva-estandar); }
.renglon:hover .casilla,
.renglon:focus-within .casilla,
.renglon .casilla:focus-visible,
.renglon .casilla:checked { opacity: 1; }

/* .casilla — casilla de selección propia, cuadrada como el registro.
   `--glifo-palomita` es la máscara de la marca; el porqué del trazo y del
   `%23000` está escrito debajo, junto a `.casilla::after`. */
.casilla {
  appearance: none;
  --glifo-palomita: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 8.4 6.4 11.8 13 4.6'/%3E%3C/svg%3E");
  display: grid;
  place-content: center;
  inline-size: 0.9375rem;
  block-size: 0.9375rem;
  margin: 0;
  background: var(--superficie-hundida);
  border: 1px solid var(--regla-media);
  border-radius: var(--radio-1);
  cursor: pointer;
  transition: background var(--dur-1) var(--curva-estandar),
              border-color var(--dur-1) var(--curva-estandar);
}
.casilla:hover { border-color: var(--regla-fuerte); }
.casilla:checked {
  background: var(--marca-500);
  border-color: var(--marca-500);
}

/* ►► LA PALOMITA ◄◄
   DEFECTO CORREGIDO. La casilla marcada dibujaba `background-size: 60% 2px`
   sobre el relleno azul: un GUION centrado, que en toda interfaz —y en la
   propia especificación del control— significa «indeterminado», o sea «hay
   algo seleccionado aquí dentro, pero no todo». Marcar un mensaje no es un
   estado parcial: es sí o no. Se dibuja la palomita.

   Por qué la SENCILLA y no el doble-check del sprite: `#i-leido` (el doble
   check) es el glifo del botón «Marcar como leído», que vive en la tira de
   acciones DEL MISMO RENGLÓN, a 40 px de aquí. Dos cosas distintas con el
   mismo signo a esa distancia es exactamente el error que la regla de margen
   existe para no cometer. El trazo es el de `#i-palomita`, sin inventar
   ninguno: mismo viewBox 16 y misma geometría, con el grosor subido de 1.3 a
   2 porque a 10 px un trazo de 1.3 se deshace.

   El `%23000` de la máscara NO es un color: una máscara sólo lee el canal
   alfa, así que ese negro es «opaco» y nada más. La tinta la pone
   `--txt-invertido`, que es la que cambia con la edición —en pantalla oscurece
   sobre el azul, en papel aclara—; un literal habría fallado en una de las dos.
   Si un motor no pintase pseudoelementos sobre un `input`, la casilla se
   queda como un cuadro azul lleno: sigue leyéndose «marcada», nunca
   «indeterminada». */
.casilla::after {
  content: "";
  inline-size: 0.625rem;
  block-size: 0.625rem;
  background: var(--txt-invertido);
  -webkit-mask-image: var(--glifo-palomita);
  mask-image: var(--glifo-palomita);
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  opacity: 0;
  scale: 0.5;
  transition: opacity var(--dur-1) var(--curva-estandar),
              scale var(--dur-1) var(--curva-salida);
}
.casilla:checked::after { opacity: 1; scale: 1; }

/* ==========================================================================
   7. PANEL DE LECTURA
   Se adapta a SU ancho con container queries, no al de la ventana: en un
   cliente de tres paneles la ventana no dice nada útil sobre este panel.
   ========================================================================== */

/* La cabecera del panel de lectura era una banda con seis mandos; hoy sólo
   lleva el regreso al registro, y ese regreso sólo existe cuando se ve un
   panel a la vez. En escritorio la banda entera —con su filo y sus 44 px—
   sobra: el registro está a la izquierda, a la vista. */
@media (min-width: 50.0625rem) {
  .panel--lectura > .panel__cabecera--volver { display: none; }
}

.lectura { padding: var(--e-6) var(--e-6) var(--e-12); }

.lectura__encabezado {
  display: flex;
  flex-direction: column;
  gap: var(--e-4);
  padding-block-end: var(--e-5);
  border-bottom: 1px solid var(--regla-fina);
}
.lectura__asunto {
  font-size: var(--txt-xl);
  font-weight: var(--peso-semi);
  color: var(--txt-1);
  text-wrap: balance;
}
.lectura__identidad { display: flex; align-items: flex-start; gap: var(--e-3); }
.lectura__quien { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.lectura__nombre { font-weight: var(--peso-medio); color: var(--txt-1); }
.lectura__correo { font-family: var(--fuente-cifra); font-size: var(--txt-2xs); color: var(--txt-3); }
/* Remitente que es una máquina: el nombre ES la dirección (112 de los 187
   mensajes de la semilla), y la plantilla ya no la escribe dos veces. Como lo
   único que queda es una dirección, se compone como se componen las
   direcciones en todo el producto —monoespaciada— pero al nivel de la
   identidad, no al del pie de firma. */
.lectura__nombre--direccion {
  font-family: var(--fuente-cifra);
  font-size: var(--txt-2xs);
  font-weight: var(--peso-normal);
  color: var(--txt-1);
  word-break: break-all;
}
.lectura__marca-tiempo { font-family: var(--fuente-cifra); font-size: var(--txt-2xs); color: var(--txt-3); }

/* Ficha del mensaje: pares dato/valor alineados como un estado de cuenta. */
.ficha {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: var(--e-1) var(--e-4);
  padding: var(--e-3) 0;
  font-size: var(--txt-2xs);
}
/* LA FICHA, SIN GRITAR Y SIN HABLAR EN CÓDIGO.
   Dos correcciones de sistema, 109 usos en el producto:
   · el DATO era versalita de 11 px. Es la etiqueta de una fila dentro de un
     plegable que ya tiene su propio rótulo arriba: no manda nada, sólo pesa.
     Baja a caja de frase y hereda los 13 px de `.ficha`.
   · el VALOR forzaba Factible Cifra sobre CUALQUIER valor, no sólo sobre cifras:
     «Correo institucional», «Se muestra», «sólo tú», «ninguna», «Interna ·
     sin red» se leían como código. De los 559 nodos monoespaciados que se
     midieron en el producto, 116 no contenían ni un dígito, y ésta es la
     regla que ponía la mayoría. La mono la pide ahora el `.cifra` de dentro,
     que es quien sabe si lo que envuelve es un número. */
.ficha__dato { color: var(--txt-3); }
.ficha__valor { color: var(--txt-2); font-variant-numeric: var(--cifras-tabulares); }
.ficha__valor .cifra { font-family: var(--fuente-cifra); }

/* .ficha--atajos — la ficha invertida del diálogo de ayuda de teclado: aquí el
   DATO es la tecla (mono, encendida) y el VALOR es la prosa. Es la única ficha
   del sistema que se lee al revés, y por eso tiene modificador propio.
   PROMOVIDO EN INTEGRACIÓN: duplicado idéntico en seis view-*.css. */
.ficha--atajos { gap: var(--e-2) var(--e-5); }
.ficha--atajos .ficha__dato {
  font-family: var(--fuente-cifra);
  text-transform: none;
  letter-spacing: 0;
  font-size: var(--txt-2xs);
  color: var(--txt-1);
}
.ficha--atajos .ficha__valor { font-family: var(--fuente-texto); }

/* --- 7.1 LA CABECERA DEL MENSAJE, PLEGADA -------------------------------
   Eran siete renglones rotulados —FOLIO, PARA, COPIA, CARPETA, TAMAÑO,
   AUTENTICACIÓN, ADJUNTO— y empujaban el cuerpo casi 300 px hacia abajo. Un
   correo se abre para leerlo. Se queda a la vista lo que se lee SIEMPRE (a
   quién iba y si el remitente es quien dice ser) y se pliega lo que se
   consulta A VECES. La línea visible es además el mando que abre el resto:
   así no se cambia un renglón de datos por un renglón de control.        */

.lectura__detalles {
  padding-block: var(--e-3);
  border-bottom: 1px solid var(--regla-fina);
}
.lectura__sumario {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  padding-block: var(--e-1);
  font-size: var(--txt-2xs);
  color: var(--txt-3);
  cursor: pointer;
  list-style: none;                 /* fuera el triángulo del navegador: */
  border-radius: var(--radio-1);    /* el chevron de la derecha ya lo dice */
}
.lectura__sumario::-webkit-details-marker { display: none; }
.lectura__sumario:hover { color: var(--txt-2); }
.lectura__sumario:hover .lectura__abrir { color: var(--txt-2); }

/* «Para» y su destinatario ocupan el ancho; el resto se pega a la derecha. */
.lectura__para {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: var(--e-2);
  overflow: hidden;
}
.lectura__destino {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  color: var(--txt-2);
}
.lectura__mas { flex: 0 0 auto; color: var(--txt-3); }

/* RETIRADO — `.lectura__sello`. Era la palomita verde de «Verificado», y se
   dibujaba en TODO correo legítimo, o sea en el 99 %: una confirmación de que
   no pasa nada, al lado de otra que decía «SPF · DKIM · DMARC» y que era una
   constante escrita en la plantilla. Sólo habla la desviación; si algún día el
   proveedor sabe decir «sin verificar», eso sí tendrá sitio aquí. */

.lectura__abrir {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: var(--e-1);
  color: var(--txt-3);
  transition: color var(--dur-1) var(--curva-estandar);
}
/* El chevron del sprite apunta a la derecha: se le da un cuarto de vuelta
   para que apunte a donde va a salir el contenido, y otro medio al abrirse.
   Es el único movimiento de esta pieza y dice en qué se convirtió el
   plegable, que es la única razón admitida para animar aquí.              */
.lectura__abrir svg {
  rotate: 90deg;
  transition: rotate var(--dur-2) var(--curva-estandar);
}
.lectura__detalles[open] .lectura__abrir svg { rotate: -90deg; }

/* El plegable llega a su ALTO REAL, sin max-height de mentiras:
   `interpolate-size: allow-keywords` está encendido en tokens.css §9.b y es
   lo que hace animable `block-size: auto`. Con las duraciones a 1 ms
   (movimiento reducido) esto abre de golpe, que es lo que se pide.        */
.lectura__detalles::details-content {
  block-size: 0;
  overflow: clip;
  transition: block-size var(--dur-desplegar) var(--curva-estandar),
              content-visibility var(--dur-desplegar) allow-discrete;
}
.lectura__detalles[open]::details-content { block-size: auto; }
.lectura__ficha { padding-block: var(--e-3) var(--e-1); }

/* --- 7.2 LOS ADJUNTOS ----------------------------------------------------
   Eran un renglón más de la ficha, con el nombre subrayado y el peso al
   lado; se perdían entre los metadatos. Aquí cada adjunto es una ficha y la
   ficha ENTERA es el enlace de descarga: el tipo de archivo se lee en el
   cuadro de la izquierda, el peso en el centro y el verbo a la derecha, así
   que no hay que adivinar qué pasa al pulsar.                             */

.lectura__adjuntos { padding-block: var(--e-4) var(--e-2); }
.lectura__adjunto-lista {
  display: grid;
  gap: var(--e-2);
  margin: 0;
  padding: 0;
  padding-block-start: var(--e-3);
  list-style: none;
}
.lectura__adjunto {
  display: grid;
  grid-template-columns: 2.5rem minmax(0, 1fr) auto auto;
  align-items: center;
  gap: var(--e-3);
  padding: var(--e-2) var(--e-3);
  background: var(--superficie-2);
  border: 1px solid var(--regla-fina);
  border-radius: var(--radio-2);
  color: var(--txt-2);
  text-decoration: none;
  transition: background var(--dur-1) var(--curva-estandar),
              border-color var(--dur-1) var(--curva-estandar);
}
.lectura__adjunto:hover {
  background: var(--superficie-3);
  border-color: var(--regla-fuerte);
}
/* El cuadro del tipo: la extensión del archivo, en Factible Cifra, dentro de una
   caja del alto de la ficha. Es la pieza que hace evidente qué es cada uno
   sin inventar un icono por formato.                                      */
.lectura__adjunto-tipo {
  display: grid;
  place-items: center;
  inline-size: 2.5rem;
  block-size: 1.875rem;
  background: var(--superficie-hundida);
  border: 1px solid var(--regla-fina);
  border-radius: var(--radio-1);
  font-size: var(--txt-3xs);
  color: var(--txt-3);
}
.lectura__adjunto-nombre {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--txt-sm);
  color: var(--txt-1);
}
.lectura__adjunto-peso {
  display: inline-flex;
  align-items: center;
  gap: var(--e-2);
  font-size: var(--txt-2xs);
  color: var(--txt-3);
}
/* `--marca-texto` ya existe (tokens.css §4.8.c, con su valor por edición): es
   el azul de marca que se lee como TEXTO en las dos. Lo comparten el Deshacer
   del aviso (§15) y cualquier enlace de marca del producto. */
.lectura__adjunto-bajar {
  display: inline-flex;
  align-items: center;
  gap: var(--e-1);
  font-size: var(--txt-2xs);
  color: var(--marca-texto);
}

.lectura__prosa {
  max-width: var(--medida-prosa);
  padding-block: var(--e-6);
  font-size: var(--txt-md);
  line-height: var(--interlinea-prosa);
  color: var(--txt-2);
}
.lectura__prosa > * + * { margin-block-start: var(--e-4); }
/* Cita: el sistema actual la deja a 2.56:1. Aquí conserva --txt-3 (6.2:1)
   y se distingue por la regla de margen, no por apagar el texto.          */
.lectura__prosa blockquote {
  margin: 0;
  padding-inline-start: var(--e-4);
  border-inline-start: 2px solid var(--regla-media);
  color: var(--txt-3);
}

/* ►► LA ÚNICA ZONA DE ACCIÓN DEL MENSAJE ABIERTO ◄◄
   El panel ofrecía tres para el mismo correo —dos botones arriba a la
   izquierda, tres iconos arriba a la derecha, cuatro al pie— con Responder,
   Reenviar y Archivar duplicados. Queda ésta, la del final del texto: es donde
   se decide qué hacer con lo que se acaba de leer. A la izquierda lo que se
   escribe (Responder es el primario), y empujado al filo derecho lo que se
   hace con el mensaje: destacar, archivar, borrar. */
.lectura__acciones {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--e-2);
  padding-block-start: var(--e-5);
  border-top: 1px solid var(--regla-fina);
}
.lectura__acciones-fin {
  display: inline-flex;
  align-items: center;
  gap: var(--e-1);
  margin-inline-start: auto;
}

/* Adaptación por ancho del PANEL. */
@container lectura (min-width: 34rem) {
  .lectura__encabezado { flex-direction: row; align-items: flex-start; justify-content: space-between; gap: var(--e-6); }
  .lectura__encabezado > .lectura__asunto { order: -1; flex: 1 1 auto; }
}
@container lectura (max-width: 30rem) {
  .lectura { padding: var(--e-4) var(--e-4) var(--e-10); }
  .lectura__asunto { font-size: var(--txt-lg); }
  .ficha { grid-template-columns: minmax(0, 1fr); gap: 0; }
  .ficha__dato { margin-block-start: var(--e-2); }
  /* Panel angosto: de la línea plegada se va la palabra «Detalles» (queda el
     chevron, que ya dice lo mismo) y del adjunto se va el verbo, no el
     icono: lo que se recorta es la etiqueta, nunca la acción.            */
  .lectura__abrir-texto { display: none; }
  .lectura__adjunto-verbo { display: none; }
}

/* ==========================================================================
   8. BOTONES
   ========================================================================== */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--e-2);
  min-height: 2rem;
  padding: var(--e-1) var(--e-3);
  background: var(--superficie-2);
  border: 1px solid var(--regla-media);
  border-radius: var(--radio-2);
  color: var(--txt-1);
  font-size: var(--txt-sm);
  font-weight: var(--peso-medio);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--dur-1) var(--curva-estandar),
              border-color var(--dur-1) var(--curva-estandar),
              color var(--dur-1) var(--curva-estandar);
}
.btn:hover { background: var(--superficie-3); border-color: var(--regla-fuerte); }
.btn:active { transform: translateY(0.5px); }
/* APAGADO CON MOTIVO.
 *
 * Antes esta regla llevaba `pointer-events: none`, y ese `pointer-events`
 * MATABA el `title` del propio mando: el puntero atravesaba el botón y nunca
 * disparaba su etiqueta. Con un mando apagado suelto no se notaba; con una
 * pantalla entera apagada —el correo real, que sólo se lee— el producto
 * quedaba lleno de botones grises sin una palabra de explicación, que es peor
 * que un error.
 *
 * Lo que evita el clic es el atributo `disabled`, no el `pointer-events`: un
 * `<button disabled>` no dispara `click` ni con el ratón, ni con el teclado,
 * ni con un `.click()` sintético. El `pointer-events` sólo estaba de más.
 * Para `aria-disabled` —que NO desactiva nada por sí solo— sí hay que
 * bloquear el puntero, y por eso van en reglas separadas.
 *
 * `> *` deja el `title` en el botón y no en el glifo de dentro: sin eso, el
 * navegador buscaría el `title` del `<svg>`, que no tiene ninguno. */
.btn[disabled] {
  opacity: 0.45;
  cursor: not-allowed;
}
.btn[disabled] > * { pointer-events: none; }
.btn[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; pointer-events: none; }

/* Los otros tres mandos del sistema que también se apagan con motivo: la tira
 * de acciones del renglón, las opciones de un menú y las de la paleta. Van con
 * la regla del `.btn` y no en la hoja de su vista porque «apagado con motivo»
 * es una pieza del sistema, no de una pantalla: el correo real lo enciende en
 * las ocho secciones a la vez. Sin hover y sin cambio de color: un mando
 * apagado que reacciona al puntero es un mando que parece pulsable. */
.accion-fila[disabled],
.menu__opcion[disabled],
.paleta__opcion[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
}
.accion-fila[disabled] > *,
.menu__opcion[disabled] > * { pointer-events: none; }
.accion-fila[disabled]:hover,
.menu__opcion[disabled]:hover,
.paleta__opcion[aria-disabled="true"]:hover {
  background: transparent;
  color: inherit;
}

.btn--primario {
  background: var(--marca-600);
  border-color: var(--marca-600);
  color: var(--sobre-marca);
}
.btn--primario:hover { background: var(--marca-500); border-color: var(--marca-500); }

.btn--fantasma { background: none; border-color: transparent; color: var(--txt-2); }
/* DEFECTO CORREGIDO: el realce era --hover-fila, un 4 % de tinta pensado para
   un renglón de 800 px de ancho. En un botón icónico de 32 px eso no se ve, y
   los tres mandos del mensaje abierto no daban ninguna señal al pasar el
   cursor. Ahora el botón se dibuja: sube a --superficie-2 y saca su filo.  */
.btn--fantasma:hover {
  background: var(--superficie-2);
  border-color: var(--regla-fina);
  color: var(--txt-1);
}

.btn--peligro { background: none; border-color: var(--riesgo-regla); color: var(--riesgo); }
.btn--peligro:hover { background: var(--riesgo-fondo); border-color: var(--riesgo); }

/* .btn--peligro-al-tocar — hermano de .accion-fila--peligro para la familia
   .btn: neutro en reposo, rojo sólo cuando el cursor o el foco están encima.
   Existe porque .btn--peligro pinta el borde rojo SIEMPRE, y tres iconos en
   una barra de herramientas con uno de ellos permanentemente en rojo gritan
   antes de que pase nada. El riesgo se anuncia al alcance de la mano, no
   desde el otro lado de la pantalla.                                       */
.btn--peligro-al-tocar:hover,
.btn--peligro-al-tocar:focus-visible {
  background: var(--riesgo-fondo);
  border-color: var(--riesgo-regla);
  color: var(--riesgo);
}

.btn--icono { inline-size: 2rem; padding: 0; }
/* .btn--volver — regreso al registro. Sólo existe en el diseño de un panel. */
.btn--volver { display: none; }
.btn--ancho { width: 100%; }
.btn--grande { min-height: 2.5rem; padding-inline: var(--e-4); font-size: var(--txt-md); }

/* Grupo de botones: una sola pieza, como una barra de herramientas. */
.grupo-btn { display: inline-flex; }
.grupo-btn .btn { border-radius: 0; margin-inline-start: -1px; }
.grupo-btn .btn:first-child { border-start-start-radius: var(--radio-2); border-end-start-radius: var(--radio-2); margin-inline-start: 0; }
.grupo-btn .btn:last-child { border-start-end-radius: var(--radio-2); border-end-end-radius: var(--radio-2); }
.grupo-btn .btn:hover, .grupo-btn .btn:focus-visible { z-index: 1; }

/* ==========================================================================
   9. CAMPOS DE FORMULARIO
   El error va PEGADO al campo (aria-describedby + regla roja), nunca como
   aviso flotante en una esquina.
   ========================================================================== */

.campo { display: flex; flex-direction: column; gap: var(--e-2); }
.campo__etiqueta { font-size: var(--txt-2xs); font-weight: var(--peso-medio); color: var(--txt-2); }
.campo__control {
  min-height: 2.25rem;
  padding: var(--e-2) var(--e-3);
  background: var(--superficie-hundida);
  border: 1px solid var(--regla-media);
  border-radius: var(--radio-2);
  color: var(--txt-1);
  font-size: var(--txt-sm);
  transition: border-color var(--dur-2) var(--curva-estandar),
              box-shadow var(--dur-2) var(--curva-estandar);
}
.campo__control::placeholder { color: var(--txt-3); }
.campo__control:hover { border-color: var(--regla-fuerte); }
.campo__control:focus { outline: none; border-color: var(--marca-500); box-shadow: 0 0 0 3px var(--realce-medio); }
.campo__control:focus-visible { outline: var(--foco-anillo) solid var(--foco); outline-offset: 1px; }
textarea.campo__control { min-height: 8rem; resize: vertical; line-height: var(--interlinea-prosa); }

.campo__ayuda { font-size: var(--txt-2xs); color: var(--txt-3); }
.campo__error {
  display: none;
  gap: var(--e-2);
  align-items: flex-start;
  font-size: var(--txt-2xs);
  color: var(--riesgo);
}
/* El estado de error se deduce del propio campo con :has(). Sin clases JS. */
.campo:has(.campo__control[aria-invalid="true"]) .campo__control,
.campo:has(.campo__control:user-invalid) .campo__control {
  border-color: var(--riesgo);
  background: var(--riesgo-fondo);
}
.campo:has(.campo__control[aria-invalid="true"]) .campo__error,
.campo:has(.campo__control:user-invalid) .campo__error { display: flex; }
.campo:has(.campo__control[aria-invalid="true"]) .campo__ayuda,
.campo:has(.campo__control:user-invalid) .campo__ayuda { display: none; }

/* ==========================================================================
   10. DISTINTIVOS
   Semánticos disciplinados: fondo mezclado contra la superficie, texto en el
   mismo sobre de croma que la marca. Nunca relleno saturado.
   ========================================================================== */

.distintivo {
  display: inline-flex;
  align-items: center;
  gap: var(--e-1);
  padding: 1px var(--e-2);
  background: var(--neutro-fondo);
  border: 1px solid var(--regla-fina);
  border-radius: var(--radio-1);
  color: var(--txt-3);
  font-size: var(--txt-3xs);
  font-weight: var(--peso-medio);
  letter-spacing: 0.01em;
  white-space: nowrap;
}
.distintivo--marca  { background: var(--marca-fondo);  border-color: var(--marca-regla);  color: var(--marca-texto-vivo); }
.distintivo--exito  { background: var(--exito-fondo);  border-color: var(--exito-regla);  color: var(--exito); }
.distintivo--aviso  { background: var(--aviso-fondo);  border-color: var(--aviso-regla);  color: var(--aviso); }
.distintivo--riesgo { background: var(--riesgo-fondo); border-color: var(--riesgo-regla); color: var(--riesgo); }
/* Cifra dentro de un distintivo (conteo de hilo, folio). */
.distintivo .cifra { font-size: var(--txt-3xs); }
/* Distintivo que sigue a un texto en la misma línea (p. ej. «SPF · DKIM ·
   DMARC  Verificado»). Estaba resuelto con el mismo estilo en línea repetido
   en cuatro vistas; es una separación, no una excepción de cada plantilla.  */
.distintivo--tras-texto { margin-inline-start: var(--e-2); }

/* .punto — señal mínima de 6px. NUNCA para estado de lectura: eso es la
   regla de margen. Sirve para presencia o para categoría. */
.punto { inline-size: 6px; block-size: 6px; border-radius: var(--radio-pleno); background: var(--txt-3); flex: 0 0 auto; }
.punto--exito { background: var(--exito); }
.punto--aviso { background: var(--aviso); }
.punto--riesgo { background: var(--riesgo); }
.punto--marca { background: var(--marca-500); }
/* Categoría de color libre: calendarios, etiquetas, libretas. Los cuatro
   semánticos dicen qué TAN BIEN va algo; esto dice a QUÉ pertenece, que es otra
   pregunta y no debe robarles su vocabulario. El color entra por la propiedad
   --punto-color y cae al gris del sistema si nadie la define.               */
.punto--categoria { background: var(--punto-color, var(--txt-3)); }

/* ==========================================================================
   11. MENÚS (Popover API)
   ========================================================================== */

/* El popover NO se posiciona a mano. El respaldo, cuando el navegador no
   sabe anclar, es el centrado que trae de fábrica —usable en cualquier
   pantalla—; el anclaje al disparador se pide más abajo y sólo dentro de un
   @supports que sea VERDAD. Ponerle aquí `position: absolute; inset: auto`
   era justamente lo que lo mandaba a la esquina superior izquierda.       */
.menu {
  min-width: 13rem;
  margin: 0;
  padding: var(--e-1);
  background: var(--superficie-3);
  border: 1px solid var(--regla-media);
  border-radius: var(--radio-3);
  box-shadow: var(--sombra-3);
  color: var(--txt-2);
}
/* ENTRADA Y SALIDA. Antes sólo había entrada (una @keyframes al abrir), así
   que el menú aparecía con cuidado y desaparecía de golpe: el ojo registra el
   corte y lee «se rompió algo». Con @starting-style se declara de dónde viene
   y con transition-behavior:allow-discrete se le pide al navegador que espere
   a que termine la transición antes de aplicar el `display:none` del popover
   —sin eso, la salida no existe por más que se declare—.
   Comprobado que las dos piezas están soportadas en el navegador de aquí. */
.menu {
  opacity: 0;
  transform: translateY(-4px) scale(0.98);
  transition: opacity var(--dur-1) var(--curva-entrada),
              transform var(--dur-1) var(--curva-entrada),
              overlay var(--dur-1) allow-discrete,
              display var(--dur-1) allow-discrete;
}
.menu:popover-open {
  opacity: 1;
  transform: none;
  transition-duration: var(--dur-2);
  transition-timing-function: var(--curva-salida);
}
@starting-style {
  .menu:popover-open { opacity: 0; transform: translateY(-4px) scale(0.98); }
}
.menu::backdrop { background: transparent; }

.menu__opcion {
  display: flex;
  align-items: center;
  gap: var(--e-3);
  width: 100%;
  min-height: 2rem;
  padding: var(--e-1) var(--e-2);
  background: none;
  border: 0;
  border-radius: var(--radio-2);
  color: inherit;
  font-size: var(--txt-sm);
  text-align: start;
  text-decoration: none;
  cursor: pointer;
}
.menu__opcion:hover { background: var(--hover-fila); color: var(--txt-1); }
.menu__opcion--peligro { color: var(--riesgo); }
.menu__opcion--peligro:hover { background: var(--riesgo-fondo); color: var(--riesgo); }
.menu__atajo { margin-inline-start: auto; font-family: var(--fuente-cifra); font-size: var(--txt-3xs); color: var(--txt-3); }
.menu__separador { height: 1px; margin: var(--e-1) 0; background: var(--regla-fina); border: 0; }
.menu__titulo { padding: var(--e-2) var(--e-2) var(--e-1); }

/* Menú anclado a su disparador. PROMOVIDO EN INTEGRACIÓN: las OCHO vistas lo
   habían escrito idéntico. Sin soporte de anclaje el popover cae al centro de
   la ventana, que sigue siendo usable.

   DEFECTO CORREGIDO. Decía `position-area: bottom span-inline-start`, y esa
   declaración es INVÁLIDA: `bottom` es una palabra física y
   `span-inline-start` es lógica, y position-area no admite mezclarlas. El
   navegador tiraba la línea entera, el menú se quedaba sin anclaje y
   aparecía pegado arriba a la izquierda de la ventana —o al centro, en la
   bandeja, que se había puesto un parche local—. La pareja lógica correcta
   es `block-end span-inline-start`: cuelga del disparador y crece hacia el
   filo de inicio, que es lo que necesita un avatar arriba a la derecha.
   Comprobado en el navegador: CSS.supports() dice `false` a la mezcla y
   `true` a la pareja lógica, así que el @supports también estaba mintiendo.

   El respaldo local de view-bandeja.css que obligaba a escribir esto con doble
   clase se retiró en la integración, así que vuelve a bastar una. */
@supports (position-area: block-end span-inline-start) {
  .menu--anclado {
    position: fixed;
    inset: auto;
    margin: 0;
    margin-block-start: var(--e-1);
    position-area: block-end span-inline-start;
    /* Pantalla corta: se voltea arriba del disparador antes que salirse. */
    position-try-fallbacks: flip-block, flip-inline;
    max-block-size: calc(100dvh - 2 * var(--e-8));
    overflow-y: auto;
  }
}

/* ==========================================================================
   12. DIÁLOGOS (<dialog>)
   ========================================================================== */

.dialogo {
  width: min(28rem, calc(100vw - 2 * var(--e-4)));
  padding: 0;
  background: var(--superficie-3);
  border: 1px solid var(--regla-media);
  border-radius: var(--radio-4);
  box-shadow: var(--sombra-3);
  color: var(--txt-2);
}
.dialogo::backdrop {
  background: var(--velo);
  backdrop-filter: blur(2px);
}
/* Entrada Y SALIDA, igual que el menú de §11: un diálogo que se va de golpe se
   lee como un fallo. El velo se funde con él, no después. */
.dialogo, .dialogo::backdrop {
  opacity: 0;
  transition: opacity var(--dur-1) var(--curva-entrada),
              transform var(--dur-1) var(--curva-entrada),
              overlay var(--dur-1) allow-discrete,
              display var(--dur-1) allow-discrete;
}
.dialogo { transform: translateY(8px) scale(0.985); }
.dialogo[open], .dialogo[open]::backdrop {
  opacity: 1;
  transition-duration: var(--dur-3);
  transition-timing-function: var(--curva-salida);
}
.dialogo[open] { transform: none; }
@starting-style {
  .dialogo[open] { opacity: 0; transform: translateY(8px) scale(0.985); }
  .dialogo[open]::backdrop { opacity: 0; }
}
@keyframes fundir-entra { from { opacity: 0; } }

.dialogo__cabecera { padding: var(--e-5) var(--e-5) var(--e-3); }
.dialogo__titulo { font-size: var(--txt-lg); }
.dialogo__cuerpo { padding: 0 var(--e-5) var(--e-5); font-size: var(--txt-sm); }
.dialogo__pie {
  display: flex;
  justify-content: flex-end;
  gap: var(--e-2);
  padding: var(--e-4) var(--e-5);
  border-top: 1px solid var(--regla-fina);
  background: color-mix(in oklab, var(--lienzo) 40%, transparent);
  border-end-start-radius: var(--radio-4);
  border-end-end-radius: var(--radio-4);
}

/* ==========================================================================
   13. PALETA DE COMANDOS  (⌘K / Ctrl+K)
   ========================================================================== */

.paleta {
  width: min(38rem, calc(100vw - 2 * var(--e-4)));
  max-height: min(28rem, calc(100dvh - 8rem));
  margin: 12vh auto auto;
  padding: 0;
  background: var(--superficie-3);
  border: 1px solid var(--regla-media);
  border-radius: var(--radio-4);
  box-shadow: var(--sombra-3);
  overflow: hidden;
  color: var(--txt-2);
}
.paleta::backdrop {
  background: var(--velo);
  backdrop-filter: blur(3px);
}
/* La paleta baja del filo superior y se va por donde vino. */
.paleta, .paleta::backdrop {
  opacity: 0;
  transition: opacity var(--dur-1) var(--curva-entrada),
              transform var(--dur-1) var(--curva-entrada),
              overlay var(--dur-1) allow-discrete,
              display var(--dur-1) allow-discrete;
}
.paleta { transform: translateY(-10px) scale(0.985); }
.paleta[open], .paleta[open]::backdrop {
  opacity: 1;
  transition-duration: var(--dur-3);
  transition-timing-function: var(--curva-salida);
}
.paleta[open] { transform: none; }
@starting-style {
  .paleta[open] { opacity: 0; transform: translateY(-10px) scale(0.985); }
  .paleta[open]::backdrop { opacity: 0; }
}

.paleta__buscador { display: flex; align-items: center; gap: var(--e-3); padding: var(--e-3) var(--e-4); border-bottom: 1px solid var(--regla-fina); }
.paleta__entrada {
  flex: 1 1 auto;
  background: none;
  border: 0;
  outline: none;
  color: var(--txt-1);
  font-size: var(--txt-md);
}
.paleta__entrada::placeholder { color: var(--txt-3); }
.paleta__lista { max-height: 20rem; overflow-y: auto; padding: var(--e-2); }
.paleta__seccion { padding: var(--e-3) var(--e-2) var(--e-1); }
.paleta__opcion {
  display: grid;
  grid-template-columns: 1.25rem minmax(0, 1fr) auto;
  align-items: center;
  gap: var(--e-3);
  width: 100%;
  min-height: 2.25rem;
  padding: var(--e-1) var(--e-2);
  background: none;
  border: 0;
  border-radius: var(--radio-2);
  color: inherit;
  font-size: var(--txt-sm);
  text-align: start;
  /* La opción es a veces un <a> y a veces un <button>. Sin esto, la mitad de
     la lista salía subrayada de arriba abajo por ser un enlace sin estilar. */
  text-decoration: none;
  cursor: pointer;
}
.paleta__opcion:hover { background: var(--hover-fila); color: var(--txt-1); }

/* ►► EL CURSOR DE LA PALETA YA NO USA LA REGLA DE MARGEN ◄◄
   Era `box-shadow: inset 2px 0 0 var(--marca-500)`: el MISMO trazo azul de
   2 px que en el registro significa «sin leer», y en el registro es lo único
   que lo significa. Con cinco dueños, un signo no significa ninguno. Aquí el
   cursor se dice como se dice un renglón elegido en cualquier registro del
   producto: fondo de selección y peso. Se ve más, no menos —el fondo cubre
   los 600 px de la fila y el trazo cubría 2— y no gasta el gesto que este
   diseño tiene reservado. */
.paleta__opcion[data-activa="true"] {
  background: var(--seleccion);
  color: var(--txt-1);
  font-weight: var(--peso-medio);
}
.paleta__icono { color: var(--txt-3); display: grid; place-items: center; }
/* PROMOVIDO EN INTEGRACIÓN: duplicado idéntico en seis view-*.css. */
.paleta__lupa { color: var(--txt-3); }
.paleta__atajo { font-family: var(--fuente-cifra); font-size: var(--txt-3xs); color: var(--txt-3); }
.paleta__pie {
  display: flex;
  align-items: center;
  gap: var(--e-4);
  padding: var(--e-2) var(--e-4);
  border-top: 1px solid var(--regla-fina);
  background: color-mix(in oklab, var(--lienzo) 40%, transparent);
  font-size: var(--txt-3xs);
  color: var(--txt-3);
}
.paleta__pista { display: flex; align-items: center; gap: var(--e-1); }

/* ==========================================================================
   14. ESQUELETOS DE CARGA
   Un esqueleto con la FORMA del registro. Nada de ruedas girando: la carga
   debe prometer la estructura que va a llegar.
   ========================================================================== */

.esqueleto {
  display: block;
  border-radius: var(--radio-1);
  background:
    linear-gradient(90deg,
      var(--superficie-2) 0%,
      var(--superficie-3) 42%,
      var(--superficie-2) 84%) 0 0 / 300% 100%;
  animation: esqueleto-barrido var(--dur-esqueleto) linear infinite;
}
@keyframes esqueleto-barrido { to { background-position: -300% 0; } }
.esqueleto--texto { height: 0.6875rem; }
.esqueleto--titulo { height: 0.875rem; }
.esqueleto--bloque { height: 3rem; border-radius: var(--radio-2); }
.esqueleto--avatar { inline-size: 1.75rem; block-size: 1.75rem; border-radius: var(--radio-pleno); }

/* Renglón esqueleto: usa las MISMAS columnas del registro por subgrid. */
.renglon--esqueleto { pointer-events: none; }
.renglon--esqueleto .renglon__regla { background: var(--regla-margen-muerta); }
@media (prefers-reduced-motion: reduce) {
  .esqueleto { animation: none; background: var(--superficie-2); }
}

/* ==========================================================================
   15. AVISO CON DESHACER
   Toda acción destructiva o de archivo confirma con texto y ofrece deshacer.
   --------------------------------------------------------------------------
   DOS PESOS, y el que más se ve es el ligero. «Marcado como leído al abrirlo»
   sale cada vez que se abre un correo: si eso pesa lo mismo que «Borrado
   definitivo», el aviso se vuelve ruido y se deja de leer justo cuando dice
   algo. Así que el aviso RUTINARIO —el que no lleva tono— encoge: tipo de
   12 px, aire corto, sombra de un piso, ancho el de su propio texto y la
   regla de margen en gris, sin color. Los que llevan tono (exito, aviso,
   riesgo) conservan la voz alta: son los que informan de algo que salió bien
   o mal, y no ocurren cuatrocientas veces al día.

   No se inventó un tono «al margen»: lo rutinario ES el caso sin tono, así
   que la jerarquía sale sola en las ocho secciones sin que ningún router
   tenga que acordarse de pasar una palabra nueva.
   El Deshacer no se toca: la banda encoge, la salida no.
   ========================================================================== */

.avisos {
  position: fixed;
  inset-block-end: var(--e-16);
  inset-inline-start: 50%;
  translate: -50% 0;
  z-index: var(--z-aviso);
  display: flex;
  flex-direction: column;
  align-items: center;   /* cada aviso mide lo que dice, no 30rem siempre */
  gap: var(--e-2);
  /* DEFECTO CORREGIDO — el techo estaba en 30rem (480 px) fuera cual fuera la
     ventana, así que en un monitor de 1600 el aviso se recortaba a un tercio
     del ancho disponible y se comía el final del asunto: «Mensaje enviado a la
     papelera · Report domain: tufuturofactible.co…». El aviso ya medía lo que
     dice (`inline-size: fit-content`); lo que no le dejaba crecer era este
     techo. Sube a 46rem y sigue cediendo ante una ventana estrecha.
     Lo que este archivo NO puede arreglar es el recorte del propio texto: el
     asunto llega ya cortado a 34 caracteres desde `routers/correo.py`
     (`_recorta`), y ése corta a media palabra. Va en la integración. */
  width: min(46rem, calc(100vw - 2 * var(--e-6)));
  pointer-events: none;
}
.aviso {
  overflow: hidden;
  display: flex;
  align-items: center;
  gap: var(--e-2);
  inline-size: fit-content;
  max-inline-size: 100%;
  padding: var(--e-1) var(--e-2) var(--e-1) var(--e-3);
  background: var(--superficie-3);
  border: 1px solid var(--regla-fina);
  border-radius: var(--radio-2);
  box-shadow: var(--sombra-1);
  font-size: var(--txt-2xs);
  color: var(--txt-2);
  pointer-events: auto;
  animation: aviso-entra var(--dur-4) var(--curva-salida) both;
}
/* El que sí levanta la voz. */
.aviso--exito,
.aviso--aviso,
.aviso--riesgo {
  gap: var(--e-3);
  padding: var(--e-3) var(--e-3) var(--e-3) var(--e-4);
  border-color: var(--regla-media);
  border-radius: var(--radio-3);
  box-shadow: var(--sombra-3);
  font-size: var(--txt-sm);
  color: var(--txt-1);
}
.aviso[data-saliendo="true"] { animation: aviso-sale var(--dur-3) var(--curva-entrada) both; }
@keyframes aviso-entra { from { opacity: 0; transform: translateY(12px) scale(0.98); } }
@keyframes aviso-sale  { to   { opacity: 0; transform: translateY(8px) scale(0.98); } }

/* Regla de margen también aquí: coherencia del lenguaje. En el aviso
   rutinario va en gris — el color es lo que se gana al tener algo que decir. */
.aviso::before {
  content: "";
  align-self: stretch;
  inline-size: var(--regla-margen-grosor);
  margin-inline: calc(var(--e-3) * -1) 0;
  /* Sin radio propio: con 2px de ancho, una esquina redondeada de 8px se
     come la regla entera. La recorta el overflow del aviso.               */
  background: var(--regla-media);
}
.aviso--exito::before,
.aviso--aviso::before,
.aviso--riesgo::before { margin-inline: calc(var(--e-4) * -1) 0; }
.aviso__texto {
  flex: 0 1 auto;
  min-width: 0;
  margin-inline-start: var(--e-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.aviso--exito .aviso__texto,
.aviso--aviso .aviso__texto,
.aviso--riesgo .aviso__texto { flex: 1 1 auto; margin-inline-start: var(--e-3); }
.aviso__accion {
  flex: 0 0 auto;
  padding: 0 var(--e-1);
  background: none;
  border: 0;
  border-radius: var(--radio-1);
  color: var(--marca-texto);
  font-size: var(--txt-2xs);
  font-weight: var(--peso-medio);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
}
.aviso__accion:hover { color: var(--txt-1); }
/* En un aviso importante el Deshacer vuelve a ser un botón con caja. */
.aviso--exito .aviso__accion,
.aviso--aviso .aviso__accion,
.aviso--riesgo .aviso__accion {
  padding: var(--e-1) var(--e-2);
  border: 1px solid var(--marca-regla);
  color: var(--marca-texto);
  text-decoration: none;
}
.aviso--exito .aviso__accion:hover,
.aviso--aviso .aviso__accion:hover,
.aviso--riesgo .aviso__accion:hover { background: var(--marca-fondo); color: var(--txt-1); }
.aviso--exito::before  { background: var(--exito); }
.aviso--aviso::before  { background: var(--aviso); }
.aviso--riesgo::before { background: var(--riesgo); }

/* ==========================================================================
   16. ESTADOS VACÍOS
   El sistema actual no tiene ninguno. Aquí hay tres formas: bandeja al día,
   búsqueda sin resultados, carpeta vacía.
   ========================================================================== */

.vacio {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--e-3);
  min-height: 18rem;
  padding: var(--e-10) var(--e-6);
  text-align: center;
}
/* El vacío de UNA SECCIÓN dentro de una lámina, no el de un panel entero.
   PROMOVIDO EN LA INTEGRACIÓN: lo declaraban por su cuenta view-admin (11rem),
   view-panel (10rem) y view-carpetas (8rem). Nunca se cargan juntas, así que
   no chocaban —pero el mismo hueco medía tres cosas distintas según en qué
   pantalla cayeras, y eso se nota al pasar de una a otra. Una medida: 10rem.
   Lo que SÍ sigue siendo de cada hoja es la condición que lo esconde, porque
   depende de qué contenedor lo rodea (`.bloque`, `.tabla`, `.admin__diario`). */
.vacio--seccion {
  min-height: 10rem;
  padding: var(--e-8) var(--e-4);
}

.vacio__figura {
  display: grid;
  place-items: center;
  inline-size: 3rem;
  block-size: 3rem;
  margin-block-end: var(--e-2);
  border: 1px solid var(--regla-fina);
  border-radius: var(--radio-3);
  background: var(--superficie-hundida);
  color: var(--txt-3);
}
.vacio__titulo { font-size: var(--txt-md); color: var(--txt-1); }
.vacio__texto { max-width: 32ch; font-size: var(--txt-xs); color: var(--txt-3); text-wrap: pretty; }
.vacio__cifra { font-size: var(--txt-2xl); color: var(--txt-1); }
.vacio__acciones { display: flex; gap: var(--e-2); margin-block-start: var(--e-2); }

/* El vacío aparece SOLO si el registro no tiene renglones. Sin JS. */
/* El estado vacío se retira en cuanto el panel tiene contenido, sea del tipo que
   sea: renglones de registro, filas del árbol de carpetas o de una tabla. Antes
   sólo miraba `.renglon`, así que en carpetas convivían el árbol lleno y el
   cartel de "aquí no hay nada".                                             */
/* `.reglas__fila` entró en la lista en la integración del 2026-08-09: era la
   única fila de registro del producto que faltaba, y por eso `carpetas/
   reglas.html` tenía que suplirlo con un `{% if %}` del servidor. */
.panel__cuerpo:has(.renglon, .arbol__fila, .tabla__fila, .reglas__fila) > .vacio { display: none; }

/* Vacío dentro de la paleta de comandos: más bajo, porque la paleta ya es una
   caja chica. PROMOVIDO EN INTEGRACIÓN: duplicado idéntico en seis vistas. */
.vacio--paleta { min-height: 9rem; }

/* ==========================================================================
   16.1 LÁMINA DE DOCUMENTACIÓN  (.doc)
   --------------------------------------------------------------------------
   PROMOVIDO EN INTEGRACIÓN. No toda vista del prototipo es la aplicación:
   la portada y la lámina de móvil son PÁGINAS que hablan del producto. Antes
   cada una traía su propio ancho (66rem / 80rem), su propia escala de título
   y su propia tira de datos. Aquí hay una sola lámina y el resto la usa.

   Se llama .doc y no .demo a propósito: «demo» ya significaba dos cosas
   distintas en dos vistas —el envoltorio de la página en móvil y los botones
   de estados de prueba en acceso— y esa colisión es justo lo que se corrige.

   La lámina se lee como la portada de un expediente: cinta de membrete
   arriba, título, entrada, y una tira de cifras cerrada por reglas.
   ========================================================================== */

.doc {
  width: min(80rem, 100%);
  margin-inline: auto;
  padding: var(--e-10) var(--e-6) var(--e-16);
}
.doc__encabezado {
  display: flex;
  flex-direction: column;
  gap: var(--e-3);
  padding-block-end: var(--e-6);
}
.doc__titulo { font-size: var(--txt-2xl); letter-spacing: -0.015em; max-width: 24ch; }
.doc__entrada { font-size: var(--txt-md); color: var(--txt-2); max-width: 62ch; }

/* Tira de datos: pares dato/valor cerrados por reglas, como el membrete de un
   estado de cuenta. Toda cifra en Factible Cifra. */
.doc__datos {
  display: flex;
  flex-wrap: wrap;
  gap: var(--e-6);
  margin: 0;
  padding-block: var(--e-4);
  border-block: 1px solid var(--regla-fina);
}
.doc__dato { display: flex; flex-direction: column; gap: 2px; }
.doc__dato dt {
  font-size: var(--txt-3xs);
  font-weight: var(--peso-medio);
  letter-spacing: var(--espaciado-versalita);
  text-transform: uppercase;
  color: var(--txt-3);
}
.doc__dato dd {
  margin: 0;
  font-family: var(--fuente-cifra);
  font-variant-numeric: var(--cifras-tabulares);
  font-size: var(--txt-md);
  color: var(--txt-1);
}
.doc__dato dd small {
  font-family: var(--fuente-texto);
  font-size: var(--txt-2xs);
  color: var(--txt-3);
}

@media (max-width: 40rem) {
  .doc { padding: var(--e-6) var(--e-4) var(--e-16); }
  .doc__titulo { font-size: var(--txt-xl); }
  .doc__datos { gap: var(--e-4); }
}
@media (max-width: 24rem) {
  .doc { padding-inline: var(--e-3); }
}

/* ==========================================================================
   17. BARRA DE NAVEGACIÓN DEL PROTOTIPO
   Andamio de demostración. No forma parte del producto.
   ========================================================================== */

/* El andamio del prototipo es fijo y cruza más de medio ancho, así que tapaba
   contenido en todas las vistas. Reservarle sitio panel por panel no alcanzaba:
   admin, redacción, acceso y la portada no usan los mismos paneles y quedaban
   fuera. Ahora el hueco se descuenta UNA vez del armazón, así que ninguna vista
   puede olvidarlo — el andamio cae siempre en tierra de nadie.               */
.app { padding-block-end: var(--alto-navprot); }
body:not(:has(.app)) { padding-block-end: var(--alto-navprot); }

/* Alto reservado para el andamio: lo descuenta .app (ver §"hueco del andamio"). */
:root { --alto-navprot: 3.5rem; }

.navprot {
  position: fixed;
  inset-block-end: var(--e-3);
  inset-inline-start: var(--e-3);
  z-index: var(--z-flotante);
  display: flex;
  align-items: center;
  gap: var(--e-1);
  max-width: calc(100vw - 2 * var(--e-3));
  padding: var(--e-1);
  background: color-mix(in oklab, var(--superficie-3) 94%, transparent);
  border: 1px solid var(--regla-media);
  border-radius: var(--radio-pleno);
  box-shadow: var(--sombra-2);
  backdrop-filter: blur(8px);
  overflow-x: auto;
  scrollbar-width: none;
}
.navprot::-webkit-scrollbar { display: none; }
.navprot__rotulo {
  padding-inline: var(--e-2);
  font-size: var(--txt-3xs);
  letter-spacing: var(--espaciado-versalita);
  text-transform: uppercase;
  color: var(--txt-3);
  white-space: nowrap;
}
.navprot__enlace {
  padding: var(--e-1) var(--e-3);
  border-radius: var(--radio-pleno);
  color: var(--txt-3);
  font-size: var(--txt-2xs);
  text-decoration: none;
  white-space: nowrap;
  transition: background var(--dur-1) var(--curva-estandar), color var(--dur-1) var(--curva-estandar);
}
.navprot__enlace:hover { background: var(--hover-fila); color: var(--txt-1); }
.navprot__enlace[aria-current="page"] { background: var(--marca-600); color: var(--sobre-marca); }

/* ==========================================================================
   18. TRANSICIONES DE VISTA
   lista → detalle y cambio de carpeta. Cortas y sin rebote.
   ========================================================================== */

@view-transition { navigation: auto; }

::view-transition-old(root) { animation: fundir-sale var(--dur-2) var(--curva-entrada) both; }
::view-transition-new(root) { animation: fundir-entra var(--dur-3) var(--curva-salida) both; }
@keyframes fundir-sale { to { opacity: 0; } }

/* RETIRADO EN LA INTEGRACIÓN — `::view-transition-*(lectura)` y `(registro)`.
   Eran código muerto por dos motivos a la vez y conviene dejarlos escritos
   para que nadie los reponga sin arreglar ninguno de los dos:

   1. NADIE SE LLAMA ASÍ. El único `view-transition-name: lectura` del producto
      vive en view-lectura.css, y esa hoja no la carga ninguna plantilla.
      `registro` no lo declara nadie, en ninguna hoja.
   2. AUNQUE SE LLAMARAN, NO CORRERÍAN. `@view-transition { navigation: auto }`
      sólo actúa en una navegación de documento, y tanto abrir un mensaje como
      cambiar de carpeta pasaron a ser intercambios de htmx. Lo que sí corre
      —y se queda— es el fundido de `root` entre secciones.

   Para que existieran de verdad haría falta envolver el intercambio en
   `document.startViewTransition()` desde el guion, que es una decisión de
   producto y no un remiendo de hoja de estilos. */

/* ==========================================================================
   19. ADAPTABLE  —  hasta 360px, con objetivos táctiles de 44px
   ========================================================================== */

/* Panel de lista estrecho: caen tamaño y señales; el registro sigue cuadrado
   porque las columnas son un token, no un valor suelto.                    */
/* Panel ancho: entra la columna de tamaño. Es una consulta al PANEL, no a
   la ventana: en un cliente de tres paneles la ventana no dice nada útil.  */
@container lista (min-width: 40rem) {
  .registro {
    --registro-columnas:
      var(--col-regla) var(--col-marca) var(--col-remitente)
      var(--col-asunto) var(--col-senales) var(--col-cifra) var(--col-hora);
  }
  .registro__cabecera .col-cifra,
  .renglon__cifra { display: block; }
}
/* Panel angosto: cae la columna de señales. */
@container lista (max-width: 26rem) {
  .registro {
    --registro-columnas:
      var(--col-regla) var(--col-marca) var(--col-remitente)
      var(--col-asunto) var(--col-hora);
  }
  .registro__cabecera .col-senales,
  .renglon__senales { display: none; }
}
/* Panel mínimo: cae la casilla; la regla de margen nunca cae. */
@container lista (max-width: 21rem) {
  .registro {
    --registro-columnas: var(--col-regla) var(--col-remitente) var(--col-asunto) var(--col-hora);
  }
  .registro__cabecera .col-marca,
  .renglon__marca { display: none; }
}

/* Tableta: se pliega la barra de carpetas. */
@media (max-width: 68rem) {
  :root { --ancho-carpetas: 4rem; --ancho-lista: 25rem; }
  .carpeta { grid-template-columns: 1.125rem; justify-items: center; padding-inline: 0; }
  /* Acotado a .panel--carpetas: sin eso, el medidor de cuota perdía su cifra
     en CUALQUIER vista por debajo de 68rem, incluida la que la usa para
     explicar un error de cuota llena. La regla es del rail plegado, no del
     componente.                                                             */
  .panel--carpetas .carpeta__nombre,
  .panel--carpetas .carpetas__titulo,
  .panel--carpetas .cuota__texto { display: none; }
  .carpeta__conteo {
    position: absolute;
    margin-inline-start: 1.25rem;
    margin-block-start: -1rem;
    font-size: 0.625rem;
  }
  .carpeta { position: relative; }
  .barra__marca { min-width: auto; }
}

/* Móvil: un solo panel a la vez. Sin JS extra: se elige con [data-vista]. */
@media (max-width: 50rem) {
  .app {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: var(--altura-barra) minmax(0, 1fr);
    grid-template-areas: "barra" "lista";
  }
  .app__carpetas { display: none; }
  .app__lectura { display: none; }
  .app[data-vista="lectura"] { grid-template-areas: "barra" "lectura"; }
  .app[data-vista="lectura"] .app__lista { display: none; }
  .app[data-vista="lectura"] .app__lectura { display: flex; grid-area: lectura; }

  /* Y si el mensaje que se estaba leyendo deja de existir —se archivó o se fue
     a la papelera desde el propio panel—, el teléfono vuelve solo al registro
     en vez de quedarse mirando un panel sin nada. Antes había ahí un cartel
     que decía «ningún mensaje abierto»; ahora no hay cartel, así que la vista
     tiene que corregirse sola. Sin JavaScript: lo dice el propio DOM. */
  .app[data-vista="lectura"]:has(> .app__lectura.panel--lectura-cerrada) {
    grid-template-areas: "barra" "lista";
  }
  .app[data-vista="lectura"]:has(> .app__lectura.panel--lectura-cerrada) .app__lista {
    display: flex;
  }
  .app[data-vista="lectura"]:has(> .app__lectura.panel--lectura-cerrada) .app__lectura {
    display: none;
  }

  /* La barra superior no cabe entera: el buscador queda en su icono y los
     accesorios de teclado desaparecen (en táctil no hay atajos). */
  .barra { gap: var(--e-2); }
  .barra__marca { min-width: auto; }
  .barra__sufijo { display: none; }
  .barra__centro { flex: 0 0 auto; }
  .buscador {
    width: auto;
    min-inline-size: var(--toque-min);
    min-height: var(--toque-min);
    justify-content: center;
    padding-inline: var(--e-2);
  }
  .buscador__texto, .buscador .tecla { display: none; }
  .solo-escritorio { display: none; }
  /* Con un solo panel a la vista hace falta regresar al registro. */
  .btn--volver { display: inline-flex; }

  /* Objetivos táctiles: 44px mínimo en todo lo que se toca. */
  .renglon > * { padding-block: var(--e-4); }
  .btn, .carpeta, .menu__opcion, .paleta__opcion { min-height: var(--toque-min); }
  .btn--icono { inline-size: var(--toque-min); block-size: var(--toque-min); }
  .accion-fila { inline-size: 2.25rem; block-size: 2.25rem; }
  .casilla { inline-size: 1.125rem; block-size: 1.125rem; }
  /* En móvil las acciones no dependen del cursor: siempre visibles al foco. */
  .renglon__acciones { position: static; opacity: 1; visibility: visible; transform: none; display: none; }
  .navprot { inset-block-end: var(--e-2); inset-inline: var(--e-2); max-width: none; }
}

/* ==========================================================================
   16.1 EL RENGLÓN DE MENSAJE SE PLIEGA A DOS LÍNEAS  (≤ 50rem)
   --------------------------------------------------------------------------
   DEFECTO CORREGIDO. Hasta aquí el registro conservaba en el teléfono el
   mismo juego horizontal del escritorio: regla · remitente 9rem · asunto ·
   hora. En 390 px eso deja al asunto ~150 px, así que se truncaban A LA VEZ
   el remitente («noreply-dmarc-suppo…») y el asunto («Ventana de manteni…»):
   dos columnas recortadas no informan ninguna, y el renglón dejaba de ser un
   registro para ser una lista de puntos suspensivos. Además contradecía lo
   que la propia lámina movil.html promete por escrito: «el remitente y la
   hora suben a la primera línea, y el asunto con su vista previa bajan a la
   segunda. Nada se recorta para que quepa.» Esa maqueta existía sólo como
   dibujo en view-movil.css; el producto no la tenía.

   Ahora el pliegue es del sistema: dos líneas, el asunto a todo el ancho, y
   la REGLA DE MARGEN cruzando ambas — sigue siendo el único portador del
   estado de no leído. Se aplica sólo a los renglones de mensaje (los que
   llevan .renglon__cuerpo); administración conserva sus propios juegos.
   ========================================================================== */
@media (max-width: 50rem) {

  /* Tres columnas: margen · contenido · hora. Cuarentena añade el puntaje.  */
  .registro:has(.renglon__cuerpo) {
    --registro-columnas: var(--col-regla) minmax(0, 1fr) var(--col-hora);
  }
  /* AQUÍ HABÍA UNA REGLA MUERTA Y VENENOSA (retirada en la integración del
     2026-08-09). Declaraba `--registro-columnas` con `var(--col-puntaje)`, un
     token que dejó de existir cuando la cuarentena retiró su columna de
     puntaje. Una sustitución que no resuelve deja la propiedad personalizada
     en «inválida garantizada», así que `grid-template-columns` caía a su
     valor inicial y el registro se quedaba con UNA pista: a 390 px la regla
     de margen pasaba a pintar el renglón entero de azul, siete bloques
     seguidos. La cuarentena declara ahora su propio juego en
     view-busqueda.css:296, que es su sitio. */

  /* El membrete rotulaba REMITENTE · ASUNTO · HORA. Plegado el renglón esas
     tres columnas ya no existen como tales: un encabezado que no cuadra con
     su columna deja de ser un membrete y pasa a ser ruido. Se retira.      */
  .registro:has(.renglon__cuerpo) > .registro__cabecera { display: none; }

  .renglon:has(> .renglon__cuerpo) {
    grid-template-rows: auto auto;
    align-items: start;
    row-gap: 1px;
  }
  /* El relleno se reparte entre las dos líneas para que la regla de margen
     siga cubriendo el alto completo del renglón (no se pone en .renglon).  */
  .renglon:has(> .renglon__cuerpo) > * { padding-block: 0; }

  .renglon:has(> .renglon__cuerpo) > .renglon__regla {
    grid-column: 1;
    grid-row: 1 / 3;
  }
  .renglon:has(> .renglon__cuerpo) > .renglon__remitente {
    grid-column: 2;
    grid-row: 1;
    padding-block-start: var(--renglon-aire);
  }
  .renglon:has(> .renglon__cuerpo) > .renglon__puntaje {
    grid-column: -3;
    grid-row: 1;
    padding-block-start: var(--renglon-aire);
  }
  .renglon:has(> .renglon__cuerpo) > .renglon__hora {
    grid-column: -2;
    grid-row: 1;
    padding-block-start: var(--renglon-aire);
  }
  /* Mismo filo derecho que la hora (§6, `.renglon__hora`): sin esto el asunto
     y la vista previa llegan hasta el borde del panel y el renglón no respira
     por la derecha. Es el mismo valor, y por la misma razón. */
  .renglon:has(> .renglon__cuerpo) > .renglon__cuerpo {
    grid-column: 2 / -1;
    grid-row: 2;
    padding-block-end: var(--renglon-aire);
    padding-inline-end: var(--e-4);
  }
  /* Casilla, señales y tamaño: fuera. En el teléfono no hay cursor para la
     selección múltiple.  (`.renglon__motivo` salió de la lista: esa celda ya
     no la escribe ninguna plantilla desde que la cuarentena retiró sus
     columnas de motivo y puntaje.)                                          */
  .renglon:has(> .renglon__cuerpo) > :is(.renglon__marca, .renglon__senales,
  .renglon__cifra) { display: none; }

  /* Con el ancho completo el asunto ya no necesita recortarse en una línea:
     se le permiten dos y la vista previa conserva la suya.                 */
  .renglon:has(> .renglon__cuerpo) .renglon__asunto {
    white-space: normal;
    text-wrap: pretty;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }
}

/* ==========================================================================
   16.2 EL PLIEGUE TAMBIÉN EN ESCRITORIO  (> 50rem)
   --------------------------------------------------------------------------
   DEFECTO CORREGIDO. El pliegue de §16.1 arreglaba el teléfono y dejaba el
   escritorio como estaba: remitente 9rem + casilla + señales + hora se comían
   ~300 px de los 576 del panel, y al asunto le quedaban ~275. Seis de cada
   ocho asuntos se truncaban. Quedaba la paradoja de que el mismo mensaje se
   leía COMPLETO en un teléfono de 390 px y recortado en un monitor de 1440:
   la rejilla le había ganado al contenido.

   Se aplica el mismo pliegue, no uno nuevo: remitente y cifras arriba, asunto
   y vista previa abajo a todo el ancho. La diferencia con el teléfono es que
   aquí NO cae nada — hay cursor, así que la casilla, las señales y el tamaño
   se quedan en la primera línea, que es donde alinean.

   La columna del asunto sigue declarada y sigue siendo flexible: vacía en la
   primera línea es la que empuja las cifras al filo derecho, y es la que el
   subgrid usa para que la segunda línea arranque bajo el remitente. La
   alineación del registro no se pierde; cambia de eje.
   ========================================================================== */
@media (min-width: 50.0625rem) {

  .renglon:has(> .renglon__cuerpo) {
    grid-template-rows: auto auto;
    align-items: start;
    row-gap: 1px;
  }
  /* El relleno se reparte entre las dos líneas para que la REGLA DE MARGEN
     siga cubriendo el alto completo del renglón.                            */
  .renglon:has(> .renglon__cuerpo) > * { padding-block: 0; }

  .renglon:has(> .renglon__cuerpo) > .renglon__regla { grid-row: 1 / 3; }

  .renglon:has(> .renglon__cuerpo) > :is(.renglon__marca, .renglon__remitente,
  .renglon__senales, .renglon__cifra, .renglon__hora) {
    grid-row: 1;
    padding-block-start: var(--renglon-aire);
  }

  /* DEFECTO CORREGIDO — las celdas de la derecha iban una columna corridas.
     Al bajar el cuerpo a la segunda línea, las de la primera se quedaron sin
     columna propia y el navegador las fue acomodando de izquierda a derecha:
     las SEÑALES caían en la pista del asunto y la HORA en la de las señales,
     así que la hora del renglón no cuadraba con su rótulo del membrete —56 px
     a la izquierda— y la última pista se quedaba vacía contra el filo. Un
     registro cuyas cifras no cuadran con su encabezado deja de ser un
     registro. Se fijan contando desde el final, que es lo único estable
     cuando el juego de columnas cambia con el ancho del panel.             */
  .renglon:has(> .renglon__cuerpo) > .renglon__hora    { grid-column: -2; }
  .renglon:has(> .renglon__cuerpo) > .renglon__senales { grid-column: -3; }
  /* Cuarentena: mismas pistas contadas desde el final (puntaje · hora). */
  .renglon:has(> .renglon__cuerpo) > .renglon__puntaje { grid-column: -3; }
  .renglon:has(> .renglon__cuerpo) > .renglon__motivo  { grid-column: -4; }
  /* LA TIRA DE ACCIONES TAMBIÉN NECESITA SU PISTA (integración 2026-08-09).
     Se quedó fuera del arreglo de dos comentarios más arriba: era la única
     celda de la primera línea sin `grid-column`, y se colocaba sola. Mientras
     el juego de columnas empezó por «margen · casilla · remitente» cayó por
     casualidad en la pista del ASUNTO, que es donde tiene que estar. Al
     encender el SELLO del remitente entró una pista nueva en tercer lugar y
     la colocación automática mandó la tira a la pista del sello: 18 px de
     ancho para 80 px de tira que, con `justify-self: end`, desbordaban 62 px
     HACIA LA IZQUIERDA y aparecían encima del rail, fuera del renglón.
     Medido: tira en x=197 con el renglón empezando en x=225.
     Se ancla contando DESDE EL FINAL, como sus vecinas y por el mismo motivo
     escrito arriba: así la pista nueva del sello no la mueve —insertar una
     pista al principio no cambia los índices negativos— y vale igual en los
     cuatro juegos de columnas. El asunto es una pista más a la izquierda que
     las señales. */
  .renglon:has(> .renglon__cuerpo) > .renglon__acciones { grid-column: -4; }
  @container lista (min-width: 40rem) {
    /* Con la pista de TAMAÑO encendida todo se corre una a la izquierda. */
    .renglon:has(> .renglon__cuerpo) > .renglon__cifra   { grid-column: -3; }
    .renglon:has(> .renglon__cuerpo) > .renglon__senales { grid-column: -4; }
    .renglon:has(> .renglon__cuerpo) > .renglon__acciones { grid-column: -5; }
  }

  /* DEFECTO CORREGIDO — el renglón no respiraba por la derecha. La HORA lleva
     `padding-inline-end: var(--e-4)` desde §6 y el CUERPO no llevaba ninguno,
     así que el asunto y la vista previa se iban a tocar el filo del panel
     mientras la cifra de arriba se quedaba a 16 px. En un registro las dos
     líneas cierran por el mismo sitio o no cierran. */
  .renglon:has(> .renglon__cuerpo) > .renglon__cuerpo {
    grid-column: 3 / -1;
    grid-row: 2;
    padding-block-end: var(--renglon-aire);
    padding-inline-end: var(--e-4);
  }

  /* ►► LA TIRA DE ACCIONES YA NO FLOTA ◄◄
     DEFECTO CORREGIDO. Era una tarjeta absoluta pegada al filo derecho, con
     fondo, borde y sombra. Con el renglón de dos líneas se montaba sobre la
     hora Y sobre la vista previa, que se seguía leyendo por debajo: tres
     botones encima del texto es lo primero que se ve como roto.

     Baja a la rejilla y ocupa la pista del ASUNTO en la PRIMERA línea, que
     el pliegue de §16.2 dejó vacía —ahí no hay nada que tapar—. Se descartó
     darle columna propia en el subgrid: se pagaría con ancho en los 187
     renglones para servir al único que tiene el cursor encima, y el asunto
     ya es lo que más falta le hace. Se descartó apagar la hora: nada la
     tapa, así que apagarla sería esconder un dato sin motivo.

     Sin tarjeta: en su propia franja no necesita fondo, ni borde, ni sombra
     para despegarse de nada. Tres iconos y aire.                          */
  .renglon:has(> .renglon__cuerpo) > .renglon__acciones {
    position: static;
    grid-row: 1;
    justify-self: end;
    align-self: end;
    padding: 0;
    background: none;
    border: 0;
    border-radius: 0;
    box-shadow: none;
    transform: none;
    transition: opacity var(--dur-1) var(--curva-estandar),
                visibility var(--dur-1);
  }
  /* 24 px es lo que cabe en la primera línea sin empujarla, también en la
     densidad compacta: el renglón mide lo mismo con la tira y sin ella, así
     que pasar el cursor por el registro no lo mueve ni un pixel.          */
  .renglon:has(> .renglon__cuerpo) > .renglon__acciones .accion-fila {
    inline-size: 1.5rem;
    block-size: 1.5rem;
  }

  /* Con el ancho completo el asunto ya no se recorta a media palabra. Se le
     permite una segunda línea antes de ceder, igual que en el teléfono.     */
  .renglon:has(> .renglon__cuerpo) .renglon__asunto {
    white-space: normal;
    text-wrap: pretty;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  /* El membrete rotulaba una columna que en la primera línea ya no lleva el
     asunto. Se retira el rótulo y se conserva la celda: es la que sostiene la
     alineación de REMITENTE y HORA con las suyas de abajo.                  */
  .registro:has(.renglon__cuerpo) > .registro__cabecera .col-asunto {
    visibility: hidden;
  }
}

@media (max-width: 24rem) {
  :root { --col-remitente: 7.5rem; --e-4: 0.75rem; }
  .barra { padding-inline: var(--e-3); gap: var(--e-2); }
  .lectura { padding: var(--e-4) var(--e-3) var(--e-12); }
}
