/* ============================================================================
   app.css — Lo propio del back office de LFPX.

   Se carga DESPUÉS del kit, así que acá va SÓLO lo que el kit no trae: la puerta
   (login), las dos pantallas públicas (la cuenta que abre el cliente y crear la
   clave), el toast, la tarjeta del modal, los detalles de la topbar, la tabla de
   programas, el punto de color de un programa, la ficha del cliente, las tres
   previas de un aviso y la leyenda filtrable del gráfico por programa. Nada de
   redefinir componentes del kit: si algo del kit no alcanza, se arregla en el kit,
   que lo usan las nueve apps de VA.

   Dos reglas duras de este archivo:

   1. Cero colores escritos a mano. Todo sale de var(--token), que es lo que hace que
      la app se reskinee sola con clientes/la-fabrica/marca/tokens.css. Las sombras y
      los velos van en rgba() neutro, que no es identidad.
   2. NO se redefine .sidebar. El anclaje del panel lateral (sticky, align-self,
      height y overflow) lo hereda del kit, y volver a declararlo acá es la forma
      clásica de romperlo: alcanza con olvidarse una de las cuatro para que la barra
      se vaya con el scroll de la página.
   ========================================================================== */

/* ---------------------------------------------------------------- La puerta */
.login {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: 24px;
  background: radial-gradient(1100px 480px at 50% -10%, var(--marca-suave), transparent 70%), var(--fondo);
}
.login__card {
  width: 100%;
  max-width: 400px;
  background: var(--superficie);
  border: 1px solid var(--borde);
  border-radius: var(--radio-lg);
  box-shadow: var(--sombra);
  padding: 30px 28px 26px;
}
.login__brand { color: var(--tinta); margin-bottom: 14px; }
.login__brand .marca-logo { display: block; }
/* El título de la tarjeta. Lo estrenó "Crear la clave", que es la única puerta que le habla a
   una persona por su nombre ("Hola, Marta.") y que tiene que decir en una línea por qué el
   link no anda ("Este link venció."). La entrada del equipo no lo usa y no lo necesita.
   Sin esta regla el h1 sale con el 2em del navegador (el kit fija familia y peso, no tamaño):
   arriba de una tarjeta de 400 px eso son 32 px y la frase se parte en tres renglones. Mismas
   medidas que .modal__title, que es la otra frase corta que encabeza una caja chica. */
.login__t { font-size: 17px; margin: 0 0 8px; }
.login__sub { color: var(--tinta-suave); font-size: 13.5px; margin: 0 0 20px; }
.login__err { color: var(--error); font-size: 13px; margin: 2px 0 6px; min-height: 18px; }
.login__foot { color: var(--tinta-tenue); font-size: 12px; margin: 16px 0 0; }
.login .btn { width: 100%; margin-top: 6px; }

/* ---------------------------------------------------------------- Las públicas
   Las dos pantallas que se dibujan SIN el shell, sobre #app entero: la cuenta corriente que
   abre el cliente desde el link de su mail y la de crear la clave. app.js las monta con
   `app.className = 'app-publica'`, que PISA la clase `.app` del kit en vez de sumarse a ella,
   así que la columna de la página la tiene que poner esta clase o no la pone nadie.

   La otra diferencia con el back office, y la que manda en las medidas de acá abajo: la
   cuenta se abre casi siempre desde un teléfono, en el momento en que llega el mail. Todo lo
   que sigue se midió a 390 px de ancho primero y en escritorio después. */
.app-publica {
  min-height: 100vh;
  min-height: 100dvh;  /* dvh descuenta la barra del navegador móvil, que es donde se lee */
  display: flex;
  flex-direction: column;
}
/* Lo que sea que la vista haya dibujado (la página, la tarjeta de la puerta o el "Cargando…")
   llena el alto: si no, un cartel de tres renglones queda pegado arriba con el fondo de la
   página abajo, y se lee como una pantalla a medio cargar. */
.app-publica > * { flex: 1 0 auto; min-width: 0; }

/* La página de la cuenta. Columna con la marca arriba, el contenido en el medio y la firma
   abajo de todo: el `flex: 1` del cuerpo es lo que empuja la firma al fondo cuando la cuenta
   es corta ("estás al día" son tres renglones). El min-height se repite acá y no se delega en
   `.app-publica` para que la pantalla se pueda dibujar sola, en un contenedor cualquiera, sin
   quedar dependiendo de quién la monta. */
.pub {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  background: var(--fondo);
}

/* La banda de marca. Angosta a propósito: el que abre este link viene del mail y lo que busca
   es el número, no el encabezado. Va sobre --superficie, como la topbar del portal, para que
   la página se lea como la misma casa. */
.pub__top {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--borde);
  background: var(--superficie);
}
.pub__marca { display: inline-flex; flex: none; color: var(--marca); }
/* El lockup es un <svg> y no un <img>, así que el `display: block` del kit no lo alcanza: en
   línea arrastra el descuelgue de la caja de texto y la banda queda 4 px más alta de un lado. */
.pub__marca .marca-logo { display: block; width: auto; }
/* Qué es esta página, al lado de la marca y separado por la misma línea que usa la topbar. */
.pub__que {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .6px;
  color: var(--tinta-tenue);
  padding-left: 12px;
  border-left: 1px solid var(--borde);
}

/* El cuerpo. 860 px es el ancho de dos tablas de seis columnas sin que las cifras se vayan a
   buscar al otro lado de la pantalla, y bastante menos que los 1200 del back office: acá no
   hay barra lateral que descontar y una línea de texto de 1200 px no se lee. */
/* El ancho de la página del cliente lo fija la TABLA DE FACTURAS, que es lo único que no se
   puede angostar sin perder una columna. Con la columna del PDF son siete (número, emitida,
   vence, estado, total, saldo y el botón que abre el papel) y a 860 no entraban: la tabla se
   iba 55 px de ancho, así que el número de factura se partía en dos renglones y la columna
   fija de la acción quedaba encima del SALDO, que es justo el número que el cliente vino a
   ver. Van 940, que es lo que necesita la tabla con lugar de sobra, y sigue siendo una medida
   cómoda de lectura para el resto de la página. */
.pub__cuerpo {
  flex: 1;
  width: 100%;
  max-width: 940px;
  margin: 0 auto;
  padding: 24px 16px 48px;
  min-width: 0;
}
@media (max-width: 560px) {
  .pub__top { padding: 12px 14px; gap: 10px; }
  .pub__cuerpo { padding: 18px 12px 36px; }
}

/* El número de la factura y el del recibo, que son la primera columna de las dos tablas de
   esta página, no se parten. Es el mismo criterio que .col-cliente y sus hermanas del back
   office, y acá pesa más: en un teléfono la tabla scrollea de costado y lo que se queda
   quieto es justamente esta columna, así que si se angosta hasta partirse, "FA-A-0001-
   00012345" queda en tres renglones y el cliente no puede cruzar la fila contra su propio
   papel. Medido a 390 px, que es donde se abre este link. La tabla se va de ancho y scrollea
   adentro de su envoltorio, que es lo que corresponde (patrón 2), no la página.

   El número: "FA-A-0001-00012345" mide 149 px en esta tipografía y la celda pone 28 de
   relleno, o sea 177 para el peor caso de hoy. Van 190, que deja lugar para un par de
   caracteres más (el nombre de un comprobante lo arma Odoo y las series crecen) sin comerse
   la mitad de la pantalla: quedan 200 px para que scrollee el resto de la fila. Un nombre más
   largo que eso se parte en dos renglones, que es la forma buena de quedarse corto. */
.pub .tbl th.col-fija-izq,
.pub .tbl td.col-fija-izq { min-width: 190px; }

