/* Estilos globales de Univai — extraídos de base.html (2026-07-13).
 * Vivían INLINE: viajaban en CADA página (10.6 KB) y el navegador NO los podía cachear
 * (los re-bajaba y re-parseaba en cada navegación). Como archivo, WhiteNoise los versiona
 * por contenido → el navegador los guarda para SIEMPRE y solo los pide cuando cambian. */
:root {
            /* B&W neutral scale — 0 tono crema. Texturas/formas hacen el carácter. */
            --bg: #fafafa;          /* off-white global */
            --surface: #ffffff;     /* pure white cards/inputs */
            --surface-soft: #f5f5f5;/* gray-100 — hover / nested */
            --border: #e5e5e5;      /* gray-200 — borders neutrales */
            --text: #0a0a0a;        /* near-black — contraste máximo */
            --muted: #737373;       /* gray-500 — copy secundario */
            --muted-strong: #525252;/* gray-600 — copy con peso */
            --accent: #0a0a0a;      /* same as text — el "color" es la ausencia */
            --accent-hover: #2c2c2c;/* hover de botones/links accent (negro un punto más claro) */
            --danger: #b00020;      /* ÚNICA excepción de color: errores/riesgo (regla: color = riesgo) */
            /* Paleta de DATA-VIZ (founder 2026-06-28): 1 tono por serie, usado en la BARRA *y* en su
               CANTIDAD → el ojo liga el número con su barra. Grayscale llamativo: negro pleno vs gris
               sólido (no lavado). 'in' = ingresos/emitido/cobrado · 'out' = egresos/recibido/pagado. */
            /* EXPERIMENTO founder 2026-06-28: POSITIVO/ingreso = VERDE (contrasta sobre blanco, donde
               un negro o gris no resaltan), NEGATIVO/egreso = NEGRO (alto contraste). Aplica a números
               Y barras (numbers match chart). Si el founder prefiere el grayscale, revertir a #0a0a0a / #8a8a8a. */
            --data-in: #0a7d3c;          /* positivo/ingreso = verde */
            --data-in-soft: #0e9a48;     /* degradado de la barra verde */
            --data-out: #0a0a0a;         /* egreso/negativo en BARRAS = negro */
            --data-out-soft: #3a3a3a;    /* degradado de la barra negra */
            --data-out-strong: #0a0a0a;  /* número negativo = negro (lo que al founder le gustó: máximo contraste) */

            /* ══ LOS CUATRO EJES QUE FALTABAN (2026-08-09, orden del founder: «efecto Apple») ══
               El color ya estaba resuelto arriba. Lo que hacía que la app se sintiera "web" y no
               "nativa" eran los otros cuatro, y ninguno necesita un framework: React y Django
               entregan el MISMO HTML+CSS al navegador. Lo que se ve es esto. */

            /* 1 · ESPACIO — rejilla de 4, y el censo la respalda (medido 2026-08-09).
               El barrido afirmó que esta escala estaba «desfasada +2px de la que la app ya usa».
               Se comprobó contando las 1,937 declaraciones de padding/margin/gap, y NO se sostiene:
                   10px×192 · 8px×192 · 12px×184 · 16px×170 · 14px×163 · 6px×126 · 4px×116 · 18px×115
               No hay una escala desplazada: hay DOS ENTRELAZADAS —la de 4 y la de 2— y por eso el
               ojo lee desorden. Cobertura de esta escala 36%, de la otra 33%: ninguna manda.
               Se conserva la de 4 porque es la mayor agrupación y porque un solo paso (4) es más
               fácil de sostener que dos. La migración de los 6/10/14/18 es progresiva: se
               convierte lo que se toca, no se hace un `sed` de 1,200 sitios a ciegas.
               🔑 Y la lección para mí: el agente lo dijo con seguridad y estaba a medias. Medir
               antes de obedecer vale igual para un humano que para un subagente. */
            --e1: 4px;   --e2: 8px;   --e3: 12px;  --e4: 16px;
            --e5: 24px;  --e6: 32px;  --e7: 48px;  --e8: 64px;

            /* 2 · TIPOGRAFÍA — MEDIDA, no inventada (corregido 2026-08-09 el mismo día).
               🪤 La primera versión decía 12 / 13.5 / 15 y la escribí de memoria. El barrido de UI
               lo puso en duda y el censo lo confirmó: **el 13.5 no existe en el código**. Lo que
               la app usa de verdad, contando las 837 declaraciones de `font-size`:
                   13px×162 · 12px×144 · 11px×112 · 14px×103 · 28px×25 · 24px×11
               Imponer 13.5 habría obligado a tocar 400 sitios para llegar a un número que me
               inventé. La escala describe lo que hay y lo ORDENA; no lo sustituye por gusto.
               Cuerpo: 11 (mínimo legible, rótulos) · 12 · 13 (el dominante) · 14.
               Despliegue: 20 · 28 · 40. */
            --t-2xs: 11px; --t-xs: 12px; --t-sm: 13px; --t-md: 14px;
            --t-lg: 20px; --t-xl: 28px;   --t-2xl: 40px;
            /* Piso DISPLAY (F1 del rework, 2026-08-23): la dirección editorial del founder
               (tipografía como arquitectura) necesita cuerpos que 40px no alcanza. Dos y solo
               dos, para titulares-cartel y el dato-héroe. Añadidos también a TIPO_OK del gate
               `check_escala_visual` — a la escala se entra por la puerta, no de contrabando. */
            --t-3xl: 56px; --t-4xl: 88px;
            --tracking-tight: -0.02em;   /* los números y titulares grandes respiran mejor apretados */

            /* 3 · RADIOS — tres, y el de píldora. Mezclar radios distintos en la misma pantalla es
               de lo que más delata a una interfaz sin sistema. */
            --r-sm: 8px; --r-md: 12px; --r-lg: 18px; --r-full: 999px;

            /* 4 · MOVIMIENTO — el eje que estaba en CERO, y el que más "sofisticación" da por peso.
               Un panel que aparece de golpe se siente web; uno que entra en 180ms desacelerando se
               siente nativo. La curva es la de las hojas de iOS: sale rápido y FRENA largo, que es
               lo que el ojo lee como "físico" en vez de "programado". */
            --ease: cubic-bezier(0.32, 0.72, 0, 1);
            --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
            --dur-1: 120ms;   /* microestados: hover, focus */
            --dur-2: 180ms;   /* aparecer/desaparecer */
            --dur-3: 260ms;   /* mover algo grande */

            /* 5 · ELEVACIÓN — sombras en capas, no bordes. Separar con SOMBRA y ESPACIO en vez de
               con línea es la diferencia más grande entre "formulario" y "producto caro". */
            --sombra-1: 0 1px 2px rgba(10,10,10,.05), 0 1px 1px rgba(10,10,10,.03);
            --sombra-2: 0 2px 4px rgba(10,10,10,.05), 0 4px 12px rgba(10,10,10,.05);
            --sombra-3: 0 8px 24px rgba(10,10,10,.09), 0 2px 6px rgba(10,10,10,.05);

            /* ── EL VIDRIO (ley nº1 del founder: «glass SOLO en lo interactuable») ──────────
               Sale de `.pricing-cta`, que es la receta aprobada y en producción desde el
               27-ago. Aquí se tokeniza por una razón concreta: allí son rgba() cableados y un
               rgba() no se entera del tema — el mismo vidrio sobre tinta se vuelve una mancha
               lechosa. Con token, el oscuro trae el suyo y las cinco familias lo heredan. */
            --vidrio: linear-gradient(165deg, rgba(255,255,255,.95), rgba(255,255,255,.45) 40%, rgba(10,10,10,.05));
            --vidrio-borde: rgba(10,10,10,.20);
            --vidrio-luz: rgba(255,255,255,.95);
            --vidrio-sombra: inset 0 1px 0 rgba(255,255,255,.95), inset 0 -1px 0 rgba(10,10,10,.06),
                             0 1px 2px rgba(10,10,10,.10), 0 8px 16px rgba(10,10,10,.12);
            /* el invertido: mismo material, contraste opuesto — tinta donde el otro es luz */
            --vidrio-inv: linear-gradient(165deg, rgba(10,10,10,.92), rgba(10,10,10,.72) 40%, rgba(10,10,10,.88));
            --vidrio-inv-borde: rgba(10,10,10,.55);
            --vidrio-inv-texto: #fafafa;

            /* Los controles nativos (inputs, scrollbars) siguen al tema. */
            color-scheme: light;
        }

        /* ══ EL TEMA OSCURO (F0 del rework, 2026-08-23) — decisión del founder: claro Y oscuro
           de primera clase («es algo que sí me pidieron»). Pares elegidos A MANO en la tarjeta
           «Temas» del sistema de diseño, no invertidos: el verde SUBE de luz para contrastar
           sobre tinta, el rojo también, y el «negro de la casa» se convierte en el claro.

           ⚠️ SE ACTIVA SOLO CON `data-theme="dark"` EXPLÍCITO — a propósito NO hay
           `@media (prefers-color-scheme: dark)` todavía. El censo del rework
           (docs/REWORK_UI_MAPA.md) contó 67 hex literales vivos en las plantillas: activar el
           oscuro en automático HOY le serviría a todo usuario con sistema oscuro una app a
           medio teñir (lo tokenizado se voltea, lo hex no). El automático llega al FINAL del
           rework, cuando ese censo dé cero — y `tests_tokens_bilingues` vigila que nadie lo
           adelante sin decidirlo.

           El selector es `[data-theme="dark"]` a CUALQUIER profundidad, no solo :root: una
           sección puede optar al oscuro localmente (la banda negra editorial de la landing es
           exactamente eso) porque las custom properties cascadean desde donde se declaran. */
        [data-theme="dark"] {
            --bg: #101012;
            --surface: #1a1b1d;
            --surface-soft: #232427;
            --border: #2a2b2e;
            --text: #f2f2f0;
            --muted: #8b8d90;
            --muted-strong: #b5b7ba;
            --accent: #f2f2f0;          /* el «color» sigue siendo la ausencia — ahora clara */
            --accent-hover: #d8d8d6;
            --danger: #e5484d;          /* sube de luz: #b00020 desaparece sobre tinta */
            --data-in: #34c86b;
            --data-in-soft: #23c55e;
            --data-out: #f2f2f0;        /* egreso = tinta, y de noche la tinta es clara */
            --data-out-soft: #b5b7ba;
            --data-out-strong: #f2f2f0;
            /* Sobre tinta una sombra clara no existe: se oscurece el ambiente, no el objeto. */
            --sombra-1: 0 1px 2px rgba(0,0,0,.35), 0 1px 1px rgba(0,0,0,.25);
            --sombra-2: 0 2px 4px rgba(0,0,0,.35), 0 4px 12px rgba(0,0,0,.30);
            --sombra-3: 0 8px 24px rgba(0,0,0,.45), 0 2px 6px rgba(0,0,0,.30);

            /* De noche el vidrio no puede ser blanco translúcido: sobre tinta eso es niebla.
               Es tinta clara MUY tenue, con la luz en el borde superior — el mismo material
               leído al revés, que es lo que hace que se reconozca como el mismo botón. */
            --vidrio: linear-gradient(165deg, rgba(255,255,255,.14), rgba(255,255,255,.06) 40%, rgba(0,0,0,.20));
            --vidrio-borde: rgba(255,255,255,.18);
            --vidrio-luz: rgba(255,255,255,.22);
            --vidrio-sombra: inset 0 1px 0 rgba(255,255,255,.16), inset 0 -1px 0 rgba(0,0,0,.30),
                             0 1px 2px rgba(0,0,0,.35), 0 8px 16px rgba(0,0,0,.40);
            --vidrio-inv: linear-gradient(165deg, rgba(242,242,240,.96), rgba(242,242,240,.86) 40%, rgba(242,242,240,.94));
            --vidrio-inv-borde: rgba(242,242,240,.70);
            --vidrio-inv-texto: #101012;
            color-scheme: dark;
        }

        /* ── EL MOVIMIENTO, APLICADO ─────────────────────────────────────────────────────────
           Todo lo que un dedo puede tocar transiciona. No es decoración: un cambio instantáneo no
           da tiempo al ojo a ligar causa y efecto, y esa desconexión es lo que se siente "barato".
           Se enumeran las propiedades a propósito — `transition: all` obliga al navegador a vigilar
           TODAS y provoca saltos cuando algo cambia de layout. */
        a, button, .btn, [role="button"], summary, input, select, textarea,
        .card, .chip, .tab, tr[data-href], label {
            transition: background-color var(--dur-1) var(--ease),
                        border-color var(--dur-1) var(--ease),
                        color var(--dur-1) var(--ease),
                        box-shadow var(--dur-2) var(--ease),
                        transform var(--dur-1) var(--ease),
                        opacity var(--dur-2) var(--ease);
        }

        /* QUIEN PIDE QUIETUD, QUIETUD. No es un detalle de accesibilidad opcional: hay gente a la
           que el movimiento le provoca mareo, y el sistema operativo ya lo dice por ella. */
        @media (prefers-reduced-motion: reduce) {
            *, *::before, *::after {
                animation-duration: 0.01ms !important;
                transition-duration: 0.01ms !important;
                /* 🩸 FALTABA ESTA LÍNEA Y LA PROMESA ERA FALSA (31-ago). Con solo la duración a
                   0.01ms, una animación `infinite` no se detiene: da MILES DE VUELTAS POR SEGUNDO.
                   Lo destapó la regresión visual al intentar retratar la landing por primera vez:
                   no se quedaba quieta, y el culpable era `lp-deriva 60s infinite alternate`
                   —el «$0.00» gigante del pie— cumpliendo la regla al pie de la letra.
                   🔑 Poner una duración a cero no PARA un bucle: lo acelera. Quien pide quietud
                      pide que TERMINE, y eso lo dice `animation-iteration-count`. */
                animation-iteration-count: 1 !important;
            }
        }

        /* Sane default: inputs/textareas/buttons no se desbordan de su contenedor.
           Aplica a TODOS los templates que extienden base.html (login, signup, panel, etc). */
        *, *::before, *::after { box-sizing: border-box; }

        body {
            font-family: "Inter", sans-serif;
            background: var(--bg);
            color: var(--text);
            display: flex;
            flex-direction: column;
            min-height: 100vh;
            margin: 0;
        }

        /* ── BUTTONS unificados — una sola "voz" para todos los class names legacy.
           v2 brutalista (2026-08-23): filo vivo, sin sombra, sin flotar. El hover del primario
           usa --accent-hover; el del fantasma es un cambio de superficie plano. Token-puro:
           el tema oscuro de F0 los voltea sin tocar nada. */
        a.btn, button.btn, a.btn-primary, button.btn-primary, a.btn-outline, button.btn-outline,
        a.primary-button, button.primary-button, a.ghost-button, button.ghost-button,
        a.cta-primary, button.cta-primary, a.cta-secondary, button.cta-secondary,
        .btn, .btn-primary, .btn-outline, .btn-ghost,
        .primary-button, .ghost-button, .cta-primary, .cta-secondary {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            padding: 13px 22px;
            border-radius: 0;
            font-size: 14px;
            font-weight: 700;
            text-decoration: none;
            white-space: nowrap;
            border: 1px solid;
            cursor: pointer;
            font-family: inherit;
            line-height: 1.2;
            transition: background-color 0.16s ease, border-color 0.16s ease, color 0.16s ease;
        }
        /* PRIMARY — tinta plena */
        a.btn-primary, button.btn-primary, a.primary-button, button.primary-button, a.cta-primary, button.cta-primary,
        .btn-primary, .primary-button, .cta-primary {
            background-color: var(--accent);
            color: var(--bg);
            border-color: var(--accent);
        }
        a.btn-primary:hover, button.btn-primary:hover, a.primary-button:hover, button.primary-button:hover,
        a.cta-primary:hover, button.cta-primary:hover,
        .btn-primary:hover, .primary-button:hover, .cta-primary:hover {
            background-color: var(--accent-hover);
            color: var(--bg);
            border-color: var(--accent-hover);
            text-decoration: none;
        }
        /* GHOST/OUTLINE — papel con borde */
        a.btn, button.btn, a.btn-outline, button.btn-outline, a.btn-ghost, button.btn-ghost,
        a.ghost-button, button.ghost-button, a.cta-secondary, button.cta-secondary,
        .btn:not(.btn-primary), .btn-outline, .btn-ghost, .ghost-button, .cta-secondary {
            background-color: var(--surface);
            color: var(--text);
            border-color: var(--border);
        }
        a.btn:hover, button.btn:hover, a.btn-outline:hover, button.btn-outline:hover, a.btn-ghost:hover, button.btn-ghost:hover,
        a.ghost-button:hover, button.ghost-button:hover, a.cta-secondary:hover, button.cta-secondary:hover,
        .btn:not(.btn-primary):hover, .btn-outline:hover, .btn-ghost:hover, .ghost-button:hover, .cta-secondary:hover {
            background-color: var(--surface-soft);
            color: var(--text);
            border-color: var(--text);
            text-decoration: none;
        }

        .container {
            flex: 1;
            width: min(1120px, calc(100% - 32px));
            margin: 0 auto;
            padding: 40px 0;
        }

        /* ── EL MASTHEAD DE IMPRENTA (v2 brutalista del rework, 2026-08-23) ──
           Orden del founder: fuera el rectángulo flotante (negro translúcido, radio 18,
           sombra, blur). Queda una cabecera de periódico: papel, UNA línea de tinta abajo,
           wordmark tipográfico y acciones en mono mayúsculas. Va EN FLUJO (no fija): el
           masthead se va con el scroll, y así su alto variable en móvil no obliga a ningún
           padding-top mágico en .container. Token-puro: se voltea solo con el tema de F0. */
        @font-face {
            font-family: "Archivo";
            font-style: normal;
            font-weight: 100 900;
            font-stretch: 62% 125%;
            font-display: swap;
            src: url("../fonts/archivo-latin.de2f1fe81758.woff2") format("woff2");
        }
        @font-face {
            font-family: "Plex Mono";
            font-style: normal;
            font-weight: 500;
            font-display: swap;
            src: url("../fonts/plexmono-latin.b1c8a895f5fd.woff2") format("woff2");
        }

        .site-header {
            background: var(--bg);
            border-bottom: 1px solid var(--text);
            padding: 0 16px;
        }

        .site-nav {
            width: min(1120px, calc(100% - 32px));
            margin: 0 auto;
            padding: 16px 0;
            display: grid;
            grid-template-columns: 1fr auto 1fr;
            align-items: center;
            gap: 16px;
        }

        .site-brand {
            justify-self: start;
            font-family: "Archivo", "Inter", sans-serif;
            font-variation-settings: 'wdth' 116;
            font-size: 20px;
            font-weight: 800;
            letter-spacing: -0.02em;
            text-transform: uppercase;
            color: var(--text);
            text-decoration: none;
        }

        .site-brand:hover,
        .site-brand:focus,
        .site-brand:active,
        .site-brand:visited {
            color: var(--text);
            text-decoration: none;
        }

        .site-links,
        .site-actions {
            display: flex;
            align-items: center;
            gap: 12px;
        }

        .site-links { justify-self: center; justify-content: center; }
        .site-actions { justify-self: end; justify-content: flex-end; }

        .site-link,
        .site-button,
        .site-button-muted,
        .site-user {
            font-family: "Plex Mono", ui-monospace, monospace;
            font-size: 12px;
            font-weight: 500;
            letter-spacing: 0.08em;
            text-transform: uppercase;
            white-space: nowrap;
        }

        .site-link {
            color: var(--text);
            text-decoration: none;
            border-bottom: 2px solid transparent;
            padding-bottom: 2px;
        }

        .site-link:hover {
            border-bottom-color: var(--text);
            text-decoration: none;
        }

        .site-button,
        .site-button-muted {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            padding: 8px 14px;
            border-radius: 0;
            text-decoration: none;
        }

        .site-button {
            border: 1px solid var(--accent);
            background: var(--accent);
            color: var(--bg);
        }

        /* 🩸 EL HOVER ESTABA MUERTO, y era el ÚNICO botón público así. MEDIDO en píxeles el
           31-ago pasando el ratón en Chromium: «CREAR CUENTA» cambiaba **0.00%** de sus píxeles
           mientras sus hermanos cambiaban 91-92%. Y es el botón que convierte visitante en
           cuenta, presente en las TRES pantallas públicas.
           La causa se ve sola: `:hover` estaba agrupado con `:visited`, y `:visited` necesita
           los colores de REPOSO (si no, el enlace se pinta morado). Al compartir bloque, el
           reposo ganó y el hover dejó de existir. `.site-button-muted`, cinco líneas más abajo,
           los separa bien — y por eso el suyo sí funciona.
           🔑 Agrupar `:hover` con `:visited` no es un atajo: es apagar el hover, porque los dos
              quieren cosas opuestas. */
        .site-button:hover,
        .site-button:active {
            background: transparent;
            color: var(--accent);
            text-decoration: none;
        }

        .site-button:focus,
        .site-button:visited {
            background: var(--accent);
            color: var(--bg);
            text-decoration: none;
        }

        .site-button-muted {
            border: 1px solid var(--text);
            background: transparent;
            color: var(--text);
        }

        /* Hover brutalista: intercambio duro tinta↔papel — nada flota, nada se difumina. */
        .site-button-muted:hover {
            background: var(--text);
            color: var(--bg);
            text-decoration: none;
        }

        .site-button-muted:focus,
        .site-button-muted:active,
        .site-button-muted:visited {
            color: var(--text);
            text-decoration: none;
        }

        .site-nav a:focus-visible,
        .site-nav button:focus-visible {
            outline: 2px solid var(--text);
            outline-offset: 2px;
        }

        .site-user {
            color: var(--muted);
        }

        button.site-button-muted {
            cursor: pointer;
        }

        form input,
        form button,
        form select,
        form textarea {
            border-radius: 12px;
            border: 1px solid var(--border);
            background-color: var(--surface);
            color: var(--text);
        }

        form input:focus,
        form button:focus,
        form select:focus,
        form textarea:focus {
            border-color: var(--accent);
            box-shadow: none;
            outline: none;
        }

        .site-footer {
            width: min(1120px, calc(100% - 32px));
            margin: 0 auto;
            padding: 0 0 28px;
            display: flex;
            justify-content: center;
            gap: 14px;
            flex-wrap: wrap;
            color: var(--muted-strong);
            font-size: 12px;
        }

        .site-footer a {
            color: var(--muted);
            font-weight: 700;
            text-decoration: none;
        }

        .site-footer a:hover {
            color: var(--text);
            text-decoration: none;
        }

        @media (max-width: 860px) {
            .site-nav {
                grid-template-columns: 1fr;
                justify-items: center;
            }

            .site-links,
            .site-actions {
                justify-content: center;
                flex-wrap: wrap;
            }

            .container {
                width: calc(100% - 24px);
            }
        }

