/* ⚠️ COPIA GENERADA · no editar.
   Fuente: design-system/pensura-componentes.css
   Se regenera con: python3 design-system/build.py
   Editar este archivo en vez de la fuente hace fallar el build. */

/* ============================================================
   PENSURA · DESIGN SYSTEM · COMPONENTES
   Fuente de verdad: design-system/pensura-componentes.css

   Depende de plataforma-marca/03-tokens/pensura-tokens.css y de
   las fuentes. No conoce ninguna pantalla: aca no hay hero, ni
   franja, ni sidebar. Si una regla habla de UNA pantalla, no va
   en esta hoja.

   Las copias que consumen las superficies (web/sitio/, y la app
   cuando migre) las genera y verifica design-system/build.py.
   Editar una copia en vez de esta hoja hace fallar el build.
   ============================================================ */

/* ==========================================================
   0-a · MEDIDAS DE LA CAPA
   Las que consumen los componentes de esta hoja. Tienen que
   vivir acá y no en la hoja de una superficie: .contenedor y
   .prosa son del sistema, así que una app que importe solo
   tokens + componentes tiene que poder resolverlas.

   (Estuvieron declaradas en web/sitio/estilos.css hasta que la
   extracción se llevó los componentes y las dejó atrás. El
   chequeo de build.py existe para que no vuelva a pasar.)
   ========================================================== */

:root {
  --web-ancho-contenido: calc(var(--esp-24) * 12);  /* 1152px · la caja de una página */
  --web-ancho-prosa:     calc(var(--esp-24) * 7);   /*  672px · medida de lectura larga */
  --web-ancho-texto:     calc(var(--esp-24) * 5.5); /*  528px · línea de apoyo dentro de un componente */

  /* 44px · el área táctil mínima de la guía de iOS. La usan .boton--sm y
     .form-linea cuando el puntero es el dedo. Vive acá hasta que 03-tokens
     tenga su escalón: entre --control-md (40) y --control-lg (48) no hay nada. */
  --control-tactil: calc(var(--control-lg) - var(--esp-1));

  /* Cuántos caracteres entran en un .control--ch. Lo declara el campo, no la
     hoja de la pantalla: style="--control-ch: 6" al lado de maxlength="6". */
  --control-ch: 6;
}


/* ==========================================================
   0-b · BREAKPOINTS
   --ancho-medio  52em  (832px)  · una columna → dos
   --ancho-chico  34em  (544px)  · compresiones finas
   No hay otro literal de medida en esta hoja.
   ========================================================== */


/* ==========================================================
   1 · BASE
   ========================================================== */

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

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

body {
  margin: 0;
  background: var(--color-fondo);
  color: var(--color-texto-primario);
  font-family: var(--font-marca);
  font-size: var(--texto-lg);            /* 16px · cuerpo por defecto en web */
  font-weight: var(--peso-regular);
  line-height: var(--interlinea-cuerpo);
  font-feature-settings: var(--font-dato-features);   /* cero barrado en la mono */
  text-wrap: pretty;
  -webkit-font-smoothing: antialiased;
}

h1, h2, h3, p, ul, ol, figure, blockquote {
  margin: 0;
}

ul {
  padding: 0;
  list-style: none;
}

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

a {
  color: var(--color-link);
  text-decoration-thickness: var(--borde-fino);
  text-underline-offset: var(--esp-1);
  transition: color var(--transicion);
}

a:hover {
  color: var(--color-texto-primario);
}

a:active {
  color: var(--color-accion-hover);
}

/* El foco visible lo declara pensura-tokens.css §9 (*:focus-visible,
   2px de --color-foco con offset). Acá solo se refuerza el borde de
   control, que es el token separado a propósito de --color-borde-sutil
   porque tiene que cumplir 3:1 (WCAG 1.4.11). */
:focus-visible {
  outline-color: var(--color-foco);
}


/* ==========================================================
   2 · UTILIDADES DE LAYOUT
   ========================================================== */

.contenedor {
  width: 100%;
  max-width: var(--web-ancho-contenido);
  margin-inline: auto;
  padding-inline: var(--esp-6);
}

.prosa {
  max-width: var(--web-ancho-prosa);
}

.solo-lectores {
  position: absolute;
  width: var(--borde-fino);
  height: var(--borde-fino);
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* El destino de un salto lleva tabindex="-1" para que el foco viaje con él y no
   sólo el scroll: sin eso el navegador scrollea pero el foco se queda en el
   <body>, y la siguiente tabulación vuelve a la navegación que se quería
   saltear. Enfocado así no lleva anillo —el -1 no es parada de tabulación, es a
   dónde el usuario acaba de pedir ir, y el anillo encerraría la página entera. */
[tabindex="-1"]:focus {
  outline: none;
}

/* Al tabular vuelve a la página: un enlace recortado e inalcanzable no es
   un salto de contenido, es una trampa de teclado.
   El z-index va por encima de todo lo del sistema (hoy el máximo es 30, los
   paneles de .selector y .menu-cuenta) y no es un número al voleo: el enlace
   aparece arriba a la izquierda, que es justo donde vive la barra pegada del
   shell, y .shell__barra estaba en el mismo 20 que este bloque; con el empate
   ganaba ella por orden de documento —el enlace va antes en el HTML— y el
   enlace quedaba enfocado y tapado, que es peor que no tenerlo. Lo que se
   enfoca se ve: si algo nuevo necesita taparlo, está mal. */
.solo-lectores:focus-visible {
  position: fixed;
  top: var(--esp-3);
  inset-inline-start: var(--esp-3);
  z-index: 50;
  width: auto;
  height: auto;
  clip-path: none;
  padding: var(--esp-2) var(--esp-4);
  background: var(--color-accion);
  color: var(--color-accion-texto);
  border-radius: var(--radio-md);
  font-size: var(--texto-md);
  text-decoration: none;
}


/* ==========================================================
   3 · TIPOGRAFÍA DE PÁGINA
   El peso 300 aparece solo en --texto-5xl y --texto-6xl.
   De 32px para abajo el mínimo es 400 (regla dura del sistema).
   ========================================================== */

.display {
  font-size: var(--texto-4xl);
  font-weight: var(--peso-light);          /* 44px · el piso del 300 */
  line-height: var(--interlinea-ajustada);
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
}

.titulo {
  font-size: var(--texto-2xl);
  font-weight: var(--peso-semibold);
  line-height: var(--interlinea-titulo);
  letter-spacing: var(--tracking-display);
  text-wrap: balance;
}

/* El h1 de una pantalla de app. Existe porque faltaba el escalón del medio:
   .display son 44px en peso 300 y viene de la web pública, donde el título
   ocupa la pantalla; arriba de una tabla de 14px se come el contenido. Y
   .titulo a 24px deja al título de pantalla del mismo tamaño que un h3 de
   prosa, que en una pantalla con secciones no alcanza.
   32px con peso 500: la regla dura dice que de 32px para abajo el mínimo es
   400, así que el peso sube al bajar el tamaño. No es .titulo más grande. */
.titulo--pantalla {
  font-size: var(--texto-3xl);
  font-weight: var(--peso-medium);
}

.bajada {
  font-size: var(--texto-lg);
  color: var(--color-texto-lectura);       /* la prosa es teal, no azul */
}

.nota {
  font-size: var(--texto-sm);
  color: var(--color-texto-suave);
}

.dato-monto--xl {
  font-size: var(--texto-xl);
}


/* ==========================================================
   4 · CONTROLES
   Reposo, hover, active y foco. Los tres estados, no solo el reposo.
   ========================================================== */

.boton {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--esp-2);
  min-height: var(--control-md);
  padding: var(--esp-3) var(--esp-6);
  border: var(--borde-fino) solid transparent;
  border-radius: var(--radio-md);
  font-family: var(--font-marca);
  font-size: var(--texto-lg);
  font-weight: var(--peso-medium);
  line-height: var(--interlinea-titulo);
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--transicion), border-color var(--transicion), color var(--transicion);
}

.boton--primario {
  background: var(--color-accion);
  color: var(--color-accion-texto);
}

.boton--primario:hover {
  background: var(--color-accion-hover);
  color: var(--color-accion-texto);
}

.boton--primario:active {
  background: var(--color-accion-hover);
}

.boton--secundario {
  background: transparent;
  border-color: var(--color-borde-control);
  color: var(--color-texto-primario);
}

.boton--secundario:hover {
  background: var(--color-superficie-hover);
  border-color: var(--color-texto-primario);
  color: var(--color-texto-primario);
}

.boton--secundario:active {
  border-color: var(--color-accion);
}

.boton--sm {
  min-height: var(--control-sm);
  padding: var(--esp-2) var(--esp-4);
  font-size: var(--texto-md);
}

/* Con el dedo, 44px. Cierra la decisión abierta de PEDIDOS.md para el
   paginador y para .form-linea a la vez. El corte es por PUNTERO y no por
   ancho: una ventana angosta en un escritorio se sigue usando con mouse, y
   una tablet apaisada de 1180px se usa con el pulgar. En una fila de
   --fila-default (44px) el botón entra justo y la fila no crece; en
   .tabla--compacta (36px) sí crece, y con el dedo eso es lo correcto. */
@media (pointer: coarse) {
  .boton--sm {
    min-height: var(--control-tactil);
  }
}

.boton--sobre-acento {
  background: var(--web-sobre-acento-fondo);
  color: var(--web-sobre-acento-texto);
}

.boton--sobre-acento:hover {
  background: var(--web-sobre-acento-hover-fondo);
  color: var(--web-sobre-acento-hover-texto);
}

.lista {
  display: grid;
  gap: var(--esp-3);
  margin-top: var(--esp-8);
}

.lista li {
  display: grid;
  grid-template-columns: var(--esp-4) 1fr;
  align-items: start;
  gap: var(--esp-3);
  font-size: var(--texto-md);
  color: var(--color-texto-suave);
}

/* Viñeta: un tramo de la columna del isotipo, no un icono prestado. */
.lista li::before {
  content: "";
  width: var(--esp-4);
  height: var(--borde-medio);
  margin-top: var(--esp-3);
  background: var(--color-acento);
  border-radius: var(--radio-full);
}


/* ==========================================================
   9 · LAS PIEZAS · un artefacto por bloque
   Todas se arman con los componentes del sistema: .chip-estado,
   .dato-monto, .dato-id, y los iconos de dominio inlineados.
   ========================================================== */

.pieza {
  background: var(--color-superficie);
  border: var(--borde-fino) solid var(--color-borde-sutil);
  border-radius: var(--radio-lg);
  box-shadow: var(--sombra-md);
  padding: var(--esp-6);
}

.pieza__cabecera {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--esp-4);
  padding-bottom: var(--esp-4);
  border-bottom: var(--borde-fino) solid var(--color-borde-sutil);
}

.pieza__pie {
  margin-top: var(--esp-4);
  padding-top: var(--esp-4);
  border-top: var(--borde-medio) solid var(--color-borde-sutil);
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--esp-4);
}

/* Renglón de cálculo: label a la izquierda, importe a la derecha.
   Es el «explicar el cálculo, siempre» del tono de voz, compuesto. */
.renglon {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: baseline;
  gap: var(--esp-4);
  padding-block: var(--esp-2);
  font-size: var(--texto-md);
}

.renglon__label {
  color: var(--color-texto-suave);
}

.renglon__detalle {
  font-family: var(--font-dato);
  font-size: var(--texto-xs);
  color: var(--color-texto-suave);
}

.renglon--total {
  font-size: var(--texto-lg);
  font-weight: var(--peso-medium);
  color: var(--color-texto-primario);
}

.chip-estado svg {
  width: var(--esp-4);
  height: var(--esp-4);
}

