/*
 * ============================================
 *  components.css — Veriko componentes compartidos (rediseño Fase 1)
 * ============================================
 *  Bloques de UI REUTILIZABLES entre páginas. Cualquier pantalla los usa tal
 *  cual; no se redefinen por-página (eso causa inconsistencia). Solo roles de
 *  token. Carga después de shell.css. En producción reemplaza a components.css.
 *
 *  Inventario:
 *   - Base: botones · card · section/heading · link-more · verdict (color+icono+
 *     texto) · tabla densa + celdas · bank-chip · list-row · usage · stat · quick.
 *   - Listas/filtros (hero #3): page-head · icon-action · field-search · segs ·
 *     filterbar/chip · count-note/results-meta · type-tag/row-action · pager ·
 *     dropdown · sheet/drawer · chip-set/chip-opt · amount-row.
 *   - Wizard/formulario (hero #3): step-bar · sel-card · ocr-hint ·
 *     field · options/option-row/switch · retry-config (slider/select/chips/preview) ·
 *     proc-state. (El FRAME del modal .wz* vive en su pantalla.)
 *   - Detalle/veredicto (hero #3): detail-back · verdict-hero · detail-card-title ·
 *     data-rows (.dl) · timeline · detail-actions. (Layout 2-col + CEP en la pantalla.)
 * ============================================
 */
/* ---------- Botones ---------- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    min-height: 44px;
    padding: 0 var(--space-4);
    border-radius: var(--btn-radius);
    border: 1px solid transparent;
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
    font-family: inherit;
    cursor: pointer;
    transition: background var(--transition-fast);
}

.btn .icon {
    width: 1.125rem;
    height: 1.125rem;
}

.btn-primary {
    background: var(--color-primary);
    color: var(--color-primary-contrast);
}

.btn-primary:hover {
    background: var(--color-primary-hover);
}

.btn-ghost {
    background: transparent;
    color: var(--color-text-secondary);
    border-color: var(--color-border);
}

.btn-ghost:hover {
    background: var(--color-surface-sunken);
    color: var(--color-text-primary);
}

.btn-block {
    width: 100%;
}

.btn-lg {
    min-height: 48px;
    font-size: var(--text-base);
}

/* ---------- Card ---------- */
.card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--card-radius);
}

/* ---------- Section + heading ---------- */
.section {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.section-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.section-title {
    font-size: var(--text-base);
    font-weight: var(--font-semibold);
    margin: 0;
}

.link-more {
    color: var(--color-text-link);
    font-weight: var(--font-medium);
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: var(--text-sm);
}

.link-more .icon {
    width: 1rem;
    height: 1rem;
}

/* ---------- Verdict badge (SIEMPRE color + icono + texto — design.md §6) ---------- */
.verdict {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 8px;
    border-radius: var(--badge-radius);
    font-size: 11px;
    font-weight: var(--font-semibold);
    white-space: nowrap;
}

.verdict .icon {
    width: 13px;
    height: 13px;
}

.verdict.ok {
    background: var(--color-success-light);
    color: var(--color-success-text);
}

.verdict.no {
    background: var(--color-danger-light);
    color: var(--color-danger-text);
}

.verdict.pending {
    background: var(--color-warning-light);
    color: var(--color-warning-text);
}

.verdict.processing {
    background: var(--color-info-light);
    color: var(--color-info-text);
}

/* async en proceso (color + icono giratorio + texto) */
.verdict.processing .icon {
    animation: verdict-spin 1s linear infinite;
}

@keyframes verdict-spin {
    to {
        transform: rotate(360deg);
    }
}

@media (prefers-reduced-motion: reduce) {
    .verdict.processing .icon {
        animation: none;
    }
}

/* ---------- Tabla densa ---------- */
.table-wrap {
    overflow-x: auto;
}

table.table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-sm);
}

.table thead th {
    text-align: left;
    font-size: var(--text-xs);
    font-weight: var(--font-semibold);
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--color-text-tertiary);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--color-border);
}

.table tbody td {
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--color-border);
    vertical-align: middle;
}

.table tbody tr:last-child td {
    border-bottom: 0;
}

.table tbody tr:hover {
    background: var(--table-row-hover);
}

.cell-amount {
    text-align: right;
    font-weight: var(--font-semibold);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.cell-amount small {
    font-weight: var(--font-normal);
    color: var(--color-text-tertiary);
    font-size: var(--text-xs);
}

.cell-amount.is-neg {
    color: var(--color-danger-text);
}

/* Celda de usuario (avatar + nombre + correo). Canónico — convergencia ×5
   (usuarios·validaciones·facturación·seguridad·beneficiarios). El correo va en mono
   con .is-mono (4/5 lo usan; facturación queda sans). */
.user-cell {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2-5);
    min-width: 0;
}

.user-cell .avatar-sm {
    background: var(--color-surface-sunken);
    color: var(--color-text-secondary);
}

.user-cell-text {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.user-cell-name {
    font-weight: var(--font-medium);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.user-cell-name a {
    color: var(--color-text-link);
}

.user-cell-name a:hover {
    text-decoration: underline;
}

.user-cell-mail {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.user-cell-mail.is-mono {
    font-family: var(--font-family-mono);
}

.cell-num {
    text-align: right;
    font-variant-numeric: tabular-nums;
}

/* numérico simple (intento/tiempo/conteo): derecha + tnum, sin negrita */
.cell-bene {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.cell-mono {
    font-family: var(--font-family-mono);
}

/* Pie de tabla: contador + enlace «ver todas». De admin/usuarios/detalle (cualquier tabla con resumen). */
.table-foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-top: 1px solid var(--color-border);
}

.table-foot-count {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
}

/* ---------- Bank chip (avatar de banco) ---------- */
.bank-chip {
    width: 36px;
    height: 36px;
    border-radius: var(--radius-md);
    background: var(--color-surface-sunken);
    display: grid;
    place-items: center;
    font-size: 10px;
    font-weight: var(--font-bold);
    color: var(--color-text-secondary);
    flex: 0 0 auto;
}

/* ---------- List-row (lista de entidades en tarjetas — móvil/tablet) ---------- */
.recent-cards {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.rcard {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    min-height: 64px;
}

/* Cuerpo en columna: línea (nombre + monto) · CLABE a ancho completo · veredicto abajo.
   Así la cuenta beneficiaria (CLABE larga) vive en su propia línea y se ve COMPLETA. */
.rcard-main {
    min-width: 0;
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
}

.rcard-line {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-2);
}

.rcard-name {
    font-weight: var(--font-medium);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    flex: 1;
    min-width: 0;
}

.rcard-amount {
    font-weight: var(--font-semibold);
    font-variant-numeric: tabular-nums;
    flex: 0 0 auto;
}

.rcard-sub {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
}

.rcard-main > .verdict {
    align-self: flex-start;
    margin-top: 2px;
}

/* dashboard: veredicto suelto */
/* Fila meta (lista densa): tipo · veredicto · fecha — bajo la CLABE, con wrap.
   El dashboard no la usa (su veredicto va suelto en .rcard-main). */
.rcard-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-2);
    margin-top: 2px;
}

.rcard-type {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: var(--text-xs);
    color: var(--color-text-secondary);
}

.rcard-type .icon {
    width: 14px;
    height: 14px;
}

.rcard-date {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
}

.recent-table {
    display: none;
}

/* lista densa: tarjetas en chico, tabla en grande */
/* Tarjetas CON monto: monto DEBAJO de la cuenta/CLABE y en grande (directiva owner, paseo C4).
   Aplica a toda .rcard que tenga .rcard-amount (dashboard · validaciones · finanzas);
   beneficiarios/insights no tienen monto → no les afecta. .rcard-line display:contents pasa
   nombre y monto a ser hijos del .rcard-main (columna) y se reordena:
   nombre → cuenta/CLABE → monto(grande) → meta/veredicto. */
.rcard-line:has(.rcard-amount) {
    display: contents;
}

.rcard-main:has(.rcard-amount) .rcard-name {
    order: 1;
}

.rcard-main:has(.rcard-amount) .rcard-sub {
    order: 2;
}

.rcard-main:has(.rcard-amount) .rcard-amount {
    order: 3;
    margin-top: 2px;
    font-size: var(--text-xl);
    font-weight: var(--font-bold);
    font-variant-numeric: tabular-nums;
}

.rcard-main:has(.rcard-amount) .rcard-meta, .rcard-main:has(.rcard-amount) > .verdict {
    order: 4;
}

/* ---------- Usage (barra de uso del plan) ---------- */
.usage {
    padding: var(--space-4) var(--space-5);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.usage-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
}

.usage-plan {
    font-weight: var(--font-semibold);
}

.usage-plan small {
    color: var(--color-text-tertiary);
    font-weight: var(--font-normal);
}

.usage-bar {
    height: 8px;
    border-radius: var(--badge-radius);
    background: var(--color-surface-sunken);
    overflow: hidden;
}

.usage-fill {
    height: 100%;
    background: var(--color-accent);
}

.usage-meta {
    display: flex;
    justify-content: space-between;
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    font-variant-numeric: tabular-nums;
}

/* Acciones del card de uso: al fondo, a la derecha (directiva owner) */
.usage-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: var(--space-4);
    margin-top: var(--space-1);
}

/* ---------- Stat (tarjeta de métrica compacta) ---------- */
.ministats {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--space-2);
}

.ministat {
    padding: var(--space-3);
}

.ministat-val {
    font-size: var(--text-xl);
    font-weight: var(--font-bold);
    font-variant-numeric: tabular-nums;
    letter-spacing: -.02em;
}

.ministat-label {
    color: var(--color-text-secondary);
    font-size: var(--text-xs);
    margin-top: 2px;
    line-height: 1.2;
}

/* ---------- Quick action (tarjeta de acción rápida) ---------- */
.quick-row {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-2);
}

.quick {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    text-align: left;
    padding: var(--space-3) var(--space-4);
    min-height: 56px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--card-radius);
}

.quick:hover {
    border-color: var(--color-border-strong);
    background: var(--color-surface-sunken);
}

.quick-ico {
    width: 40px;
    height: 40px;
    border-radius: var(--radius-md);
    background: var(--color-accent-light);
    color: var(--color-accent);
    display: grid;
    place-items: center;
    flex: 0 0 auto;
}

.quick-title {
    font-weight: var(--font-semibold);
}

.quick-copy {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
}

/* ============================================================
   Responsive de componentes.
   ============================================================ */
@media (min-width: 768px) {
    .quick-row {
        grid-template-columns: repeat(3, 1fr);
    }

    .ministat-val {
        font-size: var(--text-2xl);
    }
}

@media (min-width: 1100px) {
    .recent-cards {
        display: none;
    }

    /* en grande, la lista se muestra como tabla */
    .recent-table {
        display: block;
    }
}

/* ============================================================
   LISTAS / FILTROS / DROPDOWN / DRAWER (extraído del hero #3)
   ------------------------------------------------------------
   Bloques reutilizables de cualquier pantalla de lista/CRUD. Los
   interactivos (dropdown, sheet/drawer) son el ESTILO; el comportamiento en
   producción usa las primitivas canónicas APP.dropdown / APP.drawer (ui.js).
   ============================================================ */
/* Encabezado de página */
/* Wrapper de página (#page-*) transparente: los mocks ponen las secciones DIRECTAS en
   .content (que es flex column con gap). Al envolverlas en #page-* (block) se perdía ese
   gap → secciones apretadas. display:contents devuelve las secciones a hijas directas del
   .content, recuperando el gap (idéntico al mock). Los selectores #page-* descendientes
   siguen funcionando. */
.content > [id^="page-"] {
    display: contents;
}

/* Canon: las acciones del header NUNCA van inline con el título (a la derecha);
   se apilan DEBAJO del subtítulo, en una fila (los botones sí inline entre sí). */
.page-head {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-4);
    margin-bottom: var(--space-8);
}

.page-title {
    font-size: var(--text-xl);
    font-weight: var(--font-bold);
    letter-spacing: -.02em;
    margin: 0;
}

.page-sub {
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    margin: var(--space-1) 0 0;
}

.page-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: 0 0 auto;
}

/* Botón de icono cuadrado (+ variante de acento) */
.icon-action {
    width: 40px;
    height: 40px;
    display: grid;
    place-items: center;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: var(--color-text-secondary);
    cursor: pointer;
}

.icon-action:hover {
    background: var(--color-surface-sunken);
    color: var(--color-text-primary);
}