/* PERO ABAJO DE 560 px LA TABLA DEJA DE SER TABLA.
 *
 * El scroll horizontal adentro del envoltorio es lo correcto para una tabla del back office
 * (patrón 2) y era lo que había acá. Medido en un teléfono de 390 px con datos reales, el
 * resultado es que de las seis columnas entran tres, y la que queda afuera es la del SALDO,
 * que es la única pregunta que este cliente vino a hacerse. El scroll lateral adentro de una
 * tarjeta no se descubre solo: no hay barra visible, el gesto compite con el scroll de la
 * página, y el que abre esto lo abre parado en la calle desde el mail.
 *
 * Así que en el teléfono cada factura se apila como un bloque, con el rótulo adelante de su
 * valor. El rótulo viaja en `data-rotulo` de cada celda (views/cuenta-publica.js) porque la
 * cabecera se esconde: sin eso, tres números sueltos uno abajo del otro no dicen cuál es
 * cuál. Vale sólo para las dos tablas de la página pública, marcadas con `tbl--apila`; las
 * del back office siguen scrolleando, que ahí es lo que corresponde porque el que mira es
 * alguien sentado frente a una pantalla ancha, comparando filas entre sí. */
@media (max-width: 560px) {
  .pub .tbl--apila,
  .pub .tbl--apila tbody,
  .pub .tbl--apila tr,
  .pub .tbl--apila td { display: block; width: auto; }
  /* La cabecera se saca de la vista sin sacarla del árbol: un lector de pantalla la sigue
     leyendo como la tabla que es. */
  .pub .tbl--apila thead {
    position: absolute;
    width: 1px; height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  /* Y el botón de ordenar de cada encabezado deja de existir para el teclado. Recortar el
     `thead` lo saca de la vista pero no del orden de tabulación: con las tres tablas de la
     cuenta dibujadas, tabular desde arriba pasaba por once botones invisibles antes del
     primer PDF, con el foco en ninguna parte visible. Y ordenar no tiene sentido acá: en el
     teléfono la tabla se apila en fichas y no hay columnas que comparar. */
  .pub .tbl--apila thead .th-orden { display: none; }
  .pub .tbl--apila tr {
    padding: 12px 14px;
    border-bottom: 1px solid var(--borde);
  }
  .pub .tbl--apila tr:last-child { border-bottom: 0; }
  .pub .tbl--apila td {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 12px;
    padding: 3px 0;
    border: 0;
    text-align: left;
    /* El min-width de la primera columna es del modo tabla: acá estorba. */
    min-width: 0;
  }
  .pub .tbl--apila td::before {
    content: attr(data-rotulo);
    flex: 0 0 auto;
    color: var(--tinta-tenue);
    font-size: 12px;
    text-transform: uppercase;
    letter-spacing: .4px;
  }
  /* El número de la factura es el título del bloque, así que va solo y arriba, sin rótulo:
     "FACTURA  FCE-A 01001-00000005" en un renglón de teléfono gasta la mitad del ancho en
     decir algo que se ve. */
  .pub .tbl--apila td.col-fija-izq {
    display: block;
    font-size: 15px;
    margin-bottom: 4px;
  }
  .pub .tbl--apila td.col-fija-izq::before { content: none; }
  /* Y el saldo es lo que se vino a ver: queda como el número grande del bloque. */
  .pub .tbl--apila td[data-rotulo="Saldo"],
  .pub .tbl--apila td[data-rotulo="Monto"] { font-size: 16px; }
  /* La celda del vacío no se apila: es un mensaje, no un dato con rótulo. */
  .pub .tbl--apila td[colspan]::before { content: none; }
  .pub .tbl--apila td[colspan] { display: block; }
  /* Apilada no hay costados que fijar: la columna de la acción es un renglón más del bloque,
     y el `sticky` del kit sobre una celda que ya no es celda sólo puede correrla de lugar. */
  .pub .tbl--apila td.col-fija { position: static; }
}

/* De cuándo es el dato, abajo del nombre de la cuenta. Los dos escalones los decide el
   servidor y la vista los traduce a is-warn / is-danger: acá sólo está el color. */
.pub-frescura { font-size: 12.5px; color: var(--tinta-tenue); margin: 4px 0 0; }
.pub-frescura.is-warn { color: var(--atencion); }
.pub-frescura.is-danger { color: var(--error); }

/* El total a pagar. Es LA pregunta con la que se abre este link, así que es la única cifra de
   todo el portal que se sale de la escala del kit: 30 px (.kpi__value) es el número de una
   tarjeta más de un tablero, y esto no es una tarjeta más. El clamp lo ata al ancho: 9vw da
   35 px en un teléfono de 390 y llega al tope de 52 recién en escritorio, así que
   "$ 117.552.711" entra en un renglón en los dos lados. */
.pub-hero .card__body { padding: 26px 24px; }
/* Los tres renglones son <p>, y un <p> del kit trae .6em de margen abajo: sin esto el rótulo,
   la cifra y el pie quedan separados por un renglón vacío cada uno. */
.pub-hero .card__body > p { margin-bottom: 0; }
.pub-hero__n { font-size: clamp(34px, 9vw, 52px); line-height: 1; letter-spacing: -.03em; margin-top: 6px; }
/* Vencido y en término, abajo de la línea. auto-fit y no dos columnas fijas: con 220 px de
   mínimo se acomodan solas en una columna en el teléfono, sin un corte más que mantener. */
.pub-hero__partes {
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 14px;
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--borde-suave);
}
.pub-hero__parte { display: grid; gap: 2px; min-width: 0; }
.pub-hero__parte > b { font-size: 20px; font-weight: 800; font-variant-numeric: tabular-nums; }
/* Sólo lo vencido se pinta. Lo que está en término no es una alarma, y dos números en rojo
   dejan de distinguir cuál de los dos hay que mirar. */
.pub-hero__parte--vencido > b { color: var(--error); }
.pub-hero__pl { font-size: 12.5px; font-weight: 600; color: var(--tinta-tenue); }
.pub-hero__pd { font-size: 12.5px; line-height: 1.45; color: var(--tinta-suave); }

/* A DÓNDE TRANSFERIR. El CBU es lo único de esta pantalla que alguien va a COPIAR, y casi
   siempre desde el teléfono: el valor va grande, en su propio renglón y con el botón de copiar
   al lado. El rótulo chico arriba y no a la izquierda, para que un CBU de 22 dígitos entre
   entero sin partirse a la mitad. */
.pub-cbu {
  border: 1px solid var(--borde);
  border-radius: var(--radio-sm);
  background: var(--superficie-alt);
  padding: 4px 14px;
}
.pub-cbu__fila {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 8px 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--borde-suave);
}
.pub-cbu__fila:last-child { border-bottom: 0; }
.pub-cbu__rotulo {
  grid-column: 1 / -1;
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: .5px;
  color: var(--tinta-tenue);
  margin-bottom: -6px;
}
.pub-cbu__valor { font-size: 16px; font-variant-numeric: tabular-nums; min-width: 0; word-break: break-all; }

/* El formulario para subir el comprobante, adentro de la misma tarjeta. Aparece donde estaba
   el botón, así que necesita su propia separación arriba: sin la línea se lee como parte del
   párrafo que lo presenta. */
.pub-subir { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--borde); }
.pub-subir__estado { margin: 10px 0 0; font-size: 13px; color: var(--tinta-suave); min-height: 1.2em; }
.pub-subir__estado.is-error { color: var(--error); }
/* El comprobante dado de baja no se esconde (sigue siendo algo que el cliente informó), pero
   tampoco compite con los que valen. */
.pub .fila--baja { opacity: .55; }
.mt16 { margin-top: 16px; }

/* La firma de VA al pie, sólo en lo que ve el cliente final (regla de la casa: el back office
   no la lleva). Es el pie del Movimiento Industrial y el de Che, con una diferencia obligada:
   allá la franja va en --marca, y en LFPX la marca es el BLANCO, así que una banda de marca
   al fondo de una página oscura sería un rectángulo blanco de punta a punta. Va en
   --superficie, que es la misma franja que la banda de arriba y cierra la página. */
.pub-pie {
  border-top: 1px solid var(--borde);
  background: var(--superficie);
  padding: 16px 18px;
  font-size: 12px;
  color: var(--tinta-tenue);
}
.pub-pie__in {
  width: 100%;
  max-width: 860px;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}