/* ═════════════════════════════════════════════════════════════════════════════════════════
   LAS CINCO FAMILIAS · el contrato de interactuables (F1 · founder, 2026-08-29)
   ═════════════════════════════════════════════════════════════════════════════════════════

   POR QUÉ EXISTEN. Medido el 29-ago con un escáner kill-testeable: **474 elementos que se
   pulsan** bajo **132 nombres de clase**, y **110 de esos 132 (83%) viven en UNA sola
   plantilla**. Casi cinco de cada seis familias de botón no son de la casa: son de una
   habitación. Y **16 clases están definidas en dos o más archivos** — `.primary` en 7,
   `.pill` en 5 —, así que el mismo nombre hace cosas distintas según dónde caiga.

   Homologar pantalla por pantalla (F4) sin vocabulario es repintar 474 elementos a mano y
   esperar suerte. Esto es el vocabulario. **Cinco, no 132.**

   NINGUNA SE APLICA TODAVÍA, y es a propósito (F1: «se despliega el CSS nuevo, sin usar»).
   Aplicarlas es F4, pantalla por pantalla, con el instrumento de F0 midiendo el antes y el
   después. Definir y aplicar en el mismo commit haría imposible saber cuál de los dos movió
   el píxel.

   LAS RECETAS NO SE INVENTARON: se extrajeron de lo que ya está aprobado y en producción —
   `.pricing-cta` (el vidrio, 27-ago), `.sf-enlace` (el enlace-acción) y `.app-back` (la
   salida). Lo nuevo es que ahora viven UNA vez, con tokens, y con gemelo oscuro.

   CÓMO SE ELIGE, en una frase por familia:
     ¿es LA acción de la pantalla?            → accion-primaria    (una por pantalla, nunca dos)
     ¿es una acción de peso, pero no la principal? → accion-secundaria
     ¿es una acción que cabe en una línea de texto? → accion-terciaria
     ¿es una caja entera que se pulsa?        → cajon
     ¿saca al usuario de donde está?          → salida
   ═════════════════════════════════════════════════════════════════════════════════════════ */

