/* ==========================================================================
   UPrex — hoja de estilos única.

   Mobile-first de verdad: esto se usa parado en la calle, con una mano, con sol
   encima de la pantalla. De ahí salen tres decisiones que no son de gusto:
   contraste alto, objetivos táctiles de 44px, y nada de íconos donde una palabra
   dice lo mismo sin ambigüedad.
   ========================================================================== */

/* --------------------------------------------------------------------------
   LOS COLORES SON TOKENS Y NADA LOS ESCRIBE A MANO.

   El tema lo elige la persona (Mi cuenta → Apariencia) y se guarda en el
   navegador: es del APARATO, no del negocio. El cobrador quiere la pantalla
   oscura a la noche en su teléfono y el administrador la quiere clara en la PC
   de la oficina, y los dos entran con la misma cuenta.

   Son TRES estados y no dos. Elegir "Claro" u "Oscuro" estampa data-tema en el
   <html>; el default —"Automático"— no estampa nada y ahí manda el sistema
   operativo. Por eso el bloque de abajo define el tema claro COMPLETO sobre
   :root pelado, la media query redefine sólo los tokens (guardada con
   :not([data-tema=claro]) para que la elección explícita le gane al sistema), y
   [data-tema=oscuro] los redefine otra vez para que gane en el otro sentido.

   La regla que no se puede romper: NINGÚN color se declara adentro de la media
   query ni del [data-tema]. Un color cuya única definición viva ahí no existe en
   el estado sin estampar, y la pantalla sale con el texto de un tema sobre el
   fondo del otro.
   -------------------------------------------------------------------------- */

:root {
    --fondo:          #f3f4f6;
    --superficie:     #ffffff;
    --superficie-2:   #f8f9fb;
    --borde:          #dcdfe4;
    --borde-fuerte:   #c3c8d0;

    --texto:          #171b23;
    --texto-suave:    #58616f;
    --texto-tenue:    #838b98;

    /* --primario es el ACENTO (enlaces, foco, botón, pestaña activa) y --barra
       es el FONDO del header. En claro dan el mismo azul y parece que sobra uno;
       en oscuro no pueden serlo: un acento legible sobre fondo negro tiene que
       ser claro, y un header claro con letra blanca no se lee. */
    --primario:       #1e3a5f;
    --primario-hover: #16304f;
    --primario-suave: #eaeff5;
    --sobre-primario: #ffffff;

    --barra:          #1e3a5f;
    --sobre-barra:    #ffffff;

    --exito:          #08694a;
    --exito-suave:    #e6f4ef;
    --peligro:        #a4231b;
    --peligro-hover:  #8c1d16;
    --peligro-suave:  #fdecea;
    --aviso:          #9a5b06;
    --aviso-suave:    #fdf3e3;

    /* Los dos velos. El de "cargando" es del color del FONDO y por eso es un
       token: escrito a mano en claro, en tema oscuro tapaba la pantalla con una
       cortina blanca — que es exactamente el fogonazo que el modo oscuro viene a
       evitar. */
    --velo:           rgba(23, 27, 35, .45);
    --velo-carga:     rgba(243, 244, 246, .72);

    --panel:          #262a33;
    --panel-activo:   #eef0f3;

    --radio:      8px;
    --radio-chico: 5px;
    --e1: .25rem;
    --e2: .5rem;
    --e3: .75rem;
    --e4: 1rem;
    --e5: 1.5rem;
    --e6: 2rem;

    --alto-header:   3.25rem;
    --alto-navpie:   3.5rem;
    --sombra:        0 1px 2px rgba(23, 27, 35, .06), 0 1px 3px rgba(23, 27, 35, .04);
    --sombra-flotante: 0 4px 16px rgba(23, 27, 35, .14);

    color-scheme: light;
}

/* Automático: manda el sistema operativo. Guardada con :not([data-tema=claro])
   para que quien eligió claro a mano no vea oscuro por tener el celular en
   modo noche. */
