/* Sistema visual "Institucional sobrio" — tokens del sistema de diseño Estruva.
   Contrato normativo: DESIGN.md.

   ── CÓMO ESTÁ ORGANIZADO ESTE ARCHIVO (léelo antes de añadir un tema) ────────
   Hay TRES niveles, en este orden:

   1. :root                      → base del modo CLARO = tema "claro" (default).
   2. :root[data-mode="dark"]    → base del modo OSCURO = tema "oscuro".
   3. :root[data-mode][data-theme="X"] → una ATMÓSFERA de material. Solo
      redefine lo que cambia respecto a la base de su modo; todo lo estructural
      (espaciado, radios, z-index, tipografía) se hereda y NO se toca nunca.

   Los bloques de tema llevan los DOS atributos a propósito. Con un solo
   atributo, :root[data-theme="X"] empata en especificidad con
   :root[data-mode="dark"] y solo gana por orden en el archivo — una trampa que
   se dispara en cuanto alguien mueve un bloque. Con [data-mode][data-theme] el
   tema gana siempre. theme-service.js fija SIEMPRE ambos atributos (y el script
   anti-FOUC de index.html hace lo mismo antes del primer pintado).

   REGLA AL AÑADIR UN TEMA (DESIGN.md · The Atmosphere-Only Rule): un tema nuevo
   solo varía la atmósfera de superficie —temperatura, densidad y profundidad—.
   Nunca añade un segundo acento, ni cambia tipografía, forma o jerarquía. Y
   debe pasar AA en las TRES superficies, no solo sobre --bg-secondary.

   Los NOMBRES de los tokens se conservan del sistema anterior para que páginas
   y componentes hereden el cambio de mundo visual sin tocar cada archivo. */
:root {
    /* Paleta institucional — modo claro ("papel técnico") */
    --bg-primary: #F4F7FA;   /* Papel Técnico: fondo de página */
    --bg-secondary: #FFFFFF; /* Blanco de Superficie: tarjetas, paneles, filas */
    --bg-tertiary: #EDF1F6;  /* superficie hundida: pistas, encabezados, hover */

    --text-primary: #0F1E30;   /* Tinta Andina */
    --text-secondary: #3E5068; /* Tinta Secundaria */
    --text-muted: #5A6B80;     /* Tinta Atenuada: nunca <4.5:1 sobre su fondo */

    --accent-primary: #155E9C;        /* Azul Institucional: el único color de acción */
    --accent-primary-hover: #0F4A7D;  /* Azul Profundo */
    --accent-primary-rgb: 21, 94, 156;
    --accent-secondary: #92610F;      /* Ocre de Obra: sello secundario escaso */
    --accent-secondary-rgb: 146, 97, 15;
    /* Ocre como TEXTO sobre su propio tinte al 12%: el base caía a 4.04:1. */
    --accent-secondary-ink: #85590E;
    /* No hay tercer acento. El violeta de IA (--accent-ai) existió como token
       transicional mientras quedaban páginas sin restilar; con el Constructor
       migrado ya no lo referencia nadie y se retiró. La IA se señala con el
       mismo azul institucional y con palabras, no con un color propio
       (DESIGN.md · un solo acento de acción). */

    /* Texto sobre el acento primario (azul institucional): blanco ≈6.7:1 (AA). */
    --on-accent: #FFFFFF;
    /* Texto sobre el rojo de error sólido (botón destructivo): blanco 4.83:1. */
    --on-error: #FFFFFF;

    --success: #047857;
    --error: #DC2626;
    --warning: #B45309;
    --info: #155E9C; /* info = primario */

    /* Tinta semántica para TEXTO sobre el tinte de su propio color (badges y
       chips al 12%). El color base pierde contraste al pintarse encima de su
       propia mezcla, así que estas variantes lo devuelven por encima de 4.5:1
       sin cambiar el color semántico de bordes, iconos ni rellenos sólidos.
       MEDIDO CONTRA LAS TRES SUPERFICIES, no solo contra --bg-secondary: el
       badge también cae sobre la página y sobre la fila hundida, y ahí los
       valores anteriores fallaban (éxito 4.33/4.10, error 4.45/4.24). */
    --success-ink: #046F51;
    --warning-ink: #9E4908;
    --error-ink: #BB2020;

    /* Superficies sólidas con borde. Los nombres --glass-* se conservan como
       alias heredados, pero ya no hay glassmorphism (Flat-By-Default Rule):
       superficie blanca + Trazo de Plano. */
    --glass-bg: #FFFFFF;
    /* Trazo de Plano: bordes y divisores. En un sistema plano el borde SUSTITUYE
       a la sombra, así que es el separador estructural real (197 usos). A 1.33:1
       contra su superficie apenas se insinuaba; a ~1.65:1 la retícula se lee.
       El trazo es UNIFORME en los ocho temas: no es un rasgo de atmósfera,
       porque cambiar su peso cambia cómo se lee la jerarquía. */
    --glass-border: #C3CDD8;
    /* Trazo de CONTROL: el borde de un campo de formulario no es un divisor
       decorativo, es lo ÚNICO que identifica el control — .form-input tiene
       exactamente el mismo fondo que la tarjeta que lo contiene, así que sin
       borde perceptible el campo desaparece. Eso es SC 1.4.11 (Non-text
       Contrast), que pide 3:1 contra el fondo adyacente, y el Trazo de Plano
       (1.20-1.96:1 medido contra las tres superficies) no llega. Este token es
       el mismo neutro subido de peso hasta ≥3.19:1 contra las TRES
       superficies. NO se usa para divisores ni bordes de tarjeta: ahí manda
       --glass-border, y su ligereza es deliberada. */
    --control-border: #80878E;

    /* Alias de compatibilidad para estilos inline heredados. */
    --primary: var(--accent-primary);
    --border-color: var(--glass-border);
    --modal-backdrop: rgba(15, 30, 48, 0.45);

    /* Escala de capas (z-index) documentada para evitar conflictos:
       overlays < modales < toasts (los toasts SIEMPRE visibles por encima). */
    --z-overlay: 1000;
    --z-modal: 10000;
    --z-toast: 99999;

    /* Variables estructurales */
    --sidebar-width: 256px;
    --header-height: 70px;
    --border-radius: 8px;     /* estándar: tarjetas, inputs, botones */
    --border-radius-sm: 6px;  /* controles pequeños: chips, badges */
    --border-radius-lg: 12px; /* modales y drawer */

    /* Escala de espaciado única (DESIGN.md · Layout) */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 24px;
    --space-6: 32px;

    /* Sombra única del sistema — SOLO para overlays reales (modales, popovers,
       drawer móvil, toasts). Ninguna superficie en reposo lleva sombra. */
    --shadow-overlay: 0 8px 24px rgba(15, 30, 48, 0.16);

    /* Transiciones */
    --transition-fast: 0.2s ease;
    --transition-normal: 0.3s ease;

    color-scheme: light;
}