.icon-action--accent {
    background: var(--color-primary);
    border-color: transparent;
    color: var(--color-primary-contrast);
}

.icon-action--accent:hover {
    background: var(--color-primary-hover);
    color: var(--color-primary-contrast);
}

/* Buscador de campo (página/lista) */
.field-search {
    position: relative;
    display: flex;
    align-items: center;
}

.field-search .icon {
    position: absolute;
    left: var(--space-3);
    width: 1.125rem;
    height: 1.125rem;
    color: var(--color-text-tertiary);
    pointer-events: none;
}

.field-search input {
    width: 100%;
    min-width: 0;
    height: var(--input-height);
    padding: 0 var(--space-3) 0 calc(var(--space-3) + 26px);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: var(--color-text-primary);
    font-size: var(--text-sm);
    font-family: inherit;
}

.field-search input:focus {
    outline: none;
    border-color: var(--color-border-focus);
    box-shadow: var(--input-focus-ring);
}

/* Cluster de filtros: búsqueda + tabs + barra, compacto */
.filters {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

/* Segmentos de estado (triage + conteo) */
.segs {
    display: flex;
    gap: var(--space-2);
    overflow-x: auto;
    padding-bottom: 2px;
    scrollbar-width: none;
}

.segs::-webkit-scrollbar {
    display: none;
}

.seg {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    height: 38px;
    padding: 0 var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--badge-radius);
    background: var(--color-surface);
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    white-space: nowrap;
    cursor: pointer;
}

.seg:hover {
    background: var(--color-surface-sunken);
}

.seg .count {
    font-variant-numeric: tabular-nums;
    color: var(--color-text-tertiary);
    font-size: var(--text-xs);
}

.seg .sdot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    flex: 0 0 auto;
}

.sdot.ok {
    background: var(--color-success);
}

.sdot.pending {
    background: var(--color-warning);
}

.sdot.no {
    background: var(--color-danger);
}

.sdot.proc {
    background: var(--color-info);
}

/* .info: mismo tono que .proc (--color-info) — nombre semántico para estados neutrales/informativos (p.ej. bot lead). De admin/bots. */
.sdot.info {
    background: var(--color-info);
}

.seg.active {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: var(--color-text-inverse);
}

.seg.active .count {
    color: rgba(255,255,255,.82);
}

.seg.active .sdot {
    box-shadow: 0 0 0 2px rgba(255,255,255,.5);
}

/* Barra de chips de filtro */
.filterbar {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.filterbar-spacer {
    flex: 1;
}

.chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 36px;
    padding: 0 var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    cursor: pointer;
}

.chip:hover {
    background: var(--color-surface-sunken);
    color: var(--color-text-primary);
}

.chip .icon {
    width: 1rem;
    height: 1rem;
    color: var(--color-text-tertiary);
}

/* Contador de filtros activos dentro de un .chip (botón "Filtros"). De admin/validaciones/lista. */
.chip-count {
    display: inline-grid;
    place-items: center;
    min-width: 18px;
    height: 18px;
    padding: 0 5px;
    margin-left: 2px;
    border-radius: var(--radius-full);
    background: var(--color-accent);
    color: var(--color-text-inverse);
    font-size: 11px;
    font-weight: var(--font-semibold);
    font-variant-numeric: tabular-nums;
    line-height: 1;
}

.chip-count[hidden] {
    display: none;
}

/* Filtros activos removibles + Limpiar (fila de chips bajo la barra de filtros).
   Canónico — convergencia de usuarios/beneficiarios/notificaciones (PR convergencia IA). */
.filter-active {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.filter-active[hidden] {
    display: none;
}

.filter-active-label {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    margin-right: var(--space-1);
}

.filter-chip {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    height: 30px;
    padding: 0 4px 0 var(--space-3);
    border: 1px solid var(--color-accent);
    background: var(--color-accent-light);
    color: var(--color-text-link);
    border-radius: var(--badge-radius);
    font-size: var(--text-xs);
    font-weight: var(--font-medium);
    white-space: nowrap;
}

.filter-chip-x {
    display: grid;
    place-items: center;
    width: 20px;
    height: 20px;
    border: 0;
    background: transparent;
    color: inherit;
    border-radius: 50%;
    cursor: pointer;
    flex: 0 0 auto;
    padding: 0;
}

.filter-chip-x:hover {
    background: var(--color-surface);
}

.filter-chip-x .icon {
    width: 13px;
    height: 13px;
}

.filters-clear {
    background: transparent;
    border: 0;
    padding: 0 var(--space-1);
    color: var(--color-text-link);
    font-family: inherit;
    font-size: var(--text-xs);
    font-weight: var(--font-semibold);
    cursor: pointer;
}

.filters-clear:hover {
    text-decoration: underline;
}

/* Conteo de resultados + fila de resultados (conteo izq · acciones der) */
.count-note {
    color: var(--color-text-tertiary);
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
}

.results-meta {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
}

.results-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

/* Tabla: etiqueta de tipo + acción de fila (kebab) */
.type-tag {
    font-size: var(--text-xs);
    color: var(--color-text-secondary);
    display: inline-flex;
    align-items: center;
    gap: 5px;
}

.type-tag .icon {
    width: 14px;
    height: 14px;
    color: var(--color-text-tertiary);
}

.row-action {
    width: 40px;
    height: 40px;
    display: grid;
    place-items: center;
    border: 0;
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--color-text-tertiary);
    cursor: pointer;
}

.row-action:hover {
    background: var(--color-surface-sunken);
    color: var(--color-text-primary);
}

/* List-row: extras (tipo + fecha). La base .rcard está más arriba. */
.rcard {
    cursor: pointer;
}

.rcard-type {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    display: inline-flex;
    align-items: center;
    gap: 4px;
}

.rcard-type .icon {
    width: 12px;
    height: 12px;
}

.rcard-date {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    font-variant-numeric: tabular-nums;
}

/* Paginación */
.pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    padding-top: var(--space-2);
}

.pager-info {
    color: var(--color-text-tertiary);
    font-size: var(--text-xs);
    font-variant-numeric: tabular-nums;
}

/* Dropdown / popover — ESTILO; en prod el comportamiento = APP.dropdown */
.dropdown-wrap {
    position: relative;
    flex: 0 0 auto;
}

.dropdown {
    position: absolute;
    top: calc(100% + 6px);
    left: 0;
    min-width: 200px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    padding: var(--space-1);
    z-index: var(--z-popover);
}

.dropdown[hidden] {
    display: none;
}

.dropdown-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    width: 100%;
    min-height: 38px;
    padding: 0 var(--space-2-5);
    border: 0;
    border-radius: var(--radius-md);
    background: transparent;
    color: var(--color-text-primary);
    font-size: var(--text-sm);
    font-family: inherit;
    cursor: pointer;
    text-align: left;
}

.dropdown-item:hover {
    background: var(--color-surface-sunken);
}

.dropdown-item .check {
    width: 1rem;
    height: 1rem;
    color: var(--color-accent);
    visibility: hidden;
}

.dropdown-item[aria-selected="true"] {
    color: var(--color-text-link);
    font-weight: var(--font-semibold);
}

.dropdown-item[aria-selected="true"] .check {
    visibility: visible;
}

.ddi-label {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2-5);
    min-width: 0;
}

/* icono+texto de un .dropdown-item (menú ⋯) — canónico (convergencia bn/mg/op) */
.menu-go {
    width: 1rem;
    height: 1rem;
    color: var(--color-text-tertiary);
}

/* chevron "ir a" al final de un ítem de menú */
/* Diff badge (cambios en step-up): etiqueta mayúscula + valor viejo tachado + flecha.
   Canónico — convergencia planes/facturación. */
.diff-tag {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    min-width: 64px;
    justify-content: center;
    padding: 2px 8px;
    border-radius: var(--radius-sm);
    font-size: 10px;
    font-weight: var(--font-bold);
    text-transform: uppercase;
    letter-spacing: .04em;
    flex: 0 0 auto;
}

.diff-tag.is-create {
    background: var(--color-success-light);
    color: var(--color-success-text);
}

.diff-tag.is-edit, .diff-tag.is-credit {
    background: var(--color-warning-light);
    color: var(--color-warning-text);
}

.diff-tag.is-archive, .diff-tag.is-refund {
    background: var(--color-danger-light);
    color: var(--color-danger-text);
}

.diff-old {
    color: var(--color-text-tertiary);
    text-decoration: line-through;
}

.diff-arrow {
    color: var(--color-text-tertiary);
    margin: 0 4px;
}

/* Mini-barra de progreso (track + relleno con tono). Canónico — convergencia
   roles/notificaciones/seguridad/servicios/validaciones-detalle. (.meter = medidor
   SEGMENTADO de fuerza; .usage-bar = cuota de app — son distintos.) */
.minibar {
    height: 6px;
    border-radius: var(--badge-radius);
    background: var(--color-surface-sunken);
    overflow: hidden;
}

.minibar-fill {
    height: 100%;
    border-radius: var(--badge-radius);
    background: var(--color-accent);
    transition: width var(--transition-normal);
}

.minibar-fill.is-ok {
    background: var(--color-success);
}

.minibar-fill.is-warn {
    background: var(--color-warning);
}

.minibar-fill.is-bad {
    background: var(--color-danger);
}

.minibar-fill.is-muted {
    background: var(--color-border-strong);
}

/* Separador · icono guía · ítem destructivo dentro del menú (⋯). De admin/usuarios/detalle. */
.dropdown-sep {
    height: 1px;
    background: var(--color-border);
    margin: var(--space-1) var(--space-2);
}

.dropdown-item .icon {
    width: 1rem;
    height: 1rem;
    color: var(--color-text-tertiary);
}

.dropdown-item.is-danger {
    color: var(--color-danger-text);
}

.dropdown-item.is-danger:hover {
    background: var(--color-danger-light);
}

.dropdown-item.is-danger .icon {
    color: var(--color-danger-text);
}

/* Sheet/Drawer — ESTILO; en prod el comportamiento = APP.drawer.
   Bottom sheet en móvil; drawer lateral derecho en ≥768. */
.sheet-overlay {
    position: fixed;
    inset: 0;
    background: var(--color-surface-overlay);
    z-index: var(--z-modal);
    opacity: 0;
    visibility: hidden;
    transition: opacity var(--transition-fast);
}

.sheet-overlay.open {
    opacity: 1;
    visibility: visible;
}

.sheet {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: var(--z-modal);
    background: var(--color-surface);
    border-radius: var(--radius-xl) var(--radius-xl) 0 0;
    max-height: 85vh;
    display: flex;
    flex-direction: column;
    transform: translateY(100%);
    transition: transform var(--transition-normal);
    box-shadow: 0 -8px 30px rgb(0 0 0 / .18);
}

.sheet.open {
    transform: translateY(0);
}

.sheet-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--color-border);
}

.sheet-head h3 {
    margin: 0;
    font-size: var(--text-base);
    font-weight: var(--font-semibold);
}

.sheet-body {
    padding: var(--space-5);
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    min-height: 0;
}

/* min-height:0 + hijos sin encoger: contenido largo (p.ej. diff de auditoría) hace scroll en vez de comprimirse. De admin/seguridad. */
.sheet-body > * {
    flex-shrink: 0;
}

.sheet-foot {
    display: flex;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    padding-bottom: calc(var(--space-4) + env(safe-area-inset-bottom, 0));
    border-top: 1px solid var(--color-border);
}

.sheet-foot .btn {
    flex: 1;
}

/* Encabezado de confirmación destructiva (icono tintado + título + descripción) para
   sheets/modales de acción peligrosa. De admin/usuarios/detalle. En prod el comportamiento
   lo da APP.confirm/APP.modal; esto es la composición visual del cuerpo. */
.confirm-lead {
    display: flex;
    gap: var(--space-3);
    align-items: flex-start;
}

.confirm-ico {
    width: 40px;
    height: 40px;
    border-radius: var(--radius-md);
    display: grid;
    place-items: center;
    flex: 0 0 auto;
}

.confirm-ico .icon {
    width: 20px;
    height: 20px;
}

.confirm-ico.is-warn {
    background: var(--color-warning-light);
    color: var(--color-warning-text);
}

.confirm-ico.is-danger {
    background: var(--color-danger-light);
    color: var(--color-danger-text);
}

.confirm-body {
    min-width: 0;
}

.confirm-title {
    font-weight: var(--font-semibold);
    font-size: var(--text-base);
}