.pub-pie .marca-logo { display: block; width: auto; flex: none; }
.pub-pie__sp { flex: 1; }
.pub-pie a { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
.pub-pie a:hover { color: var(--tinta-suave); }

/* ---------------------------------------------------------------- Toast
   Confirma lo que pasó y se va. Los errores que hay que leer no van acá: van en
   banner, quietos, donde el usuario los pueda releer. */
.toast {
  position: fixed;
  left: 50%;
  bottom: 26px;
  transform: translateX(-50%) translateY(20px);
  background: var(--tinta);
  color: var(--fondo);
  font-size: 13.5px;
  font-weight: 600;
  padding: 11px 18px;
  border-radius: var(--radio);
  box-shadow: var(--sombra-lg);
  opacity: 0;
  pointer-events: none;
  transition: opacity .2s, transform .2s;
  z-index: 320;
  max-width: 90vw;
}
.toast.is-on { opacity: 1; transform: translateX(-50%) translateY(0); }
.toast--error { background: var(--error); color: var(--tinta); }
.toast--ok { background: var(--ok); color: var(--fondo); }

/* ---------------------------------------------------------------- Modal
   El kit trae el velo (.modal-back, z-index 300) pero no la tarjeta. Acá el velo y
   la tarjeta son hermanos en el DOM, así que el orden lo decide el z-index y nada
   más: por debajo de 300 el velo se come todos los clics del modal. */
.modal { position: fixed; inset: 0; z-index: 310; display: none; place-items: center; padding: 20px; }
.modal.is-open { display: grid; }
.modal__card {
  width: 100%;
  max-width: 440px;
  background: var(--elevado);
  border: 1px solid var(--borde);
  border-radius: var(--radio);
  box-shadow: var(--sombra-lg);
  padding: 22px 24px;
}
.modal__title { font-size: 16px; margin: 0 0 8px; color: var(--tinta); }
.modal__txt { font-size: 13.5px; line-height: 1.55; color: var(--tinta-suave); margin: 0 0 18px; }
.modal__acciones { display: flex; gap: 8px; justify-content: flex-end; flex-wrap: wrap; }

/* ---------------------------------------------------------------- La columna de la nav
   El panel lateral mide UNA pantalla y así tiene que quedar: es lo que hace que la
   navegación no viaje con el scroll (PATRONES.md, patrón 7). Lo que no puede pasar es que
   su COLUMNA se termine junto con él. En una pantalla larga (la cartera son 126 filas)
   abajo del panel aparecía el fondo de la página y la barra quedaba leyéndose como una
   tarjeta flotando, con el borde izquierdo del contenido cortado a media altura.

   Se pinta la COLUMNA y no el panel: la franja es del ancho de la barra, del mismo
   --superficie, así que la juntura no se ve, y el último píxel es la continuación de su
   border-right. La geometría del panel (position, height, overflow) no se toca, que es la
   forma clásica de romper el anclaje. */
.shell {
  background: linear-gradient(to right,
    var(--superficie) 0 calc(var(--sidebar-w) - 1px),
    var(--borde) calc(var(--sidebar-w) - 1px) var(--sidebar-w),
    transparent var(--sidebar-w));
}
/* Abajo del breakpoint el panel es un drawer que flota sobre el contenido: no hay columna
   que pintar, y la franja quedaría como una banda de color al costado de la tabla. */
@media (max-width: 720px) {
  .shell { background: none; }
}

/* El contador de Avisos. El del kit es la bandeja de trabajo (cuántas cosas hay para mirar) y
   va en el tono de marca; éste cuenta mails que NO SALIERON y que nadie mandó a mano todavía,
   que no es trabajo pendiente sino algo que se rompió. En LFPX la diferencia se nota el doble:
   el tono de marca es el blanco, así que sin el modificador el aviso de una falla se ve igual
   que un contador cualquiera.

   El par activo se repite porque el kit se lo pisa: con la entrada abierta, el fondo del ítem
   es --marca (blanco pleno) y el badge tiene que seguir gritando arriba de eso, así que va el
   error lleno con la tinta del fondo encima, y no el error suave, que sobre blanco desaparece. */
.side-link__count--alerta { background: var(--error-bg); color: var(--error); }
.side-link.is-active .side-link__count--alerta { background: var(--error); color: var(--fondo); }

/* ---------------------------------------------------------------- Topbar */
.topbar__logo .marca-logo { display: block; }
.topbar__quien { display: block; line-height: 1.2; }
.topbar__quien .chico { display: block; }

/* La frescura del espejo: siempre a la vista, y con su escalón por antigüedad.
   Es un dato más, así que se abre (lleva a la pantalla de conexión con Odoo). */
.topbar__frescura {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--tinta-tenue);
  border: 1px solid transparent;
  border-radius: var(--radio-sm);
  padding: 4px 9px;
  white-space: nowrap;
}
.topbar__frescura .ico { width: 14px; height: 14px; flex: none; }
.topbar__frescura:hover { text-decoration: none; border-color: var(--borde); }
.topbar__frescura.is-warn { color: var(--atencion); background: var(--atencion-bg); }
.topbar__frescura.is-danger { color: var(--error); background: var(--error-bg); }

/* ---------------------------------------------------------------- Banners y vacíos
   El kit trae info, warn y ok. El rojo es de esta app: lo piden la frescura de más de
   24 horas, la corrida cortada por credencial y el error de carga con su reintento.

   Es el banner del kit en el par de error y nada más: la caja, el aire y la alineación
   quedan las de .banner--warn. Acá tenía además `align-items: center` y
   `justify-content: space-between`, y eso lo desalineaba de su gemelo. El banner de
   frescura es el MISMO markup (ícono más texto) que cambia de modificador según la
   antigüedad del dato: al pasar de amarillo a rojo el ícono se bajaba al medio, y con un
   texto que no llegaba a llenar el renglón el space-between lo mandaba contra el borde
   derecho, con un hueco de varios centímetros entre el ícono y la primera palabra. El
   botón de reintentar se apoya en el borde derecho con margin, que es lo que hace falta
   y no le cambia la forma a los banners que no tienen botón. */
.banner--danger {
  background: var(--error-bg);
  color: var(--error);
  border-color: color-mix(in srgb, var(--error) 22%, transparent);
}
.banner--danger .btn { flex: none; margin-left: auto; }

/* EL TEXTO DE UN BANNER NO EMPUJA LA PÁGINA. `.banner` es flex y su caja de texto nace con
   `min-width: auto`, así que no se encoge por debajo de su palabra más larga: un dato de
   Odoo que viene sin espacios (una razón social, un mail) la estira y la página entera
   scrollea de costado. Medido en la ficha de un cliente a 390: con la razón social más
   larga sin cortes, el documento pasaba a 478 px de ancho; con esta regla vuelve a 390. Va
   acá y no en cada banner porque son 36 escritos a mano en ocho pantallas, y una regla que
   hay que acordarse de repetir dura hasta el banner que viene. Es la misma pareja que el
   kit ya usa en `.dl dd`: `min-width: 0` deja que la caja se encoja y `overflow-wrap:
   anywhere` le da al navegador dónde cortar cuando la palabra no trae ningún separador. */
.banner > div { min-width: 0; overflow-wrap: anywhere; }

.empty-state__t { color: var(--tinta); font-weight: 700; font-size: 15px; margin: 0 0 6px; }
.ayuda { font-size: 12.5px; color: var(--tinta-tenue); margin: 4px 0 0; }

/* ---------------------------------------------------------------- Tablas
   El kit pinta las celdas fijas del encabezado con var(--marca) sin pasar por
   --tabla-head. En LFPX la marca es el BLANCO: sin esta corrección, la primera y la
   última celda del thead quedan blancas en el medio de una banda gris. Se arregla
   acá, en la app, y no tocando el kit, porque es un efecto de esta marca. */
.tbl thead th.col-fija,
.tbl thead th.col-fija-izq {
  background: var(--tabla-head, var(--marca));
  color: var(--tabla-head-contenido, var(--marca-contenido));
}

/* La segunda línea de un encabezado: en qué escala está esa columna. En el tablero y en un
   programa conviven columnas del PERÍODO (lo facturado, lo cobrado entre las dos fechas de
   arriba) con columnas de STOCK (deuda y vencido, que son el saldo de hoy y no pertenecen a
   ningún rango). Sin decirlo en la columna, con el rango puesto en un día quedaba una fila
   que dice "facturó $ 0 y debe $ 63.875.900": los dos números en el mismo renglón, medidos
   con varas distintas y sin nada que lo aclarara.

   El `<small>` va en bloque porque el th del kit es nowrap y en línea empujaría el ancho de
   la columna. Va en minúscula y sin el letter-spacing del encabezado para que se lea como
   una aclaración y no como una segunda columna. */
