﻿/* ============================================================
   IO Ventas — sistema de diseño "Marea" (handoff Claude Design)
   Primario teal #0e7a70 · acento aquamarine #17c3ad para CTAs ·
   neutros azulados · Public Sans + IBM Plex Mono · WCAG AA ·
   claro y oscuro obligatorios.

   Hermana de IO Admin, con identidad propia: el naranja de marca
   (--brand) aparece ÚNICAMENTE en el logo. No hay botones, badges
   ni estados naranjas en Ventas.
   ============================================================ */

:root {
    /* Le dice al navegador en qué tema pinta lo que NO es nuestro: barras de
       desplazamiento, checkbox/radio sin clase, la lista de un <select>, el
       ícono de un input de fecha. Sin esto queda en "normal" (= claro) y en
       oscuro salen barras blancas. El oscuro lo cambia abajo. */
    color-scheme: light;

    /* Marca — solo el logo. No usar en UI. */
    --brand: #f39d1c;

    /* Primario: navegación activa, links, badge Enviada, foco.
       Nota: el handoff traía #0e7a70, pero sobre --primary-soft daba 4.36:1 y
       el badge "Enviada" es texto normal (12px) → AA pide 4.5. Oscurecido lo
       mínimo para llegar a 4.58:1; a simple vista es el mismo teal. */
    --primary: #0e766d;
    --primary-strong: #0a5f57;
    --primary-soft: #dcefec;

    /* Acento de acción: 1–2 CTAs por pantalla (Nueva cotización,
       Generar Word). Nunca para estados ni navegación. */
    --accent: #17c3ad;
    --accent-strong: #0fa38f;
    --on-accent: #052420; /* texto sobre accent (~9:1) */

    /* Superficies y texto (neutros azulados) */
    --bg: #f3f6f9;
    --surface: #ffffff;
    --surface-2: #e9eef3;
    --surface-3: #dfe6ed;
    --border: #d9e1e9;
    --border-strong: #c3cdd8;
    --text: #1a232c; /* 14.8:1 — AAA */
    --text-2: #526271; /* 6.1:1 — AA */
    --text-3: #8595a4; /* solo etiquetas grandes / decorativo */

    /* Estados semánticos — mismo ajuste de AA que --primary: los badges
       (Aprobada / Vencida / Rechazada) pintan este color sobre su propio
       fondo tenue, y así los tres pares quedan en 4.55:1.
       Handoff original: #1d8a4e · #96660a · #c23a3a */
    --success: #1a7b45;
    --success-bg: #e0f2e6;
    --warning: #92630a;
    --warning-bg: #faeecf;
    --danger: #be3939;
    --danger-bg: #fbe5e5;
    /* Un escalón más grave que --danger. Solo para el último cajón de antigüedad
       de cobranza (+90 días), que necesita distinguirse del de 61-90. */
    --danger-fuerte: #8c2020;
    /* Informativo — azul propio, NO el primario. El badge "Enviada" pintaba en
       teal y de un vistazo se confundía con el verde de "Aprobada": dos estados
       distintos del ciclo leídos como el mismo. El azul los separa sin tocar el
       resto de la identidad. 5.4:1 sobre su fondo (AA para texto de 12px). */
    --info: #1b5fa8;
    --info-bg: #e3ecf7;

    /* Sidebar — sigue el tema; el activo va en teal */
    --side-bg: #eaeff4;
    --side-text: #526271;
    --side-active: #0a5f57;
    --side-active-bg: var(--primary-soft);

    /* Forma y elevación — un punto más redondeado que Admin */
    --r-sm: 4px;
    --r: 8px;
    --r-lg: 12px;
    --shadow: 0 1px 2px rgba(20,30,40,.05), 0 1px 3px rgba(20,30,40,.08);
    --shadow-lg: 0 10px 28px rgba(20,30,40,.14);

    /* La paleta de Syncfusion Material 3 (--color-sf-*) se ajusta en
       css/syncfusion-theme.css (debe cargar DESPUÉS de material3.css). */
}

:root[data-theme="dark"] {
    color-scheme: dark;

    --brand: #f7a72e;

    --primary: #35b3a5;
    --primary-strong: #5accbf;
    --primary-soft: #15302c;

    --accent: #3fd6c2;
    --accent-strong: #6ce3d3;
    --on-accent: #052420;

    --bg: #0f151a;
    --surface: #161d24;
    --surface-2: #1c242c;
    --surface-3: #232d36;
    --border: #28323c;
    --border-strong: #39454f;
    --text: #e8eef2;
    --text-2: #a0aeba;
    --text-3: #6e7d8b;

    --success: #4cc07e;
    --success-bg: #152a1c;
    --warning: #e0a636;
    --warning-bg: #2a2113;
    --danger: #ef6b6b;
    --danger-bg: #2c1717;
    --info: #5fb0f2;
    --info-bg: #13232f;
    /* En oscuro lo "más grave" es más brillante, no más oscuro: sobre fondo negro
       un rojo apagado se lee como menos urgente, justo al revés de lo que se quiere. */
    --danger-fuerte: #ff9a9a;

    --side-bg: #121a20;
    --side-text: #a0aeba;
    --side-active: #5accbf;
    --side-active-bg: #15302c;

    --shadow: 0 1px 2px rgba(0,0,0,.4);
    --shadow-lg: 0 12px 32px rgba(0,0,0,.55);

    /* ── Bootstrap 5.3 en oscuro ──────────────────────────────────────────
       Bootstrap NO sigue nuestro data-theme: sus utilidades y componentes
       leen sus propias variables, que se quedan en los valores del tema
       claro (.text-muted casi negro sobre fondo oscuro, .alert en pastel).
       Se remapean a nuestros tokens: un solo lugar, aplica a todas las
       páginas. */
    --bs-body-color: var(--text);
    --bs-body-color-rgb: 232, 238, 242; /* = --text, Bootstrap lo usa en rgba() */
    --bs-body-bg: var(--surface);
    --bs-body-bg-rgb: 22, 29, 36; /* = --surface */
    --bs-emphasis-color: var(--text);
    --bs-secondary-color: var(--text-2); /* .text-muted */
    --bs-secondary-bg: var(--surface-2);
    --bs-tertiary-color: var(--text-3);
    --bs-tertiary-bg: var(--surface-3);
    --bs-border-color: var(--border);
    --bs-link-color: var(--primary);
    --bs-link-hover-color: var(--primary-strong);

    --bs-success-bg-subtle: var(--success-bg);
    --bs-success-border-subtle: var(--success);
    --bs-success-text-emphasis: var(--success);
    --bs-warning-bg-subtle: var(--warning-bg);
    --bs-warning-border-subtle: var(--warning);
    --bs-warning-text-emphasis: var(--warning);
    --bs-danger-bg-subtle: var(--danger-bg);
    --bs-danger-border-subtle: var(--danger);
    --bs-danger-text-emphasis: var(--danger);
    --bs-info-bg-subtle: var(--info-bg);
    --bs-info-border-subtle: var(--info);
    --bs-info-text-emphasis: var(--info);
}

/* ============================================================
   Base
   ============================================================ */
html, body {
    font-family: "Public Sans", system-ui, -apple-system, "Segoe UI", sans-serif;
}

body {
    background: var(--bg);
    color: var(--text);
    -webkit-font-smoothing: antialiased;
    transition: background-color .2s ease, color .2s ease;
}

h1, h2, h3, h4, h5, h6 {
    letter-spacing: -.01em;
    font-weight: 700;
    color: var(--text);
}

h1 { font-size: 1.85rem; font-weight: 800; letter-spacing: -.02em; }
h2 { font-size: 1.15rem; }
h3 { font-size: 1.05rem; }
h4, h5 { font-size: 1rem; }
h6 { font-size: .95rem; font-weight: 700; }

h1:focus { outline: none; }

a, .btn-link {
    color: var(--primary);
    text-decoration: none;
}

    a:hover, .btn-link:hover {
        color: var(--primary-strong);
        text-decoration: underline;
    }

code { color: var(--primary-strong); }

/* Montos, folios y fechas: mono tabular (igual que IO Admin) */
.num, .money, .folio, .mono {
    font-family: "IBM Plex Mono", ui-monospace, Consolas, monospace;
    font-variant-numeric: tabular-nums;
}

.num, .money { text-align: right; }

/* Etiqueta pequeña en mayúsculas (encabezados de dato, columnas) */
.label-sm {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-3);
    font-weight: 600;
}

/* Texto con significado semántico (días fuera en ámbar, atrasos en rojo) */
.txt-warning { color: var(--warning); font-weight: 600; }
.txt-danger  { color: var(--danger);  font-weight: 600; }
.txt-success { color: var(--success); font-weight: 600; }
.txt-2       { color: var(--text-2); }

/* La cifra de trabajo (el subtotal): el IVA se entera a Hacienda y el total lo
   incluye, así que el negocio se lee en el subtotal. Monoespaciada como todo
   dato numérico de las dos apps. */
.cifra-principal {
    font-family: "IBM Plex Mono", monospace;
    font-weight: 600;
    color: var(--text);
}

/* Tablita de montos del encabezado de un listado (Subtotal · IVA · Total).
   Celdas que se envuelven solas: en móvil bajan de renglón sin desbordar. */
.resumen-montos {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-top: .35rem;
}

.rm-celda {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    padding: .35rem .7rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    min-width: 7.5rem;
}

.rm-lbl {
    font-size: .68rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-3);
}

.rm-num {
    font-family: "IBM Plex Mono", monospace;
    font-size: .95rem;
    color: var(--text-2);
}

/* El subtotal es la cifra protagonista: más grande y con el texto fuerte */
.rm-principal .rm-num {
    font-size: 1.15rem;
    font-weight: 700;
    color: var(--text);
}

.rm-principal .rm-lbl {
    color: var(--text-2);
}

/* Nota bajo un título: explica sin competir con el dato */
.nota {
    color: var(--text-2);
    font-size: .85rem;
    margin: 0 0 .75rem;
    max-width: 70ch;
}

/* Íconos SVG inline (componente Icon.razor) */
.ico {
    display: inline-block;
    vertical-align: -0.18em;
    flex-shrink: 0;
}

.content { padding-top: 1.1rem; }

/* ============================================================
   Botones
   ============================================================ */
/* CTA (accent): reservado a la acción principal de la pantalla */
.btn-primary {
    background-color: var(--accent);
    border-color: var(--accent);
    color: var(--on-accent);
    font-weight: 700;
    border-radius: var(--r);
}

    .btn-primary:hover, .btn-primary:focus, .btn-primary:active {
        background-color: var(--accent-strong);
        border-color: var(--accent-strong);
        color: var(--on-accent);
    }

