/* ============================================================================
   Liberalia · Tipificación TMK — sistema visual
   Tokens de diseño: única fuente de verdad de color, tipo, espacio y motion.
   Reglas de uso del color de marca:
     - --brand-green NUNCA como texto pequeño (3.2:1 sobre blanco, falla AA)
       ni como texto sobre --brand-wash (4.04:1, falla AA). Solo acentos
       gráficos: filetes, barras, rellenos grandes.
     - Texto verde siempre con --green-ink (6.6:1 sobre blanco, 5.8:1 sobre wash).
   ========================================================================== */

:root {
  /* Color */
  --ink:        #1B2420;   /* texto principal */
  --ink-2:      #55605A;   /* texto secundario (6.3:1 sobre blanco) */
  --paper:      #F4F6F4;   /* fondo de aplicación */
  --surface:    #FFFFFF;   /* tarjetas y paneles */
  --line:       #DCE1DD;   /* filetes */
  --line-2:     #C3CBC5;   /* filetes marcados (bordes de campo) */
  --brand-green:#1FA45D;   /* acento de marca, no textual */
  --green-ink:  #0B6B3C;   /* verde accesible para texto y foco */
  --brand-wash: #E7F3EB;   /* fondo del momento venta / selección */
  --danger:     #B3261E;
  --danger-wash:#FBEDEC;
  --warn-ink:   #7A4D00;
  --warn-wash:  #FCF3E1;

  /* Tipografía. Bozon (fuente corporativa, en /assets/fonts/);
     el fallback métrico evita el salto de layout mientras carga. */
  --font-body:    system-ui, "Segoe UI", sans-serif;
  --font-display: "Bozon", "Bozon-fallback", system-ui, "Segoe UI", sans-serif;
  --font-mono:    ui-monospace, "Cascadia Mono", Consolas, monospace;

  /* Espacio (base 4 px) y forma */
  --s1: .25rem; --s2: .5rem; --s3: .75rem; --s4: 1rem; --s5: 1.5rem; --s6: 2rem;
  --radius: 6px;

  /* Motion: funcional, nunca decorativa */
  --t: 120ms ease-out;

  font-family: var(--font-body);
  color-scheme: light;
}

@font-face {
  font-family: "Bozon";
  src: url("/assets/fonts/bozon-regular.otf") format("opentype");
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: "Bozon";
  src: url("/assets/fonts/bozon-demibold.otf") format("opentype");
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: "Bozon";
  src: url("/assets/fonts/bozon-bold.otf") format("opentype");
  font-weight: 700;
  font-display: swap;
}
@font-face {
  font-family: "Bozon";
  src: url("/assets/fonts/bozon-extrabold.otf") format("opentype");
  font-weight: 800;
  font-display: swap;
}
/* Fallback con métrica ajustada: si Bozon llega tarde o no llega, no hay salto. */
@font-face {
  font-family: "Bozon-fallback";
  src: local("Segoe UI"), local("Helvetica Neue"), local("Arial");
  size-adjust: 98%;
  ascent-override: 92%;
}

* { box-sizing: border-box; }

/* El atributo hidden gana siempre, aunque la clase defina display:flex/grid */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-size: 15px;
  line-height: 1.45;
}

h1, h2, h3 { font-family: var(--font-display); margin: 0; }

.mono, td.num, .kpi-num {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
}

/* Foco visible y de alto contraste en TODO elemento interactivo */
:focus-visible {
  outline: 2px solid var(--green-ink);
  outline-offset: 2px;
  border-radius: 2px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; }
}

/* Encabezado que solo necesita el lector de pantalla: la pantalla ya dice lo
   mismo con las pestañas, pero la página no puede quedarse sin su h1. */
.solo-lector {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ── Barra superior ──────────────────────────────────────────────────────── */
.topbar {
  display: flex;
  align-items: center;
  gap: var(--s4);
  height: 48px;
  padding: 0 var(--s4);
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
/* Filete de marca: el único verde de la barra */
.topbar { box-shadow: inset 0 3px 0 var(--brand-green); }

/* El logo va siempre sobre blanco (nunca sobre verde) y con su área de respeto */
.marca {
  margin: 0;
  display: flex;
  align-items: center;
  gap: var(--s3);
  white-space: nowrap;
}
.marca img { height: 26px; display: block; }
.marca-app { font-family: var(--font-display); font-weight: 600; color: var(--ink-2); }

.topbar-agente {
  margin: 0 0 0 auto;
  color: var(--ink-2);
  max-width: 240px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Estado del tiempo real: texto + símbolo, nunca solo color */
.ws-status { margin: 0; font-size: .85rem; color: var(--ink-2); white-space: nowrap; }
.ws-status::before { content: "○ "; }
.ws-status[data-estado="conectado"]::before { content: "● "; color: var(--green-ink); }
.ws-status[data-estado="caido"] { color: var(--warn-ink); background: var(--warn-wash); padding: 2px var(--s2); border-radius: var(--radius); }
.ws-status[data-estado="caido"]::before { content: "◌ "; }

/* ── Botones ─────────────────────────────────────────────────────────────── */
button {
  font: inherit;
  padding: .45rem .9rem;
  border-radius: var(--radius);
  border: 1px solid var(--green-ink);
  background: var(--green-ink);
  color: #fff;
  cursor: pointer;
  transition: background var(--t), transform var(--t), box-shadow var(--t);
}
button:hover { background: #095A32; transform: translateY(-1px); box-shadow: 0 4px 10px -4px rgba(11, 107, 60, .45); }
button:active { transform: translateY(0); box-shadow: none; }
button:disabled { background: var(--ink-2); border-color: var(--ink-2); cursor: default; transform: none; box-shadow: none; }

/* Los enlaces (<a>) que toman prestada la apariencia de botón no heredan las
   reglas del elemento `button` de arriba: necesitan su propio padding/radio. */
a.btn-secundario, a.btn-ghost, summary.btn-secundario {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: .45rem .9rem;
  border-radius: var(--radius);
  cursor: pointer;
  text-decoration: none;
  font: inherit;
}
summary.btn-secundario { list-style: none; }
summary.btn-secundario::-webkit-details-marker { display: none; }
summary.btn-secundario::after { content: "▾"; margin-left: .5em; font-size: .75em; }
details[open] > summary.btn-secundario::after { content: "▴"; }

.btn-secundario, button.btn-secundario {
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--line-2);
}
.btn-secundario:hover { background: var(--paper); transform: none; box-shadow: none; }

.btn-ghost, button.btn-ghost {
  background: transparent;
  color: var(--ink-2);
  border: 1px solid transparent;
  padding: .35rem .6rem;
}
.btn-ghost:hover { background: var(--paper); color: var(--ink); transform: none; box-shadow: none; }

/* ── Campos ──────────────────────────────────────────────────────────────── */
label, legend { display: block; font-weight: 600; font-size: .9rem; margin: 0 0 var(--s1); }
input, select, textarea {
  font: inherit;
  color: var(--ink);
  padding: .45rem .6rem;
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  background: var(--surface);
  width: 100%;
}
/* textarea no hereda min-height/resize de input,select: el navegador la
   pinta minúscula y en línea con su etiqueta si no se fija aquí. */
textarea { min-height: 4.5rem; resize: vertical; }
input::placeholder { color: var(--ink-2); }
input:disabled, select:disabled, textarea:disabled {
  background: var(--paper);
  color: var(--ink-2);
  border-style: dashed;         /* deshabilitado ≠ solo color */
}

/* ── Tarjeta y mensajes ──────────────────────────────────────────────────── */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--s5);
  box-shadow: 0 1px 2px rgba(27, 36, 32, .04), 0 6px 20px -14px rgba(27, 36, 32, .18);
}
.msg-ok  { color: var(--green-ink); font-weight: 600; }
.msg-ok::before { content: "✓ "; }
.msg-err { color: var(--danger); font-weight: 600; }
.msg-err::before { content: "✕ "; }

/* Campo de formulario cuyo autoguardado se rindió: el valor está solo en
   pantalla, así que tiene que verse cuál es (y no solo leerse en el aviso). */
[aria-invalid="true"] { border-color: var(--danger); box-shadow: 0 0 0 2px rgba(179, 38, 30, .18); }

/* ── Tablas ──────────────────────────────────────────────────────────────── */
table { border-collapse: collapse; width: 100%; }
th {
  text-align: left;
  font-size: .8rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--ink-2);
  padding: var(--s2) var(--s3);
  border-bottom: 1px solid var(--line-2);
}
td { padding: var(--s2) var(--s3); border-bottom: 1px solid var(--line); }