@media (prefers-color-scheme: dark) {
    :root:not([data-tema="claro"]) {
        --fondo:          #0f1319;
        --superficie:     #171c24;
        --superficie-2:   #1e242e;
        --borde:          #2b3340;
        --borde-fuerte:   #3d4756;

        --texto:          #e9ecf1;
        --texto-suave:    #a3adbd;
        --texto-tenue:    #788291;

        --primario:       #86b0e8;
        --primario-hover: #a3c4f0;
        --primario-suave: #1b2738;
        --sobre-primario: #0d1218;

        --barra:          #10151d;
        --sobre-barra:    #e9ecf1;

        --exito:          #4cc79a;
        --exito-suave:    #10261e;
        --peligro:        #f08a7d;
        --peligro-hover:  #f5a99e;
        --peligro-suave:  #2c1613;
        --aviso:          #e0b055;
        --aviso-suave:    #2a2113;

        --velo:           rgba(0, 0, 0, .62);
        --velo-carga:     rgba(15, 19, 25, .78);

        --panel:          #0c0f14;
        --panel-activo:   #1e242e;

        /* En oscuro una sombra clara no existe: lo que separa una tarjeta del
           fondo es una sombra MÁS negra, no más suave. */
        --sombra:        0 1px 2px rgba(0, 0, 0, .5), 0 1px 3px rgba(0, 0, 0, .35);
        --sombra-flotante: 0 4px 20px rgba(0, 0, 0, .6);

        color-scheme: dark;
    }
}

/* Elegido a mano. Repite los mismos tokens para ganarle al sistema en el otro
   sentido: celular en claro y app en oscuro. */
:root[data-tema="oscuro"] {
    --fondo:          #0f1319;
    --superficie:     #171c24;
    --superficie-2:   #1e242e;
    --borde:          #2b3340;
    --borde-fuerte:   #3d4756;

    --texto:          #e9ecf1;
    --texto-suave:    #a3adbd;
    --texto-tenue:    #788291;

    --primario:       #86b0e8;
    --primario-hover: #a3c4f0;
    --primario-suave: #1b2738;
    --sobre-primario: #0d1218;

    --barra:          #10151d;
    --sobre-barra:    #e9ecf1;

    --exito:          #4cc79a;
    --exito-suave:    #10261e;
    --peligro:        #f08a7d;
    --peligro-hover:  #f5a99e;
    --peligro-suave:  #2c1613;
    --aviso:          #e0b055;
    --aviso-suave:    #2a2113;

    --velo:           rgba(0, 0, 0, .62);
    --velo-carga:     rgba(15, 19, 25, .78);

    --panel:          #0c0f14;
    --panel-activo:   #1e242e;

    --sombra:        0 1px 2px rgba(0, 0, 0, .5), 0 1px 3px rgba(0, 0, 0, .35);
    --sombra-flotante: 0 4px 20px rgba(0, 0, 0, .6);

    color-scheme: dark;
}

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

html { -webkit-text-size-adjust: 100%; }

body {
    margin: 0;
    background: var(--fondo);
    color: var(--texto);
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
    font-size: 15px;
    line-height: 1.5;
    /* El rebote elástico al final de un listado se lee como que la pantalla se
       movió sola cuando se está tocando con el pulgar. */
    overscroll-behavior-y: none;
}

/* El texto de la app no se selecciona (es una app, no un documento), pero los
   datos SÍ: el teléfono de un cliente se copia todo el tiempo. */
body { -webkit-user-select: none; user-select: none; }
input, textarea, select, .seleccionable, .dato, td, .lista-dato { -webkit-user-select: text; user-select: text; }

h1, h2, h3 { margin: 0 0 var(--e3); line-height: 1.25; font-weight: 600; }
h1 { font-size: 1.25rem; }
h2 { font-size: 1.05rem; }
h3 { font-size: .95rem; }
p  { margin: 0 0 var(--e3); }

a { color: var(--primario); text-decoration: none; }
a:hover { text-decoration: underline; }

/* [hidden] tiene que ganar SIEMPRE: el navegador lo aplica desde su hoja de
   usuario, que pierde contra cualquier clase del proyecto, y este sistema pliega
   cosas con hidden (los paneles de pestañas, los bloques condicionales). */
[hidden] { display: none !important; }

/* ==========================================================================
   Estructura
   ========================================================================== */

