/* ═══════════════════════════════════════════════════════════════════════
   NÚCLEO — tokens y piezas sueltas compartidas por todo el sistema.

   Antes cada plantilla traía su propio bloque <style> con su propia copia
   de los colores, los botones y los campos de formulario: seis pantallas de
   acceso repetían el MISMO CSS palabra por palabra, así que cambiar el azul
   de marca obligaba a editar nueve archivos y siempre se quedaba uno atrás.
   Todo eso vive aquí una sola vez.

   El sistema tiene dos mundos:

     · claro  — el panel de trabajo (base.html). Se usa horas seguidas, con
                tablas y formularios largos, así que manda la legibilidad.
     · oscuro — portada y pantallas de acceso (.mundo-oscuro en <html>).
                Son la primera impresión del producto y se ven un minuto.

   Los dos usan LOS MISMOS nombres de variable y las MISMAS clases: el mundo
   oscuro solo reescribe los valores. Por eso .btn, .input o .badge se ven
   correctos en ambos lados sin una sola regla duplicada.

   Compatibilidad: las variables viejas (--navy, --blue, --shadow-lg…) siguen
   existiendo porque decenas de plantillas las usan en estilos propios. Se
   conservan los nombres y solo se afinaron los valores.
   ═══════════════════════════════════════════════════════════════════════ */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

:root {
    /* ── Tinta: el azul casi negro de la marca ─────────────────────────── */
    --ink-900:    #05070e;
    --ink-800:    #090d18;
    --ink-700:    #0e1524;
    --ink-600:    #16203a;
    --navy:       #05070e;   /* compat */
    --navy-mid:   #090d18;   /* compat */
    --navy-light: #16203a;   /* compat */
    --blue:       #1a4a8c;   /* compat */
    --blue-mid:   #1e5eb0;   /* compat */

    /* ── Acento ────────────────────────────────────────────────────────────
       OJO: este es el color fijo de la interfaz del programa y a propósito
       NO sale de cuenta.color_primario. Ese campo es solo para el formato
       de las cotizaciones (ver configuracion_formato.html y generar_pdf.py)
       y no debe mover los colores del sistema: si cada cuenta pinta su
       propio panel, deja de existir un producto y empiezan a existir
       cientos, cada uno con sus propios problemas de contraste.

       El azul de marca era #2d8bff. Como COLOR DE RELLENO se ve bien, pero
       es el fondo de todos los botones primarios con texto blanco encima:
       3.4:1, por debajo del mínimo de 4.5:1. Y como color de enlace sobre
       blanco, lo mismo. Así que el token pasa a un azul un punto más
       profundo (4.9:1 con blanco, en los dos sentidos) y el brillante se
       queda en --accent-brillante, para degradados y adornos donde no hay
       texto que leer. */
    --accent:           #1a6fd4;
    --accent-dk:        #1457ab;
    --accent-brillante: #2d8bff;
    --accent-suave:     rgba(26,111,212,.08);
    --accent-glow:      rgba(26,111,212,.20);
    --cyan:             #00c8ff;

    /* ── Superficies del mundo claro ───────────────────────────────────── */
    --surface:      #ffffff;
    --surface-2:    #f5f8fc;
    --surface-3:    #e9eff7;
    --border:       #e4eaf3;
    --border-fuerte:#d3ddea;

    /* ── Texto ─────────────────────────────────────────────────────────────
       --text-3 era #7a92ab: sobre blanco daba 3.1:1, por debajo del mínimo
       de 4.5:1 que pide WCAG para texto normal, y es el color de CASI TODO
       el texto secundario de la app (166 usos). Ahora da 5.0:1 sobre blanco
       y 4.7:1 sobre --surface-2, que es el otro fondo donde aparece. */
    --text-1: #0a1120;
    --text-2: #3d5068;
    --text-3: #5f7189;

    /* ── Semáforo ──────────────────────────────────────────────────────────
       Oscurecidos respecto a los originales (#00c48c / #f5a623 / #e94b4b)
       por la misma razón: se usan como COLOR DE TEXTO en avisos y etiquetas,
       y en su versión brillante no se leían sobre fondo claro. El mundo
       oscuro los vuelve a subir de brillo más abajo, donde sí funcionan. */
    --success: #097f57;
    --warning: #a66200;
    --danger:  #d42d4a;
    /* Relleno suave de cada uno, como token y no calculado al vuelo: así el
       mundo oscuro puede dar el suyo (más luminoso) sin depender de
       color-mix(), que no existe en navegadores de hace un par de años. */
    --success-suave: rgba(9,127,87,.10);
    --warning-suave: rgba(166,98,0,.11);
    --danger-suave:  rgba(212,45,74,.09);
    --success-borde: rgba(9,127,87,.28);
    --warning-borde: rgba(166,98,0,.30);
    --danger-borde:  rgba(212,45,74,.26);

    /* ── Forma ─────────────────────────────────────────────────────────── */
    --radius:    14px;
    --radius-sm: 10px;
    --radius-lg: 20px;

    /* La sombra de reposo es mínima a propósito: la tarjeta ya está
       separada del fondo por su borde de un pelo, y un difuminado ancho
       encima de un borde fino es el "flotando en la nada" de cualquier
       plantilla genérica. La sombra grande se guarda para lo que de verdad
       está por encima del plano: diálogos, menús, la tarjeta al pasar el
       cursor. Las dos llevan desplazamiento Y difuminado — un halo de color
       centrado no es profundidad, es decoración. */
    --shadow:    0 1px 2px rgba(9,17,33,.05);
    --shadow-lg: 0 2px 4px rgba(9,17,33,.06), 0 18px 36px -18px rgba(9,17,33,.26);

    --sidebar-w: 264px;
    --nav-h:     64px;

    /* ── Tipografía ────────────────────────────────────────────────────────
       Geist para la interfaz y Geist Mono para todo lo que sea cantidad:
       precios, existencias, folios, fechas. La mono no está de adorno —
       tiene cifras de ancho fijo, así que una columna de importes queda
       alineada por el punto decimal sin trucos. Ambas autoalojadas en
       static/vendor/googlefonts/geist.css (ver scripts/bajar_geist.py). */
    --fuente:      'Geist', 'Inter', 'Plus Jakarta Sans', system-ui, sans-serif;
    --fuente-dato: 'Geist Mono', 'DM Mono', ui-monospace, monospace;
    /* Archivo, un poco expandida, solo para titulares. Ver geist.css. */
    --fuente-display: 'Archivo', 'Geist', system-ui, sans-serif;

    /* Curva de salida exponencial: arranca rápido y frena suave, que es como
       se mueven las cosas con masa. La usa todo lo que anima. */
    --salida: cubic-bezier(.22,1,.36,1);
}