/* ── Modo oscuro institucional (override) ────────────────────────────────────
   Misma arquitectura de tokens con la paleta carbonizada de DESIGN.md.
   Lo fija data-mode="dark" (theme-service.js + script anti-FOUC en index.html). */
:root[data-mode="dark"] {
    --bg-primary: #0E141B;
    --bg-secondary: #172029;
    --bg-tertiary: #1E2A36;

    --text-primary: #E8EEF4;
    --text-secondary: #B9C6D6;
    --text-muted: #8DA0B5;

    --accent-primary: #5AA3DE;       /* primario aclarado para contraste */
    --accent-primary-hover: #7CB8E8;
    --accent-primary-rgb: 90, 163, 222;
    --accent-secondary: #D9A441;     /* ocre aclarado */
    --accent-secondary-rgb: 217, 164, 65;
    --accent-secondary-ink: #D9A441; /* en oscuro el base ya cumple sobre su tinte */

    /* El azul aclarado es claro: el texto legible encima es tinta oscura. */
    --on-accent: #0E141B;
    /* Lo mismo con el rojo aclarado: blanco encima cae a 2.77:1; tinta 6.65:1. */
    --on-error: #0E141B;

    --success: #34C08B;
    --error: #F87171;
    --warning: #E8A33D;
    --info: #5AA3DE;

    /* En oscuro el tinte del 12% ACLARA el fondo: los colores ya superan 4.5:1
       sobre él, así que la tinta semántica es el color base sin aclarar. La
       excepción es el error sobre la fila hundida (4.44:1 medido), que sube un
       punto para cumplir en las TRES superficies. */
    --success-ink: var(--success);
    --warning-ink: var(--warning);
    --error-ink: #F87777;

    --glass-bg: #172029;
    --glass-border: #3A4553; /* mismo trazo de ~1.65:1 que el modo claro */
    --control-border: #6F7781;

    --modal-backdrop: rgba(0, 0, 0, 0.55);
    --shadow-overlay: 0 8px 24px rgba(0, 0, 0, 0.45);

    color-scheme: dark;
}