.app-header {
    position: sticky;
    top: 0;
    z-index: 60;
    display: flex;
    align-items: center;
    gap: var(--e3);
    height: var(--alto-header);
    padding: 0 var(--e4);
    background: var(--barra);
    color: var(--sobre-barra);
}
.app-header a { color: var(--sobre-barra); }
.app-header .marca { font-weight: 600; letter-spacing: .01em; }
.app-header .empresa { font-size: .78rem; opacity: .8; display: block; line-height: 1.2; }
.app-header .derecha { margin-left: auto; display: flex; align-items: center; gap: var(--e3); }
.app-header .usuario { font-size: .82rem; opacity: .9; }

.contenido {
    padding: var(--e4);
    padding-bottom: calc(var(--alto-navpie) + var(--e6) + env(safe-area-inset-bottom, 0px));
    max-width: 60rem;
    margin: 0 auto;
}

/* La navegación de abajo: es donde llega el pulgar. Sin íconos, con palabras
   cortas — un ícono ambiguo en una app de plata se aprende mal una vez y se
   equivoca siempre. */
.nav-pie {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 50;
    display: flex;
    background: var(--superficie);
    border-top: 1px solid var(--borde);
    padding-bottom: env(safe-area-inset-bottom, 0px);
}
.nav-pie a {
    flex: 1;
    min-height: var(--alto-navpie);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: .8rem;
    font-weight: 500;
    color: var(--texto-suave);
    text-decoration: none;
    border-top: 2px solid transparent;
}
.nav-pie a.activo { color: var(--primario); border-top-color: var(--primario); background: var(--primario-suave); }

@media (min-width: 48rem) {
    .contenido { padding-bottom: var(--e6); }
    .nav-pie {
        position: sticky;
        top: var(--alto-header);
        border-top: 0;
        border-bottom: 1px solid var(--borde);
        z-index: 40;
    }
    .nav-pie a { min-height: 2.75rem; border-top: 0; border-bottom: 2px solid transparent; }
    .nav-pie a.activo { border-bottom-color: var(--primario); }
}

/* ==========================================================================
   Tarjetas y bloques
   ========================================================================== */

.tarjeta {
    background: var(--superficie);
    border: 1px solid var(--borde);
    border-radius: var(--radio);
    padding: var(--e4);
    margin-bottom: var(--e4);
    box-shadow: var(--sombra);
}
.tarjeta > h2:first-child, .tarjeta > h1:first-child { margin-top: 0; }
.tarjeta-titulo {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--e3); margin-bottom: var(--e3);
}

/* Las cifras se acomodan solas: con auto-fit entran dos en un teléfono, tres en
   uno grande y cuatro en una tablet, sin un breakpoint por cada caso. El piso de
   7.5rem es lo que mide "$120.000" en el cuerpo del valor — abajo de eso el
   número se corta, y esta es la pantalla que se le muestra AL CLIENTE: el importe
   se tiene que leer de lejos y del otro lado del mostrador. Antes de condensar
   más, el número manda. */
.panel-cifras {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(7.5rem, 1fr));
    gap: var(--e2);
    margin-bottom: var(--e4);
}

.cifra {
    background: var(--superficie);
    border: 1px solid var(--borde);
    border-radius: var(--radio);
    padding: var(--e3);
}
.cifra .rotulo {
    font-size: .68rem; text-transform: uppercase; letter-spacing: .05em;
    font-weight: 600; color: var(--texto-tenue);
}
.cifra .valor  { font-size: 1.2rem; font-weight: 600; font-variant-numeric: tabular-nums; margin-top: 2px; }
.cifra.peligro .valor { color: var(--peligro); }
.cifra.exito   { background: var(--exito-suave); border-color: transparent; }
.cifra.exito .valor   { color: var(--exito); }
.cifra.peligro { background: var(--peligro-suave); border-color: transparent; }
/* El ámbar de "se está acercando al límite": ya existía como etiqueta y faltaba
   como cifra. Los colores salen de los mismos tokens, así que sigue el tema. */
.cifra.aviso   { background: var(--aviso-suave);   border-color: transparent; }
.cifra.aviso .valor   { color: var(--aviso); }