/* ═══ MUNDO OSCURO ═══════════════════════════════════════════════════════
   Mismos nombres, otros valores. Se activa con class="mundo-oscuro" en el
   <html> de la portada y de las pantallas de acceso. */
.mundo-oscuro {
    --surface:      #090d18;
    --surface-2:    #0e1524;
    --surface-3:    #16203a;
    --border:       rgba(255,255,255,.09);
    --border-fuerte:rgba(255,255,255,.16);

    --text-1: #f2f6fc;
    --text-2: #aebfd4;
    --text-3: #8296ae;

    --accent:       #5aabff;
    --accent-dk:    #2d8bff;
    --accent-suave: rgba(90,171,255,.12);

    --success: #34d399;
    --warning: #fbbf24;
    --danger:  #ff6b6b;
    --success-suave: rgba(52,211,153,.13);
    --warning-suave: rgba(251,191,36,.13);
    --danger-suave:  rgba(255,107,107,.13);
    --success-borde: rgba(52,211,153,.32);
    --warning-borde: rgba(251,191,36,.32);
    --danger-borde:  rgba(255,107,107,.32);

    --shadow:    0 1px 2px rgba(0,0,0,.4), 0 18px 40px -20px rgba(0,0,0,.7);
    --shadow-lg: 0 2px 6px rgba(0,0,0,.4), 0 40px 80px -30px rgba(0,0,0,.85);
}

/* ═══ BASE ══════════════════════════════════════════════════════════════ */
html { scroll-behavior: smooth; -webkit-text-size-adjust: 100%; }

body {
    font-family: var(--fuente);
    font-size: 15px;
    line-height: 1.55;
    color: var(--text-1);
    background: var(--surface-2);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    /* Geist trae cifras proporcionales por omisión; en una app que es casi
       toda números conviene lo contrario en todos lados. */
    font-variant-numeric: tabular-nums;
}

.mundo-oscuro body { background: var(--ink-900); }

a { text-decoration: none; color: inherit; }

h1, h2, h3, h4 {
    font-weight: 600;
    letter-spacing: -.025em;
    line-height: 1.15;
    text-wrap: balance;
}

/* Los titulares de verdad —los que ocupan media pantalla— van en Archivo
   con el eje de ancho abierto. Los títulos chicos de tarjeta se quedan en
   Geist: a 15px la diferencia no se nota y sí se nota el cambio de ritmo. */
.display,
.hero h1,
.seccion-titulo h2,
.cierre h2,
.page-header h1,
.logo-title,
.marca-texto {
    font-family: var(--fuente-display);
    font-stretch: 112%;
}

/* Cifras y folios: mono, para que las columnas cuadren. */
.dato, .cifra {
    font-family: var(--fuente-dato);
    font-variant-numeric: tabular-nums;
    letter-spacing: -.02em;
}