.confirm-desc {
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
    margin-top: 2px;
}

/* Caja mono read-only para claves/identificadores (api key regenerada, ids…). Canónico tras
   3er consumidor; api-docs .api-keybox + usuarios/lista .usr-keybox convergen aquí (ver porting-notes). */
.keybox {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface-sunken);
    font-family: var(--font-family-mono);
    font-size: var(--text-xs);
    overflow-wrap: anywhere;
}

.keybox code {
    flex: 1;
    min-width: 0;
    color: var(--color-text-primary);
    overflow-wrap: anywhere;
}

.keybox.is-masked {
    letter-spacing: .12em;
    color: var(--color-text-secondary);
    user-select: none;
}

.filter-group-label {
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
    margin-bottom: var(--space-2);
}

.chip-set {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.chip-opt {
    height: 36px;
    padding: 0 var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--badge-radius);
    background: var(--color-surface);
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    font-family: inherit;
    cursor: pointer;
}

.chip-opt:hover {
    background: var(--color-surface-sunken);
}

.chip-opt.selected {
    background: var(--color-accent-light);
    border-color: var(--color-accent);
    color: var(--color-text-link);
}

.amount-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.amount-row input {
    flex: 1;
    min-width: 0;
    height: var(--input-height);
    padding: 0 var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    font-size: var(--text-sm);
    font-family: inherit;
    color: var(--color-text-primary);
}

.amount-row input:focus {
    outline: none;
    border-color: var(--color-border-focus);
    box-shadow: var(--input-focus-ring);
}

.amount-row span {
    color: var(--color-text-tertiary);
}

@media (min-width: 768px) {
    .page-title {
        font-size: var(--text-2xl);
    }

    .toolbar-row {
        display: flex;
        align-items: center;
        gap: var(--space-3);
    }

    .toolbar-row .segs {
        flex: 1;
    }

    /* drawer lateral derecho */
    .sheet {
        top: 0;
        left: auto;
        right: 0;
        bottom: 0;
        width: min(420px, 92vw);
        max-height: none;
        height: 100vh;
        border-radius: 0;
        transform: translateX(100%);
        box-shadow: -8px 0 30px rgb(0 0 0 / .18);
    }

    .sheet.open {
        transform: translateX(0);
    }

    .sheet-body {
        flex: 1;
    }
}

@media (min-width: 1100px) {
    .page-head {
        margin-bottom: var(--space-5);
    }
}

/* ============================================================
   WIZARD / FORMULARIO (extraído del hero #3)
   ------------------------------------------------------------
   Captura y multipaso reutilizables por cualquier wizard/formulario
   (validaciones/importar, beneficiarios/importar, etc.). El FRAME del modal
   (.wz*) y el layout de página viven en su pantalla; esto es el contenido.
   Comportamiento interactivo en prod = primitivas APP.* / hooks reales.
   ============================================================ */
/* Step-bar (indicador de pasos) — etiquetas solo ≥768 (abajo) */
.step-bar {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-5) 0 var(--space-4);
}

.step {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--color-text-tertiary);
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    white-space: nowrap;
}

.step-dot {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    background: var(--color-surface-sunken);
    color: var(--color-text-tertiary);
    font-size: var(--text-xs);
    font-weight: var(--font-semibold);
    border: 1px solid var(--color-border);
    flex: 0 0 auto;
}

.step-dot .icon {
    width: 14px;
    height: 14px;
}

.step.active {
    color: var(--color-text-primary);
}

.step.active .step-dot {
    background: var(--color-accent);
    border-color: var(--color-accent);
    color: var(--color-text-inverse);
}

.step.done .step-dot {
    background: var(--color-success);
    border-color: var(--color-success);
    color: var(--color-text-inverse);
}

.step-line {
    flex: 1;
    height: 1px;
    background: var(--color-border);
    min-width: 12px;
}

.step-label {
    display: none;
}

/* Selection-card (elegir método/opción) */
.sel-grid {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.sel-card {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    text-align: left;
    padding: var(--space-4);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--card-radius);
    cursor: pointer;
}

.sel-card:hover {
    border-color: var(--color-border-strong);
    background: var(--color-surface-sunken);
}

.sel-card.selected {
    border-color: var(--color-accent);
    box-shadow: inset 0 0 0 1px var(--color-accent);
    background: var(--color-accent-light);
}

.sel-ico {
    width: 44px;
    height: 44px;
    border-radius: var(--radius-md);
    background: var(--color-accent-light);
    color: var(--color-accent);
    display: grid;
    place-items: center;
    flex: 0 0 auto;
}

.sel-card.selected .sel-ico {
    background: var(--color-accent);
    color: var(--color-text-inverse);
}

.sel-ico .icon {
    width: 22px;
    height: 22px;
}

.sel-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
}

.sel-title {
    font-weight: var(--font-semibold);
}

.sel-desc {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    margin-top: 2px;
}

.sel-check {
    margin-left: auto;
    color: var(--color-accent);
    flex: 0 0 auto;
    visibility: hidden;
}

.sel-card.selected .sel-check {
    visibility: visible;
}

/* Dropzone/preview de subir archivo → RETIRADOS: absorbidos por el componente
   canónico APP.dropInput (assets/css/components/drop-input.css). */
.ocr-hint {
    display: flex;
    gap: var(--space-2);
    align-items: flex-start;
    margin: 0 0 var(--space-3);
    padding: var(--space-3);
    background: var(--color-accent-light);
    border-radius: var(--radius-md);
    font-size: var(--text-xs);
    color: var(--color-text-link);
}

.ocr-hint .icon {
    width: 1rem;
    height: 1rem;
    flex: 0 0 auto;
}

/* Campos de formulario */
.field {
    margin-bottom: var(--space-4);
}

.field-label {
    display: block;
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    margin-bottom: var(--space-1-5);
}

.field-input {
    width: 100%;
    height: var(--input-height);
    padding: 0 var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    font-size: var(--text-sm);
    font-family: inherit;
    color: var(--color-text-primary);
}

.field-input--mono {
    font-family: var(--font-family-mono);
    letter-spacing: .02em;
}

.field-input:focus {
    outline: none;
    border-color: var(--color-border-focus);
    box-shadow: var(--input-focus-ring);
}

.field-row {
    display: flex;
    gap: var(--space-3);
}

.field-row > .field {
    flex: 1;
    min-width: 0;
}

/* min-width:0 = los items pueden encoger (combobox/date no desbordan) */
/* xs: solo las filas con combobox apilan dentro de modales (dos comboboxes —
   Banco emisor/receptor — no caben en ~286px y se desbordaban). Los pares de
   inputs cortos (Fecha/Monto) siguen lado a lado. Gap vertical = margin-bottom de .field. */
@media (max-width: 480px) {
    .mx-modal .field-row:has(.combobox) {
        flex-direction: column;
        gap: 0;
    }
}

.field-hint {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    margin-top: var(--space-1);
}

.field-opt {
    color: var(--color-text-tertiary);
    font-weight: var(--font-normal);
}

/* "· opcional" en el label */
/* Opciones en tarjeta (toggles como filas con divisor) + switch */
.options {
    margin-top: var(--space-7);
    border: 1px solid var(--color-border);
    border-radius: var(--card-radius);
}

.option-row {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-4);
    cursor: pointer;
}

.option-row + .option-row {
    border-top: 1px solid var(--color-border);
}

.switch {
    display: inline-block;
    position: relative;
    width: 40px;
    height: 24px;
    flex: 0 0 auto;
    cursor: pointer;
}

.switch input {
    position: absolute;
    opacity: 0;
    width: 100%;
    height: 100%;
    margin: 0;
    cursor: pointer;
}

.switch-track {
    position: absolute;
    inset: 0;
    background: var(--color-border-strong);
    border-radius: var(--badge-radius);
    transition: background var(--transition-fast);
    pointer-events: none;
}

.switch-thumb {
    position: absolute;
    top: 2px;
    left: 2px;
    width: 20px;
    height: 20px;
    background: var(--color-surface);
    border-radius: 50%;
    box-shadow: var(--shadow-sm);
    transition: transform var(--transition-fast);
    pointer-events: none;
}

.switch input:checked + .switch-track {
    background: var(--color-accent);
}

.switch input:checked + .switch-track + .switch-thumb {
    transform: translateX(16px);
}

.switch input:focus-visible + .switch-track {
    outline: var(--ring-width) solid var(--ring-color);
    outline-offset: 2px;
}

/* Switch bloqueado (toggle inmutable: candado + siempre-on). De admin/acceso/roles (columna owner). */
.switch.is-locked {
    cursor: not-allowed;
}

.switch.is-locked input {
    cursor: not-allowed;
}

.switch.is-locked .switch-track {
    background: var(--color-accent);
    opacity: .5;
}

.switch.is-locked .switch-thumb {
    background: var(--color-surface);
}

.switch.is-locked .switch-lock {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    color: var(--color-text-link);
    pointer-events: none;
}

.switch.is-locked .switch-lock .icon {
    width: 11px;
    height: 11px;
}

.switch-text {
    display: flex;
    flex-direction: column;
    flex: 1;
    min-width: 0;
}

.switch-text .switch-title {
    font-weight: var(--font-medium);
    font-size: var(--text-sm);
}

.switch-text .switch-desc {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    margin-top: 2px;
}

/* Config inline (sección dentro de .options): slider + select + chips + preview */
.retry-config {
    border-top: 1px solid var(--color-border);
    padding: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.cfg-label {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-2);
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    margin-bottom: var(--space-2);
}

.cfg-val {
    color: var(--color-text-link);
    font-variant-numeric: tabular-nums;
    font-weight: var(--font-semibold);
}

.range {
    width: 100%;
    accent-color: var(--color-accent);
    cursor: pointer;
}

.select {
    width: 100%;
    height: var(--input-height);
    padding: 0 var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    font-size: var(--text-sm);
    font-family: inherit;
    color: var(--color-text-primary);
    cursor: pointer;
}

.select:focus {
    outline: none;
    border-color: var(--color-border-focus);
    box-shadow: var(--input-focus-ring);
}

.cfg-chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
}

.retry-preview {
    display: flex;
    gap: 6px;
    align-items: flex-start;
    font-size: var(--text-xs);
    color: var(--color-text-secondary);
}

.retry-preview .icon {
    width: 14px;
    height: 14px;
    color: var(--color-text-tertiary);
    flex: 0 0 auto;
    margin-top: 1px;
}

/* Estado "procesando" (spinner centrado). NO usar `.processing` solo: choca con el
   modificador de badge `.verdict.processing` y con `.sdot.proc`. */
.proc-state {
    text-align: center;
    padding: var(--space-10) var(--space-5);
}

.proc-spinner {
    width: 48px;
    height: 48px;
    margin: 0 auto var(--space-4);
    color: var(--color-accent);
    animation: wz-spin 1s linear infinite;
}

.proc-spinner .icon {
    width: 48px;
    height: 48px;
}

@keyframes wz-spin {
    to {
        transform: rotate(360deg);
    }
}

.proc-title {
    font-size: var(--text-lg);
    font-weight: var(--font-bold);
    margin: 0 0 var(--space-1);
}

.proc-desc {
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    margin: 0;
}

@media (prefers-reduced-motion: reduce) {
    .proc-spinner {
        animation: none;
    }
}

/* Estado de error de lectura OCR (faro #5): "no se leyó bien → sube otra" */
.proc-error-ico {
    width: 56px;
    height: 56px;
    margin: 0 auto var(--space-4);
    border-radius: 50%;
    background: var(--color-warning-light);
    color: var(--color-warning-text);
    display: grid;
    place-items: center;
}

.proc-error-ico .icon {
    width: 28px;
    height: 28px;
}

.proc-state .btn {
    margin-top: var(--space-4);
}

@media (min-width: 768px) {
    .step-label {
        display: inline;
    }

    /* etiquetas de paso en pantallas grandes */
}

/* ============================================================
   DETALLE / VEREDICTO (extraído del hero #3)
   ------------------------------------------------------------
   Pantalla de detalle de una entidad con veredicto: back · verdict-hero ·
   data-rows (clave/valor) · timeline (máquina de estado). El layout 2-col y lo
   específico de cada pantalla (p.ej. CEP) viven en la pantalla. La base
   .verdict (color+icono+texto) está más arriba.
   ============================================================ */
/* Volver a la lista (flecha = arrow-right rotada) */
.detail-back {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    margin-bottom: var(--space-4);
}