.btn-secondary {
    background: var(--surface);
    border: 1px solid var(--border-strong);
    color: var(--text);
    border-radius: var(--r);
}

    .btn-secondary:hover {
        background: var(--surface-2);
        color: var(--text);
    }

.btn-danger {
    background: transparent;
    border: 1px solid var(--danger);
    color: var(--danger);
    border-radius: var(--r);
}

    .btn-danger:hover {
        background: var(--danger-bg);
        color: var(--danger);
    }

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus,
.form-control:focus, .form-check-input:focus {
    box-shadow: 0 0 0 3px var(--primary-soft);
}

/* ============================================================
   Inputs
   ============================================================ */
.form-control {
    background: var(--surface);
    border: 1px solid var(--border-strong);
    color: var(--text);
    border-radius: var(--r);
}

    .form-control:focus {
        border-color: var(--primary);
        outline: none;
        box-shadow: 0 0 0 3px var(--primary-soft);
    }

.form-label {
    font-size: .8rem;
    font-weight: 600;
    color: var(--text-2);
    margin-bottom: .3rem;
}

.valid.modified:not([type=checkbox]) { outline: 1px solid var(--success); }
.invalid { outline: 1px solid var(--danger); }
.validation-message { color: var(--danger); }

.form-floating > .form-control-plaintext::placeholder,
.form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder,
.form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* ============================================================
   Badges del ciclo de vida de la cotización
   Siempre color + etiqueta, nunca solo color. Borrador lleva borde
   punteado: todavía no es un documento "real".
   ============================================================ */
.badge-borrador, .badge-enviada, .badge-aprobada,
.badge-vencida, .badge-rechazada, .badge-neutro,
.badge-ok, .badge-warn, .badge-bad, .badge-info {
    display: inline-block;
    padding: 3px 11px;
    border-radius: 20px;
    font-size: 12px;
    font-weight: 600;
    white-space: nowrap;
    line-height: 1.45;
}

.badge-borrador {
    background: var(--surface-2);
    color: var(--text-2);
    border: 1px dashed var(--border-strong);
}

.badge-enviada, .badge-info {
    background: var(--info-bg);
    color: var(--info);
}

.badge-aprobada, .badge-ok {
    background: var(--success-bg);
    color: var(--success);
}

.badge-vencida, .badge-warn {
    background: var(--warning-bg);
    color: var(--warning);
}

.badge-rechazada, .badge-bad {
    background: var(--danger-bg);
    color: var(--danger);
}

.badge-neutro {
    background: var(--surface-2);
    color: var(--text-2);
}

/* ============================================================
   Encabezado de pantalla: título + acciones a la derecha
   ============================================================ */
.page-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin-bottom: 1.1rem;
}

.page-head h1, .page-head h2 {
    margin: 0;
    font-size: 1.35rem;
    font-weight: 800;
}

.page-head-sub {
    color: var(--text-2);
    font-size: .9rem;
    margin: .15rem 0 0;
}

.page-actions {
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
}

/* ============================================================
   Tarjeta de indicador (KPI)
   Etiqueta uppercase 11px + cifra mono + línea de contexto,
   coloreada solo si es semántica.
   ============================================================ */
.kpi-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
    gap: .75rem;
    margin-bottom: 1.25rem;
}

.kpi-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow);
    padding: .9rem 1.1rem;
}

.kpi-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-3);
    font-weight: 600;
    margin-bottom: .35rem;
}

.kpi-value {
    font-family: "IBM Plex Mono", ui-monospace, monospace;
    font-variant-numeric: tabular-nums;
    font-weight: 600;
    font-size: 1.4rem;
    color: var(--text);
    line-height: 1.2;
}

.kpi-sub {
    margin-top: .35rem;
    font-size: .8rem;
    color: var(--text-2);
}

.kpi-value.is-warning, .kpi-sub.is-warning { color: var(--warning); font-weight: 600; }
.kpi-value.is-success, .kpi-sub.is-success { color: var(--success); font-weight: 600; }
.kpi-value.is-danger,  .kpi-sub.is-danger  { color: var(--danger);  font-weight: 600; }

/* Compatibilidad: las pantallas viejas usaban .stat-box con estilos
   en línea. Aquí queda tokenizada para que siga el tema. */
.stat-box {
    background: var(--surface);
    border: 1px solid var(--border) !important;
    border-radius: var(--r-lg) !important;
    box-shadow: var(--shadow);
}

/* ============================================================
   Tarjeta / panel de contenido
   ============================================================ */
.card-seccion, .edit-panel {
    background: var(--surface);
    border: 1px solid var(--border) !important;
    border-radius: var(--r-lg) !important;
    box-shadow: var(--shadow);
}

/* flex-wrap porque el encabezado no siempre es solo un título: en "Esquema de
   pagos" lleva el combo del esquema (230px) y el botón Generar, y en una ventana
   angosta ese renglón se salía de la card. Al envolver, el control baja de línea
   en vez de desbordarse. */
.card-seccion-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: .5rem 1rem;
    padding: .7rem 1rem;
    border-bottom: 1px solid var(--border);
    font-weight: 700;
    font-size: .95rem;
}

    /* Un control del encabezado nunca es más ancho que la card (el Width fijo de
       un componente Syncfusion se convierte en style en línea). */
    .card-seccion-head .e-control-wrapper,
    .card-seccion-head .e-input-group {
        max-width: 100%;
    }

.card-seccion-body { padding: 1rem; }

/* Dato en card compacta (Cliente / Vigencia / Esquema de pago) */
.meta-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: .75rem;
}

.meta-card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r);
    padding: .7rem .9rem;
}

.meta-label {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-3);
    margin-bottom: .25rem;
}

.meta-value {
    font-size: .9rem;
    font-weight: 600;
    color: var(--text);
}

/* ============================================================
   Encabezado de documento (editor de cotización / factura)
   Folio mono + badge de estado + título grande; a la derecha el
   hilo de versiones.
   ============================================================ */
.doc-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    padding: .9rem 1.1rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow);
    margin-bottom: .75rem;
}

.doc-head-linea {
    display: flex;
    align-items: center;
    gap: .6rem;
    flex-wrap: wrap;
}

    .doc-head-linea .folio {
        font-family: "IBM Plex Mono", ui-monospace, monospace;
        font-size: .85rem;
        color: var(--text-2);
    }

.doc-titulo {
    font-size: 1.25rem;
    font-weight: 800;
    letter-spacing: -.01em;
    margin: .3rem 0 0;
}

.doc-acciones { margin-bottom: 1rem; }

/* ============================================================
   Formulario de captura larga
   ============================================================ */
.form-grid {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: .8rem;
}

    .form-grid.dos { grid-template-columns: repeat(2, minmax(0, 1fr)); }

@media (max-width: 1100px) {
    .form-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .form-grid > [style*="span 3"], .form-grid > [style*="span 4"] { grid-column: span 2 !important; }
}

@media (max-width: 640px) {
    .form-grid, .form-grid.dos { grid-template-columns: minmax(0, 1fr); }
    .form-grid > [style*="span"] { grid-column: auto !important; }
}

/* Renglón de algo que sigue existiendo pero ya no está en uso (usuario deshabilitado).
   Se atenúa en vez de esconderse: la fila tiene que seguir ahí para poder reactivarla,
   pero no debe competir con las activas al recorrer la tabla con la vista. */
.fila-inactiva {
    opacity: .55;
}

    .fila-inactiva strong {
        font-weight: 500;
    }

/* Botón de "elegir archivo" con texto propio. El input nativo se esconde (no con
   display:none, que en algunos navegadores lo saca del foco por teclado) y el label
   hace de botón: al hacer clic en él, el navegador abre el diálogo igual. */
.archivo-btn {
    display: inline-block;
    cursor: pointer;
}

    .archivo-btn input[type="file"] {
        position: absolute;
        width: 1px;
        height: 1px;
        opacity: 0;
        pointer-events: none;
    }

    .archivo-btn span {
        display: inline-block;
        padding: .45rem .9rem;
        border: 1px solid var(--border-strong);
        border-radius: var(--r-sm);
        background: var(--surface-2);
        color: var(--text);
        font-size: .9rem;
        font-weight: 600;
    }

    .archivo-btn:hover span {
        border-color: var(--primary);
        color: var(--primary);
    }

    /* El foco del teclado vive en el input escondido: sin esto, tabular hasta el
       botón no se vería por ningún lado. */
    .archivo-btn input[type="file"]:focus-visible + span {
        outline: 2px solid var(--primary);
        outline-offset: 2px;
    }

/* Imágenes de una partida en el editor: miniatura + pie + quitar, en tira.
   El ancho fijo es lo que hace que las miniaturas se lean como una galería y no
   como una columna de recortes de alturas distintas; la imagen se contiene dentro
   con object-fit, así que una captura vertical no estira su tarjeta. */
.partida-imgs {
    display: flex;
    flex-wrap: wrap;
    gap: .7rem;
    margin-bottom: .6rem;
}

.partida-img {
    margin: 0;
    width: 190px;
    display: flex;
    flex-direction: column;
    gap: .35rem;
    padding: .5rem;
    border: 1px solid var(--border);
    border-radius: var(--r);
    background: var(--surface-2);
}

    .partida-img img {
        width: 100%;
        height: 110px;
        object-fit: contain;
        border-radius: var(--r-sm);
        background: #fff;
    }

/* Aquí vivía .editor-cols (ajuste comercial | esquema de pagos, dos tarjetas a la
   mitad del ancho). Se retiró: la tabla de pagos necesita ~630px y media columna
   nunca los daba —ni en 1280— así que las celdas se pintaban fuera de la card.
   Las dos tarjetas van ahora a lo ancho, una debajo de la otra. */

/* ============================================================
   Tabla densa de montos
   ============================================================ */
/* Envoltura obligatoria de .tabla-montos dentro de una card.
   La tabla tiene un ancho mínimo propio (encabezados, montos mono y, en captura,
   inputs y botones): en una ventana angosta ese mínimo supera el ancho de la card
   y, como .card-seccion no recorta, las celdas se pintaban FUERA del borde. Con el
   scroll aquí la tabla se desliza dentro de la card y el borde vuelve a contenerla.
   Mismo patrón que .resumen-meses-scroll. */
.tabla-scroll { overflow-x: auto; }