.chip-estado--borrador  { background: var(--estado-borrador-fondo);  color: var(--estado-borrador-texto); }
.chip-estado--emitida   { background: var(--estado-emitida-fondo);   color: var(--estado-emitida-texto); }
.chip-estado--porvencer { background: var(--estado-porvencer-fondo); color: var(--estado-porvencer-texto); }
.chip-estado--vencida   { background: var(--estado-vencida-fondo);   color: var(--estado-vencida-texto); }
.chip-estado--pagada    { background: var(--estado-pagada-fondo);    color: var(--estado-pagada-texto); }

/* Varios chips juntos (el estado y una marca al lado, como «Esta sesión»).
   Sueltos se desalinean: son inline-flex, así que se apoyan en su
   baseline, y la de un chip con svg es el pie del ícono. En la tabla
   en tarjetas es peor: la celda es una grilla de etiqueta + valor, y el
   segundo chip suelto cae en otra fila, debajo de la etiqueta. */
.chips {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--esp-2);
}


/* ==========================================================
   11 · OBJECIONES
   <details> nativo: cero JavaScript, teclado y lector de
   pantalla incluidos.
   ========================================================== */

.objeciones {
  display: grid;
  margin-top: var(--esp-8);
  max-width: var(--web-ancho-prosa);
  border-top: var(--borde-fino) solid var(--color-borde-sutil);
}

.objecion {
  border-bottom: var(--borde-fino) solid var(--color-borde-sutil);
}

.objecion__pregunta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--esp-4);
  min-height: var(--fila-comoda);
  padding-block: var(--esp-4);
  font-size: var(--texto-xl);
  font-weight: var(--peso-medium);
  cursor: pointer;
  list-style: none;
  transition: color var(--transicion);
}

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

.objecion__pregunta:hover {
  color: var(--color-link);
}

.objecion__signo {
  flex: 0 0 auto;
  width: var(--esp-6);
  height: var(--esp-6);
  color: var(--color-texto-suave);
  transition: transform var(--transicion);
}

.objecion[open] .objecion__signo {
  transform: rotate(45deg);
}

.objecion__respuesta {
  padding-bottom: var(--esp-6);
  color: var(--color-texto-lectura);
}


/* ==========================================================
   16 · FORMULARIO
   Se usa en contacto.html. Vive en esta hoja y no en una
   aparte para que el control siga siendo un solo componente:
   una segunda hoja sería una segunda verdad.

   Los cuatro estados de cada campo —reposo, foco, error,
   deshabilitado— y los tres del envío —cargando, éxito,
   error— están todos acá. El error nunca se comunica solo
   con color: siempre color + icono + palabra.
   ========================================================== */

.form {
  display: grid;
  gap: var(--esp-6);
}

.form__grupo {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--esp-6);
}

.campo {
  display: grid;
  gap: var(--esp-2);
}

.campo__label {
  font-size: var(--texto-md);
  font-weight: var(--peso-medium);
  color: var(--color-texto-primario);
}

.campo__opcional {
  margin-inline-start: var(--esp-2);
  font-family: var(--font-dato);
  font-size: var(--texto-xs);
  font-weight: var(--peso-regular);
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
  color: var(--color-texto-suave);
}

/* Reposo. El borde es --color-borde-control, que es el token que
   cumple 3:1 contra la superficie (WCAG 1.4.11). */
.control {
  width: 100%;
  min-height: var(--control-lg);      /* 48px · se llena con el pulgar */
  padding: var(--esp-3) var(--esp-4);
  background: var(--color-superficie);
  color: var(--color-texto-primario);
  font-family: var(--font-marca);
  font-size: var(--texto-lg);         /* 16px · menos que esto y iOS hace zoom */
  font-weight: var(--peso-regular);
  line-height: var(--interlinea-cuerpo);
  border: var(--borde-fino) solid var(--color-borde-control);
  border-radius: var(--radio-md);
  transition: border-color var(--transicion), background-color var(--transicion);
}

.control::placeholder {
  color: var(--color-texto-suave);
}

.control:hover {
  border-color: var(--color-texto-primario);
}

/* Foco: el anillo lo pone pensura-tokens.css §9. Acá solo se engrosa
   el borde para que el campo activo se lea también sin el anillo. */
.control:focus-visible {
  border-color: var(--color-foco);
  outline-color: var(--color-foco);
}

.control:disabled {
  background: var(--color-superficie-hover);
  border-color: var(--color-borde-sutil);
  color: var(--color-texto-suave);
  cursor: not-allowed;
}

.control--area {
  min-height: calc(var(--control-lg) * 2);
  resize: vertical;
}

/* El control se estira al 100% porque en un formulario esa es la regla. Fuera
   de un formulario —un filtro al lado del buscador— tiene que medir lo suyo.
   Vive acá y no en el componente que lo necesita: el ancho es del control, y
   un componente no le cambia las medidas a otro. */
.control--auto {
  width: auto;
}

/* «Entran N caracteres». Para datos de largo fijo —un código de seis
   dígitos, un CUIT, un código postal—, que son identificadores y por eso van
   en la mono, como .dato-id. En la mono 1ch es exactamente un carácter, así
   que la cuenta es exacta: N + 1 (el cursor) más el padding y el borde.
   N lo declara el campo con --control-ch, junto a su maxlength. */
.control--ch {
  width: calc((var(--control-ch) + 1) * 1ch + var(--esp-4) * 2 + var(--borde-fino) * 2);
  max-width: 100%;
  font-family: var(--font-dato);
  font-feature-settings: var(--font-dato-features);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
}

.campo--error .control {
  border-color: var(--color-error);
  border-width: var(--borde-medio);
}

.campo__ayuda {
  font-size: var(--texto-sm);
  color: var(--color-texto-suave);
}

/* Segundo y tercer canal del error: icono y palabra, no solo el rojo. */
.campo__error {
  display: none;
  align-items: flex-start;
  gap: var(--esp-2);
  font-size: var(--texto-sm);
  color: var(--color-error);
}

.campo--error .campo__error {
  display: flex;
}

.campo__error svg {
  flex: 0 0 auto;
  width: var(--esp-4);
  height: var(--esp-4);
  margin-top: var(--esp-1);
}

.form__acciones {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--esp-4);
}

.form__legal {
  font-size: var(--texto-sm);
  color: var(--color-texto-suave);
}

/* Envío en curso: el botón no cambia de ancho ni de texto de golpe,
   se le agrega el giro y se bloquea. */
.boton[aria-busy="true"] {
  cursor: progress;
}

.boton:disabled {
  cursor: not-allowed;
  opacity: 0.7;
}

.giro {
  display: none;
  width: var(--esp-4);
  height: var(--esp-4);
  border: var(--borde-medio) solid currentColor;
  border-top-color: transparent;
  border-radius: var(--radio-full);
  animation: giro 700ms linear infinite;
}

.boton[aria-busy="true"] .giro {
  display: block;
}

@keyframes giro {
  to { transform: rotate(1turn); }
}

@media (prefers-reduced-motion: reduce) {
  .giro { animation-duration: 2400ms; }
}


/* ==========================================================
   16-b · FORMULARIO EN LÍNEA
   Un control al lado del dato sobre el que decide: el código
   que acepta una invitación, en una fila de tabla o en una
   tarjeta. No dibuja nada nuevo: rótulo .campo__label, campo
   .control + .control--ch, acciones .boton--sm, error
   .campo--error + .campo__error, rótulo oculto .solo-lectores.

   Dos piezas:
   .form-linea   la línea. Un <div>, no un <form>.
   .form--inline cada <form> de la línea. Aceptar y rechazar son
                 dos POST, así que son dos <form> hermanos dentro
                 de una .form-linea; ninguno abre su grilla.

   El alto: en la línea el control y el botón miden lo mismo,
   --control-sm con mouse y --control-tactil (44) con el dedo.
   El control no necesita sus 48px acá: los 48 son para llenar
   un formulario entero con el pulgar, y la regla de 44 ya cubre
   el toque. La letra sigue en 16px, así que iOS no hace zoom.

   ⚠️ Composición que cambia por ancho:
   - Afuera de una tabla la línea envuelve: el segundo <form>
     baja entero, nunca por la mitad. Debajo de 22em de línea el
     rótulo vuelve arriba del campo (@container form-linea).
   - Adentro de .tabla__acciones NO envuelve: la celda es
     width: 0 y una línea que envuelve colapsa a su ítem más
     ancho. Vuelve a envolver cuando .tabla--tarjetas pasa a
     tarjeta, que es cuando la celda deja de ser width: 0.
   ========================================================== */

.form-linea {
  width: 100%;   /* inline-size: sin el 100% colapsa a 0 dentro de un flex */
  container: form-linea / inline-size;
  display: flex;
  flex-wrap: wrap;
  /* Arriba y no al centro: cada <form> ya centra lo suyo, y con el error abajo
     el form de al lado se iría a flotar entre el campo y el mensaje. */
  align-items: flex-start;
  gap: var(--esp-2) var(--esp-3);
}

.form--inline {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--esp-2);
  margin: 0;
}

.form--inline .campo__label {
  white-space: nowrap;
}

/* El mismo alto para los dos, y sin padding vertical: con padding el botón
   de 32 mide 34 y la línea queda desalineada por 2px. */
.form-linea .control,
.form-linea .boton {
  min-height: var(--control-sm);
  padding-block: 0;
}

@media (pointer: coarse) {
  .form-linea .control,
  .form-linea .boton {
    min-height: var(--control-tactil);
  }
}

/* El error en tarjeta baja a su renglón, debajo del campo. Va el mensaje
   corto: el borde es el primer canal, el ícono y la palabra los otros dos. */
.form--inline .campo__error {
  flex-basis: 100%;
}

.form--inline .campo__error svg {
  margin-top: 0;
}

@container form-linea (max-width: 22em) {
  .form--inline > .campo__label:not(.solo-lectores) {
    flex-basis: 100%;
  }
}

/* --- Adentro de una celda ---
   Ni contenedor ni envoltura: la línea mide su contenido. El error queda en
   la línea, a la derecha del campo, y la fila no crece en alto (crece en
   ancho la columna, por eso el mensaje va corto). */
.tabla__acciones .form-linea {
  width: auto;
  container-type: normal;
  flex-wrap: nowrap;
  justify-content: flex-end;
}

.tabla__acciones .form--inline {
  flex-wrap: nowrap;
}

.tabla__acciones .form--inline .campo__error {
  flex-basis: auto;
}

@container tabla (max-width: 34em) {
  .tabla--tarjetas tbody .tabla__acciones .form-linea,
  .tabla--tarjetas tbody .tabla__acciones .form--inline {
    flex-wrap: wrap;
    justify-content: flex-start;
    white-space: normal;
  }
}


/* Resultado del envío. Mismo criterio que los chips: relleno + icono
   + palabra. Nunca el color solo. */
.aviso {
  display: flex;
  align-items: flex-start;
  gap: var(--esp-3);
  margin-top: var(--esp-4);
  padding: var(--esp-4);
  border-radius: var(--radio-md);
  font-size: var(--texto-md);
}

.aviso svg {
  flex: 0 0 auto;
  width: var(--esp-6);
  height: var(--esp-6);
}

.aviso--exito {
  background: var(--estado-pagada-fondo);
  color: var(--estado-pagada-texto);
}

.aviso--error {
  background: var(--estado-vencida-fondo);
  color: var(--estado-vencida-texto);
}

.aviso__titulo {
  font-weight: var(--peso-medium);
}

.aviso__p {
  margin-top: var(--esp-1);
}

/* Va después de todo lo que declara display: sin el !important, el
   display de un componente le gana al atributo y el elemento oculto se
   ve igual. Es lo que mantiene escondido el botón de tema sin JS. */
[hidden] {
  display: none !important;
}