/* ==========================================================================
   Listados
   ========================================================================== */

.lista { list-style: none; margin: 0; padding: 0; }
.lista-item {
    display: flex; align-items: center; gap: var(--e3);
    padding: var(--e3) var(--e4);
    border-bottom: 1px solid var(--borde);
    background: var(--superficie);
    min-height: 3.5rem;
    color: inherit;
    text-decoration: none;
}
.lista-item:last-child { border-bottom: 0; }
.lista-item:hover { background: var(--superficie-2); text-decoration: none; }
.lista-item .cuerpo { flex: 1; min-width: 0; }
.lista-item .titulo { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lista-item .sub    { font-size: .82rem; color: var(--texto-suave); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lista-item .monto  { font-variant-numeric: tabular-nums; font-weight: 600; text-align: right; white-space: nowrap; }
.lista-item.inactivo .titulo { color: var(--texto-tenue); font-weight: 500; }

.lista-envoltorio {
    background: var(--superficie);
    border: 1px solid var(--borde);
    border-radius: var(--radio);
    overflow: hidden;
    box-shadow: var(--sombra);
}

.vacio {
    padding: var(--e6) var(--e4);
    text-align: center;
    color: var(--texto-suave);
}
.vacio strong { display: block; color: var(--texto); margin-bottom: var(--e1); }

/* ==========================================================================
   Tablas (pantalla grande / reportes)
   ========================================================================== */


.tabla-contenedor {
    background: var(--superficie);
    border: 1px solid var(--borde);
    border-radius: var(--radio);
    overflow-x: scroll;
}

table.datos { width: 100%; border-collapse: collapse; font-size: .9rem; }
table.datos th, table.datos td { padding: var(--e2) var(--e3); text-align: left; white-space: nowrap; }
table.datos thead th {
    position: sticky;
    /* -1px y no 0: con 0 se cuela una franja de un píxel entre el borde del
       scrollport y la celda mientras se scrollea, y se lee como una línea de la
       fila de datos asomando por encima del encabezado. */
    top: -1px;
    z-index: 10;
    background: var(--superficie-2);
    font-size: .75rem; text-transform: uppercase; letter-spacing: .03em;
    color: var(--texto-suave);
    /* box-shadow y no border: con bordes colapsados el borde lo pinta la tabla,
       no la celda, y al scrollear se va con las filas. */
    box-shadow: inset 0 -1px 0 var(--borde);
}
table.datos tbody tr { border-top: 1px solid var(--borde); }
table.datos td.numero, table.datos th.numero { text-align: right; font-variant-numeric: tabular-nums; }

@media print {
    /* Impresa, la grilla va entera: un informe de trescientas filas saldría
       cortado en una pantalla. */
    .tabla-contenedor { max-height: none; overflow: visible; }
    .app-header, .nav-pie, .no-imprimir { display: none !important; }
}

/* ==========================================================================
   Formularios
   ========================================================================== */

.campo { margin-bottom: var(--e3); }
.campo > label { display: block; font-size: .8rem; font-weight: 600; color: var(--texto-suave); margin-bottom: var(--e1); }
.campo .ayuda { font-size: .76rem; color: var(--texto-tenue); margin-top: var(--e1); line-height: 1.35; }

input[type=text], input[type=email], input[type=password], input[type=number],
input[type=tel], input[type=date], input[type=search], select, textarea {
    width: 100%;
    /* 16px o más: abajo de eso iOS hace zoom al enfocar y descoloca la pantalla. */
    font-size: 16px;
    font-family: inherit;
    color: var(--texto);
    background: var(--superficie);
    border: 1px solid var(--borde-fuerte);
    border-radius: var(--radio);
    padding: var(--e2) var(--e3);
    /* 2.75rem = 44px. Es el objetivo táctil y NO se baja para condensar: lo que
       se gana de alto se paga errándole al campo con el pulgar en la calle. Lo
       que condensa el formulario es poner dos por fila, no achicar. */
    min-height: 2.75rem;
}
textarea { min-height: 5rem; resize: vertical; }
input:focus, select:focus, textarea:focus {
    outline: 2px solid var(--primario);
    outline-offset: -1px;
    border-color: var(--primario);
}
input[readonly], input:disabled, select:disabled, textarea[readonly], textarea:disabled {
    background: var(--superficie-2);
    color: var(--texto-suave);
}
input.monto, input.numero { font-variant-numeric: tabular-nums; text-align: right; }

/* --------------------------------------------------------------------------
   DOS CAMPOS POR FILA, TAMBIÉN EN EL CELULAR.

   El patrón ya existía y no se veía: el corte estaba en 34rem (544px), o sea que
   en el teléfono —que es donde se usa esto— TODOS los pares colapsaban a una
   columna y el formulario del cliente salía de once renglones. El corte real es
   22rem (352px): entra en cualquier teléfono de 360px para arriba, y el iPhone SE
   de 320px se queda en una columna, que ahí es lo correcto.

   `min-width: 0` en los hijos no es decorativo: por defecto un item de grid mide
   como mínimo su contenido, y un input[type=date] tiene ancho intrínseco propio.
   Sin esto la fila se pasa del ancho de la pantalla en vez de repartirse, y el
   síntoma es la página entera scrolleando de costado.
   -------------------------------------------------------------------------- */

.fila-campos { display: grid; gap: var(--e3); }
.fila-campos > * { min-width: 0; }
@media (min-width: 22rem) { .fila-campos.dos { grid-template-columns: 1fr 1fr; } }
@media (min-width: 34rem) { .fila-campos.tres { grid-template-columns: repeat(3, 1fr); } }

/* Cuando uno de los dos necesita más lugar que el otro: nombre + apodo,
   descripción + importe. Debajo del corte van uno abajo del otro igual. */
@media (min-width: 22rem) { .fila-campos.dos-desigual { grid-template-columns: 2fr 1fr; } }

.check { display: flex; align-items: flex-start; gap: var(--e2); margin-bottom: var(--e2); }
.check input { width: 1.15rem; height: 1.15rem; min-height: 0; margin-top: .18rem; flex: none; }
.check label { font-weight: 500; }

/* ==========================================================================
   Botones
   ========================================================================== */

.btn {
    display: inline-flex; align-items: center; justify-content: center;
    gap: var(--e2);
    min-height: 2.75rem;
    padding: var(--e2) var(--e4);
    font-size: .92rem; font-weight: 600; font-family: inherit;
    border: 1px solid transparent;
    border-radius: var(--radio-chico);
    background: var(--primario); color: var(--sobre-primario);
    cursor: pointer;
    text-decoration: none;
}
.btn:hover { background: var(--primario-hover); text-decoration: none; }
.btn:disabled, .btn[aria-disabled=true] { opacity: .5; cursor: not-allowed; }
.btn.secundario { background: var(--superficie); color: var(--texto); border-color: var(--borde-fuerte); }
.btn.secundario:hover { background: var(--superficie-2); }
.btn.peligro { background: var(--peligro); color: var(--sobre-primario); }
.btn.peligro:hover { background: var(--peligro-hover); }
.btn.bloque { width: 100%; }
.btn.chico { min-height: 2.15rem; padding: var(--e1) var(--e3); font-size: .82rem; }

.acciones { display: flex; flex-wrap: wrap; gap: var(--e2); }
.acciones.fin { justify-content: flex-end; }

/* Control segmentado: elegir UNA de pocas opciones que se ven todas a la vez.
   Se usa para el tema. Un <select> escondería las otras dos y acá justamente lo
   que se quiere es poder tocar la de al lado y ver el cambio. */
.segmentos { display: flex; gap: 0; border: 1px solid var(--borde-fuerte); border-radius: var(--radio); overflow: hidden; }
.segmentos button {
    flex: 1;
    min-height: 2.75rem;
    padding: var(--e2) var(--e3);
    font-size: .88rem; font-weight: 600; font-family: inherit;
    background: var(--superficie); color: var(--texto-suave);
    border: 0;
    border-left: 1px solid var(--borde);
    cursor: pointer;
}
.segmentos button:first-child { border-left: 0; }
.segmentos button[aria-pressed="true"] { background: var(--primario); color: var(--sobre-primario); }

/* ==========================================================================
   Etiquetas de estado
   ========================================================================== */

.etiqueta {
    display: inline-block;
    padding: 1px var(--e2);
    border-radius: 999px;
    font-size: .72rem; font-weight: 600;
    line-height: 1.5;
    white-space: nowrap;
}
.etiqueta.exito   { background: var(--exito-suave);   color: var(--exito); }
.etiqueta.peligro { background: var(--peligro-suave); color: var(--peligro); }
.etiqueta.aviso   { background: var(--aviso-suave);   color: var(--aviso); }
.etiqueta.neutra  { background: var(--superficie-2);  color: var(--texto-suave); border: 1px solid var(--borde); }

/* ==========================================================================
   Avisos
   ========================================================================== */

.aviso {
    display: flex; gap: var(--e3);
    padding: var(--e3) var(--e4);
    border: 1px solid var(--borde);
    border-left-width: 3px;
    border-radius: var(--radio-chico);
    background: var(--superficie);
    margin-bottom: var(--e4);
    font-size: .9rem;
}
.aviso.exito   { border-left-color: var(--exito);   background: var(--exito-suave); }
.aviso.error   { border-left-color: var(--peligro); background: var(--peligro-suave); }
.aviso.aviso-  { border-left-color: var(--aviso);   background: var(--aviso-suave); }
.aviso.info    { border-left-color: var(--primario); background: var(--primario-suave); }

/* ==========================================================================
   Buscador
   ========================================================================== */

.buscador { position: relative; margin-bottom: var(--e3); }
.buscador input { padding-right: 2.5rem; }
.buscador .limpiar {
    position: absolute; right: var(--e2); top: 50%; transform: translateY(-50%);
    background: none; border: 0; color: var(--texto-tenue);
    font-size: 1.1rem; cursor: pointer; padding: var(--e1) var(--e2);
}

/* ==========================================================================
   Diálogos propios (nunca alert() ni confirm())
   ========================================================================== */

.dialogo-fondo {
    position: fixed; inset: 0; z-index: 200;
    background: var(--velo);
    display: flex; align-items: center; justify-content: center;
    padding: var(--e4);
}
.dialogo-fondo[hidden] { display: none !important; }
.dialogo {
    background: var(--superficie);
    border-radius: var(--radio);
    box-shadow: var(--sombra-flotante);
    width: 100%; max-width: 24rem;
    padding: var(--e5);
}
/* El diálogo que trae un FORMULARIO adentro y no una pregunta: el alta rápida de
   cliente. Más ancho porque los campos van de a dos por fila, y con scroll propio
   porque en un teléfono no entra —sin el max-height, el botón de guardar queda
   abajo del pliegue y no hay forma de llegar—. */
.dialogo.ancho {
    max-width: 34rem;
    max-height: calc(100vh - var(--e6));
    overflow-y: auto;
}
.dialogo h2 { margin-top: 0; }
.dialogo p { color: var(--texto-suave); }
.dialogo .acciones { margin-top: var(--e4); justify-content: flex-end; }

/* ==========================================================================
   Velo de "estoy cargando"
   ========================================================================== */

.cargando {
    position: fixed; inset: 0; z-index: 300;
    display: flex; align-items: center; justify-content: center;
    background: var(--velo-carga);
    backdrop-filter: blur(1.5px);
    visibility: hidden;
    pointer-events: none;
}
.cargando.visible { visibility: visible; pointer-events: auto; }
.cargando .rueda {
    width: 2.25rem; height: 2.25rem;
    border: 3px solid var(--borde-fuerte);
    border-top-color: var(--primario);
    border-radius: 50%;
    animation: girar .7s linear infinite;
}
@keyframes girar { to { transform: rotate(360deg); } }
@media print { .cargando { display: none !important; } }

/* ==========================================================================
   Login
   ========================================================================== */

.pantalla-centrada {
    min-height: 100vh;
    display: flex; align-items: center; justify-content: center;
    padding: var(--e4);
}
.caja-login { width: 100%; max-width: 22rem; }
.caja-login .marca-grande {
    text-align: center; margin-bottom: var(--e5);
    font-size: 1.6rem; font-weight: 700; letter-spacing: .02em; color: var(--primario);
}
.caja-login .marca-grande span { display: block; font-size: .8rem; font-weight: 400; color: var(--texto-suave); letter-spacing: 0; }

/* Teclado numérico del PIN */
.teclado-pin { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--e3); margin-top: var(--e4); }
.teclado-pin button {
    min-height: 3.5rem; font-size: 1.3rem; font-weight: 600; font-family: inherit;
    background: var(--superficie); color: var(--texto);
    border: 1px solid var(--borde-fuerte); border-radius: var(--radio);
    cursor: pointer;
}
.teclado-pin button:active { background: var(--primario-suave); }
.puntos-pin { display: flex; gap: var(--e3); justify-content: center; margin: var(--e4) 0; }
.puntos-pin i {
    width: .9rem; height: .9rem; border-radius: 50%;
    border: 2px solid var(--borde-fuerte); background: transparent;
}
.puntos-pin i.lleno { background: var(--primario); border-color: var(--primario); }

/* ==========================================================================
   Utilidades
   ========================================================================== */

.texto-suave  { color: var(--texto-suave); }
.texto-tenue  { color: var(--texto-tenue); }
.texto-chico  { font-size: .82rem; }
.texto-peligro { color: var(--peligro); }
.texto-exito  { color: var(--exito); }
.numeros      { font-variant-numeric: tabular-nums; }
.sin-margen   { margin: 0; }
.mt-3 { margin-top: var(--e3); }
.mt-4 { margin-top: var(--e4); }
.mb-0 { margin-bottom: 0; }
.mb-3 { margin-bottom: var(--e3); }
.mb-4 { margin-bottom: var(--e4); }
.flex-entre { display: flex; align-items: center; justify-content: space-between; gap: var(--e3); }
.flex-gap   { display: flex; align-items: center; gap: var(--e2); flex-wrap: wrap; }
.text-upper { text-transform: uppercase; }

/* ==========================================================================
   El panel del operador.

   Barra oscura a proposito: tiene que ser obvio de un vistazo que NO es la
   aplicacion del cliente. Confundir las dos pantallas es como se suspende a la
   empresa equivocada.
   ========================================================================== */

.app-header.panel { background: var(--panel); }
.nav-pie.panel a.activo { color: var(--texto); border-color: var(--texto); background: var(--panel-activo); }

.marca-panel {
    text-align: center; margin-bottom: var(--e5);
    font-size: 1.5rem; font-weight: 700; color: var(--texto);
}
.marca-panel span {
    display: block; font-size: .78rem; font-weight: 400;
    color: var(--texto-suave); letter-spacing: .04em; text-transform: uppercase;
}

/* El codigo de 6 digitos: grande y espaciado, que es como se tipea sin errarle. */
.campo-codigo {
    font-size: 1.6rem !important;
    letter-spacing: .5em;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* El secreto del segundo factor, para copiarlo a mano. */
.secreto {
    font-family: ui-monospace, "SFMono-Regular", Consolas, monospace;
    font-size: 1.05rem;
    letter-spacing: .08em;
    background: var(--superficie-2);
    border: 1px solid var(--borde);
    border-radius: var(--radio-chico);
    padding: var(--e3);
    word-break: break-all;
    -webkit-user-select: all; user-select: all;
}

/* Una grilla que NO scrollea sola: la pantalla la muestra entera y scrollea la
   pagina. Se usa donde el cuadro es el contenido principal —el cuadro de cuotas
   del simulador— y no una tabla de datos dentro de una pantalla mas grande.
   Conserva el overflow horizontal, que en un celular sigue haciendo falta. */
.tabla-contenedor.tabla-completa { max-height: none; }

/* Al reves: el cuadro de cuotas cuando NO es el contenido de la pantalla sino
   una tabla adentro de otra cosa —el dialogo que confirma el prestamo—. Ahi
   scrollea solo, porque con las 24 filas enteras los botones de confirmar y
   volver quedan abajo de todo y hay que scrollear el dialogo para llegar a
   ellos. Es la unica diferencia entre el cuadro del simulador y el de la
   confirmacion, y por eso el partial es el mismo. */
.tabla-contenedor.tabla-scroll { max-height: 40vh; overflow-y: auto; }