/* Fila seleccionada: marcador + peso, no solo color */
.row { cursor: pointer; }
.row:hover { background: var(--paper); }
.row.sel { background: var(--brand-wash); font-weight: 600; }
.row.sel td:first-child::before { content: "▸ "; color: var(--green-ink); }

/* ═══════════════════════ Pantalla de la teleoperadora ═══════════════════ */
.panel-agente {
  display: grid;
  grid-template-columns: 360px 1fr;
  gap: var(--s4);
  padding: var(--s4);
  height: calc(100vh - 48px);   /* todo cabe en 1366×768 sin scroll */
  overflow: hidden;
}

.cola {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
}
.cola-cab {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--s2) var(--s3) 0;
  border-bottom: 1px solid var(--line);
}
.cola-n { font-weight: 400; color: var(--ink-2); font-size: .95rem; }

/* Pestañas del panel izquierdo: la cola y el contacto de campaña. Reutilizan
   la barra horizontal del backoffice (.tabs-h), pero van pegadas al filete de
   la cabecera, sin el suyo propio ni el margen inferior. El selector lleva
   .cola-cab delante a propósito: .tabs-h se declara más abajo en la hoja y,
   con la misma especificidad, ganaría. */
.cola-cab .cola-tabs { margin: 0; border-bottom: 0; flex: 1; }
.cola-cab .cola-tabs [role="tab"] { padding: var(--s1) var(--s3); white-space: nowrap; }
/* Cada pestaña ocupa el panel entero y hace su propio scroll: la cola y la
   ficha del contacto ya no se tapan la una a la otra. */
.cola-panel { flex: 1; min-height: 0; overflow-y: auto; }
/* La de la cola manda su barra de acciones fija y deja el scroll a la tabla. */
#panel-cola { display: flex; flex-direction: column; overflow: hidden; }
.cola-acciones { display: flex; justify-content: flex-end; padding: var(--s1) var(--s3) 0; }
.cola-acciones button { white-space: nowrap; }
#queue { flex: 1; min-height: 0; overflow-y: auto; }
/* La cola ocupa ahora el panel entero y se baja más, así que su cabecera se
   queda a la vista: sin ella no se sabe de qué columna es cada número. El
   filete va en box-shadow y no en border — con border-collapse, el borde de
   una celda fija se queda atrás al bajar. */
#queue thead th {
  position: sticky; top: 0; z-index: 1;
  background: var(--surface);
  box-shadow: inset 0 -1px 0 var(--line-2);
}
.cola-pie { padding: var(--s3) var(--s4); border-top: 1px solid var(--line); margin: 0; }
/* Sin campaña activa no hay botón ni aviso: la franja sobra. :empty no vale,
   los hijos siguen en el DOM con hidden. */
.cola-pie:not(:has(> :not([hidden]))) { display: none; }
#queue th, #queue td { white-space: nowrap; }
#queue-msg { padding: var(--s4); color: var(--ink-2); margin: 0; }
#queue-msg.msg-err { color: var(--danger); }

.ficha-panel {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--s4) var(--s5);
  overflow-y: auto;
}

.ficha-vacia {
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  color: var(--ink-2);
  text-align: center;
  gap: var(--s2);
}
.ficha-vacia-titulo { font-family: var(--font-display); font-size: 1.2rem; color: var(--ink); margin: 0; }
.ficha-vacia p { margin: 0; max-width: 34ch; }

.ficha-cab { display: flex; align-items: baseline; gap: var(--s3); }
.ficha-cab h2 { font-size: 1.25rem; }
#ficha-id { color: var(--ink-2); font-size: .8rem; margin: 0; }
.ficha-cab .btn-ghost { margin-left: auto; }

.ficha-contexto {
  display: flex;
  gap: var(--s5);
  margin: var(--s3) 0 var(--s4);
  padding-bottom: var(--s3);
  border-bottom: 1px solid var(--line);
}
.ficha-contexto div { margin: 0; }
.ficha-contexto dt { font-size: .75rem; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-2); }
.ficha-contexto dd { margin: 0; font-size: 1rem; }

.campo { margin: 0 0 var(--s3); border: 0; padding: 0; max-width: 560px; }
/* La nota reserva su línea: aparecer/desaparecer no desplaza el resto */
.campo-nota { min-height: 1.3em; margin: var(--s1) 0 0; font-size: .85rem; color: var(--ink-2); }
.campo-nota[data-tono="oblig"] { color: var(--green-ink); font-weight: 600; }

/* Combobox accesible */
.cbx { position: relative; }
.cbx input { padding-right: 2.2rem; }
.cbx-abrir {
  position: absolute; right: 2px; top: 2px; bottom: 2px;
  width: 2rem;
  background: transparent; color: var(--ink-2);
  border: 0; border-radius: var(--radius);
  padding: 0;
}
.cbx-abrir:hover { background: var(--paper); color: var(--ink); }
.cbx ul {
  position: absolute; z-index: 10;
  left: 0; right: 0; top: calc(100% + 2px);
  margin: 0; padding: var(--s1) 0;
  list-style: none;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  max-height: 264px;
  overflow-y: auto;
}
.cbx .cbx-grupo {
  padding: var(--s2) var(--s3) var(--s1);
  font-size: .75rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink-2);
}
.cbx [role="option"] { padding: var(--s2) var(--s3); cursor: pointer; }
.cbx [role="option"]:hover { background: var(--paper); }
.cbx [role="option"].activa { background: var(--brand-wash); font-weight: 600; }
.cbx [role="option"].activa::before { content: "▸ "; color: var(--green-ink); }
.cbx-vacio { padding: var(--s2) var(--s3); color: var(--ink-2); }

/* BRANDID como grupo de radios */
.brandid-opciones { display: flex; gap: var(--s3); }
.brandid-opciones .radio {
  display: flex; align-items: center; gap: var(--s2);
  font-weight: 400; margin: 0;
  padding: .4rem .8rem;
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  cursor: pointer;
}
.brandid-opciones .radio:has(input:checked) { background: var(--brand-wash); border-color: var(--green-ink); font-weight: 600; }
.brandid-opciones .radio:has(input:disabled) { border-style: dashed; color: var(--ink-2); cursor: default; }
.brandid-opciones input { width: auto; margin: 0; }

/* Formularios dinámicos: montados desde /forms, cero campos incrustados.
   Más ancho que los 560 px del resto de la ficha: una escala de 0 a 10 no cabe
   en una línea con ese tope y se partía en dos. Los controles de texto siguen
   acotados abajo, que son los que necesitan línea corta para leerse. */
.formularios {
  margin: 0 0 var(--s4);
  padding-top: var(--s3);
  border-top: 1px solid var(--line);
  max-width: 820px;
}
.formulario .campo > input,
.formulario .campo > select,
.formulario .campo > textarea { max-width: 560px; }
.formularios-titulo { font-size: 1rem; margin: 0 0 var(--s3); }
.formulario { margin: 0 0 var(--s4); }
.formulario:last-child { margin-bottom: 0; }
.formulario-titulo { font-size: .9rem; font-weight: 600; margin: 0 0 var(--s2); color: var(--ink-2); }

/* Descartar un formulario: el cliente no contesta y no hay forma de rellenar
   los obligatorios. El botón vive en la cabecera, junto al título, y se abre
   para pedir el motivo (opcional) antes de confirmar. */