.tabla-montos {
    width: 100%;
    border-collapse: collapse;
    color: var(--text);
    font-size: .875rem;
}

    .tabla-montos th {
        background: var(--surface-2);
        color: var(--text-3);
        border-bottom: 1px solid var(--border);
        text-transform: uppercase;
        letter-spacing: .05em;
        font-size: 11px;
        font-weight: 600;
        text-align: left;
        padding: .5rem .9rem;
        white-space: nowrap;
    }

    /* La aclaración entre paréntesis de un encabezado baja de renglón.
       En una sola línea, «MOMENTO (SALE EN "CONDICIONES DE PAGO")» le imponía
       305px a esa columna y estiraba la tabla de pagos a 810px — más ancho que
       cualquier card de esta pantalla. Partida en dos renglones, la columna se
       ajusta al dato. */
    .tabla-montos th .txt-2 {
        display: block;
        white-space: normal;
        text-transform: none;
        letter-spacing: 0;
        font-weight: 400;
    }

    /* Piso para la columna que lleva un campo de texto libre: sin él, el reparto
       de la tabla la aplastaba a ~65px y el input quedaba inservible. Si con este
       piso la tabla ya no cabe, quien cede es .tabla-scroll — no el campo. */
    .tabla-montos th.col-momento { min-width: 11rem; }

    .tabla-montos td {
        border-bottom: 1px solid var(--border);
        padding: .55rem .9rem;
        vertical-align: top;
    }

    .tabla-montos th.money, .tabla-montos td.money,
    .tabla-montos th.num, .tabla-montos td.num {
        text-align: right;
    }

    .tabla-montos td.money, .tabla-montos td.num {
        font-family: "IBM Plex Mono", ui-monospace, monospace;
        font-variant-numeric: tabular-nums;
        white-space: nowrap;
    }

    .tabla-montos tbody tr:hover td { background: var(--surface-2); }

    .tabla-montos tr.fila-totales td {
        background: var(--surface-2);
        font-weight: 700;
        border-bottom: none;
    }

    .tabla-montos .sub {
        display: block;
        color: var(--text-2);
        font-size: .78rem;
        white-space: pre-wrap;
        margin-top: .15rem;
    }

/* ============================================================
   Lista "Necesitan tu atención" (Inicio)
   ============================================================ */
.lista-atencion { display: flex; flex-direction: column; }

/* La fila ya no es un botón: lleva dentro el enlace al documento, y un <a> dentro
   de un <button> es HTML inválido. Lo clicable es .fila-abrir, no el renglón. */
.fila-atencion {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .9rem;
    padding: .65rem 1rem;
    border-bottom: 1px solid var(--border);
    color: var(--text);
}

    .fila-atencion:last-child { border-bottom: none; }

    .fila-atencion:hover { background: var(--surface-2); }

.fila-atencion-der {
    display: flex;
    align-items: center;
    gap: .6rem;
    flex-shrink: 0;
}

.fila-folio {
    font-family: "IBM Plex Mono", ui-monospace, monospace;
    font-size: 12px;
    color: var(--text-3);
    white-space: nowrap;
}

.fila-titulo {
    font-size: .9rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.fila-cliente {
    color: var(--text-2);
    font-size: .8rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.fila-monto {
    font-family: "IBM Plex Mono", ui-monospace, monospace;
    font-variant-numeric: tabular-nums;
    font-size: .85rem;
    color: var(--text-2);
}

.estado-vacio {
    padding: 1.6rem 1rem;
    text-align: center;
    color: var(--text-2);
}

    .estado-vacio strong { color: var(--text); }

@media (max-width: 720px) {
    .fila-atencion { flex-wrap: wrap; }
    .fila-cliente { display: none; }
}

/* ============================================================
   Barra de total sticky (editor de cotización / factura)
   Regla: el total nunca sale del viewport mientras se editan partidas.
   ============================================================ */
/* El editor ocupa al menos el alto disponible y empuja la barra hasta abajo.
   Sin esto, una cotización corta dejaba la banda del total a media pantalla
   con medio viewport vacío debajo: cumplía la regla pero se veía sin terminar.
   56px = alto del header; 1.1rem = padding-top de .content. */
.editor-page {
    display: flex;
    flex-direction: column;
    min-height: calc(100vh - 56px - 1.1rem);
}

    .editor-page > .total-bar { margin-top: auto; }

.total-bar {
    position: sticky;
    bottom: 0;
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    margin: 1.25rem -1.5rem 0;
    padding: .7rem 1.5rem;
    background: var(--surface);
    border-top: 1px solid var(--border-strong);
    box-shadow: 0 -6px 18px rgba(20,30,40,.1);
}

:root[data-theme="dark"] .total-bar {
    box-shadow: 0 -6px 18px rgba(0,0,0,.45);
}

.total-cifras {
    display: flex;
    align-items: baseline;
    gap: 1.35rem;
    flex-wrap: wrap;
}

.total-item { display: flex; flex-direction: column; }

.total-lbl {
    font-size: 11px;
    color: var(--text-3);
    text-transform: uppercase;
    letter-spacing: .04em;
}

.total-num {
    font-family: "IBM Plex Mono", ui-monospace, monospace;
    font-variant-numeric: tabular-nums;
    font-size: .9rem;
    color: var(--text);
}

.total-item.es-ajuste .total-num { color: var(--warning); }

.total-item.es-total .total-lbl { font-weight: 700; color: var(--text-2); }

.total-item.es-total .total-num {
    font-size: 1.25rem;
    font-weight: 600;
}

/* ============================================================
   Banda de ajuste comercial (ámbar, motivo siempre visible)
   ============================================================ */
.banda-ajuste {
    display: flex;
    align-items: center;
    gap: .75rem;
    flex-wrap: wrap;
    padding: .6rem .9rem;
    background: var(--warning-bg);
    border: 1px solid var(--warning);
    border-radius: var(--r);
    font-size: .875rem;
    color: var(--text);
}

.banda-ajuste-pct {
    font-weight: 700;
    color: var(--warning);
    font-family: "IBM Plex Mono", ui-monospace, monospace;
    white-space: nowrap;
}

.banda-ajuste-motivo { color: var(--text-2); }

/* ============================================================
   Chips de filtro por estado (Seguimiento, listados)
   ============================================================ */
.chips {
    display: flex;
    gap: .4rem;
    flex-wrap: wrap;
    align-items: center;
}

.chip {
    padding: 4px 12px;
    border-radius: 20px;
    border: 1px solid var(--border-strong);
    background: var(--surface);
    color: var(--text-2);
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    line-height: 1.5;
}

    .chip:hover { background: var(--surface-2); color: var(--text); }

    .chip.activo {
        background: var(--primary-soft);
        border-color: var(--primary);
        color: var(--primary);
    }

/* ============================================================
   Hilo de versiones (editor) — horizontal, no un dropdown escondido
   ============================================================ */
.hilo-versiones {
    display: flex;
    align-items: center;
    gap: .35rem;
    flex-wrap: wrap;
    font-size: 12px;
}

.ver-chip {
    padding: 4px 11px;
    border: 1px solid var(--border-strong);
    border-radius: 20px;
    color: var(--text-3);
    background: var(--surface-2);
    white-space: nowrap;
}

    .ver-chip.actual {
        border-color: var(--primary);
        color: var(--primary);
        background: var(--primary-soft);
        font-weight: 700;
    }

/* ============================================================
   Popup de errores (dev)
   ============================================================ */
.error-dialog-header {
    color: var(--danger);
    font-weight: 700;
}

.error-dialog-body {
    background: #101820;
    color: #dfe6ed;
    padding: .75rem;
    border-radius: var(--r-sm);
    max-height: 320px;
    overflow: auto;
    font-family: "IBM Plex Mono", ui-monospace, monospace;
    font-size: .78rem;
    white-space: pre-wrap;
    word-break: break-word;
}

.error-dialog-foot {
    font-size: .75rem;
    margin-top: .5rem;
    color: var(--text-2);
}

/* ============================================================
   Pantalla de carga de Blazor (antes del primer render)
   ============================================================ */
#blazor-error-ui {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

    #blazor-error-ui .dismiss {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }

.blazor-error-boundary {
    background: var(--danger);
    padding: 1rem;
    color: #fff;
    border-radius: var(--r);
}

    .blazor-error-boundary::after {
        content: "Ocurrió un error.";
    }

.loading-progress {
    position: absolute;
    display: block;
    width: 8rem;
    height: 8rem;
    inset: 20vh 0 auto 0;
    margin: 0 auto 0 auto;
}

    .loading-progress circle {
        fill: none;
        stroke: var(--border);
        stroke-width: 0.6rem;
        transform-origin: 50% 50%;
        transform: rotate(-90deg);
    }

        .loading-progress circle:last-child {
            stroke: var(--primary);
            stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
            transition: stroke-dasharray 0.05s ease-in-out;
        }

.loading-progress-text {
    position: absolute;
    text-align: center;
    font-weight: bold;
    inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
}

    .loading-progress-text:after {
        content: var(--blazor-load-percentage-text, "Cargando");
    }

/* ============================================================================
   Modo documento — /cotizaciones/{id}/documento

   La hoja imprimible se pinta en CotizacionDocumento.razor.css; aquí van las
   dos cosas que un CSS scopeado NO puede tocar: el <body> y la regla @page.
   El componente prende la clase .cot-print al entrar y la apaga al salir.

   OJO: @page no se puede condicionar por selector, así que "carta sin margen"
   aplica a cualquier impresión de IO Ventas. Es lo correcto hoy — el documento
   de cotización es lo único pensado para imprimirse.
   ============================================================================ */
body.cot-print {
    /* El visor trae su propio fondo; sin esto se ve el del tema por detrás */
    background: #4b5560;
}

@media print {
    @page {
        size: letter;
        margin: 0;
    }

    html, body {
        margin: 0;
        padding: 0;
        background: #fff;
    }

    /* Barra de acciones del visor: se ve en pantalla, nunca en el PDF */
    .no-print {
        display: none !important;
    }
}

/* ============================================================
   COBRANZA DE FACTURAS (/facturas)
   El cobro es un eje aparte del estatus fiscal: periodo arriba,
   pestañas de cobro, indicadores, antigüedad y resumen por mes.
   Lo que vive DENTRO del grid está en syncfusion-theme.css — ahí
   hay que vencer el line-height y la altura que M3 impone a
   .e-rowcell, y un .razor.css scopeado no alcanza.
   ============================================================ */

/* ── Navegador de periodo ───────────────────────────────────── */
.periodo {
    display: flex;
    align-items: center;
    gap: .75rem;
    flex-wrap: wrap;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow);
    padding: .5rem .7rem;
    margin-bottom: 1rem;
}

.per-nav {
    display: flex;
    align-items: center;
    gap: .15rem;
}

.per-nav button {
    width: 30px;
    height: 30px;
    border-radius: var(--r-sm);
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text-2);
    cursor: pointer;
    font-size: 1rem;
    line-height: 1;
}

