:root {
  /* Barras de desplazamiento y controles nativos del mismo tema (revisión con agentes, parte 130: el riel salía blanco en tema oscuro). */
  color-scheme: dark;
  --bg: #0b0f14;
  --bg-elevated: #10161d;
  --card-bg: #131a22;
  --card-bg-2: #0f151c;
  --input-bg: #0f1720;
  --text: #e7edf3;
  --muted: #8b98a5;
  --border: #212b35;
  --accent: #34d0a3;
  --accent-dim: #1c7a5c;
  --burbuja-saliente-texto: #eafff6;
  --accent-glow: rgba(52, 208, 163, 0.14);
  --yellow-bg: rgba(233, 179, 60, 0.14);
  --yellow-text: #e9b33c;
  --ok-bg: rgba(52, 208, 163, 0.14);
  --ok-text: #34d0a3;
  --red-bg: rgba(233, 92, 92, 0.14);
  --red-text: #e95c5c;
  --on-accent: #06110d; /* texto sobre fondo de acento (chips activos, insignias) */
  /* Tonos de los iconos del menu lateral (parte 129): uno por seccion. En el tema claro bajan de luminosidad (mismo criterio que --accent). */
  --ic-azul: #5aa9ff;
  --ic-cian: #3cc9e0;
  --ic-violeta: #a78bfa;
  --ic-naranja: #f59e5b;
  --ic-rosa: #f472b6;
  --radius: 14px;

  /* Hoja de papel (parte 133): la vista previa del comprobante en PDF que va
     a recibir un cliente. Estos tokens NO cambian con el tema a proposito --
     un PDF se imprime y se lee sobre papel blanco, asi que la vista previa
     tiene que mostrar el documento real y no una version oscura que no
     existe. Son tokens igual (regla 44) para que cualquier retoque salga de
     un solo lugar, y el contraste de los tres textos contra --papel-bg se
     midio en los dos temas. */
  --papel-bg: #fbfaf7;
  --papel-texto: #1b2430;
  --papel-muted: #6b7480;
  --papel-linea: #e3e0d8;
  --papel-acento: #0f7f60;
}

/* Tema claro (Fase 2, parte 96) -- pedido explicito del usuario: "es
   cuestion de gustos para diferentes clientes", el oscuro sigue siendo el
   default (preferencia del propio usuario). Mismo set de tokens de arriba,
   nunca colores sueltos nuevos -- toda regla del archivo que ya usa
   var(--algo) se adapta sola. Alterna con `data-theme="light"` en <html>,
   puesto por `tema.js` (se carga ANTES de este CSS -- ver el <script> al
   principio del <head> de cada pagina -- para no mostrar un parpadeo del
   tema oscuro default antes de aplicar la preferencia guardada). El verde
   de acento baja de luminosidad (#34d0a3 -> #0f9f78): el original es un
   pastel pensado para resaltar sobre fondo oscuro, usado tambien como
   COLOR DE TEXTO/ICONO en varios lugares -- sobre blanco no cumple
   contraste real (WCAG AA), asi que el claro usa una version mas oscura
   del mismo tono en vez de reusar el literal.

   Parte 159 -- «el tema claro es como demasiado brilloso», reportado por gente del usuario. La
   causa era blanco PURO (#ffffff) en las tarjetas, que son la superficie mas grande de la
   pantalla, sobre un fondo casi blanco (#f3f5f7): mucha area a luminancia maxima y casi ningun
   escalon entre hoja y tarjeta, asi que la separacion tenia que darla el brillo. Ahora la hoja
   baja a #e8ecea y la tarjeta a #f7faf8 -- se ve MAS separacion con MENOS brillo -- y los grises
   llevan una pizca del verde del acento en vez de ser neutros puros. El contraste del texto se
   midio en el navegador despues del cambio, no se supuso. */
:root[data-theme="light"] {
  color-scheme: light;
  --bg: #e8ecea;
  --bg-elevated: #fbfdfc;
  --card-bg: #f7faf8;
  --card-bg-2: #eef2f0;
  /* Los campos SI se quedan casi blancos: son superficie chica y esa diferencia es lo que los
     hace leerse como «acá se escribe». El deslumbre venia de las tarjetas, que son enormes. */
  --input-bg: #fdfefd;
  --text: #17202a;
  --muted: #5b6771;
  --border: #d2dad6;
  /* Parte 159: baja de #0f9f78 a #0b8263. El anterior daba 3.36 de contraste sobre la tarjeta
     Y 3.36 para texto blanco encima -- los dos por DEBAJO de AA para texto normal (4.5), en el
     verde que usan los botones primarios. Ahora 4.55 y 4.79: pasa en los dos sentidos. */
  --accent: #0b8263;
  --accent-dim: #096b52;
  --burbuja-saliente-texto: #eafff6;
  --accent-glow: rgba(11, 130, 99, 0.12);
  --yellow-bg: rgba(163, 116, 8, 0.12);
  --yellow-text: #8a6106;
  --ok-bg: rgba(11, 130, 99, 0.12);
  /* Un pelin mas oscuro que el acento: este va como texto CHICO sobre su propio chip verde,
     donde el acento quedaba en 4.33 (AA pide 4.5). Asi llega a 4.83. */
  --ok-text: #0a7a5d;
  --red-bg: rgba(199, 58, 58, 0.12);
  --red-text: #b93a3a;
  --on-accent: #ffffff;
  --ic-azul: #1d6fd6;
  --ic-cian: #0e8fa8;
  --ic-violeta: #6a4bd1;
  --ic-naranja: #c2560f;
  --ic-rosa: #c2377f;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background:
    radial-gradient(1200px 500px at 15% -10%, rgba(52, 208, 163, 0.08), transparent),
    var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

.page {
  max-width: 1020px;
  margin: 0 auto;
  padding: 40px 24px 64px;
}

/* --- Panel de control: sidebar + area principal (index.html) -------------
   Reemplaza el layout viejo de "todo apilado en una sola columna centrada"
   -- inspirado en un video que el usuario compartio de otra plataforma
   (agente.ec), adaptado a lo que este proyecto realmente necesita (sin CRM,
   sin facturacion, sin workflows -- eso no aplica aca). */

.app-shell {
  display: flex;
  min-height: 100vh;
  align-items: stretch;
}

.sidebar {
  width: 240px;
  flex-shrink: 0;
  background: var(--card-bg);
  border-right: 1px solid var(--border);
  padding: 22px 0;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 20px 18px;
  margin-bottom: 8px;
  border-bottom: 1px solid var(--border);
}
.sidebar-brand-name { margin: 0; font-weight: 800; font-size: 15px; }
.sidebar-brand-sub { margin: 0; font-size: 11px; color: var(--muted); }

.sidebar-group { padding: 0 12px; margin-bottom: 4px; }
.sidebar-group.hidden { display: none; }
.sidebar-group-title {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 4px 8px 6px;
}

.sidebar-link {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 10px;
  border-radius: 8px;
  border: none;
  background: transparent;
  color: var(--text);
  font-size: 13.5px;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  text-align: left;
  text-decoration: none;
}
.sidebar-link:hover { background: var(--card-bg-2); }
.sidebar-link.active { background: var(--accent-glow); color: var(--accent); }

/* Iconos del panel (parte 130): un solo sprite (iconos.svg) que reemplaza a los emojis de la interfaz. Se dibujan con el color del texto
   (currentColor), asi que siguen el tema claro/oscuro y el color del boton; miden lo que el texto de al lado (em). Modificadores:
   .ic-lleno (relleno, p. ej. estrella marcada) y un tono semantico opcional (.ic-ok, .ic-aviso, .ic-peligro, .ic-acento, .ic-apagado). */
.ic {
  width: 1.15em;
  height: 1.15em;
  flex: 0 0 auto;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: -0.2em;
}
.ic-lleno { fill: currentColor; }
.ic-ok { color: var(--ok-text); }
.ic-aviso { color: var(--yellow-text); }
.ic-peligro { color: var(--red-text); }
.ic-acento { color: var(--accent); }
.ic-apagado { color: var(--muted); }
.ic-punto-azul { color: var(--ic-azul); }
.ic-vertical { transform: rotate(90deg); }
/* El atributo hidden tiene que ocultar SIEMPRE: una regla con display propio (.btn-sm, .token-visible...) le ganaba y dejaba a la vista un botón o una caja vacía (parte 130, revisión). */
[hidden] { display: none !important; }
.chip .x .ic, .barra-limpiar .ic { width: 14px; height: 14px; vertical-align: middle; }
.modal-close .ic { width: 20px; height: 20px; vertical-align: middle; }
.toast-cerrar .ic { width: 16px; height: 16px; vertical-align: middle; }
/* Ajustes por contexto (parte 130): los botones de acción, la auditoría y la tabla de planes ya tenían tamaño fijo pensado para un emoji. */
.btn-icon .ic { width: 16px; height: 16px; vertical-align: middle; }
.audit-event-icon .ic { width: 17px; height: 17px; }
.audit-event-icon.del { color: var(--red-text); }
.audit-event-icon.ok { color: var(--ok-text); }
.plan-funcion-icono .ic { width: 16px; height: 16px; vertical-align: -3px; }
button.plan-info-btn .ic { width: 14px; height: 14px; }

/* Iconos del menu lateral (parte 129): SVG propios (sprite en index.html) en lugar de emojis -- se ven igual en cualquier sistema, toman el
   tema claro/oscuro y cada seccion tiene su color. El icono va sobre una "ficha" del mismo tono a baja opacidad; la seccion activa la
   llena. `svg.sb-ic` (no un <span>) a proposito: app.js lee `querySelector('span')` como la etiqueta del boton "Fijar menu". */
.sidebar-link .sb-ic {
  flex: 0 0 auto;
  box-sizing: content-box;
  width: 20px;
  height: 20px;
  padding: 5px;
  border-radius: 9px;
  fill: none;
  stroke: var(--ic, var(--muted));
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  background: var(--card-bg-2); /* respaldo si el navegador no soporta color-mix */
  background: color-mix(in srgb, var(--ic, var(--muted)) 16%, transparent);
  transition: background 0.15s ease, stroke 0.15s ease;
}
.sidebar-link:hover .sb-ic { background: color-mix(in srgb, var(--ic, var(--muted)) 28%, transparent); }
.sidebar-link.active .sb-ic { background: var(--ic, var(--accent)); stroke: var(--card-bg); }
.ic-verde { --ic: var(--accent); }
.ic-azul { --ic: var(--ic-azul); }
.ic-cian { --ic: var(--ic-cian); }
.ic-violeta { --ic: var(--ic-violeta); }
.ic-ambar { --ic: var(--yellow-text); }
.ic-rojo { --ic: var(--red-text); }
.ic-naranja { --ic: var(--ic-naranja); }
.ic-rosa { --ic: var(--ic-rosa); }
.ic-gris { --ic: var(--muted); }

.main-area {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.main-topbar {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  padding: 32px 32px 0;
  flex-wrap: wrap; /* si el título y lo de la derecha no caben juntos, lo de la derecha baja (antes se montaban, revisión con agentes, parte 130) */
}
.topbar-derecha { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; justify-content: flex-end; min-width: 0; }
@media (max-width: 860px) { .topbar-derecha { width: 100%; justify-content: flex-start; } }
@media (max-width: 560px) { .topbar-derecha .perfil-texto { display: none; } .topbar-derecha { gap: 8px; } }

.main-content {
  padding: 20px 32px 64px;
  /* Subido de 1500px (parte 10) -- con las columnas "Cliente" y "Alertas"
     nuevas (parte 30), 1500px ya no alcanzaba para que la fila de Acciones
     entrara en una sola linea sin recurrir al scroll horizontal de
     `.table-scroll`, aun en pantallas anchas con espacio de sobra (bug real
     reportado por el usuario). El scroll sigue siendo el respaldo real para
     ventanas angostas -- esto solo evita que se necesite en el caso comun. */
  max-width: 1800px;
}

@media (max-width: 860px) {
  .app-shell { flex-direction: column; }
  .sidebar {
    width: 100%;
    flex-direction: row;
    align-items: center;
    gap: 4px;
    overflow-x: auto;
    padding: 10px 12px;
    border-right: none;
    border-bottom: 1px solid var(--border);
  }
  .sidebar-brand { display: none; }
  .sidebar-group {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 0;
    margin: 0;
    flex-shrink: 0;
  }
  .sidebar-group-title { display: none; }
  .sidebar-link { white-space: nowrap; }
  .sidebar-fijar { display: none; }
  .main-topbar, .main-content { padding-left: 16px; padding-right: 16px; }
}

/* Menu lateral plegable (Fase 2, parte 121) -- pedido del usuario: las
   tablas de Instancias/Clientes desbordaban y salia una barra de scroll
   horizontal, que no quiere ver. En escritorio el menu es una franja de 64px
   con solo iconos; al pasar el mouse (o navegar con teclado) se despliega POR
   ENCIMA del contenido, sin empujarlo, y "Fijar menu" lo deja abierto. En
   pantallas angostas (<=860px) nada de esto aplica: sigue la barra horizontal
   de siempre. `.sidebar-slot` reserva el ancho en el flujo; `.sidebar` (sticky,
   se queda a la vista al hacer scroll) es lo que crece encima. `:has(:focus-visible)`
   y no `:focus-within` a proposito: un boton clickeado con el mouse conserva el
   foco y el menu se quedaria abierto para siempre. */
.sidebar-slot { display: contents; }
@media (min-width: 861px) {
  .sidebar-slot { display: block; width: 64px; flex-shrink: 0; position: relative; transition: width 0.18s ease; }
  .app-shell.sidebar-fijada .sidebar-slot { width: 240px; }
  .sidebar {
    position: sticky;
    top: 0;
    height: 100vh;
    width: 64px;
    overflow: hidden;
    z-index: 40;
    transition: width 0.18s ease;
  }
  .sidebar-brand { padding: 0 0 18px; justify-content: center; }
  .sidebar-brand > div:not(.brand-mark) { display: none; }
  .sidebar-group { padding: 0 8px; }
  .sidebar-group-title { display: none; }
  .sidebar-group + .sidebar-group { border-top: 1px solid var(--border); padding-top: 8px; margin-top: 4px; }
  .sidebar-link { justify-content: center; padding: 6px 0; font-size: 17px; white-space: nowrap; }
  .sidebar-link span { display: none; }

  :is(.sidebar-slot:hover, .sidebar-slot:has(:focus-visible), .app-shell.sidebar-fijada) .sidebar { width: 240px; }
  :is(.sidebar-slot:hover, .sidebar-slot:has(:focus-visible), .app-shell.sidebar-fijada) .sidebar-brand { padding: 0 20px 18px; justify-content: flex-start; }
  :is(.sidebar-slot:hover, .sidebar-slot:has(:focus-visible), .app-shell.sidebar-fijada) .sidebar-brand > div:not(.brand-mark) { display: block; }
  :is(.sidebar-slot:hover, .sidebar-slot:has(:focus-visible), .app-shell.sidebar-fijada) .sidebar-group { padding-left: 12px; padding-right: 12px; }
  :is(.sidebar-slot:hover, .sidebar-slot:has(:focus-visible), .app-shell.sidebar-fijada) .sidebar-group-title { display: block; }
  :is(.sidebar-slot:hover, .sidebar-slot:has(:focus-visible), .app-shell.sidebar-fijada) .sidebar-link { justify-content: flex-start; padding: 5px 10px; font-size: 13.5px; }
  :is(.sidebar-slot:hover, .sidebar-slot:has(:focus-visible), .app-shell.sidebar-fijada) .sidebar-link span { display: inline; }
}

/* --- Auditoria forense: feed de eventos (ver "Fase 2 (parte 5)") --------- */

.audit-feed { display: flex; flex-direction: column; gap: 10px; margin-top: 16px; }

.audit-event {
  display: flex;
  gap: 12px;
  padding: 12px 14px;
  background: var(--card-bg-2);
  border: 1px solid var(--border);
  border-radius: 10px;
}

.audit-event-icon {
  width: 32px;
  height: 32px;
  border-radius: 8px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  font-size: 15px;
}
.audit-event-icon.del { background: var(--red-bg); }
.audit-event-icon.ok { background: var(--ok-bg); }

.audit-event-body { min-width: 0; }
.audit-event-texto { margin: 0 0 4px; font-size: 13.5px; font-weight: 600; }
.audit-event-meta { margin: 0; font-size: 11.5px; color: var(--muted); }

.topbar {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  margin-bottom: 24px;
  gap: 16px;
}

.brand {
  display: flex;
  gap: 16px;
  align-items: center;
}

.brand-mark {
  width: 52px;
  height: 52px;
  border-radius: 12px;
  background: linear-gradient(135deg, var(--accent), var(--accent-dim));
  color: #06110d;
  font-weight: 800;
  font-size: 18px;
  display: flex;
  align-items: center;
  justify-content: center;
  letter-spacing: -0.02em;
  flex-shrink: 0;
  box-shadow: 0 0 0 1px rgba(255,255,255,0.06), 0 8px 24px var(--accent-glow);
}

.eyebrow {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--accent);
  margin: 0 0 4px;
}

h1 {
  margin: 0 0 6px;
  font-size: 30px;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--text);
}

.subtitle {
  margin: 0;
  color: var(--muted);
  font-size: 14px;
}

.badge {
  padding: 7px 14px;
  border-radius: 999px;
  font-size: 13px;
  font-weight: 600;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.badge-ok { background: var(--ok-bg); color: var(--ok-text); border: 1px solid rgba(52, 208, 163, 0.25); }

.dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 8px var(--accent);
}

/* Tarjeta de perfil del topbar (Fase 2, partes 98/100) -- reemplaza el
   texto plano "correo (rol)" de siempre, aprovechando el espacio libre que
   señaló el usuario en una captura real. Compacta a propósito (misma
   altura que el badge/botones de al lado) -- la idea es verse mejor sin
   robarle lugar en altura a las tablas que crecen debajo. Rediseñada en la
   parte 100 -- la primera versión (fondo plano, texto suelto) le pareció
   poco cuidada al usuario ("un diseñador gráfico se reiría de eso") --
   ahora con avatar en degradé + halo, y una insignia de rol propia en vez
   de texto suelto. */
.perfil-chip {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 6px 5px 5px;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.25);
}
.perfil-avatar {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--accent), var(--accent-dim));
  box-shadow: 0 0 0 3px var(--accent-glow);
  color: #06110d;
  font-weight: 700;
  font-size: 12.5px;
  letter-spacing: 0.02em;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.perfil-texto { display: flex; flex-direction: column; line-height: 1.3; }
.perfil-nombre { font-size: 12.5px; font-weight: 600; color: var(--text); }
.perfil-detalle { font-size: 11px; color: var(--muted); }
.perfil-rol-badge {
  flex-shrink: 0;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  padding: 4px 9px;
  border-radius: 999px;
  background: var(--ok-bg);
  color: var(--ok-text);
  border: 1px solid rgba(52, 208, 163, 0.25);
}
.perfil-rol-badge.rol-cliente { background: var(--card-bg-2); color: var(--muted); border-color: var(--border); }

.stats-row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 14px;
  margin-bottom: 24px;
}

.stat-card {
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 18px 20px;
}