.detail-back:hover {
    color: var(--color-text-primary);
}

.detail-back .icon {
    width: 1rem;
    height: 1rem;
    transform: rotate(180deg);
}

/* Veredicto hero (entidad + monto grande + badge + prueba) */
.verdict-hero {
    padding: var(--space-5);
}

.vh-top {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.vh-bank {
    width: 44px;
    height: 44px;
    font-size: 11px;
}

.vh-bene {
    font-weight: var(--font-semibold);
}

.vh-bene-sub {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    font-family: var(--font-family-mono);
}

.vh-amount {
    font-size: var(--text-4xl);
    font-weight: var(--font-bold);
    letter-spacing: -.03em;
    line-height: 1.05;
    font-variant-numeric: tabular-nums;
    margin: var(--space-4) 0 var(--space-3);
}

.verdict-hero .verdict {
    font-size: var(--text-sm);
    padding: 5px 12px;
}

.verdict-hero .verdict .icon {
    width: 16px;
    height: 16px;
}

.vh-proof {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    margin-top: var(--space-3);
    font-size: var(--text-xs);
    color: var(--color-success-text);
}

.vh-proof .icon {
    width: 14px;
    height: 14px;
}

/* Título de sección dentro de una card */
.detail-card-title {
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
    margin: 0;
    padding: var(--space-4) var(--space-5) 0;
}

/* Data rows (clave/valor) + separador de grupo (.dl-group = hairline + etiqueta) */
.dl {
    display: flex;
    flex-direction: column;
    padding: var(--space-2) 0;
}

/* mobile-first: clave ARRIBA, valor ABAJO (alineado a la izq) — evita que los valores
   largos (clave de rastreo de 20 chars, fecha+hora, CLABE de 18) se encavalguen con la
   etiqueta en pantallas chicas. En >=768px vuelve a fila «clave … valor» (media abajo). */
.dl-row {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: var(--space-1);
    padding: var(--space-2-5) var(--space-5);
    font-size: var(--text-sm);
}

.dl-k {
    color: var(--color-text-tertiary);
    flex: 0 0 auto;
}

.dl-v {
    font-weight: var(--font-medium);
    text-align: left;
    min-width: 0;
    overflow-wrap: anywhere;
}

.dl-v.mono {
    font-family: var(--font-family-mono);
    font-size: var(--text-xs);
}

.dl-group {
    margin-top: var(--space-2);
    padding: var(--space-3) var(--space-5) var(--space-1);
    border-top: 1px solid var(--color-border);
    font-size: var(--text-xs);
    font-weight: var(--font-bold);
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--color-text-primary);
}

@media (min-width: 768px) {
    .dl-row {
        flex-direction: row;
        align-items: center;
        justify-content: space-between;
        gap: var(--space-3);
    }

    .dl-v {
        text-align: right;
    }
}

/* Timeline (máquina de estado del proceso) */
.timeline {
    list-style: none;
    margin: 0;
    padding: var(--space-4) var(--space-5) var(--space-5);
}

.tl-item {
    display: flex;
    gap: var(--space-3);
}

.tl-marker {
    display: flex;
    flex-direction: column;
    align-items: center;
    flex: 0 0 auto;
}

.tl-dot {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    background: var(--color-success);
    color: var(--color-text-inverse);
    flex: 0 0 auto;
}

.tl-dot .icon {
    width: 14px;
    height: 14px;
}

.tl-dot.is-pending {
    background: var(--color-surface-sunken);
    color: var(--color-text-tertiary);
    border: 1px solid var(--color-border);
}

.tl-dot.is-warn {
    background: var(--color-warning);
    color: var(--color-text-inverse);
}

.tl-dot.is-error {
    background: var(--color-danger);
    color: var(--color-text-inverse);
}

.tl-line {
    flex: 1;
    width: 2px;
    min-height: 14px;
    background: var(--color-border);
    margin: 2px 0;
}

.tl-item:last-child .tl-line {
    display: none;
}

.tl-body {
    padding-bottom: var(--space-4);
}

.tl-item:last-child .tl-body {
    padding-bottom: 0;
}

.tl-title {
    font-weight: var(--font-medium);
    font-size: var(--text-sm);
}

.tl-title.is-pending {
    color: var(--color-text-tertiary);
    font-weight: var(--font-normal);
}

.tl-time {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    font-variant-numeric: tabular-nums;
    margin-top: 1px;
}

/* Bloque de código/marcado CRUDO (JSON·XML·HTML): cabecera (label/meta/copiar/colapsar) +
   cuerpo scrollable. De admin/validaciones/detalle; reusable por playground/i18n/operación.
   JS: [data-code]/[data-toggle]/[data-copy] + .is-collapsed/.is-copied (sin clases nuevas). */
.codeblock {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface-sunken);
    overflow: hidden;
    min-width: 0;
    max-width: 100%;
}

.codeblock-head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
    border-bottom: 1px solid var(--color-border);
    background: var(--color-surface);
    flex-wrap: wrap;
}

.codeblock.is-collapsed .codeblock-head {
    border-bottom-color: transparent;
}

.codeblock-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-family: var(--font-family-mono);
    font-size: var(--text-xs);
    font-weight: var(--font-semibold);
    color: var(--color-text-secondary);
    min-width: 0;
}

.codeblock-label .icon {
    width: 14px;
    height: 14px;
    color: var(--color-text-tertiary);
    flex: 0 0 auto;
}

.codeblock-meta {
    font-size: 11px;
    color: var(--color-text-tertiary);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.codeblock-actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 4px;
}

.codeblock-btn {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    height: 28px;
    padding: 0 var(--space-2);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-surface);
    color: var(--color-text-secondary);
    font-size: 11px;
    font-weight: var(--font-medium);
    font-family: inherit;
    cursor: pointer;
}

.codeblock-btn:hover {
    background: var(--color-surface-sunken);
    color: var(--color-text-primary);
}

.codeblock-btn .icon {
    width: 13px;
    height: 13px;
}

.codeblock-btn .codeblock-copied {
    display: none;
    color: var(--color-success-text);
}

.codeblock-btn.is-copied .codeblock-copylabel {
    display: none;
}

.codeblock-btn.is-copied .codeblock-copied {
    display: inline;
}

.codeblock-toggle .icon {
    transition: transform var(--transition-fast);
}

.codeblock:not(.is-collapsed) .codeblock-toggle .icon {
    transform: rotate(180deg);
}

.codeblock.is-collapsed .codeblock-toggle .icon {
    transform: rotate(0deg);
}

.codeblock-body {
    overflow: auto;
    max-height: 420px;
    -webkit-overflow-scrolling: touch;
}

.codeblock.is-collapsed .codeblock-body {
    display: none;
}

.codeblock pre {
    margin: 0;
    padding: var(--space-3) var(--space-4);
    font-family: var(--font-family-mono);
    font-size: var(--text-xs);
    line-height: 1.65;
    color: var(--color-text-primary);
    tab-size: 2;
}

.codeblock pre code {
    white-space: pre;
}

.codeblock-truncate {
    display: block;
    margin-top: var(--space-2);
    padding-top: var(--space-2);
    border-top: 1px dashed var(--color-border-strong);
    color: var(--color-text-tertiary);
    font-style: italic;
}

/* Fila de acciones del detalle */
.detail-actions {
    display: flex;
    gap: var(--space-2);
}

/* ============================================================
   CHARTS / KPI atoms (extraído del hero #2)
   ------------------------------------------------------------
   Primitivas de visualización reutilizables por dashboard · finanzas · métricas ·
   uso: anillo de progreso (donut) + sparkline. Genéricas — el consumidor pasa el
   % (stroke-dasharray) y los puntos (polyline). Debajo, la COMPOSICIÓN "KPI hero"
   (monto + delta + anillo + sparkline) ya COMPARTIDA por dashboard #2 y finanzas #4.
   ============================================================ */
.ring {
    position: relative;
    width: 84px;
    height: 84px;
    flex: 0 0 auto;
}

.ring svg {
    transform: rotate(-90deg);
}

.ring-center {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

.ring-pct {
    font-size: var(--text-base);
    font-weight: var(--font-bold);
    font-variant-numeric: tabular-nums;
    line-height: 1;
    letter-spacing: -.02em;
}

.ring-cap {
    font-size: 9px;
    color: var(--color-text-tertiary);
    text-transform: uppercase;
    letter-spacing: .04em;
}

/* Trazo de relleno del aro (stroke-dasharray lo anima el consumidor); acento → verde al completar */
.ring-fg {
    stroke: var(--color-accent);
    transition: stroke-dasharray var(--transition-normal), stroke var(--transition-fast);
}

.ring-fg.is-complete {
    stroke: var(--color-success);
}

.spark {
    width: 100%;
    height: 40px;
    display: block;
}

/* Leyenda de gráfica (COMPARTIDA: finanzas #4 + banxico). Punto + etiqueta; el
   consumidor colorea el .legend-dot por estado/serie (modificadores .is-* abajo). */
.chart-legend {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-4);
}

.legend-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: var(--text-xs);
    color: var(--color-text-secondary);
}

.legend-dot {
    width: 10px;
    height: 10px;
    border-radius: 3px;
    flex: 0 0 auto;
}

.legend-dot.is-ok {
    background: var(--color-success);
}

.legend-dot.is-degraded {
    background: var(--color-warning);
}

.legend-dot.is-down {
    background: var(--color-danger);
}

/* Modifiers por veredicto Banxico / serie de percentiles (de insights/métricas) */
.legend-dot.is-valid {
    background: var(--color-success);
}

.legend-dot.is-notfound {
    background: var(--color-warning);
}

.legend-dot.is-cep {
    background: var(--color-info);
}

.legend-dot.is-error {
    background: var(--color-danger);
}

.legend-dot.is-proc {
    background: var(--color-accent);
}

.legend-dot.is-pend {
    background: var(--color-border-strong);
}

.legend-dot.is-s1 {
    background: var(--color-accent);
}

.legend-dot.is-s2 {
    background: var(--color-success);
}

.legend-dot.is-s3 {
    background: var(--color-warning);
}

.legend-dot.is-s4 {
    background: var(--color-border-strong);
}

.legend-dot.is-p50 {
    background: var(--color-success);
}

.legend-dot.is-p95 {
    background: var(--color-warning);
}

.legend-dot.is-p99 {
    background: var(--color-danger);
}

.legend-dot.is-avg {
    background: var(--color-accent);
}

.legend-dot.is-soft {
    opacity: .5;
}

/* Tira de uptime / salud en el tiempo (status page densa). Una barra por
   intervalo, coloreada por estado (ok/degraded/down); pie con extremos y dato
   central opcional. COMPARTIDA: banxico (estado del servicio) + futuras vistas
   de salud. El consumidor genera las barras y el % a partir de su serie. */
