/* ============================================================
   SMAG — Design System: componentes (Fase 3)
   Se carga DESPUÉS de style.css. Todo valor de color, espaciado,
   radio y sombra sale de tokens.css — aquí no se escribe ningún
   valor a mano.

   Los nombres de clase son los que ya usaban las plantillas y los
   routers (.btn, .card, .badge-verde…): el aspecto cambia, el
   contrato no. Así ninguna plantilla ni lógica de negocio tuvo
   que tocarse.
   ============================================================ */


/* ============================================================
   1. BOTONES
   El verde de marca es EXCLUSIVAMENTE el primario de acciones.
   ============================================================ */

button,
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  min-height: 38px;
  background: var(--color-primary);
  color: var(--color-white);
  border: 1px solid var(--color-primary);
  border-radius: var(--radius-sm);
  font-family: inherit;
  font-size: var(--text-base);
  font-weight: 600;
  line-height: 1.2;
  cursor: pointer;
  text-decoration: none;
  text-align: center;
  white-space: nowrap;
  transition: background-color 0.12s ease, border-color 0.12s ease, color 0.12s ease;
}

button:hover,
.btn:hover {
  background: var(--color-primary-hover);
  border-color: var(--color-primary-hover);
  color: var(--color-white);
}

/* Anillo de foco único para todo el sistema. Se usa :focus-visible
   para que solo aparezca al navegar con teclado, no al hacer clic. */
button:focus-visible,
.btn:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* Deshabilitado en gris neutro, no en verde lavado: se lee de inmediato
   que la acción no está disponible, sin depender del matiz del color. */
button:disabled,
.btn:disabled,
.btn.disabled,
button[aria-disabled="true"],
.btn[aria-disabled="true"] {
  background: var(--color-gray-100);
  border-color: var(--color-border);
  color: var(--color-gray-500);
  cursor: not-allowed;
  pointer-events: none;
}

/* --- Variante secundaria: acción disponible pero no la principal.
   Superficie blanca con borde en vez del gris macizo anterior. --- */
.btn-secundario {
  background: var(--color-white);
  border-color: var(--color-border);
  color: var(--color-ink);
}

.btn-secundario:hover {
  background: var(--color-fila-hover);
  border-color: var(--color-gray-500);
  color: var(--color-ink);
}

/* --- Variante peligro: acciones destructivas (eliminar, anular) --- */
.btn-peligro {
  background: var(--color-danger);
  border-color: var(--color-danger);
  color: var(--color-white);
}

.btn-peligro:hover {
  background: #a5301f;
  border-color: #a5301f;
  color: var(--color-white);
}

.btn-peligro:focus-visible {
  box-shadow: var(--focus-ring-danger);
}

/* --- Tamaño compacto: acciones dentro de filas de tabla --- */
.btn-pequeno {
  padding: var(--space-1) var(--space-3);
  min-height: 30px;
  font-size: var(--text-sm);
}

.btn.full,
button.full {
  width: 100%;
  margin-top: var(--space-5);
  /* **UN BOTON A TODO LO ANCHO SI PARTE LINEA** (20/08/2026). La regla
     general de `button` lleva `white-space: nowrap`, que esta bien para un
     boton corto en una barra: partirlo en dos lo desalinea todo.

     Aqui hace lo contrario. Medido en la pagina publica de preferencias:
     «Cancelar todas las comunicaciones comerciales» ocupa 320 px dentro de
     una caja de 316, y el texto se sale del borde — en cualquier pantalla,
     no solo en las estrechas. Es la unica pagina que ve un cliente. */
  white-space: normal;
}

/* El botón-enlace de las pantallas de login/recuperación es un
   primario más: se unifica en vez de mantener su propio estilo. */
.button-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  margin-top: var(--space-5);
  padding: var(--space-2) var(--space-4);
  min-height: 38px;
  background: var(--color-primary);
  color: var(--color-white);
  border: 1px solid var(--color-primary);
  border-radius: var(--radius-sm);
  font-size: var(--text-base);
  font-weight: 600;
  text-decoration: none;
}

.button-link:hover {
  background: var(--color-primary-hover);
  border-color: var(--color-primary-hover);
}

.button-link:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* Los iconos dentro de un botón heredan el color del texto y no
   deben deformar la línea base. */
.btn .icon,
button .icon {
  flex-shrink: 0;
}

.acciones {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  align-items: center;
}

/* Dos acciones que NO se parten de línea, para celdas de tabla.

   `.acciones` envuelve a propósito —en una cabecera con seis botones eso es
   lo correcto—, pero dentro de una celda estrecha convierte dos botones en
   una columna. Vladimir, 31/08/2026: «los botones de correo y whatsapp
   ponlos uno al lado de otro, no arriba de otro».

   La celda se ensancha lo que haga falta, que es más barato que dos filas
   de alto en cada renglón de la tabla. */
/* Un correo que nadie ha abierto, en la bandeja.

   **En negrita y no en color**: el color ya significa otra cosa en las
   tablas del sistema —estado— y usarlo aquí haría que «sin leer» compitiera
   con «vencida» o «escalada». La negrita es lo que hace cualquier programa
   de correo, así que no hay que aprenderlo. */
tr.fila-sin-leer td { font-weight: 600; }
tr.fila-sin-leer .identidad-principal { font-weight: 700; }

.acciones--juntas {
  flex-wrap: nowrap;
  justify-content: flex-end;
  white-space: nowrap;
}


/* ============================================================
   2. FORMULARIOS: inputs, selects, textareas
   ============================================================ */

label {
  display: block;
  text-align: left;
  margin-top: var(--space-4);
  margin-bottom: var(--space-1);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-ink);
}

input,
select,
textarea {
  width: 100%;
  padding: var(--space-2) var(--space-3);
  min-height: 38px;
  margin-top: 0;
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  color: var(--color-ink);
  font-size: var(--text-lg);
  font-family: inherit;
  box-sizing: border-box;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

input::placeholder,
textarea::placeholder {
  color: var(--color-gray-500);
}

input:hover:not(:disabled),
select:hover:not(:disabled),
textarea:hover:not(:disabled) {
  border-color: var(--color-gray-500);
}

input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: var(--focus-ring);
}

input:disabled,
select:disabled,
textarea:disabled,
input[readonly],
textarea[readonly] {
  background: var(--color-gray-50);
  color: var(--color-gray-500);
  cursor: not-allowed;
}

textarea {
  resize: vertical;
  min-height: 90px;
  padding: var(--space-3);
  line-height: 1.5;
}

/* Campo en error: se marca el borde además del mensaje, para no
   depender solo del color rojo del texto. */
input.campo-error,
select.campo-error,
textarea.campo-error,
[aria-invalid="true"] {
  border-color: var(--color-danger);
}

input.campo-error:focus,
select.campo-error:focus,
textarea.campo-error:focus,
[aria-invalid="true"]:focus {
  box-shadow: var(--focus-ring-danger);
}

/* Cuando la etiqueta envuelve a su control (la forma más robusta de
   asociarlos dentro de un bucle, donde inventar ids únicos es frágil),
   el texto va en su propio span para conservar el salto de línea. */
.etiqueta-texto {
  display: block;
  margin-bottom: var(--space-1);
}

/* Rótulo de un GRUPO de casillas: no es un <label> porque no apunta a
   un control concreto. */
.etiqueta-grupo {
  display: block;
  margin-top: var(--space-4);
  margin-bottom: var(--space-1);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-ink);
}

/* Texto de ayuda bajo un campo.

   `font-weight: normal` explícito y no heredado: estas frases viven dentro de
   un `<label>`, que va en seminegrita, así que heredaban el peso de la
   etiqueta y salían tan marcadas como ella (Vladimir, 16/08/2026: «esa
   tipografía también es fea»). Una ayuda tiene que leerse por debajo del
   campo, no competir con él. */
.campo-ayuda {
  display: block;
  margin-top: var(--space-1);
  font-size: var(--text-xs);
  font-weight: normal;
  color: var(--color-ink-soft);
}

/* --- EL DATO SECUNDARIO DE UNA CELDA ---
   Vladimir, 23/08/2026: «el selector de color o sombreado no cubre la
   linea completa sino que se corta».

   **La mancha blanca de la tabla de campanas era esto.** Esas celdas
   llevaban `campo-ayuda`, que es el texto de ayuda que va DEBAJO de un
   campo de formulario y trae `display: block`. Un `<td>` en `display:
   block` deja de ser una celda: el navegador fabrica una celda anonima
   para tapar el hueco que queda en la fila, y esa celda anonima no
   recibe el sombreado del `hover` — de ahi el rectangulo blanco que se
   corta a mitad de la fila.

   Se ve SOLO al pasar el raton, que es justo cuando nadie esta haciendo
   una captura, y estaba en las cinco pantallas del modulo de correo. La
   clase existia y se veia bien; lo que no se sostiene es usarla en una
   celda.

   Mismo aspecto que `campo-ayuda` —pequeno y apagado— y sin tocar el
   `display`: una celda ya sabe como colocarse. --- */
td.celda-menor,
th.celda-menor {
  font-size: var(--text-xs);
  font-weight: normal;
  color: var(--color-ink-soft);
}

.campo-mensaje-error {
  display: block;
  margin-top: var(--space-1);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-danger);
}

/* --- Select: flecha propia para que se vea igual en todos los
   navegadores. Se excluyen los múltiples y los de varias filas,
   que necesitan el control nativo. --- */
select:not([multiple]):not([size]) {
  appearance: none;
  -webkit-appearance: none;
  -moz-appearance: none;
  padding-right: var(--space-6);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%234a5570' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-3) center;
}

select:not([multiple]):not([size]):disabled {
  background-color: var(--color-gray-50);
}

/* --- Casillas y radios: recuperan su tamaño natural (la regla
   global de ancho 100% los deformaba). --- */
input[type="checkbox"],
input[type="radio"] {
  width: auto;
  min-height: 0;
  accent-color: var(--color-primary);
  cursor: pointer;
}

input[type="file"] {
  padding: var(--space-2);
  font-size: var(--text-sm);
  cursor: pointer;
}

/* --- Campo de archivo con botón propio ---
   El input real se oculta a la vista pero NO se saca del documento: así
   conserva el foco de teclado y la validación nativa del navegador. Su
   <label> es el botón que se ve. */
.campo-archivo {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

/* Selector doble a propósito: `input[type="file"]` (elemento+atributo)
   tiene más especificidad que una clase suelta, y su relleno impedía
   que el control encogiera — se quedaba visible en 16x38px.
   `padding: 0` y `min-height: 0` son igual de necesarios: con
   box-sizing:border-box el relleno marca un ancho mínimo. */
.campo-archivo .campo-archivo-input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  min-height: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

.campo-archivo label {
  margin: 0;
  cursor: pointer;
}

/* El anillo de foco tiene que verse en el BOTÓN cuando el input oculto
   recibe el foco por teclado; si no, no se sabría dónde se está. */
.campo-archivo .campo-archivo-input:focus-visible + label {
  outline: none;
  box-shadow: var(--focus-ring);
}

.campo-archivo-nombre {
  color: var(--color-ink-soft);
  font-size: var(--text-sm);
  overflow-wrap: anywhere;
}

input[type="color"] {
  padding: var(--space-1);
  height: 38px;
  cursor: pointer;
}

.checkbox-line {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-4);
}

.checkbox-line label {
  margin: 0;
  font-weight: 400;
  cursor: pointer;
}

.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 var(--space-5);
}

.form-grid .full-width {
  grid-column: 1 / -1;
}

@media (max-width: 700px) {
  .form-grid {
    grid-template-columns: 1fr;
  }
}


/* ── CANTIDAD Y PRECIO SE EDITAN EN LA FILA ──────────────────────────
   Antes había que abrir el menú de tres puntos de cada línea: con 21
   líneas, 21 aperturas. «Es muuuy incómodo.»

   **Los anchos salen de una medición, no de una intuición.** Sobre la
   cotización real de 21 líneas, a 1366: con 56 y 86 px la tabla vuelve a
   sus 719 px exactos —desborde 0— y ningún valor se corta. Con 70/110
   desbordaba 21 px. Es la misma lección de la barra de filtros: cuando
   falta sitio se bajan las BASES, y se bajan las del elemento que sobra,
   no las del contenido que hay que leer. */
/* **La tabla de líneas es la más apretada del sistema:** siete columnas en
   la caja estrecha de la ficha —719 px a 1366—, y en BORRADOR lleva además
   la columna de acciones que la enviada no tiene.

   MEDIDO sobre el borrador REAL de producción, de 21 líneas: **ya desbordaba
   31 px antes de tocar nada**, y los campos añaden 28. El sitio se recupera
   del RELLENO de las columnas de cifras —16 px por lado, y les sobra—, que
   es el paso 2 de la regla: nunca del nombre del artículo, ni de los campos
   editables, ni de la columna de los tres puntos, que es con lo único que se
   trabaja. */
.tabla-lineas .col-numero {
  padding-left: var(--space-2);
  padding-right: var(--space-2);
}

.campo-en-linea {
  min-height: 30px;
  padding: 2px var(--space-2);
  font-size: var(--text-base);
  text-align: right;
}

.campo-en-linea-cantidad {
  width: 56px;
}

.campo-en-linea-precio {
  width: 86px;
}

/* El aviso de la línea, debajo del nombre del artículo: es donde hay sitio
   sin ensanchar ninguna columna de cifras. Vacío no ocupa nada, así que la
   tabla no salta al aparecer y desaparecer. */
.aviso-linea:empty {
  display: none;
}

.aviso-linea {
  display: block;
  margin-top: 2px;
  color: var(--color-danger);
  font-size: var(--text-xs);
}

/* ── «GUARDADO», y se va solo (25/08/2026) ──────────────────────────
   No hay boton de guardar en esta tabla —cada campo se guarda al salir de
   el, que es lo que permite tabular 21 lineas sin pulsar nada— y **un
   guardado mudo es indistinguible de uno que no ocurrio**. Vladimir:
   «estoy editando una cotizacion pero no veo boton para guardar los
   cambios».

   Se desvanece con una animacion, SIN temporizador en JavaScript: HTMX
   sustituye el elemento entero en cada guardado, asi que la animacion
   arranca de cero sola y no hay nada que cancelar ni que se solape.

   `forwards` es lo que lo deja invisible al terminar; sin el volveria a
   aparecer al acabar la animacion, que es peor que no ponerla. */
.aviso-linea-ok {
  color: var(--color-success);
  animation: aviso-se-va 2.4s ease-out forwards;
}

@keyframes aviso-se-va {
  0%, 55% { opacity: 1; }
  100%    { opacity: 0; }
}

/* Quien pidio no ver animaciones ve el «Guardado» fijo, no parpadeando:
   la confirmacion no puede depender de un efecto que esa persona apago. */
@media (prefers-reduced-motion: reduce) {
  .aviso-linea-ok { animation: none; opacity: 1; }
}

/* El boton que sigue al aviso de «hay cambios sin enviar». Separado de la
   caja del aviso, no dentro: el aviso es una pieza compartida por todas las
   pantallas y meterle acciones propias la convertiria en otra cosa. */
.acciones-tras-aviso {
  margin: calc(var(--space-3) * -1) 0 var(--space-4);
}


/* ── AGREGAR ARTÍCULO: los importes y el botón, en UNA fila (C6) ─────
   Antes eran cuatro campos apilados con el botón al fondo, así que añadir
   una línea costaba bajar con el scroll, pulsar y volver a subir. En una
   cotización de diez líneas, treinta viajes.

   **Mismas bases que la barra de filtros, y por la misma razón:** el
   reparto en líneas usa el tamaño BASE de cada elemento, no el que tendría
   ya encogido. Bajar `min-width` no basta — si las bases suman más que la
   línea, envuelve igual. */
.agregar-item-fila {
  display: flex;
  align-items: flex-end;
  gap: var(--space-3);
  flex-wrap: wrap;
}

/* Nunca lleva más de cuatro cifras: darle el mismo ancho que a un importe
   es regalar sitio donde no hace falta. */
.agregar-item-fila .campo-cantidad {
  flex: 0 0 88px;
}

/* Precio y descuento se reparten lo que queda. */
.agregar-item-fila .campo-importe {
  flex: 1 1 150px;
  min-width: 0;
}

/* El botón toma su ancho natural y NO encoge: partido en dos líneas deja
   de leerse como un botón. */
.agregar-item-fila > .btn {
  flex: 0 0 auto;
}

/* ── EL BUSCADOR NO SE COME LA FILA ────────────────────────────────
   Cuando el autocompletar comparte fila con el botón, se le pone un
   tope: es un campo donde se escriben tres letras, no un párrafo.
   Sin el tope se estira hasta el borde y el botón queda desterrado al
   otro extremo de la pantalla, lejos de donde está la mano.
   `min-width: 0` es lo que permite que ENCOJA en una pantalla
   estrecha: sin él, el ancho base del input impide que la fila se
   parta y el botón se sale. */
.agregar-item-fila > .autocomplete {
  flex: 1 1 320px;
  max-width: 520px;
  min-width: 0;
}

/* Los márgenes de `label`/`input` dentro de la fila los pone el contenedor. */
.agregar-item-fila label {
  margin-top: 0;
}

/* ── El «RD$» DENTRO del campo ──────────────────────────────────────
   Sale del rótulo y entra aquí: recupera ancho para la cifra y queda
   donde de verdad se necesita leer. */
.campo-con-prefijo {
  position: relative;
  display: block;
}

.campo-con-prefijo .prefijo-campo {
  position: absolute;
  left: var(--space-3);
  top: 50%;
  transform: translateY(-50%);
  color: var(--color-gray-500);
  font-size: var(--text-sm);
  pointer-events: none;
}

.campo-con-prefijo input {
  /* Sitio para el prefijo, sin que la cifra se le monte encima. */
  padding-left: 2.6rem;
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}

/* «(opcional)» junto al rótulo, en el tono de ayuda: es una precisión del
   campo, no otra etiqueta. */
.campo-ayuda-inline {
  color: var(--color-gray-500);
  font-weight: 400;
  /* Al MISMO tamaño que su rótulo: es una precisión del campo, no otro
     nivel de información. Sin esto hereda 13 px donde el rótulo va a 14 y
     quedan dos tamaños en la misma palabra. */
  font-size: inherit;
}


/* ── PANEL «AGREGAR ARTÍCULO»: TEXTO MÁS GRANDE Y RITMO PAREJO ───────
   Pedido por Vladimir dos veces: «los textos están muy pequeños».

   **Va acotado al panel a propósito.** Los rótulos salen del selector
   GLOBAL `label`, que gobierna las 48 pantallas del sistema: subirlo ahí
   sería un cambio de tipografía general, y eso no es lo que se pidió ni
   se ha aprobado. Aquí sube solo donde se captura, que es donde se pasa
   el rato.

   El ritmo: antes había 16 px entre el título y el primer campo —el
   `margin-top` del `label`— y las dos filas quedaban pegadas. Ahora se
   reparte parejo: el primer rótulo no separa de más y las filas
   respiran. */
#agregar .panel-cuerpo label {
  font-size: var(--text-base);
}

#agregar .bandeja-cab h2 {
  font-size: var(--text-xl);
}

/* El primer rótulo no necesita separarse del título: ya lo hace el relleno
   del cuerpo. Sin esto quedaban 40 px de aire arriba y 16 entre las filas.
   El `form` va en medio, por eso el selector lo atraviesa. */
#agregar .panel-cuerpo form > .autocomplete > label,
#agregar .agregar-item-fila.fila-buscar > .autocomplete > label {
  margin-top: 0;
}

/* Y las dos filas, separadas de verdad. */
#agregar .agregar-item-fila {
  margin-top: var(--space-4);
}

/* Salvo la del buscador, que es la primera de todas. */
#agregar .agregar-item-fila.fila-buscar {
  margin-top: 0;
}

/* El «RD$» al tamaño de la cifra que acompaña: a 13 px junto a un número
   de 16 se leía como una nota al margen, no como parte del importe. */
#agregar .prefijo-campo {
  font-size: var(--text-base);
}

/* --- Barra de filtros de los listados --- */
.filtros {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
  margin-bottom: var(--space-5);
  align-items: flex-end;
}

.filtros label {
  margin-top: 0;
}

.filtros > div {
  min-width: 160px;
}

.filtros button {
  margin-top: var(--space-1);
}


/* ============================================================
   3. TABLAS
   ============================================================ */

table {
  width: 100%;
  border-collapse: collapse;
  background: var(--color-white);
  border-radius: var(--radius-md);
  overflow: hidden;
  font-size: var(--text-base);
}

/* **FILAS MÁS JUNTAS** (Vladimir, 17/08/2026: «los espacios entre artículos
   son muy grandes, eso empuja mucho la página hacia abajo; haz lo mismo en
   cualquier otro lugar que sean del mismo tamaño»).

   Y no es cuestión de gusto: en un plan de 16 artículos, cada fila de más
   obliga a desplazarse, y desplazarse es perder de vista los totales. Una
   tabla de trabajo se lee mejor densa; el aire ayuda a leer un párrafo, no
   una lista que se recorre comparando.

   Se toca el vertical y NO el horizontal: apretar los lados juntaría los
   números con el borde de su columna. */
th,
td {
  text-align: left;
  padding: var(--space-2) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  vertical-align: middle;
}

/* Un campo dentro de una celda no necesita el alto de un formulario: ahí
   estira la fila entera, que es de donde venía la mitad del espacio. */
td input[type="number"],
td input[type="text"],
td select {
  padding-top: var(--space-1);
  padding-bottom: var(--space-1);
  /* **`min-height` mandaba sobre el relleno** y por eso bajarlo no cambió
     nada: los campos del sistema tienen 38 px mínimos —correcto en un
     formulario, donde se pulsan con el dedo— y dentro de una tabla eso
     estira cada fila. Aquí el campo va en una lista que se recorre leyendo,
     no rellenando, así que 30 px bastan y siguen siendo cómodos. */
  min-height: 30px;
}

th {
  background: var(--color-gray-50);
  color: var(--color-ink-soft);
  font-weight: 600;
  font-size: var(--text-sm);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  white-space: nowrap;
}

tbody tr:last-child td {
  border-bottom: none;
}

/* El resaltado de fila es una promesa: "esto se abre". Por eso NO cuelga
   de `tbody tr` —así se pintaba también en tablas donde no hay nada que
   abrir: la auditoría, las métricas, las líneas de una cotización, las
   vistas previas de importación— sino de `data-abrir`, que es justo lo
   que hace la fila pulsable (el clic lo resuelve componentes.js).
   Señalar que algo se puede abrir cuando no se puede es peor que no
   señalar nada. */
tbody tr[data-abrir],
tbody tr[data-abrir-panel] {
  cursor: pointer;
}

/* :focus-within incluye a quien navega con el tabulador: al llegar al
   enlace de la fila, la fila entera se marca igual que con el ratón. */
tbody tr[data-abrir]:hover td,
tbody tr[data-abrir]:focus-within td,
tbody tr[data-abrir-panel]:hover td,
tbody tr[data-abrir-panel]:focus-within td {
  background: var(--color-fila-hover);
}

/* Columna de importes: alineada a la derecha y con cifras de ancho
   fijo para que los montos cuadren visualmente entre filas. */
/* ══ LOS TITULOS NUMERICOS VAN CENTRADOS; LOS DATOS, NO ══
   (Vladimir, 22/08/2026. Primero pidio «el titulo TOTAL ponlo a la
   izquierda como los demas», se probo asi, y al verlo lo corrigio:
   «vi que empiezan al extremo de como terminan o empieza el texto
   dentro de la columna, pero para ese creo que queda mejor
   centralizado». Queda escrito porque el titulo a la izquierda sobre
   una columna de importes a la derecha deja el rotulo colgando en el
   borde opuesto a sus cifras.)

   La fila de encabezados es una fila de ETIQUETAS y se lee entera desde
   la izquierda; cada columna de abajo se alinea por lo que ES: los
   importes a la derecha para que cuadren cifra con cifra, los conteos
   centrados. Antes el `th` compartia regla con el `td`, asi que «TOTAL»
   era el unico titulo pegado al borde derecho de una fila de titulos
   pegados al izquierdo.

   **Van las TRES clases numericas** —23 cabeceras `numero`, 6
   `numero-recuento` y las `col-numero` de Administracion—: arreglar solo
   la de importes habria dejado «ARTICULOS» centrado en la misma fila, y
   el problema es la fila, no una celda.

   **Y el `th` se SACA de la regla de los datos, no se le anade otra
   encima.** Con la misma especificidad gana la ultima que se lee: la
   regla nueva se veria en el archivo y no en la pantalla. */
th.numero,
th.numero-recuento,
th.col-numero {
  text-align: center;
  white-space: nowrap;
}

/* ══ Y LA COLUMNA SE ENCOGE A SUS CIFRAS (Vladimir, 05/09/2026) ══

   *«Esa columna cae fuera o está sin título»*, sobre la tabla de «Por
   clasificación de cobro»: tres columnas repartiéndose el ancho entero
   dejaban «COSTO ESTIMADO» a media pantalla de sus valores.

   **El centrado del título no es el problema y no se toca** — es su
   decisión del 22/08 y sigue siendo la correcta: un rótulo a la izquierda
   sobre cifras a la derecha queda colgando en el borde opuesto. El problema
   es el ANCHO: con tres columnas de 500px, centro y derecha están a un
   palmo. Con la columna encogida a lo que ocupan sus cifras, el título cae
   justo encima de ellas y las dos alineaciones dan lo mismo.

   `width: 1%` es «lo mínimo que quepa» en una tabla `auto`: lo sobrante se
   va a la columna sin medida, que es la de texto. **Ya estaba inventado en
   `.tabla-cotizaciones`**; aquí sube a la regla general, que es donde tenía
   que haber estado. */
td.numero,
th.numero,
td.numero-recuento,
th.numero-recuento,
td.col-numero,
th.col-numero {
  width: 1%;
}

td.numero {
  text-align: right;
  font-variant-numeric: tabular-nums;
  /* **Un número NUNCA se parte en dos líneas** (Vladimir, 15/08/2026:
     «procurar que caiga en una sola, en dado caso ampliando las columnas
     afectadas»). «RD$ 12,495.00» partido en «RD$» y «12,495.00» no solo hace
     la fila el doble de alta: obliga a releer para saber si el importe es
     12.495 o 495. La columna crece lo que necesite; el resto reparte. */
  white-space: nowrap;
}

/* Lo mismo para fechas y horas, que tampoco significan nada partidas:
   «10/08/2026 16:34» roto por la mitad se lee como dos datos. */
td.col-fecha,
th.col-fecha { white-space: nowrap; }

/* Y para las personas. Van acompañadas del filtro `nombre_corto` —«Natacha
   V.»— porque una columna de personas no puede crecer indefinidamente: hay
   cinco mas peleando por el mismo ancho. El nombre entero sigue estando, en
   el `title` y en la ficha. */
td.col-persona,
th.col-persona {
  white-space: nowrap;
  /* **Centrado, y en las CINCO tablas que la usan** (Vladimir,
     22/08/2026). El nombre abreviado —«Vladimir G.»— es mucho mas
     corto que su encabezado, asi que alineado a la izquierda queda
     colgando en el borde de una columna vacia. Centrado se lee como
     un dato y no como el principio de un texto que se corto.

     Se cambia la CLASE y no cada tabla: ordenes, cotizaciones,
     solicitudes (dos) y tareas (dos) usan esta misma columna, y
     centrarla solo en dos habria dado dos productos distintos en
     capturas del mismo software. */
  text-align: center;
}


/* --- EL NUMERO QUE ESCRIBIO OTRO ---
   Vladimir, 23/08/2026: «en este cuadro NO. COTIZACION y ARTICULOS,
   centraliza el texto dentro de las columnas».

   No es un importe y no es un recuento: es el identificador que el
   proveedor le puso a SU documento. 6129, 37880, 4638, 351 — nunca se
   suman, nunca se comparan entre filas, y su largo cambia de un
   proveedor a otro. A la izquierda se despega del encabezado; a la
   derecha fingiria ser una cifra que se cuadra con la de abajo.
   Centrado se lee por lo que es, una etiqueta.

   Clase propia y no `numero-recuento`: alinean igual hoy, pero un
   recuento y una referencia son cosas distintas, y el dia que los
   recuentos cambien de alineacion esta columna no tiene por que
   seguirlos. --- */
td.col-documento,
th.col-documento {
  text-align: center;
  /* Una referencia partida en dos lineas —«378» y «80»— hay que releerla
     para saber si es 37880 o dos numeros. */
  white-space: nowrap;
}

/* --- Envoltura para que una tabla ancha se desplace sola en
   pantallas chicas en vez de romper el ancho de la página. --- */
.tabla-scroll {
  width: 100%;
  /* Ancla los hijos posicionados —el texto `.solo-lectores` de las
     columnas sin título, los menús de fila— a esta caja. Sin esto se
     resuelven contra la página: en una tabla ancha quedan más allá del
     borde derecho y estiran la pantalla entera, aunque la tabla ya
     tuviera su propio desplazamiento. */
  position: relative;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  border-radius: var(--radius-md);
}

/* Red de seguridad para móvil: una tabla que todavía no tiene su
   envoltura se desplaza sola en vez de estirar la página entera. Las
   pantallas ya migradas usan .tabla-scroll y no entran aquí. */
@media (max-width: 700px) {
  :not(.tabla-scroll) > table {
    display: block;
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
}


/* ============================================================
   4. CARDS
   ============================================================ */

.card {
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-5);
  margin-bottom: var(--space-5);
}

.card > :first-child {
  margin-top: 0;
}

.card > :last-child {
  margin-bottom: 0;
}

/* Card sin relleno: para meter una tabla a sangre dentro */
.card-plano {
  padding: 0;
  overflow: hidden;
}

.card-titulo {
  margin: 0 0 var(--space-4);
  font-size: var(--text-xl);
  font-weight: 600;
  color: var(--color-ink);
}

.page-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-bottom: var(--space-5);
}

.page-header h2 {
  margin: 0;
  font-size: var(--text-2xl);
  font-weight: 700;
  color: var(--color-ink);
}


/* ============================================================
   5. BADGES
   Los nombres de color (verde, gris, rojo…) se conservan porque
   las plantillas y los routers los generan por nombre. Lo que
   cambia es a qué token apunta cada uno.
   ============================================================ */

.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  font-size: var(--text-xs);
  font-weight: 600;
  line-height: 1.4;
  white-space: nowrap;
}

.badge-verde {
  background: var(--color-success-bg);
  color: var(--color-success);
}

.badge-gris {
  background: var(--color-gray-100);
  color: var(--color-gray-700);
}

.badge-rojo {
  background: var(--color-danger-bg);
  color: var(--color-danger);
}

.badge-amarillo {
  background: var(--color-caution-bg);
  color: var(--color-caution);
}

.badge-naranja {
  background: var(--color-warning-bg);
  color: var(--color-warning);
}

.badge-azul {
  background: var(--color-info-bg);
  color: var(--color-info);
}

/* Punto de color del semáforo de prioridad: sustituye a los emoji,
   que se veían distintos en cada sistema operativo. Es decorativo —
   el texto del badge ya dice el nivel. */
.badge-punto {
  width: 7px;
  height: 7px;
  border-radius: var(--radius-full);
  background: currentColor;
  flex-shrink: 0;
}


/* ============================================================
   6. ALERTAS
   Cuatro variantes semánticas, separadas del verde de marca.
   ============================================================ */

.alerta {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid transparent;
  border-radius: var(--radius-md);
  font-size: var(--text-base);
  line-height: 1.5;
  margin-bottom: var(--space-4);
}

.alerta .icon {
  flex-shrink: 0;
  margin-top: 1px;
}

.alerta-contenido {
  flex: 1 1 auto;
  min-width: 0;
}

.alerta-titulo {
  display: block;
  font-weight: 600;
  margin-bottom: var(--space-1);
}

/* Enlace de acción dentro de una alerta: hereda el color de la variante
   para no romper la lectura del bloque. */
.alerta-accion {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  margin-left: var(--space-2);
  color: inherit;
  font-weight: 600;
  text-decoration: underline;
  white-space: nowrap;
}

.alerta-exito {
  background: var(--color-success-bg);
  border-color: var(--color-success);
  color: var(--color-success);
}

.alerta-error,
.error {
  background: var(--color-danger-bg);
  border-color: var(--color-danger);
  color: var(--color-danger);
}

.alerta-advertencia {
  background: var(--color-warning-bg);
  border-color: var(--color-warning);
  color: var(--color-warning);
}

.alerta-info {
  background: var(--color-info-bg);
  border-color: var(--color-border);
  color: var(--color-ink);
}

/* `.error` ya existía suelto en 20 plantillas: hereda la caja de
   alerta sin que haya que tocar ninguna de ellas. */
.error {
  display: block;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-danger);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
}


/* ============================================================
   7. MODALES
   ============================================================ */

.modal-fondo {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal-fondo);
  background: rgba(22, 32, 58, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  overflow-y: auto;
}

.modal {
  position: relative;
  z-index: var(--z-modal);
  background: var(--color-white);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  width: 100%;
  max-width: 520px;
  max-height: calc(100vh - var(--space-6));
  display: flex;
  flex-direction: column;
}

.modal-ancho {
  max-width: 760px;
}

.modal-cabecera {
  display: flex;
  /* Arriba, no al centro: con un título de dos líneas la equis quedaba
     hundida a media altura del texto. */
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--color-border);
}

.modal-titulo {
  margin: 0;
  font-size: var(--text-xl);
  font-weight: 600;
  color: var(--color-ink);
  /* ══ UN NOMBRE DE ARCHIVO NO TIENE ESPACIOS (Vladimir, 06/09/2026) ══
     «El nombre del archivo sobresale del cuadro», sobre el diálogo de borrar
     en el espacio compartido.

     Son DOS cosas a la vez y hacen falta las dos: `min-width: 0` porque un
     hijo de un flex no encoge por debajo de su contenido —y sin eso el
     título ensancha el cuadro entero—, y `overflow-wrap: anywhere` porque
     `Catalogo_SMAG_2026_todos_los_botones_vinculados.pptx` es UNA palabra:
     no hay dónde partirla si no se autoriza cortar por cualquier sitio. */
  min-width: 0;
  overflow-wrap: anywhere;
}

.modal-cerrar {
  margin-left: auto;
  background: none;
  border: none;
  color: var(--color-gray-500);
  padding: var(--space-1);
  min-height: 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
}

.modal-cerrar:hover {
  background: var(--color-fila-hover);
  color: var(--color-ink);
}

.modal-cuerpo {
  padding: var(--space-5);
  overflow-y: auto;
  flex: 1 1 auto;
  /* Lo mismo que el título: en el cuerpo también se nombran archivos,
     direcciones de correo y enlaces, y ninguno de los tres tiene espacios. */
  overflow-wrap: anywhere;
}

.modal-cuerpo > :first-child {
  margin-top: 0;
}

.modal-pie {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--color-border);
  background: var(--color-gray-50);
  border-bottom-left-radius: var(--radius-lg);
  border-bottom-right-radius: var(--radius-lg);
}


/* ============================================================
   8. DROPDOWNS Y MENÚS CONTEXTUALES
   Una sola superficie de menú para el dropdown de acciones y la
   lista del autocompletado, que antes se estilaban por separado.
   ============================================================ */

.dropdown {
  position: relative;
  display: inline-block;
}

.dropdown-menu,
.autocomplete-lista {
  position: absolute;
  z-index: var(--z-menu);
  top: 100%;
  margin-top: var(--space-1);
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  overflow: hidden auto;
}

.dropdown-menu {
  display: none;
  right: 0;
  min-width: 180px;
}

.dropdown-menu.abierto {
  display: block;
}

.dropdown-menu form {
  display: block;
}

.dropdown-menu button,
.dropdown-menu a {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  min-height: 0;
  background: none;
  color: var(--color-ink);
  border: none;
  border-radius: 0;
  text-align: left;
  justify-content: flex-start;
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-base);
  font-weight: 400;
  text-decoration: none;
  box-sizing: border-box;
  cursor: pointer;
}

.dropdown-menu button:hover,
.dropdown-menu a:hover {
  background: var(--color-fila-hover);
  color: var(--color-ink);
}

.dropdown-menu button:focus-visible,
.dropdown-menu a:focus-visible {
  outline: none;
  background: var(--color-primary-bg);
  box-shadow: none;
}

/* --- Autocompletado (misma superficie, ancho del campo) --- */
.autocomplete-lista {
  display: none;
  /* **COLGADA DE LA VENTANA, no del panel** (Vladimir, 05/09/2026: «eso
     tiene un error como se despliega el autorellenado, la vista se corta»).
     Medido: la lista acababa en 488 px y el panel en 406 — 82 px cortados,
     porque `.tabla-panel` lleva `overflow: hidden` y recorta a cualquier
     hijo posicionado que asome. Colgada de la ventana no hay contenedor
     que pueda recortarla; `componentes.js` la coloca bajo el campo. */
  position: fixed;
  max-height: 240px;
}

.autocomplete-item {
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
  font-size: var(--text-base);
  color: var(--color-ink);
}

.autocomplete-item:hover {
  background: var(--color-fila-hover);
}

/* Cada resultado en dos alturas: el nombre manda, el dato que lo
   distingue va debajo y en gris. Con los dos al mismo peso la lista se
   lee como un muro y no se encuentra nada. */