.formulario-cab { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s3); flex-wrap: wrap; }
.formulario-descartar summary { font-size: .85rem; padding: .2rem .6rem; }
.formulario-descartar-panel {
  margin-top: var(--s2);
  padding: var(--s3);
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  background: var(--paper);
  width: min(26rem, 100%);
}
.formulario-descartar-panel .campo-nota { margin: 0 0 var(--s2); min-height: 0; }
.formulario-descartar-panel input { margin-bottom: var(--s2); }

/* Un formulario descartado se ve descartado: no puede confundirse con uno que
   nadie ha tocado todavía. */
.formulario-descartado {
  display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap;
  margin: 0 0 var(--s3);
  padding: var(--s2) var(--s3);
  border-left: 3px solid var(--warn-ink);
  border-radius: var(--radius);
  background: var(--warn-wash);
  color: var(--warn-ink);
  font-weight: 600;
}
/* Se apagan las preguntas, no el bloque entero: opacity en el padre crea un
   contexto de apilado y el aviso ya no podría volver a opacidad 1. */
.formulario[data-estado="descartado"] .campo,
.formulario[data-estado="descartado"] .formulario-guion,
.formulario[data-estado="descartado"] .campo-seccion { opacity: .55; }
.formulario-msg { min-height: 0; margin: 0 0 var(--s2); }
.formulario-msg[data-tono="error"] { color: var(--danger); font-weight: 600; }
.formulario .campo { max-width: none; }
.campo-obligatorio { color: var(--green-ink); }
.campo-ayuda { margin: var(--s1) 0 0; font-size: .8rem; color: var(--ink-2); }

/* Guion, secciones y extremos de escala (Formularios v2) */
.formulario-guion, .campo-guion {
  margin: 0 0 var(--s3);
  padding: var(--s2) var(--s3);
  border-left: 3px solid var(--green-ink);
  background: var(--brand-wash);
  border-radius: var(--radius);
}
.formulario-guion p, .campo-guion p { margin: 0; line-height: 1.5; }
.guion-etiqueta { display: block; font-size: .75rem; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-2); margin-bottom: var(--s1); }
.campo-seccion h5 { margin: var(--s3) 0 var(--s2); font-size: .95rem; color: var(--ink-2); border-bottom: 1px solid var(--line); padding-bottom: var(--s1); }
.escala-ancla { align-self: center; font-size: .8rem; color: var(--ink-2); }

/* Multiple (casillas) y escala (radios): una fila de botones, como BRANDID */
.campo-multiple, .campo-escala { display: flex; flex-wrap: wrap; gap: var(--s2); }
.campo-multiple .checkbox,
.campo-escala .radio {
  display: flex; align-items: center; gap: var(--s2);
  font-weight: 400; margin: 0;
  padding: .4rem .8rem;
  border: 1px solid var(--line-2);
  border-radius: var(--radius);
  cursor: pointer;
}
.campo-multiple .checkbox:has(input:checked),
.campo-escala .radio:has(input:checked) { background: var(--brand-wash); border-color: var(--green-ink); font-weight: 600; }
.campo-multiple input, .campo-escala input { width: auto; margin: 0; }

/* Escala al estilo NPS: el número ES el botón. El círculo del radio se
   esconde de la vista pero el input sigue ahí, tabulable y navegable con las
   flechas; el foco se ve en la etiqueta, que es lo que se pinta.
   Así una escala de 0 a 10 cabe en una sola fila. */
.campo-escala { align-items: center; gap: var(--s2) var(--s3); }
.escala-ancla { flex: 0 1 auto; }
/* Los números se encogen para caber, pero no se estiran: con una escala corta
   (1 a 5) estirarlos dejaba un claro enorme entre el último número y su ancla. */
.escala-botones { flex: 0 1 auto; display: flex; flex-wrap: wrap; gap: var(--s1); }
.escala-botones .radio {
  flex: 1 1 2.25rem;
  min-width: 2.25rem;
  max-width: 4.5rem;
  justify-content: center;
  gap: 0;
  padding: .35rem .25rem;
  position: relative;               /* ancla del input escondido */
  font-variant-numeric: tabular-nums;
}
.escala-botones .radio input {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
  margin: 0; padding: 0; border: 0;
}
.escala-botones .radio:has(input:focus-visible) {
  outline: 2px solid var(--green-ink);
  outline-offset: 2px;
}

/* Indicador de autoguardado: distingue guardado / rechazado / sin conexión */
#formularios-estado[data-tono="ok"]    { color: var(--green-ink); font-weight: 600; }
#formularios-estado[data-tono="error"] { color: var(--danger); font-weight: 600; }
#formularios-estado[data-tono="aviso"] { color: var(--warn-ink); font-weight: 600; }

.ficha-acciones { display: flex; align-items: center; gap: var(--s4); margin-top: var(--s4); }
.atajos { margin: 0; font-size: .8rem; color: var(--ink-2); }

/* Zona de resultado con altura reservada: sin saltos de layout */
.ficha-result { min-height: 9.5rem; margin-top: var(--s4); max-width: 560px; }
#ficha-msg { margin: 0; }

#venta-confirm {
  border: 1px solid var(--line-2);
  border-left: 3px solid var(--brand-green);
  border-radius: var(--radius);
  padding: var(--s4);
}
#venta-confirm p { margin: 0 0 var(--s3); }
.fila-botones { display: flex; gap: var(--s3); }

/* Firma visual: el número de pedido, dictable y copiable */
#pedido-panel {
  background: var(--brand-wash);
  border-top: 3px solid var(--brand-green);
  border-radius: var(--radius);
  padding: var(--s4);
}
.pedido-titulo { margin: 0; font-weight: 600; color: var(--green-ink); }
.pedido-num {
  margin: var(--s2) 0;
  font-size: 1.7rem;
  letter-spacing: .06em;
  color: var(--ink);
}
.pedido-ayuda { margin: 0 0 var(--s3); font-size: .85rem; color: var(--ink-2); }

/* ═══════════════════════ Backoffice ═════════════════════════════════════ */
.bo-main {
  padding: var(--s5) var(--s4) var(--s6);
  max-width: 1200px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--s5);
}

/* Cabecera editorial de página: la misma idea del saludo del index, a
   escala de trabajo — un kicker mono (vocabulario técnico ya usado en las
   "líneas" del index y en .ws-status) + un título con presencia real. */
.bo-header h1 { font-size: 1.7rem; margin: 0 0 var(--s1); }
.bo-header-sub { margin: 0; color: var(--ink-2); font-size: .95rem; }
.bo-kicker {
  font-size: .75rem;
  font-weight: 600;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--green-ink);
  margin: 0 0 var(--s1);
}

.filtros {
  display: flex;
  gap: var(--s4);
  align-items: end;
  flex-wrap: wrap;
}
.filtros .filtro { display: flex; flex-direction: column; }
.filtros input { width: 11rem; }

/* Exportar: acción secundaria y ocasional, replegada en vez de ocupar
   media pantalla con una tarjeta permanente. */
.filtros .exportacion { margin-left: auto; align-self: center; }
.exportacion-panel {
  flex-basis: 100%;
  margin-top: var(--s4);
  padding-top: var(--s4);
  border-top: 1px solid var(--line);
}
.exportacion-ayuda { margin: 0 0 var(--s3); color: var(--ink-2); font-size: .9rem; }
.exportacion-panel #exp-msg { margin-top: var(--s3); }
.exportacion-panel #exp-confirm {
  margin-top: var(--s3);
  border: 1px solid var(--line-2);
  border-left: 3px solid var(--brand-green);
  border-radius: var(--radius);
  padding: var(--s4);
}
.exportacion-panel #exp-confirm p { margin: 0 0 var(--s3); }

/* Resumen del periodo: un panel, no cinco cajas iguales. La cifra que
   exige acción hoy (pendientes) lleva la voz cantante; el resto es
   contexto de apoyo, alineado en una fila secundaria. */
