/* ==========================================================================
   QueueReport Global CSS — Paleta y estilos base compartidos
   Basado en CoreUI 5 + Bootstrap 5
   Paleta inspirada en Consoltic: navy profundo + indigo/azul eléctrico
   ========================================================================== */

/* --------------------------------------------------------------------------
   Escala tipográfica base del sistema

   Hasta acá no había ninguna regla `html { font-size }` en el proyecto: la base era
   la del navegador (16px) vía `--cui-body-font-size: 1rem` de CoreUI, que es una
   escala pensada para sitios de contenido y en un dashboard denso desperdicia
   pantalla.

   Todo el CSS del sistema expresa sus `font-size` en `rem` (110 declaraciones entre
   este archivo y queuereport_reports.css, ninguna en px), así que este único valor
   baja la tipografía de TODAS las pantallas de forma proporcional.

   Lo que NO escala, a propósito: las alturas fijas en px (`--qr-field-h`,
   `--qr-header-height`, `--qr-sidebar-width`, los días de los calendarios). Son áreas
   de click, no tipografía — achicarlas empeora el uso. Con el texto más chico el campo
   simplemente respira más, que es el efecto buscado.

   Para volver atrás alcanza con subir este valor (16px = el default del navegador).

   Ojo al bajarlo más: a 14px un `.form-text` de .75rem cae a 10,5px y deja de leerse.
   Los tamaños de apoyo del sistema ya tienen el piso subido para compensar; si volvés a
   tocar esta línea, revisalos (`.form-text`, `.invalid-feedback`, `.qr-pill__n`).
   -------------------------------------------------------------------------- */
html { font-size: var(--qr-fs-root); }

/* --------------------------------------------------------------------------
   Variables (colores, tipografía, radios, sombras, campos, sidebar, picker)

   Viven TODAS en static/css/qr_tokens.css, que el layout carga antes que este
   archivo. Acá no se declara ningún `:root` ni ningún valor literal de color,
   font-size, radio o sombra: sólo `var(--qr-…)`. Un tema se arma redefiniendo
   tokens (static/css/themes/), nunca tocando este archivo.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   Formularios — normalización sobre Bootstrap/CoreUI

   `-sm` deja de ser una escala aparte: en este sistema los formularios ya venían
   escritos casi enteros con `form-control-sm` / `form-select-sm`, así que las dos
   variantes se igualan en vez de mantener dos alturas conviviendo.
   -------------------------------------------------------------------------- */
.form-control,
.form-select,
.form-control-sm,
.form-select-sm,
.input-group-text {
    min-height: var(--qr-field-h);
    border-radius: var(--qr-field-radius);
    font-size: var(--qr-field-font);
    padding-left: var(--qr-field-px);
    padding-right: var(--qr-field-px);
    border-color: var(--qr-field-border);
    background-color: var(--qr-field-bg);
    color: var(--qr-text);
}
.input-group-text { background-color: var(--qr-surface-2); }

/* Alto EXACTO para los campos de verdad. El CSelect global fija su alto, así que con sólo
   `min-height` acá los dos coincidían por casualidad —Bootstrap calcula el del `-sm` con el
   line-height, que da ~30px— y en la misma fila se veía el escalón.

   Va por ELEMENTO y no por clase: hay pantallas que usan `<div class="form-control">` como
   marco de un switch o de un valor de sólo lectura, y ésos tienen que poder crecer. */
input.form-control,
select.form-select,
.input-group-text { height: var(--qr-field-h); }

/* `-lg` sigue siendo una escala aparte a propósito: se usa en los pocos campos
   protagonistas (buscadores grandes, wizards), donde el punto es que resalten. */
input.form-control-lg,
select.form-select-lg { height: var(--qr-control-height-lg); }
.form-control-lg,
.form-select-lg { min-height: var(--qr-control-height-lg); font-size: var(--qr-control-font-size-lg);
    padding-left: var(--qr-control-padding-x-lg); padding-right: var(--qr-control-padding-x-lg); }

/* El caret del select necesita su espacio propio: sin esto el texto de la opción
   se mete debajo de la flecha (se veía en los selects con `w-auto`). */
.form-select,
.form-select-sm { padding-right: 2.1rem; background-position: right .6rem center; }

/* Los multilínea y los que muestran varias filas crecen con su contenido: el alto
   fijo es solo para los de una línea. */
textarea.form-control,
select[multiple].form-select,
select[size].form-select { height: auto; min-height: var(--qr-field-h); }

/* Los `type=file` y `type=color` traen su propio alto interno y con el fijo quedan recortados. */
input.form-control[type="file"],
input.form-control[type="color"] { height: auto; min-height: var(--qr-field-h); }

/* Las etiquetas: mismo tamaño y peso en todo el sistema. Estaba escrito a mano pantalla por
   pantalla (`class="form-label small fw-semibold"`), así que alcanzaba con que una lo olvidara
   para que esa fila se viera distinta. */
.form-label {
    font-size: var(--qr-fs-md); font-weight: 600; margin-bottom: .3rem;
    color: var(--qr-text);
}
.form-text { font-size: var(--qr-fs-82); }

.form-control:hover:not(:disabled):not([readonly]),
.form-select:hover:not(:disabled) { border-color: var(--qr-field-border-hover); }

.form-control:focus,
.form-select:focus {
    border-color: var(--qr-field-focus);
    box-shadow: var(--qr-field-focus-shadow);
}
.form-control::placeholder { color: var(--qr-field-placeholder); }

/* Los botones que acompañan a un campo en la misma fila (toolbars, filtros) tienen
   que medir lo mismo o la fila queda escalonada. */
.btn-sm { min-height: var(--qr-btn-height-sm); border-radius: var(--qr-btn-radius); }
/* .btn-xs es el tamaño de los iconos de acción dentro de las tablas: NO se toca,
   ahí lo que importa es no alterar el alto de fila (ver .qr-actions). */
.btn-xs { min-height: var(--qr-btn-height-xs); }

/* input-group: se reponen los cantos rectos internos, que el border-radius de
   arriba pisaría (Bootstrap los define con menor especificidad). */
.input-group > :not(:first-child):not(.dropdown-menu):not(.form-floating) {
    border-top-left-radius: 0; border-bottom-left-radius: 0;
}
.input-group > :not(:last-child):not(.dropdown-toggle):not(.form-floating) {
    border-top-right-radius: 0; border-bottom-right-radius: 0;
}

/* --------------------------------------------------------------------------
   Fila de filtros (qr-filters)

   Estaba definida en el `<style>` de cada pantalla que la necesitaba, con el prefijo del
   sistema pero SIN estar en el sistema: dos copias con valores distintos y ninguna forma de
   cambiarlas juntas. Los estilos scoped de Svelte ganan por especificidad, así que el síntoma
   era una pantalla que se ve distinta del resto sin que nadie haya tocado el global.

   `align-items: end` alinea los campos por su BASE, que es lo único que se ve alineado cuando
   las etiquetas tienen distinto largo y alguna envuelve a dos líneas.
   -------------------------------------------------------------------------- */
.qr-filters {
    display: flex; flex-wrap: wrap; gap: .75rem; align-items: end;
}
.qr-filters__field { flex: 1 1 190px; min-width: 0; }
.qr-filters__actions { display: flex; gap: .5rem; align-items: end; margin-left: auto; }

/* Ancho al contenido para un select corto ("Encuesta / IVR", "POST / PUT"): `w-auto`
   de Bootstrap no reserva el espacio del caret y el texto termina pisándolo. */
.qr-select-auto { width: auto !important; min-width: 8.5rem; }

/* --------------------------------------------------------------------------
   CSelect — alineación con la escala de arriba

   El fuente de `frontend/components/CSelect.svelte` ya lee los tokens
   `--qr-field-*`, pero el componente se BUNDLEA dentro de cada app de Vite (21 a
   la fecha) y sus estilos van scopeados con un hash de Svelte, que gana por
   especificidad. Sin estas reglas, una pantalla que todavía no se rebuildeó
   quedaría con el CSelect en 38px al lado de inputs de 34px — la misma
   inconsistencia, al revés.

   De ahí el `!important`: es el único modo de alcanzar un estilo scoped desde el
   CSS global. Los valores son los mismos que ya trae el componente, así que
   rebuildear una app no cambia nada; cuando estén todas rebuildeadas este bloque
   se puede borrar.
   -------------------------------------------------------------------------- */
[class*="cs__control"] {
    /* `min-height` va SÍ o SÍ, no alcanza con `height`: 9 bundles viejos traen
       `min-height:38px` hardcodeado (admin/skills, admin/campaign_group y 7 de reportes)
       y el mínimo le gana a la altura, así que esas pantallas seguían en 38px pese al
       `!important` de abajo. */
    min-height: var(--qr-field-h) !important;
    height: var(--qr-field-h) !important;
    border-radius: var(--qr-field-radius) !important;
    padding-left: var(--qr-field-px) !important;
}
[class*="cs__single"],
[class*="cs__ph"] { font-size: var(--qr-field-font) !important; }

/* DateTimeField — mismo caso y misma razón que CSelect: el fuente ya lee los tokens,
   pero se bundlea scopeado en cada app y las que todavía no se rebuildearon lo tienen
   en 38px/9px. */
[class*="qr-dtf-trigger"] {
    min-height: var(--qr-field-h) !important;
    height: var(--qr-field-h) !important;
    border-radius: var(--qr-field-radius) !important;
    padding-left: var(--qr-field-px) !important;
    padding-right: var(--qr-field-px) !important;
}
[class*="qr-dtf-text"] { font-size: var(--qr-field-font) !important; }

/* --------------------------------------------------------------------------
   Body base
   -------------------------------------------------------------------------- */
body {
    background-color: var(--qr-body-bg);
    color: var(--qr-text);
    font-family: var(--qr-font-sans);
}

/* --------------------------------------------------------------------------
   Sidebar — override CoreUI 5 variables + estilos propios
   -------------------------------------------------------------------------- */
.sidebar {
    --cui-sidebar-bg:                      var(--qr-sidebar-bg);
    --cui-sidebar-color:                   var(--qr-sidebar-text);
    --cui-sidebar-brand-bg:                transparent;
    --cui-sidebar-brand-color:             var(--qr-text-inverse);
    --cui-sidebar-nav-group-bg:            rgba(var(--qr-shade-rgb), var(--qr-alpha-15));
    --cui-sidebar-nav-group-border-color:  transparent;
    --cui-sidebar-nav-group-indicator-color: var(--qr-sidebar-text);
    --cui-sidebar-nav-group-indicator-hover-color: var(--qr-sidebar-text-hover);
    --cui-sidebar-toggler-bg:              rgba(var(--qr-highlight-rgb), var(--qr-alpha-5));
    --cui-sidebar-toggler-color:           var(--qr-sidebar-text);
    /* CoreUI esconde el menú con `margin-inline-start: -ancho`: tiene que ser el mismo ancho. */
    --cui-sidebar-width:                   var(--qr-sidebar-width);

    background: linear-gradient(180deg, var(--qr-sidebar-bg) 0%, var(--qr-sidebar-bg-mid) 100%);
    border-right: 1px solid var(--qr-sidebar-border) !important;
    width: var(--qr-sidebar-width) !important;
}

/* Header del sidebar — marca */
.sidebar .sidebar-header {
    background: rgba(var(--qr-shade-rgb), var(--qr-alpha-25));
    border-bottom: 1px solid var(--qr-sidebar-border) !important;
    padding: 0 1rem;
    height: var(--qr-header-height);
    display: flex;
    align-items: center;
}

.sidebar .sidebar-brand {
    color: var(--qr-text-inverse) !important;
    font-size: var(--qr-fs-90);
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
}

.sidebar .sidebar-brand span {
    color: var(--qr-primary-light);
}

/* Marca configurable (BRAND_* del settings, fragmento templates/_brand_wordmark.html).
   Sin logo configurado se ve el wordmark de texto de siempre y nada de esto aplica. */
.qr-brand-logo {
    height: var(--qr-brand-logo-height);
    max-width: var(--qr-brand-logo-max-width);
    width: auto;
    object-fit: contain;
    vertical-align: middle;
}
.qr-brand-icon {
    height: var(--qr-brand-icon-size);
    width: var(--qr-brand-icon-size);
    object-fit: contain;
}
.qr-brand-login-logo {
    height: var(--qr-brand-login-logo-height);
    max-width: 100%;
    width: auto;
    object-fit: contain;
}
.qr-brand-login-image {
    width: 100%;
    max-width: var(--qr-brand-login-image-max-width);
    height: auto;
}
/* Variante clara/oscura sobre superficies que cambian con el modo: por defecto el logo
   claro; en modo oscuro (CoreUI o Bootstrap) el oscuro, si se configuró. */
.qr-brand-logo--on-dark { display: none; }
[data-coreui-theme="dark"] .qr-brand-logo--on-light,
[data-bs-theme="dark"] .qr-brand-logo--on-light { display: none; }
[data-coreui-theme="dark"] .qr-brand-logo--on-dark,
[data-bs-theme="dark"] .qr-brand-logo--on-dark { display: inline-block; }

/* Títulos de sección */
.sidebar .nav-title {
    color: var(--qr-sidebar-title) !important;
    font-size: var(--qr-fs-63);
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    padding: 1.1rem 1rem 0.3rem !important;
    border-top: 1px solid var(--qr-sidebar-border);
    margin-top: 0.25rem;
}

/* Links de navegación */
.sidebar .nav-link {
    color: var(--qr-sidebar-text) !important;
    border-radius: var(--qr-sidebar-item-radius);
    margin: 1px 8px;
    padding: var(--qr-sidebar-item-padding-y) var(--qr-sidebar-item-padding-x) !important;
    min-height: var(--qr-sidebar-item-height);
    font-size: var(--qr-sidebar-item-font-size);
    transition: background 0.15s, color 0.15s;
}

.sidebar .nav-link:hover {
    color: var(--qr-sidebar-text-hover) !important;
    background: var(--qr-sidebar-hover-bg) !important;
}

.sidebar .nav-link.active,
.sidebar .nav-link[aria-current="page"] {
    color: var(--qr-sidebar-text-active) !important;
    background: var(--qr-sidebar-active-bg) !important;
    box-shadow: inset 3px 0 0 var(--qr-primary);
}

/* Ícono del nav-link */
.sidebar .nav-icon {
    color: var(--qr-sidebar-text) !important;
    min-width: 1.6rem !important;
    font-size: var(--qr-icon-size-sm);
}

.sidebar .nav-link:hover .nav-icon,
.sidebar .nav-link.active .nav-icon {
    color: var(--qr-primary-light) !important;
}

/* Grupos colapsables */
.sidebar .nav-group-toggle {
    color: var(--qr-sidebar-text) !important;
    border-radius: var(--qr-sidebar-item-radius);
    margin: 1px 8px;
    padding: var(--qr-sidebar-item-padding-y) var(--qr-sidebar-item-padding-x) !important;
    min-height: var(--qr-sidebar-item-height);
    font-size: var(--qr-sidebar-item-font-size);
}

.sidebar .nav-group-toggle:hover {
    color: var(--qr-sidebar-text-hover) !important;
    background: var(--qr-sidebar-hover-bg) !important;
}

.sidebar .nav-group.show > .nav-group-toggle {
    color: var(--qr-sidebar-text-active) !important;
    background: rgba(var(--qr-primary-rgb), var(--qr-alpha-12)) !important;
}

.sidebar .nav-group-items {
    background: rgba(var(--qr-shade-rgb), var(--qr-alpha-15)) !important;
    border-radius: 0 0 var(--qr-radius-sm-plus) var(--qr-radius-sm-plus);
    margin: 0 8px 4px;
    padding: 4px 0 !important;
}

.sidebar .nav-group-items .nav-link {
    margin: 1px 0 !important;
    border-radius: var(--qr-radius-sm);
    font-size: var(--qr-fs-78);
    /* CoreUI 5 agrega padding-inline-start extra para sub-ítems — lo reseteamos */
    padding-inline-start: var(--qr-sidebar-item-padding-x) !important;
}

/* CoreUI 5 aplica margin-inline-start negativo en sub-ítems que saca el ícono del flujo */
.sidebar .nav-group-items .nav-icon {
    margin-inline-start: 0 !important;
    display: inline-flex !important;
    align-items: center;
    justify-content: center;
    min-width: 1.4rem !important;
    font-size: var(--qr-fs-82);
    flex-shrink: 0;
}

/* Margen inferior del nav para que no llegue hasta el borde del footer */
.sidebar .sidebar-nav {
    padding-bottom: 0.5rem !important;
}

/* Footer del sidebar — versión */
.qr-sidebar-footer {
    padding: 0.75rem 1rem;
    border-top: 1px solid var(--qr-sidebar-border);
    font-size: var(--qr-fs-xs);
    color: var(--qr-sidebar-title);
    letter-spacing: 0.03em;
    display: flex;
    align-items: center;
    flex-shrink: 0;
    background: rgba(var(--qr-shade-rgb), var(--qr-alpha-20));
}

.qr-sidebar-footer .bi {
    opacity: 0.6;
}

/* Botón "Apariencia" del pie del menú de siempre (templates/sidebars/_apariencia_menu.html) */
.qr-sidebar-version { display: inline-flex; align-items: center; gap: 0.25rem; }
.qr-appearance-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    height: var(--qr-control-height-sm);
    padding: 0 0.5rem;
    border: 1px solid var(--qr-sidebar-border);
    border-radius: var(--qr-radius-pill);
    background: transparent;
    color: var(--qr-sidebar-text);
    font-size: var(--qr-fs-xs);
    cursor: pointer;
}
.qr-appearance-btn:hover,
.qr-appearance-btn[aria-expanded="true"] { background: var(--qr-sidebar-hover-bg); color: var(--qr-sidebar-text-hover); }
.qr-sidebar-footer .qr-appearance-btn .bi { opacity: 1; }
.qr-appearance-menu { min-width: 15rem; }
.qr-appearance-item { display: flex; align-items: center; gap: 0.6rem; }
.qr-appearance-check { margin-left: auto; visibility: hidden; color: var(--qr-primary); }
.qr-appearance-item.is-on .qr-appearance-check { visibility: visible; }
.qr-appearance-item.is-on { font-weight: 600; }
.qr-appearance-beta { font-size: var(--qr-fs-2xs); color: var(--qr-text-muted); }
.qr-appearance-error { margin: 0.4rem 0.75rem 0.2rem; padding: 0.4rem 0.6rem; border-radius: var(--qr-radius-sm-plus); background: var(--qr-bad-bg); color: var(--qr-bad-text); font-size: var(--qr-fs-xs); }

/* --------------------------------------------------------------------------
   Menú lateral "por niveles" (templates/sidebars/niveles.html, QR_SIDEBAR=niveles).
   Nivel 1 = áreas (.qr-nl-area); nivel 2 = pantallas de un área (.qr-nl-link).
   Lo mueve static/js/qr_sidebar_niveles.js; siempre abre en el área de la pantalla actual.
   -------------------------------------------------------------------------- */
.qr-nl { display: flex; flex-direction: column; }
.qr-nl-search { padding: 0.75rem 0.75rem 0.5rem; flex-shrink: 0; }
.qr-nl-field {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    height: var(--qr-control-height-md);
    padding: 0 var(--qr-control-padding-x);
    margin: 0;
    border-radius: var(--qr-radius-md);
    border: 1px solid var(--qr-sidebar-border);
    background: var(--qr-sidebar-hover-bg);
    color: var(--qr-sidebar-title);
    cursor: text;
}
.qr-nl-field:focus-within { border-color: var(--qr-primary-light); }
.qr-nl-field input {
    flex: 1;
    min-width: 0;
    border: 0;
    outline: 0;
    background: transparent;
    color: var(--qr-sidebar-text-active);
    font-size: var(--qr-fs-md);
}
.qr-nl-field input::placeholder { color: var(--qr-sidebar-title); }
.qr-nl-kbd {
    font-family: var(--qr-font-sans);
    font-size: var(--qr-fs-2xs);
    font-weight: 600;
    color: var(--qr-sidebar-title);
    background: transparent;
    border: 1px solid var(--qr-sidebar-border);
    border-radius: var(--qr-radius-xs);
    padding: 0.1rem 0.3rem;
    box-shadow: none;
}
.qr-nl-body { flex: 1; min-height: 0; overflow-y: auto; padding: 0.25rem 0.625rem 0.75rem; scrollbar-width: thin; }
.qr-nl-pane { display: flex; flex-direction: column; gap: 1px; }

/* Nivel 1: áreas */
.qr-nl-area {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    min-height: var(--qr-control-height-lg);
    padding: 0.35rem 0.625rem;
    border: 0;
    border-radius: var(--qr-radius-md);
    background: transparent;
    color: var(--qr-sidebar-text);
    font-size: var(--qr-fs-md);
    font-weight: 500;
    text-align: left;
    width: 100%;
    cursor: pointer;
}
.qr-nl-area:hover { background: var(--qr-sidebar-hover-bg); color: var(--qr-sidebar-text-hover); }
.qr-nl-area > .bi { font-size: var(--qr-fs-base); width: 1.25rem; text-align: center; }
.qr-nl-area.is-here { color: var(--qr-sidebar-text-active); font-weight: 600; }
.qr-nl-area.is-here > .bi { color: var(--qr-primary-light); }
.qr-nl-area-name { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 0.1rem; }
.qr-nl-now {
    font-size: var(--qr-fs-xs);
    font-weight: 500;
    color: var(--qr-sidebar-text-hover);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.qr-nl-count {
    font-size: var(--qr-fs-2xs);
    font-weight: 600;
    color: var(--qr-sidebar-title);
    background: var(--qr-sidebar-hover-bg);
    border-radius: var(--qr-radius-pill);
    padding: 0.1rem 0.5rem;
}
.qr-nl-area.is-here .qr-nl-count { background: var(--qr-sidebar-active-bg); color: var(--qr-sidebar-text-active); }
.qr-nl-chev { font-size: var(--qr-fs-2xs); color: var(--qr-sidebar-title); }

/* Nivel 2: cabecera del área y pantallas */
.qr-nl-head { display: flex; align-items: center; justify-content: space-between; padding: 0.15rem 0 0.4rem; }
.qr-nl-back {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    height: var(--qr-control-height-sm);
    padding: 0 0.5rem;
    border: 0;
    border-radius: var(--qr-radius-sm-plus);
    background: transparent;
    color: var(--qr-sidebar-title);
    font-size: var(--qr-fs-sm);
    cursor: pointer;
}
.qr-nl-back:hover { background: var(--qr-sidebar-hover-bg); color: var(--qr-sidebar-text-hover); }
.qr-nl-meta { font-size: var(--qr-fs-xs); color: var(--qr-sidebar-title); padding-right: 0.5rem; }
.qr-nl-title {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.25rem 0.625rem 0.6rem;
    font-size: var(--qr-fs-lg);
    font-weight: 700;
    color: var(--qr-sidebar-text-active);
}
.qr-nl-title .bi { color: var(--qr-primary-light); }
.qr-nl-link {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-height: var(--qr-control-height-md);
    padding: var(--qr-sidebar-item-padding-y) var(--qr-sidebar-item-padding-x);
    border-radius: var(--qr-sidebar-item-radius);
    color: var(--qr-sidebar-text);
    font-size: var(--qr-sidebar-item-font-size);
    text-decoration: none;
}
.qr-nl-link:hover { background: var(--qr-sidebar-hover-bg); color: var(--qr-sidebar-text-hover); }
.qr-nl-link.active {
    background: var(--qr-sidebar-active-bg);
    color: var(--qr-sidebar-text-active);
    font-weight: 600;
    box-shadow: inset 3px 0 0 var(--qr-primary);
}
.qr-nl-link mark { padding: 0; border-radius: var(--qr-radius-2xs); background: var(--qr-sidebar-active-bg); color: var(--qr-sidebar-text-active); }
.qr-nl-link.is-first { box-shadow: inset 0 0 0 1px var(--qr-sidebar-border); }
.qr-nl-alias { margin-left: auto; font-size: var(--qr-fs-2xs); color: var(--qr-sidebar-title); }
.qr-nl-here {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    margin-top: 0.9rem;
    padding: 0.6rem;
    border: 1px dashed var(--qr-sidebar-border);
    border-radius: var(--qr-radius-md);
    background: transparent;
    color: var(--qr-sidebar-title);
    font-size: var(--qr-fs-sm);
    text-align: left;
    cursor: pointer;
}
.qr-nl-here b { color: var(--qr-sidebar-text-active); font-weight: 600; }
.qr-nl-here:hover { border-color: var(--qr-primary-light); color: var(--qr-sidebar-text-hover); }
.qr-nl-group {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.75rem 0.625rem 0.3rem;
    font-size: var(--qr-fs-xs);
    font-weight: 600;
    color: var(--qr-sidebar-title);
}
.qr-nl-empty { margin: 0; padding: 0.75rem 0.625rem; font-size: var(--qr-fs-sm); line-height: 1.5; color: var(--qr-sidebar-title); }
.qr-nl button:focus-visible,
.qr-nl a:focus-visible { outline: 2px solid var(--qr-primary-light); outline-offset: 1px; }

/* Apariencia (menú por niveles): pantalla propia en lugar de los niveles */
.qr-nl-subtitle { padding: 0.75rem 0.625rem 0.4rem; font-size: var(--qr-fs-xs); font-weight: 600; color: var(--qr-sidebar-title); }
.qr-nl-modes { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.4rem; padding: 0 0.25rem; }
.qr-nl-mode {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.35rem;
    padding: 0.6rem 0.25rem;
    border: 1px solid var(--qr-sidebar-border);
    border-radius: var(--qr-radius-md);
    background: transparent;
    color: var(--qr-sidebar-text);
    font-size: var(--qr-fs-sm);
    font-weight: 500;
    cursor: pointer;
}
.qr-nl-mode .bi { font-size: var(--qr-fs-xl); }
.qr-nl-mode:hover { color: var(--qr-sidebar-text-hover); background: var(--qr-sidebar-hover-bg); }
.qr-nl-mode.is-on { border-color: var(--qr-primary-light); background: var(--qr-sidebar-active-bg); color: var(--qr-sidebar-text-active); }
.qr-nl-themes { display: flex; flex-direction: column; gap: 0.3rem; padding: 0 0.25rem; }
.qr-nl-theme {
    display: flex;
    align-items: center;
    justify-content: space-between;
    min-height: var(--qr-control-height-md);
    padding: 0 0.625rem;
    border: 1px solid var(--qr-sidebar-border);
    border-radius: var(--qr-radius-md);
    background: transparent;
    color: var(--qr-sidebar-text);
    font-size: var(--qr-fs-sm);
    text-align: left;
    cursor: pointer;
}
.qr-nl-theme:hover { color: var(--qr-sidebar-text-hover); background: var(--qr-sidebar-hover-bg); }
.qr-nl-theme .bi { visibility: hidden; color: var(--qr-primary-light); }
.qr-nl-theme.is-on { border-color: var(--qr-primary-light); color: var(--qr-sidebar-text-active); }
.qr-nl-theme.is-on .bi { visibility: visible; }
.qr-nl-hint { margin: 0; padding: 0.5rem 0.625rem 0; font-size: var(--qr-fs-xs); line-height: 1.45; color: var(--qr-sidebar-title); }
.qr-nl-error { margin: 0.6rem 0.25rem 0; padding: 0.5rem 0.625rem; border-radius: var(--qr-radius-md); background: var(--qr-bad-bg); color: var(--qr-bad-text); font-size: var(--qr-fs-xs); }

/* Pie del menú por niveles: botón Apariencia + versión */
.qr-nl-foot { flex-shrink: 0; display: flex; flex-direction: column; gap: 0.25rem; padding: 0.5rem 0.625rem 0.6rem; border-top: 1px solid var(--qr-sidebar-border); background: rgba(var(--qr-shade-rgb), var(--qr-alpha-20)); }
.qr-nl-appearance-btn {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    min-height: var(--qr-control-height-md);
    padding: 0 0.625rem;
    border: 0;
    border-radius: var(--qr-radius-md);
    background: transparent;
    color: var(--qr-sidebar-text);
    font-size: var(--qr-fs-sm);
    cursor: pointer;
}
.qr-nl-appearance-btn:hover,
.qr-nl-appearance-btn.is-open { background: var(--qr-sidebar-hover-bg); color: var(--qr-sidebar-text-hover); }
.qr-nl-appearance-name { flex: 1; text-align: left; }
.qr-nl-appearance-mode { font-size: var(--qr-fs-xs); color: var(--qr-sidebar-title); }
.qr-nl-version { padding: 0 0.625rem; font-size: var(--qr-fs-xs); color: var(--qr-sidebar-title); letter-spacing: 0.03em; }
.qr-nl-version .bi { opacity: 0.6; }

/* Scrollbar del sidebar */
.sidebar .sidebar-nav::-webkit-scrollbar {
    width: 4px;
}
.sidebar .sidebar-nav::-webkit-scrollbar-track {
    background: transparent;
}
.sidebar .sidebar-nav::-webkit-scrollbar-thumb {
    background: rgba(var(--qr-highlight-rgb), var(--qr-alpha-10));
    border-radius: var(--qr-radius-2xs);
}

/* --------------------------------------------------------------------------
   Header — override CoreUI 5 + estilos propios
   -------------------------------------------------------------------------- */
.qr-header {
    --cui-header-bg:          var(--qr-header-bg);
    --cui-header-border-color: var(--qr-header-border);
    background: var(--qr-header-bg) !important;
    /* sombra inferior que separa el header del contenido */
    box-shadow: 0 2px 10px rgba(var(--qr-shade-rgb), var(--qr-alpha-8)), 0 1px 0 var(--qr-header-border);
    border-bottom: none !important;
}

.qr-header-top {
    height: var(--qr-header-height);
    min-height: var(--qr-header-height);
    /* Separador entre la barra principal y el breadcrumb — borde completo sin padding */
    border-bottom: 1px solid var(--qr-header-border);
}

/* Botón hamburguesa */
.qr-sidebar-toggler {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    padding: 0;
    background: transparent;
    border: none;
    border-radius: var(--qr-radius-md);
    color: var(--qr-text-secondary);
    font-size: var(--qr-fs-130);
    cursor: pointer;
    transition: background 0.15s, color 0.15s;
    flex-shrink: 0;
}

.qr-sidebar-toggler:hover {
    background: var(--qr-surface-3);
    color: var(--qr-primary);
}

/* Breadcrumb dentro del header */
.qr-breadcrumb-bar {
    padding-top: 0.2rem;
    padding-bottom: 0.2rem;
    /* alineada con el contenido: mismo margen lateral que la página */
    padding-inline: var(--qr-page-gutter-x);
    /* sin border-top propio — la separación la hace el border-bottom de .qr-header-top */
}

.qr-breadcrumb-bar .breadcrumb {
    margin-bottom: 0;
    font-size: var(--qr-fs-sm);
}

.qr-breadcrumb-bar .breadcrumb-item a {
    color: var(--qr-primary);
    text-decoration: none;
}

.qr-breadcrumb-bar .breadcrumb-item.active {
    color: var(--qr-text-secondary);
}

/* Menú de usuario (derecha del header) */
.qr-user-btn {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    color: var(--qr-text-secondary) !important;
    text-decoration: none !important;
    /* Fondo propio: sin él el navegador pone su gris de botón, que en oscuro quedaba claro. */
    background: var(--qr-surface-3);
    border: 1px solid transparent;
    border-radius: var(--qr-radius-md);
    padding: 0.3rem 0.6rem;
    font-size: var(--qr-fs-md);
    transition: background 0.15s, border-color 0.15s;
}

.qr-user-btn::after {
    /* quita la flecha por defecto del dropdown-toggle */
    display: none !important;
}

.qr-user-btn:hover,
.qr-user-btn.show {
    background: var(--qr-surface-3) !important;
    border-color: var(--qr-header-border) !important;
    color: var(--qr-text) !important;
}

.qr-user-btn .bi-person-circle {
    font-size: var(--qr-fs-110);
    color: var(--qr-primary);
}

/* Dropdown del usuario */
.qr-user-dropdown {
    min-width: 200px;
    border: 1px solid var(--qr-header-border) !important;
    border-radius: var(--qr-radius-md-plus) !important;
    box-shadow: var(--qr-shadow-popover) !important;
    padding: 0.35rem 0 !important;
    margin-top: 6px !important;
}

.qr-user-dropdown .dropdown-item {
    font-size: var(--qr-fs-md);
    padding: 0.5rem 1rem;
    color: var(--qr-text);
    display: flex;
    align-items: center;
    gap: 0.5rem;
    border-radius: var(--qr-radius-sm-plus);
    margin: 1px 4px;
    transition: background 0.12s;
}

.qr-user-dropdown .dropdown-item:hover {
    background: var(--qr-surface-3);
    color: var(--qr-primary);
}

.qr-user-dropdown .dropdown-item .bi {
    font-size: var(--qr-fs-90);
    color: var(--qr-text-muted);
    flex-shrink: 0;
}

.qr-user-dropdown .dropdown-item:hover .bi {
    color: var(--qr-primary);
}

.qr-user-dropdown .dropdown-item.text-danger,
.qr-user-dropdown .dropdown-item.text-danger:hover {
    color: var(--qr-danger) !important;
}

.qr-user-dropdown .dropdown-item.text-danger .bi,
.qr-user-dropdown .dropdown-item.text-danger:hover .bi {
    color: var(--qr-danger) !important;
}

.qr-user-dropdown .dropdown-divider {
    margin: 0.3rem 0;
    border-color: var(--qr-header-border);
}

/* --------------------------------------------------------------------------
   Área de contenido
   -------------------------------------------------------------------------- */
.qr-content-area {
    background: var(--qr-body-bg);
    overflow-y: auto;
    overflow-x: hidden;
    /* Margen de página: sale SÓLO de acá (tokens --qr-page-gutter-*). Las pantallas no
       ponen padding propio en su contenedor raíz. */
    padding: var(--qr-page-gutter-top) var(--qr-page-gutter-x) var(--qr-page-gutter-bottom);
}

/* Área de contenido del layout de reportes (layout_reportes_coreui): mismo margen. */
.report-body {
    flex: 1 1 0;
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    background: var(--qr-body-bg);
    padding: var(--qr-page-gutter-top) var(--qr-page-gutter-x) var(--qr-page-gutter-bottom);
}

/* Punto de montaje de Svelte dentro del área de contenido. consoltic.css lo fija en
   height:100%: así el contenido largo DESBORDA a #app y el padding-bottom del área queda
   tapado (la pantalla termina pegada al borde del navegador). Acá crece con su contenido y
   mide, como mínimo, el alto del área. */
.qr-content-area > #app,
.report-body > #app {
    height: auto;
    min-height: 100%;
}

/* Excepción: pantallas que LLENAN el área y scrollean por dentro (raíz con height:100%).
   Necesitan un #app de alto fijo para que su 100% resuelva. Una pantalla nueva así marca su
   raíz con .qr-page-fill; .h-100, .qr-tia (Probar IA) y .cf-shell (diseñador de formularios)
   son las existentes que todavía no lo tienen. */
.qr-content-area > #app:has(> .qr-page-fill, > .h-100, > .qr-tia, > .cf-shell),
.report-body > #app:has(> .qr-page-fill, > .h-100, > .qr-tia, > .cf-shell) {
    height: 100%;
}

/* El .container-fluid raíz de una pantalla (del template o del App.svelte) ya no suma su
   0.75rem lateral: el margen lo pone el área. Dentro de tarjetas y modales sigue igual. */
.qr-content-area .container-fluid:not(.card .container-fluid, .modal .container-fluid, .offcanvas .container-fluid),
.report-body .container-fluid:not(.card .container-fluid, .modal .container-fluid, .offcanvas .container-fluid) {
    padding-inline: 0;
}

/* Margen lateral de página para bandas a lo ancho (layout_fullscreen, barras de diseñadores). */
.qr-px-gutter {
    padding-left: var(--qr-page-gutter-x) !important;
    padding-right: var(--qr-page-gutter-x) !important;
}

/* --------------------------------------------------------------------------
   Focus rings — override Bootstrap 5 para reducir la invasividad visual
   El azul violáceo de Bootstrap por defecto es muy llamativo; usamos uno
   mucho más sutil: borde gris tenue + sombra casi transparente.
   -------------------------------------------------------------------------- */
/* Tokens: static/css/qr_tokens.css */

.form-control:focus,
.form-select:focus,
.form-check-input:focus,
.btn:focus-visible,
.btn-close:focus {
    border-color: var(--qr-focus-border) !important;
    box-shadow: var(--qr-focus-ring) !important;
    outline: none !important;
}

/* Inputs de búsqueda, textarea, etc. */
textarea:focus,
input[type="text"]:focus,
input[type="number"]:focus,
input[type="search"]:focus,
input[type="email"]:focus {
    border-color: var(--qr-focus-border) !important;
    box-shadow: var(--qr-focus-ring) !important;
    outline: none !important;
}

/* Navegación y links al hacer focus con teclado (accesibilidad) */
a:focus-visible {
    outline: 2px solid rgba(var(--qr-primary-rgb), var(--qr-alpha-35));
    outline-offset: 2px;
    border-radius: var(--qr-radius-xs);
}

/* --------------------------------------------------------------------------
   Form controls — lo que NO cubre el bloque de tokens de arriba

   ACÁ VIVÍA UN SEGUNDO SISTEMA DE FORMULARIOS COMPLETO, de una etapa anterior:
   su propio token `--qr-input-radius: 4px`, radios 3px/6px a mano, borde #d9dee3
   y su propia escala de tamaños. Estaba ~500 líneas DESPUÉS del bloque de
   `--qr-field-*`, así que ganaba por orden y el sistema de tokens no aplicaba
   nunca: los inputs salían a 3px de radio al lado de un CSelect de 8px, que es
   exactamente el desalineo que se veía en pantalla.

   Se borró. La única fuente de verdad de la forma de un campo son los tokens
   `--qr-field-*` de arriba. Si hace falta cambiar el radio o el borde de los
   inputs, se cambia el token, no se agrega un bloque nuevo acá abajo.
   -------------------------------------------------------------------------- */
.form-control,
.form-select { line-height: 1.4; }

/* textarea: misma escala que el resto pero con respiro vertical propio, porque
   su alto lo da el contenido y no el token de altura de campo. */
textarea.form-control { padding-top: var(--qr-control-padding-y); padding-bottom: var(--qr-control-padding-y); }

/* --------------------------------------------------------------------------
   Utilidades globales de color
   -------------------------------------------------------------------------- */
.text-qr-primary   { color: var(--qr-primary) !important; }
.bg-qr-primary     { background-color: var(--qr-primary) !important; }
.btn-qr-primary {
    background-color: var(--qr-primary);
    border-color: var(--qr-primary);
    color: var(--qr-text-inverse);
}
.btn-qr-primary:hover {
    background-color: var(--qr-primary-dark);
    border-color: var(--qr-primary-dark);
    color: var(--qr-text-inverse);
}

/* Helpers de color por tono (bg-*, text-*, border-*): viven en static/css/qr_tone_utilities.css,
   que los layouts cargan después de qr_tokens.css. */

/* --------------------------------------------------------------------------
   Utilidades de tamaño de texto y velo (reemplazan los style="font-size: …" y
   style="background: rgba(0,0,0,.5)" fijos de las pantallas Svelte). Llevan
   !important porque ocupan el lugar de un style inline, que le ganaba a todo.
   Una por paso de la escala t-shirt: no crear tamaños intermedios.
   -------------------------------------------------------------------------- */
.qr-fs-3xs     { font-size: var(--qr-fs-3xs) !important; }
.qr-fs-2xs     { font-size: var(--qr-fs-2xs) !important; }
.qr-fs-xs      { font-size: var(--qr-fs-xs) !important; }
.qr-fs-sm      { font-size: var(--qr-fs-sm) !important; }
.qr-fs-md      { font-size: var(--qr-fs-md) !important; }
.qr-fs-lg      { font-size: var(--qr-fs-lg) !important; }
.qr-fs-base    { font-size: var(--qr-fs-base) !important; }
.qr-fs-xl      { font-size: var(--qr-fs-xl) !important; }
.qr-fs-2xl     { font-size: var(--qr-fs-2xl) !important; }
.qr-fs-3xl     { font-size: var(--qr-fs-3xl) !important; }
.qr-fs-4xl     { font-size: var(--qr-fs-4xl) !important; }
.qr-fs-display { font-size: var(--qr-fs-display) !important; }
.qr-font-mono  { font-family: var(--qr-font-mono) !important; }
/* Velo detrás de un panel propio (overlays que no son Modal.svelte). */
.qr-veil       { background: var(--qr-backdrop) !important; }

/* --------------------------------------------------------------------------
   Tablas de listados de datos — estándar QueueReport
   Uso: <table class="table table-hover qr-table"> ... </table>
   Estandariza alto de fila, padding, tipografía y header en todos los listados.
   -------------------------------------------------------------------------- */
.qr-table {
    font-size: var(--qr-table-font-size);
    margin-bottom: 0;
}

/* Header. Los títulos van centrados por defecto; usar `text-start`/`text-end`
   en el <th> para alinear distinto. */
.qr-table > thead > tr > th {
    background-color: var(--qr-table-header-bg);
    color: var(--qr-table-header-color);
    font-weight: var(--qr-table-header-font-weight);
    font-size: var(--qr-table-header-font-size);
    padding: var(--qr-table-header-padding-y) var(--qr-table-cell-padding-x);
    text-align: center;
    border-bottom: var(--qr-table-header-border-width) solid var(--qr-border);
    vertical-align: middle;
    white-space: nowrap;
}

/* Celdas del cuerpo: alto cómodo y consistente.
   Por defecto el contenido va centrado; para alinear a la izquierda/derecha
   usar las utilidades de Bootstrap `text-start` / `text-end` en el <td>. */
.qr-table > tbody > tr > td {
    padding: var(--qr-table-cell-padding-y) var(--qr-table-cell-padding-x);
    vertical-align: middle;
    text-align: center;
    border-top: var(--qr-border-width) solid var(--qr-border-soft);
}

.qr-table > tbody > tr {
    height: var(--qr-table-row-height);
}

/* Las `.table` sin `.qr-table`: CoreUI escribe el padding de celda con literal
   (.5rem / .25rem en `-sm`), así que se pisa acá para que siga a los tokens. */
.table > :not(caption) > * > * { padding: var(--qr-table-plain-cell-padding); }
.table-sm > :not(caption) > * > * { padding: var(--qr-table-sm-cell-padding); }

.qr-table > tbody > tr:hover {
    background-color: var(--qr-surface-2);
}

/* Texto largo en TODAS las tablas, sin marcar celda por celda: toda celda del cuerpo de una
   tabla (`.table`, que incluye `.qr-table`, y `.qr-table-report`) llega como mucho a
   --qr-table-text-cell-max y lo que sobra se corta con "…" en una línea; el valor entero se ve
   al pasar el mouse (static/js/qr_table_hover.js le pone el `title`). Antes cada pantalla tenía
   que acordarse de `qr-ellipsis`, y una descripción larga estiraba la columna y le daba scroll
   horizontal a la página. Es el estándar de siempre ("el texto largo se corta, nunca envuelve"):
   las filas quedan parejas.
   - Cada `div` de la celda (el nombre arriba, la descripción abajo) se corta por separado.
   - No se toca la celda con controles (campos, selectores, menús, acciones): recortarla
     taparía el desplegable.
   - Escape: `qr-wrap` en el <td> para la celda que tiene que envolver a propósito.
   (Se midió la alternativa de dejar envolver con `overflow-wrap: anywhere`: aprieta columnas,
   parte palabras por la mitad y las filas crecen hasta un 60 %.) */
:is(.table, .qr-table-report) > tbody > tr > td:not(.qr-wrap):not(:has(input, select, textarea, .dropdown, .qr-actions, .btn-group, .form-check)) {
    max-width: var(--qr-table-text-cell-max);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
:is(.table, .qr-table-report) > tbody > tr > td:not(.qr-wrap):not(:has(input, select, textarea, .dropdown, .qr-actions, .btn-group, .form-check)) > div:not([class*="d-flex"]):not([class*="d-inline-flex"]) {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Una etiqueta con texto largo (el nombre de un horario, de una lista) también se corta. */
:is(.table, .qr-table-report) > tbody > tr > td:not(.qr-wrap) .badge {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    vertical-align: middle;
}

/* =========================================================================
   Acciones de fila estándar (qr-actions)
   Grupo de iconos de acción (ver/editar/eliminar/…). Centrado y compacto.
   Los botones no llevan padding/margin propios para no alterar el alto de
   la fila; el espaciado lo da el `gap`. El tamaño del icono es fijo, así que
   NO hace falta poner `fs-6` ni tamaños inline en cada <i>.
   Markup: <div class="qr-actions"> <button class="btn btn-xs">…</button> </div>
   ========================================================================= */
.qr-actions {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
}
.qr-actions .btn {
    padding: 0;
    margin: 0;
    border: 0;
    line-height: 1;
    background: none;
    box-shadow: none;
}
.qr-actions .btn > i,
.qr-actions .btn > .bi {
    font-size: var(--qr-icon-size-md);
    line-height: 1;
    vertical-align: middle;
}

/* =========================================================================
   Tabs estándar (qr-tabs)
   Pestañas de secciones en páginas de edición. Basado en .nav-underline.
   Uso: <ul class="nav nav-underline qr-tabs"> ... </ul>
   El contenido de cada pestaña se envuelve en .qr-tab-pane para la animación.
   ========================================================================= */
.qr-tabs {
    --qr-tab-color: var(--cui-primary);
    flex-wrap: nowrap;
    gap: 0.25rem;
    border-bottom: 0;
    overflow-x: auto;          /* si no entran, scrollean en horizontal */
    overflow-y: hidden;
    scrollbar-width: thin;
}

/* Sub-tabs (segundo nivel) — estilo pill compacto, también con scroll */
.qr-subtabs {
    display: flex;
    flex-wrap: nowrap;
    gap: 0.55rem;
    overflow-x: auto;
    overflow-y: hidden;
    scrollbar-width: thin;
    margin: 0;
    padding: 0;
    list-style: none;
}
.qr-subtabs .nav-link {
    color: var(--qr-text-secondary);
    font-size: var(--qr-fs-sm);
    font-weight: 500;
    padding: var(--qr-subtab-padding-y) var(--qr-subtab-padding-x);
    border: var(--qr-border-width) solid transparent;
    border-radius: var(--qr-subtab-radius);
    background: none;
    white-space: nowrap;
    transition: background-color 0.12s ease, color 0.12s ease;
}
.qr-subtabs .nav-link:hover {
    background: var(--qr-surface-sunken);
    color: var(--qr-primary-hover);
}
.qr-subtabs .nav-link.active {
    background: var(--qr-primary-subtle);
    color: var(--qr-primary-hover);
    border-color: var(--qr-primary-border-subtle);
}

/* Modo enfoque (editor de chatbot y otros): colapsa el layout a solo el contenido —
   oculta sidebar + cabecera del layout y da todo el ancho/alto al área de trabajo.
   Se activa agregando la clase .qr-focus-mode al <body> (y se quita al salir). */
body.qr-focus-mode.app-grid { grid-template-columns: 1fr; }
body.qr-focus-mode #sidebar { display: none; }
body.qr-focus-mode .qr-header { display: none; }
/* El header del editor ya no se esconde por CSS: la pantalla no lo renderiza en enfoque
   (`{#if !focus_mode}`), porque un header oculto con display:none igual ocupa su lugar en el
   flujo de datos y obligaba a mantener dos formas de "no está". La regla vieja
   (`.qr-editor-titlebar { display: none }`) se eliminó junto con esa clase.

   Barra de enfoque: sin header, el nombre del bot y las dos acciones viven pegados a la banda
   de tabs. Sin esto, salir del modo enfoque obligaría a recargar la página. */
.qr-editor-focusbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    padding: .4rem .75rem;
    border-bottom: 1px solid var(--qr-header-border);
    background: var(--qr-surface-2);
}
.qr-editor-focusbar__name {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    font-size: var(--qr-fs-82);
    font-weight: 600;
    color: var(--qr-text-strong);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.qr-editor-focusbar__name .bi { color: var(--qr-primary); }

/* Área de trabajo del editor visual de bots: panel de bloques + lienzo. No lleva card-body
   porque es un lienzo, no un formulario — el padding de 1rem le robaba ancho a los dos lados
   sin dar nada a cambio. El alto lo fija la acción `fillHeight` de la pantalla. */
.qr-editor-workspace {
    display: flex;
    gap: .75rem;
    min-height: 70vh;
    padding: .75rem;
}
/* El lienzo era un `card` dentro de otro `card`: dos sombras y dos redondeos anidados. */
.qr-editor-canvas {
    border: 1px solid var(--qr-header-border);
    border-radius: var(--qr-radius-md);
    background: var(--qr-surface);
}
.qr-tabs .nav-link {
    color: var(--qr-text-secondary);
    font-size: var(--qr-tab-font-size);
    font-weight: 600;
    padding: var(--qr-tab-padding-y) var(--qr-tab-padding-x);
    border: 0;
    border-bottom: var(--qr-tab-indicator-width) solid transparent;
    border-radius: 0;
    background: none;
    white-space: nowrap;
    transition: color 0.15s ease, border-color 0.15s ease, background-color 0.15s ease;
}
.qr-tabs .nav-link:hover {
    color: var(--qr-tab-color);
    background-color: var(--qr-surface-2);
}
.qr-tabs .nav-link.active {
    color: var(--qr-tab-color);
    border-bottom-color: var(--qr-tab-color);
    background: none;
}
.qr-tabs .nav-link .bi {
    opacity: 0.85;
}

/* Animación de entrada al cambiar de tab: fade + leve slide hacia arriba */
.qr-tab-pane {
    animation: qrTabFade 0.22s ease;
}
@keyframes qrTabFade {
    from {
        opacity: 0;
        transform: translateY(6px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}
@media (prefers-reduced-motion: reduce) {
    .qr-tab-pane {
        animation: none;
    }
}

/* =========================================================================
   Botones — densidad y forma alineadas a los inputs

   Mismo radio que un campo (`--qr-field-radius`): un botón al lado de un input
   en la misma fila tiene que tener la misma esquina. Antes usaban el token
   viejo `--qr-input-radius` (4px) y `.btn-sm` un 3px a mano, así que en toda
   toolbar el botón quedaba más cuadrado que el select de al lado.
   ========================================================================= */
.btn {
    border-radius: var(--qr-btn-radius);
    font-size: var(--qr-btn-font-size-md);
    font-weight: var(--qr-btn-font-weight);
    padding: var(--qr-btn-padding-y-md) var(--qr-btn-padding-x-md);
    min-height: var(--qr-btn-height-md);
    /* Icono + texto: ver el bloque "Anchor-botón e iconos" más abajo para el porqué. */
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--qr-btn-gap);
}
/* El margen horizontal de un icono DENTRO de un botón lo pone el `gap`. Se anula el que
   venga escrito (`me-1`, `ms-2`) para que el espaciado no dependa de si el autor se acordó. */
.btn > .bi { margin-left: 0 !important; margin-right: 0 !important; }
.btn-sm {
    font-size: var(--qr-btn-font-size-sm);
    padding: var(--qr-btn-padding-y-sm) var(--qr-btn-padding-x-sm);
    border-radius: var(--qr-btn-radius);
    /* Se repite el alto de arriba (bloque de formularios): el `min-height` de `.btn` de
       este bloque lo pisaría por orden. */
    min-height: var(--qr-btn-height-sm);
}
.btn-lg {
    font-size: var(--qr-btn-font-size-lg);
    padding: var(--qr-btn-padding-y-lg) var(--qr-btn-padding-x-lg);
    border-radius: var(--qr-btn-radius);
    min-height: var(--qr-btn-height-lg);
}
/* btn-xs: botón compacto para acciones de fila / toolbars densos.
   (No existe en Bootstrap; lo definimos acá.) */
.btn-xs {
    font-size: var(--qr-btn-font-size-xs);
    padding: var(--qr-btn-padding-y-xs) var(--qr-btn-padding-x-xs);
    border-radius: var(--qr-btn-radius-xs);
    line-height: 1.2;
    min-height: var(--qr-btn-height-xs);
}

/* -------------------------------------------------------------------------
   Variantes de color sólidas — tonos suaves de nuestra paleta + texto claro.
   Un fondo de color fuerte (verde/azul/rojo…) SIEMPRE lleva texto blanco;
   solo `warning` (ámbar claro) usa texto oscuro por contraste.
   Escala: 500 base · 600 hover · 700 active.
   ------------------------------------------------------------------------- */
.btn-primary {
    background-color: var(--qr-primary-light); border-color: var(--qr-primary-light); color: var(--qr-text-inverse);
}
.btn-primary:hover, .btn-primary:focus {
    background-color: var(--qr-primary); border-color: var(--qr-primary); color: var(--qr-text-inverse);
}
.btn-primary:active, .btn-primary.active, .btn-check:checked + .btn-primary {
    background-color: var(--qr-primary-hover); border-color: var(--qr-primary-hover); color: var(--qr-text-inverse);
}

.btn-secondary {
    background-color: var(--qr-neutral-solid); border-color: var(--qr-neutral-solid); color: var(--qr-text-inverse);
}
.btn-secondary:hover, .btn-secondary:focus {
    background-color: var(--qr-neutral-solid-hover); border-color: var(--qr-neutral-solid-hover); color: var(--qr-text-inverse);
}
.btn-secondary:active, .btn-secondary.active {
    background-color: var(--qr-neutral-solid-active); border-color: var(--qr-neutral-solid-active); color: var(--qr-text-inverse);
}

.btn-success {
    background-color: var(--qr-success); border-color: var(--qr-success); color: var(--qr-text-inverse);
}
.btn-success:hover, .btn-success:focus {
    background-color: var(--qr-success-solid-hover); border-color: var(--qr-success-solid-hover); color: var(--qr-text-inverse);
}
.btn-success:active, .btn-success.active {
    background-color: var(--qr-success-solid-active); border-color: var(--qr-success-solid-active); color: var(--qr-text-inverse);
}

.btn-danger {
    background-color: var(--qr-danger); border-color: var(--qr-danger); color: var(--qr-text-inverse);
}
.btn-danger:hover, .btn-danger:focus {
    background-color: var(--qr-danger-600); border-color: var(--qr-danger-600); color: var(--qr-text-inverse);
}
.btn-danger:active, .btn-danger.active {
    background-color: var(--qr-danger-700); border-color: var(--qr-danger-700); color: var(--qr-text-inverse);
}

.btn-info {
    background-color: var(--qr-info-solid); border-color: var(--qr-info-solid); color: var(--qr-text-inverse);
}
.btn-info:hover, .btn-info:focus {
    background-color: var(--qr-info-solid-hover); border-color: var(--qr-info-solid-hover); color: var(--qr-text-inverse);
}
.btn-info:active, .btn-info.active {
    background-color: var(--qr-info-solid-active); border-color: var(--qr-info-solid-active); color: var(--qr-text-inverse);
}

.btn-warning {
    background-color: var(--qr-warning-400); border-color: var(--qr-warning-400); color: var(--qr-on-warning);
}
.btn-warning:hover, .btn-warning:focus {
    background-color: var(--qr-warning); border-color: var(--qr-warning); color: var(--qr-on-warning);
}
.btn-warning:active, .btn-warning.active {
    background-color: var(--qr-warning-600); border-color: var(--qr-warning-600); color: var(--qr-on-warning);
}

/* Un `<a class="btn">` es **inline-block**, así que dentro de un flex alinea por
   BASELINE y su contenido queda unos píxeles arriba del centro — se nota apenas
   lleva un icono al lado del texto. `.qr-lh__create` y `.qr-lh__back` lo arreglaban
   cada uno por su lado; la causa es del anchor, no del header, así que va acá y vale
   para todos.

   Y NO era sólo del anchor: CoreUI define `.btn` como `display: inline-block` (verificado
   en coreui.min.css), así que un `<button class="btn"><i class="bi bi-plus"></i>Texto</button>
   tiene el mismo problema —icono alineado por baseline, unos píxeles arriba del centro— y
   además el icono queda PEGADO al texto, porque un inline-block no tiene `gap`.

   Se arregla para TODOS los botones de una vez: inline-flex + gap. El espaciado icono↔texto
   deja de depender de que cada quien se acuerde de escribir `me-1`, que es lo que hacía que
   el mismo botón se viera distinto en dos pantallas. Por eso, además, los márgenes
   horizontales de un icono DENTRO de un botón se anulan: si el autor los escribió igual, el
   gap ya puso el aire y se sumarían.

   Un botón de sólo icono tiene un único hijo, así que el gap no lo afecta.

   Las declaraciones viven ARRIBA, en el bloque `.btn` que ya existía: dos bloques de la
   misma clase en el mismo archivo es la trampa nº1 del sistema de diseño (gana el de más
   abajo y el otro desaparece en silencio). */

/* -------------------------------------------------------------------------
   Variantes outline — borde + texto en tono legible (600) sobre fondo claro;
   al hacer hover/active se rellena con el color y el texto pasa a claro,
   igual que las variantes sólidas. (warning usa texto oscuro al rellenar.)
   ------------------------------------------------------------------------- */
.btn-outline-primary {
    color: var(--qr-primary); border-color: var(--qr-primary); background-color: transparent;
}
.btn-outline-primary:hover, .btn-outline-primary:focus,
.btn-outline-primary:active, .btn-outline-primary.active,
.btn-check:checked + .btn-outline-primary {
    background-color: var(--qr-primary); border-color: var(--qr-primary); color: var(--qr-text-inverse);
}

.btn-outline-secondary {
    color: var(--qr-text-body); border-color: var(--qr-border-emphasis); background-color: transparent;
}
.btn-outline-secondary:hover, .btn-outline-secondary:focus,
.btn-outline-secondary:active, .btn-outline-secondary.active {
    background-color: var(--qr-neutral-solid-hover); border-color: var(--qr-neutral-solid-hover); color: var(--qr-text-inverse);
}

.btn-outline-success {
    color: var(--qr-success-solid-hover); border-color: var(--qr-success-solid-hover); background-color: transparent;
}
.btn-outline-success:hover, .btn-outline-success:focus,
.btn-outline-success:active, .btn-outline-success.active {
    background-color: var(--qr-success-solid-hover); border-color: var(--qr-success-solid-hover); color: var(--qr-text-inverse);
}

.btn-outline-danger {
    color: var(--qr-danger-600); border-color: var(--qr-danger-600); background-color: transparent;
}
.btn-outline-danger:hover, .btn-outline-danger:focus,
.btn-outline-danger:active, .btn-outline-danger.active {
    background-color: var(--qr-danger-600); border-color: var(--qr-danger-600); color: var(--qr-text-inverse);
}

.btn-outline-info {
    color: var(--qr-info-solid-hover); border-color: var(--qr-info-solid-hover); background-color: transparent;
}
.btn-outline-info:hover, .btn-outline-info:focus,
.btn-outline-info:active, .btn-outline-info.active {
    background-color: var(--qr-info-solid-hover); border-color: var(--qr-info-solid-hover); color: var(--qr-text-inverse);
}

.btn-outline-warning {
    color: var(--qr-warning-700); border-color: var(--qr-warning-600); background-color: transparent;
}
.btn-outline-warning:hover, .btn-outline-warning:focus,
.btn-outline-warning:active, .btn-outline-warning.active {
    background-color: var(--qr-warning-400); border-color: var(--qr-warning-400); color: var(--qr-text-emphasis);
}

/* ──────────────────────────────────────────────────────────────────────────
   Wizard stepper (pasos visuales)
   Usado por los wizards de creación multipaso (campañas, grupos de atención…).
   Markup esperado: ver "Wizards multipaso" en CLAUDE.md (raíz).
   ────────────────────────────────────────────────────────────────────────── */
.qr-stepper {
    display: flex;
    align-items: center;
    gap: 0;
    overflow-x: auto;
}
.qr-step {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.35rem;
    background: none;
    border: 0;
    padding: 0 0.25rem;
    min-width: 84px;
    color: var(--qr-text-muted);
    cursor: pointer;
}
.qr-step:disabled { cursor: default; }
.qr-step-bullet {
    width: 38px;
    height: 38px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--qr-surface-sunken);
    color: var(--qr-text-muted);
    font-size: var(--qr-fs-base);
    border: 2px solid transparent;
    transition: all 0.2s ease;
}
.qr-step-label { font-size: var(--qr-fs-74); font-weight: 600; white-space: nowrap; }
.qr-step.active .qr-step-bullet {
    background: var(--qr-primary-light);
    color: var(--qr-text-inverse);
    border-color: var(--qr-primary-border-subtle);
    box-shadow: 0 0 0 4px rgba(var(--qr-primary-light-rgb), var(--qr-alpha-15));
}
.qr-step.active .qr-step-label { color: var(--qr-primary-ink); }
.qr-step.done .qr-step-bullet { background: var(--qr-primary-subtle); color: var(--qr-primary-hover); }
.qr-step.done .qr-step-label { color: var(--qr-text-body); }
.qr-step.has-error .qr-step-bullet {
    background: var(--qr-danger-100);
    color: var(--qr-danger-600);
    border-color: var(--qr-danger-200);
    box-shadow: 0 0 0 4px rgba(var(--qr-danger-600-rgb), var(--qr-alpha-12));
}
.qr-step.has-error .qr-step-label { color: var(--qr-danger-600); }
.qr-step-line {
    flex: 1 1 auto;
    height: 2px;
    min-width: 16px;
    background: var(--qr-surface-strong);
    transition: background 0.2s ease;
}
.qr-step-line.done { background: var(--qr-primary-300); }
.qr-step-viewport { position: relative; overflow: hidden; }
.qr-step-pane { min-height: 220px; }
/* En pantallas angostas los nombres no entran: se ven sólo los círculos, y el nombre únicamente
   en el paso actual (con 4+ pasos, el último quedaba cortado contra el borde). */
@media (max-width: 575.98px) {
    .qr-step { min-width: 0; flex: 0 0 auto; }
    .qr-step:not(.active) .qr-step-label { display: none; }
}

/* =========================================================================
   Dashboards y reportería — componentes reutilizables
   Set estándar para construir paneles de métricas en TODOS los reportes:
   barra de filtros, cabecera de panel, KPIs con acento, mini-stats y cards.
   Todo basado en los tokens :root (--qr-*). Aplican en runtime (no Vite).

   Paleta de acento por tono: agregar el modificador (.qr-kpi--success, etc.)
   o setear --qr-accent / --qr-accent-soft en el elemento.
   ========================================================================= */

/* Contenedor de página de dashboard: ancho fluido + respiro inferior */
.qr-dashboard { padding-bottom: 1.5rem; }

/* --- Barra de filtros / toolbar superior ------------------------------- */
.qr-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: 1rem;
    background: var(--qr-surface);
    border: 1px solid var(--qr-border-soft);
    border-radius: var(--qr-radius-lg);
    box-shadow: var(--qr-shadow-xs);
    padding: 0.85rem 1rem;
    margin-bottom: 1rem;
}
.qr-toolbar-label {
    display: block;
    font-size: var(--qr-fs-66);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    color: var(--qr-text-muted);
    margin-bottom: 0.3rem;
}
.qr-toolbar-group { display: flex; flex-direction: column; }
.qr-toolbar-group--cta { margin-left: auto; justify-content: flex-end; }
.qr-toolbar-group--cta .btn { align-self: flex-end; }

/* --- Cabecera de página de reporte ------------------------------------- */
.qr-page-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1rem;
}
.qr-page-head__title { display: flex; align-items: center; gap: 0.8rem; }
.qr-page-head__icon {
    flex: 0 0 auto;
    width: 42px; height: 42px;
    border-radius: var(--qr-radius-lg);
    display: inline-flex; align-items: center; justify-content: center;
    font-size: var(--qr-fs-xl);
    color: var(--qr-text-inverse);
    background: linear-gradient(135deg, var(--qr-primary-light), var(--qr-primary));
    box-shadow: 0 6px 16px -4px rgba(var(--qr-primary-rgb), var(--qr-alpha-50));
}
.qr-page-head__h1 {
    font-size: var(--qr-fs-115);
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--qr-text);
    margin: 0;
    line-height: 1.1;
}
.qr-page-head__sub { font-size: var(--qr-fs-78); color: var(--qr-text-muted); margin-top: 0.1rem; }
.qr-page-head__live {
    display: inline-flex; align-items: center; gap: 0.45rem;
    font-size: var(--qr-fs-74); font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--qr-success-700);
    background: var(--qr-success-100);
    padding: 0.3rem 0.7rem; border-radius: var(--qr-radius-pill);
}
.qr-live-dot {
    width: 8px; height: 8px; border-radius: 50%;
    background: var(--qr-success-500);
    box-shadow: 0 0 0 0 rgba(var(--qr-success-500-rgb), var(--qr-alpha-60));
    animation: qr-live-pulse 1.8s ease-out infinite;
}
@keyframes qr-live-pulse {
    0%   { box-shadow: 0 0 0 0 rgba(var(--qr-success-500-rgb), var(--qr-alpha-60)); }
    70%  { box-shadow: 0 0 0 7px rgba(var(--qr-success-500-rgb), 0); }
    100% { box-shadow: 0 0 0 0 rgba(var(--qr-success-500-rgb), 0); }
}

/* --- Sección/contenedor que agrupa un bloque del dashboard ------------- */
.qr-section {
    background: var(--qr-surface-2);
    border: 1px solid var(--qr-border-soft);
    border-radius: var(--qr-radius-xl);
    padding: 1rem 1.05rem 1.1rem;
    height: 100%;
}

/* --- Cabecera de panel (segmento Voz / Chat / etc.) -------------------- */
.qr-panel-head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    margin-bottom: 0.75rem;
}
.qr-panel-head__badge {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-size: var(--qr-fs-sm);
    font-weight: 700;
    letter-spacing: 0.02em;
    padding: 0.4rem 0.9rem;
    border-radius: var(--qr-radius-md);
    color: var(--qr-text-inverse);
    background: var(--qr-accent, var(--qr-primary));
    box-shadow: 0 4px 12px -2px var(--qr-accent-soft, rgba(var(--qr-primary-rgb), var(--qr-alpha-40)));
}
.qr-panel-head__badge .bi { font-size: var(--qr-fs-90); opacity: 0.9; }
.qr-panel-head__meta {
    font-size: var(--qr-fs-xs);
    font-weight: 500;
    color: var(--qr-text-muted);
    letter-spacing: 0.01em;
}

/* Etiqueta de sub-sección sobre un grupo de KPIs */
.qr-section-label {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: var(--qr-fs-68);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.07em;
    color: var(--qr-text-muted);
    margin-bottom: 0.55rem;
}
.qr-section-label .bi { color: var(--qr-accent, var(--qr-text-muted)); font-size: var(--qr-fs-sm); }
.qr-section-label::after {
    content: "";
    flex: 1 1 auto;
    height: 1px;
    background: linear-gradient(to right, var(--qr-surface-strong), transparent);
}

/* --- KPI card premium (icono tintado + número grande + delta pill) ----- */
.qr-kpi {
    position: relative;
    height: 100%;
    background: var(--qr-surface);
    border: 1px solid var(--qr-border-soft);
    border-radius: var(--qr-radius-lg-plus);
    padding: 0.9rem 1rem;
    overflow: hidden;
    box-shadow: var(--qr-shadow-xs);
    transition: box-shadow 0.18s ease, transform 0.18s ease, border-color 0.18s ease;
}
/* Spotlight de acento difuso en la esquina superior derecha */
.qr-kpi::after {
    content: "";
    position: absolute;
    top: -55px; right: -45px;
    width: 130px; height: 130px;
    border-radius: 50%;
    background: var(--qr-accent-soft, rgba(var(--qr-primary-rgb), var(--qr-alpha-10)));
    opacity: 0.7;
    pointer-events: none;
}
.qr-kpi:hover {
    box-shadow: 0 10px 28px -8px rgba(var(--qr-shadow-rgb), var(--qr-alpha-20));
    transform: translateY(-2px);
    border-color: var(--qr-accent, var(--qr-primary));
}
.qr-kpi__icon {
    position: absolute;
    top: 0.8rem; right: 0.85rem;
    width: 32px; height: 32px;
    border-radius: var(--qr-radius-md);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: var(--qr-fs-95);
    color: var(--qr-accent, var(--qr-primary));
    background: var(--qr-accent-soft, rgba(var(--qr-primary-rgb), var(--qr-alpha-12)));
    z-index: 1;
}
.qr-kpi__label {
    position: relative;
    z-index: 1;
    font-size: var(--qr-fs-66);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--qr-text-muted);
    line-height: 1.2;
    padding-right: 2.4rem;
}
.qr-kpi__value {
    position: relative;
    z-index: 1;
    font-size: var(--qr-fs-175);
    font-weight: 800;
    line-height: 1.05;
    color: var(--qr-text);
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.025em;
    margin-top: 0.25rem;
}
.qr-kpi__value--sm { font-size: var(--qr-fs-2xl); }
.qr-kpi__value--accent { color: var(--qr-accent, var(--qr-primary)); }
.qr-kpi__sub {
    position: relative;
    z-index: 1;
    display: inline-flex;
    align-items: center;
    gap: 0.2rem;
    margin-top: 0.4rem;
    font-size: var(--qr-fs-70);
    font-weight: 700;
    color: var(--qr-accent, var(--qr-text-secondary));
    background: var(--qr-accent-soft, rgba(var(--qr-primary-rgb), var(--qr-alpha-10)));
    padding: 0.1rem 0.45rem;
    border-radius: var(--qr-radius-pill);
    font-variant-numeric: tabular-nums;
}

/* --- Mini-stat (tile tintado para métricas live) ----------------------- */
.qr-stat {
    height: 100%;
    background: var(--qr-accent-soft, rgba(var(--qr-primary-rgb), var(--qr-alpha-10)));
    border: 1px solid transparent;
    border-radius: var(--qr-radius-lg);
    padding: 0.6rem 0.5rem;
    text-align: center;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
}
.qr-stat:hover { transform: translateY(-2px); box-shadow: 0 8px 18px -8px var(--qr-accent-soft); }
.qr-stat__value {
    font-size: var(--qr-fs-150);
    font-weight: 800;
    line-height: 1.05;
    color: var(--qr-accent, var(--qr-text));
    font-variant-numeric: tabular-nums;
    letter-spacing: -0.02em;
}
.qr-stat__label {
    font-size: var(--qr-fs-64);
    color: var(--qr-text-secondary);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    margin-top: 0.15rem;
}

/* --- Card de panel de datos (gráfico / tabla) -------------------------- */
.qr-panel {
    background: var(--qr-surface);
    border: var(--qr-card-border-width) solid var(--qr-border-soft);
    border-radius: var(--qr-panel-radius);
    box-shadow: var(--qr-shadow-xs);
    overflow: hidden;
}
/* Sombra de las `.card` de CoreUI desde el token (default `none`, como CoreUI). Con
   `:where` no pesa nada: `shadow-sm` del marcado o cualquier clase del sistema le ganan. */
:where(.card) { box-shadow: var(--qr-card-shadow); }

.qr-panel__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.7rem 1rem;
    border-bottom: 1px solid var(--qr-border-soft);
}
.qr-panel__title {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-size: var(--qr-fs-sm);
    font-weight: 700;
    letter-spacing: 0.01em;
    color: var(--qr-text);
}
.qr-panel__empty {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 1.25rem;
    font-size: var(--qr-fs-82);
    color: var(--qr-text-muted);
}

/* --- Badge soft-pill (reemplaza badges sólidos de Bootstrap en datos) --- */
.qr-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    font-size: var(--qr-badge-font-size);
    font-weight: var(--qr-badge-font-weight);
    padding: var(--qr-badge-padding-y) var(--qr-badge-padding-x);
    border-radius: var(--qr-badge-radius);
    line-height: 1.15;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.qr-badge--primary { color: var(--qr-primary-ink); background: var(--qr-primary-subtle); }
.qr-badge--success { color: var(--qr-success-700); background: var(--qr-success-100); }
.qr-badge--warning { color: var(--qr-warning-700); background: var(--qr-warning-100); }
.qr-badge--danger  { color: var(--qr-danger-700); background: var(--qr-danger-100); }
.qr-badge--info    { color: var(--qr-info-700); background: var(--qr-info-100); }
.qr-badge--teal    { color: var(--qr-cat-teal-ink); background: var(--qr-cat-teal-soft); }
.qr-badge--violet  { color: var(--qr-special-700); background: var(--qr-special-100); }
.qr-badge--neutral { color: var(--qr-text-body); background: var(--qr-surface-sunken); }

/* --- Modificadores de tono (setean el acento del componente) ----------- */
.qr-accent-indigo   { --qr-accent: var(--qr-primary-500); --qr-accent-soft: rgba(var(--qr-primary-light-rgb), var(--qr-alpha-12)); }
.qr-accent-success  { --qr-accent: var(--qr-success-500); --qr-accent-soft: rgba(var(--qr-success-500-rgb), var(--qr-alpha-12)); }
.qr-accent-emerald  { --qr-accent: var(--qr-success); --qr-accent-soft: rgba(var(--qr-success-rgb), var(--qr-alpha-12)); }
.qr-accent-teal     { --qr-accent: var(--qr-cat-teal); --qr-accent-soft: rgba(var(--qr-cat-teal-rgb), var(--qr-alpha-12)); }
.qr-accent-danger   { --qr-accent: var(--qr-danger); --qr-accent-soft: rgba(var(--qr-danger-rgb), var(--qr-alpha-12)); }
.qr-accent-warning  { --qr-accent: var(--qr-warning); --qr-accent-soft: rgba(var(--qr-warning-rgb), var(--qr-alpha-12)); }
.qr-accent-orange   { --qr-accent: var(--qr-caution-500); --qr-accent-soft: rgba(var(--qr-caution-500-rgb), var(--qr-alpha-12)); }
.qr-accent-info     { --qr-accent: var(--qr-info); --qr-accent-soft: rgba(var(--qr-info-rgb), var(--qr-alpha-12)); }
.qr-accent-violet   { --qr-accent: var(--qr-special-500); --qr-accent-soft: rgba(var(--qr-special-500-rgb), var(--qr-alpha-12)); }

/* ============================================================
   Banda navy de tabs (qr-band) — patrón de reportes/supervisión
   Uso (via componente global frontend/components/NavTabs.svelte):
     <div class="qr-band"><div class="qr-band-tabs">
       <button class="qr-band-tab is-active"><i class="bi ..."></i>Tab <span class="qr-band-tab__n">3</span></button>
     </div></div>
   Regla: SOLO cuando hay 2+ tabs reales; nunca como decoración de
   una sección única. Cambios acá aplican en runtime (sin rebuild).
   ============================================================ */
.qr-band { background: var(--qr-band-bg); padding: .35rem 1rem; display: flex; align-items: center; gap: .5rem; }
.qr-band-tabs { display: flex; gap: .25rem; overflow-x: auto; scrollbar-width: none; }
.qr-band-tab { display: inline-flex; align-items: center; gap: .45rem; border: none; background: transparent;
    color: rgba(var(--qr-highlight-rgb), var(--qr-alpha-80)); font-size: var(--qr-fs-82); font-weight: 600; border-radius: var(--qr-band-tab-radius);
    padding: var(--qr-band-tab-padding-y) var(--qr-band-tab-padding-x); white-space: nowrap; transition: background .12s, color .12s; }
.qr-band-tab i { color: rgba(var(--qr-highlight-rgb), var(--qr-alpha-60)); }
.qr-band-tab:hover:not(.is-active) { background: rgba(var(--qr-highlight-rgb), var(--qr-alpha-10)); color: var(--qr-text-inverse); }
.qr-band-tab.is-active { background: var(--qr-surface); color: var(--qr-band-tab-active-text); }
.qr-band-tab.is-active i { color: var(--qr-band-tab-active-text); }
.qr-band-tab__n { background: rgba(var(--qr-highlight-rgb), var(--qr-alpha-20)); color: var(--qr-text-inverse); font-size: var(--qr-fs-70); font-weight: 700;
    border-radius: var(--qr-radius-xl); padding: .05rem .45rem; }
.qr-band-tab.is-active .qr-band-tab__n { background: var(--qr-band-tab-n-active-bg); color: var(--qr-surface); }
.qr-band-tab__n.is-bad { background: var(--qr-danger); color: var(--qr-text-inverse); }

/* ============================================================
   Paginador estándar (qr-paginator) — usado por el componente
   global frontend/components/QrPaginator.svelte en los listados.
   Ajustar fuente/tamaño/formato acá aplica en runtime (sin rebuild).
   ============================================================ */
.qr-paginator__summary { font-size: var(--qr-fs-sm); }

/* ==========================================================================
   Sub-tabs tipo "pills" (qr-pills)
   Segundo nivel de navegación dentro de un editor, debajo de la banda navy.
   Uso: <div class="qr-pills"><button class="qr-pill is-active">…</button></div>
   ========================================================================== */
.qr-pills {
    display: flex; flex-wrap: wrap; gap: .35rem;
    /* Mismo padding horizontal que el card-header y el card-body de CoreUI, para que las
       pills queden a plomo con el título de arriba y el contenido de abajo. Antes era
       .9rem y se veía corrido. */
    padding: .6rem var(--cui-card-cap-padding-x, 1rem);
    background: var(--qr-surface-2); border-bottom: 1px solid var(--qr-border);
}
/* Variante para cuando las pills NO cuelgan del card-header sino que viven dentro de un
   `card-body` (tercer nivel: una sección de una tab). La banda gris de ancho completo ahí
   queda flotando con el padding del card alrededor, así que se quita el fondo y el borde y se
   deja sólo la línea inferior, a ras del contenido. */
.qr-pills--inset {
    padding: 0 0 .6rem 0; background: none; border-bottom: 1px solid var(--qr-border);
    margin-bottom: 1rem;
}

.qr-pill {
    display: inline-flex; align-items: center; gap: var(--qr-pill-gap);
    border: var(--qr-border-width) solid var(--qr-border); background: var(--qr-surface); color: var(--qr-text-secondary);
    border-radius: var(--qr-pill-radius); padding: var(--qr-pill-padding-y) var(--qr-pill-padding-x);
    font-size: var(--qr-pill-font-size); font-weight: 600; line-height: 1.2;
    cursor: pointer; white-space: nowrap;
    transition: background .12s, color .12s, border-color .12s, box-shadow .12s;
}
.qr-pill i { font-size: var(--qr-icon-size-sm); opacity: .8; }
.qr-pill:hover:not(.is-active):not(:disabled) { background: var(--qr-surface-sunken); color: var(--qr-text-strong); border-color: var(--qr-border-strong); }
/* Deshabilitada (p. ej. dentro de un <fieldset disabled> mientras se envía un formulario):
   sin esto se ve igual que una habilitada y parece que el click no anda. */
.qr-pill:disabled { opacity: .5; cursor: not-allowed; }
.qr-pill.is-active {
    background: var(--qr-band-bg); border-color: var(--qr-band-bg); color: var(--qr-text-inverse);
    box-shadow: 0 2px 6px rgba(var(--qr-band-rgb), var(--qr-alpha-25));
}
.qr-pill.is-active i { opacity: 1; }
.qr-pill__n {
    background: var(--qr-surface-strong); color: var(--qr-text-body); font-size: var(--qr-fs-70); font-weight: 700;
    border-radius: var(--qr-radius-pill); padding: 0 .4rem; min-width: 1.15rem; text-align: center;
}
.qr-pill.is-active .qr-pill__n { background: rgba(var(--qr-highlight-rgb), var(--qr-alpha-20)); color: var(--qr-text-inverse); }
/* Contador en rojo: la sub-sección tiene errores/pendientes (igual que qr-band-tab__n). */
.qr-pill__n.is-bad { background: var(--qr-danger); color: var(--qr-text-inverse); }
.qr-pill.is-active .qr-pill__n.is-bad { background: var(--qr-danger); color: var(--qr-text-inverse); }

/* ── Tercer nivel: sub-pills (prop `sub` de NavPills) ─────────────────────
   Sin esta variante dos NavPills apilados salen idénticos y no se entiende cuál manda
   a cuál.

   Los TRES niveles de navegación (banda, pills y sub-pills) comparten el mismo
   `font-size: .82rem`: bajarlo por nivel hacía que las pills se leyeran más chicas que
   el resto de la pantalla y el sub-nivel quedaba directamente incómodo. La jerarquía la
   dan el relleno, el fondo y el padding — la banda desaparece, el activo pasa de navy
   sólido a relleno suave — no un texto más chico. */
.qr-pills--sub {
    background: transparent;
    border-bottom: 1px solid var(--qr-border-soft);
    padding-top: .5rem; padding-bottom: .5rem;
    gap: .3rem;
}
.qr-pills--sub .qr-pill {
    padding: var(--qr-pill-sub-padding-y) var(--qr-pill-sub-padding-x);
    font-size: var(--qr-pill-font-size);
    border-color: transparent;
    background: var(--qr-surface-3);
}
.qr-pills--sub .qr-pill i { font-size: var(--qr-icon-size-sm); }
.qr-pills--sub .qr-pill:hover:not(.is-active) { background: var(--qr-surface-strong); color: var(--qr-text-strong); border-color: transparent; }
.qr-pills--sub .qr-pill.is-active {
    background: var(--qr-band-soft); border-color: var(--qr-band-soft-border); color: var(--qr-band-bg); box-shadow: none;
}
.qr-pills--sub .qr-pill.is-active .qr-pill__n { background: rgba(var(--qr-band-rgb), var(--qr-alpha-15)); color: var(--qr-band-bg); }

/* ==========================================================================
   ListHeader — header estándar de pantallas de LISTADO (frontend/components/ListHeader.svelte)

   Layout:  [icono] Título          [buscar] [Filtros] [Refrescar] [Exportar ▾] [+ Nuevo]
                    subtítulo

   Vive acá y no en el <style> del componente para poder retocar el aspecto de TODOS
   los listados sin recompilar 30 bundles.
   ========================================================================== */
.qr-lh { display: flex; align-items: center; justify-content: space-between;
         gap: 1rem; flex-wrap: wrap; }

.qr-lh__title { margin: 0; font-weight: 700; font-size: var(--qr-fs-105); line-height: 1.3;
                display: flex; align-items: center; gap: .5rem; }
.qr-lh__title i { color: var(--bs-primary); font-size: var(--qr-icon-size-lg); }
.qr-lh__sub { color: var(--qr-text-secondary); font-size: var(--qr-fs-78); line-height: 1.3; margin-top: .1rem;
              overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.qr-lh__actions { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; }
.qr-lh__search { width: 260px; }

/* Los botones neutros son cuadrados y del mismo alto que el input-group-sm: sin esto
   cada uno toma el alto de su icono y la fila queda escalonada. */
/* Un único alto para todos los botones del header: el mismo de los campos (34px).
   Con 31px el `min-height: var(--qr-field-h)` que este mismo archivo le pone a `.btn-sm`
   ya los estiraba a 34, así que renderizaban 34 de alto × 31 de ancho — rectangulares,
   no cuadrados. */
.qr-lh__btn { position: relative; display: inline-flex; align-items: center;
              justify-content: center; width: var(--qr-field-h); height: var(--qr-field-h);
              padding: 0; line-height: 1; }

/* Cantidad de filtros activos, sobre el embudo. */
.qr-lh__badge { position: absolute; top: -5px; right: -5px; min-width: 15px; height: 15px;
                padding: 0 3px; border-radius: var(--qr-radius-pill); background: var(--qr-primary); color: var(--qr-text-inverse);
                font-size: var(--qr-fs-2xs); font-weight: 700; line-height: 15px; }

.qr-lh__spin { display: inline-block; animation: qr-lh-spin .8s linear infinite; }
@keyframes qr-lh-spin { to { transform: rotate(360deg); } }

/* Desplegable de exportación (propio, no el de Bootstrap: no requiere su JS). */
.qr-lh__dd { position: relative; }
.qr-lh__menu, .qr-menu { position: absolute; right: 0; top: calc(100% + 4px); z-index: 1050;
    max-height: min(60vh, 26rem); overflow-y: auto;
               min-width: 190px; background: var(--qr-surface); border: 1px solid var(--qr-border);
               border-radius: var(--qr-radius-sm-plus); box-shadow: 0 6px 20px rgba(var(--qr-shade-rgb), var(--qr-alpha-12));
               padding: .25rem; }
.qr-lh__menu button, .qr-menu button { display: flex; align-items: center; gap: .5rem; width: 100%;
                      border: none; background: transparent; text-align: left;
                      padding: .4rem .6rem; border-radius: var(--qr-radius-sm-plus); font-size: var(--qr-fs-82);
                      color: var(--qr-text); }
.qr-lh__menu button:hover, .qr-menu button:hover { background: var(--qr-surface-3); }

/* Ancla del menú (el `qr-lh__dd` del header, con nombre propio para usarlo en
   cualquier lado) y separador con rótulo, para menús que agrupan orígenes
   distintos ("del catálogo" vs "estados del marcador"). */
.qr-menu__anchor { position: relative; display: inline-block; }
.qr-menu__sep {
    padding: .35rem .7rem .2rem; font-size: var(--qr-fs-68); font-weight: 600;
    letter-spacing: .06em; text-transform: uppercase; color: var(--qr-text-muted);
    border-top: 1px solid var(--qr-header-border); margin-top: .2rem;
}
.qr-menu__sep:first-child { border-top: 0; margin-top: 0; }

/* ─────────────────────────────────────────────────────────────────────────────
   AVATAR DE IDENTIDAD — `qr-avatar`

   Iniciales sobre un color derivado del id. En una tabla de personas es lo que
   deja recorrer la columna sin leer: el color es estable por agente, así que
   "el verde" se encuentra antes que su nombre.

   El color lo pone la página (`style="background: hsl(…)"`): depende del id, y
   una clase por color sería un catálogo infinito.
   ───────────────────────────────────────────────────────────────────────────── */
.qr-avatar {
    width: 26px; height: 26px; border-radius: 50%; flex: none;
    display: grid; place-items: center;
    color: var(--qr-text-inverse); font-weight: 700; font-size: var(--qr-fs-66); line-height: 1;
    box-shadow: inset 0 0 0 1px rgba(var(--qr-highlight-rgb), var(--qr-alpha-25));
}

/* ─────────────────────────────────────────────────────────────────────────────
   BARRA DE ACCIONES EN BLOQUE — `qr-bulkbar`

   Aparece al marcar filas y actúa SÓLO sobre lo marcado. Va teñida del primario
   —no gris— porque es un modo: mientras está a la vista, los botones de arriba
   dejaron de significar "para todos".

   Empieza SIEMPRE con la cantidad de marcados. Una barra que dice "Aplicar" sin
   decir a cuántos es como se aplica algo a cuarenta agentes sin querer.
   ───────────────────────────────────────────────────────────────────────────── */
.qr-bulkbar {
    display: flex; align-items: center; flex-wrap: wrap; gap: .5rem;
    padding: .5rem .75rem;
    background: var(--qr-primary-soft); border: 1px solid var(--qr-primary-125);
    border-radius: var(--qr-field-radius);
    font-size: var(--qr-fs-sm); color: var(--qr-primary-hover);
}
.qr-bulkbar__n { font-weight: 600; white-space: nowrap; }
.qr-bulkbar__sep { width: 1px; align-self: stretch; background: var(--qr-primary-175); margin-inline: .2rem; }

/* ─────────────────────────────────────────────────────────────────────────────
   BANDEJA DE SELECCIONADOS — `qr-seltray` (SelectedTray.svelte)

   Va ARRIBA de un catálogo paginado donde se eligen varias cosas (agentes, pausas):
   lo elegido se ve aparte, como tarjetas quitables, aunque la tabla esté filtrada o
   en otra página. Sin esto, lo único que decía qué quedó elegido era un interruptor
   perdido en la fila de la página que tocara.
   ───────────────────────────────────────────────────────────────────────────── */
.qr-seltray {
    padding: .6rem .75rem;
    background: var(--qr-surface-2); border: 1px solid var(--qr-border);
    border-radius: var(--qr-field-radius);
}
.qr-seltray__head {
    display: flex; align-items: center; gap: .45rem;
    font-size: var(--qr-fs-sm); font-weight: 600; color: var(--qr-text-strong);
}
.qr-seltray__head > .bi { color: var(--qr-primary); }
.qr-seltray__n {
    min-width: 1.4rem; padding: .05rem .45rem; text-align: center;
    border-radius: var(--qr-radius-pill);
    background: var(--qr-primary-soft); color: var(--qr-primary-hover);
    font-size: var(--qr-fs-74);
}
.qr-seltray__empty { margin-top: .35rem; font-size: var(--qr-fs-sm); color: var(--qr-text-muted); }
.qr-seltray__error { margin-top: .35rem; font-size: var(--qr-fs-sm); color: var(--qr-danger); }
.qr-seltray__list { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .5rem; }
.qr-seltray__chip {
    display: inline-flex; align-items: center; gap: .45rem;
    max-width: 16rem; padding: .25rem .3rem .25rem .4rem;
    background: var(--qr-surface); border: 1px solid var(--qr-field-border);
    border-left: 3px solid var(--qr-primary);
    border-radius: var(--qr-radius-md);
    font-size: var(--qr-fs-sm); line-height: 1.2;
}
.qr-seltray__chip .qr-avatar { width: 20px; height: 20px; font-size: var(--qr-fs-60); }
.qr-seltray__text { display: flex; flex-direction: column; min-width: 0; }
.qr-seltray__label { font-weight: 600; color: var(--qr-text-strong); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qr-seltray__sub { font-size: var(--qr-fs-70); color: var(--qr-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qr-seltray__x {
    flex: none; display: grid; place-items: center;
    width: 20px; height: 20px; padding: 0;
    border: 0; border-radius: var(--qr-radius-sm); background: transparent;
    color: var(--qr-text-muted); font-size: var(--qr-fs-70);
}
.qr-seltray__x:hover { background: var(--qr-danger-50); color: var(--qr-danger); }
.qr-seltray__more {
    align-self: center; padding: .15rem .4rem;
    border: 0; background: transparent;
    font-size: var(--qr-fs-sm); font-weight: 600; color: var(--qr-primary);
}
.qr-seltray__more:hover { text-decoration: underline; }

/* Texto que se RECORTA en vez de envolver, con el valor completo en el `title`.
   En una tabla, una celda que envuelve empuja el alto de toda la fila; en una
   columna angosta, además desalinea todo lo demás.

   El `max-width: 0` del `<td>` es lo que lo hace andar: una tabla dimensiona la
   columna al contenido, así que sin eso la celda crece y nunca hay nada que
   recortar. El ancho real se lo termina dando el espacio que le queda. */
.qr-ellipsis { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
td.qr-ellipsis { max-width: 0; }

/* El código del estado: se edita en línea, es texto de máquina y tiene que
   entrar entero. Un typo acá no da ningún error y la regla no dispara nunca. */
input.qr-codecell {
    width: 9.5rem; min-width: 7rem;
    font-family: var(--qr-font-mono);
    font-size: var(--qr-fs-74);
    height: calc(var(--qr-field-h) - 8px);
    padding-inline: .4rem;
}

/* El alta se declara como <a class="btn"> en la mayoría de las pantallas. Un <a> es
   inline-block y dentro del flex alinea por baseline, así que el texto quedaba unos
   píxeles más arriba que el de los <button> hermanos. inline-flex lo centra. */
.qr-lh__create { display: inline-flex !important; align-items: center; gap: .4rem;
                 padding-left: var(--qr-btn-padding-x-cta); padding-right: var(--qr-btn-padding-x-cta); height: var(--qr-field-h); }

/* ── Header de pantallas de EDICIÓN / CREACIÓN (FormHeader.svelte) ──────────
   Reusa el mismo título, subtítulo y botones que el header de listado: es lo que hace
   que las dos pantallas de un módulo se vean de la misma familia. Sólo agrega el botón
   volver y el bloque de identidad con el icono al lado. */

/* Mismo caso que .qr-lh__create: un <a class="btn"> es inline-block y dentro del flex
   alinea por baseline, así que la flecha quedaba unos píxeles arriba del centro. */
.qr-lh__back { display: inline-flex !important; align-items: center; justify-content: center;
               width: var(--qr-field-h); height: var(--qr-field-h); padding: 0;
               line-height: 1; flex: 0 0 auto; }
/* `.qr-lh__back i` se declara DE NUEVO ~330 líneas más abajo (color, junto al resto de la
   convención de color de la toolbar). Es aditivo (acá tamaño, allá color) — no se unifica
   en un solo bloque a propósito, porque ahí abajo pertenece al grupo narrativo del color
   por significado. Si alguna vez se agrega una propiedad que ya existe en la otra, gana la
   de más abajo (~línea 2233) sin ningún aviso. */
.qr-lh__back i { font-size: var(--qr-fs-95); line-height: 1; }

/* Identidad (volver + icono + título/subtítulo). El `min-width:0` es lo que permite que
   el subtítulo largo se recorte con elipsis en vez de empujar las acciones fuera del
   card: sin él un flex item no baja de su ancho de contenido. */
.qr-lh__id { display: flex; align-items: center; gap: .75rem; min-width: 0; }
.qr-lh__id > div { min-width: 0; }

@media (max-width: 767.98px) {
    .qr-lh__search { width: 100%; }
    .qr-lh__actions { width: 100%; }
}

/* ==========================================================================
   Alerts — nota, no cartel

   CoreUI arma el `.alert` ENTERO con custom properties (`--cui-alert-padding-y`,
   `--cui-alert-bg`, …) y cada variante sólo redefine los colores. Por eso acá se
   redefinen las variables en vez de pisar `padding`/`margin` directo: así los ~346
   usos del sistema que ya vienen recortados a mano con `py-1`/`py-2`/`px-3` siguen
   ganando (las utilidades de Bootstrap llevan `!important`) y no se achican dos veces.

   El default de CoreUI es 1rem de padding y 1rem de tipografía: sobre un dashboard
   denso un aviso de una línea ocupaba tres veces lo que dice.
   ========================================================================== */
.alert {
    --cui-alert-padding-y: .55rem;
    --cui-alert-padding-x: .8rem;
    --cui-alert-margin-bottom: .75rem;
    --cui-alert-border-radius: var(--qr-field-radius);
    font-size: var(--qr-fs-82);
    line-height: 1.45;
}

/* Fondos pastel y borde al pelo. Los de CoreUI salen de `--cui-*-bg-subtle`, que en
   warning y danger llegan bastante saturados y le roban la atención al contenido de la
   pantalla. Estos son la misma familia de hue con mucha menos saturación, y el texto
   baja a un tono oscuro del propio color en vez del negro: se lee como una nota al
   margen y no como un cartel. El color sigue diciendo la severidad. */
.alert-info    { --cui-alert-bg: var(--qr-info-bg); --cui-alert-border-color: var(--qr-info-border); --cui-alert-color: var(--qr-info-text); }
.alert-success { --cui-alert-bg: var(--qr-ok-bg); --cui-alert-border-color: var(--qr-ok-border); --cui-alert-color: var(--qr-ok-text); }
.alert-warning { --cui-alert-bg: var(--qr-warn-bg); --cui-alert-border-color: var(--qr-warn-border); --cui-alert-color: var(--qr-warn-text); }
.alert-danger  { --cui-alert-bg: var(--qr-bad-bg); --cui-alert-border-color: var(--qr-bad-border); --cui-alert-color: var(--qr-bad-text); }
.alert-primary { --cui-alert-bg: var(--qr-primary-soft); --cui-alert-border-color: var(--qr-primary-150); --cui-alert-color: var(--qr-primary-dark); }
.alert-secondary { --cui-alert-bg: var(--qr-surface-3); --cui-alert-border-color: var(--qr-border); --cui-alert-color: var(--qr-text-body); }
.alert-light   { --cui-alert-bg: var(--qr-surface-2); --cui-alert-border-color: var(--qr-border-soft); --cui-alert-color: var(--qr-text-body); }

/* Los enlaces dentro del alert siguen su propio hue, no el azul de Bootstrap. */
.alert .alert-link { color: inherit; text-decoration-thickness: 1px; text-underline-offset: 2px; }

/* `.small` es `em`, así que dentro de un alert de .82rem caía a ~.72rem. */
.alert .small,
.alert small { font-size: .95em; }

.alert-heading { font-size: var(--qr-fs-88); font-weight: 700; margin-bottom: .15rem; }

/* El icono + texto se alinea SÓLO cuando el primer hijo es un icono, que es el patrón
   dominante. Poner `display:flex` en `.alert` a secas rompería los que apilan un <p>
   y una <ul> en vertical. */
.alert:has(> i:first-child),
.alert:has(> .bi:first-child) { display: flex; align-items: flex-start; gap: .5rem; }
/* El icono hereda el color del texto y va un punto más suave: marca sin gritar. */
.alert > i:first-child,
.alert > .bi:first-child { flex: 0 0 auto; line-height: 1.45; opacity: .75; }

/* CoreUI reserva el hueco de la X con `padding-inline-end: 3rem` (propiedad lógica y
   valor literal, no una variable): con el padding nuevo quedaba un vacío enorme. */
.alert-dismissible { padding-inline-end: 2.2rem; }
.alert .btn-close { padding: .55rem; }

/* ==========================================================================
   Bandas de tabs (qr-band / qr-pills) pegadas al borde de un card

   Las dos bandas son de ancho completo y esquinas rectas. Dentro de un `.card`
   (radio .375rem, `overflow: visible`) eso deja ver el fondo del card asomando en
   las esquinas, y pegadas bajo un `.card-header` quedan DOS líneas seguidas con la
   franja gris en el medio: es el "espacio blanco" y el "se ve cuadrado".

   Se resuelve con `border-radius: inherit` y NO con `overflow: hidden` en el `.card`,
   que recortaría los dropdowns de CoreUI y los paneles flotantes de CSelect.
   ========================================================================== */
/* La regla se cuelga de la BANDA, no del contenedor: las bandas viven en cuatro
   contenedores distintos (`.card`, `.bg-white border rounded` del listado de listas,
   `.inspector` del diseñador de IVR y `.modal-content` del modal de lead) y una regla
   `.card > …` dejaba afuera a tres de ellos. `inherit` toma el radio computado del
   padre, así que donde el padre no tiene radio el valor es 0 y no pasa nada. */
.qr-band:first-child,
.qr-pills:first-child {
    border-top-left-radius: inherit;
    border-top-right-radius: inherit;
}
.qr-band:last-child,
.qr-pills:last-child {
    border-bottom-left-radius: inherit;
    border-bottom-right-radius: inherit;
}

/* Si la banda va inmediatamente debajo del header, el borde lo pone la banda: con los
   dos se ven dos líneas. En navegadores sin `:has()` no aplica y queda como antes. */
.card-header:has(+ .qr-band),
.card-header:has(+ .qr-pills),
.modal-header:has(+ .qr-band),
.modal-header:has(+ .qr-pills) { border-bottom: 0; }

/* ==========================================================================
   Estado inválido de un campo

   El patrón del sistema es `class:is-invalid` en el control + `.invalid-feedback`
   con el mensaje (ver CLAUDE.md, "Patrón de campo de formulario"). Antes se usaba
   un `<small class="text-danger">` suelto, que dejaba el input con el borde normal:
   el error se leía sólo si mirabas debajo del campo.
   ========================================================================== */
.form-control.is-invalid,
.form-select.is-invalid,
.was-validated .form-control:invalid {
    border-color: var(--qr-danger);
    /* Bootstrap mete un icono de exclamación como background-image; en un <select>
       se superpone con el caret y quedan los dos encimados. */
    background-image: none;
    padding-right: var(--qr-field-px);
}
.form-select.is-invalid { padding-right: 2.1rem; }

.form-control.is-invalid:focus,
.form-select.is-invalid:focus {
    border-color: var(--qr-danger);
    box-shadow: 0 0 0 3px rgba(var(--qr-danger-rgb), var(--qr-alpha-15));
}

.invalid-feedback { font-size: var(--qr-fs-82); margin-top: .25rem; }

/* Mensaje de error para los controles PROPIOS (CSelect, DateTimeField, switches):
   no son `.form-control`, así que el selector hermano de Bootstrap no los alcanza y
   su `.invalid-feedback` nunca se mostraría. */
.qr-field-error { display: block; font-size: var(--qr-fs-82); margin-top: .25rem; color: var(--qr-danger); }

/* ==========================================================================
   Toasts (svelte-sonner)

   Este bloque estaba copiado en el `<style>` de 8 pantallas, con el mismo
   `!important` en todas. Es una decisión global — dónde aparecen los avisos del
   sistema — así que vive acá y las pantallas no lo repiten.
   ========================================================================== */
[data-sonner-toaster] {
    position: fixed !important;
    bottom: 1rem !important;
    right: 1rem !important;
    top: auto !important;
    left: auto !important;
    z-index: 99999 !important;
}
/* Una pantalla que pide `position="top-*"` se respeta: sin esto el aviso nacía anclado abajo
   y salía por el borde inferior, medio tapado. */
[data-sonner-toaster][data-y-position="top"] {
    top: 1rem !important;
    bottom: auto !important;
}
[data-sonner-toast] { font-size: var(--qr-fs-md); border-radius: var(--qr-field-radius); }

/* ==========================================================================
   Panel de filtros de un listado (qr-filterpanel)

   El panel colapsable que abre el botón de embudo del ListHeader. Antes cada
   pantalla lo armaba con `col-md-N` a mano y los anchos no cerraban la fila, así
   que los campos caían escalonados y — peor — los botones Aplicar/Limpiar eran
   una celda más de la grilla: su posición cambiaba según cómo hubieran envuelto
   los campos de arriba. Nunca estaban dos veces en el mismo lugar.

   Acá la grilla es uniforme (auto-fit) y las acciones viven en un pie fijo, así
   que Aplicar y Limpiar están SIEMPRE abajo a la derecha, en toda pantalla del
   sistema y con cualquier cantidad de filtros.
   ========================================================================== */
/* OJO: se llama `qr-filterpanel` y NO `qr-filters` porque ese nombre YA ESTÁ TOMADO
   (línea ~199) por la *fila* de filtros en línea que usan los componentes de importación.
   Nació con el nombre repetido y, como esta versión no declaraba `display`, heredaba el
   `display:flex` de aquélla: la grilla y el pie se acomodaban uno al lado del otro y los
   campos caían en escalera. Antes de crear una clase del sistema, grepearla. */
.qr-filterpanel {
    display: block;
    padding: 1rem;
    border-top: 1px solid var(--qr-header-border);
    background: var(--qr-surface-2);
}

.qr-filterpanel__grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: .8rem 1rem;
}
/* Un filtro que necesita más aire (un rango de fechas, un multi-select). */
.qr-filterpanel__grid > .is-wide { grid-column: span 2; }
@media (max-width: 575.98px) {
    .qr-filterpanel__grid { grid-template-columns: 1fr; }
    .qr-filterpanel__grid > .is-wide { grid-column: auto; }
}

.qr-filterpanel__f { min-width: 0; }
.qr-filterpanel__f > label {
    display: block;
    font-size: var(--qr-fs-sm); font-weight: 600;
    color: var(--qr-text-secondary);
    margin-bottom: .25rem;
}

.qr-filterpanel__foot {
    display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
    margin-top: 1rem; padding-top: .85rem;
    border-top: 1px solid var(--qr-border);
}
.qr-filterpanel__hint { font-size: var(--qr-fs-82); color: var(--qr-text-secondary); }
.qr-filterpanel__actions { display: flex; gap: .5rem; margin-left: auto; }

/* ==========================================================================
   Segmentación de formularios largos (qr-fieldset)

   Una pantalla de edición con veinte campos en una sola grilla se lee como una
   lista de trámites. Agruparlos por tema y titular cada grupo convierte el
   formulario en algo que se recorre: se busca la sección y recién ahí el campo.

   La regla horizontal sale del propio título (no es un `<hr>` aparte) para que
   el grupo se lea como una unidad y no como dos elementos sueltos.
   ========================================================================== */
.qr-fieldset { border: 0; padding: 0; margin: 0 0 1.7rem; min-width: 0; }
.qr-fieldset:last-child { margin-bottom: 0; }

.qr-fieldset__legend {
    display: flex; align-items: center; gap: .6rem;
    width: 100%; float: none;   /* anula el <legend> nativo */
    font-size: var(--qr-fs-78); font-weight: 700;
    letter-spacing: .07em; text-transform: uppercase;
    color: var(--qr-text-secondary);
    margin-bottom: .9rem; padding: 0;
}
.qr-fieldset__legend i { color: var(--qr-primary); font-size: var(--qr-fs-95); }
/* La línea llena lo que sobra a la derecha del título. */
.qr-fieldset__legend::after {
    content: ""; flex: 1 1 auto; height: 1px;
    background: var(--qr-header-border);
}
/* Nota aclaratoria de la sección, después de la línea. */
.qr-fieldset__note {
    flex: 0 0 auto;
    font-size: var(--qr-fs-78); font-weight: 500; letter-spacing: 0;
    text-transform: none; color: var(--qr-text-muted);
}
/* Acciones del grupo (agregar, etc.), al pie y a la derecha — el mismo lugar que en el panel
   de filtros. Si son una celda más del contenido, cambian de posición según cómo haya
   envuelto lo de arriba y nunca están dos veces en el mismo lado. */
.qr-fieldset__actions {
    display: flex; align-items: center; justify-content: flex-end; gap: .4rem;
    margin-top: .75rem;
}

/* ==========================================================================
   qr-emptybox — una sección sin contenido todavía

   No es un cartel de error: es el primer paso. Por eso dice qué PASA si se deja así y trae
   la acción para empezar, en vez del "Sin acciones." suelto que había en cada pantalla.
   ========================================================================== */
.qr-emptybox {
    display: flex; flex-direction: column; align-items: center; gap: .5rem;
    border: 1px dashed var(--qr-border-strong); border-radius: var(--qr-radius-md); background: var(--qr-surface-2);
    padding: 1.25rem; color: var(--qr-text-secondary); font-size: var(--qr-fs-82); text-align: center;
}
.qr-emptybox > .bi { font-size: var(--qr-fs-2xl); color: var(--qr-text-muted); }

/* ==========================================================================
   qr-loading — una pantalla o sección que todavía está cargando

   Centrado y con un anillo en el color primario, en vez del "Cargando…" suelto arriba a la
   izquierda. El texto dice qué se carga. Aparece con un fundido corto para que una carga rápida
   no parpadee. Componente: frontend/components/LoadingState.svelte.
   ========================================================================== */
.qr-loading {
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: .75rem;
    min-height: 60vh; padding: 2rem 1rem; text-align: center;
    animation: qr-loading-in .3s var(--qr-ease-out) .15s both;
}
.qr-loading--section { min-height: 10rem; padding: 1.5rem 1rem; }
.qr-loading__ring {
    width: 2.25rem; height: 2.25rem; border-radius: var(--qr-radius-pill);
    border: var(--qr-border-width-strong) solid var(--qr-primary-subtle);
    border-top-color: var(--qr-primary);
    animation: qr-loading-spin .8s linear infinite;
}
.qr-loading__text { color: var(--qr-text-secondary); font-size: var(--qr-fs-95); font-weight: 500; }
.qr-loading__hint { color: var(--qr-text-muted); font-size: var(--qr-fs-82); }
@keyframes qr-loading-spin { to { transform: rotate(360deg); } }
@keyframes qr-loading-in { from { opacity: 0; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
    .qr-loading { animation: none; }
    .qr-loading__ring { animation-duration: 2.4s; }
}

/* ==========================================================================
   qr-guide — la ayuda de un campo o de una sección: qué es, cómo se usa, un ejemplo

   Para lo que no entra en un `form-text` de una línea (el editor de agentes IA explica qué es
   la tarea frente a las instrucciones, cómo se arma el prompt, qué trae cada fuente). El
   ejemplo va plegado en un <details> para que la ayuda no tape el formulario.
   `--warn`: la misma caja para un aviso que cambia el resultado (una variable que no llega).
   ========================================================================== */
.qr-guide {
    border: var(--qr-border-width) solid var(--qr-border-soft);
    border-left: var(--qr-border-width-strong) solid var(--qr-primary);
    border-radius: var(--qr-radius-md); background: var(--qr-surface-2);
    padding: .6rem .8rem; font-size: var(--qr-fs-82); color: var(--qr-text-secondary);
}
.qr-guide--warn { border-left-color: var(--qr-warning); }
.qr-guide p { margin: 0 0 .4rem; }
.qr-guide p:last-child { margin-bottom: 0; }
.qr-guide ul, .qr-guide ol { margin: .2rem 0 .4rem; padding-left: 1.15rem; }
.qr-guide li { margin-bottom: .15rem; }
.qr-guide__title {
    display: flex; align-items: center; gap: .4rem; margin-bottom: .3rem;
    font-weight: 600; color: var(--qr-text);
}
.qr-guide__title .bi { color: var(--qr-primary); }
.qr-guide--warn .qr-guide__title .bi { color: var(--qr-warning-700); }
.qr-guide details { margin-top: .35rem; }
.qr-guide summary { cursor: pointer; color: var(--qr-primary); font-weight: 500; }
.qr-guide details[open] > summary { margin-bottom: .35rem; }

/* Bloque de código o texto literal (un prompt armado, un JSON de ejemplo). Conserva saltos de
   línea y corta palabras largas; con tope de alto y scroll propio. */
.qr-code-block {
    margin: 0; padding: .6rem .75rem; border-radius: var(--qr-radius-sm);
    background: var(--qr-code-bg); color: var(--qr-code-text);
    font-family: var(--qr-font-mono); font-size: var(--qr-fs-xs); line-height: 1.5;
    white-space: pre-wrap; word-break: break-word; max-height: 28rem; overflow: auto;
}
.qr-code-block--short { max-height: 12rem; }

/* Lista editable de textos cortos (una regla por ítem): número, campo y acciones de orden. */
.qr-listedit { display: flex; flex-direction: column; gap: .375rem; }
.qr-listedit__row {
    display: grid; grid-template-columns: 1.75rem minmax(0, 1fr) auto; gap: .375rem; align-items: center;
}
.qr-listedit__n {
    font-family: var(--qr-font-mono); font-size: var(--qr-fs-78); color: var(--qr-text-muted); text-align: right;
}
.qr-listedit__actions { display: inline-flex; gap: .25rem; }
.qr-listedit__empty { font-size: var(--qr-fs-sm); color: var(--qr-text-muted); padding: .25rem 0; }

/* ==========================================================================
   qr-dest — elegir UN destino entre pocas alternativas excluyentes

   Un `btn-group` con 4 opciones obliga a leer los 4 rótulos para saber cuál está
   elegido, y no deja lugar para el icono que hace reconocible cada destino de un
   vistazo. Esto es una tarjeta-opción: caja del alto de dos campos, icono arriba,
   rótulo abajo, y la elegida marcada con el primario (borde + fondo tenue).

   La forma sale de los tokens --qr-field-*, así que un cambio de radio de los
   inputs la sigue. Se usa en el enrutamiento del DID; sirve para cualquier
   selección excluyente de 2 a 5 opciones con identidad visual propia.
   ========================================================================== */
.qr-dest {
    width: 100%;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: .35rem;
    padding: .7rem .5rem;
    background: var(--qr-field-bg);
    border: 1px solid var(--qr-field-border);
    border-radius: var(--qr-field-radius);
    color: var(--qr-text-secondary);
    font-size: var(--qr-field-font); font-weight: 600; line-height: 1.2;
    text-align: center;
    transition: border-color .12s ease, background-color .12s ease, color .12s ease;
}
.qr-dest i { font-size: var(--qr-fs-115); color: var(--qr-text-muted); transition: color .12s ease; }
.qr-dest:hover { border-color: var(--qr-field-border-hover); color: var(--qr-text); }
.qr-dest:focus-visible {
    outline: 0;
    border-color: var(--qr-field-focus);
    box-shadow: var(--qr-field-focus-shadow);
}
.qr-dest.is-active {
    border-color: var(--qr-primary);
    background: rgba(var(--qr-primary-rgb), var(--qr-alpha-5));
    color: var(--qr-primary);
}
.qr-dest.is-active i { color: var(--qr-primary); }
.qr-dest:disabled { opacity: .55; cursor: not-allowed; }

/* ==========================================================================
   Ancho mínimo de los botones DE TEXTO

   Un "Guardar" o un "Limpiar" sin ancho mínimo quedan más angostos que el resto
   de los botones de la misma fila y la barra de acciones se ve despareja.

   NO va en `.btn` a secas: hay ~23 botones de sólo icono en el sistema que usan
   `btn`/`btn-sm` pelados, sin ninguna clase que los distinga, y un mínimo global
   los convertiría en rectángulos vacíos. Tampoco hay selector CSS que distinga
   "icono solo" de "icono + texto" (`:only-child` ignora los nodos de texto, así
   que `<button><i></i>Guardar</button>` daría un falso positivo).

   Por eso se aplica en los tres contextos donde un botón SIEMPRE lleva texto, y
   queda `.btn-w` como opt-in para el resto.
   ========================================================================== */
/* Tokens: static/css/qr_tokens.css */

.qr-lh__create,
.qr-filterpanel__actions .btn,
.modal-footer .btn,
.btn-w { min-width: var(--qr-btn-min-w); justify-content: center; }

/* ==========================================================================
   Iconos de la toolbar de un listado — color por significado

   Filtros, refrescar y exportar salían los tres como un icono gris idéntico
   dentro de un botón idéntico: había que leer el tooltip para saber cuál era
   cuál. Las acciones de FILA ya se colorean por significado (ver verde, editar
   azul, borrar rojo); esto lleva la toolbar a la misma convención.

   Se tiñe SÓLO el glifo: el botón conserva el mismo borde, fondo y tamaño, así
   la fila sigue leyéndose como un grupo y el color es una guía, no adorno.
   ========================================================================== */
.qr-lh__btn--filter i  { color: var(--qr-primary); }   /* acota lo que ves      */
.qr-lh__btn--refresh i { color: var(--qr-success); }   /* trae datos nuevos     */
.qr-lh__btn--export i  { color: var(--qr-warning); }   /* saca datos del sistema */
/* Segunda declaración de `.qr-lh__back i` (la primera, tamaño/line-height, está ~línea 1897).
   Aditivo a propósito — ver la nota de allá. */
.qr-lh__back i         { color: var(--qr-text-secondary); }

/* Deshabilitado: el color no debe sugerir que se puede hacer clic. */
.qr-lh__btn:disabled i { color: var(--qr-text-muted); }

/* ==========================================================================
   Paginador de listado (qr-pgbar) — la tira, y la tabla que va debajo

   Reemplaza al bloque suelto que vivía dentro del `card-body` junto a la tabla.
   El problema de antes era uno solo con dos síntomas: paginador y tabla
   compartían el padding del body, así que el paginador flotaba pegado a la
   tabla sin separación, y la tabla — que no tiene bordes exteriores — quedaba
   flotando en el medio del card sin apoyarse en nada.

   Ahora el paginador es una TIRA propia pegada al borde del card, cerrada con
   una línea, y la tabla arranca justo debajo y llega de borde a borde: el fondo
   del encabezado le hace de apoyo.

   Estructura del card de un listado (SIN card-body):

     <div class="card shadow-sm mb-4">
       <QrPaginator … />                        → .qr-pgbar
       <div class="table-responsive">
         <table class="table table-hover qr-table qr-table--bleed">
       </div>
     </div>
   ========================================================================== */
.qr-pgbar {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1rem; flex-wrap: wrap;
    padding: .6rem var(--cui-card-cap-padding-x, 1rem);
    border-bottom: 1px solid var(--qr-header-border);
    font-size: var(--qr-fs-82); color: var(--qr-text-secondary);
}
.qr-pgbar__count { font-variant-numeric: tabular-nums; }
.qr-pgbar__count b { color: var(--qr-text); font-weight: 600; }
.qr-pgbar__right { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; margin-left: auto; }

/* Cuántos por página. El `page_size` ya viaja en la query de las APIs v2, así que
   esto es sólo frontend y es lo que más se pide en un listado. */
.qr-pgsize { display: inline-flex; align-items: center; gap: .35rem; font-size: var(--qr-fs-sm); color: var(--qr-text-muted); }
.qr-pgsize select {
    font-family: inherit; font-size: var(--qr-fs-sm); color: var(--qr-text);
    height: 28px; padding: 0 1.4rem 0 .45rem; cursor: pointer; appearance: none;
    border: 1px solid var(--qr-field-border); border-radius: var(--qr-field-radius);
    background: var(--qr-field-bg) url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%2364748b' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e") no-repeat right .4rem center/9px 7px;
}

/* Salto directo. Con cientos de páginas, clickear "siguiente" no es navegación. */
.qr-pgjump { display: inline-flex; align-items: center; gap: .35rem; font-size: var(--qr-fs-sm); color: var(--qr-text-muted); }
.qr-pgjump input {
    font-family: var(--rd-mono); font-size: var(--qr-fs-sm); text-align: center;
    width: 3.4rem; height: 28px; color: var(--qr-text); background: var(--qr-field-bg);
    border: 1px solid var(--qr-field-border); border-radius: var(--qr-field-radius);
}
.qr-pgjump input:focus {
    outline: none; border-color: var(--qr-field-focus);
    box-shadow: var(--qr-field-focus-shadow);
}

/* ── Botonera segmentada ─────────────────────────────────────────────────
   Un solo objeto en vez de nueve botones sueltos. Las reglas apuntan a los
   HIJOS DIRECTOS a propósito: los bundles que todavía no se rebuildearon traen
   el markup viejo (`<ul class="pagination"><li><button class="page-link">`), que
   no es hijo directo y por lo tanto no lo tocan estas reglas. Siguen viéndose
   con el estilo de Bootstrap hasta que se recompilen. */
.qr-pagenav {
    display: inline-flex; align-items: stretch; overflow: hidden;
    border: 1px solid var(--qr-field-border); border-radius: var(--qr-field-radius);
    background: var(--qr-field-bg);
}
.qr-pagenav > button,
.qr-pagenav > span {
    font-family: inherit; font-size: var(--qr-fs-sm); font-weight: 500;
    min-width: 30px; height: 30px; padding: 0 .35rem;
    display: inline-flex; align-items: center; justify-content: center;
    border: 0; border-right: 1px solid var(--qr-border-soft); background: transparent;
    color: var(--qr-text-secondary); cursor: pointer;
    font-variant-numeric: tabular-nums;
    /* Anula el ancho mínimo de los botones de texto: acá son celdas de una
       botonera, no acciones. */
    min-width: 30px;
}
.qr-pagenav > :last-child { border-right: 0; }
.qr-pagenav > button:hover:not(:disabled):not(.is-active) { background: var(--qr-surface-3); color: var(--qr-text); }
.qr-pagenav > button:disabled { color: var(--qr-text-faint); cursor: default; }
.qr-pagenav > button.is-active { background: var(--qr-primary); color: var(--qr-text-inverse); font-weight: 600; }
.qr-pagenav > .qr-pagenav__gap { color: var(--qr-text-muted); cursor: default; }

/* ── Tabla a sangre ──────────────────────────────────────────────────────
   Sin `card-body` alrededor: la tabla llega al borde del card. Las celdas
   llevan el padding lateral del card, así el texto queda a plomo con el de la
   tira de arriba pero el fondo del encabezado llega hasta el borde. */
/* Zebra. No va por defecto en `qr-table`: en un listado de 8 columnas anchas el rayado
   compite con el contenido. Se pide donde la fila es densa y se compara hacia abajo — que
   es el caso del modal de selección. El hover le gana, para que siga marcando la fila. */
.qr-table--zebra > tbody > tr:nth-child(even) { background-color: var(--qr-surface-2); }
.qr-table--zebra > tbody > tr:hover { background-color: var(--qr-surface-3); }

.qr-table--bleed > thead > tr > th:first-child,
.qr-table--bleed > tbody > tr > td:first-child { padding-left: var(--cui-card-cap-padding-x, 1rem); }
.qr-table--bleed > thead > tr > th:last-child,
.qr-table--bleed > tbody > tr > td:last-child  { padding-right: var(--cui-card-cap-padding-x, 1rem); }
.qr-table--bleed > tbody > tr:last-child > td { border-bottom: 0; }

/* Densa. `qr-table` fija una fila de 48px pensada para 6-10 columnas anchas; un log
   crudo con 13+ columnas (ej. ccQueueCdr) necesita más columnas visibles sin scroll
   horizontal, no una fila más cómoda. Sigue siendo el sistema de ADMINISTRACIÓN
   (`qr-table`), no el de reportería (`qr-table-report`): esta pantalla vive bajo
   `/logs/`. No se toca con `font-size`/alto inline en la página — ver regla 3. */
.qr-table--dense { font-size: var(--qr-fs-74); }
.qr-table--dense > thead > tr > th { font-size: var(--qr-fs-68); padding: 0.4rem 0.5rem; }
.qr-table--dense > tbody > tr > td { padding: 0.32rem 0.5rem; }
.qr-table--dense > tbody > tr { height: 30px; }

/* Estado intermedio: los listados que todavía envuelven el paginador en un
   `card-body` (19 a la fecha) reciben la tira nueva al recompilarse, pero con el
   padding del body sumado al propio quedaría corrida 2rem y con una línea que no
   llega a los bordes. Mientras no migren, adentro de un body la tira va sin
   padding lateral y sin línea: se ve como el bloque suelto de antes, pero con el
   conteo, el selector de tamaño y el salto nuevos. Al migrar (sacar el card-body)
   pasa sola a la versión de tira. */
.card-body > .qr-pgbar:first-child {
    padding-left: 0; padding-right: 0; padding-top: 0;
    border-bottom: 0;
}

/* ==========================================================================
   Modal de DETALLE (qr-detail) — el patrón de "ver" de todo el sistema

   Lo emite frontend/components/ModalDetail.svelte, que se monta sobre el
   Modal.svelte global (backdrop, foco, Escape y cierre por arrastre ya
   resueltos). Antes cada modal de detalle escribía su propio
   `.modal fade show d-block` con un `z-index` inline y su backdrop aparte.

   Anatomía:
     [icono] Nombre del objeto            [✕]     ← mismo header que FormHeader
             #id · descripción
     ────────────────────────────────────────
     [ hecho ][ hecho ][ hecho ]                  ← tira de números (opcional)
     ( Datos )( Permisos 5 )( Usuarios 7 )        ← NavPills (opcional)
     …cuerpo…
     ────────────────────────────────────────
                          [ Cerrar ][ Editar ]
   ========================================================================== */

/* Header: misma anatomía que el de una pantalla de edición, para que abrir el
   detalle se sienta la versión chica de entrar a editar el objeto. */
.qr-detail__head {
    display: flex; align-items: center; gap: .75rem;
    padding: .85rem 1rem; border-bottom: 1px solid var(--qr-header-border);
}
.qr-detail__ic {
    width: var(--qr-field-h); height: var(--qr-field-h); flex: none;
    border-radius: var(--qr-field-radius); display: grid; place-items: center;
    background: var(--qr-primary-soft); color: var(--qr-primary); font-size: var(--qr-fs-base);
}
.qr-detail__id { min-width: 0; flex: 1; }
.qr-detail__title {
    font-size: var(--qr-fs-105); font-weight: 700; line-height: 1.3; margin: 0;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.qr-detail__sub {
    font-size: var(--qr-fs-78); color: var(--qr-text-secondary); line-height: 1.3;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Tira de hechos: los tres o cuatro números que contestan la pregunta más
   frecuente sin leer la ficha entera. Se omite si la entidad no tiene números
   propios — rellenarla con datos de adorno la vuelve estorbo. */
.qr-facts {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 130px), 1fr));
    border: 1px solid var(--qr-header-border); border-radius: var(--qr-field-radius);
    overflow: hidden; margin-bottom: 1.3rem;
}
.qr-fact { padding: .6rem .75rem; border-right: 1px solid var(--qr-header-border); }
.qr-fact:last-child { border-right: 0; }
.qr-fact__k {
    font-size: var(--qr-fs-68); letter-spacing: .07em; text-transform: uppercase;
    color: var(--qr-text-muted); margin-bottom: .2rem;
}
.qr-fact__v {
    font-size: var(--qr-fs-115); font-weight: 700; line-height: 1.1;
    font-variant-numeric: tabular-nums; color: var(--qr-text);
}
.qr-fact__v small { font-size: var(--qr-fs-78); font-weight: 500; color: var(--qr-text-secondary); }
/* Para un hecho que es texto y no número (un estado, un modo). */
.qr-fact__v.is-text { font-size: var(--qr-fs-86); font-weight: 600; }

/* Pares etiqueta / valor. Reemplaza al `<dl class="row">` con col-4/col-8, que
   es el ejemplo de la doc de Bootstrap y no un patrón del sistema. */
.qr-kv {
    display: grid; grid-template-columns: minmax(6.5rem, 9rem) minmax(0, 1fr);
    gap: .45rem 1rem;
}
.qr-kv__k { font-size: var(--qr-fs-78); color: var(--qr-text-secondary); }
/* `overflow-wrap: anywhere`: una descripción o una URL larga sin espacios envuelve dentro de su
   columna en vez de salirse de la tarjeta (en una ficha se lee entera; no se corta como en tablas). */
.qr-kv__v { font-size: var(--qr-fs-86); min-width: 0; overflow-wrap: anywhere; }
.qr-kv__v.is-empty { color: var(--qr-text-muted); }
@media (max-width: 480px) {
    .qr-kv { grid-template-columns: 1fr; gap: .15rem .5rem; }
    .qr-kv__k { margin-top: .5rem; }
}

/* Matriz: para el dato que ES una grilla (permisos entidad × acción, canales
   por agente, disponibilidad por franja). Una fila de badges por entidad no
   deja contestar "¿quién puede borrar?" sin leerla entera; una matriz se lee
   por columna. NO es el patrón general de una ficha. */
.qr-matrix { width: 100%; border-collapse: collapse; font-size: var(--qr-fs-sm); }
.qr-matrix th {
    font-size: var(--qr-fs-68); font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
    color: var(--qr-text-secondary); padding: .4rem .3rem; text-align: center;
    border-bottom: 1px solid var(--qr-header-border); white-space: nowrap;
}
.qr-matrix th:first-child,
.qr-matrix td:first-child { text-align: left; padding-left: 0; }
.qr-matrix td {
    padding: .38rem .3rem; text-align: center;
    border-bottom: 1px solid var(--qr-border-soft);
}
/* Segunda declaración de `.qr-matrix td:first-child` (la primera va agrupada con
   `th:first-child` arriba). Va separada a propósito: `font-weight` es sólo de la celda de
   dato, agruparla con el `th` se lo pondría también al encabezado. Aditivo, sin choque. */
.qr-matrix td:first-child { font-weight: 500; }
.qr-matrix tr:last-child td { border-bottom: 0; }
.qr-matrix .is-yes { color: var(--qr-success); font-weight: 700; }
.qr-matrix .is-no  { color: var(--qr-text-ghost); }

/* ==========================================================================
   qr-capset / qr-cap — editor de capacidades del asesor desde el catálogo
   (`frontend/components/CapabilityEditor.svelte`, permisos granulares Fase 3).
   Un sujeto (Cliente, Registro, Formulario…) es un bloque plegable; adentro, los
   bloques del catálogo como `qr-fieldset` con filas `qr-optrow` o una `qr-matrix`.
   ========================================================================== */
.qr-capset {
    border: 1px solid var(--qr-border-soft); border-radius: var(--qr-radius-md);
    background: var(--qr-field-bg); margin-bottom: .75rem;
}
.qr-capset > summary {
    display: flex; align-items: center; gap: .5rem; list-style: none; cursor: pointer;
    padding: .6rem .85rem; font-size: var(--qr-fs-86); font-weight: 600; color: var(--qr-text-strong);
}
.qr-capset > summary::-webkit-details-marker { display: none; }
.qr-capset[open] > summary { border-bottom: 1px solid var(--qr-border-soft); }
.qr-capset__chev { color: var(--qr-text-muted); transition: transform .15s ease; }
.qr-capset[open] .qr-capset__chev { transform: rotate(90deg); }
.qr-capset__count { margin-left: auto; font-size: var(--qr-fs-74); font-weight: 500; color: var(--qr-text-muted); }
.qr-capset__body { padding: .85rem; }
.qr-cap__hint { margin-left: .35rem; font-size: var(--qr-fs-74); font-weight: 500; color: var(--qr-text-muted); }
.qr-cap__ctl { display: inline-flex; align-items: center; gap: .5rem; }
.qr-cap__reset { padding: 0 .25rem; color: var(--qr-text-muted); }
.qr-cap__bulk { display: inline-flex; gap: .15rem; }
.qr-cap__bulk .btn { padding: 0 .3rem; font-size: var(--qr-fs-74); }
.qr-cap__select { min-width: 11rem; }
.qr-cap__num { width: 6.5rem; }
.qr-cap__check { display: inline-flex; align-items: center; gap: .3rem; margin: 0; font-size: var(--qr-fs-78);
    color: var(--qr-text-secondary); white-space: nowrap; }
.qr-cap__cell { min-width: 8.5rem; }
.qr-cap__matrix { margin-bottom: .75rem; }
.qr-cap__matrix td { vertical-align: middle; }

/* ==========================================================================
   qr-perm / qr-subnav — pantalla de Permisos del asesor y del grupo
   (`frontend/components/permissions/PermissionsPanel.svelte`). Temas en una columna a la izquierda
   (`qr-subnav`); en pantallas angostas pasan a pastillas con scroll horizontal DENTRO de la barra,
   nunca de la página. Arriba: buscador y perfiles rápidos; abajo, encabezado del tema y sus filas.
   ========================================================================== */
.qr-perm { display: flex; flex-direction: column; gap: var(--qr-sp-3); min-width: 0; }
.qr-perm__bar { display: flex; flex-wrap: wrap; align-items: center; gap: var(--qr-sp-2) var(--qr-sp-4); }
.qr-perm__search { flex: 1 1 16rem; max-width: 26rem; }
.qr-perm__presets { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem; }
.qr-perm__presets-label { font-size: var(--qr-fs-sm); font-weight: 600; color: var(--qr-text-secondary); margin-right: .15rem; }
.qr-perm__confirm { margin: 0; }
.qr-perm__confirm-actions { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .5rem; }
.qr-perm__layout { display: grid; grid-template-columns: 15.5rem minmax(0, 1fr); gap: var(--qr-sp-4); align-items: start; }
.qr-perm__main { min-width: 0; }
.qr-perm__found { margin: 0 0 var(--qr-sp-3); font-size: var(--qr-fs-sm); color: var(--qr-text-muted); }
.qr-perm__section + .qr-perm__section { margin-top: var(--qr-sp-6); }
.qr-perm__head {
    display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between;
    gap: var(--qr-sp-2) var(--qr-sp-4);
    margin-bottom: var(--qr-sp-3); padding-bottom: var(--qr-sp-3);
    border-bottom: var(--qr-border-width) solid var(--qr-border-soft);
}
.qr-perm__title {
    display: flex; align-items: center; gap: .5rem; margin: 0;
    font-size: var(--qr-fs-lg); font-weight: 700; color: var(--qr-text-strong);
}
.qr-perm__title i { color: var(--qr-primary); }
.qr-perm__help { margin: .2rem 0 0; max-width: 46rem; font-size: var(--qr-fs-sm); line-height: 1.4; color: var(--qr-text-muted); }
.qr-perm__meta { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; }
.qr-perm__items { margin-bottom: var(--qr-sp-3); }
.qr-perm__goto { padding-left: 0; font-size: var(--qr-fs-sm); }

.qr-subnav { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.qr-subnav__item {
    display: flex; align-items: center; gap: .5rem; width: 100%; text-align: left;
    border: var(--qr-border-width) solid transparent; background: transparent;
    border-radius: var(--qr-radius-md); padding: .5rem .65rem;
    font-size: var(--qr-fs-md); font-weight: 600; color: var(--qr-text-secondary); cursor: pointer;
    transition: background .12s, color .12s, border-color .12s;
}
.qr-subnav__item i { font-size: var(--qr-icon-size-sm); opacity: .8; }
.qr-subnav__item:hover:not(.is-active) { background: var(--qr-surface-sunken); color: var(--qr-text-strong); }
.qr-subnav__item.is-active { background: var(--qr-band-bg); border-color: var(--qr-band-bg); color: var(--qr-text-inverse); }
.qr-subnav__item.is-active i { opacity: 1; }
.qr-subnav__label { flex: 1 1 auto; min-width: 0; }
.qr-subnav__n {
    background: var(--qr-surface-strong); color: var(--qr-text-body); font-size: var(--qr-fs-70); font-weight: 700;
    border-radius: var(--qr-radius-pill); padding: 0 .4rem; min-width: 1.15rem; text-align: center;
    flex: none; white-space: nowrap;
}
.qr-subnav__item.is-active .qr-subnav__n { background: rgba(var(--qr-highlight-rgb), var(--qr-alpha-20)); color: var(--qr-text-inverse); }
.qr-subnav__item .qr-subnav__dirty { font-size: var(--qr-fs-3xs); opacity: 1; color: var(--qr-warning); }

@media (max-width: 767.98px) {
    .qr-perm__layout { grid-template-columns: minmax(0, 1fr); }
    .qr-subnav { flex-direction: row; overflow-x: auto; padding-bottom: .25rem; }
    .qr-subnav__item {
        width: auto; flex: none; white-space: nowrap;
        border-color: var(--qr-border); border-radius: var(--qr-radius-pill); background: var(--qr-surface);
    }
    .qr-subnav__label { flex: none; }
    .qr-perm__search { max-width: none; }
    .qr-optrow--control { flex-wrap: wrap; }
}

/* ==========================================================================
   Ancho de modal a media pantalla (`size="half"` de Modal / ModalDetail)

   Los tamaños de Bootstrap son px fijos (sm 300 · default 500 · lg 800 · xl 1140),
   y ninguno sirve para "la mitad de la pantalla": en un monitor grande el `lg`
   deja media ventana vacía y el default queda angosto.

   La fórmula tiene un piso y un techo a propósito:
     · 50vw es lo pedido;
     · el piso de 520px evita que en una laptop chica el modal quede impracticable;
     · el techo de 92vw evita que ese piso desborde la pantalla en un celular.
   ========================================================================== */
.modal-dialog.modal-half { max-width: min(92vw, max(50vw, 520px)); }

/* Cuerpo "a sangre" para modales maestro-detalle o con barra propia (lista + vista
   previa, filtros + tabla): sin padding, con ALTO FIJO y el scroll en cada panel de
   adentro, no en el modal-body. Es lo que tenían a mano los pickers que dibujaban su
   propio overlay; ahora van con <Modal> y esto adentro:
       <Modal …><div class="qr-modal-flush">…</div></Modal>                        */
.modal-body:has(> .qr-modal-flush) { padding: 0; display: flex; flex-direction: column; min-height: 0; }
.qr-modal-flush { display: flex; flex-direction: column; min-height: 0; height: min(72vh, 640px); }
.qr-modal-flush__scroll { flex: 1 1 auto; min-height: 0; overflow: auto; }
/* Variante de alto natural (wizards cortos): crece con el contenido hasta el techo. */
.qr-modal-flush--auto { height: auto; min-height: 40vh; max-height: min(78vh, 720px); }

/* ==========================================================================
   LA PIEL DE LOS MODALES — «Panel compacto»
   Ver frontend/components/MODALES.md para el porqué de cada valor.

   Se aplica seteando las VARIABLES de CoreUI, no pisando sus reglas: así el
   modal sigue calculando sus radios internos y sus bordes solo, y esto llega
   a TODO lo que use las clases `.modal-*` — incluidas las 22 copias locales
   de `Modal.svelte`, que no se pueden arreglar de otra forma sin tocarlas
   una por una.

   Ojo con lo que esto NO alcanza: el portal, el Escape y el cierre por
   backdrop son COMPORTAMIENTO y viven en el componente. Un modal escrito a
   mano va a tomar este aspecto y seguir apareciendo debajo del header.
   ========================================================================== */
.modal {
    /* 8px: el punto medio real. 12px al lado de campos de 4px se ve fuera de
       lugar y arrastra al resto del sistema; 8px se sostiene solo. */
    --cui-modal-border-radius: var(--qr-modal-radius);
    --cui-modal-inner-border-radius: calc(var(--qr-modal-radius) - var(--qr-modal-border-width));
    --cui-modal-border-width: var(--qr-modal-border-width);
    --cui-modal-border-color: var(--qr-header-border);

    /* Densidad compacta, un escalón menos apretada que el mínimo: en táctil
       los blancos de click no quedan justos. */
    --cui-modal-padding: var(--qr-modal-padding);
    --cui-modal-header-padding-y: var(--qr-modal-header-padding-y);
    --cui-modal-header-padding-x: var(--qr-modal-header-padding-x);
    --cui-modal-header-padding: var(--qr-modal-header-padding-y) var(--qr-modal-header-padding-x);
    --cui-modal-header-border-color: var(--qr-border-soft);

    --cui-modal-footer-gap: .5rem;
    --cui-modal-footer-border-color: var(--qr-border-soft);
    /* El pie sobre fondo propio separa las acciones del contenido sin sumar
       otra línea. */
    --cui-modal-footer-bg: var(--qr-surface-2);

    --cui-modal-box-shadow: var(--qr-modal-shadow);
}

/* El pie del sistema es más bajo que el de Bootstrap y ya trae su fondo. */
.modal-footer { padding: var(--qr-modal-footer-padding-y) var(--qr-modal-footer-padding-x); }

/* Los controles de adentro acompañan el radio del contenedor sin igualarlo. */
.modal-content .btn,
.modal-content .form-control,
.modal-content .form-select { border-radius: var(--qr-modal-control-radius); }

/* ── Zonas del encabezado ─────────────────────────────────────────────────
   Tres zonas: navegación (volver) · identidad (icono + título) · herramientas
   (refresh, filtros, menú), y el cierre separado por un divisor para que la ✕
   no se lea como una acción más de la fila.

   Van ACÁ y no en una barra propia debajo: una segunda barra suma ~40px a
   cada modal y empuja el contenido, y estas acciones son del modal, no del
   contenido. (La barra de búsqueda y filtros de un picker es otra cosa y sí
   va debajo: ésa opera sobre el contenido.) */
.qr-mh__back,
.qr-mh__tool {
    flex: none;
    width: 26px; height: 26px;
    border: 0; border-radius: var(--qr-radius-sm-plus);
    background: transparent;
    color: var(--qr-text-secondary);
    line-height: 1; cursor: pointer;
    display: inline-grid; place-items: center;
    transition: background .12s ease, color .12s ease;
}
.qr-mh__back { margin-right: .15rem; }
/* `.qr-mh__tool` se vuelve a declarar más abajo, dos veces: `opacity` acá, y
   `position: relative` ~línea 3708 (contexto de posicionamiento del badge del embudo de
   filtros). Las tres son aditivas — ninguna pisa una propiedad de otra — pero es la misma
   clase repartida en tres lugares del archivo sin que nada lo señale. */
.qr-mh__tool { opacity: .72; }
.qr-mh__back:hover,
.qr-mh__tool:hover { background: var(--qr-surface-sunken); color: var(--qr-text); opacity: 1; }

.qr-mh__tools { flex: none; display: flex; align-items: center; gap: .1rem; margin-left: auto; }

/* El divisor es lo que evita que cerrar se lea como una acción más. */
.qr-mh__div {
    flex: none; width: 1px; height: 18px;
    margin: 0 .35rem;
    background: var(--qr-header-border);
}

/* Con herramientas adentro, el título cede espacio ANTES que ellas. */
/* EL TITULO CRECE, y sin eso las herramientas no quedan a la derecha.
   `min-width: 0` + `text-overflow: ellipsis` sólo tienen sentido en un item flex que puede
   crecer y achicarse — faltaba justamente el `flex`. Sin él el titulo ocupa apenas su texto y
   el `justify-content: space-between` de `.modal-header` reparte el sobrante ENTRE los cuatro
   hijos (titulo · herramientas · divisor · cerrar), asi que el refresh quedaba flotando en el
   medio en vez de pegado a la derecha. Reportado el 19/09/2026 en el panel de debug. */
.modal-header .modal-title { flex: 1 1 auto; min-width: 0; overflow: hidden;
    text-overflow: ellipsis; white-space: nowrap; }

/* Zona izquierda del pie de un modal (`footer_start` de Modal.svelte): un contador de
   estado o una acción secundaria de escape ("Crear nueva"). Empuja los botones a la
   derecha, donde el sistema los espera siempre. */
.modal-footer__start {
    margin-right: auto;
    display: flex; align-items: center; gap: .75rem;
    font-size: var(--qr-fs-sm); color: var(--qr-text-secondary);
}

/* ==========================================================================
   Asignación de permisos de un grupo (qr-perm-*)

   Lo emite frontend/administracion/app_user_group/src/components/
   GroupPermissionsComponent.svelte. Reemplaza al árbol de 341 controles
   (95 módulos + 48 entidades × 5 acciones) por áreas plegadas con, adentro:
     · lista de casillas agrupada por familia — áreas binarias (Reportes, Logs)
     · matriz entidad × acción                — áreas con entidades (Admin, Chat)
   ========================================================================== */

.qr-perm-area { border: 1px solid var(--qr-header-border); border-radius: var(--qr-field-radius);
                margin-bottom: .5rem; overflow: hidden; }
.qr-perm-area__h { display: flex; align-items: center; gap: .6rem; width: 100%;
                   padding: .55rem .8rem; border: 0; background: var(--qr-surface-2);
                   font-family: inherit; text-align: left; cursor: pointer; }
.qr-perm-area__h:hover { background: var(--qr-surface-3); }
.qr-perm-area__i { color: var(--qr-primary); }
.qr-perm-area__t { font-weight: 600; font-size: var(--qr-fs-88); color: var(--qr-text); }
/* Qué TIPO de área es: se ve o no se ve, contra entidades con acciones. Es la
   distinción que ordena toda la pantalla, así que se dice explícita. */
.qr-perm-area__kind { font-size: var(--qr-fs-66); letter-spacing: .05em; text-transform: uppercase;
                      color: var(--qr-text-muted); border: 1px solid var(--qr-header-border);
                      border-radius: var(--qr-radius-xs); padding: 0 .3rem; white-space: nowrap; }
.qr-perm-area__c { font-size: var(--qr-fs-76); color: var(--qr-text-secondary);
                   font-variant-numeric: tabular-nums; white-space: nowrap; }
.qr-perm-area__b { padding: .85rem; border-top: 1px solid var(--qr-header-border); }

.qr-perm-bar { width: 64px; height: 4px; background: var(--qr-surface-strong); border-radius: var(--qr-radius-2xs);
               overflow: hidden; flex: none; }
.qr-perm-bar__f { display: block; height: 100%; background: var(--qr-primary); transition: width .18s ease; }

/* Familias de módulos binarios: Reportes son 40 casillas, agrupadas se recorren. */
/* UNA SOLA grilla para toda el área, con los encabezados de familia ocupando la
   fila entera. Antes cada familia armaba su propia grilla `auto-fit`, así que el
   número de columnas dependía de cuántos módulos tuviera —5, luego 3, luego 4,
   luego 2— y nada se alineaba verticalmente: la pantalla se leía como un choclo.
   Con una grilla única, todas las casillas caen en las mismas columnas. */
.qr-perm-bin {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 270px), 1fr));
    gap: .05rem 1.2rem;
    align-items: start;
}
/* Encabezado de familia: ocupa la fila completa de la grilla. La acción es un
   enlace discreto, no un botón — hay uno por familia y como botones competían
   con las casillas, que es lo que hay que mirar. */
.qr-perm-fam__h {
    grid-column: 1 / -1;
    display: flex; align-items: center; gap: .6rem;
    margin: .9rem 0 .3rem;
    padding-bottom: .2rem;
    border-bottom: 1px solid var(--qr-line-soft);
}
.qr-perm-bin > .qr-perm-fam__h:first-child { margin-top: 0; }
.qr-perm-fam__t {
    font-size: var(--qr-fs-70); font-weight: 700; letter-spacing: .07em;
    text-transform: uppercase; color: var(--qr-text-secondary);
    white-space: nowrap;
}
.qr-perm-fam__n {
    font-size: var(--qr-fs-70); color: var(--qr-text-muted); font-variant-numeric: tabular-nums;
}
.qr-perm-fam__a {
    margin-left: auto; border: 0; background: none; padding: 0;
    font-family: inherit; font-size: var(--qr-fs-xs); font-weight: 600;
    color: var(--qr-primary); cursor: pointer; white-space: nowrap;
}
.qr-perm-fam__a:hover { text-decoration: underline; }

/* Matriz entidad × acción */
.qr-perm-mx { width: 100%; border-collapse: collapse; font-size: var(--qr-fs-84); }
.qr-perm-mx th { font-size: var(--qr-fs-66); font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
                 color: var(--qr-text-secondary); padding: .3rem .3rem .4rem; text-align: center;
                 border-bottom: 1px solid var(--qr-header-border); white-space: nowrap;
                 vertical-align: bottom; }
/* El hueco de la izquierda es donde entra el filete que marca la fila activa. Se
   reserva SIEMPRE, no sólo en hover: si el padding apareciera con el resaltado, el
   texto de la fila saltaría cada vez que el mouse la cruza. */
.qr-perm-mx th:first-child { text-align: left; padding-left: var(--qr-perm-rail); }
.qr-perm-mx td { padding: .32rem .3rem; text-align: center; border-bottom: 1px solid var(--qr-border-soft); }
.qr-perm-mx td:first-child { text-align: left; padding-left: var(--qr-perm-rail); }
.qr-perm-mx tbody tr:last-child > td { border-bottom: 0; }

/* ── Saber en qué fila estoy ──────────────────────────────────────────────────
   Una matriz de 5 casillas iguales por fila no da ninguna referencia: se marca
   una casilla tres filas más abajo de la que se quería. El zebra da la referencia
   estática y el resaltado al pasar el mouse la dinámica.

   El zebra NO se hace con `:nth-child(odd)`: entre dos filas de entidad puede
   aparecer la fila expandida de acciones avanzadas, que corre la paridad y deja
   el rayado salteado. La alternancia la marca el componente con `is-alt`, que
   cuenta entidades y no elementos `<tr>`.

   `:focus-within` está a propósito: quien recorre la matriz con el teclado
   necesita el mismo señalamiento que quien usa el mouse, y sin esto la fila
   enfocada es la única que no se distingue. */
.qr-perm-mx tbody tr.is-alt > td { background: var(--qr-surface-2); }
.qr-perm-mx tbody tr:hover > td,
.qr-perm-mx tbody tr:focus-within > td { background: var(--qr-info-50); }
/* La celda del nombre se refuerza para que el ojo enganche el borde izquierdo,
   que es de donde se arranca a leer la fila. */
.qr-perm-mx tbody tr:hover > td:first-child,
.qr-perm-mx tbody tr:focus-within > td:first-child {
    box-shadow: inset 2px 0 0 var(--qr-primary);
    color: var(--qr-text);
}

/* "Toda la fila": dar acceso pleno a una entidad son 5 clics, y es lo que más se
   hace al armar un grupo. Incluye las acciones avanzadas — un botón que dijera
   "todo" y dejara afuera lo avanzado estaría mintiendo en las entidades que las
   tienen. */
.qr-perm-mx__rowall-h { text-align: center; }
.qr-perm-mx__rowall {
    display: inline-flex; align-items: center; gap: .25rem;
    border: 1px solid var(--qr-border); background: var(--qr-surface);
    color: var(--qr-text-secondary); border-radius: var(--qr-field-radius);
    font-size: var(--qr-fs-66); line-height: 1; padding: .25rem .4rem; cursor: pointer;
    white-space: nowrap; transition: background-color .12s ease, color .12s ease;
}
.qr-perm-mx__rowall:hover { background: var(--qr-surface-sunken); color: var(--qr-text); }
.qr-perm-mx__rowall.is-on {
    background: var(--qr-primary-soft); border-color: var(--qr-primary-border-subtle); color: var(--qr-primary);
}

/* Corte entre las dos mitades de un área mixta (módulos que sólo se ven/no se ven
   arriba, entidades con acciones abajo). Sin él las dos partes se leen como una
   sola lista y no se entiende por qué unas filas traen casillas y otras un
   interruptor. */
.qr-perm-split {
    display: flex; align-items: center; gap: .6rem;
    margin: 1rem 0 .5rem; color: var(--qr-text-muted);
    font-size: var(--qr-fs-66); font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
}
.qr-perm-split::after {
    content: ""; flex: 1; height: 1px; background: var(--qr-header-border);
}

/* Las listas de módulos binarios tienen el mismo problema de referencia: en
   Reportes son 40 interruptores iguales. Se raya y se resalta igual que la
   matriz, pero SÓLO acá dentro: `.qr-switch-row` es un componente global y
   pintarle un fondo de base le cambiaría la cara a todas las pantallas. */
.qr-perm-md__r .qr-switch-row,
.qr-perm-bin .qr-switch-row {
    /* Mismo hueco a la izquierda que la matriz, por el mismo motivo: es donde entra el
       filete de `:focus-within` y tiene que estar reservado de antemano. */
    padding: .3rem .5rem .3rem var(--qr-perm-rail);
    border-radius: var(--qr-field-radius);
}
.qr-perm-md__r .qr-switch-row:nth-of-type(odd),
.qr-perm-bin .qr-switch-row:nth-of-type(odd) { background: var(--qr-surface-2); }
.qr-perm-md__r .qr-switch-row:hover,
.qr-perm-bin .qr-switch-row:hover { background: var(--qr-info-50); }
.qr-perm-md__r .qr-switch-row:focus-within,
.qr-perm-bin .qr-switch-row:focus-within {
    background: var(--qr-info-50); box-shadow: inset 2px 0 0 var(--qr-primary);
}
/* El "todo" de la columna: es lo que convierte 34 clics en uno. */
.qr-perm-mx__all { display: block; width: 100%; margin-top: .25rem; border: 0;
                   background: var(--qr-surface-sunken); color: var(--qr-text-body); border-radius: var(--qr-radius-xs);
                   font-family: inherit; font-size: var(--qr-fs-64); text-transform: none;
                   letter-spacing: 0; padding: .05rem .3rem; cursor: pointer; }
.qr-perm-mx__all:hover { background: var(--qr-surface-stronger); }
.qr-perm-mx__n { display: inline-flex; align-items: center; gap: .4rem; }

/* Píldora de acciones avanzadas. Son 6 en sólo 3 de las 48 entidades: como
   columnas darían 45 filas con 6 celdas vacías, así que van en una fila que se
   expande. Deshabilitada mientras la entidad no tenga "Ver". */
.qr-perm-adv { font-family: var(--rd-mono); font-size: var(--qr-fs-66);
               font-weight: 600; border: 1px solid var(--qr-special-200); background: var(--qr-special-50); color: var(--qr-special-700);
               border-radius: var(--qr-radius-pill); padding: 0 .35rem; cursor: pointer; }
.qr-perm-adv:hover:not(:disabled) { background: var(--qr-special-100); }
.qr-perm-adv.is-on { background: var(--qr-special-700); border-color: var(--qr-special-700); color: var(--qr-text-inverse); }
.qr-perm-adv:disabled { opacity: .4; cursor: not-allowed; }
.qr-perm-adv-row > td { background: var(--qr-primary-25); padding: .55rem .3rem .6rem 0 !important; text-align: left !important; }
.qr-perm-adv-box { border-left: 2px solid var(--qr-special-200); padding-left: .7rem; }
.qr-perm-adv-box__t { font-size: var(--qr-fs-68); font-weight: 700; letter-spacing: .06em;
                      text-transform: uppercase; color: var(--qr-special-700); margin-bottom: .4rem; }
.qr-perm-adv-list { display: flex; flex-wrap: wrap; gap: .3rem 1rem; }
.qr-perm-adv-list label { display: flex; align-items: center; gap: .4rem; font-size: var(--qr-fs-84); cursor: pointer; }

/* Plantillas de rol */
.qr-perm-tpl { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 210px), 1fr)); gap: .6rem; }
.qr-perm-tplc { display: flex; flex-direction: column; gap: .15rem; text-align: left;
                border: 1px solid var(--qr-field-border); border-radius: var(--qr-field-radius);
                background: var(--qr-field-bg); padding: .7rem .8rem; cursor: pointer; font-family: inherit; }
.qr-perm-tplc:hover { border-color: var(--qr-primary); }
.qr-perm-tplc.is-active { border-color: var(--qr-primary); background: var(--qr-primary-25);
                          box-shadow: 0 0 0 2px rgba(var(--qr-primary-rgb), var(--qr-alpha-12)); }
.qr-perm-tplc__t { display: flex; align-items: center; gap: .4rem; font-weight: 700;
                   font-size: var(--qr-fs-86); color: var(--qr-text); }
.qr-perm-tplc__t i { color: var(--qr-primary); }
.qr-perm-tplc__d { font-size: var(--qr-fs-78); color: var(--qr-text-secondary); line-height: 1.4; }
.qr-perm-tplc__m { font-size: var(--qr-fs-70); color: var(--qr-text-muted); margin-top: .2rem; }

/* Diff antes de aplicar: reemplazar los permisos de un grupo a ciegas es la
   clase de acción que después nadie sabe explicar. */
.qr-perm-diff { font-size: var(--qr-fs-84); }
.qr-perm-diff__r { display: flex; align-items: center; gap: .5rem; padding: .3rem 0;
                   border-bottom: 1px solid var(--qr-border-soft); }
.qr-perm-diff__r:last-child { border-bottom: 0; }
.qr-perm-diff__s { width: 18px; flex: none; text-align: center; font-weight: 700; }
.qr-perm-diff__s.is-add { color: var(--qr-success); }
.qr-perm-diff__s.is-del { color: var(--qr-danger); }
.qr-perm-diff__n { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qr-perm-diff__t { font-size: var(--qr-fs-74); color: var(--qr-text-muted); text-align: right; }

/* ── Maestro-detalle para áreas binarias con varias familias ──────────────
   Reportes son 30 módulos en 10 familias: en una grilla plana es una pared de
   casillas, aunque estén alineadas. Acá las familias hacen de índice —se ve de un
   golpe cuáles tienen algo— y a la derecha nunca hay más de 5 casillas.

   Se usa SÓLO con 2+ familias. Con una sola (Logs, Bases de Conocimiento) el
   panel izquierdo sería una lista de un ítem, así que ahí va la lista plana. */
.qr-perm-md {
    display: grid;
    grid-template-columns: minmax(0, 240px) minmax(0, 1fr);
    border: 1px solid var(--qr-header-border);
    border-radius: var(--qr-field-radius);
    overflow: hidden;
    min-height: 240px;
}
@media (max-width: 767.98px) {
    .qr-perm-md { grid-template-columns: 1fr; }
    .qr-perm-md__l { border-right: 0; border-bottom: 1px solid var(--qr-header-border); max-height: 190px; }
}

.qr-perm-md__l { border-right: 1px solid var(--qr-header-border); background: var(--qr-surface-2); overflow-y: auto; }
.qr-perm-md__i {
    display: flex; align-items: center; gap: .5rem; width: 100%;
    padding: .45rem .7rem; border: 0; background: none;
    font-family: inherit; font-size: var(--qr-fs-82); text-align: left;
    color: var(--qr-text); cursor: pointer;
}
.qr-perm-md__i:hover { background: var(--qr-surface-sunken); }
/* La barra de la izquierda marca la familia activa sin mover el texto, que es lo
   que pasa si se usa un borde. */
.qr-perm-md__i.is-active {
    background: var(--qr-surface); font-weight: 600;
    box-shadow: inset 3px 0 0 var(--qr-primary);
}
/* La etiqueta se marca con su propia clase y NO con `span:first-child`: el primer
   span de la fila es el punto de estado, así que ese selector le daba `flex:1` al
   punto y lo estiraba a lo ancho de toda la fila. */
.qr-perm-md__lbl {
    flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.qr-perm-md__n {
    font-size: var(--qr-fs-xs); color: var(--qr-text-muted);
    font-variant-numeric: tabular-nums; white-space: nowrap;
}
/* Un punto: dice "esta familia tiene algo" sin tener que leer el conteo. */
.qr-perm-md__dot { width: 6px; height: 6px; border-radius: 50%; flex: none; background: transparent; }
.qr-perm-md__dot.is-on { background: var(--qr-primary); }
.qr-perm-md__dot.is-part { background: var(--qr-warning); }

.qr-perm-md__r { padding: .8rem .9rem; }
.qr-perm-md__h {
    display: flex; align-items: center; gap: .6rem;
    margin-bottom: .5rem; padding-bottom: .4rem;
    border-bottom: 1px solid var(--qr-line-soft);
}
.qr-perm-md__t { font-weight: 700; font-size: var(--qr-fs-86); }

/* ==========================================================================
   Interruptor del sistema (qr-switch)

   Reemplaza al `.form-switch` de CoreUI, que dibuja el pulgar con un SVG de fondo
   y `background-position`: se ve blando, no tiene sombra y su transición arrastra.
   Éste es un `<input type=checkbox>` con `appearance:none`, así que sigue siendo un
   checkbox de verdad — teclado, `:checked`, formularios y lectores de pantalla
   funcionan igual, sin markup extra ni JS.

   Un permiso se lee mejor como encendido/apagado que como una casilla de
   formulario: no es un dato que se carga, es un acceso que se da.

       <label class="qr-switch-row">
         <input type="checkbox" class="qr-switch" bind:checked={x} />
         <span>Etiqueta</span>
       </label>
   ========================================================================== */
.qr-switch {
    appearance: none; -webkit-appearance: none;
    flex: none; margin: 0;
    width: 2.15rem; height: 1.15rem;
    border: 0; border-radius: var(--qr-radius-pill);
    background: var(--qr-control-off);
    position: relative; cursor: pointer;
    transition: background-color .18s ease;
}
.qr-switch::after {
    content: ""; position: absolute; top: 2px; left: 2px;
    width: calc(1.15rem - 4px); height: calc(1.15rem - 4px);
    border-radius: 50%; background: var(--qr-surface);
    box-shadow: 0 1px 2px rgba(var(--qr-shadow-rgb), var(--qr-alpha-30));
    transition: transform .18s cubic-bezier(.4, 0, .2, 1);
}
.qr-switch:hover:not(:disabled):not(:checked) { background: var(--qr-control-off-hover); }
.qr-switch:checked { background: var(--qr-primary); }
.qr-switch:checked::after { transform: translateX(1rem); }
.qr-switch:focus-visible {
    outline: none; box-shadow: var(--qr-field-focus-shadow);
}
.qr-switch:disabled { opacity: .45; cursor: not-allowed; }

/* Variantes semánticas, para cuando encender significa algo más que "sí". */
.qr-switch--danger:checked  { background: var(--qr-danger); }
.qr-switch--success:checked { background: var(--qr-success); }

@media (prefers-reduced-motion: reduce) {
    .qr-switch, .qr-switch::after { transition: none; }
}

/* Fila etiqueta + interruptor. El control va a la IZQUIERDA para que todas las
   filas arranquen alineadas: a la derecha el ojo tendría que recorrer etiquetas de
   largo distinto para encontrarlo. */
.qr-switch-row {
    display: flex; align-items: center; gap: .6rem;
    padding: .3rem 0; margin: 0; cursor: pointer; min-width: 0; font-size: var(--qr-fs-84);
}
.qr-switch-row > span {
    min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    color: var(--qr-text-secondary);
}
.qr-switch-row:hover > span { color: var(--qr-text); }
.qr-switch-row > .qr-switch:checked ~ span { color: var(--qr-text); font-weight: 500; }


/* ─────────────────────────────────────────────────────────────────────────────
   LISTA DE OPCIONES — `qr-optlist` / `qr-optrow`

   Un permiso o modo que ADEMÁS necesita explicarse. `qr-switch-row` no sirve
   acá: recorta la etiqueta a una línea a propósito, para que una grilla de
   casillas mantenga el ritmo. Cuando cada opción arrastra una ayuda de dos
   renglones, el ritmo lo da la fila con marco y el texto puede envolver.

   El interruptor va a la IZQUIERDA, igual que en `qr-switch-row`: todas las
   filas arrancan alineadas.

     <label class="qr-optrow">
       <input type="checkbox" class="qr-switch" bind:checked={valor} />
       <span class="qr-optrow__b">
         <span class="qr-optrow__t">Etiqueta</span>
         <span class="qr-optrow__d">Qué habilita, en una línea.</span>
       </span>
     </label>
   ───────────────────────────────────────────────────────────────────────── */
.qr-optlist {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(min(100%, 330px), 1fr));
    gap: .5rem;
    align-items: stretch;
}
/* Una sola columna: para opciones cuya ayuda no entra en media pantalla, o que
   llevan un control ancho a la derecha (ver `qr-optrow__c`). */
.qr-optlist--single { grid-template-columns: 1fr; max-width: 62rem; }

.qr-optrow {
    display: flex; align-items: flex-start; gap: .65rem;
    margin: 0; padding: .55rem .7rem; cursor: pointer;
    border: 1px solid var(--qr-field-border);
    border-radius: var(--qr-field-radius);
    background: var(--qr-field-bg);
    transition: border-color .15s ease, background-color .15s ease;
}
.qr-optrow:hover { border-color: var(--qr-field-border-hover); }
.qr-optrow:focus-within { border-color: var(--qr-field-focus); }
/* Encendida: apenas teñida. Un fondo fuerte haría que la mitad de la grilla
   compita con la acción primaria de la pantalla. */
.qr-optrow:has(.qr-switch:checked) { border-color: var(--qr-primary-border-subtle); background: var(--qr-primary-25); }

.qr-optrow > .qr-switch { margin-top: .12rem; }
/* Variante de elección única (radio): misma fila, mismo teñido al quedar elegida. */
.qr-optrow:has(> input[type="radio"]:checked) { border-color: var(--qr-primary-border-subtle); background: var(--qr-primary-25); }
.qr-optrow > input[type="radio"] { margin-top: .12rem; flex: none; }
.qr-optrow > input[type="radio"]:checked ~ .qr-optrow__b .qr-optrow__t { color: var(--qr-text); }
.qr-optrow__b { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.qr-optrow__t {
    font-size: var(--qr-fs-84); font-weight: 600; color: var(--qr-text-secondary);
    transition: color .15s ease;
}
.qr-optrow__d { font-size: var(--qr-fs-74); line-height: 1.35; color: var(--qr-text-muted); }
.qr-optrow:hover .qr-optrow__t { color: var(--qr-text); }
.qr-optrow > .qr-switch:checked ~ .qr-optrow__b .qr-optrow__t { color: var(--qr-text); }

/* Fila con un control propio a la derecha (tri-estado, select, número). Deja de
   ser un `<label>` clickeable entero: el control se maneja solo. */
.qr-optrow--control { cursor: default; align-items: center; }
.qr-optrow__c { margin-left: auto; flex: none; }

/* ─────────────────────────────────────────────────────────────────────────────
   TRI-ESTADO — `qr-tri`

   Heredar / Permitir / Denegar: un valor de tres estados donde el default NO es
   ninguno de los dos extremos. Control segmentado, no tres botones sueltos —
   son la misma decisión.

   El estado activo se pinta PASTEL, no sólido: son varias filas en pantalla y
   una columna de botones verdes y rojos macizos tapa el resto del formulario.

     <div class="qr-tri" role="group">
       <button type="button" class="qr-tri__b is-inherit is-active">Heredar</button>
       <button type="button" class="qr-tri__b is-allow">Permitir</button>
       <button type="button" class="qr-tri__b is-deny">Denegar</button>
     </div>
   ───────────────────────────────────────────────────────────────────────── */
.qr-tri {
    display: inline-flex;
    border: 1px solid var(--qr-field-border);
    border-radius: var(--qr-field-radius);
    background: var(--qr-field-bg);
    overflow: hidden;
}
.qr-tri__b {
    border: 0; border-left: 1px solid var(--qr-field-border);
    background: transparent; cursor: pointer;
    font-family: inherit; font-size: var(--qr-fs-78); font-weight: 600;
    color: var(--qr-text-muted);
    padding: 0 .7rem; height: calc(var(--qr-field-h) - 6px);
    white-space: nowrap;
    transition: background-color .15s ease, color .15s ease;
}
.qr-tri__b:first-child { border-left: 0; }
.qr-tri__b:hover:not(.is-active) { background: var(--qr-surface-3); color: var(--qr-text-secondary); }
.qr-tri__b:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--qr-field-focus-ring); }
.qr-tri__b.is-active.is-inherit { background: var(--qr-surface-3); color: var(--qr-text-secondary); }
.qr-tri__b.is-active.is-allow   { background: var(--qr-success-100); color: var(--qr-success-700); }
.qr-tri__b.is-active.is-deny    { background: var(--qr-danger-100); color: var(--qr-danger-700); }
/* Estado intermedio "acotado": ni abierto ni cerrado, sino una lista. Va en el
   primario porque es la opción que además exige elegir algo. */
.qr-tri__b.is-active.is-some    { background: var(--qr-primary-subtle); color: var(--qr-primary-hover); }

/* ─────────────────────────────────────────────────────────────────────────────
   CANALES — `qr-changroup` / `qr-chan`

   Qué canales atiende un agente en una campaña (voz · chat · email). Es la MISMA
   decisión repetida tres veces, así que va como control segmentado — misma familia
   que `qr-tri` — y no como tres pastillas sueltas de radio 999px: una pastilla
   redonda dentro de una celda de tabla se lee como una etiqueta de estado, no como
   algo que se puede apretar, y el encendido sólido (azul/verde/naranja macizos)
   convierte la columna en el objeto más fuerte de la pantalla.

   El encendido va PASTEL, con el color propio de cada canal, igual que el tri-estado.

     <div class="qr-changroup" role="group">
       <button type="button" class="qr-chan is-audio is-on"><i class="bi bi-telephone-fill"></i>Voz</button>
       <button type="button" class="qr-chan is-chat"><i class="bi bi-chat-dots-fill"></i>Chat</button>
     </div>
   ───────────────────────────────────────────────────────────────────────────── */
.qr-changroup {
    display: inline-flex;
    border: 1px solid var(--qr-field-border);
    border-radius: var(--qr-field-radius);
    background: var(--qr-field-bg);
    overflow: hidden;
}
.qr-chan {
    display: inline-flex; align-items: center; gap: .3rem;
    border: 0; border-left: 1px solid var(--qr-field-border);
    background: transparent; cursor: pointer;
    font-family: inherit; font-size: var(--qr-fs-74); font-weight: 600;
    color: var(--qr-text-muted);
    padding: 0 .55rem; height: calc(var(--qr-field-h) - 10px);
    white-space: nowrap;
    transition: background-color .15s ease, color .15s ease;
}
.qr-chan:first-child { border-left: 0; }
.qr-chan:hover:not(:disabled):not(.is-on) { background: var(--qr-surface-3); color: var(--qr-text-secondary); }
.qr-chan:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--qr-field-focus-ring); }
.qr-chan:disabled { opacity: .45; cursor: not-allowed; }
.qr-chan.is-on.is-audio { background: var(--qr-info-100); color: var(--qr-info-700); }
.qr-chan.is-on.is-chat  { background: var(--qr-success-100); color: var(--qr-success-700); }
.qr-chan.is-on.is-email { background: var(--qr-caution-100); color: var(--qr-warning-700); }

/* ─────────────────────────────────────────────────────────────────────────────
   ÁRBOL SELECCIONABLE — `qr-tree` / `qr-treerow`

   Una jerarquía donde se ENCIENDEN nodos (calificaciones por `dispo_parent`, nodos
   de un IVR, carpetas). Es la versión en filas de `qr-optlist`: con tres niveles,
   las tarjetas de `qr-optrow` no dejan ver de quién cuelga cada cosa.

   La profundidad se pasa por la custom property `--d` en la fila (`style="--d:2"`),
   no con una clase por nivel: el árbol no tiene una profundidad máxima conocida.

     <div class="qr-tree">
       <div class="qr-treerow" style="--d:0">
         <button class="qr-treerow__tw"><i class="bi bi-chevron-down"></i></button>
         <input type="checkbox" class="qr-switch" />
         <span class="qr-treerow__b">
           <span class="qr-treerow__t">Gestión comercial</span>
           <span class="qr-treerow__k">TEST_COMERCIAL</span>
         </span>
       </div>
     </div>
   ───────────────────────────────────────────────────────────────────────── */
.qr-tree {
    border: 1px solid var(--qr-field-border);
    border-radius: var(--qr-field-radius);
    background: var(--qr-field-bg);
    overflow: hidden;
}
/* El scroll va en el ÁRBOL, no en el cuerpo del modal: así el buscador y las
   herramientas quedan fijas y no hay que subir hasta arriba para volver a filtrar.
   El tope en `vh` es para que el modal no crezca más que la ventana. */
.qr-tree--scroll { max-height: min(55vh, 30rem); overflow-y: auto; }

.qr-treerow {
    --d: 0;
    display: flex; align-items: center; gap: .55rem;
    margin: 0;
    padding: .35rem .6rem;
    padding-left: calc(.6rem + var(--d) * 1.35rem);
    border-top: 1px solid var(--qr-header-border);
    position: relative;
    transition: background-color .12s ease;
}
.qr-treerow:first-child { border-top: 0; }
.qr-treerow:hover { background: var(--qr-surface-2); }
/* Encendida: apenas teñida, igual que `qr-optrow`. Con varias ramas abiertas, un
   fondo fuerte convierte la lista en el objeto más pesado de la pantalla. */
.qr-treerow.is-on { background: var(--qr-primary-25); }

/* Guía vertical de la rama: nace del padre y baja por los hijos. Sin ella, con
   tres niveles no se distingue un hijo de un nieto salvo contando píxeles. */
.qr-treerow[style*="--d:1"]::before,
.qr-treerow[style*="--d:2"]::before,
.qr-treerow[style*="--d:3"]::before {
    content: ""; position: absolute; top: 0; bottom: 0;
    left: calc(.95rem + (var(--d) - 1) * 1.35rem);
    border-left: 1px solid var(--qr-header-border);
}

/* Twisty. En una hoja se mantiene el hueco (`is-leaf`) para que todos los switches
   de un mismo nivel queden a plomo. */
.qr-treerow__tw {
    flex: none; width: 1.1rem; height: 1.1rem;
    display: inline-flex; align-items: center; justify-content: center;
    border: 0; background: transparent; padding: 0; cursor: pointer;
    color: var(--qr-text-muted); font-size: var(--qr-fs-xs);
}
.qr-treerow__tw:hover { color: var(--qr-text); }
.qr-treerow__tw.is-leaf { visibility: hidden; cursor: default; }

.qr-treerow__b { display: flex; align-items: baseline; gap: .5rem; min-width: 0; }
.qr-treerow__t {
    font-size: var(--qr-fs-84); font-weight: 600; color: var(--qr-text-secondary);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    transition: color .12s ease;
}
.qr-treerow:hover .qr-treerow__t { color: var(--qr-text); }
.qr-treerow.is-on .qr-treerow__t { color: var(--qr-text); }
/* En un árbol de selección simple, el cuerpo de la fila ES el botón de elegir. Va como
   `button.qr-treerow__b` (0,1,1) para ganarle a `.btn` sin tener que apilar utilidades
   de Bootstrap (`btn-link p-0 text-start text-decoration-none`) en cada uso. */
button.qr-treerow__b {
    border: 0; background: transparent; padding: 0; margin: 0;
    font: inherit; color: inherit; text-align: left; cursor: pointer; flex: 1;
}
.qr-treerow__k {
    flex: none;
    font-family: var(--qr-font-mono);
    font-size: var(--qr-fs-xs); color: var(--qr-text-muted);
}
.qr-treerow__a { margin-left: auto; flex: none; display: flex; gap: .2rem; }

/* Ruta de un nodo dentro del árbol ("Comercial › Venta › Venta nueva"), para
   cuando el nodo se muestra FUERA del árbol y solo no se entiende. */
.qr-path { font-size: var(--qr-fs-74); color: var(--qr-text-muted); }
.qr-path__s { margin: 0 .25rem; opacity: .6; }

/* ─────────────────────────────────────────────────────────────────────────────
   SEGMENTADO DE VARIAS OPCIONES — `qr-toggleset` / `qr-toggle`

   N opciones del mismo tipo que se encienden a la vez y son UNA decisión: qué
   teléfonos del registro se discan, qué días aplica un horario. Misma familia que
   `qr-tri` (una de N) y `qr-changroup` (canales, que tienen color propio por canal);
   éste es el genérico y usa el color primario para todo lo encendido.

   Es el reemplazo de la fila de `btn btn-sm btn-primary / btn-outline-secondary`
   sueltos: cinco botones separados se leen como cinco acciones, no como un control.
   ───────────────────────────────────────────────────────────────────────────── */
.qr-toggleset {
    display: inline-flex;
    border: 1px solid var(--qr-field-border);
    border-radius: var(--qr-field-radius);
    background: var(--qr-field-bg);
    overflow: hidden;
}
.qr-toggle {
    display: inline-flex; align-items: center; gap: .3rem;
    border: 0; border-left: 1px solid var(--qr-field-border);
    background: transparent; cursor: pointer;
    font-family: inherit; font-size: var(--qr-fs-78); font-weight: 600;
    color: var(--qr-text-muted);
    padding: 0 .7rem; height: calc(var(--qr-field-h) - 6px);
    white-space: nowrap;
    transition: background-color .15s ease, color .15s ease;
}
.qr-toggle:first-child { border-left: 0; }
.qr-toggle:hover:not(:disabled):not(.is-on) { background: var(--qr-surface-3); color: var(--qr-text-secondary); }
.qr-toggle:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--qr-field-focus-ring); }
.qr-toggle:disabled { opacity: .45; cursor: not-allowed; }
.qr-toggle.is-on { background: var(--qr-primary-subtle); color: var(--qr-primary-hover); }

/* ─────────────────────────────────────────────────────────────────────────────
   NÚMERO EN UNA CELDA — `qr-numcell`

   Un entero corto editable dentro de una tabla (segundos de timbre, prioridad). Un
   `form-control-sm` normal ocupa el ancho de la columna y hace que un campo de dos
   dígitos parezca un campo de texto largo. Va sobre `.form-control.form-control-sm`
   para conservar foco, estado inválido y deshabilitado del sistema.
   ───────────────────────────────────────────────────────────────────────────── */
/* Va como `input.qr-numcell` y NO como `.qr-numcell`: el alto de los campos lo fija
   `input.form-control { height: var(--qr-field-h) }`, que es 0,1,1 y le gana a una clase
   sola. Con `.qr-numcell` el ancho se achicaba pero el alto seguía siendo el de un campo
   de formulario completo. */
input.qr-numcell {
    width: 3.5rem;
    min-width: 3.5rem;
    margin-inline: auto;
    text-align: center;
    padding-inline: .3rem;
    /* Mismo alto que el segmentado (`qr-tri`, `qr-chan`): en una fila de tabla los
       controles conviven, y un campo de 34px al lado de un toggle de 24px se lee como
       si fuera otra categoría de cosa. */
    height: calc(var(--qr-field-h) - 8px);
    min-height: calc(var(--qr-field-h) - 8px);
    font-size: var(--qr-fs-78);
}
/* Las flechitas del number roban la mitad del ancho útil en una celda angosta.
   Segunda declaración de `input.qr-numcell` (la primera, la caja, está unas líneas
   arriba) — separada a propósito para que quede pegada a su par `::-webkit-*-spin-button`.
   Aditiva: no pisa ninguna propiedad de la de arriba. */
input.qr-numcell::-webkit-outer-spin-button,
input.qr-numcell::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
input.qr-numcell { -moz-appearance: textfield; appearance: textfield; }

/* ==========================================================================
   EntityPicker — selector rico de entidades  (frontend/components/EntityPicker.svelte)

   El estado CERRADO es una tarjeta, no un chip: avatar, nombre, línea técnica y
   estado de lo elegido. Es lo que los viejos `ModalSelectX` nunca mostraban —
   había que irse a otra pantalla para saber si lo que elegiste era lo correcto.

   Vive acá y no en un `<style>` del componente a propósito: es pieza del sistema
   de diseño y se retoca en runtime, sin rebuildear los ~21 bundles de Vite.
   ========================================================================== */
/* Tokens: static/css/qr_tokens.css */

.qr-picker { display: block; }
.qr-picker.is-disabled { opacity: .6; pointer-events: none; }

.qr-picker__label {
    display: flex; align-items: center; gap: .4rem;
    font-size: var(--qr-fs-78); font-weight: 650; color: var(--qr-text-secondary);
    margin-bottom: .35rem;
}
.qr-picker__req {
    font-size: var(--qr-fs-68); font-weight: 600; padding: 1px 6px; border-radius: var(--qr-radius-sm);
    background: rgba(var(--qr-primary-rgb), var(--qr-alpha-10)); color: var(--qr-primary);
}
.qr-picker__req--soft { background: var(--qr-surface-sunken); color: var(--qr-text-muted); }

/* ── campo (estado cerrado) ─────────────────────────────────────────────── */
.qr-picker__field {
    display: flex; align-items: center; gap: .5rem;
    min-height: calc(var(--qr-field-h) + 12px);
    padding: .4rem .5rem;
    border: 1px solid var(--qr-field-border);
    border-radius: var(--qr-picker-radius);
    background: var(--qr-field-bg);
    cursor: pointer;
    transition: border-color .12s ease, box-shadow .12s ease;
}
.qr-picker__field:hover { border-color: var(--qr-field-border-hover); }
.qr-picker__field:focus-visible,
.qr-picker__field.is-open {
    outline: none;
    border-color: var(--qr-field-focus);
    box-shadow: var(--qr-field-focus-shadow);
}
/* Abierto, el panel queda pegado abajo: el campo y el panel se leen como un solo control.
   ¡OJO! Ésta es la SEGUNDA declaración de `.qr-picker__field.is-open` (la primera, borde +
   sombra de foco, está agrupada con `:focus-visible` unas líneas arriba). Es el duplicado
   más frágil del archivo: si se reordena o se borra una de las dos por error, el picker
   abierto pierde la unión visual con el panel sin que nada avise. Aditiva hoy — no pisa
   ninguna propiedad de la de arriba —, pero si alguna vez se agrega la misma propiedad en
   ambas, gana ÉSTA por estar más abajo. */
.qr-picker__field.is-open { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.qr-picker.is-invalid .qr-picker__field { border-color: var(--qr-danger); }

.qr-picker__placeholder {
    display: flex; align-items: center; gap: .45rem;
    color: var(--qr-field-placeholder); font-size: var(--qr-field-font);
    padding: .25rem .15rem;
}
.qr-picker__cards { display: flex; flex-direction: column; gap: .35rem; flex: 1; min-width: 0; }
.qr-picker__card { display: flex; align-items: center; gap: var(--qr-picker-gap); min-width: 0; }
.qr-picker__card-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.qr-picker__card-actions { display: flex; gap: .25rem; margin-left: auto; flex: none; }

.qr-picker__avatar {
    width: var(--qr-picker-avatar); height: var(--qr-picker-avatar);
    border-radius: var(--qr-radius-md); flex: none;
    display: grid; place-items: center;
    color: var(--qr-text-inverse); font-size: var(--qr-fs-sm); font-weight: 700; letter-spacing: -.02em;
}
.qr-picker__avatar--ghost { background: var(--qr-surface-strong); color: var(--qr-text-muted); }

.qr-picker__title {
    font-size: var(--qr-fs-90); font-weight: 600; letter-spacing: -.01em; color: var(--qr-text);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* El id, delante del nombre. Va INLINE dentro de `.qr-picker__title` y no como hermano:
   el cuerpo de la fila es una columna flex, así que un hermano se iría a su propio renglón.
   Monoespaciado, y sin fondo propio: un chip gris se confundiría con el hover de la fila. */
.qr-picker__id {
    font-family: var(--qr-picker-mono); font-size: var(--qr-fs-xs); font-weight: 600;
    color: var(--qr-text-muted); margin-right: .35rem;
}

/* Monoespaciada a propósito: es el dato TÉCNICO (proveedor/modelo, id, extensión) y
   alineado se compara de un vistazo entre una fila y la siguiente. */
.qr-picker__subtitle {
    font-size: var(--qr-fs-77); color: var(--qr-text-secondary); font-family: var(--qr-picker-mono);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.qr-picker__more {
    align-self: flex-start;
    border: 1px dashed var(--qr-field-border); background: transparent;
    color: var(--qr-text-secondary); font-size: var(--qr-fs-75); font-weight: 600;
    padding: 2px 9px; border-radius: var(--qr-radius-pill); cursor: pointer;
}
.qr-picker__more:hover { border-color: var(--qr-primary); color: var(--qr-primary); }

.qr-picker__tools { display: flex; align-items: center; gap: .25rem; margin-left: auto; flex: none; }
.qr-picker__chev { color: var(--qr-text-muted); font-size: var(--qr-fs-75); padding-right: .2rem; }
.qr-picker__iconbtn {
    width: 26px; height: 26px; border-radius: var(--qr-radius-sm-plus); flex: none;
    border: 1px solid var(--qr-field-border); background: transparent;
    color: var(--qr-text-secondary); font-size: var(--qr-fs-75); line-height: 1; cursor: pointer;
}
.qr-picker__iconbtn:hover { background: var(--qr-surface-sunken); color: var(--qr-text); }
.qr-picker__help { font-size: var(--qr-fs-76); color: var(--qr-text-muted); margin-top: .3rem; }

/* ── densidad "field"  (<EntityPicker density="field">) ─────────────────────
   El cerrado a la escala EXACTA de un input: mismo alto (--qr-field-h), mismo
   radio y mismo padding horizontal que `.form-control`. Es para el formulario
   donde el picker comparte fila con inputs comunes y 49px contra 34px se lee
   como un error de maquetado.

   Lo que se pierde acá —el subtítulo y la segunda tarjeta— NO se pierde: sigue
   estando en el panel, que es donde realmente se compara antes de elegir.

   Todo cuelga de `.qr-picker--field`: es aditivo, no redefine ninguna clase del
   sistema, así que el modo tarjeta queda intacto.
   ────────────────────────────────────────────────────────────────────────── */
.qr-picker--field .qr-picker__field {
    height: var(--qr-field-h);
    min-height: var(--qr-field-h);
    padding: 0 .3rem 0 var(--qr-field-px);
    border-radius: var(--qr-field-radius);
    gap: .4rem;
}
.qr-picker--field .qr-picker__placeholder { padding: 0; }

/* En fila y no apiladas: entran las que el JS midió que entran (`fieldFitCount`, ver
   `recomputeFieldFit` en EntityPicker.svelte); el resto va al contador "+N". */
.qr-picker--field .qr-picker__cards     { flex-direction: row; align-items: center; gap: .35rem; }
.qr-picker--field .qr-picker__card      { flex: 1; gap: .45rem; }
/* card-body conserva su `flex: 1`, y eso es lo que empuja badges y acciones a la derecha —
   pero SÓLO tiene sentido con una sola tarjeta ocupando toda la fila. Con dos o más, `flex: 1`
   las repartiría el ancho en partes iguales sin importar el contenido (un nombre corto
   estirado igual que uno largo) y desarmaría justo lo que `recomputeFieldFit` midió: el ancho
   NATURAL de cada una. `.qr-picker__cards--multi` se activa cuando hay más de una visible. */
.qr-picker--field .qr-picker__cards--multi .qr-picker__card { flex: 0 0 auto; max-width: 100%; }
.qr-picker--field .qr-picker__card-body { flex-direction: row; align-items: center; gap: .35rem; }
.qr-picker--field .qr-picker__subtitle  { display: none; }
.qr-picker--field .qr-picker__title     { font-size: var(--qr-field-font); font-weight: 500; }

.qr-picker--field .qr-picker__avatar {
    width: var(--qr-picker-avatar-xs); height: var(--qr-picker-avatar-xs);
    border-radius: var(--qr-radius-sm-plus); font-size: var(--qr-fs-60);
}

.qr-picker--field .qr-picker__card-badges {
    display: flex; align-items: center; gap: .25rem;
    flex: none; overflow: hidden;
}

/* Sin recuadro: un botón de 26px con borde no entra en 34px sin apretar todo. */
.qr-picker--field .qr-picker__iconbtn { width: 22px; height: 22px; border: none; }
.qr-picker--field .qr-picker__more    { align-self: center; flex: none; padding: 1px 7px; }

/* Label y ayuda a la escala de `.form-label` / `.form-text`. No alcanza con emparejar el
   control: si el label del picker mide .78rem y el del input de al lado .85rem, los dos
   controles arrancan a distinta altura y la fila vuelve a verse torcida — que es justo lo
   que esta densidad viene a arreglar. */
.qr-picker--field .qr-picker__label {
    font-size: var(--qr-fs-md); font-weight: 600; margin-bottom: .3rem; color: var(--qr-text);
}
.qr-picker--field .qr-picker__help { font-size: var(--qr-fs-82); }

/* Fila "probe" de `recomputeFieldFit`: mismas tarjetas, fuera de pantalla, invisible y sin
   afectar el layout — `position: fixed` para que se calcule igual sin importar dónde viva el
   picker (una tabla con overflow, una card angosta). `white-space: nowrap` para que nunca
   envuelva: envuelta mediría un ancho menor al que tendría en una sola línea real. */
.qr-picker__fieldprobe {
    position: fixed; top: -9999px; left: -9999px;
    display: inline-flex; align-items: center; gap: .35rem; white-space: nowrap;
    visibility: hidden; pointer-events: none;
}
/* La tarjeta probe SIEMPRE a su ancho natural (nunca estirada por `flex: 1`): es justo lo que
   se está midiendo. */
.qr-picker__card--probe { flex: 0 0 auto !important; }

/* Botón "+ Crear nuevo" del footer del panel — mismo look que `.qr-picker__link`, con el
   icono separado por `gap` en vez de un `me-*` (ver Botones en SISTEMA_DE_DISENO.md). */
.qr-picker__link--create { display: inline-flex; align-items: center; gap: .25rem; }

/* ── panel ──────────────────────────────────────────────────────────────── */
.qr-picker__panel {
    position: fixed; z-index: 1080;
    display: flex; flex-direction: column;
    background: var(--qr-picker-panel-bg);
    border: 1px solid var(--qr-field-focus);
    border-top: none;
    border-radius: 0 0 var(--qr-picker-radius) var(--qr-picker-radius);
    box-shadow: 0 18px 40px -12px rgba(var(--qr-shadow-rgb), var(--qr-alpha-30));
    overflow: hidden;
}
.qr-picker__panel.is-up {
    border-top: 1px solid var(--qr-field-focus); border-bottom: none;
    border-radius: var(--qr-picker-radius) var(--qr-picker-radius) 0 0;
}

.qr-picker__search {
    display: flex; align-items: center; gap: .5rem;
    padding: .5rem .65rem; border-bottom: 1px solid var(--qr-border-soft); flex: none;
    color: var(--qr-text-muted);
}
.qr-picker__search-input {
    flex: 1; min-width: 0; border: none; outline: none; background: transparent;
    font-size: var(--qr-field-font); color: var(--qr-text);
}
.qr-picker__search-input::placeholder { color: var(--qr-field-placeholder); }

.qr-picker__filters {
    display: flex; flex-wrap: wrap; gap: .35rem;
    padding: .45rem .65rem; border-bottom: 1px solid var(--qr-border-soft); flex: none;
}
.qr-picker__chip {
    font-size: var(--qr-fs-75); padding: 2px 10px; border-radius: var(--qr-radius-pill); cursor: pointer;
    border: 1px solid var(--qr-field-border); background: var(--qr-surface); color: var(--qr-text-secondary);
}
.qr-picker__chip.is-on { background: var(--qr-primary); border-color: var(--qr-primary); color: var(--qr-text-inverse); }

/* El error de la API se MUESTRA. Una lista vacía por un retCode 1 se lee como
   "no hay nada", que es un diagnóstico equivocado. */
.qr-picker__error {
    display: flex; align-items: center; gap: .5rem; flex: none;
    padding: .5rem .65rem; font-size: var(--qr-fs-sm);
    background: rgba(var(--qr-danger-rgb), var(--qr-alpha-8)); color: var(--qr-report-bad);
    border-bottom: 1px solid rgba(var(--qr-danger-rgb), var(--qr-alpha-20));
}

.qr-picker__list { overflow-y: auto; flex: 1; }
.qr-picker__row {
    display: flex; align-items: center; gap: var(--qr-picker-gap);
    padding: .45rem .65rem; cursor: pointer;
    border-left: 2px solid transparent;
}
.qr-picker__row.is-active { background: var(--qr-picker-row-hover); }
.qr-picker__row.is-selected { background: rgba(var(--qr-primary-rgb), var(--qr-alpha-8)); border-left-color: var(--qr-primary); }
.qr-picker__row.is-disabled { opacity: .45; cursor: not-allowed; }
.qr-picker__row .qr-picker__avatar {
    width: var(--qr-picker-avatar-sm); height: var(--qr-picker-avatar-sm);
    border-radius: var(--qr-radius-md); font-size: var(--qr-fs-68);
}
.qr-picker__row-body { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.qr-picker__row-meta {
    display: flex; align-items: center; gap: .5rem; margin-left: auto; flex: none;
}

.qr-picker__check {
    width: 16px; height: 16px; border-radius: var(--qr-radius-sm); flex: none;
    border: 1.5px solid var(--qr-field-border);
    display: grid; place-items: center; font-size: var(--qr-fs-60); color: var(--qr-text-inverse);
}
.qr-picker__check.is-on { background: var(--qr-primary); border-color: var(--qr-primary); }

.qr-picker__badges {
    /* Sin wrap a propósito: los badges que no entran se colapsan en un "+N" desde el
       componente. Una fila que envuelve estira el alto de TODA la lista. */
    display: flex; flex-wrap: nowrap; overflow: hidden; gap: .25rem; margin-top: .15rem;
}
.qr-picker__badge {
    font-size: var(--qr-fs-68); font-weight: 600; padding: 1px 6px; border-radius: var(--qr-radius-sm);
    background: var(--qr-surface-sunken); color: var(--qr-text-secondary); white-space: nowrap;
}
.qr-picker__metric {
    font-size: var(--qr-fs-70); color: var(--qr-text-muted); text-align: right;
    font-variant-numeric: tabular-nums;
}
.qr-picker__metric b { display: block; font-size: var(--qr-fs-82); font-weight: 650; color: var(--qr-text); }

.qr-picker__status {
    display: inline-flex; align-items: center; gap: .28rem; flex: none;
    font-size: var(--qr-fs-70); font-weight: 600; padding: 2px 7px; border-radius: var(--qr-radius-sm-plus);
    background: var(--qr-surface-sunken); color: var(--qr-text-secondary); white-space: nowrap;
}
.qr-picker__dot { width: 6px; height: 6px; border-radius: var(--qr-radius-pill); background: currentColor; flex: none; }
.qr-picker__status--ok     { background: rgba(var(--qr-success-rgb), var(--qr-alpha-12)); color: var(--qr-success-ink); }
.qr-picker__status--warn   { background: rgba(var(--qr-warning-rgb), var(--qr-alpha-15)); color: var(--qr-warning-700); }
.qr-picker__status--bad    { background: rgba(var(--qr-danger-rgb), var(--qr-alpha-12));  color: var(--qr-report-bad); }
.qr-picker__status--accent { background: rgba(var(--qr-primary-rgb), var(--qr-alpha-10)); color: var(--qr-primary); }
.qr-picker__status--muted  { background: var(--qr-surface-sunken); color: var(--qr-text-muted); }

.qr-picker__empty, .qr-picker__loading { padding: 1.1rem .65rem; }
.qr-picker__empty { text-align: center; color: var(--qr-text-muted); font-size: var(--qr-fs-84); }
.qr-picker__loading { display: flex; flex-direction: column; gap: .5rem; }
.qr-picker__skeleton {
    height: 30px; border-radius: var(--qr-radius-sm-plus);
    background: linear-gradient(90deg, var(--qr-surface-sunken) 25%, var(--qr-surface-strong) 37%, var(--qr-surface-sunken) 63%);
    background-size: 400% 100%;
    animation: qr-picker-shimmer 1.2s ease-in-out infinite;
}
@keyframes qr-picker-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
@media (prefers-reduced-motion: reduce) { .qr-picker__skeleton { animation: none; } }

.qr-picker__footer {
    display: flex; align-items: center; gap: .6rem; flex: none;
    padding: .4rem .65rem; border-top: 1px solid var(--qr-border-soft); background: var(--qr-surface-2);
    font-size: var(--qr-fs-xs); color: var(--qr-text-muted);
}
.qr-picker__hints kbd {
    font-family: var(--qr-picker-mono); font-size: var(--qr-fs-2xs);
    border: 1px solid var(--qr-field-border); border-bottom-width: 2px; border-radius: var(--qr-radius-xs);
    padding: 0 4px; color: var(--qr-text-secondary); background: var(--qr-surface);
}
.qr-picker__count { margin-left: auto; font-variant-numeric: tabular-nums; }
.qr-picker__link {
    border: none; background: transparent; padding: 0 .2rem; cursor: pointer;
    color: var(--qr-primary); font-size: var(--qr-fs-74); font-weight: 600;
}
.qr-picker__link:hover { text-decoration: underline; }

/* ==========================================================================
   Mapa de ajustes (qr-smap) — navegación DENTRO de una pestaña larga

   Para una pestaña con veinte o treinta ajustes, segmentarla en fieldsets no
   alcanza: la pantalla sigue siendo un muro y hay que barrerla entera para
   encontrar una cosa. El mapa la parte en zonas y muestra sólo la elegida, así
   nunca hay más de un puñado de controles a la vista.

   La columna izquierda no es una tabla de contenidos: es un PLANO. El orden y
   la altura de las zonas imitan aquello que se está configurando —la pantalla
   del asesor de arriba hacia abajo, el recorrido de una llamada por la cola—,
   así que mirarlo ya contesta "¿dónde estoy?" antes de leer las etiquetas.

   Vivía como <style> local de la pantalla del agente de campañas. Al usarlo
   una segunda y tercera pantalla dejó de ser exclusivo, y dos copias de esto
   divergen sin que nadie se entere. Lo consume `components/SettingsMap.svelte`.
   ========================================================================== */
.qr-smap { display: grid; gap: 1.25rem; align-items: start; }
@media (min-width: 992px) { .qr-smap { grid-template-columns: 16rem minmax(0, 1fr); } }

.qr-smap__map {
    display: grid; gap: .4rem; align-content: start;
    padding: .6rem; border: 1px solid var(--qr-header-border);
    border-radius: var(--qr-field-radius); background: var(--qr-surface-2);
}
.qr-smap__title {
    font-size: var(--qr-fs-68); font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
    color: var(--qr-text-muted); padding: 0 .2rem .1rem;
}
.qr-smap__sep {
    font-size: var(--qr-fs-66); font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
    color: var(--qr-text-muted); margin-top: .35rem; padding: .35rem .2rem 0;
    border-top: 1px solid var(--qr-header-border);
}

.qr-smap__zone {
    display: flex; align-items: center; gap: .45rem; width: 100%; text-align: left;
    font: inherit; font-size: var(--qr-fs-78); color: var(--qr-text-secondary);
    padding: .5rem .6rem; cursor: pointer;
    background: var(--qr-surface); border: 1px dashed var(--qr-border-strong); border-radius: var(--qr-field-radius);
    transition: border-color .12s ease, background-color .12s ease, color .12s ease;
}
.qr-smap__zone--tall { min-height: 4.2rem; align-items: flex-start; }
.qr-smap__zone:hover { border-color: var(--qr-field-border-hover); color: var(--qr-text); }
.qr-smap__zone:focus-visible { outline: none; box-shadow: inset 0 0 0 2px var(--qr-field-focus-ring); }
.qr-smap__zone.is-active {
    border-style: solid; border-color: var(--qr-primary);
    background: var(--qr-primary-soft); color: var(--qr-primary-hover); font-weight: 600;
}
.qr-smap__zone i { flex: none; }
.qr-smap__zonet { min-width: 0; }
.qr-smap__zonen {
    margin-left: auto; flex: none;
    font-size: var(--qr-fs-68); font-weight: 600; color: var(--qr-text-muted);
}
.qr-smap__zone.is-active .qr-smap__zonen { color: var(--qr-primary-light); }

/* Numeración de las zonas cuando el mapa representa una SECUENCIA (el recorrido de
   una llamada), no una disposición espacial. Sin esto, "Espera" y "Timbra" se leen
   como dos opciones sueltas en vez de dos momentos, uno después del otro. */
.qr-smap__step {
    flex: none; width: 1.15rem; height: 1.15rem; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: var(--qr-fs-62); font-weight: 700;
    background: var(--qr-surface-strong); color: var(--qr-text-muted);
}
.qr-smap__zone.is-active .qr-smap__step { background: var(--qr-primary); color: var(--qr-text-inverse); }

/* Campo que sólo aplica si el de arriba está encendido. La sangría con la guía a la
   izquierda es lo que hace ver de un vistazo que depende de otra cosa. */
.qr-dep { margin-left: 1.15rem; padding-left: .8rem; border-left: 2px solid var(--qr-primary-subtle); }

/* ==========================================================================
   Tarjeta seleccionable (qr-pickcard)

   Un checkbox que ocupa una tarjeta entera, para elegir de un conjunto chico y
   visual (bibliotecas de archivos, plantillas, canales) donde la lista plana con
   la casilla al costado no deja ver el contenido de cada opción.

   Vivía como <style> local de la pestaña de multimedia del grupo de atención.
   ========================================================================== */
.qr-pickcard {
    display: flex; align-items: center; gap: .5rem; height: 100%;
    padding: .5rem; cursor: pointer; background: var(--qr-surface);
    border: 1px solid var(--qr-field-border); border-radius: var(--qr-field-radius);
    transition: border-color .12s ease, background-color .12s ease;
}
.qr-pickcard:hover { border-color: var(--qr-field-border-hover); }
.qr-pickcard.is-on { border-color: var(--qr-primary); background: var(--qr-info-50); }

/* =========================================================================
   PickerModal — el picker en una ventana grande (frontend/components/PickerModal.svelte)

   Reusa las filas de `qr-picker__*`: son el MISMO objeto y tienen que verse igual en el
   panel corto y en la ventana. Lo propio de acá es la barra fija y el alto de la lista.

   El scroll va en `.qr-pkm__list` y NO en el `modal-body`: con el body scrolleable el
   buscador se va con el scroll y hay que subir cada vez que se quiere filtrar.
   ========================================================================= */
.qr-pkm {
    display: flex;
    flex-direction: column;
    min-height: 0;
    /* ALTO FIJO. Sin esto la caja cambia de tamaño al cruzar entre lista y ficha, y otra
       vez al paginar a una página con menos filas: el modal "late" con cada click. */
    height: min(64vh, 30rem);
}

/* Escenario del slide: los dos paneles comparten la MISMA celda de grilla, así durante la
   transición se cruzan superpuestos en vez de apilarse y empujar el alto. El overflow
   recorta al que entra/sale por el costado. */
.qr-pkm__stage {
    display: grid;
    overflow: hidden;
    flex: 1 1 auto;
    min-height: 0;
}

.qr-pkm__panel {
    grid-area: 1 / 1;
    display: flex;
    flex-direction: column;
    min-height: 0;
}

@media (prefers-reduced-motion: reduce) {
    .qr-pkm__panel { transition: none !important; }
}

.qr-pkm__bar {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
    padding-bottom: .65rem;
    border-bottom: 1px solid var(--qr-header-border);
}

.qr-pkm__search {
    display: flex;
    align-items: center;
    gap: .4rem;
    flex: 1 1 14rem;
    min-width: 10rem;
    height: var(--qr-field-h);
    padding: 0 var(--qr-field-px);
    border: 1px solid var(--qr-field-border);
    border-radius: var(--qr-field-radius);
    background: var(--qr-field-bg);
    color: var(--qr-text-muted);
}

.qr-pkm__search:focus-within {
    border-color: var(--qr-field-focus);
    box-shadow: var(--qr-field-focus-shadow);
}

.qr-pkm__search input {
    flex: 1;
    min-width: 0;
    border: 0;
    outline: none;
    background: transparent;
    font-size: var(--qr-field-font);
    color: var(--qr-text);
}

.qr-pkm__x {
    border: 0;
    background: transparent;
    color: var(--qr-text-muted);
    padding: 0 .15rem;
    line-height: 1;
}

.qr-pkm__x:hover { color: var(--qr-text); }

/* ── botón embudo + panel de filtros desplegable ─────────────────────────
   Mismo gesto que el `ListHeader` de cualquier listado: embudo con el conteo encima, y el
   panel se despliega debajo de la barra. Los chips de faceta viven ADENTRO del panel: en la
   barra competían por el ancho con la búsqueda y no dejaban lugar a filtros de verdad. */
/* El embudo y el refresh subieron al encabezado del modal, así que el badge se posiciona
   sobre un `.qr-mh__tool`, que es quien crea el contexto. Tercera declaración de esta
   clase — ver la nota junto a la caja base, ~línea 2527. */
.qr-mh__tool { position: relative; }
.qr-pkm__badge {
    position: absolute; top: -2px; right: -2px;
    min-width: 15px; height: 15px; padding: 0 3px;
    border-radius: var(--qr-radius-pill);
    background: var(--qr-primary); color: var(--qr-text-inverse);
    font-size: var(--qr-fs-60); font-weight: 700; line-height: 15px;
}

.qr-pkm__filterpanel {
    flex: none;
    margin-top: .5rem;
    padding: .75rem;
    border: 1px solid var(--qr-header-border);
    border-radius: var(--qr-field-radius);
    background: var(--qr-surface-2);
}

.qr-pkm__chips { display: flex; gap: .3rem; flex-wrap: wrap; }
.qr-pkm__chips + .qr-filterpanel__grid { margin-top: .75rem; }

/* La casilla de un filtro `flag` se alinea con el alto de los selects de al lado, para que la
   fila de filtros no quede escalonada. */
.qr-pkm__flag {
    display: flex; align-items: center; gap: .5rem;
    min-height: var(--qr-field-h);
    margin: 0;
    font-size: var(--qr-fs-md);
    cursor: pointer;
}

/* Los filtros propios de la pantalla van DEBAJO de los de la entidad, separados: son otra
   cosa (uno los hereda, los otros los puso esta pantalla). */
.qr-pkm__extrafilters {
    margin-top: .75rem;
    padding-top: .75rem;
    border-top: 1px dashed var(--qr-header-border);
}

.qr-pkm__filterfoot {
    display: flex; align-items: center; gap: 1rem;
    margin-top: .75rem; padding-top: .6rem;
    border-top: 1px solid var(--qr-border);
}
.qr-pkm__filterfoot > .btn { margin-left: auto; }

/* La lista se come el alto sobrante del panel y es la que scrollea. El `max-height` que
   tenía acá se fue al `.qr-pkm`: con el alto del contenedor fijo, acotar también la lista
   dejaba un hueco muerto abajo cuando la página traía pocas filas. */
.qr-pkm__list {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    margin-top: .5rem;
    border: 1px solid var(--qr-header-border);
    border-radius: var(--qr-field-radius);
}

/* ── la tabla ────────────────────────────────────────────────────────────
   Es una tabla y no una pila de divs a propósito: con encabezados y zebra se compara hacia
   ABAJO por columna, que es para lo que se abre este modal. Una fila-tarjeta se lee de a una
   y obliga a recordar lo que decía la anterior. */
.qr-pkm__table { margin-bottom: 0; }

/* El encabezado se queda al tope mientras scrollean las filas: en la fila 15 de 20, sin
   esto, ya no se sabe qué columna es cuál. */
.qr-pkm__table > thead > tr > th {
    position: sticky;
    top: 0;
    z-index: 1;
}

/* Filas más bajas que las de un listado: acá entran 20 y el modal no crece. */
.qr-pkm__table > tbody > tr { height: auto; cursor: pointer; }
.qr-pkm__table > tbody > tr > td { padding: .45rem .75rem; }

.qr-pkm__tr.is-selected { background-color: rgba(var(--qr-primary-rgb), var(--qr-alpha-8)) !important; }
.qr-pkm__tr.is-disabled { opacity: .5; cursor: not-allowed; }

.qr-pkm__cknum { width: 40px; }
.qr-pkm__thact { width: 44px; }

/* Nombre: avatar + las dos líneas del descriptor. `min-width: 0` en la celda para que el
   título pueda recortarse con elipsis en vez de ensanchar la tabla. */
.qr-pkm__name { display: flex; align-items: center; gap: .6rem; min-width: 0; }
.qr-pkm__nametext { display: flex; flex-direction: column; min-width: 0; }

.qr-pkm__statecell { display: inline-flex; align-items: center; gap: .4rem; flex-wrap: wrap; justify-content: center; }
.qr-pkm__dash { color: var(--qr-text-muted); }

.qr-pkm__skelcell { padding: .35rem .75rem !important; }
.qr-pkm__skelcell .qr-picker__skeleton { height: 24px; }

/* El spinner del refresh, que ahora vive en el encabezado (`.qr-mh__tool`). */
.qr-pkm__spin { display: inline-block; animation: qr-pkm-spin .7s linear infinite; }
@keyframes qr-pkm-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .qr-pkm__spin { animation: none; } }

/* El paginador va ARRIBA de la tabla y fuera del scroll. Abajo obliga a scrollear hasta el
   fondo de la página para cambiar de página, y es donde nadie lo busca. Comparte fila con el
   contador, que es la otra cosa que se mira antes de decidir si vale la pena paginar. */
.qr-pkm__pager {
    display: flex;
    align-items: center;
    gap: 1rem;
    flex: none;
    flex-wrap: wrap;
    margin-top: .6rem;
}
.qr-pkm__pager > .qr-pkm__count { flex: none; }
/* El paginador del sistema trae su propio bloque; acá va a la derecha. */
.qr-pkm__pager > *:last-child { margin-left: auto; }

/* El "volver", el refresh y el embudo se mudaron a las zonas del encabezado que ahora da
   `Modal.svelte` (`.qr-mh__*`, más arriba en esta hoja). Acá quedó sólo lo del cuerpo. */

/* ── ficha (el panel que entra desde la derecha) ─────────────────────────── */
.qr-pkm__detail {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding-right: .25rem;
}

.qr-pkm__dhead {
    display: flex;
    align-items: center;
    gap: .65rem;
    padding-bottom: .7rem;
    border-bottom: 1px solid var(--qr-header-border);
}

.qr-pkm__davatar { width: 42px; height: 42px; border-radius: var(--qr-radius-md-plus); font-size: var(--qr-fs-base); }

.qr-pkm__dtitle {
    display: flex;
    flex-direction: column;
    min-width: 0;
    flex: 1;
}
.qr-pkm__dtitle b { font-size: var(--qr-fs-base); letter-spacing: -.01em; color: var(--qr-text); }

.qr-pkm__dbadges { margin-top: .6rem; flex-wrap: wrap; }

/* Dos columnas: etiqueta angosta y valor ancho. En pantalla chica se apilan, porque a
   menos de 30rem la etiqueta se parte en dos líneas y la ficha se vuelve ilegible. */
.qr-pkm__dl {
    display: grid;
    grid-template-columns: minmax(8rem, 12rem) 1fr;
    gap: .1rem .9rem;
    margin: .8rem 0 0;
}
.qr-pkm__dl dt {
    font-size: var(--qr-fs-78);
    font-weight: 600;
    color: var(--qr-text-secondary);
    padding: .3rem 0;
}
.qr-pkm__dl dd {
    margin: 0;
    padding: .3rem 0;
    font-size: var(--qr-fs-md);
    color: var(--qr-text);
    overflow-wrap: anywhere;
}
.qr-pkm__dl dt + dd { border-bottom: 1px solid var(--qr-header-border); }
.qr-pkm__dl dt { border-bottom: 1px solid var(--qr-header-border); }

@media (max-width: 575.98px) {
    .qr-pkm__dl { grid-template-columns: 1fr; }
    .qr-pkm__dl dt { padding-bottom: 0; border-bottom: 0; }
}

/* El ojo del detalle. Atenuado y no invisible: con `opacity: 0` sólo existía en hover, y
   en una pantalla táctil eso es una función que no se descubre nunca. */
.qr-pkm__eye {
    flex: none;
    border: 0;
    background: transparent;
    color: var(--qr-text-muted);
    padding: 0 .25rem;
    line-height: 1;
    opacity: .38;
    transition: opacity .12s ease, color .12s ease;
}

.qr-pkm__tr:hover .qr-pkm__eye,
.qr-pkm__tr.is-selected .qr-pkm__eye { opacity: 1; }

.qr-pkm__eye:hover { color: var(--qr-success); }

.qr-pkm__count {
    font-size: var(--qr-fs-82);
    color: var(--qr-text-secondary);
}

.qr-pkm__of { color: var(--qr-text-muted); }

/* El enlace del pie del panel corto que abre la ventana. */
.qr-picker__expand {
    border: 0;
    background: transparent;
    color: var(--qr-primary);
    font-size: var(--qr-fs-76);
    font-weight: 600;
    padding: 0;
    white-space: nowrap;
}

.qr-picker__expand:hover { text-decoration: underline; }

/* ==========================================================================
   InboundComposer — la caja de escribir con tipos de mensaje (qr-ic)
   --------------------------------------------------------------------------
   Componente global (`frontend/components/InboundComposer.svelte`): simula un mensaje
   entrante de cualquier canal. Vive acá y no en el <style> del componente porque el
   componente es compartido: un estilo scoped se bundlearía en cada app que lo importe y
   habría que recompilarlas todas para mover un padding.
   ========================================================================== */

.qr-ic { display: flex; flex-direction: column; gap: .5rem; }

.qr-ic__panel {
    position: relative;
    border: 1px solid var(--qr-header-border);
    border-radius: var(--qr-field-radius);
    background: var(--qr-surface);
    overflow: hidden;
}

/* Barra de canal + tipo + modo. Envuelve en pantallas angostas en vez de recortar: el
   selector de tipo escondido es el que hace que nadie descubra la función. */
.qr-ic__head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .5rem;
    padding: .5rem 2.25rem .5rem .625rem;
    background: var(--qr-surface-2);
    border-bottom: 1px solid var(--qr-header-border);
}

/* Los dos selectores (canal y tipo). Ancho acotado: el chip cerrado muestra icono + nombre,
   y dejarlos elásticos hacía que la barra cambiara de forma al elegir un canal de nombre largo. */
.qr-ic__sel { flex: 1 1 11rem; min-width: 9rem; max-width: 15rem; }
.qr-ic__sel--type { flex: 1 1 12rem; }

/* Fila de una opción dentro del panel del select. */
.qr-ic__opt { display: flex; align-items: center; gap: .45rem; min-width: 0; }
.qr-ic__opt > i { font-size: var(--qr-fs-base); flex: 0 0 auto; }
.qr-ic__opt-t { font-weight: 600; white-space: nowrap; }
.qr-ic__opt-d {
    margin-left: auto;
    font-size: var(--qr-fs-70);
    color: var(--qr-text-muted);
    white-space: nowrap;
}

.qr-ic__modes { margin-left: auto; }

/* Anclado a la esquina superior derecha del panel y FUERA del flujo: dentro de la barra se
   corría de lugar (o se caía a una segunda línea) según cuántos canales y pestañas de modo
   hubiera, que es distinto en cada canal. El `padding-right` del head es el hueco que le
   reserva para que nada quede debajo. */
.qr-ic__close {
    position: absolute;
    top: .3rem;
    right: .3rem;
    z-index: 2;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.6rem;
    height: 1.6rem;
    border: 1px solid var(--qr-header-border);
    border-radius: var(--qr-field-radius);
    background: var(--qr-surface);
    color: var(--qr-text-secondary);
    font-size: var(--qr-fs-sm);
    cursor: pointer;
}

.qr-ic__close:hover { background: var(--qr-surface-3); color: var(--qr-text); }

.qr-ic__body { padding: .625rem; }

.qr-ic__raw {
    font-family: var(--qr-font-mono);
    font-size: var(--qr-fs-82);
    line-height: 1.45;
}

/* "Lo que ve el modelo": es la mitad honesta del componente. El orquestador recibe un
   string, así que el usuario tiene que ver la derivación, no suponerla. */
.qr-ic__preview {
    border-top: 1px dashed var(--qr-header-border);
    padding: .5rem .625rem;
    background: var(--qr-surface-2);
}

.qr-ic__preview.is-bad { background: var(--qr-danger-50); }

.qr-ic__preview-h {
    display: flex;
    align-items: center;
    gap: .35rem;
    font-size: var(--qr-fs-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--qr-text-muted);
    margin-bottom: .25rem;
}

.qr-ic__preview-b {
    font-size: var(--qr-fs-88);
    color: var(--qr-text);
    white-space: pre-wrap;
    word-break: break-word;
}

.qr-ic__vars { display: flex; flex-wrap: wrap; gap: .25rem; margin-top: .35rem; }

.qr-ic__send { display: flex; align-items: flex-end; gap: .5rem; }

/* El "+" que abre el panel desde la caja simple. Alto de campo para que no baile al lado
   del textarea autosize; cuadrado porque es sólo un icono. */
.qr-ic__more { height: var(--qr-field-h); width: var(--qr-field-h); padding: 0; flex: 0 0 auto; }

/* ==========================================================================
   ChatComposer — caja de escribir un mensaje de chat (qrc-)
   --------------------------------------------------------------------------
   Componente global (`frontend/components/ChatComposer.svelte`): textarea que crece + emojis +
   botón Enviar. Vive acá por lo mismo que qr-ic: es compartido.

   Las tres piezas de la fila (el "+" de InboundComposer, la caja y Enviar) miden
   `--qr-field-h` con una línea escrita. Cuando el textarea crece, los botones quedan abajo,
   alineados con el borde inferior de la caja (`align-items: flex-end`), y no se estiran.
   La cuenta de la caja: 20px de línea + 2×6px de padding + 2px de borde = 34px. Si se toca
   `line-height` o el padding de `.qrc-input`, hay que tocar LINE_HEIGHT/PADDING_Y del
   componente (el autosize los usa para el tope de filas).
   ========================================================================== */

.qrc-composer { display: flex; align-items: flex-end; gap: .5rem; min-width: 0; }

.qrc-input-wrap {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
    min-height: var(--qr-field-h);
    display: flex;
    align-items: flex-end;
    border: 1px solid var(--qr-field-border);
    border-radius: var(--qr-field-radius);
    background: var(--qr-field-bg);
    transition: border-color .12s, box-shadow .12s;
}
.qrc-input-wrap:hover { border-color: var(--qr-field-border-hover); }
.qrc-input-wrap:focus-within {
    border-color: var(--qr-field-focus);
    box-shadow: var(--qr-field-focus-shadow);
}
.qrc-composer.disabled .qrc-input-wrap { background: var(--qr-body-bg); }

.qrc-input {
    flex: 1 1 auto;
    min-width: 0;
    resize: none;
    border: none;
    outline: none;
    background: transparent;
    color: inherit;
    padding: 6px var(--qr-field-px);
    font-size: var(--qr-field-font);
    line-height: 20px;
}
.qrc-input::placeholder { color: var(--qr-field-placeholder); }

/* El disparador de emojis mide 30px: con 1px de aire entra justo en los 32px internos de la
   caja y no la empuja más alta que un campo. */
.qrc-emoji-slot { flex: 0 0 auto; padding: 1px; }

.qrc-send { flex: 0 0 auto; height: var(--qr-field-h); }

.qr-ic__sendbar {
    display: flex;
    align-items: center;
    gap: .5rem;
    width: 100%;
    padding: .25rem 0;
}

/* ==========================================================================
   KeyValueVarsEditor — variables clave/valor (qr-kve)
   --------------------------------------------------------------------------
   Componente global (`frontend/components/KeyValueVarsEditor.svelte`). Grilla de 4 columnas
   con anchos fijos salvo el valor: el nombre y el tipo tienen largo acotado y dejarlos
   elásticos hacía que cada fila se alineara distinto.
   ========================================================================== */

.qr-kve__rows { display: flex; flex-direction: column; gap: .375rem; }

.qr-kve__row {
    display: grid;
    grid-template-columns: minmax(9rem, 1fr) 7.5rem minmax(10rem, 1.4fr) auto;
    gap: .375rem;
    align-items: center;
}

@media (max-width: 720px) {
    .qr-kve__row { grid-template-columns: 1fr 1fr; }
    .qr-kve__val { grid-column: 1 / -1; }
}

/* El prefijo va PEGADO al campo, no como texto de ayuda aparte: es parte del nombre con el
   que la variable se escribe en el prompt. */
.qr-kve__name { display: flex; align-items: center; gap: .25rem; min-width: 0; }

.qr-kve__prefix {
    font-family: var(--qr-font-mono);
    font-size: var(--qr-fs-78);
    color: var(--qr-text-muted);
    white-space: nowrap;
}

.qr-kve__val { display: flex; align-items: center; min-width: 0; }
.qr-kve__val > .form-control { width: 100%; }

.qr-kve__del { flex: 0 0 auto; }

.qr-kve__empty {
    font-size: var(--qr-fs-sm);
    color: var(--qr-text-muted);
    padding: .25rem 0 .1rem;
}

/* ==========================================================================
   Test de IA (qr-tia) — la consola de prueba de prompts
   --------------------------------------------------------------------------
   Pantalla `frontend/chat_app/test_ia/`. Va acá y no en su <style> porque en un `<style>`
   scoped de Svelte estas reglas le ganan por especificidad al sistema y desalinean la
   pantalla del resto sin que nadie toque el global (es exactamente lo que pasaba con el
   header `.tia-cardhd` navy que esta pantalla se dibujaba a mano).
   ========================================================================== */

/* La consola ocupa el alto de la ventana y el scroll vive adentro de cada panel: un scroll
   de página en una pantalla de chat aleja el composer del historial. */
.qr-tia { display: flex; flex-direction: column; height: 100%; min-height: 0; overflow: hidden; }

/* El card del header no se encoge: es un item flex de una columna con altura acotada y, sin
   esto, la consola de abajo se lo come cuando la ventana es baja. */
.qr-tia > .card:first-child { flex: 0 0 auto; }

.qr-tia__card { flex: 1 1 auto; display: flex; flex-direction: column; min-height: 0; }
.qr-tia__body { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; overflow: auto; }

/* Resumen de la config visible desde la tab Prueba: evita el viaje de ida y vuelta para
   confirmar contra qué modelo se está probando. */
.qr-tia-summary {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .375rem;
    padding: .375rem .5rem;
    margin-bottom: .625rem;
    border: 1px solid var(--qr-header-border);
    border-radius: var(--qr-field-radius);
    background: var(--qr-surface-2);
}

.qr-tia-workspace { flex: 1 1 auto; min-height: 0; display: grid; grid-template-rows: 1fr; }

/* Splitpanes necesita que el padre le pase el alto y que los panes no crezcan solos. */
.qr-tia-workspace .splitpanes,
.qr-tia-workspace .splitpanes__pane { min-height: 0; overflow: hidden; height: 100%; }
.qr-tia-workspace .splitpanes__splitter { background: var(--qr-header-border); width: 6px; }
.qr-tia-workspace .splitpanes__splitter:hover { background: var(--qr-primary); }

/* ── Panel izquierdo: prompt ─────────────────────────────────────────────── */

.qr-tia-prompt { height: 100%; display: grid; grid-template-rows: auto 1fr; background: var(--qr-surface); }
/* Barra de herramientas del panel de prompt: usa el `qr-tbar` genérico (más abajo). */
.qr-tia-prompt__bar { border-bottom: 1px solid var(--qr-header-border); }
.qr-tia-prompt__editor { min-height: 0; overflow: auto; padding: .5rem; }
.qr-tia-prompt__editor .editor-container,
.qr-tia-prompt__editor .editor-area { height: 100%; }

/* ── Panel derecho: conversación + composer ──────────────────────────────── */

.qr-tia-right { height: 100%; display: grid; grid-template-rows: 1fr auto; background: var(--qr-surface); min-height: 0; }

/* Comparación A/B: dos columnas iguales. En pantallas angostas se apilan y cada una scrollea
   por su cuenta; poner una al lado de la otra abajo de 1200px deja dos rendijas ilegibles. */
.qr-tia-lanes { min-height: 0; display: grid; grid-template-rows: 1fr; }
.qr-tia-lanes.is-split { grid-template-columns: 1fr 1fr; gap: 1px; background: var(--qr-header-border); }
@media (max-width: 1200px) {
    .qr-tia-lanes.is-split { grid-template-columns: 1fr; grid-template-rows: 1fr 1fr; }
}

/* Mismo aire lateral que la lista de mensajes (.75rem): la caja queda alineada con las burbujas. */
.qr-tia-composer { border-top: 1px solid var(--qr-header-border); padding: .5rem .75rem; background: var(--qr-surface); }

/* ── Una conversación ─────────────────────────────────────────────────────── */

/* Columna flex y no grilla de 3 filas fijas: entre la cabecera y los mensajes puede aparecer
   la alerta de error, y con la grilla la alerta se quedaba con la fila elástica y la lista de
   mensajes dejaba de scrollear. */
.qr-tia-chat { min-height: 0; display: flex; flex-direction: column; background: var(--qr-surface); }

.qr-tia-chat__head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .375rem;
    padding: .375rem .5rem;
    border-bottom: 1px solid var(--qr-header-border);
}

.qr-tia-chat__id { display: flex; align-items: center; gap: .375rem; font-size: var(--qr-fs-md); }
.qr-tia-chat__stats { display: flex; align-items: center; flex-wrap: wrap; gap: .25rem; }
.qr-tia-chat__acts { display: flex; align-items: center; gap: .25rem; margin-left: auto; }

.qr-tia-msgs { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding: .75rem; background: var(--qr-body-bg); }
.qr-tia-msgs__empty { font-size: var(--qr-fs-82); color: var(--qr-text-muted); text-align: center; margin-top: 1.5rem; }

.qr-tia-row { display: flex; margin-bottom: .625rem; }
.qr-tia-row.is-user { justify-content: flex-end; }

.qr-tia-bubble {
    max-width: 86%;
    padding: .5rem .625rem;
    border-radius: var(--qr-radius-md-plus);
    font-size: var(--qr-fs-md);
    background: var(--qr-surface);
    border: 1px solid var(--qr-header-border);
    box-shadow: var(--qr-shadow-xs);
}

.qr-tia-bubble.is-user { background: var(--qr-primary); border-color: var(--qr-primary); color: var(--qr-text-inverse); border-bottom-right-radius: var(--qr-radius-xs); }
.qr-tia-bubble.is-assistant { border-bottom-left-radius: var(--qr-radius-xs); }
.qr-tia-bubble.is-tool { background: var(--qr-warning-50); border-color: var(--qr-warning-300); }
.qr-tia-bubble.is-system { background: var(--qr-surface-3); }

.qr-tia-bubble__meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    font-size: var(--qr-fs-68);
    font-weight: 600;
    opacity: .75;
    margin-bottom: .2rem;
}

.qr-tia-bubble__body { white-space: pre-wrap; word-break: break-word; }
/* Texto que respeta los saltos de línea que trae (p. ej. el resumen del copiloto en la supervisión). */
.qr-prewrap { white-space: pre-wrap; word-break: break-word; }
.qr-tia-bubble__sum { font-size: var(--qr-fs-76); opacity: .8; margin-top: .35rem; }

.qr-tia-bubble__payload { border: 0; background: transparent; color: inherit; opacity: .6; padding: 0; }
.qr-tia-bubble__payload:hover { opacity: 1; }

.qr-tia-tools { margin-top: .35rem; font-size: var(--qr-fs-76); }
.qr-tia-tools pre { white-space: pre-wrap; word-break: break-word; margin: .25rem 0 0; font-size: var(--qr-fs-xs); }

/* ── Tool call pendiente ──────────────────────────────────────────────────── */

.qr-tia-tool {
    border-top: 1px solid var(--qr-warning-300);
    background: var(--qr-warning-50);
    padding: .5rem .625rem;
}

.qr-tia-tool__head { display: flex; align-items: center; gap: .5rem; margin-bottom: .375rem; font-size: var(--qr-fs-md); }
.qr-tia-tool__sel { width: auto; min-width: 10rem; }

.qr-tia-tool__args {
    max-height: 9rem;
    overflow: auto;
    font-size: var(--qr-fs-xs);
    background: var(--qr-surface);
    border: 1px solid var(--qr-header-border);
    border-radius: var(--qr-field-radius);
    padding: .375rem .5rem;
    margin: 0;
    white-space: pre-wrap;
    word-break: break-word;
}

.qr-tia-tool__out { min-height: 7rem; max-height: 14rem; resize: vertical; }
.qr-tia-tool__acts { display: flex; align-items: center; flex-wrap: wrap; gap: .375rem; margin-top: .375rem; }

/* ── Menú de pruebas guardadas ────────────────────────────────────────────── */

/* El listado vive DENTRO del modal de pruebas: no se posiciona solo. Antes era un dropdown
   absoluto en el header y ahí quedaba atrapado en su contexto de apilamiento. */
.qr-tia-cases__empty { font-size: var(--qr-fs-78); color: var(--qr-text-muted); padding: .3rem .55rem .5rem; }
.qr-tia-cases__list { display: flex; flex-direction: column; gap: .1rem; }
.qr-tia-cases__row { display: flex; align-items: center; border-radius: var(--qr-field-radius); }
.qr-tia-cases__row:hover { background: var(--qr-surface-3); }
.qr-tia-cases__row.is-active { background: var(--qr-primary-soft); }

.qr-tia-cases__item {
    flex: 1; min-width: 0;
    display: flex; align-items: center; gap: .5rem;
    border: 0; background: transparent; text-align: left;
    padding: .4rem .55rem; border-radius: var(--qr-field-radius);
    font-size: var(--qr-fs-84); color: var(--qr-text);
}
.qr-tia-cases__item:hover { background: var(--qr-surface-3); }
.qr-tia-cases__item > i { color: var(--qr-text-secondary); flex: 0 0 auto; }

.qr-tia-cases__del {
    flex: 0 0 auto; border: 0; background: transparent; color: var(--qr-text-faint);
    width: 1.85rem; height: 1.85rem; border-radius: var(--qr-field-radius);
}
.qr-tia-cases__del:hover { background: var(--qr-danger-100); color: var(--qr-danger); }

.qr-tia-cases__form { display: flex; gap: .375rem; padding: .375rem .3rem .2rem; }
.qr-tia-cases__form .form-control { flex: 1; }

/* El payload crudo se lee y se copia; que no envuelva de forma rara importa más que el ancho. */
.qr-tia-payload__pre { max-height: 60vh; overflow: auto; white-space: pre-wrap; word-break: break-word; margin: 0; font-size: var(--qr-fs-78); }


/* ==========================================================================
   Barra de herramientas — `qr-tbar`
   --------------------------------------------------------------------------
   La barra de acciones de un PANEL (el encabezado del editor de prompt, el de un canvas).
   No confundir con `qr-toolbar`, que es la barra de FILTROS: ahí cada `qr-toolbar-group` es
   una COLUMNA (etiqueta arriba, control abajo), así que meterle botones sueltos los apila en
   vertical — que es exactamente el error que motivó esta clase.

   Es una barra, no una fila de botones: las acciones van en GRUPOS SEGMENTADOS
   (`qr-tbar__group`), con un solo marco por grupo y los botones separados por una línea de
   1px. Cinco botones con borde propio se leen como cinco cosas sin relación; un segmento se
   lee como un control. Los grupos también son lo que agrupa por familia — en el editor de
   prompt, lo que toca el PROMPT contra lo que toca las TOOLS.

   Chrome neutro y GLIFO teñido, igual que el header de listado: cinco botones de colores
   distintos compiten entre sí y cinco iconos grises son indistinguibles sin leer el tooltip.
   Y llevan TEXTO: en una barra que se usa todo el tiempo, obligar a pasar el mouse por encima
   para saber qué hace cada icono se paga en cada uso.

   <div class="qr-tbar qr-tbar--flush">
     <span class="qr-tbar__title"><i class="bi bi-file-text"></i>System prompt</span>
     <div class="qr-tbar__group">
       <button class="qr-tbar__btn"><i class="bi bi-journal-text text-indigo-600"></i>Biblioteca</button>
     </div>
     <div class="qr-tbar__group">…</div>
     <div class="qr-tbar__group qr-tbar__end">…</div>
   </div>
   ========================================================================== */

.qr-tbar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .375rem;
    padding: .375rem .5rem;
    background: var(--qr-surface-2);
    border: 1px solid var(--qr-header-border);
    border-radius: var(--qr-field-radius);
}

/* Cuando la barra ES el encabezado de un panel: sin marco propio ni radio, sólo la línea de
   abajo. Un recuadro dentro de otro recuadro se lee como dos cosas distintas. */
.qr-tbar--flush { border: 0; border-radius: 0; border-bottom: 1px solid var(--qr-header-border); }

.qr-tbar__title {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    font-size: var(--qr-fs-70);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--qr-text-muted);
    margin-right: .15rem;
    white-space: nowrap;
}

/* El segmento: UN marco para todo el grupo. Es lo que hace que se lea como una barra y no
   como botones sueltos. */
.qr-tbar__group {
    display: inline-flex;
    align-items: stretch;
    background: var(--qr-surface);
    border: 1px solid var(--qr-header-border);
    border-radius: var(--qr-field-radius);
    overflow: hidden;
}

.qr-tbar__btn {
    display: inline-flex;
    align-items: center;
    gap: .32rem;
    border: 0;
    background: transparent;
    color: var(--qr-text);
    height: 1.8rem;
    padding: 0 .55rem;
    font-size: var(--qr-fs-78);
    font-weight: 600;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
}

/* La línea que separa dos acciones dentro del mismo segmento. */
.qr-tbar__group .qr-tbar__btn + .qr-tbar__btn { border-left: 1px solid var(--qr-header-border); }

.qr-tbar__btn:hover:not(:disabled) { background: var(--qr-surface-3); }
.qr-tbar__btn:active:not(:disabled) { background: var(--qr-surface-strong); }

.qr-tbar__btn:focus-visible {
    outline: 0;
    box-shadow: inset 0 0 0 2px var(--qr-field-focus-ring);
}

/* El estado "abierto" de una acción que despliega algo (un panel, una ventana). */
.qr-tbar__btn.is-on { background: var(--qr-primary-soft); color: var(--qr-primary); }

.qr-tbar__btn:disabled { opacity: .5; cursor: not-allowed; }

.qr-tbar__btn > i { font-size: var(--qr-fs-lg); line-height: 1; }

/* Contador pegado a su acción (las tools cargadas, los errores de una validación). */
.qr-tbar__n {
    font-size: var(--qr-fs-66);
    font-weight: 700;
    line-height: 1;
    padding: .12rem .3rem;
    border-radius: var(--qr-radius-pill);
    background: var(--qr-special-100);
    color: var(--qr-special-700);
}

/* Empuja lo que sigue al extremo derecho. */
.qr-tbar__end { margin-left: auto; }

/* ==========================================================================
   Herramientas por etapa del voicebot (ModalStage > pestaña Herramientas)

   MAESTRO-DETALLE DE ALTO FIJO. Antes era una lista vertical: una tarjeta por
   herramienta con su switch, sus instrucciones y su árbol de acciones, todas
   desplegadas a la vez. Los agentes reales tienen hasta OCHO herramientas —y la
   pestaña las lista todas, porque apagar una no borra su flujo y el tool apagado
   tiene que seguir visible—, así que el modal se volvía una tira interminable y
   había que scrollear la ventana entera para llegar a la de abajo.

   Ahora la lista completa vive a la izquierda (siempre visible) y sólo se
   despliega el detalle de la elegida. El alto lo fija el contenedor y NO depende
   del contenido: cambiar de herramienta no mueve el modal (regla 6 de MODALES.md),
   y cada columna scrollea por su cuenta.
   ========================================================================== */
/* El modal de la etapa: alto fijo DE VERDAD.
   `52vh` era una fracción del viewport, no "viewport menos cromo": con el header, las
   pestañas, los dos avisos y el pie sumando ~448px, en un notebook (viewport ~610px) el
   modal desbordaba ~155px y el botón Guardar quedaba abajo del fondo.

   Ahora el diálogo toma el alto disponible (con techo), el pie queda clavado y el
   maestro-detalle se queda con lo que sobra. Requiere `scrollable` en el <Modal>: es lo que
   pone `modal-dialog-scrollable`, la clase que acota el diálogo. Si el espacio no alcanza,
   scrollea el CUERPO del modal —nunca el overlay con el pie adentro. */
.modal-dialog-scrollable:has(.qr-stagemodal) .modal-content {
    height: 100%;
    max-height: min(100%, 760px);
}
.modal-body:has(> .qr-stagemodal) { display: flex; flex-direction: column; min-height: 0; }
.qr-stagemodal,
.qr-stagemodal__pane { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }

.qr-stagetools {
    display: grid;
    grid-template-columns: 210px minmax(0, 1fr);
    /* Toma el alto que sobra del cuerpo del modal, no una fracción del viewport. El piso es
       la red de seguridad para el caso en que `:has()` no aplique. */
    flex: 1 1 auto;
    min-height: 240px;
    border: 1px solid var(--qr-header-border);
    border-radius: var(--qr-radius-md);
    background: var(--qr-surface);
    overflow: hidden;
}

/* Columna izquierda: la lista completa de herramientas del agente. */
.qr-stagetools__list {
    overflow-y: auto;
    padding: 6px;
    background: var(--qr-body-bg);
    border-right: 1px solid var(--qr-header-border);
}

.qr-stagetools__item {
    width: 100%;
    display: flex; align-items: center; gap: 8px;
    text-align: left;
    padding: 6px 8px;
    border: 1px solid transparent; border-radius: var(--qr-radius-sm-plus);
    background: transparent; color: var(--qr-text-emphasis); cursor: pointer;
}
.qr-stagetools__item + .qr-stagetools__item { margin-top: 2px; }
.qr-stagetools__item:hover { background: var(--qr-surface); border-color: var(--qr-header-border); }
.qr-stagetools__item.is-active {
    background: var(--qr-surface);
    border-color: var(--qr-primary);
    box-shadow: 0 0 0 2px rgba(var(--qr-primary-light-rgb), var(--qr-alpha-15));
}

/* Punto de estado: verde si el bot puede llamarla en esta etapa, gris si está apagada. */
.qr-stagetools__dot { flex: 0 0 auto; width: 8px; height: 8px; border-radius: 50%; background: var(--qr-dot-idle); }
.qr-stagetools__item.is-on .qr-stagetools__dot { background: var(--qr-success-500); }

.qr-stagetools__name {
    flex: 1 1 auto; min-width: 0;
    font-size: var(--qr-fs-78); font-weight: 600;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.qr-stagetools__item.is-off .qr-stagetools__name { font-weight: 500; color: var(--qr-text-secondary); }

/* Conteo de acciones: es lo que hace visible que apagar una herramienta NO borra su flujo. */
.qr-stagetools__count {
    flex: 0 0 auto;
    font-size: var(--qr-fs-66); font-weight: 700; color: var(--qr-text-body);
    background: var(--qr-surface-strong); border-radius: var(--qr-radius-pill);
    padding: 0 .38rem; min-width: 1.1rem; text-align: center;
}

/* Columna derecha: el detalle de la elegida. */
.qr-stagetools__detail { overflow-y: auto; min-width: 0; padding: 12px 14px; }

.qr-stagetools__head { display: flex; align-items: center; gap: 10px; }
.qr-stagetools__title { font-weight: 600; }
.qr-stagetools__kept { margin-left: auto; font-size: var(--qr-fs-sm); color: var(--qr-text-secondary); white-space: nowrap; }

.qr-stagetools__body { margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--qr-header-border); }

/* Red de seguridad: hoy NO es alcanzable —el caso "la etapa no tiene herramientas" lo
   atrapa antes el `{:else if !stage_tools.length}` del modal, y con lista no vacía
   `seleccionInicial()` siempre deja una elegida. Se conserva para que el panel derecho
   nunca quede en blanco si alguna de esas dos cosas cambia. */
.qr-stagetools__empty {
    display: flex; align-items: center; gap: 6px;
    height: 100%; justify-content: center;
    font-size: var(--qr-fs-78); color: var(--qr-text-secondary);
}

/* Vista previa de las instrucciones de la etapa: se estira igual que el maestro-detalle,
   así cambiar de pestaña tampoco mueve el modal (antes era un style inline con
   min-height/max-height a mano). */
.qr-stage-preview {
    flex: 1 1 auto;
    min-height: 240px;
    overflow-y: auto;
    padding: 1rem;
    border: 1px solid var(--qr-header-border);
    border-radius: var(--qr-radius-md);
    background: var(--qr-body-bg);
}

/* ══════════════════════════════════════════════════════════════════════════
   DISEÑADOR DE VARIABLES — `VariablesDesigner.svelte` + `VariableNode.svelte`

   Es una TABLA de datos: encabezado (Nombre · Tipo · Valor · acciones) y una fila por
   variable, todas del mismo alto (`--qr-control-height-sm`). Los campos no llevan borde
   propio; el realce aparece sólo en foco. Un grupo o una lista contiene otras filas.

   La sangría NO se calcula por nivel: la pone el contenedor de los hijos (`__hijos`) con su
   margen y su línea vertical, y se compone sola con la recursión.
   ══════════════════════════════════════════════════════════════════════════ */

.qr-vars { display: flex; flex-direction: column; gap: .4rem; --qr-vars-cols: minmax(6rem, 34%) 7.2rem minmax(0, 1fr) 3.4rem; }
.qr-vars.is-disabled { opacity: .6; pointer-events: none; }

/* Barra mínima: título + contador + ayuda a la izquierda, botones chicos a la derecha. */
.qr-vars__head { display: flex; align-items: center; justify-content: space-between; gap: .5rem; flex-wrap: nowrap; }
.qr-vars__title {
    display: inline-flex; align-items: center; gap: .35rem; min-width: 0;
    font-size: var(--qr-fs-82); font-weight: 600; color: var(--qr-text-strong);
}
.qr-vars__titulo { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qr-vars__title .bi-braces { color: var(--qr-primary); }
.qr-vars__n {
    background: var(--qr-surface-strong); color: var(--qr-text-body); font-size: var(--qr-fs-70); font-weight: 700;
    border-radius: var(--qr-radius-pill); padding: 0 .4rem; min-width: 1.15rem; text-align: center;
}
.qr-vars__info { border: none; background: none; padding: 0 .15rem; color: var(--qr-text-muted); line-height: 1; }
.qr-vars__info:hover, .qr-vars__info.is-on { color: var(--qr-primary); }
.qr-vars__tools { display: flex; align-items: center; gap: .3rem; flex: 0 0 auto; }
.qr-vars__tools .btn { display: inline-flex; align-items: center; gap: .25rem; white-space: nowrap; }
.qr-vars__help { font-size: var(--qr-fs-78); color: var(--qr-text-secondary); }
.qr-vars__hint { font-size: var(--qr-fs-75); color: var(--qr-text-muted); }

/* La tabla */
.qr-vars__tabla {
    border: 1px solid var(--qr-header-border); border-radius: var(--qr-radius-md);
    background: var(--qr-surface); overflow: hidden;
}
.qr-vars__thead, .qr-var__row {
    display: grid; grid-template-columns: var(--qr-vars-cols); align-items: center; column-gap: .25rem;
    padding: 0 .35rem;
}
.qr-vars__thead {
    background: var(--qr-table-header-bg); color: var(--qr-table-header-color);
    font-size: var(--qr-fs-70); font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
    padding-top: .25rem; padding-bottom: .25rem; border-bottom: 1px solid var(--qr-border);
}
.qr-vars__thead > span { padding-left: .45rem; }
.qr-vars__thead > span:first-child { padding-left: 1.55rem; }   /* alinea con el campo, pasado el plegado */

/* Vacío: una línea con su acción. */
.qr-vars__vacio {
    display: flex; align-items: center; justify-content: space-between; gap: .5rem;
    padding: .4rem .6rem; color: var(--qr-text-secondary); font-size: var(--qr-fs-78);
}

.qr-vars__json { min-height: 220px; font-size: var(--qr-fs-sm); }

.qr-vars__sugerencias { display: flex; align-items: center; flex-wrap: wrap; gap: .3rem; }
.qr-vars__chip {
    display: inline-flex; align-items: center; gap: .2rem;
    border: 1px dashed var(--qr-border-strong); background: var(--qr-surface); color: var(--qr-text-body);
    border-radius: var(--qr-radius-pill); padding: .12rem .6rem; font-size: var(--qr-fs-75); font-weight: 600;
    cursor: pointer; transition: background .12s, border-color .12s, color .12s;
}
.qr-vars__chip:hover { background: var(--qr-surface-sunken); border-color: var(--qr-border-emphasis); color: var(--qr-text-emphasis); }

.qr-vars__avisos { font-size: var(--qr-fs-75); color: var(--qr-warning-700); }
.qr-vars__avisos .bi { margin-right: .3rem; }

/* ── Una fila ─────────────────────────────────────────────────────────── */
.qr-var__row { min-height: calc(var(--qr-control-height-sm) + .4rem); border-bottom: 1px solid var(--qr-border); transition: background .1s; }
.qr-vars__tbody > .qr-var:last-child > .qr-var__row { border-bottom: none; }
.qr-var__row:hover { background: var(--qr-surface-2); }
.qr-var__row:focus-within { background: var(--qr-surface-2); }

.qr-var__celda { display: flex; align-items: center; min-width: 0; position: relative; border-radius: var(--qr-radius-sm); }
.qr-var__celda.is-bad { box-shadow: inset 0 0 0 1px var(--qr-danger); background: var(--qr-danger-50); }

/* Campos de la celda: sin borde hasta el foco. */
.qr-var__input {
    width: 100%; min-width: 0; height: var(--qr-control-height-sm); padding: 0 .45rem;
    font-size: var(--qr-fs-82); color: var(--qr-text-body);
    background: transparent; border: 1px solid transparent; border-radius: var(--qr-radius-sm);
    text-overflow: ellipsis; outline: none;
}
.qr-var__input::placeholder { color: var(--qr-text-muted); }
.qr-var__input:hover { border-color: var(--qr-border); }
.qr-var__input:focus {
    background: var(--qr-surface); border-color: var(--qr-focus-border); box-shadow: var(--qr-focus-ring);
    text-overflow: clip;
}
.qr-var__nombre { font-family: var(--bs-font-monospace); font-weight: 600; }

.qr-var__caret {
    flex: 0 0 auto; width: 1.1rem; height: 1.1rem; padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    border: none; background: none; color: var(--qr-text-muted); border-radius: var(--qr-radius-sm);
}
.qr-var__caret:hover { background: var(--qr-surface-strong); color: var(--qr-text-body); }
.qr-var__caret--vacio { pointer-events: none; }
.qr-var__indice {
    flex: 0 0 auto; min-width: 1.8rem; margin-left: .3rem; text-align: center;
    font-size: var(--qr-fs-xs); font-weight: 700; color: var(--qr-text-secondary);
    background: var(--qr-surface-strong); border-radius: var(--qr-radius-sm); padding: .1rem .3rem;
}

/* Tipo: select compacto con el ícono del tipo adelante */
.qr-var__tipo-ico {
    position: absolute; left: .45rem; font-size: var(--qr-fs-75); color: var(--qr-text-muted); pointer-events: none;
}
.qr-var__tipo { padding-left: 1.5rem; cursor: pointer; color: var(--qr-text-secondary); }

.qr-var__switch { display: inline-flex; align-items: center; gap: .4rem; padding-left: .45rem; min-height: var(--qr-control-height-sm); }
.qr-var__switch .form-check-input { margin: 0; float: none; }
.qr-var__resumen {
    border: none; background: none; padding: 0 .45rem; font-size: var(--qr-fs-78); color: var(--qr-text-secondary);
    min-height: var(--qr-control-height-sm); text-align: left;
}
.qr-var__resumen:hover { color: var(--qr-primary); }
.qr-var__celda--valor > .qr-var__hint { padding-left: .45rem; }

/* Acciones: íconos chicos que se vuelven protagonistas con el hover de la fila */
.qr-var__acciones { display: flex; align-items: center; justify-content: flex-end; gap: .1rem; }
.qr-var__ico {
    width: var(--qr-control-height-sm); height: var(--qr-control-height-sm); padding: 0;
    display: inline-flex; align-items: center; justify-content: center; flex: 0 0 auto;
    border: none; background: none; border-radius: var(--qr-radius-sm);
    font-size: var(--qr-fs-78); color: var(--qr-text-muted);
}
.qr-var__ico:hover { background: var(--qr-surface-strong); color: var(--qr-text-body); }
.qr-var__ico--primary { color: var(--qr-primary); }
.qr-var__ico--quitar:hover, .qr-var__ico--danger { color: var(--qr-danger); }

.qr-var__confirma { padding: 0 .6rem .25rem 1.7rem; font-size: var(--qr-fs-75); color: var(--qr-danger); }

/* La línea vertical es lo que hace legible el anidado. */
.qr-var__hijos {
    margin-left: 1.1rem; border-left: 1px solid var(--qr-border); background: var(--qr-surface-2);
    border-bottom: 1px solid var(--qr-border);
}
.qr-var__hijos > .qr-var:last-of-type > .qr-var__row { border-bottom: none; }

.qr-var__agregar {
    display: inline-flex; align-items: center; gap: .3rem;
    border: none; background: none; color: var(--qr-primary);
    font-size: var(--qr-fs-76); font-weight: 600; padding: .25rem .6rem; margin: 0;
    border-radius: var(--qr-radius-sm);
}
.qr-var__agregar:hover { background: var(--qr-primary-soft); }

/* ==========================================================================
   Paleta de acciones del diseñador de voicebot v2 (qr-vbpal-*)
   --------------------------------------------------------------------------
   Flota sobre el lienzo de SvelteFlow, pegada a la izquierda y debajo de la
   botonera (.vb-overlay--tl, que vive en top:10px). Va acá y no en el <style>
   del componente porque un estilo scoped de Svelte le gana al global por
   especificidad y desincroniza la pantalla del resto del sistema; además esto
   aplica en runtime, sin rebuild de los ~21 bundles.
   ========================================================================== */
.qr-vbpal {
    position: absolute; top: 52px; left: 10px; z-index: 18;
    width: 236px; max-height: calc(100% - 96px);
    display: flex; flex-direction: column;
    background: var(--qr-surface); border: 1px solid var(--qr-border); border-radius: var(--qr-radius-md);
    box-shadow: 0 4px 16px rgba(var(--qr-shade-rgb), var(--qr-alpha-10));
}

.qr-vbpal-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 7px 10px; font-size: var(--qr-fs-78); font-weight: 700; color: var(--qr-text-strong);
    border-bottom: 1px solid var(--qr-border-soft);
}

.qr-vbpal-close { border: 0; background: transparent; color: var(--qr-text-muted); cursor: pointer; line-height: 1; }
.qr-vbpal-close:hover { color: var(--qr-primary); }

/* Buscador: la diferencia con la paleta del chatbot. Con diez acciones ya cuesta
   encontrar una de memoria, y filtra también por descripción. */
.qr-vbpal-search {
    display: flex; align-items: center; gap: 6px;
    margin: 7px 8px 0; padding: 3px 7px;
    border: 1px solid var(--qr-border); border-radius: var(--qr-radius-sm-plus); background: var(--qr-surface-2);
}
.qr-vbpal-search > i { font-size: var(--qr-fs-xs); color: var(--qr-text-muted); }
.qr-vbpal-search input {
    flex: 1; min-width: 0; border: 0; background: transparent; outline: none;
    font-size: var(--qr-fs-76); color: var(--qr-text-emphasis); padding: 2px 0;
}
.qr-vbpal-search button { border: 0; background: transparent; color: var(--qr-text-muted); cursor: pointer; font-size: var(--qr-fs-62); line-height: 1; }
.qr-vbpal-search button:hover { color: var(--qr-danger-600); }

.qr-vbpal-hint { font-size: var(--qr-fs-66); color: var(--qr-text-muted); padding: 6px 10px 0; }

/* Dos secciones que no se pueden confundir (VB-027..030): "Etapas" crea una etapa y
   "Acciones de herramienta" va DENTRO de la caja de una herramienta. Cada una con su
   ícono y su borde de color: antes era una sola lista y una acción soltada en el vacío
   parecía crear una etapa. */
.qr-vbpal-sec {
    margin: 6px 2px 8px; padding-bottom: 4px;
    border: 1px solid var(--qr-border-soft); border-left: 3px solid var(--qr-border);
    border-radius: var(--qr-radius-sm-plus);
}
.qr-vbpal-sec-head {
    display: flex; align-items: center; gap: 6px; padding: 5px 8px;
    font-size: var(--qr-fs-68); font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
}
.qr-vbpal-sec--stage { border-left-color: var(--qr-primary); }
.qr-vbpal-sec--stage .qr-vbpal-sec-head { color: var(--qr-primary); background: var(--qr-primary-soft); }
.qr-vbpal-sec--action { border-left-color: var(--qr-info); }
.qr-vbpal-sec--action .qr-vbpal-sec-head { color: var(--qr-info-text); background: var(--qr-info-bg); }
.qr-vbpal-sec-icon { color: var(--qr-primary); }
.qr-vbpal-sec .qr-vbpal-hint { padding: 4px 8px 2px; }
.qr-vbpal-sec .qr-vbpal-search { margin: 6px 6px 2px; }
.qr-vbpal-item:focus-visible { outline: 2px solid var(--qr-primary); outline-offset: 1px; }

/* Menú "¿en qué etapa y herramienta?" (click en una acción): el nombre de cada etapa
   encabeza sus herramientas. */
.qr-vbpal-menu-stage {
    margin: 8px 0 2px; font-size: var(--qr-fs-68); font-weight: 700;
    text-transform: uppercase; letter-spacing: .03em; color: var(--qr-text-secondary);
}

/* Voicebot · a dónde va una transferencia. `qr-vb-target-line` es la confirmación bajo el
   selector del destino ("Va a transferir a: Soporte (#6)"); `qr-vb-target-warn` marca un
   destino que no existe, está borrado o inactivo (formulario, lista de acciones y lienzo). */
.qr-vb-target-line { margin-top: 4px; font-size: var(--qr-fs-xs); color: var(--qr-text-secondary); }
.qr-vb-target-line strong { color: var(--qr-text-body); }
.qr-vb-target-warn, .qr-vb-target-line.qr-vb-target-warn { color: var(--qr-warn-text); }

.qr-vbpal-body { overflow: auto; padding: 4px 6px 8px; }

.qr-vbpal-group {
    font-size: var(--qr-fs-63); font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
    padding: 7px 4px 2px;
}

.qr-vbpal-item {
    display: flex; align-items: flex-start; gap: 7px;
    padding: 5px 8px; border-radius: var(--qr-radius-sm-plus); cursor: grab;
    border: 1px solid transparent; color: var(--qr-text-emphasis);
}
.qr-vbpal-item:hover { background: var(--qr-surface-2); border-color: var(--qr-border); }
.qr-vbpal-item:active { cursor: grabbing; }
.qr-vbpal-item > .bi { font-size: var(--qr-fs-82); line-height: 1.35; }
.qr-vbpal-item-txt { min-width: 0; display: flex; flex-direction: column; }
.qr-vbpal-item-label { font-size: var(--qr-fs-76); font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Dos líneas de descripción y corte: es la "indicación de qué hace", no un párrafo. */
.qr-vbpal-item-desc {
    font-size: var(--qr-fs-64); color: var(--qr-text-muted); line-height: 1.25;
    display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

.qr-vbpal-empty {
    display: flex; align-items: center; gap: 6px;
    font-size: var(--qr-fs-70); color: var(--qr-text-muted); padding: 12px 8px; text-align: left;
}

/* Colapsada: una pestaña pegada al borde, para no perder el acceso ni el ancho. */
.qr-vbpal-tab {
    position: absolute; top: 52px; left: 0; z-index: 18;
    border: 1px solid var(--qr-border); border-left: 0; border-radius: 0 var(--qr-radius-md) var(--qr-radius-md) 0;
    background: var(--qr-surface); color: var(--qr-primary); padding: 8px 7px; cursor: pointer;
    box-shadow: 0 2px 6px rgba(var(--qr-shade-rgb), var(--qr-alpha-8));
}
.qr-vbpal-tab:hover { background: var(--qr-surface-2); }

/* Caja de herramienta resaltada mientras se arrastra una acción encima. La clase la
   pone/saca el diseñador por DOM (no por estado reactivo): el $effect que carga los
   vínculos etapa↔herramienta dispara una consulta por etapa, y no puede repetirse en
   cada dragover. */
.svelte-flow__node.qr-vbpal-drop-ok > * {
    outline: 2px dashed var(--qr-primary);
    outline-offset: 3px;
    border-radius: var(--qr-radius-sm-plus);
}

/* Mientras se arrastra una acción de la paleta (`qr-vbpal-dragging` en el lienzo), el
   diseñador marca por DOM qué la acepta: la caja de una herramienta prendida
   (`qr-vbpal-target`) y una etapa con herramientas (`qr-vbpal-target-stage`, al soltar
   pregunta en cuál). El resto (etapas sin herramientas, conectores, el fondo) se atenúa y
   el navegador muestra "no permitido". El destino bajo el puntero pasa a borde lleno. */
.svelte-flow__node.qr-vbpal-target > *,
.svelte-flow__node.qr-vbpal-target-stage > * {
    outline: 2px dashed var(--qr-primary);
    outline-offset: 3px;
    border-radius: var(--qr-radius-sm-plus);
}
.svelte-flow__node.qr-vbpal-target::after,
.svelte-flow__node.qr-vbpal-target-stage::after {
    content: 'Soltá acá';
    position: absolute; top: -13px; right: 6px; z-index: 5; pointer-events: none;
    padding: 1px 7px; border-radius: var(--qr-radius-pill);
    font-size: var(--qr-fs-62); font-weight: 700; white-space: nowrap;
    color: var(--qr-on-primary); background: var(--qr-primary);
}
.svelte-flow__node.qr-vbpal-target-stage::after { content: 'Soltá acá · elegís la herramienta'; }
.svelte-flow__node.qr-vbpal-drop-ok > * { outline-style: solid; }
.svelte-flow__node.qr-vbpal-invalid { opacity: .3; filter: grayscale(70%); }
.qr-vbpal-dragging .svelte-flow__edges,
.qr-vbpal-dragging .svelte-flow__edgelabel-renderer,
.qr-vbpal-dragging .svelte-flow__background { opacity: .3; }

/* Modo enfoque del diseñador: nodo fuera de la vecindad de la etapa seleccionada
   (`buildGraph` en VoicebotDesigner.svelte pone `vb-dim` en `n.class`, que SvelteFlow
   aplica al wrapper `.svelte-flow__node`). */
.svelte-flow__node.vb-dim {
    opacity: .22;
    filter: grayscale(55%);
    transition: opacity .15s ease, filter .15s ease;
}

/* ==========================================================================
   PROBADOR DEL DISEÑADOR DE VOICEBOT (`qr-vbtest-*`)
   --------------------------------------------------------------------------
   Tab "Probar" de /voicebot/v2/edit/<id>/: el formulario de contexto a la
   izquierda y el panel en vivo (conversación + traza) a la derecha.

   Va acá y no en el `<style>` del componente porque un estilo scoped de Svelte
   le gana al global por especificidad y desincroniza la pantalla del resto.
   ========================================================================== */

/* Dos columnas: el contexto se define una vez y se mira poco, la conversación
   se mira todo el tiempo — por eso el panel vivo se lleva el ancho. */
.qr-vbtest { display: grid; grid-template-columns: 22rem 1fr; gap: 1rem; align-items: start; }
@media (max-width: 1200px) { .qr-vbtest { grid-template-columns: 1fr; } }

.qr-vbtest-form { display: flex; flex-direction: column; gap: .75rem; }

/* Tabla clave/valor de las variables sueltas. */
.qr-vbtest-vars { display: flex; flex-direction: column; gap: .375rem; }
.qr-vbtest-var { display: grid; grid-template-columns: 1fr 1fr auto; gap: .375rem; align-items: center; }

/* Barra de estado de la prueba: el punto de color y el texto de qué está pasando. */
.qr-vbtest-status {
    display: flex; align-items: center; flex-wrap: wrap; gap: .5rem;
    padding: .5rem .625rem;
    border: 1px solid var(--qr-header-border);
    border-radius: var(--qr-field-radius);
    background: var(--qr-surface-2);
    font-size: var(--qr-fs-82);
}
.qr-vbtest-status__dot { width: .625rem; height: .625rem; border-radius: 50%; background: var(--qr-dot-idle); flex: 0 0 auto; }
.qr-vbtest-status__dot.is-live { background: var(--qr-success-600); animation: qr-vbtest-pulse 1.4s ease-in-out infinite; }
.qr-vbtest-status__dot.is-ringing { background: var(--qr-warning); animation: qr-vbtest-pulse 1s ease-in-out infinite; }
.qr-vbtest-status__dot.is-ended { background: var(--qr-dot-muted); }
.qr-vbtest-status__dot.is-error { background: var(--qr-danger-600); }

@keyframes qr-vbtest-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }

/* Panel vivo. El scroll vive adentro de cada zona: un scroll de página aleja
   el botón "Cortar" de lo que se está leyendo. */
.qr-vbtest-live { display: flex; flex-direction: column; gap: .75rem; min-width: 0; }

.qr-vbtest-panel {
    border: 1px solid var(--qr-header-border);
    border-radius: var(--qr-field-radius);
    background: var(--qr-surface);
    display: flex; flex-direction: column; min-height: 0;
}
.qr-vbtest-panel__head {
    display: flex; align-items: center; gap: .5rem;
    padding: .375rem .625rem;
    border-bottom: 1px solid var(--qr-header-border);
    font-size: var(--qr-fs-78); font-weight: 600; color: var(--qr-text-body);
}
.qr-vbtest-panel__body { overflow-y: auto; padding: .625rem; }
.qr-vbtest-panel__body--talk { height: 22rem; background: var(--qr-body-bg); }
.qr-vbtest-panel__body--trace { height: 16rem; }

.qr-vbtest-empty { font-size: var(--qr-fs-sm); color: var(--qr-text-muted); text-align: center; margin-top: 2rem; }

/* Un turno de la conversación. El humano a la izquierda, el bot a la derecha:
   quién dijo qué se lee por posición, sin tener que leer la etiqueta. */
.qr-vbtest-turn { display: flex; margin-bottom: .5rem; }
.qr-vbtest-turn.is-bot { justify-content: flex-end; }

.qr-vbtest-bubble {
    max-width: 82%;
    padding: .4rem .55rem;
    border-radius: var(--qr-radius-md-plus);
    font-size: var(--qr-fs-84);
    background: var(--qr-surface);
    border: 1px solid var(--qr-header-border);
    box-shadow: var(--qr-shadow-xs);
}
.qr-vbtest-bubble.is-human { border-bottom-left-radius: var(--qr-radius-xs); }
.qr-vbtest-bubble.is-bot {
    background: var(--qr-primary);
    border-color: var(--qr-primary);
    color: var(--qr-text-inverse);
    border-bottom-right-radius: var(--qr-radius-xs);
}
.qr-vbtest-bubble__meta {
    display: flex; align-items: center; justify-content: space-between; gap: .5rem;
    font-size: var(--qr-fs-66); font-weight: 600; opacity: .75; margin-bottom: .15rem;
}
.qr-vbtest-bubble__body { white-space: pre-wrap; word-break: break-word; }

/* Una línea de la traza. Es lo que hace que el probador sirva para depurar:
   sin ella esto es un teléfono con pasos extra. */
.qr-vbtest-trace { display: flex; flex-direction: column; gap: .25rem; }
.qr-vbtest-trace__row {
    display: grid; grid-template-columns: 4.25rem 1fr; gap: .5rem;
    font-size: var(--qr-fs-76); line-height: 1.35;
    padding: .25rem .35rem;
    border-left: 3px solid var(--qr-border);
    background: var(--qr-surface-2);
    border-radius: 0 var(--qr-radius-sm) var(--qr-radius-sm) 0;
}
.qr-vbtest-trace__row.is-stage { border-left-color: var(--qr-primary-light); }
.qr-vbtest-trace__row.is-tool { border-left-color: var(--qr-warning); }
.qr-vbtest-trace__row.is-result { border-left-color: var(--qr-success); }
.qr-vbtest-trace__row.is-state { border-left-color: var(--qr-border-emphasis); }
.qr-vbtest-trace__row.is-error { border-left-color: var(--qr-danger-600); background: var(--qr-danger-50); }
/* Voz a voz: formato de audio / interrupciones, y consumo del proveedor. */
.qr-vbtest-trace__row.is-audio { border-left-color: var(--qr-info-600); }
.qr-vbtest-trace__row.is-usage { border-left-color: var(--qr-special-600); }

.qr-vbtest-trace__time { color: var(--qr-text-muted); font-variant-numeric: tabular-nums; }
.qr-vbtest-trace__name { font-weight: 600; color: var(--qr-text-strong); }
.qr-vbtest-trace__data {
    white-space: pre-wrap; word-break: break-word;
    margin: .2rem 0 0; font-size: var(--qr-fs-70); color: var(--qr-text-body);
    max-height: 9rem; overflow: auto;
}

/* Probador en VENTANA FLOTANTE (FloatingWindow): columna única, con tres pestañas
   (Configuración / Conversación / Depuración). `FloatingWindow.fw-body` ya deja a su hijo
   directo con `flex:1 1 auto; min-height:0`, así que esta columna puede repartir su alto
   entre lo fijo (estado, avisos, tabs, botones) y lo único que scrollea (`qr-vbtest-scroll`).
   Sin el `min-height:0` acá el contenido empujaría el alto del panel más allá de la ventana
   en vez de scrollear adentro (el bug que se reportó: "no tiene scroll"). */
.qr-vbtest--floating {
    display: flex; flex-direction: column; gap: .5rem;
    height: 100%; min-height: 0;
}
.qr-vbtest--floating .qr-vbtest-turn { cursor: pointer; }

/* Único contenedor que scrollea. El resto (estado, avisos, pestañas, botones) es `flex:none`
   y queda siempre a la vista. */
.qr-vbtest-scroll { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
/* Padding lateral, superior e inferior del contenido (pedido explícito: antes el contenido
   tocaba los bordes de la ventana). */
.qr-vbtest-pad { display: flex; flex-direction: column; gap: .875rem; padding: .875rem 1rem 1.125rem; }

/* Barra de botones: SIEMPRE visible, fija abajo, fuera del área que scrollea. `align-items:
   center` es lo que evita que un botón se estire en alto dentro de la fila flex. */
.qr-vbtest-footer {
    flex: none;
    display: flex; align-items: center; gap: .375rem;
    padding: .5rem .75rem;
    border-top: 1px solid var(--qr-header-border);
    background: var(--qr-surface-2);
}

/* Un evento con etapa/herramienta se puede clickear: el lienzo lo centra y resalta. */
.qr-vbtest-trace__row.is-linked { cursor: pointer; }
.qr-vbtest-trace__row.is-linked:hover { background: var(--qr-surface-2); }
.qr-vbtest-trace__row.is-selected { background: var(--qr-primary-soft); }
.qr-vbtest-trace__row:focus-visible { outline: 2px solid var(--qr-primary); outline-offset: 1px; }

/* ==========================================================================================
   RECORRIDO DE LA LLAMADA (`qr-vb-trail-*`): el probador agrupa los eventos por turno
   (cliente dijo → la IA pidió un tool → resultado → el bot dijo). Lo pinta
   `VoicebotTrail.svelte` con lo que arma `utils/voicebot_trail.js`.
   ========================================================================================== */
.qr-vb-trail { display: flex; flex-direction: column; gap: .625rem; min-width: 0; }

/* Encabezado: etapa actual, duración, consumo y avisos. */
.qr-vb-trail-head {
    display: flex; flex-wrap: wrap; align-items: center; gap: .375rem .875rem;
    padding: .4rem .6rem; font-size: var(--qr-fs-76);
    background: var(--qr-surface-2); border: 1px solid var(--qr-border); border-radius: var(--qr-radius-md);
}
.qr-vb-trail-head__stat { display: inline-flex; align-items: center; gap: .3rem; color: var(--qr-text-body); }
.qr-vb-trail-head__stat b { color: var(--qr-text-strong); font-variant-numeric: tabular-nums; }
.qr-vb-trail-head__switch { margin-left: auto; display: inline-flex; align-items: center; gap: .35rem; cursor: pointer; }

/* Un turno: arranca con la llamada o con cada frase del cliente. */
.qr-vb-trail-turn {
    display: flex; flex-direction: column; gap: .4rem; padding: .5rem .6rem;
    background: var(--qr-surface); border: 1px solid var(--qr-border); border-radius: var(--qr-radius-md);
}
.qr-vb-trail-turn.has-warn { border-left: 3px solid var(--qr-warning); }
.qr-vb-trail-turn__head {
    display: flex; align-items: center; gap: .5rem; font-size: var(--qr-fs-70);
    font-weight: 600; color: var(--qr-text-body); text-transform: uppercase; letter-spacing: .03em;
}
.qr-vb-trail-turn__warns { display: flex; flex-direction: column; gap: .25rem; }
.qr-vb-trail-turn__warn {
    display: flex; gap: .4rem; align-items: baseline; font-size: var(--qr-fs-73);
    padding: .25rem .4rem; border-radius: var(--qr-radius-sm);
    color: var(--qr-warning-800); background: var(--qr-warning-50);
}

/* Cada ítem del turno: tiempo relativo a la izquierda, contenido a la derecha. */
.qr-vb-trail-item { display: grid; grid-template-columns: 3.4rem minmax(0, 1fr); gap: .5rem; align-items: start; }
.qr-vb-trail-time { font-size: var(--qr-fs-70); color: var(--qr-text-muted); font-variant-numeric: tabular-nums; padding-top: .2rem; }
.qr-vb-trail-body { display: flex; flex-direction: column; gap: .25rem; min-width: 0; }

/* Lo que se dijo: el cliente, el bot y lo que el motor no tomó. */
.qr-vb-trail-say {
    padding: .35rem .55rem; font-size: var(--qr-fs-84); white-space: pre-wrap; word-break: break-word;
    border-radius: var(--qr-radius-md-plus); border: 1px solid var(--qr-header-border); background: var(--qr-surface);
}
.qr-vb-trail-say.is-customer { border-bottom-left-radius: var(--qr-radius-xs); }
.qr-vb-trail-say.is-bot {
    background: var(--qr-primary-soft); border-color: var(--qr-primary-border-subtle);
    color: var(--qr-text-strong); border-bottom-right-radius: var(--qr-radius-xs);
}
.qr-vb-trail-say.is-ignored { border-style: dashed; background: var(--qr-surface-2); color: var(--qr-text-body); }
.qr-vb-trail-say.is-dropped { color: var(--qr-text-muted); text-decoration: line-through; background: var(--qr-surface-2); }
.qr-vb-trail-say.is-muted { color: var(--qr-text-muted); background: var(--qr-surface-2); }
.qr-vb-trail-flags { display: flex; flex-wrap: wrap; align-items: center; gap: .25rem; }
.qr-vb-trail-hint { font-size: var(--qr-fs-70); color: var(--qr-text-muted); }

/* Tarjeta de un tool pedido por la IA: argumentos, acciones, variables y resultado. */
.qr-vb-trail-tool {
    display: flex; flex-direction: column; gap: .35rem; padding: .45rem .55rem;
    background: var(--qr-surface-2); border: 1px solid var(--qr-border); border-left: 3px solid var(--qr-warning);
    border-radius: var(--qr-radius-sm-plus); font-size: var(--qr-fs-76);
}
.qr-vb-trail-tool.is-ok { border-left-color: var(--qr-success); }
.qr-vb-trail-tool.is-bad { border-left-color: var(--qr-danger-600); background: var(--qr-danger-50); }
.qr-vb-trail-tool__head { display: flex; align-items: center; flex-wrap: wrap; gap: .4rem; }
.qr-vb-trail-tool__name { font-family: var(--qr-font-mono); font-weight: 700; color: var(--qr-text-strong); }
.qr-vb-trail-args { display: grid; grid-template-columns: max-content minmax(0, 1fr); gap: .1rem .6rem; margin: 0; }
.qr-vb-trail-args dt { font-weight: 600; color: var(--qr-text-body); }
.qr-vb-trail-args dd { margin: 0; color: var(--qr-text-strong); word-break: break-word; font-family: var(--qr-font-mono); }
.qr-vb-trail-action { display: flex; flex-direction: column; gap: .15rem; }
.qr-vb-trail-action__line { display: flex; align-items: center; flex-wrap: wrap; gap: .35rem; }
.qr-vb-trail-action.is-ok .qr-vb-trail-action__mark { color: var(--qr-success-700); }
.qr-vb-trail-action.is-failed .qr-vb-trail-action__mark { color: var(--qr-danger-700); }
.qr-vb-trail-action.is-skipped .qr-vb-trail-action__mark { color: var(--qr-text-muted); }
.qr-vb-trail-api { font-size: var(--qr-fs-70); color: var(--qr-text-body); font-family: var(--qr-font-mono); padding-left: 1rem; }
.qr-vb-trail-chips { display: flex; flex-wrap: wrap; gap: .25rem; }
.qr-vb-trail-chip {
    display: inline-flex; gap: .25rem; max-width: 100%; padding: .05rem .45rem; font-size: var(--qr-fs-70);
    border-radius: var(--qr-radius-pill); color: var(--qr-special-700); background: var(--qr-special-100);
}
.qr-vb-trail-chip > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qr-vb-trail-pre {
    margin: .2rem 0 0; padding: .35rem .5rem; max-height: 12rem; overflow: auto; white-space: pre-wrap; word-break: break-word;
    font-family: var(--qr-font-mono); font-size: var(--qr-fs-70); color: var(--qr-text-body);
    background: var(--qr-surface); border: 1px solid var(--qr-border-soft); border-radius: var(--qr-radius-sm);
}

/* Separador de cambio de etapa: se tiene que ver de lejos. */
.qr-vb-trail-stage {
    display: flex; flex-direction: column; gap: .25rem; padding: .4rem .6rem;
    background: var(--qr-primary-soft); border-top: 2px solid var(--qr-primary); border-bottom: 1px solid var(--qr-primary-border-subtle);
    border-radius: var(--qr-radius-sm); font-size: var(--qr-fs-78);
}
.qr-vb-trail-stage.is-initial { border-top-color: var(--qr-border-strong); background: var(--qr-surface-2); }
.qr-vb-trail-stage__route { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; font-weight: 700; color: var(--qr-primary-ink); }
.qr-vb-trail-stage__link { cursor: pointer; background: none; border: 0; padding: 0; font: inherit; color: inherit; text-decoration: underline dotted; }
.qr-vb-trail-stage__link:hover { color: var(--qr-primary-hover); }
.qr-vb-trail-stage__meta { font-weight: 400; color: var(--qr-text-body); }

/* Avisos sueltos del turno: errores, DTMF, transferencia, cierre. */
.qr-vb-trail-note {
    display: flex; align-items: baseline; flex-wrap: wrap; gap: .4rem; padding: .25rem .5rem;
    font-size: var(--qr-fs-76); color: var(--qr-text-body);
    background: var(--qr-surface-2); border-left: 3px solid var(--qr-border-emphasis); border-radius: 0 var(--qr-radius-sm) var(--qr-radius-sm) 0;
}
.qr-vb-trail-note.is-error { color: var(--qr-danger-800); background: var(--qr-danger-50); border-left-color: var(--qr-danger-600); }
.qr-vb-trail-note.is-end { color: var(--qr-text-strong); font-weight: 600; border-left-color: var(--qr-text-body); }
.qr-vb-trail-note.is-tech { font-family: var(--qr-font-mono); font-size: var(--qr-fs-70); color: var(--qr-text-muted); }
.qr-vb-trail-empty { font-size: var(--qr-fs-sm); color: var(--qr-text-muted); text-align: center; margin-top: 2rem; }

/* Recorrido EN VIVO en el lienzo del diseñador (`applyLiveMarks` en VoicebotDesigner, por
   DOM): etapa/caja en curso con borde de alerta, lo ya visitado en verde con su número de
   orden, y cada acción que corrió marcada (hecha / falló / salteada). */
.svelte-flow__node.vb-live-visited > * { outline: 2px solid var(--qr-success); outline-offset: 2px; border-radius: var(--qr-radius-sm-plus); }
.svelte-flow__node.vb-live-current > * { outline: 3px solid var(--qr-warning); outline-offset: 3px; border-radius: var(--qr-radius-sm-plus); }
.svelte-flow__node.vb-live-visited[data-live-order]::before {
    content: attr(data-live-order);
    position: absolute; top: -11px; left: -9px; z-index: 5; pointer-events: none;
    min-width: 20px; padding: 1px 6px; border-radius: var(--qr-radius-pill); text-align: center;
    font-size: var(--qr-fs-62); font-weight: 700; color: var(--qr-text-inverse); background: var(--qr-success-600);
}
.svelte-flow__node.vb-live-current[data-live-order]::before { background: var(--qr-warning-600); }
.svelte-flow__node .aln-card.vb-live-act-ok { box-shadow: inset 3px 0 0 var(--qr-success); }
.svelte-flow__node .aln-card.vb-live-act-failed { box-shadow: inset 3px 0 0 var(--qr-danger-600); background: var(--qr-danger-50); }
.svelte-flow__node .aln-card.vb-live-act-skipped { box-shadow: inset 3px 0 0 var(--qr-border-emphasis); opacity: .6; }
.svelte-flow__node .aln-card.vb-live-act-current { outline: 2px solid var(--qr-warning); outline-offset: 1px; }
/* Click en un evento del probador: pulso sobre el nodo (y la tarjeta de la acción). */
.svelte-flow__node.vb-live-focus > *, .svelte-flow__node .aln-card.vb-live-focus { animation: vb-live-focus 0.7s ease-in-out 3; }
@keyframes vb-live-focus { 0%, 100% { box-shadow: none; } 50% { box-shadow: 0 0 0 6px var(--qr-primary-soft); } }

/* ════════════════════════════════════════════════════════════════════════════
   SOFTPHONE — coordinación entre pestañas
   ────────────────────────────────────────────────────────────────────────────
   El teléfono WebRTC vive en el layout, o sea en todas las pantallas, pero sólo
   UNA pestaña registra contra Asterisk. Las demás muestran el estado en gris con
   un cartel que dice dónde está el mando: un teléfono en blanco mientras otra
   pestaña está en llamada parece roto.
   ════════════════════════════════════════════════════════════════════════════ */

/* Disparador de una pestaña que NO tiene el mando: apagado, pero legible. */
.qr-sp-passive { opacity: .62; filter: grayscale(1); }
.qr-sp-passive:hover { opacity: .85; }

/* Cartel de estado del mando dentro del panel. */
.qr-sp-note {
    background: var(--qr-primary-soft); border: 1px solid var(--qr-primary-border-subtle); color: var(--qr-primary-dark);
    border-radius: var(--qr-radius-md); padding: 7px 9px; font-size: var(--qr-fs-xs);
    line-height: 1.35; margin-bottom: 8px;
}
.qr-sp-note--off { background: var(--qr-surface-2); border-color: var(--qr-border); color: var(--qr-text-body); }
.qr-sp-note--on { background: var(--qr-success-50); border-color: var(--qr-success-200); color: var(--qr-success-800); }
.qr-sp-note .qr-sp-note-hint { color: inherit; opacity: .78; margin-top: 2px; }
.qr-sp-note .btn { width: 100%; }

/* Dígitos DTMF enviados en la llamada en curso. */
.qr-sp-dtmf {
    font-family: var(--qr-font-mono); font-size: var(--qr-fs-sm); color: var(--qr-text-body);
    margin-bottom: 6px; word-break: break-all;
}


/* Anclaje flotante para los layouts que no tienen barra superior de admin
   (la consola de supervisión es full-screen y el Svelte ocupa todo el viewport). */
.qr-sp-floating {
    position: fixed; top: 10px; right: 12px; z-index: 1085;
}

/* ==========================================================================
   PANEL DE DEBUG DE INTERACCIONES  (.qr-idbg-*)
   --------------------------------------------------------------------------
   Prefijo propio y no una variante de `qr-panel`/`qr-badge`: es una pantalla
   TÉCNICA, con densidad e intención distintas al resto de la administración
   (filas crudas de 60 columnas, JSON anidado, badges de reloj). Reusar las
   clases del sistema acá habría terminado en un `<style>` de pantalla que las
   pisa por especificidad, que es justo lo que el sistema prohíbe.
   ========================================================================== */

/* Barra de control del refresco y del alcance. */
.qr-idbg-bar {
    display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
    padding: 10px 14px; border-bottom: 1px solid var(--cui-border-color);
    background: var(--qr-surface-2);
}
.qr-idbg-bar__g { display: flex; align-items: center; gap: 6px; }
.qr-idbg-bar__lbl {
    font-size: var(--qr-fs-xs); font-weight: 600; letter-spacing: .02em;
    text-transform: uppercase; color: var(--qr-text-secondary); white-space: nowrap;
}
.qr-idbg-bar__sep { flex: 1 1 auto; }

/* Estado del refresco: latido cuando está vivo, apagado cuando la pestaña no se ve. */
.qr-idbg-live {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: var(--qr-fs-75); color: var(--qr-text-secondary); white-space: nowrap;
}
.qr-idbg-live__dot {
    width: 8px; height: 8px; border-radius: 50%; background: var(--qr-dot-muted); flex: 0 0 auto;
}
.qr-idbg-live.is-on .qr-idbg-live__dot { background: var(--qr-success); animation: qr-idbg-pulse 2s infinite; }
.qr-idbg-live.is-paused .qr-idbg-live__dot { background: var(--qr-warning); }
@keyframes qr-idbg-pulse { 0%,100% { opacity: 1; } 50% { opacity: .25; } }

/* Señales de alerta. GRITAN: fondo, borde y mayúsculas, no un punto en una columna. */
.qr-idbg-sig {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 1px 7px; border-radius: var(--qr-radius-sm); border: 1px solid transparent;
    font-size: var(--qr-fs-68); font-weight: 700; letter-spacing: .02em;
    line-height: 1.5; white-space: nowrap; cursor: help;
}
.qr-idbg-sig--danger  { background: var(--qr-danger-100); border-color: var(--qr-danger-300); color: var(--qr-danger-800); }
.qr-idbg-sig--warning { background: var(--qr-warning-100); border-color: var(--qr-warning-300); color: var(--qr-warning-800); }
.qr-idbg-sig--ok      { background: var(--qr-success-100); border-color: var(--qr-success-300); color: var(--qr-success-800); }
.qr-idbg-sigs { display: flex; flex-wrap: wrap; gap: 3px; }

/* Etiqueta del reloj de una fecha. Sin esto el panel hace perder horas. */
.qr-idbg-clock {
    display: inline-block; padding: 0 5px; border-radius: var(--qr-radius-xs);
    font-size: var(--qr-fs-2xs); font-weight: 600; line-height: 1.6;
    background: var(--qr-primary-subtle); color: var(--qr-primary-dark); white-space: nowrap;
}
.qr-idbg-clock--system { background: var(--qr-caution-100); color: var(--qr-caution-800); }

/* Inspector de fila cruda: clave → valor, vertical, TODAS las columnas. */
.qr-idbg-raw { border: 1px solid var(--qr-border); border-radius: var(--qr-radius-sm-plus); overflow: hidden; }
.qr-idbg-raw__hd {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    padding: 6px 10px; background: var(--qr-surface-3); border-bottom: 1px solid var(--qr-border);
    font-size: var(--qr-fs-75); font-weight: 600; color: var(--qr-text-strong);
}
.qr-idbg-raw__body { max-height: 420px; overflow: auto; }
.qr-idbg-raw__r {
    display: grid; grid-template-columns: minmax(150px, 220px) 70px 1fr;
    gap: 8px; align-items: start;
    padding: 3px 10px; border-bottom: 1px solid var(--qr-border-soft);
    font-size: var(--qr-fs-75); line-height: 1.5;
}
.qr-idbg-raw__r:nth-child(even) { background: var(--qr-surface-2); }
.qr-idbg-raw__k {
    font-family: var(--qr-font-mono);
    font-weight: 600; color: var(--qr-text-emphasis); word-break: break-all;
}
.qr-idbg-raw__t { font-size: var(--qr-fs-2xs); color: var(--qr-text-muted); text-transform: uppercase; }
.qr-idbg-raw__v {
    font-family: var(--qr-font-mono);
    color: var(--qr-text); word-break: break-word; min-width: 0;
}
/* NULL, cadena vacía y objeto vacío se ven DISTINTO: la diferencia ya fue un bug real. */
.qr-idbg-null  { color: var(--qr-text-muted); font-style: italic; }
.qr-idbg-empty { color: var(--qr-warning-700); font-style: italic; }
.qr-idbg-mask  { color: var(--qr-text-secondary); letter-spacing: .15em; }

/* Fila del maestro: toda la fila abre el detalle. */
.qr-idbg-row { cursor: pointer; }

/* Celda JSON: el árbol arriba, el crudo copiable abajo. */
.qr-idbg-json { border-left: 2px solid var(--qr-primary-border-subtle); padding-left: 8px; }
.qr-idbg-json--bad { border-left-color: var(--qr-danger-300); }
.qr-idbg-json__raw {
    margin: 4px 0 0; padding: 6px 8px; background: var(--qr-surface-2); border-radius: var(--qr-radius-sm);
    font-size: var(--qr-fs-70); white-space: pre-wrap; word-break: break-all; max-height: 160px;
    overflow: auto;
}

/* Bloque de sección dentro del detalle (tramos, participantes, sala…). */
.qr-idbg-sec { margin-bottom: 14px; }
.qr-idbg-sec__hd {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    margin-bottom: 6px; font-size: var(--qr-fs-sm); font-weight: 700; color: var(--qr-text-strong);
}
.qr-idbg-sec__n {
    padding: 0 6px; border-radius: var(--qr-radius-md-plus); background: var(--qr-surface-strong); color: var(--qr-text-body);
    font-size: var(--qr-fs-68); font-weight: 700;
}
.qr-idbg-empty-note {
    padding: 10px 12px; border: 1px dashed var(--qr-border-strong); border-radius: var(--qr-radius-sm-plus);
    background: var(--qr-surface-2); color: var(--qr-text-secondary); font-size: var(--qr-fs-78);
}

/* Contraste Redis ↔ MySQL, lado a lado. */
.qr-idbg-cmp { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.qr-idbg-cmp__side { border: 1px solid var(--qr-border); border-radius: var(--qr-radius-sm-plus); overflow: hidden; }
.qr-idbg-cmp__hd {
    padding: 5px 10px; background: var(--qr-surface-3); border-bottom: 1px solid var(--qr-border);
    font-size: var(--qr-fs-74); font-weight: 700; color: var(--qr-text-strong);
}
.qr-idbg-cmp__hd--redis { background: var(--qr-danger-100); color: var(--qr-danger-800); }
.qr-idbg-cmp__hd--mysql { background: var(--qr-info-100); color: var(--qr-info-800); }
.qr-idbg-cmp__row {
    padding: 4px 10px; border-bottom: 1px solid var(--qr-border-soft); font-size: var(--qr-fs-74);
    font-family: var(--qr-font-mono);
}
.qr-idbg-cmp__row.is-diff { background: var(--qr-warning-100); }
@media (max-width: 767.98px) { .qr-idbg-cmp { grid-template-columns: 1fr; } }

/* El PUNTERO de origen de un tramo (`linked_model` / `work_record_model`): la tabla y la
   fila de la que realmente salió. Va en monoespaciada porque es un identificador de base,
   y tiene variante roja porque un puntero colgado es el hallazgo, no un detalle. */
.qr-idbg-ptr {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 1px 7px; border: 1px solid var(--qr-primary-border-subtle); border-radius: var(--qr-radius-sm);
    background: var(--qr-primary-soft); color: var(--qr-primary-dark);
    font-family: var(--qr-font-mono);
    font-size: var(--qr-fs-xs); font-weight: 600; white-space: nowrap;
}
.qr-idbg-ptr--bad  { background: var(--qr-danger-100); border-color: var(--qr-danger-300); color: var(--qr-danger-800); }

/* Una tabla cuyo reloj NADIE declaró. No se pinta como si fuera el del sistema: decir
   "no sé" es más barato que afirmar un offset equivocado. */
.qr-idbg-clock--unknown { background: var(--qr-surface-3); color: var(--qr-text-secondary); }

/* El SQL exacto que corrió y su EXPLAIN, a la vista. En un panel de debug poder leer la
   consulta vale más que cualquier explicación de lo que hace. */
.qr-idbg-sql {
    margin: 0; padding: 6px 9px; background: var(--qr-code-bg); color: var(--qr-code-text); border-radius: var(--qr-radius-sm-plus);
    font-family: var(--qr-font-mono);
    font-size: var(--qr-fs-70); white-space: pre-wrap; word-break: break-word;
}
.qr-idbg-explain {
    font-family: var(--qr-font-mono);
    font-size: var(--qr-fs-68); color: var(--qr-text-body);
}
.qr-idbg-explain b { color: var(--qr-success-800); }
.qr-idbg-explain b.is-bad { color: var(--qr-danger-800); }

/* ─────────────────────────────────────────────────────────────────────────────
   EL SOBRE DE AUDITORÍA EN TRES NIVELES (panel de debug de interacciones)

   Los tres niveles se distinguen por COLOR DE BORDE IZQUIERDO y por sangría, no
   por tres cajas idénticas apiladas: la jerarquía es parte del dato — el ACTOR
   cuelga del tramo y la PRESENCIA cuelga del actor. Un sobre repartido leído
   como tres JSON sueltos no dice nada.
   ───────────────────────────────────────────────────────────────────────────── */
.qr-idbg-env {
    border: 1px solid var(--qr-border); border-radius: var(--qr-radius-sm-plus); overflow: hidden;
    margin-bottom: 12px; background: var(--qr-surface);
}
.qr-idbg-env__hd {
    display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
    padding: 6px 10px; background: var(--qr-surface-2); border-bottom: 1px solid var(--qr-border);
    font-size: var(--qr-fs-78); font-weight: 700; color: var(--qr-text-strong);
}
.qr-idbg-env__bd { padding: 8px 10px; }

.qr-idbg-lvl {
    border-left: 3px solid var(--qr-border-strong); padding: 4px 0 4px 8px; margin-bottom: 6px;
}
.qr-idbg-lvl--thread      { border-left-color: var(--qr-primary-light); }
.qr-idbg-lvl--participant { border-left-color: var(--qr-cat-sky); margin-left: 14px; }
.qr-idbg-lvl--presence    { border-left-color: var(--qr-cat-teal); margin-left: 28px; }
.qr-idbg-lvl__hd {
    display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap;
    font-size: var(--qr-fs-75); font-weight: 700; color: var(--qr-text-strong);
}
.qr-idbg-lvl__model { font-weight: 500; color: var(--qr-text-secondary); font-family: var(--qr-font-mono); }
.qr-idbg-lvl__q { font-size: var(--qr-fs-68); color: var(--qr-text-muted); font-weight: 400; }
.qr-idbg-lvl__empty { font-size: var(--qr-fs-xs); color: var(--qr-text-muted); font-style: italic; }

/* Una clave del sobre: nombre, valor y —si está fuera de su nivel— por qué. */
.qr-idbg-kv {
    display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap;
    font-size: var(--qr-fs-73); padding: 1px 0;
}
.qr-idbg-kv__k { font-family: var(--qr-font-mono); color: var(--qr-text-body); min-width: 130px; }
.qr-idbg-kv__v { font-weight: 600; color: var(--qr-text); word-break: break-all; }
.qr-idbg-kv.is-off .qr-idbg-kv__k { color: var(--qr-warning-800); }

.qr-idbg-kflag {
    padding: 0 5px; border-radius: var(--qr-radius-md); font-size: var(--qr-fs-64); font-weight: 700;
    border: 1px solid transparent; white-space: nowrap;
}
.qr-idbg-kflag--misplaced { background: var(--qr-warning-100); border-color: var(--qr-warning-300); color: var(--qr-warning-800); }
.qr-idbg-kflag--dropped   { background: var(--qr-special-100); border-color: var(--qr-special-300); color: var(--qr-special-800); }

/* Las claves que ya NO van al JSON, con la columna donde viven y SU valor. Es lo
   que evita el soporte falso: una ausencia explicada deja de ser sospechosa. */
.qr-idbg-drop { border: 1px solid var(--qr-special-200); border-radius: var(--qr-radius-sm-plus); overflow: hidden; }
.qr-idbg-drop__hd {
    padding: 5px 10px; background: var(--qr-special-50); border-bottom: 1px solid var(--qr-special-200);
    font-size: var(--qr-fs-73); font-weight: 700; color: var(--qr-special-800);
}
.qr-idbg-drop__row {
    display: grid; grid-template-columns: 150px 1fr 140px; gap: 8px;
    padding: 4px 10px; border-bottom: 1px solid var(--qr-border-soft); font-size: var(--qr-fs-xs);
    align-items: baseline;
}
.qr-idbg-drop__row:last-child { border-bottom: 0; }
.qr-idbg-drop__key { font-family: var(--qr-font-mono); color: var(--qr-special-800); font-weight: 700; }
.qr-idbg-drop__col { font-family: var(--qr-font-mono); color: var(--qr-text-secondary); }
.qr-idbg-drop__val { font-weight: 700; color: var(--qr-text); word-break: break-all; }
.qr-idbg-drop__note { font-size: var(--qr-fs-68); color: var(--qr-warning-700); }
@media (max-width: 767.98px) {
    .qr-idbg-drop__row { grid-template-columns: 1fr; }
    .qr-idbg-lvl--participant { margin-left: 8px; }
    .qr-idbg-lvl--presence { margin-left: 16px; }
}

/* ==========================================================================
   VALIDADOR INLINE DE CONDICIONES (voicebot)  (.qr-condtest-*)
   --------------------------------------------------------------------------
   El evaluador del motor no evalúa expresiones: renderiza Jinja y pregunta si
   quedó algo no vacío. "1 = 1" y "{{context.perfil}} = \"directivo\"" dan
   VERDADERO siempre, con perfil correcto o no. Un validador de sintaxis no
   atrapa nada de eso; sólo probar con valores y ver el renderizado lo muestra.
   Ver docs/limpieza_codigo/fix_should_run.md.
   ========================================================================== */

.qr-condtest {
    border: 1px solid var(--qr-border); border-radius: var(--qr-radius-md); background: var(--qr-surface-2);
    padding: 10px 12px; margin-top: 10px;
}
.qr-condtest__head {
    display: flex; align-items: center; justify-content: space-between;
    gap: 8px; flex-wrap: wrap;
}
.qr-condtest__title {
    display: flex; align-items: center; gap: 6px;
    font-size: var(--qr-fs-78); font-weight: 700; color: var(--qr-text-strong);
}
.qr-condtest__title i { color: var(--qr-text-secondary); }
.qr-condtest__format {
    font-size: var(--qr-fs-66); font-weight: 700; letter-spacing: .02em; text-transform: uppercase;
    padding: 1px 7px; border-radius: var(--qr-radius-md-plus); background: var(--qr-surface-strong); color: var(--qr-text-body);
}

/* Ejemplos: un puñado de condiciones bien escritas para insertar de un clic. */
.qr-condtest__examples { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.qr-condtest__example {
    font-family: var(--qr-font-mono);
    font-size: var(--qr-fs-68); line-height: 1.4; max-width: 100%;
    padding: 3px 8px; border-radius: var(--qr-radius-pill); border: 1px solid var(--qr-border-strong);
    background: var(--qr-surface); color: var(--qr-text-strong); cursor: pointer;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.qr-condtest__example:hover { border-color: var(--qr-border-emphasis); background: var(--qr-surface-3); }

/* Valores de prueba: una fila por variable, la que falta se agrega sola. */
.qr-condtest__vars { display: flex; flex-direction: column; gap: 6px; margin-top: 10px; }
.qr-condtest__var { display: flex; align-items: center; gap: 8px; }
.qr-condtest__var-key {
    flex: 0 0 auto; width: 220px; max-width: 42%;
    font-family: var(--qr-font-mono);
    font-size: var(--qr-fs-74); color: var(--qr-text-emphasis);
}
.qr-condtest__var-val { flex: 1 1 auto; min-width: 0; }
.qr-condtest__missing {
    font-size: var(--qr-fs-64); font-weight: 700; text-transform: uppercase; letter-spacing: .02em;
    color: var(--qr-warning-700); white-space: nowrap;
}

/* Lo que quedó tras renderizar: la explicación, no sólo el resultado. */
.qr-condtest__code {
    font-family: var(--qr-font-mono);
    font-size: var(--qr-fs-76); line-height: 1.4; white-space: pre-wrap; word-break: break-word;
    background: var(--qr-code-bg); color: var(--qr-code-text); border-radius: var(--qr-radius-sm-plus); padding: 8px 10px;
    margin-top: 8px;
}

/* El resultado: grande, con color, no un texto gris al pie. */
.qr-condtest__result {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: var(--qr-fs-sm); font-weight: 700; border-radius: var(--qr-radius-sm-plus);
    padding: 4px 10px; margin-top: 8px;
}
.qr-condtest__result--true  { background: var(--qr-success-100); color: var(--qr-success-700); }
.qr-condtest__result--false { background: var(--qr-danger-100); color: var(--qr-danger-700); }

/* Warnings: el error más caro y más común no puede ser un texto gris al pie. */
.qr-condtest__warn {
    display: flex; align-items: flex-start; gap: 8px;
    margin-top: 8px; padding: 8px 10px; border-radius: var(--qr-radius-sm-plus);
    background: var(--qr-danger-50); border: 1px solid var(--qr-danger-200); color: var(--qr-danger-800);
    font-size: var(--qr-fs-76); font-weight: 600; line-height: 1.4;
}
.qr-condtest__warn i { font-size: var(--qr-fs-base); margin-top: 1px; }
.qr-condtest__warn ul { margin: 0; padding-left: 16px; font-weight: 600; }

/* ═══════════════════════════════════════════════════════════════════════════════════════
   Catálogo de variables de una plantilla de difusión (qr-fcat-*)

   Vive dentro de una `FloatingWindow`, que es una columna flex con `position: relative`:
   por eso el contenedor pide `inset: 0` en vez de un `height: 100%` que no resolvería.

   La ventana es flotante y no modal A PROPOSITO: el usuario escribe la plantilla MIENTRAS
   mira las variables. Por eso el constructor de la expresión vive pegado al pie, siempre a
   la vista, y lo que scrollea es sólo la lista.
   ═══════════════════════════════════════════════════════════════════════════════════════ */

.qr-fcat { position: absolute; inset: 0; display: flex; flex-direction: column; min-height: 0; }

.qr-fcat__top {
    flex: none; padding: .5rem .6rem; border-bottom: 1px solid var(--qr-header-border);
    display: flex; flex-direction: column; gap: .4rem;
}

/* El aviso de los destinatarios sin contacto. No es decorativo: es el número que convierte
   "puede pasar" en "le pasa a 3.000 de 10.000", y por eso va arriba y no al pie. */
.qr-fcat__risk {
    display: flex; align-items: flex-start; gap: .4rem;
    font-size: var(--qr-fs-xs); line-height: 1.35;
    background: var(--qr-warning-50); border: 1px solid var(--qr-warning-200); color: var(--qr-warning-800);
    border-radius: var(--qr-field-radius); padding: .35rem .5rem;
}
.qr-fcat__risk .bi { margin-top: 1px; }

.qr-fcat__body { flex: 1 1 auto; min-height: 0; overflow: auto; padding: .25rem .35rem .5rem; }

.qr-fcat__group {
    display: flex; align-items: center; gap: .35rem;
    font-size: var(--qr-fs-63); font-weight: 700; text-transform: uppercase; letter-spacing: .03em;
    color: var(--qr-text-secondary); padding: .5rem .3rem .2rem;
}

/* La fila es el contenedor y el item es el boton: la forma `fields.*` va como boton HERMANO
   y no adentro, porque un interactivo dentro de otro no es alcanzable por teclado. */
.qr-fcat__row { display: flex; align-items: stretch; gap: .15rem; }

.qr-fcat__exact {
    flex: none; align-self: center;
    font-family: var(--bs-font-monospace); font-size: var(--qr-fs-60); font-weight: 700;
    line-height: 1; padding: .2rem .3rem;
    background: var(--qr-surface-2); color: var(--qr-text-secondary);
    border: 1px solid var(--qr-header-border); border-radius: var(--qr-field-radius);
    cursor: pointer;
}
.qr-fcat__exact:hover { background: var(--qr-primary-soft); color: var(--qr-primary-hover); border-color: var(--qr-primary-border-subtle); }

.qr-fcat__item {
    display: flex; align-items: center; gap: .4rem; flex: 1 1 auto; min-width: 0;
    padding: .28rem .4rem; border: 1px solid transparent; border-radius: var(--qr-field-radius);
    background: transparent; color: var(--qr-text); text-align: left; cursor: pointer;
}
.qr-fcat__item:hover { background: var(--qr-surface-2); border-color: var(--qr-header-border); }
.qr-fcat__item.is-used { background: var(--qr-primary-soft); border-color: var(--qr-primary-border-subtle); }
.qr-fcat__item > .bi { flex: none; font-size: var(--qr-fs-xs); }

.qr-fcat__itxt { min-width: 0; flex: 1 1 auto; display: flex; flex-direction: column; }
.qr-fcat__ilabel {
    font-size: var(--qr-fs-76); font-weight: 600;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.qr-fcat__ipath {
    font-family: var(--bs-font-monospace); font-size: var(--qr-fs-66);
    color: var(--qr-text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Origen del campo. "Está en los datos" NO es lo mismo que "está en el contrato", así que se
   distinguen por color y no sólo por texto. */
.qr-fcat__org {
    flex: none; font-size: var(--qr-fs-60); font-weight: 700; line-height: 1;
    padding: .15rem .3rem; border-radius: var(--qr-radius-pill); border: 1px solid transparent;
}
.qr-fcat__org--native   { background: var(--qr-surface-3); color: var(--qr-text-body); border-color: var(--qr-border); }
.qr-fcat__org--declared { background: var(--qr-primary-soft); color: var(--qr-primary-hover); border-color: var(--qr-primary-border-subtle); }
.qr-fcat__org--detected { background: var(--qr-caution-50); color: var(--qr-warning-700); border-color: var(--qr-caution-200); }

.qr-fcat__empty {
    display: flex; flex-direction: column; gap: .3rem;
    font-size: var(--qr-fs-74); color: var(--qr-text-muted); padding: 1rem .6rem; text-align: center;
}

/* El constructor. Tres ranuras: principal, respaldo y literal final — el literal NO es
   opcional, porque un parámetro vacío marca el registro SENT_ERROR_INTERNAL y no lo envía. */
.qr-fcat__build {
    flex: none; border-top: 1px solid var(--qr-header-border); background: var(--qr-surface-2);
    padding: .5rem .6rem; display: flex; flex-direction: column; gap: .35rem;
}

.qr-fcat__slot { display: flex; align-items: center; gap: .4rem; }
.qr-fcat__slotk {
    flex: none; width: 5.2rem; font-size: var(--qr-fs-68); font-weight: 700;
    color: var(--qr-text-secondary); text-transform: uppercase; letter-spacing: .02em;
}
.qr-fcat__slotv {
    flex: 1 1 auto; min-width: 0;
    font-family: var(--bs-font-monospace); font-size: var(--qr-fs-70);
    background: var(--qr-surface); border: 1px solid var(--qr-field-border);
    border-radius: var(--qr-field-radius); padding: .18rem .35rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.qr-fcat__slotv.is-empty { color: var(--qr-field-placeholder); font-style: italic; }

.qr-fcat__expr {
    font-family: var(--bs-font-monospace); font-size: var(--qr-fs-68); line-height: 1.35;
    background: var(--qr-code-bg); color: var(--qr-code-text); border-radius: var(--qr-field-radius);
    padding: .35rem .45rem; word-break: break-all;
}

/* Los DOS casos de la vista previa, lado a lado: el feliz y el que rompe la tanda. */
.qr-fcat__prev { display: grid; grid-template-columns: 1fr 1fr; gap: .35rem; }
.qr-fcat__case {
    border: 1px solid var(--qr-header-border); border-radius: var(--qr-field-radius);
    background: var(--qr-surface); padding: .3rem .4rem; min-width: 0;
}
.qr-fcat__casek {
    font-size: var(--qr-fs-63); font-weight: 700; text-transform: uppercase; letter-spacing: .02em;
    color: var(--qr-text-secondary); display: flex; align-items: center; gap: .25rem;
}
.qr-fcat__casev {
    font-size: var(--qr-fs-74); color: var(--qr-text); margin-top: .15rem;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.qr-fcat__case.is-empty { background: var(--qr-danger-50); border-color: var(--qr-danger-200); }
.qr-fcat__case.is-empty .qr-fcat__casev { color: var(--qr-danger-800); font-style: italic; }


/* ==========================================================================
   COMPONENTES COMPARTIDOS (frontend/components)

   Estilos que vivían en el <style> scoped de cada componente. Acá aplican en
   runtime (sin rebuild) y siguen al tema: sólo tokens semánticos. Cada clase
   es del componente que nombra el título; no redefinirlas en una pantalla.
   ========================================================================== */

/* --- Campo de fecha/hora (DateTimeField.svelte) — prefijo qr-dtf- ---
   Movido del <style> scoped del componente (F2 de tokens, 24/09/2026). */
.qr-dtf { position: relative; }

/* Alto, radio, borde y tipografía salen de los tokens `--qr-field-*` de
   static/css/queuereport_global.css — los MISMOS que usan los `form-control` y el
   CSelect. Antes eran 38px / radio 9px / borde #d8dee9 fijos, y en una fila junto a
   un input (34px / 8px / #dde3ec) el escalón se veía. */
.qr-dtf-trigger {
    width: 100%;
    display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
    height: var(--qr-field-h); padding: 0 var(--qr-field-px);
    background: var(--qr-field-bg);
    border: 1px solid var(--qr-field-border);
    border-radius: var(--qr-field-radius);
    cursor: pointer; transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.qr-dtf-trigger:hover:not(.disabled) { border-color: var(--qr-field-border-hover); }
.qr-dtf-trigger.active {
    border-color: var(--qr-field-focus);
    box-shadow: var(--qr-field-focus-shadow);
}
.qr-dtf-trigger.is-invalid { border-color: var(--qr-danger); }
.qr-dtf-trigger.is-invalid.active { box-shadow: 0 0 0 3px rgba(var(--qr-danger-rgb), var(--qr-alpha-15)); }
.qr-dtf-trigger.disabled { background: var(--qr-surface-2); cursor: default; opacity: 0.7; }

.qr-dtf-value { display: inline-flex; align-items: center; gap: 0.45rem; min-width: 0; }
.qr-dtf-value > .bi { color: var(--qr-primary); font-size: var(--qr-fs-md); flex: 0 0 auto; }
.qr-dtf-text {
    font-size: var(--qr-field-font); font-weight: 600; color: var(--qr-text-strong);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    font-variant-numeric: tabular-nums;
}
.qr-dtf-text.is-empty { font-weight: 500; color: var(--qr-field-placeholder); }

.qr-dtf-actions { display: inline-flex; align-items: center; gap: 0.25rem; flex: 0 0 auto; }
.qr-dtf-clear {
    display: inline-flex; align-items: center; justify-content: center;
    width: 18px; height: 18px; border-radius: var(--qr-radius-sm-plus); color: var(--qr-text-muted); font-size: var(--qr-fs-2xs); cursor: pointer;
}
.qr-dtf-clear:hover { background: var(--qr-danger-100); color: var(--qr-danger-600); }
.qr-dtf-caret { font-size: var(--qr-fs-70); color: var(--qr-text-muted); transition: transform 0.18s ease; }
.qr-dtf-caret.open { transform: rotate(180deg); }

/* Portal a <body>: fixed + z-index alto para ganarle a modales y footers. */
.qr-dtf-menu {
    position: fixed;
    padding: 0.6rem 0.7rem 0.7rem;
    background: var(--qr-surface); border: 1px solid var(--qr-border); border-radius: var(--qr-radius-lg);
    box-shadow: 0 16px 40px -12px rgba(var(--qr-shadow-rgb), var(--qr-alpha-30));
    z-index: 20000;
}

.qr-dtf-cal-head { display: flex; align-items: center; justify-content: space-between; gap: 0.25rem; margin-bottom: 0.45rem; }
.qr-dtf-title {
    flex: 1 1 auto;
    display: inline-flex; align-items: center; justify-content: center; gap: 0.25rem;
    border: 0; background: transparent; cursor: pointer;
    font-size: var(--qr-fs-82); font-weight: 700; color: var(--qr-text-emphasis);
    padding: 0.25rem 0.4rem; border-radius: var(--qr-radius-md); transition: background 0.12s ease;
}
.qr-dtf-title:hover { background: var(--qr-surface-3); }
.qr-dtf-title.is-root { cursor: default; }
.qr-dtf-title.is-root:hover { background: transparent; }
.qr-dtf-title .bi { font-size: var(--qr-fs-70); color: var(--qr-text-muted); }
.qr-dtf-nav {
    border: 0; background: transparent; color: var(--qr-text-secondary); cursor: pointer;
    width: 26px; height: 26px; border-radius: var(--qr-radius-md); flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center;
}
.qr-dtf-nav:hover { background: var(--qr-surface-3); color: var(--qr-text-emphasis); }

.qr-dtf-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.qr-dtf-week { margin-bottom: 0.2rem; }
.qr-dtf-wd { text-align: center; font-size: var(--qr-fs-66); font-weight: 700; color: var(--qr-text-muted); padding: 0.2rem 0; }

.qr-dtf-day {
    aspect-ratio: 1 / 1;
    border: 0; background: transparent; cursor: pointer;
    font-size: var(--qr-fs-78); color: var(--qr-text-strong);
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: var(--qr-radius-md); transition: background 0.1s ease, color 0.1s ease;
}
.qr-dtf-day.empty { cursor: default; }
.qr-dtf-day:disabled { color: var(--qr-text-faint); cursor: default; }
.qr-dtf-day:not(.empty):not(:disabled):hover { background: var(--qr-primary-soft); }
.qr-dtf-day.today { font-weight: 800; color: var(--qr-primary); }
.qr-dtf-day.selected { background: var(--qr-primary) !important; color: var(--qr-text-inverse) !important; font-weight: 700; }

/* Grilla de meses / años: 3 columnas, mismo alto que el calendario de días. */
.qr-dtf-grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 4px; padding: 0.15rem 0; }
.qr-dtf-cell {
    border: 0; background: transparent; cursor: pointer;
    font-size: var(--qr-fs-sm); font-weight: 600; color: var(--qr-text-strong);
    padding: 0.55rem 0.2rem; border-radius: var(--qr-radius-md);
    transition: background 0.1s ease, color 0.1s ease;
    font-variant-numeric: tabular-nums;
}
.qr-dtf-cell:disabled { color: var(--qr-text-faint); cursor: default; }
.qr-dtf-cell:not(:disabled):hover { background: var(--qr-primary-soft); }
.qr-dtf-cell.selected { background: var(--qr-primary); color: var(--qr-text-inverse); }

.qr-dtf-time {
    display: flex; align-items: center; gap: 0.35rem;
    margin-top: 0.6rem; padding-top: 0.55rem; border-top: 1px solid var(--qr-surface-3);
}
.qr-dtf-time > .bi-clock { color: var(--qr-text-muted); font-size: var(--qr-fs-sm); }
/* TimeField.svelte: popover de sólo hora, sin calendario arriba. */
.qr-dtf-menu.is-time-only .qr-dtf-time { margin-top: 0; padding-top: 0; border-top: 0; }
.qr-dtf-sel {
    border: 1px solid var(--qr-border); border-radius: var(--qr-radius-md); background: var(--qr-surface);
    font-size: var(--qr-fs-sm); font-weight: 600; color: var(--qr-text-strong);
    padding: 0.2rem 0.25rem; cursor: pointer; font-variant-numeric: tabular-nums;
}
.qr-dtf-sel:focus { outline: none; border-color: var(--qr-primary); }
.qr-dtf-colon { font-weight: 700; color: var(--qr-text-muted); }
.qr-dtf-now {
    margin-left: auto;
    display: inline-flex; align-items: center; gap: 0.25rem;
    border: 1px solid var(--qr-border); border-radius: var(--qr-radius-md); background: var(--qr-surface-2);
    font-size: var(--qr-fs-xs); font-weight: 600; color: var(--qr-text-body); padding: 0.22rem 0.5rem; cursor: pointer;
}
.qr-dtf-now:hover { background: var(--qr-primary-soft); color: var(--qr-primary-dark); border-color: var(--qr-primary-125); }

.qr-dtf-foot {
    display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
    margin-top: 0.6rem; padding-top: 0.55rem; border-top: 1px solid var(--qr-surface-3);
}
.qr-dtf-link {
    border: 0; background: transparent; color: var(--qr-text-secondary);
    font-size: var(--qr-fs-75); font-weight: 600; cursor: pointer; padding: 0.2rem 0.1rem;
}
.qr-dtf-link:hover { color: var(--qr-danger-600); }
.qr-dtf-apply {
    display: inline-flex; align-items: center; gap: 0.3rem;
    border: 0; border-radius: var(--qr-radius-md);
    background: var(--qr-primary); color: var(--qr-text-inverse);
    font-size: var(--qr-fs-78); font-weight: 600; padding: 0.35rem 0.7rem; cursor: pointer;
}
.qr-dtf-apply:disabled { opacity: 0.5; cursor: default; }
.qr-dtf-apply:not(:disabled):hover { background: var(--qr-primary-dark); }

/* --- Selector de rango de fechas (DateRangePicker.svelte) — prefijo qr-drp- ---
   Movido del <style> scoped del componente (F2 de tokens, 24/09/2026). */
.qr-drp { position: relative; }

.qr-drp-trigger {
    width: 100%;
    display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
    min-height: 38px; padding: 0.25rem 0.6rem;
    background: var(--qr-surface); border: 1px solid var(--qr-field-border); border-radius: var(--qr-radius-md);
    cursor: pointer; transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.qr-drp-trigger:hover { border-color: var(--qr-field-border-hover); }
.qr-drp-trigger.active { border-color: var(--qr-primary); box-shadow: 0 0 0 3px rgba(var(--qr-primary-rgb), var(--qr-alpha-12)); }

.qr-drp-value { display: inline-flex; align-items: center; gap: 0.45rem; min-width: 0; }
.qr-drp-value > .bi-calendar3 { color: var(--qr-primary); font-size: var(--qr-fs-md); }
.qr-drp-text { font-size: var(--qr-fs-82); font-weight: 600; color: var(--qr-text-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qr-drp-arrow { font-size: var(--qr-fs-70); color: var(--qr-text-muted); }
.qr-drp-caret { font-size: var(--qr-fs-70); color: var(--qr-text-muted); transition: transform 0.18s ease; flex: 0 0 auto; }
.qr-drp-caret.open { transform: rotate(180deg); }

.qr-drp-menu {
    position: absolute; top: calc(100% + 6px); left: 0;
    display: flex; align-items: stretch;
    background: var(--qr-surface); border: 1px solid var(--qr-border); border-radius: var(--qr-radius-lg);
    box-shadow: 0 16px 40px -12px rgba(var(--qr-shadow-rgb), var(--qr-alpha-25));
    z-index: 1060; overflow: hidden;
}

.qr-drp-presets {
    display: flex; flex-direction: column; gap: 0.1rem;
    padding: 0.4rem; border-right: 1px solid var(--qr-surface-3); min-width: 140px;
}
.qr-drp-preset {
    text-align: left; border: 0; background: transparent;
    font-size: var(--qr-fs-sm); font-weight: 500; color: var(--qr-text-body);
    padding: 0.4rem 0.6rem; border-radius: var(--qr-radius-md); cursor: pointer;
    white-space: nowrap; transition: background 0.12s ease;
}
.qr-drp-preset:hover { background: var(--qr-surface-2); }
.qr-drp-preset.active { background: var(--qr-primary); color: var(--qr-text-inverse); font-weight: 600; }

.qr-drp-cal { padding: 0.6rem 0.7rem 0.7rem; width: 262px; }
.qr-drp-cal-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 0.4rem; }
.qr-drp-month { font-size: var(--qr-fs-82); font-weight: 700; color: var(--qr-text-emphasis); }
.qr-drp-nav {
    border: 0; background: transparent; color: var(--qr-text-secondary); cursor: pointer;
    width: 26px; height: 26px; border-radius: var(--qr-radius-md); display: inline-flex; align-items: center; justify-content: center;
}
.qr-drp-nav:hover { background: var(--qr-surface-3); color: var(--qr-text-emphasis); }

.qr-drp-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.qr-drp-week { margin-bottom: 0.2rem; }
.qr-drp-wd { text-align: center; font-size: var(--qr-fs-66); font-weight: 700; color: var(--qr-text-muted); padding: 0.2rem 0; }

.qr-drp-day {
    position: relative;
    aspect-ratio: 1 / 1;
    border: 0; background: transparent; cursor: pointer;
    font-size: var(--qr-fs-78); color: var(--qr-text-strong);
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: var(--qr-radius-md); transition: background 0.1s ease, color 0.1s ease;
}
.qr-drp-day.empty { cursor: default; }
.qr-drp-day:not(.empty):hover { background: var(--qr-primary-soft); }
.qr-drp-day.today { font-weight: 800; color: var(--qr-primary); }
.qr-drp-day.in-range { background: var(--qr-accent-soft, rgba(var(--qr-primary-rgb), var(--qr-alpha-12))); border-radius: 0; color: var(--qr-primary-dark); }
.qr-drp-day.start, .qr-drp-day.end {
    background: var(--qr-primary) !important; color: var(--qr-text-inverse) !important; font-weight: 700;
}
.qr-drp-day.start { border-radius: var(--qr-radius-md) 0 0 var(--qr-radius-md); }
.qr-drp-day.end { border-radius: 0 var(--qr-radius-md) var(--qr-radius-md) 0; }
.qr-drp-day.start.end { border-radius: var(--qr-radius-md); }

.qr-drp-foot {
    display: flex; align-items: center; justify-content: space-between; gap: 0.5rem;
    margin-top: 0.6rem; padding-top: 0.55rem; border-top: 1px solid var(--qr-surface-3);
}
.qr-drp-range { font-size: var(--qr-fs-xs); font-weight: 600; color: var(--qr-text-body); font-variant-numeric: tabular-nums; }
.qr-drp-range .bi { color: var(--qr-text-muted); }
.qr-drp-apply {
    display: inline-flex; align-items: center; gap: 0.3rem;
    border: 0; border-radius: var(--qr-radius-md);
    background: var(--qr-primary); color: var(--qr-text-inverse);
    font-size: var(--qr-fs-78); font-weight: 600; padding: 0.35rem 0.7rem; cursor: pointer;
}
.qr-drp-apply:disabled { opacity: 0.5; cursor: default; }
.qr-drp-apply:not(:disabled):hover { background: var(--qr-primary-dark); }

/* --- Selector múltiple de campañas (CampaignMultiSelect.svelte) — prefijo qr-cms- ---
   Movido del <style> scoped del componente (F2 de tokens, 24/09/2026). */
.qr-cms { position: relative; }

/* Forma tomada del sistema (`--qr-field-*`), no a mano: este trigger comparte fila
   con selects y con CSelect, y estaba 4px más alto y con 5px más de radio que ellos. */
.qr-cms-trigger {
    width: 100%;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    min-height: var(--qr-field-h);
    padding: 0.25rem var(--qr-field-px);
    background: var(--qr-field-bg);
    border: 1px solid var(--qr-field-border);
    border-radius: var(--qr-field-radius);
    cursor: pointer;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.qr-cms-trigger:hover { border-color: var(--qr-field-border-hover); }
.qr-cms-trigger.active {
    border-color: var(--qr-field-focus);
    box-shadow: var(--qr-field-focus-shadow);
}

.qr-cms-content { display: flex; align-items: center; gap: 0.3rem; flex-wrap: wrap; min-width: 0; overflow: hidden; }
.qr-cms-placeholder { display: inline-flex; align-items: center; gap: 0.4rem; font-size: var(--qr-field-font); color: var(--qr-field-placeholder); }
.qr-cms-count { display: inline-flex; align-items: center; gap: 0.4rem; font-size: var(--qr-field-font); font-weight: 600; color: var(--qr-text-strong); }

.qr-cms-chip {
    display: inline-flex; align-items: center; gap: 0.1rem;
    max-width: 120px;
    font-size: var(--qr-fs-74); font-weight: 600;
    color: var(--qr-accent, var(--qr-primary));
    background: var(--qr-accent-soft, rgba(var(--qr-primary-rgb), var(--qr-alpha-10)));
    padding: 0.12rem 0.5rem 0.12rem 0.3rem;
    border-radius: var(--qr-radius-pill);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.qr-cms-chip .bi { font-size: var(--qr-fs-base); line-height: 1; }

.qr-cms-caret { font-size: var(--qr-fs-70); color: var(--qr-text-muted); transition: transform 0.18s ease; flex: 0 0 auto; }
.qr-cms-caret.open { transform: rotate(180deg); }

.qr-cms-menu {
    position: absolute; top: calc(100% + 6px); left: 0;
    width: max(100%, 300px);
    background: var(--qr-surface);
    border: 1px solid var(--qr-border);
    border-radius: var(--qr-radius-lg);
    box-shadow: 0 16px 40px -12px rgba(var(--qr-shadow-rgb), var(--qr-alpha-25));
    z-index: 1060;
    overflow: hidden;
}

.qr-cms-search {
    display: flex; align-items: center; gap: 0.45rem;
    padding: 0.55rem 0.7rem;
    border-bottom: 1px solid var(--qr-surface-3);
}
.qr-cms-search > .bi { color: var(--qr-text-muted); font-size: var(--qr-fs-md); }
.qr-cms-search input {
    flex: 1 1 auto; border: 0; outline: none;
    font-size: var(--qr-fs-82); color: var(--qr-text-strong); background: transparent;
}
.qr-cms-clear {
    border: 0; background: var(--qr-bad-bg); color: var(--qr-danger-600);
    font-size: var(--qr-fs-70); font-weight: 700;
    padding: 0.15rem 0.5rem; border-radius: var(--qr-radius-pill); cursor: pointer; white-space: nowrap;
}
.qr-cms-clear:hover { background: var(--qr-danger-100); }

.qr-cms-list { max-height: 340px; overflow-y: auto; padding: 0.35rem; }

.qr-cms-group {
    display: flex; align-items: center; gap: 0.4rem;
    font-size: var(--qr-fs-66); font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em;
    color: var(--qr-accent, var(--qr-text-secondary));
    padding: 0.5rem 0.5rem 0.3rem;
}
.qr-cms-group .bi { font-size: var(--qr-fs-78); }
.qr-cms-group-count {
    margin-left: auto; font-size: var(--qr-fs-64); font-weight: 700;
    color: var(--qr-accent, var(--qr-text-secondary));
    background: var(--qr-accent-soft, var(--qr-border-soft));
    padding: 0 0.4rem; border-radius: var(--qr-radius-pill);
}

.qr-cms-item {
    width: 100%; display: flex; align-items: center; gap: 0.55rem;
    padding: 0.4rem 0.5rem; border: 0; background: transparent;
    border-radius: var(--qr-radius-md); cursor: pointer; text-align: left;
    transition: background 0.12s ease;
}
.qr-cms-item:hover { background: var(--qr-surface-2); }
.qr-cms-item.checked { background: var(--qr-accent-soft, rgba(var(--qr-primary-rgb), var(--qr-alpha-8))); }
.qr-cms-check { flex: 0 0 auto; color: var(--qr-accent, var(--qr-text-muted)); font-size: var(--qr-fs-95); line-height: 1; }
.qr-cms-item:not(.checked) .qr-cms-check { color: var(--qr-text-faint); }

.qr-cms-item-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; line-height: 1.25; }
.qr-cms-item-name { font-size: var(--qr-fs-82); font-weight: 600; color: var(--qr-text-emphasis); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qr-cms-item-desc { font-size: var(--qr-fs-70); color: var(--qr-text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.qr-cms-empty { display: flex; align-items: center; justify-content: center; gap: 0.4rem; padding: 1.25rem; color: var(--qr-text-muted); font-size: var(--qr-fs-sm); }

/* --- Explicación del alcance por campañas (CampaignScopeExplainer.svelte) ---
   Movido del <style> scoped del componente (F2 de tokens, 24/09/2026). */
/* Sólo lo propio del componente: los alerts, badges, fieldset, kv y tabla salen
   del sistema de diseño y no se redefinen acá (un estilo scoped de Svelte le gana
   al global por especificidad y desincronizaría la pantalla del resto). */
.qr-scope__load {
    display: flex; align-items: center; gap: .5rem;
    font-size: var(--qr-fs-84); color: var(--qr-text-secondary); padding: .6rem 0;
}
.qr-scope__verdict, .qr-scope__warn {
    display: flex; align-items: flex-start; gap: .55rem;
    padding: .6rem .75rem; margin-bottom: .6rem; font-size: var(--qr-fs-84);
}
.qr-scope__verdict i, .qr-scope__warn i { margin-top: .1rem; flex: none; }
.qr-scope__count { font-size: var(--qr-fs-78); opacity: .85; margin-top: .15rem; }
.qr-scope__hint { font-size: var(--qr-fs-74); color: var(--qr-text-muted); margin-left: .35rem; }
.qr-scope__how { margin-bottom: .8rem; }
.qr-scope__toggle {
    display: inline-flex; align-items: center; gap: .4rem;
    border: 0; background: none; padding: .2rem 0;
    font-size: var(--qr-fs-sm); color: var(--qr-primary); cursor: pointer;
}
.qr-scope__toggle:hover { text-decoration: underline; }
.qr-scope__table { margin-top: .5rem; max-height: 22rem; overflow: auto; }
/* Una vía larga no debe estirar la fila: se recorta y el texto completo va en el
   `title`, igual que en las tablas de reportería. */
.qr-scope__via {
    max-width: 16rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* --- Zona para soltar archivos (FileDropzone.svelte) — prefijo qr-drop ---
   Movido del <style> scoped del componente (F2 de tokens, 24/09/2026). */
.qr-drop {
    border: 2px dashed var(--qr-field-border);
    border-radius: var(--qr-radius-lg);
    background: var(--cui-tertiary-bg, var(--qr-surface-2));
    padding: 2rem 1.25rem;
    text-align: center;
    cursor: pointer;
    transition: border-color .15s, background-color .15s, transform .15s;
}
.qr-drop:hover:not(.is-disabled) { border-color: var(--qr-primary-300); background: var(--qr-primary-25); }
.qr-drop:focus-visible { outline: 2px solid var(--qr-primary-400); outline-offset: 2px; }
.qr-drop.is-over {
    border-color: var(--qr-primary);
    background: var(--qr-primary-soft);
    transform: scale(1.005);
}
.qr-drop.is-disabled { opacity: .55; cursor: not-allowed; }

.qr-drop__ic { font-size: var(--qr-fs-4xl); line-height: 1; color: var(--qr-primary); margin-bottom: .35rem; }
.qr-drop__t { font-weight: 600; font-size: var(--qr-fs-95); }
.qr-drop__s { font-size: var(--qr-fs-md); color: var(--cui-secondary-color); }
.qr-drop__link { color: var(--qr-primary); text-decoration: underline; }

.qr-drop__ext { display: flex; gap: .3rem; justify-content: center; flex-wrap: wrap;
                margin-top: .75rem; }
.qr-drop__chip {
    font-size: var(--qr-fs-70); font-weight: 600; letter-spacing: .02em;
    padding: .1rem .45rem; border-radius: var(--qr-radius-pill);
    background: rgba(var(--qr-primary-rgb), var(--qr-alpha-10)); color: var(--qr-primary-dark);
}
.qr-drop__hint { font-size: var(--qr-fs-75); color: var(--cui-secondary-color);
                 margin-top: .6rem; }

.qr-file {
    display: flex; align-items: center; gap: .75rem;
    border: 1px solid var(--qr-field-border);
    border-radius: var(--qr-radius-lg); padding: .7rem .85rem;
    background: var(--cui-body-bg);
}
.qr-file__ic { font-size: var(--qr-fs-3xl); line-height: 1; }
.qr-file__body { min-width: 0; flex: 1; }
.qr-file__name {
    font-weight: 600; font-size: var(--qr-fs-90);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.qr-file__meta { font-size: var(--qr-fs-78); color: var(--cui-secondary-color); }

/* --- Campo del panel de filtros (FilterField.svelte) — prefijo qr-filter- ---
   Movido del <style> scoped del componente (F2 de tokens, 24/09/2026). */
.qr-filter-field {
    display: flex;
    flex-direction: column;
    gap: 4px;
    min-width: var(--qr-ff-w);
    flex: 0 1 var(--qr-ff-w);
}
.qr-filter-field.grow { flex: 1 1 var(--qr-ff-w); }

.qr-filter-label {
    font-size: var(--qr-fs-68);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: var(--qr-text-body);
    white-space: nowrap;
}

/*
 * Acá NO se define la forma de los controles: el alto, el radio, el borde y la
 * tipografía salen del sistema (`--qr-field-*` en queuereport_global.css) y valen
 * para `.form-control`, `.form-select` y `CSelect` por igual.
 *
 * Hubo un override de `height: 38px; border-radius: 10px` con el comentario "se
 * igualan al alto de CSelect". Cuando el sistema bajó a 34px/4px, este bloque —que
 * es scoped, y por eso le GANA al global— dejó de igualar y pasó a hacer lo
 * contrario: un `<select>` medía 38px al lado de un CSelect de 34px, dentro del
 * mismo panel de filtros. Si mañana hay que cambiar la escala, se cambia el token.
 *
 * Lo único que sobrevive es el padding de la flecha nativa del navegador, que el
 * sistema no puede saber que hace falta acá.
 */
.qr-filter-field select.form-select { padding-right: 30px; }

/* CSelect ocupa todo el ancho del campo, como los controles nativos. */
.qr-filter-field .cs { width: 100%; }

/* --- SelectField.svelte ---
   Movido del <style> scoped del componente (F2 de tokens, 24/09/2026). */
.qr-sf-card, .qr-sf-chip {
    display: inline-flex; align-items: center; gap: 0.6rem;
    border: 1px solid var(--qr-field-border); border-left: 3px solid var(--qr-primary);
    border-radius: var(--qr-radius-md); background: linear-gradient(0deg, var(--qr-surface-2), var(--qr-surface));
    box-shadow: 0 1px 2px rgba(var(--qr-primary-rgb), var(--qr-alpha-5));
}
.qr-sf-card { display: flex; width: 100%; padding: 0.5rem 0.6rem; }
.qr-sf-chip { padding: 0.25rem 0.4rem 0.25rem 0.5rem; max-width: 100%; gap: 0.45rem; }
.qr-sf-card.disabled, .qr-sf-chip.disabled { opacity: .6; }

.qr-sf-ico {
    flex: 0 0 auto; display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; border-radius: var(--qr-radius-sm-plus); color: var(--qr-primary);
    background: var(--qr-primary-soft); font-size: var(--qr-fs-sm);
}
.qr-sf-ico.lg { width: 34px; height: 34px; border-radius: var(--qr-radius-md); font-size: var(--qr-fs-base); }

.qr-sf-text { display: flex; flex-direction: column; line-height: 1.2; min-width: 0; }
.qr-sf-text.grow { flex: 1 1 auto; }
.qr-sf-label { font-weight: 600; font-size: var(--qr-fs-88); color: var(--qr-text-emphasis); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qr-sf-sub { font-size: var(--qr-fs-xs); color: var(--qr-text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qr-sf-extra {
    display: inline-block; font-size: var(--qr-fs-66); font-weight: 700; letter-spacing: .02em;
    color: var(--qr-text-body); background: var(--qr-border-soft); border: 1px solid var(--qr-border); border-radius: var(--qr-radius-pill); padding: 0 0.4rem;
}

.qr-sf-btn {
    flex: 0 0 auto; border: 1px solid var(--qr-field-border); background: var(--qr-surface); color: var(--qr-text-secondary);
    width: 26px; height: 26px; border-radius: var(--qr-radius-sm-plus); font-size: var(--qr-fs-78);
    display: inline-flex; align-items: center; justify-content: center; cursor: pointer; transition: all .12s ease;
}
.qr-sf-btn:hover { background: var(--qr-border-soft); color: var(--qr-text-strong); }
.qr-sf-btn.danger:hover { background: var(--qr-danger-100); color: var(--qr-danger-600); border-color: var(--qr-danger-200); }

/* --- Link a la ficha de un registro (LeadLink.svelte) — prefijo qr-leadlink ---
   Movido del <style> scoped del componente (F2 de tokens, 24/09/2026). */
.qr-leadlink {
    border: 0; background: transparent; padding: 0; cursor: pointer;
    display: inline-flex; align-items: center; gap: .3rem; max-width: 100%;
    color: var(--qr-link); font-weight: 600; font-size: inherit;
}
.qr-leadlink:hover { color: var(--qr-link-hover); }
.qr-leadlink:hover .qr-leadlink__txt { text-decoration: underline; }
.qr-leadlink__txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qr-leadlink__id { color: var(--qr-text-muted); font-weight: 500; font-size: .78em; }

/* --- Campo de catálogo (CatalogField.svelte) ---
   Movido del <style> scoped del componente (F2 de tokens, 24/09/2026). */
.qr-tts-secret {
    display: flex;
    align-items: center;
    background: var(--qr-field-bg);
}

/* --- Formularios dinámicos (FormRenderer.svelte) ---
   Movido del <style> scoped del componente (F2 de tokens, 24/09/2026). */
/* El `.form-label` NO se redefine acá: el sistema ya lo fija en .85rem/600
   (queuereport_global.css). Estaba pisado a .875rem/500 —y por ser scoped le ganaba
   al global—, así que los tres formularios que rinden por acá mostraban labels más
   finos que el resto del admin sin que nadie lo hubiera decidido. */
.qr-form-section { animation: qrFadeIn 0.18s ease; }
@keyframes qrFadeIn { from { opacity: 0; transform: translateY(4px); } to { opacity: 1; transform: none; } }

/* =========================================================================
   Cierre de tematización de las pantallas (24/09/2026): lo que quedaba como
   style="" fijo o color a mano en los .svelte, ahora con clases y tokens.
   ========================================================================= */

/* --- Utilidades semánticas sueltas (reemplazan style="color:#…" / "background:#…") --- */
.qr-bg-soft      { background-color: var(--qr-surface-2) !important; }
.qr-text-body    { color: var(--qr-text-body) !important; }
.qr-text-faint   { color: var(--qr-text-faint) !important; }
.qr-text-special { color: var(--qr-special-700) !important; }
/* Botones chicos que tenían style="padding: 1px 6px" / "2px 8px" a mano. */
.qr-pad-xs       { padding: 1px 6px !important; }
.qr-pad-sm       { padding: 2px 8px !important; }

/* --- Mensajes de chat por tipo (components/message_type/*) --- */
.qr-mt-icon {
    padding: 8px; border-radius: var(--qr-radius-sm-plus);
    display: flex; align-items: center; justify-content: center;
    color: var(--qr-text-inverse);
}
.qr-mt-icon--glass { background: rgba(var(--qr-highlight-rgb), var(--qr-alpha-25)); }
.qr-mt-icon--dark  { background: var(--qr-surface-inverse-2); }
.qr-mt-icon--round { border-radius: var(--qr-radius-lg); }
.qr-mt-icon__glyph { color: var(--qr-text-inverse); }
.qr-mt-file-in     { background-color: var(--qr-surface-stronger); }
.qr-mt-media-out   { background-color: var(--qr-text-secondary); }
.qr-mt-confirm-out { background-color: var(--qr-success-100); }
.qr-mt-thumb       { max-width: 80%; height: auto; border-radius: var(--qr-radius-sm-plus); }
.qr-mt-video       { max-width: 280px; border-radius: var(--qr-radius-sm-plus); }

/* --- Texto de plantilla WhatsApp armado con {@html} (WhatsappPreview): code en
   línea, variable con valor de prueba y variable sin valor. --- */
.qr-wa-code        { font-family: var(--qr-font-mono); background: var(--qr-surface-3); padding: 2px 4px; border-radius: var(--qr-radius-xs); }
.qr-wa-var         { background: var(--qr-warning-100); padding: 0 2px; border-radius: var(--qr-radius-2xs); }
.qr-wa-var-missing { color: var(--qr-danger-600); font-family: var(--qr-font-mono); }

/* --- Panel grande de registros de una lista de difusión (ModalBroadcastRecords) --- */
.qr-records-panel {
    position: fixed; top: 10vh; left: 10vw; width: 80vw; height: 80vh;
    background: var(--qr-surface); border-radius: var(--qr-radius-md-plus); box-shadow: var(--qr-shadow-xl);
    display: flex; flex-direction: column; overflow: hidden; z-index: 10301;
}

/* --- Filas de los modales de selección viejos (ModalSelectLlmModel, ModalSelectLeadList,
   ModalSelectContext, ModalSelectLead): el hover y la fila elegida iban por JS. --- */
.qr-pick-group {
    background: var(--qr-surface-3); color: var(--qr-text-secondary);
    font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
}
.qr-pick-row { background: var(--qr-surface); border-color: var(--qr-border) !important; transition: background .1s; cursor: pointer; }
.qr-pick-row:hover { background: var(--qr-primary-25); }
.qr-pick-row.is-on { background: var(--qr-primary-soft); border-color: var(--qr-primary-light) !important; }
.qr-pick-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--qr-dot-muted); }
.qr-pick-dot.is-on { background: var(--qr-success); }
.qr-pick-avatar { width: 30px; height: 30px; background: var(--qr-primary-soft); color: var(--qr-primary); }
.qr-ctx-row { background: transparent; }
.qr-ctx-row.is-on { background: var(--qr-primary-soft); }
.qr-ctx-row__icon { color: var(--qr-text-muted); }
.qr-ctx-row__name { color: var(--qr-text); }
.qr-ctx-row.is-on .qr-ctx-row__icon,
.qr-ctx-row.is-on .qr-ctx-row__name { color: var(--qr-primary); }

/* --- Contexto del voicebot (ContextSection del agente, StageContextPanel de la etapa).
   `qr-ctxcat`: los datos del sistema agrupados, cada uno con su interruptor (`qr-switch-row`).
   `qr-ctxvars`: una lista de variables como chips, con quitar y "Agregar variable". --- */
.qr-ctxcat { display: flex; flex-direction: column; gap: 1rem; }
.qr-ctxcat__group {
    font-size: var(--qr-fs-75); font-weight: 700; color: var(--qr-text-secondary);
    text-transform: uppercase; letter-spacing: .04em; margin-bottom: .4rem;
}
.qr-ctxcat__grid {
    display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: .6rem 1.5rem;
}
.qr-ctxcat__desc { display: block; font-size: var(--qr-fs-75); color: var(--qr-text-muted); font-weight: 400; }

.qr-ctxvars { display: flex; flex-direction: column; gap: .4rem; }
.qr-ctxvars__label { font-size: var(--qr-fs-82); font-weight: 600; color: var(--qr-text-strong); }
.qr-ctxvars__help { font-size: var(--qr-fs-78); color: var(--qr-text-secondary); }
.qr-ctxvars__list { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem; }
.qr-ctxvars__empty { font-size: var(--qr-fs-78); color: var(--qr-text-muted); }
.qr-ctxvars__error { font-size: var(--qr-fs-78); color: var(--qr-danger-700); }
.qr-ctxvars__chip {
    display: inline-flex; align-items: center; gap: .2rem; max-width: 100%;
    font-family: var(--qr-font-mono); font-size: var(--qr-fs-75);
    padding: .12rem .3rem .12rem .6rem; border-radius: var(--qr-radius-pill);
    color: var(--qr-primary-hover); background: var(--qr-primary-subtle);
    overflow-wrap: anywhere;
}
.qr-ctxvars__chip--exclude { color: var(--qr-danger-700); background: var(--qr-danger-100); }
.qr-ctxvars__chip--used { color: var(--qr-text-body); background: var(--qr-surface-sunken); padding-right: .6rem; }
.qr-ctxvars__chip.is-off { text-decoration: line-through; color: var(--qr-text-muted); }
.qr-ctxvars__chip.is-unknown { box-shadow: inset 0 0 0 1px var(--qr-warning-700); }
.qr-ctxvars__x {
    border: 0; background: transparent; color: inherit; cursor: pointer;
    padding: 0 .2rem; line-height: 1; border-radius: var(--qr-radius-pill);
}
.qr-ctxvars__x:hover { background: var(--qr-surface-strong); }

/* ==========================================================================
   Puente: utilidades CLARAS de Bootstrap/CoreUI en modo oscuro (25/09/2026)

   Unas 700 veces en ~200 pantallas se usan `bg-white`, `bg-light`, `text-dark`,
   `table-light`… Son utilidades con color fijo (y !important) que en modo oscuro
   dejaban placas blancas y textos invisibles. En vez de tocar y recompilar cada
   pantalla, acá esas clases pasan a leer los tokens semánticos cuando el modo es
   oscuro; en claro no cambia nada. En pantallas NUEVAS no se usan: van las clases
   semánticas del sistema (superficie, texto y borde por token).
   Selector html:root[data-bs-theme="dark"] (0,2,1 + clase): gana a la utilidad
   de CoreUI aunque las dos lleven !important.
   ========================================================================== */
html:root[data-bs-theme="dark"] .bg-white,
html:root[data-bs-theme="dark"] .bg-body {
    background-color: var(--qr-surface) !important;
}
html:root[data-bs-theme="dark"] .bg-light,
html:root[data-bs-theme="dark"] .bg-body-tertiary,
html:root[data-bs-theme="dark"] .bg-body-secondary {
    background-color: var(--qr-surface-3) !important;
}
html:root[data-bs-theme="dark"] .text-bg-light,
html:root[data-bs-theme="dark"] .text-bg-white {
    background-color: var(--qr-surface-3) !important;
    color: var(--qr-text) !important;
}
html:root[data-bs-theme="dark"] .text-dark,
html:root[data-bs-theme="dark"] .text-black,
html:root[data-bs-theme="dark"] .text-body-emphasis,
html:root[data-bs-theme="dark"] .link-dark {
    color: var(--qr-text) !important;
}
html:root[data-bs-theme="dark"] .border-light,
html:root[data-bs-theme="dark"] .border-white {
    border-color: var(--qr-border) !important;
}
html:root[data-bs-theme="dark"] .table-light {
    --cui-table-color:          var(--qr-text);
    --cui-table-bg:             var(--qr-surface-3);
    --cui-table-border-color:   var(--qr-border);
    --cui-table-striped-bg:     var(--qr-surface-sunken);
    --cui-table-striped-color:  var(--qr-text);
    --cui-table-active-bg:      var(--qr-surface-strong);
    --cui-table-active-color:   var(--qr-text);
    --cui-table-hover-bg:       var(--qr-surface-strong);
    --cui-table-hover-color:    var(--qr-text);
    --bs-table-color:           var(--qr-text);
    --bs-table-bg:              var(--qr-surface-3);
    --bs-table-border-color:    var(--qr-border);
    color: var(--qr-text);
    border-color: var(--qr-border);
}
html:root[data-bs-theme="dark"] .btn-light {
    --cui-btn-color:               var(--qr-text);
    --cui-btn-bg:                  var(--qr-surface-3);
    --cui-btn-border-color:        var(--qr-border);
    --cui-btn-hover-color:         var(--qr-text);
    --cui-btn-hover-bg:            var(--qr-surface-strong);
    --cui-btn-hover-border-color:  var(--qr-border-strong);
    --cui-btn-active-color:        var(--qr-text);
    --cui-btn-active-bg:           var(--qr-surface-strong);
    --cui-btn-active-border-color: var(--qr-border-strong);
    --cui-btn-disabled-color:      var(--qr-text-muted);
    --cui-btn-disabled-bg:         var(--qr-surface-3);
    --cui-btn-disabled-border-color: var(--qr-border);
}

/* Avisos flotantes (svelte-sonner, `<Toaster>` en muchas pantallas): se montan con su tema
   "light" fijo. En modo oscuro sus variables pasan a las superficies y a los tokens de estado
   del tema, sin tocar ni recompilar cada pantalla. */
html:root[data-bs-theme="dark"] [data-sonner-toaster][data-sonner-theme="light"] {
    --normal-bg:      var(--qr-surface);
    --normal-bg-hover: var(--qr-surface-3);
    --normal-border:  var(--qr-border);
    --normal-border-hover: var(--qr-border-strong);
    --normal-text:    var(--qr-text);
    --success-bg:     var(--qr-ok-bg);
    --success-border: var(--qr-ok-border);
    --success-text:   var(--qr-ok-text);
    --info-bg:        var(--qr-info-bg);
    --info-border:    var(--qr-info-border);
    --info-text:      var(--qr-info-text);
    --warning-bg:     var(--qr-warn-bg);
    --warning-border: var(--qr-warn-border);
    --warning-text:   var(--qr-warn-text);
    --error-bg:       var(--qr-bad-bg);
    --error-border:   var(--qr-bad-border);
    --error-text:     var(--qr-bad-text);
}

/* ── Campo con ayuda de variables ─────────────────────────────────────────────────────
   `AutoCompleteInput` cuando la pantalla ofrece el panel de variables (voicebot): el editor
   ocupa el ancho y a su derecha va el ícono `{ }` que abre el panel para insertar en el
   cursor. Sin panel, el campo no lleva estas clases. */
.qr-var-field {
    display: flex;
    align-items: stretch;
    gap: 4px;
}

.qr-var-field__editor {
    flex: 1 1 auto;
    min-width: 0;
}

.qr-var-field__btn {
    flex: none;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: var(--qr-field-h);
    min-height: var(--qr-field-h);
    padding: 0;
    border: 1px solid var(--qr-field-border);
    border-radius: var(--qr-field-radius);
    background: var(--qr-field-bg);
    color: var(--qr-text-muted);
    font-size: var(--qr-fs-sm);
}

.qr-var-field__btn:hover {
    border-color: var(--qr-field-border-hover);
    background: var(--qr-surface-2);
    color: var(--qr-primary);
}

.qr-var-field__btn:focus-visible {
    outline: none;
    box-shadow: var(--qr-field-focus-shadow);
}

/* ==========================================================================
   Traza de debug del voicebot (Logs → Traza de voicebot, app_logs/pages/voicebot).
   Una fila por evento: tiempo desde el inicio, hora, categoría, nombre y resumen;
   al abrirla, el detalle y el JSON crudo. Sólo tokens semánticos.
   ========================================================================== */
.qr-vbtrace-toolbar {
    display: flex; flex-wrap: wrap; align-items: center; gap: .5rem .75rem;
    padding: .65rem 1rem; border-bottom: var(--qr-border-width) solid var(--qr-border);
    background: var(--qr-surface-2);
}
.qr-vbtrace-toolbar__search { flex: 1 1 14rem; max-width: 22rem; }
.qr-vbtrace-toolbar__pills { display: flex; flex-wrap: wrap; gap: .3rem; }
.qr-vbtrace-toolbar__opts { display: flex; flex-wrap: wrap; align-items: center; gap: .25rem 1rem; margin-left: auto; }

.qr-vbtrace-route { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem; }
.qr-vbtrace-route__sep { color: var(--qr-text-muted); font-size: var(--qr-fs-xs); }

.qr-vbtrace-tl { list-style: none; margin: 0; padding: 0; }
.qr-vbtrace-ev { border-bottom: var(--qr-border-width) solid var(--qr-border-soft); }
.qr-vbtrace-ev:last-child { border-bottom: 0; }
.qr-vbtrace-ev.is-error { background: var(--qr-danger-50); }
.qr-vbtrace-ev.is-warning { background: var(--qr-warning-50); }
.qr-vbtrace-ev.is-muted .qr-vbtrace-ev__sum { color: var(--qr-text-muted); }

.qr-vbtrace-ev__head {
    display: grid; width: 100%; align-items: baseline; gap: .15rem .75rem;
    grid-template-columns: 5.2rem 5.4rem 6.6rem minmax(9rem, 13rem) minmax(0, 1fr) auto;
    padding: .38rem 1rem; border: 0; background: none; text-align: left; color: var(--qr-text-body);
    font-size: var(--qr-fs-sm); cursor: pointer;
}
.qr-vbtrace-ev__head:hover { background: var(--qr-surface-sunken); }
.qr-vbtrace-ev__head:focus-visible { outline: none; box-shadow: var(--qr-field-focus-shadow); }
.qr-vbtrace-ev__t {
    font-family: var(--qr-font-mono); font-size: var(--qr-fs-xs); color: var(--qr-text-secondary);
    font-variant-numeric: tabular-nums; white-space: nowrap;
}
.qr-vbtrace-ev__name {
    font-family: var(--qr-font-mono); font-size: var(--qr-fs-xs); color: var(--qr-text-strong);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.qr-vbtrace-ev__sum { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qr-vbtrace-ev.is-open .qr-vbtrace-ev__sum { white-space: normal; }
.qr-vbtrace-ev__marks { display: inline-flex; gap: .35rem; color: var(--qr-text-muted); font-size: var(--qr-fs-xs); white-space: nowrap; }
.qr-vbtrace-ev__lvl-error { color: var(--qr-danger-700); }
.qr-vbtrace-ev__lvl-warning { color: var(--qr-warning-700); }

.qr-vbtrace-ev__body {
    padding: .5rem 1rem .85rem calc(5.2rem + 5.4rem + 2.5rem);
    background: var(--qr-surface-2); border-top: var(--qr-border-width) dashed var(--qr-border-soft);
}
.qr-vbtrace-ev__sec { margin-top: .6rem; }
.qr-vbtrace-ev__sec:first-child { margin-top: 0; }
.qr-vbtrace-ev__label {
    font-size: var(--qr-fs-68); font-weight: 700; letter-spacing: .06em; text-transform: uppercase;
    color: var(--qr-text-muted); margin-bottom: .2rem; display: flex; align-items: center; gap: .5rem;
}
.qr-vbtrace-code {
    margin: 0; padding: .55rem .7rem; border-radius: var(--qr-radius-sm);
    background: var(--qr-code-bg); color: var(--qr-code-text);
    font-family: var(--qr-font-mono); font-size: var(--qr-fs-70); line-height: 1.45;
    white-space: pre-wrap; word-break: break-word; max-height: 22rem; overflow: auto;
}
.qr-vbtrace-code--short { max-height: 10rem; }
.qr-vbtrace-frag { font-family: var(--qr-font-mono); font-size: var(--qr-fs-xs); color: var(--qr-text-secondary); }

@media (max-width: 768px) {
    .qr-vbtrace-ev__head { grid-template-columns: 4.6rem minmax(0, 1fr) auto; }
    .qr-vbtrace-ev__head > .qr-vbtrace-ev__clock,
    .qr-vbtrace-ev__head > .qr-badge { display: none; }
    .qr-vbtrace-ev__sum { grid-column: 1 / -1; }
    .qr-vbtrace-ev__body { padding-left: 1rem; }
}

/* ==========================================================================
   Lienzo de flujos compartido (qr-flow-*) — frontend/components/flow_canvas/
   --------------------------------------------------------------------------
   Las piezas genéricas de los lienzos (FlowEdge, ProblemsPanel, NodePalette)
   y el "cromo" que comparten: tema de xyflow, nodo, grupo, barra, buscador y
   panel lateral. Todo con tokens semánticos: un tema cambia el lienzo entero.

   Uso: <SvelteFlow class="qr-flow-canvas"> (o un contenedor con esa clase).
   `--qr-flow-c` es el color de CONTEXTO de cada pieza (lo fijan el tipo de
   arista, el tono y el estado de traza); las piezas lo leen para su trazo,
   su punta, su etiqueta o su ícono.
   ========================================================================== */

/* ---- Tema de xyflow ---- */
.qr-flow-canvas {
    --xy-background-color: var(--qr-surface-3);
    --xy-background-pattern-color: var(--qr-border);
    --xy-edge-stroke: var(--qr-text-muted);
    --xy-edge-stroke-selected: var(--qr-primary);
    --xy-connectionline-stroke: var(--qr-primary);
    --xy-node-border-radius: var(--qr-radius-md);
    --xy-handle-background-color: var(--qr-flow-handle);
    --xy-handle-border-color: var(--qr-surface);
    --xy-selection-background-color: rgba(var(--qr-primary-rgb), .08);
    --xy-selection-border: var(--qr-border-width) dotted var(--qr-primary);
    --xy-minimap-background-color: var(--qr-surface);
    --xy-minimap-mask-background-color: rgba(var(--qr-shadow-rgb), .08);
    --xy-minimap-node-background-color: var(--qr-border-strong);
    --xy-controls-button-background-color: var(--qr-surface);
    --xy-controls-button-background-color-hover: var(--qr-surface-3);
    --xy-controls-button-color: var(--qr-text-secondary);
    --xy-controls-button-color-hover: var(--qr-text-strong);
    --xy-controls-button-border-color: var(--qr-border-soft);
    --xy-controls-box-shadow: var(--qr-shadow-sm);
    --xy-edge-label-background-color: transparent;
    --xy-attribution-background-color: transparent;
}

/* Etiquetas y anclas de las aristas POR ENCIMA de los nodos: xyflow las pone en
   una capa anterior a la de los nodos y una arista que pasa cerca de una tarjeta
   perdía su etiqueta debajo. La capa no recibe clics (cada etiqueta sí). */
.qr-flow-canvas .svelte-flow__edge-labels { z-index: 2000; pointer-events: none; }

.qr-flow-canvas .svelte-flow__minimap {
    border: var(--qr-border-width) solid var(--qr-border);
    border-radius: var(--qr-radius-md);
    overflow: hidden;
}
.qr-flow-canvas .svelte-flow__controls { border-radius: var(--qr-radius-sm); overflow: hidden; }
.qr-flow-canvas .svelte-flow__handle { width: 8px; height: 8px; }
/* Lienzo de sólo lectura: los puntos de conexión no sirven para nada y ensucian. */
.qr-flow-canvas.is-readonly .svelte-flow__handle { opacity: 0; pointer-events: none; }

/* Minimapa: color por tono/estado con `nodeClass` (<MiniMap nodeClass={…}>). */
.qr-flow-canvas .svelte-flow__minimap-node.is-tone-neutral { fill: var(--qr-text-muted); }
.qr-flow-canvas .svelte-flow__minimap-node.is-tone-primary { fill: var(--qr-primary); }
.qr-flow-canvas .svelte-flow__minimap-node.is-tone-success { fill: var(--qr-success-600); }
.qr-flow-canvas .svelte-flow__minimap-node.is-tone-warning { fill: var(--qr-warning-600); }
.qr-flow-canvas .svelte-flow__minimap-node.is-tone-danger { fill: var(--qr-danger-600); }
.qr-flow-canvas .svelte-flow__minimap-node.is-tone-info { fill: var(--qr-info-600); }
.qr-flow-canvas .svelte-flow__minimap-node.is-tone-special { fill: var(--qr-special-600); }
.qr-flow-canvas .svelte-flow__minimap-node.is-group { fill: var(--qr-surface-3); stroke: var(--qr-border-strong); }
.qr-flow-canvas .svelte-flow__minimap-node.has-warning { fill: var(--qr-warning-300); }
.qr-flow-canvas .svelte-flow__minimap-node.has-error { fill: var(--qr-danger-300); }
.qr-flow-canvas .svelte-flow__minimap-node.is-visited { fill: var(--qr-success-600); }
.qr-flow-canvas .svelte-flow__minimap-node.is-trace-error { fill: var(--qr-danger-600); }
.qr-flow-canvas .svelte-flow__minimap-node.is-muted { opacity: .35; }

/* ---- Tonos: fijan --qr-flow-c en cualquier pieza del lienzo ---- */
:is(.qr-flow-edge, .qr-flow-edge-label, .qr-flow-anchor, .qr-flow-palette__item, .qr-flow-node, .qr-flow-group, .qr-flow-node__out) {
    --qr-flow-c: var(--qr-text-muted);
}
.qr-flow-edge.is-implicit, .qr-flow-edge-label.is-implicit, .qr-flow-anchor.is-implicit { --qr-flow-c: var(--qr-border-strong); }
.qr-flow-edge.is-branch, .qr-flow-edge-label.is-branch, .qr-flow-anchor.is-branch { --qr-flow-c: var(--qr-flow-route); }
.qr-flow-edge.is-broken, .qr-flow-edge-label.is-broken, .qr-flow-anchor.is-broken { --qr-flow-c: var(--qr-danger-600); }
:is(.qr-flow-edge, .qr-flow-edge-label, .qr-flow-anchor, .qr-flow-palette__item, .qr-flow-node, .qr-flow-group, .qr-flow-node__out).is-tone-neutral { --qr-flow-c: var(--qr-text-secondary); }
:is(.qr-flow-edge, .qr-flow-edge-label, .qr-flow-anchor, .qr-flow-palette__item, .qr-flow-node, .qr-flow-group, .qr-flow-node__out).is-tone-primary { --qr-flow-c: var(--qr-primary); }
:is(.qr-flow-edge, .qr-flow-edge-label, .qr-flow-anchor, .qr-flow-palette__item, .qr-flow-node, .qr-flow-group, .qr-flow-node__out).is-tone-success { --qr-flow-c: var(--qr-success-600); }
:is(.qr-flow-edge, .qr-flow-edge-label, .qr-flow-anchor, .qr-flow-palette__item, .qr-flow-node, .qr-flow-group, .qr-flow-node__out).is-tone-warning { --qr-flow-c: var(--qr-warning-600); }
:is(.qr-flow-edge, .qr-flow-edge-label, .qr-flow-anchor, .qr-flow-palette__item, .qr-flow-node, .qr-flow-group, .qr-flow-node__out).is-tone-danger { --qr-flow-c: var(--qr-danger-600); }
:is(.qr-flow-edge, .qr-flow-edge-label, .qr-flow-anchor, .qr-flow-palette__item, .qr-flow-node, .qr-flow-group, .qr-flow-node__out).is-tone-info { --qr-flow-c: var(--qr-info-600); }
:is(.qr-flow-edge, .qr-flow-edge-label, .qr-flow-anchor, .qr-flow-palette__item, .qr-flow-node, .qr-flow-group, .qr-flow-node__out).is-tone-special { --qr-flow-c: var(--qr-special-600); }
/* Traza de una corrida y resaltados: pisan el tono (van después a propósito). */
.qr-flow-edge.is-visited, .qr-flow-edge-label.is-visited, .qr-flow-anchor.is-visited { --qr-flow-c: var(--qr-success-600); }
.qr-flow-edge.is-trace-error, .qr-flow-edge-label.is-trace-error, .qr-flow-anchor.is-trace-error { --qr-flow-c: var(--qr-danger-600); }
.qr-flow-edge.is-highlighted, .qr-flow-edge-label.is-highlighted, .qr-flow-anchor.is-highlighted,
.qr-flow-edge.is-selected, .qr-flow-edge-label.is-selected, .qr-flow-anchor.is-selected { --qr-flow-c: var(--qr-primary); }

/* ---- Arista (FlowEdge) ---- */
/* El CSS de xyflow (`.svelte-flow__edge-path`, misma especificidad y cargado DESPUÉS en el bundle
   de cada pantalla) pinta la línea con sus variables: se las alimenta con el color del contexto,
   así la línea toma el tono, la traza y el salto roto sin depender del orden de carga (también
   seleccionada o con foco). La regla de abajo, más específica, queda de respaldo. */
.qr-flow-edge {
    --xy-edge-stroke: var(--qr-flow-c);
    --xy-edge-stroke-selected: var(--qr-flow-c);
    --xy-edge-stroke-width: calc(var(--qr-border-width) * 1.5);
}
.qr-flow-edge .qr-flow-edge__path {
    stroke: var(--qr-flow-c);
    stroke-width: calc(var(--qr-border-width) * 1.5);
    transition: stroke var(--qr-dur-fast) var(--qr-ease), stroke-width var(--qr-dur-fast) var(--qr-ease);
}
.qr-flow-edge__arrow { fill: var(--qr-flow-c); }
.qr-flow-edge.is-implicit .qr-flow-edge__path { stroke-dasharray: 5 4; }
.qr-flow-edge.is-broken .qr-flow-edge__path { stroke-dasharray: 2 4; }
.qr-flow-edge.is-visited .qr-flow-edge__path,
.qr-flow-edge.is-trace-error .qr-flow-edge__path,
.qr-flow-edge.is-highlighted .qr-flow-edge__path,
.qr-flow-edge.is-selected .qr-flow-edge__path,
.svelte-flow__edge:hover .qr-flow-edge__path { stroke-width: var(--qr-border-width-strong); }
.svelte-flow__edge.selected .qr-flow-edge__path { stroke: var(--qr-flow-c); }
.qr-flow-edge.is-muted, .qr-flow-edge-label.is-muted, .qr-flow-anchor.is-muted { opacity: .3; }

/* Etiqueta: un chip chico sobre la línea, del color de su arista. */
.qr-flow-edge-label.svelte-flow__edge-label { padding: 0; font-size: inherit; background: transparent; }
.qr-flow-edge__label {
    display: inline-block; max-width: 160px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    padding: 1px 6px; line-height: 1.4;
    border: var(--qr-border-width) solid color-mix(in srgb, var(--qr-flow-c) 35%, var(--qr-surface));
    border-radius: var(--qr-radius-sm);
    background: var(--qr-surface); color: var(--qr-flow-c);
    font-family: var(--qr-font-sans); font-size: var(--qr-fs-2xs); font-weight: 600;
    box-shadow: var(--qr-shadow-xs);
}
.qr-flow-edge-label.is-implicit .qr-flow-edge__label { color: var(--qr-text-secondary); border-style: dashed; }
.qr-flow-edge__label.is-button { cursor: pointer; }
.qr-flow-edge__label.is-button:hover { background: var(--qr-surface-2); }
.qr-flow-edge__label.is-button:focus-visible { outline: none; box-shadow: var(--qr-field-focus-shadow); }

/* Anclas de reconexión (sólo con data.reconnectable): el punto es el remate de
   la línea, macizo y del color de su arista. */
.qr-flow-anchor {
    display: flex; align-items: center; justify-content: center;
    cursor: grab; pointer-events: all;
}
.qr-flow-anchor:active { cursor: grabbing; }
.qr-flow-anchor.is-selected { z-index: 5000 !important; }
.qr-flow-anchor__dot {
    width: 9px; height: 9px; border-radius: 50%;
    background: var(--qr-flow-c);
    border: var(--qr-border-width) solid var(--qr-surface);
    box-shadow: 0 0 0 1px rgba(var(--qr-shadow-rgb), .12);
    transition: width var(--qr-dur-fast) var(--qr-ease), height var(--qr-dur-fast) var(--qr-ease), box-shadow var(--qr-dur-fast) var(--qr-ease);
    pointer-events: none;
}
.qr-flow-anchor:hover .qr-flow-anchor__dot,
.qr-flow-anchor.is-selected .qr-flow-anchor__dot {
    width: 12px; height: 12px;
    box-shadow: 0 0 0 4px rgba(var(--qr-primary-rgb), .18), 0 0 0 1px rgba(var(--qr-shadow-rgb), .15);
}

/* ---- Contenedor, barra y pantalla completa ---- */
.qr-flow-shell {
    position: relative; display: flex; flex-direction: column;
    min-height: 0; height: 100%;
    background: var(--qr-surface-3);
}
/* Lleva tabindex="-1" para recibir los atajos: sin anillo al tomar el foco. */
.qr-flow-shell:focus { outline: none; }
.qr-flow-shell:fullscreen,
.qr-flow-shell.is-fullscreen { width: 100vw; height: 100vh; background: var(--qr-body-bg); }
.qr-flow-shell__stage { position: relative; flex: 1; min-height: 0; }

.qr-flow-toolbar {
    display: flex; align-items: center; flex-wrap: wrap; gap: 6px;
    padding: 6px 10px;
    background: var(--qr-surface);
    border-bottom: var(--qr-border-width) solid var(--qr-border);
}
.qr-flow-toolbar__spacer { flex: 1; }
.qr-flow-tool {
    display: inline-flex; align-items: center; gap: 4px;
    height: var(--qr-control-height-sm); padding: 0 var(--qr-btn-padding-x-sm);
    border: var(--qr-border-width) solid var(--qr-border); border-radius: var(--qr-radius-sm);
    background: var(--qr-surface); color: var(--qr-text-body);
    font-family: inherit; font-size: var(--qr-fs-xs); font-weight: 600; white-space: nowrap;
    cursor: pointer;
}
.qr-flow-tool:hover:not(:disabled) { background: var(--qr-surface-2); border-color: var(--qr-border-strong); }
.qr-flow-tool:focus-visible { outline: none; box-shadow: var(--qr-field-focus-shadow); }
.qr-flow-tool:disabled { opacity: .5; cursor: default; }
.qr-flow-tool.is-on { background: var(--qr-primary-soft); border-color: var(--qr-primary-border-subtle); color: var(--qr-primary); }
.qr-flow-tool__count {
    min-width: 16px; padding: 0 4px; border-radius: var(--qr-radius-pill);
    background: var(--qr-primary-subtle); color: var(--qr-primary);
    font-size: var(--qr-fs-2xs); font-weight: 700; text-align: center;
}

/* ---- Buscador de nodo (centra el lienzo en el elegido) ---- */
.qr-flow-search { position: relative; display: flex; align-items: center; }
.qr-flow-search > .bi { position: absolute; left: 8px; color: var(--qr-text-muted); font-size: var(--qr-fs-xs); pointer-events: none; }
.qr-flow-search__input {
    width: 190px; height: var(--qr-control-height-sm); padding: 0 8px 0 26px;
    border: var(--qr-border-width) solid var(--qr-field-border); border-radius: var(--qr-field-radius);
    background: var(--qr-field-bg); color: var(--qr-text);
    font-family: inherit; font-size: var(--qr-fs-xs); outline: none;
}
.qr-flow-search__input:focus { border-color: var(--qr-field-focus); box-shadow: var(--qr-field-focus-shadow); }
.qr-flow-search__list {
    position: absolute; top: calc(100% + 4px); right: 0; z-index: 30;
    width: 260px; max-width: calc(100vw - 32px); max-height: 300px; overflow: auto;
    margin: 0; padding: 4px; list-style: none;
    background: var(--qr-surface); border: var(--qr-border-width) solid var(--qr-border);
    border-radius: var(--qr-radius-md); box-shadow: var(--qr-shadow-popover);
}
.qr-flow-search__item {
    width: 100%; display: flex; flex-direction: column; align-items: flex-start;
    padding: 5px 8px; border: 0; border-radius: var(--qr-radius-sm);
    background: transparent; text-align: left; cursor: pointer; font-family: inherit;
}
.qr-flow-search__item:hover, .qr-flow-search__item.is-active { background: var(--qr-surface-3); }
.qr-flow-search__name { font-family: var(--qr-font-mono); font-size: var(--qr-fs-xs); font-weight: 700; color: var(--qr-text-emphasis); }
.qr-flow-search__meta { font-size: var(--qr-fs-2xs); color: var(--qr-text-muted); }
.qr-flow-search__empty { padding: 8px; font-size: var(--qr-fs-xs); color: var(--qr-text-muted); }

/* ---- Panel lateral (detalle del nodo elegido) ---- */
.qr-flow-side {
    position: absolute; top: 0; right: 0; bottom: 0; z-index: 20;
    width: 360px; max-width: 92%;
    display: flex; flex-direction: column;
    background: var(--qr-surface);
    border-left: var(--qr-border-width) solid var(--qr-border);
    box-shadow: var(--qr-shadow-popover);
}
.qr-flow-side__head {
    display: flex; align-items: flex-start; gap: 8px;
    padding: 10px 12px; border-bottom: var(--qr-border-width) solid var(--qr-border-soft);
}
.qr-flow-side__title { flex: 1; min-width: 0; font-size: var(--qr-fs-sm); font-weight: 700; color: var(--qr-text-emphasis); }
.qr-flow-side__sub { font-size: var(--qr-fs-2xs); color: var(--qr-text-muted); }
.qr-flow-side__body { flex: 1; min-height: 0; overflow: auto; padding: 10px 12px; font-size: var(--qr-fs-xs); }
.qr-flow-side__foot {
    display: flex; justify-content: flex-end; gap: 6px;
    padding: 8px 12px; border-top: var(--qr-border-width) solid var(--qr-border-soft);
}
.qr-flow-side__label {
    margin: 10px 0 3px; font-size: var(--qr-fs-2xs); font-weight: 700;
    text-transform: uppercase; letter-spacing: .05em; color: var(--qr-text-muted);
}
.qr-flow-side__label:first-child { margin-top: 0; }
.qr-flow-side__code {
    margin: 0; padding: 6px 8px; max-height: 14rem; overflow: auto;
    border-radius: var(--qr-radius-sm); background: var(--qr-code-bg); color: var(--qr-code-text);
    font-family: var(--qr-font-mono); font-size: var(--qr-fs-2xs); line-height: 1.45;
    white-space: pre-wrap; word-break: break-word;
}

/* ---- Nodo y grupo (tarjeta genérica para los nodos de un lienzo) ---- */
.qr-flow-node {
    position: relative; box-sizing: border-box; width: 100%; height: 100%;
    display: flex; flex-direction: column; gap: 3px; overflow: hidden;
    padding: 7px 10px;
    background: var(--qr-surface); color: var(--qr-text-body);
    border: var(--qr-border-width) solid var(--qr-border);
    border-left: var(--qr-border-width-strong) solid var(--qr-flow-c);
    border-radius: var(--qr-radius-md);
    box-shadow: var(--qr-shadow-xs);
    font-size: var(--qr-fs-xs);
    transition: box-shadow var(--qr-dur-fast) var(--qr-ease), border-color var(--qr-dur-fast) var(--qr-ease), opacity var(--qr-dur-fast) var(--qr-ease);
}
.svelte-flow__node:hover > .qr-flow-node { box-shadow: var(--qr-shadow-sm); }
.qr-flow-node__head { display: flex; align-items: center; gap: 6px; min-width: 0; }
.qr-flow-node__icon {
    flex: 0 0 auto; width: 22px; height: 22px;
    display: flex; align-items: center; justify-content: center;
    border-radius: var(--qr-radius-sm);
    background: color-mix(in srgb, var(--qr-flow-c) 14%, var(--qr-surface)); color: var(--qr-flow-c);
    font-size: var(--qr-fs-xs);
}
.qr-flow-node__title {
    flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-size: var(--qr-fs-sm); font-weight: 700; color: var(--qr-text-emphasis);
}
.qr-flow-node__type { font-size: var(--qr-fs-2xs); color: var(--qr-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qr-flow-node__summary {
    font-family: var(--qr-font-mono); font-size: var(--qr-fs-2xs); color: var(--qr-text-secondary); line-height: 1.35;
    display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
    word-break: break-word;
}
.qr-flow-node__marks { display: flex; flex-wrap: wrap; gap: 4px; margin-top: auto; }
/* Salidas con nombre (sí/no, casos de un switch…): una fila de chips al pie, cada
   uno encima de su punto de conexión. */
.qr-flow-node__outs { display: flex; justify-content: space-around; gap: 4px; margin: 2px -4px -2px; }
.qr-flow-node__out {
    min-width: 0; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    padding: 0 5px; border-radius: var(--qr-radius-pill);
    background: color-mix(in srgb, var(--qr-flow-c) 12%, var(--qr-surface)); color: var(--qr-flow-c);
    font-size: var(--qr-fs-3xs); font-weight: 700; line-height: 1.5;
}
.qr-flow-node.is-selected { border-color: var(--qr-primary); box-shadow: var(--qr-focus-ring); }
.qr-flow-node.has-warning { border-color: var(--qr-warning-300); border-left-color: var(--qr-warning-600); }
.qr-flow-node.has-error { border-color: var(--qr-danger-300); border-left-color: var(--qr-danger-600); }
.qr-flow-node.is-visited { background: var(--qr-ok-bg); border-color: var(--qr-ok-border); }
.qr-flow-node.is-trace-error { background: var(--qr-danger-50); border-color: var(--qr-danger-300); }
.qr-flow-node.is-dry { border-style: dashed; }
.qr-flow-node.is-muted { opacity: .45; }
.qr-flow-node.is-highlighted { box-shadow: var(--qr-focus-ring); }
/* Nodo "fantasma": algo apunta a un nodo que no existe. */
.qr-flow-node.is-ghost {
    border: var(--qr-border-width) dashed var(--qr-danger-300);
    background: var(--qr-danger-50); color: var(--qr-bad-text); box-shadow: none;
}

.qr-flow-group {
    position: relative; box-sizing: border-box; width: 100%; height: 100%;
    border: var(--qr-border-width) dashed var(--qr-border-strong);
    border-radius: var(--qr-radius-lg);
    background: color-mix(in srgb, var(--qr-flow-c) 5%, transparent);
}
.qr-flow-group__head {
    position: absolute; top: 0; left: 0; right: 0;
    display: flex; align-items: center; gap: 6px; min-width: 0;
    padding: 8px 10px;
    font-size: var(--qr-fs-xs); color: var(--qr-text-secondary);
}
.qr-flow-group__title { flex: 0 1 auto; min-width: 0; font-weight: 700; color: var(--qr-text-emphasis); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.qr-flow-group__meta {
    flex: 1 1 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-family: var(--qr-font-mono); font-size: var(--qr-fs-2xs); color: var(--qr-text-muted);
}
.qr-flow-group.is-selected { border-color: var(--qr-primary); border-style: solid; }
.qr-flow-group.has-warning { border-color: var(--qr-warning-600); }
.qr-flow-group.has-error { border-color: var(--qr-danger-600); }
.qr-flow-group.is-visited { border-color: var(--qr-success-600); }
.qr-flow-group.is-trace-error { border-color: var(--qr-danger-600); }
.qr-flow-group.is-muted { opacity: .5; }

/* ---- Panel de problemas (ProblemsPanel) ---- */
.qr-flow-problems { position: relative; display: inline-block; }
.qr-flow-problems__toggle {
    display: inline-flex; align-items: center; gap: 5px;
    height: var(--qr-control-height-sm); padding: 0 var(--qr-btn-padding-x-sm);
    border: var(--qr-border-width) solid var(--qr-border); border-radius: var(--qr-radius-sm);
    background: var(--qr-surface); color: var(--qr-text-body);
    font-family: inherit; font-size: var(--qr-fs-xs); font-weight: 600; white-space: nowrap;
    cursor: pointer;
}
.qr-flow-problems__toggle:disabled { cursor: default; }
.qr-flow-problems__toggle:focus-visible { outline: none; box-shadow: var(--qr-field-focus-shadow); }
.qr-flow-problems__toggle.is-ok { color: var(--qr-success-600); }
.qr-flow-problems__toggle.is-warning { color: var(--qr-warn-text); border-color: var(--qr-warn-border); background: var(--qr-warn-bg); }
.qr-flow-problems__toggle.is-error { color: var(--qr-bad-text); border-color: var(--qr-bad-border); background: var(--qr-bad-bg); }
.qr-flow-problems__count {
    min-width: 16px; padding: 0 5px; border-radius: var(--qr-radius-pill);
    color: var(--qr-text-inverse); font-size: var(--qr-fs-2xs); font-weight: 700; text-align: center;
}
.qr-flow-problems__count.is-error { background: var(--qr-danger-600); }
.qr-flow-problems__count.is-warning { background: var(--qr-warning-600); }
.qr-flow-problems__list {
    position: absolute; top: calc(100% + 4px); right: 0; z-index: 30;
    width: 330px; max-width: calc(100vw - 32px); max-height: 340px; overflow: auto;
    margin: 0; padding: 4px; list-style: none;
    background: var(--qr-surface); border: var(--qr-border-width) solid var(--qr-border);
    border-radius: var(--qr-radius-md); box-shadow: var(--qr-shadow-popover);
}
.qr-flow-problems.is-left .qr-flow-problems__list { right: auto; left: 0; }
.qr-flow-problems__item {
    width: 100%; display: flex; align-items: flex-start; gap: 8px;
    padding: 6px 8px; border: 0; border-radius: var(--qr-radius-sm);
    background: transparent; color: inherit; text-align: left;
    font-family: inherit; cursor: pointer;
}
.qr-flow-problems__item:hover:not(:disabled) { background: var(--qr-surface-3); }
.qr-flow-problems__item:focus-visible { outline: none; box-shadow: var(--qr-field-focus-shadow); }
.qr-flow-problems__item:disabled { cursor: default; opacity: 1; }
.qr-flow-problems__icon { flex: 0 0 auto; font-size: var(--qr-fs-sm); line-height: 1.35; }
.qr-flow-problems__item.is-error .qr-flow-problems__icon { color: var(--qr-danger-600); }
.qr-flow-problems__item.is-warning .qr-flow-problems__icon { color: var(--qr-warning-600); }
.qr-flow-problems__item.is-info .qr-flow-problems__icon { color: var(--qr-info-600); }
.qr-flow-problems__body { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.qr-flow-problems__node {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    font-family: var(--qr-font-mono); font-size: var(--qr-fs-xs); font-weight: 700; color: var(--qr-text-emphasis);
}
.qr-flow-problems__msg { font-size: var(--qr-fs-xs); color: var(--qr-text-secondary); line-height: 1.35; word-break: break-word; }
.qr-flow-problems__go { flex: 0 0 auto; color: var(--qr-text-muted); }
.qr-flow-problems__item:hover .qr-flow-problems__go { color: var(--qr-primary); }

/* ---- Paleta de nodos (NodePalette) ---- */
/* Deja libre el pie izquierdo: ahí van los controles de zoom de xyflow (<Controls>). */
.qr-flow-palette {
    position: absolute; top: 12px; left: 12px; z-index: 18;
    width: 264px; max-width: calc(100% - 24px); max-height: calc(100% - 132px);
    display: flex; flex-direction: column;
    background: var(--qr-surface); border: var(--qr-border-width) solid var(--qr-border);
    border-radius: var(--qr-radius-md); box-shadow: var(--qr-shadow-popover);
}
.qr-flow-palette__head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 8px 10px; border-bottom: var(--qr-border-width) solid var(--qr-border-soft);
    font-size: var(--qr-fs-sm); font-weight: 700; color: var(--qr-text-strong);
}
.qr-flow-palette__title { display: inline-flex; align-items: center; gap: 6px; }
.qr-flow-palette__title > .bi { color: var(--qr-primary); }
.qr-flow-palette__close { border: 0; background: transparent; color: var(--qr-text-muted); cursor: pointer; line-height: 1; }
.qr-flow-palette__close:hover { color: var(--qr-primary); }
.qr-flow-palette__search {
    display: flex; align-items: center; gap: 6px;
    margin: 8px 8px 0; padding: 0 8px; height: var(--qr-control-height-sm);
    border: var(--qr-border-width) solid var(--qr-field-border); border-radius: var(--qr-field-radius);
    background: var(--qr-field-bg); color: var(--qr-text-muted);
}
.qr-flow-palette__search:focus-within { border-color: var(--qr-field-focus); box-shadow: var(--qr-field-focus-shadow); }
.qr-flow-palette__search input {
    flex: 1; min-width: 0; border: 0; outline: none; background: transparent;
    color: var(--qr-text); font-family: inherit; font-size: var(--qr-fs-xs);
}
.qr-flow-palette__search button { border: 0; background: transparent; color: var(--qr-text-muted); cursor: pointer; font-size: var(--qr-fs-2xs); line-height: 1; }
.qr-flow-palette__search button:hover { color: var(--qr-danger-600); }
.qr-flow-palette__hint { padding: 6px 10px 0; font-size: var(--qr-fs-2xs); color: var(--qr-text-muted); }
.qr-flow-palette__body { flex: 1; min-height: 0; overflow: auto; padding: 4px 8px 10px; }
.qr-flow-palette__group {
    display: flex; align-items: center; gap: 6px; padding: 10px 2px 4px;
    font-size: var(--qr-fs-2xs); font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
    color: var(--qr-text-muted);
}
.qr-flow-palette__item {
    display: flex; align-items: center; gap: 8px; margin-bottom: 4px; padding: 6px 8px;
    border: var(--qr-border-width) solid var(--qr-border-soft); border-radius: var(--qr-radius-sm);
    background: var(--qr-surface); color: var(--qr-text-body);
    cursor: pointer; user-select: none;
    transition: background var(--qr-dur-fast) var(--qr-ease), border-color var(--qr-dur-fast) var(--qr-ease), box-shadow var(--qr-dur-fast) var(--qr-ease);
}
.qr-flow-palette__item[draggable="true"] { cursor: grab; }
.qr-flow-palette__item[draggable="true"]:active { cursor: grabbing; }
.qr-flow-palette__item:hover { background: var(--qr-surface-2); border-color: var(--qr-border); box-shadow: var(--qr-shadow-xs); }
.qr-flow-palette__item:focus-visible { outline: none; box-shadow: var(--qr-field-focus-shadow); }
.qr-flow-palette__item.is-disabled { opacity: .5; cursor: not-allowed; }
.qr-flow-palette__icon {
    flex: 0 0 auto; width: 28px; height: 28px;
    display: flex; align-items: center; justify-content: center;
    border-radius: var(--qr-radius-sm);
    background: color-mix(in srgb, var(--qr-flow-c) 13%, var(--qr-surface)); color: var(--qr-flow-c);
    font-size: var(--qr-fs-sm);
}
.qr-flow-palette__txt { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.qr-flow-palette__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: var(--qr-fs-xs); font-weight: 600; color: var(--qr-text-emphasis); }
.qr-flow-palette__desc {
    font-size: var(--qr-fs-2xs); color: var(--qr-text-muted); line-height: 1.25;
    display: -webkit-box; -webkit-line-clamp: 2; line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.qr-flow-palette__grip { flex: 0 0 auto; color: var(--qr-text-faint); opacity: 0; transition: opacity var(--qr-dur-fast) var(--qr-ease); }
.qr-flow-palette__item:hover .qr-flow-palette__grip { opacity: 1; }
.qr-flow-palette__item.is-disabled .qr-flow-palette__grip { visibility: hidden; }
.qr-flow-palette__empty { display: flex; align-items: center; gap: 6px; padding: 12px 4px; font-size: var(--qr-fs-xs); color: var(--qr-text-muted); }
/* Cerrada: una pestaña pegada al borde, para no perder el acceso ni el ancho. */
.qr-flow-palette-tab {
    position: absolute; top: 12px; left: 0; z-index: 18;
    padding: 8px 7px;
    border: var(--qr-border-width) solid var(--qr-border); border-left: 0;
    border-radius: 0 var(--qr-radius-md) var(--qr-radius-md) 0;
    background: var(--qr-surface); color: var(--qr-primary);
    box-shadow: var(--qr-shadow-sm); cursor: pointer;
}
.qr-flow-palette-tab:hover { background: var(--qr-surface-2); }
.qr-flow-palette-tab:focus-visible { outline: none; box-shadow: var(--qr-field-focus-shadow); }

/* ── Diseñador de IVR: variables en plantillas y borrado de nodos/aristas ─────────────────
   Los menús de `TemplateField` se montan en <body> (portal) y se posicionan con `left/top`
   dinámicos: dentro del modal del nodo un menú `absolute` quedaba cortado. Sólo tokens. */
.qr-tpl-back { position: fixed; inset: 0; z-index: 1080; }
.qr-tpl-pop, .qr-tpl-sg {
    position: fixed; z-index: 1085; margin: 0;
    background: var(--qr-surface); border: var(--qr-border-width) solid var(--qr-border);
    border-radius: var(--qr-radius-md); box-shadow: var(--qr-shadow-popover);
}
.qr-tpl-pop { padding: .5rem; }
.qr-tpl-pop__list { overflow-y: auto; overscroll-behavior: contain; }
.qr-tpl-sg { list-style: none; padding: .25rem; max-height: 14rem; overflow-y: auto; overscroll-behavior: contain; }
.qr-tpl-pop__grp {
    display: flex; align-items: center; gap: .3rem; padding: .4rem .25rem .2rem;
    font-size: var(--qr-fs-66); font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
    color: var(--qr-text-muted);
}
.qr-tpl-pop__item, .qr-tpl-sg__item {
    display: flex; flex-direction: column; gap: 1px; width: 100%; text-align: left;
    border: 0; background: transparent; border-radius: var(--qr-radius-sm-plus); padding: .25rem .45rem;
}
.qr-tpl-pop__item:hover, .qr-tpl-sg__item:hover, .qr-tpl-sg__item.is-active { background: var(--qr-surface-3); }
.qr-tpl-pop__item code, .qr-tpl-sg__item code { font-size: var(--qr-fs-xs); color: var(--qr-primary); }
.qr-tpl-pop__item span, .qr-tpl-sg__item span { font-size: var(--qr-fs-68); color: var(--qr-text-secondary); }
.qr-tpl-pop__empty { padding: .5rem .25rem; font-size: var(--qr-fs-xs); color: var(--qr-text-muted); }

/* Botón "quitar" del nodo (aparece al pasar el mouse o al estar seleccionado) y de la arista. */
.qr-ivr-del {
    display: inline-flex; align-items: center; justify-content: center; flex: none;
    width: 1.25rem; height: 1.25rem; padding: 0; line-height: 1;
    border: var(--qr-border-width) solid var(--qr-border); border-radius: 50%;
    background: var(--qr-surface); color: var(--qr-text-muted); font-size: var(--qr-fs-70);
    box-shadow: var(--qr-shadow-sm); cursor: pointer; pointer-events: all;
}
.qr-ivr-del:hover { background: var(--qr-danger-600); border-color: var(--qr-danger-600); color: var(--qr-text-inverse); }
.qr-ivr-del:focus-visible { outline: none; box-shadow: var(--qr-field-focus-shadow); }
.qr-ivr-del--node { opacity: 0; transition: opacity .12s; }
.qr-ivr-node-wrap:hover .qr-ivr-del--node, .qr-ivr-node-wrap.is-sel .qr-ivr-del--node,
.qr-ivr-del--node:focus-visible { opacity: 1; }

/* Matriz "Acceso por grupo de asesores" (visibilidad por campo, `frontend/components/FieldAccessMatrix.svelte`):
   bloque plegable y un selector por celda con el estado de la regla (sin regla / sólo lectura / oculto). */
.qr-field-access { margin-top: 1.25rem; border: var(--qr-border-width) solid var(--qr-border-soft); border-radius: var(--qr-radius-sm-plus); }
.qr-field-access__toggle { background: transparent; border: 0; color: var(--qr-text); }
.qr-field-access__sticky { background: var(--qr-surface); }
.qr-field-access__seccion td { background: var(--qr-surface-2); font-weight: 600; }
.qr-field-access__link { color: var(--qr-text-secondary); }
.qr-field-access__chip {
    border: var(--qr-border-width) solid var(--qr-field-border); border-radius: var(--qr-radius-sm-plus);
    background: var(--qr-surface); color: var(--qr-text-secondary); font-size: var(--qr-fs-xs);
}
.qr-field-access__chip--READONLY { background: var(--qr-warn-bg); border-color: var(--qr-warn-border); color: var(--qr-warn-text); font-weight: 600; }
.qr-field-access__chip--HIDDEN { background: var(--qr-bad-bg); border-color: var(--qr-bad-border); color: var(--qr-bad-text); font-weight: 600; }
.qr-field-access__chip.is-changed { outline: 2px solid var(--qr-primary); }
.qr-field-access__heredado { font-size: var(--qr-fs-xs); color: var(--qr-text-muted); }

/* ==========================================================================================
   sup-ax — Supervisión en vivo: los dos ejes del asesor (persona y trabajo), capacidad y detalle.
   Componentes locales de frontend/administracion/app_supervision. Sólo tokens.
   ========================================================================================== */

/* Etiqueta con tono: persona, «puede recibir», etapa de voz. */
.sup-ax-tag { display: inline-flex; align-items: center; gap: .35rem; max-width: 100%; padding: .12rem .55rem; border-radius: var(--qr-radius-xl);
    font-size: var(--qr-fs-74); font-weight: 600; line-height: 1.3; white-space: nowrap; background: var(--qr-surface-3); color: var(--qr-text-body); }
.sup-ax-tag--ok   { background: var(--qr-success-100); color: var(--qr-success-700); }
.sup-ax-tag--warn { background: var(--qr-warning-100); color: var(--qr-warning-700); }
.sup-ax-tag--bad  { background: var(--qr-danger-100);  color: var(--qr-danger-700); }
.sup-ax-tag--info { background: var(--qr-info-100);    color: var(--qr-info-700); }
.sup-ax-tag--acc  { background: var(--qr-primary-subtle); color: var(--qr-primary-hover); }
.sup-ax-tag--mute { background: var(--qr-surface-3);   color: var(--qr-text-body); }
.sup-ax-tag__why { font-weight: 500; overflow: hidden; text-overflow: ellipsis; opacity: .85; }
.sup-ax-dot { width: .5rem; height: .5rem; border-radius: 50%; background: currentColor; flex-shrink: 0; }

/* Tira de contadores */
.sup-ax-kpis, .sup-ax-pills { display: grid; grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr)); gap: .5rem; width: 100%; }
.sup-ax-kpi { display: flex; flex-direction: column; gap: .05rem; min-width: 0; padding: .35rem .65rem; border-radius: var(--qr-radius-md);
    background: var(--qr-surface); border: 1px solid var(--qr-field-border); }
.sup-ax-kpi__l { font-size: var(--qr-fs-68); color: var(--qr-text-secondary); text-transform: uppercase; letter-spacing: .03em; font-weight: 600; }
.sup-ax-kpi__v { font-size: var(--qr-fs-105); font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; color: var(--qr-text); }
.sup-ax-kpi__s { font-size: var(--qr-fs-68); color: var(--qr-text-muted); min-height: 1em; }
.sup-ax-kpi--ok .sup-ax-kpi__v { color: var(--qr-success-700); }
.sup-ax-kpi--warn .sup-ax-kpi__v { color: var(--qr-warning-700); }
.sup-ax-kpi--bad .sup-ax-kpi__v { color: var(--qr-danger-700); }
.sup-ax-kpi--info .sup-ax-kpi__v { color: var(--qr-info-700); }

/* Pestaña «Marcadores» de Supervisión (monitoreo de marcadores, F1, 16/10/2026) */
.sup-dm-row--bad > td { background: var(--qr-danger-100); }
.sup-dm-alert { font-size: var(--qr-fs-68); color: var(--qr-danger-700); }
.sup-dm-state { display: inline-flex; align-items: center; gap: .35rem; font-weight: 600; }
.sup-dm-state--ok { color: var(--qr-success-700); }
.sup-dm-state--warn { color: var(--qr-warning-700); }
.sup-dm-state--bad { color: var(--qr-danger-700); }
.sup-dm-state--off { color: var(--qr-text-muted); }
.sup-dm-bar { display: inline-flex; width: 8rem; height: .55rem; border-radius: var(--qr-radius-xl); overflow: hidden;
    background: var(--qr-surface-3); vertical-align: middle; }
.sup-dm-bar__s { display: inline-block; height: 100%; }
.sup-dm-bar__s--todial { background: var(--qr-info-300); }
.sup-dm-bar__s--ring { background: var(--qr-info-600); }
.sup-dm-bar__s--ans { background: var(--qr-warning-500); }
.sup-dm-bar__s--agent { background: var(--qr-success-600); }
.sup-dm-wait { color: var(--qr-warning-700); }
.sup-dm-legend { display: flex; flex-wrap: wrap; gap: .9rem; }
.sup-dm-legend .sup-dm-bar__s { width: .6rem; height: .6rem; border-radius: var(--qr-radius-sm); margin-right: .3rem; vertical-align: middle; }
.sup-dm-drop { display: inline-block; padding: .05rem .5rem; border-radius: var(--qr-radius-xl); font-weight: 600;
    font-variant-numeric: tabular-nums; }
.sup-dm-drop--ok { background: var(--qr-success-100); color: var(--qr-success-700); }
.sup-dm-drop--warn { background: var(--qr-warning-100); color: var(--qr-warning-700); }
.sup-dm-drop--bad { background: var(--qr-danger-100); color: var(--qr-danger-700); }
.sup-dm-drop--none { color: var(--qr-text-muted); }
.sup-dm-prog { display: inline-block; width: 4.5rem; height: .4rem; border-radius: var(--qr-radius-xl); background: var(--qr-surface-3);
    overflow: hidden; vertical-align: middle; margin-right: .35rem; }
.sup-dm-prog__f { display: block; height: 100%; background: var(--qr-primary); }
.sup-dm-foot { display: flex; flex-wrap: wrap; align-items: center; gap: .4rem; padding-top: .6rem; border-top: 1px solid var(--qr-field-border); }
.sup-dm-pill { display: inline-flex; align-items: center; gap: .35rem; padding: .12rem .6rem; border-radius: var(--qr-radius-xl);
    font-size: var(--qr-fs-78); font-weight: 600; }
.sup-dm-pill--ok { background: var(--qr-success-100); color: var(--qr-success-700); }
.sup-dm-pill--warn { background: var(--qr-warning-100); color: var(--qr-warning-700); }
.sup-dm-pill--bad { background: var(--qr-danger-100); color: var(--qr-danger-700); }
.sup-dm-tech summary { cursor: pointer; color: var(--qr-text-secondary); }

/* Filtros: panel agrupado que se muestra u oculta desde la banda de pestañas */
.sup-ax-fpanel { display: flex; flex-wrap: wrap; align-items: stretch; gap: .5rem; padding: .5rem .65rem;
    background: var(--qr-surface); border: 1px solid var(--qr-field-border); border-radius: var(--qr-radius-md); }
.sup-ax-fgroup { display: flex; flex-direction: column; gap: .3rem; padding: 0 .75rem 0 0; margin-right: .25rem; border-right: 1px solid var(--qr-surface-3); }
.sup-ax-fgroup:nth-last-child(2) { border-right: 0; }
.sup-ax-fgroup__t { font-size: var(--qr-fs-66); text-transform: uppercase; letter-spacing: .04em; font-weight: 700; color: var(--qr-text-muted); }
.sup-ax-fgroup__f { display: flex; flex-wrap: wrap; align-items: flex-end; gap: .5rem; }
.sup-ax-field { display: flex; flex-direction: column; gap: .1rem; margin: 0; }
.sup-ax-field > span { font-size: var(--qr-fs-68); color: var(--qr-text-secondary); font-weight: 600; }
.sup-ax-field .form-select { width: auto; min-width: 8.5rem; max-width: 14rem; }
.sup-ax-switch { padding-bottom: .3rem; white-space: nowrap; }
.sup-ax-ffoot { display: flex; flex-direction: column; justify-content: space-between; align-items: flex-end; gap: .3rem; margin-left: auto; }
.sup-ax-filters__count { font-size: var(--qr-fs-74); color: var(--qr-text-secondary); }
/* Botón de la banda de pestañas que muestra u oculta los filtros */
.sup-filter-toggle { display: inline-flex; align-items: center; gap: .4rem; border: 0; background: transparent; white-space: nowrap;
    color: rgba(var(--qr-highlight-rgb), .78); font-size: var(--qr-fs-82); font-weight: 600; border-radius: var(--qr-radius-md); padding: .42rem .7rem; }
.sup-filter-toggle:hover { background: rgba(var(--qr-highlight-rgb), .1); color: var(--qr-text-inverse); }
.sup-filter-toggle.is-open { background: rgba(var(--qr-highlight-rgb), .18); color: var(--qr-text-inverse); }

/* Frescura de los datos (barra de arriba) */
.sup-ax-fresh { display: inline-flex; align-items: center; gap: .35rem; padding: .12rem .55rem; border-radius: var(--qr-radius-xl);
    font-size: var(--qr-fs-74); font-weight: 600; background: var(--qr-surface-3); color: var(--qr-text-body); }
.sup-ax-fresh--live { background: var(--qr-success-100); color: var(--qr-success-700); }
.sup-ax-fresh--recent { background: var(--qr-warning-100); color: var(--qr-warning-700); }
.sup-ax-fresh--stale { background: var(--qr-danger-100); color: var(--qr-danger-700); }

/* Tabla de asesores: una fila de 36 px por asesor */
.sup-ax-wrap { transition: opacity .2s; }
.sup-ax-wrap.is-stale { opacity: .55; }
.sup-ax-table { width: 100%; border-collapse: collapse; }
.sup-ax-table th { font-size: var(--qr-fs-68); text-transform: uppercase; letter-spacing: .03em; color: var(--qr-text-secondary); font-weight: 700;
    text-align: left; padding: .4rem .6rem; border-bottom: 2px solid var(--qr-field-border); white-space: nowrap; }
.sup-ax-table th.is-person { color: var(--qr-primary-hover); }
.sup-ax-table th.is-work { color: var(--qr-info-700); }
.sup-ax-table td.is-sep, .sup-ax-table th.is-sep { border-left: 1px solid var(--qr-field-border); }
.sup-ax-row { height: 36px; cursor: pointer; border-bottom: 1px solid var(--qr-surface-3); }
.sup-ax-row:hover { background: var(--qr-surface-2); }
.sup-ax-row.is-selected { background: var(--qr-primary-soft); }
.sup-ax-row:focus-visible { outline: 2px solid var(--qr-primary); outline-offset: -2px; }
.sup-ax-row td { padding: .15rem .6rem; vertical-align: middle; }
.sup-ax-who { display: flex; align-items: center; gap: .5rem; min-width: 0; }
.sup-ax-avatar { display: inline-grid; place-items: center; width: 26px; height: 26px; border-radius: 50%; flex-shrink: 0;
    background: var(--qr-primary-soft); color: var(--qr-primary); font-weight: 700; font-size: var(--qr-fs-xs); }
.sup-ax-who__txt { display: flex; flex-direction: column; min-width: 0; line-height: 1.15; }
.sup-ax-who__name { font-weight: 600; font-size: var(--qr-fs-82); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sup-ax-who__sub { font-size: var(--qr-fs-66); color: var(--qr-text-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sup-ax-person { display: flex; align-items: center; gap: .45rem; flex-wrap: wrap; }
.sup-ax-sub { font-size: var(--qr-fs-68); color: var(--qr-text-muted); font-variant-numeric: tabular-nums; }
.sup-ax-sub--bad { color: var(--qr-danger-700); font-weight: 600; }
.sup-ax-work { display: flex; align-items: center; gap: .35rem; flex-wrap: wrap; }
.sup-ax-chip { display: inline-flex; align-items: center; gap: .3rem; padding: .08rem .5rem .08rem .15rem; border-radius: var(--qr-radius-xl);
    background: var(--qr-surface); border: 1px solid var(--qr-field-border); font-size: var(--qr-fs-74); white-space: nowrap; }
.sup-ax-chip--hot { border-color: var(--qr-danger-200); }
.sup-ax-chip--alert { background: var(--qr-danger-100); border-color: var(--qr-danger-200); color: var(--qr-danger-700); }
.sup-ax-ic { font-size: var(--qr-fs-62); font-weight: 700; letter-spacing: .03em; padding: .08rem .35rem; border-radius: var(--qr-radius-xl); color: var(--qr-text-inverse); }
.sup-ax-ic--v { background: var(--qr-danger-600); }
.sup-ax-ic--c { background: var(--qr-info-600); }
.sup-ax-ic--g { background: var(--qr-special-600); }
.sup-ax-t { font-variant-numeric: tabular-nums; font-weight: 600; }
.sup-ax-t--bad { color: var(--qr-danger-700); }
.sup-ax-muted { font-size: var(--qr-fs-74); color: var(--qr-text-muted); }
.sup-ax-alert { color: var(--qr-danger-600); margin-left: .35rem; }

/* Estado vacío / error */
.sup-ax-empty { display: flex; flex-direction: column; align-items: center; gap: .35rem; text-align: center; padding: 2.5rem 1rem; color: var(--qr-text-secondary); }
.sup-ax-empty > i { font-size: var(--qr-fs-xl); color: var(--qr-text-muted); }
.sup-ax-empty__t { font-weight: 700; color: var(--qr-text); }
.sup-ax-empty--error > i { color: var(--qr-danger-600); }

/* Detalle del asesor (en el modal global) */
.sup-ax-md { display: flex; flex-direction: column; gap: .9rem; }
.sup-ax-sec { display: flex; flex-direction: column; gap: .4rem; }
.sup-ax-sec__t { font-size: var(--qr-fs-68); text-transform: uppercase; letter-spacing: .04em; font-weight: 700; color: var(--qr-text-muted); }
.sup-ax-sec__t.is-work { color: var(--qr-info-700); }
.sup-ax-sec__t.is-person { color: var(--qr-primary-hover); }
.sup-ax-line { display: flex; align-items: center; gap: .5rem; flex-wrap: wrap; font-size: var(--qr-fs-82); }
.sup-ax-card { border: 1px solid var(--qr-field-border); border-radius: var(--qr-radius-md); padding: .5rem .65rem; display: flex; flex-direction: column; gap: .3rem; }
.sup-ax-card__row { display: flex; align-items: center; justify-content: space-between; gap: .5rem; flex-wrap: wrap; font-size: var(--qr-fs-82); }
.sup-ax-audio { display: flex; flex-wrap: wrap; gap: .4rem; align-items: center; }
.sup-ax-audio__note { font-size: var(--qr-fs-68); color: var(--qr-text-muted); }
.sup-ax-alertbox { background: var(--qr-bad-bg); border: 1px solid var(--qr-bad-border); color: var(--qr-bad-text); border-radius: var(--qr-radius-md);
    padding: .45rem .65rem; font-size: var(--qr-fs-82); display: flex; flex-direction: column; gap: .15rem; }
.sup-ax-timeline { display: flex; flex-direction: column; gap: .2rem; font-size: var(--qr-fs-82); }
/* Tiempo del día por estado: una barra apilada y su leyenda */
.sup-ax-stack { display: flex; height: .85rem; border-radius: var(--qr-radius-xl); overflow: hidden; background: var(--qr-surface-3); }
.sup-ax-stack__seg { height: 100%; min-width: 2px; }
.sup-ax-legend { display: flex; flex-wrap: wrap; gap: .25rem .9rem; font-size: var(--qr-fs-74); color: var(--qr-text-secondary); }
.sup-ax-legend__dot { display: inline-block; width: .55rem; height: .55rem; border-radius: 50%; margin-right: .3rem; }
.sup-ax-legend b { color: var(--qr-text); font-variant-numeric: tabular-nums; }
/* Gráficos del día: llamadas por hora y calificaciones, lado a lado */
.sup-ax-charts { display: grid; grid-template-columns: 1.4fr 1fr; gap: .75rem; align-items: start; }
.sup-ax-box { background: var(--qr-surface); border: 1px solid var(--qr-border); border-radius: var(--qr-radius-md-plus); padding: .75rem .9rem; display: flex; flex-direction: column; gap: .5rem; }
.sup-ax-box__t { font-size: var(--qr-fs-82); font-weight: 700; color: var(--qr-text); }
.sup-ax-bars { display: flex; flex-direction: column; gap: .4rem; }
.sup-ax-bars__row { display: grid; grid-template-columns: minmax(0, 8rem) 1fr auto; align-items: center; gap: .5rem; font-size: var(--qr-fs-74); }
.sup-ax-bars__n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--qr-text-body); }
.sup-ax-bars__track { height: .55rem; background: var(--qr-surface-3); border-radius: var(--qr-radius-xl); overflow: hidden; }
.sup-ax-bars__fill { display: block; height: 100%; border-radius: var(--qr-radius-xl); background: var(--qr-text-muted); }
.sup-ax-bars__fill--ok { background: var(--qr-success-500); }
.sup-ax-bars__fill--info { background: var(--qr-info-500); }

/* Celular: la fila pasa a tarjeta (nombre y persona arriba; gestiones y capacidad; canales y acciones) */
@media (max-width: 820px) {
    .sup-ax-table thead { display: none; }
    .sup-ax-table, .sup-ax-table tbody { display: block; }
    .sup-ax-row { display: grid; grid-template-columns: 1fr auto auto; grid-template-areas: "who who person" "work work cap" "chan chan act";
        height: auto; gap: .25rem .5rem; padding: .5rem .25rem; }
    .sup-ax-row td { display: block; padding: 0; border: 0; }
    .sup-ax-row td.is-sep { border-left: 0; }
    .sup-ax-row .c-who { grid-area: who; }
    .sup-ax-row .c-person { grid-area: person; }
    .sup-ax-row .c-work { grid-area: work; }
    .sup-ax-row .c-chan { grid-area: chan; }
    .sup-ax-row .c-cap { grid-area: cap; }
    .sup-ax-row .c-act { grid-area: act; }
    .sup-ax-row .c-old { display: none; }
}
@media (max-width: 640px) {
    .sup-ax-kpis, .sup-ax-pills { grid-template-columns: repeat(2, 1fr); }
    .sup-ax-charts { grid-template-columns: 1fr; }
    .sup-ax-fgroup { border-right: 0; width: 100%; }
    .sup-ax-field { flex: 1 1 8rem; }
    .sup-ax-field .form-select { width: 100%; max-width: none; }
    .sup-ax-ffoot { flex-direction: row; width: 100%; align-items: center; }
}