@media (min-width: 52em) {
  .contenedor {
    padding-inline: var(--esp-10);
  }

  /* Formulario: nombre y empresa, y email y teléfono, comparten fila
     recién cuando hay ancho. En mobile es una columna. */
  .form__grupo {
    grid-template-columns: repeat(2, 1fr);
  }
}


/* ==========================================================
   INTERRUPTOR DE TEMA · el mecanismo
   Hay dos formas de estar en oscuro —el atributo, o la
   preferencia del sistema sin eleccion manual en contra— y CSS
   no deja combinar un @media con una lista de selectores. Para
   no repetir cada regla dos veces, estos dos interruptores se
   prenden una vez y las reglas los consumen.
   Cada superficie declara ADEMAS los suyos, con sus valores.
   ========================================================== */

:root {
  /* Palancas de la tabla. Viven en :root y no en .tabla para que sean tokens
     del sistema y no variables privadas de un componente: la densidad es una
     decisión de pantalla, y --tabla-alto-max lo fija la pantalla. */
  --tabla-fila:     var(--fila-default);
  --tabla-pad:      var(--esp-4);
  --tabla-alto-max: none;

  --web-en-claro:  block;
  --web-en-oscuro: none;
  --web-sobre-acento-fondo:       var(--color-fondo);
  --web-sobre-acento-texto:       var(--color-texto-primario);
  --web-sobre-acento-hover-fondo: var(--color-fondo-alt);
  --web-sobre-acento-hover-texto: var(--color-texto-primario);
}
[data-theme="oscuro"] {
  --web-en-claro:  none;
  --web-en-oscuro: block;
  --web-sobre-acento-fondo:       var(--color-accion);
  --web-sobre-acento-texto:       var(--color-accion-texto);
  --web-sobre-acento-hover-fondo: var(--color-accion-hover);
  --web-sobre-acento-hover-texto: var(--color-accion-texto);
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="claro"]) {
    --web-en-claro:  none;
    --web-en-oscuro: block;
    --web-sobre-acento-fondo:       var(--color-accion);
    --web-sobre-acento-texto:       var(--color-accion-texto);
    --web-sobre-acento-hover-fondo: var(--color-accion-hover);
    --web-sobre-acento-hover-texto: var(--color-accion-texto);
  }
}


/* ==========================================================
   17 · TABLA
   El componente más usado del producto. No dibuja nada que ya
   exista: el estado va en .chip-estado, el importe en
   .dato-monto, el identificador en .dato-id, la acción de fila
   en .boton--sm y el pie de celda en .nota.

   Tres densidades (--fila-*), encabezado fijo, ordenamiento con
   sus tres estados, fila seleccionada y fila-link accesible.
   El alto de la caja lo fija la pantalla con --tabla-alto-max:
   esta hoja no conoce ninguna pantalla.

   ⚠️ Composición que cambia por ancho, no por tema: en angosto
   .tabla--tarjetas reagrupa la fila como tarjeta. El corte se
   mide con @container sobre .tabla-caja, no con el viewport,
   porque dentro del shell la tabla nunca tiene el ancho de la
   ventana.
   ========================================================== */

.tabla-caja {
  position: relative;
  /* inline-size hace que el ancho NO dependa del contenido: sin el 100%
     la caja colapsa a 0 dentro de un flex o grid. */
  width: 100%;
  container: tabla / inline-size;
  max-height: var(--tabla-alto-max);
  overflow: auto;
  background: var(--color-superficie);
  border: var(--borde-fino) solid var(--color-borde-sutil);
  border-radius: var(--radio-md);
}

/* Dentro de una .pieza la tarjeta ya es el marco: si la caja dibuja el suyo
   quedan dos bordes uno dentro del otro. La caja conserva el resto —el scroll,
   el alto máximo y el contenedor que mide el corte a tarjetas—; solo deja de
   dibujarse. Descendiente y no hijo: un listado paginado llega envuelto en
   las regiones del panel que lo carga, y la tarjeta sigue siendo su marco. */
.pieza .tabla-caja {
  border: 0;
  border-radius: 0;
}

.tabla {
  width: 100%;
  border-collapse: separate;   /* collapse rompe el sticky del th en WebKit */
  border-spacing: 0;
  font-size: var(--texto-md);
  line-height: var(--interlinea-dato);
}

/* Las densidades pisan la medida en las celdas, no una variable privada:
   --tabla-fila y --tabla-pad son tokens del sistema y su valor por defecto
   vive en :root. */
.tabla--compacta {
  font-size: var(--texto-sm);   /* 13px · el piso del peso 400 */
}

.tabla--compacta thead th,
.tabla--compacta tbody td {
  height: var(--fila-compacta);
  padding-inline: var(--esp-3);
}

.tabla--compacta .tabla__vacio td {
  height: calc(var(--fila-compacta) * 4);
}

.tabla--comoda thead th,
.tabla--comoda tbody td {
  height: var(--fila-comoda);
  padding-inline: var(--esp-6);
}

.tabla--comoda .tabla__vacio td {
  height: calc(var(--fila-comoda) * 4);
}

.tabla caption {
  padding: var(--esp-3) var(--esp-4);
  color: var(--color-texto-suave);
  font-size: var(--texto-sm);
  text-align: left;
}

/* --- Encabezado --- */

/* El encabezado se distingue del cuerpo por FORMA, no por tamaño: es el
   mismo tratamiento que .kicker —mono, 12px, versalita, con tracking—, que
   es lo que el sistema ya usa para rotular una columna de datos.
   El peso BAJA de 500 a 400: la fila de arriba no necesitaba negrita,
   necesitaba ser otra cosa.
   El fondo deja de ser la franja gris y pasa a la superficie, pero sigue
   siendo opaco: con .tabla--enc-fijo el th se pega al scrollear y tiene que
   tapar las filas que pasan por debajo. El que separa es el borde. */
.tabla thead th {
  height: var(--tabla-fila);
  padding-inline: var(--tabla-pad);
  background: var(--color-superficie);
  border-bottom: var(--borde-medio) solid var(--color-borde-sutil);
  color: var(--color-texto-suave);
  font-family: var(--font-dato);
  font-size: var(--texto-xs);
  font-weight: var(--peso-regular);
  letter-spacing: var(--tracking-kicker);
  text-transform: uppercase;
  text-align: start;
  white-space: nowrap;
}

/* Fijo al scrollear: se pega a .tabla-caja, no a la ventana. */
.tabla--enc-fijo thead th {
  position: sticky;
  top: 0;
  z-index: 2;
}

/* --- Ordenamiento · tres estados ---
   El estado vive en aria-sort del th, que es lo que lee el lector
   de pantalla; el CSS solo lo dibuja. Dos canales: la flecha (una
   sola, opaca) más el filete de acento bajo la columna activa. */

.tabla__orden {
  display: inline-flex;
  align-items: center;
  gap: var(--esp-2);
  width: 100%;
  min-height: var(--control-sm);
  margin: 0;
  padding: 0;
  background: none;
  border: 0;
  color: inherit;
  font: inherit;
  /* El th es kicker (mayúscula y tracking), y un botón no los hereda: el
     navegador le pone text-transform: none y letter-spacing: normal. */
  letter-spacing: inherit;
  text-transform: inherit;
  text-align: inherit;
  cursor: pointer;
  transition: color var(--transicion);
}

.tabla__orden:hover {
  color: var(--color-texto-primario);
}

.tabla__num .tabla__orden {
  justify-content: flex-end;
}

.tabla__orden-icono {
  flex: 0 0 auto;
  width: var(--esp-4);
  height: var(--esp-4);
}

/* Sin ordenar: las dos flechas atenuadas. */
.tabla__orden-icono .i-asc,
.tabla__orden-icono .i-desc {
  opacity: 0.45;
}

.tabla th[aria-sort="ascending"] .i-asc,
.tabla th[aria-sort="descending"] .i-desc {
  opacity: 1;
}

.tabla th[aria-sort="ascending"] .i-desc,
.tabla th[aria-sort="descending"] .i-asc {
  display: none;
}

.tabla th[aria-sort="ascending"],
.tabla th[aria-sort="descending"] {
  color: var(--color-texto-primario);
  box-shadow: inset 0 calc(var(--borde-grueso) * -1) 0 0 var(--color-acento);
}

/* --- Cuerpo --- */

.tabla tbody tr {
  background: var(--color-superficie);
  transition: background-color var(--transicion);
}

/* Jerarquía por sustracción. La celda común baja a suave y suben las dos que
   se usan: la que identifica la fila (.tabla__celda--titulo) y el importe
   (.tabla__num). Cada fila pasa a decir «quién, cuánto, y el resto».
   Con el 68% del sistema en peso 500, agregar énfasis no alcanzaba: había
   que sacárselo a lo que no lo necesitaba. No sube ningún peso, no cambia
   ninguna medida, no entra ningún color.
   --color-texto-suave son 7.61 sobre el fondo: sigue siendo AA. */
.tabla tbody td {
  height: var(--tabla-fila);
  padding-inline: var(--tabla-pad);
  border-bottom: var(--borde-fino) solid var(--color-borde-sutil);
  color: var(--color-texto-suave);
  vertical-align: middle;
}

.tabla tbody tr:last-child td {
  border-bottom: 0;
}

.tabla tbody tr:hover {
  background: var(--color-superficie-hover);
}

/* Seleccionada: relleno más filete de acento. El primer canal es la
   casilla marcada; el color nunca va solo. */
.tabla tbody tr[aria-selected="true"] {
  background: var(--color-superficie-hover);
}

.tabla tbody tr[aria-selected="true"] td:first-child {
  box-shadow: inset var(--borde-grueso) 0 0 0 var(--color-acento);
}

/* --- Columnas --- */

/* Números e importes: a la derecha y alineados por la coma.
   nowrap no es cosmético: sin él, "$ 1.284.500,00" se parte entre el signo y
   la cifra en cuanto la columna se aprieta, y un importe partido en dos
   renglones se lee mal y rompe la columna que tabular-nums vino a alinear. */
.tabla .tabla__num {
  text-align: end;
  white-space: nowrap;
  color: var(--color-texto-primario);   /* el importe es lo que se vino a leer */
  font-variant-numeric: tabular-nums lining-nums;
  font-feature-settings: "tnum" 1;
}

.tabla .tabla__acciones {
  position: relative;   /* queda por encima del área de clic de la fila-link */
  z-index: 1;
  width: 0;             /* se encoge a su contenido */
  text-align: end;
  white-space: nowrap;
}

.tabla__acciones-grupo {
  display: flex;
  justify-content: flex-end;
  gap: var(--esp-2);
}

/* La celda que identifica la fila: el nombre, el número de factura. */
/* Va calificada con .tabla —igual que .tabla__num— y no suelta: desde que
   .tabla tbody td baja el color a suave, un selector de una sola clase (0,1,0)
   pierde contra (0,1,2) y la celda que identifica la fila se quedaba gris.
   Es la mitad de «quién, cuánto»: sin esto la jerarquía queda a medias. */
.tabla .tabla__celda--titulo {
  color: var(--color-texto-primario);
  font-weight: var(--peso-medium);
}

/* Pie de celda: el segundo renglón va con .nota. */
.tabla__celda-pila {
  display: grid;
  gap: var(--esp-1);
  align-content: center;
}

/* --- Primera columna fija ---
   Para tablas anchas de comparación. La celda hereda el fondo de su
   fila, así que hover y selección siguen leyéndose bajo la columna fija. */
.tabla--col-fija th:first-child,
.tabla--col-fija td:first-child {
  position: sticky;
  inset-inline-start: 0;
  z-index: 1;
  background: inherit;
  border-inline-end: var(--borde-fino) solid var(--color-borde-sutil);
}