/* ── Superficies del navegador ─────────────────────────────────────────────
   Selección, cursor de texto, barra de desplazamiento y anillo de foco son
   parte del diseño aunque no las dibujemos nosotros. Sin esto, cada una
   aparece con el color por omisión del navegador, que no pertenece a
   ningún sistema. */
::selection { background: var(--accent); color: #fff; }
.mundo-oscuro ::selection { background: rgba(90,171,255,.32); color: #fff; }

input, textarea, select { caret-color: var(--accent); }

* { scrollbar-width: thin; scrollbar-color: var(--border-fuerte) transparent; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
    background: var(--border-fuerte);
    border: 3px solid transparent;
    background-clip: content-box;
    border-radius: 100px;
}
::-webkit-scrollbar-thumb:hover { background-color: var(--text-3); background-clip: content-box; }

/* Un solo anillo de foco para todo el sistema, visible en ambos mundos.
   :focus-visible y no :focus, para que no aparezca al hacer clic con el
   ratón pero sí al llegar con el tabulador. */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: 4px;
}

/* ═══ BOTONES ═══════════════════════════════════════════════════════════ */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    padding: 10px 18px;
    border-radius: var(--radius-sm);
    font-family: var(--fuente);
    font-size: .875rem;
    font-weight: 550;
    letter-spacing: -.01em;
    line-height: 1.2;
    cursor: pointer;
    border: 1px solid transparent;
    white-space: nowrap;
    transition: background .18s ease, border-color .18s ease,
                color .18s ease, box-shadow .18s ease, transform .12s ease;
}

.btn:active { transform: translateY(.5px) scale(.995); }

.btn:disabled, .btn[aria-disabled="true"] {
    opacity: .5;
    cursor: not-allowed;
    transform: none;
    box-shadow: none;
}

.btn-grande { padding: 14px 26px; font-size: .95rem; border-radius: 12px; }
.btn-chico  { padding: 7px 13px; font-size: .8rem; }

.btn-primary, .btn-primario {
    background: var(--accent);
    color: #fff;
    box-shadow: 0 1px 2px rgba(9,17,33,.12);
}
.btn-primary:hover, .btn-primario:hover {
    background: var(--accent-dk);
    box-shadow: 0 2px 6px rgba(9,17,33,.18);
}

.btn-outline, .btn-fantasma {
    background: transparent;
    color: var(--text-1);
    border-color: var(--border-fuerte);
}
.btn-outline:hover, .btn-fantasma:hover {
    border-color: var(--accent);
    color: var(--accent);
    background: var(--accent-suave);
}

.btn-dark {
    background: var(--ink-700);
    color: #fff;
}
.btn-dark:hover { background: var(--ink-600); }

/* Botón claro sobre tinta: el contrario del primario, para la portada. */
.btn-claro {
    background: #fff;
    color: var(--ink-900);
    font-weight: 600;
}
.btn-claro:hover {
    background: #fff;
    box-shadow: 0 0 0 4px rgba(255,255,255,.14);
}

.btn-sutil {
    background: transparent;
    color: var(--text-2);
}
.btn-sutil:hover { background: var(--surface-3); color: var(--text-1); }

/* El giro de "cargando" que usan los formularios de acceso. */
.spinner {
    display: none;
    width: 16px; height: 16px;
    border: 2px solid rgba(255,255,255,.3);
    border-top-color: currentColor;
    border-radius: 50%;
    animation: girar .6s linear infinite;
}
.loading .spinner  { display: block; }
.loading .btn-text { display: none; }
.loading .btn-icon { display: none; }
@keyframes girar { to { transform: rotate(360deg); } }

/* ═══ CAMPOS ════════════════════════════════════════════════════════════ */
label {
    display: block;
    font-size: .78rem;
    font-weight: 550;
    color: var(--text-2);
    margin-bottom: 7px;
    letter-spacing: 0;
    /* Sin VERSALITAS: en español las mayúsculas sostenidas pierden los
       acentos visuales de las palabras largas ("DESCRIPCIÓN DEL PRODUCTO")
       y se leen más lento. El peso y el color ya separan la etiqueta. */
    text-transform: none;
}

.field { margin-bottom: 18px; }

.field-hint, .campo-nota {
    font-size: .78rem;
    color: var(--text-3);
    margin-top: 6px;
}

.input, .field input, .field select, .field textarea {
    width: 100%;
    padding: 11px 14px;
    background: var(--surface);
    border: 1px solid var(--border-fuerte);
    border-radius: var(--radius-sm);
    font-family: var(--fuente);
    font-size: .9rem;
    color: var(--text-1);
    outline: none;
    transition: border-color .18s, box-shadow .18s, background .18s;
}

.input::placeholder,
.field input::placeholder,
.field textarea::placeholder { color: var(--text-3); }

.input:hover,
.field input:hover { border-color: var(--text-3); }

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