.per-nav button:hover:not(:disabled) {
    background: var(--surface-2);
    color: var(--text);
}

.per-nav button:disabled {
    opacity: .4;
    cursor: default;
}

.per-lbl {
    min-width: 9.5rem;
    text-align: center;
    font-weight: 700;
    font-size: .95rem;
    font-variant-numeric: tabular-nums;
}

/* Segmentado Mes / Año / Todo */
.seg {
    display: flex;
    border: 1px solid var(--border-strong);
    border-radius: 20px;
    overflow: hidden;
}

.seg button {
    border: 0;
    background: var(--surface);
    color: var(--text-2);
    font: inherit;
    font-size: 12px;
    font-weight: 600;
    padding: .25rem .8rem;
    cursor: pointer;
}

.seg button + button { border-left: 1px solid var(--border); }
.seg button:hover { background: var(--surface-2); color: var(--text); }

.seg button[aria-pressed="true"] {
    background: var(--primary-soft);
    color: var(--primary);
}

/* ── Pestañas de cobro ──────────────────────────────────────── */
.tabs-cobro {
    display: flex;
    gap: .2rem;
    border-bottom: 1px solid var(--border);
    margin-bottom: 1rem;
    overflow-x: auto;
}

.tabs-cobro button {
    border: 0;
    background: transparent;
    font: inherit;
    font-size: .9rem;
    font-weight: 600;
    color: var(--text-2);
    padding: .55rem .9rem;
    cursor: pointer;
    position: relative;
    display: flex;
    align-items: center;
    gap: .45rem;
    white-space: nowrap;
}

.tabs-cobro button:hover { color: var(--text); }
.tabs-cobro button[aria-selected="true"] { color: var(--primary); }

.tabs-cobro button[aria-selected="true"]::after {
    content: "";
    position: absolute;
    left: .55rem;
    right: .55rem;
    bottom: -1px;
    height: 2px;
    background: var(--primary);
    border-radius: 2px 2px 0 0;
}

.tabs-cobro .cnt {
    font-family: "IBM Plex Mono", monospace;
    font-size: .72rem;
    font-weight: 600;
    background: var(--surface-2);
    color: var(--text-2);
    border-radius: 20px;
    padding: 1px 7px;
}

.tabs-cobro button[aria-selected="true"] .cnt {
    background: var(--primary-soft);
    color: var(--primary);
}

/* Filete de color en la tarjeta que exige acción */
.kpi-card.acento-warning, .kpi-card.acento-danger {
    position: relative;
    overflow: hidden;
}

.kpi-card.acento-warning::before, .kpi-card.acento-danger::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 3px;
}

.kpi-card.acento-warning::before { background: var(--warning); }
.kpi-card.acento-danger::before  { background: var(--danger); }

/* ── Ficha en dos unidades (Facturas) ────────────────────────────────────────
   La cifra grande va sin IVA y el total con IVA queda chico en la esquina inferior
   derecha. La tarjeta se vuelve columna flex para que esa esquina se pegue ABAJO aunque
   la de al lado sea más alta (la fila es un grid y todas miden lo mismo): en flujo
   normal quedaba pegada a la línea de contexto, a media altura. Es una clase aparte y
   no .kpi-card porque Inicio y Análisis usan la tarjeta con otra estructura adentro. */
.kpi-card.kpi-con-total {
    display: flex;
    flex-direction: column;
}

.kpi-total {
    margin-top: auto;
    padding-top: .55rem;
    align-self: flex-end;
    display: inline-flex;
    align-items: baseline;
    gap: .35rem;
    white-space: nowrap;
}

.kpi-total-lbl {
    font-size: .66rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-3);
}

.kpi-total-num {
    font-family: "IBM Plex Mono", ui-monospace, monospace;
    font-variant-numeric: tabular-nums;
    font-size: .8rem;
    color: var(--text-2);
}

/* ── Recurrentes del mes (Facturas): colapsable con el total en el encabezado ──
   El encabezado es un <button> que ocupa todo el ancho de la card; el estado vive en
   aria-expanded (escrito como texto, ver la regla 4 del CLAUDE.md del cliente). */
.recurrentes .rec-head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .5rem 1rem;
    width: 100%;
    border: 0;
    border-radius: var(--r-lg);
    background: transparent;
    color: var(--text);
    font: inherit;
    text-align: left;
    padding: .7rem 1rem;
    cursor: pointer;
}

.recurrentes .rec-head:hover { background: var(--surface-2); }

.recurrentes .rec-head:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: -2px;
}

/* Abierta, el encabezado se separa del detalle con el mismo filete que
   .card-seccion-head y deja de redondear las esquinas de abajo. */
.recurrentes .rec-head[aria-expanded="true"] {
    border-bottom: 1px solid var(--border);
    border-radius: var(--r-lg) var(--r-lg) 0 0;
}

.rec-caret {
    display: inline-flex;
    color: var(--text-3);
    transition: transform .15s ease;
}

.rec-head[aria-expanded="true"] .rec-caret {
    transform: rotate(180deg);
    color: var(--primary);
}

/* Base de 12rem y no auto: con auto la base era el ancho del renglón de conteos
   ("6 de 8 facturado(s) · 2 sin facturar · 1 pagado(s)"), que en un teléfono no cabe
   junto al caret y mandaba el título a la línea siguiente, dejando el caret solo. */
.rec-titulo {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    flex: 1 1 12rem;
    min-width: 0;
}

.rec-nombre {
    font-weight: 700;
    font-size: .95rem;
}

.rec-cnt {
    font-size: .8rem;
    color: var(--text-2);
}

/* Los tres montos, con la misma tablita que .resumen-montos pero en spans: dentro de
   un <button> no puede haber bloques. Reusa .rm-lbl / .rm-num / .rm-principal. */
.rec-totales {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
    margin-left: auto;
}

.rec-tot {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    padding: .3rem .7rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    min-width: 6.5rem;
}

.rec-body { padding: .75rem 1rem 1rem; }
.rec-body .nota { margin: 0 0 .75rem; }

/* ── Filtro fiscal + montos de lo listado en un renglón, pegado al grid ──────── */
.barra-grid {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: .5rem 1rem;
}

.barra-grid .resumen-montos { margin-top: 0; }

/* Enlace dentro de un .kpi-sub que dispara una acción, no una navegación */
.link-btn {
    border: 0;
    background: transparent;
    padding: 0;
    font: inherit;
    font-weight: 600;
    color: var(--primary);
    cursor: pointer;
    text-decoration: underline;
}

.link-btn:hover { color: var(--primary-strong); }

/* ── Antigüedad del saldo ───────────────────────────────────── */
.aging {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow);
    padding: .75rem .95rem;
    display: flex;
    flex-direction: column;
    gap: .55rem;
    margin-bottom: 1.25rem;
}

.aging-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.aging-t { font-size: .85rem; font-weight: 700; }
.aging-s { font-size: .76rem; color: var(--text-3); }

.aging-bar {
    display: flex;
    height: 9px;
    border-radius: 20px;
    overflow: hidden;
    background: var(--surface-2);
}

.aging-bar span { display: block; }

.aging-legend {
    display: flex;
    flex-wrap: wrap;
    gap: .15rem 1.1rem;
}

.aging-legend div {
    display: flex;
    align-items: center;
    gap: .4rem;
    font-size: .76rem;
    color: var(--text-2);
}

.aging-legend i {
    width: 9px;
    height: 9px;
    border-radius: 2px;
    flex: none;
}

.aging-legend b {
    font-family: "IBM Plex Mono", monospace;
    color: var(--text);
    font-weight: 600;
}

/* Etiqueta que nombra para qué es una fila de chips */
.chips-et {
    font-size: .72rem;
    color: var(--text-3);
    text-transform: uppercase;
    letter-spacing: .05em;
    font-weight: 700;
    margin-right: .15rem;
}

/* ── Confirmación contra el banco ───────────────────────────────
   Capturado no es confirmado: hasta que el depósito aparece en el
   estado de cuenta de IO Admin, el cobro está dicho pero no probado. */
.conf-ok, .conf-pend {
    font-size: .68rem;
    font-weight: 700;
    font-style: normal;
    white-space: nowrap;
}

.conf-ok { color: var(--success); }
.conf-pend { color: var(--warning); }

/* ── Resumen por mes ────────────────────────────────────────── */
.resumen-meses {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow);
    overflow: hidden;
    margin-top: 1.5rem;
}

.resumen-meses-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: .65rem .95rem;
    border-bottom: 1px solid var(--border);
    font-weight: 700;
    font-size: .9rem;
    flex-wrap: wrap;
}

.resumen-meses-head .s {
    font-weight: 500;
    font-size: .78rem;
    color: var(--text-3);
}

/* La tabla se desborda sola en móvil sin arrastrar la página con ella */
.resumen-meses-scroll { overflow-x: auto; }

.tabla-meses {
    width: 100%;
    border-collapse: collapse;
    font-size: .82rem;
    min-width: 480px;
}

.tabla-meses th {
    text-align: left;
    font-size: .72rem;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-3);
    font-weight: 700;
    padding: .45rem .8rem;
    border-bottom: 1px solid var(--border);
}

.tabla-meses th.money { text-align: right; }

.tabla-meses td {
    padding: .4rem .8rem;
    border-bottom: 1px solid var(--border);
    color: var(--text-2);
}

.tabla-meses td.money {
    text-align: right;
    font-family: "IBM Plex Mono", monospace;
    font-variant-numeric: tabular-nums;
}

.tabla-meses td:first-child {
    font-weight: 600;
    color: var(--text);
}

.tabla-meses tbody tr { cursor: pointer; }
.tabla-meses tbody tr:hover td { background: var(--surface-2); }
.tabla-meses tbody tr.sel td,
.tabla-meses tbody tr.sel:hover td { background: var(--primary-soft); }

.tabla-meses td.pend { color: var(--text); font-weight: 600; }
.tabla-meses td.cero { color: var(--text-3); }

.pct {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    justify-content: flex-end;
}

.pct-track {
    width: 56px;
    height: 6px;
    border-radius: 20px;
    background: var(--surface-3);
    overflow: hidden;
    flex: none;
}