/* El suelo que comparten las cinco. 44px de alto real: el censo del 29-ago encontró que
   «nadie midió contraste, foco ni área táctil» en las 80 familias de una sola plantilla. */
.accion-primaria,
.accion-secundaria,
.accion-terciaria,
.cajon,
.salida,
.par {
    display: inline-flex;
    align-items: center;
    gap: var(--e2);
    min-height: 44px;
    box-sizing: border-box;
    border: 1px solid transparent;
    border-radius: var(--r-md);
    font-family: inherit;
    text-decoration: none;
    cursor: pointer;
    transition: transform var(--dur-1) var(--ease), background var(--dur-1) var(--ease),
                color var(--dur-1) var(--ease);
}
/* Un foco visible en las cinco, de una vez. El censo contó 26 declaraciones de foco en 13
   archivos: la mayoría de las familias no tenía ninguna, y quien navega con teclado no veía
   dónde estaba. */
.accion-primaria:focus-visible,
.accion-secundaria:focus-visible,
.accion-terciaria:focus-visible,
.cajon:focus-visible,
.salida:focus-visible,
.par:focus-visible {
    outline: 2px solid var(--text);
    outline-offset: 2px;
}
/* Desactivado, que el censo encontró estilado en 7 sitios y CERO en hojas compartidas — y
   `.btnv` no lo tenía en ninguna parte: se veía igual vivo que muerto. */