.uptime {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.uptime-track {
    display: flex;
    gap: 3px;
    height: 38px;
    align-items: stretch;
}

.uptime-bar {
    flex: 1 1 0;
    min-width: 0;
    border-radius: 2px;
    background: var(--color-success);
}

.uptime-bar.is-ok {
    background: var(--color-success);
}

.uptime-bar.is-degraded {
    background: var(--color-warning);
}

.uptime-bar.is-down {
    background: var(--color-danger);
}

/* Bucket sin lectura (pocas probes): gris neutro para que la tira muestre el
   periodo completo de forma coherente sin pintar "caída" donde no hubo dato. */
.uptime-bar.is-empty {
    background: var(--color-border);
}

.uptime-foot {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.uptime-rule {
    flex: 1;
    height: 1px;
    background: var(--color-border);
}

.uptime-end {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    white-space: nowrap;
}

.uptime-mid {
    font-size: var(--text-xs);
    color: var(--color-text-secondary);
    font-weight: var(--font-medium);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* KPI hero (COMPARTIDO: dashboard #2 + finanzas #4) — monto grande + delta + anillo +
   sparkline. Generaliza las antiguas .flujo (dashboard) y .fin-hero (finanzas). El
   consumidor mete .ring (con su %) y .spark dentro; el footer es opcional. */
.kpi-hero {
    padding: var(--space-5);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.kpi-hero-top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-4);
}

.kpi-hero .label {
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
}

.kpi-hero .val {
    font-size: var(--text-4xl);
    font-weight: var(--font-bold);
    letter-spacing: -.03em;
    line-height: 1.05;
    font-variant-numeric: tabular-nums;
}

.kpi-hero .delta {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: var(--text-xs);
    color: var(--color-success-text);
    font-variant-numeric: tabular-nums;
    margin-top: var(--space-1);
}

/* Negativos SIN color (format-spec): la bajada va neutra; el ícono ↓ da la dirección. */
.kpi-hero .delta.delta-down {
    color: var(--color-text-secondary);
}

.kpi-hero .delta .icon {
    width: 14px;
    height: 14px;
}

.kpi-hero-foot {
    display: flex;
    justify-content: space-between;
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
}

.kpi-hero-foot b {
    color: var(--color-text-secondary);
    font-variant-numeric: tabular-nums;
}

@media (min-width: 768px) {
    .kpi-hero-top {
        align-items: center;
    }
}

/* ============================================================
   TABS (extraído del hero #4) — tabs de contenido (Resumen/SAT/Facturas…). El
   comportamiento en prod usa los hooks reales; aquí el estilo. El gap del
   .tab-panel da aire entre secciones del panel activo.
   ============================================================ */
.tabs {
    display: flex;
    gap: var(--space-5);
    border-bottom: 1px solid var(--color-border);
    overflow-x: auto;
    scrollbar-width: none;
    margin-bottom: var(--space-5);
}

.tabs::-webkit-scrollbar {
    display: none;
}

.tab-btn {
    flex: 0 0 auto;
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) 0;
    border: 0;
    background: transparent;
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    font-family: inherit;
    cursor: pointer;
    white-space: nowrap;
}

.tab-btn:hover {
    color: var(--color-text-primary);
}

.tab-btn.active {
    color: var(--color-text-primary);
}

.tab-btn.active::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: -1px;
    height: 2px;
    background: var(--color-accent);
    border-radius: 2px 2px 0 0;
}

.tab-soon {
    font-size: 9px;
    font-weight: var(--font-semibold);
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--color-text-tertiary);
    background: var(--color-surface-sunken);
    border: 1px solid var(--color-border);
    padding: 1px 6px;
    border-radius: var(--badge-radius);
}

.tab-panel {
    display: flex;
    flex-direction: column;
    gap: var(--space-7);
}

.tab-panel[hidden] {
    display: none;
}

.tab-empty {
    text-align: center;
    padding: var(--space-10) var(--space-5);
    color: var(--color-text-tertiary);
}

.tab-empty .icon {
    width: 40px;
    height: 40px;
    margin: 0 auto var(--space-3);
    color: var(--color-text-tertiary);
}

.tab-empty-title {
    font-weight: var(--font-semibold);
    color: var(--color-text-secondary);
}

/* ============================================================
   PLAN CARD (extraído del hero #6) — tarjeta de plan + grid (faro de pricing; la
   landing /precios hereda este patrón). La tabla comparativa (.compare*), los
   toggles (.seg-toggle) y el resultado (.result) siguen en checkout.css.
   ============================================================ */
.plan-grid {
    display: grid;
    grid-template-columns: 1fr;
    row-gap: var(--space-6);
    column-gap: var(--space-4);
}

.plan-card {
    padding: var(--space-5);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    position: relative;
}

.plan-card--featured {
    border-color: var(--color-accent);
    box-shadow: inset 0 0 0 1px var(--color-accent);
}

.plan-badge {
    position: absolute;
    top: -11px;
    left: var(--space-5);
    background: var(--color-accent);
    color: var(--color-text-inverse);
    font-size: 11px;
    font-weight: var(--font-semibold);
    letter-spacing: .02em;
    padding: 3px 10px;
    border-radius: var(--badge-radius);
}

.plan-head {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.plan-name {
    font-size: var(--text-lg);
    font-weight: var(--font-bold);
}

.plan-desc {
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
}

.plan-price {
    display: flex;
    align-items: baseline;
    gap: 6px;
    flex-wrap: wrap;
}

.plan-amount {
    font-size: var(--text-4xl);
    font-weight: var(--font-bold);
    letter-spacing: -.03em;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.plan-unit {
    color: var(--color-text-tertiary);
    font-size: var(--text-sm);
}

.plan-iva {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    margin-top: var(--space-1);
}

.plan-features {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.plan-features li {
    display: flex;
    gap: var(--space-2);
    align-items: flex-start;
    font-size: var(--text-sm);
}

.plan-features li .icon {
    width: 1rem;
    height: 1rem;
    flex: 0 0 auto;
    margin-top: 2px;
}

.plan-features li.yes .icon {
    color: var(--color-success);
}

.plan-features li.no {
    color: var(--color-text-tertiary);
}

.plan-features li.no .icon {
    color: var(--color-text-tertiary);
}

.plan-card .plan-cta {
    margin-top: auto;
}

@media (min-width: 768px) {
    .plan-grid {
        grid-template-columns: repeat(3, 1fr);
        align-items: stretch;
    }
}

/* Nota de mockup (SOLO en los candidatos de diseño; no es componente de producción) */
.note {
    font-size: 11px;
    color: var(--color-text-tertiary);
    text-align: center;
    padding-top: var(--space-2);
}

/* ============================================================
   EXTRAÍDO de validaciones/importar (2026-06-03) — primitivos reutilizables que
   esa pantalla aportó. Genéricos, solo tokens, SIN márgenes de página (la página
   coloca). Las pantallas dejan de redefinir estos bloques.
   ============================================================ */
/* Verdict: variantes extra (completa ok/no/pending/processing de arriba) */
.verdict.warn {
    background: var(--color-warning-light);
    color: var(--color-warning-text);
}

.verdict.info {
    background: var(--color-info-light);
    color: var(--color-info-text);
}

.verdict.muted {
    background: var(--color-surface-sunken);
    color: var(--color-text-secondary);
}

.verdict.queued {
    background: var(--color-surface-sunken);
    color: var(--color-text-tertiary);
}

.verdict.running {
    background: var(--color-info-light);
    color: var(--color-info-text);
}

.verdict.running .icon {
    animation: verdict-spin 1s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
    .verdict.running .icon {
        animation: none;
    }
}

/* Botón peligro + estado disabled */
.btn-danger {
    background: var(--color-danger);
    color: var(--color-text-inverse);
}

.btn-danger:hover {
    background: var(--color-danger-hover);
}

.btn:disabled {
    opacity: var(--opacity-disabled, .5);
    cursor: not-allowed;
}

/* KPI strip compacto (conteos derivados) — complementa .kpi-hero */
.kpi-strip {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(108px, 1fr));
    gap: var(--space-2);
}

.kpi-tile {
    padding: var(--space-3) var(--space-3-5);
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    min-width: 0;
}

.kpi-tile-val {
    font-size: var(--text-2xl);
    font-weight: var(--font-bold);
    font-variant-numeric: tabular-nums;
    letter-spacing: -.02em;
    line-height: 1;
}

.kpi-tile-label {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    color: var(--color-text-secondary);
    font-size: var(--text-xs);
    line-height: 1.2;
}

.kpi-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex: 0 0 auto;
    background: var(--color-border-strong);
}

.kpi-dot.ok {
    background: var(--color-success);
}

.kpi-dot.warn {
    background: var(--color-warning);
}

.kpi-dot.bad {
    background: var(--color-danger);
}

.kpi-dot.info {
    background: var(--color-info);
}

.kpi-dot.muted {
    background: var(--color-text-tertiary);
}

.kpi-tile.is-total {
    background: var(--color-surface-sunken);
}

.kpi-tile.is-total .kpi-tile-val {
    color: var(--color-text-primary);
}

@media (min-width: 1100px) {
    .kpi-strip {
        gap: var(--space-3);
    }
}

/* Checkbox inline (primitivo de formulario) */
.check-inline {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
    cursor: pointer;
    min-height: 32px;
    user-select: none;
}

.check-inline input {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
}

.check-box {
    width: 18px;
    height: 18px;
    border: 1.5px solid var(--color-border-strong);
    border-radius: var(--radius-sm);
    display: grid;
    place-items: center;
    flex: 0 0 auto;
    background: var(--color-surface);
    transition: background var(--transition-fast), border-color var(--transition-fast);
}

.check-box .icon {
    width: 13px;
    height: 13px;
    color: var(--color-text-inverse);
    opacity: 0;
}

.check-inline input:checked + .check-box {
    background: var(--color-accent);
    border-color: var(--color-accent);
}

.check-inline input:checked + .check-box .icon {
    opacity: 1;
}

.check-inline input:focus-visible + .check-box {
    outline: var(--ring-width) solid var(--ring-color);
    outline-offset: 2px;
}

/* Tira "avísame cuando termine" (flujos async) */
.notify-strip {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    padding: var(--space-3) var(--space-4);
}

.notify-lead {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    color: var(--color-text-primary);
}

.notify-lead .icon {
    width: 1.125rem;
    height: 1.125rem;
    color: var(--color-accent);
}

.notify-channels {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: var(--space-2) var(--space-4);
    margin-left: auto;
}

/* Indicador de progreso async (committing) */
.progress-card {
    padding: var(--space-4) var(--space-5);
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.progress-top {
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.progress-spin {
    width: 22px;
    height: 22px;
    color: var(--color-accent);
    flex: 0 0 auto;
    animation: verdict-spin 1s linear infinite;
}

.progress-spin .icon {
    width: 22px;
    height: 22px;
}

@media (prefers-reduced-motion: reduce) {
    .progress-spin {
        animation: none;
    }
}

.progress-label {
    font-weight: var(--font-semibold);
    font-size: var(--text-sm);
}

.progress-count {
    margin-left: auto;
    font-variant-numeric: tabular-nums;
    font-weight: var(--font-semibold);
    color: var(--color-text-link);
    font-family: var(--font-family-mono);
    font-size: var(--text-sm);
}

.progress-bar {
    height: 8px;
    border-radius: var(--badge-radius);
    background: var(--color-surface-sunken);
    overflow: hidden;
}

.progress-fill {
    height: 100%;
    background: var(--color-accent);
    border-radius: var(--badge-radius);
    transition: width var(--transition-normal);
}

.progress-sub {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
}

/* Panel de estado (vacío / terminal / error / éxito) + result banner */
.state-panel {
    display: flex;
    flex-direction: column;
    align-items: center;
    text-align: center;
    padding: var(--space-6) var(--space-5);
    gap: var(--space-2);
}

.state-ico {
    width: 56px;
    height: 56px;
    border-radius: 50%;
    display: grid;
    place-items: center;
    margin-bottom: var(--space-2);
}

.state-ico .icon {
    width: 28px;
    height: 28px;
}

.state-ico.bad {
    background: var(--color-danger-light);
    color: var(--color-danger-text);
}

.state-ico.muted {
    background: var(--color-surface-sunken);
    color: var(--color-text-tertiary);
}

.state-ico.ok {
    background: var(--color-success-light);
    color: var(--color-success-text);
}

.state-title {
    font-size: var(--text-lg);
    font-weight: var(--font-bold);
    margin: 0;
}

.state-desc {
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    margin: 0;
    max-width: 44ch;
}

.state-actions {
    display: flex;
    gap: var(--space-2);
    margin-top: var(--space-4);
    flex-wrap: wrap;
    justify-content: center;
}

.result-banner {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-4);
}

.result-banner .state-ico {
    margin: 0;
    width: 40px;
    height: 40px;
}

.result-banner .state-ico .icon {
    width: 20px;
    height: 20px;
}

.result-banner-body {
    min-width: 0;
}

.result-banner-title {
    font-weight: var(--font-semibold);
    font-size: var(--text-base);
}

.result-banner-sub {
    color: var(--color-text-secondary);
    font-size: var(--text-sm);
    margin-top: 2px;
}

/* Skeleton de filas (loader) */
.skeleton-rows {
    display: flex;
    flex-direction: column;
    gap: 1px;
    width: 100%;
}

.sk-row {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--color-border);
}

.sk-bar {
    height: 12px;
    border-radius: var(--radius-sm);
    background: linear-gradient(90deg, var(--color-surface-sunken) 0%, var(--color-surface-muted) 50%, var(--color-surface-sunken) 100%);
    background-size: 200% 100%;
    animation: sk-shimmer 1.3s ease-in-out infinite;
}

@keyframes sk-shimmer {
    to {
        background-position: -200% 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .sk-bar {
        animation: none;
    }
}

.sk-1 {
    width: 18px;
    flex: 0 0 auto;
}

.sk-2 {
    width: 84px;
    flex: 0 0 auto;
}

.sk-3 {
    flex: 1;
}

.sk-4 {
    width: 72px;
    flex: 0 0 auto;
}

/* Líneas de texto apiladas (detalle/prosa) — APP.skeleton.lines() */
.sk-lines {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    width: 100%;
}

.sk-lines .sk-bar {
    flex: 0 0 auto;
}

/* Alerta inline (preflight / cuota 422 / error de banda) */
.preflight {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-4);
    border: 1px solid var(--color-danger);
    border-radius: var(--card-radius);
    background: var(--color-danger-light);
}

.preflight .icon {
    width: 1.25rem;
    height: 1.25rem;
    color: var(--color-danger-text);
    flex: 0 0 auto;
    margin-top: 1px;
}

.preflight-body {
    min-width: 0;
}

.preflight-title {
    font-weight: var(--font-semibold);
    font-size: var(--text-sm);
    color: var(--color-danger-text);
}

.preflight-desc {
    font-size: var(--text-sm);
    color: var(--color-text-secondary);
    margin-top: 2px;
}

.preflight-actions {
    display: flex;
    gap: var(--space-2);
    margin-top: var(--space-3);
    flex-wrap: wrap;
}

/* Celdas editables inline (tablas/cards con edición) */
.cell-input {
    width: 100%;
    min-width: 0;
    height: 36px;
    padding: 0 var(--space-2-5);
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    background: var(--color-surface-sunken);
    color: var(--color-text-primary);
    font-size: var(--text-sm);
    font-family: inherit;
}

.cell-input:hover {
    border-color: var(--color-border-strong);
}

.cell-input:focus {
    outline: none;
    background: var(--color-surface);
    border-color: var(--color-border-focus);
    box-shadow: var(--input-focus-ring);
}

.cell-input.mono {
    font-family: var(--font-family-mono);
    font-size: var(--text-xs);
    letter-spacing: -.01em;
}

.cell-input.amount {
    font-family: var(--font-family-mono);
    font-weight: var(--font-semibold);
    font-variant-numeric: tabular-nums;
}

.cell-input[readonly] {
    background: transparent;
    border-color: transparent;
    padding-left: 0;
    cursor: default;
}

.cell-input[readonly]:hover {
    border-color: transparent;
}

.cell-input[data-missing="true"] {
    border-color: var(--color-warning);
    background: var(--color-warning-light);
}

.cell-input[data-bad="true"] {
    border-color: var(--color-danger);
    background: var(--color-danger-light);
}

.cell-money {
    display: flex;
    align-items: center;
    gap: 3px;
}

.cell-money .cur {
    color: var(--color-text-tertiary);
    flex: 0 0 auto;
    font-weight: var(--font-semibold);
}

.cell-select {
    flex: 1;
    min-width: 0;
    height: 36px;
    padding: 0 var(--space-2);
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    background: var(--color-surface-sunken);
    color: var(--color-text-primary);
    font-size: var(--text-sm);
    font-family: inherit;
    cursor: pointer;
}

.cell-select:hover {
    border-color: var(--color-border-strong);
}

.cell-select:focus {
    outline: none;
    background: var(--color-surface);
    border-color: var(--color-border-focus);
    box-shadow: var(--input-focus-ring);
}

.cell-select[disabled] {
    background: transparent;
    cursor: default;
    padding-left: 0;
}

/* ============================================================
   GAPS DE NORMALIZACIÓN (2026-06-04) — primitivos que faltaban y que casi toda
   pantalla usa (las pantallas dejan de inventar su versión). Genéricos, solo
   tokens. dist NO regenerado: se hornean junto con lo que aporte cuenta/perfil.
   ============================================================ */
/* Variantes de botón faltantes (usadas por webhooks/perfil/otros) */
.btn-secondary {
    background: var(--color-surface-sunken);
    color: var(--color-text-primary);
    border-color: var(--color-border);
}

.btn-secondary:hover {
    background: var(--color-surface);
    border-color: var(--color-border-strong);
}

.btn-outline {
    background: var(--color-surface);
    color: var(--color-text-primary);
    border-color: var(--color-border);
}

.btn-outline:hover {
    background: var(--color-surface-sunken);
    border-color: var(--color-border-strong);
}

.btn-sm {
    min-height: 36px;
    padding: 0 var(--space-3);
    font-size: var(--text-xs);
}

.btn-sm .icon {
    width: 1rem;
    height: 1rem;
}

/* Badge genérico (etiqueta/estado NO-veredicto: tipo de cuenta, rol, "Nuevo", "Archivada") */
.badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 8px;
    border-radius: var(--badge-radius);
    font-size: 11px;
    font-weight: var(--font-semibold);
    white-space: nowrap;
    background: var(--color-surface-sunken);
    color: var(--color-text-secondary);
    border: 1px solid var(--color-border);
}