.pct-track i {
    display: block;
    height: 100%;
    background: var(--success);
}

.pct-track i.parcial { background: var(--warning); }

/* ── Modal de cobro ─────────────────────────────────────────── */
/* El título del diálogo lo arma Blazored y no lo recorta: con un cliente de
   nombre largo empujaba la × fuera de la caja. Aquí se le pone el freno.
   (Blazored no fija white-space ni overflow, así que no hay pelea de reglas.) */
.bm-title {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.cobro-cargando { padding: 1.5rem; }

.cobro-modal {
    display: flex;
    flex-direction: column;
    gap: .9rem;
}

/* auto-fit y no repeat(3): el ancho que manda aquí es el del MODAL, no el de la
   ventana. Con tres columnas fijas dentro de un diálogo angosto los importes se
   salían de su cajita aunque la pantalla fuera de 1280px, y el @media de abajo
   —que mira el viewport— nunca llegaba a dispararse. */
.cobro-saldos {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
    gap: .5rem;
}

.cobro-saldos > div {
    background: var(--surface-2);
    border-radius: var(--r);
    padding: .45rem .6rem;
    display: flex;
    flex-direction: column;
    gap: .1rem;
    min-width: 0;
}

.cobro-lbl {
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-3);
    font-weight: 700;
}

.cobro-num {
    font-family: "IBM Plex Mono", monospace;
    font-size: .9rem;
    font-weight: 600;
    color: var(--text-2);
}

.cobro-saldos .es-saldo .cobro-num {
    color: var(--text);
    font-size: 1.05rem;
}

/* Flex y no grid, por la misma razón: los dos campos se reparten lo que haya y
   bajan de renglón solos cuando el modal es angosto, sin depender del viewport. */
.cobro-captura {
    display: flex;
    flex-wrap: wrap;
    gap: .7rem;
    align-items: end;
}

    .cobro-captura > div {
        flex: 1 1 9rem;
        min-width: 0;
    }

.cobro-lista {
    border: 1px solid var(--border);
    border-radius: var(--r);
    overflow: hidden;
}

.cobro-lista-head {
    padding: .35rem .6rem;
    background: var(--surface-2);
    font-size: .72rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-3);
}

.cobro-fila {
    display: flex;
    align-items: center;
    gap: .6rem;
    padding: .35rem .6rem;
    font-size: .8rem;
}

.cobro-fila + .cobro-fila { border-top: 1px solid var(--border); }

.cobro-fecha {
    font-family: "IBM Plex Mono", monospace;
    color: var(--text-2);
}

/* El monto empuja al botón de quitar hasta la orilla */
.cobro-monto {
    font-family: "IBM Plex Mono", monospace;
    color: var(--text);
    font-weight: 600;
    margin-left: auto;
}

.cobro-quitar {
    border: 0;
    background: transparent;
    color: var(--danger);
    font-size: .75rem;
    font-weight: 700;
    cursor: pointer;
    padding: 0;
}

.cobro-quitar:disabled { opacity: .5; cursor: default; }
.cobro-quitar-no { color: var(--text-3); font-size: .75rem; }

.cobro-sin-desglose { background: var(--surface-2); }

.cobro-pie {
    margin: 0;
    font-size: .76rem;
    line-height: 1.45;
    color: var(--text-2);
}

.cobro-pie strong { color: var(--text); }

.cobro-acciones {
    display: flex;
    justify-content: flex-end;
    gap: .5rem;
}

/* ── Revisión contra el banco (modal) ───────────────────────────
   Tabla-lista, no <table>: cada renglón lleva varios depósitos apilados
   y una <table> obligaría a rowspan para lo mismo. */
.rev-banco {
    display: flex;
    flex-direction: column;
    gap: .9rem;
}

.rev-alcance {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .6rem;
}

.rev-alcance-s {
    font-size: .78rem;
    color: var(--text-3);
}

.rev-lista {
    border: 1px solid var(--border);
    border-radius: var(--r);
    overflow: hidden;
    /* El detalle del depósito puede ser largo: que scrollee la lista y no el modal. */
    max-height: 22rem;
    overflow-y: auto;
}

.rev-fila {
    display: grid;
    grid-template-columns: 1.6rem minmax(8rem, 1.1fr) minmax(9rem, 1.6fr) 6.5rem 7.5rem;
    gap: .5rem;
    align-items: start;
    padding: .45rem .6rem;
    font-size: .8rem;
}

.rev-fila + .rev-fila { border-top: 1px solid var(--border); }
.rev-fila.sel { background: var(--surface-2); }

.rev-head {
    position: sticky;
    top: 0;
    z-index: 1;
    background: var(--surface-2);
    font-size: .7rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--text-3);
}

.rev-chk { display: flex; align-items: center; }

.rev-fac {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    min-width: 0;
}