.accion-primaria[disabled], .accion-primaria[aria-disabled="true"],
.accion-secundaria[disabled], .accion-secundaria[aria-disabled="true"],
.accion-terciaria[disabled], .accion-terciaria[aria-disabled="true"],
.cajon[disabled], .cajon[aria-disabled="true"],
.salida[disabled], .salida[aria-disabled="true"],
.par[disabled], .par[aria-disabled="true"] {
    opacity: .45;
    cursor: not-allowed;
    transform: none;
    box-shadow: none;
}

/* 1 · LA ACCIÓN DE LA PANTALLA. Vidrio, y solo aquí: la ley nº1 del founder es «glass SOLO en
      lo interactuable», así que el material ES la señal de que esto se toca. UNA por pantalla:
      dos primarias es no tener ninguna. */
.accion-primaria {
    padding: var(--e3) var(--e5);
    color: var(--text);
    background: var(--vidrio);
    border-color: var(--vidrio-borde);
    border-top-color: var(--vidrio-luz);
    box-shadow: var(--vidrio-sombra);
    backdrop-filter: blur(12px) saturate(1.2);
    -webkit-backdrop-filter: blur(12px) saturate(1.2);
    font-family: "Plex Mono", ui-monospace, monospace;
    font-size: var(--t-2xs);
    font-weight: 500;
    letter-spacing: .14em;
    text-transform: uppercase;
}
.accion-primaria:hover { transform: translateY(-1px); }