.autocomplete-item {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.autocomplete-nombre {
  color: var(--color-ink);
}

.autocomplete-detalle {
  font-size: var(--text-sm);
  color: var(--color-gray-500);
}

/* Marcado con las flechas del teclado: quien captura varias líneas no
   suelta el teclado para usar el ratón. */
.autocomplete-item-activo {
  background: var(--color-gray-50);
}

.autocomplete-item.autocomplete-vacio {
  color: var(--color-gray-500);
  cursor: default;
}

.autocomplete-item.autocomplete-vacio:hover {
  background: transparent;
}


/* ============================================================
   9. ESTADOS VACÍOS
   ============================================================ */

.estado-vacio {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: var(--space-3);
  padding: var(--space-7) var(--space-5);
  color: var(--color-ink-soft);
}

.estado-vacio-icono {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 56px;
  height: 56px;
  border-radius: var(--radius-full);
  background: var(--color-gray-50);
  color: var(--color-gray-500);
}

.estado-vacio-titulo {
  margin: 0;
  font-size: var(--text-xl);
  font-weight: 600;
  color: var(--color-ink);
}

.estado-vacio-texto {
  margin: 0;
  max-width: 42ch;
  font-size: var(--text-base);
  line-height: 1.5;
}

/* Variante compacta: dentro de una tarjeta o media pantalla. Ocupa
   bastante menos alto, para que una ficha con tres secciones vacías
   no se convierta en una página de puro hueco. */
.estado-vacio-compacto {
  padding: var(--space-4);
  gap: var(--space-2);
}

.estado-vacio-compacto .estado-vacio-icono {
  width: 36px;
  height: 36px;
}

.estado-vacio-compacto .estado-vacio-titulo {
  font-size: var(--text-lg);
}

.estado-vacio-compacto .estado-vacio-texto {
  font-size: var(--text-sm);
  max-width: 52ch;
}


/* ============================================================
   10. INDICADOR DE CARGA
   ============================================================ */

/* Indicador de htmx: se muestra solo mientras hay petición en curso.
   htmx aplica la clase .htmx-request al elemento que dispara. */
.htmx-indicator {
  display: none;
}

.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator {
  display: inline-flex;
}


/* ============================================================
   11b. UTILIDADES DE MAQUETACIÓN
   Conjunto corto y con nombre para lo que antes se resolvía con
   estilos sueltos dentro de las plantillas. Se añade solo lo que
   se repite; no es una librería de utilidades.
   ============================================================ */

/* Anchos de formulario. Solo dos, a propósito: antes convivían cuatro
   (500, 600, 700 y 800px) sin criterio, y eso se nota al pasar de una
   pantalla a otra. */
.card-estrecho {
  max-width: 800px;
}

.card-angosto {
  max-width: 560px;
}

/* --- Paginación --- */
.paginacion {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-4);
  margin-top: var(--space-5);
}

.paginacion-info {
  color: var(--color-ink-soft);
  font-size: var(--text-sm);
  white-space: nowrap;
}

/* --- Bloque de totales (cotizaciones) ---
   Columna a la derecha con pares concepto/importe. Los importes van con
   cifras de ancho fijo para que las columnas de números cuadren. */
.totales {
  max-width: 320px;
  margin-left: auto;
  margin-top: var(--space-4);
}

.totales p {
  display: flex;
  justify-content: space-between;
  gap: var(--space-4);
  margin: 0 0 var(--space-2);
  font-size: var(--text-base);
  color: var(--color-ink-soft);
}

.totales p span:last-child {
  font-variant-numeric: tabular-nums;
  color: var(--color-ink);
}

.totales .total-final {
  margin-top: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--color-ink);
}

/* Beneficio interno: dato reservado, nunca sale en el PDF al cliente */
.beneficio-interno {
  max-width: 320px;
  margin-left: auto;
  margin-top: var(--space-3);
  padding: var(--space-3);
  border: 1px dashed var(--color-success);
  border-radius: var(--radius-sm);
  background: var(--color-success-bg);
  color: var(--color-success);
  font-size: var(--text-sm);
}

.beneficio-interno strong {
  display: block;
  font-variant-numeric: tabular-nums;
  margin-bottom: var(--space-1);
}

.beneficio-interno span {
  display: block;
  font-size: var(--text-xs);
  font-weight: 400;
  opacity: 0.85;
}

/* Card que encabeza una pantalla y pide atención. El filo va en tinta,
   no en verde: el verde queda reservado para acciones. */
.card-destacado {
  border-left: 4px solid var(--color-ink);
}

/* Subtítulo de un grupo dentro de una card */
.grupo-titulo {
  margin: var(--space-4) 0 var(--space-2);
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--color-ink);
}

.grupo-titulo:first-child {
  margin-top: 0;
}

/* Enlace de cierre de una lista: "Ver todas →" */
.enlace-ver-todo {
  margin: var(--space-2) 0 0;
  font-size: var(--text-sm);
}

.enlace-ver-todo a {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  color: var(--color-primary-texto);
  text-decoration: none;
  font-weight: 600;
}

.enlace-ver-todo a:hover {
  text-decoration: underline;
}

/* Resumen de canales bajo las cifras del tablero */
.resumen-canal {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
  margin: 0 0 var(--space-5);
  color: var(--color-ink-soft);
  font-size: var(--text-base);
}

.resumen-canal span {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
}

/* Fecha/hora secundaria junto a un elemento de lista */
.marca-tiempo {
  color: var(--color-ink-soft);
  font-size: var(--text-sm);
}

/* Subtítulo que separa secciones dentro de un formulario en rejilla */
.form-seccion {
  margin: var(--space-4) 0 0;
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--color-ink);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.form-grid .form-seccion {
  grid-column: 1 / -1;
}

/* La caja de campos del redactor de correo. Antes reutilizaba
   `.form-seccion`, que es el RÓTULO de una sección y lleva mayúsculas: al
   envolver los campos con ella, las mayúsculas se heredaban y las frases de
   ayuda salían gritadas (Vladimir, 16/08/2026: «esa tipografía también es
   fea»). Aquí solo hace falta el espacio entre campos. */
.campos-plantilla > * + * {
  margin-top: var(--space-4);
}

/* La primera sección no lleva línea de separación arriba */
.form-seccion:first-child {
  margin-top: 0;
  padding-top: 0;
  border-top: none;
}

/* ── LA CAJA DEL REDACTOR ─────────────────────────────────────────────
   Vladimir, 14/08/2026: «esta pantalla está mal diseñada» — y lo primero
   que saltaba era un aviso azul GRITANDO EN MAYÚSCULAS en mitad del
   panel central.

   **La causa: el redactor usaba `form-seccion` como caja.** Esa clase es
   un SUBTÍTULO —una etiqueta corta en mayúsculas para separar bloques de
   un formulario— y como contenedor le imponía sus mayúsculas a todo lo
   de dentro: el aviso, las etiquetas y el desplegable del HTML.

   Un texto largo en mayúsculas no es solo feo: se lee bastante más
   despacio, porque desaparece el perfil de las palabras. Y un aviso que
   grita entrena a ignorar los avisos.

   Aquí solo hace falta separar, así que eso es lo único que hace.       */
.redactor {
  margin-top: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}

/* Botonera de cierre de un formulario */
.acciones-formulario {
  margin-top: var(--space-5);
}

/* Fila de casillas con más aire entre ellas */
.checkbox-line-ancha {
  gap: var(--space-5);
  flex-wrap: wrap;
}

/* Respeta los saltos de línea que el usuario escribió (notas) */
.texto-preservado {
  white-space: pre-wrap;
}

/* Campo de búsqueda: ocupa el doble que los demás filtros. `relative`
   porque la lista del autocompletado se posiciona sobre él. */
.filtro-principal {
  flex: 2;
  position: relative;
}

.autocomplete {
  position: relative;
}

.filtros .checkbox-line {
  margin-top: 0;
  margin-bottom: var(--space-1);
}

/* El enlace real de una fila: es lo que permite abrir el registro con
   el teclado, y lo que el ratón acompaña al pulsar en cualquier parte
   de la fila (`data-abrir`). Antes solo tenía estilo dentro de
   `.fila-enlazada`, así que en las pantallas que lo usan sin esa clase
   —alertas, tareas, precios de un artículo— salía como un enlace azul
   subrayado del navegador, ajeno al resto del sistema. */
.enlace-fila {
  color: var(--color-ink);
  text-decoration: none;
  font-weight: 600;
}

[data-abrir]:hover .enlace-fila,
.enlace-fila:hover,
.enlace-fila:focus-visible {
  color: var(--color-primary-texto);
  text-decoration: underline;
}

/* Lista de registros relacionados dentro de una ficha */
.lista-simple {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.lista-simple li {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-border);
  font-size: var(--text-base);
}

.lista-simple li:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

/* <summary> con aspecto de botón pequeño (bloques desplegables de
   la ficha de cliente). Oculta el triángulo nativo. */
.summary-boton {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3);
  min-height: 30px;
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  color: var(--color-ink);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  list-style: none;
  width: fit-content;
}

.summary-boton::-webkit-details-marker {
  display: none;
}

.summary-boton:hover {
  background: var(--color-fila-hover);
  border-color: var(--color-gray-500);
}

.summary-boton:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* Contenido que se despliega bajo un summary-boton */
.detalle-cuerpo {
  margin-top: var(--space-3);
  min-width: 260px;
  max-width: 400px;
}

.detalle-cuerpo-ancho {
  max-width: 520px;
}

.detalle-separado {
  margin-top: var(--space-4);
}

.acciones-separadas {
  margin-top: var(--space-3);
}

.detalle-cuerpo > :first-child {
  margin-top: 0;
}

/* Fila de formulario compacta: campo + botón alineados abajo */
.form-linea {
  display: flex;
  gap: var(--space-3);
  align-items: flex-end;
  flex-wrap: wrap;
  max-width: 450px;
  margin-bottom: var(--space-4);
}

.form-linea > div {
  flex: 1;
  min-width: 180px;
}

.form-linea label {
  margin-top: 0;
}

/* Fila que alinea acciones de FORMULARIOS DISTINTOS en la misma linea.
   «Vista previa» manda la plantilla elegida y «Enviar la cotizacion en
   PDF» manda el PDF: son dos envios distintos y no pueden compartir
   formulario. Se alinean desde fuera, con una separacion visible para que
   no se lean como un solo grupo de botones. */
.fila-acciones {
  display: flex;
  align-items: flex-end;
  gap: var(--space-6);
  flex-wrap: wrap;
  /* El margen que traia el formulario, ahora en la fila: si lo conservara
     cada uno, los dos botones quedarian a distinta altura. */
  margin-bottom: var(--space-4);
}

/* Dentro de la fila el formulario ya no arrastra su margen inferior: lo
   pone la fila, o los dos botones quedarian a distinta altura. */
.fila-acciones > .form-linea {
  margin-bottom: 0;
}

/* Los botones de la fila se apoyan en la misma linea base que el boton
   del formulario, que va pegado al selector. */
.fila-acciones > .btn {
  margin-bottom: 0;
}

/* Formulario de alta dentro de una ficha (agregar artículo a una
   cotización): ancho de trabajo cómodo, separado de lo de arriba. */
.form-agregar {
  max-width: 600px;
  margin-top: var(--space-4);
}

.form-linea-corta {
  max-width: 320px;
}

.form-linea-ancha {
  max-width: 600px;
}

/* Formulario corto alineado a la derecha (descuento de una cotización) */
.form-linea-derecha {
  margin-left: auto;
  margin-top: var(--space-3);
}

/* Bloques que deben quedar en la misma línea dentro de una celda */
.en-linea {
  display: inline-block;
}

.sin-salto {
  white-space: nowrap;
}

/* Texto que solo oyen los lectores de pantalla. Sirve para decir con
   palabras lo que en pantalla se entiende por color o posición (quién
   envió un mensaje del chat, por ejemplo). No usar display:none: eso
   también lo oculta al lector. */
.solo-lectores {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* --- Grupo de opciones excluyentes (radio) ---
   Cada opción es una caja pulsable completa, no solo el circulito: en
   una pantalla de importación el usuario está decidiendo qué pasa con
   sus datos y conviene que el área de clic sea generosa. */
.opciones {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: var(--space-3) 0;
}

.opcion {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  margin: 0;
  font-weight: 400;
  color: var(--color-ink);
  cursor: pointer;
}

.opcion:hover {
  border-color: var(--color-gray-500);
  background: var(--color-fila-hover);
}

.opcion input {
  margin-top: 2px;
}

.opcion:has(input:checked) {
  border-color: var(--color-primary);
  background: var(--color-primary-bg);
}

.opcion:has(input:focus-visible) {
  box-shadow: var(--focus-ring);
}

/* Opción suelta en línea (casilla de confirmación dentro de una fila) */
.opcion-inline {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  margin: 0;
  font-size: var(--text-xs);
  font-weight: 400;
  white-space: nowrap;
  cursor: pointer;
}

/* Vista previa de una imagen ya guardada (logo, foto, firma) */
.vista-imagen {
  display: block;
  margin-top: var(--space-2);
  max-height: 80px;
  width: auto;
  border-radius: var(--radius-sm);
}

.vista-imagen-redonda {
  width: 60px;
  height: 60px;
  border-radius: var(--radius-full);
  object-fit: cover;
  max-height: none;
}

/* Lista de barras del tablero de inteligencia, con separación abajo */
.bar-list-separada {
  margin-bottom: var(--space-5);
}

/* Campo numérico corto (umbral, cantidad) que no debe ocupar toda la fila */
.campo-estrecho {
  width: 90px;
  margin-top: 0;
}

/* <summary> con aspecto de enlace, para desplegar detalle opcional */
.summary-enlace {
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--color-primary-texto);
  font-weight: 600;
  list-style: none;
  width: fit-content;
}

.summary-enlace::-webkit-details-marker {
  display: none;
}

.summary-enlace:hover {
  text-decoration: underline;
}

.summary-enlace:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  border-radius: var(--radius-sm);
}

/* --- Fila de regla configurable (umbrales de alertas) --- */
.regla {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  padding: var(--space-3) 0;
  border-bottom: 1px solid var(--color-border);
}

.regla:last-of-type {
  border-bottom: none;
}

.regla-texto {
  flex: 1;
  min-width: 220px;
}

.regla-nombre {
  display: block;
  font-weight: 600;
  color: var(--color-ink);
  margin: 0;
}

.regla-descripcion {
  margin: var(--space-1) 0 0;
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

.regla-umbral {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.regla-umbral .campo-ayuda {
  margin: 0;
  white-space: nowrap;
}

.regla .checkbox-line {
  margin: 0;
}

.regla .checkbox-line label {
  margin: 0;
}

/* Dato de una fila que exige atención (tarea vencida) */
.dato-vencido {
  color: var(--color-danger);
  font-weight: 600;
}

/* Editor de HTML en crudo (cuerpo de un correo por aprobar) */
.textarea-codigo {
  font-family: ui-monospace, "Cascadia Code", Consolas, monospace;
  font-size: var(--text-sm);
  line-height: 1.5;
}

/* Vista previa del correo, aislada en un iframe */
.vista-previa {
  width: 100%;
  height: 400px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-white);
}

/* Nota de cierre bajo un bloque (quién aprobó y cuándo) */
.nota-proceso {
  margin-top: var(--space-4);
  color: var(--color-ink-soft);
  font-size: var(--text-sm);
}

/* Barra de filtros embebida dentro de una card, sin su propio margen */
.filtros-embebidos {
  padding: 0;
  margin: var(--space-3) 0;
}

/* --- Lista de notificaciones --- */
.notificaciones {
  list-style: none;
  padding: 0;
  margin: 0;
}

.notificaciones li {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  flex-wrap: wrap;
  /* **Con aire a los lados** (Vladimir, 18/08/2026: «las viñetas verdes
     quedaron casi fuera del recuadro»). Sin relleno horizontal, el punto
     nacía pegado al borde de la tarjeta.

     Y una nota de método: el primer intento cayó en `.regla`, otra clase con
     las mismas cinco líneas de CSS. Buscar por el TEXTO de una regla y no por
     su selector encuentra la primera que se le parezca — que es lo que pasó,
     y por eso él lo seguía viendo igual. */
  padding: var(--space-3) var(--space-5);
  border-bottom: 1px solid var(--color-border);
}

.notificaciones li:last-child {
  border-bottom: none;
}

/* La no leída se marca con un punto además de la negrita: el peso solo
   se nota comparando, el punto se ve de un vistazo. */
.notificacion-sin-leer .notificacion-texto {
  font-weight: 600;
  color: var(--color-ink);
}

.notificacion-sin-leer::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: var(--radius-full);
  background: var(--color-primary);
  flex-shrink: 0;
  align-self: center;
}

.notificaciones li:not(.notificacion-sin-leer)::before {
  content: "";
  width: 7px;
  flex-shrink: 0;
}

/* Botón que se lee como enlace: abre la notificación y la marca leída */
.notificacion-texto {
  background: none;
  border: none;
  padding: 0;
  min-height: 0;
  color: var(--color-ink-soft);
  font-size: var(--text-base);
  font-weight: 400;
  text-align: left;
  cursor: pointer;
  flex: 1 1 auto;
  justify-content: flex-start;
}

.notificacion-texto:hover {
  background: none;
  color: var(--color-primary-texto);
  text-decoration: underline;
}

/* Fila de detalle bajo otra fila de tabla (historial de precios) */
.subfila td {
  padding-left: var(--space-6);
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
  background: var(--color-gray-50);
}


/* ============================================================
   12. TABLA ADMINISTRATIVA (estándar único del sistema)

   Reemplaza los tres modelos de interacción que convivían: botones de
   texto por fila, formularios desplegados dentro de la celda, y filas
   sin acciones. Reglas:
     - La primera columna es identidad, en dos líneas.
     - Una acción directa (icono) + un menú para el resto.
     - Lo destructivo vive dentro del menú, nunca a la vista.
     - Badge SOLO para lo que se sale de lo normal.
   ============================================================ */

/* --- Contenedor: la tabla vive en su propia superficie, sin la card
   con relleno que la separaba del borde --- */
.tabla-panel {
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  /* **`clip` y no `hidden`, y es la TERCERA vez que este detalle rompe lo
     mismo** (09/09/2026). `overflow: hidden` crea un contenedor de
     desplazamiento y con el DESACTIVA `position: sticky` en todo lo que
     lleve dentro, sin avisar de nada.

     Aqui se llevaba por delante el circulo de las flechas laterales: al
     bajar por el listado de cotizaciones se iba con la pagina en vez de
     acompanar. Vladimir: «este se queda fijo arriba, si puedes hacer que
     baje y suba con uno tambien».

     `clip` recorta EXACTAMENTE IGUAL --las esquinas redondeadas siguen
     recortando la tabla-- y no crea contenedor de desplazamiento, asi que
     el sticky de dentro sigue funcionando. */
  overflow: clip;
}

/* ── DOS PANELES APILADOS NECESITAN AIRE (14/09/2026) ─────────────────
   Vladimir, viendo los tres cuadros de Aprobaciones: «debes darle un espacio
   de separacion a esos 3 cuadros, separalos un poco, se ve muy cargado asi».

   **`.tabla-panel` no declaraba ningun margen**, asi que los cuadros se
   tocaban y lo unico que los separaba era el hueco que dejara por casualidad
   lo que hubiera entre medias. Con el selector de hermano adyacente el aire
   lo gana SOLO el segundo y siguientes: las nueve pantallas que apilan
   paneles respiran, y las que llevan uno solo no cambian ni un pixel.

   **La excepcion son los que van EN REJILLA**, y es importante: en
   `whatsapp_tarifas` dos paneles son dos columnas, no dos filas. Ahi la
   rejilla ya los separa con su `gap`, y un margen arriba bajaria la segunda
   columna dejandola descuadrada respecto de la primera. */
.tabla-panel + .tabla-panel {
  margin-top: var(--space-5);
}

.op-grid > .tabla-panel + .tabla-panel {
  margin-top: 0;
}

/* Barra superior: filtros y buscador dentro del panel, no encima */
.tabla-barra {
  display: flex;
  align-items: center;
  /* Siete controles en fila: el hueco de 12 px se comía 60 px de la línea. */
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  flex-wrap: wrap;
}

/* ── UNA SOLA LÍNEA DESDE 1366 px ──────────────────────────────────
   Comprobado por Vladimir en TRES máquinas: las dos fechas y el botón
   «Filtrar» caían a una segunda línea.

   MEDIDO en el navegador, no supuesto: a 1366 px la barra dispone de
   **1023 px útiles** —el `shell` deja 1140 al contenido, menos su relleno
   y el del panel— y los siete controles sumaban 1187.

   La causa no era una sola:

     · `.buscador` con `flex: 1` y `min-width: 220px`, un suelo que no
       cede, y su `<input>` con anchura intrínseca propia que tampoco;
     · tres `.campo-filtro` de 150 px fijos con `flex: 0 0 auto`, o sea
       incapaces de encoger;
     · los `select` dimensionados por su opción más larga.

   **Y lo que hace falta saber para no volver a pelearse con esto:** el
   reparto en líneas usa el tamaño BASE de cada elemento, no el que
   tendría ya encogido. Por eso bajar `min-width` no basta: si las bases
   suman más que la línea, envuelve igual y luego encoge dentro de cada
   fila. Hay que bajar las BASES. */

.tabla-barra .buscador {
  flex: 1 1 180px;
  /* Sin esto un elemento flex no baja de su anchura intrínseca. */
  min-width: 0;
  position: relative;
}

.tabla-barra .buscador input {
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
}

.tabla-barra .buscador input {
  padding-left: var(--space-6);
  min-height: 34px;
  font-size: var(--text-base);
}

.tabla-barra .buscador .icon {
  position: absolute;
  left: var(--space-3);
  top: 50%;
  transform: translateY(-50%);
  color: var(--color-gray-500);
  pointer-events: none;
}

.tabla-barra .campo-filtro {
  width: 115px;
  min-width: 0;
  flex: 0 0 auto;
}

/* EL RANGO DE FECHAS ES UNA UNIDAD, y por eso lleva envoltorio propio.
   Cuando la barra envuelve —por debajo de 1366— «Desde» y «Hasta» bajan
   JUNTOS. Partido a mitad de línea se lee como dos filtros distintos, que
   es peor que la segunda línea que veníamos a quitar.

   Es lo único que obliga a tocar la plantilla: dos hermanos no se pueden
   agrupar solo con CSS. El dimensionado sigue viviendo aquí. */
.tabla-barra .rango-fechas {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: 0 0 auto;
}

/* Un campo de fecha nativo necesita más que un texto corriente: pinta
   dd/mm/aaaa y su icono de calendario. */
.tabla-barra .rango-fechas .campo-filtro {
  width: 126px;
}

.tabla-barra select {
  width: auto;
  /* Tope ALTO a propósito: no debe morder a ningún desplegable de hoy —el
     más ancho es «Segmento» de Artículos, ~202 px—, solo frenar a un futuro
     con una opción desmedida.

     **Se probaron dos topes más bajos y los dos cortaban texto.** 165 dejaba
     la opción más larga de Segmento 7 px cortada en Clientes; 190 la cortaba
     12 px en Artículos. Y es el fallo que NO se ve: la barra queda en una
     línea y parece correcta, con un desplegable aplastado hasta que su texto
     no se lee. Eso es peor que la segunda línea que veníamos a quitar.

     El sitio se saca de los campos de texto y del hueco, no de los
     desplegables: son ellos los que llevan las palabras. */
  max-width: 210px;
  min-height: 34px;
  font-size: var(--text-base);
}

.tabla-panel table {
  border-radius: 0;
}

/* **El relleno LATERAL baja de 16 a 12 px** (Vladimir, 22/08/2026: «que se
   vea todo en una sola pantalla sin necesidad de usar el scroll»). En una
   tabla de siete columnas, 16 px por lado son 224 px de margenes internos —
   mas que cualquiera de las columnas de datos—. Doce siguen siendo holgura
   comoda para leer y devuelven 56 px de ancho util.

   El VERTICAL no se toca: apretar el alto de la fila no gana nada aqui y
   hace la tabla mas dificil de recorrer con la vista. */
/* --- Encabezado discreto: es una guía, no un titular --- */
.tabla-panel th {
  padding: var(--space-2) var(--space-3);
  background: transparent;
  border-bottom: 1px solid var(--color-border);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-gray-500);
  letter-spacing: 0.04em;
}

/* --- Fila: dos líneas de información ---
   **Le ganaba a la regla general y por eso el plan seguía igual de alto**
   (17/08/2026): bajar el relleno de `td` no cambió nada aquí porque
   `.tabla-panel td` es más específica y lo volvía a subir. Al apretar filas
   hay que apretar también las reglas que las pisan, o el cambio se ve en unas
   pantallas y en otras no — que es peor que no hacerlo. */
.tabla-panel td {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--color-border);
  vertical-align: middle;
  color: var(--color-ink-soft);
}

/* El resaltado de las filas de la bandeja no se repite aquí: lo da la
   regla única de `tbody tr[data-abrir]` de la sección 3. Tenerlo dos
   veces era lo que permitía que una se quedara atrás de la otra. */

/* --- Celda de identidad: lo que el usuario busca con la vista --- */
.celda-identidad {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.identidad-texto {
  min-width: 0;
}

/* ── EL NOMBRE PASA A DOS LÍNEAS, Y AHÍ SE CORTA ──────────────────────
   **Origen (07/08/2026, medido contra producción).** La lista de
   cotizaciones desbordaba **165 px** a 1366, y el scroll se llevaba la
   columna de acciones —la última, 96 px— que es la única con la que se
   trabaja. La primera columna medía **604 px de 1233**, el 49% de la tabla.

   La causa era este `white-space: nowrap`: con él, el mínimo de la columna
   es **el nombre entero** —«CENTRO MEDICO ESPECIALIZADO TU SALUD CETS»— y
   el `text-overflow: ellipsis` que ya estaba **no llegaba a actuar**, porque
   nada limitaba el ancho de la celda. Al permitir el salto, el mínimo pasa a
   ser la palabra más larga.

   **NO se recortó a una línea, y es a propósito:** el ancho se recupera de
   la columna que lo fuerza, nunca del contenido que hay que leer. Con dos
   líneas el nombre completo se sigue leyendo.

   **Y con TOPE DE DOS**, con recorte en la tercera: sin tope, un nombre
   extremo da filas de cuatro líneas y la lista se escanea peor que con el
   scroll que veníamos a quitar.

   *Compone con D1:* los nombres están en MAYÚSCULAS en la base y el mismo
   texto en minúsculas ocuparía ~10% menos. Eso no se toca aquí. */
.identidad-principal {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  font-weight: 600;
  color: var(--color-ink);
  text-decoration: none;
  overflow: hidden;
  text-overflow: ellipsis;
  overflow-wrap: anywhere;
}

a.identidad-principal:hover {
  color: var(--color-primary-texto);
  text-decoration: underline;
}

.identidad-secundaria {
  display: block;
  margin-top: 1px;
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Separador entre datos secundarios: "ana@x.do · 809-555-1010" */
.identidad-secundaria span + span::before {
  content: " · ";
  color: var(--color-border);
}

/* --- Avatar / inicial del registro --- */
.avatar-tabla {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  border-radius: var(--radius-full);
  background: var(--color-gray-100);
  color: var(--color-ink-soft);
  font-size: var(--text-sm);
  font-weight: 700;
}

.avatar-tabla-inactivo {
  opacity: 0.55;
}

/* --- Dato secundario en su propia columna --- */
.dato-apagado {
  color: var(--color-ink-soft);
  font-size: var(--text-base);
}

.dato-vacio {
  color: var(--color-gray-500);
}

/* --- Acciones: un icono para lo frecuente, un menú para el resto --- */
.acciones-fila {
  /* **`relative` es lo que ancla el menú a SU botón** (Vladimir, 16/08/2026:
     «elegí esa plantilla para eliminarla y mira dónde me apareció el menú de
     los 3 puntos»).

     `.menu-fila` es `position: absolute; right: 0`, así que se coloca contra
     el ancestro posicionado más cercano. Sin esta línea no había ninguno cerca
     y el menú se iba al borde derecho de la pantalla: en la galería, el menú
     de la primera tarjeta aparecía sobre la cuarta. En las tablas pasaba
     desapercibido porque las filas ocupan el ancho entero y el menú caía casi
     donde tocaba — el fallo estaba desde siempre, la rejilla solo lo enseñó. */
  position: relative;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-1);
}

.btn-icono {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  min-height: 0;
  padding: 0;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius-sm);
  color: var(--color-gray-500);
  cursor: pointer;
}

.btn-icono:hover {
  background: var(--color-gray-100);
  border-color: var(--color-border);
  color: var(--color-ink);
}

.btn-icono:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
  color: var(--color-ink);
}

/* El menú de tres puntos SIEMPRE está visible: es el punto de entrada a
   todas las acciones y no puede depender del ratón. Solo los iconos
   directos (Editar, Ver) aparecen al pasar por la fila o al enfocarlos
   con el teclado, para que la tabla en reposo quede limpia. */
.btn-icono-directo {
  opacity: 0;
  transition: opacity 0.1s ease;
}

tr:hover .btn-icono-directo,
tr:focus-within .btn-icono-directo,
.btn-icono-directo:focus-visible {
  opacity: 1;
}

/* Sin ratón (táctil) todo se ve siempre: nada puede quedar escondido
   detrás de un hover que ahí no existe. */
@media (hover: none) {
  .btn-icono-directo { opacity: 1; }
}

/* Cuando una acción es de uso muy frecuente en esa pantalla, su icono
   se marca como fijo y no se oculta nunca. */
.btn-icono-fijo { opacity: 1; }

/* --- Menú contextual de tres puntos --- */
.menu-fila {
  /* **`fixed`, no `absolute`** (Vladimir, 16/08/2026: en la última fila de una
     tabla «Eliminar» salía cortado).

     Colgando de la fila, cualquier ancestro con `overflow` lo recortaba — y
     `.tabla-scroll` tiene `overflow-x: auto` a propósito, que en CSS obliga a
     recortar también en vertical. Contra la ventana no hay nada que lo corte.

     Las coordenadas las pone `posicionarMenu()` al abrirlo, que además lo
     abre hacia arriba cuando abajo no cabe. Aquí solo queda el aspecto. */
  position: fixed;
  z-index: var(--z-menu);
  min-width: 210px;
  /* **Y un techo** (Vladimir, 07/09/2026: «hay algunos artículos del catálogo
     que los 3 puntos se muestran así», con el menú estirado de lado a lado).

     La nota que explica por qué no se puede eliminar enumera los sitios, y un
     artículo muy usado está en seis: «5 cotizaciones · 1 precio de proveedor ·
     1 orden de compra · 3 planes de compra · 1 solicitud · 6 respuestas». Sin
     techo, esa línea decide el ancho del menú y se come la pantalla.

     Con el techo, la nota parte en varias líneas y las opciones se quedan
     donde estaban. */
  max-width: 24rem;
  padding: var(--space-1);
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}

.menu-fila button,
.menu-fila a {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  min-height: 0;
  padding: var(--space-2) var(--space-3);
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--color-ink);
  font-size: var(--text-base);
  font-weight: 400;
  text-align: left;
  justify-content: flex-start;
  text-decoration: none;
  cursor: pointer;
}

.menu-fila button:hover,
.menu-fila a:hover {
  background: var(--color-fila-hover);
  color: var(--color-ink);
}

.menu-fila .peligro,
.menu-fila .peligro:hover {
  color: var(--color-danger);
}

.menu-fila .peligro:hover {
  background: var(--color-danger-bg);
}

/* Aviso dentro de un menú cuando no hay ninguna acción disponible: es
   más honesto que mostrar un menú vacío. */
.menu-nota {
  display: block;
  padding: var(--space-2) var(--space-3);
  color: var(--color-ink-soft);
  font-size: var(--text-sm);
  /* La nota puede ser larga —enumera dónde está metido el artículo— así que
     se parte en líneas en vez de estirar el menú. `balance` reparte el texto
     entre ellas para que no quede una línea suelta de dos palabras. */
  line-height: 1.4;
  text-wrap: balance;
}

.menu-fila hr {
  border: none;
  border-top: 1px solid var(--color-border);
  margin: var(--space-1) 0;
}

/* --- Badge: SOLO para lo que se sale de lo normal --- */
/* ── MAS PEQUEÑAS, OPCION B (Vladimir, 15/09/2026) ───────────────────
   Se le enseñaron las tres juntas —como estaban, mas pequeñas, y color
   solido con texto blanco— y eligio la B.

   **El motivo, y es el suyo del 06/09:** en una lista de treinta filas lo
   que se busca es el numero y el cliente; el estado se mira despues. El
   color solido se ve antes pero llena la pantalla, que es lo mismo que le
   molestaba de los degradados —«colores medianos o combinados»—.

   Los colores y los textos NO cambian: solo el peso en la fila.          */
.etiqueta {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 1px 6px;
  border-radius: var(--radius-sm);
  font-size: .67rem;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
}

.etiqueta::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: var(--radius-full);
  background: currentColor;
  flex-shrink: 0;
}

.etiqueta-neutra   { background: var(--color-gray-100);   color: var(--color-gray-700); }
.etiqueta-exito    { background: var(--color-success-bg); color: var(--color-success); }
.etiqueta-espera   { background: var(--color-caution-bg); color: var(--color-caution); }
.etiqueta-atencion { background: var(--color-warning-bg); color: var(--color-warning); }
.etiqueta-error    { background: var(--color-danger-bg);  color: var(--color-danger); }

/* Estado normal: texto plano, sin caja. Un sistema donde todo está
   "Activo" no necesita 24 etiquetas verdes diciéndolo. */
.estado-normal {
  color: var(--color-ink-soft);
  font-size: var(--text-base);
}

/* --- Pie del panel: conteo + paginación --- */
.tabla-pie {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--color-border);
  flex-wrap: wrap;
}

.tabla-pie-conteo {
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

/* --- Cabecera de pantalla: un solo botón destacado --- */
/* ══ LA CABECERA DE MODULO: LAS ACCIONES SIEMPRE DEBAJO ══
   (Vladimir, 05/09/2026)

   *«Todos igual, me refiero a la colocación: en algunos está al lado del
   título y en otro está debajo del título. Como quede mejor, pero todos
   iguales.»*

   Y tenía razón en que estaban distintos, aunque el HTML fuera el mismo: era
   un flex en una sola fila que **envolvía cuando no cabía**. Clientes tiene
   un botón más que Catálogo, así que su fila bajaba y las otras no — y con
   la ventana un poco más estrecha, cualquiera de las tres podía cambiar de
   sitio. **Ponerlas «al lado» no se puede garantizar**: depende del número
   de botones y del ancho de la pantalla, que son dos cosas que cambian.

   Debajo sí es estable, y además se lee mejor: título → qué es esta pantalla
   → qué puedo hacer aquí, en una sola columna. Con las acciones a la
   derecha, la vista salta un hueco vacío que crece con la pantalla, y en un
   monitor ancho «Nuevo artículo» acaba a un palmo de su título.

   `grid` en vez de `flex` porque es justo lo que se pide: dos filas, y la
   segunda empieza donde empieza la primera. ═══════════════════════════ */
.encabezado-modulo {
  display: grid;
  /* **`start`, y no es opcional.** Un hijo de grid se estira a lo ancho de
     la columna si no se dice otra cosa, y en «Bandeja de correo» —donde el
     boton va suelto, sin envolverlo en `.acciones`— eso convirtio «Redactar»
     en una barra verde de lado a lado. Lo vio la comprobacion visual de las
     cabeceras, no una prueba: es CSS, y no revienta nada. */
  justify-items: start;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}

.encabezado-modulo h1 {
  font-size: var(--text-2xl);
  margin: 0;
}

.encabezado-modulo .subtitle {
  margin: var(--space-1) 0 0;
}

/* Acción secundaria de cabecera: discreta, sin caja */
/* **Pesa como los demás botones** (Vladimir, 14/09/2026). Lo vio en
   «Reordenar contactos», al lado de «Agregar contacto»: *«ponlo en negrita
   para que se vea igual que el botón de al lado»*, y despues precisó *«o sin
   marco pero en negrita»*.

   Todos los botones del sistema van en 600 y este bajaba a 500, así que
   junto a cualquier otro parecía apagado. **Lo que lo distingue sigue
   siendo que no lleva fondo ni marco**, que es para lo que existe: no hacía
   falta además quitarle peso a la letra. Se arregla en la clase y no en ese
   botón, para que valga en los demás sitios donde convive con otro. */
.btn-discreto {
  background: transparent;
  border-color: transparent;
  color: var(--color-ink-soft);
}

.btn-discreto:hover {
  background: var(--color-gray-100);
  border-color: transparent;
  color: var(--color-ink);
}

/* ── EL DISCRETO QUE VA EN PAREJA (14/09/2026) ────────────────────────
   Vladimir, sobre «Reordenar contactos» al lado de «Agregar contacto»:
   «creo que los 2 titulos tienen tamano diferente del texto».

   **Medido: no lo tenian.** Los dos son `btn-pequeno` —13 px— y los dos
   pesan 600, porque `.btn-discreto` no toca ni el tamano ni el grosor. Lo
   que los separaba era el COLOR: un gris claro al mismo grosor se lee como
   mas fino, y de ahi la impresion de que uno era menor.

   Este modificador le da la tinta entera. **Existe como clase aparte y no
   se cambia `.btn-discreto`** porque esa la usan 49 pantallas y ahi el gris
   es intencional: marca la accion secundaria frente a la principal. Aqui no
   hay principal y secundaria — son dos formas de trabajar con la misma
   lista de contactos, y por eso tienen que leerse igual.

   El marco sigue sin ponerse: «o sin marco pero en negrita» (13/09). */
.btn-discreto--tinta {
  color: var(--color-ink);
}

/* --- Aviso accionable: reemplaza a las tarjetas de estadística --- */
.aviso-modulo {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
  background: var(--color-caution-bg);
  border: 1px solid var(--color-caution);
  border-radius: var(--radius-md);
  color: var(--color-caution);
  font-size: var(--text-base);
}

.aviso-modulo a {
  margin-left: auto;
  color: inherit;
  font-weight: 600;
  white-space: nowrap;
}

/* --- Confirmación destructiva: escribir para confirmar --- */
.confirmar-escribiendo code {
  padding: 1px 6px;
  background: var(--color-gray-100);
  border-radius: var(--radius-sm);
  font-weight: 700;
  color: var(--color-ink);
}


/* ============================================================
   13. PANEL LATERAL (slide-over)

   Para editar un registro sin salir del listado y sin deformar la
   tabla, que es lo que hacía el formulario desplegado dentro de la
   celda. Comparte motor con el modal (mismo store de Alpine): foco
   que entra, queda atrapado y vuelve al origen, y cierre con Escape.
   Solo cambia la presentación.
   ============================================================ */

.panel-fondo {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal-fondo);
  background: rgba(22, 32, 58, 0.45);
}