.tabla--col-fija thead th:first-child {
  z-index: 3;
  background: var(--color-superficie-hover);
}

/* --- Fila que es un link entero ---
   Un <a> real en la celda título, estirado sobre la fila con ::after.
   Es foco de teclado, Enter navega, y el menú contextual da la URL.
   Nunca un onclick en el <tr>. */
.tabla__fila--link {
  position: relative;
}

.tabla__link {
  color: var(--color-texto-primario);
  font-weight: var(--peso-medium);
  text-decoration: none;
}

.tabla__link::after {
  content: "";
  position: absolute;
  inset: 0;
}

.tabla__fila--link:hover .tabla__link {
  text-decoration: underline;
  text-decoration-color: var(--color-link);
  text-underline-offset: var(--esp-1);
}

/* El anillo va en la fila, no en el texto: lo que se activa es la fila. */
.tabla__fila--link .tabla__link:focus-visible {
  outline: none;
}

.tabla__fila--link:has(.tabla__link:focus-visible) {
  outline: var(--borde-medio) solid var(--color-foco);
  outline-offset: calc(var(--borde-fino) * -2);
}

/* --- Cuerpo sin filas ---
   Solo el hueco. El componente .vacio completo es otro pedido. */
.tabla__vacio td {
  height: calc(var(--tabla-fila) * 4);
  padding: var(--esp-6);
  border-bottom: 0;
  color: var(--color-texto-suave);
  text-align: center;
  vertical-align: middle;
}

/* --- Esperando datos nuevos ---
   La tabla se atenúa y se queda quieta, con su alto y sus filas. No se
   vacía: vaciarla colapsa el alto, salta la página entera y el clic
   siguiente cae en otro lugar.

   Vive acá y no en §19 aunque lo prenda el paginador: es el estado ocupado
   de LA TABLA, y lo va a prender también el buscador y cualquier filtro.
   Un componente no estiliza a otro; declara su estado y el otro lo enciende. */
.tabla-caja[aria-busy="true"] {
  opacity: 0.55;
  cursor: progress;
  pointer-events: none;
  transition: opacity var(--transicion);
}

/* --- Angosto · la fila se reagrupa como tarjeta ---
   El corte se mide sobre la caja, no sobre la ventana. */
@container tabla (max-width: 34em) {
  .tabla--tarjetas thead {
    position: absolute;
    width: var(--borde-fino);
    height: var(--borde-fino);
    overflow: hidden;
    clip-path: inset(50%);
  }

  .tabla--tarjetas tbody tr {
    display: grid;
    padding: var(--esp-3) var(--esp-4);
    border-bottom: var(--borde-fino) solid var(--color-borde-sutil);
  }

  .tabla--tarjetas tbody tr:last-child {
    border-bottom: 0;
  }

  .tabla--tarjetas tbody td {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: baseline;
    gap: var(--esp-4);
    height: auto;
    padding: var(--esp-1) 0;
    border-bottom: 0;
    justify-items: end;   /* el chip no se estira: sigue siendo una píldora */
    text-align: end;
  }

  /* La etiqueta de columna sale del encabezado, escrita en data-etiqueta. */
  .tabla--tarjetas tbody td::before {
    content: attr(data-etiqueta);
    justify-self: start;
    color: var(--color-texto-suave);
    font-size: var(--texto-sm);
    text-align: start;
  }

  /* La celda que identifica la fila es el título de la tarjeta: ancho
     completo, sin etiqueta. */
  .tabla--tarjetas tbody .tabla__celda--titulo {
    grid-template-columns: minmax(0, 1fr);
    justify-items: start;
    padding-bottom: var(--esp-2);
    text-align: start;
  }

  .tabla--tarjetas tbody .tabla__celda--titulo::before {
    display: none;
  }

  .tabla--tarjetas tbody .tabla__acciones {
    width: auto;
    justify-items: start;
    padding-top: var(--esp-3);
  }

  .tabla--tarjetas tbody .tabla__acciones::before {
    display: none;
  }

  .tabla--tarjetas tbody .tabla__acciones-grupo {
    justify-content: flex-start;
  }

  /* El filete de selección pasa a la tarjeta entera. */
  .tabla--tarjetas tbody tr[aria-selected="true"] {
    box-shadow: inset var(--borde-grueso) 0 0 0 var(--color-acento);
  }

  .tabla--tarjetas tbody tr[aria-selected="true"] td:first-child {
    box-shadow: none;
  }
}


/* ==========================================================
   18 · ESTADO VACÍO
   No es un estado vacío: son tres. Sin datos todavía (primera
   vez), sin resultados (el filtro no encontró nada) y error al
   cargar. Cada uno explica qué pasó y ofrece SU salida: crear,
   limpiar el filtro, reintentar. La diferencia la hace el
   modificador; el bloque es siempre el mismo.

   No dibuja nada que ya exista: la acción va con .boton
   —primaria en sin-datos, secundaria en los otros dos— y el
   texto de apoyo con .nota. El icono va inlineado en el HTML
   (24×24, trazo 1.5, currentColor): dentro de un <img> el SVG
   se renderiza aislado y currentColor cae a negro.

   Dos contextos con una clase y un modificador: suelto en la
   página, o dentro del <td> de .tabla__vacio con --embebido,
   donde el alto disponible es fijo (cuatro filas de la densidad
   activa) y hay que entrar en él.
   ========================================================== */

.vacio {
  display: grid;
  justify-items: center;
  align-content: center;
  gap: var(--esp-3);
  padding: var(--esp-12) var(--esp-6);
  text-align: center;
}

.vacio__icono {
  width: var(--esp-6);
  height: var(--esp-6);
  color: var(--color-texto-suave);
}

/* 20px: por debajo de 32px el mínimo es peso 400, así que medium. */
.vacio__titulo {
  margin: 0;
  color: var(--color-texto-primario);
  font-size: var(--texto-xl);
  font-weight: var(--peso-medium);
}

/* La línea de apoyo corta antes que la prosa larga de la web: 528px, que es
   la medida de lectura dentro de un componente. Ver §0-a. */
.vacio .nota {
  max-width: var(--web-ancho-texto);
  margin: 0;
}

.vacio__acciones {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--esp-3);
  margin-top: var(--esp-3);
}

/* Segundo canal del error: el icono se tiñe, y la palabra ya está en el
   título. El color nunca va solo. */
.vacio--error .vacio__icono {
  color: var(--color-error);
}

/* --- Embebido en la tabla ---
   El <td> de .tabla__vacio ya centra y ya paga el padding, y su alto es
   un presupuesto cerrado: en compacta son 4×36px menos el padding. Ahí
   el icono es lo primero que sobra —es decorativo y aria-hidden, no
   dice nada que el título no diga— y los gaps bajan un escalón. */
.vacio--embebido {
  gap: var(--esp-2);
  padding: 0;
}

.vacio--embebido .vacio__icono {
  display: none;
}

.vacio--embebido .vacio__titulo {
  font-size: var(--texto-lg);
}

.vacio--embebido .vacio__acciones {
  margin-top: var(--esp-2);
}


/* ==========================================================
   19 · PAGINADOR
   Paginación por seek (keyset), no por offset: no existe el
   número de página, ni el total, ni el salto a la 7. Solo hay
   página anterior, página siguiente y cuántos ítems trajo esta.
   El componente no tiene dónde poner numeritos porque el
   backend no los emite.

   No dibuja nada que ya exista: los dos botones son
   .boton--secundario.boton--sm, el deshabilitado es
   .boton:disabled, el conteo es .nota y el giro de la espera es
   .giro dentro del botón apretado.

   El conteo es texto del servidor —«Mostrando 20 clientes»,
   «Mostrando 1 cliente»—: esta hoja solo lo ubica y lo baja de
   jerarquía. Ningún plural se resuelve en CSS.

   ⚠️ Composición que cambia por ancho, no por tema: en angosto
   el conteo pasa arriba y los dos botones quedan solos en su
   línea. El corte se mide con @container sobre el paginador,
   como en la tabla, porque debajo de una tabla de 500px en una
   pantalla de 1280 el que manda es el ancho de la tabla.
   ========================================================== */

.paginador {
  /* Igual que .tabla-caja: con inline-size el ancho no puede depender del
     contenido, así que sin el 100% colapsa a 0 dentro de un flex o un grid
     —y midiendo 0 la @container de angosto da verdadera siempre. */
  width: 100%;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--esp-3);
  container: paginador / inline-size;
  padding-block: var(--esp-3);
}

/* Secundaria la información: el conteo no compite con los botones, que son
   la única navegación que existe. El tamaño y el color los pone .nota. */
.paginador__conteo {
  flex: 1 1 auto;
  margin: 0;
  text-align: center;
}

.paginador__flecha {
  flex: 0 0 auto;
  width: var(--esp-4);
  height: var(--esp-4);
}

/* Segundo canal del deshabilitado: el borde pasa a punteado. .boton:disabled
   solo baja la opacidad, y una opacidad no es un canal —es el mismo color más
   flojo. Punteado es forma, y la palabra sigue siendo «Anterior».
   No aplica mientras el paginador espera: ahí los botones también están
   deshabilitados, pero por otro motivo y con su propio indicador. */
.paginador:not([aria-busy="true"]) .boton:disabled {
  border-style: dashed;
}

/* --- Cargando ---
   La paginación va por htmx: la tabla se reemplaza sin recargar la página.
   Entre el clic y la llegada hay una espera y tiene que verse.
   El atenuado de la tabla de atrás lo declara §17, que es de quién es ese
   estado; el paginador solo le prende el aria-busy. */

.paginador__cargando {
  display: none;
}

.paginador[aria-busy="true"] .paginador__cargando {
  display: block;
}

.paginador[aria-busy="true"] .paginador__conteo-texto {
  display: none;
}

/* --- Angosto ---
   Los dos botones y el conteo no entran en una línea cómoda: el conteo se
   va arriba a lo ancho y los botones quedan en su línea, cada uno contra su
   borde. No se apilan en columna: «Anterior» y «Siguiente» son opuestos y
   uno debajo del otro se leen como una lista de dos pasos.

   26em (416px): medido contra el conteo más largo que emite la app hoy,
   que es «Mostrando 20 observaciones» y no «Mostrando 20 clientes».
   Ver la ficha. */
@container paginador (max-width: 26em) {
  .paginador__conteo {
    order: -1;
    flex: 1 1 100%;
  }
}


/* ==========================================================
   20 · BUSCADOR
   Búsqueda en vivo por htmx con delay:300ms sobre cada tecla:
   no hay botón «Buscar» y el componente vive casi siempre en
   transición. Por eso el estado «buscando» no es un detalle:
   es el que más veces se ve.

   No dibuja nada que ya exista: el campo es .control con sus
   cuatro estados, el giro de la espera es .giro, el pie es
   .nota, los filtros son .control--auto, y la tabla de abajo
   se atenúa con .tabla-caja[aria-busy="true"], que vive en §17
   porque es el estado ocupado de la tabla.

   Dos huecos reservados para que nada salte mientras se
   escribe: el slot del icono a la izquierda —donde la lupa se
   reemplaza por el giro, misma medida, mismo lugar— y el
   renglón del pie, que mantiene su alto esté vacío o no. El
   campo no cambia de tamaño y el cursor no se mueve aunque el
   indicador aparezca y desaparezca cinco veces.

   ⚠️ Composición que cambia por ancho, no por tema: en angosto
   los filtros bajan a su propia línea. Sale del flex-wrap del
   grupo, sin consulta de ancho: envuelven cuando no entran.
   ========================================================== */

.buscador {
  display: grid;
  gap: var(--esp-2);
  width: 100%;
}

.buscador__campo {
  position: relative;
  display: flex;
}