/* 2 · PESO, PERO NO LA PRINCIPAL. El mismo material leído al revés: tinta donde el otro es luz.
      Se reconoce como pariente del primario —misma geometría, misma tipografía— y aun así
      nadie los confunde, que es justo lo que se le pide a una secundaria. */
.accion-secundaria {
    padding: var(--e3) var(--e5);
    color: var(--vidrio-inv-texto);
    background: var(--vidrio-inv);
    border-color: var(--vidrio-inv-borde);
    box-shadow: var(--sombra-1);
    font-family: "Plex Mono", ui-monospace, monospace;
    font-size: var(--t-2xs);
    font-weight: 500;
    letter-spacing: .14em;
    text-transform: uppercase;
}
.accion-secundaria:hover { transform: translateY(-1px); }

/* 3 · UNA ACCIÓN QUE CABE EN UNA LÍNEA DE TEXTO. Receta de `.sf-enlace`. El subrayado NO es
      decorativo: sin él, un enlace-acción apagado es indistinguible de prosa — el bug 2 del
      29-ago era exactamente eso, con 1.21 de contraste. */
.accion-terciaria {
    padding: var(--e2) 0;
    min-height: 0;
    color: var(--muted);
    background: none;
    font-family: "Plex Mono", ui-monospace, monospace;
    font-size: var(--t-2xs);
    letter-spacing: .12em;
    text-transform: uppercase;
    text-decoration: underline;
    text-underline-offset: 3px;
}
.accion-terciaria:hover { color: var(--text); }
/* la flechita viaja con la familia, no con la página */
.accion-terciaria .fl { display: inline-block; transition: transform var(--dur-2) var(--ease); }
.accion-terciaria:hover .fl { transform: translateX(4px); }