.tbl thead th small {
  display: block;
  margin-top: 2px;
  font-size: 10.5px;
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  opacity: .78;
}

/* Anchos mínimos de las columnas de identidad: el nombre de un cliente, de un programa,
   de una persona del equipo o de un diario de Odoo no se parte en dos líneas mientras la
   tabla scrollea de costado. Son siempre la columna fija de la izquierda (.col-fija-izq),
   la que dice de quién es la fila: cuando las columnas de plata la aprietan, lo primero
   que se pierde es justamente eso. La de corridas se reconoce por su fecha con hora
   ("12/08/2026, 21:37"), así que ahí el ancho es el de un renglón entero. */
.col-cliente { min-width: 200px; }
.col-programa { min-width: 180px; }
.col-persona { min-width: 180px; }
.col-diario { min-width: 170px; }
.col-cuando { min-width: 150px; white-space: nowrap; }
.col-acciones { text-align: right; white-space: nowrap; }

/* La fila de totales y la de saldos iniciales de la migración. Ninguna de las dos
   abre nada (son la suma y una fila aparte declarada), y por eso no llevan
   .is-clickable: se distinguen por peso y por el borde de arriba. */
.tbl tbody tr.fila-total td {
  font-weight: 800;
  color: var(--tinta);
  background: var(--superficie-alt);
  border-top: 2px solid var(--borde);
}
.tbl tbody tr.fila-total:hover td { background: var(--superficie-alt); }
.tbl tbody tr.fila-aparte td { color: var(--tinta-suave); background: var(--superficie-alt); }
.tbl tbody tr.fila-aparte td .ayuda { margin: 0; }

/* ------------------------------------------------- La nota de abajo de una tabla
   Lo que se dice SOBRE la tabla y no es una fila suya: que la lista quedó recortada, hasta
   dónde llega y dónde está el resto. Va afuera del `.tbl-wrap`, y esa es toda la razón por
   la que existe la clase: adentro sería una fila con `colspan`, o sea tan ancha como la
   tabla, y en un teléfono la tabla scrollea de costado dentro de su contenedor. El aviso
   terminaba fuera de la pantalla y había que arrastrar la tabla para leerlo, justo el aviso
   que dice que lo que se está mirando no es todo. Acá se mide contra el ancho de la
   tarjeta, así que envuelve solo y se lee entero a 390. */
.tbl-nota {
  padding: 10px 16px 12px;
  border-top: 1px solid var(--borde);
  background: var(--superficie-alt);
  color: var(--tinta-suave);
}
.tbl-nota .ayuda { margin: 0 0 2px; }
.tbl-nota .ayuda:last-child { margin-bottom: 0; }

/* ------------------------------------------------- El atraso en la lista de clientes
   Decisión de diseño, y el porqué. La cartera real son 126 clientes y 76 tienen algo
   vencido: 43 en 1 a 30 días, 29 en 31 a 60, 4 en 61 a 90 y ninguno arriba de 90. Con
   `row--vencido` del kit (fondo de error en la fila entera) tres de cada cuatro filas
   quedaban rojas, y un rojo que está en casi todas las filas no distingue nada: la
   pantalla se lee como una alarma permanente, que es el estado en el que se dejan de mirar
   las alarmas. El dato que importa además no es "tiene algo vencido" (eso lo tiene casi
   toda la cartera) sino CUÁNTO hace que está vencido, que es lo que decide a quién se
   llama primero, y el fondo de la fila no puede decir eso: es un sí o un no.

   Entonces el énfasis va donde está la información y escalado por gravedad: la CELDA del
   vencido toma el color del tramo y la ETIQUETA del tramo lo dice con letras. El fondo de
   la fila queda reservado para el tramo más grave (hoy +90 días), que en esta cartera son
   cero clientes: el día que aparezca uno se va a ver desde la otra punta de la lista, que
   es lo único que se le pide a un rojo, ser raro.

   La escala la reparte views/clientes.js (gravedadDe) por la POSICIÓN del tramo, no por su
   clave: si metricas.js agrega un corte, entra acá sin tocar nada. Los cuatro colores son
   los tres del contrato más el sin color: ninguno, atención, error y error de fondo. */
.vencido-medio { color: var(--atencion); font-weight: 700; }
.vencido-grave { color: var(--error); font-weight: 700; }
.vencido-critico { color: var(--error); font-weight: 800; }
/* La etiqueta del tramo más grave, la única llena: las otras tres son las pills del kit
   (neutral, warn, danger), que son fondo suave y texto de color. */
.pill--critico { background: var(--error); color: var(--fondo); }
/* Y la fila entera, sólo para ese tramo. Las celdas fijas se repiten porque traen fondo
   propio para tapar lo que pasa por debajo (kit.css lo hace igual con `row--vencido`). */
.tbl tbody tr.row--critico td { background: var(--error-bg); }
.tbl tbody tr.row--critico td.col-fija,
.tbl tbody tr.row--critico td.col-fija-izq { background: var(--error-bg); }

/* ---------------------------------------------------------------- Programas
   El color de un programa es su acento de marca, y llega por atributo style con el
   token que devuelve Marca.acento(slug). Acá sólo va la forma: el color no se
   escribe en el CSS porque el catálogo de programas sale de Odoo y crece solo. */
.punto-programa {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  display: inline-block;
  flex: none;
  background: var(--tinta-tenue);
}
.celda-programa { display: flex; align-items: center; gap: 8px; min-width: 0; }
.celda-programa > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------------------------------------------------------------- El selector de rango
   La página nunca scrollea de costado (PATRONES.md, patrón 2), y el selector de rango era
   el único lugar donde lo hacía: a 390 px el botón medía 381 px arrancando en x=30, así que
   el documento daba 411 de scroll contra 390 de pantalla y el tablero entero se corría con
   el dedo, con el chevron del selector cortado contra el borde.

   El culpable es la leyenda ("Este mes · agosto de 2026 (hasta el 12/08)") en un solo
   renglón adentro de un `.btn`, que es nowrap. Se arregla dejando que la caja se encoja: un
   `.btn` es inline-flex y sus hijos no se achican solos, así que la parte que puede ceder
   (la leyenda del período) lleva `min-width: 0` y se recorta con puntos suspensivos. No se
   pierde el dato: el período completo está arriba, en palabras, al lado del título, que es
   donde manda el patrón 9 que esté. Y el `min-width: 0` de los contenedores es lo que
   permite que el encogido llegue hasta el botón: sin él, una columna flex nace con
   `min-width: auto` y no baja de su contenido. */
.page-head__actions,
.page-head__actions > .filtros-bar,
.rangepick { min-width: 0; max-width: 100%; }
#rango-btn { max-width: 100%; }
#rango-btn > .muted { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------------------------------------------------------------- Ficha del cliente
   La cabecera de la cuenta corriente: quién es a la izquierda, sus cifras a la
   derecha. El mail y la razón social pueden ser largos, así que la columna de datos
   va con minmax(0, …) y el texto se corta con VAKit.cortable. */
.ficha { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: 18px; align-items: start; }
.ficha__datos { min-width: 0; }
.ficha__nombre { font-size: 20px; letter-spacing: -.02em; margin: 0 0 4px; }
.ficha__meta { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: 13px; color: var(--tinta-suave); min-width: 0; }
.ficha__meta > span { min-width: 0; overflow-wrap: anywhere; }
.ficha__cifras { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 12px; }
.ficha__cifra { background: var(--superficie-alt); border-radius: var(--radio-sm); padding: 10px 12px; }
.ficha__cifra b { display: block; font-size: 19px; font-weight: 800; font-variant-numeric: tabular-nums; }
.ficha__cifra span { font-size: 12px; color: var(--tinta-tenue); }
@media (max-width: 900px) {
  .ficha { grid-template-columns: minmax(0, 1fr); }
}