.badge .icon {
    width: 13px;
    height: 13px;
}

.badge-neutral {
    background: var(--color-surface-sunken);
    color: var(--color-text-tertiary);
}

.badge-info {
    background: var(--color-info-light);
    color: var(--color-info-text);
    border-color: transparent;
}

.badge-accent {
    background: var(--color-accent-light);
    color: var(--color-text-link);
    border-color: transparent;
}

.badge-success {
    background: var(--color-success-light);
    color: var(--color-success-text);
    border-color: transparent;
}

.badge-warning {
    background: var(--color-warning-light);
    color: var(--color-warning-text);
    border-color: transparent;
}

.badge-danger {
    background: var(--color-danger-light);
    color: var(--color-danger-text);
    border-color: transparent;
}

/* Callout / banner inline accionable (info/warning/success/danger). El danger
   intenso de preflight ya existe (.preflight); esto es el banner sobrio. */
.callout {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    font-size: var(--text-sm);
    line-height: var(--leading-normal);
    background: var(--color-surface-sunken);
    color: var(--color-text-secondary);
}

.callout > .icon {
    width: 1.125rem;
    height: 1.125rem;
    flex: 0 0 auto;
    margin-top: 1px;
}

.callout-body {
    min-width: 0;
    flex: 1;
}

.callout-title {
    font-weight: var(--font-semibold);
}

.callout-action {
    margin-left: auto;
    flex: 0 0 auto;
    align-self: center;
}

.callout-info {
    background: var(--color-info-light);
    color: var(--color-info-text);
}

.callout-info > .icon {
    color: var(--color-info-text);
}

.callout-warning {
    background: var(--color-warning-light);
    color: var(--color-warning-text);
}

.callout-warning > .icon {
    color: var(--color-warning-text);
}

.callout-success {
    background: var(--color-success-light);
    color: var(--color-success-text);
}

.callout-success > .icon {
    color: var(--color-success-text);
}

.callout-danger {
    background: var(--color-danger-light);
    color: var(--color-danger-text);
}

.callout-danger > .icon {
    color: var(--color-danger-text);
}

/* Avatar de iniciales reusable (cards/identidad). El topbar le añade su margen
   vía `.topbar .avatar` en shell.css. */
.avatar {
    width: 36px;
    height: 36px;
    border-radius: var(--badge-radius);
    background: var(--color-accent);
    color: var(--color-text-inverse);
    display: grid;
    place-items: center;
    font-weight: var(--font-semibold);
    font-size: var(--text-sm);
    flex: 0 0 auto;
}

.avatar-sm {
    width: 28px;
    height: 28px;
    font-size: var(--text-xs);
}

.avatar-lg {
    width: 48px;
    height: 48px;
    font-size: var(--text-base);
}

.avatar-muted {
    background: var(--color-surface-sunken);
    color: var(--color-text-secondary);
}

/* OTP — fila de inputs de dígito (el modal contenedor usa .mx-modal/overlays) */
.otp-input {
    display: flex;
    gap: var(--space-2);
    justify-content: center;
}

.otp-digit {
    width: 44px;
    height: 52px;
    text-align: center;
    font-size: var(--text-xl);
    font-weight: var(--font-semibold);
    font-variant-numeric: tabular-nums;
    font-family: var(--font-family-mono);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: var(--color-text-primary);
}

.otp-digit:focus {
    outline: none;
    border-color: var(--color-border-focus);
    box-shadow: var(--input-focus-ring);
}

.otp-digit.is-filled {
    border-color: var(--color-border-strong);
}

.otp-digit.is-error {
    border-color: var(--color-danger);
}

/* ============================================================
   EXTRACCIÓN cuenta/perfil (2026-06-04) — composiciones reusables que el perfil
   estrenó y que sirven a settings/detalle en general. Genéricas, solo tokens.
   (CD las generó como `.pf-*`/`.notice`/`.kpi-strip`[stat] sobre un dist viejo →
   aquí quedan CANÓNICAS; la reconciliación de vocab se anota en porting-notes.)
   ============================================================ */
/* Composición de tarjeta: cabecera + cuerpo + pie + divisor full-bleed (sobre .card) */
.card-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
    flex-wrap: wrap;
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--color-border);
}

.card-head.no-border {
    border-bottom: 0;
    padding-bottom: 0;
}