/* ════════════════════════════════════════════════════════════════════════════
   ATMÓSFERAS DE MATERIAL — "Obra: seis materiales, una sola luz de trabajo"
   ════════════════════════════════════════════════════════════════════════════
   Seis superficies que el ingeniero ya toca en campo, traducidas a tono plano.
   Lo que cambia entre ellas es el MATERIAL sobre el que está impresa la app:
   temperatura del neutro y profundidad de las capas. Lo que NO cambia: el azul
   de acción (un solo acento, retocado en tono para contrastar sobre cada
   fondo), la tipografía, la forma, la jerarquía y el peso del trazo.

   Cada bloque solo redefine tokens de COLOR. Ninguno toca espaciado, radios,
   z-index ni tipografía: eso vive una sola vez arriba.

   Todos los valores están medidos, no estimados (instrumento calibrado contra
   negro/blanco = 21.00 y blanco sobre #155E9C = 6.75). Criterio de aprobación,
   aplicado a los OCHO temas con CERO fallos:
     · tinta principal / secundaria / atenuada ≥ 4.5:1 sobre las TRES superficies
     · acento y su hover ≥ 4.5:1 sobre las tres superficies Y sobre su propio
       tinte al 10% (el patrón real de .badge-info)
     · ocre ≥ 4.5:1 sobre las tres superficies; --accent-secondary-ink ≥ 4.5:1
       sobre el tinte de ocre al 12% compuesto sobre las tres
     · --*-ink ≥ 4.5:1 sobre el tinte al 12% de su color compuesto sobre las
       TRES superficies (no solo sobre --bg-secondary: el badge también cae
       sobre la página y sobre la fila hundida)
     · color semántico base ≥ 3:1 sobre ese mismo tinte (iconos, SC 1.4.11)
     · --on-accent / --on-error ≥ 4.5:1 sobre su relleno sólido
     · --accent-primary-rgb y --accent-secondary-rgb recalculados desde SU hex

   ERROR SILENCIOSO MÁS PROBABLE: cambiar --accent-primary y olvidar
   --accent-primary-rgb. Alimenta los rgba() del anillo de foco, el nav activo,
   la dropzone, el blockquote y el stat-icon; si se desalinean, esos tintes se
   quedan con el azul del tema anterior y NINGÚN test de contraste lo detecta.
   Lo mismo con --accent-secondary-rgb.

   ── Modo claro ──────────────────────────────────────────────────────────── */

/* CAL — el muro encalado. Blanco cálido de cal viva, la luz más alta del
   sistema, para leer pliegos largos sin fatiga. La crema vive TAMBIÉN en la
   tarjeta (retiene el 77% del croma de la página), no solo en la canaleta:
   si el material se queda en el margen, el tema es un paspartú, no un tema. */
:root[data-mode="light"][data-theme="cal"] {
    --bg-primary: #F5EEDC;
    --bg-secondary: #FCF7E9;
    --bg-tertiary: #EBE2CC;

    --text-primary: #221E16;
    --text-secondary: #4A4233;
    --text-muted: #655C4B;

    --accent-primary: #12558F;
    --accent-primary-hover: #0E4270;
    --accent-primary-rgb: 18, 85, 143;
    --accent-secondary: #7E5410;
    --accent-secondary-rgb: 126, 84, 16;
    --accent-secondary-ink: #79510F;

    --success: #046A4E;
    --error: #C42222;
    --warning: #96450A;
    --info: #12558F;

    --success-ink: #04654B;
    --warning-ink: #90420A;
    --error-ink: #AB1E1E;

    --glass-bg: #FCF7E9;
    --glass-border: #C6C2B7;
    --control-border: #7F7D76;

    --modal-backdrop: rgba(34, 30, 22, 0.45);
    --shadow-overlay: 0 8px 24px rgba(60, 48, 26, 0.16);

    color-scheme: light;
}

/* ACERO — chapa galvanizada. Neutro frío azulado y la retícula más marcada de
   los claros. El id es "galvanizado" y NO "acero" a propósito: "acero" es un id
   LEGADO del sistema fotográfico anterior y era OSCURO; reutilizarlo haría que
   el script anti-FOUC arrancara este tema claro con data-mode="dark" (destello
   negro a pantalla completa y desplegables nativos ilegibles) y dejaría sin
   migración a quien tuviera guardado el acero oscuro. */