/* ------------------------------------------- Escribir un aviso y ver cómo queda
   Siempre en dos columnas: a la izquierda lo que se escribe, a la derecha cómo lo va a
   recibir el cliente. Lo usan el panel de avisos (editar la plantilla de cada mail) y la
   ficha del cliente (mandarle uno ahora). El texto de la izquierda es siempre el mismo
   control; lo que cambia es en qué se convierte del otro lado, y son tres cosas distintas.

   Antes esto era una pantalla sola ("el mensaje de cobranza"), que se dio de baja: el texto
   de los avisos pasó a ser dato del panel. Las clases quedaron porque el control es el mismo. */
.input--texto {
  min-height: 320px;
  line-height: 1.55;
  resize: vertical;
  white-space: pre-wrap;
}

/* 1. El mail, dibujado adentro de un iframe con su propio srcdoc. Va en marco y no suelto en
   la página por dos razones que no se pueden resolver de otra forma: el HTML de un mail trae
   sus estilos adentro y sueltos se los lleva puestos a la pantalla, y el mail está armado en
   la capa CLARA de la marca (los clientes de mail no garantizan el negro), así que sobre el
   fondo oscuro del portal habría que reescribirlo para verlo.

   Sin alto acá: el JS le mide el contenido al iframe y le escribe el atributo `height`, para
   que el mail se lea de corrido en vez de scrollear adentro de un recuadro. El atributo es lo
   que se puede pisar desde el CSS sin pelear, así que este bloque no declara `height` ni
   `min-height`. Tampoco fondo: el documento de adentro trae el suyo. */
.mail-previa {
  display: block;
  width: 100%;
  border: 1px solid var(--borde);
  border-radius: var(--radio);
}

/* 2. El texto plano: la previa del reclamo por WhatsApp y el cuerpo de un mail viejo que se
   guardó sin dibujar. Se muestra como lo que es, una burbuja de chat: renglones respetados y
   el ancho de un teléfono, no el de la tarjeta. Un texto editado a lo ancho de una pantalla
   de escritorio se lee bien acá y mal donde se lee de verdad. */
.av-previa-texto {
  white-space: pre-wrap;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--tinta);
  background: var(--superficie-alt);
  border: 1px solid var(--borde-suave);
  border-radius: var(--radio);
  padding: 14px 16px;
  max-width: 420px;
}

/* 3. El recuadro con los datos para transferir, tal como lo dibuja el mailer: llega armado
   del servidor, con sus estilos escritos adentro y su propio papel claro. No va en iframe
   porque es un pedazo y no un documento: un srcdoc de dos renglones se mide mal y queda
   flotando en un marco de 520 px.

   Y por eso lleva `color`: los VALORES de la tabla (el CBU, el alias) los escribe el mailer
   sin color, contando con heredar la tinta del mail. Acá heredan la del portal, que es el
   BLANCO, y sobre el papel claro de la pieza quedan invisibles: un CBU que no se ve es
   exactamente lo que esta previa viene a evitar. `--marca-contenido` es, en esta marca, la
   tinta que va sobre el blanco. Ojo: vale porque el portal corre siempre en la capa oscura.
   El día que una pantalla se dibuje con data-tema="claro", este contenedor se revisa. */
.av-pieza-mail { max-width: 420px; color: var(--marca-contenido); }
/* Los huecos: el botón que lo inserta y, al lado, qué dato es. En dos columnas para que los
   seis entren sin hacer scroll y el ojo compare los nombres, que es lo que se viene a hacer
   acá (cuál era el del vencimiento). */
.huecos { display: grid; gap: 6px; }
.hueco { display: flex; align-items: center; gap: 10px; font-size: 12.5px; min-width: 0; }
.hueco > .muted { min-width: 0; }
.hueco .btn, .hueco .tag { flex: none; }

/* ---------------------------------------------------------------- Gráficos
   La leyenda FILTRABLE del gráfico por programa. El kit estila `.leg` como un rótulo, que es
   lo que es en un gráfico de dos líneas; con `filtrable`, charts.js lo dibuja como un
   <button> (seis programas en un mismo lienzo se pisan, y la salida es poder apagar los que
   no se están mirando). Un <button> no hereda ni la tipografía ni el color del contenedor,
   así que sin este bloque la leyenda sale con el gris de sistema del navegador, con borde y
   con la fuente del SO en el medio de la tarjeta.

   El área tocable: 32 px de alto, que es la excepción de densidad declarada del kit
   (PATRONES.md, "lo visual, en números"). Es una tira de seis controles que acompaña a un
   gráfico, no un formulario. El `gap` del contenedor baja a 2 px SÓLO cuando adentro hay
   botones: los 8 px de relleno de cada uno ya separan las entradas, y sumados a los 14 del
   kit dejaban la leyenda con el doble de aire que la de al lado. Ese mismo relleno es el que
   se descuenta con el margen negativo, para que el primer chip caiga en el borde del
   contenido y no 8 px adentro, desalineado del renglón de ayuda que va justo abajo (medido:
   la leyenda arrancaba en 57 y el texto en 49). Es el par que ya usa `.barrow--clic`. */
.vachart-leyenda:has(button.leg) { gap: 2px; margin-left: -8px; }
.vachart-leyenda button.leg {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  margin: 0;
  padding: 5px 8px;
  min-height: 32px;
  border-radius: var(--radio-sm);
  cursor: pointer;
  transition: background .12s, color .12s, opacity .12s;
}
.vachart-leyenda button.leg:hover { background: var(--superficie-alt); color: var(--tinta); }
.vachart-leyenda button.leg:focus-visible { outline: 2px solid var(--foco); outline-offset: 1px; }

/* Apagada, y se dice tres veces porque tiene que leerse de un vistazo con seis programas en
   la misma tira: el CHIP DE COLOR casi en cero (es lo que ata la entrada a su línea en el
   gráfico, así que apagarlo es la señal más fuerte que hay), el nombre en --tinta-tenue y la
   raya encima. Ninguna de las tres lo vuelve ilegible: el nombre queda en 5,7:1 sobre la
   tarjeta y el chip conserva su tono, que es lo que hace falta para volver a prenderlo.

   El chip se apaga con opacidad y no con `color`: su tono llega por `style` inline (sale de
   Marca.serie) y una regla de esta hoja no le gana.

   Del tachado: `text-decoration` se propaga a los descendientes en bloque, y el chip es un
   hijo del botón, así que había que medirlo antes de escribirlo. Medido en Chromium: la raya
   se dibuja sobre el texto y no llega al chip, que es una caja de alto cero sin contenido.
   Si algún motor la pintara igual, es una línea fina sobre una regla de 18 px y no rompe nada.

   El estado real lo dice `aria-pressed`, que es lo que lee un lector de pantalla. La clase
   está por si charts.js llegara a dibujar una y no la otra: las dos pintan igual. */
.vachart-leyenda button.leg.is-off,
.vachart-leyenda button.leg[aria-pressed="false"] {
  color: var(--tinta-tenue);
  text-decoration: line-through;
}
.vachart-leyenda button.leg.is-off .leg__solid,
.vachart-leyenda button.leg[aria-pressed="false"] .leg__solid { opacity: .25; }
/* Al pasar por encima de una apagada se adelanta lo que va a pasar: el color vuelve. */
.vachart-leyenda button.leg.is-off:hover .leg__solid,
.vachart-leyenda button.leg[aria-pressed="false"]:hover .leg__solid { opacity: .7; }

/* EL MOVIMIENTO DEL PUNTO, AL LADO DEL ACUMULADO.
   Las líneas del portal van acumuladas, así que el número grande del tooltip es un total
   corrido. Al lado va lo que se movió en ese punto, que es lo que la acumulación esconde, y
   tiene que leerse como un dato secundario y no como una segunda versión del total: por eso
   va en --tinta-tenue y un punto más chico. El ancho mínimo es para que la columna de
   movimientos quede alineada cuando el gráfico tiene nueve series y se movieron tres. */
.vachart__tip-mov {
  color: var(--tinta-tenue);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  min-width: 5.5em;
  text-align: right;
  padding-left: 8px;
}

/* ---------------------------------------------------------------- Formularios
   El kit no marca el campo que falló (patrón 3: el error se marca EN el campo, no
   sólo arriba del formulario). */