.panel-lateral {
  position: fixed;
  z-index: var(--z-modal);
  top: 0;
  right: 0;
  bottom: 0;
  width: 100%;
  max-width: 480px;
  display: flex;
  flex-direction: column;
  background: var(--color-white);
  box-shadow: var(--shadow-lg);
}

/* En móvil ocupa todo el ancho disponible: no tiene sentido dejar una
   franja del listado detrás que no se puede usar. */
@media (max-width: 560px) {
  .panel-lateral {
    max-width: none;
  }
}

.panel-cabecera {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--color-border);
  flex-shrink: 0;
}

.panel-titulo {
  margin: 0;
  font-size: var(--text-xl);
  font-weight: 600;
  color: var(--color-ink);
}

.panel-cerrar {
  margin-left: auto;
  min-height: 0;
  padding: var(--space-1);
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  color: var(--color-gray-500);
  cursor: pointer;
}

.panel-cerrar:hover {
  background: var(--color-fila-hover);
  color: var(--color-ink);
}

.panel-cuerpo {
  flex: 1 1 auto;
  overflow-y: auto;
  padding: var(--space-5);
}

.panel-cuerpo > :first-child {
  margin-top: 0;
}

.panel-pie {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--color-border);
  background: var(--color-gray-50);
  flex-shrink: 0;
}

/* El formulario ocupa el panel entero para que su botón de guardar
   quede fijo abajo, sin depender de cuánto se desplace el contenido. */
.panel-formulario {
  display: contents;
}

/* ============================================================
   14. CENTRO DE OPERACIONES (pantalla de inicio)

   Bandeja unificada de asuntos que esperan una decisión. Reemplaza al
   dashboard de tarjetas de cifras: un número sin contexto no pide
   ninguna decisión, y por eso no ocupa espacio aquí.

   Lenguaje visual: superficie blanca sobre el fondo de la app, sin
   bordes de caja — la elevación la da una sombra mínima. El color se
   reserva para lo que significa algo (crítico, atención, éxito y la
   acción principal); todo lo demás es tinta y gris.
   ============================================================ */

/* ══ LA MISMA CABECERA QUE `encabezado-modulo` (Vladimir, 05/09/2026) ══

   *«Esta pantalla, los subtítulos ponlos como están las demás páginas:
   debajo del título, a la izquierda.»* Lo dijo sobre «Plantillas de correo»,
   y el motivo de que fuera distinta es que **el sistema tiene DOS cabeceras
   de pantalla** —esta y `.encabezado-modulo`—, cada una con su reparto. Las
   dos hacen lo mismo: título, frase y acciones.

   Ahora las dos reparten igual: **las acciones debajo, a la izquierda**. No
   es solo consistencia — arregla de raíz el fallo del 22/08 en las órdenes
   de compra, cuando dos fichas del mismo plan se veían distintas **según lo
   largo que fuera el nombre del proveedor**: los botones cabían arriba o se
   caían abajo. Con una fila propia, el texto ya no decide dónde están.

   Que sigan siendo dos clases es deuda, no diseño, y está anotada en
   `ESTADO.md` para unirlas en una. Mientras tanto **se comportan igual**,
   que es lo que se ve. */
.op-cab {
  display: grid;
  /* Sin esto, un hijo de grid se estira a lo ancho de la columna: un botón
     suelto —sin envolver en `.acciones`— sale como una barra de lado a lado. */
  justify-items: start;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}

/* La frase puede partirse en dos líneas; es lo que tiene que ceder. */
.op-cab > :first-child {
  min-width: 0;
}

/* ══ Y EL BOTON VERDE, A LA DERECHA (Vladimir, 05/09/2026) ══

   *«Aunque ciertamente los botones verdes se ven mejor a la derecha en todos
   los lados, si puedes colocarlos a la derecha mejor.»*

   Así que la fila de acciones ocupa el ancho entero y **la acción principal
   se va al final**: las secundarias —imprimir, exportar, importar— quedan
   pegadas al título, y lo que crea algo nuevo queda separado de ellas.

   **La principal se reconoce por lo que NO lleva**: `btn` a secas. Las demás
   son `btn-discreto`, `btn-secundario`, `btn-peligro` o `btn-pequeno`. Se
   empuja la PRIMERA que aparezca —y con ella todo lo que venga detrás—, para
   que dos principales seguidas no abran dos huecos.

   Y `~ *` en vez de tocar cada botón: sin eso, una segunda principal se
   separaría de la primera con otro hueco en medio. */
.encabezado-modulo > .acciones,
.op-cab > .acciones {
  justify-self: stretch;
}

.encabezado-modulo > .acciones > .btn:not([class*=" btn-"]),
.op-cab > .acciones > .btn:not([class*=" btn-"]),
.encabezado-modulo > .btn:not([class*=" btn-"]),
.op-cab > .btn:not([class*=" btn-"]) {
  margin-left: auto;
}

/* ══ Y SI LA PRINCIPAL VA DENTRO DE UN FORMULARIO (13/09/2026) ══

   Vladimir: *«en proveedores los botones verde estan a la izquierda y en todo
   el sistema los botones verde estan a la derecha»*.

   La regla de arriba alcanza a los hijos DIRECTOS de `.acciones`. En la ficha
   del proveedor, «Solicitar cotización» envía datos, así que va envuelto en
   un `<form>` — y ese envoltorio dejaba fuera al botón, que se quedaba pegado
   a «Notas» y «Editar» mientras en el resto del sistema el verde va al final.

   Se arregla aquí y no en esa plantilla para que valga en cualquier pantalla
   donde la acción principal necesite un formulario. */
.encabezado-modulo > .acciones > form:has(> .btn:not([class*=" btn-"])),
.op-cab > .acciones > form:has(> .btn:not([class*=" btn-"])) {
  margin-left: auto;
}

/* Lo que va detrás de la principal se queda pegado a ella. */
.encabezado-modulo > .acciones > .btn:not([class*=" btn-"]) ~ *,
.op-cab > .acciones > .btn:not([class*=" btn-"]) ~ * {
  margin-left: 0;
}

.op-saludo {
  margin: 0;
  font-size: var(--text-2xl);
  font-weight: 800;
  color: var(--color-ink);
  letter-spacing: -0.02em;
}

/* Primer nivel de lectura: el estado del negocio en una frase. Va un
   punto por encima del texto normal para ganarle a la bandeja. */
.op-frase {
  margin: var(--space-2) 0 0;
  font-size: var(--text-lg);
  color: var(--color-ink-soft);
}

.op-frase b {
  color: var(--color-ink);
  font-weight: 700;
}

.op-grid {
  display: grid;
  grid-template-columns: 1fr 320px;
  gap: var(--space-4);
  align-items: start;
}

@media (max-width: 960px) {
  .op-grid { grid-template-columns: 1fr; }
}

/* Sin esto, un hijo de la rejilla no encoge por debajo del ancho de su
   contenido (una tabla ancha, un texto largo) y desborda la pantalla en
   móvil aunque la tabla tenga su propio scroll. */
.op-grid > * { min-width: 0; }

/* ═══ Cotización en borrador · el añadir y el cliente, anclados arriba ═══
   El formulario de artículos se captura muchas veces seguidas; si hay que
   ir con el scroll en cada línea, es inviable (cotizaciones de 20-35
   artículos). Se anclan bajo la topbar: «Agregar artículo» a la izquierda
   y «Cliente» a la derecha quedan siempre a la vista y la tabla corre por
   debajo. Ambos tienen fondo propio (.bandeja / .rail-caja), así que tapan
   limpiamente lo que pasa por detrás.

   Solo en pantalla ancha: por debajo de 960px la rejilla es de una sola
   columna y anclarlos comería el alto útil del móvil. */
@media (min-width: 961px) {
  .cot-fijo-izq,
  .cot-fijo-der {
    position: sticky;
    top: calc(var(--topbar-height) + var(--space-3));
    z-index: 5;
  }
}

/* --- Bandeja --------------------------------------------------
   Sin borde: una sombra mínima basta para separarla del fondo y quita
   una línea de ruido en una pantalla que ya tiene muchas filas. */
.bandeja {
  background: var(--color-white);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}

.bandeja-cab {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5) var(--space-3);
  /* Envuelve: con un botón de acción al lado del título, en móvil no
     caben en una línea y la tarjeta desbordaba la pantalla. */
  flex-wrap: wrap;
}

/* --- EL TITULO DE UN PANEL USA EL TOKEN DE LOS TITULOS ---
   Vladimir, 23/08/2026: «sube un poquito el tamano a Precios cargados sin
   solicitud», sin igualarlo al titulo de la pagina ni bajar ese.

   No hace falta inventar una medida: la escala YA declara 20 px para
   «titulos de tarjeta» y este componente estaba usando el token del
   CUERPO DE TEXTO, 16 px. Era una desviacion del sistema, no una
   decision — y por eso el panel se leia como parrafo en negrita.

   Sube en TODOS los paneles a la vez, que es lo que evita el problema
   de verdad: dos titulos de panel de distinto tamano en la misma
   pantalla se leen como un error, no como una jerarquia. La jerarquia
   sigue clara — 28 la pagina, 20 el panel. --- */
.bandeja-cab h2 {
  margin: 0;
  font-size: var(--text-xl);
  font-weight: 700;
  color: var(--color-ink);
  letter-spacing: -0.01em;
}

/* Único punto rojo de la cabecera: cuenta lo que exige decisión. */
.conteo-pill {
  padding: 1px var(--space-2);
  border-radius: var(--radius-pill);
  background: var(--color-danger-bg);
  color: var(--color-danger);
  font-size: var(--text-xs);
  font-weight: 800;
}

/* Franja: rótulo estructural, no una barra de color. Sin relleno de
   fondo se pierde una caja y la lista respira. */
.franja {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-4) var(--space-5) var(--space-2);
  font-size: var(--text-xs);
  font-weight: 800;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-gray-500);
}

/* --- Asunto --- */
.dec {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  padding: var(--space-3) var(--space-5);
  border-top: 1px solid var(--color-gray-100);
}

/* La primera fila de cada franja no lleva línea: ya la separa el rótulo */
.franja + .dec { border-top: none; }

/* ── LA FILA QUE CUELGA DE LA ANTERIOR ────────────────────────────────
   Los botones de «por dónde se lo mando» pertenecen al proveedor de la
   fila de arriba, no son otro proveedor. Sin la sangría y sin quitar la
   línea de separación, con cinco proveedores la columna se lee como diez
   filas sueltas y hay que contar para saber qué botón es de quién.     */
.dec-sangrada {
  border-top: none;
  padding-top: 0;
  padding-left: calc(var(--space-5) + var(--space-3) + 16px);
}

/* Que envolver es lo correcto: en el móvil los dos botones no caben al
   lado, y el que se sale es el de WhatsApp — el más usado con proveedores. */
.acciones-envio {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* Misma regla que en las tablas: se resalta lo que se abre, y solo eso.
   Una franja de decisiones mezcla filas que llevan a una pantalla con
   filas que solo informan o que llevan dentro un formulario. */
.dec[data-abrir] { cursor: pointer; }

.dec[data-abrir]:hover,
.dec[data-abrir]:focus-within { background: var(--color-fila-hover); }

.dec-icono {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  flex-shrink: 0;
  border-radius: var(--radius-sm);
}

/* Color solo donde comunica. Lo neutro no lleva fondo: si todo va
   teñido, el rojo deja de significar nada. */
.marca-error    { background: var(--color-danger-bg);  color: var(--color-danger); }
.marca-atencion { background: var(--color-warning-bg); color: var(--color-warning); }
.marca-exito    { background: var(--color-success-bg); color: var(--color-success); }
.marca-espera   { background: transparent; color: var(--color-gray-500); }
.marca-neutra   { background: transparent; color: var(--color-gray-500); }

.dec-cuerpo { flex: 1; min-width: 0; }

.dec-tit {
  display: block;
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--color-ink);
  line-height: 1.4;
}

.dec-sub {
  display: block;
  margin-top: 2px;
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
  line-height: 1.45;
}

.dec-acc {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-shrink: 0;
  flex-wrap: wrap;
}

/* El importe en juego. Cifras de ancho fijo para que cuadren entre
   filas, y en tinta: es un dato, no una alarma. */
.impacto {
  font-size: var(--text-base);
  font-weight: 700;
  color: var(--color-ink);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Jerarquía de acciones: lo de AHORA se ve como botón; lo de la semana
   es un enlace discreto. El peso del botón indica cuánto corre prisa. */
.dec-accion-suave {
  background: transparent;
  border-color: transparent;
  color: var(--color-ink-soft);
  font-weight: 600;
}

.dec-accion-suave:hover {
  background: var(--color-gray-100);
  border-color: transparent;
  color: var(--color-ink);
}

/* En móvil la acción baja debajo del texto en vez de estrujarlo */
@media (max-width: 620px) {
  .dec { flex-wrap: wrap; align-items: flex-start; }
  .dec-acc { width: 100%; padding-left: 44px; gap: var(--space-2); }
}

/* Fila de cierre de franja: lo que no cabe, con su salida al listado
   del módulo correspondiente. */
.dec-resto {
  padding-top: var(--space-2);
  padding-bottom: var(--space-3);
}

/* Ya no hace falta apagarle el resaltado: sin `data-abrir` no se pinta.
   Este parche existía para tapar la regla general que pintaba todo. */

/* --- Carril derecho --- */
/* ══ EL CARRIL DERECHO, COMO LA REFERENCIA (Vladimir, 29/08/2026) ══
   «Quiero el menú derecho así.»

   Lo que cambia es el TRATAMIENTO, no el contenido —ese ya era el suyo—:
   cabecera oscura con el rótulo en blanco, cada línea separada de la
   siguiente, el rótulo en color y la cifra en tinta.

   El relleno se va de la caja y pasa a sus partes, porque la cabecera
   tiene que llegar de borde a borde. */
.rail-caja {
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  margin-bottom: var(--space-4);
}

/* Lo que no es la cabecera respira dentro. */
.rail-caja > *:not(.rail-tit) {
  padding-left: var(--space-4);
  padding-right: var(--space-4);
}
.rail-caja > *:last-child { padding-bottom: var(--space-3); }

.rail-caja:last-child { margin-bottom: 0; }

/* Tercer nivel de lectura: las métricas van después de las prioridades,
   y su rótulo es deliberadamente discreto. */
/* La cabecera: la misma tinta del menú, para que las dos columnas oscuras
   de la pantalla —menú y carril— se reconozcan como del mismo sistema. */
.rail-tit {
  margin: 0 0 var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--color-menu);
  font-size: var(--text-xs);
  font-weight: 800;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--color-white);
}

/* Cada dato, separado del siguiente por una línea: son cifras distintas y
   sin la línea se leen como un bloque. */
.rail-caja .pulso {
  padding-top: var(--space-2);
  padding-bottom: var(--space-2);
}
.rail-caja .pulso + .pulso { border-top: 1px solid var(--color-gray-100); }

/* El rótulo lleva el color; la cifra va en tinta, que es lo que la hace
   legible. Es la referencia, y el primer intento —cada línea sobre un piso
   de color— Vladimir lo descartó. */
.rail-caja .pulso-lbl { font-weight: 600; font-size: var(--text-sm); }
.pulso--cotizado .pulso-lbl { color: var(--color-azul); }
.pulso--aceptado .pulso-lbl,
.pulso--aceptado .pulso-val { color: var(--color-primary-texto); }
.pulso--beneficio .pulso-lbl { color: var(--color-morado); }

.pulso {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: var(--space-3);
  padding: var(--space-2) 0;
}

.pulso + .pulso { border-top: 1px solid var(--color-gray-100); }

.pulso-lbl {
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

.pulso-val {
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--color-ink);
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.01em;
}

/* La barra pertenece a la tasa de cierre: pegada a su cifra, no
   flotando entre dos filas. */
/* La barra pertenece a la tasa de cierre, pero necesita aire: pegada al
   rótulo se leía como un subrayado del texto, no como una medida. */
.pulso-con-barra { padding-bottom: var(--space-1); }

.barra-mini {
  height: 6px;
  margin: var(--space-1) 0 var(--space-3);
  border-radius: var(--radius-pill);
  background: var(--color-border);
  overflow: hidden;
}

.barra-mini i {
  display: block;
  height: 100%;
  background: var(--color-primary);
  border-radius: var(--radius-pill);
}

.rail-nota {
  margin: var(--space-2) 0 var(--space-1);
  font-size: var(--text-xs);
  color: var(--color-gray-500);
}

.oport {
  display: flex;
  gap: var(--space-3);
  align-items: center;
  padding: var(--space-2) 0;
}

.oport + .oport { border-top: 1px solid var(--color-gray-100); }

.oport-txt {
  flex: 1;
  min-width: 0;
  font-size: var(--text-sm);
  line-height: 1.4;
}

.oport-txt a {
  color: var(--color-ink);
  font-weight: 600;
  text-decoration: none;
}

.oport-txt a:hover { color: var(--color-primary-texto); text-decoration: underline; }

.oport-txt span {
  display: block;
  margin-top: 1px;
  font-size: var(--text-xs);
  color: var(--color-ink-soft);
}

/* Cabecera compacta en móvil: los botones dejan de ocupar una fila
   entera y el contenido sube. */
@media (max-width: 620px) {
  .op-cab { gap: var(--space-3); margin-bottom: var(--space-4); }
  .op-saludo { font-size: 22px; }
  .op-frase { font-size: var(--text-base); margin-top: var(--space-1); }
  .op-cab .acciones { width: 100%; gap: var(--space-2); }
  .op-cab .acciones .btn {
    flex: 1;
    min-height: 34px;
    padding: var(--space-1) var(--space-3);
    font-size: var(--text-sm);
  }
}


/* --- Ficha de registro (cliente, proveedor, artículo…) ---
   Reutiliza el lenguaje del Centro de Operaciones: a la izquierda lo que
   se hace con el registro, a la derecha quién es. Que ambas pantallas
   compartan estructura es lo que hace que se sientan un solo producto. */
.bandeja + .bandeja { margin-top: var(--space-4); }

/* --- DOS CAJAS EN PARALELO, A LA MISMA ALTURA ---
   Vladimir, 23/08/2026: «sube el otro cuadro y colocalos ambos de manera
   paralela, deben quedar con la misma altura ambos cuadros».

   Las dos son cortas —unas notas de dos lineas y el estado con sus
   botones—: una encima de otra dejaban media pantalla de aire y obligaban
   a bajar para ver en que punto esta la orden.

   **La misma altura NO se fija con una medida**, que se quedaria corta en
   cuanto una de las dos crezca. Se deja que la rejilla estire las dos
   celdas hasta la mas alta, que es su comportamiento por defecto: por eso
   aqui NO hay `align-items: start` como en `.op-grid`, y quitarlo es
   justamente lo que hace el trabajo.

   Y se anula el margen de `.bandeja + .bandeja`: dentro de la rejilla, la
   segunda caja es hermana de la primera, asi que heredaba una separacion
   superior que la bajaba y las dejaba descuadradas. Va DESPUES de aquella
   regla, no solo con mas especificidad: en este proyecto una regla que se
   lee bien en el archivo y no se ve en la pantalla ya ha costado cuatro
   caidas. --- */
.par-de-bandejas {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-4);
}

.par-de-bandejas > .bandeja + .bandeja { margin-top: 0; }

/* --- LA REJILLA TAMBIEN SE SEPARA DE LO QUE TIENE AL LADO ---
   Vladimir, 23/08/2026: «quedo muy pegado del cuadro de arriba, dale mas
   espacios, separalos». **Medido: el hueco era CERO.**

   La separacion entre tarjetas la da `.bandeja + .bandeja`, y lo que sigue
   a la tabla dejo de ser una caja para ser esta rejilla, asi que la regla
   ya no la alcanzaba. No es que el hueco fuera pequeno: no existia, y lo
   que se veia era la sombra de la tarjeta.

   **Y se separa MAS que dos cajas seguidas** —24 px contra 16—: la rejilla
   no es una tarjeta mas de la columna, es una FILA de dos. Con el mismo
   hueco que el resto, el ojo la lee como continuacion de la tabla de
   arriba en vez de como un bloque nuevo. --- */
.bandeja + .par-de-bandejas,
.par-de-bandejas + .bandeja,
.par-de-bandejas + .par-de-bandejas {
  margin-top: var(--space-5);
}

/* Una al lado de la otra solo mientras quepan: por debajo de esto, dos
   columnas dejan los botones en dos lineas cada uno. */
@media (max-width: 900px) {
  .par-de-bandejas { grid-template-columns: minmax(0, 1fr); }
  .par-de-bandejas > .bandeja + .bandeja { margin-top: var(--space-4); }
}

.bandeja-cab .acciones { margin-left: auto; }

/* Formulario embebido en una fila de la bandeja (redactar correo) */
.dec .form-linea { margin: 0; }


/* Subfila de contexto dentro de la bandeja (historial de precios de un
   proveedor). Sangrada y sin icono: pertenece a la fila de arriba. */
.subfila-precio {
  padding-top: var(--space-1);
  padding-bottom: var(--space-1);
  padding-left: calc(var(--space-5) + 32px + var(--space-3));
  border-top: none;
}

/* Igual que en `.dec-resto`: sin `data-abrir` no hay nada que apagar. */


/* ============================================================
   15. Cotizaciones
   ============================================================ */

/* Cifras alineadas a la derecha: así los montos se comparan de un
   vistazo por la columna de las unidades, no por el primer dígito. */
.col-numero { text-align: right; }

/* El total cierra la caja de totales: sin línea superior fina sino con
   una separación real, para que se lea como conclusión y no como una
   fila más de la lista. */
.pulso-total {
  border-top: 1px solid var(--color-border) !important;
  margin-top: var(--space-2);
  padding-top: var(--space-3);
  align-items: baseline;
}

/* --- Elegir canal de envío ---
   Cada opción muestra a dónde va el mensaje. Antes era un menú
   desplegable que solo decía "Por correo" sin la dirección. */
.opciones-envio {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-top: var(--space-3);
}

.opcion-envio { margin: 0; }

.opcion-envio-btn {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-3);
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  text-align: left;
  text-decoration: none;
  color: inherit;
  cursor: pointer;
  transition: border-color 0.12s ease, background-color 0.12s ease;
}

.opcion-envio-btn:hover {
  border-color: var(--color-primary);
  background: var(--color-surface);
}

.opcion-envio-btn:focus-visible {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: var(--focus-ring);
}

/* El hilo dentro del panel lateral: el panel ya acota la altura y ya
   desplaza. Si el hilo conservara su tope y su scroll propios habria dos
   barras anidadas, y quien arrastra acaba moviendo la que no es. */
.panel-cuerpo .chat-thread {
  max-height: none;
  overflow-y: visible;
  padding: 0;
}

/* El hilo de chat dentro de una bandeja: alinea su margen con el del
   resto de la tarjeta, que trae el suyo propio más estrecho. */
.bandeja .chat-thread {
  padding: var(--space-2) var(--space-5);
}


/* ============================================================
   16. Importaciones
   ============================================================ */

/* --- Recorrido de tres pasos --- */
.pasos-imp {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-5);
  list-style: none;
  margin: 0 0 var(--space-5);
  padding: 0;
}

.paso-imp {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  /* gray-500 no llega a 4.5:1 sobre la superficie de la página: el paso
     pendiente se leia por debajo del minimo AA. */
  color: var(--color-ink-soft);
}

.paso-imp-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: var(--radius-full);
  background: var(--color-gray-100);
  color: var(--color-ink-soft);
  font-size: var(--text-xs);
  font-weight: 700;
}

.paso-imp-actual { color: var(--color-ink); font-weight: 600; }
.paso-imp-actual .paso-imp-num { background: var(--color-primary); color: var(--color-white); }
.paso-imp-hecho .paso-imp-num { background: var(--color-success-bg); color: var(--color-success); }

/* --- Recuento de la validación --- */
.resumen-imp {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
}

.resumen-imp-dato {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  background: var(--color-gray-50);
}

.resumen-imp-num {
  font-size: var(--text-2xl);
  font-weight: 700;
  line-height: 1;
  color: var(--color-ink);
  font-variant-numeric: tabular-nums;
}

.resumen-imp-lbl {
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

.resumen-imp-ok     { background: var(--color-success-bg); }
.resumen-imp-espera { background: var(--color-warning-bg); }
.resumen-imp-error  { background: var(--color-danger-bg); }

/* El grupo de opciones como `fieldset`: la leyenda da el nombre accesible
   al conjunto de radios, pero el borde nativo del navegador no es del
   sistema. Se quita el marco y se conserva la semántica. */
fieldset.opciones {
  border: 0;
  margin: 0;
  padding: 0;
  min-width: 0;
}

fieldset.opciones > legend {
  padding: 0;
  margin-bottom: var(--space-2);
}

/* El `fieldset` que solo sirve para DESHABILITAR un grupo entero.
   Nace de la regla «nada se esconde»: una acción que no está disponible
   se muestra deshabilitada con el motivo escrito, nunca desaparece. El
   `disabled` del fieldset alcanza a todos sus campos de una vez, que es
   justo lo que hace falta; el marco nativo del navegador no es del
   sistema y se quita. */
fieldset.sin-marco {
  border: 0;
  margin: 0;
  padding: 0;
  min-width: 0;
}

/* Un precio anulado sigue en el historial, pero no compite.
   El tachado sobre el número es lo que dice de un vistazo que ese importe
   ya no cuenta; el resto de la línea se lee normal porque el motivo y
   quién lo anuló SÍ hay que poder leerlos — son lo que justifica la
   anulación. Bajar la fila entera los haría ilegibles. */
.dec-anulada {
  background: var(--color-surface);
}

.precio-anulado {
  text-decoration: line-through;
  text-decoration-thickness: 1px;
  color: var(--color-ink-soft);
}

fieldset.sin-marco:disabled {
  opacity: 0.55;
  /* Sin esto, el cursor sigue siendo el de un control activo y el
     usuario prueba a pulsar antes de leer el motivo. */
  cursor: not-allowed;
}


/* ============================================================
   17. Índice de administración y reglas de alerta
   ============================================================ */

/* --- Tarjetas de un índice de secciones --- */
.rejilla-hub {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: var(--space-4);
}

.hub-tarjeta {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-5);
  background: var(--color-white);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  text-decoration: none;
  color: inherit;
  transition: box-shadow 0.12s ease, transform 0.12s ease;
}

.hub-tarjeta:hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-1px);
}

.hub-tarjeta:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.hub-icono {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-md);
  background: var(--color-success-bg);
  color: var(--color-primary-texto);
  margin-bottom: var(--space-1);
}

.hub-titulo {
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--color-ink);
  letter-spacing: -0.01em;
}

.hub-texto {
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
  line-height: 1.5;
}

/* --- Una regla de alerta como fila de bandeja ---
   Cada regla es su propio formulario: se guarda sola, sin arrastrar al
   resto. Envuelve en pantallas chicas para que los controles no se
   compriman contra el texto. */
.regla-fila {
  flex-wrap: wrap;
  align-items: center;
}

.regla-controles {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-left: auto;
}

.regla-umbral {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.regla-umbral input { margin: 0; }
.regla-umbral .campo-ayuda { margin: 0; white-space: nowrap; }
.regla-fila .checkbox-line { margin: 0; }


/* ============================================================
   18. Pantallas sin sesión (login, 2FA, contraseñas)
   ============================================================ */

/* Es la primera pantalla que ve cualquiera, incluido un cliente en una
   demostración: sustituye a `center-screen` + `login-box`, que dejaban la
   tarjeta blanca flotando sobre un fondo plano. */
.pantalla-auth {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  padding: var(--space-5);
  background: var(--color-surface);
}

.auth-caja {
  width: 100%;
  max-width: 380px;
  padding: var(--space-6);
  background: var(--color-white);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  text-align: center;
}

.auth-caja-ancha { max-width: 440px; }

.auth-marca {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
}

.auth-logo {
  max-height: 56px;
  width: auto;
}

.auth-empresa {
  margin: 0;
  font-size: var(--text-sm);
  font-weight: 600;
  letter-spacing: 0.04em;
  /* **SIN MAYUSCULAS** — pendiente PUB2, cerrado el 20/08/2026. El nombre de
     la empresa se escribe como la empresa lo escribe: «Soluciones Medicas
     AG», no «SOLUCIONES MEDICAS AG». Las mayusculas de adorno gritan, se
     leen peor y aqui salen en la unica pantalla que ve un cliente. El dato
     no se toca: se quita el disfraz. */
  color: var(--color-ink-soft);
}

.auth-titulo {
  margin: 0 0 var(--space-2);
  font-size: var(--text-xl);
  font-weight: 700;
  color: var(--color-ink);
  letter-spacing: -0.01em;
}

.auth-sub {
  margin: 0 0 var(--space-4);
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--color-ink-soft);
}

.auth-pie {
  margin: var(--space-4) 0 0;
  font-size: var(--text-sm);
}

/* Un código de seis dígitos se teclea, se compara y se corrige: separar
   las cifras y centrarlas evita releerlo dígito a dígito. */
.campo-codigo {
  text-align: center;
  font-size: var(--text-xl);
  letter-spacing: 0.4em;
  font-variant-numeric: tabular-nums;
}

.auth-caja .alerta { text-align: left; }
.auth-caja label { text-align: left; }

.auth-pie a {
  color: var(--color-primary-texto);
  font-weight: 600;
}

.auth-caja .alerta { margin: var(--space-4) 0 0; }
.auth-caja form { margin-top: var(--space-4); }


/* ============================================================
   19. Centro de Marca (Empresa)
   ============================================================ */

/* Dos columnas del mismo peso: a la izquierda se escribe, a la derecha se
   ve el resultado. No es un carril de contexto como en las fichas, así que
   no reutiliza .op-grid. */
.marca-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-5);
  align-items: start;
}

@media (max-width: 1000px) {
  .marca-grid { grid-template-columns: minmax(0, 1fr); }
}

/* La vista previa acompaña mientras se baja por el formulario: sin esto
   habría que subir a mirar cada vez que se cambia un campo. */
.marca-previa-col { position: sticky; top: var(--space-5); }

@media (max-width: 1000px) {
  .marca-previa-col { position: static; }
}

.marca-logo-actual {
  display: flex;
  align-items: center;
  min-height: 56px;
  margin-bottom: var(--space-3);
}

.marca-logo-actual .vista-imagen { max-height: 56px; width: auto; }

/* --- Pestañas del documento previsualizado --- */
.marca-pestanas {
  display: flex;
  gap: var(--space-1);
  padding: 0 var(--space-5) var(--space-3);
  flex-wrap: wrap;
}

.marca-pestana {
  padding: var(--space-2) var(--space-3);
  border: 0;
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--color-ink-soft);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  transition: background-color 0.12s ease, color 0.12s ease;
}

/* Resaltado a `--color-fila-hover` y no `--color-gray-50` (28/08/2026):
   sobre blanco, el gris claro da 1,05:1 y no se ve. Vladimir: «es casi
   imperceptible ese sombreado». */
.marca-pestana:hover { background: var(--color-fila-hover); }

.marca-pestana:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

.marca-pestana-activa {
  background: var(--color-success-bg);
  color: var(--color-primary-texto);
}

.marca-lienzo {
  padding: var(--space-5);
  background: var(--color-surface);
  border-top: 1px solid var(--color-border);
}

/* --- El PDF de la cotización, a escala --- */
.prev-hoja {
  padding: var(--space-5);
  background: var(--color-white);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  font-size: var(--text-xs);
  color: var(--color-ink);
}

.prev-hoja-cab {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-4);
  padding-bottom: var(--space-3);
  border-bottom: 2px solid var(--color-ink);
}

.prev-logo { max-height: 36px; width: auto; margin-bottom: var(--space-2); display: block; }

.prev-nombre {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--color-ink);
  letter-spacing: -0.01em;
}

.prev-legal { margin: var(--space-1) 0 0; color: var(--color-ink-soft); }

.prev-doc { text-align: right; }
.prev-doc-tipo {
  margin: 0;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--color-ink-soft);
}
.prev-doc-num { margin: 0; font-size: var(--text-lg); font-weight: 700; }

.prev-para { margin: var(--space-4) 0; }
.prev-para p { margin: 0; }
.prev-etq {
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-ink-soft);
}

.prev-tabla { width: 100%; border-collapse: collapse; font-size: var(--text-xs); }
.prev-tabla th {
  text-align: left;
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
  color: var(--color-ink-soft);
  font-weight: 600;
}
.prev-tabla td { padding: var(--space-2) 0; border-bottom: 1px solid var(--color-gray-100); }
.prev-tabla th:last-child, .prev-tabla td:last-child,
.prev-tabla th:nth-child(2), .prev-tabla td:nth-child(2) { text-align: right; }

.prev-total {
  margin: var(--space-3) 0 0;
  text-align: right;
  font-size: var(--text-lg);
  font-weight: 700;
}

.prev-pie {
  margin-top: var(--space-5);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  color: var(--color-ink-soft);
}
.prev-pie p { margin: 0 0 var(--space-1); }
.prev-contacto { color: var(--color-ink-soft); }

/* --- El correo --- */
.prev-correo {
  background: var(--color-white);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
  font-size: var(--text-sm);
}

.prev-correo-cab {
  padding: var(--space-5);
  background: var(--color-surface);
  text-align: center;
  border-bottom: 1px solid var(--color-border);
}

.prev-logo-correo { margin: 0 auto var(--space-2); }

.prev-correo-cuerpo { padding: var(--space-5); }
.prev-correo-cuerpo p { margin: 0 0 var(--space-3); }

.prev-boton {
  display: inline-block;
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-sm);
  background: var(--color-primary);
  color: var(--color-white);
  font-weight: 600;
}

.prev-firma { color: var(--color-ink-soft); }

.prev-correo-pie {
  padding: var(--space-4) var(--space-5);
  background: var(--color-gray-50);
  color: var(--color-ink-soft);
  font-size: var(--text-xs);
  text-align: center;
}
.prev-correo-pie p { margin: 0; }

/* --- El mensaje de WhatsApp --- */
.prev-wa-burbuja {
  position: relative;
  max-width: 300px;
  padding: var(--space-3) var(--space-4) var(--space-5);
  border-radius: var(--radius-md);
  background: #d9fdd3;
  font-size: var(--text-sm);
  color: var(--color-ink);
}
.prev-wa-burbuja p { margin: 0 0 var(--space-2); }
.prev-wa-firma { color: var(--color-ink-soft); }

.prev-wa-hora {
  position: absolute;
  right: var(--space-3);
  bottom: var(--space-2);
  font-size: var(--text-xs);
  color: var(--color-ink-soft);
}

.prev-wa-nota {
  margin: var(--space-3) 0 0;
  font-size: var(--text-xs);
  color: var(--color-ink-soft);
}


/* ============================================================
   20. Miga de pan
   ============================================================ */

/* Va encima del titulo, en las pantallas de segundo nivel. Devuelve al
   listado padre —no al historial— y conserva filtros, busqueda y pagina. */
.miga {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
  font-size: var(--text-sm);
  flex-wrap: wrap;
}

.miga-volver {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-2) var(--space-1) var(--space-1);
  margin-left: calc(var(--space-2) * -1);
  border-radius: var(--radius-sm);
  color: var(--color-ink-soft);
  font-weight: 600;
  text-decoration: none;
  transition: background-color 0.12s ease, color 0.12s ease;
}

.miga-volver:hover {
  background: var(--color-fila-hover);
  color: var(--color-primary-texto);
  text-decoration: none;
}

.miga-volver:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* gray-500 se queda en 4.40:1 sobre la superficie de la pagina: por
   debajo del minimo AA, igual que paso con los pasos de importacion. */
.miga-sep { color: var(--color-ink-soft); }

/* El registro actual no es un enlace: ya estas en el. Se recorta si el
   nombre es largo en vez de empujar la fila a dos lineas. */
.miga-actual {
  color: var(--color-ink-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 60ch;
}

/* Flechas de orden de las cuentas bancarias */
.orden-cuenta {
  display: inline-flex;
  gap: 2px;
  margin-right: var(--space-2);
}

.orden-cuenta .btn-icono:disabled {
  opacity: 0.3;
  cursor: default;
}

.pagos-nota {
  padding: 0 var(--space-5) var(--space-4);
  margin: 0;
}

/* Firma cargada, con su accion de quitarla al lado */
.firma-actual {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-bottom: var(--space-3);
}

/* Bloque de cuentas dentro de la vista previa del Centro de Marca */
.prev-cuentas {
  margin: var(--space-4) 0;
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface);
  border-left: 3px solid var(--color-primary);
  border-radius: var(--radius-sm);
}

.prev-cuentas-tit {
  margin: 0 0 var(--space-1);
  font-size: 9px;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--color-ink-soft);
}

.prev-cuentas-linea {
  margin: 0 0 2px;
  font-weight: 700;
  color: var(--color-ink);
}

/* --- Vigencia de una cotización ---
   La fecha pasada se marca en el color de advertencia. El estado del
   documento no cambia por vencer: son dos cosas distintas. */
.fecha-vencida {
  color: var(--color-warning);
  font-weight: 600;
}

/* ── CADA CANAL LLEVA SU COLOR, SIEMPRE ───────────────────────────────
   Vladimir, 13/08/2026: «los sobres azul donde sea que aparezcan igual
   que la burbuja de whatsapp verde... igual los titulos».

   El color pertenece al CANAL, no al elemento: va en el icono, en el
   titulo de la franja y donde sea que se nombre. Antes cada pantalla
   elegia por su cuenta —gris aqui, verde alla— y el mismo sobre salia de
   tres colores distintos segun donde estuviera.                       */
.canal-icono {
  display: inline-flex;
  align-items: center;
  color: var(--color-correo);
  margin-right: var(--space-1);
}

.canal-icono.canal-whatsapp { color: var(--color-whatsapp); }

/* Las franjas que encabezan una seccion de canal. */
.franja-correo,
.franja-correo svg { color: var(--color-correo); }
.franja-whatsapp,
.franja-whatsapp svg { color: var(--color-whatsapp); }