/* 4 · UNA CAJA ENTERA QUE SE PULSA. Un destino, no una acción: se anuncia con el borde y
      responde con el fondo.
      🩸 ESTA REGLA DECÍA «NO LLEVA VIDRIO: el vidrio es de las acciones», Y SE CORRIGIÓ EL 30-ago
      CON SU PRIMER CASO REAL. La frase se escribió cuando `cajon` tenía CERO usos —o sea que
      nunca se había probado contra nada—, y el primero en llegar fue el muro de meses de
      Sincronización: 68 destinos en pantalla, aprobados en el lab CON vidrio. La ley nº1 del
      founder dice «vidrio SOLO en lo interactuable», no «solo en las acciones»; una casilla que
      se pulsa 68 veces por pantalla necesita ese aviso más que nadie.
      🔑 Lo que la familia fija es la GEOMETRÍA y el CONTRATO (columna, 44 px, foco, desactivado,
      estado activo). El MATERIAL lo elige la pantalla, y con tokens `--vidrio*` para que gire con
      el tema. Una regla que nunca se probó contra un caso real no es una regla: es una intuición
      con formato de regla. */
.cajon {
    display: flex;
    align-items: stretch;
    flex-direction: column;
    gap: var(--e1);
    width: 100%;
    padding: var(--e4);
    text-align: left;
    color: var(--text);
    background: var(--surface);
    border-color: var(--border);
    font-size: var(--t-sm);
}
.cajon:hover { background: var(--surface-soft); }
.cajon[aria-current="true"], .cajon.is-active { border-color: var(--text); }