/* El hueco del icono: --esp-4 de aire, 24px de icono, --esp-4 de aire. */
.buscador__input {
  padding-inline-start: calc(var(--esp-4) * 2 + var(--esp-6));
}

.buscador__lupa {
  position: absolute;
  inset-inline-start: var(--esp-4);
  top: 50%;
  translate: 0 -50%;
  width: var(--esp-6);
  height: var(--esp-6);
  color: var(--color-texto-suave);
  pointer-events: none;   /* el clic cae en el campo, no en el icono */
}

/* El giro ocupa el mismo slot que la lupa, centrado en sus 24px. */
.buscador__giro {
  position: absolute;
  inset-inline-start: calc(var(--esp-4) + var(--esp-1));
  top: 50%;
  margin-top: calc(var(--esp-2) * -1);
  pointer-events: none;
}

/* --- Buscando ---
   La lupa se reemplaza por el giro: no se agrega nada al final del campo,
   donde vive la x nativa, y no cambia ninguna medida. */
.buscador[aria-busy="true"] .buscador__lupa {
  display: none;
}

.buscador[aria-busy="true"] .buscador__giro {
  display: block;
}

/* Segundo canal: la palabra. El giro se puede no ver —es chico y gira—;
   «Buscando…» se lee y se anuncia. */
.buscador__cargando {
  display: none;
}

.buscador[aria-busy="true"] .buscador__cargando {
  display: block;
}

.buscador[aria-busy="true"] .buscador__texto {
  display: none;
}

/* El pie reserva su alto siempre: si apareciera y desapareciera, la tabla
   de abajo subiría y bajaría 20px en cada tecla. */
.buscador__pie {
  min-height: var(--esp-6);
  margin: 0;
}

/* Deshabilitado: el color lo pone .control:disabled; acá el icono acompaña
   y la palabra va en el pie, que dice cuándo se habilita. */
.buscador:has(.control:disabled) .buscador__lupa {
  opacity: 0.5;
}

/* --- Con filtros al lado ---
   Una fila: el buscador se estira, los filtros ocupan lo suyo. Los filtros
   son .control--auto, la variante que ya declara §16: acá solo se les da
   su lugar en la fila. */
.buscador-fila {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--esp-3);
  width: 100%;
}

/* 240px es el piso: por debajo, el placeholder no se lee entero. Medido
   contra «Buscar por nombre…», que es el más largo que usa la app hoy. */
.buscador-fila .buscador {
  flex: 1 1 calc(var(--esp-24) * 2.5);
}

.buscador-fila__filtro {
  flex: 0 1 auto;
  min-width: calc(var(--esp-24) * 1.5);
}


/* ==========================================================
   21 · ENCABEZADO DE PÁGINA
   La banda de arriba del contenido: dice dónde estoy y ofrece
   la acción principal de la pantalla. No es la topbar del
   shell —logo, menú, usuario—: esto vive dentro del área de
   contenido y cambia con cada pantalla.

   No dibuja nada que ya exista: el título es .titulo--pantalla,
   la bajada es .bajada y las acciones son .boton.

   El título va con el escalón de app (32px/500) y no con
   .display (44px/300), que viene de la web pública y arriba de
   una tabla densa se come el contenido: lo que el usuario vino
   a mirar son las filas.

   ⚠️ Composición que cambia por ancho, no por tema: en angosto
   las acciones bajan a su propia línea. Se mide con @container
   sobre el encabezado, no con el viewport, porque dentro del
   shell el área de contenido nunca tiene el ancho de la
   ventana.
   ========================================================== */

.encabezado {
  /* Igual que .tabla-caja y .paginador: con inline-size el ancho no puede
     depender del contenido, así que sin el 100% colapsa a 0. */
  width: 100%;
  display: grid;
  gap: var(--esp-3);
  container: encabezado / inline-size;
  padding-block-end: var(--esp-6);
}

/* El filete de marca, y está SIEMPRE. Es el mismo gesto que la viñeta de
   .lista y que el filete de la web pública: un tramo de la columna del
   isotipo. Lo nuevo es cuándo aparece — hasta ahora la salvia solo salía a
   decir un estado (columna ordenada, fila seleccionada, destino actual), así
   que una pantalla en reposo no tenía un solo gesto de Pensura.

   No se confunde con la salvia de estado, y por eso: las tres marcas de
   estado son inset box-shadow pegadas al borde de algo con lo que se
   interactúa. Esto es una regla suelta, arriba del título, en el único lugar
   de la pantalla donde no hay nada que apretar. */
.encabezado::before {
  content: "";
  width: var(--esp-8);
  height: var(--borde-grueso);
  background: var(--color-acento);
  border-radius: var(--radio-full);
}

.encabezado__fila {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--esp-4);
}

/* min-width: 0 es lo que permite que un título largo se parta en dos
   líneas en vez de empujar al botón fuera de la fila. */
.encabezado__texto {
  flex: 1 1 calc(var(--esp-24) * 2.5);
  min-width: 0;
  display: grid;
  gap: var(--esp-2);
}

.encabezado__titulo {
  margin: 0;
  text-wrap: balance;
}

/* La misma medida de lectura corta que usa .vacio: una explicación de 120
   caracteres en una línea de 1200px no se lee, se barre. */
.encabezado__bajada {
  max-width: var(--web-ancho-texto);
  margin: 0;
}

/* Las acciones no se achican y no parten su palabra: lo que cede es el
   título, que se parte en dos líneas. Un botón con la etiqueta cortada
   deja de decir qué hace. */
.encabezado__acciones {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: var(--esp-3);
}

.encabezado__acciones .boton {
  white-space: nowrap;
}

/* --- El dato dentro del título ---
   El nombre de la entidad es parte del h1: misma medida, mismo peso. Si
   además es link a su ficha, lo único que cambia es el subrayado —el
   canal que no depende del color— y en hover suma el color de link. Si
   cambiara de tamaño o de peso, se leería como un segundo título. */
.encabezado__dato {
  color: inherit;
  text-decoration: underline;
  text-decoration-color: var(--color-borde-control);
  text-decoration-thickness: var(--borde-fino);
  text-underline-offset: var(--esp-1);
}

.encabezado__dato:hover {
  color: var(--color-link);
  text-decoration-color: var(--color-link);
}

/* --- Angosto ---
   El título y la acción no entran en una línea cómoda: las acciones bajan
   enteras a la suya, alineadas al comienzo. No se estiran a lo ancho: un
   botón de 100% se lee como el envío de un formulario, no como la acción
   de una pantalla. */
@container encabezado (max-width: 30em) {
  .encabezado__acciones {
    flex: 1 1 100%;
  }
}


/* ==========================================================
   22 · SHELL · el marco de la app
   Lo que rodea a toda pantalla: dónde estoy parado, en qué
   empresa, y quién soy. No es contenido: es el mueble.

   Sale de la maqueta «Pensura Empresa», que resolvió dos
   preguntas que PEDIDOS.md dejó abiertas:

   1. La sidebar oscura en ambos temas era una decisión de la
      etapa Pico. No sigue: el marco usa --color-superficie y
      acompaña al tema, como cualquier otra superficie.
   2. En ancho la navegación es horizontal, en la barra de
      arriba. No hay sidebar. Una columna fija de 250px al lado
      de una tabla densa es espacio que la tabla necesita, y la
      app no tiene tantos destinos como para justificarla.

   En angosto la navegación baja a una barra de tabs abajo, al
   alcance del pulgar, y la barra de arriba se queda solo con
   la identidad y la cuenta.

   No dibuja nada que ya exista: el logotipo es un <img> de la
   plataforma de marca, el selector de empresa es .selector, la
   cuenta es .menu-usuario y el contenido lo maqueta cada
   pantalla con .encabezado y los suyos.
   ========================================================== */

.shell {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  /* `dvh` descuenta la barra del navegador en mobile: sin esto lo anclado
     abajo queda tapado hasta que se scrollea. */
  min-height: 100dvh;
  background: var(--color-fondo-alt);
}

/* --- Barra de arriba ---
   Alto fijo y pegada: es el ancla de la pantalla. Lo que cambia entre ancho
   y angosto es QUÉ lleva adentro, no la barra. */
.shell__barra {
  position: sticky;
  top: 0;
  z-index: 20;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  gap: var(--esp-6);
  min-height: var(--control-lg);
  padding: var(--esp-2) var(--esp-6);
  background: var(--color-superficie);
  border-bottom: var(--borde-fino) solid var(--color-borde-sutil);
}

.shell__marca {
  display: flex;
  align-items: center;
  flex: 0 0 auto;
}

/* El lockup horizontal en ancho; en angosto lo reemplaza el isotipo, que es
   la misma marca en el espacio que hay. Las dos versiones de cada uno —para
   fondo claro y para fondo oscuro— van en el HTML y el CSS elige: el tema se
   resuelve en el cliente, así que el servidor no puede mandar una sola. */
.shell__logo {
  height: var(--esp-6);
  width: auto;
}

.shell__logo--claro { display: var(--web-en-claro); }
.shell__logo--oscuro { display: var(--web-en-oscuro); }

.shell__marca--ancho { display: flex; }
.shell__marca--angosto { display: none; }

/* --- Navegación en ancho ---
   Los destinos, en la barra. El actual se marca por DOS canales: el peso de
   la palabra y el filete de acento abajo. Solo con color no alcanza. */
.shell__nav {
  display: flex;
  align-items: center;
  gap: var(--esp-1);
  min-width: 0;
}

.shell__tab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--esp-2);
  min-height: var(--control-sm);
  padding: 0 var(--esp-3);
  border: 0;
  border-radius: var(--radio-md);
  background: transparent;
  color: var(--color-texto-suave);
  font-family: var(--font-marca);
  font-size: var(--texto-md);
  font-weight: var(--peso-regular);
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--transicion), color var(--transicion);
}

.shell__tab:hover {
  background: var(--color-superficie-hover);
  color: var(--color-texto-primario);
}

.shell__tab[aria-current] {
  background: var(--color-superficie-hover);
  color: var(--color-texto-primario);
  font-weight: var(--peso-semibold);
  box-shadow: inset 0 -2px 0 0 var(--color-acento);
}

/* El ícono solo se ve en la barra de abajo. En ancho la palabra alcanza y el
   ícono duplicaría el mensaje. */
.shell__tab-icono { display: none; }

/* La cuenta se va al extremo opuesto de la marca. */
.shell__cuenta {
  margin-inline-start: auto;
  display: flex;
  align-items: center;
  gap: var(--esp-2);
}

/* Un atajo de la barra: el destino frecuente que no es una tab y que no vale
   esconder adentro del menú de cuenta —abrirlo y buscar el ítem son dos pasos
   para algo que entra como un ícono—.

   Va solo con el ícono y sin borde: al lado del avatar, que sí lo tiene, dos
   piezas encerradas competirían y la barra dejaría de leerse como identidad.
   El nombre no se pierde: va en `title` y en `aria-label`, como en cualquier
   control de solo ícono del sistema. Comparte la caja del avatar
   (--control-sm) para que los dos apoyen en la misma línea. */
.shell__accion {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: var(--control-sm);
  height: var(--control-sm);
  border-radius: var(--radio-full);
  background: transparent;
  color: var(--color-texto-suave);
  cursor: pointer;
  transition: background-color var(--transicion), color var(--transicion);
}

.shell__accion svg {
  width: var(--esp-4);
  height: var(--esp-4);
}

.shell__accion:hover {
  background: var(--color-superficie-hover);
  color: var(--color-texto-primario);
}

/* --- Área de contenido --- */
.shell__contenido {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  padding: var(--esp-6) var(--esp-8) var(--esp-12);
}

/* --- Barra de tabs, solo en angosto ---
   Anclada abajo y SIEMPRE visible: es la única navegación que queda cuando
   la barra de arriba se reduce. El composer se esconde al scrollear; esto no. */