/* Canal no disponible: se explica el motivo en vez de esconder la opción */
.opcion-envio-bloqueada,
.sin-canales {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: var(--space-2) 0 0;
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

.sin-canales > span {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

/* ── Comunicaciones dentro de una cotización ──────────────────────────
   Va debajo de la tabla de artículos y con su mismo ancho. La separación
   marca que empieza otra cosa: arriba se arma la oferta, aquí se habla
   con el cliente. */
.bloque-comunicaciones { margin-top: 24px; }

/* Vista previa del envío. NO se llama `.vista-previa`: esa clase ya
   existe desde el rediseño para el iframe de la cola de aprobación, y
   dos definiciones del mismo nombre hacen que la última pise a la
   primera —fue justo lo que rompió esa pantalla—. */
.envio-previa {
  border: 1px solid var(--linea);
  border-radius: var(--radio);
  overflow: hidden;
  margin: 12px 0;
  background: var(--superficie);
}
.envio-previa-cab,
.envio-previa-pie {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 12px 16px;
}
.envio-previa-cab { border-bottom: 1px solid var(--linea); }
.envio-previa-pie { border-top: 1px solid var(--linea); }
/* El correo trae su propio HTML: aislado, para que no pise los estilos
   de la aplicación ni al revés. */
.envio-previa-cuerpo {
  display: block;
  width: 100%;
  min-height: 360px;
  border: 0;
  background: #fff;
}

/* Una fila por canal en el pie de la vista previa: a la izquierda qué es
   y su estado, a la derecha la acción. */
.envio-previa-pie {
  display: block;
  padding: 0;
}
.canal-envio {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 16px;
  border-top: 1px solid var(--linea);
}
.canal-envio-texto { display: flex; flex-direction: column; gap: 2px; }
.canal-envio button[disabled] { opacity: .5; cursor: not-allowed; }

/* Título de una opción con su etiqueta de estado a la derecha.
   La etiqueta va separada del texto —no en línea— para que se vea de un
   golpe por qué canales ya salió el mensaje, sin leer la frase entera. */
.dec-tit-etiquetado {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  width: 100%;
}

/* La acción principal del bloque de Comunicaciones: mandarle el documento
   al cliente. Va arriba del todo porque es lo que más se hace. */
.accion-principal-comunicaciones {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
}


/* --- Panel de edición: subtítulo, pares y grupos plegables ---------
   Nacen con el panel de edición del cliente (04/08/2026). El panel
   sustituye a un formulario de pantalla completa con 28 campos seguidos:
   lo que se corrige a diario queda arriba y abierto, y lo que escribe el
   sistema se pliega al fondo. Todo sale de tokens.css. */

.panel-sub {
  margin: calc(var(--space-2) * -1) 0 var(--space-4);
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

/* Dos campos que se leen juntos —plazo y monto, tipo y número— sin
   obligar a bajar la vista dos veces por un dato de una línea. */
/* --- Teléfono + extensión ------------------------------------------
   La extensión es un dato SUBORDINADO: acompaña al número, no compite
   con él. Por eso comparte fila y ocupa lo justo para tres o cuatro
   dígitos — darle media fila diría que son dos datos igual de
   importantes, y no lo son.

   Vive junta porque separarlas invita a escribir «ext. 105» dentro del
   teléfono, que es exactamente el fallo del 07/08/2026. */
.campo-telefono {
  display: grid;
  grid-template-columns: 1fr 88px;
  gap: var(--space-2);
  align-items: end;
}

.campo-telefono > label {
  min-width: 0;
}

.panel-fila-2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
}

.panel-plegable {
  border-top: 1px solid var(--color-border);
  margin-top: var(--space-4);
}

.panel-plegable-cab {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-3) 0;
  background: none;
  border: 0;
  cursor: pointer;
  text-align: left;
  color: var(--color-ink);
}

.panel-plegable-cab:hover .panel-plegable-t {
  color: var(--color-primary-texto);
}

.panel-plegable-cab:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

.panel-plegable-t {
  font-size: var(--text-base);
  font-weight: 600;
}

/* Qué hay dentro, sin tener que abrirlo para averiguarlo. */
.panel-plegable-sub {
  font-size: var(--text-xs);
  color: var(--color-ink-soft);
  margin-left: auto;
}

.panel-plegable-chevron {
  display: inline-flex;
  color: var(--color-ink-soft);
  transition: transform 120ms ease;
}

.panel-plegable-chevron-abierto {
  transform: rotate(180deg);
}

@media (prefers-reduced-motion: reduce) {
  .panel-plegable-chevron { transition: none; }
}

.panel-plegable-cuerpo {
  padding-bottom: var(--space-3);
}


/* --- Resultado de una importación ---------------------------------
   Nace el 04/08/2026. Una importación decía «216 actualizados» y no
   había cambiado nada: ese 216 eran las filas recorridas. Lo que hacía
   falta ver de un vistazo era CUÁNTOS cambiaron de verdad, así que ese
   número se destaca — también cuando es cero, porque un cero ahí es
   información. */

.imp-cifras {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-5);
}

.imp-cifra {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  min-width: 0;
}

.imp-cifra-num {
  font-size: var(--text-2xl);
  font-weight: 700;
  color: var(--color-ink);
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

.imp-cifra-lbl {
  font-size: var(--text-xs);
  color: var(--color-ink-soft);
}

/* El dato que faltaba: se lee antes que los demás. */
.imp-cifra-clave .imp-cifra-num { color: var(--color-primary-texto); }
.imp-cifra-clave .imp-cifra-lbl { color: var(--color-ink); font-weight: 600; }

/* El veredicto de la confirmación: el verbo y el número, no una casilla. */
.imp-veredicto {
  font-size: var(--text-lg);
  color: var(--color-ink);
  margin: 0 0 var(--space-3);
}

.imp-veredicto-fuerte strong { color: var(--color-primary-texto); }

/* ── ¿El cliente ya respondió? ──────────────────────────────────────
   Va junto al botón de enviar y en la cola de aprobación. Tono INFO,
   no de aviso: no es que algo esté mal, es un dato que el sistema no
   puede tener y la persona sí. Un fondo ámbar la leería como error y
   quien la ve varias veces al día dejaría de leerla.

   Cuando ya está marcada cambia a verde y el texto pasa a decir quién
   y cuándo: deja de ser una pregunta y pasa a ser un hecho. */
.pregunta-respondio {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  margin: var(--space-3) 0;
  background: var(--color-info-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.pregunta-respondio-texto { display: flex; flex-direction: column; gap: 2px; }
.pregunta-respondio.esta-respondida {
  background: var(--color-success-bg);
  border-color: var(--color-success);
}
.pregunta-respondio.esta-respondida .dec-tit {
  display: flex; align-items: center; gap: var(--space-2);
  color: var(--color-success);
}
.pregunta-respondio form { flex-shrink: 0; }

/* En pantalla estrecha la pregunta manda y el botón baja entero: un
   botón de 40px al lado de tres líneas de texto no se acierta. */
@media (max-width: 640px) {
  .pregunta-respondio { flex-direction: column; align-items: stretch; }
  .pregunta-respondio form,
  .pregunta-respondio .btn { width: 100%; }
}

/* ══════════════════════════════════════════════════════════════════
   GALERÍA DE PLANTILLAS DE CORREO Y CAMPAÑAS
   Añadido el 06/08/2026.

   Regla de oro del Design System: ni un color, espaciado, radio o
   sombra escrito a mano. Todo sale de tokens.css.
   ══════════════════════════════════════════════════════════════════ */

/* --- La cuadrícula de plantillas ---------------------------------
   Se VE, no se lee un nombre. `auto-fill` con un mínimo de 260px: en
   una pantalla ancha entran cuatro y en una tableta dos, sin puntos
   de corte escritos a mano que se queden viejos. */
.galeria-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
  gap: var(--space-4);
  margin-bottom: var(--space-6);
}

.galeria-tarjeta {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-white);
  /* **SIN `overflow: hidden`** (Vladimir, 16/08/2026: «sigue con un error, no
     se despliega completo»). Estaba para redondear la esquina de la miniatura,
     pero de paso RECORTABA el menú de los tres puntos: «Eliminar» quedaba
     cortado por el borde inferior de la tarjeta. El recorte que sí hace falta
     —el del correo escalado— lo hace `.galeria-miniatura`, que tiene el suyo;
     las esquinas se redondean ahí abajo. */
  display: flex;
  flex-direction: column;
}

/* La miniatura es el correo REAL dentro de un iframe, escalado. Una
   captura guardada se queda vieja en cuanto alguien edita la
   plantilla, y entonces la galería enseña algo que ya no existe. */
.galeria-miniatura {
  position: relative;
  height: var(--alto-miniatura);
  overflow: hidden;
  /* Las esquinas de arriba, que antes ponía la tarjeta con su `overflow`.
     Van aquí porque este es el elemento que de verdad recorta algo. */
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
}

.galeria-miniatura iframe {
  width: var(--ancho-correo);
  height: var(--alto-miniatura-fuente);
  border: 0;
  /* 0.43 = 260/600: el ancho de tarjeta más estrecho sobre el ancho
     real de un correo. Así se ve la proporción verdadera. */
  transform: scale(0.43);
  transform-origin: top left;
  pointer-events: none;
}

/* Capa transparente por encima del iframe: sin ella el clic cae
   dentro del marco y no abre la plantilla. */
.galeria-capa {
  position: absolute;
  inset: 0;
  display: block;
}

.galeria-capa:focus-visible { box-shadow: var(--focus-ring); border-radius: var(--radius-sm); }

.galeria-cuerpo { padding: var(--space-4); display: grid; gap: var(--space-2); }

.galeria-cabecera {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-2);
}

.galeria-nombre {
  font-weight: 700;
  color: var(--color-ink);
  text-decoration: none;
  font-size: var(--text-base);
}

.galeria-nombre:hover { text-decoration: underline; }

.galeria-meta { margin: 0; display: flex; flex-wrap: wrap; gap: var(--space-1); }

.galeria-uso { margin: 0; font-size: var(--text-xs); color: var(--color-ink-soft); }

/* --- Vista previa, escritorio y móvil ----------------------------
   Es un cambio de ANCHO del mismo marco, no dos vistas distintas:
   así lo que se compara es el correo, no dos renderizados. */
.vista-previa-caja { position: sticky; top: var(--space-4); }

.vista-previa-marco {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--color-surface);
  height: var(--alto-vista-previa);
  margin: var(--space-3) 0;
  transition: max-width 0.2s ease;
}

.vista-previa-marco iframe { width: 100%; height: 100%; border: 0; }

/* 390px es el ancho útil de un móvil corriente. Casi todo se lee ahí,
   y un correo que se ve bien en 600 puede salirse en 390. */
.vista-previa-marco.es-movil { max-width: var(--ancho-movil); margin-inline: auto; }

@media (prefers-reduced-motion: reduce) {
  .vista-previa-marco { transition: none; }
}

/* --- Selector de dos posiciones ---------------------------------- */
.segmentado {
  display: inline-flex;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.segmentado button {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3);
  border: 0;
  background: var(--color-white);
  color: var(--color-ink-soft);
  font-size: var(--text-xs);
  cursor: pointer;
}

.segmentado button + button { border-left: 1px solid var(--color-border); }
.segmentado button.activo { background: var(--color-primary-bg); color: var(--color-ink); font-weight: 600; }
.segmentado button:focus-visible { box-shadow: var(--focus-ring); position: relative; z-index: 1; }
/* El grande: cuando la eleccion ES lo primero de la pantalla —a quien se le
   escribe en Redactar (18/09/2026)—, no un ajuste secundario. */
.segmentado--grande { margin-top: var(--space-2); }
.segmentado--grande button { font-size: var(--text-base); padding: var(--space-2) var(--space-4); }

/* --- Artículo ya elegido en el redactor -------------------------- */
.fila-elegida {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  margin-bottom: var(--space-2);
}

/* --- El desglose de por qué queda alguien fuera ------------------
   Regla 2: quien queda fuera no desaparece, aparece con su motivo. */
.lista-motivos {
  margin: 0 0 var(--space-4);
  padding-left: var(--space-4);
  color: var(--color-ink-soft);
  font-size: var(--text-sm);
}

.lista-motivos li { margin-bottom: var(--space-1); }

/* --- El texto legal en la página pública ------------------------- */
.texto-legal {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  padding: var(--space-3);
  margin-bottom: var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
  max-height: var(--alto-texto-legal);
  overflow-y: auto;
  white-space: pre-wrap;
}

/* --- Los colores de Marca ---------------------------------------- */
.colores-marca { display: flex; flex-wrap: wrap; gap: var(--space-3); }
.colores-marca label { flex: 0 0 auto; }
.colores-marca input[type="color"] {
  width: var(--ancho-selector-color);
  height: var(--alto-control);
  padding: 2px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  cursor: pointer;
}

/* --- Detalles compartidos ---------------------------------------- */
.titulo-seccion {
  margin: 0 0 var(--space-3);
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--color-ink);
  letter-spacing: -0.01em;
}

.etiqueta-copiable {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: 1px var(--space-2);
  font-size: var(--text-xs);
}

.linea-separadora {
  border: 0;
  border-top: 1px solid var(--color-border);
  margin: var(--space-4) 0;
}

.form-acciones {
  display: flex;
  justify-content: flex-end;
  gap: var(--space-2);
  margin-top: var(--space-5);
}

@media (max-width: 600px) {
  .form-acciones { flex-direction: column-reverse; }
  .form-acciones .btn { width: 100%; }
}

/* ══════════════════════════════════════════════════════════════════
   CENTRO DE PREFERENCIAS — la pantalla que ve un CLIENTE, sin sesión
   Añadido el 06/08/2026.
   ══════════════════════════════════════════════════════════════════ */

.auth-identidad {
  margin: 0 0 var(--space-4);
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
  text-align: center;
}

/* Las casillas: grandes y separadas. Esta pantalla se lee sobre todo en
   el teléfono, con el correo abierto al lado, y una lista apretada se
   marca mal con el dedo. */
.preferencias-lista {
  display: grid;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
}

.opcion-preferencia {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  cursor: pointer;
  /* 44px es el mínimo cómodo para el dedo; por debajo se falla el toque
     y en esta pantalla fallar el toque significa cambiar un permiso. */
  min-height: var(--alto-tactil);
}

.opcion-preferencia:hover { background: var(--color-surface); }

.opcion-preferencia input { width: var(--tamano-casilla); height: var(--tamano-casilla); flex: 0 0 auto; }

/* El «o» que separa ajustar de irse. La baja completa sigue estando,
   pero deja de competir con el botón de guardar. */
.separador-o {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: var(--space-5) 0;
  color: var(--color-ink-soft);
  font-size: var(--text-sm);
}

.separador-o::before,
.separador-o::after {
  content: "";
  flex: 1;
  border-top: 1px solid var(--color-border);
}

/* EL AVISO DE LO QUE NO CUBRE. Texto normal, NO `campo-ayuda`: es lo
   único de esta pantalla que puede causar daño real. Si alguien cree que
   canceló todo, deja de esperar sus cotizaciones — y cuando no le llegue
   una, el problema es de la empresa. En letra pequeña no se lee. */
.preferencias-aviso {
  margin: var(--space-4) 0 0;
  font-size: var(--text-base);
  line-height: 1.55;
  color: var(--color-ink);
}

.texto-legal-plegable {
  margin-top: var(--space-5);
  font-size: var(--text-sm);
}

.texto-legal-plegable summary {
  cursor: pointer;
  color: var(--color-ink-soft);
}

/* ══════════════════════════════════════════════════════════════════
   SELECTOR DE ENVÍO DE COTIZACIÓN — una sección por persona
   Añadido el 06/08/2026.
   ══════════════════════════════════════════════════════════════════ */

.envio-personas { display: grid; gap: var(--space-4); margin-bottom: var(--space-4); }

.envio-persona {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
}

.envio-persona-nombre {
  margin: 0 0 var(--space-2);
  font-weight: 700;
  color: var(--color-ink);
}

.envio-canal {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-2);
  border-radius: var(--radius-sm);
  cursor: pointer;
  min-height: var(--alto-tactil);
}

.envio-canal:hover { background: var(--color-surface); }
.envio-canal input { margin-top: 2px; width: var(--tamano-casilla); height: var(--tamano-casilla); }
.envio-canal-cuerpo { display: grid; gap: 2px; }
.envio-canal-tit { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-sm); }

/* Regla 2: lo que no se puede marcar NO desaparece, se ve con su motivo. */
.envio-canal-bloqueado {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

.envio-resumen { margin: 0 0 var(--space-3); font-size: var(--text-sm); }

/* El texto libre del envío por WhatsApp, dentro del selector. Uno solo: el
   mensaje es el mismo para todos. */
.envio-texto { display: block; margin-bottom: var(--space-4); }
.envio-texto textarea { width: 100%; }

/* ── COLUMNA «ESTADO DEL MENSAJE» ─────────────────────────────────────
   Sustituye a «Enviada por»: cada linea lleva el icono de su canal, asi
   que decir aparte por donde salio seria repetirse.

   Texto Y color, nunca color solo. Vladimir, 12/08/2026: «no todo el
   mundo lo va a entender inmediatamente por un codigo de colores». Un
   codigo que hay que aprenderse no es informacion; la palabra se entiende
   el primer dia y el color solo ayuda a barrer la lista.                */
/* **UNA LINEA POR ESTADO, SIEMPRE** (Vladimir, 19/08/2026: «arregla que el
   texto del estado del mensaje nunca caiga en 2 líneas»).

   Tenia `flex-wrap: wrap`, y con «Entregado 1 de 2 · sin abrir · ayer» el
   «ayer» se caia solo debajo. Partido asi se lee como si fueran DOS
   estados —uno por canal— cuando es uno solo, que es justo la confusion
   que esta columna existe para evitar.

   La columna crece con su contenido en vez de partirlo: la tabla ya se
   desplaza a lo ancho dentro de `.tabla-scroll`, asi que ensancharla no
   rompe la pagina. */
.estado-mensaje {
  min-width: 15rem;
  white-space: nowrap;
}

.em {
  display: flex;
  align-items: center;
  gap: .4rem;
  flex-wrap: nowrap;
  white-space: nowrap;
  margin-bottom: .2rem;
  line-height: 1.35;
}
.em:last-child { margin-bottom: 0; }
.em svg { flex-shrink: 0; }

.em-txt { font-size: .82rem; font-weight: 600; }
/* La hora y el matiz («sin abrir») en gris: acompañan al estado, no
   compiten con el. */
.em-nota { font-size: .74rem; color: var(--color-ink-soft); font-weight: 400; }

.em-leido     { color: var(--color-success); }
/* **«Entregado» en tinta, no en azul** (Vladimir, 18/08/2026: «ese texto de
   color azul en el estado de whatsapp no se corresponde»).

   El azul venía de evitar que «Entregado» y «Leído» fueran el mismo verde a
   simple vista — la columna existe para distinguirlos—, pero el azul en una
   tabla se lee como un enlace y ese texto no lleva a ningún sitio.

   En tinta se resuelven las dos cosas: deja de parecer pulsable y sigue
   distinguiéndose de «Leído», que se queda en verde porque es el estado que
   de verdad importa ver de un vistazo. */
.em-entregado { color: var(--color-ink); }
.em-enviado   { color: var(--color-ink-soft); }
.em-falla     { color: var(--color-danger); }
/* ══ EL MOTIVO, A LA VISTA Y DEBAJO (10/09/2026) ══

   Vladimir: «son mensajes diferentes, motivos diferentes». El motivo es lo
   que dice si hay que corregir el número o esperar, así que no va escondido
   en un globo al pasar el ratón: va escrito, debajo de su «No llegó».

   `.em` no parte línea —la hora tiene que ir pegada al estado—, y un motivo
   sí necesita partirla: por eso va en su propio bloque. */
.em-motivo {
  display: block;
  max-width: 22rem;
  margin: -.1rem 0 .35rem 1.2rem;
  font-size: .74rem;
  line-height: 1.35;
  color: var(--color-ink-soft);
  white-space: normal;
}

/* La marca de fallo va SEPARADA del avance, no dentro de su frase: es lo
   que distingue «el otro no lo ha abierto» de «al otro nunca le llego».
   Sin ella, «Leido 1 de 2» hace esperar cuando hay que corregir un dato. */
.em-marca {
  display: inline-flex;
  align-items: center;
  gap: .22rem;
  background: var(--color-danger-bg);
  color: var(--color-danger);
  border-radius: var(--radius-full);
  padding: .1rem .45rem;
  font-size: .72rem;
  font-weight: 700;
  white-space: nowrap;
}

/* ── COMUNICACIONES: UNA FILA POR PERSONA ─────────────────────────────
   «la vista por contacto e indicando los medios correo o whatsapp o
   ambos» (Vladimir, 12/08/2026). La pregunta al mirar este panel no es
   cuantos mensajes salieron, es QUE SABE CADA PERSONA.                 */
.com-persona {
  /* El MISMO margen lateral que `.dec`, que es la fila normal de este
     panel. Sin el, el nombre se pega al borde izquierdo y el estado se
     corta por la derecha: el bloque no heredaba ningun margen porque en
     este panel lo pone cada fila, no el contenedor. */
  padding: var(--space-2) var(--space-5);
  border-bottom: 1px solid var(--color-border);
}
.com-persona:last-child { border-bottom: none; }
/* El buzon de la empresa no es nadie a quien llamar: se apaga para que no
   compita con las personas de verdad. */
.com-buzon .com-nombre { color: var(--color-ink-soft); font-weight: 500; }

.com-quien { display: flex; align-items: baseline; gap: .5rem; margin-bottom: .15rem; }
.com-nombre { font-weight: 600; font-size: .9rem; }
.com-cargo { font-size: .76rem; color: var(--color-ink-soft); }

.com-fila {
  display: flex;
  align-items: center;
  gap: .55rem;
  flex-wrap: wrap;
  /* Sangrado bajo el nombre de la persona, sin margen extra a la derecha:
     el del bloque ya lo pone. */
  padding: .1rem 0 .1rem 1rem;
}
/* ── LA FRANJA FINA QUE LLEVA A SU COTIZACION (15/09/2026) ────────────
   Vladimir, al proponerle sombrear estas filas: «las franjas son finas,
   ¿cómo se va a sombrear por línea, no importa que sean finas?».

   **Importa, y por eso la banda no es la fila: es el ancho del panel.**
   Sombrear `.com-fila` tal cual encendería una tira sangrada de cuatro
   píxeles de alto que no se lee como una fila — parecería un defecto de
   pintado. Los margenes negativos llevan la banda hasta los dos bordes
   del bloque, igual que hace `.dec`, que es la fila normal de este panel;
   el relleno lateral devuelve el texto a su sitio, así que **nada se
   mueve al pasar el ratón**: solo se enciende el fondo.

   El sombreado en sí lo pone `.dec-pulsable`, que es de donde sale la
   regla; aquí solo se le da cuerpo a la franja. */
.com-fila-va {
  color: inherit;
  text-decoration: none;
  margin-left: calc(var(--space-5) * -1);
  margin-right: calc(var(--space-5) * -1);
  padding: .25rem var(--space-5) .25rem calc(1rem + var(--space-5));
}

.com-fila-va:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* La direccion o el telefono empujan el estado a la derecha, para que al
   bajar la vista los estados queden alineados y se lean en columna. */
.com-destino {
  font-size: .8rem;
  color: var(--color-ink-soft);
  flex: 1 1 auto;
  min-width: 8rem;
  word-break: break-all;
}

/* ── CAJA CON TOPE DE ALTURA ──────────────────────────────────────────
   Para listas que crecen sin limite dentro de una ficha: un cliente de
   años acumula decenas de cotizaciones y, sin tope, todo lo que viene
   despues —contactos, notas, comunicaciones— queda debajo del pliegue.

   El tope esta en `rem` y no en filas: asi acompaña al tamaño de letra
   de quien lo mira, y quien tenga la letra grande no ve media fila.    */
.caja-con-tope {
  max-height: 21rem;
  overflow-y: auto;
  /* **La barra se ve.** Sin marcarla, una caja con más contenido del que
     cabe parece que se acaba ahí — es el mismo fallo que Vladimir encontró
     en la galería de avatares el 13/08, y aquí esconde mensajes enviados a
     un cliente. */
  scrollbar-width: thin;
  scrollbar-color: var(--color-border) transparent;
}

.caja-con-tope::-webkit-scrollbar { width: var(--space-2); }
.caja-con-tope::-webkit-scrollbar-track { background: transparent; }
.caja-con-tope::-webkit-scrollbar-thumb {
  background: var(--color-border);
  border-radius: var(--radius-sm);
}
.caja-con-tope::-webkit-scrollbar-thumb:hover { background: var(--color-gray-500); }
/* La cabecera se queda fija al desplazar. Sin esto, a la tercera fila ya
   no se sabe cual columna es cual — y aqui una columna es el total. */
.caja-con-tope thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--color-surface);
}

/* La cotizacion a la que pertenece cada mensaje. Solo aparece en la ficha
   del CLIENTE, donde se mezclan meses: sin ella, tres filas iguales de la
   misma persona no dicen a cual pertenece cada una. */
.com-ref {
  display: inline-block;
  margin-left: .35rem;
  font-size: .72rem;
  font-weight: 600;
  color: var(--color-ink-soft);
  white-space: nowrap;
}

/* El icono lleva el color de SU CANAL —verde WhatsApp, azul correo—, no el
   del estado. Vladimir, 12/08/2026. El canal se reconoce sin leer; el
   estado lo dice el texto, que es donde se entiende sin aprenderse un
   codigo de colores. En una linea fallida el icono sigue siendo del canal
   y la alarma la llevan el texto y la marca, ambos en rojo. */
.em-canal-correo   { color: var(--color-correo); }
.em-canal-whatsapp { color: var(--color-whatsapp); }

/* ── EL OJO NO ES UN CANAL, Y TENÍA QUE NOTARSE ───────────────────────
   Vladimir, 14/08/2026, viendo «Los 2 la vieron» sobre «Leído 2 de 4»:
   «si Meta los rechazó, ¿por qué tenemos una métrica de 2 lecturas en
   WhatsApp?».

   **No había ninguna métrica de WhatsApp** —los cuatro envíos eran de
   correo— pero su lectura era la razonable: dos líneas, dos iconos, y el
   de abajo con el color de su canal. El de arriba salía VERDE porque
   heredaba el tono del estado, y ese verde es el de WhatsApp.

   La línea de personas responde otra pregunta —cuánta GENTE la vio, por
   el canal que sea— así que su icono va en gris de texto: no compite con
   los dos colores que sí significan canal.                            */
.em-canal-personas { color: var(--color-ink-soft); }
.em-canal-alerta   { color: var(--color-danger); }
/* El ojo de «los 2 la vieron» y el triangulo de aviso NO son canales:
   conservan el color del estado, porque ahi el color ES la informacion. */

/* ══ SOLICITUDES A PROVEEDORES ═══════════════════════════════════════ */

/* La fila sin cotizar se apaga: de un vistazo se ve cuanto le falta por
   contestar a ese proveedor sin leer casilla por casilla. */
.fila-no-cotizada td { opacity: .62; }

/* ── «LO COTIZÓ» TIENE QUE PARECER UN BOTÓN ───────────────────────────
   Vladimir, 14/08/2026: «pero ese botón no está en ningún lado, tengo que
   verlo para saber cómo funciona».

   **Y estaba, pero no se leía como un control**: una casilla del sistema
   con texto gris al lado parece un rótulo de la columna, no algo que se
   pulsa. Al no pulsarlo, el campo de precio seguía bloqueado — y de ahí
   la conclusión razonable de que los precios «no se pueden editar».

   Ahora es una pastilla con borde: se ve que se pulsa, y al marcarla
   cambia de color, así que la fila dice de un vistazo si ese proveedor
   tiene el artículo.                                                    */
.marca-cotizado {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  cursor: pointer;
  font-size: var(--text-sm);
  white-space: nowrap;
  padding: .3rem .6rem;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-white);
}

.marca-cotizado:hover { border-color: var(--color-gray-500); }

/* Marcada: verde suave. Es el estado que dice «este proveedor sí lo
   tiene», y se reconoce sin leer al recorrer veinte líneas. */
.marca-cotizado:has(input:checked) {
  border-color: var(--color-primary);
  background: var(--color-primary-bg);
  color: var(--color-primary-texto);
  font-weight: 600;
}

.marca-cotizado:focus-within {
  outline: 2px solid var(--color-azul);
  outline-offset: 1px;
}
.col-si-no { width: 8.5rem; }

.campo-precio-captura {
  display: inline-flex;
  align-items: center;
  gap: .25rem;
  justify-content: flex-end;
}
.campo-precio-captura .prefijo-moneda { color: var(--color-ink-soft); font-size: var(--text-xs); }
.campo-precio-captura input {
  width: 7.5rem;
  text-align: right;
}
/* Un campo desactivado tiene que PARECER desactivado: si no, se teclea
   dentro y no pasa nada, y eso se lee como que el sistema esta roto. */
/* Gris propio y NO `--color-fila-hover`: ese color significa «esta fila se
   abre», y un campo apagado no abre nada. Reutilizarlo enseñaria a la vista
   un significado falso — lo caza `test_filas_pulsables`. */
.campo-precio-captura input:disabled {
  background: var(--color-gray-100, #f1f3f7);
  cursor: not-allowed;
}

/* El error vive EN la fila, sin sacar a nadie de la pantalla ni vaciarle
   lo escrito: cuarenta lineas tecleadas de un PDF no se pueden perder por
   una coma mal puesta. */
.error-en-fila {
  display: block;
  margin-top: .2rem;
  font-size: var(--text-xs);
  color: var(--color-danger);
  font-weight: 600;
}

/* ── La comparacion ── */
.comparar-resumen {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-6);
  padding: var(--space-4) var(--space-5);
}
.comparar-lbl { font-size: var(--text-xs); color: var(--color-ink-soft); }
.comparar-val { font-size: 1.5rem; font-weight: 700; color: var(--color-ink); }
.comparar-ahorro {
  display: flex;
  align-items: center;
  gap: .3rem;
  color: var(--color-success);
}

.tabla-comparar th { white-space: nowrap; }
/* El mejor precio de cada fila, marcado. Con fondo Y con palabra: un color
   que hay que aprenderse no es informacion. */
.celda-mejor { background: var(--color-success-bg); }
.celda-marca {
  display: block;
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--color-success);
}

/* El cuerpo de una bandeja cuando NO es una tabla: un formulario, unas
   notas. La tabla trae su propio margen; esto no lo tenia y el contenido
   quedaba pegado a los bordes.

   Se descubrio escribiendo las pantallas de proveedores, y arregla tambien
   «Administracion -> Tipos de incidencia», que ya usaba esta clase sin que
   existiera. */
.bandeja-cuerpo { padding: var(--space-4) var(--space-5) var(--space-5); }

/* Campos en rejilla, para formularios de varios datos cortos —numero,
   fecha, moneda—. Se acomoda sola: en el telefono cae a una columna sin
   que haya que decirlo. */
.rejilla-campos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: var(--space-3) var(--space-4);
  margin-bottom: var(--space-3);
}

/* ── DOS FILAS EXACTAS, SIN HUECO A LA DERECHA ──────────────────────
   `auto-fit` reparte por lo que quepa, no por lo que convenga: seis
   campos salian 4+2 y media segunda fila quedaba vacia. Cuatro columnas
   fijas, el archivo ocupando dos y el boton en la ultima celda llenan
   las dos filas enteras y le quitan al bloque un renglon completo.

   `align-items: end` alinea por la BASE de los campos, que es lo unico
   que hace que el boton quede a la altura del cuadro del archivo y no
   flotando a media altura. */
.rejilla-documento {
  grid-template-columns: repeat(4, 1fr);
  align-items: end;
  margin-bottom: 0;
}

/* La celda del archivo se lleva TRES columnas y dentro van, uno junto a
   otro, «Elegir archivo» y «Guardar»: la accion principal del bloque
   estaba en la ultima columna, a media pantalla del ultimo campo que se
   toca. `align-items: flex-end` los deja a la misma altura pese a que el
   primero lleva rotulo encima y el segundo no. */
.rejilla-documento .campo-doble {
  grid-column: span 3;
  display: flex;
  align-items: flex-end;
  gap: var(--space-3);
  min-width: 0;
}

/* La envoltura se encoge a lo que ocupa el boton: si se queda a lo ancho
   de la celda, «Guardar» aparece a 50 px de «Elegir archivo» en vez de al
   lado. */
.rejilla-documento .campo-doble .campo-archivo-envoltura {
  flex: 0 1 auto;
  min-width: 0;
}

.rejilla-documento .campo-doble > .btn {
  flex: 0 0 auto;
}

.pie-de-rejilla {
  margin-top: var(--space-2);
}

/* ── UNA BANDEJA QUE OCUPA MENOS ────────────────────────────────────
   Para bloques que se rellenan UNA vez y no deben empujar fuera de la
   pantalla al que se usa veinte. No cambia ningun tamano de letra ni de
   campo: solo aprieta los rellenos y el hueco entre filas. */
.bandeja-compacta .bandeja-cab {
  padding: var(--space-3) var(--space-5) var(--space-2);
}

.bandeja-compacta .bandeja-cuerpo {
  padding: var(--space-3) var(--space-5) var(--space-4);
}

.bandeja-compacta .rejilla-campos {
  gap: var(--space-2) var(--space-4);
}

/* ═══ EL COSTO DENTRO DE LA COTIZACION (21/08/2026) ═══
   Entre el articulo y el precio. Verde de acento y no gris: es un dato que
   se mira para decidir, no una nota al pie.

   **Todo sale de tokens.css**, incluidas las columnas: `min-content` y
   `max-content` dicen «lo que ocupe» en vez de inventar un ancho en pixeles
   que con un nombre de proveedor largo se queda corto. */
.costo-proveedores {
  border: 1px solid var(--color-primary);
  background: var(--color-primary-bg);
  border-radius: var(--radius-md);
  overflow: hidden;
  margin-top: var(--space-3);
}

.costo-linea {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-sm);
}

.costo-linea strong { font-variant-numeric: tabular-nums; }

.costo-etq {
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--color-primary-texto);
}

.costo-sep { color: var(--color-text-muted); }

/* Enlace y no boton con fondo: abrir el detalle es una accion menor dentro
   de una linea de datos. */
.costo-mas {
  margin-left: auto;
  background: none;
  border: 0;
  padding: 0;
  font: inherit;
  font-weight: 600;
  color: var(--color-primary-texto);
  cursor: pointer;
}

.costo-mas:hover { text-decoration: underline; }

.costo-tabla {
  border-top: 1px solid var(--color-primary);
  background: var(--color-white);
}

.costo-fila {
  display: grid;
  grid-template-columns: min-content 1fr auto max-content max-content;
  gap: var(--space-3);
  align-items: center;
  padding: var(--space-2) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  font-size: var(--text-sm);
}

.costo-fila:last-child { border-bottom: 0; }
.costo-fila.mejor { background: var(--color-primary-bg); }

.costo-fila .pos {
  color: var(--color-ink-soft);
  font-variant-numeric: tabular-nums;
}

.costo-fila .quien { font-weight: 600; }
.costo-fila .tendencia { font-size: var(--text-xs); color: var(--color-text-muted); }

.costo-fila .monto {
  text-align: right;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.costo-fila .cuando {
  text-align: right;
  font-size: var(--text-xs);
  color: var(--color-ink-soft);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* El margen del precio que se esta tecleando. */
.margen-linea {
  margin: var(--space-2) 0 0;
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-primary-texto);
}

/* Vender por debajo del costo a veces se hace; no es un error del sistema,
   pero no puede leerse igual que un margen sano. */
.margen-linea--perdida { color: var(--color-danger); }

@media (max-width: 700px) {
  .costo-fila { grid-template-columns: min-content 1fr auto; }
  .costo-fila .cuando { display: none; }
}

/* El formulario de agregar cuelga de la bandeja, no del cuerpo, asi que
   no heredaba el relleno lateral: el rotulo arrancaba pegado al borde
   mientras el titulo de arriba si estaba sangrado. Se le da el cuerpo, sin
   relleno abajo porque la tabla viene justo detras. */
.cuerpo-de-agregar {
  padding-bottom: 0;
}

/* Y en pantalla estrecha vuelve a repartirse: cuatro columnas fijas en
   un telefono dan campos de tres centimetros. */
@media (max-width: 1000px) {
  .rejilla-documento {
    grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  }
  .rejilla-documento .campo-doble {
    grid-column: auto;
    flex-wrap: wrap;
  }
}

/* ═══ COMPARAR PRECIOS Y PLAN DE COMPRA (14/08/2026) ═══
   Dos pantallas y dos formas a proposito: la comparacion es un RANKING
   —renglones que se leen de arriba abajo buscando el mas barato— y el plan
   es un DOCUMENTO agrupado por proveedor. Si las dos fueran la misma tabla,
   la primera obligaria a comparar de memoria. */

/* ── El buscador ── */
.buscar-articulo {
  display: flex;
  align-items: flex-end;
  gap: var(--space-3);
  flex-wrap: wrap;
}
.buscar-articulo-campo { flex: 1 1 22rem; margin: 0; }

/* Elegir entre varios articulos que casan con lo escrito. */
.lista-elegir { display: flex; flex-direction: column; }
.lista-elegir-fila {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-5);
  border-top: 1px solid var(--color-border);
  color: var(--color-ink);
  text-decoration: none;
}
/* **El mismo gris que el resto, desde el 28/08/2026.** Aqui decia «gris
   suave y NO `--color-fila-hover`, que promete abrir una ficha; esto abre
   una comparacion, que es otra cosa». La distincion era cierta en el codigo
   y **no llegaba al ojo**: eran dos grises casi identicos sosteniendo una
   diferencia que nadie percibe, y el mas claro no se veia —1,05:1—.
   Vladimir: «usa el mismo gris que sombrea todos los demas vinculos». */
.lista-elegir-fila:hover { background: var(--color-fila-hover); }

/* ── El ranking ── */
.precio-filas { display: flex; flex-direction: column; }
.precio-fila { border-top: 1px solid var(--color-border); }