.resumen {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: var(--s6);
}
.resumen-foco {
  flex: 1 1 240px;
  padding-right: var(--s6);
  border-right: 1px solid var(--line);
}
.resumen-foco-label {
  display: block;
  font-size: .78rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--ink-2);
  margin-bottom: var(--s2);
}
.resumen-foco-num { display: block; font-size: 3.2rem; font-weight: 700; line-height: 1; color: var(--ink); }
.resumen-foco-nota { margin: var(--s2) 0 0; color: var(--ink-2); font-size: .9rem; }
.resumen-foco[data-alerta="si"] .resumen-foco-num { color: var(--warn-ink); }
.resumen-foco[data-alerta="si"] .resumen-foco-nota { color: var(--warn-ink); font-weight: 600; }

.resumen-secundarias {
  flex: 2 1 420px;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: var(--s4);
  margin: 0;
  align-content: center;
}
.resumen-secundarias > div { margin: 0; }
.resumen-secundarias dt { font-size: .75rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-2); margin-bottom: var(--s1); }
.resumen-secundarias dd { margin: 0; font-family: var(--font-display); font-weight: 700; }
.resumen-secundarias .resumen-num { font-size: 1.6rem; }
.resumen-secundarias .kpi-estado { font-size: 1.3rem; }
.resumen-secundarias .kpi-estado[data-tono="ok"] { color: var(--green-ink); }
.resumen-secundarias .kpi-estado[data-tono="ok"]::before { content: "✓ "; }
.resumen-secundarias .kpi-nota { min-height: 1.2em; margin: var(--s1) 0 0; font-size: .8rem; color: var(--ink-2); }
.resumen-secundarias > div[data-alerta="si"] dd,
.resumen-secundarias > div[data-alerta="si"] .kpi-nota { color: var(--warn-ink); font-weight: 600; }

.estado-vacio { color: var(--ink-2); padding: var(--s5); text-align: center; }

/* Panorama del periodo: la evolución diaria es la que cuenta la historia,
   por eso lleva el ancho completo; el resto son rankings de apoyo. */
#graficos > .bo-kicker { margin-bottom: var(--s4); }

.graficos-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--s4);
}
.grafico-hero { grid-column: 1 / -1; }
.graficos-grid figure { margin: 0; }
.graficos-grid h2 { font-size: 1rem; margin-bottom: var(--s4); }
.graficos-grid canvas { max-height: 220px; }
.grafico-hero canvas { max-height: 280px; }
.graficos-grid details { margin-top: var(--s3); font-size: .85rem; }
.graficos-grid summary { color: var(--ink-2); cursor: pointer; padding: var(--s1) 0; }
.tabla-datos td { padding: var(--s1) var(--s3) var(--s1) 0; border: 0; }
.tabla-datos td:last-child { text-align: right; }

/* Respuestas de formularios: la descarga es una acción ocasional junto al
   título, no una tarjeta propia compitiendo con la tabla. */
.respuestas-cab { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--s4); margin-bottom: var(--s4); }
.respuestas-cab h2 { margin: 0; }
.bo-export-panel { flex-basis: 100%; margin-top: var(--s3); padding-top: var(--s3); border-top: 1px solid var(--line); }

.bo-topbar .btn-ghost { margin-left: auto; }
.bo-lnk-admin {
  margin-left: var(--s4);
  font-size: .9rem;
}
.bo-lnk-admin::before { content: "←"; margin-right: .45em; }

/* Admin: rail de navegación vertical, como un panel de ajustes — cinco
   secciones distintas se leen mejor como sitios que como pestañas de
   navegador. El activo se marca por filete + fondo, sin caja completa. */
.catalogos {
  display: grid;
  grid-template-columns: 180px 1fr;
  gap: var(--s5);
  align-items: start;
}
.tabs {
  display: flex;
  flex-direction: column;
  gap: var(--s1);
  border-right: 1px solid var(--line);
  padding-right: var(--s4);
}
.tabs button {
  background: transparent; color: var(--ink-2); border: 0;
  border-right: 2px solid transparent; border-radius: var(--radius) 0 0 var(--radius);
  padding: var(--s3) var(--s4) var(--s3) var(--s3);
  margin-right: -1px;
  text-align: left;
  font-weight: 500;
  transition: color var(--t), border-color var(--t), background var(--t);
}
.tabs button:hover { color: var(--ink); background: var(--paper); transform: none; box-shadow: none; }
.tabs button[aria-selected="true"] {
  color: var(--ink); font-weight: 600;
  border-right-color: var(--green-ink);
  background: var(--brand-wash);
}
.catalogos-panel { min-width: 0; }
.admin-tabla-card { max-width: none; margin-top: var(--s4); overflow-x: auto; }
.admin-table td:first-child { max-width: 280px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Formularios en escritorio: dos botones de acción no caben en fila junto
   al resto de columnas dentro del panel más estrecho que deja el rail, así
   que se apilan; y el código puede ser una cadena larga sin espacios, así
   que se trunca igual que el título. Solo por encima del punto de corte
   móvil — ahí abajo la tabla ya se reorganiza entera como tarjetas. */
@media (min-width: 721px) {
  #panel-formularios .admin-table td:last-child { display: flex; flex-direction: column; align-items: flex-start; gap: var(--s1); }
  #panel-formularios .admin-table td:nth-child(2) { max-width: 150px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
}
.admin-form-card { max-width: 520px; margin-top: var(--s4); }
.admin-form-card label { margin-top: var(--s3); }
.admin-form-card input[type="checkbox"] { width: auto; margin-right: var(--s2); }
.admin-form-card .fila-botones { margin-top: var(--s4); }
.btn-anadir { margin-top: var(--s3); }
/* La celda del título recorta con puntos suspensivos (nowrap), pero el aviso
   de bloqueado va dentro de esa misma celda y así no se leía nunca entero, que
   es justo lo que tiene que explicar. Se le devuelve el salto de línea. */
.frm-bloqueado-nota { color: var(--ink-2); font-size: .8rem; display: block; white-space: normal; }
#frm-body .frm-toggle, #frm-body .frm-duplicar { margin-right: var(--s2); }

/* Áreas táctiles ≥ 44 px en el backoffice (uso desde tablet y móvil) */
.app-backoffice button, .app-backoffice input, .app-backoffice select, .app-backoffice summary {
  min-height: 44px;
}
.app-backoffice .row td { padding-top: .7rem; padding-bottom: .7rem; }

/* Reorganización real en tablet/móvil: tablas apiladas, no comprimidas */
@media (max-width: 720px) {
  /* La tarjeta gana aire en escritorio (var(--s5)) a costa de ancho útil;
     en pantallas estrechas ese margen extra empuja el contenido de las
     tablas apiladas más allá del borde visible. */
  .card { padding: var(--s4); }
  .bo-topbar { flex-wrap: wrap; height: auto; padding: var(--s2) var(--s4); row-gap: var(--s1); }
  /* El rail de catálogos vuelve a fila horizontal con scroll: no cabe
     una barra lateral en un teléfono. */
  .catalogos { grid-template-columns: 1fr; gap: var(--s4); }
  .tabs {
    flex-direction: row; border-right: 0; border-bottom: 1px solid var(--line);
    padding-right: 0; padding-bottom: 0;
    gap: var(--s4); overflow-x: auto;
  }
  .tabs button {
    flex: none; border-right: 0; border-radius: 0; background: transparent;
    border-bottom: 2px solid transparent; margin-right: 0; margin-bottom: -1px;
    padding: var(--s3) var(--s1);
  }
  .tabs button[aria-selected="true"] { border-bottom-color: var(--green-ink); border-right-color: transparent; }
  .filtros { flex-direction: column; align-items: stretch; }
  .filtros input { width: 100%; }
  .filtros .exportacion { margin-left: 0; align-self: stretch; }
  /* El divisor del resumen es vertical solo mientras el foco y las
     secundarias comparten fila; apilados, sería una raya huérfana. */
  .resumen-foco { border-right: 0; border-bottom: 1px solid var(--line); padding-right: 0; padding-bottom: var(--s4); }
  .admin-table thead { display: none; }
  .admin-table tr { display: block; padding: var(--s2) 0; border-bottom: 1px solid var(--line-2); }
  .admin-table td {
    display: flex; justify-content: space-between; gap: var(--s3);
    border: 0; padding: var(--s1) var(--s3);
    max-width: none !important; white-space: normal !important;
    /* Un código sin espacios (p. ej. "e2e-alta-1785312888133") es un único
       token: sin esto no hay dónde partirlo, y el elemento flex nunca se
       encoge — empuja la fila fuera de la pantalla en vez de ir a una
       segunda línea. */
    overflow-wrap: anywhere;
  }
  .admin-table td::before { content: attr(data-label); font-weight: 600; color: var(--ink-2); flex: none; }
}

/* ═══════════════════════ Inicio (menú de entrada) ════════════════════════ */
.app-inicio {
  display: grid; place-items: center; min-height: 100vh; padding: var(--s6);
  /* Halo de marca muy suave: da profundidad sin abandonar el papel claro
     que usa el resto de la aplicación, y sin tocar el fondo del logo. */
  background:
    radial-gradient(680px circle at 50% 0%, rgba(31, 164, 93, .12), transparent 65%),
    var(--paper);
}
.inicio-lienzo { width: min(760px, 100%); text-align: center; }
.inicio-marca { margin: 0 0 var(--s6); }
.inicio-marca img { height: 44px; }
.inicio-saludo {
  font-size: clamp(2rem, 5vw, 3rem);
  font-weight: 700;
  letter-spacing: -.01em;
  margin: 0 0 var(--s2);
}
.inicio-sub { color: var(--ink-2); margin: 0 0 var(--s6); font-size: 1.05rem; }

.consolas {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--s5);
  text-align: left;
}
.consola {
  display: block;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--s6);
  text-decoration: none;
  color: inherit;
  transition: border-color var(--t), transform var(--t), box-shadow var(--t);
}
.consola:hover, .consola:focus-visible {
  border-color: var(--green-ink);
  transform: translateY(-3px);
  box-shadow: 0 12px 28px -14px rgba(11, 107, 60, .4);
}
/* Guiño a la propia centralita: "línea" con su piloto, como en un conmutador */
.consola-linea {
  display: flex;
  align-items: center;
  gap: var(--s2);
  font-size: .75rem;
  letter-spacing: .1em;
  color: var(--ink-2);
  margin: 0 0 var(--s5);
}
.consola-punto {
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--brand-green);
  flex: none;
}
.consola h2 { font-size: 1.4rem; margin: 0 0 var(--s2); }
.consola p { margin: 0 0 var(--s5); color: var(--ink-2); font-size: .95rem; line-height: 1.5; }
.consola-cta { font-weight: 600; color: var(--green-ink); font-size: .95rem; }

