/* =============================================================================
   Vantyx ERP — tema claro con shell oscuro
   El menú lateral y la barra superior van en oscuro; el área de trabajo en claro.
   Es lo que se lee mejor durante ocho horas: el contraste alto se reserva para la
   navegación, que se mira de reojo, y el contenido queda en claro, que es donde
   la vista se queda leyendo cifras.
   Estructura: variables, shell, componentes, pantallas de acceso, utilidades y responsive.
   ============================================================================= */

:root {
    /* ---- Área de trabajo: claro ---- */
    --vtx-bg:            #f1f5f9;
    --vtx-surface:       #ffffff;
    --vtx-surface-2:     #f8fafc;
    --vtx-border:        #cbd5e1;
    --vtx-border-soft:   #e2e8f0;

    --vtx-text:          #1e293b;
    --vtx-text-muted:    #475569;
    --vtx-text-dim:      #64748b;

    /* Fondos tenues para cabeceras de tabla y pies de tarjeta. Antes eran negros
       translúcidos, que sobre claro salían como manchas sucias. */
    --vtx-tint:          rgba(15, 23, 42, .04);
    --vtx-tint-strong:   rgba(15, 23, 42, .06);

    /* ---- Shell: oscuro ---- */
    --vtx-sidebar:       #0b1220;
    --vtx-sidebar-hover: #16203a;
    --vtx-topbar:        #111c33;

    /* Texto sobre el shell oscuro. */
    --vtx-on-dark:       #e2e8f0;
    --vtx-on-dark-muted: #94a3b8;

    /* ---- Acento ----
       El cian brillante funciona sobre oscuro pero no sobre blanco: para texto y
       enlaces se usa la variante profunda, que sí tiene contraste suficiente. */
    --vtx-accent:        #0e7490;
    --vtx-accent-bright: #22d3ee;
    --vtx-accent-dark:   #155e75;
    --vtx-accent-soft:   rgba(14, 116, 144, .12);

    --vtx-success:       #16a34a;
    --vtx-warning:       #d97706;
    --vtx-danger:        #dc2626;
    --vtx-info:          #0284c7;
    --vtx-purple:        #7c3aed;

    --vtx-sidebar-width: 268px;
    --vtx-topbar-height: 60px;
    --vtx-radius:        10px;
    --vtx-radius-sm:     6px;
    --vtx-shadow:        0 1px 3px rgba(15, 23, 42, .08), 0 1px 2px rgba(15, 23, 42, .06);
}

/* ----------------------------------------------------------- shell oscuro ---
   El menú y la barra superior siguen sobre fondo oscuro, así que dentro de ellos
   se redefinen los tokens de texto y acento. Se hace aquí, una vez, y no regla por
   regla: cada componente del shell hereda los valores correctos sin saberlo. */

.vtx-sidebar,
.vtx-topbar {
    --vtx-text:        var(--vtx-on-dark);
    --vtx-text-muted:  var(--vtx-on-dark-muted);
    --vtx-text-dim:    #64748b;

    /* El cian profundo no se ve sobre azul oscuro: aquí manda el brillante. */
    --vtx-accent:      var(--vtx-accent-bright);
    --vtx-accent-dark: #0e7490;
    --vtx-accent-soft: rgba(34, 211, 238, .14);

    --vtx-surface:     #16203a;
    --vtx-surface-2:   #1d2b4a;
    --vtx-border:      #334155;
    --vtx-border-soft: #24314a;

    --vtx-tint:        rgba(0, 0, 0, .18);
    --vtx-tint-strong: rgba(0, 0, 0, .26);

    color: var(--vtx-on-dark);
}

/* ---------------------------------------------------------------- base --- */

* { box-sizing: border-box; }

html, body {
    height: 100%;
}

body {
    margin: 0;
    background: var(--vtx-bg);
    color: var(--vtx-text);
    font-family: "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    font-size: 14px;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
}

a { color: var(--vtx-accent); text-decoration: none; }
a:hover { color: var(--vtx-accent-dark); text-decoration: underline; }