/* El mejor precio se marca con fondo Y con palabra: un color que hay que
   aprenderse no es informacion. Mismo criterio que `.celda-mejor`. */
.precio-fila--mejor { background: var(--color-success-bg); }

.precio-fila-cab {
  display: grid;
  grid-template-columns: auto auto 1fr auto auto;
  align-items: center;
  gap: var(--space-3);
  /* **Más juntas** (Vladimir, 18/08/2026: «la altura de las líneas debe ser
     menor; cuando haya muchas cotizaciones se va a complicar con el scroll»).
     Con dos proveedores da igual; con doce decide si la comparación entra de un
     vistazo o hay que desplazarse — y desplazarse para comparar precios es
     perder de vista con qué se compara. */
  padding: var(--space-2) var(--space-5);
  margin: 0;
  cursor: pointer;
}
/* Marca el radio de ese proveedor: no abre una ficha, pero SI responde al
   clic, y eso es todo lo que el resaltado promete (28/08/2026). */
.precio-fila-cab:hover { background: var(--color-fila-hover); }
.precio-fila--mejor .precio-fila-cab:hover { background: var(--color-primary-bg); }

.precio-puesto {
  font-weight: 700;
  color: var(--color-ink-soft);
  font-size: var(--text-sm);
  min-width: 1.5rem;
}
.precio-quien { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.precio-marca {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--color-success);
  background: var(--color-white);
  border-radius: var(--radius-pill);
  padding: 0 var(--space-2);
}
.precio-marca--plan { color: var(--color-azul); background: var(--color-info-bg); }

.precio-cifra {
  font-weight: 700;
  font-size: var(--text-lg);
  white-space: nowrap;
  text-align: right;
}
.precio-fecha {
  display: block;
  font-size: var(--text-xs);
  font-weight: 400;
  color: var(--color-ink-soft);
}

.precio-tendencia {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-xs);
  font-weight: 600;
  white-space: nowrap;
  min-width: 7rem;
  justify-content: flex-end;
}
/* Subir un precio de compra es malo y bajarlo es bueno: al reves que en una
   grafica de ventas. El color va por lo que significa aqui. */
.precio-tendencia--subio { color: var(--color-danger); }
.precio-tendencia--bajo { color: var(--color-success); }
.precio-tendencia--estable { color: var(--color-ink-soft); }
.precio-tendencia--sin_dato { color: var(--color-ink-soft); font-weight: 400; }

/* ── El historial desplegable ── */
.precio-abrir {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  background: none;
  border: none;
  cursor: pointer;
  padding: 0 var(--space-5) var(--space-3) calc(var(--space-5) + 2.4rem);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-primary-texto);
  font-family: inherit;
}
.precio-abrir:hover { text-decoration: underline; }
.precio-chevron { display: inline-flex; transition: transform .15s ease; }
.precio-chevron--abierto { transform: rotate(180deg); }

.precio-historial {
  padding: var(--space-2) var(--space-5) var(--space-4) calc(var(--space-5) + 2.4rem);
}
.precio-historial-fila {
  display: grid;
  grid-template-columns: 6rem 1fr 6rem;
  gap: var(--space-3);
  padding: var(--space-2) 0;
  border-bottom: 1px dashed var(--color-border);
  font-size: var(--text-sm);
}
.precio-historial-fila.es-vigente { font-weight: 700; }
.precio-sin-historial {
  /* La nota de debajo aportaba casi tanto alto como la fila misma. */
  padding: 0 var(--space-5) var(--space-2) calc(var(--space-5) + 2.4rem);
  font-size: var(--text-xs);
  color: var(--color-ink-soft);
}

/* ── La barra del plan: el puente entre las dos pantallas ── */
.plan-barra {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  padding: var(--space-4) var(--space-5);
  border-top: 1px solid var(--color-border);
  background: var(--color-gray-50);
}
.plan-barra p { margin: 0; }
.plan-barra-accion { display: flex; align-items: flex-end; gap: var(--space-3); }
.plan-barra-cantidad { margin: 0; width: 6rem; }

/* ── El plan de compra ── */
/* El aviso de que el documento no sale de la empresa. Va arriba y con el
   motivo escrito: alguien lo va a querer mandar tal cual. */
.plan-interno {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-info-bg);
  font-size: var(--text-sm);
  color: var(--color-ink);
}
.plan-interno svg { flex-shrink: 0; margin-top: .1rem; }

.plan-en-curso {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  color: var(--color-ink);
  text-decoration: none;
}
.plan-en-curso .dec-sub { display: block; }

.form-en-fila { display: inline; margin: 0; }

/* Los tres pasos del plan: armandose → finalizado → generado. Que una cosa
   lleve a la otra, no una etiqueta de color que haya que aprenderse. */
.plan-pasos {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}
.plan-paso {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex: 1 1 14rem;
  padding: var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-white);
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}
.plan-paso .dec-sub { display: block; }
.plan-paso-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.6rem;
  height: 1.6rem;
  flex-shrink: 0;
  border-radius: var(--radius-full);
  background: var(--color-gray-100);
  font-weight: 700;
  font-size: var(--text-xs);
}
.plan-paso.es-actual {
  border-color: var(--color-primary);
  background: var(--color-primary-bg);
  color: var(--color-ink);
}
.plan-paso.es-actual .plan-paso-num {
  background: var(--color-primary);
  color: var(--color-white);
}
.plan-paso.es-hecho .plan-paso-num {
  background: var(--color-success-bg);
  color: var(--color-success);
}

/* ── EL PASO 3 LLEVA A LAS ORDENES (15/09/2026) ───────────────────────
   Vladimir, despues de generar dos: «estaba perdido buscando donde podia
   ver esas 2 ordenes o como continuar… donde se visualiza casi no se
   nota, debe mostrarse mas a la vista».

   Era un recuadro y ahora es la salida del recorrido, asi que tiene que
   verse que se pulsa: la flecha a la derecha —empujada por el `auto`— y
   el sombreado de `.dec-pulsable`, que es la misma regla de toda la casa.
   **Solo cuando las ordenes existen**: antes no hay nada que abrir. */
a.plan-paso {
  text-decoration: none;
}
a.plan-paso:hover {
  border-color: var(--color-primary-hover);
}
.plan-paso-ir {
  margin-left: auto;
  display: inline-flex;
  flex-shrink: 0;
  color: var(--color-primary);
}
a.plan-paso:focus-visible {
  outline: none;
  box-shadow: var(--focus-ring);
}

/* El selector de a que plan va el articulo, en la barra del buscador. */
.plan-barra-destino { display: flex; flex-direction: column; gap: var(--space-1); }
.plan-barra-selector { margin: 0; min-width: 16rem; }
.plan-barra-selector select { margin: 0; }

/* ── Instrucciones de pantalla (15/08/2026) ──
   «Que se vea como una instruccion para que quien lo use lo entienda»
   (Vladimir). La operan personas no tecnicas: la explicacion de para que
   sirve una pantalla y como se usa va EN la pantalla, no en un manual. */
.instrucciones {
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-primary);
  border-radius: var(--radius-md);
  background: var(--color-white);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-4);
  font-size: var(--text-base);
  color: var(--color-ink);
}
.instrucciones p { margin: 0 0 var(--space-3); }
.instrucciones-cuando { color: var(--color-ink-soft); }
.instrucciones-pasos {
  margin: 0 0 var(--space-3);
  padding-left: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.instrucciones-pasos li { padding-left: var(--space-1); }
.instrucciones-nota {
  margin: 0;
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

/* ── Bloque de formulario COMPACTO (15/08/2026) ──
   «Este cuadro es enorme, se puede compactar y reducir su tamaño en más de
   50%» (Vladimir, sobre «Su documento» en la captura de precios).

   Va como modificador y NO tocando `label`/`input` globales: esos altos
   generosos son correctos en un alta de cliente, donde el formulario ES la
   pantalla. Aquí el formulario es un preámbulo —lo que se viene a hacer es
   teclear precios— y robaba el sitio de la tabla. */
.bloque-compacto label { margin-top: 0; }
/* El macro `campo_archivo` pinta un <label> suelto en vez de un
   `.etiqueta-texto`: se igualan los dos para que las seis columnas tengan la
   misma cabecera. */
.bloque-compacto .etiqueta-texto,
.bloque-compacto label { font-size: var(--text-xs); }
.bloque-compacto input,
.bloque-compacto select,
.bloque-compacto textarea {
  /* En `rem`, no en px: la regla del Design System es que las medidas no se
     escriban a mano en píxeles, y aquí hace falta un alto propio —los 38 px
     del control base son los correctos donde el formulario ES la pantalla. */
  min-height: 2rem;
  padding: var(--space-1) var(--space-2);
  font-size: var(--text-base);
}
.bloque-compacto .acciones-formulario {
  margin-top: var(--space-3);
  align-items: center;
  gap: var(--space-3);
}
.bloque-compacto .campo-ayuda { margin: 0; }

/* Más columnas en el mismo ancho: con 13rem de mínimo, seis campos cortos
   se partían en dos filas y dejaban un hueco a la derecha. */
.rejilla-apretada {
  grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
  gap: var(--space-2) var(--space-3);
  align-items: start;
  margin-bottom: 0;
}
/* En pantalla de escritorio se fija el numero de columnas en vez de dejarlo
   al reparto automatico: con `auto-fit` entraban CINCO de los seis campos y
   el sexto se quedaba solo en una fila entera para el. Seis caben de sobra
   —163 px cada uno a 1280— y eso quita una fila completa del cuadro. */
@media (min-width: 1100px) {
  .rejilla-apretada { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}
/* El cuerpo tambien aprieta: es un preambulo, no la pantalla. */
.bandeja .bloque-compacto { margin-top: calc(var(--space-1) * -1); }
/* Las notas van DENTRO de la rejilla ocupando todo el ancho, en vez de en un
   bloque aparte con su propio margen. */
.campo-ancho-completo { grid-column: 1 / -1; }
.textarea-baja { min-height: 2.8rem; }

/* El campo de archivo dentro de un bloque compacto: el nombre del archivo va
   en una linea propia y con puntos suspensivos en vez de partirse en tres
   renglones. En una columna de 143 px, «cotizacion-lucimed-agosto.pdf» hacia
   esa celda el doble de alta que las otras cinco y estiraba la fila entera. */
.bloque-compacto .campo-archivo { gap: var(--space-2); }
.bloque-compacto .campo-archivo-nombre {
  display: block;
  width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: var(--text-xs);
}

/* El precio unitario debajo del precio de la caja: es un calculo, no lo que
   dijo el proveedor, y tiene que verse como tal. */
.precio-por-unidad {
  display: block;
  font-size: var(--text-xs);
  font-weight: 400;
  color: var(--color-ink-soft);
}
/* El precio tecleado en la fila de una orden de compra. */
.celda-precio { width: 6.5rem; text-align: right; }

/* Columna de accion siempre visible dentro de una tabla (15/08/2026).
   «Dentro de los 3 puntos no tiene gracia, pensé que lo ibas a colocar como
   un botón visible» (Vladimir, sobre solicitar cotización en Proveedores).

   No sustituye al menu de fila: convive con el. La regla es cual va donde —
   la accion PRINCIPAL de ese registro se ve; el resto vive en el menu. */
.col-accion-fija {
  width: 1%;
  white-space: nowrap;
  text-align: right;
}
.col-accion-fija form { margin: 0; }



/* ── Elegir a QUE WhatsApp del proveedor se le escribe (15/08/2026) ──
   «El botón de comunicarme con el proveedor debe desplegar los diferentes
   números de WhatsApp y yo elegir» (Vladimir). Solo aparece con mas de uno:
   elegir entre una cosa no es elegir, y ese clic de mas lo pagaria el caso
   normal. */
.menu-whatsapp { position: relative; }
.menu-whatsapp-lista {
  position: absolute;
  z-index: var(--z-menu);
  top: calc(100% + var(--space-1));
  left: 0;
  min-width: 14rem;
  padding: var(--space-1);
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}
.menu-whatsapp-lista form { margin: 0; }
.menu-whatsapp-lista button {
  display: block;
  width: 100%;
  padding: var(--space-2) var(--space-3);
  background: none;
  border: none;
  border-radius: var(--radius-sm);
  cursor: pointer;
  font: inherit;
  text-align: left;
  color: var(--color-ink);
}
.menu-whatsapp-lista button:hover { background: var(--color-primary-bg); }
.menu-whatsapp-lista .dec-sub { display: block; }


/* ══════════════════════════════════════════════════════════════════════
   LA GALERÍA DE PLANTILLAS · las dos familias, separadas y que se note
   ══════════════════════════════════════════════════════════════════════
   Vladimir, 16/08/2026: «las quiero separadas, las comerciales y las
   transaccionales, pero una separación que se note marcada». Antes las
   separaba un `<h2>` del mismo tamaño que cualquier otro título: al bajar
   la vista no se notaba haber cruzado a otra familia. */
.familia-cab {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-4);
  margin: var(--space-6) 0 var(--space-4);
  padding: var(--space-3) var(--space-4);
  /* **Redondeadas como todo lo demás** (Vladimir, 16/08/2026: «para tener una
     imagen estándar, a estos 2 cuadros hazlos ovalados como los demás»). La
     franja lateral se queda porque aquí SÍ distingue las dos familias —es lo
     único que las separa de un vistazo—, pero con las esquinas redondeadas la
     caja deja de parecer una pieza de otro sistema. */
  border-radius: var(--radius-lg);
  border-left: 4px solid var(--color-ink);
}

/* ── CADA FAMILIA CON SU COLOR ──
   Vladimir, 16/08/2026: «estos títulos, para poder identificarlos dale un
   color ya sea de fondo o de cualquier otro tipo o diseño diferente».

   Las dos cabeceras eran idénticas —mismo negro, misma línea— así que
   separaban pero no DECÍAN cuál era cuál: al bajar la vista había que leer
   el título para saber en qué mitad estabas. El color lo dice antes.

   No son dos colores decorativos: son los que el sistema ya usa para esas
   dos cosas. Verde de marca en lo que sale de tu mano; azul de correo —el
   mismo del icono del canal— en lo que sale solo. */
.familia-comercial {
  border-left-color: var(--color-primary);
  background: var(--color-primary-bg);
}

.familia-comercial .familia-tit {
  color: var(--color-primary-texto);
}

.familia-automatica {
  border-left-color: var(--color-azul);
  background: var(--color-info-bg);
}

.familia-automatica .familia-tit {
  color: var(--color-azul);
}

/* El corte entre familias: aire de sobra, para que la separación se note
   antes de leer nada. Sin línea: con dos colores distintos, una raya más
   sería repetir lo mismo por tercera vez. */
.familia-corte {
  margin-top: var(--space-8);
  /* Aire al saltar desde el enlace con `#transaccionales`: sin esto la
     cabecera queda pegada al borde de arriba y no se ve que hay una sección
     encima. */
  scroll-margin-top: var(--space-6);
}

.familia-tit {
  margin: 0;
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--color-ink);
}

.familia-sub {
  margin: var(--space-1) 0 0;
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

.familia-cuenta {
  flex: none;
  font-size: var(--text-xs);
  color: var(--color-ink-soft);
  white-space: nowrap;
}

/* La acción de cada tarjeta, a la vista y no en un menú. */
.galeria-accion {
  margin-top: var(--space-3);
}

/* La lupa sobre la miniatura: dice que eso se puede AGRANDAR, que es lo que
   no se sabía. Aparece al pasar por encima; en táctil se ve siempre, porque
   allí no hay «pasar por encima». */
.galeria-lupa {
  position: absolute;
  right: var(--space-3);
  bottom: var(--space-3);
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radio-completo);
  background: var(--color-ink);
  color: var(--color-blanco);
  font-size: var(--text-xs);
  opacity: 0;
  transition: opacity var(--transicion-rapida);
  pointer-events: none;
}

.galeria-miniatura:hover .galeria-lupa,
.galeria-capa:focus-visible ~ .galeria-lupa {
  opacity: 1;
}

@media (hover: none) {
  .galeria-lupa { opacity: 1; }
}

/* La capa es un botón: hereda estilos de botón que aquí estorban. */
.galeria-capa {
  border: 0;
  padding: 0;
  background: transparent;
  cursor: zoom-in;
}

/* El correo entero dentro del modal. Alto generoso: el objetivo es
   RECONOCERLO, y para eso hay que ver más que el encabezado. */
.vista-grande {
  display: block;
  width: 100%;
  height: min(70vh, 780px);
  border: 1px solid var(--color-border);
  border-radius: var(--radio-md);
  background: var(--color-blanco);
}

.modal-cuerpo-limpio {
  padding: var(--space-4);
}

/* El paso siguiente al pie del redactor: no es un aviso, es la continuación
   del trabajo — por eso lleva el mismo peso que una tarjeta y no el de una
   alerta. */
.paso-siguiente {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--space-5);
  flex-wrap: wrap;
}

.paso-siguiente-tit {
  margin: 0;
  font-weight: 600;
  color: var(--color-ink);
}

/* ── UN RECUENTO NO ES UN IMPORTE ──
   Vladimir, 17/08/2026: «en artículos, los números están a la derecha,
   centralízalos».

   Los importes van a la derecha porque se comparan entre filas: alineados
   por la coma decimal, se ve de un vistazo cuál es mayor. Un recuento de
   artículos —23, 3, 3— no se compara con nada; pegado al borde derecho solo
   queda lejos de su encabezado. Centrado se lee con la columna.

   Conserva `tabular-nums` porque eso no es cosa de alineación: hace que
   todas las cifras midan igual y la columna no baile al cambiar de fila. */
td.numero-recuento {
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* ── EL RÓTULO DE UN GRUPO DENTRO DEL MENÚ ──
   Separa «precios que ya te dieron» del recorrido de compras, que es una
   secuencia. Sin nombre, un separador solo dice «aquí hay una raya»; con él,
   dice de qué son las dos entradas que vienen debajo. */
.sidebar-rotulo {
  display: block;
  padding: var(--space-2) var(--space-4) var(--space-1);
  font-size: var(--text-2xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-ink-soft);
  opacity: 0.75;
}

/* Colapsado no cabe el texto: el separador ya marca el corte. */
.sidebar.colapsado .sidebar-rotulo { display: none; }

/* ── EL BLOQUE DE LO QUE OFRECIÓ EL PROVEEDOR, EN DOS FILAS ──
   Cuatro campos cortos apilados a lo ancho ocupaban media pantalla. En dos
   filas se abarca de un vistazo, y la primera enseña por su forma que las dos
   casillas de artículo son alternativas: van juntas y del mismo tamaño. */
.ofrecido-fila {
  display: grid;
  gap: var(--space-4);
  align-items: end;
  margin-bottom: var(--space-4);
}

.ofrecido-fila--articulo { grid-template-columns: 1fr 1fr; }

/* Las cifras no crecen: un precio no necesita medio ancho de pantalla. Y el
   botón tampoco — al dejarlo en `auto` la rejilla lo estiró hasta el borde y
   quedó un botón de un palmo para una acción menor (Vladimir, 17/08/2026:
   «y ese botón tan grande»). `justify-self` lo devuelve a su tamaño. */
.ofrecido-fila--cifras { grid-template-columns: 160px 160px 1fr; }

.ofrecido-fila--cifras .btn { justify-self: center; }

.ofrecido-fila .campo { margin-bottom: 0; }

@media (max-width: 720px) {
  .ofrecido-fila--articulo,
  .ofrecido-fila--cifras { grid-template-columns: 1fr; }
}

/* ══════════════════════════════════════════════════════════════════════
   EL CARRITO DE LA COMPRA
   ══════════════════════════════════════════════════════════════════════
   La tabla a la izquierda y el carrito a la derecha, y el carrito PEGADO:
   con 23 artículos hay que bajar, y un total que se va de la pantalla no
   sirve para lo único que hace útil esto — ver el efecto de cada elección
   mientras se elige. */
.carrito {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: var(--space-6);
  align-items: start;
}

.carrito-panel {
  position: sticky;
  top: var(--space-4);
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-5);
  max-height: calc(100vh - var(--space-8));
  overflow-y: auto;
}

.carrito-grupo { margin-bottom: var(--space-4); }
.carrito-grupo h3 { font-size: var(--text-sm); margin-bottom: var(--space-2); }

.carrito-linea {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
  font-size: var(--text-sm);
  margin-bottom: var(--space-1);
}

.carrito-subtotal,
.carrito-total {
  display: flex;
  justify-content: space-between;
  border-top: 1px solid var(--color-border);
  padding-top: var(--space-2);
  margin-top: var(--space-2);
}

.carrito-total { font-size: var(--text-lg); }

/* Avisa, no impide: ámbar, no rojo. Comprar menos de lo pedido puede ser
   deliberado — lo que no puede es pasar desapercibido. */
.carrito-aviso {
  background: var(--color-caution-bg);
  border-radius: var(--radius-sm);
  padding: var(--space-3);
  font-size: var(--text-xs);
  margin: var(--space-3) 0;
}

/* La celda ENTERA es el control: se elige señalando el precio que se elige,
   no una casilla diminuta a su lado. */
.celda-elegir {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-2);
  cursor: pointer;
}

.celda-elegible:has(input:checked) {
  background: var(--color-info-bg);
  outline: 2px solid var(--color-primary);
  outline-offset: -2px;
}

/* **UNA SOLA definicion.** Habia dos —`width: 5rem` mas arriba y esta—,
   y la de arriba estaba MUERTA: ganaba esta por orden. No era inocuo:
   una prueba que leyera «la regla de .celda-cantidad» cogia la primera
   y comprobaba algo que no pintaba nada. Lo cazó un sabotaje el
   22/08/2026. Es la colisión de clases del 18/08, otra vez. */
.celda-cantidad {
  width: 90px;
  text-align: right;
}

/* **El campo sigue a SU columna, y por eso va aqui y no en la clase.**
   `.celda-cantidad` la usa tambien el carrito de la compra para un campo
   de PRECIO (`step="0.01"`), asi que centrarla de golpe habria centrado
   un importe — justo lo contrario de la decision del 17-18/08. Se centra
   solo cuando la celda que lo contiene es un recuento. */
.numero-recuento .celda-cantidad { text-align: center; }

/* **El margen SOLO en el carrito.** Ahí la cantidad aparece debajo del precio
   al elegir y necesita separarse de él; en el plan de compra el mismo campo va
   solo en su celda, y ese margen le sumaba altura a cada una de las 16 filas.
   Una clase compartida entre dos pantallas tiene que llevar únicamente lo que
   vale en las dos — el 17/08/2026 esto costó tres intentos de apretar filas
   buscando la causa en el sitio equivocado. */
.carrito .celda-cantidad { margin-top: var(--space-2); }

/* El formulario que envuelve el campo no aporta alto: existe solo para poder
   guardar al salir del campo. */
.form-en-fila { margin: 0; }
.form-en-fila input { margin: 0; }

/* **Solo dentro del carrito.** Esta clase ya existía para la comparación,
   donde marca el fondo de la celda ganadora; al reutilizar el nombre hoy con
   `font-size: var(--text-2xs)`, el precio GANADOR pasó a verse más pequeño que los que
   perdieron — exactamente al revés de lo que tiene que comunicar.
   Segundo choque de nombres del día, después de `.celda-cantidad`. */
.carrito .celda-mejor { font-size: var(--text-2xs); color: var(--color-success); }

@media (max-width: 1100px) {
  .carrito { grid-template-columns: minmax(0, 1fr); }
  .carrito-panel { position: static; max-height: none; }
}

/* ── LOS DOS CUADROS DEL FINAL DEL PLAN, EN PARALELO ──
   Ponerle nombre y cerrarlo son dos cosas menores: apiladas a ancho completo
   empujaban el plan hacia abajo como si fueran secciones importantes. */
.plan-cierre {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-5);
  /* **`stretch`, no `start`** (Vladimir, 17/08/2026: «los 2 que van paralelos
     deben ser de la misma altura»). Con `start`, cada tarjeta medía lo que
     medía su contenido: dos cuadros a la par que no terminan igual se leen
     como si uno estuviera a medio cargar. */
  align-items: stretch;
  /* Y con aire arriba y abajo: pegados a la tabla parecían parte de ella. */
  margin: var(--space-6) 0;
}

.plan-cierre .bandeja {
  margin: 0;
  height: 100%;
}

/* **Y más bajos** («este está muy alto»): son dos campos cortos, no un
   formulario de alta. El cuerpo de estas dos tarjetas no necesita el relleno
   generoso que sí ayuda cuando hay que rellenar diez cosas. */
.plan-cierre .bandeja-cuerpo { padding: var(--space-4); }
.plan-cierre .bandeja-cab { padding: var(--space-3) var(--space-4); }
.plan-cierre label { margin-bottom: var(--space-3); }
.plan-cierre .rail-nota { margin-bottom: var(--space-3); }

/* Las notas y el botón comparten fila: una nota de una línea no necesita
   cinco de alto ni un botón debajo esperando. */
.plan-datos-fila {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--space-3);
  align-items: end;
}

.plan-datos-fila label { margin-bottom: 0; }

@media (max-width: 900px) {
  .plan-cierre { grid-template-columns: 1fr; }
}

/* ── EL FORMULARIO DE PRECIO RÁPIDO, EN TRES LÍNEAS ──
   Vladimir, 17/08/2026: «hazlo más pequeño, coloca más de un cuadro de texto
   por línea… creo que puede hacerse máximo de 3 líneas».

   Y lo que lo justifica es el nombre de la pantalla: se llama «rápido». Seis
   campos apilados ocupaban una pantalla entera para anotar un precio que
   alguien acaba de decir por teléfono. */
.form-precio-fila {
  display: grid;
  grid-template-columns: 1fr 1.4fr 0.6fr;
  gap: var(--space-4);
  align-items: end;
  margin-bottom: var(--space-4);
}

.form-precio-fila > div { min-width: 0; }
.form-precio-fila input { width: 100%; }

@media (max-width: 640px) {
  .form-precio-fila { grid-template-columns: 1fr; }
}

/* Proveedor y artículo, a la par: son los dos «quién y qué» del precio y se
   eligen a la vez. Apilados empujaban las tres cifras fuera de la pantalla,
   que era justo lo que había que evitar. */
.form-precio-fila--quien { grid-template-columns: 1fr 1fr; }

/* ── EL ANCHO DE UN CAMPO DICE CUÁNTO SE ESPERA ESCRIBIR EN ÉL ──
   Vladimir, 17/08/2026: «no lo quería a ancho completo… fíjate cómo queda el
   cuadro de precio, enorme de ancho; si se puede por norma reducir el ancho,
   hazlo».

   No hay ninguna norma que obligue a ocupar todo: al revés, un precio de cinco
   dígitos en una caja de 600 px promete algo que no cabe, y el ojo tarda más en
   encontrar dónde escribir. La tarjeta se queda ancha —eso era lo que hacía
   falta para las dos líneas— pero con un tope, y las cifras a su medida. */
.card-medio {
  max-width: 920px;
}

/* Fecha y moneda son fijas; el precio, lo justo para un importe con decimales.
   El espacio sobrante se lo queda el aire, no las cajas. */
/* **`form-precio-fila`, no `precio-fila`.** El nombre corto ya estaba
   ocupado por las filas de «Comparar precios», y dárselo a esta rejilla les
   impuso tres columnas fijas: el texto acabó cayendo encima del texto y no se
   podía leer.
   TERCERA colisión de nombres en un día —antes `.celda-cantidad` y
   `.celda-mejor`—, las tres por reutilizar un nombre sin comprobar si existía.
   Antes de crear una clase: buscarla. */
.form-precio-fila { grid-template-columns: 200px 200px 120px; }
.form-precio-fila--quien { grid-template-columns: 1fr 1fr; }

@media (max-width: 640px) {
  .form-precio-fila,
  .form-precio-fila--quien { grid-template-columns: 1fr; }
}

/* El listado, cuando hay un eslabón intermedio: sigue accesible pero deja de
   ser el destino del gesto de «atrás». */
.miga-raiz {
  color: var(--color-ink-soft);
  text-decoration: none;
}
.miga-raiz:hover { text-decoration: underline; }

/* El icono del carrito, alineado con su título. */
.carrito-panel h2 {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

/* ── UNA COLUMNA DE LECTURA ──
   Para pantallas cuyo contenido son líneas de texto cortas: notificaciones,
   avisos, historiales. Estiradas a todo el ancho, el dato de la derecha queda
   a un palmo del texto y la mirada cruza el vacío para relacionarlos. */
.columna-lectura {
  max-width: 860px;
}

/* ══════════════════════════════════════════════════════════════════════
   LA COMPARACIÓN: EL ARTÍCULO Y LA CANTIDAD SE QUEDAN QUIETOS
   ══════════════════════════════════════════════════════════════════════
   Vladimir, 18/08/2026: «si agrego más proveedores se va a generar un scroll
   hacia la derecha, ¿cierto? así es que lo quiero».

   Sí — y por eso hace falta esto. Al desplazarse a la derecha, la columna del
   artículo se iría con todo lo demás y se acabarían mirando precios sin ver a
   qué artículo pertenecen. Con tres proveedores no se nota; con diez, la tabla
   deja de servir para lo único que hace: comparar.

   Las dos primeras columnas se quedan fijas y los proveedores pasan por
   delante. Llevan fondo propio porque, sin él, el contenido que pasa por
   debajo se transparenta y se lee encima del texto. */
.tabla-comparar th:nth-child(1),
.tabla-comparar td:nth-child(1) {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--color-white);
  min-width: 260px;
  max-width: 260px;
}

.tabla-comparar th:nth-child(2),
.tabla-comparar td:nth-child(2) {
  position: sticky;
  /* Exactamente lo que mide la primera: si no cuadra, se solapan o queda un
     hueco por el que se ve pasar el contenido. */
  left: 260px;
  z-index: 2;
  background: var(--color-white);
  min-width: 110px;
  max-width: 110px;
}

/* Los encabezados van por encima de sus celdas Y de las columnas que pasan:
   son las dos capas a la vez, esquina superior izquierda incluida. */
.tabla-comparar thead th:nth-child(1),
.tabla-comparar thead th:nth-child(2) {
  z-index: 3;
  background: var(--color-gray-50);
}

/* La línea que separa lo fijo de lo que se mueve: sin ella, al desplazar
   parece que el texto flota sobre la tabla. */
.tabla-comparar th:nth-child(2),
.tabla-comparar td:nth-child(2) {
  border-right: 1px solid var(--color-border);
}

/* ── EL CARRITO SE PLIEGA ──
   Con seis proveedores, 320 px de panel son una columna que no se ve. Plegado
   queda una franja estrecha con el total —lo único que hay que vigilar
   mientras se elige— y la tabla se lleva el ancho. */
.carrito--plegado { grid-template-columns: minmax(0, 1fr) 64px; }

.carrito-panel { position: relative; }

.carrito-plegar {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  border: 1px solid var(--color-border);
  background: var(--color-white);
  border-radius: var(--radius-sm);
  cursor: pointer;
  padding: 2px 8px;
  color: var(--color-ink-soft);
}

.carrito--plegado .carrito-panel { padding: var(--space-3) var(--space-2); }

.carrito-total-plegado {
  margin-top: var(--space-8);
  font-weight: 700;
  font-size: var(--text-sm);
  writing-mode: vertical-rl;
  text-align: right;
}

/* **La tabla de comparación NO colapsa sus bordes, y esa es la razón de que
   las columnas fijas no se fijaran.** (Vladimir, 18/08/2026: «las columnas que
   te pedí inmovilizar siguen igual».)

   `position: sticky` en un `<th>` o un `<td>` no tiene efecto cuando la tabla
   usa `border-collapse: collapse` — que es lo que hereda del estilo general de
   tablas del sistema. Con los bordes separados sí funciona, y para que el
   aspecto no cambie se repone la línea con `box-shadow`, que sí se pinta en
   celdas fijas.

   Va SOLO en esta tabla: el resto del sistema sigue con bordes colapsados. */
.tabla-comparar {
  border-collapse: separate;
  border-spacing: 0;
  /* **`overflow: visible`, y ESTA era la razon de todo.** (Vladimir mando la
     captura del inspector el 18/08/2026 y ahi se veia: `position: sticky`
     aplicada, SIN tachar, y justo debajo `overflow: hidden` en el estilo
     general de `table`.)

     Un antepasado con `overflow: hidden` recorta a sus descendientes y anula
     el pegado: la celda es sticky y no tiene contra que pegarse. La regla
     estuvo bien escrita las cinco veces; faltaba quitar lo que la
     desactivaba.

     Ese `overflow` existe para redondear las esquinas de la tabla. Aqui se
     pierde ese redondeo -lo pone el contenedor con desplazamiento, que
     tambien lo tiene- y se gana la columna fija. */
  overflow: visible;
}

.tabla-comparar th,
.tabla-comparar td {
  border-bottom: none;
  box-shadow: inset 0 -1px 0 var(--color-border);
}

/* La separación entre lo quieto y lo que se mueve, por el mismo medio. */
.tabla-comparar th:nth-child(2),
.tabla-comparar td:nth-child(2) {
  border-right: none;
  box-shadow: inset -1px 0 0 var(--color-border), inset 0 -1px 0 var(--color-border);
}

/* La tabla enfocable marca dónde está el foco: sin esto, quien tabula no sabe
   que llegó a ella y no entiende por qué las flechas hacen algo. */
.tabla-scroll:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* ── LO QUE EL PROVEEDOR OFRECE POR SU CUENTA ──
   Vladimir, 18/08/2026: «me está seleccionando como mejor opción a
   presentaciones diferentes del artículo solicitado… con un sombreado de otro
   color es diferente».

   Ámbar y no verde, y con su propia palabra: el verde significa «la mejor de
   las respuestas a lo que pediste», y esto no responde a nada — nadie más
   cotizó esa presentación porque nadie se la pidió. Llamarlo «el mejor» hacía
   que el plan automático se llevara el artículo pedido a un proveedor Y su
   otra presentación a otro. */
.celda-ofrecida { background: var(--color-caution-bg); }
.celda-marca--ofrecida { color: var(--color-caution); }
.es-ofrecida { background: var(--color-caution-bg); }

/* La fila de un artículo que nadie pidió, entera en ámbar — incluidas las dos
   columnas fijadas, que llevan fondo blanco propio y romperían la continuidad
   de color justo donde está el nombre del artículo. */
.fila-ofrecida td { background: var(--color-caution-bg); }
.fila-ofrecida td:nth-child(1),
.fila-ofrecida td:nth-child(2) { background: var(--color-caution-bg); }

/* La fila que NADIE cotizó, en rojo suave e incluidas las columnas fijadas
   —que llevan fondo propio y partirían el color justo en el nombre—.
   Rojo y no ámbar: el ámbar dice «esto es distinto de lo que pediste»; esto
   dice «esto te va a faltar». */
.fila-sin-cotizar td,
.fila-sin-cotizar td:nth-child(1),
.fila-sin-cotizar td:nth-child(2) { background: var(--color-danger-bg); }

/* ── LAS ETIQUETAS DE FILA DE LA COMPARACIÓN ──
   `etq-neutra` se usaba desde hace tiempo sin estilo propio: salía como texto
   suelto pegado al nombre del artículo. Se le da forma aquí, junto a la nueva
   de peligro, para que las dos se lean como lo que son: una marca, no una
   continuación del nombre. */
.etq {
  display: inline-block;
  margin-left: var(--space-2);
  padding: 1px 8px;
  border-radius: var(--radius-pill);
  font-size: var(--text-2xs);
  font-weight: 600;
  white-space: nowrap;
  vertical-align: middle;
}

.etq-neutra { background: var(--color-gray-100); color: var(--color-ink-soft); }
.etq-peligro { background: var(--color-danger); color: var(--color-white); }

/* ── EL ICONO DE CANAL DENTRO DE UN BOTÓN PRINCIPAL ──
   Vladimir, 18/08/2026: «aquí el sobre se pierde». `canal-icono` pinta el
   sobre del azul de correo, y sobre el verde del botón principal ese azul
   desaparece. Dentro de un botón sólido el icono toma el color del texto —el
   blanco— porque ahí lo que identifica al canal es la palabra, no el matiz.

   En los secundarios no se toca: sobre fondo blanco, el azul del correo y el
   verde de WhatsApp son justo lo que distingue un botón del otro de un
   vistazo. */
.btn:not(.btn-secundario) .canal-icono,
.btn:not(.btn-secundario) .canal-icono svg { color: currentColor; }

/* ══════════════════════════════════════════════════════════════════════
   FLECHAS PARA RECORRER UNA TABLA ANCHA (19/08/2026)
   ══════════════════════════════════════════════════════════════════════
   El comportamiento y el porqué de cada decisión están en `componentes.js`
   (`flechasDeTabla`). Aquí va solo el aspecto.

   La envoltura existe SOLO para anclar las flechas: la caja que se
   desplaza sigue siendo `.tabla-scroll`, sin tocar. Si se anclaran a
   `.tabla-scroll` se irían con el contenido al desplazarse, que es
   justamente lo contrario de lo que se pide. */
.tabla-con-flechas { position: relative; }

/* **`overflow: hidden` DESACTIVA `position: sticky`, y esta es la segunda
   vez** (19/08/2026; la primera fue el 18/08 con las columnas fijas de la
   tabla, y está anotada en ESTADO.md).

   Vladimir: «las 2 flechas quedaron fijas y no bajan con el scroll del
   ratón». No era la regla del pegado —estaba bien escrita, con su `top`—:
   `.bandeja` recorta a sus descendientes, y un elemento pegajoso dentro de
   algo que recorta no tiene contra qué pegarse. Se iba con la tabla.

   Medido en el navegador antes de tocar nada: el círculo bajaba a -321,
   -821 y -1421 en vez de quedarse a 76 px de arriba.

   Ese `overflow` existe para redondear las esquinas de la bandeja. Aquí se
   pierde ese redondeo —lo pone el contenedor con desplazamiento, que
   también lo tiene— y se gana la flecha que acompaña. Se quita SOLO en las
   bandejas que llevan flechas: las demás siguen igual. */