@media (max-width: 560px) {
  .app-inicio { padding: var(--s4); }
  .consolas { grid-template-columns: 1fr; }
  .consola { padding: var(--s5); }
}

/* ═══════════════════════ Login ══════════════════════════════════════════ */
.app-login { display: grid; place-items: center; min-height: 100vh; }
.login-caja { width: min(360px, 90vw); }
/* Enlace al manual de uso bajo el formulario de acceso y en las cabeceras */
.login-manual { margin: var(--s4) 0 0; text-align: center; font-size: .9rem; }
.login-manual a { color: var(--green-ink); font-weight: 600; text-decoration: none; }
.login-manual a:hover, .login-manual a:focus-visible { text-decoration: underline; }
a.btn-ghost { text-decoration: none; border-radius: var(--radius); }
.login-caja .marca { margin: 0 0 var(--s2); }
.login-caja .marca img { height: 48px; }
.login-caja h1 { font-size: 1rem; font-weight: 400; color: var(--ink-2); margin-bottom: var(--s4); }
.login-caja label { margin-top: var(--s3); }
.login-caja button { width: 100%; margin-top: var(--s4); }
.login-caja #msg { margin: var(--s3) 0 0; }

/* ── Constructor de formularios (backoffice) ─────────────────────────────── */
/* Cada campo en construcción se lee como una unidad separada de la siguiente:
   sin el marco, una lista de diez campos es un muro indistinguible. */
.campo-def {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: var(--s3);
  margin: var(--s3) 0;
  background: var(--paper);
}
.campo-def .cd-quitar { margin-top: var(--s3); }
.cd-barra { display: flex; align-items: center; gap: var(--s2); margin-bottom: var(--s2); }
.cd-asa { cursor: grab; user-select: none; color: var(--ink-2); font-size: 1.1rem; padding: 0 var(--s1); }
.campo-def.con-problema { border-color: var(--danger); }
input.con-problema { border-color: var(--danger); }
#frm-codigo-aviso { margin: 0 0 var(--s2); }
.cd-condicion { margin-top: var(--s3); }
.cd-condicion summary { cursor: pointer; font-weight: 600; }
.regla { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; margin: var(--s2) 0; }
.regla input, .regla select { width: auto; margin: 0; }
.var-def { display: flex; flex-wrap: wrap; gap: var(--s2); align-items: center; margin: var(--s2) 0; }
.var-def input { width: auto; margin: 0; }
.vd-aviso { color: var(--danger); }
.campo-condicional { border-left: 3px dashed var(--warn-ink); padding-left: var(--s2); }
#frm-problemas { margin: var(--s2) 0; }
/* La previsualización es una muestra, no un formulario: se distingue del
   resto para que nadie intente rellenarla. */
.frm-preview {
  border: 1px dashed var(--line);
  border-radius: var(--radius);
  padding: var(--s3);
  margin: var(--s2) 0 var(--s4);
}
.frm-bloqueado-nota { color: var(--ink-2); }

/* Pestañas de la pantalla de respuestas e indicadores — barra horizontal
   propia. .tabs por sí sola sigue siendo el carril vertical de admin.html
   (más arriba); .tabs-h la reorienta sin tocar esas reglas. */
.tabs-h { flex-direction: row; gap: var(--s2); margin: 0 0 var(--s3); border-right: 0; padding-right: 0; border-bottom: 1px solid var(--line); }
.tabs-h [role="tab"] { background: none; border: 0; border-bottom: 2px solid transparent; padding: var(--s2) var(--s3); margin: 0; cursor: pointer; font-weight: 600; color: var(--ink-2); border-radius: 0; }
.tabs-h [role="tab"]:hover { background: none; color: var(--ink); }
.tabs-h [role="tab"][aria-selected="true"] { color: var(--green-ink); background: none; border-bottom-color: var(--green-ink); }
#ind-distribuciones h3 { font-size: .95rem; margin: var(--s3) 0 var(--s1); }
.card.grafico canvas { max-height: 240px; }

/* Tarjetas secundarias del resumen de indicadores: mismo patrón que
   .resumen-secundarias, pero como <div> sueltos en vez de <dl>. */
.resumen-item { flex: 1 1 150px; }
.resumen-label { display: block; font-size: .75rem; font-weight: 600; text-transform: uppercase; letter-spacing: .05em; color: var(--ink-2); margin-bottom: var(--s1); }
.resumen-num { display: block; font-family: var(--font-display); font-weight: 700; font-size: 1.6rem; }
.resumen-nota { display: block; font-size: .8rem; color: var(--ink-2); margin-top: var(--s1); }