.rev-cliente {
    color: var(--text);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.rev-sub {
    font-size: .72rem;
    color: var(--text-3);
    font-family: "IBM Plex Mono", monospace;
}

.rev-dep {
    display: flex;
    flex-direction: column;
    gap: .15rem;
    min-width: 0;
}

.rev-dep-fila {
    display: flex;
    gap: .45rem;
    align-items: baseline;
    min-width: 0;
}

.rev-dep-fecha,
.rev-dep-monto {
    font-family: "IBM Plex Mono", monospace;
    color: var(--text-2);
    white-space: nowrap;
}

.rev-dep-monto { font-weight: 600; color: var(--text); }

.rev-dep-desc {
    color: var(--text-3);
    font-size: .74rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.rev-num {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: .1rem;
    font-family: "IBM Plex Mono", monospace;
    text-align: right;
}

.rev-head .rev-num { font-family: inherit; }

/* Depósitos pendientes dentro del modal de cobro de UNA factura */
.cobro-banco { border-color: var(--success); }
.cobro-banco .cobro-lista-head { color: var(--success); }

/* .cobro-captura y .cobro-saldos ya no necesitan @media: se adaptan al ancho del
   modal por sí solas (flex-wrap / auto-fit). Aquí solo queda lo que sí depende
   del viewport. */
@media (max-width: 620px) {
    .per-lbl { min-width: 7.5rem; }

    /* En angosto la rejilla de 5 columnas no cabe: cada renglón se vuelve bloque. */
    .rev-fila {
        grid-template-columns: 1.6rem 1fr;
    }

    .rev-head { display: none; }

    .rev-fila .rev-dep,
    .rev-fila .rev-num {
        grid-column: 2;
        align-items: flex-start;
        text-align: left;
    }
}

/* ── Confirmación de acciones que no se deshacen ────────────────
   Compartido por borrar un borrador de factura y borrar un cliente. */
.confirmar-modal {
    display: flex;
    flex-direction: column;
    gap: .85rem;
}

.confirmar-texto {
    margin: 0;
    font-size: .95rem;
    line-height: 1.5;
    color: var(--text);
}

.confirmar-detalle {
    margin: 0;
    font-family: "IBM Plex Mono", ui-monospace, Consolas, monospace;
    font-size: .85rem;
    color: var(--text-2);
    background: var(--surface-2);
    border-radius: 8px;
    padding: .55rem .7rem;
    /* Un folio largo o una razón social no deben ensanchar el diálogo. */
    overflow-wrap: anywhere;
}

.confirmar-acciones {
    display: flex;
    justify-content: flex-end;
    gap: .5rem;
    /* En pantallas angostas los dos botones caben apilados antes que salirse. */
    flex-wrap: wrap;
}

/* ── Modal de alta/edición de cliente ───────────────────────────
   El .form-grid.dos ya colapsa a una columna en pantallas angostas; aquí solo
   va lo propio del diálogo. */
.cliente-modal {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.cliente-modal-acciones {
    display: flex;
    justify-content: flex-end;
    gap: .5rem;
    flex-wrap: wrap;
}

/* Bloque "Servicio de timbrado": separado del resto porque casi ningún cliente lo usa. */
.cliente-timbrado {
    border-top: 1px solid var(--border);
    padding-top: .85rem;
}

.cliente-timbrado-titulo {
    font-weight: 600;
    font-size: .85rem;
    margin-bottom: .6rem;
}

/* ── El diálogo va SIEMPRE encima del indicador de carga ────────
   Blazored apila su contenedor en z-index 1000 y el overlay de pantalla completa
   (.io-loading--fixed) en 2000, así que una carga que se quedara puesta tapaba el
   cuadro de confirmación: se veían los botones detrás del velo y no se podían
   pulsar (pasó al borrar un borrador desde Facturas, 2026-09-02).

   La causa de fondo era otra —faltaba un StateHasChanged() para apagar el overlay
   antes de abrir el cuadro, ya corregida— pero el orden se fija igual: un diálogo
   pide una decisión y un spinner solo informa. Si los dos coinciden, el que manda
   es el que necesita el clic; un spinner encima de un diálogo deja al usuario
   atorado sin salida. */
body .bm-container { z-index: 2100; }

/* ── Caja del diálogo (Blazored) ────────────────────────────────
   Blazored pinta .blazored-modal con un BLANCO fijo, propio de su CSS. En tema
   oscuro eso dejaba el diálogo blanco con el texto claro de la app encima:
   medido, 1.17:1 entre el valor de un campo y su fondo — invisible. Y no se
   notaba en una captura pequeña, porque el velo oscuro de atrás engaña a la
   vista; se descubrió midiendo el contraste, no mirando.

   La caja sigue el tema como cualquier otra superficie de la app. Aplica a TODOS
   los modales (cliente, recurrente, partida, concepto, confirmar…). */
/* El selector va con `body` a propósito. Blazored trae su CSS SCOPEADO
   (.blazored-modal[b-mwsrhhqq2g]) dentro de IOVentas.Client.styles.css, que carga
   DESPUÉS de app.css: con un `.blazored-modal` a secas la regla empata en peso y
   pierde por orden, y el diálogo se quedaba blanco sin que nada lo delatara.
   `body .bm-container .blazored-modal` pesa más que el par clase+atributo y gana
   sin recurrir a !important. */
body .bm-container .blazored-modal {
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
    box-shadow: var(--shadow-lg);
}

/* La × de cerrar viene en negro fijo, del mismo CSS que asumía diálogo blanco:
   sobre la superficie oscura medía 1.24:1 y no se veía. Sigue al texto tenue. */
body .bm-container .bm-close {
    color: var(--text-2);
    background: transparent;
    opacity: 1;
}

    body .bm-container .bm-close:hover,
    body .bm-container .bm-close:focus-visible {
        color: var(--text);
    }

/* ── Encabezado de los modales (Blazored) ───────────────────────
   El .bm-header es flex con el título y la ×; en pantallas angostas un título
   largo ("Editar: <razón social>") empuja la × fuera de la caja. El título se
   encoge y recorta, y la × conserva su ancho: nunca debe quedar inalcanzable. */
.bm-header {
    align-items: center;
    gap: .5rem;
}

.bm-title {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.bm-close { flex: 0 0 auto; }

/* ── Columna "Facturado" del listado de cotizaciones ────────────
   Conteo arriba (facturas emitidas / exhibiciones planeadas) y el porcentaje
   del monto abajo. El "?" del denominador es intencional: marca que faltan
   capturar las exhibiciones. */
.fact-avance {
    display: flex;
    flex-direction: column;
    line-height: 1.25;
}

.fact-conteo {
    font-family: "IBM Plex Mono", ui-monospace, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: .82rem;
    color: var(--text);
}

.fact-pct {
    font-size: .72rem;
    color: var(--text-2);
}

/* Falta facturar */
.fact-pct.es-parcial { color: var(--warning); font-weight: 600; }
/* Más facturado que cotizado: siempre es algo que revisar */
.fact-pct.es-exceso  { color: var(--danger);  font-weight: 600; }

/* ── Cartera antigua (proyección de cobro) ──────────────────────
   <details> colapsado: el dinero viejo del histórico no debe inflar el número
   principal, pero tampoco desaparecer. */
.cartera-antigua {
    margin-top: 1.5rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r);
    box-shadow: var(--shadow);
}

.cartera-antigua > summary {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: .25rem .6rem;
    padding: .85rem 1rem;
    cursor: pointer;
    /* El marcador nativo no se alinea con un summary flex en todos los navegadores. */
    list-style-position: inside;
}

.cartera-antigua > summary:hover { background: var(--surface-2); }
.cartera-antigua[open] > summary { border-bottom: 1px solid var(--border); }

.ca-titulo {
    font-weight: 700;
    color: var(--text);
}

.ca-resumen {
    font-size: .82rem;
    color: var(--text-2);
}

.ca-resumen strong {
    font-family: "IBM Plex Mono", ui-monospace, Consolas, monospace;
    color: var(--text);
}

.cartera-antigua > .nota,
.cartera-antigua > .tabla-scroll { padding: 0 1rem; }
.cartera-antigua > .tabla-scroll { padding-bottom: 1rem; }

/* ── Cobro esperado (Proyección) ─────────────────────────────────
   El mes en curso ocupa tres columnas (pendiente / ya cobrado / total) y lleva un fondo
   apenas tintado para que se lea como un bloque; los meses siguientes, una sola. */
.cobro-esperado { margin-bottom: 1.5rem; }

.cobro-esperado-iva {
    color: var(--text-2);
    font-size: .78rem;
    font-weight: 400;
}

.tabla-cobro .col-origen { min-width: 15rem; }

/* Encabezados en la letra de la interfaz: el .money global los ponía en mono, y "octubre"
   salía en otra letra que "septiembre" justo al lado. El mono es para la cifra, no el título. */
.tabla-cobro th { font-family: inherit; }
.tabla-cobro td.col-origen .sub { max-width: 22rem; }

.tabla-cobro th.col-actual-grupo { text-align: center; }

.tabla-cobro th.col-actual,
.tabla-cobro td.col-actual { background: var(--primary-soft); }

.tabla-cobro td.col-actual-total { font-weight: 700; }

/* La última columna del mes en curso separa lo que ya es del mes de lo que viene. */
.tabla-cobro th.col-actual-grupo,
.tabla-cobro .col-actual-total { border-right: 1px solid var(--border-strong); }

/* El pipeline es referencia: mismo renglón, menos peso, y una raya que lo separa del total. */
.tabla-cobro tr.fila-referencia td {
    color: var(--text-2);
    font-style: italic;
    border-top: 2px solid var(--border);
}
.tabla-cobro tr.fila-referencia td.col-actual { background: transparent; }

/* En el celular caben el nombre del renglón y una columna de montos: al deslizar hacia los
   otros meses, el nombre se perdía y ya no se sabía qué cifra era cuál. Se queda fijo a la
   izquierda, más angosto, y sin la nota de abajo (el nombre basta para leer la cifra). */
@media (max-width: 760px) {
    .tabla-cobro .col-origen {
        min-width: 8.5rem;
        position: sticky;
        left: 0;
        z-index: 1;
        background: var(--surface);
        box-shadow: 1px 0 0 var(--border);
    }
    .tabla-cobro th.col-origen { background: var(--surface-2); }
    .tabla-cobro td.col-origen .sub { display: none; }
}

/* ── Claves del SAT: buscador + alta bajo demanda ───────────────
   El enlace vive pegado bajo su combo, en tamaño chico: es la excepción, no
   la acción principal. */
.clave-nueva {
    background: none;
    border: 0;
    padding: 0;
    margin-top: .3rem;
    font-size: .74rem;
    color: var(--primary);
    cursor: pointer;
}

.clave-nueva:hover { color: var(--primary-strong); text-decoration: underline; }

.clave-nueva-modal {
    display: flex;
    flex-direction: column;
    gap: .85rem;
}

/* ── Evidencia de timbrado: botón </> y visor de código ─────────
   El botón vive en el renglón junto a Abrir; se ve como código a propósito
   para que se lea como "algo técnico", no como una acción de negocio. */
.btn-codigo {
    font-family: "IBM Plex Mono", ui-monospace, Consolas, monospace;
    font-size: .72rem;
    line-height: 1;
    padding: .32rem .4rem;
    background: var(--surface-2);
    color: var(--text-2);
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    cursor: pointer;
}

.btn-codigo:hover {
    color: var(--primary);
    border-color: var(--primary);
}

/* Botones de ícono de la columna de acciones: cobro, descargar XML + PDF, abrir en otra
   pestaña y duplicar. Misma caja que .btn-codigo para que los cinco iconos de la columna se
   vean del mismo tamaño; cambia el interior, que aquí es un SVG y hay que centrarlo (un
   inline-svg se apoya en la línea base y queda descuadrado).

   .btn-abrir es un <a> y no un <button>: con target="_blank" Blazor no lo intercepta y abre
   pestaña de verdad, así que el clic con rueda y el menú contextual también funcionan. El
   precio es que arrastra las reglas globales de enlace, que lo pintarían del color primario;
   por eso entra a este grupo, que le fija el color de los demás iconos. */
.btn-cobro,
.btn-descarga,
.btn-abrir,
.btn-duplicar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: .82rem;
    line-height: 1;
    padding: .3rem .42rem;
    background: var(--surface-2);
    color: var(--text-2);
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    cursor: pointer;
}

/* El subrayado del hover global (a:hover) gana por especificidad a un .btn-abrir solo, y aquí
   dentro no hay texto que subrayar —solo el ícono—: la raya sale debajo de la caja. */
.btn-abrir,
.btn-abrir:hover { text-decoration: none; }

.btn-cobro:hover:not(:disabled),
.btn-descarga:hover:not(:disabled),
.btn-abrir:hover,
.btn-duplicar:hover:not(:disabled) {
    color: var(--primary);
    border-color: var(--primary);
}

/* Mientras bajan los dos archivos, o mientras se crea el duplicado. Sin esto el botón
   deshabilitado se ve idéntico al activo y el usuario vuelve a picarle. */
.btn-descarga:disabled,
.btn-duplicar:disabled {
    opacity: .5;
    cursor: default;
}

.intentos { display: flex; flex-direction: column; gap: .6rem; }

.intento {
    border: 1px solid var(--border);
    border-left: 3px solid var(--border-strong);
    border-radius: var(--r);
    overflow: hidden;
}

.intento.es-ok    { border-left-color: var(--success); }
.intento.es-error { border-left-color: var(--danger); }

.intento-head {
    width: 100%;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .5rem;
    padding: .6rem .75rem;
    background: var(--surface-2);
    border: 0;
    cursor: pointer;
    text-align: left;
    color: var(--text);
}

.intento-head:hover { background: var(--surface-3); }

.intento-fecha {
    font-family: "IBM Plex Mono", ui-monospace, Consolas, monospace;
    font-size: .8rem;
}

.intento-amb, .intento-ms { font-size: .72rem; color: var(--text-2); }

.intento-cod {
    font-size: .74rem;
    padding: .1rem .35rem;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
}

/* Empuja el chevron a la derecha sin depender del número de elementos previos. */
.intento-chevron { margin-left: auto; color: var(--text-3); }

.intento-cuerpo {
    padding: .75rem;
    display: flex;
    flex-direction: column;
    gap: .7rem;
}

.intento-msg {
    font-size: .85rem;
    padding: .5rem .6rem;
    border-radius: var(--r-sm);
    background: var(--surface-2);
    overflow-wrap: anywhere;
}

.intento-msg.es-error {
    background: var(--danger-bg);
    color: var(--danger);
    font-weight: 600;
}

.intento-uuid, .intento-meta {
    font-size: .76rem;
    color: var(--text-2);
    font-family: "IBM Plex Mono", ui-monospace, Consolas, monospace;
}

.bloque-codigo {
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    overflow: hidden;
}

.bloque-codigo-head {
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .35rem .6rem;
    background: var(--surface-2);
    border-bottom: 1px solid var(--border);
}

.bloque-codigo-titulo { font-size: .78rem; font-weight: 600; }
.bloque-codigo-tam { font-size: .7rem; color: var(--text-3); margin-left: auto; }
.bloque-codigo-vacio { margin: 0; padding: .6rem; font-size: .8rem; color: var(--text-3); }

.bloque-codigo-cuerpo {
    margin: 0;
    padding: .6rem;
    max-height: 300px;
    /* El scroll vive aquí: un JSON de una sola línea no debe estirar el modal. */
    overflow: auto;
    background: var(--surface);
    font-family: "IBM Plex Mono", ui-monospace, Consolas, monospace;
    font-size: .74rem;
    line-height: 1.5;
    color: var(--text-2);
    white-space: pre-wrap;
    overflow-wrap: anywhere;
}

/* ── Modal de alta/edición de servicio recurrente ───────────────
   Son once campos en rejilla auto-fit; el numero de columnas lo decide el ancho
   del diálogo, no el del viewport. */
/* Cuerpo y pie de los modales de captura. La lista de selectores crece con cada modal
   nuevo (partida, concepto, tarifa, perfil, usuario comparten .partida-modal): es el mismo
   layout de siempre —campos apilados y botones a la derecha— y separarlo por pantalla solo
   multiplicaría reglas idénticas. */
.recurrente-modal,
.partida-modal {
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

.recurrente-modal-acciones,
.partida-modal-acciones {
    display: flex;
    justify-content: flex-end;
    gap: .5rem;
    flex-wrap: wrap;
    /* Pegado al fondo del diálogo: en un celular el formulario mide más que la
       ventana, y con el pie normal "Aceptar" se iba con el scroll — había que
       adivinar que seguía ahí abajo. El fondo es obligatorio: sin él, los campos
       se transparentan por debajo de los botones. */
    position: sticky;
    bottom: 0;
    background: var(--surface);
    padding-top: .7rem;
}

/* Campo cuyo input lleva ancho fijo (ej. "Día 1–28" a 90px). Los demás campos usan
   inputs al 100%, que empujan la etiqueta de línea y la hacen VERSE apilada aunque
   .form-label sea inline-block; con un input angosto la etiqueta se le pega al lado. */
.campo-angosto .form-label { display: block; }


/* ============================================================
   Formulario del recurrente: rejilla de 6 columnas
   ============================================================
   Seis y no cuatro para poder repartir por mitades (3+3) y tercios (2+2+2) en la
   misma rejilla. Con auto-fit todos los campos medían igual y los dos textos largos
   —concepto y clave del SAT— quedaban tan angostos como el día de generación. */
/* auto-fit y NO columnas fijas con span: dentro de un modal el número de columnas lo
   decide el ancho del DIÁLOGO, no el del viewport, así que un @media miente y un
   @container obliga a containment que aquí rompió el reparto (los items con
   "span 3" salían de 92px dentro de una rejilla de 750px).
   Con minmax(300px) el diálogo de 750px da DOS columnas de ~360px: el doble de los
   ~210px que tenían antes el concepto y la clave, que es justo lo que se pidió. */
.recurrente-form {
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    /* Más aire vertical que horizontal: cada campo lleva su línea de ayuda debajo,
       y con el gap parejo de .8rem el bloque se leía como un muro. */
    gap: 1.1rem .9rem;
}

/* Los que ocupan la fila entera: la decisión de timbrado (que merece su marco), el
   renglón de importes y las notas. 1/-1 y no un span fijo, porque con auto-fit el
   número de columnas cambia y un span de 3 dejaría la caja colgando. */
.recurrente-form .col-full { grid-column: 1 / -1; }

/* Monto, IVA y Total juntos en una sola fila: se leen como una operación, no como
   tres campos sueltos que hay que ir sumando con la vista. */
.recurrente-importes {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: .9rem;
    align-items: end;
}

/* Texto de ayuda bajo un campo. NO usa .label-sm: esa clase es MAYÚSCULAS con
   letter-spacing, pensada para etiquetas de dos palabras — una frase entera en
   versalitas se lee como un letrero de advertencia y encima ocupa dos renglones. */
.campo-ayuda {
    display: block;
    margin-top: .3rem;
    font-size: 12px;
    line-height: 1.4;
    color: var(--text-3);
}

/* La descripción oficial de la clave del SAT: es un DATO, no una explicación, así que
   se distingue del resto de las ayudas. */
.campo-ayuda-dato { color: var(--text-2); font-weight: 500; }
.campo-ayuda-alerta { color: var(--warning); }

/* Casilla con su etiqueta al lado, alineada a la altura de un input para que no
   flote a media fila junto a los campos vecinos. */
.campo-check {
    display: flex;
    align-items: center;
    min-height: 34px;
}

/* Bloque enmarcado para la decisión que emite CFDIs sola. */
.campo-destacado {
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: .7rem .9rem;
    background: var(--surface-2);
}

.campo-destacado.es-alerta {
    border-color: var(--warning);
    background: var(--warning-bg);
}

/* Dos inputs angostos lado a lado (día y mes). */
.campo-par { display: flex; gap: .5rem; }

/* Valor calculado que no se captura. Se ve como campo para alinear con sus vecinos,
   pero sin borde de input: si pareciera editable, alguien intentaría corregir el IVA
   ahí en vez de ajustar el monto. */
.campo-lectura {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    min-height: 34px;
    padding: 0 .7rem;
    border-radius: 6px;
    background: var(--surface-2);
    color: var(--text-2);
    font-size: .95rem;
}

.campo-lectura-total {
    color: var(--text);
    font-weight: 600;
    background: var(--primary-soft);
}


/* ============================================================
   Drill-down: ver de qué cotizaciones habla un número
   ============================================================ */

/* ── Botón de ícono en celda de grid (ver documento) ───────────
   Cuadrado de 28px para que el blanco de clic no dependa del trazo del ícono. */
.btn-icono {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    flex: 0 0 auto;
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--text-2);
    cursor: pointer;
    padding: 0;
    text-decoration: none;
}

.btn-icono:hover {
    background: var(--surface-2);
    color: var(--primary);
    border-color: var(--border-strong);
}

.btn-icono:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 1px;
}

/* ── Celda "Facturado" clicable ────────────────────────────────
   Se ve como dato, no como botón, hasta que el puntero encima delata que
   se puede abrir: la columna se lee mucho más de lo que se pulsa. */
.fact-drill {
    border: 0;
    background: none;
    padding: 0;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
    width: 100%;
    border-radius: var(--r-sm);
}

.fact-drill:hover .fact-conteo { color: var(--primary); text-decoration: underline; }

.fact-drill:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

/* ── KPI que se abre (Inicio) ──────────────────────────────────
   La tarjeta entera es el botón. El signo de la derecha dice si hay algo
   que abrir y si ya está abierto. */
.kpi-card.kpi-drill {
    display: block;
    width: 100%;
    text-align: left;
    font: inherit;
    color: inherit;
    cursor: pointer;
}

.kpi-card.kpi-drill:hover { border-color: var(--primary); }

.kpi-card.kpi-drill:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
}