/* 5 · SACA AL USUARIO DE DONDE ESTÁ. Receta de `.app-back`. Una sola por pantalla y siempre en
      el mismo sitio: el censo contó **6 patrones de salida en 4 posiciones**, y 13 de 41 vistas
      del panel sin ninguna.
      🪤 EL GLIFO VA AQUÍ, no en la plantilla: se escribía con CUATRO grafías distintas
      (`←` ×7 · `&larr;` ×6 · `&#8592;` ×2 · `&lsaquo;` ×2 — que además es OTRO carácter, el del
      paginador). Con la flecha en el CSS, escribirla mal deja de ser posible. */
.salida {
    padding: var(--e2) var(--e3);
    /* 🩸 EL AIRE DE ABAJO NO ES ADORNO, Y COSTÓ UN DEPLOY. La salida siempre es lo primero de su
       superficie y siempre necesita separarse de lo que abre. `.app-back` —el canon viejo— lleva
       este mismo `margin-bottom: var(--e4)`; sin él, CADA UNA de las 15 migraciones que quedan
       movería su pantalla 16 px. F4 cambia el NOMBRE, no el aspecto: el día que se decida
       cambiar el aspecto será una decisión, no un efecto secundario.
       Lo cazó la regresión visual: al migrar la superficie de Planes, la página ENCOGIÓ 28 px
       (1161 → 1133) y el gate abortó el deploy. */
    margin-bottom: var(--e4);
    color: var(--text);
    background: none;
    font-size: var(--t-sm);
    font-weight: 700;
}
.salida::before { content: "\2190"; }
.salida:hover { background: var(--surface-soft); }