/* Exportación nocturna (dashboard) */
#export-nocturna { margin-top: var(--s4); }
.exp-dia { border-top: 1px solid var(--line); padding: var(--s2) 0; }
.exp-dia summary { cursor: pointer; display: flex; flex-wrap: wrap; gap: var(--s3); align-items: baseline; }
.exp-fecha { font-weight: 600; }
.exp-estado { font-size: .85rem; font-weight: 600; }
.exp-estado[data-tono="ok"] { color: var(--green-ink); }
.exp-estado[data-tono="ok"]::before { content: "✓ "; }
.exp-estado[data-tono="aviso"] { color: var(--warn-ink); }
.exp-estado[data-tono="error"] { color: var(--danger); }
.exp-resumen { color: var(--ink-2); font-size: .85rem; }
.exp-objetos { margin-top: var(--s2); }
.exp-objetos td:last-child { text-align: right; }
#exp-audio { display: block; width: 100%; margin-top: var(--s3); }

/* Pestaña Campañas */
.camp-estado { font-weight: 600; font-size: .85rem; }
.camp-estado[data-tono="ok"] { color: var(--green-ink); }
.camp-estado[data-tono="ok"]::before { content: "● "; }
.camp-estado[data-tono="aviso"] { color: var(--warn-ink); }
.camp-estado[data-tono="neutro"] { color: var(--ink-2); }
#camp-muestra-cab th { white-space: nowrap; }
#camp-analisis .admin-tabla-card { max-height: 16rem; overflow: auto; }
/* Distintivo de modo prueba: no es el estado (Borrador/Activa/Cerrada), es
   otra cosa — una campaña activa puede seguir en pruebas — así que va aparte,
   con su propio color, para que no se confunda con .camp-estado. */
.camp-prueba {
  display: inline-block;
  margin-top: 2px;
  padding: 1px var(--s2);
  border-radius: var(--radius);
  background: var(--warn-wash);
  color: var(--warn-ink);
  font-weight: 600;
  font-size: .75rem;
}

/* Cerrar borra los contactos y no se puede deshacer: en rojo para que no se
   confunda con Pausar (reversible, no borra nada — ese usa .btn-secundario
   normal). */
.btn-secundario.btn-peligro {
  background: var(--danger-wash);
  color: var(--danger);
  border: 1px solid var(--danger);
}
.btn-secundario.btn-peligro:hover { background: var(--danger); color: #fff; transform: none; box-shadow: none; }

/* Ficha del contacto de campaña (pestaña del panel izquierdo de la agente).
   Ya no es una tarjeta flotante sobre la cola: ocupa la pestaña entera. */
.contacto-panel { padding: var(--s4); }
.contacto-cab { display: flex; flex-wrap: wrap; gap: var(--s3); align-items: baseline; }
.contacto-cab h3 { margin: 0; }
.contacto-empresa { color: var(--ink-2); font-size: .9rem; }
/* Por qué el botón Llamar no marca de verdad: bien visible, no solo un texto
   perdido entre los demás datos. */
.contacto-prueba { color: var(--warn-ink); background: var(--warn-wash); font-weight: 600; font-size: .8rem; padding: 2px var(--s2); border-radius: var(--radius); }
.contacto-datos { display: grid; grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); gap: var(--s2) var(--s3); margin: var(--s3) 0; }
.contacto-datos dt { font-size: .75rem; text-transform: uppercase; letter-spacing: .04em; color: var(--ink-2); }
.contacto-datos dd { margin: 0; }
/* Llamar no puede quedarse al final de una lista larga de variables: la fila
   de botones se pega al fondo de la pestaña y se alcanza sin bajar. */
.contacto-panel .fila-botones {
  position: sticky;
  bottom: 0;
  padding: var(--s3) 0 0;
  background: var(--surface);
  box-shadow: 0 -8px 12px -10px rgba(27, 36, 32, .3);
}

/* ── «La llamada del contacto manda» (incidente del 28/09) ─────────────────
   Todo lo que sigue dice en pantalla lo que antes había que adivinar: en qué
   punto está la llamada del contacto, de quién es la ficha abierta y que hay
   una llamada que no es la que se tiene delante. Los colores salen de los
   pares tinta/lavado que ya existen, todos con contraste AA (≥ 4,5:1). */

/* En qué fase está el contacto, dicho por el servidor. */
.contacto-fase {
  margin: var(--s2) 0 0;
  padding: var(--s2) var(--s3);
  border-left: 3px solid var(--ink-2);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink);
  font-weight: 600;
}
.contacto-fase[data-fase="marcando"],
.contacto-fase[data-fase="llamando"],
.contacto-fase[data-fase="en_conversacion"] {
  border-left-color: var(--green-ink);
  background: var(--brand-wash);
  color: var(--green-ink);
}
.contacto-fase[data-fase="falta_tipificar"] {
  border-left-color: var(--warn-ink);
  background: var(--warn-wash);
  color: var(--warn-ink);
}
.contacto-fase[data-fase="no_salio"] {
  border-left-color: var(--danger);
  background: var(--danger-wash);
  color: var(--danger);
}

/* Confirmación de «No contesta» sobre una llamada contestada. Va dentro de la
   tarjeta, no en un diálogo del navegador. */
.contacto-confirma {
  margin: var(--s3) 0 0;
  padding: var(--s3);
  border: 1px solid var(--warn-ink);
  border-radius: var(--radius);
  background: var(--warn-wash);
  color: var(--ink);
}
.contacto-confirma p { margin: 0 0 var(--s3); }
/* La fila de botones de la tarjeta se pega al fondo; la de la confirmación no. */
.contacto-panel .contacto-confirma .fila-botones {
  position: static;
  flex-wrap: wrap;
  padding: 0;
  background: transparent;
  box-shadow: none;
}
.contacto-panel .fila-botones { flex-wrap: wrap; }

/* La pantalla no se ha cargado entera (módulos de versiones distintas): arriba
   del todo y sin posibilidad de no verlo. */
.aviso-carga {
  margin: 0;
  padding: var(--s3) var(--s4);
  background: var(--danger);
  color: #fff;
  font-weight: 600;
  text-align: center;
}

/* Aviso fijo: hay una llamada de la agente que no es la ficha abierta. */
.aviso-llamada {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--s3);
  margin: 0 0 var(--s4);
  padding: var(--s3);
  border-left: 3px solid var(--danger);
  border-radius: var(--radius);
  background: var(--danger-wash);
  color: var(--danger);
  font-weight: 600;
}
.aviso-llamada[data-estado="terminada"] {
  border-left-color: var(--warn-ink);
  background: var(--warn-wash);
  color: var(--warn-ink);
}
.aviso-llamada p { margin: 0; flex: 1 1 18rem; }
/* Con el aviso puesto, la ficha vacía ya no puede ocupar el alto entero. */
.aviso-llamada:not([hidden]) ~ .ficha-vacia { height: auto; padding: var(--s6) 0; }

/* De quién es la llamada de la ficha: en su propia línea, bajo la cabecera. */
.ficha-cab { flex-wrap: wrap; }
.ficha-contacto {
  order: 9;
  flex: 1 0 100%;
  margin: 0;
  font-weight: 600;
  color: var(--green-ink);
}
.ficha-contacto[data-contacto="no"] { color: var(--ink-2); font-weight: 400; }

/* Por qué se ha abierto esta ficha sola. */
.ficha-nota {
  margin: var(--s2) 0 0;
  padding: var(--s2) var(--s3);
  border-left: 3px solid var(--green-ink);
  border-radius: var(--radius);
  background: var(--brand-wash);
  color: var(--ink);
  font-size: .9rem;
}

/* La ficha abierta no es la de la llamada del contacto reservado. */
.ficha-aviso {
  margin: var(--s2) 0 0;
  padding: var(--s3);
  border-left: 3px solid var(--ink-2);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink);
}
.ficha-aviso[data-tono="error"] {
  border-left-color: var(--danger);
  background: var(--danger-wash);
  color: var(--danger);
  font-weight: 600;
}
.ficha-aviso[data-tono="info"] {
  border-left-color: var(--warn-ink);
  background: var(--warn-wash);
  color: var(--warn-ink);
}
.ficha-aviso p { margin: 0; }
.ficha-aviso .fila-botones { margin-top: var(--s2); flex-wrap: wrap; }
.ficha-aviso .fila-botones:not(:has(> :not([hidden]))) { display: none; }
.ficha-aviso .campo-nota[data-tono="error"] { color: var(--danger); font-weight: 600; min-height: 0; }