.shell__tabs {
  display: none;
  position: sticky;
  bottom: 0;
  z-index: 20;
  flex: 0 0 auto;
  background: var(--color-superficie);
  border-top: var(--borde-fino) solid var(--color-borde-sutil);
  /* La franja del gesto de iOS: sin esto el último tab queda debajo de ella. */
  padding-bottom: env(safe-area-inset-bottom, 0);
}

.shell__tabs .shell__tab {
  flex: 1 1 0;
  flex-direction: column;
  gap: var(--esp-1);
  min-height: var(--esp-16);
  border-radius: 0;
  font-size: var(--texto-2xs);
  font-weight: var(--peso-medium);
}

.shell__tabs .shell__tab[aria-current] {
  background: transparent;
  box-shadow: inset 0 2px 0 0 var(--color-acento);
}

.shell__tabs .shell__tab-icono {
  display: block;
  width: var(--esp-6);
  height: var(--esp-6);
}

/* La cuenta pendiente de cada destino. Va sobre el ícono y lleva número: un
   punto de color solo diría «algo pasó» y no cuánto. */
.shell__tab-badge {
  position: absolute;
  margin-inline-start: var(--esp-6);
  margin-block-start: calc(var(--esp-2) * -1);
  min-width: var(--esp-4);
  height: var(--esp-4);
  padding: 0 var(--esp-1);
  border-radius: var(--radio-full);
  background: var(--color-error);
  color: var(--color-texto-invertido);
  font-size: var(--texto-2xs);
  font-weight: var(--peso-medium);
  line-height: var(--esp-4);
  text-align: center;
}

/* --- Angosto ---
   La navegación baja; arriba quedan la identidad y la cuenta. El corte va en
   ancho de ventana y no con @container: el shell ES la ventana. */
@media (max-width: 48em) {
  .shell__barra {
    gap: var(--esp-3);
    padding-inline: var(--esp-4);
  }

  .shell__marca--ancho { display: none; }
  .shell__marca--angosto { display: flex; }

  .shell__nav {
    display: none;
  }

  .shell__tabs {
    display: flex;
  }

  .shell__contenido {
    padding: var(--esp-4) var(--esp-4) var(--esp-8);
  }
}


/* ==========================================================
   23 · SELECTOR DE EMPRESA
   Multiempresa: quién factura. Es lo primero que se mira al
   entrar y lo que hay que poder cambiar sin pensar.

   Va sobre <details> nativo, como .objeciones: se abre y se
   cierra sin JavaScript, y con Escape se cierra solo. El
   buscador de adentro sí necesita JS, y aparece recién cuando
   hay suficientes empresas como para que buscar sea más rápido
   que mirar.
   ========================================================== */

.selector {
  position: relative;
}

.selector > summary {
  display: flex;
  align-items: center;
  gap: var(--esp-2);
  min-height: var(--control-sm);
  padding: 0 var(--esp-2) 0 var(--esp-1);
  border: var(--borde-fino) solid var(--color-borde-sutil);
  border-radius: var(--radio-md);
  background: var(--color-fondo-alt);
  color: var(--color-texto-primario);
  font-size: var(--texto-md);
  font-weight: var(--peso-medium);
  cursor: pointer;
  list-style: none;
}

.selector > summary::-webkit-details-marker { display: none; }

.selector > summary:hover,
.selector[open] > summary {
  border-color: var(--color-borde-control);
}

/* Las iniciales. El acento de marca de fondo y el par que el sistema declara
   para texto sobre el acento: es oscuro en los dos temas, porque el acento es
   claro en los dos. */
.selector__sigla {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: var(--esp-6);
  height: var(--esp-6);
  border-radius: var(--radio-sm);
  background: var(--color-acento);
  color: var(--web-sobre-acento-texto);
  font-size: var(--texto-2xs);
  font-weight: var(--peso-semibold);
  line-height: 1;
}

.selector__nombre {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.selector__chevron {
  flex: 0 0 auto;
  width: var(--esp-4);
  height: var(--esp-4);
  color: var(--color-texto-suave);
}

.selector[open] .selector__chevron {
  rotate: 180deg;
}

/* --- El panel --- */
.selector__panel {
  position: absolute;
  z-index: 30;
  top: calc(100% + var(--esp-1));
  inset-inline-start: 0;
  width: max(100%, calc(var(--esp-24) * 3.3));
  max-height: min(60vh, calc(var(--esp-24) * 4));
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: var(--esp-1);
  background: var(--color-superficie);
  border: var(--borde-fino) solid var(--color-borde-sutil);
  border-radius: var(--radio-md);
  box-shadow: var(--sombra-lg);
}

/* Pegado arriba de la lista que scrollea, con el fondo del panel para que las
   filas pasen por debajo sin verse. */
.selector__buscar {
  position: sticky;
  top: calc(var(--esp-1) * -1);
  z-index: 1;
  margin: calc(var(--esp-1) * -1) calc(var(--esp-1) * -1) var(--esp-1);
  padding: var(--esp-1);
  background: var(--color-superficie);
  border-bottom: var(--borde-fino) solid var(--color-borde-sutil);
}

.selector__opcion {
  display: flex;
  align-items: center;
  gap: var(--esp-2);
  width: 100%;
  min-height: var(--fila-default);
  padding: var(--esp-1) var(--esp-2);
  border: 0;
  border-radius: var(--radio-sm);
  background: none;
  color: var(--color-texto-primario);
  font-family: var(--font-marca);
  font-size: var(--texto-md);
  text-align: start;
  text-decoration: none;
  cursor: pointer;
}

.selector__opcion:hover {
  background: var(--color-superficie-hover);
}

.selector__datos {
  display: grid;
  gap: 1px;
  min-width: 0;
}

/* La empresa activa se marca con el tilde, no con el fondo: el fondo ya es el
   hover, y dos cosas con el mismo color se confunden. */
.selector__activa {
  margin-inline-start: auto;
  flex: 0 0 auto;
  width: var(--esp-4);
  height: var(--esp-4);
  color: var(--color-exito);
}

.selector__vacio {
  padding: var(--esp-3) var(--esp-2);
  color: var(--color-texto-suave);
  font-size: var(--texto-sm);
}


/* ==========================================================
   24 · MENÚ DE USUARIO
   Quién soy y lo que se hace con la cuenta. Mismo <details>
   que el selector; lo que cambia es que se abre desde el borde
   opuesto y que la última acción es de salida.
   ========================================================== */

.menu-cuenta {
  position: relative;
}

.menu-cuenta > summary {
  display: grid;
  place-items: center;
  width: var(--control-sm);
  height: var(--control-sm);
  border: var(--borde-fino) solid var(--color-borde-sutil);
  border-radius: var(--radio-full);
  background: var(--color-fondo-alt);
  color: var(--color-texto-suave);
  font-size: var(--texto-sm);
  font-weight: var(--peso-medium);
  cursor: pointer;
  list-style: none;
}

.menu-cuenta > summary::-webkit-details-marker { display: none; }

.menu-cuenta > summary:hover {
  border-color: var(--color-borde-control);
  color: var(--color-texto-primario);
}

.menu-cuenta__panel {
  position: absolute;
  z-index: 30;
  top: calc(100% + var(--esp-1));
  inset-inline-end: 0;
  width: calc(var(--esp-24) * 2.7);
  padding: var(--esp-1);
  background: var(--color-superficie);
  border: var(--borde-fino) solid var(--color-borde-sutil);
  border-radius: var(--radio-md);
  box-shadow: var(--sombra-lg);
}

/* La identidad arriba del todo: el menú confirma en qué cuenta se está antes
   de ofrecer nada. En multiempresa equivocarse de cuenta es caro. */
.menu-cuenta__quien {
  display: flex;
  align-items: center;
  gap: var(--esp-3);
  padding: var(--esp-2);
}

.menu-cuenta__avatar {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: var(--esp-8);
  height: var(--esp-8);
  border-radius: var(--radio-full);
  border: var(--borde-fino) solid var(--color-borde-sutil);
  background: var(--color-fondo-alt);
  color: var(--color-texto-suave);
  font-size: var(--texto-sm);
  font-weight: var(--peso-medium);
  line-height: 1;
}

.menu-cuenta__datos {
  display: grid;
  gap: 1px;
  min-width: 0;
}

.menu-cuenta__datos strong,
.menu-cuenta__datos span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.menu-cuenta__datos strong {
  font-size: var(--texto-md);
  font-weight: var(--peso-medium);
}

.menu-cuenta__separador {
  height: var(--borde-fino);
  margin: var(--esp-1) var(--esp-2);
  background: var(--color-borde-sutil);
  border: 0;
}

.menu-cuenta__item {
  display: flex;
  align-items: center;
  gap: var(--esp-2);
  width: 100%;
  min-height: var(--fila-default);
  padding: 0 var(--esp-2);
  border: 0;
  border-radius: var(--radio-sm);
  background: none;
  color: var(--color-texto-primario);
  font-family: var(--font-marca);
  font-size: var(--texto-md);
  text-align: start;
  text-decoration: none;
  cursor: pointer;
}

.menu-cuenta__item:hover {
  background: var(--color-superficie-hover);
}

/* Cerrar sesión sale del flujo: baja de jerarquía y va después del separador.
   No es destructiva —no borra nada— así que no se pinta de rojo; el rojo se
   reserva para lo que no tiene vuelta atrás. */
.menu-cuenta__item--salir {
  color: var(--color-texto-suave);
}

/* Cerrar clickeando afuera, sin JS. Un <details> abierto solo se cierra
   volviendo a clickear su summary: clickear en cualquier otro lado lo deja
   abierto tapando la pantalla, que no es lo que se espera de un menú.

   El truco: con el menú abierto, el summary extiende un velo invisible sobre
   toda la pantalla. Clickear ahí ES clickear el summary, y eso cierra el
   <details>. El velo va por debajo del panel (z-index 30), así que los clicks
   de adentro del menú siguen llegando a donde tienen que llegar.

   Contrapartida conocida: ese primer click se consume en cerrar y no llega a
   lo que haya debajo —incluido el summary del otro menú, que cierra este y
   pide un segundo click para abrirse—. Es como se portan los menús de
   siempre, y es el precio de no poner JS a manejar el foco y el click.

   Vale para los dos desplegables de la barra, que son el mismo patrón. */
.selector[open] > summary::before,
.menu-cuenta[open] > summary::before {
  content: "";
  position: fixed;
  z-index: 10;
  inset: 0;
  cursor: default;
}


/* ==========================================================
   25 · BADGE GENÉRICO
   El estado de cualquier cosa que NO sea una factura: una
   corrida, una lista de precios, una credencial, una membresía.

   ⚠️ Tiene que distinguirse de .chip-estado a simple vista, o
   en una pantalla con los dos se leen como lo mismo. La
   diferencia es de FORMA, no de color: el chip se rellena, el
   badge se contornea y lleva un punto. Un chip y un badge del
   mismo color siguen siendo dos cosas distintas y se nota sin
   leerlos.

   El punto repite el estado en un segundo canal junto con la
   palabra, así que el color nunca va solo. Es del mismo tono
   que el borde: no agrega información, refuerza.
   ========================================================== */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--esp-2);
  padding: var(--esp-1) var(--esp-3);
  border: var(--borde-fino) solid currentColor;
  border-radius: var(--radio-full);
  background: transparent;
  font-size: var(--texto-xs);
  font-weight: var(--peso-medium);
  white-space: nowrap;
}

.badge__punto {
  width: var(--esp-2);
  height: var(--esp-2);
  flex: 0 0 auto;
  border-radius: var(--radio-full);
  background: currentColor;
}