:root[data-mode="light"][data-theme="galvanizado"] {
    --bg-primary: #D1DEEA;
    --bg-secondary: #E4EDF6;
    --bg-tertiary: #C0D0DE;

    --text-primary: #0F171E;
    --text-secondary: #33404C;
    --text-muted: #465464;

    --accent-primary: #124E8A;
    --accent-primary-hover: #0E3D6C;
    --accent-primary-rgb: 18, 78, 138;
    --accent-secondary: #794D07;
    --accent-secondary-rgb: 121, 77, 7;
    --accent-secondary-ink: #6B4406;

    --success: #04724F;
    --error: #C92020;
    --warning: #9C4808;
    --info: #124E8A;

    --success-ink: #03593D;
    --warning-ink: #7D3A06;
    --error-ink: #951818;

    --glass-bg: #E4EDF6;
    --glass-border: #B3BAC2;
    --control-border: #6B6F74;

    --modal-backdrop: rgba(15, 23, 30, 0.45);
    --shadow-overlay: 0 8px 24px rgba(15, 33, 51, 0.18);

    color-scheme: light;
}

/* HORMIGÓN — hormigón curado y encofrado. El único tema claro sin un solo
   blanco: las superficies se separan por peso, no por brillo. Es el que peor lo
   pasa de contraste (su página es la más oscura de los claros, así que los
   tintes del 12% se compositan sobre un gris ya cargado) y por eso sus tintas
   semánticas están las más oscurecidas del sistema. */
:root[data-mode="light"][data-theme="hormigon"] {
    --bg-primary: #CDD0CA;
    --bg-secondary: #E1E4DE;
    --bg-tertiary: #C0C4BE;

    --text-primary: #101310;
    --text-secondary: #343934;
    --text-muted: #474D46;

    --accent-primary: #0C4675;
    --accent-primary-hover: #09365B;
    --accent-primary-rgb: 12, 70, 117;
    --accent-secondary: #684509;
    --accent-secondary-rgb: 104, 69, 9;
    --accent-secondary-ink: #5E3F08;

    --success: #04604B;
    --error: #B41C1C;
    --warning: #83380A;
    --info: #0C4675;

    --success-ink: #034F3E;
    --warning-ink: #733109;
    --error-ink: #881515;

    --glass-bg: #E1E4DE;
    --glass-border: #B0B3AE;
    --control-border: #666765;

    --modal-backdrop: rgba(16, 19, 16, 0.48);
    --shadow-overlay: 0 8px 24px rgba(20, 26, 20, 0.20);

    color-scheme: light;
}

/* ── Modo oscuro ───────────────────────────────────────────────────────────
   Los tres oscuros forman una escalera de profundidad: asfalto L* 2.2, oscuro
   institucional L* 6.1, grafito L* 17.2. Óxido (L* 8.4) se separa por
   temperatura, no por profundidad. */

/* ASFALTO — asfalto recién tendido. Negro mate casi sin croma (C* 0.5, el
   neutro más puro del sistema) y capas apenas despegadas del fondo: la
   superficie más silenciosa, para trabajar de noche. */
:root[data-mode="dark"][data-theme="asfalto"] {
    --bg-primary: #070809;
    --bg-secondary: #101314;
    --bg-tertiary: #181C1D;

    --text-primary: #E8EDEC;
    --text-secondary: #B3BCBB;
    --text-muted: #8B9695;

    --accent-primary: #5FA9E2;
    --accent-primary-hover: #7CB9E8;
    --accent-primary-rgb: 95, 169, 226;
    --accent-secondary: #D9A441;
    --accent-secondary-rgb: 217, 164, 65;
    --accent-secondary-ink: #D9A441;

    --on-accent: #070809;
    --on-error: #070809;

    --success: #34C08B;
    --error: #F87171;
    --warning: #E8A33D;
    --info: #5FA9E2;

    --success-ink: #34C08B;
    --warning-ink: #E8A33D;
    --error-ink: #F87171;

    --glass-bg: #101314;
    --glass-border: #383B3C;
    --control-border: #696B6C;

    --modal-backdrop: rgba(0, 0, 0, 0.62);
    --shadow-overlay: 0 8px 24px rgba(0, 0, 0, 0.55);

    color-scheme: dark;
}