/* Sin cotizaciones detrás no hay nada que abrir: se apaga y deja de invitar al clic. */
.kpi-card.kpi-drill:disabled {
    cursor: default;
    opacity: .75;
}

.kpi-card.kpi-drill:disabled:hover { border-color: var(--border); }

.kpi-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
}

.kpi-caret {
    color: var(--text-3);
    flex: 0 0 auto;
    transition: transform .15s ease;
}

.kpi-card.abierto .kpi-caret { transform: rotate(180deg); color: var(--primary); }

.kpi-card.abierto { border-color: var(--primary); }

/* ── Panel que despliega el KPI ────────────────────────────────
   Va bajo la fila completa, no dentro de la tarjeta: en la retícula de tres
   columnas una lista dentro de una tarjeta queda de 190px de ancho. */
.kpi-detalle {
    background: var(--surface);
    border: 1px solid var(--primary);
    border-radius: var(--r-lg);
    box-shadow: var(--shadow);
    margin: -.5rem 0 1.25rem;
    overflow: hidden;
}

.kpi-detalle-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    padding: .6rem 1rem;
    border-bottom: 1px solid var(--border);
    background: var(--surface-2);
    font-size: .85rem;
    font-weight: 600;
}

.kpi-detalle-head .kpi-detalle-sub {
    font-weight: 400;
    color: var(--text-2);
}

/* El panel no empuja la lista de atención fuera de la pantalla: con 40 enviadas
   en la calle, la sección de abajo quedaba a tres scrolls del indicador que la abrió. */
.kpi-detalle .lista-atencion {
    max-height: 26rem;
    overflow-y: auto;
}

/* La fila de la lista deja de ser un botón entero para poder llevar acciones
   propias dentro: el <a> del documento no puede vivir dentro de un <button>. */
.fila-atencion-acciones {
    display: flex;
    align-items: center;
    gap: .35rem;
    flex: 0 0 auto;
}

.fila-abrir {
    display: flex;
    align-items: center;
    gap: .75rem;
    min-width: 0;
    flex: 1 1 auto;
    border: 0;
    background: none;
    padding: 0;
    font: inherit;
    color: inherit;
    text-align: left;
    cursor: pointer;
}

.fila-abrir:hover .fila-titulo { text-decoration: underline; }

.fila-abrir:focus-visible {
    outline: 2px solid var(--primary);
    outline-offset: 2px;
    border-radius: var(--r-sm);
}

/* ============================================================
   Modal: facturas de una cotización
   ============================================================ */
.fact-cot-cab {
    display: flex;
    align-items: baseline;
    gap: .6rem;
    margin: 0 0 .75rem;
    padding-bottom: .6rem;
    border-bottom: 1px solid var(--border);
}

.fact-cot-proyecto {
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.fact-cot-tabla {
    width: 100%;
    border-collapse: collapse;
    font-size: .85rem;
}

.fact-cot-tabla th {
    text-align: left;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-3);
    font-weight: 600;
    padding: .3rem .5rem;
    border-bottom: 1px solid var(--border);
}

.fact-cot-tabla td {
    padding: .45rem .5rem;
    border-bottom: 1px solid var(--border);
    vertical-align: middle;
}

.fact-cot-tabla .num { text-align: right; }

.fact-cot-tabla td.num {
    font-family: "IBM Plex Mono", ui-monospace, Consolas, monospace;
    font-variant-numeric: tabular-nums;
}

/* La cancelada existió pero no cuenta: se ve, atenuada y tachada en el monto. */
.fact-cot-tabla tr.es-cancelada td { color: var(--text-3); }
.fact-cot-tabla tr.es-cancelada td.num { text-decoration: line-through; }

.fact-cot-cuadre {
    display: flex;
    flex-wrap: wrap;
    gap: 1.25rem;
    margin-top: .9rem;
    padding: .7rem .9rem;
    background: var(--surface-2);
    border-radius: var(--r);
}

.fact-cot-cuadre > div {
    display: flex;
    flex-direction: column;
    gap: .15rem;
}

.fact-cot-cuadre span {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-3);
    font-weight: 600;
}

.fact-cot-cuadre strong {
    font-family: "IBM Plex Mono", ui-monospace, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: .95rem;
}

.fact-cot-cuadre .es-parcial strong { color: var(--warning); }
.fact-cot-cuadre .es-exceso strong  { color: var(--danger); }
.fact-cot-cuadre .es-cuadrado strong { color: var(--success); }

.fact-cot-nota {
    margin: .6rem 0 0;
    font-size: .78rem;
    color: var(--text-2);
}


/* ============================================================
   Tablero del Inicio: franjas con desglose
   ============================================================ */
.franja { margin-bottom: 1.5rem; }