.field--error > .input,
.field--error > .select,
.field--error .clave > .input { border-color: var(--error); }
.field__error { color: var(--error); font-size: 12.5px; margin-top: 5px; }

/* La tira de segmentos scrollea de costado en mobile en vez de romper el renglón. */
.seg--scroll { max-width: 100%; overflow-x: auto; }

/* ---------------------------------------------------------------- Roles por tildes
   El alta y la edición de una cuenta eligen los roles con casillas y no con un
   desplegable: los roles se combinan, así que hay que ver los tres juntos con lo que
   habilita cada uno. La casilla va adentro de la etiqueta, de modo que lo que se toca es
   el renglón entero y no un cuadradito de 17 px.

   La casilla se dibuja acá en vez de dejar la nativa. El navegador la pinta con su
   paleta clara, y sobre la base oscura de LFPX eso es un cuadrado blanco con el tilde
   casi invisible adentro. Es el mismo criterio con el que el kit se dibuja la flecha del
   select, y el único lugar del portal con un checkbox: el resto de los controles ya los
   pone VAKit. */
.rolpick { display: grid; gap: 6px; }
.rolpick__opt {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--borde);
  border-radius: var(--radio-sm);
  background: var(--superficie);
  cursor: pointer;
}
.rolpick__opt:hover { border-color: var(--marca); }
.rolpick__opt:has(input:checked) { border-color: var(--marca); background: var(--superficie-alt); }
/* El rol que ya quedó cubierto por otro se deshabilita en lugar de desaparecer: se
   sigue leyendo qué es, y el resumen de abajo dice por qué no se puede tildar. */
.rolpick__opt:has(input:disabled) { cursor: default; opacity: .6; }
.rolpick__opt input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 17px;
  height: 17px;
  flex: none;
  margin: 1px 0 0;
  display: grid;
  place-content: center;
  border: 1.5px solid var(--borde);
  border-radius: var(--radio-sm);
  background: var(--superficie-alt);
  cursor: inherit;
}
.rolpick__opt input[type="checkbox"]:checked { background: var(--marca); border-color: var(--marca); }
/* El tilde son dos lados de una caja girados 45 grados: un pseudoelemento y ningún
   archivo, que es lo que evita una imagen más y un color escrito a mano. */
.rolpick__opt input[type="checkbox"]::after {
  content: '';
  width: 9px;
  height: 5px;
  border-left: 2px solid var(--marca-contenido);
  border-bottom: 2px solid var(--marca-contenido);
  transform: rotate(-45deg) translate(1px, -2px);
  opacity: 0;
}
.rolpick__opt input[type="checkbox"]:checked::after { opacity: 1; }
.rolpick__txt { min-width: 0; font-size: 13.5px; line-height: 1.35; }
.rolpick__txt b { display: block; color: var(--tinta); }
.rolpick__txt small { display: block; margin-top: 2px; font-size: 12.5px; color: var(--tinta-tenue); }

/* El resumen de abajo dice en criollo qué va a poder la persona con lo que quedó
   tildado. Contesta "y esto qué habilita", así que se lee como una frase y no como una
   etiqueta más. El aviso de lo que sobra va en atención: no es un error, es una
   aclaración de por qué esa casilla quedó apagada. */
.rolpick__sum { font-size: 12.5px; line-height: 1.5; margin-top: 8px; }
.rolpick__puede { color: var(--tinta-suave); }
.rolpick__aviso { color: var(--atencion); }

/* ---------------------------------------------------------------- Utilidades
   Las que el kit no trae (el kit sólo tiene .mb8/.mb16/.mb24, .muted, .mono,
   .between, .flex, .gap8, .gap12, .hr y .empty-state). */
.mt8 { margin-top: 8px; }
.mt16 { margin-top: 16px; }
.mt24 { margin-top: 24px; }
.chico { font-size: 12.5px; }
.small { font-size: 12px; }
.nowrap { white-space: nowrap; }
.right { margin-left: auto; }
/* El envoltorio de un buscador que vive arriba de una tabla, adentro de un .card__head.
   `min-width: 0` es lo único que hace: un ítem flex nace con `min-width: auto` y no se
   encoge por debajo de su contenido, así que un campo de 280px se llevaba puesto el ancho
   de la tarjeta y la PÁGINA ENTERA de Conexión Odoo se corría de costado en el teléfono
   (medido: scrollWidth 428 contra clientWidth 390). Con esto el `max-width: 100%` del campo
   pasa a valer y el buscador se achica en vez de empujar. Es la misma mitad del problema
   que resuelve `minmax(0, 1fr)` en una grilla. */
.right--buscador { min-width: 0; }
/* La etiqueta que FRENA una acción, no la que informa. Hoy es una sola: "recibidos sin
   imputar" en la fila de un cliente al que se le está por reclamar. Va en el color de
   atención de la marca porque tiene que ganarle al resto de las etiquetas de la fila: si se
   lee igual que el nombre del programa, no frena nada. */
.tag--alerta { background: var(--atencion-bg); color: var(--atencion); }
.mono-sm { font-variant-numeric: tabular-nums; font-size: 12.5px; color: var(--tinta-suave); }
.wrap-acciones { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-top: 14px; }
/* Adentro de un estado vacío el texto va centrado, así que la acción también. Con el flex sin
   justificar, "Ir al inicio" quedaba pegado al borde izquierdo de la caja, a la altura de la
   primera palabra de un título que está en el medio. */
.empty-state .wrap-acciones { justify-content: center; }
.detalle-sec { margin-bottom: 20px; }
.detalle-sec > h4 { font-size: 12px; text-transform: uppercase; letter-spacing: .6px; color: var(--tinta-tenue); margin: 0 0 10px; }
.btn--bloque { width: 100%; }

/* ---------------------------------------------------------------- Cinco KPIs en fila
   El kit fija cuatro columnas (.grid--kpi) y este portal tiene filas de cinco: en el
   tablero facturado neto, facturado con IVA, cobrado, deuda y vencido; en clientes los
   cuatro tramos de atraso más los que no tienen nada vencido; en un programa las mismas
   cinco del tablero. En los tres casos las cinco son una sola lectura y se miran juntas:
   la quinta sola en un segundo renglón se lee como un dato aparte, y en clientes es
   justamente la que hace que las tarjetas cierren contra el contador. El kit no se toca,
   que sus cuatro columnas son las de las otras ocho apps de VA.

   Los cortes son los del kit (1080 a dos columnas, 720 a una) más uno propio a 1400: abajo
   de eso van tres columnas, porque la quinta parte de una pantalla más angosta ya no
   alcanza para una cifra de esta cartera. Con cinco columnas la fila se hace más densa (el
   aire entre tarjetas y el de adentro bajan a 14px) y el número a 24px: así "$ 117.552.711"
   entra en un solo renglón desde 1401 px para arriba, con margen. La cuenta está hecha
   sobre trece caracteres, que es lo que mide hoy la cifra más larga de la casa: el día que
   la facturación pase a los miles de millones hay que volver a medir acá.

   El segundo selector es por conteo: cualquier fila de KPIs de esta app que llegue a cinco
   se acomoda igual, aunque quien la escribió no supiera de esta clase. */