/* 6 · UN GRUPO DE IGUALES (founder, 30-ago) — la familia que faltaba.
      📏 NO SE AÑADIÓ POR GUSTO, SE MIDIÓ: **17 plantillas** tienen una fila de 3 o más acciones
      PARES —descargar Excel/CSV/XML/PDF, elegir el mes, cambiar de vista—, o sea un tercio del
      producto. Y el producto ya tenía respuesta para eso, dicha de 17 maneras: `.mes-mini`,
      `.con-btn`, `.lb-chip`, `.export-link`, `.enlace-m`, `.uz-btn-ghost`, `.is-on`… **ninguna
      un bloque relleno.** Meterlas en `accion-secundaria` hacía gritar a 17 pantallas: se probó
      en `invoice_detail` y las tres descargas pesaban más que la propia factura, que es lo que
      la pantalla existe para enseñar.

      UNA FAMILIA, DOS USOS, y por eso no son dos:
        · ELEGIR uno   — Mes/Año, Facturado/Cobrado: uno va marcado `aria-current="true"`.
        · HACER uno    — Excel/CSV/XML/PDF: ninguno marcado, todos hacen algo distinto.
      Se ven igual porque SON lo mismo: hermanos del mismo peso. La marca de activo es un estado
      opcional, no otra familia.

      El grupo lleva el borde y el recorte; cada hermano solo su tinta. Así los bordes no se
      duplican entre vecinos, que es el defecto clásico de las filas de botones. */
.grupo-de-pares {
    display: inline-flex;
    flex-wrap: wrap;
    border: 1px solid var(--border);
    border-radius: var(--r-md);
    overflow: hidden;
    background: var(--surface);
}
.par {
    padding: 0 var(--e4);
    border: 0;
    border-radius: 0;
    background: none;
    color: var(--muted);
    font-family: "Plex Mono", ui-monospace, monospace;
    font-size: var(--t-2xs);
    font-weight: 500;
    letter-spacing: .12em;
    text-transform: uppercase;
    justify-content: center;
}
/* la raya entre hermanos la pone el hermano, no el grupo: así no sobra una al final */
.grupo-de-pares > .par + .par { border-left: 1px solid var(--border); }
.par:hover { background: var(--surface-soft); color: var(--text); }
/* ELEGIDO. `aria-current` no es decoración: es lo que hace que un lector de pantalla diga cuál
   está puesto. La clase acompaña para los casos donde el marcado no puede llevar el atributo. */
.par[aria-current="true"], .par.es-el-elegido {
    background: var(--text);
    color: var(--bg);
}
.par:focus-visible { outline-offset: -2px; }