.franja-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: .55rem;
}

.franja-head h2 {
    margin: 0;
    font-size: .82rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--text-2);
}

.franja-head a { font-size: .82rem; font-weight: 600; }

/* La franja arrima sus tarjetas al panel que abren; el margen de .kpi-row de
   pantallas viejas dejaba un hueco entre la tarjeta y su propio detalle. */
.franja .kpi-row { margin-bottom: .75rem; }

/* ── Desglose de montos (componente Desglose.razor) ────────────
   Una sola definición para la tarjeta y para el renglón: antes cada uno
   pintaba lo suyo y acabaron distintos —el renglón perdió el IVA y cambiaba
   de orden según si la factura estaba cobrada—. Solo cambian los tamaños. */
.dg { margin-top: .1rem; }

.dg-sub {
    display: flex;
    align-items: baseline;
    gap: .4rem;
}

.dg-num, .dg-linea {
    font-family: "IBM Plex Mono", ui-monospace, Consolas, monospace;
    font-variant-numeric: tabular-nums;
}

.dg-num {
    font-weight: 600;
    font-size: 1.35rem;
    color: var(--text);
    line-height: 1.15;
}

.dg-linea {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .6rem;
    margin-top: .2rem;
}

/* El IVA se entera a Hacienda: se ve, pero no compite. */
.dg-iva {
    font-size: .74rem;
    color: var(--text-3);
}

/* El Total en su propio color y en negritas: es la cifra que se cobra. */
.dg-total {
    font-size: .88rem;
    font-weight: 700;
    color: var(--primary);
}

.dg-lbl {
    font-family: "Public Sans", system-ui, sans-serif;
    font-size: 10px;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--text-3);
    font-weight: 600;
}

/* Versión de renglón: misma jerarquía, un escalón más chica y a la derecha.
   Ancho fijo para que la columna de cifras quede a plomo en toda la lista. */
.dg-mini {
    flex: 0 0 auto;
    min-width: 190px;
    margin-top: 0;
}

.dg-mini .dg-sub { justify-content: flex-end; }
.dg-mini .dg-num { font-size: .92rem; }
.dg-mini .dg-linea { justify-content: flex-end; gap: .75rem; margin-top: .05rem; }
.dg-mini .dg-iva { font-size: .7rem; }
.dg-mini .dg-total { font-size: .76rem; }

/* El tinte semántico va solo en el Subtotal y en la nota: pintar los tres
   montos del mismo color los vuelve indistinguibles entre sí. */
.kpi-card.tono-success .dg-num, .kpi-card.tono-success .kpi-sub { color: var(--success); }
.kpi-card.tono-warning .dg-num, .kpi-card.tono-warning .kpi-sub { color: var(--warning); }
.kpi-card.tono-danger  .dg-num, .kpi-card.tono-danger  .kpi-sub { color: var(--danger); }

/* Badge y saldo juntos, FUERA del bloque de montos: un dato que solo existe en
   algunas filas no puede vivir en la columna de cifras sin desalinearla. */
.fila-estado {
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    gap: .15rem;
    flex: 0 0 auto;
    min-width: 92px;
}

/* Saldo abierto de una factura: es lo único rojo del renglón porque es lo único
   que exige una llamada. */
.fila-saldo {
    font-family: "IBM Plex Mono", ui-monospace, Consolas, monospace;
    font-variant-numeric: tabular-nums;
    font-size: .8rem;
    font-weight: 600;
    color: var(--warning);
    flex: 0 0 auto;
}

@media (max-width: 720px) {
    .dg-mini { min-width: 0; }
}

/* ── Liga factura ↔ cotización (editor de factura) ──────────────
   El selector se lleva el ancho y el botón queda fijo al lado: el texto de una
   cotización trae folio, proyecto, cliente y total, y con el combo a la mitad
   se cortaba justo en el proyecto, que es por donde se busca. */
.liga-cot {
    display: flex;
    align-items: center;
    gap: .5rem;
}

.liga-cot > :first-child { flex: 1 1 auto; min-width: 0; }
.liga-cot > .e-btn { flex: 0 0 auto; }

/* Las cotizaciones ya amparadas, arriba del selector. Cada renglón es folio +
   proyecto y, a la derecha, cuánto del total de la factura le toca: una factura
   puede cubrir dos trabajos y el reparto es lo que evita contarla dos veces. */
.liga-cot-tabla {
    width: 100%;
    border-collapse: collapse;
    margin-bottom: .5rem;
}

.liga-cot-tabla td {
    padding: .35rem .5rem;
    border-bottom: 1px solid var(--border);
    vertical-align: middle;
}

.liga-cot-tabla tr:last-child td { border-bottom: none; }
.liga-cot-tabla td.num { text-align: right; width: 1%; white-space: nowrap; }

.liga-cot-proyecto {
    display: block;
    font-size: .78rem;
    color: var(--text-2);
}

/* El cuadre del reparto contra el total de la factura. */
.liga-cot-cuadre {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: .4rem;
    font-size: .82rem;
    color: var(--text-2);
    margin-bottom: .5rem;
}

.liga-cot-cuadre strong { font-family: "IBM Plex Mono", ui-monospace, monospace; }
.liga-cot-cuadre.es-cuadrado strong { color: var(--success); }
.liga-cot-cuadre.es-parcial strong  { color: var(--warning); }
.liga-cot-falta { color: var(--warning); font-weight: 600; }

.liga-cot-acciones {
    display: flex;
    gap: .5rem;
    margin-top: .5rem;
}

/* Los folios de cotización en el grid de facturas. Son varios cuando un mismo
   CFDI ampara dos trabajos, así que envuelven en vez de desbordar la celda. */
.cot-chips {
    display: flex;
    flex-wrap: wrap;
    gap: .15rem .35rem;
}

.cot-chip {
    font-family: "IBM Plex Mono", ui-monospace, monospace;
    font-size: .78rem;
    white-space: nowrap;
}

/* Explicación bajo un campo: por qué importa lo que se está capturando. */
.form-hint {
    margin-top: .3rem;
    font-size: .78rem;
    color: var(--text-2);
}

/* Descripción del catálogo SAT bajo su clave, en la tabla de conceptos.
   Diminuta y en sans: la clave es el dato (va en mono), esto es la ayuda. */
.clave-desc {
    display: block;
    font-family: "Public Sans", system-ui, sans-serif;
    font-size: .68rem;
    line-height: 1.25;
    color: var(--text-3);
    max-width: 190px;
    margin-top: .1rem;
}

/* Combo sin opciones: explica en vez de sonar a error de red. */
.combo-vacio {
    display: block;
    padding: .6rem .9rem;
    font-size: .82rem;
    color: var(--text-2);
}


/* Aviso de borradores pendientes en el alta de factura (NuevaFacturaModal) */
.nuevafac-lista { margin: .4rem 0 0; padding-left: 1.1rem; font-size: .82rem; line-height: 1.5; }
.nuevafac-lista li { margin-bottom: .15rem; }
.nuevafac-suyo .confirmar-acciones { margin-top: .5rem; justify-content: flex-start; }
.conceptos-vacio { text-align: center; color: var(--text-2); font-size: .85rem; padding: 1rem .5rem; }


/* Recurrente del mes que todavía no se factura: se tiñe el renglón entero, no solo
   la etiqueta, porque la lista se recorre con la vista buscando huecos y un texto
   ámbar al final de la fila se pierde entre montos. */
.tabla-montos tr.rec-pendiente { background: var(--warning-bg); }
.tabla-montos tr.rec-pendiente td { font-weight: 500; }


/* Unidad del indicador (con/sin IVA) pegada a su etiqueta. Va aquí y no en la nota
   de abajo porque en la misma fila conviven cifras de las dos clases —lo por cobrar
   es dinero por entrar, con IVA; el resto es subtotal— y sin la unidad al lado del
   nombre se comparan como si fueran lo mismo. */
.kpi-unidad {
    display: inline-block;
    margin-left: .35rem;
    padding: 1px 6px;
    border-radius: 4px;
    background: var(--surface-2);
    color: var(--text-3);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: .03em;
    text-transform: none;
    vertical-align: middle;
}

/* ── Cotizaciones anteriores (editor de una cotización nueva) ────────────────
   Atajo para ver cómo se cotizó antes. Cada una abre su documento en pestaña
   nueva, así que son anclas, no botones. */
.anteriores-lista {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: .5rem;
}

.anterior-chip {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    padding: .5rem .65rem;
    border: 1px solid var(--border);
    border-radius: 8px;
    background: var(--surface);
    color: var(--text);
    text-decoration: none;
    transition: border-color .15s, background .15s;
}

    .anterior-chip:hover {
        border-color: var(--primary);
        background: var(--surface-2);
    }

.anterior-folio {
    font-weight: 600;
    color: var(--primary);
}

/* El proyecto es lo que identifica la cotización de un vistazo; si no cabe, se
   corta con puntos suspensivos en vez de romper la retícula. */
.anterior-proyecto {
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ── Tarifas de referencia en el alta de partida ─────────────────────────────
   Banda fija arriba del modal: se consulta justo mientras se teclea la tarifa,
   así que no puede irse con el scroll del alcance. Cada una es un botón porque
   un clic la copia al campo. */
.tarifas-ref {
    position: sticky;
    top: 0;
    z-index: 3;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .4rem;
    padding: .5rem .6rem;
    margin: -.25rem 0 .75rem;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: 8px;
}

.tarifas-ref-lbl { margin-right: .15rem; }

/* Sin categoría elegida, o sin nada cobrado en ella: el porqué de que no haya botones. */
.tarifas-ref-aviso { font-size: 12px; color: var(--text-2); }

.tarifa-ref {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0;
    padding: .3rem .55rem;
    border: 1px solid var(--border);
    border-radius: 6px;
    background: var(--surface);
    color: var(--text);
    cursor: pointer;
    text-align: left;
    transition: border-color .15s, background .15s;
}

    .tarifa-ref:hover { border-color: var(--primary); }

    /* La del cliente que se está cotizando es la que manda: se marca con el primario. */
    .tarifa-ref.es-cliente { border-color: var(--primary); }

    /* El precio de lista se distingue de lo realmente cobrado. */
    .tarifa-ref.es-lista { opacity: .85; }

.tarifa-ref-quien {
    font-size: 11px;
    font-weight: 600;
    max-width: 130px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.tarifa-ref-monto { font-size: 13px; font-weight: 600; }

.tarifa-ref-origen { font-size: 10px; color: var(--text-2, #64748b); }