.grid--kpi5,
.grid--kpi:has(> :nth-child(5)) { grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 14px; }
.grid--kpi5 .kpi__value,
.grid--kpi:has(> :nth-child(5)) .kpi__value { font-size: 24px; }
.grid--kpi5 .kpi, .grid--kpi5 .card__body,
.grid--kpi:has(> :nth-child(5)) .kpi { padding: 14px; }
@media (max-width: 1400px) {
  .grid--kpi5,
  .grid--kpi:has(> :nth-child(5)) { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }
  .grid--kpi5 .kpi__value,
  .grid--kpi:has(> :nth-child(5)) .kpi__value { font-size: 30px; }
  /* Vuelven las medidas del kit: 16px 18px la tarjeta de KPI, 18px el cuerpo de una card. */
  .grid--kpi5 .kpi,
  .grid--kpi:has(> :nth-child(5)) .kpi { padding: 16px 18px; }
  .grid--kpi5 .card__body { padding: 18px; }
}
@media (max-width: 1080px) {
  .grid--kpi5,
  .grid--kpi:has(> :nth-child(5)) { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 720px) {
  .grid--kpi5,
  .grid--kpi:has(> :nth-child(5)) { grid-template-columns: minmax(0, 1fr); }
}

/* SEIS tarjetas: el radar de auspicios, donde las dos primeras son de qué entra por mes (la
   pauta que se renueva y los eventos que no) y las cuatro siguientes son el trabajo
   pendiente. Las seis son filtros, así que van en una sola fila cuando la pantalla da: una
   segunda fila de tarjetas-filtro se lee como otro grupo de cosas, y no lo es.

   El corte a tres columnas es a 1560 y no a 1400 como el de cinco, y la cuenta es la misma
   de aquel: la cifra más larga de esta pantalla es el ingreso recurrente, trece caracteres
   ("$ 218.560.812"), y abajo de ese ancho la sexta parte de la pantalla la parte en dos
   renglones. Tres y tres es el reparto que queda: dos filas de tres, y la de arriba son
   justo las dos de plata más la primera del trabajo. */
.grid--kpi6 { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 12px; }
.grid--kpi6 .kpi__value { font-size: 22px; }
.grid--kpi6 .card__body { padding: 13px; }
@media (max-width: 1560px) {
  .grid--kpi6 { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 18px; }
  .grid--kpi6 .kpi__value { font-size: 30px; }
  .grid--kpi6 .card__body { padding: 18px; }
}
@media (max-width: 1080px) {
  .grid--kpi6 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 720px) {
  .grid--kpi6 { grid-template-columns: minmax(0, 1fr); }
}

/* En pantalla angosta la topbar se queda con lo esencial: la marca, la frescura y
   salir. El nombre de quien está adentro ya está en la barra lateral. */
@media (max-width: 900px) {
  .topbar__title, .topbar__sep { display: none; }
}
/* La frescura no se esconde en mobile: se recorta. Un número sin la hora del dato al
   lado es exactamente el problema que este portal viene a resolver. */
@media (max-width: 720px) {
  .topbar__quien { display: none; }
  .topbar__frescura { max-width: 46vw; overflow: hidden; padding: 4px 6px; }
  .topbar__frescura span { overflow: hidden; text-overflow: ellipsis; }
}

/* ---------------------------------------------------------------------------
   Barras apiladas (Charts.barrasApiladas), el dibujo del forecast.

   Viven acá y no en kit.css por la misma razón que la leyenda filtrable de arriba: el
   kit define cómo se ve un gráfico (.vachart, los tonos, el tooltip), pero quien DIBUJA
   es charts.js, que es propio de esta app. Un estilo de un dibujo que el kit no hace no
   puede vivir en el kit.
   --------------------------------------------------------------------------- */
.vachart__barra { fill: var(--serie-1); }
.vachart__barra[data-tono="marca"] { fill: var(--marca); }
.vachart__barra[data-tono="ok"]    { fill: var(--ok); }
.vachart__barra[data-tono="warn"]  { fill: var(--atencion); }
.vachart__barra[data-tono="info"]  { fill: var(--info); }
/* El segmento con trama: el rayado se dibuja con el color de la serie, así que "sin fecha
   de fin" es la misma plata que "con fecha de fin" y no una categoría nueva. */
.vachart__trama { stroke: var(--serie-1); stroke-width: 3.2; }
.vachart__trama[data-tono="marca"] { stroke: var(--marca); }
.vachart__trama[data-tono="ok"]    { stroke: var(--ok); }
.vachart__trama[data-tono="warn"]  { stroke: var(--atencion); }
.vachart__trama[data-tono="info"]  { stroke: var(--info); }
/* El corte entre lo facturado y lo firmado. Es la única marca del gráfico que no es un
   dato, y por eso va tenue y punteada: no compite con ninguna barra. */
.vachart__corte { stroke: var(--tinta-tenue); stroke-width: 1; stroke-dasharray: 3 4; }
.vachart__corte-lbl { fill: var(--acento); letter-spacing: .08em; text-transform: uppercase; font-size: 11px; }

/* La zona de clic de cada columna. Es transparente y del ancho del paso: lo que se abre es
   el mes, no la barra, así que el blanco de arriba de una barra chica también abre. El hover
   pinta apenas la columna para que se entienda que es cliqueable antes de tocarla, y el foco
   del teclado se dibuja con el borde propio, porque `outline` sobre un rect de SVG lo
   recortan los navegadores contra el viewBox. */
.vachart__zona { fill: transparent; cursor: pointer; }
.vachart__zona:hover { fill: color-mix(in srgb, var(--tinta) 6%, transparent); }
.vachart__zona:focus { outline: none; }
.vachart__zona:focus-visible {
  fill: color-mix(in srgb, var(--tinta) 6%, transparent);
  stroke: var(--acento); stroke-width: 2; stroke-dasharray: 4 3;
}

/* El detalle de un mes, agrupado por programa. Cada bloque es un programa con su plata, y la
   barra de proporción de abajo del título dice cuánto del mes es suyo sin tener que comparar
   cifras. El color sale del token de acento del programa, el mismo que usa el punto de la
   Cartera: si mañana cambia el color de El Campo, cambia acá también. */
/* El token es `--borde` y no `--linea`, que no existe en ninguna hoja: una custom property
   indefinida hace inválida la declaración entera y el borde cae a `none`, sin que nada avise.
   Con los nueve bloques plegados, esa línea es la única separación entre un programa y el
   siguiente, y en el teléfono, donde el monto se va a un segundo renglón, sin ella no se sabe
   si una cifra cierra el bloque de arriba o encabeza el de abajo. */
.fc-prog { margin-bottom: 12px; border-top: 1px solid var(--borde); }
.fc-prog:last-of-type { border-bottom: 1px solid var(--borde); }

/* La fila del resumen es lo único que se ve con el bloque plegado, así que lleva todo lo que
   hace falta para comparar programas sin abrir ninguno: el nombre, cuántos contratos, qué
   parte del mes es, la plata y la barra. El marcador propio del navegador se saca en los dos
   dialectos (`list-style` para todos, `::-webkit-details-marker` para Safari), o queda un
   triangulito al lado del galón del kit. */
.fc-prog__sum { display: block; padding: 10px 0 8px; cursor: pointer; list-style: none; }
.fc-prog__sum::-webkit-details-marker { display: none; }
.fc-prog__sum:hover .fc-prog__nombre { opacity: .85; }
.fc-prog__sum:focus { outline: none; }
.fc-prog__sum:focus-visible { outline: 2px solid var(--acento); outline-offset: 2px; border-radius: var(--radio-sm); }

/* El galón gira cuando el bloque está abierto: es la única señal de que hay algo adentro
   antes de tocarlo. Con `prefers-reduced-motion` gira igual, sin transición. */
.fc-prog__galon { display: inline-flex; color: var(--tinta-tenue); transition: transform .15s ease; }
.fc-prog[open] .fc-prog__galon { transform: rotate(180deg); }
.fc-plegar.is-on .ico { transform: rotate(180deg); }
.fc-plegar .ico { transition: transform .15s ease; }
@media (prefers-reduced-motion: reduce) {
  .fc-prog__galon, .fc-plegar .ico { transition: none; }
}

/* La línea de arriba del panel: la explicación a la izquierda y el abrir todos a la derecha. */
.fc-panel__top { display: flex; align-items: flex-start; gap: 16px; }
.fc-panel__top .ayuda { flex: 1; }
/* La densidad la pone `.btn--sm` del kit; acá sólo se declara que no se estira. */
.fc-plegar { flex: none; }

.fc-prog__head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; margin-bottom: 6px; }
.fc-prog__nombre { font-weight: 600; }
.fc-prog__monto { margin-left: auto; font-variant-numeric: tabular-nums; font-weight: 600; }
.fc-prog__parte { color: var(--tinta-tenue); font-size: 12px; font-variant-numeric: tabular-nums; }
/* El riel de la barra tenía el mismo token inventado, así que computaba transparente: la
   barra del 6% era una rayita corta sin nada contra qué medirse, que es justo lo que la barra
   existe para evitar. */