.badge--ok        { color: var(--color-exito); }
.badge--error     { color: var(--color-error); }
.badge--pendiente { color: var(--estado-porvencer-texto); }
.badge--neutro    { color: var(--color-texto-suave); }


/* ==========================================================
   26 · BOTÓN PELIGRO
   Variante de .boton para lo que no tiene vuelta atrás:
   eliminar, revocar, anular.

   Es SECUNDARIO en peso, no primario en rojo. Un botón rojo
   lleno compite por la atención con la acción principal de la
   pantalla y termina siendo lo primero que se toca; acá el rojo
   está en el borde y en la palabra, y el relleno recién aparece
   en el hover, cuando ya hay intención.
   ========================================================== */

.boton--peligro {
  background: transparent;
  border-color: var(--color-error);
  color: var(--color-error);
}

.boton--peligro:hover {
  background: var(--color-error);
  color: var(--color-texto-invertido);
  border-color: var(--color-error);
}

.boton--peligro:active {
  background: var(--color-error);
}


/* ==========================================================
   27 · MÉTRICA
   El número grande del dashboard: facturado, recaudado, saldo
   vencido. Tres datos que se leen de un vistazo antes de bajar
   a nada.

   No dibuja nada que ya exista: el número es .dato-monto, la
   etiqueta y el detalle son .nota. Lo que aporta la métrica es
   el ORDEN de esas tres cosas, que es lo que la hace legible:
   qué es, cuánto, y de qué está hecho.

   Ese tercer renglón no es decoración. «$ 8.412.400» sin «31
   facturas emitidas · 4 pendientes» es un número que no se
   puede verificar ni accionar.
   ========================================================== */

.metricas {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(calc(var(--esp-24) * 2), 1fr));
  gap: var(--esp-3);
}

.metrica {
  display: grid;
  gap: var(--esp-2);
  min-width: 0;
  padding: var(--esp-4) var(--esp-6);
  background: var(--color-superficie);
  border: var(--borde-fino) solid var(--color-borde-sutil);
  border-radius: var(--radio-md);
}

/* Alineado a la izquierda, no a la derecha como en una tabla: acá no hay
   columna con la que alinear, y el número arranca donde arranca su etiqueta. */
.metrica__valor {
  font-size: var(--texto-3xl);
  text-align: start;
  white-space: nowrap;
}

/* La métrica que duele. El color va en el número, no en la caja: teñir la caja
   entera la convierte en una alerta y esto sigue siendo un dato. */
.metrica--alerta .metrica__valor {
  color: var(--color-error);
}


/* ==========================================================
   28 · ANOMALÍA
   «Requiere tu atención»: algo se salió de su patrón. Un cliente
   que se atrasó por primera vez en un año, una facturación que
   cayó contra su promedio, un abono que este mes no se emitió.

   Dos variantes explícitas y no un acordeón cualquiera:

   - COMPACTA es el default de la lista. Titular y nada más.
   - EXPANDIDA es la de mayor severidad, abierta de entrada.

   Abrir todas seria una pared de texto y no leerse ninguna;
   abrir ninguna obliga a un clic para saber si importa. La
   severidad decide, y es del servidor: el CSS solo la viste.

   El punto de la izquierda repite la severidad en un segundo
   canal junto con el kicker, así el color no va solo.
   ========================================================== */

.anomalias {
  display: grid;
  gap: var(--esp-2);
}

.anomalia {
  background: var(--color-superficie);
  border: var(--borde-fino) solid var(--color-borde-sutil);
  border-radius: var(--radio-md);
  box-shadow: var(--sombra-sm);
}

/* La de mayor severidad se marca en el borde y en la sombra, no rellenando la
   tarjeta: el relleno de color es del chip de estado y confundirlos cuesta. */
.anomalia--alta {
  border-color: var(--color-error);
  box-shadow: var(--sombra-md);
}

/* Sirve como <button> y como <summary>: la anomalía se despliega sin JavaScript. */
.anomalia__titular {
  display: flex;
  align-items: flex-start;
  gap: var(--esp-3);
  width: 100%;
  padding: var(--esp-4);
  border: 0;
  background: none;
  color: inherit;
  font-family: var(--font-marca);
  text-align: start;
  cursor: pointer;
  list-style: none;
}

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

.anomalia__punto {
  flex: 0 0 auto;
  width: var(--esp-2);
  height: var(--esp-2);
  margin-top: var(--esp-2);
  border-radius: var(--radio-full);
  background: var(--color-atencion);
}

.anomalia--alta .anomalia__punto {
  background: var(--color-error);
}

.anomalia__texto {
  display: grid;
  gap: var(--esp-1);
  min-width: 0;
}

.anomalia--alta .anomalia__texto .kicker {
  color: var(--color-error);
}

.anomalia__titulo {
  font-size: var(--texto-md);
  font-weight: var(--peso-medium);
  line-height: var(--interlinea-titulo);
}

.anomalia__chevron {
  flex: 0 0 auto;
  width: var(--esp-4);
  height: var(--esp-4);
  margin-inline-start: auto;
  color: var(--color-texto-suave);
  transition: rotate var(--transicion);
}

.anomalia[open] .anomalia__chevron,
.anomalia__titular[aria-expanded="true"] .anomalia__chevron {
  rotate: 180deg;
}

.anomalia__detalle {
  display: grid;
  gap: var(--esp-3);
  padding: 0 var(--esp-4) var(--esp-4);
}

.anomalia__detalle p {
  max-width: 62ch;
  color: var(--color-texto-lectura);
}

.anomalia__acciones {
  display: flex;
  flex-wrap: wrap;
  gap: var(--esp-2);
}

/* --- La chispa ---
   Seis barras: los cinco periodos anteriores y este. No es un gráfico —no lleva
   ejes ni números— sino la forma del patrón contra el que la anomalía se
   detectó. La última barra se tiñe porque es la que se salió; las otras son
   contexto y van en el color del borde.

   El dato exacto va en el renglón de abajo, en .dato-monto. La chispa no lo
   reemplaza: lo hace entendible. */
.spark {
  display: flex;
  align-items: flex-end;
  gap: var(--esp-1);
  height: var(--esp-12);
}

.spark__barra {
  flex: 1;
  min-height: 2px;
  border-radius: 2px;
  background: var(--color-borde-sutil);
}

.spark__barra--foco {
  background: var(--color-error);
}

/* En ancho el detalle y la chispa comparten renglón: la chispa a la derecha,
   del alto del bloque de texto. En angosto se apilan. */
@media (min-width: 48em) {
  .anomalia__detalle {
    grid-template-columns: 1fr calc(var(--esp-24) * 2.3);
    align-items: end;
    gap: var(--esp-6);
    padding-inline: var(--esp-6);
  }

  .anomalia__detalle > .spark {
    height: var(--esp-16);
  }
}


/* ==========================================================
   29 · ÍTEM DE BANDEJA
   Todo lo que pasó y hay que ver: una anomalía detectada, algo
   que el asistente dejó, un aviso que se le mandó al cliente,
   un ticket con soporte, un comunicado de Pensura.

   Cinco orígenes × tres estados. El ORIGEN se dice con el icono
   y el kicker; el ESTADO, con el chip. Son dos ejes distintos y
   por eso son dos marcas distintas: un ticket puede requerir
   acción o ser informativo, y una anomalía también.

   Lo que decide si algo requiere acción es el servidor. Acá el
   único estado que cambia la forma es «descartado», que baja
   todo el ítem de intensidad sin ocultarlo: descartar no es
   borrar, y tiene que poder revisarse después.
   ========================================================== */

.bandeja {
  display: grid;
  gap: var(--esp-2);
}

.bandeja-item {
  background: var(--color-superficie);
  border: var(--borde-fino) solid var(--color-borde-sutil);
  border-radius: var(--radio-md);
}

.bandeja-item--accion {
  border-color: var(--color-borde-control);
}

.bandeja-item--descartado {
  opacity: 0.6;
}

/* Sirve como <button> y como <summary> de un <details>: el hilo se abre sin JavaScript, igual
   que el acordeón de objeciones. De ahí el `list-style` —el triangulito por defecto— que hay
   que sacar en los dos motores. */
.bandeja-item__titular {
  display: flex;
  align-items: center;
  gap: var(--esp-3);
  width: 100%;
  padding: var(--esp-4);
  border: 0;
  background: none;
  color: inherit;
  font-family: var(--font-marca);
  text-align: start;
  cursor: pointer;
  list-style: none;
}

.bandeja-item__titular::-webkit-details-marker {
  display: none;
}

.bandeja-item__icono {
  display: grid;
  place-items: center;
  flex: 0 0 auto;
  width: var(--esp-8);
  height: var(--esp-8);
  border-radius: var(--radio-sm);
  background: var(--color-fondo-alt);
}

.bandeja-item__icono svg {
  width: var(--esp-4);
  height: var(--esp-4);
}

/* El color del icono dice el origen. Nunca va solo: al lado está el kicker con
   la palabra, que es lo que se lee. */
.bandeja-item--anomalia    .bandeja-item__icono { color: var(--color-error); }
.bandeja-item--asistente   .bandeja-item__icono { color: var(--color-link); }
.bandeja-item--comunicacion .bandeja-item__icono { color: var(--color-exito); }
.bandeja-item--ticket      .bandeja-item__icono { color: var(--color-texto-primario); }
.bandeja-item--comunicado  .bandeja-item__icono { color: var(--color-acento); }

.bandeja-item__texto {
  display: grid;
  gap: var(--esp-1);
  min-width: 0;
}

.bandeja-item__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--esp-2);
}

.bandeja-item__titulo {
  font-size: var(--texto-md);
  font-weight: var(--peso-medium);
  line-height: var(--interlinea-titulo);
}

/* Sin leer: el título en semibold. No un punto de color al costado, que en una
   lista larga se pierde, ni el fondo teñido, que es del estado. */
.bandeja-item--sin-leer .bandeja-item__titulo {
  font-weight: var(--peso-semibold);
}