.stat-label {
  margin: 0 0 6px;
  font-size: 12px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.stat-value {
  margin: 0;
  font-size: 26px;
  font-weight: 800;
  color: var(--text);
}

.card {
  background: var(--card-bg);
  border-radius: var(--radius);
  border: 1px solid var(--border);
  padding: 26px;
  margin-bottom: 20px;
}

.card-head h2 {
  margin: 0 0 18px;
  font-size: 18px;
  font-weight: 700;
}

.count { color: var(--muted); font-weight: 400; }

.add-form, .send-form {
  display: flex;
  gap: 12px;
  align-items: flex-end;
  flex-wrap: wrap;
  margin-bottom: 8px;
}
/* La ayuda que cambia con el canal (parte 145). Ocupa el ancho para caer en su propio renglón:
   dentro del campo le sumaba alto y, como la fila se alinea por abajo, subía el selector unos
   píxeles y quedaba descolocado de «Cliente» y «Alias». Acá no mueve nada y no se puede montar
   encima del texto de abajo, pase lo que pase con el largo del texto o el ancho de la pantalla. */
.add-form-ayuda { flex: 1 0 100%; margin: 2px 0 0; }
/* El <select> y el <input> no miden lo mismo por defecto: el navegador le da al select unos
   píxeles más de alto (medido: 2). En una fila que se alinea por abajo eso deja los bordes de
   arriba desparejos y la fila deja de leerse como una sola línea. Con `box-sizing: border-box`
   (que el panel usa en todo) estos 40 px incluyen el borde y el relleno. */
.add-form .field > select, .add-form .field > input,
.send-form .field > select, .send-form .field > input { height: 40px; }
/* Y el botón de la fila, por exactamente lo mismo (parte 150, lo vio el usuario en una captura):
   con su relleno propio medía 36 px, cuatro menos que los campos. Al alinearse la fila por abajo,
   los bordes de arriba quedaban desparejos y la fila se leía torcida. No se toca `.btn` en general:
   esto vale solo para un botón que comparte renglón con campos. */
.add-form > button, .send-form > button { height: 40px; }

/* Un gestor de contraseñas (LastPass, 1Password, Dashlane, Bitwarden) inyecta su ícono como un
   elemento HERMANO del campo, dentro del mismo `.field`. Aunque mida 0x0, el `gap` de la columna
   le suma sus 6 px: el campo crece y, como la fila se alinea por abajo, los campos vecinos bajan
   y la fila se lee torcida. Medido reproduciendo la inyección real de LastPass: el campo pasó de
   63 a 69 px y sus dos vecinos se corrieron 6 px (lo vio el usuario en una captura, parte 159).
   No alcanza con pedirles que se abstengan: estos campos YA llevan `data-lpignore`, `data-1p-ignore`,
   `data-bwignore` y `data-form-type="other"`, y el ícono aparece igual.
   Así que se lo saca del flujo en vez de nombrar a cada extensión. El invariante en el que se apoya
   es de NUESTRO marcado: todo hijo directo de un `.field` es una de estas etiquetas o lleva clase;
   lo que inyecta una extensión no cumple ninguna de las dos (`test/css-campos.test.js` lo vigila).
   Con `position: absolute` el ícono se sigue viendo -- no se desactiva el gestor, solo deja de
   empujar. */
.field > :not(label):not(input):not(select):not(textarea):not(button):not(p):not(span):not(datalist):not([class]) {
  /* `!important` porque el elemento inyectado trae `position: relative` en un `style` EN LÍNEA,
     que le gana a cualquier hoja de estilos. Comprobado: sin esto el selector acierta y el
     `position` calculado sigue siendo `relative`. */
  position: absolute !important;
}
.field {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 1;
  min-width: 180px;
}

.field-wide { flex-basis: 100%; }

/* Retencion de medios POR PLAN (Fase 2, parte 113) -- 5 campos cortos en
   vez de uno solo, mismos tokens de color que el resto del formulario. */
.retencion-media-grid {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
}
.retencion-media-item {
  display: flex;
  flex-direction: column;
  gap: 6px;
  min-width: 110px;
}

label {
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
}

/* Bug real encontrado en el login/registro (captura del usuario): esta
   regla nunca cubrió `email`/`password`/`tel`/`number`/`date`/`search` --
   esos tipos quedaban con el estilo NATIVO del navegador (caja clara,
   angosta, sin nada del tema oscuro), mientras un `type="text"` de al lado
   sí se veía bien -- de ahí la inconsistencia visible en el formulario de
   registro (Correo/WhatsApp/Contraseña sin estilo, Nombre sí). */
input[type="text"], input[type="email"], input[type="password"], input[type="tel"],
input[type="number"], input[type="date"], input[type="time"], input[type="search"],
textarea, input[type="file"], select {
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  font-size: 14px;
  font-family: inherit;
  background: var(--card-bg-2);
  color: var(--text);
}

input[type="text"]::placeholder, input[type="email"]::placeholder, input[type="password"]::placeholder,
input[type="tel"]::placeholder, input[type="number"]::placeholder, input[type="search"]::placeholder,
textarea::placeholder { color: #5c6773; }
input[type="text"]:focus, input[type="email"]:focus, input[type="password"]:focus, input[type="tel"]:focus,
input[type="number"]:focus, input[type="date"]:focus, input[type="time"]:focus, input[type="search"]:focus, textarea:focus {
  outline: none;
  border-color: var(--accent-dim);
  box-shadow: 0 0 0 3px var(--accent-glow);
}
/* El icono nativo del selector de fecha es oscuro por defecto -- invisible
   contra el fondo oscuro que recién le empezamos a aplicar arriba. Mismo
   criterio para el de hora (`type="time"`, Fase 2 parte 120 -- horario del
   Bot de IA), agregado a la regla base de inputs en el mismo cambio (regla
   dura 39). */
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator { filter: invert(0.8); cursor: pointer; }

textarea { resize: vertical; }

code {
  background: var(--card-bg-2);
  border: 1px solid var(--border);
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 12.5px;
  color: var(--accent);
}

.btn {
  padding: 10px 18px;
  border-radius: 8px;
  border: none;
  font-weight: 700;
  font-size: 14px;
  cursor: pointer;
}

.btn-primary { background: var(--accent); color: #06110d; }
.btn-primary:hover { background: #45dcb2; }
.btn-secondary { background: var(--card-bg-2); color: var(--text); border: 1px solid var(--border); }
.btn-secondary:hover { border-color: var(--accent-dim); }

.hint {
  font-size: 13px;
  color: var(--muted);
  margin: 8px 0 0;
}

.supervision-hint {
  background: var(--yellow-bg);
  color: var(--yellow-text);
  border: 1px solid rgba(233, 179, 60, 0.3);
  border-radius: 8px;
  padding: 10px 14px;
  margin: 0 0 16px;
}

.form-error {
  font-size: 13px;
  color: var(--red-text);
  min-height: 18px;
  margin: 8px 0 0;
}

/* Contenedor con scroll horizontal propio para las tablas de instancias/
   clientes -- evita que, cuando el contenido natural de las columnas no
   entra en el ancho disponible, el navegador comprima la ultima columna
   (Acciones) hasta forzar sus botones a partirse en dos lineas. En vez de
   eso, la tabla crece a su ancho natural y el contenedor scrollea -- mismo
   patron estandar para tablas de datos, y solo se activa en el caso
   realmente angosto (bug real reportado por el usuario: los botones se
   partian en dos lineas aun habiendo espacio de sobra en la pantalla,
   porque el ancho se repartia mal entre columnas, no porque faltara ancho
   real). Ver "Fase 2 (parte 30)".
*/
.table-scroll { overflow-x: auto; }

/* Buscador de la tabla de Clientes/Instancias (Fase 2, parte 95) -- pedido
   explicito del usuario pensando en escala ("imagina tener 500 clientes").
   Filtra en el cliente (JS, sin ir al servidor) contra los campos visibles
   de cada fila -- a este volumen (decenas/cientos de filas, no miles) no
   hace falta busqueda del lado del servidor. */
.tabla-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 14px;
}
.tabla-buscar {
  flex: 1;
  max-width: 420px;
}
/* Buscador con lupa (parte 130): el ícono es un SVG real dentro de la caja; antes iba como emoji dentro del placeholder. */
.tabla-buscar-caja { position: relative; display: flex; flex: 1; max-width: 420px; }
.tabla-buscar-caja .ic { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--muted); pointer-events: none; }
.tabla-buscar-caja .tabla-buscar { width: 100%; max-width: none; padding-left: 36px; }
.tabla-buscar-conteo {
  font-size: 12.5px;
  color: var(--muted);
  white-space: nowrap;
}

.accounts-table {
  /* min-width (no width fijo) -- con `width: 100%` el navegador trataba ese
     100% como un limite duro y RECORTABA el contenido que no entraba (los
     botones de Acciones quedaban tapados por el borde de la pantalla, sin
     que .table-scroll llegara a activar el scroll -- bug real, visto en
     captura). `min-width: 100%` dice "al menos el ancho del contenedor,
     pero crece si el contenido (ej. `.row-actions` en una sola linea) lo
     necesita" -- ahi si el contenedor detecta el desborde y scrollea. */
  min-width: 100%;
  width: max-content;
  border-collapse: collapse;
  margin-top: 20px;
}

.accounts-table th {
  text-align: left;
  font-size: 11px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom: 1px solid var(--border);
  padding: 12px 16px;
}

/* La fila de títulos se lee como una fila más (parte 169, el usuario: «los textos del encabezado
   los veo como los textos de los detalles»). El tamaño, las mayúsculas y el gris ya la
   distinguían, pero sin una banda propia y sin peso no se lee como el ENCABEZADO de la tabla.
   Va sobre `.accounts-table`, que es la misma tabla de Instancias, Clientes, Auditoría y Mis
   clientes -- un solo idioma visual (regla 93), no un parche en una pantalla. */
.accounts-table thead th {
  background: var(--card-bg-2);
  font-weight: 700;
  color: var(--text);
  border-bottom: 2px solid var(--border);
}

/* Columnas de CONTEO (parte 169, pedido del usuario sobre el cupo): el número pegado al borde
   derecho y con cifras de ancho fijo, que es lo que deja comparar dos filas de un vistazo.
   El rótulo va al mismo lado que su número, o el encabezado apunta al vacío. */
.accounts-table th.col-num, .accounts-table td.col-num { text-align: right; }
.accounts-table td.col-num { font-variant-numeric: tabular-nums; }
.accounts-table td.col-num input { text-align: right; font-variant-numeric: tabular-nums; }

/* Los dos campos editables de la fila de Clientes. Estaban con `style=` en línea dentro del JS,
   donde ningún tema los alcanza y no se pueden medir; el del teléfono además necesitaba más ancho
   desde que muestra el número formateado (+593 99 000 1122 no entra en 100 px). */
.accounts-table .celda-tel { width: 148px; padding: 6px 8px; font-size: 12px; }
.accounts-table .celda-cupo { width: 64px; padding: 6px 8px; }

.accounts-table td {
  padding: 14px 12px;
  border-bottom: 1px solid var(--border);
  font-size: 14px;
  vertical-align: middle;
  color: var(--text);
}

.accounts-table td:first-child, .accounts-table th:first-child { padding-left: 4px; }

/* Columnas ordenables (Fase 2, parte 95) -- pedido explicito del usuario:
   clic en el titulo de cualquier columna ordena asc/desc. `data-sort-dir`
   lo pone el JS (`asc`/`desc`) solo en la columna activa -- la flechita
   sale con contenido generado, nunca un caracter literal en el HTML (asi
   no aparece en ninguna otra columna por accidente). */
.th-ordenable {
  cursor: pointer;
  user-select: none;
  transition: color 0.15s ease;
}
.th-ordenable:hover { color: var(--text); }
.th-ordenable::after {
  content: '↕';
  display: inline-block;
  margin-left: 5px;
  opacity: 0.35;
  font-size: 10px;
}
.th-ordenable[data-sort-dir="asc"]::after { content: '↑'; opacity: 1; color: var(--accent); }
.th-ordenable[data-sort-dir="desc"]::after { content: '↓'; opacity: 1; color: var(--accent); }

.instancia-nombre { display: block; font-weight: 600; }

/* La fecha de "Desde" no debe partirse en dos lineas dentro de su columna.
   Clase explicita en vez de nth-child(3) -- ese selector posicional quedo
   apuntando a la columna equivocada en cuanto se agrego "Cliente" como
   columna nueva (bug real, encontrado por el usuario -- ver "Fase 2 (parte
   30)"): la posicion de una columna cambia segun el rol (admin ve una
   columna mas que un cliente), asi que depender del INDICE nunca es
   robusto aca. */
.accounts-table .col-desde { white-space: nowrap; }

/* Un mensaje de error real de WhatsApp puede ser largo ("QR refs attempts
   ended", el detalle de un rechazo de envio, etc.) -- sin tope, una sola
   fila con un error largo podia inflar el ancho de TODA la tabla y forzar el
   scroll horizontal por si sola, aun con las demas columnas ya compactas
   (bug real, encontrado revisando esta misma columna -- ver "Fase 2 (parte
   30)"). Se trunca con "..." y el texto completo queda en el `title` (mismo
   patron que ya usa `.chat-list-preview` en el visor de chats). */
/* Parte 168: fecha y hora en el MISMO renglón (el usuario: «la hora puedes subirla al lado de la
   fecha desde»). `nowrap` para que no se parta justo en el espacio del medio. */
.accounts-table .col-desde { white-space: nowrap; }
.accounts-table .col-desde .fecha-hora { color: var(--muted); font-size: 12px; }
.accounts-table .col-ultimo-error {
  max-width: 150px; /* parte 130: antes 220px; el texto completo sigue en el tooltip */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Pantallas medianas (parte 130, junto con el menú «⋯» de acciones): se ocultan las dos columnas de menos valor para que las acciones de
   uso diario sigan a la vista. «Desde» es solo informativa; el último error pasa también al globito del estado (filaInstanciaHtml). */
@media (max-width: 1350px) {
  .accounts-table .col-desde { display: none; }
  .accounts-table th { padding-left: 10px; padding-right: 10px; }
  .accounts-table td { padding-left: 8px; padding-right: 8px; }
}
@media (max-width: 1200px) { .accounts-table .col-ultimo-error { display: none; } .accounts-table tbody td.row-actions { padding-left: 4px; padding-right: 4px; } }

/* Mismo criterio que .col-ultimo-error, para la tabla de "Clientes": un
   correo real es corto, pero uno de prueba (o uno raro) puede ser bastante
   largo y por si solo forzar el scroll horizontal de toda la tabla (bug
   real, visto con capturas del usuario tras agregar la columna "WhatsApp
   del cliente" -- parte 41). Se trunca con "..." y el texto completo queda
   en el `title`. */
.accounts-table .col-correo {
  max-width: 170px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Tabla de "Clientes" (parte 41/42): a diferencia de "Instancias" (que ya
   pasó sus botones de Acciones a solo-icono en la parte 31), acá el pedido
   fue mantener el TEXTO de cada botón y solo agregarle un icono -- así que
   la columna sigue siendo mas ancha por diseño. Padding/fuente un poco mas
   compactos que el `.btn-sm` genérico, scoped a esta fila nomas, para que
   los 4 botones con icono+texto entren en una sola línea sin scroll
   horizontal en una pantalla de escritorio normal (probado a 1600px). */
.row-actions-compact.row-actions { gap: 1px; }
.row-actions-compact .btn-sm { padding: 4px 5px; font-size: 10.5px; }

/* Nombre del cliente (parte 42): texto libre elegido por el admin al
   registrarlo -- mismo criterio que .col-correo, para que un nombre largo
   no infle la tabla entera por si solo. */
.accounts-table .col-nombre-cliente {
  max-width: 115px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Tooltip propio de toda la app (Fase 2, parte 95) -- reemplaza el `title`
   nativo del navegador, ver el motor en ui.js (mostrarTooltip/data-tooltip).
   position:fixed (no relativo al elemento) para no quedar cortado por el
   scroll horizontal de `.table-scroll` ni por ningun otro `overflow` en el
   medio -- mismo criterio que `.contacto-dropdown`/`.msg-reaccion-picker`. */
.ui-tooltip {
  position: fixed;
  z-index: 999;
  background: var(--card-bg-2);
  border: 1px solid var(--border);
  border-top: 2px solid var(--accent);
  border-radius: 8px;
  padding: 7px 12px;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--text);
  white-space: pre-line;
  max-width: 280px;
  box-shadow: 0 10px 28px rgba(0, 0, 0, 0.4);
  pointer-events: none;
  opacity: 0;
  transform: translateY(2px) scale(0.97);
  transition: opacity 0.12s ease, transform 0.12s ease;
}
.ui-tooltip.visible { opacity: 1; transform: translateY(0) scale(1); }
/* Flechita apuntando al elemento -- un cuadradito rotado 45°, mismo fondo/
   borde que el cuerpo, recortado a la mitad visible con overflow:hidden en
   un contenedor implicito no hace falta: se posiciona centrado y la mitad
   que sobra queda tapada por el propio cuerpo del tooltip (z-index menor). */
.ui-tooltip::after {
  content: '';
  position: absolute;
  left: 50%;
  width: 9px;
  height: 9px;
  margin-left: -5px;
  background: var(--card-bg-2);
  border: 1px solid var(--border);
  transform: rotate(45deg);
  z-index: -1;
}
.ui-tooltip-abajo::after { top: -5px; border-bottom: none; border-right: none; }
.ui-tooltip-arriba::after { bottom: -5px; border-top: none; border-left: none; }

.accounts-table .empty {
  text-align: center;
  color: var(--muted);
  padding: 28px;
}

.state-pill {
  display: inline-block;
  padding: 4px 12px;
  border-radius: 999px;
  font-size: 12.5px;
  font-weight: 700;
}

.state-Esperando_QR, .state-esperando { background: var(--yellow-bg); color: var(--yellow-text); }

/* Badge + boton de vigencia apilados (no en linea) -- ver vigenciaHtml() en
   app.js. En linea, un texto de badge largo empujaba el boton a un lugar
   horizontal distinto segun la fila (bug real de alineacion reportado por el
   usuario, "Fase 2 (parte 30)"); apilados, el boton siempre cae en el mismo
   lugar relativo, sin importar el largo del texto de la fila de arriba. */
/* Parte 168: en fila y envolviendo, no apilada. El motivo viejo de apilarla (que el botón quedara
   siempre en el mismo lugar por más largo que fuera el texto del chip) ya no aplica: el botón es un
   ícono de ancho fijo, así que no hay nada que se desalinee. */
.vigencia-cell { display: flex; flex-flow: row wrap; align-items: center; gap: 6px; }
.cfg-aviso-campo { color: var(--yellow-text); }
.vigencia-faltan { font-size: 12px; font-weight: 600; color: var(--yellow-text); white-space: nowrap; }

.vigencia-badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 700;
  white-space: nowrap;
}
.vigencia-sinvencer { background: var(--card-bg-2); color: var(--muted); }

.plan-select {
  background: var(--card-bg-2);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 4px 6px;
  font-size: 12px;
}

/* Fase 2 (parte 103) -- boton/badge "que incluye este plan". El boton usa
   button.plan-info-btn (elemento+clase) para ganarle en especificidad a
   .btn-icon (32x32, pensado para los botones de accion de la fila) sin
   depender del orden en el archivo -- este es mas chico a proposito, es un
   dato informativo secundario, no una accion principal de la fila. */
.plan-celda { display: flex; align-items: center; gap: 6px; }
button.plan-info-btn { width: 20px; height: 20px; font-size: 12px; }
.plan-badge-click { cursor: pointer; }
.plan-badge-click:hover { filter: brightness(1.15); }

/* .confirm-modal.planes-modal (combinado) para ganarle a .confirm-modal
   (mismo motivo de especificidad que arriba) sin importar donde caiga cada
   regla en el archivo. Ancho generoso a proposito (pedido explicito del
   usuario: "tienes todo un espacio en ancho, amplia de forma horizontal")
   -- con 580px la tabla de 6 columnas quedaba angosta, el texto de cada
   funcion se partia en 3-4 lineas, Y ademas sobraba tan poco que igual
   aparecia la barra de scroll horizontal (lo peor de los dos mundos). Con
   ~900px entra completa sin scroll en cualquier pantalla de escritorio
   normal; `overflow-x:auto` en `.planes-tabla-wrap` queda solo como
   respaldo real para una ventana angosta de verdad, no como algo que se
   vea en el uso normal. */
.confirm-modal.planes-modal { max-width: min(920px, 94vw); text-align: left; }
/* Tabla de planes rediseñada (parte 130, pedido del usuario: «algo que impacte», más profesional que íconos finos que parecían emojis).
   Cada plan tiene su color (--plan, tokens de ambos temas, regla 44): su ficha, sus barras de nivel y sus checks. La columna del plan de
   esta instancia se tiñe y su ficha va llena. Los planes se suman, y las barras (1 a 5) lo dicen sin palabras. */
.plan-t-basico { --plan: var(--ic-cian); }
.plan-t-estandar { --plan: var(--ic-azul); }
.plan-t-avanzado { --plan: var(--ic-violeta); }
.plan-t-forense { --plan: var(--yellow-text); }
.plan-t-dedicado { --plan: var(--accent); }
/* Los dos de Telegram (parte 145): mismos tonos que sus equivalentes de WhatsApp, para que la
   tabla se lea igual en los dos canales. Tokens ya definidos sobre :root (regla 44). */
.plan-t-telegram_estandar { --plan: var(--ic-azul); }
.plan-t-telegram_avanzado { --plan: var(--ic-violeta); }
.planes-sub { margin: 2px 0 0; color: var(--muted); font-size: 13px; }
.planes-tabla-wrap { overflow-x: auto; margin: 14px 0 10px; }
.planes-tabla { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 13px; table-layout: fixed; }
.planes-tabla th, .planes-tabla td { padding: 9px 8px; border-bottom: 1px solid var(--border); text-align: center; }
.planes-tabla th:first-child, .planes-tabla td:first-child { width: 29%; text-align: left; color: var(--muted); font-weight: 500; text-wrap: balance; }
.planes-tabla thead th { vertical-align: bottom; border-bottom: 1px solid var(--border); padding: 12px 6px 14px; font-weight: 700; }
.planes-tabla thead th.plan-col-actual { border-radius: 14px 14px 0 0; }
.planes-tabla td.plan-col-actual, .planes-tabla th.plan-col-actual { background: var(--card-bg-2); background: color-mix(in srgb, var(--plan) 8%, transparent); }
.plan-cab-in { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.plan-cab-lado { display: block; font-size: 11px; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); padding-bottom: 40px; }
svg.ic.plan-ficha { box-sizing: content-box; width: 22px; height: 22px; padding: 11px; border-radius: 15px; stroke: var(--plan); stroke-width: 1.8; background: var(--card-bg-2); background: color-mix(in srgb, var(--plan) 16%, transparent); }
.plan-col-actual svg.ic.plan-ficha { background: var(--plan); stroke: var(--card-bg); box-shadow: 0 6px 18px color-mix(in srgb, var(--plan) 35%, transparent); }
.plan-nombre { font-size: 14px; font-weight: 700; color: var(--text); }
.plan-nivel { display: inline-flex; gap: 3px; }
.plan-nivel i { width: 10px; height: 4px; border-radius: 2px; background: var(--border); }
.plan-nivel i.on { background: var(--plan); }
.plan-cifra { display: flex; flex-direction: column; align-items: center; line-height: 1.05; margin-top: 4px; }
.plan-cifra b { font-size: 26px; font-weight: 800; color: var(--text); font-variant-numeric: tabular-nums; letter-spacing: -0.02em; }
.plan-cifra small { font-size: 11px; font-weight: 500; color: var(--muted); }
.plan-cifra-sin b { color: var(--muted); font-weight: 600; }
.plan-lema { font-size: 11px; font-weight: 500; color: var(--muted); line-height: 1.3; min-height: 2.6em; text-wrap: balance; }
.plan-actual-tag { white-space: nowrap; font-size: 10.5px; font-weight: 700; letter-spacing: 0.02em; color: var(--card-bg); background: var(--plan); border-radius: 999px; padding: 2px 9px; }
.plan-grupo th { text-align: left; font-size: 11px; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: var(--muted); padding: 18px 8px 6px; }
.planes-tabla .plan-grupo th:first-child { width: auto; }
.plan-funcion-icono { display: inline-block; width: 20px; text-align: center; margin-right: 6px; color: var(--muted); }
.plan-si { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px; border-radius: 50%; color: var(--plan); background: var(--card-bg-2); background: color-mix(in srgb, var(--plan) 18%, transparent); }
.plan-col-actual .plan-si { background: var(--plan); color: var(--card-bg); }
.plan-si .ic { width: 13px; height: 13px; stroke-width: 3; }
.plan-no { display: inline-block; width: 10px; height: 2px; border-radius: 1px; background: var(--border); vertical-align: middle; }
.plan-retencion { font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; white-space: nowrap; }
.plan-retencion-sin { color: var(--plan); }
.planes-nota { display: flex; gap: 8px; align-items: flex-start; margin: 0 0 14px; font-size: 12px; color: var(--muted); line-height: 1.5; }
.planes-nota .ic { flex: 0 0 auto; width: 15px; height: 15px; margin-top: 1px; }
/* En una pantalla angosta la tabla se desplaza por dentro, con la columna de las funciones fija a la izquierda. */
@media (max-width: 720px) {
  .planes-tabla { min-width: 640px; }
  .planes-tabla th:first-child, .planes-tabla td:first-child { position: sticky; left: 0; z-index: 1; background: var(--card-bg); }
  .planes-tabla .plan-grupo th { position: static; }
}

/* CTA de mejora de plan -- mas llamativo que un parrafo simple: fondo
   destacado + boton real de WhatsApp con mensaje precargado (nunca solo el
   numero como texto, pedido explicito del usuario -- "algo bien
   contactante"). */
.planes-cta { text-align: center; background: linear-gradient(135deg, var(--card-bg-2), var(--card-bg)); border: 1px solid var(--accent); border-radius: 10px; padding: 14px 16px; margin: 4px 0 14px; }
.planes-cta-texto { font-size: 13px; color: var(--text); margin: 0 0 10px; line-height: 1.5; }
.planes-cta-btn { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; padding: 9px 18px; }
.planes-cta-fallback { font-size: 13px; color: var(--text); margin: 0; }
.vigencia-ok { background: var(--ok-bg); color: var(--ok-text); }
.vigencia-porvencer { background: var(--yellow-bg); color: var(--yellow-text); }
.vigencia-vencida { background: var(--red-bg); color: var(--red-text); }
.state-Conectado { background: var(--ok-bg); color: var(--ok-text); }
.state-Desconectado, .state-Error { background: var(--red-bg); color: var(--red-text); }

/* flex-wrap: nowrap a proposito (Fase 2, parte 30) -- antes envolvia los
   botones en 2 lineas apenas la celda se quedaba corta, aun con espacio de
   sobra en la pantalla (bug real reportado por el usuario: el ancho se
   repartia mal entre columnas, no faltaba ancho real). Con los botones de
   accion pasados a iconos (ver .btn-icon abajo) el ancho total de esta fila
   bajo drasticamente -- ya no deberia necesitarse el scroll de
   `.table-scroll` en una pantalla de escritorio normal; ese scroll sigue
   siendo el respaldo real solo para ventanas realmente angostas. */
.row-actions { display: flex; gap: 4px; flex-wrap: nowrap; white-space: nowrap; align-items: center; }
/* Compound selector (no !important) para ganarle en especificidad a
   `.accounts-table td` -- la columna de Acciones necesita menos padding
   lateral que el resto para dejarle mas margen de sobra al ancho total de la
   tabla (ver "Fase 2 (parte 30)", el objetivo es evitar el scroll horizontal
   en una pantalla normal). */
.accounts-table td.row-actions { padding-left: 10px; padding-right: 10px; }
/* Parte 130: la celda de acciones era `display: flex` y dejaba de ser una celda de tabla -- su borde inferior no coincidía con el
   del resto de la fila (se veían líneas cortadas). Vuelve a ser celda; los botones van en línea con el mismo espacio. */
td.row-actions { display: table-cell; white-space: nowrap; vertical-align: middle; }
td.row-actions > * { vertical-align: middle; }
td.row-actions > * + * { margin-left: 4px; }

.btn-sm {
  padding: 6px 12px;
  font-size: 12.5px;
  border-radius: 6px;
  border: none;
  cursor: pointer;
  font-weight: 700;
  display: inline-block;
  text-decoration: none;
}

/* Botones de accion de la tabla de instancias, como icono en vez de texto --
   antes eran 5-6 botones de texto ("Ver chats", "Cerrar sesión", etc.) que
   sumaban ~500px de ancho por fila, forzando scroll horizontal aun con
   espacio de sobra en la pantalla (bug real reportado por el usuario, "Fase
   2 (parte 30)"). El significado de cada boton se sigue explicando por
   color (ver .btn-qr/.btn-restart/.btn-disconnect/.btn-rename/.btn-del mas
   abajo, sin cambios) y por `title`/`aria-label` al pasar el mouse. */
.btn-icon {
  width: 32px;
  height: 32px;
  padding: 0;
  font-size: 15px;
  line-height: 1;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
/* Parte 130 («un mismo idioma visual», pedido del usuario): los botones de acción de las tablas son FICHAS como las del menú
   lateral -- una baldosa del color de lo que hacen, sin borde, que se intensifica al pasar el mouse. El color dice qué pasa:
   verde = abrir o actuar, ámbar = pausar o cortar, rojo = borrar, gris = ver o editar; la ficha verde LLENA marca lo que ya está
   activo o listo (QR por escanear, webhook o bot configurado, reactivar). Mismo ancho de siempre (32px, regla 62). */
.row-actions .btn-icon { --ic: var(--muted); border: none; border-radius: 9px; color: var(--ic); background: var(--card-bg-2); background: color-mix(in srgb, var(--ic) 15%, transparent); transition: background 0.15s ease, color 0.15s ease, transform 0.15s ease; }
.row-actions .btn-icon.btn-rename { --ic: var(--muted); color: var(--text); }
.row-actions .btn-icon.btn-restart { --ic: var(--accent); }
.row-actions .btn-icon.btn-disconnect { --ic: var(--yellow-text); }
.row-actions .btn-icon.btn-del { --ic: var(--red-text); }
.row-actions .btn-icon.btn-qr { --ic: var(--accent); background: var(--accent); color: var(--on-accent); box-shadow: 0 6px 16px -10px var(--accent); }
.row-actions .btn-icon:hover:not(:disabled) { background: color-mix(in srgb, var(--ic) 28%, transparent); transform: translateY(-1px); }
.row-actions .btn-icon.btn-qr:hover:not(:disabled) { background: color-mix(in srgb, var(--accent) 85%, var(--text)); }
.row-actions .btn-icon:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.row-actions .btn-icon:disabled { opacity: 0.4; }
@media (prefers-reduced-motion: reduce) { .row-actions .btn-icon, .row-actions .btn-icon:hover { transition: none; transform: none; } }

.btn-qr { background: var(--accent); color: #06110d; }
.btn-qr:hover { background: #45dcb2; }
.btn-rename { background: var(--card-bg-2); color: var(--text); border: 1px solid var(--border); }
.btn-rename:hover { border-color: var(--accent-dim); }
.btn-restart { background: var(--card-bg-2); color: var(--ok-text); border: 1px solid rgba(52, 208, 163, 0.25); }
.btn-restart:hover { border-color: var(--ok-text); }
.btn-disconnect { background: var(--yellow-bg); color: var(--yellow-text); border: 1px solid rgba(233, 179, 60, 0.25); }
.btn-disconnect:hover { background: rgba(233, 179, 60, 0.22); }
.btn-del { background: var(--red-bg); color: var(--red-text); border: 1px solid rgba(233, 92, 92, 0.25); }
.btn-del:hover { background: rgba(233, 92, 92, 0.22); }
.btn-sm:disabled { opacity: 0.45; cursor: not-allowed; }

.footer {
  text-align: center;
  color: var(--muted);
  font-size: 12.5px;
  margin-top: 12px;
}

.modal {
  position: fixed;
  inset: 0;
  /* Encima del menú lateral (40, que al pasar el mouse se desplegaba SOBRE la ventana abierta, visto en la parte 130) y de los
     menús de la lista de chats (60); debajo de los tooltips (999), los desplegables (1000) y los avisos (2000). Los diálogos de
     confirmación de ui.js también son `.modal` y se agregan al final del documento, así que igual quedan encima de otra ventana. */
  z-index: 100;
  background: rgba(4, 8, 12, 0.7);
  display: flex;
  align-items: center;
  justify-content: center;
}

.modal.hidden { display: none; }

.modal-content {
  --pad-modal: 28px; /* lo usa el pie de la ventana (.pie-ventana) para llegar de borde a borde */
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: var(--pad-modal);
  text-align: center;
  max-width: 340px;
  position: relative;
  /* Parte 130: una ventana más alta que la pantalla se desplaza por dentro. Antes quedaba cortada arriba y abajo sin forma de
     llegar al final (el fondo oscuro no se desplaza). */
  max-height: calc(100vh - 32px);
  overflow-y: auto;
}

.modal-content h3 { margin-top: 0; }

/* OJO (parte 182): esta regla NO lleva el estilo de ninguna imagen concreta. Durante mucho tiempo
   traía el del código QR -- 240x240, fondo blanco y 8 px de relleno -- y se lo aplicaba a CUALQUIER
   imagen dentro de CUALQUIER ventana. Así, la foto de perfil de la ficha de vinculación y el logo de
   la empresa salían con un recuadro blanco alrededor y corridos 12 px hacia abajo, sin que nada
   fallara: una foto cuadrada dentro del círculo se veía como una calcomanía con marco blanco (lo vio
   el usuario en real). El fondo blanco y la zona de silencio son del QR, que los necesita para poder
   escanearse, y por eso hoy viven en `.qr-marco img`. Una imagen NUEVA en una ventana define su
   propio tamaño y su propio fondo; lo vigila `test/reglas-estaticas.test.js`. */
.modal-content img { max-width: 100%; }

#vigenciaModal .modal-content,
#productoModal .modal-content {
  text-align: left;
  max-width: 380px;
}

/* Bot de IA por instancia (Fase 2, parte 120) -- formulario largo, por eso
   el modal es mas ancho y scrollea por dentro en vez de salirse de la
   pantalla. Solo tokens de color ya definidos (regla dura 44). */
#botModal .modal-content {
  text-align: left;
  width: min(1440px, max(calc(100vw - 160px), min(94vw, 760px)));
  max-width: none;
  max-height: 90vh;
  overflow-y: auto;
}
.bot-seccion {
  border-top: 1px solid var(--border);
  margin-top: 18px;
  padding-top: 14px;
}
.bot-seccion h4 { margin: 0 0 4px; font-size: 14px; color: var(--text); }
.bot-seccion .hint { margin: 0 0 10px; }
.bot-grid { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.bot-fila {
  display: flex;
  gap: 8px;
  align-items: flex-start;
  margin-bottom: 8px;
}
.bot-fila .field { min-width: 0; }
.bot-fila.hidden, .bot-campo-condicional.hidden { display: none; }
/* Marca del asistente (parte 130): vista previa con el mismo verde de la burbuja saliente del chat. */
.bot-marca { margin-top: 6px; padding-top: 12px; border-top: 1px dashed var(--border); }
.bot-marca-vista { display: flex; justify-content: flex-end; margin: 4px 0 8px; }
.bot-marca-vista.hidden { display: none; }
.bot-marca-burbuja { max-width: 340px; padding: 8px 12px; border-radius: 12px 4px 12px 12px; background: var(--accent-dim); color: var(--burbuja-saliente-texto); font-size: 13.5px; line-height: 1.45; }
.bot-marca-burbuja p { margin: 0; }
.bot-marca-burbuja p + p { margin-top: 8px; }
.bot-marca-burbuja .marca-cursiva { font-style: italic; opacity: 0.9; }
.bot-marca-burbuja .marca-negrita { font-weight: 700; }
/* Estilos de la firma (decimoquinto addendum): con la barra de la cita, o sombreada como el «código en línea» de WhatsApp. */
.bot-marca-burbuja .marca-con-barra { padding-left: 9px; border-left: 3px solid color-mix(in srgb, currentColor 45%, transparent); }
.bot-marca-burbuja .marca-sombreada { justify-self: start; display: inline-block; font-family: 'Courier New', Consolas, monospace; font-size: 0.92em; padding: 1px 6px; border-radius: 4px; background: color-mix(in srgb, currentColor 16%, transparent); }
.bot-marca-burbuja.cita p { padding-left: 10px; border-left: 3px solid currentColor; opacity: 0.92; }
.bot-marca-burbuja.cita p + p { margin-top: 0; padding-top: 6px; }
.bot-marca-nota { margin: 0; font-size: 12px; }
.bot-check {
  display: flex;
  align-items: center;
  gap: 8px;
  font-weight: 600;
  color: var(--text);
  cursor: pointer;
}
.bot-dias { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.bot-dias label { display: flex; align-items: center; gap: 4px; font-weight: 400; color: var(--text); cursor: pointer; }
#botAdminBox { margin-top: 12px; }
#botAdminBox.hidden { display: none; }
/* Estado real del bot (parte 123): dice la verdad sobre si va a atender ahora
   mismo, y si no, por que. Tres tonos con los tokens de siempre (regla 44). */
.bot-estado {
  margin: 12px 0 0;
  padding: 10px 12px;
  border-radius: 8px;
  font-size: 13px;
  line-height: 1.45;
  background: var(--card-bg-2);
  border: 1px solid var(--border);
  color: var(--muted);
}
.bot-estado.hidden { display: none; }
.bot-estado.ok { background: var(--ok-bg); border-color: transparent; color: var(--ok-text); }
.bot-estado.aviso { background: var(--yellow-bg); border-color: transparent; color: var(--yellow-text); }
.bot-consumo-linea { margin: 6px 0 0; font-size: 12.5px; color: var(--muted); }

/* "Probar mi bot" (parte 123, fase 3): simulador de conversacion dentro del modal. Solo tokens (regla 44). */
.bot-probar-hilo {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 90px;
  max-height: 300px;
  overflow-y: auto;
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--card-bg-2);
}
.bot-burbuja {
  max-width: 82%;
  padding: 8px 12px;
  border-radius: 10px;
  font-size: 13.5px;
  line-height: 1.45;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.bot-burbuja.cliente { align-self: flex-end; background: var(--accent-glow); border: 1px solid var(--accent-dim); color: var(--text); }
.bot-burbuja.bot { align-self: flex-start; background: var(--card-bg); border: 1px solid var(--border); color: var(--text); }
.bot-burbuja.escribiendo { color: var(--muted); font-style: italic; }
.bot-nota { align-self: center; text-align: center; max-width: 92%; padding: 6px 12px; border-radius: 8px; font-size: 12.5px; background: var(--yellow-bg); color: var(--yellow-text); overflow-wrap: anywhere; }
.bot-probar-vacio { margin: auto; color: var(--muted); font-size: 12.5px; text-align: center; }
.bot-probar-entrada { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-top: 10px; }
.bot-probar-entrada input { flex: 1 1 260px; min-width: 0; }

/* IA del bot: modo/tope (admin), clave propia e informe de consumo (parte 124). Solo tokens (regla 44). */
.bot-ia-admin { display: flex; gap: 12px; flex-wrap: wrap; margin: 12px 0 8px; }
.bot-ia-admin .field { flex: 1 1 240px; min-width: 0; }
.bot-ia-clave { margin: 10px 0 14px; }
.bot-ia-clave .hint { margin: 4px 0 0; }
.bot-informe-controles { display: flex; gap: 12px; flex-wrap: wrap; margin: 12px 0 8px; }
.bot-informe-controles .field { flex: 1 1 140px; min-width: 0; }
.bot-informe-explicacion { list-style: none; margin: 8px 0 14px; padding: 0; display: grid; gap: 4px; font-size: 13px; }
.bot-informe-explicacion li { padding-left: 14px; position: relative; color: var(--text); }
.bot-informe-explicacion li::before { content: ""; position: absolute; left: 0; top: 0.65em; width: 6px; height: 2px; background: var(--accent); }
.bot-informe-explicacion li:first-child { font-weight: 600; }
.bot-informe-tabla { width: 100%; border-collapse: collapse; font-size: 13px; }
.bot-informe-tabla th, .bot-informe-tabla td { padding: 7px 10px; border-bottom: 1px solid var(--border); text-align: left; }
.bot-informe-tabla th { color: var(--muted); font-weight: 500; font-size: 12px; }
.bot-informe-tabla .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.bot-informe-tabla tfoot td { font-weight: 700; border-top: 2px solid var(--border); }
.bot-informe-tabla tr.vacia td { color: var(--muted); }
.bot-informe-tabla.sin-costo .col-costo { display: none; }
.bot-informe-tabla.sin-tokens .col-tokens { display: none; }
/* Diseño 2, segunda fase: con qué IA se respondió en cada periodo, y el resumen por IA. La columna se oculta con el mismo
   criterio que los tokens (solo detalle técnico, regla 73). */
.bot-informe-tabla.sin-tokens .col-ia { display: none; }
.bot-informe-tabla .col-ia { color: var(--muted); white-space: nowrap; }
.bot-modelos { margin: 14px 0 10px; }
.bot-modelos h5 { margin: 0 0 6px; font-size: 13px; color: var(--text); }
.bot-modelos.hidden { display: none; }
#botInformeResultado.hidden { display: none; }
.bot-ia-clave.hidden { display: none; }
/* Parte 126: el modal del bot aprovecha el ancho. Con pantalla ancha, la configuracion va a la izquierda y "Probar mi bot" y
   "Consumo de IA" a la derecha, siempre a la vista mientras se edita; en pantalla angosta todo vuelve a una columna. */
.bot-cols { display: block; }
.bot-admin-cols { display: block; }
@media (min-width: 1240px) {
  .bot-cols { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(400px, 1fr); gap: 0 32px; align-items: start; }
  .bot-admin-cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 0 32px; align-items: start; }
  .bot-admin-cols > div > :first-child { margin-top: 0; }
  .bot-col-lateral { position: sticky; top: 0; max-height: calc(90vh - 140px); overflow-y: auto; padding-right: 6px; }
  .bot-col-config > .bot-seccion:first-child, .bot-col-lateral > .bot-seccion:first-child { border-top: 0; margin-top: 12px; padding-top: 0; }
}
/* Pie de las ventanas (pedido del usuario, parte 130): «Ir arriba» y «Cerrar» (y «Guardar» donde lo hay), para no tener que subir
   hasta la X ni elegir entre barras de desplazamiento. Estrenado en el Bot de IA y extendido a toda ventana larga con
   `UI.pieVentana()` (ui.js): se pega al fondo de lo que se desplaza y ocupa todo el ancho, borde a borde. */
.pie-ventana,
#botModal .bot-pie {
  position: sticky;
  bottom: calc(-1 * var(--pad-modal, 28px));
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin: 18px calc(-1 * var(--pad-modal, 28px)) calc(-1 * var(--pad-modal, 28px));
  padding: 12px var(--pad-modal, 28px) var(--pad-modal, 28px);
  background: var(--card-bg);
  border-top: 1px solid var(--border);
  border-radius: 0 0 var(--radius) var(--radius);
}
.pie-ventana[hidden] { display: none; }
.pie-ventana > .acciones-pie:only-child { margin-left: auto; }
.bot-acciones-pie, .acciones-pie { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; justify-content: flex-end; }
/* Cada botón lleva su ícono en una «ficha» del color de su acción (mismo lenguaje que el menú lateral, parte 129) y un gesto propio
   al pasar el mouse: la flecha sube, la X gira y el botón se eleva con un brillo de su color. Solo tokens (regla 44). */
.btn-pie {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 6px 18px 6px 6px;
  border-radius: 12px;
  font-weight: 600;
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.btn-pie .pie-ic {
  box-sizing: content-box;
  width: 16px;
  height: 16px;
  padding: 7px;
  border-radius: 9px;
  fill: none;
  stroke: var(--ic, var(--muted));
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
  background: var(--card-bg); /* respaldo si el navegador no soporta color-mix */
  background: color-mix(in srgb, var(--ic, var(--muted)) 16%, transparent);
  transition: background 0.18s ease, transform 0.25s ease;
}
.btn-pie.pie-arriba { --ic: var(--ic-cian); }
.btn-pie.pie-cerrar { --ic: var(--ic-rosa); }
.btn-pie.pie-guardar { --ic: var(--on-accent); padding-right: 22px; }
.btn-pie.pie-guardar .pie-ic { background: color-mix(in srgb, var(--on-accent) 14%, transparent); }
.btn-pie:hover:not(:disabled) {
  transform: translateY(-1px);
  border-color: color-mix(in srgb, var(--ic) 50%, var(--border));
  box-shadow: 0 8px 20px -10px color-mix(in srgb, var(--ic) 70%, transparent);
}
.btn-pie:hover:not(:disabled) .pie-ic { background: color-mix(in srgb, var(--ic) 28%, transparent); }
.btn-pie.pie-guardar:hover:not(:disabled) { box-shadow: 0 10px 24px -10px var(--accent); }
.btn-pie.pie-guardar:hover:not(:disabled) .pie-ic { background: color-mix(in srgb, var(--on-accent) 22%, transparent); }
.btn-pie.pie-arriba:hover .pie-ic { transform: translateY(-2px); }
.btn-pie.pie-cerrar:hover .pie-ic { transform: rotate(90deg); }
.btn-pie:active:not(:disabled) { transform: translateY(0); }
.btn-pie:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn-pie:disabled { opacity: 0.6; cursor: default; }
.btn-pie[hidden] { display: none; }
@media (prefers-reduced-motion: reduce) {
  .btn-pie, .btn-pie .pie-ic { transition: none; }
  .btn-pie:hover:not(:disabled), .btn-pie.pie-arriba:hover .pie-ic, .btn-pie.pie-cerrar:hover .pie-ic { transform: none; }
}
/* Parte 127: clave dedicada (admin), opciones del informe y lista de instancias con la IA detenida. Solo tokens (regla 44). */
.bot-dedicada { margin: 12px 0 8px; padding-top: 10px; border-top: 1px solid var(--border); }
.bot-dedicada.hidden, #botRespaldoFila.hidden { display: none; }
.bot-dedicada .hint { margin: 4px 0 8px; }
.bot-informe-opcion { font-weight: 500; font-size: 13px; }
/* La regla .bot-probar-entrada input estira todo input a 260px: una casilla dentro de esa fila no debe heredarla. */
.bot-probar-entrada .bot-check { flex: 0 0 auto; white-space: nowrap; }
.bot-probar-entrada .bot-check input[type="checkbox"] { flex: 0 0 auto; width: auto; min-width: 0; }
.bot-detalles { margin: 4px 0 10px; font-size: 13px; color: var(--muted); }
.bot-detalles summary { cursor: pointer; color: var(--accent); }
.bot-detalles p { margin: 6px 0 0; line-height: 1.5; }
#botPorClaveLinea:empty { display: none; }
.bot-detenidas-tabla { width: 100%; border-collapse: collapse; font-size: 13px; }
.bot-detenidas-tabla th, .bot-detenidas-tabla td { padding: 7px 10px; border-bottom: 1px solid var(--border); text-align: left; vertical-align: top; }
.bot-detenidas-tabla th { color: var(--muted); font-weight: 500; font-size: 12px; }
.bot-detenidas-tabla .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.bot-detenidas-tabla .detalle { display: block; color: var(--muted); font-size: 12px; margin-top: 2px; }
#botPausadosIA { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
#botPausadosIA.hidden { display: none; }
#botInformeDesdeNota.hidden { display: none; }
/* Aviso de responsabilidad sobre la clave propia (parte 125): tiene que verse, no perderse entre los hints. */
.bot-ia-responsabilidad { margin: 0 0 12px; padding: 10px 12px; border-radius: 8px; border: 1px solid var(--yellow-text); background: var(--yellow-bg); color: var(--text); font-size: 13px; line-height: 1.45; }
.bot-ia-responsabilidad strong { color: var(--yellow-text); }
.bot-ia-responsabilidad ul { margin: 6px 0 8px; padding-left: 18px; display: grid; gap: 3px; }
.bot-ia-responsabilidad .bot-check { margin: 4px 0 0; font-weight: 600; }
#botModeloLinea:empty { display: none; }

/* ===== Bot de IA: rediseño de la parte 130, según la maqueta aprobada por el usuario =====
   Pestañas · estado con interruptor · 4 pasos · secciones plegables con resumen · tablas que se editan fila por fila ·
   «Pruébalo aquí» siempre a la vista. Mismo lenguaje de «fichas» de color que el menú lateral. Solo tokens (regla 44). */

/* Ficha: ícono dentro de una baldosa redondeada del color de lo que representa (el mismo recurso del menú lateral). */
svg.ficha {
  box-sizing: content-box;
  width: 20px;
  height: 20px;
  padding: 7px;
  border-radius: 10px;
  flex: 0 0 auto;
  fill: none;
  stroke: var(--ic, var(--muted));
  stroke-width: 1.8;
  stroke-linecap: round;
  stroke-linejoin: round;
  vertical-align: middle;
  background: var(--card-bg-2); /* respaldo si el navegador no soporta color-mix */
  background: color-mix(in srgb, var(--ic, var(--muted)) 16%, transparent);
}

.bot-panel[hidden] { display: none; }
#botPanelConsumo, #botPanelAdmin { margin-top: 18px; }
#botInformeError:empty { display: none; }
/* Alto fijo: cambiar de pestaña no hace saltar la ventana (antes se recentraba con cada alto distinto). El pie queda abajo aunque
   la pestaña sea corta. */
#botModal .modal-content { height: 90vh; display: flex; flex-direction: column; }
#botModal .modal-content > * { flex-shrink: 0; }
#botModal .bot-pie { margin-top: auto; }
#botModal .bot-panel { padding-bottom: 18px; }
.bot-tabs { display: flex; gap: 28px; border-bottom: 1px solid var(--border); margin: 8px 0 18px; overflow-x: auto; scrollbar-width: none; }
.bot-tab {
  font: inherit;
  font-size: 14px;
  font-weight: 600;
  background: transparent;
  border: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  color: var(--muted);
  padding: 10px 2px;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.15s ease, border-color 0.15s ease;
}
.bot-tab .ic { width: 16px; height: 16px; }
.bot-tab:hover { color: var(--text); }
.bot-tab.activa { color: var(--text); border-bottom-color: var(--accent); }
.bot-tab.activa .ic { color: var(--accent); }
.bot-tab:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.bot-tab.hidden { display: none; }

/* Estado del asistente + interruptor */
/* Modo del asistente (parte 181): el punto intermedio entre «apagado» y «contesta solo». Solo se
   ve con el asistente encendido -- apagado no hace ninguna de las dos cosas, y ofrecer elegir
   entre dos maneras de no hacer nada confunde. Compuestas `.hidden` (reglas 28 y 134). */
.bot-modo {
  display: grid;
  gap: 8px;
  margin: 10px 0 0;
}
.bot-modo.hidden { display: none; }
.bot-modo-resumen {
  font-size: 12.5px;
  color: var(--muted);
  line-height: 1.5;
}
.bot-modo-resumen.hidden { display: none; }

.bot-estado-barra {
  --punto: var(--muted);
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 14px 18px;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: var(--card-bg-2);
}
.bot-estado-barra.ok { --punto: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); border-color: color-mix(in srgb, var(--accent) 28%, transparent); }
.bot-estado-barra.aviso { --punto: var(--yellow-text); background: color-mix(in srgb, var(--yellow-text) 8%, transparent); border-color: color-mix(in srgb, var(--yellow-text) 28%, transparent); }
.bot-estado-barra.apagado { --punto: var(--yellow-text); background: color-mix(in srgb, var(--yellow-text) 6%, transparent); border-color: color-mix(in srgb, var(--yellow-text) 22%, transparent); }
.bot-estado-barra.pendiente { border-style: dashed; border-color: var(--yellow-text); }
.bot-estado-punto { width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; background: var(--punto); box-shadow: 0 0 0 4px color-mix(in srgb, var(--punto) 18%, transparent); }
.bot-estado-barra.ok .bot-estado-punto { animation: bot-latido 2.6s ease-in-out infinite; }
@keyframes bot-latido { 50% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--punto) 6%, transparent); } }
.bot-estado-textos { flex: 1 1 auto; min-width: 0; }
.bot-estado-titulo { font-weight: 600; font-size: 15px; color: var(--text); }
.bot-estado-detalle { color: var(--muted); font-size: 13px; margin-top: 2px; line-height: 1.45; }
.bot-estado-barra.pendiente .bot-estado-detalle { color: var(--yellow-text); font-weight: 600; }
.bot-interruptor { position: relative; display: inline-flex; align-items: center; gap: 10px; flex: 0 0 auto; padding: 6px; font-size: 14px; font-weight: 600; color: var(--text); cursor: pointer; user-select: none; }
.bot-interruptor input { position: absolute; left: 6px; top: 6px; z-index: 1; width: 44px; height: 26px; margin: 0; opacity: 0; cursor: pointer; }
.bot-riel { position: relative; width: 44px; height: 26px; border-radius: 999px; background: var(--border); flex: 0 0 auto; transition: background 0.2s ease; }
.bot-riel span { position: absolute; top: 3px; left: 3px; width: 20px; height: 20px; border-radius: 50%; background: var(--muted); transition: left 0.2s ease, background 0.2s ease; }
.bot-interruptor input:checked + .bot-riel { background: var(--accent); }
.bot-interruptor input:checked + .bot-riel span { left: 21px; background: var(--on-accent); }
.bot-interruptor input:focus-visible + .bot-riel { outline: 2px solid var(--accent); outline-offset: 3px; }
.bot-interruptor:has(input:disabled) { opacity: 0.55; cursor: not-allowed; }
.bot-interruptor input:disabled { cursor: not-allowed; }
#botModal #botBorrador, #botModal #botPausadosIA { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
#botModal #botBorrador.hidden, #botModal #botPausadosIA.hidden { display: none; }

/* Los 4 pasos (primera vez: dentro de una tarjeta con la introducción) */
.bot-pasos-caja { margin-top: 16px; }
.bot-pasos-caja.primera-vez { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; padding: 20px 22px; background: var(--card-bg-2); border: 1px solid var(--border); border-radius: 12px; }
.bot-pasos-intro { flex: 1 1 220px; }
.bot-pasos-titulo { font-size: 19px; font-weight: 700; letter-spacing: -0.01em; color: var(--text); text-wrap: balance; }
.bot-pasos-sub { color: var(--muted); font-size: 14px; margin-top: 4px; }
.bot-pasos { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; flex: 3 1 620px; }
.bot-paso {
  width: 100%;
  height: 100%;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 14px;
  border-radius: 12px;
  background: var(--card-bg-2);
  border: 1px solid var(--border);
  color: var(--text);
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.15s ease, transform 0.15s ease, background 0.15s ease;
}
.primera-vez .bot-paso { background: var(--card-bg); }
.bot-paso:hover { transform: translateY(-1px); border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); }
.bot-paso:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.bot-paso.actual { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 6%, var(--card-bg-2)); }
.bot-bola { width: 30px; height: 30px; border-radius: 50%; flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center; font-size: 14px; font-weight: 700; color: var(--muted); border: 1px solid color-mix(in srgb, var(--muted) 45%, var(--border)); box-sizing: border-box; }
.bot-paso.actual .bot-bola { border: 2px solid var(--accent); color: var(--accent); }
.bot-paso.hecho .bot-bola { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.bot-bola .ic { width: 16px; height: 16px; stroke-width: 2.6; }
.bot-paso-txt { display: flex; flex-direction: column; min-width: 0; }
.bot-paso-n { font-size: 11px; letter-spacing: 0.07em; text-transform: uppercase; color: var(--muted); font-weight: 700; }
.bot-paso-t { font-size: 14px; font-weight: 600; margin-top: 1px; }
.bot-paso-e { font-size: 12.5px; color: var(--muted); margin-top: 1px; }
.bot-paso.hecho .bot-paso-e { color: var(--accent); }
.bot-paso.actual .bot-paso-e { color: var(--yellow-text); font-weight: 600; }
@media (max-width: 900px) { .bot-pasos { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (prefers-reduced-motion: reduce) { .bot-paso, .bot-paso:hover { transition: none; transform: none; } .bot-estado-barra.ok .bot-estado-punto { animation: none; } }

/* Ejemplos para empezar */
.bot-ejemplos { margin-top: 4px; }
.bot-ejemplos.hidden { display: none; }
/* Cinco ejemplos (parte 130, se sumó «Restaurante o cafetería»): cinco columnas a lo ancho; en dos columnas el último ocupa la fila
   entera para no quedar solo a un lado. */
.bot-ejemplos-grid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px; }
@media (max-width: 1100px) {
  .bot-ejemplos-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .bot-ejemplos-grid > .bot-ejemplo:last-child:nth-child(odd) { grid-column: 1 / -1; }
}
@media (max-width: 560px) { .bot-ejemplos-grid, .bot-pasos { grid-template-columns: minmax(0, 1fr); } }
.bot-ejemplo.bot-sec-cab { background: var(--card-bg-2); border: 1px solid var(--border); border-radius: 12px; transition: border-color 0.15s ease, transform 0.15s ease; }
.bot-ejemplo.bot-sec-cab:hover { border-color: color-mix(in srgb, var(--ic, var(--accent)) 55%, var(--border)); transform: translateY(-1px); }
.bot-ejemplo:has(.ic-azul) { --ic: var(--ic-azul); }
.bot-ejemplo:has(.ic-violeta) { --ic: var(--ic-violeta); }
.bot-ejemplo:has(.ic-cian) { --ic: var(--ic-cian); }
.bot-ejemplo:has(.ic-naranja) { --ic: var(--ic-naranja); }

/* Encabezados de grupo con su insignia */
.bot-grupo { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin: 26px 0 12px; }
.bot-grupo h4 { margin: 0; font-size: 17px; font-weight: 700; letter-spacing: -0.01em; color: var(--text); }
.bot-grupo p { margin: 0; color: var(--muted); font-size: 13.5px; }
.bot-grupo.bloqueado { opacity: 0.6; }
.bot-col-config > .bot-grupo:first-child, .bot-tarjeta > .bot-grupo:first-child, #botConsumoSeccion > .bot-grupo:first-child { margin-top: 2px; }
.bot-tarjeta > .bot-grupo:first-child { margin-bottom: 2px; }
.bot-insignia { font-size: 11px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; padding: 4px 9px; border-radius: 999px; white-space: nowrap; }
.bot-insignia.obl { background: var(--ok-bg); color: var(--ok-text); }
.bot-insignia.rec { background: color-mix(in srgb, var(--ic-azul) 14%, transparent); color: var(--ic-azul); }
.bot-insignia.opc { background: var(--card-bg-2); color: var(--muted); border: 1px solid var(--border); }

.bot-tarjeta { display: flex; flex-direction: column; gap: 14px; padding: 18px; background: var(--card-bg-2); border: 1px solid var(--border); border-radius: 12px; }
.bot-tarjeta.actual { border-color: var(--accent); }
.bot-tarjeta.hidden { display: none; }
.bot-tarjeta .field { min-width: 0; }
.bot-tarjeta .hint { margin: 0; }
.bot-faltan { display: flex; flex-wrap: wrap; gap: 8px 18px; align-items: center; font-size: 13px; color: var(--muted); }
.bot-faltan.hidden { display: none; }
.bot-faltan strong { color: var(--text); font-weight: 600; }
.bot-falta { display: inline-flex; align-items: center; gap: 6px; }
.bot-falta::before { content: ""; width: 14px; height: 14px; border-radius: 50%; border: 1.5px solid var(--muted); box-sizing: border-box; flex: 0 0 auto; }
.bot-falta.hecho { color: var(--accent); }
.bot-falta.hecho::before { background: var(--accent); border-color: var(--accent); }

/* Secciones plegables: cabecera con ficha, título, resumen de una línea, chip y flecha */
.bot-secciones { display: flex; flex-direction: column; gap: 12px; }
.bot-sec { background: var(--card-bg-2); border: 1px solid var(--border); border-radius: 12px; overflow: hidden; transition: border-color 0.15s ease; }
.bot-sec.abierta { border-color: color-mix(in srgb, var(--muted) 42%, var(--border)); }
.bot-sec-cab { display: flex; align-items: center; gap: 14px; width: 100%; box-sizing: border-box; padding: 13px 16px; background: transparent; border: none; color: var(--text); font: inherit; text-align: left; cursor: pointer; }
.bot-sec > .bot-sec-cab:hover { background: color-mix(in srgb, var(--text) 3%, transparent); }
.bot-sec-cab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.bot-sec-txt { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.bot-sec-tit { font-weight: 600; font-size: 15px; }
.bot-sec-res { color: var(--muted); font-size: 13px; margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bot-sec-flecha { color: var(--muted); width: 16px; height: 16px; transition: transform 0.2s ease; }
.bot-sec-cab[aria-expanded="true"] .bot-sec-flecha { transform: rotate(90deg); }
.bot-sec-cuerpo { padding: 4px 16px 16px; display: flex; flex-direction: column; gap: 12px; }
.bot-sec-cuerpo[hidden] { display: none; }
.bot-sec-cuerpo .field { min-width: 0; }
.bot-sec-cuerpo .bot-grid { margin-bottom: 0; }
.bot-sec-cuerpo .bot-dias { margin: 4px 0 0; }
.bot-sec-ayuda { margin: 0; color: var(--muted); font-size: 13px; line-height: 1.5; }
.bot-sec-ayuda.hidden { display: none; }
.bot-secciones.bloqueado .bot-sec { opacity: 0.55; }
.bot-secciones.bloqueado .bot-sec-cab { cursor: not-allowed; }
#botToquesFinos.hidden { display: none; }
.bot-chip { display: inline-flex; align-items: center; gap: 5px; flex: 0 0 auto; font-size: 12px; font-weight: 600; padding: 4px 10px; border-radius: 999px; white-space: nowrap; background: var(--card-bg); color: var(--muted); border: 1px solid var(--border); }
.bot-chip.ok { background: var(--ok-bg); color: var(--ok-text); border-color: transparent; }
.bot-chip .ic { width: 13px; height: 13px; stroke-width: 2.6; }

/* Tabla del catálogo y de las preguntas frecuentes: cada fila se ve como tabla y se edita al tocarla */
.bot-lista { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; background: var(--card-bg); }
.bot-lista-cab, .bot-fila-vista { display: grid; align-items: center; }
.bot-lista-catalogo .bot-lista-cab, .bot-lista-catalogo .bot-fila-vista { grid-template-columns: minmax(0, 1.1fr) minmax(64px, 0.45fr) minmax(0, 1.5fr) 40px; }
.bot-lista-faq .bot-lista-cab, .bot-lista-faq .bot-fila-vista { grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr) 40px; }
.bot-lista-cab { color: var(--muted); font-size: 11.5px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; }
.bot-lista-cab > span { padding: 10px 12px; }
.bot-lista:has(.bot-lista-filas:empty) .bot-lista-cab { display: none; }
.bot-lista-filas:not(:empty) + .bot-lista-vacia { display: none; }
.bot-lista-vacia { margin: 0; padding: 14px; color: var(--muted); font-size: 13px; text-align: center; }
.bot-lista-filas > .bot-fila { display: block; margin: 0; border-top: 1px solid var(--border); } /* la regla vieja de .bot-fila era flex: cada fila medía distinto */
.bot-lista:has(.bot-lista-filas:empty) { border-style: dashed; }
.bot-lista-filas.plegada > .bot-fila:nth-child(n+4):not(.editando) { display: none; }
.bot-fila-vista { cursor: pointer; background: var(--card-bg-2); }
.bot-fila-vista[hidden] { display: none; }
.bot-fila-vista:hover { background: color-mix(in srgb, var(--text) 4%, var(--card-bg-2)); }
.bot-celda { padding: 10px 12px; font-size: 13.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text); }
.bot-celda + .bot-celda { color: color-mix(in srgb, var(--text) 75%, var(--muted)); }
.bot-celda.vacia { color: var(--muted); font-style: italic; }
.bot-fila-editar { justify-self: center; width: 30px; height: 30px; display: inline-flex; align-items: center; justify-content: center; border: none; border-radius: 8px; background: transparent; color: var(--muted); cursor: pointer; }
.bot-fila-editar:hover, .bot-fila-editar:focus-visible { color: var(--accent); background: var(--accent-glow); outline: none; }
.bot-fila-editar .ic { width: 14px; height: 14px; }
.bot-tag-ejemplo { display: inline-block; margin-right: 8px; padding: 1px 7px; border-radius: 999px; border: 1px dashed var(--yellow-text); color: var(--yellow-text); font-size: 10.5px; font-weight: 700; font-style: normal; letter-spacing: 0.05em; text-transform: uppercase; vertical-align: 1px; }
.bot-fila.editando { background: var(--card-bg); box-shadow: inset 3px 0 0 var(--accent); }
.bot-fila-editor { padding: 12px 12px 12px 15px; display: flex; flex-direction: column; gap: 10px; }
.bot-fila-editor[hidden] { display: none; }
.bot-fila-campos { display: grid; gap: 8px; }
.bot-fila-campos input, .bot-fila-campos textarea { width: 100%; min-width: 0; box-sizing: border-box; }
.bot-fila-campos-catalogo { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); }
.bot-fila-campos-catalogo > [data-campo="descripcion"] { grid-column: 1 / -1; }
.bot-fila-editor-acciones { display: flex; justify-content: flex-end; gap: 8px; }
.bot-fila-editor-acciones .btn-sm { display: inline-flex; align-items: center; gap: 6px; }
.bot-fila-editor-acciones .ic { width: 14px; height: 14px; }
.bot-lista-acciones { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.bot-btn-agregar { display: inline-flex; align-items: center; gap: 8px; padding: 9px 14px; }
.bot-btn-agregar .ic { width: 16px; height: 16px; }
.bot-ver-todas { background: none; border: none; color: var(--muted); font: inherit; font-size: 13px; cursor: pointer; padding: 4px 2px; text-decoration: underline; text-underline-offset: 3px; }
.bot-ver-todas:hover { color: var(--text); }
.bot-ver-todas.hidden { display: none; }

/* Columnas: configuración a la izquierda, «Pruébalo aquí» a la derecha (430px, como la maqueta) */
#botModal .bot-cols { margin-top: 24px; }
@media (min-width: 1240px) {
  #botModal .bot-cols { grid-template-columns: minmax(0, 1fr) minmax(380px, 430px); gap: 0 28px; }
  #botModal .bot-col-lateral { top: 12px; margin-top: 2px; max-height: calc(90vh - 190px); padding-right: 2px; }
}
@media (max-width: 1239px) { #botModal .bot-col-lateral { margin-top: 26px; } }
/* En pantalla angosta el simulador va debajo de todo: sin tope de alto propio (la ventana entera se desplaza). */
@media (max-width: 1239px) { #botModal .bot-col-lateral { position: static; max-height: none; overflow: visible; } }

/* «Pruébalo aquí» */
.bot-probar { display: flex; flex-direction: column; gap: 14px; padding: 18px; background: var(--card-bg-2); border: 1px solid var(--border); border-radius: 12px; }
.bot-probar-cab { display: flex; align-items: center; gap: 12px; }
.bot-probar-textos { flex: 1 1 auto; min-width: 0; }
.bot-probar-titulo { font-weight: 700; font-size: 16px; color: var(--text); }
.bot-probar-sub { color: var(--muted); font-size: 13px; margin-top: 2px; }
#botModal .bot-probar-hilo { min-height: 300px; max-height: 420px; padding: 14px; gap: 10px; border-radius: 10px; background: var(--bg); }
#botModal .bot-probar-vacio { margin: auto; display: flex; flex-direction: column; align-items: center; gap: 14px; max-width: 320px; text-align: center; color: var(--muted); font-size: 14px; }
.bot-probar-vacio p { margin: 0; line-height: 1.5; }
.bot-probar-vacio svg.ficha { width: 26px; height: 26px; padding: 10px; border-radius: 12px; }
.bot-sugerencias { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; }
.bot-sugerencia { font: inherit; font-size: 12px; font-weight: 600; padding: 5px 11px; border-radius: 999px; background: var(--card-bg); color: var(--muted); border: 1px solid var(--border); cursor: pointer; transition: color 0.15s ease, border-color 0.15s ease; }
.bot-sugerencia:hover:not(:disabled) { color: var(--accent); border-color: var(--accent); }
.bot-sugerencia:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.bot-sugerencia:disabled { opacity: 0.5; cursor: not-allowed; }
#botModal .bot-burbuja { max-width: 80%; padding: 9px 12px; border-radius: 12px; font-size: 14px; }
#botModal .bot-burbuja.cliente { align-self: flex-start; background: var(--card-bg-2); border: 1px solid var(--border); border-top-left-radius: 4px; color: var(--text); }
#botModal .bot-burbuja.bot { align-self: flex-end; background: var(--accent-dim); border: 1px solid transparent; border-top-right-radius: 4px; color: var(--burbuja-saliente-texto); }
#botModal .bot-burbuja.escribiendo { font-style: italic; opacity: 0.85; }
.bot-probar .bot-probar-entrada { margin-top: 0; flex-wrap: nowrap; }
.bot-probar-pie { display: flex; justify-content: space-between; align-items: center; gap: 12px; color: var(--muted); font-size: 12.5px; }
.bot-btn-sutil { background: transparent; border: none; color: var(--muted); font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; padding: 6px 8px; border-radius: 8px; white-space: nowrap; }
.bot-btn-sutil:hover { color: var(--text); background: var(--card-bg); }
.bot-btn-sutil:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
#botProbarError:empty { display: none; }

/* Pestañas «Consumo de IA» y «Solo administrador» */
#botConsumoSeccion { border-top: 0; margin-top: 0; padding-top: 0; display: flex; flex-direction: column; gap: 12px; }
#botConsumoSeccion > .hint { margin: 0; }
#botConsumoSeccion .bot-estado { margin: 0; }
.bot-informe .bot-informe-controles { margin: 0; }
.bot-informe .bot-probar-entrada { margin-top: 0; }
#botAdminBox .bot-admin-cols { display: grid; grid-template-columns: minmax(0, 1fr); gap: 16px; }
@media (min-width: 1100px) { #botAdminBox .bot-admin-cols { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); } }
#botAdminBox .bot-tarjeta { gap: 10px; }
#botAdminBox .bot-ia-admin { margin: 0; }
#botAdminBox .bot-dedicada { margin: 4px 0 0; }

/* Pie del Bot: último guardado y errores a la izquierda, acciones a la derecha */
.bot-pie-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; flex: 1 1 240px; }
.bot-pie-info .hint { margin: 0; }
.bot-pie-info .form-error { margin: 0; min-height: 0; }
.bot-pie-info .form-error:empty { display: none; }

/* Servicio del Bot de IA en Configuracion (parte 123). */
.bot-servicio-estado { margin: 4px 0 10px; font-size: 13px; color: var(--muted); }
.bot-servicio-estado strong { color: var(--text); }
.bot-servicio-acciones { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }

/* Visor de archivo dentro del panel (PDF/imagenes) -- ver "Fase 2 (parte 21)".
   A diferencia de abrir el blob en una pestaña nueva (window.open), un
   navegador SI garantiza que un <iframe> intente renderizar el contenido en
   vez de descargarlo -- es la forma confiable de "visualizar ahi mismo". */
.archivo-preview-content {
  width: 90vw;
  height: 90vh;
  max-width: none;
  --pad-modal: 20px;
  padding: var(--pad-modal);
  display: flex;
  flex-direction: column;
}
.archivo-preview-content h3 { flex: none; }
.archivo-preview-contenido {
  flex: 1;
  width: 100%;
  overflow: auto;
  border-radius: 8px;
  background: #525659;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  padding: 16px;
}
.archivo-preview-contenido canvas { max-width: 100%; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.4); }

/* Gestion real de grupos (Fase 2, parte 76) -- modal de "Info del grupo". */
.grupo-info-content {
  width: 90vw;
  max-width: 480px;
  max-height: 85vh;
  --pad-modal: 20px;
  padding: var(--pad-modal);
  text-align: left;
  display: flex;
  flex-direction: column;
}
.grupo-info-cuerpo { overflow-y: auto; }
/* Selector de mapa real para "Compartir ubicación" (Fase 2, parte 80,
   addendum) -- mismo ancho/alto que .grupo-info-content, con lugar real
   para el mapa (Leaflet necesita una altura explícita, un div sin alto
   fijo se colapsa a 0px). */
.ubicacion-mapa-content {
  width: 90vw;
  max-width: 480px;
  --pad-modal: 20px;
  padding: var(--pad-modal);
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.ubicacion-mapa-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ubicacion-mapa-div {
  width: 100%;
  height: 320px;
  border-radius: var(--radius-sm, 8px);
  overflow: hidden;
  border: 1px solid var(--border);
}
/* Jerarquia visual explicita (bug real reportado por el usuario -- "toca
   adivinar de que es cada cosa" comparado con WhatsApp Desktop, que separa
   claramente nombre/subtitulo/descripcion) -- el nombre siempre mas grande y
   con mas peso que la descripcion. */
.grupo-info-header { padding-bottom: 10px; margin-bottom: 8px; border-bottom: 1px solid var(--border); }
.grupo-info-header-add { display: flex; align-items: center; gap: 10px; }
.grupo-info-nombre-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.grupo-info-nombre { font-size: 17px; font-weight: 700; }
.grupo-info-sub { color: var(--muted); font-size: 11.5px; margin: 2px 0 8px; }
.grupo-info-desc-row { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.grupo-info-desc { color: var(--muted); font-size: 13px; }
.grupo-agregar-btn { width: 100%; margin-bottom: 10px; }
.grupo-buscar-input {
  width: 100%;
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  padding: 8px 10px;
  margin-bottom: 8px;
  box-sizing: border-box;
}
.grupo-contactos-lista { max-height: 45vh; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; margin-bottom: 10px; }
.grupo-contacto-fila {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 6px;
  border-radius: 6px;
  cursor: pointer;
}
.grupo-contacto-fila:hover { background: rgba(255, 255, 255, 0.04); }
.grupo-contacto-fila input[type="checkbox"] { flex: none; }
.grupo-contacto-nombre { font-size: 13.5px; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.grupo-contacto-numero { font-size: 11.5px; color: var(--muted); flex: none; }
.grupo-confirmar-btn { width: 100%; }

/* Privacidad de cuenta (Fase 2, parte 83) -- mismo lenguaje visual que el
   resto de los modales de este archivo (grupo-info-content), una fila
   por categoria: etiqueta a la izquierda, control a la derecha (select
   nativo -- 8 de las 9 categorias son un simple enum cerrado de WhatsApp,
   no hace falta nada mas elaborado). */
.privacidad-lista { display: flex; flex-direction: column; gap: 10px; margin-bottom: 14px; }
.privacidad-fila {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.privacidad-fila label { font-size: 13px; color: var(--text); }
.privacidad-fila select {
  min-width: 190px;
  padding: 6px 8px;
  font-size: 12.5px;
  background: var(--card-bg-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
}
.privacidad-fila-checkbox {
  border-top: 1px solid var(--border);
  padding-top: 12px;
  margin-top: 4px;
}
.privacidad-fila-checkbox label { flex: 1; }
.privacidad-fila-checkbox input { width: 16px; height: 16px; accent-color: var(--accent); cursor: pointer; flex-shrink: 0; }
@media (max-width: 480px) {
  .privacidad-fila:not(.privacidad-fila-checkbox) { flex-direction: column; align-items: stretch; gap: 4px; }
  .privacidad-fila select { min-width: 0; width: 100%; }
}
.grupo-rol-btn.activo { border-color: var(--accent); color: var(--accent); }

/* Foto/permisos/enlace del grupo (Fase 2, parte 76, addendum). */
.grupo-info-foto-row { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.grupo-info-foto { flex: none; width: 56px; height: 56px; border-radius: 50%; overflow: hidden; background: var(--border); display: flex; align-items: center; justify-content: center; }
.grupo-info-foto img { width: 100%; height: 100%; object-fit: cover; }
.grupo-info-foto-vacia { font-size: 24px; }
.grupo-info-foto-acciones { display: flex; flex-wrap: wrap; gap: 6px; }
.grupo-permisos { display: flex; flex-direction: column; gap: 6px; padding: 8px 0; border-bottom: 1px solid var(--border); margin-bottom: 8px; }
.grupo-permiso-fila { display: flex; align-items: center; gap: 8px; font-size: 12.5px; color: var(--muted); cursor: pointer; }
.grupo-enlace-row { padding-bottom: 8px; border-bottom: 1px solid var(--border); margin-bottom: 10px; }
.grupo-enlace-valor { display: flex; gap: 6px; margin: 4px 0 8px; }
.grupo-enlace-valor input {
  flex: 1;
  min-width: 0;
  background: var(--input-bg);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  padding: 6px 8px;
  font-size: 12px;
}
.grupo-participantes-lista { display: flex; flex-direction: column; gap: 4px; }
.grupo-participante-fila {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 6px 4px;
  border-radius: 6px;
}
.grupo-participante-fila:hover { background: rgba(255, 255, 255, 0.04); }
.grupo-participante-info { display: flex; flex-direction: column; min-width: 0; }
.grupo-participante-nombre { font-size: 13.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.grupo-participante-numero { font-size: 11.5px; color: var(--muted); }
.grupo-admin-badge {
  font-size: 10px;
  color: var(--accent);
  border: 1px solid var(--accent);
  border-radius: 4px;
  padding: 0 4px;
}
.grupo-participante-acciones { display: flex; gap: 4px; flex: none; flex-wrap: wrap; justify-content: flex-end; max-width: 160px; }
.grupo-participante-acciones .btn-sm { font-size: 10.5px; padding: 4px 6px; white-space: nowrap; }
/* Se estira al ancho del modal (como el visor de WhatsApp Desktop, ver
   captura del usuario) en vez de mostrarse en su resolucion original -- una
   imagen chica (ej. un comprobante) se veia diminuta en medio de todo el
   espacio del modal. `object-fit: contain` respeta la proporcion real. */
.archivo-preview-contenido img { width: 100%; height: auto; max-height: 100%; object-fit: contain; }
.archivo-preview-mensaje { color: #fff; font-size: 14px; margin: auto; }
/* Vista previa de texto plano (.txt, .csv, .log...) -- ver decodificarTexto
   en chats.js. Fondo/letra clara sobre el mismo gris que ya usa el visor de
   PDF/imagen, monoespaciada (como cualquier editor de texto real), con los
   saltos de linea reales respetados. */
.archivo-preview-texto {
  width: 100%;
  color: #eaeaea;
  background: #3a3d3f;
  border-radius: 6px;
  padding: 14px;
  font-family: ui-monospace, "Courier New", monospace;
  font-size: 13px;
  white-space: pre-wrap;
  word-break: break-word;
}
#vigenciaModal .field { margin-bottom: 14px; }
#vigenciaModal input[type="date"] { width: 100%; }
.modal-actions { display: flex; justify-content: space-between; align-items: center; gap: 10px; margin-top: 6px; }

/* La X de TODAS las ventanas (pedido del usuario, parte 130: «feos y cholitos»): una ficha redonda que al pasar el mouse toma el
   rosa de «Cerrar» (mismo tono que el botón del pie del Bot de IA) y gira la X. El botón de descargar del visor, a su lado, usa
   la misma ficha en celeste. Solo tokens (regla 44). */
.modal-close,
.archivo-preview-descargar {
  position: absolute;
  top: 14px;
  right: 14px;
  width: 36px;
  height: 36px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--card-bg-2);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  color: var(--muted);
  transition: color 0.18s ease, background 0.18s ease, border-color 0.18s ease, box-shadow 0.18s ease;
  --ic: var(--ic-rosa);
}
.modal-close .ic,
.archivo-preview-descargar .ic { width: 18px; height: 18px; transition: transform 0.25s ease; }
.modal-close:hover,
.archivo-preview-descargar:hover {
  color: var(--ic);
  background: color-mix(in srgb, var(--ic) 14%, var(--card-bg-2));
  border-color: color-mix(in srgb, var(--ic) 50%, var(--border));
  box-shadow: 0 6px 16px -8px color-mix(in srgb, var(--ic) 70%, transparent);
}
.modal-close:hover .ic { transform: rotate(90deg); }
.modal-close:focus-visible,
.archivo-preview-descargar:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .modal-close, .archivo-preview-descargar, .modal-close .ic { transition: none; }
  .modal-close:hover .ic { transform: none; }
}

/* Descargar desde el visor de archivo (Fase 2, parte 105, addendum) -- el
   visor renderiza el PDF/imagen/texto DENTRO del panel (pdf.js/<img>/<pre>,
   ver "Fase 2 parte 21") a proposito, para no depender del visor de blobs
   del navegador -- pero eso mismo dejo sin forma de guardar el archivo
   original (a diferencia del resto de tipos, que ya descargan solos o via
   los controles nativos de video/audio). Mismo look que .modal-close,
   ubicado a su izquierda. */
.archivo-preview-descargar { right: 58px; --ic: var(--ic-cian); } /* la ficha la define la regla de .modal-close, arriba */

/* Encabezado de ventana con ícono en ficha llena (mismo lenguaje que la sección activa del menú lateral): primero en el Bot de IA
   (parte 130). El margen derecho deja lugar a la X. */
.modal-titulo { display: flex; align-items: center; gap: 14px; margin: 0 56px 10px 0; text-align: left; }
.modal-titulo h3 { margin: 0; font-size: 20px; letter-spacing: -0.01em; line-height: 1.2; }
.modal-titulo-sub { margin: 3px 0 0; color: var(--muted); font-size: 13.5px; }
.ficha-titulo {
  flex: 0 0 auto;
  box-sizing: content-box;
  width: 22px;
  height: 22px;
  padding: 10px;
  border-radius: 13px;
  fill: none;
  stroke: var(--on-accent);
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
  background: var(--accent);
  box-shadow: 0 10px 24px -12px var(--accent), inset 0 0 0 1px color-mix(in srgb, var(--on-accent) 12%, transparent);
}

.hidden { display: none; }

/* Ventana de vinculación (parte 130, «darle un retoque de diseño»): a lo ancho, pasos a la izquierda y el QR a la derecha en su marco
   blanco (el QR necesita fondo claro para escanearse, también en tema oscuro); en una pantalla angosta, uno debajo del otro. */
.modal-content.qr-modal { max-width: min(720px, calc(100vw - 32px)); width: 100%; text-align: left; }
.qr-cuerpo { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 20px 28px; align-items: center; margin-top: 14px; }
.qr-cuerpo.hidden { display: none; } /* regla 28: el display de arriba le ganaba a .hidden y se veían las dos vistas juntas */
.qr-pasos { margin: 0; padding: 0; list-style: none; counter-reset: paso; display: grid; gap: 14px; }
/* El número va en ::before, posicionado: con el <li> en grid, el texto y cada <strong> quedaban como celdas sueltas. */
.qr-pasos li { counter-increment: paso; position: relative; min-height: 28px; padding: 4px 0 0 40px; font-size: 14px; line-height: 1.45; color: var(--text); }
.qr-pasos li::before { content: counter(paso); position: absolute; left: 0; top: 0; display: inline-flex; align-items: center; justify-content: center; width: 28px; height: 28px; border-radius: 50%; font-size: 13px; font-weight: 700; color: var(--accent); background: var(--ok-bg); font-variant-numeric: tabular-nums; }
.qr-pasos strong { font-weight: 650; }
/* La línea que une los números, tomada de WhatsApp Desktop (parte 130, vigésimo addendum): se lee como una secuencia
   de cuatro pasos y no como cuatro avisos sueltos. El último no la lleva. Vale para el panel y para vincular.html. */
   El tono sale de --accent (regla 44): con --border a secas la línea quedaba en rgb(33,43,53) sobre la tarjeta oscura,
   o sea invisible -- medido en el navegador, no supuesto. */
.qr-pasos li:not(:last-child)::after { content: ''; position: absolute; left: 13px; top: 31px; bottom: -13px; width: 2px; border-radius: 1px; background: color-mix(in srgb, var(--accent) 32%, transparent); }
.qr-marco { margin: 0; display: flex; flex-direction: column; align-items: center; gap: 10px; }
/* El fondo blanco y el relleno son del CÓDIGO, no de «una imagen en una ventana»: un QR se lee por
   contraste y necesita su zona de silencio alrededor, así que aquí el blanco es correcto en los dos
   temas (mismo criterio que los tokens `--papel-*` del comprobante, regla 117). Vivían en la regla
   base `.modal-content img` y se los llevaban también la foto de perfil y el logo de la empresa. */
.modal-content .qr-marco img { width: 248px; height: 248px; margin: 0; padding: 12px; border-radius: 16px; background: #fff; box-shadow: 0 0 0 1px var(--border), 0 18px 40px -22px var(--accent); }
.qr-vivo { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; color: var(--muted); }
.qr-punto { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 0 var(--accent-glow); animation: qr-pulso 1.8s ease-out infinite; }
@keyframes qr-pulso { 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--accent) 45%, transparent); } 100% { box-shadow: 0 0 0 8px transparent; } }
@media (prefers-reduced-motion: reduce) { .qr-punto { animation: none; } }
/* `text-decoration: none` porque desde la parte 150 esta clase la usa también un <a> (el enlace a la
   guía de cómo cuidar el número), y un enlace trae subrayado propio: sin esto, los dos renglones de
   la ventana del QR se verían distintos entre sí. */
.qr-alternativa { grid-column: 1 / -1; display: flex; align-items: center; gap: 10px; width: 100%; padding: 12px 14px; border: 1px dashed var(--border); border-radius: 12px; background: transparent; color: var(--muted); font: inherit; font-size: 13px; text-align: left; text-decoration: none; cursor: pointer; }
.qr-alternativa strong { color: var(--accent); font-weight: 650; }
.qr-alternativa:hover { border-color: var(--accent); background: var(--accent-glow); }
.qr-alternativa:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.qr-alternativa .ic { flex: 0 0 auto; width: 18px; height: 18px; color: var(--accent); }
.qr-codigo-form, .qr-codigo-resultado { grid-column: 1 / -1; }
.qr-codigo-form label { display: block; font-size: 13px; color: var(--muted); margin-bottom: 8px; }
.qr-codigo-fila { display: flex; gap: 10px; flex-wrap: wrap; }
.qr-codigo-fila input { flex: 1 1 220px; min-width: 0; }
.qr-codigo-rotulo { margin: 0 0 8px; font-size: 13px; color: var(--muted); }
.qr-codigo-caja { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 18px; margin-bottom: 18px; border-radius: 14px; background: var(--card-bg-2); border: 1px solid var(--border); }
/* Una CLASE y no el id del panel (parte 191): `vincular.html` usa otro id, así que con `#qrCodigoValor` la página
   pública mostraba el código en letra normal -- justo la pantalla donde un desconocido tiene que copiarlo sin errores. */
.qr-codigo-valor { font-family: ui-monospace, 'Cascadia Mono', Consolas, monospace; font-size: 30px; font-weight: 700; letter-spacing: 0.18em; color: var(--text); font-variant-numeric: tabular-nums; }
.qr-exito { text-align: center; padding: 18px 0 6px; }

/* Datos de la vinculación (parte 168): la tercera vista de la ventana. Una sola columna -- acá no hay
   QR al costado, es una ficha de lectura. Todos los colores salen de tokens ya definidos (regla 44). */
.qr-datos { grid-template-columns: minmax(0, 1fr); }
.vinc-estado { grid-column: 1 / -1; display: inline-flex; align-items: center; gap: 9px; justify-self: start; margin: 0; padding: 7px 14px; border-radius: 999px; font-size: 13px; font-weight: 650; background: var(--card-bg-2); color: var(--muted); border: 1px solid var(--border); }
.vinc-punto { flex: 0 0 auto; width: 9px; height: 9px; border-radius: 50%; background: currentColor; }
.vinc-estado--activa { background: var(--ok-bg); color: var(--ok-text); border-color: transparent; }
.vinc-estado--cerrada { background: var(--yellow-bg); color: var(--yellow-text); border-color: transparent; }
.vinc-nota { grid-column: 1 / -1; margin: 0; }
.vinc-nota.hidden { display: none; }

/* --- Ficha de la vinculación (parte 169) ---------------------------------
   La ventana arranca por QUIÉN es el número (foto, nombre, número, estado), como la ficha de
   contacto de WhatsApp, y recién después vienen los campos. */
.vinc-cabecera { grid-column: 1 / -1; display: flex; align-items: center; gap: 14px; min-width: 0; }
.vinc-cabecera-texto { display: grid; gap: 4px; justify-items: start; min-width: 0; }
.vinc-titulo { margin: 0; font-size: 17px; font-weight: 700; color: var(--text); overflow-wrap: anywhere; }
.vinc-numero { margin: 0; font-size: 14px; color: var(--muted); font-variant-numeric: tabular-nums; }
.vinc-numero.hidden { display: none; }
/* El avatar: la foto que guardamos al vincular, o la silueta si ese número no tiene ninguna.
   `overflow: hidden` + `object-fit: cover` para que una foto rectangular no se deforme.

   CUADRADO REDONDEADO, no círculo (parte 182, pedido del usuario al ver su propia foto de perfil).
   Este producto es para NEGOCIOS, así que la mayoría de las fotos de perfil son un logo cuadrado, no
   una cara: un recorte circular le come las cuatro esquinas al logo y se lee como una calcomanía
   metida a la fuerza. Un cuadrado redondeado respeta el logo entero y sigue quedando bien con una
   cara. Es lo único de esta ficha que se aparta de la de contacto de WhatsApp, y a propósito. */
.vinc-avatar {
  flex: 0 0 auto; position: relative; width: 84px; height: 84px; border-radius: 26%; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  background: var(--card-bg-2); border: 1px solid var(--border); color: var(--muted);
  box-shadow: 0 10px 20px -12px rgba(0, 0, 0, 0.6);
}
/* El alto relieve va en un `::after` y NO en un `box-shadow: inset` del propio contenedor: un inset
   se dibuja sobre el fondo pero DEBAJO del contenido, así que la foto lo taparía entero. Luz arriba
   y sombra abajo es lo que hace que se lea como una pieza levantada del fondo. */
.vinc-avatar::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit; pointer-events: none;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.16), inset 0 -2px 3px -1px rgba(0, 0, 0, 0.3);
}
/* `margin`, `padding` y `background` en cero a propósito: son los del código QR y los traía la regla
   base `.modal-content img` (ver el porqué allá arriba). `border-radius: inherit` recorta la foto
   aunque el contenedor no recorte, que es el caso del recuadro de «listo» (`overflow: visible`). */
.vinc-avatar img { width: 100%; height: 100%; object-fit: cover; display: block; margin: 0; padding: 0; background: none; border-radius: inherit; }
/* Regla 28/134: `.vinc-avatar` fija `display: flex` mucho despues de `.hidden`, asi que sin esta
   compuesta el avatar del recuadro de «listo» se veria vacio antes de que llegue la foto. */
.vinc-avatar.hidden { display: none; }
/* Regla 28/134: las dos reglas de arriba fijan `display` DESPUÉS de `.hidden`, así que sin estas
   compuestas la foto vacía y la silueta se verían las dos a la vez. */
.vinc-avatar img.hidden { display: none; }
/* La silueta de cuando no hay foto ocupa el mismo lugar que ocuparía la foto: a 34 px se veía
   chica y perdida dentro del círculo (lo notó el usuario). Un marcador de avatar se lee como tal
   cuando llena el círculo, como el de la ficha de contacto de WhatsApp. */
.vinc-avatar-icono { width: 62%; height: 62%; }
.vinc-avatar-icono.hidden { display: none; }

/* Aviso de dispositivos que no son ni el teléfono ni esta instancia. */
.vinc-aviso { grid-column: 1 / -1; margin: 0; }
.vinc-aviso.hidden { display: none; }

/* Bloque del negocio: existe solo en cuentas de WhatsApp Business. */
.vinc-negocio { grid-column: 1 / -1; display: grid; gap: 10px; }
.vinc-negocio.hidden { display: none; }
.vinc-negocio-titulo { display: flex; align-items: center; gap: 8px; margin: 4px 0 0; font-size: 13px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
.vinc-negocio-titulo .ic { width: 16px; height: 16px; }
.vinc-horario.hidden { display: none; }
.vinc-subtitulo { margin: 2px 0 0; font-size: 12px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
.vinc-subtitulo.hidden { display: none; }

/* La foto en el recuadro de «listo», con el visto como insignia encima. El avatar de acá se
   centra solo porque ese recuadro ya centra su contenido. */
.vinc-avatar-exito { width: 88px; height: 88px; margin: 0 auto; overflow: visible; border-color: var(--ok-text); }
.vinc-avatar-exito.hidden { display: none; }
/* La insignia se apoya en la esquina inferior derecha, que en un cuadrado redondeado queda más
   afuera que en un círculo: con los -2 px de antes se montaba sobre la foto. */
.vinc-avatar-visto {
  position: absolute; right: -6px; bottom: -6px; width: 28px; height: 28px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--ok-text); color: var(--on-accent); border: 2px solid var(--card-bg);
}
.vinc-avatar-visto .ic { width: 15px; height: 15px; }
.success-icon.hidden { display: none; }
/* Rótulo y valor en dos columnas, con el valor alineado a la derecha: se lee como una ficha y no como
   una lista de frases. `tabular-nums` para que los números y las fechas queden en columna (regla 97). */
/* Misma razon: las listas del bloque del negocio y del horario nacen ocultas. */
.vinc-lista.hidden { display: none; }
.vinc-lista { grid-column: 1 / -1; display: grid; gap: 1px; margin: 0; padding: 0; border-radius: 14px; overflow: hidden; background: var(--border); border: 1px solid var(--border); }
.vinc-fila { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; padding: 11px 16px; background: var(--card-bg-2); }
.vinc-fila dt { margin: 0; font-size: 13px; color: var(--muted); white-space: nowrap; }
.vinc-fila dd { margin: 0; min-width: 0; font-size: 14px; font-weight: 600; color: var(--text); text-align: right; overflow-wrap: anywhere; font-variant-numeric: tabular-nums; }
/* Dentro del recuadro de «listo», que está centrado: la ficha se mantiene legible pero más discreta. */
.vinc-lista-compacta { margin: 14px 0 4px; text-align: left; }
.vinc-lista-compacta .vinc-fila { padding: 9px 14px; }
/* Regla 28/134: `.qr-alternativa` fija `display: flex` DESPUÉS de `.hidden`, así que sin esta regla
   compuesta el botón que nace oculto («hay un código listo») se vería siempre. */
.qr-alternativa.hidden { display: none; }
@media (max-width: 520px) {
  .vinc-cabecera { flex-direction: column; align-items: flex-start; gap: 10px; }
  .vinc-fila { flex-direction: column; align-items: flex-start; gap: 2px; }
  .vinc-fila dd { text-align: left; }
}

@media (max-width: 640px) {
  .qr-cuerpo { grid-template-columns: minmax(0, 1fr); }
  .qr-marco { order: -1; }
  .modal-content .qr-marco img { width: 220px; height: 220px; }
}

/* Menú «⋯» de cada fila de Instancias (parte 130): solo aparece con las acciones que no entraron en la fila, cada una con su nombre. */
.menu-fila { position: fixed; z-index: 1000; min-width: 230px; padding: 6px; display: grid; gap: 2px; background: var(--card-bg); border: 1px solid var(--border); border-radius: 12px; box-shadow: 0 18px 44px -18px rgba(0, 0, 0, 0.55); }
.menu-fila button { display: flex; align-items: center; gap: 10px; width: 100%; padding: 9px 10px; border: 0; border-radius: 8px; background: transparent; color: var(--text); font: inherit; font-size: 13px; text-align: left; cursor: pointer; }
.menu-fila button .ic { width: 16px; height: 16px; flex: 0 0 auto; color: var(--muted); }
.menu-fila button:hover:not(:disabled), .menu-fila button:focus-visible { background: var(--card-bg-2); outline: none; }
.menu-fila button.peligro, .menu-fila button.peligro .ic { color: var(--red-text); }
.menu-fila button:disabled { opacity: 0.5; cursor: not-allowed; }

/* Diálogos de «Mis clientes» (parte 130): editar, cambiar la contraseña. */
.confirm-modal.dialogo-cliente { max-width: min(460px, calc(100vw - 32px)); width: 100%; text-align: left; }
.dialogo-cliente-form { display: grid; gap: 14px; margin-top: 8px; }
.dialogo-cliente-form .field { display: grid; gap: 6px; }
.dialogo-cliente-form .field small.hint { margin: 0; font-size: 12px; }
.dialogo-cliente-form input[readonly] { opacity: 0.75; }
.dialogo-cliente-intro { margin: 0; line-height: 1.5; }
.dialogo-clave-fila { display: flex; gap: 8px; align-items: center; }
.dialogo-clave-fila input { flex: 1 1 auto; min-width: 0; }
.dialogo-cliente-error:empty { display: none; }
.btn-link { justify-self: start; display: inline-flex; align-items: center; gap: 6px; padding: 2px 0; border: 0; background: none; color: var(--accent); font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; }
.btn-link:hover { text-decoration: underline; }
.btn-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.btn-link .ic { width: 15px; height: 15px; }
/* Regla 28: esta regla va DESPUES de .hidden y tiene su misma especificidad, asi que sin esto un .btn-link.hidden
   se seguiria viendo (el display: inline-flex de arriba gana). */
.btn-link.hidden { display: none; }

.success-icon {
  color: var(--accent);
  display: flex;
  justify-content: center;
  margin: 8px 0 4px;
}

.success-text {
  font-size: 16px;
  font-weight: 700;
  color: var(--text);
  margin: 4px 0 2px;
}

#successSub { margin-top: 0; }

/* --- Dialogos propios (ui.js): toasts + modal de confirmacion ------------
   Reemplazan alert()/confirm() nativos en todo el panel -- ver ui.js. */

.toast-stack {
  position: fixed;
  bottom: 24px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  gap: 10px;
  align-items: center;
  z-index: 2000;
  pointer-events: none;
}

.toast {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  background:
    linear-gradient(var(--toast-tinte, transparent), var(--toast-tinte, transparent)),
    var(--card-bg);
  border: 1px solid var(--border);
  border-left: 3px solid var(--muted);
  color: var(--text);
  padding: 14px 16px;
  border-radius: 12px;
  font-size: 13.5px;
  max-width: 440px;
  box-shadow: 0 12px 32px rgba(0,0,0,0.4);
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 0.22s ease, transform 0.22s ease;
  pointer-events: auto;
}
.toast.visible { opacity: 1; transform: translateY(0); }

.toast-icono {
  font-size: 18px;
  flex-shrink: 0;
  line-height: 1;
  margin-top: 1px;
}
/* Iconos SVG de avisos y confirmaciones (parte 130): el color lo da el tipo, antes lo daba el emoji. */
.toast-icono .ic { width: 20px; height: 20px; vertical-align: top; }
.toast-exito .toast-icono { color: var(--ok-text); }
.toast-error .toast-icono { color: var(--red-text); }
.toast-advertencia .toast-icono { color: var(--yellow-text); }
.toast-info .toast-icono { color: var(--ic-azul); }
.confirm-icono .ic { width: 38px; height: 38px; }
.confirm-icono.advertencia { color: var(--yellow-text); }
.confirm-icono.info { color: var(--ic-azul); }
.toast-texto {
  flex: 1;
  line-height: 1.5;
  white-space: pre-line;
  padding-top: 1px;
}
/* Primera linea del mensaje = titulo (mas peso visual); el resto
   (separado por doble salto de linea, ver ui.js) queda como detalle en
   tono mas suave -- mismo criterio "titulo + cuerpo" que ya se usa en los
   avisos de WhatsApp (_construirAvisoInstancia, accountManager.js). */
.toast-texto strong { color: var(--text); font-weight: 600; }
.toast-texto small { display: block; margin-top: 4px; color: var(--muted); font-size: 12.5px; line-height: 1.45; }

.toast-cerrar {
  border: none;
  background: transparent;
  color: var(--muted);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
  padding: 0 0 0 4px;
  margin-top: -1px;
}
.toast-cerrar:hover { color: var(--text); }

.toast-exito { border-left-color: var(--accent); --toast-tinte: rgba(52, 208, 163, 0.07); }
.toast-error { border-left-color: var(--red-text); --toast-tinte: rgba(233, 92, 92, 0.07); }
.toast-advertencia { border-left-color: var(--yellow-text); --toast-tinte: rgba(233, 179, 60, 0.07); }
.toast-info { border-left-color: #53bdeb; --toast-tinte: rgba(83, 189, 235, 0.07); }

.confirm-modal { max-width: 400px; text-align: center; }
.confirm-icono {
  font-size: 34px;
  line-height: 1;
  margin-bottom: 10px;
}
.confirm-titulo { margin: 0 0 8px; font-size: 17px; }
.confirm-mensaje { margin: 0 0 20px; color: var(--muted); font-size: 14px; line-height: 1.5; white-space: pre-line; }
.confirm-overlay .modal-actions { justify-content: center; }

.send-estado-envio {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13.5px;
  font-weight: 600;
  margin: 4px 0 0;
  padding: 10px 14px;
  border-radius: 8px;
  background: var(--card-bg-2);
  border: 1px solid var(--border);
}
.send-estado-envio.hidden { display: none; }
.send-estado-envio .msg-tick { font-size: 14px; }

.texto-modal-mensaje { text-align: left; }
.texto-modal-input {
  width: 100%;
  padding: 10px 12px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--card-bg-2);
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
  margin-bottom: 18px;
}

/* Selector de contacto con búsqueda (Fase 2, parte 36) -- ver
   UI.crearSelectorContacto() en ui.js. position:fixed calculado en JS,
   mismo criterio que .ui-tooltip/.msg-reaccion-picker (nunca queda
   cortado por un contenedor con overflow en el medio). */
.contacto-dropdown {
  position: fixed;
  z-index: 1000;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  max-height: 280px;
  overflow-y: auto;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}
.contacto-dropdown.hidden { display: none; }
.contacto-dropdown-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 10px;
  padding: 8px 12px;
  cursor: pointer;
  font-size: 13.5px;
}
.contacto-dropdown-item:hover, .contacto-dropdown-item.activo { background: var(--card-bg-2); }
.contacto-dropdown-nombre {
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.contacto-dropdown-numero { color: var(--muted); font-size: 12px; flex-shrink: 0; white-space: nowrap; }

.file-preview.hidden { display: none; }
.file-preview {
  display: block;
  margin-top: 6px;
  font-size: 12.5px;
  color: var(--muted);
}

@media (max-width: 640px) {
  .stats-row { grid-template-columns: 1fr; }
  .topbar { flex-direction: column; }
}

/* --- Visor de chats (Fase 2) --------------------------------------------
   A diferencia del panel de instancias (cajas centradas con max-width), esta
   pantalla usa TODO el ancho/alto de la ventana -- igual que WhatsApp
   Desktop/Web -- porque es una pantalla de trabajo, no un formulario. El
   ancho de la lista de chats se auto-ajusta con clamp() segun el ancho de
   ventana en vez de quedar fijo, y el panel de conversacion siempre ocupa
   TODO el espacio restante (flex: 1). */

.chats-page {
  height: 100vh;
  width: 100%;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}

/* Fase 2 (parte 82) -- sugerencia real del usuario, con captura: el
   selector de instancia (topbar) y "Conversaciones (N)"/Papelera
   (antes `.chat-list-head`, su propia fila con borde y padding propios)
   son informacion relacionada del mismo panel -- se fusionan en UNA sola
   fila para recuperar la altura completa de esa fila para mas chats
   visibles. `.chats-topbar-conteo` reemplaza a `.chat-list-head`. */
.chats-topbar {
  display: flex;
  align-items: center;
  gap: 20px;
  padding: 10px 20px;
  border-bottom: 1px solid var(--border);
  flex-wrap: wrap;
  flex-shrink: 0;
}

.chats-topbar-left { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

.chats-instancia-selector { display: flex; align-items: center; gap: 8px; }
.chats-instancia-selector > span {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}
.chats-instancia-selector select { padding: 6px 10px; font-size: 13px; min-width: 170px; }

.chats-topbar-conteo {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-left: 20px;
  border-left: 1px solid var(--border);
  font-size: 13.5px;
  font-weight: 700;
  flex-wrap: wrap;
}

.chats-topbar-user { margin-left: auto; margin-block: 0; }

@media (max-width: 720px) {
  .chats-topbar-conteo { padding-left: 0; border-left: none; }
  .chats-topbar-user { margin-left: 0; }
}

.chats-layout {
  flex: 1;
  display: flex;
  min-height: 0;
}

.chat-list-panel {
  width: clamp(280px, 26vw, 400px);
  flex-shrink: 0;
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  min-height: 0;
}

.chat-thread-panel {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: var(--bg-elevated);
}

/* `.chat-list-head` (fila propia para "Conversaciones (N)"/Papelera) se
   eliminó en la parte 82 -- fusionada dentro de `.chats-topbar-conteo`. */

.chat-list-toolbar {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.chat-list-toolbar.hidden { display: none; }

/* Parte 82 -- antes las 5 casillas de filtro y los 5 botones de accion
   compartian una unica fila `flex-wrap: wrap`, que en el ancho angosto del
   panel de chats terminaba partida en 3 lineas mezcladas (ver captura del
   usuario). Separados en dos filas propias: filtros (que a su vez pueden
   envolver a una segunda linea si hace falta) y acciones, siempre alineadas
   a la derecha -- nunca mas de 2 lineas mezcladas. */
.chat-list-filtros {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}

.chat-list-acciones {
  display: flex;
  align-items: center;
  gap: 6px;
  justify-content: flex-end;
}

.toggle-eliminados {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  color: var(--muted);
  cursor: pointer;
  user-select: none;
}
.toggle-eliminados input { accent-color: var(--accent); cursor: pointer; }
.toggle-eliminados.hidden { display: none; }

/* Chips de filtro (parte 82) -- mismo lenguaje visual que
   `.lista-filtro-pill` (parte 64): borde sutil siempre, se rellena con el
   acento cuando la casilla está marcada -- mas compacto y mas claro que un
   checkbox+texto suelto, y solo aplica DENTRO de la barra de filtros de la
   lista de chats (la casilla "Mostrar eliminados" del hilo de mensajes,
   mas abajo en este archivo, sigue con el estilo original sin tocar). */
.chat-list-filtros .toggle-eliminados {
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: 999px;
  transition: background-color 0.15s, border-color 0.15s;
}
.chat-list-filtros .toggle-eliminados:hover { border-color: var(--accent-dim); }
.chat-list-filtros .toggle-eliminados:has(input:checked) {
  background: var(--accent-glow);
  border-color: var(--accent-dim);
  color: var(--text);
}
.chat-list-filtros .toggle-eliminados input { width: 12px; height: 12px; }

/* Listas (Fase 2, parte 64) -- tabs de filtro estilo pill, inspiradas en la
   funcion real de WhatsApp Desktop (captura que paso el usuario). */
.listas-filtro-bar {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  flex-wrap: nowrap;
  overflow-x: auto;
}
.listas-filtro-bar.hidden { display: none; }
.lista-filtro-pill {
  border: 1px solid var(--border);
  background: transparent;
  color: var(--muted);
  border-radius: 999px;
  padding: 4px 12px;
  font-size: 12px;
  cursor: pointer;
  white-space: nowrap;
  font-family: inherit;
  flex-shrink: 0;
}
.lista-filtro-pill:hover { border-color: var(--accent); color: var(--text); }
.lista-filtro-pill.activa { background: var(--accent); border-color: var(--accent); color: #08130f; }
.lista-filtro-count { opacity: 0.75; font-size: 11px; }

.seleccion-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 14px;
  background: var(--accent-glow);
  border-bottom: 1px solid var(--border);
  font-size: 12.5px;
  font-weight: 600;
  flex-shrink: 0;
}
.seleccion-bar > div { display: flex; gap: 6px; }
.seleccion-bar.hidden { display: none; }

.chat-item-checkbox, .msg-item-checkbox {
  width: 16px;
  height: 16px;
  accent-color: var(--accent);
  cursor: pointer;
  flex-shrink: 0;
}

.chat-list-item.eliminado { opacity: 0.55; }
.chat-list-item.eliminado .chat-list-name::after {
  content: ' (eliminado)';
  font-weight: 400;
  color: var(--red-text);
  font-size: 11px;
}

.msg-bubble.eliminado { opacity: 0.5; }
.msg-bubble.eliminado .msg-restaurar-btn {
  border: none;
  background: transparent;
  color: var(--accent);
  cursor: pointer;
  font-size: 11px;
  padding: 0 0 0 8px;
  text-decoration: underline;
}

.new-chat-form.hidden { display: none; }
.new-chat-form {
  display: flex;
  gap: 6px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.new-chat-form input {
  flex: 1;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--card-bg-2);
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
}

.chat-list { overflow-y: auto; flex: 1; }

.chat-list-empty { padding: 24px 16px; color: var(--muted); font-size: 13px; text-align: center; }

.chat-list-item-papelera {
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  font-size: 12.5px;
}
.chat-list-item-papelera .nombre { font-weight: 600; font-size: 14px; }
.chat-list-item-papelera .detalle { color: var(--muted); margin: 3px 0; }
.chat-list-item-papelera button { margin-top: 4px; }

.msg-delete-btn {
  border: none;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  font-size: 12px;
  padding: 0 0 0 8px;
  opacity: 0.6;
}
.msg-delete-btn:hover { opacity: 1; color: var(--red-text); }

.chat-list-item {
  display: flex;
  gap: 10px;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  align-items: center;
}
.chat-list-item:hover { background: var(--card-bg-2); }
.chat-list-item.active { background: var(--accent-glow); }

.chat-avatar {
  width: 42px;
  height: 42px;
  border-radius: 50%;
  background: var(--accent-dim);
  color: #06110d;
  display: flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  flex-shrink: 0;
}

.chat-list-info { flex: 1; min-width: 0; }
.chat-list-name {
  font-weight: 600;
  font-size: 14px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.chat-avatar-img { object-fit: cover; }

/* "Posible bloqueo" (Fase 2, parte 38) -- ver el tooltip en el propio
   elemento (title nativo, alcanza para un badge chico como este). */
.chat-bloqueo-badge { font-size: 12px; cursor: help; }

.chat-list-numero {
  font-size: 11.5px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.chat-list-preview {
  font-size: 12.5px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.chat-list-meta { display: flex; flex-direction: column; align-items: flex-end; gap: 4px; flex-shrink: 0; }
.chat-list-time { font-size: 11px; color: var(--muted); }
/* «Esperando respuesta» (parte 130): con el filtro puesto, cada chat dice cuánto lleva esperando el cliente, en ámbar. */
.chat-list-time.chat-espera { color: var(--yellow-text); font-weight: 600; white-space: nowrap; }
.chat-espera .ic { width: 12px; height: 12px; vertical-align: -2px; }

.chat-unread-badge {
  background: var(--accent);
  color: #06110d;
  border-radius: 999px;
  min-width: 18px;
  height: 18px;
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 5px;
}

.chat-thread-header {
  padding: 12px 20px;
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
  background: var(--card-bg);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.chat-thread-header.hidden { display: none; }
.chat-thread-header-info { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.chat-thread-header-title { font-weight: 700; }
/* "en linea"/"escribiendo..."/"grabando audio..." (Fase 2, parte 43) --
   subtitulo chico bajo el nombre, mismo lugar donde WhatsApp Desktop lo
   muestra. .hidden lo oculta cuando no hay presencia que mostrar (contacto
   con privacidad que lo esconde, o simplemente "unavailable"). */
.chat-thread-header-presencia { font-size: 12px; color: var(--accent); }
.chat-thread-header-presencia.hidden { display: none; }
.chat-thread-toolbar {
  display: flex;
  align-items: center;
  gap: 8px;
}

.chat-thread-messages.hidden { display: none; }
.chat-thread-messages {
  flex: 1;
  overflow-y: auto;
  padding: 20px clamp(16px, 6vw, 80px);
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.msg-row {
  display: flex;
  align-items: flex-end;
  gap: 6px;
  width: 100%;
}
.msg-row.entrante { justify-content: flex-start; }
.msg-row.saliente { justify-content: flex-end; }

.msg-bubble {
  max-width: min(65%, 640px);
  padding: 8px 12px;
  border-radius: 10px;
  font-size: 14px;
  line-height: 1.4;
  white-space: pre-wrap;
  word-break: break-word;
  display: flex;
  flex-direction: column;
  position: relative;
}
.msg-bubble.entrante { background: var(--card-bg-2); border: 1px solid var(--border); }
.msg-bubble.saliente { background: var(--accent-dim); color: var(--burbuja-saliente-texto); }
/* Links dentro del texto del mensaje (ver linkifyHtml en chats.js) -- mismo
   criterio de color que WhatsApp real, para que se note que es clickeable
   sobre el fondo oscuro de la burbuja. */
.msg-texto a { color: #53bdeb; text-decoration: underline; word-break: break-all; }
.msg-bubble.saliente .msg-texto a { color: #d3f4ff; }

/* Formato de texto real de WhatsApp (Fase 2, parte 70) -- negrita/cursiva/
   tachado ya los renderiza bien el navegador por defecto con <strong>/<em>/
   <del>, solo <code> (monoespaciado) necesita una fuente y un fondo propios
   para distinguirse del resto del texto, igual que en la app real. */
.msg-texto code {
  font-family: 'Courier New', Consolas, monospace;
  background: rgba(0, 0, 0, 0.15);
  padding: 1px 4px;
  border-radius: 4px;
  font-size: 0.95em;
  white-space: pre-wrap;
}

/* Reaccionar (Fase 3, parte 1) -- ver "Fase 2 (parte 29)" */
.msg-reaccionar-btn {
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: 13px;
  padding: 0 3px;
  opacity: 0.65;
  line-height: 1;
}
.msg-reaccionar-btn:hover { opacity: 1; }
.msg-reenviar-btn {
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: 12px;
  padding: 0 3px;
  opacity: 0.65;
  line-height: 1;
}
.msg-reenviar-btn:hover { opacity: 1; }
.msg-responder-btn {
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: 12px;
  padding: 0 3px;
  opacity: 0.65;
  line-height: 1;
}
.msg-responder-btn:hover { opacity: 1; }
/* Editar/revocar (Fase 2, parte 80) -- mismo aspecto que reaccionar/reenviar/responder. */
.msg-editar-btn, .msg-revocar-btn {
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: 12px;
  padding: 0 3px;
  opacity: 0.65;
  line-height: 1;
}
.msg-editar-btn:hover, .msg-revocar-btn:hover { opacity: 1; }
.msg-editado-tag { font-size: 11px; color: var(--muted); font-style: italic; }
/* "Destacar" real (Fase 2, parte 83) -- mismo aspecto liviano que
   reaccionar/reenviar/responder cuando NO esta destacado; una vez marcado
   se pone opaco del todo y color dorado, para distinguirlo de un simple
   hover -- mismo criterio de color que usa WhatsApp real para su estrella. */
.msg-destacar-btn {
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: 12px;
  padding: 0 3px;
  opacity: 0.65;
  line-height: 1;
  color: inherit;
}
.msg-destacar-btn:hover { opacity: 1; }
.msg-destacar-btn.activo { opacity: 1; color: var(--yellow-text); }

/* Botones de accion de la burbuja (parte 130). Con emojis el color venia del propio glifo; con SVG el trazo es currentColor y un <button> NO
   hereda el color de la burbuja (toma el del sistema: negro), asi que en el tema oscuro quedaban casi invisibles (1,1:1 sobre la entrante).
   Se fija el color segun la burbuja y se sube la opacidad en reposo. La prueba de contraste de test/integracion-ui-panel.js lo vigila (>= 3:1). */
.msg-bubble.entrante button.msg-reaccionar-btn,
.msg-bubble.entrante button.msg-reenviar-btn,
.msg-bubble.entrante button.msg-responder-btn,
.msg-bubble.entrante button.msg-editar-btn,
.msg-bubble.entrante button.msg-revocar-btn,
.msg-bubble.entrante button.msg-delete-btn,
.msg-bubble.entrante button.msg-destacar-btn:not(.activo) { color: var(--muted); }
.msg-bubble.saliente button.msg-reaccionar-btn,
.msg-bubble.saliente button.msg-reenviar-btn,
.msg-bubble.saliente button.msg-responder-btn,
.msg-bubble.saliente button.msg-editar-btn,
.msg-bubble.saliente button.msg-revocar-btn,
.msg-bubble.saliente button.msg-delete-btn,
.msg-bubble.saliente button.msg-destacar-btn:not(.activo) { color: var(--burbuja-saliente-texto); }
.msg-bubble button.msg-reaccionar-btn,
.msg-bubble button.msg-reenviar-btn,
.msg-bubble button.msg-responder-btn,
.msg-bubble button.msg-editar-btn,
.msg-bubble button.msg-revocar-btn,
.msg-bubble button.msg-delete-btn,
.msg-bubble button.msg-destacar-btn:not(.activo) { opacity: 0.85; }
.msg-bubble button.msg-reaccionar-btn:hover,
.msg-bubble button.msg-reenviar-btn:hover,
.msg-bubble button.msg-responder-btn:hover,
.msg-bubble button.msg-editar-btn:hover,
.msg-bubble button.msg-revocar-btn:hover,
.msg-bubble button.msg-delete-btn:hover,
.msg-bubble button.msg-destacar-btn:not(.activo):hover { opacity: 1; }
.msg-bubble button.msg-delete-btn:hover { color: var(--red-text); }
/* Candado de plan (parte 130): el botón se ve igual de legible (misma regla de contraste de arriba) y un candado chico en su esquina dice
   que el plan de la instancia no incluye esa función; al tocarlo, chats.js explica qué plan la trae. */
.msg-bubble button.msg-bloqueado-plan { position: relative; cursor: help; }
.msg-bubble button.msg-bloqueado-plan .ic.msg-candado { position: absolute; right: -3px; bottom: -4px; width: 9px; height: 9px; stroke-width: 3; color: var(--yellow-text); }
/* Ubicación/contacto/encuesta (Fase 2, parte 80) -- mismo tratamiento visual
   liviano que .msg-link-preview, sin tarjeta con miniatura. */
.msg-especial { margin-top: 4px; }
.msg-especial a { color: inherit; text-decoration: underline; }
.msg-poll-pregunta { font-weight: 600; margin-bottom: 4px; }
.msg-poll-opciones { margin: 0 0 4px 18px; padding: 0; }
.msg-poll-opciones li { font-size: 0.95em; }
.msg-poll-nota { font-size: 11px; color: var(--muted); font-style: italic; }
/* Menu "+" del compose bar (Fase 2, parte 80) -- mismo criterio de
   posicionamiento fixed que .msg-reaccion-picker. */
.reply-mas-menu {
  position: fixed;
  z-index: 1000;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 4px;
  display: flex;
  flex-direction: column;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
}
.reply-mas-menu button {
  border: none;
  background: transparent;
  cursor: pointer;
  text-align: left;
  padding: 8px 14px;
  border-radius: 6px;
  font-size: 13.5px;
  white-space: nowrap;
}
.reply-mas-menu button:hover { background: rgba(127, 127, 127, 0.2); }
.msg-reaccion-picker {
  /* position:fixed calculado en JS (chats.js) segun la posicion real del
     boton en la PANTALLA -- ver "Fase 2 (parte 29)". Un picker posicionado
     solo con CSS relativo a la burbuja quedaba tapado por el encabezado del
     hilo cuando el mensaje estaba cerca del borde superior (bug real,
     encontrado probando esto con Playwright). */
  position: fixed;
  z-index: 1000;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 20px;
  padding: 4px 6px;
  display: flex;
  gap: 2px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
}
.msg-reaccion-picker button {
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: 18px;
  padding: 2px 5px;
  border-radius: 50%;
  line-height: 1.3;
}
.msg-reaccion-picker button:hover { background: rgba(127, 127, 127, 0.25); }
/* Fase 2 (parte 97): un pill por emoji distinto (antes solo mostraba UN
   emoji suelto, sin contenedor) -- `gap` los separa cuando hay mas de uno
   (varias personas del grupo reaccionando con emojis distintos). */
.msg-reaccion-badge {
  position: absolute;
  bottom: -9px;
  right: 10px;
  display: flex;
  gap: 3px;
}
.msg-reaccion-pill {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 999px;
  padding: 0px 5px;
  font-size: 13px;
  line-height: 1.5;
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.35);
}
/* Cantidad real de gente que reacciono con ESE emoji -- solo aparece si es
   mas de 1 (ver reaccionBadgeHtml() en chats.js), mismo criterio visual
   que WhatsApp Desktop. */
.msg-reaccion-cantidad {
  font-size: 10.5px;
  font-weight: 600;
  color: var(--muted);
}
.msg-reaccion-badge { cursor: pointer; pointer-events: auto; }
.msg-reaccion-badge:hover .msg-reaccion-pill { border-color: var(--accent); }

/* Panel de detalle de reacciones (Fase 2, parte 100) -- "quién reaccionó
   con qué", mismo criterio visual que WhatsApp Desktop (captura real del
   usuario). `position: fixed`, mismo mecanismo que `.msg-reaccion-picker`
   -- no queda cortado por el scroll del hilo. */
.msg-reacciones-detalle {
  position: fixed;
  z-index: 1000;
  width: 260px;
  max-height: 360px;
  display: flex;
  flex-direction: column;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: 0 16px 40px rgba(0, 0, 0, 0.45);
  overflow: hidden;
}
.reacciones-detalle-titulo {
  padding: 12px 14px 8px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--text);
}
.reacciones-detalle-tabs {
  display: flex;
  gap: 6px;
  padding: 0 14px 10px;
  border-bottom: 1px solid var(--border);
  overflow-x: auto;
}
.reacciones-tab {
  flex-shrink: 0;
  border: 1px solid var(--border);
  background: var(--card-bg-2);
  color: var(--muted);
  border-radius: 999px;
  padding: 4px 10px;
  font-size: 12.5px;
  cursor: pointer;
}
.reacciones-tab.activa { border-color: var(--accent); color: var(--text); background: var(--accent-glow); }
.reacciones-detalle-lista { overflow-y: auto; padding: 6px 0; }
.reaccion-fila {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
}
.reaccion-fila.es-propia { cursor: pointer; }
.reaccion-fila.es-propia:hover { background: var(--card-bg-2); }
.reaccion-fila-avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: var(--accent);
  color: #06110d;
  font-weight: 700;
  font-size: 12px;
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.reaccion-fila-texto { flex: 1; min-width: 0; display: flex; flex-direction: column; line-height: 1.3; }
.reaccion-fila-nombre { font-size: 13px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.reaccion-fila-quitar { font-size: 11px; color: var(--red-text); }
.reaccion-fila-emoji { font-size: 16px; flex-shrink: 0; }

.msg-emoji-picker {
  /* Mismo criterio de posicionamiento que .msg-reaccion-picker (arriba) --
     position:fixed calculado en JS segun la posicion real del boton en la
     pantalla, para no quedar tapado por ningun contenedor con overflow. */
  position: fixed;
  z-index: 1000;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 6px 8px 8px;
  width: 300px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
}
.msg-emoji-tabs {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--border);
  padding-bottom: 6px;
  margin-bottom: 6px;
  overflow-x: auto;
}
.msg-emoji-tabs button {
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: 16px;
  padding: 3px 5px;
  border-radius: 6px;
  flex-shrink: 0;
  opacity: 0.55;
}
.msg-emoji-tabs button:hover { background: rgba(127, 127, 127, 0.25); }
.msg-emoji-tabs button.activa { opacity: 1; background: rgba(127, 127, 127, 0.25); }
.msg-emoji-grid {
  display: grid;
  grid-template-columns: repeat(10, 1fr);
  gap: 2px;
  max-height: 220px;
  overflow-y: auto;
}
.msg-emoji-grid button {
  border: none;
  background: transparent;
  cursor: pointer;
  font-size: 18px;
  padding: 3px;
  border-radius: 6px;
  line-height: 1.3;
}
.msg-emoji-grid button:hover { background: rgba(127, 127, 127, 0.25); }

.msg-quote {
  border-left: 3px solid var(--accent);
  background: rgba(0, 0, 0, 0.18);
  border-radius: 6px;
  padding: 5px 8px;
  margin-bottom: 5px;
  font-size: 12.5px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.msg-bubble.saliente .msg-quote { color: rgba(234, 255, 246, 0.75); }

/* Separador de fecha entre mensajes (addendum "Fase 2 parte 56") -- mismo
   criterio visual que WhatsApp Desktop: una etiqueta centrada, chica,
   flotando sobre el fondo del hilo. */
.msg-date-separator {
  display: flex;
  justify-content: center;
  margin: 12px 0;
}
.msg-date-separator span {
  background: var(--card-bg-2);
  color: var(--muted);
  font-size: 12px;
  padding: 4px 12px;
  border-radius: 8px;
}

/* Etiqueta "Reenviado" (Fase 2, parte 50) -- mismo criterio visual que
   WhatsApp Desktop: texto chico, gris, en itálica, con el ícono de flecha
   antes del texto del mensaje. */
.msg-reenviado-tag {
  font-size: 12px;
  font-style: italic;
  color: var(--muted);
  margin-bottom: 3px;
}
.msg-bubble.saliente .msg-reenviado-tag { color: rgba(234, 255, 246, 0.7); }

/* Nombre de quien mandó el mensaje dentro de un grupo (Fase 2, parte 80,
   addendum) -- mismo criterio visual que WhatsApp: un color propio,
   distinto del texto normal, arriba de todo el contenido de la burbuja. */
.msg-remitente {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--accent, #4fd1c5);
  margin-bottom: 3px;
}
.msg-bubble.saliente .msg-remitente { color: rgba(234, 255, 246, 0.85); }

.msg-meta {
  font-size: 10.5px;
  color: var(--muted);
  margin-top: 4px;
  align-self: flex-end;
  display: flex;
  align-items: center;
  gap: 3px;
}
.msg-bubble.saliente .msg-meta { color: rgba(234, 255, 246, 0.7); }
/* Quién escribió un saliente (parte 130): el asistente o otro sistema. Sigue el color del meta de la burbuja (ambos temas). */
.msg-origen {
  display: inline-flex; align-items: center; gap: 3px; margin-right: 4px; padding: 0 6px; border-radius: 999px;
  font-weight: 600; letter-spacing: 0.02em; background: color-mix(in srgb, currentColor 16%, transparent);
}
.msg-origen .ic { width: 1.1em; height: 1.1em; }
.msg-origen-bot { color: var(--burbuja-saliente-texto); }
.msg-tick { font-size: 12px; line-height: 1; display: inline-flex; align-items: center; }
.msg-tick .ic { width: 1.25em; height: 1.25em; vertical-align: middle; stroke-width: 2.4; }
.msg-tick.leido { color: #53bdeb; }

/* Archivos adjuntos dentro de una burbuja -- ver "Fase 2 (parte 19)" */
.msg-media { margin-bottom: 5px; }
.msg-media-image img { max-width: 100%; max-height: 320px; border-radius: 6px; display: block; cursor: zoom-in; }
.msg-media-video video, .msg-media-audio audio { max-width: 100%; border-radius: 6px; display: block; }
.msg-media-loading, .msg-media-error {
  font-size: 12.5px;
  color: var(--muted);
  font-style: italic;
  padding: 4px 0;
}
.msg-media-cargar {
  border: 1px solid var(--border);
  background: rgba(0, 0, 0, 0.15);
  color: inherit;
  border-radius: 6px;
  padding: 6px 10px;
  font-size: 13px;
  cursor: pointer;
}
.msg-media-cargar:disabled { opacity: 0.6; cursor: default; }
.msg-bubble.saliente .msg-media-cargar { border-color: rgba(234, 255, 246, 0.35); background: rgba(0, 0, 0, 0.12); }
.msg-media-doc-nombre { margin-top: 6px; font-size: 13px; font-weight: 600; }
/* Miniatura de documento (Fase 2, parte 51/52) -- mismo criterio de tamaño
   que una imagen inline, pero mas chica (es solo una vista previa, no el
   documento completo). Ancho FIJO (`width`, no `max-width`) para que una
   miniatura chica tambien se agrande a este tamaño, no solo se achique una
   grande.
   Historia corta (2026-09-11): un `jpegThumbnail` ENTRANTE puede venir en
   una resolucion muchisimo mas baja que la nuestra (un caso real midio
   17x24 pixeles) -- agrandarla a 220px, y despues a 150px, seguia
   viendose pixelada/borrosa; reducir el tamaño de MUESTRA no soluciona
   una imagen de origen pobre, solo lo disimula un poco. **La solucion real
   fue generar NOSOTROS la miniatura de un PDF entrante** (misma
   `generarMiniaturaPdf()` que ya se usaba para lo saliente, ver
   `_descargarMediaEntrante()` en accountManager.js) en vez de conformarnos
   con la de WhatsApp -- con eso, tanto lo que se manda como lo que se
   recibe (si es un PDF) usan una miniatura de la MISMA calidad (300px),
   asi que se volvio a subir el ancho de muestra a 220px. Un documento que
   NO es PDF (Word, Excel, ZIP...) sigue dependiendo del `jpegThumbnail` de
   WhatsApp si vino -- ahi si puede seguir viendose borroso en un caso raro,
   limite aceptado (no hay forma de generar una miniatura propia para esos
   formatos, tampoco lo hace WhatsApp Desktop). */
.msg-media-doc-thumb { margin-bottom: 6px; }
.msg-media-doc-thumb img { width: 220px; height: auto; border-radius: 6px; display: block; }

/* Tarjeta de vista previa de un link (pendiente desde "Fase 2 parte 48",
   resuelto en el addendum de "Fase 2 parte 56") -- mismo criterio visual
   que WhatsApp Desktop: miniatura arriba (ancho completo de la tarjeta,
   no los 220px fijos de una miniatura de documento -- de ahi el selector
   compuesto de mayor especificidad de abajo, mismo patron ya usado en este
   proyecto para el bug de `.hidden`+`display`), titulo en negrita,
   descripcion, dominio abajo. */
.msg-link-preview {
  margin-top: 6px;
  border-radius: 8px;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.18);
  max-width: 320px;
}
.msg-bubble.saliente .msg-link-preview { background: rgba(0, 0, 0, 0.12); }
.msg-link-preview .msg-media-doc-thumb { margin-bottom: 0; }
.msg-link-preview .msg-media-doc-thumb img { width: 100%; max-height: 200px; object-fit: cover; border-radius: 0; display: block; }
.msg-link-preview-body { padding: 8px 10px; }
.msg-link-preview-titulo { font-size: 13.5px; font-weight: 600; line-height: 1.3; }
.msg-link-preview-descripcion {
  margin-top: 2px;
  font-size: 12.5px;
  color: var(--muted);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.msg-bubble.saliente .msg-link-preview-descripcion { color: rgba(234, 255, 246, 0.75); }
.msg-link-preview-dominio {
  margin-top: 4px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--muted);
}
.msg-bubble.saliente .msg-link-preview-dominio { color: rgba(234, 255, 246, 0.6); }

.chat-thread-empty.hidden { display: none; }
.chat-thread-empty {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  font-size: 14px;
  text-align: center;
  padding: 24px;
}

/* Recuperar historial (Fase 2, parte 56) -- mismo cuidado de siempre contra
   el bug de especificidad `.hidden` (ver comentario de parte 4/6 en
   CLAUDE.md): `.recuperar-historial-banner.hidden` de mayor especificidad
   para no depender del orden de aparicion en el archivo. */
.recuperar-historial-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px 16px;
  background: var(--card-bg-2);
  border-bottom: 1px solid var(--border);
  font-size: 12.5px;
  color: var(--muted);
  flex-shrink: 0;
}
.recuperar-historial-banner.hidden { display: none; }
/* Variante "agotado" (addendum 2026-09-12, ver CLAUDE.md): mas discreta a
   proposito -- ya no invita a "recuperar mensajes" (no se espera que haya
   mas), pero deja un "Reintentar" chico como defensa por si el estado
   quedo mal marcado por algun motivo. */
.recuperar-historial-banner.agotado {
  opacity: 0.65;
  font-size: 11.5px;
}
.recuperar-historial-banner.agotado .btn-sm {
  padding: 3px 8px;
  font-size: 11px;
}

.chat-reply-form.hidden { display: none; }
.chat-reply-form {
  display: flex;
  gap: 10px;
  padding: 12px clamp(16px, 6vw, 80px);
  border-top: 1px solid var(--border);
  background: var(--card-bg);
  flex-shrink: 0;
}
.chat-reply-form textarea {
  flex: 1;
  resize: none;
  padding: 10px 14px;
  border-radius: 20px;
  border: 1px solid var(--border);
  background: var(--card-bg-2);
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
  max-height: 120px;
}

.reply-adjuntar-btn {
  border: none;
  background: transparent;
  color: var(--muted);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  flex-shrink: 0;
  width: 34px;
  height: 34px;
  border-radius: 50%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background 0.15s, color 0.15s;
}
/* Resalte circular al pasar el mouse (parte 55) -- mismo "toque" tactil que
   WhatsApp Desktop, en vez de solo cambiar el color del icono. */
.reply-adjuntar-btn:hover { color: var(--accent); background: var(--card-bg-2); }

.reply-archivo-preview.hidden { display: none; }
.reply-archivo-preview {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px clamp(16px, 6vw, 80px);
  background: var(--card-bg-2);
  border-top: 1px solid var(--border);
  font-size: 12.5px;
  color: var(--muted);
  flex-shrink: 0;
}
.reply-archivo-preview button {
  border: none;
  background: transparent;
  color: var(--muted);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
.reply-archivo-preview button:hover { color: var(--red-text); }

/* Nota de voz (Fase 2, parte 58) -- barra de grabacion/vista previa, mismo
   contenedor visual que ya usan la cita y el adjunto (`.reply-archivo-preview`)
   para consistencia. `#notaDeVozEstado` muestra "🔴 Grabando... 0:04" (texto
   parpadeante para que se note que hay un microfono activo) o el titulo de
   la vista previa; `.nota-de-voz-audio` es el reproductor una vez detenida
   la grabacion, antes de mandarla. */
#notaDeVozEstado { flex-shrink: 0; }
#notaDeVozEstado.grabando { color: var(--red-text); animation: notaDeVozParpadeo 1.2s ease-in-out infinite; }
@keyframes notaDeVozParpadeo { 0%, 100% { opacity: 1; } 50% { opacity: 0.4; } }
.nota-de-voz-audio { flex: 1; height: 32px; }
.nota-de-voz-audio.hidden { display: none; }
.reply-adjuntar-btn:disabled { opacity: 0.35; cursor: default; }
/* Regla 28: `.hidden` vive en la 1937 y esta clase fija `display: inline-flex` mucho más abajo, con la
   MISMA especificidad -- así que gana la de abajo y el botón no se ocultaba. Hace falta la regla
   compuesta. La necesita el visor al abrir una instancia de Telegram (parte 147): ahí no hay adjuntos,
   ni notas de voz, ni encuestas/ubicación/contacto. */
.reply-adjuntar-btn.hidden { display: none; }
/* El asistente con IA todavía no contratado (parte 148): se VE, atenuado, y al tocarlo se explica.
   Antes se escondía y el producto por el que el cliente se registró no aparecía en ninguna parte. */
.asistente-sin-contratar { opacity: 0.45; }
.asistente-sin-contratar:hover { opacity: 1; }
.reply-adjuntar-btn.grabando { color: var(--red-text); }

/* Menu "⋮" de un chat (Fase 2, parte 59) -- mismo patron de position:fixed
   calculado en JS que ya usan .msg-reaccion-picker/.msg-emoji-picker, para
   no quedar cortado por ningun contenedor con overflow. */
.chat-menu-dropdown {
  position: fixed;
  z-index: 1000;
  background: var(--card-bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 6px;
  min-width: 220px;
  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.4);
  display: flex;
  flex-direction: column;
}
.chat-menu-dropdown button {
  display: flex;
  align-items: center;
  gap: 10px;
  border: none;
  background: transparent;
  color: var(--text);
  cursor: pointer;
  font-size: 13px;
  font-family: inherit;
  padding: 9px 10px;
  border-radius: 6px;
  text-align: left;
  width: 100%;
}
.chat-menu-dropdown button:hover { background: var(--card-bg-2); }
.chat-menu-dropdown button:disabled { opacity: 0.4; cursor: default; }
.chat-menu-dropdown button:disabled:hover { background: transparent; }
.chat-menu-dropdown .chat-menu-separador { border-top: 1px solid var(--border); margin: 4px 0; }
.chat-menu-dropdown .chat-menu-peligro { color: var(--red-text); }

/* Popovers de Listas (Fase 2, parte 64) -- gestion (crear/renombrar/eliminar,
   desde el toolbar) y asignacion (por chat, desde el menu "⋮") -- comparten
   la base `.chat-menu-dropdown` de arriba (mismo position:fixed calculado en
   JS, mismo `cerrarChatMenuDropdown()` para cerrar cualquiera de los dos). */
.listas-gestion-dropdown, .chat-listas-dropdown { min-width: 240px; max-width: 300px; }
.listas-gestion-titulo { font-size: 11px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; padding: 4px 8px 8px; }
.listas-gestion-filas, .chat-listas-checks { display: flex; flex-direction: column; gap: 2px; max-height: 240px; overflow-y: auto; }
.listas-gestion-vacio { padding: 8px; color: var(--muted); font-size: 12px; }
.lista-gestion-fila { display: flex; align-items: center; gap: 6px; padding: 6px 8px; border-radius: 6px; }
.lista-gestion-fila:hover { background: var(--card-bg-2); }
.lista-gestion-nombre { flex: 1; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Bug real (preexistente, parte 64 -- se hizo visible recien con datos reales
   de la parte 74): `.chat-menu-dropdown button` (arriba) fija `width: 100%;
   display: flex;`, pensado para los items del menu "⋮" (una fila completa
   por boton). Con la misma especificidad, esta regla NUNCA sobreescribia
   `width`/`display` -- los 2 botones (renombrar/eliminar) se repartian TODO
   el ancho de la fila entre los dos, y el nombre de la lista (`flex: 1;
   flex-basis: 0%`) quedaba en 0px de ancho, invisible. */
.lista-gestion-fila button { display: inline-flex; width: auto; flex: 0 0 auto; border: none; background: transparent; cursor: pointer; font-size: 13px; padding: 2px 4px; border-radius: 4px; color: var(--text); }
.lista-gestion-fila button:hover { background: var(--card-bg); }
.chat-lista-check { display: flex; align-items: center; gap: 8px; padding: 7px 8px; border-radius: 6px; font-size: 13px; cursor: pointer; }
.chat-lista-check:hover { background: var(--card-bg-2); }
.chat-lista-check input { accent-color: var(--accent); cursor: pointer; }
.listas-gestion-nueva { display: flex; flex-wrap: wrap; gap: 6px; padding: 8px 4px 2px; border-top: 1px solid var(--border); margin-top: 4px; }
.listas-gestion-nueva input[type="text"] {
  flex: 1;
  min-width: 0;
  padding: 6px 8px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--card-bg-2);
  color: var(--text);
  font-family: inherit;
  font-size: 12.5px;
}
/* Checkbox de "sincronizar con el telefono" (Fase 2, parte 74) -- una etiqueta
   real de WhatsApp en vez de una lista interna, con el limite real de ~20.
   Ocupa su propia fila (flex-basis: 100%) para no amontonarse con el input
   de texto y el boton "Crear" en un dropdown angosto. */
.lista-sync-check { flex-basis: 100%; display: flex; align-items: center; gap: 6px; font-size: 11.5px; color: var(--muted); cursor: pointer; padding: 2px 2px; }
.lista-sync-check input { accent-color: var(--accent); cursor: pointer; }
/* Boton "Recuperar listas del telefono" (Fase 2, parte 74, addendum) --
   ancho completo del dropdown, margen abajo para separarlo de las filas. */
.listas-recuperar-telefono-btn { width: 100%; margin: 2px 0 8px; }

/* Buscar dentro de un chat (Fase 2, parte 59) -- barra fija arriba del hilo,
   mismo look que el resto de las barras de este panel. Mismo cuidado de
   siempre con el bug de especificidad .hidden + display propio (ver "Fase 2
   parte 4/6") -- la regla compuesta va antes a proposito, por legibilidad,
   aunque gana igual sin importar el orden. */
.chat-busqueda-bar.hidden { display: none; }
.chat-busqueda-bar {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px clamp(16px, 6vw, 80px);
  background: var(--card-bg-2);
  border-bottom: 1px solid var(--border);
  flex-shrink: 0;
}
.chat-busqueda-bar input {
  flex: 1;
  padding: 8px 10px;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: var(--card-bg);
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
}
.chat-busqueda-bar button {
  border: none;
  background: transparent;
  color: var(--muted);
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
}
.chat-busqueda-bar button:hover { color: var(--red-text); }

.chat-busqueda-resultados.hidden { display: none; }
.chat-busqueda-resultados {
  overflow-y: auto;
  flex: 1;
  border-bottom: 1px solid var(--border);
}
.chat-busqueda-resultado-item {
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  font-size: 12.5px;
}
.chat-busqueda-resultado-item:hover { background: var(--card-bg-2); }
.chat-busqueda-resultado-item .fecha { color: var(--muted); font-size: 11px; margin-bottom: 2px; }
.chat-busqueda-resultado-item .texto { white-space: pre-wrap; word-break: break-word; }
.chat-busqueda-sin-resultados { padding: 24px 16px; color: var(--muted); font-size: 13px; text-align: center; }

/* Resalte breve al saltar a un mensaje encontrado por el buscador. */
@keyframes msgResaltado {
  0%, 100% { background: transparent; }
  30% { background: var(--accent-glow); }
}
.msg-bubble.resaltado { animation: msgResaltado 1.6s ease-out; }

/* Favorito / Cerrado / Bloqueado (Fase 2, parte 59) -- badges chicos en la
   lista de chats, mismo criterio visual que .chat-bloqueo-badge (parte 38). */
.chat-favorito-badge, .chat-cerrado-badge { font-size: 12px; }
.chat-list-item.cerrado { opacity: 0.65; }
.chat-list-item.cerrado .chat-list-name::after {
  content: ' (cerrado)';
  font-weight: 400;
  color: var(--muted);
  font-size: 11px;
}

/* Archivado (Fase 2, parte 73) -- mismo criterio visual atenuado que
   cerrado/eliminado, oculto por defecto (toggle "Mostrar archivados"). */
.chat-list-item.archivado { opacity: 0.65; }

/* Mensaje de "contacto bloqueado" que reemplaza el formulario de respuesta --
   mismo lugar/estilo que .reply-archivo-preview, para no agregar un patron
   visual nuevo solo para este caso. */
.reply-bloqueado-aviso {
  padding: 10px clamp(16px, 6vw, 80px);
  background: var(--card-bg-2);
  border-top: 1px solid var(--border);
  font-size: 12.5px;
  color: var(--muted);
  text-align: center;
  flex-shrink: 0;
}
.reply-bloqueado-aviso.hidden { display: none; }

/* Bot de IA en un chat (Fase 2, parte 123): aviso sobre el cuadro de
   respuesta cuando el bot esta frenado en ESTE chat, con el boton para volver
   a encenderlo ahi mismo. Compuesta `.bot-chat-banner.hidden` (regla dura 28). */
.bot-chat-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px clamp(16px, 4vw, 40px);
  background: var(--yellow-bg);
  border-top: 1px solid var(--border);
  font-size: 12.5px;
  color: var(--yellow-text);
  flex-shrink: 0;
}
.bot-chat-banner.hidden { display: none; }

/* Modo entrenamiento (parte 181): el asistente redactó una respuesta y NO la envió -- está en el
   cuadro de abajo, lista para corregir y mandar. Va en el celeste del acento y no en el ámbar del
   aviso de arriba a propósito: esto no es un problema que revisar, es trabajo hecho esperando un
   visto bueno. Por eso usa el verde de «listo» (`--ok-bg`/`--ok-text`, tokens que ya existen --
   regla 44) y no el ámbar del aviso de arriba, que quiere decir «algo está frenado».
   Compuesta `.borrador-bot-banner.hidden` (reglas 28 y 134). */
.borrador-bot-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 8px clamp(16px, 4vw, 40px);
  background: var(--ok-bg);
  border-top: 1px solid var(--border);
  font-size: 12.5px;
  color: var(--ok-text);
  flex-shrink: 0;
}
.borrador-bot-banner.hidden { display: none; }
.borrador-bot-banner span { min-width: 0; }
.chat-bot-badge { font-size: 12px; white-space: nowrap; }

@media (max-width: 760px) {
  .chats-layout { flex-direction: column; }
  .chat-list-panel { width: 100%; max-height: 40vh; border-right: none; border-bottom: 1px solid var(--border); }
}

/* --- Barra de la lista de chats (Fase 2, parte 122) ---------------------------
   Replica la propuesta aprobada por el usuario (maqueta "Bandeja compacta"):
   cabecera (volver / instancia / total), fila de busqueda con 3 botones
   cuadrados, fila de chips que se pliega al bajar por la lista, menus con el
   nombre de cada accion escrito, y filas de chat de 2 lineas con el numero
   junto al nombre. Solo tokens de color de :root (regla dura 44). Las reglas
   compuestas `.x.hidden` son a proposito (regla dura 28): cada una de estas
   clases define su propio `display` y le ganaria a `.hidden` (definida mas
   arriba en el archivo). */

.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.chat-list-panel { position: relative; }

.barra-cabecera {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 12px;
  min-height: 52px;
  flex-shrink: 0;
}
/* Mientras se seleccionan chats, la barra de seleccion REEMPLAZA a la cabecera
   (mismo lugar, no una fila mas). */
.chat-list-panel:has(#chatSeleccionBar:not(.hidden)) .barra-cabecera { display: none; }

.barra-instancia { flex: 1; min-width: 0; display: block; margin: 0; }
.barra-instancia select {
  width: 100%;
  height: 36px;
  padding: 0 10px;
  font-size: 13.5px;
  font-weight: 600;
  border-radius: 8px;
}
.barra-total {
  flex-shrink: 0;
  font-size: 12px;
  font-weight: 400;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.barra-total.hidden { display: none; }

.barra-btn {
  position: relative;
  flex: none;
  width: 36px;
  height: 36px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--card-bg-2);
  color: var(--text);
  font-family: inherit;
  font-size: 16px;
  line-height: 1;
  text-decoration: none;
  cursor: pointer;
}
.barra-btn:hover { border-color: var(--accent-dim); }
.barra-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.barra-btn-texto { width: auto; padding: 0 12px; font-size: 13px; font-weight: 600; }
/* Recuperar historial en curso: el progreso vive en un item del menu, asi que
   el boton "⋯" lo marca. */
.barra-btn.ocupado { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-glow); }

.insignia {
  position: absolute;
  top: -5px;
  right: -5px;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  border-radius: 9px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 11px;
  font-weight: 700;
  display: grid;
  place-items: center;
  font-variant-numeric: tabular-nums;
}
.insignia.hidden { display: none; }

.chat-list-toolbar { padding: 0 12px 8px; gap: 8px; }
.chat-list-toolbar > .seleccion-bar { margin: 0 -12px; padding: 10px 12px; min-height: 52px; }

.barra-buscar { display: flex; align-items: center; gap: 6px; }
.barra-buscador { position: relative; flex: 1; min-width: 0; margin: 0; }
.barra-buscador input[type="text"] {
  width: 100%;
  height: 36px;
  padding: 0 30px 0 32px;
  font-size: 13.5px;
  border-radius: 8px;
}
.barra-lupa {
  position: absolute;
  left: 10px;
  top: 50%;
  transform: translateY(-50%);
  font-size: 13px;
  pointer-events: none;
}
.barra-limpiar {
  position: absolute;
  right: 4px;
  top: 50%;
  transform: translateY(-50%);
  width: 26px;
  height: 26px;
  border: 0;
  border-radius: 6px;
  background: none;
  color: var(--muted);
  font-size: 16px;
  cursor: pointer;
}
.barra-limpiar:hover { color: var(--text); }
.barra-limpiar.hidden { display: none; }
.barra-enviar-oculto { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }
.barra-error { margin: 0; font-size: 12px; min-height: 0; }
.barra-error:empty { display: none; }

/* Fila de chips: se pliega al bajar por la lista y vuelve al subir (ver
   chats-barra.js). */
.barra-chips { overflow: hidden; max-height: 90px; transition: max-height 0.22s ease, opacity 0.22s ease; }
.barra-chips-interior { display: flex; flex-wrap: wrap; gap: 6px; padding: 2px 2px 2px 0; }
.barra-slot { display: contents; }
.chat-list-panel.barra-colapsada .barra-chips { max-height: 0; opacity: 0; }

.chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--card-bg-2);
  color: var(--text);
  font-family: inherit;
  font-size: 12.5px;
  font-weight: 600;
  line-height: 1.3;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
}
.chip:hover { border-color: var(--accent-dim); }
.chip.activo, .chip-check:has(input:checked) { background: var(--accent); color: var(--on-accent); border-color: transparent; }
.chip .n { font-variant-numeric: tabular-nums; font-weight: 700; opacity: 0.8; }
.chip .n:empty { display: none; }
.chip .x { margin-left: 2px; font-size: 15px; line-height: 1; }
.chip-check input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.chip-check:has(input:focus-visible), .chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.chip.hidden { display: none; }

/* Menus flotantes (Filtros / Nuevo / Acciones / Listas). */
.barra-menu {
  position: absolute;
  z-index: 60;
  min-width: 236px;
  max-width: calc(100% - 16px);
  overflow-y: auto;
  padding: 6px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--card-bg);
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
}
.barra-menu.hidden { display: none; }
.barra-menu-titulo {
  margin: 0;
  padding: 6px 10px 2px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}
.barra-menu-item {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 8px 10px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--text);
  font-family: inherit;
  font-size: 13.5px;
  font-weight: 400;
  text-align: left;
  cursor: pointer;
}
.barra-menu-item:hover, .barra-menu-item:focus-visible, .barra-menu-check:has(input:focus-visible) { background: var(--card-bg-2); outline: none; }
.barra-menu-item:disabled { opacity: 0.5; cursor: default; }
.barra-menu-item.hidden { display: none; }
.barra-menu-item .menu-ic { width: 20px; flex: none; text-align: center; }
.barra-menu-item .der { margin-left: auto; color: var(--muted); font-size: 11.5px; font-variant-numeric: tabular-nums; }
.barra-menu-item.seleccionada .menu-txt { font-weight: 700; color: var(--accent); }
.barra-menu-item .etiqueta {
  margin-left: 8px;
  padding: 1px 6px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--card-bg-2);
  color: var(--muted);
  font-size: 10.5px;
  font-weight: 700;
}
.barra-menu-sep { height: 1px; margin: 6px 4px; background: var(--border); }
.barra-menu-peligro { color: var(--red-text); }
.barra-menu-peligro:hover, .barra-menu-peligro:focus-visible { background: var(--red-bg); }
.barra-menu-pie { display: flex; align-items: center; justify-content: space-between; gap: 8px; padding: 2px 4px 0 10px; }
.barra-menu-pie .hint { margin: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; }

/* Interruptor (checkbox real oculto dentro del item; el span dibuja la palanca). */
.barra-menu-check input { position: absolute; opacity: 0; pointer-events: none; }
.interruptor {
  position: relative;
  flex: none;
  width: 32px;
  height: 18px;
  margin-left: auto;
  border-radius: 999px;
  background: var(--border);
  transition: background 0.15s;
}
.interruptor::after {
  content: "";
  position: absolute;
  top: 2px;
  left: 2px;
  width: 14px;
  height: 14px;
  border-radius: 50%;
  background: var(--card-bg);
  box-shadow: 0 0 0 1px var(--muted) inset;
  transition: transform 0.15s;
}
.barra-menu-check input:checked + .interruptor { background: var(--accent); }
.barra-menu-check input:checked + .interruptor::after { transform: translateX(14px); box-shadow: none; }

/* Filas de la lista: 2 lineas, numero junto al nombre. */
.chat-list .chat-avatar { width: 40px; height: 40px; font-size: 14px; }
.chat-list .chat-list-item { position: relative; min-height: 64px; padding: 10px 14px; }
.chat-list-info {
  display: grid;
  grid-template-columns: minmax(0, max-content) minmax(0, 1fr);
  column-gap: 6px;
  align-items: baseline;
}
.chat-list-info .chat-list-name { grid-column: 1; grid-row: 1; }
.chat-list-info .chat-list-numero { grid-column: 2; grid-row: 1; }
.chat-list-info .chat-list-preview { grid-column: 1 / -1; grid-row: 2; }
/* El tacho por fila aparece solo al pasar el mouse (antes ocupaba una tercera
   linea de la fila); en pantallas tactiles queda siempre visible. */
.chat-list-item .msg-delete-btn {
  position: absolute;
  top: 8px;
  right: 8px;
  padding: 2px 6px;
  border-radius: 6px;
  background: var(--card-bg-2);
  opacity: 0;
}
.chat-list-item:hover .msg-delete-btn, .chat-list-item:focus-within .msg-delete-btn { opacity: 0.85; }
@media (hover: none) {
  .chat-list-item .msg-delete-btn { opacity: 0.6; }
}
.chat-list-iniciar { background: var(--accent-glow); }
.chat-list-iniciar .chat-avatar { background: var(--accent); color: var(--on-accent); font-size: 20px; }

/* Vista compacta (menu "⋯"): aun mas chats a la vista. */
.chat-list.compacta .chat-list-item { min-height: 52px; padding: 6px 14px; }
.chat-list.compacta .chat-avatar { width: 34px; height: 34px; font-size: 12px; }
.chat-list.compacta .chat-list-numero { display: none; }
.chat-list.compacta .chat-list-meta { flex-direction: row; align-items: center; gap: 8px; }

/* Sugerencias de contactos del buscador: el campo ahora es angosto, pero la
   lista necesita ancho para nombre + numero. */
.contacto-dropdown { min-width: 260px; }

/* ===== Un mismo idioma visual en todo el panel (parte 130, pedido del usuario) =====
   El título de cada sección y cada tarjeta llevan la «ficha» de color de su sección (la misma del menú lateral); los indicadores
   de Instancias también. Solo tokens (regla 44). */
.titulo-vista { display: flex; align-items: center; gap: 14px; min-width: 0; }
.titulo-vista h1 { margin-bottom: 0; }
.ficha-vista {
  flex: 0 0 auto;
  box-sizing: content-box;
  width: 24px;
  height: 24px;
  padding: 11px;
  border-radius: 14px;
  fill: none;
  stroke: var(--card-bg);
  stroke-width: 1.9;
  stroke-linecap: round;
  stroke-linejoin: round;
  background: var(--ic, var(--accent));
  box-shadow: 0 12px 26px -14px var(--ic, var(--accent));
}
.card-head h2 { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.card-head h2 svg.ficha { width: 18px; height: 18px; padding: 7px; }
.stat-card { display: flex; align-items: center; gap: 16px; }
.stat-card svg.ficha { width: 22px; height: 22px; padding: 11px; border-radius: 13px; }
.stat-textos { min-width: 0; }

/* Selector de archivo propio: el del navegador decía «Choose File / No file chosen» en inglés. */
.archivo-elegir { position: relative; display: flex; align-items: center; gap: 12px; min-height: 42px; padding: 5px; border: 1px dashed var(--border); border-radius: 8px; background: var(--card-bg-2); }
.archivo-elegir input[type="file"] { position: absolute; width: 1px; height: 1px; padding: 0; border: 0; opacity: 0; overflow: hidden; }
.archivo-boton { display: inline-flex; align-items: center; gap: 8px; padding: 7px 14px; font-size: 13.5px; cursor: pointer; color: var(--text); font-weight: 700; }
.archivo-boton .ic { width: 16px; height: 16px; }
.archivo-elegir input[type="file"]:focus-visible + .archivo-boton { outline: 2px solid var(--accent); outline-offset: 2px; }
.archivo-nombre { color: var(--muted); font-size: 13px; font-weight: 400; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.archivo-elegir.con-archivo { border-style: solid; border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); }
.archivo-elegir.con-archivo .archivo-nombre { color: var(--text); }
.btn-ic { display: inline-flex; align-items: center; gap: 8px; }
.btn-ic .ic { width: 16px; height: 16px; }
.detalle-ayuda { margin: 8px 0 14px; font-size: 13px; color: var(--muted); }
.detalle-ayuda summary { cursor: pointer; color: var(--accent); font-weight: 600; width: fit-content; }
.detalle-ayuda p { margin: 8px 0 0; line-height: 1.55; max-width: 110ch; }

/* Botón flotante «Ir arriba» de las secciones largas del panel (aparece al bajar; Configuración lo trae en su barra). */
.ir-arriba-flotante { position: fixed; right: 24px; bottom: calc(24px + env(safe-area-inset-bottom, 0px)); z-index: 30; background: var(--card-bg); box-shadow: 0 12px 28px -14px var(--bg); }
.ir-arriba-flotante[hidden] { display: none; }

/* ===== Configuración (retoque de la parte 130) ===== */
.cfg-resumen { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 12px; margin-bottom: 20px; }
@media (max-width: 1300px) { .cfg-resumen { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 760px) { .cfg-resumen { grid-template-columns: minmax(0, 1fr); } }
.cfg-dato {
  --tono: var(--muted);
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px 18px;
  border-radius: var(--radius);
  background: var(--card-bg);
  border: 1px solid var(--border);
  color: var(--text);
  text-decoration: none;
  position: relative;
  overflow: hidden;
  transition: border-color 0.15s ease, transform 0.15s ease;
}
.cfg-dato::after { content: ""; position: absolute; left: 0; top: 14px; bottom: 14px; width: 3px; border-radius: 0 3px 3px 0; background: var(--tono); opacity: 0.9; }
.cfg-dato[data-tono="ok"] { --tono: var(--accent); }
.cfg-dato[data-tono="aviso"] { --tono: var(--yellow-text); }
.cfg-dato[data-tono="peligro"] { --tono: var(--red-text); }
.cfg-dato:hover { border-color: color-mix(in srgb, var(--tono) 45%, var(--border)); transform: translateY(-1px); }
.cfg-dato:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cfg-dato-txt { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.cfg-dato-tit { font-size: 11px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; color: var(--muted); }
.cfg-dato-val { font-size: 16px; font-weight: 700; margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-variant-numeric: tabular-nums; }
.cfg-dato-sub { font-size: 12.5px; color: var(--muted); margin-top: 1px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cfg-dato[data-tono="aviso"] .cfg-dato-sub { color: var(--yellow-text); }
.cfg-barra { display: block; height: 6px; border-radius: 999px; background: var(--card-bg-2); border: 1px solid var(--border); overflow: hidden; margin-top: 7px; }
.cfg-barra[hidden] { display: none; }
.cfg-barra > span { display: block; height: 100%; width: 0; border-radius: 999px; background: var(--accent); transition: width 0.5s ease; }
.cfg-barra[data-tono="aviso"] > span { background: var(--yellow-text); }
.cfg-barra[data-tono="peligro"] > span { background: var(--red-text); }
.cfg-barra-grande { height: 8px; margin: 0 0 8px; max-width: 520px; }

.cfg-cuerpo { display: grid; grid-template-columns: minmax(0, 1fr); gap: 24px; align-items: start; }
@media (min-width: 1200px) { .cfg-cuerpo { grid-template-columns: 220px minmax(0, 1fr); } }
.cfg-indice { display: none; }
@media (min-width: 1200px) { .cfg-indice { display: flex; flex-direction: column; gap: 4px; position: sticky; top: 20px; } }
.cfg-indice a { display: flex; align-items: center; gap: 10px; padding: 7px 10px; border-radius: 10px; color: var(--muted); text-decoration: none; font-size: 13.5px; font-weight: 600; transition: background 0.15s ease, color 0.15s ease; }
.cfg-indice a svg.ficha { width: 16px; height: 16px; padding: 6px; border-radius: 8px; }
.cfg-indice a:hover { background: var(--card-bg); color: var(--text); }
.cfg-indice a.activo { background: var(--card-bg); color: var(--text); box-shadow: inset 0 0 0 1px var(--border); }
.cfg-indice a.activo svg.ficha { background: var(--ic); stroke: var(--card-bg); }
.cfg-indice a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cfg-secciones { display: flex; flex-direction: column; min-width: 0; }
.cfg-form { display: flex; flex-direction: column; }
.cfg-sec { scroll-margin-top: 16px; }
.cfg-sec-cab { display: flex; align-items: flex-start; gap: 14px; margin-bottom: 18px; }
.cfg-sec-cab > div { flex: 1 1 auto; min-width: 0; }
.cfg-sec-cab h2 { margin: 0; font-size: 18px; font-weight: 700; letter-spacing: -0.01em; }
.cfg-sec-cab p { margin: 4px 0 0; color: var(--muted); font-size: 13.5px; }
.cfg-sec-cab svg.ficha { width: 20px; height: 20px; padding: 9px; border-radius: 12px; }
.cfg-sec-cab > .btn-sm { flex: 0 0 auto; }
.cfg-campos { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px 20px; }
@media (max-width: 900px) { .cfg-campos { grid-template-columns: minmax(0, 1fr); } }
.cfg-campos .field { min-width: 0; }
.cfg-campos .hint { margin: 2px 0 0; font-size: 12.5px; }
.cfg-detalle { margin-top: 16px; font-size: 13px; color: var(--muted); border-top: 1px dashed var(--border); padding-top: 12px; }
.cfg-detalle summary { cursor: pointer; color: var(--accent); font-weight: 600; width: fit-content; }
.cfg-detalle p { margin: 8px 0 0; line-height: 1.55; max-width: 110ch; }
.cfg-tabla-planes { border: 1px solid var(--border); border-radius: 12px; overflow: hidden; max-width: 820px; }
.cfg-plan-fila { display: grid; grid-template-columns: minmax(130px, 1fr) minmax(0, 1.2fr) minmax(0, 1.4fr); align-items: center; gap: 16px; padding: 10px 16px; border-top: 1px solid var(--border); background: var(--card-bg-2); }
.cfg-plan-cab { border-top: none; background: var(--card-bg); color: var(--muted); font-size: 11.5px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; }
.cfg-plan-cab small { display: block; margin-top: 2px; font-size: 11.5px; font-weight: 500; letter-spacing: 0; text-transform: none; }
.cfg-plan { display: inline-flex; align-items: center; gap: 10px; font-weight: 600; }
.cfg-plan .ic { color: var(--accent); width: 16px; height: 16px; }
.cfg-celda input { width: 100%; max-width: 220px; font-variant-numeric: tabular-nums; }
/* Separador de canal dentro de la tabla de planes (parte 145): sin esto «Básico» y «Estándar»
   parecerían de los dos canales a la vez, y son listas distintas. */
.cfg-plan-subcab { background: var(--card-bg); padding-top: 12px; padding-bottom: 6px; }
.cfg-plan-subcab [role="rowheader"] { display: flex; align-items: center; gap: 8px; font-size: 12px; text-transform: uppercase; letter-spacing: 0.6px; color: var(--muted); font-weight: 600; }
/* Telegram todavía no descarga adjuntos, así que esa celda no tiene nada que preguntar: se dice,
   en vez de dejar un campo que no hace nada (regla 115: una ventana que no tiene qué ofrecer, no
   lo ofrece). */
.cfg-celda-na { color: var(--muted); font-size: 13px; font-style: italic; }
@media (max-width: 640px) { .cfg-plan-fila { grid-template-columns: minmax(0, 1fr); gap: 6px; } .cfg-plan-cab { display: none; } }
.cfg-estado-servicio { margin: -6px 0 16px; font-weight: 600; }
.cfg-gasto { margin-top: 16px; }
.cfg-acciones { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 18px; }
.cfg-acciones .form-error { margin: 0; }
/* Botón con ícono + texto (regla 85). Sirve para cualquier botón -- `.btn`, `.btn-sm`, los sutiles
   de login --, no solo los de Configuración: `.cfg-btn-ic` es el nombre viejo, de cuando esto existía
   solo ahí, y se conserva para no tocar el marcado que ya lo usa. */
.btn-ic, .cfg-btn-ic { display: inline-flex; align-items: center; gap: 8px; }
.btn-ic .ic, .cfg-btn-ic .ic { width: 16px; height: 16px; }
/* Regla 28/134, y acá NO es teórica: esto fija `display` en la línea ~3824, MUY por debajo de
   `.hidden` (1937), así que sin esta línea un botón con ícono al que se le ponga `hidden` se
   quedaría a la vista. Varios nacen ocultos a propósito: «Reanudar hoy», «Quitar clave»,
   «Volver a la global», «Reactivar todos». */
.btn-ic.hidden, .cfg-btn-ic.hidden { display: none; }
/* Barra para guardar: fija al pie mientras se recorre el formulario; se enciende cuando hay cambios. */
.cfg-pie {
  position: sticky;
  bottom: 14px;
  z-index: 5;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
  margin: 0 0 20px;
  padding: 10px 12px 10px 18px;
  border-radius: var(--radius);
  background: var(--card-bg);
  border: 1px solid var(--border);
  box-shadow: 0 16px 36px -22px var(--bg);
}
.cfg-pie-estado { display: flex; align-items: center; gap: 10px; min-width: 0; color: var(--muted); font-size: 13.5px; flex-wrap: wrap; }
.cfg-pie-estado .form-error { margin: 0; min-height: 0; }
.cfg-pie-estado .form-error:empty { display: none; }
.cfg-pie-punto { width: 8px; height: 8px; border-radius: 50%; background: var(--muted); flex: 0 0 auto; }
.cfg-pie.sucio { border-color: color-mix(in srgb, var(--yellow-text) 55%, var(--border)); }
.cfg-pie.sucio .cfg-pie-punto { background: var(--yellow-text); box-shadow: 0 0 0 4px color-mix(in srgb, var(--yellow-text) 18%, transparent); }
.cfg-pie.sucio #cfgPieTexto { color: var(--yellow-text); font-weight: 600; }
@media (prefers-reduced-motion: reduce) { .cfg-dato, .cfg-barra > span { transition: none; } .cfg-dato:hover { transform: none; } }

/* ===== Estadísticas (parte 130) =====
   Filtros arriba, 6 indicadores con su variación frente al periodo anterior, gráficas (Chart.js) y un mapa de calor propio.
   Solo tokens (regla 44); las gráficas leen los mismos tokens desde public/estadisticas.js. */
.est-filtros { display: flex; align-items: flex-end; gap: 18px; flex-wrap: wrap; padding: 18px 22px; }
.est-campo-instancia { flex: 0 1 300px; min-width: 200px; }
.est-periodos { display: inline-flex; gap: 4px; padding: 4px; border-radius: 12px; background: var(--card-bg-2); border: 1px solid var(--border); flex-wrap: wrap; }
.est-periodo { font: inherit; font-size: 13px; font-weight: 600; padding: 7px 13px; border-radius: 9px; border: none; background: transparent; color: var(--muted); cursor: pointer; transition: background 0.15s ease, color 0.15s ease; }
.est-periodo:hover { color: var(--text); }
.est-periodo.activo { background: var(--card-bg); color: var(--text); box-shadow: 0 1px 0 var(--border), inset 0 0 0 1px var(--border); }
.est-periodo:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.est-acciones { display: flex; align-items: center; gap: 12px; margin-left: auto; }
.est-en-vivo { display: inline-flex; align-items: center; gap: 8px; color: var(--muted); font-size: 12.5px; }
.est-en-vivo-punto { width: 8px; height: 8px; border-radius: 50%; background: var(--accent); box-shadow: 0 0 0 0 var(--accent-glow); animation: est-latido 2.4s ease-in-out infinite; }
@keyframes est-latido { 50% { box-shadow: 0 0 0 6px var(--accent-glow); } }
.est-rango { margin: -6px 0 14px; color: var(--muted); font-size: 13px; }
#estError:empty { display: none; }
.est-kpis { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 12px; margin-bottom: 20px; }
@media (max-width: 1500px) { .est-kpis { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 760px) { .est-kpis { grid-template-columns: minmax(0, 1fr); } }
.est-kpi { position: relative; display: flex; align-items: flex-start; gap: 14px; padding: 18px; border-radius: var(--radius); background: var(--card-bg); border: 1px solid var(--border); min-width: 0; }
.est-kpi svg.ficha { width: 20px; height: 20px; padding: 9px; border-radius: 12px; }
.est-kpi-txt { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.est-kpi-tit { font-size: 11px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; color: var(--muted); padding-right: 54px; }
.est-kpi-val { font-size: 26px; font-weight: 800; letter-spacing: -0.02em; margin-top: 4px; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.est-kpi-sub { font-size: 12.5px; color: var(--muted); margin-top: 2px; line-height: 1.4; }
.est-delta { position: absolute; top: 14px; right: 14px; font-size: 12px; font-weight: 700; padding: 3px 8px; border-radius: 999px; background: var(--card-bg-2); color: var(--muted); border: 1px solid var(--border); font-variant-numeric: tabular-nums; }
.est-delta[hidden] { display: none; }
.est-delta[data-tono="bueno"] { background: var(--ok-bg); color: var(--ok-text); border-color: transparent; }
.est-delta[data-tono="malo"] { background: var(--red-bg); color: var(--red-text); border-color: transparent; }
.est-minutos { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 8px; font-size: 12px; font-weight: 500; color: var(--muted); }
.est-minutos input { flex: 0 0 60px; width: 60px; max-width: 60px; padding: 4px 6px; font-size: 12.5px; }
.est-grid { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 16px; }
@media (max-width: 1100px) { .est-grid { grid-template-columns: minmax(0, 1fr); } }
.est-card { margin: 0; padding: 20px 22px; display: flex; flex-direction: column; gap: 14px; min-width: 0; }
.est-card-cab { display: flex; align-items: flex-start; gap: 12px; }
.est-card-cab svg.ficha { width: 18px; height: 18px; padding: 8px; border-radius: 11px; }
.est-card-cab h2 { margin: 0; font-size: 16px; font-weight: 700; }
.est-card-cab p { margin: 3px 0 0; color: var(--muted); font-size: 13px; }
.est-lienzo { position: relative; width: 100%; min-width: 0; }
.est-lienzo-alto { height: 290px; }
.est-lienzo-dona { height: 290px; }
.est-lienzo-bajo { height: 150px; }
.est-nota, .est-calor-pico { margin: 0; color: var(--muted); font-size: 12.5px; line-height: 1.5; }
.est-calor { display: flex; flex-direction: column; gap: 3px; overflow-x: auto; padding-bottom: 2px; }
.est-calor-fila { display: grid; grid-template-columns: 38px repeat(24, minmax(14px, 1fr)); gap: 3px; align-items: center; min-width: 460px; }
.est-calor-cab span { font-size: 10.5px; color: var(--muted); text-align: left; }
.est-calor-dia { font-size: 11.5px; font-weight: 600; color: var(--muted); }
.est-calor-celda { height: 22px; border-radius: 5px; background: var(--card-bg-2); background: color-mix(in srgb, var(--ic-cian) calc(var(--nivel, 0) * 100%), var(--card-bg-2)); border: 1px solid color-mix(in srgb, var(--border) 70%, transparent); cursor: default; transition: transform 0.1s ease; }
.est-calor-celda:hover { transform: scale(1.15); border-color: var(--ic-cian); }
.est-ahora { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
.est-ahora li { display: grid; grid-template-columns: auto auto minmax(0, 1fr); align-items: center; gap: 12px; padding: 10px 12px; border-radius: 12px; background: var(--card-bg-2); border: 1px solid var(--border); }
.est-ahora li[data-tono="aviso"] { border-color: color-mix(in srgb, var(--yellow-text) 45%, var(--border)); }
.est-ahora li svg.ficha { width: 16px; height: 16px; padding: 7px; }
.est-ahora strong { font-size: 20px; font-variant-numeric: tabular-nums; }
.est-ahora span { color: var(--muted); font-size: 13px; }
.est-ahora li:has(.est-ahora-ir) { grid-template-columns: auto auto minmax(0, 1fr) auto; }
.est-ahora-ir { font-size: 12.5px; font-weight: 600; color: var(--accent); text-decoration: none; white-space: nowrap; }
.est-ahora-ir:hover, .est-ahora-ir:focus-visible { text-decoration: underline; }
.est-ir-chats { align-self: flex-start; text-decoration: none; }
@media (prefers-reduced-motion: reduce) { .est-en-vivo-punto { animation: none; } .est-calor-celda:hover { transform: none; } }

/* ===== Guía paso a paso (parte 130) =====
   Oscurece todo menos lo que se explica (un recorte con sombra gigante) y lo señala con una burbuja. Por encima de las ventanas
   (100) y los desplegables (1000), por debajo de los avisos (2000). Solo tokens (regla 44). */
.guia-capa { position: fixed; inset: 0; z-index: 1500; display: none; }
.guia-capa.visible { display: block; }
.guia-capa.sin-foco { background: color-mix(in srgb, var(--bg) 72%, transparent); }
.guia-foco {
  position: fixed;
  border-radius: 12px;
  box-shadow: 0 0 0 9999px color-mix(in srgb, var(--bg) 72%, transparent), 0 0 0 2px var(--accent), 0 0 24px 4px color-mix(in srgb, var(--accent) 45%, transparent);
  pointer-events: none;
  transition: left 0.25s ease, top 0.25s ease, width 0.25s ease, height 0.25s ease;
}
.guia-burbuja {
  position: fixed;
  width: 340px;
  box-sizing: border-box;
  padding: 16px 18px 14px;
  border-radius: 14px;
  background: var(--card-bg);
  border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--border));
  box-shadow: 0 24px 60px -24px var(--bg), 0 0 0 1px color-mix(in srgb, var(--accent) 10%, transparent);
  color: var(--text);
}
.guia-burbuja.entra { animation: guia-entra 0.22s ease-out; }
@keyframes guia-entra { from { opacity: 0; transform: translateY(6px) scale(0.98); } to { opacity: 1; transform: none; } }
.guia-flecha { position: absolute; width: 14px; height: 14px; background: var(--card-bg); border: 1px solid color-mix(in srgb, var(--accent) 40%, var(--border)); transform: rotate(45deg); }
.guia-flecha[hidden] { display: none; }
.guia-burbuja[data-lado="abajo"] .guia-flecha { top: -8px !important; border-right: none; border-bottom: none; }
.guia-burbuja[data-lado="arriba"] .guia-flecha { top: auto !important; bottom: -8px; border-left: none; border-top: none; }
.guia-burbuja[data-lado="derecha"] .guia-flecha { left: -8px !important; border-top: none; border-right: none; }
.guia-burbuja[data-lado="izquierda"] .guia-flecha { left: auto !important; right: -8px; border-bottom: none; border-left: none; }
.guia-cab { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.guia-ficha { display: inline-flex; width: 28px; height: 28px; align-items: center; justify-content: center; border-radius: 9px; background: var(--accent); color: var(--on-accent); flex: 0 0 auto; }
.guia-ficha .ic { width: 16px; height: 16px; }
.guia-contador { flex: 1 1 auto; font-size: 11px; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase; color: var(--accent); }
.guia-cerrar { width: 28px; height: 28px; display: inline-flex; align-items: center; justify-content: center; border: none; border-radius: 8px; background: transparent; color: var(--muted); cursor: pointer; }
.guia-cerrar:hover { color: var(--text); background: var(--card-bg-2); }
.guia-cerrar .ic { width: 16px; height: 16px; }
.guia-titulo { margin: 0 0 6px; font-size: 17px; font-weight: 700; letter-spacing: -0.01em; text-wrap: balance; }
.guia-texto { margin: 0; color: color-mix(in srgb, var(--text) 82%, var(--muted)); font-size: 14px; line-height: 1.55; }
.guia-puntos { display: flex; gap: 5px; margin: 14px 0 12px; flex-wrap: wrap; }
.guia-puntos span { width: 6px; height: 6px; border-radius: 999px; background: var(--border); transition: width 0.2s ease, background 0.2s ease; }
.guia-puntos span.hecho { background: color-mix(in srgb, var(--accent) 45%, var(--border)); }
.guia-puntos span.activo { width: 18px; background: var(--accent); }
.guia-pie { display: flex; align-items: center; gap: 8px; }
.guia-espacio { flex: 1 1 auto; }
.guia-btn { padding: 8px 14px; font-size: 13.5px; }
.guia-btn[hidden], .guia-btn-texto[hidden] { display: none; }
.guia-btn-texto { background: none; border: none; padding: 4px 2px; color: var(--muted); font: inherit; font-size: 12px; cursor: pointer; text-decoration: underline; text-underline-offset: 3px; }
.guia-btn-texto:hover { color: var(--text); }
.guia-burbuja button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) { .guia-foco { transition: none; } .guia-burbuja.entra { animation: none; } .guia-puntos span { transition: none; } }
/* Los pasos del menú lateral lo abren para que se lean los nombres. */
@media (min-width: 861px) {
  .sidebar-slot.guia-abierto .sidebar { width: 240px; }
  .sidebar-slot.guia-abierto .sidebar-brand { padding: 0 20px 18px; justify-content: flex-start; }
  .sidebar-slot.guia-abierto .sidebar-brand > div:not(.brand-mark) { display: block; }
  .sidebar-slot.guia-abierto .sidebar-group { padding-left: 12px; padding-right: 12px; }
  .sidebar-slot.guia-abierto .sidebar-group-title { display: block; }
  .sidebar-slot.guia-abierto .sidebar-link { justify-content: flex-start; padding: 5px 10px; font-size: 13.5px; }
  .sidebar-slot.guia-abierto .sidebar-link span { display: inline; }
}
/* Botón «?» de la ventana del Bot, a la izquierda de la X (misma ficha, en celeste). */
.modal-guia { position: absolute; top: 14px; right: 58px; width: 36px; height: 36px; display: inline-flex; align-items: center; justify-content: center; padding: 0; border: 1px solid var(--border); border-radius: 10px; background: var(--card-bg-2); color: var(--muted); cursor: pointer; --ic: var(--ic-cian); transition: color 0.18s ease, background 0.18s ease, border-color 0.18s ease; }
.modal-guia .ic { width: 18px; height: 18px; }
.modal-guia:hover { color: var(--ic); background: color-mix(in srgb, var(--ic) 14%, var(--card-bg-2)); border-color: color-mix(in srgb, var(--ic) 50%, var(--border)); }
.modal-guia:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
#botModal .modal-titulo { margin-right: 104px; }
.btn-guia { color: var(--ic-cian); }

/* --- Firma de las personas y formato de WhatsApp en la burbuja (parte 130, decimoquinto addendum, regla 107) --------------------------
   La cita («> ») va con la barra al costado y el sombreado (`...`) con fondo gris, como en el teléfono. Siguen el color del texto de
   cada burbuja (currentColor): sirven en los dos temas y en las dos burbujas sin colores nuevos (regla 44). */
.msg-texto .wa-cita, .firma-burbuja .wa-cita, .firma-aviso-texto .wa-cita {
  display: block; margin: 2px 0; padding: 2px 0 2px 9px; border-radius: 2px;
  border-left: 3px solid color-mix(in srgb, currentColor 45%, transparent);
  background: color-mix(in srgb, currentColor 7%, transparent);
}
.msg-texto code.wa-sombreado, .firma-burbuja code.wa-sombreado, .firma-aviso-texto code.wa-sombreado {
  font-family: 'Courier New', Consolas, monospace; font-size: 0.92em; padding: 1px 5px; border-radius: 4px; border: 0;
  color: inherit; background: color-mix(in srgb, currentColor 16%, transparent);
}
.msg-origen-persona, .msg-origen-telefono { color: var(--burbuja-saliente-texto); }

/* Debajo del cuadro de respuesta: con qué línea sale lo que escribes. Se oculta junto con el formulario. */
.firma-aviso { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 2px 14px 8px; font-size: 12.5px; color: var(--muted); }
.firma-aviso.hidden, #replyForm.hidden + .firma-aviso { display: none; }
.firma-aviso > .ic { width: 14px; height: 14px; }
.firma-aviso-texto { color: var(--text); }
.firma-aviso-texto .wa-cita { display: inline-block; margin: 0; }
.firma-aviso .btn-link { font-size: 12.5px; }

/* Ventana «Firma de tus mensajes». */
.firma-modal { max-width: 540px; }
.firma-form, .firma-modal .grupo-info-sub { text-align: left; }
.firma-form { display: grid; gap: 14px; }
.firma-interruptor { position: relative; display: flex; align-items: center; gap: 10px; font-weight: 600; cursor: pointer; }
.firma-interruptor input { position: absolute; opacity: 0; pointer-events: none; }
.firma-interruptor .interruptor { margin-left: 0; }
.firma-interruptor input:checked + .interruptor { background: var(--accent); }
.firma-interruptor input:checked + .interruptor::after { transform: translateX(14px); box-shadow: none; }
.firma-interruptor input:focus-visible + .interruptor { outline: 2px solid var(--accent); outline-offset: 2px; }
.firma-interruptor input:disabled + .interruptor { opacity: 0.6; }
.firma-campos { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 10px 14px; transition: opacity 0.15s; }
.firma-campos.apagada .field:not(.firma-campo-nombre) { opacity: 0.55; } /* tu nombre sirve también con la firma apagada */
.firma-campos .field { margin: 0; }
.firma-vista { display: grid; gap: 8px; padding: 12px; border-radius: 12px; background: var(--card-bg-2); border: 1px solid var(--border); }
.firma-vista-etq { font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); }
.firma-burbuja {
  justify-self: start; max-width: 380px; padding: 8px 12px; border-radius: 4px 12px 12px 12px;
  background: var(--card-bg); border: 1px solid var(--border); color: var(--text); font-size: 13.5px; line-height: 1.45; white-space: pre-wrap;
}

/* --- Tablas del panel en pantallas angostas: cada fila, una tarjeta (parte 130, decimosexto addendum, pedido del usuario) -----------
   Con el menú «⋯» (regla 103) la tabla entra de 1100 a 1850 px, pero en una tablet o un teléfono seguía desplazándose de lado (regla 62).
   Por debajo de 900 px las filas dejan de ser filas: cada una es una tarjeta con sus datos rotulados (`data-etq`) y sus botones abajo.
   Como ya no hay tabla, aquí `td.row-actions` SÍ puede ser flex (la regla 93 lo prohíbe solo en la tabla real, donde rompía las líneas). */
@media (max-width: 900px) {
  .accounts-table thead { display: none; }
  .accounts-table, .accounts-table tbody, .accounts-table tr, .accounts-table td { display: block; width: auto; }
  .accounts-table tr {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 2px 14px;
    margin: 0 0 10px; padding: 12px 14px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--card-bg-2);
  }
  .accounts-table td { border: 0; padding: 4px 0; max-width: none; white-space: normal; }
  .accounts-table td[data-etq]::before {
    content: attr(data-etq); display: block; margin-bottom: 2px;
    font-size: 11px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--muted);
  }
  .accounts-table td.celda-titulo { grid-column: 1 / -1; font-size: 15px; font-weight: 700; padding-bottom: 6px; }
  .accounts-table td.empty { grid-column: 1 / -1; text-align: center; }
  /* En la tarjeta sobra el espacio: vuelven las dos columnas que se ocultan en la tabla angosta. */
  .accounts-table .col-desde, .accounts-table .col-ultimo-error, .accounts-table .col-correo, .accounts-table .col-nombre-cliente { display: block; }
  .accounts-table tbody td.row-actions {
    grid-column: 1 / -1; display: flex; flex-wrap: wrap; gap: 6px; justify-content: flex-start;
    margin-top: 8px; padding: 10px 0 0; border-top: 1px solid var(--border);
  }
  .accounts-table .instancia-nombre { display: block; }
  /* En tarjeta el rótulo va arriba a la izquierda: alinear el número a la derecha lo separaría de él. */
  .accounts-table th.col-num, .accounts-table td.col-num { text-align: left; }
  .accounts-table td.col-num input { text-align: left; }
}

/* --- Pagos (parte 131) ---------------------------------------------------
   Todos los colores salen de un token de :root (regla 44), así que la sección
   se adapta sola a los dos temas sin una sola regla propia de tema claro. */

.chips-filtro { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
/* Regla 28: sin la compuesta, la insignia roja de «Por revisar» nace con `hidden` y un 0 adentro,
   y se veía SIEMPRE -- un aviso de pagos por revisar que no existen. */
.chip-badge.hidden { display: none; }
.chip-badge {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 18px; height: 18px; padding: 0 5px; margin-left: 6px;
  border-radius: 9px; background: var(--red-bg); color: var(--red-text);
  font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.chip.activo .chip-badge { background: var(--on-accent); color: var(--accent); }

/* La ventana de armar la orden lista instancias con sus meses: necesita más ancho que una
   ventana de formulario normal, igual que la del QR (regla 93). */
.modal-content.modal-ancho { max-width: min(760px, calc(100vw - 32px)); width: 100%; text-align: left; }

/* Estado de una orden. Cinco estados, tres colores: lo que espera algo (ámbar), lo que salió
   bien (verde) y lo que se cortó (rojo); lo anulado queda en gris, que no pide nada a nadie. */
.estado-pago {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 3px 9px; border-radius: 999px; font-size: 12px; font-weight: 600; white-space: nowrap;
}
.estado-pago .ic { width: 13px; height: 13px; }
.estado-pago.es-pendiente { background: var(--yellow-bg); color: var(--yellow-text); }
.estado-pago.es-reportada { background: var(--accent-glow); color: var(--accent); }
.estado-pago.es-confirmada { background: var(--ok-bg); color: var(--ok-text); }
.estado-pago.es-rechazada { background: var(--red-bg); color: var(--red-text); }
.estado-pago.es-anulada { background: var(--card-bg-2); color: var(--muted); }

/* Medios de pago: una tarjeta por medio, con sus datos plegados hasta que se piden. */
.metodos-lista { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; }
.metodo-tarjeta {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--card-bg-2); padding: 14px; display: flex; flex-direction: column; gap: 8px;
}
.metodo-tarjeta.inactivo { opacity: 0.55; }
.metodo-cabeza { display: flex; align-items: center; gap: 9px; }
.metodo-cabeza .ic { width: 20px; height: 20px; color: var(--accent); flex: none; }
.metodo-cabeza strong { font-size: 15px; }
.metodo-tipo { color: var(--muted); font-size: 12px; }
.metodo-datos {
  background: var(--input-bg); border: 1px solid var(--border); border-radius: 10px;
  padding: 10px 12px; font-size: 13px; line-height: 1.5; white-space: pre-wrap; word-break: break-word;
  font-variant-numeric: tabular-nums;
}
.metodo-acciones { display: flex; gap: 6px; margin-top: auto; }

/* Armado de la orden: un renglón por instancia. */
.orden-renglones { display: flex; flex-direction: column; gap: 8px; margin: 4px 0 14px; }
.orden-renglon {
  display: grid; grid-template-columns: auto 1fr auto auto; gap: 10px; align-items: center;
  border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; background: var(--card-bg-2);
}
.orden-renglon.elegida { border-color: var(--accent-dim); background: var(--accent-glow); }
.orden-renglon-nombre { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.orden-renglon-nombre strong { font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.orden-renglon-nombre span { color: var(--muted); font-size: 12px; }
.orden-renglon-meses { display: flex; align-items: center; gap: 6px; }
.orden-renglon-meses input { width: 64px; text-align: center; }
.orden-renglon-meses label { color: var(--muted); font-size: 12px; }
.orden-renglon-sub { font-weight: 700; font-variant-numeric: tabular-nums; min-width: 72px; text-align: right; }
.orden-renglon.sin-precio { opacity: 0.6; }
/* Una instancia sin fecha de vencimiento no caduca: cobrarla le ESTRENA un vencimiento, así
   que la fila se ve distinta (ámbar, tokens de :root -- regla 44) y no pasa desapercibida. */
.orden-renglon.sin-vencimiento { border-color: var(--yellow-text); background: var(--yellow-bg); }
.orden-renglon-nombre strong { display: flex; align-items: center; gap: 8px; }
.renglon-marca {
  flex: none; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--yellow-text); background: var(--yellow-bg); border: 1px solid var(--yellow-text);
  border-radius: 999px; padding: 1px 8px;
}
.renglon-aviso { color: var(--yellow-text); font-size: 12px; }
.orden-total {
  display: flex; align-items: center; justify-content: space-between;
  border-top: 1px solid var(--border); padding-top: 12px; margin-bottom: 14px; font-size: 15px;
}
.orden-total strong { font-size: 22px; font-variant-numeric: tabular-nums; color: var(--accent); }

/* Resumen de una orden ya armada (detalle y confirmación). */
.orden-resumen { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
.orden-resumen-fila { display: flex; justify-content: space-between; gap: 12px; font-size: 14px; }
.orden-resumen-fila span { color: var(--muted); }
.orden-resumen-fila strong { font-variant-numeric: tabular-nums; text-align: right; }
.orden-resumen-lista { display: flex; flex-direction: column; gap: 6px; border: 1px solid var(--border); border-radius: 10px; padding: 10px 12px; background: var(--card-bg-2); }
.orden-resumen-lista .orden-resumen-fila { font-size: 13px; }
/* El detalle de una orden va por BLOQUES (parte 155): cada uno responde una pregunta, en el orden
   del recorrido real de la orden. Antes era una lista plana de doce filas y para encontrar un dato
   había que leerla entera. */
.orden-bloque { display: flex; flex-direction: column; gap: 9px; }
.orden-bloque + .orden-bloque { margin-top: 15px; padding-top: 15px; border-top: 1px solid var(--border); }
/* `text-align: left` explícito: la ventana centra su texto por defecto (por eso `.modal-titulo`
   también lo pide), y un rótulo de sección centrado se lee como un título más, no como la etiqueta
   del bloque que tiene debajo. */
.orden-bloque-titulo { margin: 0; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted); text-align: left; }
/* La etiqueta puede ser larga (el nombre de una instancia) y el valor tiene que seguir alineado a
   la derecha aunque la etiqueta se parta: sin esto, la segunda línea quedaba centrada bajo la
   primera y la fila se leía torcida. */
.orden-resumen-fila span { min-width: 0; flex: 1 1 auto; text-align: left; }
.orden-resumen-fila strong { flex: 0 0 auto; }
.orden-motivo { margin: 0; font-size: 14px; line-height: 1.5; text-align: left; }

/* El aviso de que el monto no coincide. No es decorativo: es lo que frena la confirmación. */
.aviso-diferencia {
  border-radius: 10px; padding: 11px 13px; font-size: 13px; line-height: 1.5; margin-bottom: 12px;
  background: var(--yellow-bg); color: var(--yellow-text); border: 1px solid currentColor;
}
.aviso-diferencia.exacto { background: var(--ok-bg); color: var(--ok-text); }

/* Regla 28: `#confirmarAceptarCampo` es la casilla que solo aparece en las confirmaciones que la
   piden. Sin la compuesta salía en TODAS, como una casilla huérfana sin explicación. */
.checkbox-linea.hidden { display: none; }
.checkbox-linea { display: flex; align-items: center; gap: 9px; margin-bottom: 14px; font-size: 14px; cursor: pointer; }
.checkbox-linea input { width: 16px; height: 16px; accent-color: var(--accent); flex: none; }

/* Selector de archivo propio: el del navegador dice «Choose File» en inglés (regla 93). */
.archivo-propio { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.archivo-nombre { color: var(--muted); font-size: 13px; overflow: hidden; text-overflow: ellipsis; }

/* Pantallas angostas: los renglones dejan de ser una grilla y se apilan (regla 62). */
@media (max-width: 720px) {
  .orden-renglon { grid-template-columns: auto 1fr; row-gap: 8px; }
  .orden-renglon-meses, .orden-renglon-sub { grid-column: 2; justify-self: start; text-align: left; }
}

/* El encabezado de las tarjetas de Pagos lleva un botón junto al título. `.card-head` no tiene
   layout propio (las demás secciones solo ponen un h2 ahí), así que el botón caía DEBAJO y a la
   izquierda, donde el menú lateral desplegado lo tapa. Acá va en línea, a la derecha. */
#view-pagos .card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 18px; }
#view-pagos .card-head h2 { margin-bottom: 0; }

/* «Cómo escribirlo» (parte 132): la receta para escribir el asistente. Texto para leer, así que
   la medida de línea es angosta a propósito; todos los colores salen de tokens (regla 44). */
.bot-panel.receta { max-width: 720px; font-size: 14.5px; line-height: 1.6; }
.receta-entrada { font-size: 15.5px; color: var(--text); margin: 0 0 18px; }
.receta-idea {
  background: var(--accent-glow); border: 1px solid var(--accent-dim); border-radius: 10px;
  padding: 13px 15px; margin: 0 0 22px; font-size: 14px;
}
.receta-idea.final { margin: 24px 0 0; }
.receta-idea strong { color: var(--accent); }
.receta-pasos { counter-reset: receta; list-style: none; padding: 0; margin: 0; }
.receta-pasos > li {
  counter-increment: receta; position: relative; padding: 0 0 22px 42px; margin: 0;
  border-left: 2px solid var(--border); padding-left: 42px;
}
.receta-pasos > li:last-child { border-left-color: transparent; padding-bottom: 6px; }
.receta-pasos > li::before {
  content: counter(receta); position: absolute; left: -15px; top: 0;
  width: 28px; height: 28px; border-radius: 50%;
  background: var(--card-bg); border: 2px solid var(--accent-dim); color: var(--accent);
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums;
}
.receta-pasos h4 { margin: 2px 0 8px; font-size: 15.5px; font-weight: 650; }
.receta-pasos p { margin: 0 0 10px; color: var(--muted); }
.receta-pasos p strong, .receta-lista strong { color: var(--text); }
.receta-ej {
  border-left: 3px solid var(--ok-text); background: var(--card-bg-2);
  border-radius: 0 8px 8px 0; padding: 10px 13px; font-size: 13.5px;
}
.receta-ej.mal { border-left-color: var(--red-text); }
.receta-ej span {
  display: block; font-size: 11px; font-weight: 700; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--ok-text); margin-bottom: 4px;
}
.receta-ej.mal span { color: var(--red-text); }
.receta-ej em { display: block; margin-top: 6px; color: var(--muted); font-size: 13px; }
.receta-lista { margin: 0 0 12px; padding-left: 20px; color: var(--muted); }
.receta-lista li { margin: 5px 0; }
.receta-sub { font-size: 16px; font-weight: 650; margin: 26px 0 8px; }
.bot-panel.receta > p { color: var(--muted); margin: 0 0 12px; }

/* Avisos del revisor, en la pestaña «Configurar». */
.bot-revision { display: flex; flex-direction: column; gap: 8px; margin: 0 0 18px; }
.bot-aviso {
  display: flex; gap: 10px; align-items: flex-start;
  border: 1px solid var(--border); border-radius: 10px; padding: 11px 13px;
  background: var(--card-bg-2); font-size: 13.5px;
}
.bot-aviso .ic { width: 17px; height: 17px; flex: none; margin-top: 2px; }
.bot-aviso b { display: block; font-size: 14px; font-weight: 650; margin-bottom: 3px; color: var(--text); }
.bot-aviso span { color: var(--muted); }
.bot-aviso.nivel-alto { border-color: var(--red-text); background: var(--red-bg); }
.bot-aviso.nivel-alto .ic { color: var(--red-text); }
.bot-aviso.nivel-medio { border-color: var(--yellow-text); background: var(--yellow-bg); }
.bot-aviso.nivel-medio .ic { color: var(--yellow-text); }
.bot-aviso.nivel-idea .ic { color: var(--accent); }
.bot-revision-pie { font-size: 13px; color: var(--muted); }
.bot-revision-pie a { color: var(--accent); cursor: pointer; }

/* Segunda versión de la receta (parte 132, tras la prueba con personas): cada paso dice en qué
   sección del formulario se llena, porque quien menos sabía no podía mapear una cosa con la otra. */
.receta-campo { display: block; font-size: 12px; font-weight: 500; font-style: normal; color: var(--accent); margin-top: 3px; }
.receta-nota { background: var(--card-bg-2); border-radius: 8px; padding: 10px 12px; font-size: 13.5px; }
.receta-nota.fuerte { background: var(--yellow-bg); color: var(--text); }
.receta-nota.fuerte strong { color: var(--yellow-text); }
.receta-num { font-family: ui-monospace, "SFMono-Regular", Consolas, monospace; font-size: 0.95em; background: var(--input-bg); padding: 1px 5px; border-radius: 4px; }
.receta-chequeo { counter-reset: none; }
.receta-chequeo li { margin: 7px 0; }
.receta-ej br { content: ""; display: block; margin-bottom: 5px; }

/* --- Configuración → «Tu empresa» (parte 133) -----------------------------------------------
   Los datos del membrete, y al lado la hoja real del comprobante que se va a generar. La vista
   previa no es adorno: el dueño escribe un RUC o sube un logo y ve EXACTAMENTE lo que le va a
   llegar a su cliente, sin tener que confirmar un pago de verdad para enterarse. Todos los
   colores salen de tokens (regla 44); los de la hoja son los `--papel-*` de `:root`. */
.cfg-empresa { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 420px); gap: 26px; align-items: start; }
.cfg-empresa-campos { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.cfg-empresa-par { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.cfg-empresa-vista { position: sticky; top: 16px; min-width: 0; }
.cfg-vista-tit {
  display: flex; align-items: center; gap: 7px; margin: 0 0 10px;
  font-size: 12px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase; color: var(--muted);
}
.cfg-vista-tit .ic { width: 1.05em; height: 1.05em; }

/* La hoja. Sombra y un borde muy tenue para que se lea como un papel apoyado sobre el panel y no
   como otra tarjeta más de la pantalla. */
.papel {
  background: var(--papel-bg); color: var(--papel-texto);
  border: 1px solid var(--papel-linea); border-radius: 8px;
  padding: 20px 20px 16px; font-size: 12px; line-height: 1.45;
  box-shadow: 0 10px 28px rgba(0, 0, 0, .28), 0 2px 6px rgba(0, 0, 0, .18);
}
.papel-cab { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 12px; align-items: start; }
.papel-logo {
  width: 46px; height: 46px; border-radius: 8px; flex: none;
  background: var(--papel-acento); color: var(--papel-bg);
  display: flex; align-items: center; justify-content: center;
  font-size: 20px; font-weight: 700; overflow: hidden;
  background-size: cover; background-position: center;
}
/* Sin logo cargado se dibuja la inicial del nombre comercial: una hoja con un hueco gris se ve
   peor que una con una marca simple, y así la vista previa nunca sale rota. */
.papel-logo::before { content: attr(data-inicial); }
.papel-logo.con-imagen { background-color: transparent; }
.papel-logo.con-imagen::before { content: ''; }
.papel-emisor { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.papel-emisor strong { font-size: 14.5px; line-height: 1.25; }
.papel-emisor span { color: var(--papel-muted); font-size: 11px; overflow-wrap: anywhere; }
.papel-sello { display: flex; flex-direction: column; align-items: flex-end; gap: 1px; text-align: right; flex: none; }
.papel-sello-tit { font-size: 9.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--papel-acento); }
.papel-sello-num { font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums; }
.papel-sello-fecha { font-size: 10.5px; color: var(--papel-muted); font-variant-numeric: tabular-nums; }
.papel-regla { height: 2px; background: var(--papel-acento); border-radius: 2px; margin: 14px 0 12px; opacity: .85; }
.papel-cliente { display: flex; flex-direction: column; gap: 1px; margin-bottom: 12px; }
.papel-rotulo { font-size: 9.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase; color: var(--papel-muted); }
.papel-cliente strong { font-size: 12.5px; }
.papel-cliente span:not(.papel-rotulo) { color: var(--papel-muted); font-size: 11px; }
.papel-items { border-top: 1px solid var(--papel-linea); }
.papel-item {
  display: flex; justify-content: space-between; gap: 12px;
  padding: 7px 0; border-bottom: 1px solid var(--papel-linea);
}
.papel-item span:last-child { font-variant-numeric: tabular-nums; flex: none; }
.papel-total { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 10px 0 0; }
.papel-total span { font-size: 11px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--papel-muted); }
.papel-total strong { font-size: 17px; color: var(--papel-acento); font-variant-numeric: tabular-nums; }
.papel-pie {
  display: flex; flex-direction: column; gap: 4px;
  margin-top: 14px; padding-top: 10px; border-top: 1px solid var(--papel-linea);
  font-size: 10px; color: var(--papel-muted);
}
.papel-pie #papelNota { white-space: pre-line; color: var(--papel-texto); font-size: 10.5px; }
.papel-legal { font-style: italic; }

/* Debajo de 1100 px la hoja deja de caber al lado sin apretar los campos, así que pasa arriba:
   se sigue viendo primero lo que el cliente recibe, que es de lo que trata la sección. */
@media (max-width: 1100px) {
  .cfg-empresa { grid-template-columns: minmax(0, 1fr); }
  .cfg-empresa-vista { position: static; order: -1; max-width: 460px; }
}
@media (max-width: 560px) {
  .cfg-empresa-par { grid-template-columns: 1fr; }
  .papel { padding: 16px 14px 13px; }
  .papel-cab { grid-template-columns: auto minmax(0, 1fr); }
  .papel-sello { grid-column: 1 / -1; align-items: flex-start; text-align: left; }
}

/* --- Casilla con explicación (parte 135) ---------------------------------------------------
   `.checkbox-linea` alcanza para una frase corta. Esta es para una decisión que necesita decir
   qué implica («no se cobra»): el título arriba y el porqué debajo, dentro de la misma etiqueta
   para que se pueda marcar tocando cualquier parte. */
.casilla-larga {
  display: flex; align-items: flex-start; gap: 11px; cursor: pointer;
  margin: 4px 0 16px; padding: 12px 14px;
  border: 1px solid var(--border); border-radius: 10px; background: var(--card-bg-2);
}
.casilla-larga:hover { border-color: var(--accent-dim); }
.casilla-larga input { width: 17px; height: 17px; accent-color: var(--accent); flex: none; margin-top: 2px; }
.casilla-larga span { font-size: 13px; line-height: 1.5; color: var(--muted); }
.casilla-larga b { display: block; font-size: 14px; color: var(--text); margin-bottom: 2px; }
.casilla-larga input:checked ~ span b { color: var(--yellow-text); }
.casilla-larga:has(input:checked) { border-color: var(--yellow-text); background: var(--yellow-bg); }

/* La insignia de la fila de Instancias: se ve de un vistazo cuál no entra en una orden. */
.marca-no-cobra {
  display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 999px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  background: var(--yellow-bg); color: var(--yellow-text); white-space: nowrap;
}

/* Aviso al pie del armador de órdenes: nunca se acorta la lista en silencio. */
.aviso-no-cobra {
  margin: 10px 0 0; padding: 9px 12px; border-radius: 8px; font-size: 13px;
  background: var(--card-bg-2); border: 1px solid var(--border); color: var(--muted);
}

/* --- El comprobante en PDF al confirmar un pago (parte 136) ----------------------------------
   Pedido del usuario: «que se vea visualmente y luego procesar el envío, durando el envío una
   animación aunque sea corta da una luz de elegancia». La animación cubre trabajo REAL (aplicar
   las vigencias, generar el PDF y mandarlo por WhatsApp son varios segundos de verdad), nunca
   una espera inventada: los pasos se marcan con lo que respondió el servidor. */
/* .modal-content centra su texto (línea 1090). Un documento se lee alineado a la izquierda
   siempre, esté donde esté: la hoja se saca de ese centrado, igual que ya hacen .qr-modal y
   .modal-ancho. Se vio en la captura, no razonándolo. */
.recibo-ventana { max-width: 760px; text-align: left; }
.recibo-cuerpo { display: grid; gap: 18px; }
@media (min-width: 680px) { .recibo-cuerpo { grid-template-columns: minmax(0, 1fr) 210px; align-items: start; } }

.recibo-hoja-caja { position: relative; overflow: hidden; border-radius: 8px; }
.recibo-hoja { font-size: 11.5px; }
.recibo-hoja .papel-items { min-height: 42px; }

/* El barrido: una banda de luz que recorre la hoja mientras se está trabajando. Se apaga sola
   al terminar (se le quita la clase), así nadie se queda mirando una animación eterna. */
.recibo-barrido { position: absolute; inset: 0; pointer-events: none; opacity: 0; }
.recibo-barrido.activo {
  opacity: 1;
  background: linear-gradient(105deg, transparent 38%, rgba(255, 255, 255, .55) 50%, transparent 62%);
  background-size: 260% 100%;
  animation: recibo-barrer 1.5s linear infinite;
}
@keyframes recibo-barrer { from { background-position: 160% 0; } to { background-position: -60% 0; } }

.recibo-pasos { display: flex; flex-direction: column; gap: 3px; }
.recibo-paso {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border-radius: 8px; font-size: 13px; color: var(--muted);
}
.recibo-punto {
  width: 9px; height: 9px; border-radius: 50%; flex: none;
  background: var(--border); box-shadow: 0 0 0 3px transparent;
}
.recibo-paso.curso { color: var(--text); background: var(--card-bg-2); }
.recibo-paso.curso .recibo-punto { background: var(--accent); animation: recibo-latir 1.1s ease-in-out infinite; }
@keyframes recibo-latir { 0%, 100% { box-shadow: 0 0 0 0 var(--accent-glow); } 50% { box-shadow: 0 0 0 5px var(--accent-glow); } }
.recibo-paso.hecho { color: var(--text); }
.recibo-paso.hecho .recibo-punto { background: var(--accent); }
.recibo-paso.falla { color: var(--yellow-text); }
.recibo-paso.falla .recibo-punto { background: var(--yellow-text); }

/* Sin movimiento: se ven los mismos estados, quietos. Nunca se pierde información por apagar
   una animación (el paso en curso se distingue igual por su fondo y su color). */
@media (prefers-reduced-motion: reduce) {
  .recibo-barrido.activo { animation: none; background: none; }
  .recibo-paso.curso .recibo-punto { animation: none; box-shadow: 0 0 0 4px var(--accent-glow); }
}

/* La hoja entra desde abajo la primera vez, una sola vez: es el documento apareciendo, no un
   efecto que se repita en cada repintado. */
.recibo-hoja.entrando { animation: recibo-entrar .45s cubic-bezier(.2, .7, .3, 1) both; }
@keyframes recibo-entrar { from { opacity: 0; transform: translateY(14px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) { .recibo-hoja.entrando { animation: none; } }

/* --- La marca de quien opera la plataforma, en la pantalla del QR (parte 139) -----------------
   Pedido del usuario: «en la pantalla de QR poner el logo para identificar nuestra marca». Va
   ARRIBA de la ventana y nunca DENTRO del código: un logo encima del QR tapa módulos, y esta es
   la única pantalla cuyo trabajo es escanearse bien (la misma razón por la que en la parte 130
   tampoco se copió el logo de WhatsApp Web ahí adentro).
   El logo sale de Configuración → Tu empresa, así que puede ser de cualquier forma: la caja lo
   encierra con `object-fit: contain` para que uno ancho o uno alto entren igual sin deformarse. */
/* Regla 28, y este es el peor de los cuatro: la fila de la marca tiene que ocultarse cuando no hay
   logo cargado, en vez de dejar un hueco (regla 124). Sin la compuesta nunca se ocultaba, y pasa en
   `vincular.html`, que es la página PÚBLICA que abre el cliente final de un cliente. */
.qr-marca.hidden { display: none; }
/* MEMBRETE: el logo arriba y el nombre debajo, centrados, como la hoja de los comprobantes en PDF
   (maqueta elegida por el usuario, parte 169). Antes eran logo y nombre en fila, chicos -- él lo
   describió como «pequeñito y muy simple», y agrandarlos en su sitio no alcanzó: lo que faltaba era
   composición, no tamaño. Es lo primero que ve un cliente final al abrir el enlace de vinculación,
   que muchas veces es su primer contacto con el producto. */
.qr-marca {
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
  margin: 0 0 24px; padding-bottom: 22px; border-bottom: 1px solid var(--border);
}
/* Caja RECTANGULAR por el mismo motivo que en el comprobante (parte 140): en una cuadrada, una
   marca ancha solo llena una franja del medio y se ve diminuta. Así una ancha usa todo el ancho
   y una cuadrada sigue entrando por el alto, sin deformarse ninguna. */
.qr-marca img { width: 96px; height: 70px; object-fit: contain; flex: none; }
.qr-marca span { font-size: 22px; font-weight: 700; color: var(--text); letter-spacing: -0.02em; text-align: center; line-height: 1.2; }
/* En la página pública la ventana ya viene centrada y con su propia línea de «conexión segura»
   debajo, así que la marca no necesita el separador: se apoya en la del título. */
.vinc-pagina .qr-marca { border-bottom: 0; padding-bottom: 0; margin-bottom: 14px; }

/* --- Conectar una instancia de Telegram (parte 141) -------------------------------------------- */
/* La ventana de Telegram (parte 145): dos columnas -- cómo se crea el bot a la izquierda, lo que
   hay que completar a la derecha. Antes era la ventana angosta por defecto (340 px, centrada) y
   el campo del token, que es lo único que hay que hacer acá, quedaba al final de una tira larga. */
.tg-cols { display: grid; grid-template-columns: 1.15fr 1fr; gap: 26px; align-items: start; margin-top: 18px; }
.tg-col-titulo { margin: 0 0 12px; font-size: 13px; text-transform: uppercase; letter-spacing: 0.5px; color: var(--muted); font-weight: 600; }
/* La columna del token se separa con una línea, no con una caja: es el mismo formulario, no otra
   tarjeta (regla 93 -- borde y relleno dicen «objeto aparte» y acá no lo son). */
.tg-col-token { border-left: 1px solid var(--border); padding-left: 26px; }
.tg-col-token .field { margin-bottom: 0; }
/* Apiladas en una pantalla angosta: primero lo que hay que completar, después la explicación. */
@media (max-width: 720px) {
  .tg-cols { grid-template-columns: minmax(0, 1fr); gap: 20px; }
  .tg-col-token { border-left: 0; padding-left: 0; border-top: 1px solid var(--border); padding-top: 20px; order: -1; }
}
.tg-pasos { margin-bottom: 0; }
/* La compuesta va PRIMERO y es obligatoria: `.hidden { display: none }` vive en la línea 1937 y
   esta regla en la 4435, las dos con un solo nombre de clase, así que sin esto gana la de abajo y
   el recuadro NO se oculta (regla 28). Pasó en real: el aviso verde «Conectado como» salía en toda
   instancia de Telegram, conectada o no, con el nombre en blanco y ofreciendo desconectar un bot
   que no existía. */
.tg-conectado.hidden { display: none; }
.tg-conectado {
  display: flex; align-items: center; justify-content: space-between; gap: 14px; flex-wrap: wrap;
  padding: 13px 16px; margin-bottom: 18px;
  background: var(--ok-bg); border: 1px solid var(--accent-dim); border-radius: 10px;
}
.tg-conectado p { margin: 0; display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--text); }
.tg-conectado .ic { color: var(--accent); }
.tg-conectado strong { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; }
/* El aviso de que en Telegram escribe primero el cliente: es la diferencia con WhatsApp que más
   confunde, así que va a la vista y no escondida en un desplegable. */
.tg-ojo { display: flex; align-items: flex-start; gap: 9px; margin-top: 14px; }
.tg-ojo .ic { flex: none; margin-top: 1px; color: var(--ic-azul); }
#tgToken { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; }
/* La insignia del canal en la fila de Instancias. */
.marca-canal-tg {
  display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 999px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .04em;
  background: rgba(90, 169, 255, .14); color: var(--ic-azul); white-space: nowrap;
}

/* --- Respaldo cifrado (parte 149) ------------------------------------------------------------ */
/* La lista de copias que hay en la carpeta. Cada fila dice el nombre del archivo tal cual, porque
   es el que hay que escribir en el script de restauración el día que haga falta. */
.respaldo-copias { padding: 0 var(--pad-modal, 22px) 18px; }
.respaldo-lista { list-style: none; margin: 8px 0 0; padding: 0; display: flex; flex-direction: column; gap: 7px; }
.respaldo-lista li {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 9px 12px; border: 1px solid var(--border); border-radius: 9px; background: var(--bg-elevated);
}
.respaldo-nombre { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px; color: var(--text); }
.respaldo-meta { font-size: 12.5px; color: var(--muted); margin-right: auto; }

/* --- Reporte de cobranzas (parte 159) ---------------------------------------------------------
   Todos los colores salen de tokens de :root (regla 44): la tarjeta se ve igual de bien en los
   dos temas sin una sola regla por tema. Las barras son divs con un ancho en porcentaje y no una
   gráfica: son cinco filas, y cargar Chart.js acá sería desmedido. */
.cobranzas-controles { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; margin: 10px 0 16px; }
.cobranzas-controles .field { flex: 0 1 170px; min-width: 140px; }
.cobranzas-controles .field > input, .cobranzas-controles .field > select { height: 40px; }
.cobranzas-atajos { display: flex; gap: 6px; flex-wrap: wrap; padding-bottom: 2px; }

.cobranzas-totales { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 12px; margin-bottom: 16px; }
.cobranzas-tot { background: var(--bg-elevated); border: 1px solid var(--border); border-radius: 12px; padding: 14px 16px; }
.cobranzas-tot-rotulo { margin: 0 0 4px; font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
.cobranzas-tot-valor { margin: 0; font-size: 26px; font-weight: 800; letter-spacing: -0.02em; font-variant-numeric: tabular-nums; }
.cobranzas-var { display: inline-block; margin-top: 6px; font-size: 12px; font-weight: 700; padding: 2px 8px; border-radius: 999px; }
.cobranzas-var.sube { background: var(--ok-bg); color: var(--ok-text); }
.cobranzas-var.baja { background: var(--red-bg); color: var(--red-text); }
.cobranzas-var.neutra { background: transparent; border: 1px solid var(--border); color: var(--muted); }

/* Lo que todavía no entró: es lo más accionable, así que se ve distinto del resto. */
.cobranzas-pendiente { background: var(--bg-elevated); border: 1px solid var(--border); border-left: 3px solid var(--muted); border-radius: 10px; padding: 12px 14px; margin-bottom: 16px; }
.cobranzas-pendiente.urgente { border-left-color: var(--yellow-text); background: var(--yellow-bg); }
.cobranzas-pendiente p { margin: 0; font-size: 14px; }
.cobranzas-nota { margin: 6px 0 0 !important; font-size: 12px; color: var(--muted); }

.cobranzas-bloque { margin-top: 20px; }
.cobranzas-bloque > h3 { font-size: 14px; margin: 0 0 10px; letter-spacing: -0.01em; }

.cobranzas-serie { width: 100%; }
.cobranzas-serie .cobranzas-monto { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; position: relative; }
/* La barrita se dibuja DETRÁS del número, en la misma celda: da la forma de la serie sin sumar
   una columna a una tabla que ya no puede crecer de ancho (regla 62). */
.cobranzas-mini { position: absolute; left: 0; top: 50%; transform: translateY(-50%); height: 18px; background: var(--accent-glow); border-radius: 4px; z-index: 0; }
.cobranzas-serie tr.cobranzas-vacio td { color: var(--muted); }

.cobranzas-lista { list-style: none; margin: 0; padding: 0; display: grid; gap: 12px; }
.cobranzas-barra-cab { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 5px; }
.cobranzas-barra-nombre { font-size: 14px; font-weight: 600; overflow-wrap: anywhere; }
.cobranzas-barra-monto { font-size: 14px; font-weight: 700; font-variant-numeric: tabular-nums; white-space: nowrap; }
.cobranzas-barra-pista { height: 8px; background: var(--border); border-radius: 999px; overflow: hidden; }
.cobranzas-barra-relleno { height: 100%; background: var(--accent); border-radius: 999px; }
.cobranzas-barra-pie { margin: 5px 0 0; font-size: 12px; color: var(--muted); }

@media (max-width: 700px) {
  .cobranzas-controles .field { flex: 1 1 140px; }
  .cobranzas-tot-valor { font-size: 22px; }
}

/* --- Varios adjuntos en «Enviar mensaje» (parte 166) -------------------------------------------
   Todos los colores salen de tokens de :root (regla 44): se ve igual en los dos temas sin una sola
   regla por tema. La lista ocupa su propia fila a lo ancho y nace oculta, así el formulario sin
   adjuntos queda exactamente como estaba (y nada empuja al vecino -- reglas 146 y 151). */
.archivo-lista { display: flex; flex-direction: column; gap: 8px; }
.archivo-lista.hidden { display: none; }

.adjunto-fila {
  display: flex; align-items: center; gap: 12px;
  background: var(--bg-elevated); border: 1px solid var(--border); border-radius: 10px; padding: 8px 10px;
}
.adjunto-mini {
  flex: none; width: 44px; height: 44px; border-radius: 8px; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  background: var(--input-bg); border: 1px solid var(--border); color: var(--muted);
}
.adjunto-mini img { width: 100%; height: 100%; object-fit: cover; }
.adjunto-mini .ic { width: 20px; height: 20px; }

.adjunto-datos { flex: 1 1 auto; min-width: 0; }
/* El nombre puede ser larguísimo y no debe ensanchar la fila (regla 62). */
.adjunto-nombre { margin: 0; font-size: 14px; font-weight: 600; overflow-wrap: anywhere; }
.adjunto-meta { margin: 2px 0 0; font-size: 12px; color: var(--muted); }
.adjunto-nota { margin: 2px 0 0; font-size: 12px; color: var(--accent); }
.adjunto-pie { margin-top: 6px; width: 100%; height: 32px; font-size: 13px; }

.adjunto-quitar { flex: none; }

@media (max-width: 700px) {
  .adjunto-fila { align-items: flex-start; }
  .adjunto-mini { width: 36px; height: 36px; }
}

/* --- Campañas de envío (parte 170) ---------------------------------------
   Dos mitades: arriba se arma la campaña, abajo se miran las que ya existen. Todos los colores
   salen de un token de :root (regla 44), así que la sección se adapta sola a los dos temas. */
.campania-form { display: grid; gap: 16px; margin-top: 14px; }
.campania-ritmo { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 16px; }
.campania-ritmo .field { max-width: 190px; }
.campania-acciones { display: flex; flex-wrap: wrap; gap: 10px; }
/* El aviso del ritmo no es decorativo: es el único lugar donde se explica POR QUÉ hay un piso de
   3 segundos, que es lo que protege la línea del cliente. */
.camp-aviso-ritmo { display: flex; align-items: flex-start; gap: 8px; margin: 0; }
.camp-aviso-ritmo .ic { flex: 0 0 auto; width: 16px; height: 16px; margin-top: 2px; color: var(--accent); }

/* Los números desde los que se envía. Una casilla por instancia; las que no están conectadas se
   ven apagadas y no se pueden marcar -- elegir un número que no puede enviar solo sirve para que
   el servidor rechace el pedido. */
.camp-instancias { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 8px; }
.camp-instancia {
  display: grid; grid-template-columns: auto 1fr; align-items: center; gap: 4px 10px;
  padding: 10px 12px; border: 1px solid var(--border); border-radius: 12px; background: var(--card-bg-2); cursor: pointer;
}
.camp-instancia input { grid-row: span 2; }
.camp-instancia-nombre { font-weight: 650; color: var(--text); }
.camp-instancia .hint { margin: 0; font-size: 12px; }
.camp-instancia-off { opacity: 0.55; cursor: not-allowed; }

/* El ensayo en seco. Nace oculto, así que lleva su regla compuesta (regla 28/134): sin ella, la
   caja se vería vacía desde el primer momento. */
.camp-ensayo.hidden { display: none; }
.camp-ensayo { display: grid; gap: 14px; margin-top: 18px; padding: 18px; border: 1px solid var(--accent-dim); border-radius: var(--radius); background: var(--card-bg-2); }
.camp-ensayo-titulo { display: flex; align-items: center; gap: 8px; margin: 0; font-size: 15px; }
.camp-ensayo-titulo .ic { width: 18px; height: 18px; color: var(--accent); }
.camp-cifras { display: grid; grid-template-columns: repeat(auto-fit, minmax(130px, 1fr)); gap: 10px; }
.camp-cifra { display: grid; gap: 2px; padding: 12px 14px; border-radius: 12px; background: var(--card-bg); text-align: center; }
.camp-cifra strong { font-size: 22px; font-weight: 700; color: var(--text); font-variant-numeric: tabular-nums; }
.camp-cifra span { font-size: 12px; color: var(--muted); }
.camp-avisos { margin: 0; padding-left: 20px; display: grid; gap: 4px; color: var(--yellow-text); font-size: 13px; }
.camp-corregido { margin: 0; }
/* La muestra del mensaje se ve como una burbuja de WhatsApp a propósito: es la única forma de que
   alguien entienda de un vistazo cómo le va a llegar, con las variables ya reemplazadas. */
.camp-muestra { display: grid; gap: 6px; }
.camp-burbuja {
  padding: 10px 14px; border-radius: 12px 12px 12px 4px; background: var(--accent-dim);
  color: var(--burbuja-saliente-texto); white-space: pre-wrap; overflow-wrap: anywhere; max-width: 460px;
}

/* La lista de campañas. */
.camp-lista { display: grid; gap: 12px; margin-top: 14px; }
.camp-tarjeta { display: grid; gap: 8px; padding: 16px; border: 1px solid var(--border); border-radius: var(--radius); background: var(--card-bg-2); }
.camp-cabecera { display: flex; align-items: flex-start; justify-content: space-between; gap: 14px; flex-wrap: wrap; }
.camp-nombre { margin: 0 0 2px; font-size: 15px; font-weight: 700; color: var(--text); }
.camp-cabecera .hint { margin: 0; }
.camp-avance, .camp-motivo { margin: 0; font-variant-numeric: tabular-nums; }
.camp-motivo { color: var(--yellow-text); }
/* La barra de avance: lo único que se mira mientras una campaña de horas está corriendo. */
.camp-barra { height: 8px; border-radius: 999px; background: var(--border); overflow: hidden; }
.camp-barra-relleno { height: 100%; background: var(--accent); transition: width 0.4s ease; }
.camp-acciones { display: flex; flex-wrap: wrap; gap: 6px; padding: 0; border: 0; }

@media (max-width: 640px) {
  .campania-ritmo .field { max-width: none; flex: 1 1 140px; }
}

/* Las versiones del mensaje (parte 171). Cada una es una caja propia para que se vea que son
   alternativas entre sí, y no un mensaje largo partido en pedazos. */
.camp-mensajes { display: grid; gap: 10px; margin-bottom: 10px; }
.camp-mensaje { display: grid; gap: 6px; padding: 12px; border: 1px solid var(--border); border-radius: 12px; background: var(--card-bg-2); }
.camp-mensaje-cabecera { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.camp-mensaje-nro { font-size: 12px; font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--muted); }
.camp-mensaje textarea { width: 100%; }
/* Con varias versiones se muestra una burbuja por cada una: ver solo la primera escondería justo
   lo que se quiere revisar antes de mandar. */
.camp-muestra .camp-burbuja + .camp-burbuja { margin-top: 6px; }


/* Versiones hechas con IA (parte 172). La fila junta los dos botones de «más versiones» con los dos
   selectores, para que se lea como una sola decision: cuantas y en que tono. */
.camp-ia { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px; margin-bottom: 10px; }
.camp-ia-campo { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted); }
.camp-ia-campo select { min-width: 110px; }
.camp-ia-aviso { display: grid; gap: 4px; margin-bottom: 10px; padding: 10px 12px; border-left: 3px solid var(--accent); border-radius: 8px; background: var(--card-bg-2); }
/* `.hidden` esta muy por encima en la hoja, asi que una regla posterior que fije `display` le gana y
   el aviso se queda a la vista (reglas 28 y 134). */
.camp-ia-aviso.hidden { display: none; }
.camp-ia-aviso p { margin: 0; font-size: 13px; }
/* Mismo criterio que `.add-form`/`.send-form` (regla 138 (c)): en una fila donde un boton comparte
   renglon con campos, los dos miden lo mismo. Sin esto el boton quedaba en 38 px y el selector en
   43, y al alinearse la fila por abajo los bordes de arriba salian desparejos. */
.camp-ia > button, .camp-ia .camp-ia-campo select { height: 40px; }

/* El reparto por dias del ensayo en seco (parte 173): cuanto entra hoy y cuanto queda para manana.
   Se destaca porque es lo que decide si la campania se mira dos veces antes de lanzarla. */
.camp-reparto { margin: 10px 0; padding: 12px 14px; border-left: 3px solid var(--accent); border-radius: 8px; background: var(--card-bg-2); }
.camp-reparto p { margin: 0 0 6px; font-size: 13px; }
.camp-reparto p:last-child { margin-bottom: 0; }
.camp-cupos { margin: 0 0 6px; padding-left: 18px; font-size: 13px; color: var(--muted); }
.camp-cupos li { margin: 2px 0; }

/* Las fuentes de destinatarios (parte 174): con quien hay conversacion, la agenda, cada Lista. Se
   suman entre si, asi que se leen como una lista de casillas y no como opciones excluyentes. */
.camp-fuentes { display: grid; gap: 6px; }
.camp-fuente { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 8px 10px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 10px; cursor: pointer; }
.camp-fuente:hover { border-color: var(--accent); }
.camp-fuente-nombre { font-weight: 600; font-size: 14px; }
.camp-fuente-n { font-variant-numeric: tabular-nums; font-weight: 700; font-size: 14px; color: var(--accent); }
.camp-fuente-det { grid-column: 2 / -1; font-size: 12px; }
.camp-fuente-vacia { opacity: 0.55; cursor: default; }
/* La agenda incluye a quien nunca escribio: se marca distinto porque es la que mas denuncias
   genera, y una denuncia es lo que termina restringiendo la linea. */
.camp-fuente-ojo { border-left: 3px solid var(--ic-naranja); }
.camp-fuentes-total { margin: 4px 0 0; }
.camp-importar { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-top: 8px; }
.camp-importar > button { height: 40px; }

/* Detalle de una campania (parte 174): quien recibio, quien no y por que. */
.camp-detalle { max-width: 860px; }
.camp-det-chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 10px 0; }
.camp-det-tabla { max-height: 55vh; overflow-y: auto; }
.camp-det-num { font-variant-numeric: tabular-nums; white-space: nowrap; }
.camp-det-error { font-size: 12px; color: var(--muted); }

/* La lista de «no molestar» (parte 174). */
.camp-excluidos { margin-top: 10px; }
.camp-excluidos .table-scroll { max-height: 40vh; overflow-y: auto; }

/* El archivo adjunto de una campania (parte 174). */
.camp-adjunto-fila { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.camp-adjunto-fila > button.btn { height: 40px; }
.camp-con-archivo { margin: 8px 0; }

/* El cupo de cada linea, bajo las casillas de instancias (parte 174). */
.camp-cupos-fila { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.camp-cupos-fila.hidden { display: none; }
.camp-cupo { display: inline-flex; align-items: center; gap: 6px; padding: 4px 8px; border: 1px solid var(--border); border-radius: 999px; font-size: 12px; color: var(--muted); }
.camp-cupo strong { color: var(--accent); font-variant-numeric: tabular-nums; }
.camp-cupo-sin strong { color: var(--ic-naranja); }

/* Elegir contactos para una campania (parte 175): la lista con buscador, filtros y seleccion. */
.camp-contactos { max-width: 720px; }
.camp-contactos-barra { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin: 10px 0 6px; }
.camp-contactos-lista { max-height: 46vh; overflow-y: auto; border: 1px solid var(--border); border-radius: 12px; }
.camp-contacto { display: grid; grid-template-columns: auto auto 1fr auto auto auto; align-items: center; gap: 4px 10px; padding: 8px 12px; border-bottom: 1px solid var(--border); cursor: pointer; }
.camp-contacto:last-child { border-bottom: none; }
.camp-contacto:hover { background: var(--card-bg-2); }
.camp-contacto-ini { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; background: var(--card-bg-2); font-size: 12px; font-weight: 700; color: var(--muted); }
.camp-contacto-nombre { font-size: 14px; }
.camp-contacto-num { font-variant-numeric: tabular-nums; font-size: 13px; color: var(--muted); white-space: nowrap; }
.camp-contacto-det { grid-column: 3 / -1; font-size: 12px; }
.camp-filtros { display: flex; flex-wrap: wrap; gap: 8px; }
.camp-elegir-fila { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.camp-elegir-fila > button.btn { height: 40px; }

/* Lo que de verdad va a recibir esa persona (parte 178), que no es lo mismo que como la tienes
   guardada. Se pinta como una ficha para que se lea de un vistazo al repasar cien filas; la de
   «le toca el respaldo» va apagada y no en rojo: no es un error, es un saludo mas generico. */
.camp-saludo { font-size: 12px; padding: 2px 8px; border-radius: 999px; background: var(--card-bg-2); color: var(--text); white-space: nowrap; }
.camp-saludo-flojo { color: var(--muted); font-style: italic; }
.camp-corregir { white-space: nowrap; }

/* Un boton que se lee como un enlace, para «ver la lista» de lo elegido (parte 177). */
.btn-enlace { padding: 0; border: none; background: none; color: var(--accent); font: inherit; text-decoration: underline; cursor: pointer; }
.btn-enlace:hover { opacity: 0.85; }

/* La cobertura de las variables en el ensayo (parte 177). */
.camp-cobertura { margin: 10px 0; padding: 12px 14px; border-left: 3px solid var(--ic-naranja); border-radius: 8px; background: var(--card-bg-2); }
.camp-cobertura p { margin: 0 0 6px; font-size: 13px; }
.camp-cobertura ul { margin: 0 0 6px; padding-left: 18px; font-size: 13px; }