.fc-prog__barra { display: block; height: 4px; border-radius: 2px; background: var(--borde); overflow: hidden; }
.fc-prog__barra > i { display: block; height: 100%; border-radius: 2px; }
/* La cifra no se parte nunca: con la columna angosta, "$ 31.700.000" se cortaba después del
   signo y quedaba el peso solo arriba del número. Un monto en dos renglones se lee dos veces
   antes de creerle. */
.fc-prog .tbl td.num { white-space: nowrap; }

/* La muestra de la leyenda cuando la serie va con trama. El mismo rayado del gráfico, para
   que se puedan atar sin pasar el mouse. El COLOR lo pone la vista por `style` (el mismo
   token que le pasa a la serie), así que acá no hay variantes por tono: una muestra que no
   coincide con su barra es peor que no tener leyenda. */
.vachart-leyenda .leg__trama {
  width: 18px; height: 11px; border-radius: 2px;
  background-image: repeating-linear-gradient(45deg,
    currentColor 0 3px, transparent 3px 6px);
}
.vachart-leyenda .leg__solid--relleno { height: 11px; border: 0; border-radius: 2px; }

/* La cifra titular del forecast: el número del mes que viene es el que se viene a mirar,
   así que se lee antes que los otros tres. El resto de las tarjetas queda igual, porque si
   se destacan las cuatro no se destaca ninguna. */
.kpi__value--titular { color: var(--acento); font-size: 30px; }
/* La tabla del forecast mezcla dos cosas: los meses que se facturaron de verdad y los que
   dicen los contratos. Se distinguen o se leen como una sola serie, y ahí el forecast pasa
   por facturación. */
.tbl .fila--historia th, .tbl .fila--historia td { color: var(--tinta-suave); }
.tbl .fila--corte th {
  background: var(--superficie-alt); color: var(--tinta-tenue);
  font-size: 11.5px; letter-spacing: .06em; text-transform: uppercase; font-weight: 700;
  padding-top: 7px; padding-bottom: 7px;
}

/* Un segmento de siete opciones no entra en un teléfono, y `.seg` es inline-flex con los
   botones en nowrap: sin este envoltorio estira la tarjeta entera y con ella la tabla, y la
   página termina con 729px de scroll horizontal (medido a 390px de ancho). Scrollea él, como
   hace .tbl-wrap con una tabla ancha, en vez de romper el ancho de la página. */
.seg-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.seg-wrap > .seg { max-width: none; }

/* El segmento que scrollea avisa que hay más a la derecha. Con ocho cajones, a 1460px los dos
   últimos quedan fuera de vista y nada lo dice: un scroll sin borde difuminado se lee como
   "esto es todo". La máscara difumina la última franja, así que el corte se ve. */
.seg-wrap {
  -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
  mask-image: linear-gradient(to right, #000 calc(100% - 24px), transparent 100%);
}

/* LA CELDA DE PLATA QUE ABRE SU RECORTE (el tablero: la deuda y el vencido de un programa).
   Se pinta como el resto del número y no como un link azul: son 16 celdas por pantalla, y
   subrayadas y en color la tabla deja de leerse como una tabla de números. El subrayado
   aparece al pasar por encima, que es donde alguien está por hacer clic. */
.tbl td .celda-link { color: inherit; text-decoration: none; }
.tbl td .celda-link:hover { text-decoration: underline; }
.tbl td .celda-link:focus-visible { outline: 2px solid var(--marca); outline-offset: 2px; border-radius: 2px; }

/* La columna de la casilla de la tanda, en la cartera. Angosta y centrada: es un control, no
   un dato, y con el ancho de una columna de texto queda la casilla flotando a la izquierda. */
.tbl th.col-elegir, .tbl td.col-elegir { width: 36px; text-align: center; padding-left: 8px; padding-right: 0; }
.tbl td.col-elegir input { cursor: pointer; }

/* El detalle de lo que no salió en una tanda. Es una lista adentro de un banner, así que no
   lleva los márgenes de una lista suelta. */
.lista-simple { margin: 4px 0 0; padding-left: 18px; }
.lista-simple li { margin: 2px 0; }

/* ===========================================================================
   LA CARTERA ABIERTA POR PROGRAMA
   ===========================================================================
   Un bloque plegable por programa, con su resumen siempre a la vista. El gesto es el mismo
   que el de los programas del Forecast (`.fc-prog`): `<details>` sin el marcador del
   navegador, galón que gira, línea arriba de cada bloque. Cuando las dos ramas estén en
   main conviene unificarlos en una sola clase; hasta entonces son dos porque nacieron en
   paralelo, y dos plegados que se ven distinto en la misma app es peor que la duplicación.

   El rótulo del agrupador en la barra de filtros. Va delante del segmento porque agrupar no
   es filtrar: sin él, el que lo toca espera que la lista se achique (patrón 10 del kit). */
.filtros-bar__rot { color: var(--tinta-tenue); font-size: 13px; align-self: center; }

.gr { border-top: 1px solid var(--borde); }
.gr:last-of-type { border-bottom: 1px solid var(--borde); }

/* La fila del resumen es lo único que se ve con el bloque plegado, así que lleva todo lo que
   hace falta para comparar programas sin abrir ninguno: el nombre, cuántos clientes, la
   deuda, lo vencido y la barra de proporción. En grilla y no en flex para que las cinco
   columnas queden alineadas entre un programa y el siguiente: con flex, los montos bailan
   según el largo del nombre y dejan de compararse de un vistazo, que es lo único que se les
   pide. El marcador propio del navegador se saca en los dos dialectos (`list-style` para
   todos, `::-webkit-details-marker` para Safari) o queda un triangulito al lado del galón. */
.gr__sum {
  display: grid;
  grid-template-columns: 16px minmax(140px, 1fr) auto minmax(120px, auto) minmax(120px, auto) 64px;
  align-items: center;
  gap: 12px;
  padding: 12px 16px;
  cursor: pointer;
  list-style: none;
}
.gr__sum::-webkit-details-marker { display: none; }
.gr__sum:hover { background: var(--superficie-alt); }
.gr__sum:focus { outline: none; }
.gr__sum:focus-visible { outline: 2px solid var(--foco); outline-offset: -2px; }

/* El galón gira cuando el bloque está abierto: es la única señal de que hay algo adentro
   antes de tocarlo. Con `prefers-reduced-motion` gira igual, sin transición. */
.gr__galon { display: inline-flex; color: var(--tinta-tenue); transition: transform .15s ease; }
.gr[open] .gr__galon { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .gr__galon { transition: none; } }

.gr__nombre { display: flex; align-items: center; gap: 8px; font-weight: 600; }
.gr__n { color: var(--tinta-tenue); font-size: 13px; }
/* Las dos cifras, con cifras de ancho fijo y alineadas a la derecha: es la comparación que
   hace toda la lectura de esta pantalla, y con el ancho variable de la fuente los millones de
   un programa no se pueden apilar contra los del de abajo. */
.gr__monto, .gr__venc { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.gr__monto { font-weight: 600; }
.gr__venc { color: var(--tinta-suave); font-size: 13px; }

/* La barra dice qué parte es cada programa del más grande de los que se ven. El riel usa
   --borde (un token que existe): con un token inventado la declaración entera es inválida y
   el riel se vuelve transparente, así que la barra del 6% queda como una rayita corta sin
   nada contra qué medirse. */
.gr__barra { display: block; height: 4px; border-radius: 2px; background: var(--borde); overflow: hidden; }
.gr__barra > i { display: block; height: 100%; border-radius: 2px; }

/* La tabla del grupo va pegada al borde del bloque, como la de la lista de corrido. */
.gr__body > .tbl-wrap { border-top: 1px solid var(--borde-suave); }

/* En pantalla angosta el resumen se apila: el nombre arriba y los números abajo, en vez de
   comprimir cinco columnas hasta que la cifra se parta en dos renglones. Un monto en dos
   renglones se lee dos veces antes de creerle. */
@media (max-width: 720px) {
  .gr__sum { grid-template-columns: 16px 1fr auto; row-gap: 4px; }
  .gr__monto { grid-column: 2; text-align: left; }
  .gr__venc { grid-column: 3; }
  .gr__barra { grid-column: 1 / -1; }
}