/* Tras guardar una llamada que no era la que tocaba. */
.ficha-ofrece { margin-top: var(--s3); flex-wrap: wrap; }

/* La fila de la llamada del contacto reservado, y las etiquetas de la cola. */
.row.fila-contacto { box-shadow: inset 3px 0 0 var(--green-ink); }
.fila-etiqueta {
  display: inline-block;
  padding: 0 var(--s2);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink-2);
  font-family: var(--font-body);
  font-size: .75rem;
  font-weight: 600;
  white-space: nowrap;
}
.row.fila-contacto .fila-etiqueta { background: var(--brand-wash); color: var(--green-ink); }

/* ── Tarjeta: avisos de cita, seguro y última llamada; pestaña Agenda ── */
/* «Ofrecer seguro hogar: NO»: franja roja en la tarjeta y en la cabecera de la
   ficha. Rojo lleno y texto blanco (6,5:1), como el aviso de carga
   incompleta: tiene que verse sin buscarlo, no leerse entre los datos. */
.contacto-alerta,
.ficha-alerta {
  margin: 0 0 var(--s3);
  padding: var(--s2) var(--s3);
  border-radius: var(--radius);
  background: var(--danger);
  color: #fff;
  font-weight: 700;
  letter-spacing: .04em;
  text-align: center;
}
/* En la cabecera de la ficha, cada aviso en su propia línea bajo «Contacto: …»
   (que va en el orden 9). */
.ficha-alerta { order: 10; flex: 1 0 100%; margin: var(--s2) 0 0; }

/* La cita (cuándo, quién y por qué) y la encuesta ya hecha: notas con filete,
   con los mismos pares tinta/lavado AA que la línea de fase. */
.contacto-nota,
.ficha-encuesta {
  margin: var(--s2) 0 0;
  padding: var(--s2) var(--s3);
  border-left: 3px solid var(--warn-ink);
  border-radius: var(--radius);
  background: var(--warn-wash);
  color: var(--warn-ink);
  font-weight: 600;
}
.contacto-nota-encuesta,
.ficha-encuesta {
  border-left-color: var(--green-ink);
  background: var(--brand-wash);
  color: var(--green-ink);
}
.ficha-encuesta { order: 11; flex: 1 0 100%; }
/* La última llamada va pegada a «Ya se le ha llamado N veces»: sin el hueco
   que reserva .campo-nota. */
.contacto-ultima { min-height: 0; }

/* El motivo de la cita, bajo el día y la hora. */
#campo-cita .cita-motivo-etiqueta { margin-top: var(--s3); }

/* Pestaña Agenda: la lista hace su propio scroll y la cabecera queda fija.
   Todo va bajo #panel-agenda para no chocar con la agenda del backoffice. */
#panel-agenda { display: flex; flex-direction: column; overflow: hidden; }
#panel-agenda .agenda-cab {
  display: flex;
  align-items: center;
  gap: var(--s2);
  padding: var(--s1) var(--s3) 0;
}
#panel-agenda .agenda-cab button { white-space: nowrap; }
#panel-agenda .agenda-resumen { margin: 0; flex: 1; font-size: .85rem; color: var(--ink-2); }
#panel-agenda .agenda-msg { margin: var(--s2) var(--s3) 0; font-size: .9rem; }
#panel-agenda .agenda-lista { flex: 1; min-height: 0; overflow-y: auto; padding: 0 var(--s3) var(--s3); }
/* El día se queda arriba al bajar por sus citas. */
#panel-agenda .agenda-dia-titulo {
  position: sticky;
  top: 0;
  z-index: 1;
  margin: 0;
  padding: var(--s3) 0 var(--s1);
  background: var(--surface);
  box-shadow: inset 0 -1px 0 var(--line-2);
  font-family: var(--font-body);
  font-size: .8rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--ink-2);
}
#panel-agenda .agenda-dia[data-vencidas] .agenda-dia-titulo { color: var(--danger); }
#panel-agenda .agenda-citas { list-style: none; margin: 0; padding: 0; }
#panel-agenda .agenda-cita { padding: var(--s2) var(--s2); border-bottom: 1px solid var(--line); }
/* Las propias: filete y lavado verdes, y la palabra «Tuya» (no solo color). */
#panel-agenda .agenda-cita[data-mia] { background: var(--brand-wash); box-shadow: inset 3px 0 0 var(--green-ink); }
#panel-agenda .agenda-cita[data-mia] .agenda-agente { color: var(--green-ink); font-weight: 600; }
#panel-agenda .agenda-linea { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 var(--s2); }
#panel-agenda .agenda-hora { font-weight: 700; }
#panel-agenda .agenda-titulo { font-weight: 600; }
#panel-agenda .agenda-detalle { font-size: .85rem; color: var(--ink-2); }
#panel-agenda .agenda-motivo { margin: var(--s1) 0 0; font-size: .9rem; }
#panel-agenda .agenda-sin-motivo { color: var(--ink-2); font-style: italic; }
#panel-agenda .agenda-estado { margin: var(--s1) 0 0; font-size: .8rem; font-weight: 600; color: var(--warn-ink); }
/* ── Backoffice: reparto por marca y pestaña Agenda ── */

/* «Por marca» va en su propia línea bajo el desglose del avance: son cinco
   grupos y no caben a continuación. En mono, como el avance, para que los
   «trabajados/total» se lean de un vistazo. */
.camp-por-marca {
  display: block;
  margin-top: var(--s1);
  color: var(--ink-2);
  font-family: var(--font-mono);
  font-size: .8rem;
}

/* La pantalla de la agente usa el mismo id para su pestaña Agenda y la hoja
   de estilos es una sola: aquí el panel se comporta como los demás del
   backoffice, digan lo que digan las reglas del panel de la agente. El
   [hidden] global (con !important) sigue mandando para ocultarlo. */
.app-backoffice #panel-agenda { display: block; height: auto; overflow: visible; }

.bo-agenda-filtros { margin-bottom: var(--s3); }
.bo-agenda-filtros select { width: 14rem; }
/* Las filas no se pueden pulsar: sin la mano, que prometería un detalle. */
.bo-agenda-tabla .row { cursor: default; }
.bo-agenda-tabla td { vertical-align: top; }
.bo-agenda-tabla td:nth-child(-n+2) { white-space: nowrap; }   /* Fecha y Hora */
/* El motivo llega a 200 caracteres: con un mínimo de ancho se lee en dos o
   tres líneas en vez de en una columna de una palabra por línea. */
.bo-agenda-motivo { min-width: 14rem; }
.bo-agenda-sub { display: block; color: var(--ink-2); font-size: .8rem; }
.bo-agenda-estado { font-weight: 600; font-size: .85rem; }
.bo-agenda-estado[data-tono="ok"] { color: var(--green-ink); }
.bo-agenda-estado[data-tono="aviso"] { color: var(--warn-ink); }
.bo-agenda-estado[data-tono="neutro"] { color: var(--ink-2); }
.bo-agenda-vacia { padding: var(--s4); color: var(--ink-2); text-align: center; }

@media (max-width: 720px) {
  .bo-agenda-filtros select { width: 100%; }
  .bo-agenda-motivo { min-width: 0; }
  /* Apilada, la celda pone la etiqueta a un lado y el valor al otro. */
  .bo-agenda-tabla td > div { text-align: right; }
}

/* ── Agenda: casilla «Mostrar citas pasadas» (09/10/2026) ── */
.agenda-pasadas { display: inline-flex; align-items: center; gap: .3em; font-size: .85em; margin-left: auto; white-space: nowrap; }

/* ── Backoffice: pestaña Informe ── */

/* Un informe para leer y para imprimir, no un panel de control: cifras
   grandes, mucho aire, rejilla simétrica y filetes finos en vez de cajas y
   sombras. El verde es el acento (la serie principal, los números de sección,
   el filete de cada indicador); lo secundario va en grises neutros. */