.bandeja:has(.tabla-con-flechas),
.carrito-tabla:has(.tabla-con-flechas) { overflow: visible; }

/* **Y LO MISMO PARA EL AUTOCOMPLETAR** (Vladimir, 20/08/2026: «ahora sale
   cortado»). Es la TERCERA vez que `.bandeja { overflow: hidden }` recorta
   algo que tiene que salirse de su caja: primero las flechas fijadas de la
   tabla, luego el carrito, ahora la lista de sugerencias.

   El desplegable cuelga del campo con `position: absolute`, así que vive
   FUERA de la tarjeta; un ancestro con `overflow: hidden` lo corta por abajo
   y solo se ve la primera línea a medias — que es peor que no verlo, porque
   parece que la lista está vacía.

   `:has()` acota el remedio a las tarjetas que de verdad llevan un
   autocompletar: las demás conservan su recorte, que existe para que las
   esquinas redondeadas no se coman el contenido. */
.bandeja:has(.autocomplete),
.bandeja-cuerpo:has(.autocomplete) { overflow: visible; }

/* ============================================================
   LO QUE ESTÁ FILTRANDO, Y LA SALIDA (20/08/2026)
   ============================================================
   Una línea, no una tarjeta: es un aviso de estado, no contenido. Va entre
   la barra de filtros y la tabla porque ese es el camino de los ojos entre
   lo que se pidió y lo que salió. */
.filtros-activos {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  padding: var(--space-2) var(--space-4);
  border-top: 1px solid var(--color-border);
  background: var(--color-surface);
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

/* El valor filtrado en tinta plena: es el dato que se está mirando. */
.filtros-activos b { color: var(--color-ink); font-weight: 600; }

.filtros-activos-salir {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  color: var(--color-primary-texto);
  text-decoration: none;
  font-weight: 600;
  white-space: nowrap;
}

.filtros-activos-salir:hover { text-decoration: underline; }

/* Franja de alto completo. El círculo va dentro y se pega al centro de lo
   que se ve, para que en una tabla larga no quede arriba ni abajo.
   `pointer-events` solo en el círculo: la franja no debe robarle el clic
   a las celdas que tiene debajo. */
.tabla-flecha {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 2.75rem;
  display: flex;
  /* **ARRIBA, no en el centro** (Vladimir, 19/08/2026: «el botón debería
     aparecer desde arriba e ir bajando con uno; aparece muy abajo en la
     página y si estoy buscando un artículo que está arriba no sé que
     existe»).

     Centrado en una tabla de veinte filas, el botón nacía a mitad del
     bloque: quien mira las primeras líneas no lo ve, y un control que hay
     que descubrir bajando no cumple su función. Arranca arriba y lo sigue
     `position: sticky` en el círculo. */
  align-items: flex-start;
  justify-content: center;
  border: 0;
  padding: 0;
  margin: 0;
  background: transparent;
  cursor: pointer;
  z-index: 4;
  pointer-events: none;
}

/* **EL FONDO SE REPONE EN CADA ESTADO, y no es por gusto** (19/08/2026).
   `button, .btn` pinta el fondo verde de marca y `button:hover` lo repinta.
   `button:hover` pesa (0,1,1) y `.tabla-flecha` pesa (0,1,0): al pasar el
   ratón GANABA el genérico y la franja entera de 44 px por todo el alto de
   la tabla se teñía de verde.

   Se veía solo en la flecha bajo el cursor, que es lo que lo hacía parecer
   un adorno raro en vez de un fallo. Es la lección del 18/08 otra vez: en
   CSS, «no se aplica» casi nunca significa «no se desplegó» — hay que
   mirar QUÉ lo anula. */
.tabla-flecha:hover,
.tabla-flecha:focus,
.tabla-flecha:focus-visible,
.tabla-flecha:active,
.tabla-flecha:disabled {
  background: transparent;
  border-color: transparent;
  box-shadow: none;
}

/* El degradado dice «hay más por aquí» antes incluso de mirar la flecha:
   si el guion no llegara a correr, el borde difuminado sigue contando que
   la tabla continúa. */
.tabla-flecha::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.tabla-flecha-der::before {
  background: linear-gradient(to left,
    rgba(243, 246, 252, .92), rgba(243, 246, 252, 0));
}
.tabla-flecha-izq::before {
  background: linear-gradient(to right,
    rgba(243, 246, 252, .92), rgba(243, 246, 252, 0));
}

/* Marcadas, no discretas — el mismo criterio que las de la galería de
   avatares (13/08/2026): un control que hay que buscar no cumple su
   función, y este existe justo para decir «hay más de lo que ves». */
.tabla-flecha i {
  position: sticky;
  /* ══ A MEDIA PANTALLA, NO PEGADO ARRIBA (09/09/2026) ══

     Vladimir: *«se quedó fijo arriba, no baja con uno; necesito que baje con
     uno, si no estamos en lo mismo»*.

     Estaba anclado justo debajo de la barra superior. Técnicamente
     acompañaba —se quedaba a la vista— pero **se leía como fijo**: el ojo
     está en la fila que se lee, a media pantalla, y el control quedaba
     arriba del todo, lejos.

     A `45vh` viaja con la mirada: se coloca donde uno está mirando y baja y
     sube con la página mientras la tabla siga delante.

     `margin-top` era para no nacer pisando los nombres de las columnas. A
     media pantalla ya no hace falta y además retrasaría su aparición. */
  top: 45vh;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius-full);
  background: var(--color-primary);
  color: var(--color-white);
  box-shadow: 0 2px 5px rgba(22, 32, 58, .28);
  pointer-events: auto;
  transition: background .12s ease, box-shadow .12s ease, transform .12s ease;
}
.tabla-flecha:hover i {
  background: var(--color-primary-hover);
  box-shadow: 0 3px 9px rgba(22, 32, 58, .34);
}
.tabla-flecha:active i { transform: scale(.94); }
.tabla-flecha:focus-visible i {
  outline: 2px solid var(--color-ink);
  outline-offset: 3px;
}
.tabla-flecha svg { width: 20px; height: 20px; }

/* `hidden` no basta contra `display: flex` — la misma trampa que en la
   galería. */
.tabla-flecha[hidden] { display: none; }

/* La derecha sí va al borde: ahí no hay nada fijo que tapar. La posición
   de la izquierda la calcula el guion, midiendo las columnas quietas; el
   valor de aquí es solo el de partida, por si el guion no corre. */
.tabla-flecha-der { right: 0; }
.tabla-flecha-izq { left: 370px; }

@media (prefers-reduced-motion: reduce) {
  .tabla-flecha i { transition: none; }
}

/* En el teléfono se arrastra sin pensarlo y las flechas estorban más de
   lo que ayudan — mismo criterio que la galería. */
@media (max-width: 640px) {
  .tabla-flecha { display: none; }
}

/* ══════════════════════════════════════════════════════════════════════
   LA COMPOSICIÓN: CUÁNTOS Y QUÉ PARTE DEL TOTAL (19/08/2026)
   ══════════════════════════════════════════════════════════════════════
   Vladimir: «me permite visualizar y a la vez que me muestre el porcentaje
   de cómo están estructurados mis clientes; hasta ahora solo me dice la
   cantidad pero no el porcentaje».

   El cálculo y su frontera con Inteligencia están en `app/core/composicion.py`.
   Aquí solo el aspecto.

   NO se usa ninguna librería de gráficos. Una barra apilada y una leyenda
   son cajas con un ancho en tanto por ciento; traer 200 KB en cada carga
   para dibujarlas sería pagar mucho por poco, y además el color dejaría de
   salir de `tokens.css`. */
.composicion {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* ── La cifra grande, con lo que se está mirando al lado ── */
.comp-cifra { display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; }
.comp-cifra b {
  font-size: var(--text-2xl);
  font-weight: 700;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  color: var(--color-ink);
}
.comp-cifra span { color: var(--color-ink-soft); font-size: var(--text-base); }

/* ── Las pestañas de qué se está midiendo ──
   Son enlaces, no botones de guion: así el reparto elegido viaja en la
   dirección y se puede volver a él, compartirlo o recargar sin perderlo. */
.comp-medidas { display: flex; flex-wrap: wrap; gap: var(--space-1); }
.comp-medida {
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-ink-soft);
  text-decoration: none;
  border: 1px solid transparent;
}
/* **El mismo gris que el resto de vinculos, desde el 28/08/2026.** Aqui
   decia «gris neutro y NO `--color-fila-hover`, que significa esta fila se
   abre». El resultado era correcto —`gray-100` si se ve— pero el motivo ya
   no: ese token dejo de significar «se abre» y pasa a significar «esto
   responde al clic», que es lo que hace una pestana de medida. Dos grises a
   1,15 y 1,18 sosteniendo una diferencia que nadie percibe. */
.comp-medida:hover { background: var(--color-fila-hover); color: var(--color-ink); }
.comp-medida[aria-current="page"] {
  background: var(--color-primary-bg);
  color: var(--color-primary-texto);
  border-color: var(--color-primary);
}
.comp-medida:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }

/* ── La barra ── */
.comp-barra {
  display: flex;
  height: 34px;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: var(--color-gray-100);
}

.comp-tramo {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-white);
  font-size: var(--text-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-decoration: none;
  overflow: hidden;
  /* Sin esto, un tramo del 0,5 % desaparece del todo y el total deja de
     sumar lo que se ve. Uno estrecho no se puede leer, pero sí señalar. */
  min-width: 3px;
  transition: filter .12s ease, opacity .12s ease;
}
.comp-tramo:hover { filter: brightness(1.14); }
.comp-tramo:focus-visible { outline: 2px solid var(--color-ink); outline-offset: -3px; }

/* Con un filtro puesto, el tramo elegido se queda encendido y el resto se
   apaga: la barra sigue diciendo de dónde salió lo que se está viendo. */
.comp-barra[data-filtrando="si"] .comp-tramo { opacity: .26; }
.comp-barra[data-filtrando="si"] .comp-tramo[aria-current="page"] { opacity: 1; }

/* ── La leyenda ── */
.comp-leyenda {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--space-1) var(--space-4);
  margin: 0;
  padding: 0;
  list-style: none;
}
.comp-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2);
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: inherit;
}
.comp-item:hover { background: var(--color-gray-100); }
.comp-item:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 1px; }
.comp-item[aria-current="page"] { background: var(--color-primary-bg); }
.comp-punto { width: 11px; height: 11px; border-radius: 3px; flex-shrink: 0; }
.comp-nombre {
  flex: 1;
  font-size: var(--text-base);
  color: var(--color-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.comp-num {
  font-size: var(--text-base);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--color-ink);
}
/* El porcentaje con ancho fijo para que la columna quede a plomo: leer
   una lista de cifras desalineadas cuesta el doble. */
.comp-pct {
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
  font-variant-numeric: tabular-nums;
  min-width: 3.6rem;
  text-align: right;
}

/* ── El alcance: a cuántos se les puede escribir ──
   Va aparte de la composición porque no es un reparto de categorías: son
   tres preguntas independientes —¿tiene correo? ¿WhatsApp? ¿alguno?— y un
   mismo cliente cuenta en varias. Apilarlas en una barra diría que suman
   el total, y no suman. */
.comp-alcance {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: var(--space-4);
  padding-top: var(--space-4);
  border-top: 1px solid var(--color-border);
}
.alcance-dato { display: flex; flex-direction: column; gap: var(--space-1); }
.alcance-rotulo {
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
  display: flex;
  align-items: center;
  gap: var(--space-1);
}
.alcance-cifra {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-variant-numeric: tabular-nums;
}
.alcance-cifra b { font-size: var(--text-xl); font-weight: 700; color: var(--color-ink); }
.alcance-cifra span { font-size: var(--text-sm); color: var(--color-ink-soft); }
.alcance-barra {
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--color-gray-100);
  overflow: hidden;
}
.alcance-barra i { display: block; height: 100%; border-radius: var(--radius-pill); background: var(--color-primary); }
/* Cuando la mayoría NO se puede alcanzar, el color lo dice: aquí sí es
   semántico, porque hay un lado bueno y uno malo. */
.alcance-barra i.escaso { background: var(--color-warning); }

@media (prefers-reduced-motion: reduce) {
  .comp-tramo { transition: none; }
}

/* Los tramos que solo INFORMAN —el plan de compra, donde no hay nada que
   filtrar— no se pintan como pulsables: sin cursor de mano y sin aclarar
   al pasar por encima. Un control que parece pulsable y no lleva a ningún
   sitio enseña a desconfiar de los que sí lo son. */
.comp-tramo--fijo { cursor: default; }
.comp-tramo--fijo:hover { filter: none; }
.comp-item--fijo { cursor: default; }
.comp-item--fijo:hover { background: transparent; }

/* ── LAS TRES FORMAS DEL GRÁFICO (19/08/2026) ──
   Vladimir: «que lo muestren tipo barra, ya sea horizontal o vertical, que
   me muestre diferentes opciones; en un solo gráfico de barras puedo ver lo
   que representa cada segmento y se ve mejor».

   Las barras horizontales son las de partida y no por gusto: en la apilada
   cada tramo empieza donde acaba el anterior, así que comparar dos del medio
   obliga a medir a ojo. En barras todas salen de la misma línea. */
.comp-formas {
  display: flex;
  gap: var(--space-1);
  align-self: flex-start;
  padding: 3px;
  background: var(--color-gray-100);
  border-radius: var(--radius-pill);
}
.comp-forma {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-pill);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-ink-soft);
  text-decoration: none;
}
.comp-forma:hover { color: var(--color-ink); }
.comp-forma[aria-current="page"] {
  background: var(--color-white);
  color: var(--color-ink);
  box-shadow: var(--shadow-sm);
}
.comp-forma:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
/* En pantalla estrecha se quedan los iconos: tres rótulos no caben y
   partirlos en dos líneas rompe la fila. */
@media (max-width: 560px) {
  .comp-forma span { display: none; }
}

/* ── Barras horizontales ── */
.comp-barras { display: flex; flex-direction: column; gap: var(--space-1); }

.comp-b-fila {
  display: grid;
  /* El nombre con ancho fijo para que TODAS las pistas empiecen en la misma
     columna: si cada barra arrancara donde acaba su nombre, comparar
     largos dejaría de significar nada. */
  grid-template-columns: minmax(0, 13rem) 1fr auto 3.6rem;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-2);
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: inherit;
}
.comp-b-fila:hover { background: var(--color-gray-100); }
.comp-b-fila:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 1px; }
.comp-b-fila[aria-current="page"] { background: var(--color-primary-bg); }
.comp-b-fila--fijo { cursor: default; }
.comp-b-fila--fijo:hover { background: transparent; }

.comp-b-nombre {
  font-size: var(--text-base);
  color: var(--color-ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.comp-b-pista {
  display: block;
  height: 20px;
  background: var(--color-gray-100);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.comp-b-pista i {
  display: block;
  height: 100%;
  border-radius: var(--radius-sm);
  /* Un valor diminuto tiene que seguir viéndose: una barra de cero píxeles
     dice «ninguno», que es distinto de «pocos». */
  min-width: 3px;
}
.comp-b-num {
  font-size: var(--text-base);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--color-ink);
  text-align: right;
}
.comp-b-pct {
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
  font-variant-numeric: tabular-nums;
  text-align: right;
}

@media (max-width: 640px) {
  .comp-b-fila { grid-template-columns: 1fr auto 3.4rem; }
  .comp-b-pista { grid-column: 1 / -1; order: 3; }
}

/* ── Columnas verticales ── */
.comp-columnas {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: minmax(0, 1fr);
  gap: var(--space-2);
  align-items: end;
}
.comp-c-col {
  display: grid;
  /* Filas fijas para que todas las columnas compartan la misma línea de
     base y la misma altura de pista: si cada una midiera lo suyo, las
     alturas dejarían de ser comparables, que es lo único que aporta. */
  grid-template-rows: auto 150px auto auto;
  gap: var(--space-1);
  justify-items: center;
  padding: var(--space-2) var(--space-1);
  border-radius: var(--radius-sm);
  text-decoration: none;
  color: inherit;
}
.comp-c-col:hover { background: var(--color-gray-100); }
.comp-c-col:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 1px; }
.comp-c-col[aria-current="page"] { background: var(--color-primary-bg); }
.comp-c-col--fijo { cursor: default; }
.comp-c-col--fijo:hover { background: transparent; }

.comp-c-cifra {
  font-size: var(--text-sm);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--color-ink);
}
.comp-c-pista {
  display: flex;
  align-items: flex-end;
  width: 100%;
  max-width: 64px;
  height: 100%;
  background: var(--color-gray-100);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.comp-c-pista i {
  display: block;
  width: 100%;
  border-radius: var(--radius-sm);
  min-height: 3px;
}
.comp-c-nombre {
  font-size: var(--text-xs);
  color: var(--color-ink-soft);
  text-align: center;
  line-height: 1.25;
  /* Dos líneas y a partir de ahí puntos suspensivos: con siete columnas,
     un nombre largo sin tope empuja la fila entera hacia abajo. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.comp-c-pct {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-ink);
  font-variant-numeric: tabular-nums;
}

/* Con siete categorías en un teléfono las columnas quedan de 30 px y el
   nombre no cabe: se desplaza a lo ancho en vez de encogerlas más. */
@media (max-width: 640px) {
  .comp-columnas {
    grid-auto-columns: 5.5rem;
    overflow-x: auto;
    padding-bottom: var(--space-2);
  }
}

/* El panel de gráficos necesita más ancho que uno de formulario: con 480 px
   las barras quedan de 150 y dejan de compararse, que es lo único que
   aportan. No se ensancha `.panel-lateral` entero —los formularios están
   bien como están—, se ensancha SOLO cuando lleva gráficos. */
.panel-lateral:has(.composicion) { max-width: 720px; }

/* Dentro del panel las barras van más estrechas de rótulo: 13 rem de nombre
   sobre 720 px dejarían la pista sin sitio. */
.panel-cuerpo .comp-b-fila { grid-template-columns: minmax(0, 9.5rem) 1fr auto 3.6rem; }
.panel-cuerpo .comp-columnas { grid-auto-columns: minmax(0, 1fr); }

/* ══════════════════════════════════════════════════════════════════════
   IMPRIMIR EL GRÁFICO (19/08/2026)
   ══════════════════════════════════════════════════════════════════════
   Vladimir: «creo que los gráficos deberían de tener una opción de impresión
   por si quiero presentar algo a un tercero».

   Y ese «a un tercero» es lo que decide qué sale en el papel. */
.grafico-imprimir {
  display: flex;
  justify-content: flex-end;
  margin-bottom: var(--space-3);
}

/* Solo existe en el papel: en pantalla el título ya está en la cabecera del
   panel, y repetirlo sería decir dos veces lo mismo. */
.solo-al-imprimir { display: none; }

@media print {
  /* La salida del filtro no se imprime: en el papel no hay dónde
     pulsar, y el dato de que estaba filtrado ya lo dice la línea. */
  .filtros-activos-salir { display: none; }

  /* **NO BASTA CON OCULTAR: HAY QUE SACARLO DEL FLUJO** (Vladimir,
     19/08/2026: «en clientes hay una sección que se abre hasta con 18 hojas
     para imprimir, claro, el resto son en blanco […] yo puedo elegir
     imprimir del 1-1 si quiero, pero si alguien no ve eso va a mandar 18
     páginas a imprimir»).

     Y es un defecto, no una molestia: el papel se gasta igual.

     La primera versión usaba `visibility: hidden`, que esconde pero **deja
     el hueco**. Con 293 clientes en la tabla, el documento seguía midiendo
     lo que mide esa tabla: dieciocho hojas, una con el gráfico y diecisiete
     en blanco.

     Con `display: none` el elemento deja de ocupar sitio, y el documento
     mide lo que mide el gráfico. */
  .sidebar,
  .topbar-light,
  .panel-fondo,
  .modal-fondo { display: none !important; }

  /* De todo lo que hay en la pantalla se queda SOLO el panel. Se apunta por
     nombre y no por posición: por posición se rompe en cuanto alguien mueva
     un bloque. */
  /* **Y la hoja del catalogo, que es un documento entero.** Se apunta
     aqui como superviviente en vez de montar un mecanismo aparte: asi su
     encabezado, su aviso y sus botones desaparecen del papel por esta
     misma regla, sin marcarlos uno a uno. Sin esto la hoja salia EN
     BLANCO — la regla se escribio para la ficha con grafico y no sabia
     que algun dia habria otra cosa que imprimir. */
  .content > *:not(.panel-envoltura):not(.hoja-imprimible) { display: none !important; }
  .panel-envoltura { display: block !important; }
  .hoja-imprimible { display: block !important; }

  /* El panel deja de flotar y se convierte en el documento: si siguiera
     fijo, el navegador imprimiría solo lo que cabe en una pantalla y
     cortaría el resto. */
  .panel-lateral {
    position: static;
    max-width: none;
    width: 100%;
    box-shadow: none;
    overflow: visible;
  }
  /* Y su cuerpo deja de tener desplazamiento propio: lo que no se veía sin
     bajar tiene que salir en el papel. */
  .panel-formulario,
  .panel-cuerpo {
    overflow: visible !important;
    max-height: none !important;
    height: auto !important;
  }

  /* Nada de alturas heredadas de la pantalla, que es de donde salían las
     hojas de sobra. */
  html, body, .shell, .content {
    height: auto !important;
    min-height: 0 !important;
    overflow: visible !important;
  }

  /* **Un papel no tiene botones.** Enseñar pestañas y el aspa de cerrar
     delante de un banco o un socio hace que parezca una captura de pantalla
     en vez de un informe. */
  .panel-cerrar,
  .panel-pie,
  .grafico-imprimir,
  .comp-formas { display: none; }

  /* De las pestañas de medida se queda SOLO la elegida, y como texto: «137,
     46,8 %» sin decir que es un reparto por segmento no significa nada fuera
     de la pantalla. */
  .comp-medida { display: none; }
  .comp-medida[aria-current="page"] {
    display: inline-block;
    background: none;
    border: 0;
    padding: 0;
    color: var(--color-ink-soft);
    font-weight: 600;
  }

  /* ── La hoja de trabajo del catalogo ──
     Van AQUI dentro y no en una seccion de impresion aparte: hay una
     prueba que exige una sola, «las reglas del papel tienen que estar
     juntas, y estas pruebas leerian la primera que encontraran». Y es
     verdad: la comprobacion de que la hoja sobrevive lee ESTA. */

  /* En el papel el marco sobra: el papel ya es el marco. */
  .hoja-imprimible {
    border: 0;
    border-radius: 0;
    padding: 0;
  }

  /* La cabecera se repite en cada hoja. Sin esto, de la segunda en
     adelante hay cuatro columnas sin decir cual es cual — y la que
     importa, donde se escribe el precio, es una raya vacia. */
  .hoja-tabla thead { display: table-header-group; }
  .hoja-tabla tr { page-break-inside: avoid; }
  .hoja-tabla td, .hoja-tabla th { padding: 5px 7px; }

  .solo-al-imprimir { display: block; }
  .grafico-titulo-impreso {
    font-size: var(--text-xl);
    font-weight: 700;
    color: var(--color-ink);
    margin: 0 0 var(--space-2);
  }

  /* Los colores de las barras SÍ se imprimen: sin ellos, siete tramos grises
     no se distinguen y el gráfico deja de decir nada. */
  .comp-b-pista i,
  .comp-c-pista i,
  .comp-tramo,
  .comp-punto,
  .alcance-barra i {
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }

  /* Que no se parta una barra entre dos páginas. */
  .comp-b-fila, .comp-c-col, .alcance-dato { break-inside: avoid; }
}

/* ── EL CANAL QUE ES UN BOTÓN (19/08/2026) ──
   Vladimir: «el contacto por WhatsApp con clientes y proveedores debería
   usar el mismo formato, establecer estandarización».

   Al cliente se le escribe por la API y su canal es una CASILLA; al
   proveedor se le abre `wa.me`, que abre UNA conversación, así que su canal
   es un BOTÓN. La diferencia de comportamiento es real y no se puede
   quitar — lo que sí se quita es que además se vean distintos.

   Aquí el botón se despinta de botón y se pinta de fila de canal: mismo
   borde, mismo relleno, misma tipografía. Lo único que lo delata es que al
   pasar por encima se marca, que es lo que dice «esto se pulsa». */
.envio-canal--accion {
  width: 100%;
  text-align: left;
  background: var(--color-white);
  border: 1px solid var(--color-border);
  color: inherit;
  font: inherit;
  font-weight: 400;
  min-height: 0;
  cursor: pointer;
}
.envio-canal--accion:hover {
  background: var(--color-primary-bg);
  border-color: var(--color-primary);
}
.envio-canal--accion:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* ── LOS WHATSAPP SIN ABRIR (19/08/2026) ──
   Vladimir: «ahí puede pasar un mensaje hasta un día sin uno saberlo».

   La burbuja de la barra hereda de `.topbar-badge`, que ya existe para la
   campana: es la misma idea —algo que pasó y hay que mirar— y verlas
   distintas haría pensar que son cosas de distinta clase.

   **Verde WhatsApp y no ámbar**, que es lo único que cambia: el ámbar de la
   campana significa «revísalo»; este dice ADEMÁS de qué canal viene, y con
   dos burbujas juntas el color es lo que las separa de un vistazo. */
.topbar-badge--wa { background: var(--color-whatsapp); color: var(--color-whatsapp-texto); }

/* El mismo número dentro del menú. Aquí no flota sobre un icono: va detrás
   del texto, así que es una pastilla en línea y no un globo posicionado. */
.nav-contador {
  display: inline-block;
  min-width: 1.15rem;
  padding: 0 5px;
  margin-left: var(--space-1);
  background: var(--color-whatsapp);
  color: var(--color-whatsapp-texto);
  border-radius: var(--radius-pill);
  font-size: 0.65rem;
  font-weight: 700;
  line-height: 1.5;
  text-align: center;
  /* Que no arrastre el subrayado ni el color del enlace activo. */
  vertical-align: middle;
}

/* ── HISTORIAL DE REVISIONES DE UNA COTIZACION (25/08/2026) ──────────
   Una fila por cada vez que el documento salio con otro contenido. Se lee
   de arriba abajo como una linea de tiempo: cuando, cuanto y el papel.

   **Sin viñetas y con la fecha en tabulares**: son cifras que se comparan
   entre filas —«de 39,353 a 41,713»— y con ancho variable bailan. */
.rev-lista { list-style: none; margin: 0; padding: 0; display: flex;
             flex-direction: column; gap: var(--space-2); }
.rev-fila { display: flex; flex-direction: column; gap: 2px;
            padding-bottom: var(--space-2); border-bottom: 1px solid var(--color-border); }
.rev-lista > .rev-fila:last-child { border-bottom: 0; padding-bottom: 0; }
.rev-cuando { font-size: var(--text-xs); color: var(--color-ink-soft);
              font-variant-numeric: tabular-nums; }
.rev-datos { font-size: var(--text-sm); color: var(--color-ink);
             font-variant-numeric: tabular-nums; }
.rev-pdf { font-size: var(--text-xs); }

/* ═══════════════════════════════════════════════════════════════════
   FICHA DE CLIENTE · un solo tablero, y el ancho para quien lo necesita
   (26/08/2026)

   Vladimir pidió partir la columna «Teléfono» en Cargo / Teléfono /
   WhatsApp / Correo, y vio él mismo el problema: «solo que veo que el
   espacio es muy pequeño para que todo eso quepa».

   NO CABÍA, y al medir por qué salió lo de fondo. La ficha daba 320 px
   FIJOS a un carril de siete cajas y apretaba en 694 px las cuatro cosas
   que se usan. De 311 clientes: crédito 1, notas 1, campaña 0, UTM 0,
   tipo de negocio 0 — cinco de esas siete cajas pintaban guiones en
   todas las fichas.

   Y lo que ordenó el rediseño lo vio él: «igual tiene que ver con los
   contactos de la empresa, ahí puedes ver todo en un solo tablero». Las
   mismas personas salían DOS VECES en la pantalla — en la tabla y en
   «Permiso de contacto» —, así que para saber si a alguien se le puede
   escribir había que cruzar a ojo dos sitios distintos.
   ═══════════════════════════════════════════════════════════════════ */

/* El tablero se lleva el ancho entero; debajo, dos columnas iguales.
   **Comunicaciones a la IZQUIERDA** (Vladimir, 26/08): es lo que más se
   mira y lo que se lee primero. */
.ficha-par {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
  align-items: start;
  /* **Arriba también** (Vladimir, 26/08: «quedó pegada a la ficha de
     contactos»). Las bandejas sueltas se separan con `.bandeja + .bandeja`,
     pero el par es un `div` y esa regla no le llega: quedaba a 0 px del
     tablero. */
  margin-top: var(--space-4);
  margin-bottom: var(--space-4);
}

/* **LOS DOS ARRANCAN A LA MISMA ALTURA.** `.bandeja + .bandeja` les daba
   16 px de margen superior al segundo, así que Cotizaciones empezaba más
   abajo que Comunicaciones —351 contra 367, medido en pantalla— y el par
   se veía descuadrado. Dentro de la rejilla los separa el `gap`, no el
   margen. */
.ficha-par > .bandeja { margin-top: 0; }

@media (max-width: 960px) {
  .ficha-par { grid-template-columns: 1fr; }
}

/* Sin esto, un hijo de la rejilla no encoge por debajo del ancho de su
   contenido y desborda la pantalla aunque la tabla tenga su scroll. */
.ficha-par > * { min-width: 0; }

/* ── EL PIE DE LA FICHA: UNA CAJA, NO CUATRO (26/08/2026) ───────────
   Vladimir: «siento que esto se ve horrible».

   **Y el problema no era la información: son OCHO datos.** Eran cuatro
   cajas de 81, 84, 118 y 223 px alineadas arriba, así que las tres cortas
   dejaban huecos colgando debajo de la larga — cuatro bordes de alturas
   distintas, medidos en pantalla.

   Con una caja eso desaparece de raíz. Y no se esconde nada: un acordeón
   resolvía lo mismo, pero cobraba un clic por dato — con ocho, pulsar
   cuesta más que leerlos, y el crédito se consulta justo cuando se va a
   cotizar. */
.ficha-pie {
  background: var(--color-white);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  padding: var(--space-4) var(--space-5);
}

/* **Título de sección como los demás**, no rótulo pequeño en mayúsculas
   (Vladimir, 16/09/2026: los cinco títulos de la ficha «con la misma
   tipografía, el mismo tamaño y en negrita»). Medido: iba a 12 px. */
.ficha-pie-tit {
  margin: 0 0 var(--space-4);
  font-size: var(--text-xl);
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--color-ink);
}

/* `auto-fit` y no cuatro columnas fijas: en una pantalla estrecha pasan a
   tres, dos o una sin que nada se recorte. */
.ficha-datos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 0 var(--space-6);
}

/* **EL RÓTULO VA ENCIMA DEL VALOR, no enfrente.** Así los valores se
   alinean en columna y se leen de un barrido vertical. Enfrente —como
   estaban— cada fila deja un hueco distinto entre etiqueta y dato, y la
   vista tiene que saltar de un lado a otro en cada línea. */
.dato-campo {
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-gray-100);
  min-width: 0;
}

.dato-lbl {
  display: block;
  font-size: var(--text-xs);
  color: var(--color-gray-500);
  margin-bottom: 1px;
}

.dato-val {
  display: block;
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--color-ink);
}

.ficha-observacion {
  margin-top: var(--space-4);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-gray-100);
}

/* ── DESACTIVAR NO ES UN DATO ───────────────────────────────────────
   Estaba dentro de una caja llamada «Cuenta», entre las de referencia y
   **sin un solo dato dentro**: un botón que apaga un cliente presentado
   como si fuera información suya.

   Sale de ahí y se queda solo al pie, sin caja: una acción que se toma a
   propósito no debe estar donde la vista pasa buscando el segmento. */
.ficha-cuenta {
  display: flex;
  justify-content: flex-end;
  margin-top: var(--space-4);
}

/* ── LOS PERMISOS, EN LA FILA DE LA PERSONA ─────────────────────────
   Vienen de la caja «Permiso de contacto», que desaparece. Están donde
   se decide a quién escribir, no al otro lado de la pantalla.

   **Lo que NO se tiene se pinta en gris, no se esconde.** Ver «Correo»
   apagado dice «a esta persona no se le escribe»; que no aparezca nada
   dice lo mismo, pero sólo si ya lo sabes. */
/* **Las tres en una línea, no envueltas.** Al medir en pantalla salían en
   tres renglones y la fila se iba a 92 px de alto: el permiso es una
   ojeada, no un párrafo. Si no caben, la celda hace scroll antes que
   crecer hacia abajo. */
.permisos-fila {
  display: flex;
  gap: 4px;
  flex-wrap: nowrap;
  overflow: hidden;
}

/* Las etiquetas de permiso van más apretadas que las del resto del
   sistema: son tres seguidas en una celda estrecha. */
.permisos-fila .etiqueta,
.permisos-fila .permiso-no {
  font-size: var(--text-2xs);
  padding: 2px 6px;
  white-space: nowrap;
}

.permiso-no {
  display: inline-block;
  font-size: var(--text-2xs);
  font-weight: 600;
  padding: 3px 8px;
  border-radius: 999px;
  white-space: nowrap;
  color: var(--color-gray-400);
  border: 1px solid var(--color-border);
  background: transparent;
}

/* La estrella del contacto principal. Una pastilla del mismo tamaño que
   las de permiso, para un dato que tiene uno de cada tres contactos,
   pesaba más de lo que dice. */
.marca-principal {
  color: var(--color-warning);
  font-size: var(--text-xs);
  flex: 0 0 auto;
  line-height: 1;
}

/* El nombre y el correo se recortan antes que estirar la tabla. Es la
   misma lección de la tabla de campañas del 25/08: `max-width: 0` en una
   celda significa «no reclames ancho». */
.tabla-contactos { table-layout: fixed; width: 100%; }

/* **EL REPARTO SE DECLARA, o `fixed` da SIETE COLUMNAS IGUALES.**
   Medido en pantalla, no supuesto (26/08/2026): sin esto salían siete de
   139 px cada una — el correo cortado, «Acciones» con 139 para tres
   puntos, y las filas a 92 px porque los permisos envolvían en tres
   renglones. El HTML era correcto y no había forma de verlo sin mirar.

   Los porcentajes suman 100 y se reparten por lo que ocupa cada dato:
   el correo llega a 55 caracteres, «Acciones» son tres puntos.

   **Los permisos se midieron TRES VECES, y las tres hizo falta.** Las
   pastillas piden 198 px: el primer reparto les daba 143 y «Promos» salía
   cortada. Al ampliarlo, el relleno de la celda se comía 24 px más y
   seguía cortando. Y al pasar la letra de 10 px a la escala del sistema
   pasaron a pedir 210 y volvieron a cortarse por tres píxeles.

   Un permiso a medias es peor que no enseñarlo: dice que la persona
   acepta algo que no se llega a leer. */
.tabla-contactos th:nth-child(1) { width: 20%; }     /* Persona   */
.tabla-contactos th:nth-child(2) { width: 11%; }     /* Cargo     */
.tabla-contactos th:nth-child(3) { width: 11%; }     /* Teléfono  */
.tabla-contactos th:nth-child(4) { width: 11%; }     /* WhatsApp  */
.tabla-contactos th:nth-child(5) { width: 19%; }     /* Correo    */
.tabla-contactos th:nth-child(6) { width: 24%; }     /* Permisos  */
.tabla-contactos th:nth-child(7) { width: 4%;  }     /* Acciones  */

/* La celda de permisos aprieta su relleno lateral: son tres pastillas
   seguidas y cada píxel de margen sale de ellas. */
.tabla-contactos td:nth-child(6) { padding-left: 8px; padding-right: 8px; }

/* **UN NÚMERO PARTIDO EN DOS LÍNEAS ES ILEGIBLE.** Al arreglar el ancho de
   los permisos las filas pasaron de 50 px a 67: «809-555-0101 ext. 22» no
   cabía en su columna y envolvía. Se recorta con su `title` entero, que
   es lo mismo que se hace con el correo.

   Lo cazó volver a medir el alto DESPUÉS de arreglar el ancho — la misma
   lección de la tabla de campañas del 25/08, donde el primer arreglo bajó
   las filas y estiró la tabla. */