.bandeja-item__extracto {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bandeja-item__cuando {
  margin-inline-start: auto;
  flex: 0 0 auto;
  white-space: nowrap;
}

/* --- El hilo ---
   Lo que se dijo, en orden. Va sobre el fondo alterno para que se lea como
   «adentro» del ítem y no como otro ítem de la lista. */
.bandeja-item__hilo {
  display: grid;
  gap: var(--esp-3);
  padding: var(--esp-4);
  background: var(--color-fondo-alt);
  border-top: var(--borde-fino) solid var(--color-borde-sutil);
}

.bandeja-item__mensaje {
  display: grid;
  gap: 2px;
}

.bandeja-item__mensaje span:last-child {
  color: var(--color-texto-lectura);
}


/* ==========================================================
   30 · COMPOSER
   La barra para preguntarle al asistente. Está en TODA pantalla,
   no en una sección propia: preguntar no es un destino al que
   se va, es algo que se hace mientras se mira otra cosa.

   Por eso no hay pantalla de bienvenida del asistente ni hilo
   vacío con sugerencias: el placeholder es la única invitación
   que hace falta, y al enviar la pantalla pasa al hilo.

   Se esconde al scrollear hacia abajo y vuelve al subir. La
   barra de tabs, en cambio, nunca se va: si se fueran las dos,
   no quedaría forma de moverse.
   ========================================================== */

/* Anclado abajo: está siempre disponible, no al final de la página. Va pegado con `sticky`
   y no `fixed` para que siga siendo parte del flujo del shell —así no tapa el último renglón
   del contenido ni hay que reservarle lugar con un padding que después queda desactualizado. */
.composer {
  position: sticky;
  bottom: 0;
  z-index: 15;
  display: flex;
  align-items: center;
  gap: var(--esp-2);
  padding: var(--esp-3) var(--esp-4);
  background: var(--color-fondo);
  border-top: var(--borde-fino) solid var(--color-borde-sutil);
  transition: translate var(--transicion), opacity var(--transicion);
}

/* En angosto se apoya arriba de la barra de tabs, que ocupa los 64px de abajo. */
@media (max-width: 48em) {
  .composer {
    bottom: var(--esp-16);
  }
}

/* La píldora: es el único control de la app que la usa, y se la gana porque no
   está en un formulario —no tiene etiqueta, ni error, ni vecinos con los que
   alinear—. Es una sola cosa, siempre disponible. */
.composer .control {
  border-radius: var(--radio-full);
  padding-inline: var(--esp-6);
}

.composer__enviar {
  flex: 0 0 auto;
  border-radius: var(--radio-full);
}

.composer__enviar svg {
  width: var(--esp-6);
  height: var(--esp-6);
}

/* En angosto el botón se reduce al icono: la palabra «Preguntar» se come el
   ancho que el campo necesita para mostrar lo que se está escribiendo. La
   palabra no se borra —se esconde a la vista— así el botón sigue teniendo
   nombre para un lector de pantalla. */
@media (max-width: 48em) {
  .composer__enviar-texto {
    position: absolute;
    width: var(--borde-fino);
    height: var(--borde-fino);
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}

.composer--oculto {
  translate: 0 120%;
  opacity: 0;
}

/* En el hilo del asistente el composer no se esconde: ahí escribir es lo que se está
   haciendo, y que se vaya al scrollear el historial obliga a volver arriba para seguir
   preguntando. Es el mismo componente y el mismo lugar; lo único que cambia es que se queda. */
.composer--fijo {
  translate: none;
  opacity: 1;
}


/* ==========================================================
   31 · BURBUJA
   El hilo con el asistente. Un solo hilo, siempre el mismo: no
   hay lista de conversaciones que administrar.

   La pregunta va a la derecha y la respuesta a la izquierda,
   que es la convención de cualquier mensajería y no hace falta
   explicarla. Lo que sí importa: la respuesta NO lleva avatar
   ni nombre de personaje. El asistente no es un personaje, es
   Pensura.
   ========================================================== */

.hilo {
  display: grid;
  gap: var(--esp-4);
}

.burbuja {
  max-width: 85%;
  padding: var(--esp-3) var(--esp-4);
  border-radius: var(--radio-lg);
  font-size: var(--texto-md);
  line-height: var(--interlinea-cuerpo);
}

.burbuja--pregunta {
  justify-self: end;
  background: var(--estado-emitida-fondo);
  color: var(--estado-emitida-texto);
  border-end-end-radius: var(--radio-sm);
}

.burbuja--respuesta {
  justify-self: start;
  background: var(--color-superficie);
  border: var(--borde-fino) solid var(--color-borde-sutil);
  border-end-start-radius: var(--radio-sm);
}

/* Lo que el asistente entrega —un reporte, un resumen— va como adjunto y no
   como texto: es un objeto que se abre y que además queda en la bandeja. */
.burbuja__adjunto {
  display: flex;
  align-items: center;
  gap: var(--esp-3);
  margin-top: var(--esp-3);
  padding: var(--esp-3);
  background: var(--color-fondo);
  border: var(--borde-fino) solid var(--color-borde-sutil);
  border-radius: var(--radio-md);
}

.burbuja__adjunto svg {
  flex: 0 0 auto;
  width: var(--esp-6);
  height: var(--esp-6);
  color: var(--color-link);
}

.burbuja__adjunto-datos {
  display: grid;
  gap: 1px;
  min-width: 0;
}

/* «Te lo dejé también en tu bandeja». Es la línea que hace que el chat no sea
   un agujero negro: todo lo que entrega queda en algún lado. */
.burbuja__eco {
  display: flex;
  align-items: center;
  gap: var(--esp-2);
  margin-top: var(--esp-3);
  color: var(--color-exito);
  font-size: var(--texto-sm);
}

.burbuja__eco svg {
  flex: 0 0 auto;
  width: var(--esp-4);
  height: var(--esp-4);
}

/* Cuando hace falta que lo mire una persona. Va con el relleno de «por vencer»
   —lo mismo que un vencimiento próximo— porque es exactamente eso: algo que
   quedó pendiente de otro y tiene fecha. */
.burbuja__ticket {
  display: grid;
  gap: var(--esp-2);
  margin-top: var(--esp-3);
  padding: var(--esp-3);
  border-radius: var(--radio-md);
  background: var(--estado-porvencer-fondo);
  color: var(--estado-porvencer-texto);
  font-size: var(--texto-sm);
}

/* --- Llegando ---
   La respuesta se escribe por streaming. El cursor marca que todavía no
   terminó: sin él, una respuesta a medias se lee como una respuesta corta. */
.burbuja--llegando::after {
  content: "";
  display: inline-block;
  width: 2px;
  height: 1em;
  margin-inline-start: 2px;
  vertical-align: text-bottom;
  background: currentColor;
  animation: burbuja-cursor 1s steps(2) infinite;
}

@keyframes burbuja-cursor {
  50% { opacity: 0; }
}

/* --- Pensando ---
   Antes del primer caracter no hay nada que mostrar, y un hueco vacío se lee
   como que se colgó. Tres puntos y la palabra: el movimiento solo no dice
   cuánto falta, pero dice que sigue vivo. */
.burbuja--pensando {
  display: flex;
  align-items: center;
  gap: var(--esp-2);
  color: var(--color-texto-suave);
}

.burbuja__punto {
  width: var(--esp-1);
  height: var(--esp-1);
  border-radius: var(--radio-full);
  background: currentColor;
  animation: burbuja-pensando 1.2s ease-in-out infinite;
}

.burbuja__punto:nth-child(2) { animation-delay: 0.15s; }
.burbuja__punto:nth-child(3) { animation-delay: 0.3s; }

@keyframes burbuja-pensando {
  0%, 60%, 100% { opacity: 0.3; }
  30%           { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .burbuja--llegando::after,
  .burbuja__punto {
    animation: none;
  }
}


/* ==========================================================
   32 · LISTA DE ENLACES
   «Acá hay un grupo de N enlaces relacionados, elegí uno.» Seis
   grupos en la pantalla de configuración de facturación, más los
   accesos internos de empresas y usuarios.

   No es una superficie: vive DENTRO de una .pieza y no lleva
   fondo, borde, radio ni sombra propios. La caja ya la puso la
   .pieza que la contiene, y anidar dos radios es lo que hace que
   una pantalla de configuración parezca un formulario dentro de
   un formulario.

   No dibuja nada que ya exista: el fondo del hover es el mismo
   --color-superficie-hover de .tabla tbody tr:hover, el
   separador el mismo --color-borde-sutil que separa filas de
   tabla, y el filete de reacción el mismo gesto de la columna
   ordenada.

   La fila entera es el <a>: el area de click va de borde a borde
   de la .pieza —de ahi el margin negativo, que paga el padding
   de la pieza y lo devuelve como padding propio— y mide 44px de
   alto. Con <a> inline dentro de <li> el blanco entre el final
   de la palabra y el borde no era clickeable, que en una lista
   de accesos es justo donde cae el mouse.
   ========================================================== */

.lista-enlaces {
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Sangria negativa: la fila llega al borde de la .pieza y la devuelve como
   padding, asi el texto sigue alineado con el resto del contenido pero la
   banda de hover no queda flotando en el medio. --esp-6 es el padding de
   .pieza; si la pieza cambia de padding, esto lo sigue por token. */
.lista-enlaces a {
  display: flex;
  align-items: center;
  gap: var(--esp-3);
  min-height: var(--fila-default);
  margin-inline: calc(var(--esp-6) * -1);
  padding: var(--esp-2) var(--esp-6);
  border-bottom: var(--borde-fino) solid var(--color-borde-sutil);
  color: var(--color-texto-primario);
  font-size: var(--texto-md);
  font-weight: var(--peso-regular);
  text-decoration: none;
  transition: background var(--transicion), box-shadow var(--transicion);
}

/* Mismo criterio que .tabla tbody tr:last-child: el ultimo separador seria
   una linea al aire, porque abajo ya no hay otra fila. */
.lista-enlaces li:last-child a {
  border-bottom: 0;
}

/* Los dos canales del hover, igual que .tabla__orden resuelve la columna
   activa: el fondo NO va solo. Suma el filete de acento contra el borde de
   arranque de la fila —forma, no color— y el subrayado del texto, que es el
   canal que sobrevive en escala de grises y con el mouse quieto. */
.lista-enlaces a:hover {
  background: var(--color-superficie-hover);
  box-shadow: inset var(--borde-grueso) 0 0 0 var(--color-acento);
  text-decoration: underline;
  text-underline-offset: var(--esp-1);
}

/* El foco de teclado no se apoya en el hover: es su propio anillo, y va con
   offset negativo porque la fila llega al borde de la pieza y un anillo por
   fuera quedaria cortado por el overflow. */
.lista-enlaces a:focus-visible {
  outline: var(--borde-medio) solid var(--color-foco);
  outline-offset: calc(var(--borde-medio) * -1);
  background: var(--color-superficie-hover);
}

/* --- El icono de fila, opcional ---
   Soportado, no obligatorio: la lista sin icono es el caso por defecto y es
   el que usan los seis grupos de configuracion. Cuando va, va a la
   izquierda, 24x24 y en el trazo de los iconos de dominio, con el mismo
   gris de .selector__chevron. Se tine con la fila en hover para que no
   quede un icono apagado al lado de un texto activo.
   Sin chevron a la derecha: en un grupo de cuatro filas serian cuatro
   flechas iguales diciendo «esto es un link», que es lo que ya dicen el
   subrayado y el filete. */
.lista-enlaces__icono {
  flex: 0 0 auto;
  width: var(--esp-6);
  height: var(--esp-6);
  color: var(--color-texto-suave);
}

.lista-enlaces a:hover .lista-enlaces__icono,
.lista-enlaces a:focus-visible .lista-enlaces__icono {
  color: var(--color-texto-primario);
}

/* El destino actual, si la lista queda a la vista en la pantalla a la que
   lleva: el filete se queda puesto y el texto pasa a peso 500. Mismo par
   —forma mas peso— que usa el shell para el destino activo. */
.lista-enlaces a[aria-current="page"] {
  box-shadow: inset var(--borde-grueso) 0 0 0 var(--color-acento);
  font-weight: var(--peso-medium);
}


/* ==========================================================
   33 · VOLVER
   El camino de vuelta de una pantalla de detalle o de edición:
   un link al pie, con flecha y con el nombre de la pantalla a la
   que vuelve.

   Al pie y no arriba del título: arriba le cobraba una línea de
   alto a cada pantalla antes de lo que el usuario vino a mirar,
   y para irse el shell ya ofrece la navegación de arriba. Al pie
   es lo que queda a mano cuando se terminó de leer o de cargar.

   La flecha va con palabra: una flecha sola no dice adónde va
   —«volver» a qué, al listado o al paso anterior—. Y la palabra
   es el destino («Clientes»), no «Volver a clientes»: la flecha
   ya dice «volver».
   ========================================================== */

.volver {
  /* flex y no inline-flex: es su propia fila, no un pedazo de la línea
     de lo que venga antes. fit-content para que el área de clic sea la
     del texto y no el ancho entero del contenido. */
  display: flex;
  width: fit-content;
  align-items: center;
  gap: var(--esp-2);
  min-height: var(--control-sm);   /* 32px de área de clic */
  margin-block-start: var(--esp-6);
  color: var(--color-link);
  font-size: var(--texto-sm);
  text-decoration: none;
}

.volver:hover {
  text-decoration: underline;
  text-underline-offset: var(--esp-1);
}

.volver svg {
  flex: 0 0 auto;
  width: var(--esp-4);
  height: var(--esp-4);
}