/* GRAFITO — mina 6B sobre papel volteado. Gris azulado claramente levantado del
   negro: el oscuro que todavía se lee como plano dibujado. Al ser el más claro
   de los oscuros, su azul tiene que subir a L* 78 para seguir cumpliendo AA
   sobre su propio tinte; es el mismo azul, más alto de tono (hue 255 frente a
   259-262 del resto de oscuros). */
:root[data-mode="dark"][data-theme="grafito"] {
    --bg-primary: #232B35;
    --bg-secondary: #2E3742;
    --bg-tertiary: #39434F;

    --text-primary: #F6F9FC;
    --text-secondary: #C6CFD9;
    /* Grafito es el oscuro más claro del sistema, así que su tinta atenuada es
       la que menos margen tiene. Con #A6B1BF cumplía sobre las tres superficies
       (6.58 / 5.55 / 4.62) pero caía a 4.46:1 sobre el tinte del acento al 10%
       — el fondo del ítem SELECCIONADO del menú de temas, rgb(55,70,84)—, que
       es una cuarta superficie que la matriz de tres no vigila. Este valor deja
       ese par en 4.88 y sube el peor caso del tema a 5.06. */
    --text-muted: #AEB9C7;

    --accent-primary: #87C9FA;
    --accent-primary-hover: #9DD3FB;
    --accent-primary-rgb: 135, 201, 250;
    --accent-secondary: #E2B158;
    --accent-secondary-rgb: 226, 177, 88;
    --accent-secondary-ink: #E7BD72;

    --on-accent: #232B35;
    --on-error: #232B35;

    --success: #48D09D;
    --error: #FB9393;
    --warning: #F0B455;
    --info: #87C9FA;

    --success-ink: #66D8AD;
    --warning-ink: #F2BC66;
    --error-ink: #FCAEAE;

    --glass-bg: #2E3742;
    --glass-border: #4E5766;
    --control-border: #8C929C;

    --modal-backdrop: rgba(10, 13, 16, 0.55);
    --shadow-overlay: 0 8px 24px rgba(0, 0, 0, 0.45);

    color-scheme: dark;
}

/* ÓXIDO — hierro oxidado. El único soporte cálido del sistema (a* +2.5, b*
   +4.5; todos los demás tienen b* negativo o nulo), contra el que el azul
   institucional queda en complementario y manda sin subir de tono.
   Su croma de página es 5.1: por debajo del croma del oscuro institucional
   vigente (5.6). Eso es deliberado — la temperatura de un neutro es soporte, no
   señal; en cuanto un fondo sube de croma empieza a competir con el color que
   SÍ significa algo (The Semantic Ink Rule). Y es el tema que más depende de
   que el estado se acompañe siempre de texto o icono: su suelo ferroso está en
   el mismo bando cromático que el rojo y el ámbar. */
:root[data-mode="dark"][data-theme="oxido"] {
    --bg-primary: #1E1712;
    --bg-secondary: #2A211A;
    --bg-tertiary: #362B22;

    --text-primary: #F3EDE7;
    --text-secondary: #D4C7BA;
    --text-muted: #AC9C8C;

    --accent-primary: #6FB3E8;
    --accent-primary-hover: #89C1EC;
    --accent-primary-rgb: 111, 179, 232;
    --accent-secondary: #E0A94A;
    --accent-secondary-rgb: 224, 169, 74;
    --accent-secondary-ink: #E0A94A;

    --on-accent: #1E1712;
    --on-error: #1E1712;

    --success: #40CE9A;
    --error: #FA8181;
    --warning: #F0BE4F;
    --info: #6FB3E8;

    --success-ink: #40CE9A;
    --warning-ink: #F0BE4F;
    --error-ink: #FA8484;

    --glass-bg: #2A211A;
    --glass-border: #4E4438;
    --control-border: #807970;

    --modal-backdrop: rgba(12, 7, 4, 0.58);
    --shadow-overlay: 0 8px 24px rgba(0, 0, 0, 0.50);

    color-scheme: dark;
}