.tabla-contactos td:nth-child(3),
.tabla-contactos td:nth-child(4) {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tabla-contactos td.celda-recorta,
.tabla-contactos th.celda-recorta { max-width: 0; }
.tabla-contactos .recortado {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* El nombre no puede estirar su celda ni envolverse. */
.tabla-contactos .identidad-principal {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── EL BOTÓN DE NOTA, CON SU ESTADO (26/08/2026) ───────────────────
   Vladimir: «colocar una libreta, nota, su icono correspondiente, si uno
   quiere escribir algo solo le da click y cuando haya algo escrito en esa
   nota debemos usar algo que indique que hay un texto».

   Sube a la cabecera y deja de ocupar sitio en la ficha. Antes era un
   bloque de 280 px que casi siempre decía que no había nada — hay CERO
   notas en toda la base.

   **El NÚMERO en vez de un punto.** Cuesta lo mismo y dice más: «3» y
   «hay algo» ocupan igual, pero sólo uno de los dos se puede usar para
   decidir si merece la pena abrirlo.

   **Y no se usa el rojo, a propósito.** En este sistema el rojo significa
   que algo va mal. Una nota escrita no es un problema, y gastar el rojo
   aquí haría que el rojo de verdad se leyera con menos urgencia — que es
   la forma más rápida de que se dejen de mirar los avisos. */
.btn-nota {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
}

/* Sin notas: un botón discreto más. No pide atención porque no tiene nada
   que contar. */
.btn-nota .nota-cuantas { display: none; }

/* Con notas: el botón cambia de fondo y enseña cuántas hay. */
.btn-nota.tiene-notas {
  background: var(--color-warning-bg);
  border-color: var(--color-warning);
  color: var(--color-warning);
}

.btn-nota.tiene-notas .nota-cuantas {
  display: inline-block;
  font-size: var(--text-2xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1;
  padding: 2px 5px;
  border-radius: 999px;
  background: var(--color-warning);
  color: var(--color-white);
}

/* ── GRUPOS PLEGABLES DENTRO DE UNA FICHA (27/08/2026) ───────────────
   Vladimir, señalando el «Qué pasó» de las campañas: «esos títulos pueden
   ser desplegables, como están en las campañas de correo masivo».

   **No contradice el argumento que hay más arriba contra el acordeón**
   —«cobraba un clic por dato»—, porque no se pliegan los DATOS: se pliegan
   los GRUPOS. El primero va abierto, así que lo que se consulta antes de
   comprar sigue leyéndose sin pulsar nada; lo que se mira una vez al mes
   —cómo se registró, si está activo— cuesta un clic y deja de ocupar
   pantalla en las otras 23 fichas.

   Y el resumen del título no es adorno: dice lo que hay dentro sin abrir
   —«Crédito 30 días · Entrega sin costo»—, que es lo que evita el clic. */
.ficha-grupo {
  border-top: 1px solid var(--color-gray-100);
}

.ficha-grupo:first-of-type {
  border-top: 0;
}

/* **Se tiene que NOTAR que se abre** (Vladimir, 27/08/2026: «debe ir en
   texto más grande y destacado, que se note que es autodesplegable»).

   La primera versión los pintó como rótulos de sección —pequeños y en
   gris— y así no se leen como un control: parecen un título. Ahora pesan lo
   que pesa un `<h3>`, la fila entera se ilumina al pasar por encima y la
   flecha tiene su propio botón redondo. Tres señales, no una. */
.ficha-grupo-cab {
  list-style: none;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-3) var(--space-3) var(--space-2);
  margin: 0 calc(var(--space-2) * -1);
  border-radius: var(--radius-md);
  font-size: var(--text-base);
  font-weight: 700;
  color: var(--color-ink);
  transition: background-color 0.12s ease;
}

.ficha-grupo-cab:hover {
  background: var(--color-fila-hover);
}

.ficha-grupo-cab::-webkit-details-marker {
  display: none;
}

.ficha-grupo-cab:hover .ficha-grupo-t {
  color: var(--color-primary-texto);
}

.ficha-grupo-cab:focus-visible {
  outline: 2px solid var(--color-primary-600);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

/* El resumen se va a la derecha y NO se lee como parte del título: es lo
   que hay dentro, en gris y sin negrita. */
.ficha-grupo-res {
  margin-left: auto;
  font-weight: 400;
  font-size: var(--text-sm);
  color: var(--color-gray-500);
  text-align: right;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* La flecha en su propio botón: un chevrón suelto en gris claro se lee
   como adorno; dentro de un círculo se lee como algo que se pulsa. */
.ficha-grupo-chev {
  color: var(--color-gray-500);
  display: inline-grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-full);
  background: var(--color-gray-100);
  transition: transform 0.15s ease, background-color 0.12s ease;
  flex: 0 0 auto;
}

.ficha-grupo-cab:hover .ficha-grupo-chev {
  background: var(--color-primary-bg);
  color: var(--color-primary-texto);
}

.ficha-grupo[open] > .ficha-grupo-cab .ficha-grupo-chev {
  transform: rotate(90deg);
}

/* Abierto, el resumen sobra: lo que dice está justo debajo.
   **`display: none` y no `visibility: hidden`**: lo segundo lo esconde pero
   le deja el sitio, y aquí ese sitio es ancho que le quita al título. */
.ficha-grupo[open] > .ficha-grupo-cab .ficha-grupo-res {
  display: none;
}

.ficha-grupo-cuerpo {
  padding-bottom: var(--space-3);
}

@media (prefers-reduced-motion: reduce) {
  .ficha-grupo-chev { transition: none; }
}

/* ── EL MISMO TABLERO SIN LA COLUMNA DE PERMISOS (27/08/2026) ────────
   La ficha del proveedor usa la tabla del cliente, pero con SEIS columnas
   en vez de siete: al cliente le escribimos nosotros y hay que preguntarle;
   al proveedor le pedimos, y eso no se consiente.

   Sin esto, los anchos de siete columnas se aplicaban a seis y **la última
   —los tres puntos— se quedaba con el 24 % que era de «Puede recibir»**,
   dejando el correo estrujado. Un ancho que sobra en una columna vacía es
   un ancho que le falta al dato que sí se lee. */
.tabla-contactos.sin-permisos th:nth-child(1) { width: 24%; }  /* Persona      */
.tabla-contactos.sin-permisos th:nth-child(2) { width: 16%; }  /* Departamento */
.tabla-contactos.sin-permisos th:nth-child(3) { width: 14%; }  /* Teléfono     */
.tabla-contactos.sin-permisos th:nth-child(4) { width: 14%; }  /* WhatsApp     */
.tabla-contactos.sin-permisos th:nth-child(5) { width: 28%; }  /* Correo       */
.tabla-contactos.sin-permisos th:nth-child(6) { width: 4%;  }  /* Acciones     */

/* ── LOS BLOQUES DEL MENÚ LLEVAN RÓTULO (27/08/2026) ────────────────
   Se le enseñaron las tres opciones dibujadas —como estaba, separado por una
   raya, y con rótulos escritos— y Vladimir eligió la tercera. Yo recomendaba
   la raya sola por no gastar cuatro renglones en una columna estrecha; su
   argumento pesa más: **entra gente nueva** y el rótulo enseña de qué va cada
   bloque sin que nadie lo explique.

   **Y luego hicieron falta dos correcciones, las dos suyas:**

   **(1) «Se pierden dentro de todo».** Tenía razón: el rótulo salía en
   `--color-ink-soft` **con 0,75 de opacidad**, o sea el mismo gris de los
   enlaces y encima descolorido. Un rótulo que se lee igual que lo que rotula
   no rotula nada. Ahora va en la tinta del sistema, a plena opacidad y con
   más peso. **No se usa el verde a propósito**: en este menú el verde
   significa «estás aquí», y cuatro rótulos verdes competirían con el único
   sitio donde ese color tiene que significar algo.

   **(2) El rótulo va pegado a SU bloque, no flotando entre dos.** El aire
   va encima y no repartido: así se lee como el título de lo que viene
   debajo y no como una línea suelta entre dos grupos.

   **Y una hipótesis mía que resultó FALSA, anotada para que nadie la
   repita.** Vladimir dijo «como que arrastra a todo el menú», y di por
   hecho que los cuatro rótulos habían hecho la barra más alta que la
   pantalla —`.sidebar-nav` lleva `overflow-y: auto`— y que el navegador la
   desplazaba al entrar. **Medido en el navegador: no desborda.** Con la
   ventana en 620 px el contenido ocupa 372 y caben 460, y apretar el rótulo
   ahorra 12 px, no los 40 que yo suponía. La causa sigue sin encontrarse.
   El apretón se queda porque mejora la lectura, no porque arregle aquello. */
.sidebar-nav > .sidebar-rotulo {
  padding: var(--space-3) var(--space-3) 0;
  margin-top: var(--space-1);
  font-size: var(--text-2xs);
  font-weight: 800;
  letter-spacing: 0.09em;
  /* Sobre la tinta, el rótulo de grupo se ACLARA. Con `--color-ink` —que
     ahora ES el fondo— desaparecía por completo. */
  color: var(--color-menu-texto);
  opacity: 0.7;
  line-height: 1.2;
}

/* El primero no necesita aire por encima: lo de arriba es el borde. */
.sidebar-nav > .sidebar-rotulo:first-of-type {
  padding-top: var(--space-2);
  margin-top: 0;
}


/* ══ LAS PESTAÑAS DE SECCION (28/08/2026) ═════════════════════════════
   Las pantallas hermanas de la seccion, en la cabecera de la propia
   pantalla. Aparecen solas —las pinta `base.html`— en las 22 pantallas que
   pertenecen a una seccion.

   El motivo esta en `app/core/secciones.py`: al pasar los desplegables del
   menu a panel flotante, moverse DENTRO de una seccion paso de un clic a
   dos. Esto lo devuelve a uno y ademas las enseña sin abrir nada. */
.pestanas-seccion {
  display: flex;
  gap: 2px;
  margin: 0 0 var(--space-5);
  border-bottom: 1px solid var(--color-border);
  /* Siete pestañas no caben en una ventana estrecha. Se deslizan en su
     propia caja **y la pagina no se mueve** — que es la regla de las tablas
     anchas de este sistema, aplicada aqui. */
  overflow-x: auto;
  scrollbar-width: thin;
}

.pestanas-seccion a {
  padding: var(--space-3) var(--space-4);
  border-bottom: 2px solid transparent;
  color: var(--color-ink-soft);
  text-decoration: none;
  font-size: var(--text-sm);
  font-weight: 600;
  white-space: nowrap;
  /* La linea de abajo se pinta sobre el borde del contenedor, no debajo. */
  margin-bottom: -1px;
}

.pestanas-seccion a:hover {
  color: var(--color-ink);
  background: var(--color-fila-hover);
}

/* La pestaña de la pantalla en la que se esta. Lleva subrayado Y color, no
   solo color: en una fila de siete, el peso del texto solo no basta para
   encontrarla de un vistazo. */
.pestanas-seccion a.active {
  color: var(--color-primary-texto);
  border-bottom-color: var(--color-primary);
  font-weight: 700;
}

@media (max-width: 640px) {
  .pestanas-seccion a {
    padding: var(--space-3) var(--space-3);
  }
}


/* ══ EL TABLERO POR TEMAS (28/08/2026) ═══════════════════════════════
   Vladimir, enseñando ocho alertas de precio listadas fila a fila: «ese
   dashboard me arrastra todo eso y es demasiado». Lo que pidio: cuadros por
   tema, con color, y **limpio y sencillo**.

   Cada cuadro dice CUANTOS y como se reparten; los nombres estan dentro, al
   pulsar. Una lista no escala — ocho alertas ya sobraban.

   Los colores salen de los TOKENS del sistema, no escritos aqui: el modulo
   `app/core/tablero.py` devuelve el nombre —`danger`, `info`...— y esta hoja
   lo resuelve. El dia que cambie la identidad, cambian solos. */
.tablero-cuadros {
  display: grid;
  /* DOS COLUMNAS FIJAS, no `auto-fit`. El orden que pidio Vladimir el
     28/08/2026 son PAREJAS —clientes+cotizaciones, comunicaciones+tareas,
     solicitudes+precios— y cada pareja es un paso del negocio. Con
     `auto-fit` el navegador metia tres o cuatro por fila segun el ancho de
     la ventana y las parejas se deshacian: el cuadro de precios podia
     acabar al lado del de tareas, que no tienen nada que ver. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
  align-content: start;
}

/* En pantalla estrecha las parejas no caben, y forzarlas apretaria los
   numeros hasta partirlos. Se apilan en el mismo orden, que sigue leyendose
   como el flujo. */
@media (max-width: 720px) {
  .tablero-cuadros { grid-template-columns: 1fr; }
}

.cuadro {
  display: flex;
  flex-direction: column;
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
}

.cuadro:hover {
  /* Solo la sombra. El primer intento oscurecia el borde con un `#c3d2ea`
     inventado, y la paleta no tiene un paso entre `--color-border` y el
     gris oscuro: antes que meter un color fuera del sistema, se levanta la
     tarjeta, que es lo que ya hace el resto de la interfaz. */
  box-shadow: var(--shadow-sm);
}

/* La franja de color: es lo que distingue un tema de otro de un vistazo,
   antes de leer nada. */
.cuadro-franja {
  height: 3px;
  flex: 0 0 3px;
  background: var(--color-ink-soft);
}
.cuadro--danger  .cuadro-franja { background: var(--color-danger); }
.cuadro--warning .cuadro-franja { background: var(--color-warning); }
.cuadro--neutra  .cuadro-franja { background: var(--color-ink-soft); }
.cuadro--brand   .cuadro-franja { background: var(--color-brand); }
.cuadro--azul    .cuadro-franja { background: var(--color-azul); }

.cuadro-cuerpo {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  flex: 1;
}

.cuadro-tema {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--color-ink-soft);
}
.cuadro--danger  .cuadro-tema { color: var(--color-danger); }
.cuadro--warning .cuadro-tema { color: var(--color-warning); }
.cuadro--neutra  .cuadro-tema { color: var(--color-ink-soft); }
.cuadro--brand   .cuadro-tema { color: var(--color-brand); }
.cuadro--azul    .cuadro-tema { color: var(--color-azul); }

.cuadro-cifra {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
}

.cuadro-cifra b {
  /* De la escala, no escrito a mano: `--text-2xl` es el mayor que hay y es
     el que usan los titulos de pantalla. Un 30px suelto se sale del sistema
     y una prueba lo vigila. */
  font-size: var(--text-2xl);
  font-weight: 800;
  line-height: 1;
  /* Cifras de ancho fijo: sin esto, «11» y «88» ocupan distinto y los cinco
     cuadros bailan al recargar. */
  font-variant-numeric: tabular-nums;
}

.cuadro-cifra span {
  color: var(--color-ink-soft);
  font-size: var(--text-sm);
}

/* La barra: el reparto de un vistazo, antes de leer las cifras. */
.cuadro-barra {
  display: flex;
  height: 7px;
  border-radius: 99px;
  overflow: hidden;
  background: var(--color-gray-100);
}

.cuadro-barra i { display: block; height: 100%; }

.cuadro-reparto {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: var(--text-sm);
}

/* El rotulo de un grupo DENTRO del cuadro. Solo aparece donde hace falta
   —hoy en Comunicaciones— y por eso no se le da mucho peso: separa, no
   compite con el titulo del tema. */
.cuadro-grupo {
  margin-top: var(--space-1);
  font-size: var(--text-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-gray-500);
}

/* El primero no lleva aire arriba: ya lo pone la barra que tiene encima. */
.cuadro-grupo:first-child { margin-top: 0; }

.cuadro-fila {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--color-ink-soft);
}

.cuadro-fila i {
  width: 8px;
  height: 8px;
  flex: 0 0 8px;
  border-radius: 2px;
}

.cuadro-fila b {
  margin-left: auto;
  color: var(--color-ink);
  font-variant-numeric: tabular-nums;
}

/* Los tonos del reparto. Mismos tokens que la franja, para que la fila y el
   trozo de barra que le corresponde sean del mismo color. */
.tono-danger  { background: var(--color-danger); }
.tono-warning { background: var(--color-warning); }
.tono-success { background: var(--color-success); }

.tono-brand   { background: var(--color-brand); }
.tono-azul    { background: var(--color-azul); }
.tono-neutra  { background: var(--color-border); }

/* La conclusion: la frase que dice que hacer sin interpretar los numeros. */
.cuadro-pie {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: auto;
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-gray-100);
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

.cuadro-pie .icon {
  margin-left: auto;
  color: var(--color-gray-500);
  flex-shrink: 0;
}

/* ── VISTA PREVIA DE UN SEGUIMIENTO (29/08/2026) ──────────────────────
   Vladimir pidió ver el mensaje antes de mandarlo, «como en el cuadro de
   comunicaciones de cada cotización». Son DOS formas porque son dos
   medios: el correo trae su propio HTML y va aislado en un iframe; el
   WhatsApp es texto donde los saltos de línea SON el formato. */
.vista-previa-cabecera {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-border);
}

.vista-previa-correo {
  display: block;
  width: 100%;
  min-height: 460px;
  border: 0;
  background: var(--color-white);
}

/* `pre` para conservar los saltos: son lo que pone el enlace en su propia
   línea, que en un teléfono es lo que se puede tocar. Y `pre-wrap` para
   que un mensaje largo no obligue a desplazarse en horizontal. */
.vista-previa-whatsapp {
  margin: 0;
  padding: var(--space-4);
  white-space: pre-wrap;
  word-break: break-word;
  font-family: inherit;
  font-size: var(--text-base);
  line-height: 1.6;
  color: var(--color-ink);
}

/* ── EL RESUMEN DEL TRABAJO (29/08/2026) ──────────────────────────────
   Vladimir: «te da un resumen total de todo tu trabajo, por día, semana y
   mes». Cuenta lo que SALIÓ; el rastreo no entra en esta pantalla. */
.resumen-envios {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}

.resumen-cifra {
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
}

.resumen-rot {
  display: block;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--color-ink-soft);
}

.resumen-num {
  display: block;
  font-size: var(--text-2xl);
  font-weight: 700;
  line-height: 1.2;
  /* Los dígitos alineados: son cifras que se comparan de un vistazo. */
  font-variant-numeric: tabular-nums;
}

.resumen-pie {
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

/* Lo que espera una decisión se distingue de lo que solo informa. */
.resumen-espera .resumen-num { color: var(--color-warning); }

/* La columna de las casillas: estrecha y sin empujar al texto. */
.col-marca { width: 38px; padding-right: 0; }

/* El pie de tabla pasa a llevar una acción, no solo el conteo. */
.tabla-pie { display: flex; align-items: center; gap: var(--space-3); }
.tabla-pie .btn { margin-left: auto; }

/* ── CABECERA DE UNA SECCIÓN DENTRO DE UN PANEL (29/08/2026) ──────────
   Vladimir, al ver los rótulos «Por WhatsApp», «Por correo» y «Salidas
   automáticas»: «estos 3 textos caen demasiado hacia la izquierda, debes
   moverlo mas a la derecha».

   Y era literal: la clase se usaba en la plantilla pero **no existía en
   esta hoja**, así que la cabecera no tenía ningún relleno y quedaba
   pegada al borde mientras la tabla de debajo sí lo tenía. El mismo fallo
   que los iconos inventados: un nombre que no existe no da error, solo
   deja de hacer efecto.

   El relleno lateral es el de `.tabla-barra`, que es lo que va justo
   debajo: así el título, el filtro y la tabla arrancan en la misma línea
   vertical. */
/* **Solo DENTRO de un panel.** El rótulo «Aprobaciones» va fuera de uno y
   tiene que alinearse con el título de la página, no con la tabla:
   indentarlo lo dejaría descolgado respecto al h1 que tiene encima. */
.tabla-panel > .op-cab-compacta {
  padding: var(--space-4) var(--space-4) var(--space-3);
}

/* Fuera del panel, solo separa del bloque anterior. */
.op-cab-compacta {
  margin-bottom: var(--space-3);
}

/* El título de sección es menor que el de la página: es un rótulo dentro
   de una pantalla, no el nombre de la pantalla. */
.op-cab-compacta h2,
.op-cab-compacta h3 {
  margin: 0 0 var(--space-1);
}

/* ── EL TÍTULO DE UN CUADRO MIDE LO MISMO SEA h2 O h3 (14/09/2026) ────
   Vladimir, sobre Aprobaciones: «valida que los 3 titulos tenga el mismo
   tamano de texto».

   Aquí solo se fijaba el MARGEN, así que el tamaño lo decidía el valor por
   defecto del navegador: 1.5em para `h2` y 1.17em para `h3`. Dos cuadros
   hermanos se veían distintos por su etiqueta, que es una decisión de
   ESTRUCTURA y no de jerarquía visual — y por eso la etiqueta no se tocó:
   «Salidas automáticas» no cuelga de «Aprobaciones».

   20 px es lo que la escala ya declaraba para «títulos de tarjeta», que es
   exactamente lo que son estos. Solo DENTRO de un panel: el rótulo de
   segmento que va suelto encima sigue por encima de ellos, que es la
   jerarquía que él pidió el 29/08. */
.tabla-panel > .op-cab-compacta h2,
.tabla-panel > .op-cab-compacta h3 {
  font-size: var(--text-xl);
  font-weight: 600;
  line-height: 1.3;
}

.op-cab-compacta .subtitle {
  margin: 0;
}

/* ── LA TASA DE CIERRE, CON SU ANILLO (29/08/2026) ────────────────────
   Un porcentaje suelto se lee; un anillo se ENTIENDE sin leer, porque se
   ve cuánto falta. Es lo que la referencia de Vladimir enseña primero. */
.tasa-cierre {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-top: var(--space-2);
  padding-bottom: var(--space-2);
  border-top: 1px solid var(--color-gray-100);
}

/* El anillo se dibuja con un degradado cónico: el porcentaje entra por la
   variable `--pct`, así que la plantilla no escribe ni un color. */
.tasa-dona {
  flex: 0 0 52px;
  width: 52px;
  height: 52px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: conic-gradient(var(--color-azul) 0 var(--pct),
                             var(--color-border) var(--pct) 100%);
}

.tasa-dona i {
  width: 38px;
  height: 38px;
  border-radius: 50%;
  background: var(--color-white);
  display: grid;
  place-items: center;
  font-style: normal;
  font-size: var(--text-xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.tasa-cuerpo { flex: 1; min-width: 0; }

.tasa-fila {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
}

/* ── LOS ACCESOS DEL CARRIL (29/08/2026) ──────────────────────────────
   Cuatro destinos que se pulsan a diario. El color no es adorno: los hace
   reconocibles antes de leer la palabra, y es el MISMO que ya identifica a
   esa sección en el resto del sistema, así que no hay nada que aprender. */
.rail-accesos {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
}

.acceso {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  font-size: var(--text-sm);
  font-weight: 600;
  text-decoration: none;
}

.acceso:hover { filter: brightness(0.97); }
.acceso:focus-visible { outline: 2px solid var(--color-azul); outline-offset: 2px; }

.acceso--azul   { background: var(--color-azul-bg);    color: var(--color-azul); }
.acceso--rojo   { background: var(--color-danger-bg);  color: var(--color-danger); }
.acceso--verde  { background: var(--color-primary-bg); color: var(--color-primary-texto); }
.acceso--morado { background: var(--color-morado-bg);  color: var(--color-morado); }


/* ══════════════════════════════════════════════════════════════════════
   EL CUADRO ANCHO DEL TABLERO (Vladimir, 29/08/2026)
   ══════════════════════════════════════════════════════════════════════

   «Hazme una caja que abarque las 2 columnas y que sea de aprobaciones,
   que refleje el estado de los mensajes en automatico y manuales y
   colocalo arriba, de primero».

   **No es un componente nuevo: es el mismo `.cuadro` con el doble de
   ancho.** Mismos colores, misma franja, mismo clic. Lo unico que cambia
   es como se reparte lo de dentro: con 2x de ancho y la misma altura, lo
   que estaba en columna se pone EN FILA. Apilado, un cuadro ancho deja
   dos tercios de papel vacio a la derecha y el ojo lo lee como un error
   de maquetacion, no como enfasis.

   Y esto es lo que le da sentido a que Aprobaciones sea el ancho: es el
   unico cuadro con DOS grupos que se leen a la vez —lo que espera tu
   visto bueno y lo que sale solo—, y en fila caben uno al lado del otro
   en vez de uno debajo del otro. */
.cuadro--ancho {
  grid-column: 1 / -1;
}

/* ══ AQUI HUBO UNA REGLA DE PARIDAD, Y ERA UNA TRAMPA ══
   Decia «el ultimo, si queda impar, que se estire». No disparaba nunca:
   `nth-child` cuenta HIJOS, y la caja ancha es un hijo mas, asi que con
   seis cuadros el ultimo cae en posicion PAR. El hueco seguia ahi y la
   regla parecia estar cubriendolo.

   Ahora quien es ancho se dice en los datos (`Cuadro.ancho`), que no
   depende de cuantos cuadros haya ni de en que orden se lean. */

.cuadro--ancho .cuadro-cuerpo {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
  align-items: start;
  column-gap: var(--space-6);
  row-gap: var(--space-2);
}

/* Columna izquierda: el tema, la cifra, la barra y la conclusion — la
   lectura rapida completa, sin moverse de sitio respecto a los demas
   cuadros. */
.cuadro--ancho .cuadro-tema,
.cuadro--ancho .cuadro-cifra,
.cuadro--ancho .cuadro-barra,
.cuadro--ancho .cuadro-pie {
  grid-column: 1;
}

/* Columna derecha: el desglose, en dos bloques uno al lado del otro.
   `align-content: start` evita que dos grupos de distinto largo se
   estiren y dejen de alinearse por arriba. */
/* **`display: contents` es lo que hace que agrupar no cambie nada.** El
   envoltorio de cada grupo desaparece de la maquetacion y sus filas caen
   directas en la columna del reparto, exactamente como cuando la lista era
   plana. Los cinco cuadros normales se pintan igual que antes. */
.cuadro-col {
  display: contents;
}

/* En el ancho SI es una caja: cada grupo pasa a ser una celda de la
   rejilla, y por eso quedan lado a lado. */
.cuadro--ancho .cuadro-col {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.cuadro--ancho .cuadro-reparto {
  grid-column: 2;
  grid-row: 1 / -1;
  display: grid;
  /* Columnas de ancho ACOTADO, pegadas a la izquierda. Con `1fr` un
     grupo solo se estiraba a toda la mitad derecha, y como la cifra va
     alineada a la derecha (`margin-left: auto`) quedaba a un palmo de su
     etiqueta. Le pasa a Lista de precios, que no agrupa nada. */
  grid-template-columns: repeat(auto-fit, minmax(170px, 230px));
  justify-content: start;
  align-content: start;
  column-gap: var(--space-5);
}

.cuadro--ancho .cuadro-grupo {
  margin-top: 0;
}

/* En pantalla estrecha no hay dos columnas que abarcar: vuelve a ser un
   cuadro normal y se apila, que es como se lee bien en un telefono. */
@media (max-width: 720px) {
  .cuadro--ancho .cuadro-cuerpo {
    display: flex;
    flex-direction: column;
  }
  /* **`display: contents` es lo que hace que agrupar no cambie nada.** El
   envoltorio de cada grupo desaparece de la maquetacion y sus filas caen
   directas en la columna del reparto, exactamente como cuando la lista era
   plana. Los cinco cuadros normales se pintan igual que antes. */
.cuadro-col {
  display: contents;
}

/* En el ancho SI es una caja: cada grupo pasa a ser una celda de la
   rejilla, y por eso quedan lado a lado. */
.cuadro--ancho .cuadro-col {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.cuadro--ancho .cuadro-reparto {
    display: flex;
    flex-direction: column;
  }
}


/* La linea de «Mostrando N de M» con una accion al final. La accion
   pertenece al RESULTADO —imprime lo que el filtro dejo—, no a la
   pantalla, y por eso viaja con la cuenta y no con los botones de
   arriba. */
/* Y combinado con `.resumen-de-la-lista` es la fila «cuenta + acción»:
   el aire de una y el reparto de la otra. El margen lateral lo pone la
   primera, así que aquí no se toca. */
.subtitle--con-accion {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
}



/* ══════════════════════════════════════════════════════════════════════
   LA HOJA DE TRABAJO DEL CATALOGO (Vladimir, 30/08/2026)
   ══════════════════════════════════════════════════════════════════════

   No es una tabla de pantalla impresa: es un papel para salir a pedir
   precios y anotarlos a mano. De ahi las dos columnas vacias con su linea
   y el tamano de letra mas apretado — caben 34 renglones por hoja, que es
   lo que cuenta el aviso de «unas N hojas». */
.hoja-imprimible {
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
}

.hoja-cab {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-5);
  padding-bottom: var(--space-4);
  margin-bottom: var(--space-4);
  border-bottom: 2px solid var(--color-ink);
}

.hoja-empresa {
  margin: 0;
  font-size: var(--text-sm);
  font-weight: 700;
  color: var(--color-ink);
}

.hoja-tit {
  margin: 2px 0 0;
  font-size: var(--text-xl);
  font-weight: 700;
  color: var(--color-ink);
}

.hoja-filtros {
  margin: 4px 0 0;
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

.hoja-meta {
  text-align: right;
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

.hoja-meta p { margin: 0; }

.hoja-tabla {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}

.hoja-tabla th {
  text-align: left;
  padding: 6px 8px;
  border-bottom: 1px solid var(--color-ink);
  font-size: var(--text-2xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-ink-soft);
}

.hoja-tabla td {
  padding: 7px 8px;
  border-bottom: 1px solid var(--color-border);
  vertical-align: top;
}

.hoja-n {
  width: 40px;
  text-align: right;
  color: var(--color-gray-500);
  font-variant-numeric: tabular-nums;
}

.hoja-emp {
  width: 90px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

/* Las columnas para escribir. El ancho es el del boligrafo, no el del
   dato: si fueran estrechas no cabria el nombre de un proveedor. */
.hoja-anotar { width: 150px; }

.hoja-nombre { display: block; font-weight: 600; color: var(--color-ink); }

.hoja-seg {
  display: block;
  font-size: var(--text-2xs);
  color: var(--color-gray-500);
}


/* ══════════════════════════════════════════════════════════════════════
   ROLES Y PERMISOS (30/08/2026)
   ══════════════════════════════════════════════════════════════════════ */

/* Las tarjetas de rol. Se leen antes que la matriz porque contestan la
   pregunta facil —que es cada rol y cuanta gente lo tiene— y dejan la
   matriz para la dificil. */
.roles-fila {
  display: grid;
  gap: var(--space-3);
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  margin-bottom: var(--space-5);
}

.rol-tarjeta {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-4);
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}

/* El que no se puede editar se distingue por el FONDO, no por un borde de
   color: un borde de color en una tarjeta significa "esto pide atencion"
   en el resto del sistema, y aqui no pide nada — solo esta cerrado. */
.rol-tarjeta--fijo { background: var(--color-surface); }

.rol-nombre {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  font-size: var(--text-base);
  font-weight: 700;
  color: var(--color-ink);
}

.rol-nombre .icon { color: var(--color-azul); }

.rol-desc {
  flex: 1 1 auto;
  margin: 0;
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

.rol-pie {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-border);
  font-size: var(--text-sm);
}

.rol-gente {
  color: var(--color-ink);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

/* ── La matriz ── */
.matriz-permisos th.matriz-col,
.matriz-permisos td.matriz-col {
  text-align: center;
  width: 120px;
}

.matriz-sub {
  display: block;
  font-weight: 400;
  font-size: var(--text-2xs);
  color: var(--color-gray-500);
}

/* El grupo NO es una fila de datos: no se resalta al pasar el raton, que
   prometeria que se abre. Misma razon que `tbody tr[data-abrir]`. */
.matriz-grupo td {
  background: var(--color-surface);
  font-size: var(--text-2xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-gray-500);
}

.matriz-titulo {
  display: block;
  font-weight: 600;
  color: var(--color-ink);
}

/* La explicacion no es adorno: quien mueve un interruptor tiene que poder
   saber que apaga sin abrir el codigo. */
.matriz-explica {
  display: block;
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

/* ── El interruptor ──
   Es un `checkbox` de verdad, disfrazado. Asi viaja solo en el formulario,
   lo lee un lector de pantalla y funciona con el teclado sin una linea de
   JavaScript. Un `div` con `onclick` no hace ninguna de las tres. */
.interruptor {
  appearance: none;
  -webkit-appearance: none;
  width: 40px;
  height: 23px;
  margin: 0;
  border: 0;
  border-radius: var(--radius-pill);
  background: var(--color-border);
  cursor: pointer;
  position: relative;
  vertical-align: middle;
  transition: background 0.14s ease;
}

.interruptor::after {
  content: "";
  position: absolute;
  top: 2.5px;
  left: 2.5px;
  width: 18px;
  height: 18px;
  border-radius: var(--radius-full);
  background: var(--color-white);
  box-shadow: 0 1px 2px rgba(22, 32, 58, 0.3);
  transition: transform 0.14s ease;
}

.interruptor:checked { background: var(--color-primary); }
.interruptor:checked::after { transform: translateX(17px); }

.interruptor:focus-visible {
  outline: 2px solid var(--color-azul);
  outline-offset: 2px;
}

/* Tocado y sin guardar. El color de aviso y no el verde: lo que dice es
   «esto todavia no esta hecho», no «esto esta bien». */
.interruptor--tocado {
  box-shadow: 0 0 0 3px var(--color-caution);
}

.interruptor--fijo {
  cursor: not-allowed;
  opacity: 0.6;
}

/* ── La barra de cambios sin guardar ──
   Fija abajo: la matriz es mas alta que la pantalla, y un boton al final
   del documento obligaria a bajar hasta el fondo para guardar un cambio
   hecho en la primera fila. */
.barra-guardar {
  position: sticky;
  bottom: 0;
  z-index: 3;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-top: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--color-ink);
  color: var(--color-white);
  border-radius: var(--radius-md);
}

.barra-guardar[hidden] { display: none; }

.barra-guardar-txt { font-size: var(--text-sm); }
.barra-guardar-txt b { font-variant-numeric: tabular-nums; }

.barra-guardar-acc {
  display: flex;
  gap: var(--space-2);
}

/* Los botones sobre la tinta: el secundario en blanco no se leeria. */
.barra-guardar .btn-secundario {
  background: transparent;
  color: var(--color-white);
  border-color: rgba(255, 255, 255, 0.35);
}

.barra-guardar .btn-secundario:hover {
  background: rgba(255, 255, 255, 0.12);
}


/* ══════════════════════════════════════════════════════════════════════
   EL ESPACIO COMPARTIDO (30/08/2026)
   ══════════════════════════════════════════════════════════════════════ */

/* La ruta de carpetas. NO es la miga de pan de la aplicacion —esa dice en
   que seccion estas— sino donde estas DENTRO del espacio. Se parecen y son
   dos cosas: una lleva a Administracion, la otra a la carpeta de abril. */
/* El campo de archivo sin su boton: lo dispara otro control —una zona de
   arrastrar y soltar, un boton de la cabecera—. **`display: none` y no
   `hidden` a secas**: el atributo lo pisa cualquier regla con mas peso, y
   un `<input type=file>` reapareciendo suelto en medio de la pantalla es
   feo y confunde. */
.campo-archivo--oculto { display: none; }

.ruta-espacio {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
  font-size: var(--text-sm);
}

.ruta-espacio a { color: var(--color-azul); text-decoration: none; font-weight: 500; }
.ruta-espacio a:hover { text-decoration: underline; }
.ruta-espacio b { font-weight: 600; color: var(--color-ink); }
.ruta-sep { color: var(--color-gray-500); }

/* Los nombres de carpeta llevan su icono pegado al texto. */
.identidad-principal .icon { vertical-align: -2px; margin-right: 4px; color: var(--color-azul); }

/* ── La zona de soltar ── */
.soltar-espacio {
  margin-top: var(--space-4);
  padding: var(--space-6);
  text-align: center;
  background: var(--color-white);
  border: 2px dashed var(--color-border);
  border-radius: var(--radius-lg);
}

/* Al arrastrar encima. **Cambia el borde Y el fondo**: solo el borde se ve
   poco cuando el cursor lleva un archivo tapando media pantalla. */
.soltar-espacio.soltar-encima {
  border-color: var(--color-primary);
  background: var(--color-primary-bg);
}

.soltar-tit { margin: 0 0 2px; font-size: var(--text-base); font-weight: 600; }
.soltar-sub { margin: 0 0 var(--space-4); font-size: var(--text-sm); color: var(--color-ink-soft); }
.soltar-acc { display: flex; gap: var(--space-2); justify-content: center; flex-wrap: wrap; }

/* ── La subida en curso ── */
.subida-fila {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
  border-bottom: 1px solid var(--color-border);
  font-size: var(--text-sm);
}

.subida-fila:last-child { border-bottom: 0; }

/* El nombre se recorta; lo que no puede recortarse es el ESTADO, que es
   el dato por el que se mira esta lista. */
.subida-nombre {
  flex: 1 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.subida-barra {
  flex: 0 0 120px;
  height: 6px;
  border-radius: var(--radius-pill);
  background: var(--color-border);
  overflow: hidden;
}

.subida-barra i { display: block; height: 100%; width: 0; background: var(--color-primary); }

.subida-estado {
  flex: 0 0 auto;
  max-width: 45%;
  text-align: right;
  font-size: var(--text-xs);
  color: var(--color-gray-500);
}

.subida-ok { color: var(--color-primary); font-weight: 600; }

/* El que no entro se ve en rojo Y se queda en pantalla: la lista no se
   recarga si algo fallo, porque recargar borraria la unica explicacion. */
.subida-mal { color: var(--color-danger); font-weight: 600; }


/* ══════════════════════════════════════════════════════════════════════
   REGISTRO DE ACTIVIDAD (30/08/2026)
   ══════════════════════════════════════════════════════════════════════ */

.col-hora {
  width: 68px;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* ── La separacion por persona ──
   Vladimir pidio la pantalla «horizontal como todas las demas», asi que la
   separacion no puede ser una tarjeta: es un RENGLON dentro de la misma
   tabla. Con una tabla por persona se pierde el orden del dia, que es la
   mitad de lo que se viene a mirar aqui. */
.fila-persona td {
  background: var(--color-surface);
  border-top: 1px solid var(--color-border);
  padding: var(--space-2) var(--space-4);
}

/* **No se resalta al pasar el raton**, aunque sea una fila. La regla del
   sistema dice que el resaltado promete «esto se abre», y un rotulo no
   abre nada: señalarlo seria mentir. */
.fila-persona:hover td { background: var(--color-surface); cursor: default; }

.persona-linea {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  flex-wrap: wrap;
}

/* Quien no hizo nada: la MISMA fila, en gris. No un cuadro aparte —eso
   hacia pensar que eran dos cosas distintas y son la misma: gente, con o
   sin movimientos— pero tampoco al mismo peso, porque debajo no viene
   nada y el ojo no tiene que buscarlo. */
.fila-persona--quieta td { background: var(--color-white); }
.fila-persona--quieta .persona-linea b,
.fila-persona--quieta .persona-linea .resumen b { color: var(--color-gray-500); }

.persona-linea b { font-size: var(--text-base); }
.persona-linea .rol { font-size: var(--text-sm); color: var(--color-gray-500); }

/* El resumen a la derecha del nombre: el numero macro y el detalle en la
   misma linea, para no tener que mirar en dos sitios. */
.persona-linea .resumen {
  margin-left: auto;
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
  font-variant-numeric: tabular-nums;
}

.persona-linea .resumen b { color: var(--color-ink); }

/* El dia, solo cuando hay mas de uno. Mas fuerte que el rotulo de persona
   porque esta por encima en la jerarquia: dia > persona > movimiento. */
.fila-dia td {
  background: var(--color-ink);
  color: var(--color-white);
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-2xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.fila-dia:hover td { background: var(--color-ink); cursor: default; }

/* ============================================================
   ADJUNTOS EN EL CHAT (01/09/2026)
   ============================================================
   La foto que manda un cliente es un pedido, así que se ve a
   tamaño legible dentro de la burbuja y se abre entera al
   pulsarla. Alto máximo para que una foto vertical de teléfono
   no empuje el resto de la conversación fuera de la pantalla. */
.chat-foto { display: block; margin-bottom: 6px; }
.chat-foto img {
  display: block;
  max-width: 100%;
  max-height: 320px;
  border-radius: 10px;
  border: 1px solid var(--linea);
}
.chat-audio { display: block; width: 100%; max-width: 280px; margin-bottom: 6px; }
.chat-archivo {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 6px;
  padding: 6px 10px;
  border: 1px solid var(--linea);
  border-radius: 10px;
  background: var(--superficie);
  font-weight: 600;
  text-decoration: none;
}
.chat-archivo--espera { font-weight: 500; opacity: .75; }
.chat-marca { margin-left: 6px; }

/* **El mensaje que no llegó, en la conversación** (08/09/2026). Va en rojo
   y con icono porque compite con «Enviado» y «Leído», que son verdes y
   neutros: si se pareciera a ellos, quien atiende leería la fila entera
   como «salió» y seguiría adelante. */
.chat-no-llego {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  margin-left: 6px;
  color: var(--color-danger);
  font-weight: 600;
  cursor: help;
}
.chat-descargar {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-bottom: 6px;
  font-size: .78rem;
  font-weight: 600;
  opacity: .8;
  text-decoration: none;
}
.chat-descargar:hover { opacity: 1; text-decoration: underline; }

/* **Ver el correo tal cual llego.** Va DEBAJO del texto, discreto: el hilo
   es para leer, y el diseno del remitente es la excepcion que se pide. */
.chat-original {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  margin-top: 8px;
  font-size: .78rem;
  font-weight: 600;
  opacity: .75;
  text-decoration: none;
}
.chat-original:hover { opacity: 1; text-decoration: underline; }

/* ============================================================
   ENLACES DE UNA FICHA  (Vladimir, 13/09/2026)

   «esos botones, pueden llevar color?» — si, pero TENIDO, no solido.
   Vladimir vio la version solida: «estan muy chillones». Y ademas el
   solido competia con «Solicitar cotizacion», que es la accion que de
   verdad importa en esa pantalla: aqui el color dice IMPORTANCIA, y dos
   botones fuertes en la misma vista es no decir nada.

   Cada marca en claro: se reconoce igual de rapido, no grita, y la ficha
   sigue pareciendo del mismo software que el resto.
   ============================================================ */
.btn-enlace {
  border: 1px solid var(--linea, #d8e1f2);
  background: var(--color-superficie, #fff);
  color: var(--color-tinta);
}
.btn-enlace:hover { filter: brightness(.97); }
.enl-web  { background: var(--enlace-web-fondo); border-color: var(--enlace-web-linea); color: var(--enlace-web-texto); }
.enl-fb   { background: var(--enlace-fb-fondo); border-color: var(--enlace-fb-linea); color: var(--enlace-fb-texto); }
.enl-ig   { background: var(--enlace-ig-fondo); border-color: var(--enlace-ig-linea); color: var(--enlace-ig-texto); }
.enl-maps { background: var(--enlace-maps-fondo); border-color: var(--enlace-maps-linea); color: var(--enlace-maps-texto); }
.enl-waze { background: var(--enlace-waze-fondo); border-color: var(--enlace-waze-linea); color: var(--enlace-waze-texto); }
.enl-catalogo { background: var(--enlace-catalogo-fondo); border-color: var(--enlace-catalogo-linea); color: var(--enlace-catalogo-texto); }
.enl-portal { background: var(--enlace-portal-fondo); border-color: var(--enlace-portal-linea); color: var(--enlace-portal-texto); }

/* ============================================================
   MOVER CARPETAS (01/09/2026)
   ============================================================
   Solo existe dentro del modo mover. En la vista normal no hay
   ningún icono de más — que es lo que Vladimir señaló como feo
   de hacerlo con flechas siempre visibles. */
.fila-movible { cursor: grab; }
.fila-movible--viajando { opacity: .45; }
.asidero {
  display: inline-flex;
  margin-right: 6px;
  opacity: .55;
  cursor: grab;
}
.asidero:active { cursor: grabbing; }
/* La estrella de fijada: presente pero callada. Marca la excepción,
   no compite con el nombre de la carpeta. */
.pin-fijada {
  display: inline-flex;
  margin-left: 6px;
  color: var(--acento, #24a40c);
  opacity: .8;
}

/* Separa el bloque de carpetas fijadas del resto. Es una raya y no un
   título: un encabezado «Fijadas» ocuparía una fila entera para decir lo
   que la propia posición ya dice, y aquí lo que hace falta es que se vea
   dónde termina lo que alguien puso a propósito. */
tr.fila-separadora td {
  padding: 0;
  height: 0;
  border-bottom: 2px solid var(--linea);
}

/* Los títulos que agrupan las carpetas fijadas y el resto. Van en la
   propia tabla y no encima de ella: así el orden se lee donde está la
   lista, sin tener que mirar a otro sitio. */
tr.fila-titulo td {
  padding: 10px 12px 4px;
  font-size: .72rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--texto-suave);
  background: transparent;
}
tr.fila-titulo.fila-separadora td { border-top: 2px solid var(--linea); }

/* De qué listas sale la audiencia de una campaña (03/09/2026).
   Vladimir: «cuando eliges una o más listas te indica, además de la
   cantidad, a cuáles listas se envió».

   Va DEBAJO del rótulo y ENCIMA de las cifras, en tinta suave: es el
   contexto de los números, no un número más. Sin `padding` propio heredaría
   el del rótulo y quedaría pegado al borde. */
.rail-sub {
  margin: calc(var(--space-3) * -1) 0 var(--space-3);
  padding: var(--space-2) var(--space-4) 0;
  font-size: var(--text-sm);
  color: var(--color-text-soft);
}

/* ── El aviso de cliente duplicado (04/09/2026) ────────────────────
   Vladimir: «desde que registre un dato duplicado, lo que debe hacer es
   mostrar el cliente que está duplicando, mostrar al que pertenecen esos
   datos».

   Va sobre el formulario, así que ocupa el ancho entero y se separa de la
   rejilla: metido dentro heredaría las dos columnas y el aviso saldría
   partido por la mitad. */
.aviso-duplicado { margin-bottom: var(--space-4); }
.lista-coincidencias {
  margin: calc(var(--space-2) * -1) 0 var(--space-2);
  padding: 0 0 0 var(--space-4);
  list-style: none;
}
.lista-coincidencias li {
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-gray-100);
}
.lista-coincidencias li:last-child { border-bottom: 0; }
/* El nombre y el segmento en la misma línea; el porqué debajo, más pequeño:
   lo primero identifica al cliente, lo segundo explica por qué salió. */
.lista-coincidencias .dato-apagado { margin-left: var(--space-2); }
.lista-coincidencias .dec-sub { display: block; }

/* ══════════════════════════════════════════════════════════════════════
   LA COLUMNA DEL CLIENTE, MAS ANCHA Y SIN SCROLL (Vladimir, 05/09/2026)
   ══════════════════════════════════════════════════════════════════════

   «Trata de hacerle un poco más de espacio al nombre del cliente, creo que
   es la columna que menos espacio tiene, pero haz eso sin que tengamos que
   usar scroll después.»

   Tenía razón: «BELLO LAB SRL» caía en dos líneas y «LABORATORIO…» salía
   cortado, mientras la fila entera iba holgada.

   ══ LO QUE SE MIDIÓ, PORQUE A OJO SALE MAL ══

   El primer intento fue darle un mínimo de 16 rem, y **eso creó el scroll
   que él pidió evitar**: la tabla pasó de 1090 a 1131 px en una caja de
   1090. Medido en el navegador, no supuesto.

   Y al medir apareció el motivo: **la tabla no tenía holgura**. Ocupaba
   exactamente el ancho disponible y ninguna columna sobraba — ni siquiera
   la de acciones, que son dos botones de 30 px y su relleno.

   Así que el espacio no salió de otra columna: salió del RELLENO. Con 8 px
   por lado en vez de 12, la tabla sigue midiendo 1090 y el reparto
   automático le da lo liberado al nombre:

       nombre del cliente:  215 px  ->  247 px   (+15 %)
       ancho de la tabla:  1090 px  -> 1090 px   (sin scroll)

   Va acotado a esta tabla: `numero`, `col-fecha` y `col-persona` las
   comparten cinco tablas más, y estrecharlas en todas sería cambiar seis
   pantallas para arreglar una. */
.tabla-cotizaciones th,
.tabla-cotizaciones td {
  padding-left: 8px;
  padding-right: 8px;
}

.tabla-cotizaciones th.col-quien,
.tabla-cotizaciones td.col-quien {
  /* Un SUELO, no un ancho: si mañana crece el contenido de otra columna, el
     nombre no vuelve a ser el primero en encogerse. No fuerza nada hoy
     —mide 247 con o sin esto— y en una pantalla estrecha sigue cediendo. */
  min-width: 15rem;
}

/* Las que ya no crecen —fecha, importe y persona, todas con `nowrap`— se
   encogen a su contenido para que lo sobrante vaya al nombre. */
/* `td.numero` y `th.numero` ya no hacen falta aquí: desde el 05/09 la regla
   general los encoge. Quedan fecha y persona, que son de esta tabla. */
.tabla-cotizaciones td.col-fecha,
.tabla-cotizaciones th.col-fecha,
.tabla-cotizaciones td.col-persona,
.tabla-cotizaciones th.col-persona {
  width: 1%;
}

/* ══════════════════════════════════════════════════════════════════════
   UN MENSAJE DE WHATSAPP NO ES SOLO SU TEXTO (Vladimir, 05/09/2026)
   ══════════════════════════════════════════════════════════════════════

   «En este mensaje de whatsapp no veo el link para que descargue el mismo
   o es que viaja adjunto?»

   El enlace SÍ viajaba —es el botón que Meta aprobó con la plantilla— pero
   la vista previa pintaba solo el cuerpo. Un mensaje enseñado a medias es
   peor que ninguno: se aprueba lo que no se ha visto.

   Aquí van las dos piezas que faltaban, con la forma que tienen en el
   teléfono: el pie en gris pequeño y el botón separado por una línea. No es
   adorno — es lo que hace que se reconozca como «esto es el botón». */
.pie-del-mensaje {
  margin: 0;
  padding: 0 var(--space-4) var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

.boton-del-mensaje {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  margin: 0 var(--space-4) var(--space-3);
  padding: var(--space-2);
  border-top: 1px solid var(--color-border);
  color: var(--color-whatsapp-texto);
  font-weight: 600;
}

/* La dirección, tal cual, para poder comprobarla de un vistazo: en una URL
   la diferencia entre la buena y la mala es un carácter. */
.dato-monoespaciado {
  font-family: ui-monospace, "Cascadia Code", Consolas, monospace;
  word-break: break-all;
}


/* ══════════════════════════════════════════════════════════════════════
   TARJETAS DE CIFRA — un componente del sistema, no del Centro de correos
   ══════════════════════════════════════════════════════════════════════

   Vivían en `correos.css`, que **solo se carga en el Centro de correos**.
   La segunda pantalla que las usó —Consumo y costos de WhatsApp— se pintó
   sin estilo: las cifras salieron una debajo de otra, como texto suelto.

   Un componente que usan dos módulos va en la hoja común. La regla de
   `correos.css` sigue siendo buena para lo que SÍ es suyo —la retícula de
   esa pantalla—, y por eso solo se mudó esto. */
/* ── Las cifras ── */
.tarjetas-metrica {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 1px;
  background: var(--color-border);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  overflow: hidden;
  margin-bottom: var(--space-4);
}
.metrica { background: var(--color-white); padding: var(--space-4); }
.metrica-rot { display: block; color: var(--color-text-muted); font-size: var(--text-sm); }
.metrica-val {
  display: block;
  font-size: var(--text-2xl);
  font-weight: 700;
  line-height: 1.15;
  margin-top: var(--space-1);
  /* Los dígitos alineados: es una tabla de cifras aunque no lo parezca. */
  font-variant-numeric: tabular-nums;
}
.metrica-pie { display: block; color: var(--color-text-muted); font-size: var(--text-xs); }
.metrica--bien .metrica-val { color: var(--color-primary-dark); }

/* ══════════════════════════════════════════════════════════════════════
   EL RESUMEN DE UNA BANDEJA, Y SU BUSCADOR (Vladimir, 05/09/2026)
   ══════════════════════════════════════════════════════════════════════

   «Colócalo mejor, sácalo más a la derecha y bájalo un poco.»

   La línea «58 conversaciones · 37 esperan respuesta» iba pegada al filtro
   y al borde izquierdo, justo encima del rótulo «REMITENTE» — dos textos
   pequeños alineados en la misma vertical, compitiendo. A la derecha y con
   aire se lee como lo que es: **el pie del filtro**, no un segundo
   subtítulo de la pantalla. */
.resumen-de-la-lista {
  margin: var(--space-3) var(--space-4) var(--space-2);
  /* **A la izquierda** (Vladimir, 05/09/2026: «estos textos a la izquierda,
     si te dije a la derecha fue un error»). Lo que hacía falta era el aire
     —separarlo del filtro y del rótulo de la primera columna—, no el lado.
     Alineado a la izquierda arranca donde arranca la lectura de la tabla. */
  text-align: left;
  color: var(--color-ink-soft);
  font-size: var(--text-sm);
  /* Las cifras alineadas: se comparan de un vistazo entre visitas. */
  font-variant-numeric: tabular-nums;
}

/* El buscador de la barra de filtros. Ancho suficiente para un correo
   entero —que es lo que la gente pega— sin comerse la fila de filtros. */
.campo-buscar {
  min-width: 15rem;
  flex: 1 1 15rem;
  max-width: 22rem;
}


/* ══ LOS NUMEROS DE PAGINA (Vladimir, 07/09/2026) ══
   «Puede tambien mostrar las paginas hasta un rango, asi uno no tiene que
   estar dando tantos clicks y puedo ir directamente hacia una pagina
   especifica que me haya quedado.»

   Con doce paginas y solo dos flechas, volver a la 9 desde la 4 son cinco
   clics. Los numeros van entre las dos flechas, que es donde se buscan. */
.paginacion-numeros {
  display: flex;
  align-items: center;
  gap: 2px;
  flex-wrap: wrap;
  justify-content: center;
}

.paginacion-numero,
.paginacion-actual {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2rem;
  height: 2rem;
  padding: 0 var(--space-2);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  text-decoration: none;
}

.paginacion-numero {
  color: var(--color-ink-soft);
}

.paginacion-numero:hover {
  background: var(--color-fila-hover);
  color: var(--color-ink);
}

/* La actual no es un enlace --ir a donde ya estas no hace nada-- y por eso
   se distingue con peso y fondo, no con color de enlace. */
.paginacion-actual {
  background: var(--color-ink);
  color: var(--color-white);
  font-weight: 600;
}

.paginacion-hueco {
  padding: 0 var(--space-1);
  color: var(--color-gray-500);
}

/* ══ LOS ENLACES DE UN PROVEEDOR (13/09/2026) ══
   Tres accesos en fila, que bajan de linea si no caben. Sin altura fija: el
   bloque entero desaparece cuando no hay ninguno. */
.bandeja-enlaces .enlaces-lista {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding: var(--space-3) var(--space-4) var(--space-4);
}

.bandeja-enlaces .enlaces-lista .btn {
  text-decoration: none;
}

/* ══ WHATSAPP Y CORREO: UN BOTON Y SU MENU DE PERSONAS (18/09/2026) ══
   `.bandeja` lleva `overflow: hidden` —redondea las esquinas de sus tablas—
   y eso recortaria el menu a ras del boton. Solo esta caja lo suelta: no
   tiene tabla que recortar. */
.bandeja.bandeja-enlaces { overflow: visible; }
/* `.btn.enl-wa` y no `.enl-wa`: este es un <button> —los demas enlaces son
   <a>— y `button:hover` (0,1,1) lo pintaba del verde de la accion principal. */
.btn.enl-wa,
.btn.enl-wa:hover { background: var(--enlace-wa-fondo); border-color: var(--enlace-wa-linea); color: var(--enlace-wa-texto); }
.btn.enl-correo,
.btn.enl-correo:hover { background: var(--enlace-correo-fondo); border-color: var(--enlace-correo-linea); color: var(--enlace-correo-texto); }
/* Cada menu toma el tono de SU boton: el resaltado de fila y la cuenta. */
.enlace-menu--enl-wa { --tono-fondo: var(--enlace-wa-fondo); --tono-texto: var(--enlace-wa-texto); }
.enlace-menu--enl-correo { --tono-fondo: var(--enlace-correo-fondo); --tono-texto: var(--enlace-correo-texto); }
.enlace-menu { position: relative; }
.enlace-menu-cuenta {
  min-width: 18px;
  padding: 0 5px;
  border-radius: var(--radius-pill);
  background: var(--tono-texto);
  color: var(--color-white);
  font-size: var(--text-2xs);
  font-weight: 700;
  line-height: 18px;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
/* El menu cuelga pegado al boton —sin hueco—: con un hueco, el raton pasa
   por una franja que no es ni boton ni menu y el menu se cierra a medio
   camino. El aire lo pone el relleno de arriba, que SI es del menu. */
.enlace-menu-lista {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 40;
  display: none;
  min-width: 300px;
  max-width: min(360px, calc(100vw - 32px));
  padding-top: 6px;
}
.enlace-menu:hover .enlace-menu-lista,
.enlace-menu:focus-within .enlace-menu-lista { display: block; }
.enlace-menu-lista::before {
  content: "";
  position: absolute;
  inset: 6px 0 0;
  z-index: -1;
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
}
/* ══ CADA COSA EN SU LINEA (18/09/2026) ══
   Vladimir: *«hay un correo muy grande y creo que cae abajo, se tapa»*. La
   direccion iba en una columna al lado del nombre, y una larga le quitaba
   el sitio: el nombre se partia en cuatro lineas y la direccion se le
   montaba encima. Ahora el nombre y la accion van arriba, y el dato
   —numero o correo— en su propia linea debajo, a todo lo ancho, partiendo
   por donde haga falta. Ninguna longitud puede tapar a nada. */
.enlace-menu-opcion {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "quien accion" "dato dato";
  align-items: baseline;
  column-gap: var(--space-3);
  row-gap: 2px;
  padding: 10px 14px;
  color: var(--color-ink);
  text-decoration: none;
}
.enlace-menu-opcion + .enlace-menu-opcion { border-top: 1px solid var(--color-border); }
.enlace-menu-opcion:hover,
.enlace-menu-opcion:focus-visible { background: var(--tono-fondo); }
.enlace-menu-quien { grid-area: quien; display: flex; flex-direction: column; min-width: 0; }
.enlace-menu-quien strong { font-weight: 600; overflow-wrap: anywhere; }
.enlace-menu-quien span { font-size: var(--text-xs); color: var(--color-ink-soft); }
.enlace-menu-dato {
  grid-area: dato;
  min-width: 0;
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.enlace-menu-accion { grid-area: accion; font-size: var(--text-xs); font-weight: 600; color: var(--tono-texto); text-align: right; white-space: nowrap; }

/* ══ REORDENAR: EL AGARRE Y LOS BOTONES DE UN PASO (13/09/2026) ══ */
.asidero {
  display: inline-flex;
  align-items: center;
  color: var(--color-gray-500);
  cursor: grab;
  margin-right: var(--space-1);
}

.asidero:active {
  cursor: grabbing;
}

.fila-movible--viajando {
  opacity: 0.5;
}

/* La fila en modo reordenar: agarre, flechas y nombre en una sola linea. */
.celda-reordenar {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.celda-reordenar .identidad-principal {
  margin-left: var(--space-1);
}


/* ═══ EL PERFIL · lo que hay que saber para trabajar con una empresa ═══
   Vladimir (13/09/2026), mockup aprobado el 14/09.

   El panel reutiliza `panel_lateral`, así que aquí solo vive lo que es
   propio del perfil: el resumen destacado arriba y las secciones plegadas.
   Nada de esto inventa colores ni medidas: sale de los tokens, como el
   resto del sistema. */

/* El botón CAMBIA cuando hay algo escrito, igual que el de Notas: sin eso,
   entrar es la única forma de saber si el perfil está relleno. */
.btn-perfil.tiene-perfil {
  color: var(--color-ink);
}

.perfil-marca {
  color: var(--color-primary);
  font-size: 0.7rem;
  line-height: 1;
}

/* ── EL RESUMEN, ARRIBA Y APARTE ──────────────────────────────────────
   Él lo pidió así: «resumen visible primero: unas pocas líneas, sin obligar
   a leer toda la ficha». No es una sección más, y por eso no se pliega ni
   se pinta como ellas. */
.perfil-resumen {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-primary);
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
}

/* Sin resumen escrito, el filo verde prometería algo que no hay. */
.perfil-resumen--vacio {
  border-left-color: var(--color-border);
}

.perfil-etiqueta {
  display: block;
  font-size: var(--text-2xs);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-ink-soft);
}

.perfil-resumen p {
  margin: var(--space-2) 0 0;
}

.perfil-firma {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-3);
  padding-top: var(--space-3);
  border-top: 1px dashed var(--color-border);
  color: var(--color-ink-soft);
  font-size: var(--text-sm);
}

.perfil-pista {
  color: var(--color-ink-soft);
  font-style: italic;
}

/* ── LAS SECCIONES, PLEGADAS ──────────────────────────────────────────
   Con las cinco abiertas el panel obliga a bajar aunque estén vacías. Cada
   una dice si tiene algo: eso hace visible qué falta por completar sin
   entrar en ninguna. */
.perfil-secciones {
  display: grid;
  gap: var(--space-2);
}

.perfil-seccion {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.perfil-seccion > summary {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  cursor: pointer;
  font-weight: 600;
  font-size: var(--text-base);
  /* Sin esto el navegador pinta su propio triángulo además del nuestro. */
  list-style: none;
}

.perfil-seccion > summary::-webkit-details-marker {
  display: none;
}

.perfil-seccion > summary:hover {
  background: var(--color-fila-hover);
}

.perfil-flecha {
  color: var(--color-ink-soft);
  flex: none;
  transition: transform 0.15s ease;
}

.perfil-seccion[open] .perfil-flecha {
  transform: rotate(90deg);
}

.perfil-llena {
  margin-left: auto;
  color: var(--color-primary);
  display: flex;
}

.perfil-vacia {
  margin-left: auto;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-ink-soft);
}

/* El cuerpo se alinea con el título, no con la flecha: leído en columna,
   un texto que arranca bajo el icono parece de otra cosa. */
.perfil-cuerpo {
  padding: 0 var(--space-3) var(--space-3) calc(var(--space-3) + 22px);
  color: var(--color-ink-soft);
  font-size: var(--text-base);
}

.perfil-cuerpo p {
  margin: 0;
}

.perfil-historial {
  margin-top: var(--space-4);
}

.perfil-cambios {
  margin: 0;
  padding: 0;
  list-style: none;
  display: grid;
  gap: var(--space-2);
}

.perfil-acciones {
  margin-top: var(--space-5);
}


/* ═══ EL EDITOR DEL CORREO · se escribe el texto, no el HTML ═══════════
   Vladimir (14/09/2026): «el HTML no debería aparecer como campo de edición
   habitual… un editor visual que permita cambiar texto, negritas, listas y
   enlaces, conservando el diseño del correo».

   Las zonas de texto se pintan como lo que son —párrafos— y lo que no se
   toca lleva su candado a la vista. Esconder los datos automáticos sería
   peor: quien aprueba tiene que ver el correo entero. */
.editor-correo {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.editor-barra {
  display: flex;
  gap: var(--space-1);
  padding: var(--space-2);
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
}

.editor-barra button {
  min-width: 30px;
  min-height: 28px;
  padding: 0 var(--space-2);
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  color: var(--color-ink);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
}

.editor-barra button:hover {
  background: var(--color-fila-hover);
  border-color: var(--color-gray-500);
}

.editor-zona {
  padding: var(--space-3) var(--space-4);
  min-height: 60px;
  outline: none;
}

/* El foco TIENE que verse: sin él no se sabe dónde se está escribiendo, y
   con varios bloques seguidos eso es teclear a ciegas. */
.editor-zona:focus {
  background: var(--color-surface);
  box-shadow: inset 0 0 0 2px var(--color-primary);
}

.editor-zona p {
  margin: 0 0 var(--space-2);
}

/* ── LO QUE NO SE EDITA, A LA VISTA Y BLOQUEADO ───────────────────────
   Con el candado escrito. Un bloque que simplemente no reacciona al clic
   se lee como una avería; uno que dice por qué, no. */
.editor-fijo {
  margin: var(--space-2) var(--space-4);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
}

.editor-candado {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-ink-soft);
  border-bottom: 1px dashed var(--color-border);
}

.editor-fijo-cuerpo {
  padding: var(--space-2);
  /* El recuadro de datos trae su propia tabla de correo: se deja respirar
     sin tocarla, porque tocarla es justo lo que rompe el correo. */
  overflow-x: auto;
}


/* ═══ ESCRIBIRLE CON LA VENTANA CERRADA ═══════════════════════════════
   Vladimir (14/09/2026): «ibas a construir algo en whatsapp para hablarle a
   los clientes desde plantillas fuera de la ventana de 24 horas».

   El texto va en `pre` porque es el mensaje EXACTO que va a recibir el
   cliente —Meta lo congela al aprobar la plantilla— y ahí los saltos de
   línea son el formato. Pintarlo como párrafo enseñaría un mensaje que no
   es el que sale. */
.reabrir {
  margin-top: var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.reabrir-intro {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  margin: 0;
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}

.reabrir-opcion + .reabrir-opcion {
  border-top: 1px solid var(--color-border);
}

.reabrir-cab {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4) var(--space-2);
}

.reabrir-texto {
  margin: 0;
  padding: 0 var(--space-4) var(--space-3);
  /* Los saltos de línea SON el formato del mensaje. */
  white-space: pre-wrap;
  font-family: inherit;
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}


/* ── UNA FILA QUE LLEVA A ALGÚN SITIO LO ANUNCIA (15/09/2026) ─────────
   Vladimir, sobre el historial de comunicaciones: «eso tiene vida, o sea,
   se linkea, ¿debe sombrearse?».

   La regla funciona en los dos sentidos y conviene tenerla escrita: **si
   lleva a algún sitio, tiene que sombrearse** —si no, parece muerto— y
   **si se sombrea, tiene que llevar** —si no, se pulsa y no pasa nada, que
   es peor—.

   Es la misma lección del 29/08 en la cola de aprobación: se veía que había
   vínculo y la fila no se sombreaba, así que no parecía pulsable. */
.dec-pulsable {
  cursor: pointer;
}

.dec-pulsable:hover {
  background: var(--color-fila-hover);
}

/* ── LOS DOCUMENTOS DE LA FICHA (15/09/2026) ──────────────────────────
   Proveedores y clientes, la misma pieza. Vladimir: «colócale también un
   cuadro para subir el catálogo del proveedor… saber lo que venden cada
   uno», y «listo para reutilizarse en cliente».                          */

/* Separado de Enlaces y de lo que venga debajo, como los demás paneles. */
.panel-documentos {
  margin-block: var(--space-4);
}

/* La cabecera lleva el título a la izquierda y «Subir documento» empujado
   al borde derecho: la acción del panel en su esquina, como en los demás. */
/* **El mismo título que Enlaces, Contactos y Comunicaciones** (Vladimir,
   16/09/2026: «que tengan la misma tipografía, el mismo tamaño y que estén
   todos en negrita»). Medido: iba a 600 y los otros a 700. */
.panel-cabecera--con-accion h2 {
  margin: 0;
  font-size: var(--text-xl);
  font-weight: 700;
  color: var(--color-ink);
  letter-spacing: -0.01em;
}
.panel-cabecera--con-accion > .btn {
  margin-left: auto;
}

/* El nombre, el formato y el tamaño, debajo del tipo y en su propia línea:
   el tipo es lo que se busca al recorrer; el resto se lee cuando ya se
   encontró. */
.documento-detalle {
  display: block;
  margin-top: 2px;
  font-size: var(--text-xs);
}

/* ══ EL VENCIMIENTO, EN DOS TONOS ══
   Naranja cuando faltan menos de 30 días —hay tiempo de pedir el nuevo—;
   rojo cuando ya pasó. Un registro sanitario vencido es riesgo regulatorio,
   y un color que se ve de lejos es lo que hace que alguien lo pida a tiempo.
   Con texto, no solo color: «vencido» se lee también sin distinguir tonos. */
.vence-cerca {
  color: var(--color-warning);
  font-weight: 600;
}
.vence-pasado {
  color: var(--color-danger);
  font-weight: 600;
}

/* Fecha del documento y vencimiento lado a lado en el panel de subida: son
   un par y se rellenan juntos. En móvil, una debajo de otra. */
.campos-en-fila {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3);
}
@media (max-width: 480px) {
  .campos-en-fila { grid-template-columns: 1fr; }
}

/* Una cotización del proveedor: «Ver precios» y, si se adjuntó, su PDF,
   en la misma fila y sin partirse en dos líneas (Vladimir, 16/09/2026). */
.acciones-documento {
  display: inline-flex;
  gap: var(--espacio-2, 8px);
  white-space: nowrap;
}

/* ══ EL AVANCE DE UNA SUBIDA (16/09/2026) ══
   Encima de los botones del panel, a lo ancho: es lo que se mira mientras
   se espera. Un catálogo de 25 MB tarda minuto y medio en una conexión
   normal, y sin esto «Guardar» parecía no hacer nada. */
.avance-de-subida {
  display: grid;
  gap: var(--space-1);
  padding: var(--space-3) var(--space-5) 0;
  font-size: var(--text-sm);
  color: var(--color-ink-soft);
}
.avance-de-subida[hidden] { display: none; }
.avance-de-subida progress {
  width: 100%;
  height: 8px;
  accent-color: var(--color-primary);
}
.avance-de-subida--fallo { color: var(--color-danger); }

/* ══ DOCUMENTOS, CON SCROLL POR DENTRO (16/09/2026) ══
   Vladimir: «ponle un scroll por si crece mucho». En el cliente entran
   también sus cotizaciones, y un cliente que cotiza cada semana llena la
   ficha. Unas ocho filas a la vista; los títulos de columna se quedan
   arriba al desplazarse para no perder qué es cada dato. */
.documentos-con-scroll {
  max-height: 440px;
  overflow-y: auto;
}
/* La tabla general lleva `overflow: hidden` y eso anula `sticky`: medido,
   el título se iba con las filas. Aquí no hace falta recortar nada. */
.documentos-con-scroll table { overflow: visible; }
.documentos-con-scroll thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--color-gray-50);
}

/* La primera columna —qué documento o qué mensaje es— no se parte palabra
   por palabra en pantallas medianas (medido a 1024 px el 16/09/2026: «Orden
   de compra» ocupaba tres renglones). Lo que sobra lo desplaza la tabla. */
.documentos-con-scroll td:first-child,
.documentos-con-scroll th:first-child {
  min-width: 210px;
}

/* ══ «ABRIR» EN LA COLUMNA DE «VISTA PREVIA» (17/09/2026) ══
   Vladimir: el botón de WhatsApp quedaba en el borde derecho y el del correo
   pegado al selector. Se fija el ancho del selector y la fila de WhatsApp
   reserva exactamente lo mismo, descontando lo que ella ocupa antes del
   texto (icono de 32 px y su separación):

     correo:    [selector ANCHO] gap → Vista previa
     WhatsApp:  [icono 32] gap [texto X] gap → Abrir
     X = ANCHO + gap − 32 − gap − gap                                     */
.bandeja {
  --ancho-selector-correo: 360px;
}
.fila-acciones .form-linea {
  max-width: none;
}
.fila-acciones .form-linea > div {
  flex: 0 0 var(--ancho-selector-correo);
}
.dec.dec-alineada .dec-cuerpo {
  flex: 0 0 calc(var(--ancho-selector-correo) - 32px - var(--space-3));
}
@media (max-width: 760px) {
  .fila-acciones .form-linea > div { flex: 1 1 auto; }
  .dec.dec-alineada .dec-cuerpo { flex: 1; }
}

/* El icono del canal, EN LA LINEA del mensaje: como bloque caia solo en
   una linea encima del titulo (Vladimir, 18/09/2026). */
.tabla-comunicaciones td:first-child .em-canal {
  display: inline-flex;
  vertical-align: -2px;
  margin-right: var(--space-1);
}

/* ══ UNA DIRECCION LARGA NO SE SALE DE SU CAJA (18/09/2026) ══
   Vladimir, con un correo largo tapando un nombre: *«eso se debe corregir
   para todo el sistema»*. Una direccion de correo es UNA palabra para el
   navegador: si no cabe, no la parte y se sale de su caja. Se midieron los
   sitios donde pasaba —el panel de destinatario de la cotizacion, el de la
   conversacion de correo, «A quien» de Redactar, los datos de la ficha del
   proveedor y las lineas de Comunicaciones— y todos pintan la direccion con
   estas tres clases.
   `break-word` y no `anywhere`, a proposito: `anywhere` tambien encoge el
   ancho minimo, y en una tabla eso estrecharia columnas que hoy estan bien.
   Este solo parte cuando de verdad no cabe. */
.dato-apagado,
.dato-val,
.dec-sub { overflow-wrap: break-word; }
/* En una fila flexible el hijo no encoge por debajo de su palabra mas larga
   sin esto, y entonces partir no sirve de nada. */
.pulso > .dato-apagado { min-width: 0; }

/* ── INTEGRACIONES: UNA FILA PLEGABLE POR SERVICIO (19/09/2026) ─────────
   Vladimir: «si mañana hay 10 integraciones se haría súper grande esa
   página». Cerrada, cada fila dice qué es, para qué sirve y cómo está; la
   configuración solo al abrirla. Reusa `.ficha-grupo` —el mismo gesto que
   los grupos de las fichas—: cerrada, SOLO el título (Vladimir: «que cada
   integración tenga solo su título»); el estado va dentro, arriba del
   cuerpo, y el cuerpo a dos columnas. */
.integracion .integracion-estado {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0 0 var(--space-3);
}

.integracion > .integracion-cuerpo {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: var(--space-5);
  padding-top: var(--space-3);
}

.integracion .integracion-seccion {
  margin-top: var(--space-5);
}

@media (max-width: 900px) {
  .integracion > .integracion-cuerpo { grid-template-columns: minmax(0, 1fr); }
}

/* ── EL MANUAL DE USO (19/09/2026) ──────────────────────────────────
   Una pantalla para LEER, no para operar, y eso cambia dos cosas: el texto
   necesita medida —una línea de 120 caracteres se pierde al saltar de
   renglón— y los títulos necesitan aire para que se vea dónde empieza cada
   apartado. El resto lo pone el sistema de diseño: las tablas son las
   mismas `.tabla` de los listados. */
.manual-cuerpo {
  max-width: 68ch;
  line-height: 1.65;
  color: var(--color-ink);
}

.manual-cuerpo h2 {
  font-size: var(--text-xl);
  font-weight: 800;
  margin: var(--space-6) 0 var(--space-3);
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-gray-100);
}

.manual-cuerpo > h2:first-child {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}

.manual-cuerpo h3 {
  font-size: var(--text-base);
  font-weight: 700;
  margin: var(--space-5) 0 var(--space-2);
}

.manual-cuerpo p { margin: 0 0 var(--space-3); }

.manual-cuerpo ul {
  margin: 0 0 var(--space-4);
  padding-left: var(--space-5);
}

.manual-cuerpo li { margin-bottom: var(--space-2); }

.manual-cuerpo blockquote {
  margin: 0 0 var(--space-4);
  padding: var(--space-3) var(--space-4);
  border-left: 3px solid var(--color-primary-600);
  background: var(--color-surface);
  border-radius: var(--radius-sm);
}

.manual-cuerpo blockquote p:last-child { margin-bottom: 0; }

.manual-cuerpo .tabla-scroll { margin-bottom: var(--space-4); }

/* El recorrido de compras se dibuja con caracteres: aquí el ancho fijo ES
   el formato, igual que en la vista previa de WhatsApp. */
.manual-codigo {
  margin: 0 0 var(--space-4);
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface);
  border: 1px solid var(--color-gray-100);
  border-radius: var(--radius-md);
  overflow-x: auto;
  font-size: var(--text-sm);
  line-height: 1.5;
}

.manual-indice {
  display: flex;
  flex-direction: column;
}

.manual-indice a {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  color: var(--color-text-suave);
  text-decoration: none;
  font-size: var(--text-sm);
}

.manual-indice a:hover { background: var(--color-fila-hover); }

.manual-indice a.active {
  background: var(--color-primary-bg);
  color: var(--color-primary-texto);
  font-weight: 700;
}

/* ── EL RNC, PRIMERO Y SOLO EN SU LÍNEA (20/09/2026) ────────────────
   Vladimir: «que colocar RNC/cédula quede completamente solo en esa línea,
   así desde que lo cargue inmediatamente aparece el nombre».

   **El orden es la función, no la estética**: el número se teclea antes que
   el nombre porque el nombre lo trae la DGII. Con el nombre delante se
   escribe a mano lo que iba a llegar solo, y escrito de otra forma — que es
   lo que hubo que corregir en 160 fichas.

   El tipo va PEGADO al número y no en otra celda: son un solo dato, y
   separarlos es lo que hacía que en la primera línea hubiera dos cosas que
   no tienen que ver una con otra. */
.campo-identificacion-fila {
  display: grid;
  grid-template-columns: minmax(0, 240px) minmax(0, 1fr);
  gap: var(--space-3);
  align-items: center;
}

.campo-identificacion-fila > * { margin: 0; }

.campo-opcional {
  font-weight: 400;
  color: var(--color-gray-500);
}

@media (max-width: 720px) {
  .campo-identificacion-fila { grid-template-columns: minmax(0, 1fr); }
}