/* El anillo del campo ya comunica el foco; el outline general encima se ve
   como un doble borde. */
.input:focus-visible,
.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible { outline: none; }

.input-wrap { position: relative; display: flex; align-items: center; }

.input-wrap .input-icon {
    position: absolute; left: 14px;
    color: var(--text-3);
    font-size: .85rem;
    pointer-events: none;
    transition: color .18s;
}
.input-wrap:focus-within .input-icon { color: var(--accent); }
/* En estas pantallas el <i> va después del <input> (para que el ícono quede
   encima), así que no hay selector de hermano que valga: todos los
   .input-wrap del sistema llevan ícono a la izquierda. */
.input-wrap input { padding-left: 40px; }
.input-wrap.sin-icono input { padding-left: 14px; }

.toggle-pass {
    position: absolute; right: 10px;
    background: none; border: none;
    color: var(--text-3);
    cursor: pointer;
    font-size: .85rem;
    padding: 6px;
    border-radius: 6px;
    transition: color .18s, background .18s;
}
.toggle-pass:hover { color: var(--text-1); background: var(--surface-3); }

/* ═══ AVISOS ════════════════════════════════════════════════════════════ */
.alert {
    display: none;
    align-items: flex-start;
    gap: 10px;
    padding: 11px 14px;
    border-radius: var(--radius-sm);
    font-size: .85rem;
    margin-bottom: 20px;
    border: 1px solid;
    animation: entrar .25s var(--salida);
}
.alert.show { display: flex; }
.alert i { margin-top: .18em; flex-shrink: 0; }
.alert.error   { background: var(--danger-suave);  border-color: var(--danger-borde);  color: var(--danger); }
.alert.success { background: var(--success-suave); border-color: var(--success-borde); color: var(--success); }
.alert.info    { background: var(--accent-suave); border-color: var(--accent-glow); color: var(--accent); }

@keyframes entrar { from { opacity: 0; transform: translateY(-4px); } to { opacity: 1; transform: none; } }

/* ═══ ETIQUETAS ═════════════════════════════════════════════════════════ */
.badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 10px;
    border-radius: 100px;
    font-size: .74rem;
    font-weight: 550;
    line-height: 1.45;
    white-space: nowrap;
}

.badge-blue   { background: var(--accent-suave); color: var(--accent); }
.badge-green  { background: var(--success-suave); color: var(--success); }
.badge-yellow { background: var(--warning-suave); color: var(--warning); }
.badge-red    { background: var(--danger-suave);  color: var(--danger); }
.badge-gris   { background: var(--surface-3); color: var(--text-2); }

/* ═══ TABLAS ════════════════════════════════════════════════════════════ */
.table-wrap { overflow-x: auto; }

table { width: 100%; border-collapse: collapse; font-size: .875rem; }

thead th {
    padding: 12px 16px;
    text-align: left;
    font-size: .78rem;
    font-weight: 550;
    color: var(--text-3);
    border-bottom: 1px solid var(--border);
    white-space: nowrap;
    letter-spacing: 0;
}

tbody td {
    padding: 13px 16px;
    border-bottom: 1px solid var(--border);
    color: var(--text-1);
    vertical-align: middle;
}

tbody tr:last-child td { border-bottom: none; }
tbody tr { transition: background .15s; }
tbody tr:hover { background: var(--surface-2); }

/* ═══ ESTELAS ═══════════════════════════════════════════════════════════
   El haz de luz que atraviesa la portada y la cabecera del panel. Es una
   imagen generada (scripts/generar_estelas.py), no un degradado suelto:
   son filamentos que nacen juntos a la izquierda y se abren hacia la
   derecha. Siempre detrás del contenido y siempre inerte al ratón. */
.estelas {
    position: absolute;
    inset: 0;
    pointer-events: none;
    background-repeat: no-repeat;
    background-position: center;
    background-size: cover;
    z-index: 0;
}
.estelas-oscuro { background-image: url('../img/estelas-oscuro.svg'); }
.estelas-claro  { background-image: url('../img/estelas-claro.svg'); }

/* ═══ ANIMACIÓN DE ENTRADA ══════════════════════════════════════════════
   Un solo gesto, escalonado: el contenido sube al lugar que ya ocupa. */
@keyframes subir {
    from { opacity: 0; transform: translateY(14px); }
    to   { opacity: 1; transform: none; }
}

.anim { animation: subir .5s var(--salida) both; }
.anim-d1 { animation-delay: .04s; }
.anim-d2 { animation-delay: .08s; }
.anim-d3 { animation-delay: .12s; }
.anim-d4 { animation-delay: .16s; }
.anim-d5 { animation-delay: .20s; }
.anim-d6 { animation-delay: .24s; }

/* Quien pidió menos movimiento en su sistema operativo no lo pidió como
   sugerencia. */
@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
    }
}