.inf-controles .respuestas-cab { margin-bottom: var(--s3); }
.inf-acciones { flex-wrap: wrap; }
.inf-filtro-form select { width: 22rem; max-width: 100%; }

.inf-informe {
  margin-top: var(--s5);
  padding: var(--s6) calc(var(--s6) + var(--s2));
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
/* Modo +Orange: para enviar una captura del informe al cliente con su
   naranja (el corporativo de Orange: FF7900 de acento y F16E00, el accesible,
   para texto). Solo redefine los tokens de acento dentro del bloque del
   informe; el resto del backoffice sigue en verde. */
/* Escalera de las series de los gráficos (principal, segunda, tercera):
   con Liberalia, dos verdes y un gris; con +Orange, naranja, negro y gris
   (la paleta secundaria de Orange), porque dos naranjas no se distinguen. */
.inf-informe {
  --inf-serie-1: var(--green-ink);
  --inf-serie-2: var(--brand-green);
  --inf-serie-3: var(--line-2);
}
.inf-informe[data-marca="masorange"] {
  --brand-green: #FF7900;
  --green-ink: #F16E00;
  --brand-wash: #FFF0E0;
  --inf-serie-1: #FF7900;
  --inf-serie-2: #000000;
  --inf-serie-3: #8F8F8F;
}
/* El botón lleva un punto con el color activo. */
.btn-inf-marca::before {
  content: "";
  display: inline-block;
  width: .6em;
  height: .6em;
  margin-right: .45em;
  border-radius: 50%;
  background: var(--brand-green);
  vertical-align: baseline;
}
.btn-inf-marca[data-marca="masorange"]::before { background: #FF7900; }
.inf-seccion { padding: var(--s6) 0; border-top: 1px solid var(--line); }
.inf-seccion:first-child { padding-top: 0; border-top: 0; }
.inf-seccion:last-child { padding-bottom: 0; }
.inf-seccion-titulo {
  display: flex;
  align-items: baseline;
  gap: var(--s3);
  margin: 0 0 var(--s5);
  font-size: 1.35rem;
  font-weight: 700;
}
.inf-seccion-num { font-size: .8rem; font-weight: 600; letter-spacing: .08em; color: var(--green-ink); }

/* Cabecera: el título del formulario manda; periodo y alcance, debajo. */
.inf-cabecera h2 { margin: 0 0 var(--s3); font-size: 2.2rem; font-weight: 700; line-height: 1.15; }
.inf-cabecera-datos { display: flex; flex-wrap: wrap; gap: var(--s2) var(--s5); margin: 0; color: var(--ink-2); }
.inf-cabecera-datos span + span { padding-left: var(--s5); border-left: 1px solid var(--line-2); }

/* Los cuatro indicadores: mismas columnas, separadas por un filete fino. */
.inf-kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); }
.inf-kpi { padding: 0 var(--s5); border-left: 1px solid var(--line); }
.inf-kpi:first-child { padding-left: 0; border-left: 0; }
.inf-kpi h3 {
  margin: 0 0 var(--s3);
  font-family: var(--font-body);
  font-size: .78rem;
  font-weight: 600;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.inf-kpi h3::before {
  content: "";
  display: block;
  width: 24px;
  height: 3px;
  margin-bottom: var(--s3);
  border-radius: 2px;
  background: var(--brand-green);
}
.inf-kpi-num {
  margin: 0;
  font-family: var(--font-display);
  font-size: 3.6rem;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -.02em;
  font-variant-numeric: tabular-nums;
}
.inf-kpi-nota { margin: var(--s3) 0 0; font-size: .9rem; color: var(--ink-2); }

/* Rejillas de gráficos: columnas iguales, nunca una figura más ancha que otra. */
.inf-rejilla { display: grid; gap: var(--s6); }
.inf-rejilla-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.inf-rejilla-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; }
.inf-figura { margin: 0; min-width: 0; }
.inf-figura figcaption { margin-bottom: var(--s3); font-family: var(--font-display); font-weight: 600; }
/* Chart.js con maintainAspectRatio: false necesita un contenedor con alto. */
.inf-lienzo { position: relative; height: 240px; }
/* Mejoras: una fila de 30 px por área (la pone informe.js en --filas). */
#inf-mejoras-lienzo { height: calc(var(--filas, 6) * 30px + 16px); max-width: 760px; }

/* Cifras de fricción: grandes y en verde, encima de la tabla de drivers. */
.inf-cifras { margin-bottom: var(--s5); }
.inf-cifra-num { margin: 0; font-family: var(--font-display); font-size: 2.4rem; font-weight: 700; line-height: 1; color: var(--green-ink); }
.inf-cifra-txt { margin: var(--s2) 0 0; font-size: .9rem; color: var(--ink-2); }

/* Tablas de datos: un filete oscuro bajo la cabecera y filetes claros entre
   filas; cifras a la derecha con los dígitos alineados. */
.inf-tabla-envoltura { margin-bottom: var(--s5); overflow-x: auto; }
.inf-rejilla .inf-tabla-envoltura { margin-bottom: 0; }
.inf-tabla thead th { border-bottom: 1px solid var(--ink); }
.inf-tabla .num { text-align: right; }
.inf-tabla td.num { font-family: var(--font-body); font-variant-numeric: tabular-nums; }
.inf-tabla tbody th {
  text-align: left;
  font-size: .95rem;
  font-weight: 500;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  border-bottom: 1px solid var(--line);
}
.inf-tabla .inf-grupo th {
  padding-top: var(--s4);
  font-family: var(--font-display);
  font-weight: 600;
  color: var(--green-ink);
  border-bottom-color: var(--line-2);
}
.inf-tabla .inf-vacia th { font-weight: 400; color: var(--ink-2); }

/* El panel es más estrecho que la ventana (lo comparte con el rail de
   pestañas): las tres columnas dejan de caber antes de llegar a tablet. */
@media (max-width: 1080px) {
  .inf-rejilla-3, .inf-rejilla-2 { grid-template-columns: minmax(0, 1fr); }
}
@media (max-width: 900px) {
  .inf-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: var(--s5); }
  .inf-kpi:nth-child(3) { padding-left: 0; border-left: 0; }
}
@media (max-width: 720px) {
  .inf-informe { padding: var(--s4); }
  .inf-filtro-form select { width: 100%; }
  .inf-cabecera h2 { font-size: 1.6rem; }
  .inf-kpi-num { font-size: 2.6rem; }
}

/* Impresión: solo el informe, en A4 apaisado, sin la cabecera de la app, las
   pestañas ni los botones, y con la rejilla de escritorio (el ancho útil de un
   A4 apaisado cae dentro de los puntos de corte de arriba). Los lienzos se
   escalan por CSS: Chart.js no se redibuja al imprimir. */
@media print {
  @page { size: A4 landscape; margin: 12mm; }
  * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  body { background: #fff; }
  .topbar, .bo-header, .tabs, .inf-controles { display: none !important; }
  .bo-main { max-width: none; padding: 0; }
  .catalogos { display: block; }
  .inf-informe { margin: 0; padding: 0; border: 0; }
  .inf-seccion { padding: var(--s4) 0; break-inside: avoid; page-break-inside: avoid; }
  .inf-figura, .inf-kpis, .inf-tabla tr { break-inside: avoid; page-break-inside: avoid; }
  .inf-kpis { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .inf-kpi:nth-child(3) { padding-left: var(--s5); border-left: 1px solid var(--line); }
  .inf-rejilla { gap: var(--s5); }
  .inf-rejilla-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .inf-rejilla-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .inf-lienzo, #inf-mejoras-lienzo { height: auto; }
  .inf-lienzo canvas { width: auto !important; height: auto !important; max-width: 100%; max-height: 190px; }
  .inf-tabla-envoltura { overflow: visible; }
  .inf-tabla th, .inf-tabla td { padding-top: 3px; padding-bottom: 3px; }
}