.card-title {
    font-size: var(--text-base);
    font-weight: var(--font-semibold);
    margin: 0;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.card-title .icon {
    width: 1.125rem;
    height: 1.125rem;
    color: var(--color-text-tertiary);
}

.card-title.danger, .card-title.danger .icon {
    color: var(--color-danger-text);
}

.card-sub {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    margin-top: 2px;
}

.card-body {
    padding: var(--space-5);
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

.card-body.tight {
    gap: var(--space-3);
}

.card-foot {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
    flex-wrap: wrap;
    padding: var(--space-4) var(--space-5);
    border-top: 1px solid var(--color-border);
}

.card-foot.between {
    justify-content: space-between;
    align-items: center;
}

.card-foot.start {
    justify-content: flex-start;
}

.card-divider {
    height: 1px;
    background: var(--color-border);
    margin: 0 calc(-1 * var(--space-5));
}

.card-danger {
    border-color: var(--color-danger-text);
    background: var(--color-danger-light);
}

.card-danger .card-head {
    border-bottom-color: var(--color-danger-light);
}

/* Campo en modo lectura (label + valor + acciones inline) — settings ver→editar */
.readfield {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    flex-wrap: wrap;
}

.readfield-main {
    min-width: 0;
    flex: 1 1 auto;
}

.readfield-label {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
}

.readfield-val {
    font-size: var(--text-base);
    font-weight: var(--font-medium);
    margin-top: 2px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    max-width: 100%;
    overflow-wrap: anywhere;
}

.readfield-val .icon {
    width: 1rem;
    height: 1rem;
    color: var(--color-text-tertiary);
}

.readfield-val.flush {
    margin-top: 0;
}

.inline-actions {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    flex: 0 0 auto;
    flex-wrap: wrap;
}

/* Input con botón pegado (toggle ver/ocultar, copiar, limpiar) */
.input-affix {
    position: relative;
    display: flex;
    align-items: center;
}

.input-affix .field-input {
    padding-right: 44px;
}

.affix-btn {
    position: absolute;
    right: 4px;
    width: 36px;
    height: 36px;
    display: grid;
    place-items: center;
    border: 0;
    background: transparent;
    color: var(--color-text-tertiary);
    border-radius: var(--radius-md);
    cursor: pointer;
}

.affix-btn:hover {
    color: var(--color-text-primary);
    background: var(--color-surface-sunken);
}

.affix-btn .icon {
    width: 1.125rem;
    height: 1.125rem;
}

/* Medidor de nivel (fuerza de contraseña, etc.) — 4 segmentos por data-level */
.meter {
    display: flex;
    flex-direction: column;
    gap: 6px;
}

.meter-track {
    display: flex;
    gap: 4px;
}

.meter-seg {
    flex: 1;
    height: 5px;
    border-radius: var(--badge-radius);
    background: var(--color-surface-sunken);
    transition: background var(--transition-fast);
}

.meter[data-level="1"] .meter-seg:nth-child(-n+1) {
    background: var(--color-danger);
}

.meter[data-level="2"] .meter-seg:nth-child(-n+2) {
    background: var(--color-warning);
}

.meter[data-level="3"] .meter-seg:nth-child(-n+3) {
    background: var(--color-info);
}

.meter[data-level="4"] .meter-seg {
    background: var(--color-success);
}

.meter-label {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    font-variant-numeric: tabular-nums;
}

.meter-label b {
    font-weight: var(--font-semibold);
    color: var(--color-text-secondary);
}

/* Selector de opción como FILAS (radio) — distinto de .sel-card (grid de tarjetas) */
.radio-group {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
}

.radio-row {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-3) var(--space-4);
    border: 1px solid var(--color-border);
    border-radius: var(--card-radius);
    cursor: pointer;
}

.radio-row:hover {
    background: var(--color-surface-sunken);
}

.radio-row.selected {
    border-color: var(--color-accent);
    box-shadow: inset 0 0 0 1px var(--color-accent);
    background: var(--color-accent-light);
}

.radio-row input {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
}

.radio-dot {
    width: 20px;
    height: 20px;
    border-radius: 50%;
    border: 1.5px solid var(--color-border-strong);
    flex: 0 0 auto;
    display: grid;
    place-items: center;
}

.radio-dot::after {
    content: "";
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: var(--color-accent);
    transform: scale(0);
    transition: transform var(--transition-fast);
}

.radio-row.selected .radio-dot {
    border-color: var(--color-accent);
}

.radio-row.selected .radio-dot::after {
    transform: scale(1);
}

.radio-row input:focus-visible + .radio-dot {
    outline: var(--ring-width) solid var(--ring-color);
    outline-offset: 2px;
}

.radio-ico {
    width: 36px;
    height: 36px;
    border-radius: var(--radius-md);
    background: var(--color-surface-sunken);
    color: var(--color-text-secondary);
    display: grid;
    place-items: center;
    flex: 0 0 auto;
}

.radio-text {
    flex: 1;
    min-width: 0;
}

.radio-title {
    font-weight: var(--font-medium);
    font-size: var(--text-sm);
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.radio-desc {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    margin-top: 1px;
}

/* Fila con icono (canal de notif / sesión / dispositivo): ico + cuerpo + meta + acciones */
.media-row {
    display: flex;
    align-items: flex-start;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
}

.media-row + .media-row {
    border-top: 1px solid var(--color-border);
}

.media-ico {
    width: 40px;
    height: 40px;
    border-radius: var(--radius-md);
    background: var(--color-surface-sunken);
    color: var(--color-text-secondary);
    display: grid;
    place-items: center;
    flex: 0 0 auto;
}

.media-row.on .media-ico {
    background: var(--color-accent-light);
    color: var(--color-accent);
}

.media-main {
    flex: 1;
    min-width: 0;
}

.media-titlerow {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.media-title {
    font-weight: var(--font-semibold);
    font-size: var(--text-sm);
}

.media-meta {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    margin-top: 3px;
}

.media-meta .mono {
    font-family: var(--font-family-mono);
}

.media-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex: 0 0 auto;
    flex-wrap: wrap;
    justify-content: flex-end;
}

/* Estados de fila (checklist/onboarding): hecho · siguiente (resalte) · automático */
.media-row.is-done .media-ico {
    background: var(--color-success-light);
    color: var(--color-success-text);
}

.media-row.is-next {
    background: var(--color-accent-light);
}

.media-row.is-next .media-ico {
    background: var(--color-accent);
    color: var(--color-text-inverse);
}

.media-row.is-auto .media-ico {
    background: var(--color-surface-sunken);
    color: var(--color-text-tertiary);
}

/* Stat strip (resumen de métricas con deeplink, dividido por hairline).
   NOTA: distinto de .kpi-strip (tiras de CONTEO de import) — nombres separados a propósito. */
.stat-strip {
    display: grid;
    grid-template-columns: 1fr 1fr;
}

.stat-cell {
    padding: var(--space-4) var(--space-5);
    border-top: 1px solid var(--color-border);
    border-left: 1px solid var(--color-border);
    display: flex;
    flex-direction: column;
    gap: 3px;
    min-width: 0;
}

.stat-cell:nth-child(-n+2) {
    border-top: 0;
}

.stat-cell:nth-child(odd) {
    border-left: 0;
}

.stat-val {
    font-size: var(--text-2xl);
    font-weight: var(--font-bold);
    letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
    line-height: 1.1;
}

.stat-val small {
    font-size: var(--text-sm);
    font-weight: var(--font-medium);
    color: var(--color-text-tertiary);
    letter-spacing: 0;
}

.stat-label {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
}

.stat-deeplink {
    margin-top: 2px;
    align-self: flex-start;
    display: inline-flex;
    align-items: center;
    gap: 3px;
    color: var(--color-text-link);
    font-size: var(--text-xs);
    font-weight: var(--font-medium);
}

.stat-deeplink .icon {
    width: 13px;
    height: 13px;
}

.stat-deeplink:hover {
    text-decoration: underline;
}

@media (min-width: 768px) {
    .stat-strip {
        grid-template-columns: repeat(3, 1fr);
    }

    .stat-cell:nth-child(-n+3) {
        border-top: 0;
    }

    .stat-cell:nth-child(odd) {
        border-left: 1px solid var(--color-border);
    }

    .stat-cell:nth-child(3n+1) {
        border-left: 0;
    }
}

/* Variantes de botón estrenadas por perfil */
.btn-link {
    background: transparent;
    border: 0;
    min-height: 0;
    padding: 0;
    color: var(--color-text-link);
    font-weight: var(--font-semibold);
    font-size: var(--text-sm);
    cursor: pointer;
}

.btn-link:hover {
    color: var(--color-text-link-hover);
    text-decoration: underline;
}

.btn-link.danger {
    color: var(--color-danger-text);
}

.btn-danger-ghost {
    background: transparent;
    color: var(--color-danger-text);
    border-color: var(--color-danger-text);
}

.btn-danger-ghost:hover {
    background: var(--color-danger-light);
}

/* Completar state-panel con título + descripción (vacío de sesiones) */
.state-title {
    font-weight: var(--font-semibold);
    color: var(--color-text-secondary);
}

.state-desc {
    font-size: var(--text-sm);
    color: var(--color-text-tertiary);
    max-width: 34ch;
}

/* Badge extra-chico (inline dentro de un título) */
.badge-xs {
    font-size: 10px;
}

/* Callout: completar el patrón de banner accionable (icono + texto + acciones).
   Consolida la `.notice` que CD re-inventó en perfil → al portar, `.notice`→`.callout`. */
.callout-ico {
    flex: 0 0 auto;
    margin-top: 1px;
}

.callout-ico .icon {
    width: 1.25rem;
    height: 1.25rem;
}

.callout-text {
    font-size: var(--text-sm);
    margin-top: 2px;
}

.callout-actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    margin-top: var(--space-3);
}

/* ============================================================
   EXTRACCIÓN acceso/pre-auth (2026-06-04) — primitivos de FORMULARIO globales que
   estrenó el cluster de acceso (login/registro/recuperar). Genéricos, solo tokens.
   El chrome pre-auth vive en su capa de familia `preauth.css`.
   ============================================================ */
/* Campo con icono guía a la izquierda (mail/lock/usuario…) sobre .field-input */
.field-icon {
    position: relative;
}

.field-icon > .icon {
    position: absolute;
    left: var(--space-3);
    top: 50%;
    transform: translateY(-50%);
    width: 18px;
    height: 18px;
    color: var(--color-text-tertiary);
    pointer-events: none;
}

.field-icon .field-input {
    padding-left: calc(var(--space-3) + 26px);
}

/* Campo de teléfono compuesto: icono + prefijo de país (+52) + dígitos (tabular) */
.field-phone {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    height: var(--input-height);
    padding-left: var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    transition: border-color var(--transition-fast), box-shadow var(--transition-fast);
}

.field-phone:focus-within {
    border-color: var(--color-border-focus);
    box-shadow: var(--input-focus-ring);
}

.field-phone.is-error {
    border-color: var(--color-danger);
}

.field-phone > .icon {
    width: 18px;
    height: 18px;
    color: var(--color-text-tertiary);
    flex: 0 0 auto;
}

.field-phone .phone-cc {
    color: var(--color-text-secondary);
    font-weight: var(--font-medium);
    font-variant-numeric: tabular-nums;
    flex: 0 0 auto;
    padding-right: var(--space-2);
    border-right: 1px solid var(--color-border);
}

.field-phone input {
    flex: 1;
    min-width: 0;
    height: 100%;
    border: 0;
    background: transparent;
    outline: none;
    font-family: inherit;
    font-size: var(--text-sm);
    color: var(--color-text-primary);
    padding: 0 var(--space-3) 0 0;
    font-variant-numeric: tabular-nums;
    letter-spacing: .02em;
}

.field-input.is-error {
    border-color: var(--color-danger);
}

/* Estado "cargando" del botón (.btn): oculta el label y muestra el spinner */
.btn .btn-spin {
    display: none;
    width: 18px;
    height: 18px;
}

.btn.is-loading {
    pointer-events: none;
}

.btn.is-loading .btn-label, .btn.is-loading .btn-ico {
    display: none;
}

.btn.is-loading .btn-spin {
    display: inline-block;
    animation: btn-spin .8s linear infinite;
}

.btn.is-loading .btn-spin .icon {
    width: 18px;
    height: 18px;
}

@keyframes btn-spin {
    to {
        transform: rotate(360deg);
    }
}

@media (prefers-reduced-motion: reduce) {
    .btn.is-loading .btn-spin {
        animation: none;
    }
}

/* ============================================================
   EXTRACCIÓN uso (2026-06-04) — completaciones SIN conflicto de familias que ya
   existen (card-head · stat-cell · usage). Genéricas, solo tokens. (CD redefinió
   además badge/callout/card/stat-strip en su bundle; eso NO se toma — ver
   porting-notes; la reconciliación va al portar uso.)
   ============================================================ */
/* Sub-partes de la cabecera de tarjeta (texto + aside de acciones) */
.card-head-text {
    min-width: 0;
}

.card-head-aside {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

/* Completar .stat-cell: fila val+badge · subtexto · valor atenuado */
.stat-row {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-wrap: wrap;
}

.stat-sub {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    font-variant-numeric: tabular-nums;
}

.stat-val.is-muted {
    color: var(--color-text-tertiary);
    font-weight: var(--font-semibold);
}

/* Usage: tonos del relleno (ok/warn/danger) + porcentaje con tono */
.usage-fill.is-warn {
    background: var(--color-warning);
}

.usage-fill.is-danger {
    background: var(--color-danger);
}

.usage-pct {
    font-variant-numeric: tabular-nums;
    font-weight: var(--font-semibold);
}

.usage-pct.is-warn {
    color: var(--color-warning-text);
}

.usage-pct.is-danger {
    color: var(--color-danger-text);
}

/* ============================================================
   Charts — capa centralizada (movido de finanzas.css + extraído de insights/métricas).
   Card de gráfica · donut · viz de serie temporal. Leyenda: ver .chart-legend arriba.
   ============================================================ */
/* Card de gráfica (movido de finanzas.css; compartido finanzas/insights/banxico) */
.chart-card {
    padding: var(--space-5);
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.chart-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
}

.chart-title {
    font-size: var(--text-base);
    font-weight: var(--font-semibold);
    margin: 0;
}

.chart-sub {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    margin: 2px 0 0;
}

/* Donut (movido de finanzas.css) — distribución categórica multi-segmento */
.donut-wrap {
    display: flex;
    align-items: center;
    gap: var(--space-5);
}

.donut {
    position: relative;
    width: 116px;
    height: 116px;
    flex: 0 0 auto;
}

.donut svg {
    transform: rotate(-90deg);
}

.donut-center {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
}

.donut-total {
    font-size: var(--text-lg);
    font-weight: var(--font-bold);
    font-variant-numeric: tabular-nums;
    line-height: 1;
    letter-spacing: -.02em;
}

.donut-cap {
    font-size: 9px;
    color: var(--color-text-tertiary);
    text-transform: uppercase;
    letter-spacing: .04em;
}

.donut-legend {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    flex: 1;
    min-width: 0;
}

.dleg {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    font-size: var(--text-sm);
}

.dleg .legend-dot {
    border-radius: 50%;
    width: 9px;
    height: 9px;
}

.dleg-name {
    color: var(--color-text-secondary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.dleg-val {
    margin-left: auto;
    font-weight: var(--font-semibold);
    font-variant-numeric: tabular-nums;
    flex: 0 0 auto;
}

/* Segmento de dona (trazo por veredicto). El contenedor .donut ya rota -90°. De admin/usuarios/detalle. */
.donut-seg {
    fill: none;
    stroke-width: 14;
}

.donut-seg.is-valid {
    stroke: var(--color-success);
}

.donut-seg.is-notfound {
    stroke: var(--color-warning);
}

.donut-seg.is-cep {
    stroke: var(--color-info);
}

.donut-seg.is-error {
    stroke: var(--color-danger);
}

.donut-seg.is-track {
    stroke: var(--color-surface-sunken);
}

.donut-seg.is-proc {
    stroke: var(--color-accent);
}

.donut-seg.is-pend {
    stroke: var(--color-border-strong);
}

/* Paleta categórica genérica (series sin semántica de veredicto). De admin/planes (dona por plan). */
.donut-seg.is-s1 {
    stroke: var(--color-accent);
}

.donut-seg.is-s2 {
    stroke: var(--color-success);
}

.donut-seg.is-s3 {
    stroke: var(--color-warning);
}

.donut-seg.is-s4 {
    stroke: var(--color-border-strong);
}

/* Viz de serie temporal SVG (barras apiladas / líneas de percentiles + ejes/grid).
   El consumidor pinta stroke/fill con getComputedStyle de tokens (no #hex). De insights. */
.chart {
    width: 100%;
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
}

.chart-svg {
    width: 100%;
    height: auto;
    display: block;
}

.chart-svg .chart-grid-line {
    stroke: var(--color-border);
    stroke-width: 1;
}

.chart-svg .chart-axis-base {
    stroke: var(--color-border-strong);
    stroke-width: 1;
}

/* Etiquetas de eje de chart (<text> SVG). Canónico — convergencia de los charts admin. */
.chart-axis-label {
    font-size: 11px;
    fill: var(--color-text-tertiary);
    font-family: var(--font-family-mono);
}

/* tick numérico */
.chart-cat-label {
    font-size: 12px;
    fill: var(--color-text-tertiary);
    font-family: var(--font-family);
}

/* etiqueta de categoría */
.chart-bar-val {
    font-size: 10px;
    fill: var(--color-text-secondary);
    font-family: var(--font-family-mono);
    font-variant-numeric: tabular-nums;
}

/* valor sobre la barra */
.chart-axis-x {
    text-anchor: middle;
}

.chart-axis-y {
    text-anchor: end;
}

.chart-bar {
    shape-rendering: crispEdges;
}

.chart-bar.is-valid {
    fill: var(--color-success);
}

.chart-bar.is-notfound {
    fill: var(--color-warning);
}

.chart-bar.is-cep {
    fill: var(--color-info);
}

.chart-bar.is-error {
    fill: var(--color-danger);
}

.chart-line {
    fill: none;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.chart-line.is-avg {
    stroke-dasharray: 5 5;
}

.chart-point {
    stroke: var(--color-surface);
    stroke-width: 1.5;
}

/* Lista-ranking con barra de proporción (de insights · admin top-banks/top-errors reusa) */
.rank {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
}

.rank-row {
    display: grid;
    grid-template-columns: 1.5em 36px minmax(0, 1fr) auto;
    align-items: center;
    gap: var(--space-2) var(--space-3);
    padding: var(--space-3) var(--space-4);
}

.rank-row + .rank-row {
    border-top: 1px solid var(--color-border);
}

.rank-no {
    font-family: var(--font-family-mono);
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    text-align: right;
}

.rank-main {
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 5px;
}

.rank-namerow {
    display: flex;
    align-items: baseline;
    gap: var(--space-2);
    min-width: 0;
}

.rank-name {
    font-weight: var(--font-medium);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    min-width: 0;
}

.rank-bar-track {
    height: 6px;
    border-radius: var(--badge-radius);
    background: var(--color-surface-sunken);
    overflow: hidden;
}

.rank-bar-fill {
    height: 100%;
    border-radius: var(--badge-radius);
    background: var(--color-accent);
    transition: width var(--transition-normal);
}

.rank-bar-fill.is-error {
    background: var(--color-danger);
}

.rank-stats {
    text-align: right;
    display: flex;
    flex-direction: column;
    gap: 2px;
    flex: 0 0 auto;
}

.rank-val {
    font-weight: var(--font-semibold);
    font-variant-numeric: tabular-nums;
}

.rank-sub {
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.rank-sub .is-bad {
    color: var(--color-danger-text);
}

@media (min-width: 1100px) {
    .rank-row {
        padding: var(--space-3) var(--space-5);
        gap: var(--space-2) var(--space-4);
    }
}

/* Panel KPI — superficie única dividida por hairlines (gap 1px); celda hero + medidor.
   Reflujo 2 col (hero a todo el ancho) → 5 col en una fila. De insights/métricas. */
.kpi-panel {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 1px;
    background: var(--color-border);
    border: 1px solid var(--color-border);
    border-radius: var(--card-radius);
    overflow: hidden;
}

.kpi-cell {
    background: var(--color-surface);
    padding: var(--space-4);
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    min-width: 0;
}

.kpi-cell-val {
    font-size: var(--text-2xl);
    font-weight: var(--font-bold);
    font-variant-numeric: tabular-nums;
    letter-spacing: -.02em;
    line-height: 1.05;
}

.kpi-cell-label {
    font-size: var(--text-xs);
    color: var(--color-text-secondary);
    line-height: 1.2;
}

.kpi-cell-sub {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    font-size: var(--text-xs);
    color: var(--color-text-tertiary);
    font-variant-numeric: tabular-nums;
    margin-top: 1px;
}

.kpi-cell-sub .icon {
    width: 13px;
    height: 13px;
    flex: 0 0 auto;
}

.kpi-cell-meter {
    height: 4px;
    border-radius: var(--badge-radius);
    background: var(--color-surface-sunken);
    overflow: hidden;
    margin-top: var(--space-2);
}

.kpi-cell-meter-fill {
    height: 100%;
    background: var(--color-success);
    border-radius: var(--badge-radius);
    transition: width var(--transition-normal);
}

/* KPI deeplinkable (la celda es <a>) + delta ↑/↓ + sufijo de unidad. De admin/validaciones/lista. */
a.kpi-cell {
    text-decoration: none;
    transition: background var(--transition-fast);
}

a.kpi-cell:hover {
    background: var(--color-surface-sunken);
}

a.kpi-cell.is-hero:hover {
    background: var(--color-surface-muted);
}

.kpi-delta {
    display: inline-flex;
    align-items: center;
    gap: 3px;
    font-weight: var(--font-medium);
}

.kpi-delta.is-up {
    color: var(--color-success-text);
}

.kpi-delta.is-down {
    color: var(--color-danger-text);
}

.kpi-delta .icon {
    width: 13px;
    height: 13px;
}

.kpi-unit {
    font-size: var(--text-base);
    font-weight: var(--font-medium);
    color: var(--color-text-tertiary);
    letter-spacing: 0;
}

.kpi-cell.is-hero {
    grid-column: 1 / -1;
    background: var(--color-surface-sunken);
    gap: var(--space-1);
}

.kpi-cell.is-hero .kpi-cell-val {
    font-size: var(--text-4xl);
}

@media (min-width: 720px) {
    .kpi-panel {
        grid-template-columns: 1.25fr 1fr 1fr 1fr 1fr;
    }

    .kpi-cell.is-hero {
        grid-column: auto;
        justify-content: center;
    }
}

/* sr-only — utilidad solo-lectores-de-pantalla (faltaba en el sistema) */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
}

/* Skeleton — superficie shimmer reutilizable (usa @keyframes sk-shimmer ya central) */
.skeleton {
    border-radius: var(--radius-md);
    background: linear-gradient(90deg, var(--color-surface-sunken) 0%, var(--color-surface-muted) 50%, var(--color-surface-sunken) 100%);
    background-size: 200% 100%;
    animation: sk-shimmer 1.3s ease-in-out infinite;
}

.skeleton.is-chart {
    width: 100%;
}

.skeleton.is-donut {
    width: 116px;
    height: 116px;
    border-radius: 50%;
    flex: 0 0 auto;
}

@media (prefers-reduced-motion: reduce) {
    .skeleton {
        animation: none;
    }
}

/* Disclosure — sección colapsable (plegada por defecto): cabecera + contador + chevron (de notificaciones) */
.disclosure-head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    width: 100%;
    padding: var(--space-1) 0;
    background: transparent;
    border: 0;
    cursor: pointer;
    color: inherit;
    min-height: 36px;
    font-family: inherit;
}

.disclosure-count {
    font-size: var(--text-xs);
    font-weight: var(--font-semibold);
    color: var(--color-text-secondary);
    background: var(--color-surface-sunken);
    border-radius: var(--badge-radius);
    padding: 1px 7px;
    font-variant-numeric: tabular-nums;
}

.disclosure-chev {
    width: 16px;
    height: 16px;
    color: var(--color-text-tertiary);
    margin-left: auto;
    transition: transform var(--transition-fast);
}

.disclosure-head[aria-expanded="true"] .disclosure-chev {
    transform: rotate(180deg);
}

.disclosure-body {
    padding-top: var(--space-2);
}

.disclosure-body[hidden] {
    display: none;
}

/* State pill — indicador on/off con punto + texto (de notificaciones) */
.state-pill {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: var(--text-sm);
    font-weight: var(--font-semibold);
}

.state-pill-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    flex: 0 0 auto;
}

.state-pill.is-on {
    color: var(--color-success-text);
}

.state-pill.is-on .state-pill-dot {
    background: var(--color-success);
}

.state-pill.is-off {
    color: var(--color-text-tertiary);
}

.state-pill.is-off .state-pill-dot {
    background: var(--color-text-tertiary);
}

.state-pill.is-down {
    color: var(--color-danger-text);
    font-weight: var(--font-semibold);
}

.state-pill.is-down .state-pill-dot {
    background: var(--color-danger);
}

/* HTTP method badge — método coloreado en mono (de api-docs; playground/admin devtools reusa) */
.method-badge {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 52px;
    padding: 2px 8px;
    border-radius: var(--radius-sm);
    font-family: var(--font-family-mono);
    font-size: 11px;
    font-weight: var(--font-bold);
    letter-spacing: .02em;
    text-transform: uppercase;
    flex: 0 0 auto;
}

.method-badge.is-get {
    background: var(--color-accent-light);
    color: var(--color-text-link);
}

.method-badge.is-post {
    background: var(--color-success-light);
    color: var(--color-success-text);
}

.method-badge.is-put {
    background: var(--color-warning-light);
    color: var(--color-warning-text);
}

.method-badge.is-delete {
    background: var(--color-danger-light);
    color: var(--color-danger-text);
}

/* HTTP status pill — código + punto + texto (nunca solo color) (de api-docs) */
.http-status {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: var(--text-xs);
    font-weight: var(--font-semibold);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.http-status-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex: 0 0 auto;
}

.http-status.is-2xx {
    color: var(--color-success-text);
}

.http-status.is-2xx .http-status-dot {
    background: var(--color-success);
}

.http-status.is-4xx {
    color: var(--color-warning-text);
}

.http-status.is-4xx .http-status-dot {
    background: var(--color-warning);
}

.http-status.is-5xx {
    color: var(--color-danger-text);
}

.http-status.is-5xx .http-status-dot {
    background: var(--color-danger);
}

/* ── Paginación (APP.pagination) ───────────────────────────────────────
   Primitivo compartido. Estas clases vivían en admin/assets/css/admin-tests.css
   —la hoja de UNA pantalla— aunque el docblock de assets/js/components/
   pagination.js ya declaraba que estaban aquí. El efecto era que el
   primitivo solo se veía bien en /admin/tests: cualquier otra pantalla que
   lo usara pintaba un paginador sin estilos, que es parte de por qué
   /admin/billing dejó sus contenedores de paginador vacíos.
   ─────────────────────────────────────────────────────────────────── */
.pagination {
    display: flex;
    gap: var(--space-1);
    justify-content: center;
    padding: var(--space-3);
    align-items: center;
    flex-wrap: wrap;
}
.pagination-btn {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: var(--space-1-5) var(--space-2-5);
    font-size: var(--text-xs);
    cursor: pointer;
    color: var(--color-text-primary);
    font-family: inherit;
    min-width: 32px;
    line-height: 1.4;
}
.pagination-btn:hover:not([disabled]) { background: var(--color-surface-sunken); }
.pagination-btn[disabled] { opacity: 0.4; cursor: not-allowed; }
.pagination-btn.is-current {
    background: var(--color-primary);
    color: var(--color-text-inverse);
    border-color: var(--color-primary);
}
.pagination-info {
    font-size: var(--text-xs);
    color: var(--color-text-secondary);
    margin: 0 auto 0 0;
}
.pagination-controls {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
}