/* Reset Básico */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    /* Una sola familia de trabajo (DESIGN.md): Public Sans. */
    font-family: 'Public Sans', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
    font-size: 15px;
    background-color: var(--bg-primary);
    color: var(--text-primary);
    transition: background-color var(--transition-normal), color var(--transition-normal);
    line-height: 1.5;
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Jerarquía tipográfica (DESIGN.md): la autoridad viene del orden, no del tamaño. */
h1, h2, h3, h4, h5, h6 {
    color: var(--text-primary);
    font-family: inherit;
    letter-spacing: -0.01em;
}
h1 { font-size: 28px; font-weight: 700; line-height: 1.2; }  /* Display */
h2 { font-size: 20px; font-weight: 600; line-height: 1.3; }  /* Headline */
h3 { font-size: 16px; font-weight: 600; line-height: 1.4; }  /* Title */
h4, h5, h6 { font-weight: 600; }

a {
    text-decoration: none;
    color: var(--accent-primary);
    transition: color var(--transition-fast);
}

a:hover {
    color: var(--accent-primary-hover);
}

button {
    cursor: pointer;
    font-family: inherit;
    border: none;
    background: none;
}

/* Placeholder tokenizado A NIVEL DE ELEMENTO, no por clase. Sin esta regla,
   cualquier campo que no declare ::placeholder hereda el gris por defecto de
   Chrome (#757575), que no se entera del tema: medido sobre la superficie de
   cada uno da 4.61:1 en claro (pasa por casualidad) y baja hasta 2.62:1 en
   grafito — falla en 7 de los 8. Solo dos reglas lo declaraban (.form-input y
   .search-input-pro), así que las clases locales de página y los inputs con
   estilo inline se quedaban con el gris del navegador. */
input::placeholder,
textarea::placeholder {
    color: var(--text-muted);
    /* Firefox baja el placeholder a opacity .54 por defecto y eso se comería
       el contraste que acabamos de fijar. */
    opacity: 1;
}

/* Trazo de CONTROL a nivel de elemento (SC 1.4.11): alcanza a los campos con
   estilo inline y a los que no llevan clase. Las clases de componente y de
   página lo declaran explícitamente en components.css, porque una regla de
   clase gana a esta por especificidad. */
input, textarea, select {
    border-color: var(--control-border);
}

/* Foco visible en todo control interactivo (DESIGN.md): anillo de 2px del
   acento con offset. Sin excepciones; nunca outline:none sin sustituto. */
:focus-visible {
    outline: 2px solid var(--accent-primary);
    outline-offset: 2px;
}

ul {
    list-style: none;
}

/* Layout Principal */
.app-container {
    display: flex;
    min-height: 100vh;
}

.main-content {
    flex: 1;
    display: flex;
    flex-direction: column;
    margin-left: var(--sidebar-width);
    /* Altura EXACTA, no mínima: es lo que convierte a .router-view en el
       scrollport real de la app. Con `min-height` la columna crecía con su
       contenido, el que scrolleaba era el documento y .router-view quedaba como
       un contenedor de scroll que nunca scrollea —inerte pero declarado, así
       que secuestraba a todos sus descendientes: cualquier `position: sticky`
       dentro de una página se iba con el scroll en vez de pegarse (le pasaba a
       .pf-aside de Perfil y a .field-mapper-container). Con la altura fija, el
       header queda fuera del área que scrollea y lo sticky se pega debajo de
       él, en `top: 0` del scrollport. */
    height: 100vh;
    /* Sin esto, el `min-width: auto` implícito del flex item deja que la columna
       crezca hasta el ancho mínimo de su contenido (p. ej. la tabla de 1155px de
       Licitaciones) y arrastra al body a scrollear en horizontal. Con min-width:0
       el ancho lo manda el viewport y el scroll horizontal se queda dentro del
       contenedor que lo necesita (.pj-table-host, .table-container). */
    min-width: 0;
}

.router-view {
    flex: 1;
    /* Sin `min-height: 0` un flex item no baja de su altura de contenido, así
       que `flex: 1` no lo acotaría y no habría nada que scrollear. */
    min-height: 0;
    padding: 2rem;
    overflow-y: auto;
}

/* Responsive básico */
@media (max-width: 768px) {
    .main-content {
        margin-left: 0;
    }
}

/* Utilidades de Diseño */
/* .glass-panel: nombre heredado; hoy es una superficie sólida plana con borde
   (Flat-By-Default Rule: sin blur, sin sombra en reposo). */
.glass-panel {
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--border-radius);
}

/* Animaciones */
@keyframes fadeIn {
    from { opacity: 0; transform: translateY(10px); }
    to { opacity: 1; transform: translateY(0); }
}

.animate-fade-in {
    animation: fadeIn 0.4s ease forwards;
}