h1, h2, h3, h4, h5 { color: #0f172a; font-weight: 600; margin-top: 0; }

hr { border: 0; border-top: 1px solid var(--vtx-border-soft); margin: 1.25rem 0; }

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: var(--vtx-bg); }
::-webkit-scrollbar-thumb { background: #334155; border-radius: 5px; }
::-webkit-scrollbar-thumb:hover { background: #475569; }

/* --------------------------------------------------------------- shell --- */

/* El armazón ocupa la ventana y no se desplaza: quien se desplaza es el contenido.
   Así la barra superior y el pie quedan siempre a la vista, y una rejilla larga no
   arrastra consigo la cabecera de la pantalla.
   dvh va después de vh a propósito: en el móvil descuenta la barra del navegador, y
   quien no lo entienda se queda con la regla anterior. */
.vtx-shell {
    height: 100vh;
    height: 100dvh;
    overflow: hidden;
}

/* Menú lateral */
.vtx-sidebar {
    position: fixed;
    inset: 0 auto 0 0;
    width: var(--vtx-sidebar-width);
    background: var(--vtx-sidebar);
    border-right: 1px solid var(--vtx-border-soft);
    display: flex;
    flex-direction: column;
    z-index: 1040;
    transition: transform .2s ease;
}

/* La marca lleva al panel de inicio: es un enlace, no un rótulo. */
.vtx-brand {
    height: var(--vtx-topbar-height);
    display: flex;
    align-items: center;
    gap: .7rem;
    padding: 0 1.15rem;
    border-bottom: 1px solid var(--vtx-border-soft);
    flex-shrink: 0;
    color: inherit;
    text-decoration: none;
    transition: background .12s ease;
}

.vtx-brand:hover,
.vtx-brand:focus-visible {
    background: var(--vtx-sidebar-hover);
    text-decoration: none;
    color: inherit;
}

.vtx-brand:hover .vtx-brand__mark { transform: scale(1.05); }

.vtx-brand:focus-visible { outline: 2px solid var(--vtx-accent); outline-offset: -2px; }

/* La marca del menú es la imagen del logotipo, no un cuadro con una letra.
   Va por alto y no por ancho: la V es más ancha que alta, y fijar el ancho la
   dejaría más pequeña de lo que le toca al lado del nombre. */
.vtx-brand__mark {
    height: 30px;
    width: auto;
    transition: transform .12s ease;
    flex-shrink: 0;
}

.vtx-brand__name { font-size: 1.2rem; font-weight: 700; color: #f8fafc; letter-spacing: .3px; line-height: 1.1; }
.vtx-brand__tag  { font-size: .66rem; color: var(--vtx-text-dim); letter-spacing: .4px; text-transform: uppercase; }

/* Empresa y sucursal activas */
.vtx-context {
    padding: .85rem 1.15rem;
    border-bottom: 1px solid var(--vtx-border-soft);
    background: rgba(255, 255, 255, .015);
    flex-shrink: 0;
}

.vtx-context__label   { font-size: .65rem; text-transform: uppercase; color: var(--vtx-text-dim); letter-spacing: .5px; }
.vtx-context__company { font-weight: 600; color: #f1f5f9; font-size: .9rem; margin-top: .1rem; }
.vtx-context__branch  { font-size: .78rem; color: var(--vtx-text-muted); }
.vtx-context__branch i { color: var(--vtx-accent); margin-right: .3rem; }

/* Navegación */
.vtx-nav { flex: 1; overflow-y: auto; padding: .6rem 0 1.5rem; }

.vtx-nav__section {
    font-size: .64rem; text-transform: uppercase; letter-spacing: .8px;
    color: var(--vtx-text-dim); padding: 1rem 1.15rem .35rem;
}

.vtx-nav__link {
    display: flex; align-items: center; gap: .7rem;
    padding: .55rem 1.15rem;
    color: #cbd5e1;
    border-left: 3px solid transparent;
    cursor: pointer;
    transition: background .12s ease, color .12s ease;
}

.vtx-nav__link:hover { background: var(--vtx-sidebar-hover); color: #fff; text-decoration: none; }

.vtx-nav__link.active {
    background: var(--vtx-accent-soft);
    border-left-color: var(--vtx-accent);
    color: #fff;
}

.vtx-nav__link i:first-child { width: 18px; text-align: center; font-size: .92rem; color: var(--vtx-text-muted); }
.vtx-nav__link:hover i:first-child,
.vtx-nav__link.active i:first-child { color: var(--vtx-accent); }

.vtx-nav__text { flex: 1; font-size: .875rem; }

.vtx-nav__caret { font-size: .7rem; color: var(--vtx-text-dim); transition: transform .18s ease; }
.vtx-nav__group.open > .vtx-nav__link .vtx-nav__caret { transform: rotate(90deg); }

.vtx-nav__children { display: none; background: rgba(0, 0, 0, .22); }
.vtx-nav__group.open > .vtx-nav__children { display: block; }

.vtx-nav__children .vtx-nav__link { padding-left: 3.05rem; font-size: .83rem; }
.vtx-nav__children .vtx-nav__link i:first-child { font-size: .78rem; }

/* Barra superior */
/* min-width:0 evita que una tabla ancha estire la columna y saque al pie de cuadro:
   sin eso, un elemento flexible se niega a encogerse por debajo de su contenido. */
.vtx-main {
    margin-left: var(--vtx-sidebar-width);
    height: 100%;
    min-width: 0;
    display: flex;
    flex-direction: column;
}

.vtx-topbar {
    height: var(--vtx-topbar-height);
    /* La barra superior es parte del shell: se queda oscura con el menu. */
    background: var(--vtx-topbar);
    border-bottom: 1px solid rgba(255, 255, 255, .06);
    display: flex; align-items: center; gap: 1rem;
    padding: 0 1.4rem;
    position: sticky; top: 0; z-index: 1030;
}

.vtx-topbar__toggle {
    background: none; border: 0; color: var(--vtx-on-dark-muted);
    font-size: 1.15rem; cursor: pointer; padding: .35rem .5rem; border-radius: var(--vtx-radius-sm);
}
.vtx-topbar__toggle:hover { background: var(--vtx-sidebar-hover); color: #fff; }

.vtx-topbar__title { font-size: 1.02rem; font-weight: 600; color: var(--vtx-on-dark); }
.vtx-topbar__spacer { flex: 1; }

.vtx-chip {
    display: inline-flex; align-items: center; gap: .4rem;
    background: var(--vtx-surface-2); border: 1px solid var(--vtx-border);
    border-radius: 999px; padding: .28rem .8rem;
    font-size: .76rem; color: var(--vtx-text-muted);
}
.vtx-chip i { color: var(--vtx-accent); }
/* Estos distintivos viven en la barra superior, que es oscura. El aviso llevaba un
   marrón oscuro sobre un fondo casi transparente y se perdía contra el azul del
   fondo: justo lo contrario de lo que se quiere de un aviso que cuenta los días que
   le quedan a la prueba. Van en sólido, que es lo que se lee de un vistazo. */
.vtx-chip--warning {
    background: #f59e0b;
    border-color: #f59e0b;
    color: #451a03;
    font-weight: 600;
}
.vtx-chip--warning i { color: #451a03; }

/* Vencida ya no es una advertencia: es un hecho, y en ámbar parecía que aún quedaba
   margen. */
.vtx-chip--danger {
    background: #dc2626;
    border-color: #dc2626;
    color: #ffffff;
    font-weight: 600;
}
.vtx-chip--danger i { color: #ffffff; }

.vtx-user {
    display: flex; align-items: center; gap: .6rem;
    background: none; border: 0; color: var(--vtx-text);
    padding: .3rem .55rem; border-radius: var(--vtx-radius-sm); cursor: pointer;
}
.vtx-user:hover { background: var(--vtx-surface-2); }

.vtx-avatar {
    width: 32px; height: 32px; border-radius: 50%;
    background: linear-gradient(135deg, var(--vtx-accent), var(--vtx-accent-dark));
    color: #06202a; display: grid; place-items: center;
    font-weight: 700; font-size: .78rem; flex-shrink: 0;
}

.vtx-user__name { font-size: .84rem; font-weight: 600; line-height: 1.15; }
.vtx-user__role { font-size: .68rem; color: var(--vtx-text-dim); }

/* min-height:0 es lo que de verdad permite el desplazamiento: un hijo flexible no
   se encoge por debajo de su contenido si no se le dice, y sin esto el contenido
   empujaría el pie fuera de la ventana en vez de desplazarse. */
.vtx-content {
    flex: 1;
    min-height: 0;
    /* Menos aire abajo que arriba: debajo viene el pie, que ya separa por su cuenta. */
    padding: 1rem 1.1rem .5rem;
    overflow-y: auto;
    overflow-x: hidden;
}

/* ---------------------------------------------------------------- pie de página --- */

.vtx-footer {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
    padding: .5rem 1.4rem;
    border-top: 1px solid var(--vtx-border-soft);
    background: var(--vtx-surface);
    color: var(--vtx-text-dim);
    font-size: .74rem;
}

.vtx-footer__sep { opacity: .45; }
.vtx-footer a { color: inherit; text-decoration: none; }
.vtx-footer a:hover { color: var(--vtx-accent); text-decoration: underline; }

.vtx-page-head { display: flex; align-items: flex-start; gap: 1rem; margin-bottom: .7rem; flex-wrap: wrap; }
.vtx-page-head h1 { font-size: 1.15rem; margin: 0; }
.vtx-page-head p  { color: var(--vtx-text-muted); margin: .2rem 0 0; font-size: .86rem; }

/* ----------------------------------------------------------- componentes --- */

.vtx-card {
    background: var(--vtx-surface);
    border: 1px solid var(--vtx-border-soft);
    border-radius: var(--vtx-radius);
    overflow: hidden;
}

.vtx-card__head {
    padding: .5rem .9rem;
    border-bottom: 1px solid var(--vtx-border-soft);
    display: flex; align-items: center; gap: .6rem;
}
.vtx-card__head h2, .vtx-card__head h3 { font-size: .95rem; margin: 0; font-weight: 600; }
.vtx-card__head i { color: var(--vtx-accent); }
.vtx-card__body { padding: .75rem .9rem; }
.vtx-card__body--flush { padding: 0; }

/* Tarjeta de indicador */
.vtx-kpi {
    background: var(--vtx-surface);
    border: 1px solid var(--vtx-border-soft);
    border-radius: var(--vtx-radius);
    padding: .8rem .9rem;
    display: flex; align-items: flex-start; gap: 1rem;
    height: 100%;
    position: relative; overflow: hidden;
}

.vtx-kpi::after {
    content: ""; position: absolute; inset: 0 0 auto 0; height: 3px;
    background: var(--kpi-color, var(--vtx-accent));
}

.vtx-kpi__icon {
    width: 42px; height: 42px; border-radius: var(--vtx-radius-sm);
    display: grid; place-items: center; font-size: 1.05rem; flex-shrink: 0;
    background: color-mix(in srgb, var(--kpi-color, var(--vtx-accent)) 15%, transparent);
    color: var(--kpi-color, var(--vtx-accent));
}

.vtx-kpi__label { font-size: .73rem; text-transform: uppercase; letter-spacing: .4px; color: var(--vtx-text-muted); }
.vtx-kpi__value { font-size: 1.5rem; font-weight: 700; color: var(--vtx-text); line-height: 1.2; margin-top: .1rem; }
.vtx-kpi__foot  { font-size: .74rem; color: var(--vtx-text-dim); margin-top: .15rem; }

.vtx-kpi--success { --kpi-color: var(--vtx-success); }
.vtx-kpi--warning { --kpi-color: var(--vtx-warning); }
.vtx-kpi--danger  { --kpi-color: var(--vtx-danger); }
.vtx-kpi--info    { --kpi-color: var(--vtx-info); }
.vtx-kpi--purple  { --kpi-color: var(--vtx-purple); }

/* Tablas */
.vtx-table { width: 100%; border-collapse: collapse; font-size: .84rem; }
.vtx-table th {
    text-align: left; padding: .4rem .9rem;
    font-size: .69rem; text-transform: uppercase; letter-spacing: .5px;
    color: var(--vtx-text-dim); background: var(--vtx-tint-strong);
    border-bottom: 1px solid var(--vtx-border-soft); white-space: nowrap;
}
.vtx-table td { padding: .42rem .9rem; border-bottom: 1px solid var(--vtx-border-soft); }
.vtx-table tbody tr:last-child td { border-bottom: 0; }
.vtx-table tbody tr:hover { background: var(--vtx-tint); }
.vtx-table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* Tabla que rueda dentro de su tarjeta en vez de hacer rodar la página entera.
   El alto lo mide vtx-tabla-alta.js: depende de dónde empiece la tabla y de cuánto
   mida la pantalla, y eso no se puede escribir aquí.

   La cabecera se queda pegada arriba. Sin esto, rodar la tabla deja al usuario ante
   una parrilla de datos sin saber de qué columna es cada cosa, que es justo lo que se
   quería evitar. */
[data-vtx-tabla-scroll] { overflow-x: auto; }

[data-vtx-tabla-scroll] thead th {
    position: sticky;
    top: 0;
    z-index: 2;

    /* El fondo de la cabecera es un tinte al 6 % —translúcido a propósito, para que
       tome el color de la tarjeta—. Pegada arriba eso deja ver las filas pasando por
       debajo, y el texto se superpone con el de las columnas.

       Se pone el color de la superficie debajo y el mismo tinte encima, como imagen:
       queda exactamente igual que antes pero opaco, y sin escribir ningún color a
       mano, así que sigue el token de superficie si algún día cambia. */
    background-color: var(--vtx-surface);
    background-image: linear-gradient(var(--vtx-tint-strong), var(--vtx-tint-strong));

    /* Con border-collapse el borde no viaja con el elemento pegado; la sombra interior
       hace de línea y sí se queda. */
    box-shadow: inset 0 -1px 0 var(--vtx-border-soft);
}

.vtx-empty { padding: 1.5rem .9rem; text-align: center; color: var(--vtx-text-dim); }
.vtx-empty i { font-size: 1.8rem; display: block; margin-bottom: .35rem; opacity: .5; }

/* Formularios */
.vtx-field { margin-bottom: .65rem; }
.vtx-label { display: block; font-size: .78rem; font-weight: 600; color: var(--vtx-text-muted); margin-bottom: .18rem; }

.vtx-input, .vtx-select, .vtx-textarea {
    width: 100%;
    background: #ffffff;
    border: 1px solid var(--vtx-border);
    border-radius: var(--vtx-radius-sm);
    color: var(--vtx-text);
    padding: .42rem .65rem;
    font-size: .88rem; font-family: inherit;
    transition: border-color .12s ease, box-shadow .12s ease;
}

.vtx-input:focus, .vtx-select:focus, .vtx-textarea:focus {
    outline: 0; border-color: var(--vtx-accent);
    box-shadow: 0 0 0 3px var(--vtx-accent-soft);
}

.vtx-input::placeholder { color: var(--vtx-text-dim); }
.vtx-input[readonly] { background: #0a1120; color: var(--vtx-text-muted); }

.vtx-input-group { position: relative; }
.vtx-input-group i {
    position: absolute; left: .65rem; top: 50%; transform: translateY(-50%);
    color: var(--vtx-text-dim); font-size: .85rem; pointer-events: none;
}
.vtx-input-group .vtx-input { padding-left: 2.05rem; }

.vtx-check { display: flex; align-items: center; gap: .5rem; font-size: .84rem; color: var(--vtx-text-muted); }
.vtx-check input { width: 15px; height: 15px; accent-color: var(--vtx-accent); }

/* Botones */
.vtx-btn {
    display: inline-flex; align-items: center; justify-content: center; gap: .45rem;
    border: 1px solid transparent; border-radius: var(--vtx-radius-sm);
    padding: .42rem .85rem;
    font-size: .86rem; font-weight: 600; font-family: inherit;
    cursor: pointer; transition: filter .12s ease, background .12s ease;
    text-decoration: none;
}
.vtx-btn:hover { text-decoration: none; filter: brightness(1.1); }
.vtx-btn:disabled { opacity: .55; cursor: not-allowed; filter: none; }

/* Texto blanco sobre el acento profundo: con el texto oscuro de antes el contraste
   se quedaba en 3,1 y el botón principal era lo peor leído de la pantalla. */
.vtx-btn--primary { background: var(--vtx-accent); color: #ffffff; }
.vtx-btn--primary:hover { background: var(--vtx-accent-dark); color: #ffffff; }
.vtx-btn--ghost { background: transparent; border-color: var(--vtx-border); color: var(--vtx-text-muted); }
.vtx-btn--ghost:hover { background: var(--vtx-surface-2); color: var(--vtx-text); }
.vtx-btn--danger { background: var(--vtx-danger); color: #fff; }
.vtx-btn--block { width: 100%; }
.vtx-btn--lg { padding: .7rem 1.3rem; font-size: .92rem; }

/* Botón de sólo icono, el de las columnas de acciones de los listados.
   Estaba escrito como un style="padding:.25rem .5rem" repetido en cuarenta y un
   botones de veintiséis pantallas; además de ser una copia por sitio, quedaba
   demasiado pequeño para acertarle en una tabla llena. */
.vtx-btn--icono {
    padding: 0; width: 30px; height: 30px;
    flex: 0 0 auto;
}
.vtx-btn--icono i { font-size: .95rem; }

/* Avisos */
.vtx-alert {
    display: flex; align-items: flex-start; gap: .65rem;
    border-radius: var(--vtx-radius-sm);
    padding: .75rem 1rem; margin-bottom: 1rem;
    font-size: .85rem; border: 1px solid;
}
.vtx-alert i { margin-top: .12rem; }
.vtx-alert--error   { background: rgba(239, 68, 68, .1);  border-color: rgba(239, 68, 68, .30);  color: #b91c1c; }
.vtx-alert--success { background: rgba(34, 197, 94, .1);  border-color: rgba(34, 197, 94, .30);  color: #166534; }
.vtx-alert--warning { background: rgba(245, 158, 11, .1); border-color: rgba(245, 158, 11, .30); color: #92400e; }
.vtx-alert--info    { background: rgba(56, 189, 248, .1); border-color: rgba(56, 189, 248, .30); color: #075985; }
.vtx-alert ul { margin: .3rem 0 0; padding-left: 1.1rem; }

/* Insignias */
.vtx-badge {
    display: inline-block; padding: .16rem .55rem; border-radius: 999px;
    font-size: .7rem; font-weight: 600;
}
.vtx-badge--success { background: rgba(34, 197, 94, .14);  color: #166534; }
.vtx-badge--warning { background: rgba(245, 158, 11, .18); color: #92400e; }
.vtx-badge--danger  { background: rgba(239, 68, 68, .12);  color: #b91c1c; }
.vtx-badge--info    { background: rgba(56, 189, 248, .16); color: #075985; }
.vtx-badge--muted   { background: rgba(148, 163, 184, .14); color: var(--vtx-text-muted); }


/* ------------------------------------------------- documentos y captura --- */

/* Botón secundario: acción real pero no la principal de la pantalla. */
.vtx-btn--secondary { background: var(--vtx-surface-2); border-color: var(--vtx-border); color: var(--vtx-text); }
.vtx-btn--secondary:hover { background: var(--vtx-border); color: var(--vtx-text); }

.vtx-badge--purple { background: rgba(167, 139, 250, .16); color: #6d28d9; }

/* Pie de tarjeta: separa las acciones del contenido sin abrir otra tarjeta. */
.vtx-card__foot {
    padding: .85rem 1.15rem;
    border-top: 1px solid var(--vtx-border-soft);
    background: var(--vtx-tint);
}

/* Texto de ayuda bajo un campo. */
.vtx-help {
    display: block;
    font-size: .74rem; line-height: 1.35;
    color: var(--vtx-text-dim);
}
.vtx-help i { margin-right: .3rem; }

/* Barra de filtros: se envuelve sola en pantallas angostas. */
.vtx-filters { display: flex; flex-wrap: wrap; gap: .75rem; align-items: flex-end; }
.vtx-filters .vtx-field { flex: 1 1 160px; margin-bottom: 0; }
.vtx-field--actions { display: flex; gap: .4rem; flex: 0 0 auto; }

/* Campo compacto para las rejillas de captura (formas de pago, líneas). */
.vtx-field--inline { display: flex; flex-direction: column; gap: .15rem; min-width: 120px; margin-bottom: 0; }
.vtx-field--inline > label { font-size: .68rem; color: var(--vtx-text-dim); text-transform: uppercase; letter-spacing: .03em; }
.vtx-field--inline .vtx-input { padding: .3rem .5rem; font-size: .8rem; }

/* Los campos que llevan un nombre y no un dato corto: la cuenta bancaria y el banco.

   Con el suelo de 120 px, «BANCO POPULAR DOMINICANO · CUENTA PERSONAL BPD» se corta a
   la mitad, y en un desplegable eso es peor que en un texto: el nativo no pone puntos
   suspensivos ni deja ver el resto, así que dos cuentas del mismo banco se ven iguales
   y se elige la equivocada sin enterarse.

   Con max-width al 100% no se sale de la fila en una pantalla estrecha. */
.vtx-field--ancho { min-width: 340px; max-width: 100%; }

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

/* Documento anulado: sigue legible, pero se lee como histórico. */
.vtx-row--void > td { opacity: .55; text-decoration: line-through; }
.vtx-row--void > td:last-child { text-decoration: none; }

/* Buscador con sugerencias. El contenedor debe tener position:relative. */
.vtx-suggest {
    display: none;
    position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 40;
    max-height: 320px; overflow-y: auto;
    background: var(--vtx-surface-2);
    border: 1px solid var(--vtx-border);
    border-radius: var(--vtx-radius-sm);
    box-shadow: var(--vtx-shadow);
}
.vtx-suggest.open { display: block; }
.vtx-suggest__item { padding: .5rem .75rem; cursor: pointer; border-bottom: 1px solid var(--vtx-border-soft); }
.vtx-suggest__item:last-child { border-bottom: 0; }
.vtx-suggest__item:hover { background: var(--vtx-sidebar-hover); }
.vtx-suggest__name { font-size: .85rem; font-weight: 600; }
.vtx-suggest__meta { font-size: .72rem; color: var(--vtx-text-dim); }

/* Cuadro de totales de un documento. */
.vtx-totals { display: flex; flex-direction: column; gap: .1rem; }
.vtx-totals__row {
    display: flex; justify-content: space-between; align-items: baseline; gap: 1.5rem;
    padding: .32rem 0;
    font-size: .85rem; color: var(--vtx-text-muted);
}
.vtx-totals__row .num { font-variant-numeric: tabular-nums; color: var(--vtx-text); }
.vtx-totals__row--strong { border-top: 1px solid var(--vtx-border-soft); padding-top: .5rem; font-weight: 600; color: var(--vtx-text); }
.vtx-totals__row--total {
    border-top: 2px solid var(--vtx-border);
    margin-top: .3rem; padding-top: .55rem;
    font-size: 1rem; font-weight: 700; color: var(--vtx-text);
}
.vtx-totals__row--total .num { color: var(--vtx-accent); }

/* Totales en barra.
   En un diálogo, seis renglones en columna se comen el alto que necesitan las tablas
   de arriba, y estos totales no pueden esconderse en una pestaña: son el marcador que
   dice si el pago cuadra. Puestos en fila se leen igual y ocupan un tercio.
   En pantalla estrecha vuelven a la columna de siempre. */
@media (min-width: 720px) {
    .vtx-totals--barra { flex-direction: row; flex-wrap: wrap; gap: .2rem; }

    .vtx-totals--barra .vtx-totals__row {
        flex-direction: column; align-items: flex-start; gap: .05rem;
        border-top: 0; margin-top: 0; padding: .3rem 1rem .3rem 0;
    }

    .vtx-totals--barra .vtx-totals__row + .vtx-totals__row {
        border-left: 1px solid var(--vtx-border-soft); padding-left: 1rem;
    }

    .vtx-totals--barra .vtx-totals__row--total { font-size: .95rem; margin-left: auto; padding-right: 0; }
}

/* Ficha de datos en dos columnas. */
.vtx-dl { display: grid; grid-template-columns: auto 1fr; gap: .45rem 1rem; margin: 0; font-size: .84rem; }
.vtx-dl dt { color: var(--vtx-text-dim); font-weight: 500; }
.vtx-dl dd { margin: 0; color: var(--vtx-text); }

/* -------------------------------------------------------------- acceso --- */

.vtx-auth {
    position: relative;
    min-height: 100vh;
    display: grid; place-items: center;
    padding: 2rem 1rem;
    background:
        radial-gradient(1100px 520px at 15% -10%, rgba(34, 211, 238, .10), transparent 60%),
        radial-gradient(900px 460px at 100% 100%, rgba(14, 116, 144, .14), transparent 55%),
        var(--vtx-bg);
}

/* Fondo con movimiento.
   Tres manchas de color que se desplazan muy despacio. El recorte va aquí y no en
   .vtx-auth: si la tarjeta crece —la lista de empresas de un usuario con varias— el
   contenedor tiene que poder desbordar y desplazarse, y un overflow:hidden arriba
   dejaría media pantalla inalcanzable. */
.vtx-auth__fondo {
    position: absolute;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
    z-index: 0;
}

/* El difuminado sale del propio degradado y no de filter: blur().
   Un blur de setenta píxeles sobre tres elementos de medio millar cuesta caro en
   una máquina modesta, y esto se ve en la pantalla de entrar, que es justo donde
   nadie quiere que el ventilador arranque. El resultado es el mismo. */
.vtx-auth__fondo span {
    position: absolute;
    border-radius: 50%;
    will-change: transform;
}

.vtx-auth__fondo span:nth-child(1) {
    width: 620px; height: 620px;
    top: -180px; left: -140px;
    background: radial-gradient(circle, rgba(34, 211, 238, .34), rgba(34, 211, 238, 0) 68%);
    animation: vtx-deriva-a 16s linear infinite;
}

.vtx-auth__fondo span:nth-child(2) {
    width: 540px; height: 540px;
    bottom: -200px; right: -120px;
    background: radial-gradient(circle, rgba(14, 116, 144, .36), rgba(14, 116, 144, 0) 68%);
    animation: vtx-deriva-b 21s linear infinite;
}

.vtx-auth__fondo span:nth-child(3) {
    width: 460px; height: 460px;
    top: 42%; left: 58%;
    background: radial-gradient(circle, rgba(124, 58, 237, .22), rgba(124, 58, 237, 0) 68%);
    animation: vtx-deriva-c 26s linear infinite;
}

/* Recorridos cerrados: el último fotograma coincide con el primero, así que el ciclo
   se repite sin salto y no hace falta «alternate».
   Y el ritmo es lineal a propósito. Con ease-in-out el movimiento se detiene en cada
   extremo, y como el usuario mira esta pantalla unos segundos sueltos, lo más
   probable era que cayera justo en la parte quieta y la diera por estática.
   Solo se anima transform, que el navegador resuelve sin recalcular la página. */
@keyframes vtx-deriva-a {
    0%   { transform: translate3d(0, 0, 0) scale(1); }
    25%  { transform: translate3d(180px, 120px, 0) scale(1.16); }
    50%  { transform: translate3d(260px, -60px, 0) scale(1.04); }
    75%  { transform: translate3d(90px, -140px, 0) scale(1.2); }
    100% { transform: translate3d(0, 0, 0) scale(1); }
}

@keyframes vtx-deriva-b {
    0%   { transform: translate3d(0, 0, 0) scale(1.08); }
    25%  { transform: translate3d(-200px, -90px, 0) scale(.94); }
    50%  { transform: translate3d(-120px, -230px, 0) scale(1.18); }
    75%  { transform: translate3d(80px, -110px, 0) scale(1); }
    100% { transform: translate3d(0, 0, 0) scale(1.08); }
}

@keyframes vtx-deriva-c {
    0%   { transform: translate3d(0, 0, 0) scale(.92); }
    25%  { transform: translate3d(-190px, 90px, 0) scale(1.22); }
    50%  { transform: translate3d(-260px, -80px, 0) scale(1); }
    75%  { transform: translate3d(-70px, -170px, 0) scale(1.14); }
    100% { transform: translate3d(0, 0, 0) scale(.92); }
}

/* Quien pidió que la pantalla se esté quieta, se queda quieta: a algunas personas un
   fondo en movimiento les provoca mareo. El color se conserva; lo que para es el
   desplazamiento. */
@media (prefers-reduced-motion: reduce) {
    .vtx-auth__fondo span { animation: none; }
}

.vtx-auth__card {
    position: relative;
    z-index: 1;
    width: 100%; max-width: 420px;
    background: var(--vtx-surface);
    border: 1px solid var(--vtx-border-soft);
    border-radius: 14px;
    box-shadow: var(--vtx-shadow);
    padding: 2rem;
}
.vtx-auth__card--wide { max-width: 620px; }

.vtx-auth__brand { text-align: center; margin-bottom: 1.6rem; }

/* El logotipo completo, con el nombre dentro. Se limita por alto para que la
   tarjeta de acceso no crezca en pantallas anchas, y el ancho lo pone la propia
   proporción de la imagen. */
.vtx-auth__logo {
    height: 110px;
    width: auto;
    max-width: 100%;
    display: block;
    margin: 0 auto .5rem;
}

.vtx-auth__tag  { font-size: .76rem; color: var(--vtx-text-muted); margin-top: .3rem; }

.vtx-auth__title { font-size: 1.02rem; font-weight: 600; margin-bottom: .25rem; }
.vtx-auth__hint  { font-size: .82rem; color: var(--vtx-text-muted); margin-bottom: 1.2rem; }
.vtx-auth__foot  { text-align: center; margin-top: 1.3rem; font-size: .83rem; color: var(--vtx-text-muted); }

/* Tarjetas de selección (empresa / sucursal) */
.vtx-pick { display: grid; gap: .7rem; }

.vtx-pick__item {
    display: flex; align-items: center; gap: .9rem;
    width: 100%; text-align: left;
    background: #ffffff;
    border: 1px solid var(--vtx-border);
    border-radius: var(--vtx-radius);
    padding: .9rem 1rem;
    color: var(--vtx-text);
    cursor: pointer; font-family: inherit;
    transition: border-color .12s ease, background .12s ease, transform .12s ease;
}
.vtx-pick__item:hover {
    border-color: var(--vtx-accent);
    background: var(--vtx-surface-2);
    text-decoration: none; color: var(--vtx-text);
    transform: translateY(-1px);
}

.vtx-pick__icon {
    width: 40px; height: 40px; border-radius: var(--vtx-radius-sm);
    background: var(--vtx-accent-soft); color: var(--vtx-accent);
    display: grid; place-items: center; font-size: 1.05rem; flex-shrink: 0;
}
.vtx-pick__body { flex: 1; min-width: 0; }
.vtx-pick__name { font-weight: 600; color: var(--vtx-text); }
.vtx-pick__meta { font-size: .77rem; color: var(--vtx-text-muted); }
.vtx-pick__go   { color: var(--vtx-text-dim); }

/* ----------------------------------------------------------- utilidades --- */

.vtx-grid { display: grid; gap: 1rem; }
.vtx-grid--kpi  { grid-template-columns: repeat(auto-fill, minmax(232px, 1fr)); }
.vtx-grid--half { grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); }

.vtx-row { display: flex; gap: .7rem; flex-wrap: wrap; }
.vtx-col { flex: 1; min-width: 200px; }

.text-muted   { color: var(--vtx-text-muted) !important; }
.text-dim     { color: var(--vtx-text-dim) !important; }
.text-success { color: var(--vtx-success) !important; }
.text-warning { color: var(--vtx-warning) !important; }
.text-danger  { color: var(--vtx-danger) !important; }
.text-accent  { color: var(--vtx-accent) !important; }
.text-right   { text-align: right; }
.fw-600       { font-weight: 600; }
.mt-0 { margin-top: 0; }  .mt-1 { margin-top: .5rem; }  .mt-2 { margin-top: 1rem; }
.mb-0 { margin-bottom: 0; } .mb-2 { margin-bottom: 1rem; } .mb-3 { margin-bottom: 1.5rem; }

.vtx-bar { height: 6px; background: var(--vtx-tint-strong); border-radius: 3px; overflow: hidden; }
.vtx-bar__fill { height: 100%; background: var(--vtx-accent); border-radius: 3px; }

/* ------------------------------------------------------------ responsive --- */

.vtx-backdrop {
    display: none;
    position: fixed; inset: 0;
    background: rgba(0, 0, 0, .5);
    z-index: 1035;
}

@media (max-width: 991px) {
    .vtx-sidebar { transform: translateX(-100%); }
    .vtx-shell.sidebar-open .vtx-sidebar { transform: translateX(0); }
    .vtx-shell.sidebar-open .vtx-backdrop { display: block; }
    .vtx-main { margin-left: 0; }
    .vtx-content { padding: 1rem; }
    .vtx-user__name, .vtx-user__role { display: none; }
}

@media (min-width: 992px) {
    .vtx-shell.sidebar-collapsed .vtx-sidebar { transform: translateX(-100%); }
    .vtx-shell.sidebar-collapsed .vtx-main { margin-left: 0; }
}

/* Membrete de los documentos impresos. En pantalla no se ve: esos datos ya están en
   la barra superior. En el papel son imprescindibles —una factura sin el RNC del
   emisor no le sirve a quien la recibe—. */
.vtx-membrete { display: none; }

@media print {
    .vtx-sidebar, .vtx-topbar, .vtx-footer { display: none; }
    .vtx-main { margin-left: 0; height: auto; }
    body { background: #fff; color: #000; }

    /* Se deshace el armazón de altura fija: si no, al imprimir una factura larga
       saldría solo lo que cabía en la pantalla y el resto se perdería. */
    .vtx-shell { height: auto; overflow: visible; }
    .vtx-content { overflow: visible; min-height: 0; }

    /* Los botones no se imprimen: en el papel son manchas de tinta que no hacen nada.
       Se marcan con esta clase en vez de esconder todo lo que parezca un botón,
       porque hay tablas con casillas marcadas que sí tienen que salir. */
    .vtx-no-imprimir { display: none !important; }

    .vtx-membrete {
        display: flex; align-items: flex-start; gap: 1rem;
        padding-bottom: .6rem; margin-bottom: .9rem;
        border-bottom: 2px solid #000;
    }

    .vtx-membrete__logo { max-height: 70px; max-width: 220px; object-fit: contain; }
    .vtx-membrete__datos { font-size: 10.5pt; line-height: 1.35; }
    .vtx-membrete__nombre { font-size: 13pt; font-weight: 700; }
    .vtx-membrete__comercial { font-style: italic; }

    /* Una tarjeta impresa no necesita sombra ni fondo: solo su marco. */
    .vtx-card { box-shadow: none; border: 1px solid #999; break-inside: avoid; }
    .vtx-card__head { background: none; border-bottom: 1px solid #999; }

    /* Las filas de un documento no se parten entre dos páginas. */
    .vtx-table tr { break-inside: avoid; }
    .vtx-table thead { display: table-header-group; }
}

/* ------------------------------------------------------- indicador ocupado --- */

.vtx-spinner {
    width: 34px; height: 34px;
    border: 3px solid var(--vtx-border);
    border-top-color: var(--vtx-accent);
    border-radius: 50%;
    animation: vtx-spin .7s linear infinite;
}

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

/* Capa que cubre la pantalla mientras hay una operación en curso. */
.vtx-busy {
    position: fixed; inset: 0; z-index: 2000;
    display: none; place-items: center;
    background: rgba(15, 23, 42, .55);
    backdrop-filter: blur(2px);
}
.vtx-busy.open { display: grid; }

.vtx-busy__box {
    display: flex; flex-direction: column; align-items: center; gap: .9rem;
    background: var(--vtx-surface);
    border: 1px solid var(--vtx-border);
    border-radius: var(--vtx-radius);
    box-shadow: var(--vtx-shadow);
    padding: 1.6rem 2.2rem;
}
.vtx-busy__text { font-size: .88rem; color: var(--vtx-text-muted); }

/* Botón que ya disparó su acción: no admite un segundo clic. */
.vtx-btn.is-loading { position: relative; color: transparent !important; pointer-events: none; }
.vtx-btn.is-loading > * { visibility: hidden; }
.vtx-btn.is-loading::after {
    content: "";
    position: absolute; top: 50%; left: 50%;
    width: 15px; height: 15px; margin: -8px 0 0 -8px;
    border: 2px solid var(--vtx-border);
    border-top-color: var(--vtx-text-muted);
    border-radius: 50%;
    animation: vtx-spin .6s linear infinite;
}
.vtx-btn--primary.is-loading::after,
.vtx-btn--danger.is-loading::after { border-color: rgba(255, 255, 255, .35); border-top-color: #ffffff; }

/* Quien prefiera menos animación no debería ver nada girando. */
@media (prefers-reduced-motion: reduce) {
    .vtx-spinner, .vtx-btn.is-loading::after { animation-duration: 2.5s; }
}

/* Indicador discreto dentro de una tarjeta, para recálculos en vivo. */
.vtx-inline-busy {
    display: none; align-items: center; gap: .4rem;
    font-size: .75rem; color: var(--vtx-text-dim);
}
.vtx-inline-busy.open { display: inline-flex; }
.vtx-inline-busy .vtx-spinner { width: 13px; height: 13px; border-width: 2px; }

/* =========================================================================
   Pestañas
   Para formularios largos que se leen por bloques. Los paneles ocultos siguen
   en el DOM —solo se les quita la visibilidad— porque sus campos tienen que
   viajar en el envío igual que los visibles.
   ========================================================================= */
.vtx-tabs {
    background: var(--vtx-surface);
    border: 1px solid var(--vtx-border-soft);
    border-radius: var(--vtx-radius);
    overflow: hidden;
}

.vtx-tabs__bar {
    display: flex; flex-wrap: wrap;
    gap: .15rem;
    padding: 0 .5rem;
    border-bottom: 1px solid var(--vtx-border-soft);
    background: var(--vtx-tint);
}

.vtx-tab {
    appearance: none; background: none; border: 0;
    display: inline-flex; align-items: center; gap: .45rem;
    padding: .8rem .95rem;
    font-size: .85rem; font-weight: 500;
    color: var(--vtx-text-dim);
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    cursor: pointer;
    transition: color .15s ease, border-color .15s ease;
}
.vtx-tab i { font-size: .85rem; color: inherit; }
.vtx-tab:hover { color: var(--vtx-text); }
.vtx-tab[aria-selected="true"] {
    color: var(--vtx-accent);
    border-bottom-color: var(--vtx-accent);
}
.vtx-tab:focus-visible {
    outline: 2px solid var(--vtx-accent);
    outline-offset: -2px;
}

/* Cuántos campos de esa pestaña quedaron mal. Sin esto, un error en una pestaña
   cerrada deja al usuario ante un formulario que no guarda y no dice dónde. */
.vtx-tab__errors {
    display: none;
    min-width: 1.15rem; padding: 0 .3rem;
    border-radius: 999px;
    background: var(--vtx-danger); color: #fff;
    font-size: .68rem; font-weight: 700; line-height: 1.15rem;
    text-align: center;
}
.vtx-tab.has-errors { color: var(--vtx-danger); }
.vtx-tab.has-errors .vtx-tab__errors { display: inline-block; }

/* Estado de partida: sin JavaScript se ven todos los paneles, uno debajo de otro
   y con su título, de modo que el formulario sigue siendo usable. El script marca
   el contenedor como listo y a partir de ahí mandan las pestañas. */
.vtx-tabs__bar { display: none; }
.vtx-tabs__panel { display: block; padding: 1.15rem; }
.vtx-tabs__panel + .vtx-tabs__panel { border-top: 1px solid var(--vtx-border-soft); }
.vtx-tabs__panel-title {
    display: flex; align-items: center; gap: .6rem;
    font-size: .95rem; font-weight: 600; margin: 0 0 .9rem;
}
.vtx-tabs__panel-title i { color: var(--vtx-accent); }

.vtx-tabs--ready .vtx-tabs__bar { display: flex; }
.vtx-tabs--ready .vtx-tabs__panel { display: none; border-top: 0; }
.vtx-tabs--ready .vtx-tabs__panel.is-active { display: block; }
.vtx-tabs--ready .vtx-tabs__panel-title { display: none; }

@media (max-width: 640px) {
    .vtx-tabs__bar { padding: 0 .25rem; }
    .vtx-tab { padding: .7rem .6rem; font-size: .8rem; }
    .vtx-tab span.vtx-tab__text { display: none; }
    .vtx-tab i { font-size: 1rem; }
}

/* =========================================================================
   Mayúsculas
   El servidor guarda el texto en mayúsculas; esto hace que se vea así desde
   que se teclea, para que lo que se ve al capturar sea lo que se guarda.

   Se aplica al contenedor y no a cada campo: con `text-transform` en el
   elemento, el valor que viaja en el envío no cambia —solo su presentación—,
   así que la conversión real sigue estando del lado del servidor y esto es
   únicamente coherencia visual.
   ========================================================================= */
.vtx-input,
.vtx-textarea,
.vtx-select,
.vtx-table td,
.vtx-table th {
    text-transform: uppercase;
}

/* Lo que no es texto de negocio conserva su caja: el correo es la identidad
   con que alguien entra, la contraseña distingue mayúsculas, y una ruta o un
   token dejan de funcionar si se les cambia. */
.vtx-input[type="email"],
.vtx-input[type="password"],
.vtx-input[type="url"],
.vtx-input[type="date"],
.vtx-input[type="number"],
.vtx-sin-mayusculas,
.vtx-sin-mayusculas .vtx-input,
.vtx-sin-mayusculas .vtx-textarea {
    text-transform: none;
}

/* El texto de ayuda, las etiquetas y los avisos se leen mejor como se escribieron. */
.vtx-help,
.vtx-label,
.vtx-alert,
.vtx-empty,
.vtx-badge,
.vtx-page-head p,
.vtx-card__head h2,
.vtx-card__head h3 {
    text-transform: none;
}

/* =========================================================================
   Diálogos de confirmación
   SweetAlert2 trae su propia paleta clara. Aquí se le pasan los colores del
   tema para que no parezca una pieza pegada de otro sistema.
   ========================================================================= */
.vtx-swal.swal2-popup {
    background: var(--vtx-surface);
    color: var(--vtx-text);
    border: 1px solid var(--vtx-border-soft);
    border-radius: var(--vtx-radius);
    box-shadow: var(--vtx-shadow);
    font-family: inherit;
}

.vtx-swal .swal2-title {
    color: var(--vtx-text);
    font-size: 1.15rem;
    font-weight: 600;
}

.vtx-swal .swal2-html-container {
    color: var(--vtx-text-muted);
    font-size: .9rem;
}

.vtx-swal .swal2-actions { gap: .5rem; }

.vtx-swal .swal2-styled {
    border-radius: var(--vtx-radius-sm);
    font-size: .875rem;
    font-weight: 500;
    padding: .5rem 1.1rem;
    box-shadow: none;
}

/* El aro de foco propio se ve mejor que el que trae la librería. */
.vtx-swal .swal2-styled:focus-visible {
    outline: 2px solid var(--vtx-accent);
    outline-offset: 2px;
    box-shadow: none;
}

/* =========================================================================
   Modales
   Se apoyan en el modal de Bootstrap y solo le cambian la piel. Existen para
   no repetir los mismos estilos en línea en cada pantalla que abre uno.
   ========================================================================= */
.vtx-modal .modal-content {
    background: var(--vtx-surface);
    border: 1px solid var(--vtx-border);
    border-radius: var(--vtx-radius);
    color: var(--vtx-text);
}

.vtx-modal .modal-header,
.vtx-modal .modal-footer {
    border-color: var(--vtx-border-soft);
    padding: .85rem 1.15rem;
}

.vtx-modal .modal-title {
    font-size: 1rem;
    font-weight: 600;
    display: flex;
    align-items: center;
    gap: .5rem;
}

.vtx-modal .modal-title i { color: var(--vtx-accent); }

.vtx-modal .modal-body { padding: 0; }

/* El botón de cierre de Bootstrap trae su icono en un SVG negro fijo; el filtro
   lo lleva al color del texto para que no sea un borrón sobre fondo claro. */
.vtx-modal .btn-close {
    filter: none;
    opacity: .6;
}
.vtx-modal .btn-close:hover { opacity: 1; }

/* Dentro del modal las pestañas ya están enmarcadas por el propio diálogo. */
.vtx-modal .vtx-tabs {
    border: 0;
    border-radius: 0;
}

.vtx-modal .vtx-tabs__panel { max-height: min(60vh, 520px); overflow-y: auto; }

/* Lo que solo tiene sentido en la página completa: sus botones de guardar viven
   en el pie del diálogo cuando el mismo formulario se abre dentro de un modal.

   Lleva !important porque estos bloques suelen ir con .d-flex de Bootstrap, que
   declara display: flex !important y si no ganaría siempre. */
.vtx-modal .vtx-solo-pagina { display: none !important; }

/* Mientras se trae el formulario, el diálogo no debe saltar de tamaño. */
.vtx-modal__cargando {
    display: grid;
    place-items: center;
    gap: .6rem;
    min-height: 260px;
    color: var(--vtx-text-dim);
    font-size: .85rem;
}

/* =========================================================================
   Notificaciones flotantes
   El aviso de lo que acaba de pasar. Antes era un recuadro fijo que empujaba
   la tabla hacia abajo y solo se iba recargando la página.
   ========================================================================= */
.vtx-toast.swal2-popup {
    background: var(--vtx-surface);
    color: var(--vtx-text);
    border: 1px solid var(--vtx-border);
    border-radius: var(--vtx-radius);
    box-shadow: var(--vtx-shadow);
    padding: .85rem 1rem;
    font-family: inherit;
}

.vtx-toast .swal2-title {
    color: var(--vtx-text);
    font-size: .87rem;
    font-weight: 500;
    margin: 0;
    padding: 0;
    text-align: left;
}

.vtx-toast .swal2-timer-progress-bar { background: var(--vtx-accent); }

/* Queda por encima del modal de Bootstrap: si algo falla dentro de un diálogo,
   el aviso tiene que verse igual. */
.swal2-container.swal2-top-end { z-index: 1080; }

/* =========================================================================
   Filtros
   ========================================================================= */

/* Los checks de un filtro van en fila: apilados se comían el alto de la
   barra y empujaban la tabla fuera de la primera pantalla. */
.vtx-checks {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: .25rem 1rem;
}

.vtx-checks .vtx-check { margin: 0; }

/* Botón compacto, para barras de filtro donde el botón no es la acción
   principal de la pantalla. */
.vtx-btn--sm {
    padding: .35rem .7rem;
    font-size: .8rem;
    gap: .35rem;
}

.vtx-btn--sm i { font-size: .78rem; }

/* Botón pegado a un campo —imprimir, buscar, elegir—. Toma la altura del campo
   en lugar de la suya: el input y el botón tienen distinto relleno y tamaño de
   letra, así que igualarlos a base de píxeles se descuadra al primer retoque. */
.vtx-campo-con-boton {
    display: flex;
    gap: .5rem;
    align-items: stretch;
}

.vtx-campo-con-boton > .vtx-input-group,
.vtx-campo-con-boton > .vtx-input {
    flex: 1 1 auto;
    min-width: 0;
}

.vtx-campo-con-boton > .vtx-btn {
    flex: 0 0 auto;
    padding-top: 0;
    padding-bottom: 0;
}

/* Botones de acción de una fila de tabla: en línea y pegados a la derecha.
   Cada acción que envía algo va en su propio <form>, y esos formularios se
   envolvían en cuanto el botón crecía —al pasar de icono a icono con texto—
   dejando el de eliminar debajo del de editar.

   La clase se usa de dos formas y NO se comportan igual:

     · <div class="vtx-acciones"> dentro de la celda, en veintiún listados. Una caja
       flex, que es para lo que se escribió esto.

     · <td class="vtx-acciones">, la celda misma, en los otros veintiséis. Aquí el
       flex hace daño: un <td> que deja de ser table-cell no se estira al alto de la
       fila, y su borde inferior queda cuatro píxeles por encima del de las demás
       celdas. La raya de separación se ve partida justo antes de la última columna.
       La celda se queda como celda y los botones se alinean en línea. */
div.vtx-acciones {
    display: flex;
    justify-content: flex-end;
    align-items: center;
    gap: .35rem;
    flex-wrap: nowrap;
}

div.vtx-acciones > form { margin: 0; }

.vtx-acciones { white-space: nowrap; }

td.vtx-acciones { text-align: right; }
td.vtx-acciones > * { vertical-align: middle; }
td.vtx-acciones form { display: inline-block; margin: 0; }
td.vtx-acciones .vtx-btn--icono + .vtx-btn--icono,
td.vtx-acciones form + .vtx-btn--icono,
td.vtx-acciones .vtx-btn--icono + form { margin-left: .3rem; }

/* ------------------------------------------------------- datos de empresa --- */

.vtx-empresa { display: flex; gap: 1.4rem; align-items: flex-start; flex-wrap: wrap; }
.vtx-empresa__datos { flex: 1; min-width: 280px; margin: 0; }

.vtx-empresa__logo {
    flex: 0 0 auto;
    width: 180px; height: 180px;
    display: grid; place-items: center;
    padding: .8rem;
    border: 1px solid var(--vtx-border-soft);
    border-radius: var(--vtx-radius);
    background: var(--vtx-surface-2);
}

/* contain y no cover: un logo recortado dice otra cosa que el logo. */
.vtx-empresa__logo img { max-width: 100%; max-height: 100%; object-fit: contain; }

.vtx-empresa__sinlogo,
.vtx-logo__vacio {
    display: grid; place-items: center; gap: .3rem;
    color: var(--vtx-text-dim); font-size: .76rem;
}

.vtx-empresa__sinlogo i,
.vtx-logo__vacio i { font-size: 1.6rem; opacity: .5; }

/* Carga del logo, con su vista previa al lado del campo. */
.vtx-logo { display: flex; gap: 1.1rem; align-items: flex-start; flex-wrap: wrap; }
.vtx-logo__campos { flex: 1; min-width: 260px; }

.vtx-logo__marco {
    flex: 0 0 auto;
    width: 150px; height: 150px;
    display: grid; place-items: center;
    padding: .7rem;
    border: 1px dashed var(--vtx-border);
    border-radius: var(--vtx-radius);
    background: var(--vtx-surface-2);
}

.vtx-logo__marco img { max-width: 100%; max-height: 100%; object-fit: contain; }

/* --------------------------------------------------------------- atajos --- */

.vtx-atajos { display: grid; gap: .6rem; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }

.vtx-atajo {
    display: flex; align-items: center; gap: .8rem;
    padding: .8rem .9rem;
    border: 1px solid var(--vtx-border-soft);
    border-radius: var(--vtx-radius);
    background: var(--vtx-surface-2);
    color: var(--vtx-text);
    text-decoration: none;
    transition: border-color .15s ease, background .15s ease;
}

.vtx-atajo:hover { border-color: var(--vtx-accent); background: var(--vtx-tint); color: var(--vtx-text); }
.vtx-atajo i { font-size: 1.1rem; color: var(--vtx-accent); width: 1.4rem; text-align: center; }
.vtx-atajo strong { display: block; font-size: .88rem; }
.vtx-atajo small { color: var(--vtx-text-dim); font-size: .74rem; }

/* ------------------------------------------------------- tabla compacta --- */

/* Para las tablas que no son un listado sino un resumen —los totales de un
   documento—, donde cada renglón es una cifra corta y el aire de una rejilla de
   trabajo solo obliga a bajar la vista. */
.vtx-table--compacta td,
.vtx-table--compacta th { padding: .3rem .75rem; }

.vtx-table--compacta tbody tr:hover { background: transparent; }

/* El cuerpo de tarjeta que solo envuelve una de esas tablas tampoco necesita el
   margen de una pantalla completa. */
.vtx-card__body--ajustado { padding: .6rem .55rem; }

/* ---------------------------------------------------------------------------
   Lo que queda de sesión, al pie de la barra lateral.

   Discreto a propósito: es un dato de fondo, de los que se miran cuando uno se
   pregunta «¿me va a sacar?». Sólo levanta la voz —ámbar— en los últimos cinco
   minutos, que es cuando hay algo que hacer al respecto.
   --------------------------------------------------------------------------- */
.vtx-sesion {
    display: flex; align-items: center; gap: .5rem;
    padding: .75rem 1.15rem;
    border-top: 1px solid var(--vtx-border-soft);
    font-size: .8rem; color: var(--vtx-text-dim);
    letter-spacing: .2px;
}

.vtx-sesion i { font-size: .85rem; }

.vtx-sesion__reloj {
    /* Tabulares: sin esto el ancho baila con cada segundo y el contador tiembla. */
    font-variant-numeric: tabular-nums;
    font-size: 1rem; font-weight: 600; color: var(--vtx-text-muted);
}

.vtx-sesion--pronto { color: var(--vtx-warning, #f59e0b); }
.vtx-sesion--pronto .vtx-sesion__reloj { color: var(--vtx-warning, #f59e0b); }

/* =====================================================================
   Sello de entorno
   =====================================================================
   La franja que avisa de que lo que se ve no es producción.

   Va fija arriba del todo y por delante de la barra lateral, que también
   es fija y arranca en el borde superior: sin apartarla, la franja
   quedaría debajo y no la vería nadie.

   La altura es fija y el contenido no envuelve. No es capricho: la barra
   lateral y el armazón se apartan con esa misma medida, y una franja que
   creciera al partirse en dos líneas dejaría el menú tapado justo en las
   pantallas estrechas. Por eso la nota se calla antes de llegar a eso. */
:root { --vtx-sello-alto: 2rem; }

.vtx-sello {
    position: fixed;
    inset: 0 0 auto 0;
    z-index: 1100;
    height: var(--vtx-sello-alto);
    display: flex;
    align-items: center;
    gap: .55rem;
    padding: 0 .9rem;
    font-size: .82rem;
    white-space: nowrap;
    overflow: hidden;
    border-bottom: 1px solid rgba(0, 0, 0, .2);
}

.vtx-sello strong { font-weight: 700; }

.vtx-sello__detalle {
    padding: .05rem .45rem;
    border-radius: 4px;
    background: rgba(0, 0, 0, .13);
}

.vtx-sello__nota { opacity: .85; }

@media (max-width: 980px) {
    .vtx-sello__nota { display: none; }
}

@media (max-width: 620px) {
    .vtx-sello__ecf { display: none; }
}

/* Las rayas no son adorno: son lo que hace que se reconozca de reojo, sin
   leer, y lo que impide confundirla con un aviso más del sistema. */
.vtx-sello--pruebas {
    color: #451a03;
    background: repeating-linear-gradient(135deg,
        #fbbf24, #fbbf24 14px, #f59e0b 14px, #f59e0b 28px);
}

.vtx-sello--peligro {
    color: #ffffff;
    background: repeating-linear-gradient(135deg,
        #dc2626, #dc2626 14px, #b91c1c 14px, #b91c1c 28px);
}

body:has(.vtx-sello) .vtx-shell {
    height: calc(100vh - var(--vtx-sello-alto));
    height: calc(100dvh - var(--vtx-sello-alto));
    margin-top: var(--vtx-sello-alto);
}

body:has(.vtx-sello) .vtx-sidebar { top: var(--vtx-sello-alto); }

/* =========================================================================
   Barra de listado

   La cabecera de una pantalla de listado ocupaba tres renglones: el título
   grande —que la barra superior ya repite—, la fila de filtros y, encima de
   todo, el botón de crear. Eran unos 120 px antes de la primera fila de datos,
   en pantallas cuyo trabajo es precisamente mostrar filas.

   Ahora los filtros y la acción principal comparten un solo renglón: los
   campos crecen, el botón se va al final. El título sale del documento porque
   la barra superior ya lo lleva.
   ========================================================================= */

.vtx-barra-listado {
    display: flex;
    align-items: flex-end;
    flex-wrap: wrap;
    gap: .55rem .7rem;
}

/* El formulario no forma caja: sus campos son hijos directos de la barra.

   Con el formulario como una caja más, sus campos no podían repartirse entre
   renglones: o cabían todos o el formulario entero se llevaba el ancho y el
   botón caía a un renglón propio. Sin caja, filtros y botón fluyen en la misma
   corriente y el botón se acomoda al final de donde quede sitio.

   El <form> sigue existiendo en el documento: sólo desaparece su caja, así que
   el envío y los scripts que lo buscan funcionan igual. */
.vtx-barra-listado > form {
    display: contents;
}

/* `margin-left:auto` y no `justify-content`: así el botón se pega al extremo
   aunque los filtros no llenen el ancho, y al envolverse en pantalla estrecha
   cae solo en su renglón en vez de quedar apretado contra el último campo. */
.vtx-barra-listado__accion {
    margin-left: auto;
    flex: 0 0 auto;
}

/* Los campos de la barra no llevan el margen inferior de un formulario: aquí
   debajo no viene otro campo, viene la tabla. */
.vtx-barra-listado .vtx-field { margin-bottom: 0; }

/* `vtx-col` nació para una rejilla de formulario, donde exigir 200 px de ancho
   mínimo tiene sentido. En la barra no: con cuatro o cinco filtros esos mínimos
   suman más que la pantalla y la barra se parte en tres renglones, que era
   justo lo que se quería evitar.

   Pero soltarlo del todo tampoco sirve: los campos se encogen hasta cortar el
   texto —una fecha que enseña «07/2…», un desplegable que dice «TODC»—. 9rem es
   lo que mide una fecha completa con su icono, y por debajo de eso no bajan.

   Con el mínimo rebajado, el `max-width` que cada filtro lleva escrito pasa a
   mandar —hasta ahora el mínimo lo anulaba— y el campo de buscar, que no tiene
   tope, se queda con el espacio que sobra. */
.vtx-barra-listado .vtx-col { min-width: 9rem; }

/* Los checks de un filtro, uno debajo del otro.

   `vtx-checks` los pone en fila porque apilados hacían crecer la barra. Dentro de
   `vtx-barra-listado` eso ya no pasa: al lado hay campos con etiqueta, que son más
   altos que dos checks juntos, así que apilarlos no cuesta alto y estrecha la barra. */
.vtx-checks--apilados {
    flex-direction: column;
    align-items: flex-start;
    gap: .3rem;
}

/* La acción de la barra reducida a su icono.

   Más grande que un botón de fila: es la acción principal de la pantalla y ahora no
   tiene texto que la anuncie, así que el blanco tiene que verse de lejos. Se aplica
   por estar en la barra y no por llevar la clase `__accion`: en las pantallas con
   varios botones esa clase se quedó en el primero, que suele ser un enlace de
   navegación, y el de crear se habría quedado pequeño. */
.vtx-barra-listado .vtx-btn--icono {
    width: 2.4rem;
    height: 2.4rem;
}

.vtx-barra-listado .vtx-btn--icono i { font-size: 1.05rem; }

/* El icono de dentro de un botón toma el color del botón, no el de la cabecera.

   `.vtx-card__head i` pinta de acento todos los iconos de la cabecera —está pensado
   para el iconito que acompaña al título—, y ese color le ganaba al blanco del botón,
   que sólo se hereda. En un botón primario eso deja el icono del mismo color que su
   fondo: invisible. Se veía igual antes, pero el texto blanco al lado lo disimulaba;
   al dejar el botón sólo con el signo, quedó un cuadro vacío. */
.vtx-card__head .vtx-btn i { color: inherit; }

/* Las dos formas de agregar una línea a un documento: buscar en el catálogo o
   escribirla a mano. Van en el mismo renglón porque son alternativas entre sí, con
   una raya que las separa sin sugerir que una dependa de la otra. */
.vtx-agregar-linea {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: .6rem;
    flex-wrap: wrap;

    /* Reclama el hueco que queda en la cabecera. Con `ms-auto` no lo reclamaba: el
       sobrante se lo llevaba el margen y la barra se quedaba en su ancho mínimo, con
       lo que el buscador y el botón caían en renglones distintos aun sobrando sitio. */
    flex: 1 1 auto;
    min-width: 0;
}

/* La raya viaja pegada al botón. Suelta en la barra, al partirse ésta en dos
   renglones la raya se quedaba arriba separando nada, y con cero de alto porque no
   tenía contra qué estirarse. */
.vtx-agregar-linea__aparte {
    display: flex;
    align-items: stretch;
    gap: .6rem;
    flex: 0 0 auto;
}

/* Toma el alto de lo que tiene al lado y no uno fijo: así no se descuadra al
   retocar el relleno de los campos ni el tamaño de los botones. */
.vtx-agregar-linea__raya {
    width: 1px;
    margin: .15rem 0;
    background: var(--vtx-border);
    flex: 0 0 auto;
}

/* =========================================================================
   Indicadores: todos en el mismo renglón
   ========================================================================= */

/* `auto-fill` repartía por un mínimo de 232 px, así que en cuanto no cabían seis
   el último se caía solo a un segundo renglón —y quedaba un hueco al lado—. Ahora
   se reparten el ancho que haya: son de dos a seis por pantalla y caben siempre.

   `grid-template-columns: none` es imprescindible y no sobra: `grid-auto-columns`
   sólo manda en las pistas implícitas, y la plantilla anterior seguía creando
   pistas explícitas de 232 px. Sin anularla, los tres primeros medían 232 y los
   otros tres cero. */
.vtx-grid--kpi {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: minmax(0, 1fr);
}

/* Cada tarjeta se mide a sí misma.

   La cifra se ajusta a lo que su tarjeta da de sí, no al ancho de la ventana. Es
   lo único exacto: el menú lateral se abre y se cierra, así que a igual ventana
   la tarjeta puede medir 247 px o 164, y con consultas de ventana habría que
   adivinar en cuál de los dos estados estamos. */
.vtx-kpi {
    container-type: inline-size;
    min-width: 0;
}

/* Sin esto el bloque de textos no baja de su contenido y desborda la tarjeta. */
.vtx-kpi > div:last-child { min-width: 0; }

.vtx-kpi__value { font-size: clamp(.95rem, 9.5cqw, 1.5rem); }

/* Apretada, el icono estorba más de lo que aporta: el color lo sigue dando la
   franja de arriba, y el sitio lo necesita la cifra.

   El umbral se mide sobre la CAJA DE CONTENIDO de la tarjeta —lo que hace una
   consulta de contenedor—, no sobre su ancho total: son 29 px menos de relleno. */
@container (max-width: 170px) {
    .vtx-kpi__icon { display: none; }
}

/* En teléfono o tableta —donde el menú ya pasa a superponerse— se reparten en
   varias filas: seis columnas de cien píxeles no las lee nadie. */
@media (max-width: 991px) {
    .vtx-grid--kpi {
        grid-auto-flow: row;
        grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    }

    .vtx-kpi__value { font-size: 1.35rem; }
}

/* =========================================================================
   Las sugerencias no se cortan contra el borde de la tarjeta
   ========================================================================= */

/* `.vtx-card` recorta a propósito —así una tabla a ras no le cuadra las esquinas
   redondeadas—, pero eso también recortaba la lista de sugerencias del buscador
   de clientes y de artículos: se veía el primer nombre y el resto desaparecía
   contra el borde. Pasaba en cotizaciones, donde la tarjeta del encabezado es
   corta; en facturas la lista cabía por poco y el fallo no se notaba.

   La tarjeta que contiene un buscador deja de recortar. */
.vtx-card:has(.vtx-suggest) { overflow: visible; }

/* Y lo que sí había que recortar se recorta por su cuenta: el cuerpo a ras
   redondea sus esquinas de abajo, que es lo único que el recorte de la tarjeta
   estaba resolviendo. La lista no vive aquí dentro, así que no la toca. */
.vtx-card:has(.vtx-suggest) .vtx-card__body--flush {
    overflow: hidden;
    border-radius: 0 0 var(--vtx-radius) var(--vtx-radius);
}
