/* =========================================================
   Pyxoom360 Global Styles - Light & Dark Mode Support
   ========================================================= */

/* =========================================================
   Identidad Pyxoom360 — tokens de plataforma
   Importados de "Identidad Pyxoom360" (wwwroot/css/pyxoom.css del proyecto de diseño).
   Sólo vive aquí lo que cruza componentes; el resto va en el *.razor.css de cada uno.
   Ojo: esta es la escala FRÍA de la pantalla. La escala cálida del papel de los reportes
   impresos está en PyxoomBrandDefaults.Neutros y no se toca desde aquí.
   ========================================================= */
:root {
    /* Constantes de marca: no dependen del tema. */
    --pyx-navy: #0f1b34;
    --pyx-blue: #225dc1;
    --pyx-blue-deep: #2137cd;
    /* Bug 22854 (2026-09-14): esta SÍ dependía del tema y no debía estar en el bloque de
       "constantes de marca" -- un azul casi blanco fijo puesto de fondo detrás de texto (la
       pregunta/celda activa en Interactivo) se volvía invisible en modo oscuro, porque el texto
       oscuro nunca cambia junto con este fondo. color-mix con transparent dejar pasar la
       superficie real de detrás (--mud-palette-surface, que SÍ cambia con el tema), igual que
       --pyx-canvas-soft y compañía arriba. */
    --pyx-blue-soft: color-mix(in srgb, var(--mud-palette-primary) 12%, transparent);
    /* Estrellas de calificación (Interactivo/Index.razor, CalificacionMultiple.razor) -- dorado
       clásico de rating, separado de --pyx-blue porque no es un color de marca ni de estado. */
    --pyx-star-gold: #ffb400;
    /* Corrección de estilo (2026-08-04), punto 32: "aparece un texto así como de máquina
       de escribir, no me gusta porque se ve muy vintage... hay que poner uno igual al
       estilo general, y quizá bajarle el tamaño o que sea más delgada la letra". El
       rótulo deja de ser monoespaciado y pasa a la tipografía general; conserva su papel
       de etiqueta menor (tamaño chico, versalitas) pero con el peso del resto del app. */
    --pyx-label: var(--pyx-sans);
    --pyx-sans: 'Plus Jakarta Sans', system-ui, 'Segoe UI', Roboto, sans-serif;
    /* Escala del texto chico (etiquetas, encabezados de tabla, datos tabulares, notas al pie) --
       formalizada 2026-09-14: eran 38 reglas con var(--pyx-label) y un font-size a mano cada una,
       con el mismo rol repitiéndose en tamaños ligeramente distintos por simple deriva (ver
       migración en cada regla de abajo). Cuatro escalones reales, por frecuencia de uso real, no
       por redondeo arbitrario. */
    /* AB#23304: los escalones de 9 / 9.5 / 10 px se leían demasiado chicos (pasada visual); piso de 10.5 px. */
    --pyx-font-3xs: 10.5px; /* encabezados de columna en tablas densas */
    --pyx-font-2xs: 10.5px; /* etiqueta/eyebrow/badge en versalitas -- el escalón más usado */
    --pyx-font-xs: 11px;    /* nota al pie / metadato secundario */
    --pyx-font-sm: 11.5px;  /* dato numérico tabular / chip denso */
    --pyx-positive: oklch(0.64 0.12 162);
    --pyx-caution: oklch(0.70 0.13 68);
    --pyx-negative: oklch(0.60 0.17 28);
    /* círculos evaluadores — mismo orden que el enum Circulo */
    --pyx-c-auto: #225dc1;
    --pyx-c-jefes: oklch(0.56 0.15 296);
    --pyx-c-pares: oklch(0.62 0.12 205);
    --pyx-c-colab: oklch(0.64 0.12 162);
    --pyx-c-clien: oklch(0.70 0.13 68);

    /* Superficies y tinta: se derivan de la paleta de MudBlazor, no se fijan en hex.
       MudThemeProvider intercambia el VALOR de --mud-palette-* al cambiar a modo oscuro y no
       marca el DOM con ninguna clase (.mud-theme-dark es una utilidad de color, no el modo),
       así que colgar overrides de una clase no funcionaría: los fondos claros del mapa, del
       heatmap y del índice se quedarían incrustados sobre el tema oscuro.
       En claro estos resuelven exactamente a los valores de PyxoomBrandDefaults.Plataforma,
       porque son los que ThemeService carga en PaletteLight. */
    --pyx-canvas: var(--mud-palette-background);
    --pyx-canvas-soft: var(--mud-palette-background-gray);
    --pyx-border: var(--mud-palette-lines-default);
    --pyx-border-strong: var(--mud-palette-lines-inputs);
    --pyx-divider: var(--mud-palette-divider-light);
    --pyx-ink: var(--mud-palette-text-primary);
    --pyx-ink-muted: var(--mud-palette-text-secondary);
    /* AB#23304: antes usaba text-disabled (contraste 3.1-3.4:1, bajo el 4.5:1 de AA); ahora el secundario. */
    --pyx-ink-faint: var(--mud-palette-text-secondary);
    --pyx-c-empty: var(--mud-palette-lines-default);

    /* Texto/superficies blancas fijas sobre cualquier fondo oscuro de marca (--pyx-navy en el
       login, --pyx-blue en el panel lateral, --pyx-navy en tarjetas de énfasis): NO se derivan
       de --mud-palette-*, esas cambian con el tema y estos fondos no. Blanco a .82/.60/.40 de
       opacidad da >= 4.5:1 de contraste WCAG AA contra ambos fondos (verificado: 17.1:1 sobre
       navy, 6.2:1 sobre blue) -- no bajar estos valores sin volver a verificar contraste. */
    --pyx-ink-on-dark: rgba(255, 255, 255, .82);
    --pyx-ink-on-dark-muted: rgba(255, 255, 255, .6);
    --pyx-ink-on-dark-faint: rgba(255, 255, 255, .4);
    --pyx-border-on-dark: rgba(255, 255, 255, .08);
    --pyx-border-on-dark-strong: rgba(255, 255, 255, .10);
    --pyx-surface-on-dark-hover: rgba(255, 255, 255, .08);

    /* Escala de espaciado. Antes convivían tres: PageHeader con 18/22, PageSection con 24 y
       el Panorama con 18/20/22. Un solo padding de tarjeta y un solo ritmo entre bloques.
       --pyx-pad-card-h es sólo el componente horizontal, para los pocos sitios (barra de
       filtros colapsable) que necesitan combinarlo con un valor vertical propio dentro del
       mismo shorthand -- var() sustituye por tokens, así que meter --pyx-pad-card (dos
       tokens: "20px 22px") en un slot de un shorthand de padding de varios valores rompe la
       declaración entera y cae en el reset "* { padding: 0 }" de MudBlazor (bug real,
       encontrado en vivo 2026-09-15 con el panel "Filtros" pegado a los bordes). */
    --pyx-pad-card: 20px 22px;
    --pyx-pad-card-h: 22px;
    --pyx-gap-block: 18px;
    --pyx-gutter-top: 24px;
    --pyx-gap-toolbar: 16px;
    --pyx-gap-controls: 8px;

    /* Alto de contenido de .pyx-topbar (barra superior móvil, < 960px) sin contar el
       env(safe-area-inset-top) que la propia barra añade como padding. */
    --pyx-topbar-height: 56px;
}

/* Tokens de espaciado más compactos por debajo de sm (599px): --pyx-pad-card a 20px 22px se come
   12.8% del ancho de un teléfono de 375px en cada tarjeta, diálogo y encabezado -- la ganancia
   más barata disponible para móvil porque no toca ningún markup. */
@media (max-width: 599px) {
    :root {
        --pyx-pad-card: 12px 14px;
        --pyx-pad-card-h: 14px;
        --pyx-gap-block: 12px;
        --pyx-gutter-top: 16px;
    }
}

body {
    -webkit-font-smoothing: antialiased;
    text-wrap: pretty;
    /* Sin esto, cada vez que el contenido pasa de necesitar scroll vertical a no necesitarlo (p.
       ej. una tabla de filtros que pasa de 2 filas a un EmptyState corto) el ancho disponible de
       la página cambia por el grosor de la barra (~15-17px), y cualquier fila flex cerca del
       límite de su ancho se reacomoda o incluso rompe línea. Reserva el gutter siempre. */
    scrollbar-gutter: stable;
}

/* Rótulo monoespaciado en versalitas que encabeza cada bloque de la plataforma. */
.pyx-eyebrow {
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-2xs);
    font-weight: 600;
    letter-spacing: .12em;
    text-transform: uppercase;
    color: var(--pyx-ink-faint);
}

.pyx-mono {
    font-family: var(--pyx-label);
    font-size: 12px;
    color: var(--pyx-ink-faint);
}

/* Gutter de página. Sin appbar el contenido arrancaba pegado al borde superior. */
.pyx-page {
    padding-top: var(--pyx-gutter-top);
}

/* Breakpoints canónicos (mismos que MudBlazor Breakpoint, menos 1px para "por debajo de"):
   @media (max-width: 599px)  -> por debajo de sm (600)
   @media (max-width: 959px)  -> por debajo de md (960)
   @media (max-width: 1279px) -> por debajo de lg (1280)
   No introducir otro valor sin justificarlo en un comentario junto a la regla. */

/* Corrección: esta regla compensaba el hamburguesa flotante de cuando el drawer se ocultaba en
   Lg (1280px). UX 22591 bajó ese umbral a Md (960px) pero la regla se quedó en 1279px -- entre
   960 y 1279px quedaban 64px de relleno superior sin ningún elemento que los justificara. Ahora
   compensa .pyx-topbar (la barra superior fija que reemplazó al hamburguesa suelto), que vive
   exactamente bajo el mismo umbral de 960px que el drawer. */
@media (max-width: 959px) {
    .pyx-page {
        padding-top: calc(var(--pyx-topbar-height) + env(safe-area-inset-top) + 16px);
    }
}

/* Sección de contenido: mismo padding y mismo ritmo que el encabezado de página. */
.pyx-section {
    padding: var(--pyx-pad-card);
    margin-bottom: var(--pyx-gap-block);
}

/* La plataforma no usa sombra: la tarjeta se delimita con 1px de borde. */
.pyx-card {
    border: 1px solid var(--pyx-border);
    border-radius: 12px;
    background: var(--mud-palette-surface);
}

/* Tarjeta de acento. El filete claro no se ve sobre el lienzo claro, pero en modo oscuro el
   fondo de página ES el navy de marca: sin él, esta tarjeta desaparecería contra la página.
   Doble clase para ganarle a `.mud-paper { background-color: ... }`, que se define más abajo
   en este mismo archivo y con una sola clase empataba en especificidad y ganaba por orden. */
.pyx-card--dark,
.mud-paper.pyx-card--dark {
    border: 1px solid var(--pyx-border-on-dark-strong);
    border-radius: 12px;
    background-color: var(--pyx-navy);
    color: #fff;
}

.pyx-card--dark .mud-typography {
    color: inherit;
}

/* AB#23225: acento de la tarjeta «Total» de Interactive > Progreso solo con tokens del tema
   (primary cambia con el modo claro/oscuro; el navy fijo de .pyx-card--dark no). Doble clase
   para ganarle a MudBlazor a igual especificidad. */
.mud-paper.pyx-kpi--total {
    border-color: var(--mud-palette-primary);
}

.mud-paper.pyx-kpi--total > b {
    color: var(--mud-palette-primary);
}

/* Scrollbar */
::-webkit-scrollbar {
    width: 8px;
    height: 8px;
}

::-webkit-scrollbar-track {
    background: var(--mud-palette-background);
    border-radius: 4px;
}

::-webkit-scrollbar-thumb {
    background: var(--mud-palette-text-disabled);
    border-radius: 4px;
}

::-webkit-scrollbar-thumb:hover {
    background: var(--mud-palette-text-secondary);
}

/* Body & Typography */
body {
    font-family: var(--pyx-sans);
    background-color: var(--mud-palette-background);
    color: var(--mud-palette-text-primary);
}

/* =========================================================
   Layout Components
   ========================================================= */

/* En escritorio no hay barra superior: la navegación, la marca y las acciones de usuario viven
   todas en el sidebar. Las reglas de .appbar-glass se retiraron con ella.

   Por debajo de Md (960px, UX 22591) el drawer se oculta y .pyx-topbar "traslada" esa misma
   identidad y esas mismas acciones a una barra horizontal fija: mismo azul que el drawer
   (.drawer-custom usa --pyx-blue, no --pyx-navy -- HU-3, 6.2:1 de contraste con blanco), para que
   se lea como una continuación del mismo panel y no como una barra nueva sin relación.
   position:fixed + z-index alto porque MudMainContent no reserva espacio para ella (no es un
   MudAppBar); .pyx-page compensa con padding-top más arriba en este archivo. */
.pyx-topbar {
    display: none;
}

@media (max-width: 959px) {
    .pyx-topbar {
        display: flex;
        align-items: center;
        gap: 6px;
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        z-index: 1300;
        min-height: var(--pyx-topbar-height);
        padding: 0 8px;
        padding-top: env(safe-area-inset-top);
        box-sizing: border-box;
        background-color: var(--pyx-blue);
        border-bottom: 1px solid var(--pyx-border-on-dark);
        box-shadow: 0 2px 10px rgba(15, 27, 52, .12);
    }
}

/* Íconos y textos de la barra siempre claros sobre el azul de marca, tema claro u oscuro --
   mismo criterio que .drawer-footer más abajo (HU-3: el panel es azul fijo, no sigue el tema). */
.pyx-topbar .mud-button-root,
.pyx-topbar .mud-icon-root {
    color: var(--pyx-ink-on-dark) !important;
}

.pyx-topbar-icon {
    flex-shrink: 0;
}

/* Marca: logo Pyxoom + logo del tenant, horizontal, ocupando el hueco entre el hamburguesa y las
   acciones. min-width:0 es lo que permite que .pyx-mark (ancho por cqw) se comprima en vez de
   forzar overflow cuando el tenant también trae logo. */
.pyx-topbar-brand {
    display: flex;
    align-items: center;
    gap: 8px;
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
}

.pyx-topbar-tenant {
    display: flex;
    align-items: center;
    flex-shrink: 0;
    padding-left: 8px;
    margin-left: 2px;
    border-left: 1px solid var(--pyx-border-on-dark);
}

.pyx-topbar-tenant img {
    max-height: 32px;
    max-width: 84px;
    object-fit: contain;
    background: #fff;
    border-radius: 6px;
    padding: 3px 6px;
    display: block;
}

/* Bajo 360px ambos logos ya no caben cómodos con las acciones -- se recorta el del tenant, nunca
   el de Pyxoom360 (es quien "presta" el portal). */
@media (max-width: 359px) {
    .pyx-topbar-tenant img {
        max-width: 56px;
    }
}

.pyx-topbar-actions {
    display: flex;
    align-items: center;
    gap: 0;
    flex-shrink: 0;
}

.pyx-topbar-avatar {
    cursor: pointer;
}

/* Barra inferior de Interactivo (BottomNav.razor) -- estilo Instagram, sólo bajo 960px y sólo
   pintada por Interactivo/Index.razor cuando corresponde (nunca durante la captura). Fondo claro
   (no azul): a diferencia de .pyx-topbar, que "es" el sidebar trasladado, esta barra es nueva --
   se apoya en la superficie normal de la app para no competir visualmente con el contenido. */
.pyx-bottomnav {
    display: none;
}

@media (max-width: 959px) {
    .pyx-bottomnav {
        display: flex;
        position: fixed;
        bottom: 0;
        left: 0;
        right: 0;
        z-index: 1200;
        background-color: var(--mud-palette-surface);
        border-top: 1px solid var(--pyx-border);
        padding-bottom: env(safe-area-inset-bottom);
        box-shadow: 0 -2px 10px rgba(15, 27, 52, .08);
    }

    /* La barra inferior reserva su propio alto -- sin esto el último tramo de cada sección de
       Interactivo queda tapado detrás de ella. min-height, no height fijo: coincide con
       .pyx-bottomnav-item aunque cambie el alto real por safe-area.
       !important: MainLayout.razor le pone Class="pyx-page pb-8" a TODAS las páginas, y
       .pb-8 es una utilidad de MudBlazor con "padding-bottom: 32px !important" -- sin esto acá,
       ese !important le gana a esta regla sin importar la especificidad (verificado en vivo:
       32px fijo, la regla de abajo nunca se aplicaba). */
    .pyx-page:has(.pyx-bottomnav) {
        padding-bottom: calc(56px + env(safe-area-inset-bottom) + 12px) !important;
    }
}

.user-avatar {
    background-color: var(--mud-palette-primary) !important;
    font-size: 11px;
    font-weight: 700;
}

/* Bloque de usuario al pie del sidebar (antes a la derecha de la appbar). */
.drawer-user {
    display: flex;
    align-items: center;
    gap: 9px;
    width: 100%;
    padding: 7px 9px;
    border-radius: 10px;
    cursor: pointer;
    transition: background-color .15s ease;
}

.drawer-user:hover,
.drawer-user:focus-visible {
    background-color: var(--pyx-canvas-soft);
}

.drawer-user-text {
    flex: 1;
    min-width: 0;
    text-align: left;
}

.drawer-user-text b {
    display: block;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--mud-palette-text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.drawer-user-text span {
    display: block;
    margin-top: 1px;
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-2xs);
    color: var(--mud-palette-text-secondary);
}

/* Drawer -- HU-3: fondo azul y texto blanco fijos, independientes del toggle claro/oscuro
   (Marcelo: "que sea azul y que se pinte en blanco... más tipo como pixel"). --pyx-blue (el
   mismo azul de los botones primarios) en vez de --pyx-navy -- el navy leía casi negro y no
   se percibía como "azul"; --pyx-blue da 6.2:1 de contraste con blanco (WCAG AA) y conecta el
   panel con el resto de la identidad de marca. Se fuerza en vez de --mud-palette-drawer-background
   porque ese token sí cambia con el tema. */
.drawer-custom {
    background-color: var(--pyx-blue) !important;
    border-right: 1px solid var(--pyx-border-on-dark) !important;
}

/* AB#23001: el pie del sidebar (bloque de usuario, "Mi Perfil", copyright) vivía dentro del
   mismo scroll que la navegación -- en ventanas bajas o con todos los grupos del menú
   desplegados, el pie quedaba fuera de vista hasta hacer scroll dentro del drawer. Se ancla el
   pie y sólo la navegación hace scroll. Va aquí y no en un *.razor.css porque MudDrawer no
   reenvía el atributo de scoping de Blazor a su raíz (CLAUDE.md, "Blazor CSS isolation..."). */
.drawer-custom .mud-drawer-content {
    height: 100%;
    min-height: 0;
    overflow: hidden;
}

.drawer-custom .mud-drawer-content > .mud-navmenu {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}

.drawer-custom .drawer-header,
.drawer-custom .drawer-footer {
    flex-shrink: 0;
}

/* Pantallas bajas (portátil 1366x768 a 125% de escala): esconder el copyright gana ~20px sin
   sacrificar el nombre de usuario ni "Mi Perfil". */
@media (max-height: 620px) {
    .drawer-custom .drawer-footer .mud-typography-caption {
        display: none;
    }
}

/* La marca se centra sola (margin-inline:auto en .pyx-mark) y se dimensiona contra el ancho de
   este contenedor, así que aquí sólo va el aire vertical y el filete que la separa del menú. */
.drawer-header {
    padding: 18px 0;
    border-bottom: 1px solid var(--pyx-border-on-dark);
}

/* Co-branding: logotipo del tenant bajo el de Pyxoom360, separado por un filete propio. El fondo
   del panel es navy y los logotipos que suben los clientes suelen venir en tinta oscura sobre
   fondo transparente, así que se coloca sobre una placa blanca en vez de dejarlo desaparecer. */
/* Vista previa del logotipo en Personalización. La placa clara y el borde punteado en el estado
   vacío hacen visible que ahí va una imagen, aunque el logotipo del cliente sea de tinta clara. */
/* Barras horizontales de competencias en Inicio.

   La gráfica anterior era un MudChart de barras verticales: los nombres iban en el eje X, girados
   y a un tamaño que el componente no deja ajustar, y en la columna angosta del tablero resultaban
   ilegibles. Con barras horizontales el nombre se lee de corrido y la letra la fijamos aquí. */
.pyx-barras {
    display: flex;
    flex-direction: column;
    gap: 10px;
}

.pyx-barra {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 84px 44px;
    align-items: center;
    gap: 10px;
}

/* 13px, no los ~9px del eje de MudChart. Dos líneas como máximo y elipsis: un nombre largo no debe
   descuadrar la altura de la fila ni empujar la barra fuera de la tarjeta. */
.pyx-barra-nombre {
    font-size: 13px;
    line-height: 1.3;
    color: var(--mud-palette-text-primary);
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.pyx-barra-pista {
    height: 10px;
    border-radius: 5px;
    background: var(--mud-palette-background-grey);
    overflow: hidden;
}

.pyx-barra-relleno {
    display: block;
    height: 100%;
    border-radius: 5px;
    background: var(--mud-palette-primary);
}

.pyx-barra-valor {
    font-size: 13px;
    font-weight: 700;
    text-align: right;
    color: var(--mud-palette-text-primary);
}

/* Con 18 competencias esta columna es bastante más alta que la tabla de eventos y las dos
   tarjetas juntas, y la rejilla deja un hueco blanco debajo de ellas. Se le da scroll propio: la
   altura queda acotada y las competencias de abajo siguen alcanzables sin bajar toda la página.

   El pie de la tarjeta ("Escala x a y" y "Ver en Análisis") queda fuera del área desplazable, así
   que la referencia de la escala se lee siempre. */
.pyx-barras-scroll {
    overflow-y: auto;
    /* Respaldo por si la tarjeta no está dentro de .pyx-seccion-alta: sin una altura tope el
       scroll no aparece nunca. */
    max-height: 340px;
    /* Aire para que la barra de desplazamiento no se encime con el número. */
    padding-right: 6px;
}

/* La tarjeta se estira a lo alto de su columna de la rejilla y el área de barras se queda con lo
   que sobre. Así el tope no es un número fijo que adivine cuántas competencias hay: la altura la
   marca la columna de al lado, y el scroll aparece sólo cuando de verdad no caben. */
.pyx-seccion-alta {
    height: 100%;
    display: flex;
    flex-direction: column;
}

/* Las dos tarjetas de listas (pendientes y planes) rara vez tienen el mismo contenido. Al
   estirarse las dos a lo alto de la fila, el botón de pie de cada una se empuja hasta abajo para
   que cierren parejas en vez de dejar el borde inferior a distinta altura. */
.pyx-seccion-alta > .mud-button-root:last-child {
    margin-top: auto;
}

.pyx-seccion-alta .pyx-barras-scroll {
    flex: 1 1 auto;
    max-height: none;
    /* Con una o dos competencias la tarjeta no debe encogerse hasta verse rota. */
    min-height: 160px;
}

/* Barra de desplazamiento discreta: la tarjeta es plana y una barra del ancho por defecto pesa
   más que el contenido. */
.pyx-barras-scroll::-webkit-scrollbar {
    width: 6px;
}

.pyx-barras-scroll::-webkit-scrollbar-thumb {
    background: var(--mud-palette-lines-default);
    border-radius: 3px;
}

.pyx-barras-scroll::-webkit-scrollbar-track {
    background: transparent;
}

/* Agrupador de las listas de Inicio (pendientes por evento, participantes por plan). */
.pyx-grupo {
    margin-bottom: 14px;
}

.pyx-grupo-encabezado {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 8px;
    margin-bottom: 8px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--mud-palette-text-secondary);
}

/* Nombre de evento/plan sin tope (Index.razor) -- min-width:0 para que pueda encoger/envolver
   en vez de empujar el MudChip vecino fuera de la fila. */
.pyx-grupo-encabezado > span {
    min-width: 0;
}

/* Captcha de arrastrar y soltar de la pantalla de acceso. */
.pyx-captcha {
    border: 1px solid var(--pyx-divider);
    border-radius: 8px;
    padding: 12px;
    margin-bottom: 16px;
    background: var(--mud-palette-background-grey);
}

.pyx-captcha-instruccion {
    margin: 0 0 8px;
    font-size: 12px;
    color: var(--mud-palette-text-secondary);
}

/* Una sola línea, centrada y con los íconos repartiéndose todo el ancho disponible: cada uno es
   flex:1 con aspect-ratio 1, así que crecen hasta donde el contenedor deje y siguen cuadrados
   sea cual sea la cantidad que mande el reto. nowrap es lo que impide que el sexto ícono caiga a
   una segunda fila, que es como se veía antes. */
.pyx-captcha-iconos {
    display: flex;
    flex-wrap: nowrap;
    justify-content: center;
    align-items: center;
    gap: 4px;
    margin-bottom: 10px;
}

/* cursor:grab y user-select:none: sin lo segundo, arrastrar selecciona el texto de al lado y el
   gesto se siente roto. */
/* Punto 85d: los íconos eran grises sobre fondo claro y se leían como decoración apagada. En
   primario y más grandes se ven como lo que son, la pieza con la que hay que interactuar.
   touch-action:none es requisito del arrastre por puntero: sin él el navegador móvil se queda el
   gesto para desplazar la página y el arrastre nunca empieza. */
.pyx-captcha-icono {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    /* flex:1 1 0 + min-width:0 reparte el ancho a partes iguales; aspect-ratio los mantiene
       cuadrados sin fijar una altura, de modo que el tamaño lo decide el contenedor. */
    flex: 1 1 0;
    min-width: 0;
    aspect-ratio: 1;
    border: 1px solid var(--mud-palette-primary);
    border-radius: 6px;
    background: var(--mud-palette-surface);
    cursor: grab;
    user-select: none;
    touch-action: none;
    color: var(--mud-palette-primary);
    transition: transform .12s ease, box-shadow .12s ease;
}

    /* El glifo escala con la caja: MudIcon emite un SVG con medidas fijas según su Size, y sin
       esto los íconos se quedarían pequeños dentro de recuadros grandes. */
    .pyx-captcha-icono svg {
        width: 62%;
        height: 62%;
    }

    .pyx-captcha-icono:active {
        cursor: grabbing;
    }

    .pyx-captcha-icono:hover {
        transform: translateY(-1px);
        box-shadow: 0 2px 6px rgba(0, 0, 0, .12);
    }

    /* El ícono se puede activar con teclado, así que necesita foco visible al tabular. */
    .pyx-captcha-icono:focus-visible {
        outline: 2px solid var(--mud-palette-primary);
        outline-offset: 2px;
    }

    /* Mientras se arrastra, el original se atenúa: la copia que sigue al puntero es la que manda. */
    .pyx-captcha-icono--arrastrando {
        opacity: .35;
    }

/* La copia que sigue al dedo o al ratón. Va en el body para que ningún overflow la recorte, y
   sin eventos para no taparse a sí misma al calcular qué hay debajo. */
.pyx-captcha-fantasma {
    position: fixed;
    z-index: 2000;
    pointer-events: none;
    transform: translate(-50%, -50%);
    opacity: .9;
    box-shadow: 0 4px 12px rgba(0, 0, 0, .2);
}

.pyx-captcha-objetivo {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    height: 56px;
    border: 2px dashed var(--mud-palette-primary);
    border-radius: 6px;
    font-size: 12px;
    color: var(--mud-palette-primary);
    background: color-mix(in srgb, var(--mud-palette-primary) 6%, transparent);
    transition: background .12s ease, border-color .12s ease;
}

/* Punto 85e: al pasar el ícono por encima, la zona se enciende. Sin esta señal no hay forma de
   saber que se puede soltar ahí, que es justo donde se atascaba el gesto. */
.pyx-captcha-objetivo--encima {
    background: color-mix(in srgb, var(--mud-palette-primary) 18%, transparent);
    border-style: solid;
}

.pyx-captcha-objetivo--error {
    border-color: var(--mud-palette-error);
    color: var(--mud-palette-error);
    background: color-mix(in srgb, var(--mud-palette-error) 8%, transparent);
}

.pyx-captcha-ok {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 12.5px;
    color: var(--mud-palette-success);
}

.pyx-logo-vista-previa {
    width: 180px;
    height: 72px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: #fff;
    border: 1px solid var(--pyx-divider);
    border-radius: 8px;
    padding: 8px;
}

    .pyx-logo-vista-previa img {
        max-width: 100%;
        max-height: 100%;
        object-fit: contain;
    }

.pyx-logo-vista-previa--vacia {
    border-style: dashed;
    background: var(--mud-palette-background-grey);
    color: var(--mud-palette-text-disabled);
}

.drawer-tenant-logo {
    margin: 14px 16px 0;
    padding-top: 14px;
    border-top: 1px solid var(--pyx-border-on-dark);
    display: flex;
    justify-content: center;
}

    .drawer-tenant-logo img {
        max-height: 34px;
        max-width: 150px;
        object-fit: contain;
        background: #fff;
        border-radius: 6px;
        padding: 5px 9px;
    }

.drawer-logo-icon {
    color: #fff;
}

.drawer-logo-text {
    color: #fff;
}

.drawer-footer {
    border-top: 1px solid var(--pyx-border-on-dark);
    display: flex;
    flex-direction: column;
    gap: 2px;
    padding-top: 8px;
}

.drawer-footer .mud-button-root,
.drawer-footer .mud-typography-caption {
    color: var(--pyx-ink-on-dark) !important;
}

.drawer-footer .mud-icon-root {
    color: var(--pyx-ink-on-dark) !important;
}

.drawer-user:hover,
.drawer-user:focus-visible {
    background-color: var(--pyx-surface-on-dark-hover) !important;
}

.drawer-user-text b {
    color: #fff !important;
}

.drawer-user-text span {
    color: var(--pyx-ink-on-dark-muted) !important;
}

/* MudMenu no estira su activador, así que el botón de Configuración se centraba mientras
   Notificaciones —un MudButton suelto— alineaba a la izquierda. Se igualan los tres bloques
   del pie para que compartan margen con los ítems de navegación de arriba. */
.drawer-footer .mud-menu,
.drawer-footer .mud-menu > .mud-menu-activator {
    display: block;
    width: 100%;
}

.drawer-footer .mud-button-root {
    justify-content: flex-start;
    padding-left: 9px;
}

.drawer-footer .mud-typography-caption {
    margin-top: 6px;
}

/* Botonera inferior de Interactivo/Index.razor (.pyx-eval-acciones) y CalificacionMultiple.razor
   (.pyx-acciones-footer): en desktop van a los extremos de la sección (justify-space-between en el
   propio contenedor ya lo resuelve, siempre que el contenedor mida el 100% del ancho disponible).
   Aquí solo se resuelve el tamaño en móvil -- más chico, pero sin dejar de estar en los extremos.
   Va en app.css y no en un *.razor.css porque MudButton no reenvía el atributo de scoping de
   Blazor a su .mud-button-root, así que una regla con ámbito nunca llegaría a aplicarse. */
@media (max-width: 600px) {
    .pyx-eval-acciones .mud-button-root,
    .pyx-acciones-footer .mud-button-root {
        padding: 4px 10px;
        font-size: 12px;
        min-width: 0;
    }
    .pyx-eval-acciones .mud-button-root .mud-icon-root,
    .pyx-acciones-footer .mud-button-root .mud-icon-root {
        font-size: 16px;
    }
}

/* .pyx-eval-acciones es el pie de RenderEvaluando() -- se renderiza FUERA de MudTabs (rama
   _step != Listado de Interactivo/Index.razor), así que "position: sticky" siempre fue seguro
   ahí; simplemente no estaba hecho. Sin esto, alguien contestando un cuestionario de ~100
   preguntas en el teléfono tiene que desplazarse toda la competencia visible para llegar a
   "Siguiente". No hay barra inferior que esquivar aquí (BottomNav sólo se pinta en _step ==
   Listado), así que basta con bottom:0 + la propia safe-area. */
@media (max-width: 959px) {
    .pyx-eval-acciones {
        position: sticky;
        bottom: 0;
        z-index: 10;
        background-color: var(--mud-palette-surface);
        padding-bottom: calc(16px + env(safe-area-inset-bottom));
        box-shadow: 0 -2px 10px rgba(15, 27, 52, .08);
    }
}

/* .pyx-acciones-footer es el pie de CalificacionMultiple.razor: Cancelar a la izquierda, el
   grupo de avance/guardar/finalizar a la derecha. Row en desktop; bajo 600px eran 5 controles
   forzados en una sola fila sin wrap -- se apilan en su lugar (mismo criterio que .pyx-eval-
   acciones arriba: sin barra inferior que esquivar, esta pantalla no vive en el flujo con
   pestañas de Interactivo). */
.pyx-acciones-footer {
    flex-direction: row;
}

/* AB#23178: en el celular la botonera quedaba centrada y desordenada. Rejilla 2×2:
   arriba "Guardar y salir" | contador (+ acuse de guardado); abajo "Guardar avance" | "Finalizar"
   a medio ancho cada uno. display: contents deja que los hijos del grupo entren a la rejilla. */
@media (max-width: 599px) {
    .pyx-acciones-footer.d-flex {
        display: grid !important;
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
        grid-template-areas:
            "salir cont"
            "guardar fin";
        align-items: center;
        gap: 8px 10px;
    }

    .pyx-acciones-footer-grupo.d-flex {
        display: contents !important;
    }

    .pyx-af-salir { grid-area: salir; justify-self: start; }
    .pyx-af-cont { grid-area: cont; justify-self: end; text-align: right; }
    .pyx-af-ok { display: none; }
    .pyx-af-guardar { grid-area: guardar; width: 100%; }
    .pyx-af-fin { grid-area: fin; width: 100%; }
}

/* "Calificar Juntas" en Interactivo/Index.razor (pyx-eval-cta-sticky): ahora puede ser sticky
   porque ya no vive dentro de un MudTabPanel (ver comentario junto al bloque en el .razor). En
   móvil se despega del fondo lo que mide .pyx-bottomnav -- si se pega a bottom:0 a secas queda
   tapada detrás de la barra inferior, que es position:fixed y no reserva su propio espacio en el
   flujo del documento. */
.pyx-eval-cta-sticky {
    position: sticky;
    bottom: 0;
    z-index: 5;
}

@media (max-width: 959px) {
    .pyx-eval-cta-sticky {
        bottom: calc(56px + env(safe-area-inset-bottom) + 12px);
    }
}

/* Main Content */
.main-content {
    background-color: var(--mud-palette-background);
    min-height: 100vh;
}

/* MudMainContent reserva --mud-appbar-height de padding superior para no quedar debajo de la
   barra. Al retirarla, ese hueco se quedó: 52px de aire muerto encima de cada pantalla,
   sumados al gutter propio del contenedor. */
.mud-layout .main-content {
    padding-top: 0;
}

/* Empty layout background — el degradado arranca del navy de marca hacia el lienzo frío,
   para que la portada se lea como Pyxoom y no como un gris genérico. */
.empty-layout {
    background: radial-gradient(1200px 600px at 50% -10%, var(--pyx-blue-soft) 0%, transparent 60%),
                linear-gradient(160deg, var(--pyx-canvas) 0%, var(--pyx-canvas-soft) 100%);
    min-height: 100vh;
}

/* Tabla en clave de dato: cabeceras monoespaciadas en versalitas y cifras alineadas.
   Es opt-in por clase — no todas las tablas del app son tabulares de números. */
.pyx-table .mud-table-head .mud-table-cell {
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-3xs);
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--pyx-ink-faint) !important;
    border-bottom: 1px solid var(--pyx-border);
}

/* HU-6: fila del evento Activo en "Eventos Recientes" (Pages/Index.razor) -- filete de color
   para que salte a la vista sin depender solo del StatusBadge de la columna Estado. */
.pyx-row-activo > td:first-child {
    box-shadow: inset 3px 0 0 var(--pyx-positive);
}

/* HU-7: toda la fila navega al detalle del evento -- el cursor confirma que es clickeable
   (Hover="true" de MudTable ya da el fondo, esto completa la afordancia). */
.pyx-row-click {
    cursor: pointer;
}

.pyx-num,
.pyx-table .pyx-num {
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-sm);
    font-variant-numeric: tabular-nums;
}

/* =========================================================
   Navigation
   ========================================================= */

/* HU-3: texto/ícono siempre blanco sobre el panel azul -- ya no se deriva de
   --mud-palette-drawer-text (theme-dependent), color fijo de marca.

   El estado activo YA NO puede ser "fondo --pyx-blue" (era el mismo color que el panel entero,
   invisible contra sí mismo). Se invierte a un "pill" blanco sólido con texto --pyx-blue: mayor
   contraste posible entre dos tonos de la paleta (6.2:1, igual que blanco-sobre-azul) y además
   cambia de TIPO (relleno sólido, no solo de tono), así que la fila activa no depende de que el
   usuario distinga dos azules parecidos para saber dónde está. */
.nav-item {
    color: var(--pyx-ink-on-dark) !important;
    transition: all 0.2s ease-in-out;
}

.nav-item .mud-icon-root {
    color: var(--pyx-ink-on-dark) !important;
}

.nav-item:hover {
    background-color: var(--pyx-surface-on-dark-hover) !important;
}

.nav-item.active {
    background-color: #fff !important;
    color: var(--pyx-blue) !important;
    font-weight: 600;
}

.nav-item.active .mud-icon-root {
    color: var(--pyx-blue) !important;
}

.nav-item.active .mud-typography {
    color: var(--pyx-blue) !important;
}

/* MudNavLink global fixes */
.mud-nav-link {
    color: var(--pyx-ink-on-dark) !important;
}

.mud-nav-link .mud-icon-root {
    color: var(--pyx-ink-on-dark) !important;
}

.mud-nav-link.active {
    background-color: #fff !important;
    color: var(--pyx-blue) !important;
}

.mud-nav-link.active .mud-icon-root {
    color: var(--pyx-blue) !important;
}

.mud-nav-link.active .mud-typography {
    color: var(--pyx-blue) !important;
}

.mud-nav-link:not(.active):hover {
    background-color: var(--pyx-surface-on-dark-hover) !important;
}

/* Bug 22931: entradas del menú marcadas "Próximamente" (Comparativo histórico, Panorama), como
   en el mock del PO: el nombre atenuado y la etiqueta a su derecha, al mismo nivel. Ojo: la
   Class de MudNavLink cae en el <div class="mud-nav-item">, no en el <a class="mud-nav-link">,
   así que los selectores parten de .mud-nav-item. El panel mide ~252px, por eso el nombre puede
   partirse en dos líneas (la etiqueta se queda a un lado, centrada) y la etiqueta es más chica
   que un MudChip Small normal. */
.mud-nav-item.nav-item--proximamente .mud-nav-link {
    color: var(--pyx-ink-on-dark) !important;
    cursor: default;
    padding-right: 4px !important;
}

/* Sólo el nombre y el icono se atenúan; la etiqueta se queda nítida, como en el mock. */
.mud-nav-item.nav-item--proximamente .mud-nav-link .mud-icon-root,
.mud-nav-item.nav-item--proximamente .pyx-nav-proximamente__texto {
    color: var(--pyx-ink-on-dark) !important;
    opacity: 0.55;
}

.mud-nav-item.nav-item--proximamente .mud-nav-link:hover {
    background-color: transparent !important;
}

.mud-nav-item.nav-item--proximamente .mud-nav-link-text {
    white-space: normal;
    line-height: 1.2;
}

.mud-nav-item.nav-item--proximamente .pyx-nav-proximamente {
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    gap: 4px;
}

.mud-nav-item.nav-item--proximamente .pyx-nav-proximamente__texto {
    min-width: 0;
}

.mud-nav-item.nav-item--proximamente .mud-chip.pyx-chip-proximamente {
    flex: 0 0 auto;
    height: 16px !important;
    line-height: 16px !important;
    font-size: 10px !important;
    letter-spacing: 0 !important;
    padding: 0 5px !important;
    margin: 0 !important;
    color: var(--pyx-ink-on-dark) !important;
    border: none !important;
    background-color: rgba(255, 255, 255, 0.18) !important;
    opacity: 1;
}

.mud-nav-item.nav-item--proximamente .mud-chip.pyx-chip-proximamente .mud-chip-content {
    padding: 0 !important;
}

/* =========================================================
   MudMenuItem fixes - ensure text is always visible
   ========================================================= */
.mud-menu-item {
    color: var(--mud-palette-text-primary) !important;
}

.mud-menu-item:hover {
    background-color: var(--mud-palette-action-default-hover) !important;
}

/* =========================================================
   Form Elements - Ensure label visibility
   ========================================================= */
.mud-input-label {
    color: var(--mud-palette-text-secondary) !important;
}

.mud-input-label.mud-focused {
    color: var(--mud-palette-primary) !important;
}

.mud-input-control-input:focus-within .mud-input-label {
    color: var(--mud-palette-primary) !important;
}

/* Feedback 2026-09-16: en vez del placeholder grande de Material Design para campos vacíos (label
   grande y centrado hasta que el campo tiene valor, entonces encoge y flota arriba), el nombre del
   campo debe verse SIEMPRE chico y flotando arriba, tenga o no valor -- así ningún campo se ve
   distinto a otro sólo por estar contestado o no. Se fuerza el label Y la muesca que el borde
   outlined le recorta alrededor (<legend>, normalmente sólo "auto" cuando MudBlazor agrega
   ".mud-shrink"); de paso esto también deja sin efecto el bug de invalidación de Chrome con el
   combinador "~" para MudSelect (encontrado en vivo 2026-09-15, "el texto dentro del input se
   mueve") -- ya no depende de que ".mud-shrink" se detecte para aplicarse, siempre está activo. */
.mud-input-label-outlined {
    transform: translate(14px, -6px) scale(0.75) !important;
}

/* AB#23018: el max-width nativo de MudBlazor 8.0.0 (calc(100% - 14px)) se aplica ANTES del
   scale(0.75) de arriba, así que la elipsis se calcula sobre el ancho a tamaño completo y
   recorta el "*" de obligatorio en cualquier label largo dentro de una columna angosta
   (MudItem md="6" en un MudDialog MaxWidth.Small). Se compensa el ancho útil con la misma
   escala para que el label completo (incluido el asterisco) quepa antes de truncarse. */
.mud-input-label-outlined {
    max-width: calc((100% - 14px) / 0.75) !important;
}

.mud-input-outlined-border legend {
    width: auto !important;
    padding: 0 5px !important;
}

.mud-input > input,
.mud-input > textarea,
.mud-select-input {
    color: var(--mud-palette-text-primary) !important;
}

.mud-input-control > .mud-input-control-input-container > .mud-input.mud-input-underline::before {
    border-bottom-color: var(--mud-palette-lines-inputs);
}

.mud-input-control > .mud-input-control-input-container > .mud-input.mud-input-underline::after {
    border-bottom-color: var(--mud-palette-primary);
}

/* MudSelect items */
.mud-select-item {
    color: var(--mud-palette-text-primary) !important;
}

.mud-select-item:hover {
    background-color: var(--mud-palette-action-default-hover) !important;
}

.mud-select-item.mud-selected {
    background-color: var(--mud-palette-primary-hover) !important;
    color: var(--mud-palette-primary) !important;
}

/* =========================================================
   Table Styles
   ========================================================= */
.mud-table-head {
    background-color: var(--mud-palette-surface);
}

.mud-table-head .mud-table-cell {
    color: var(--mud-palette-text-primary) !important;
    font-weight: 600;
    border-bottom: 2px solid var(--mud-palette-lines-default);
}

.mud-table-body .mud-table-row:hover {
    background-color: var(--mud-palette-table-hover);
}

.mud-table-body .mud-table-cell {
    color: var(--mud-palette-text-primary) !important;
    border-bottom: 1px solid var(--mud-palette-table-lines);
}

.mud-table-pagination-actions button {
    color: var(--mud-palette-text-primary) !important;
}

/* Feedback 2026-09-16 ("en mobile hay cosas que no se ven bien"): por debajo de Breakpoint.Sm
   (600px, el que usan las 23 pantallas con MudTable) cada fila pasa a "modo tarjeta" -- cada
   <td> se convierte en una fila propia (etiqueta a la izquierda, valor a la derecha). Varias
   pantallas ponen Style="min-width: Npx" en la columna "Avance" (la barra de progreso) pensando
   sólo en el layout de escritorio -- pero ese min-width no se neutraliza en modo tarjeta, y como
   nada limita el <table> al 100% de su contenedor, la fila entera crece hasta ese min-width
   (verificado en vivo: 402px de fila dentro de un viewport de 390px) y el resto del contenido
   -- el valor de "Estatus", la fecha de "Fecha de Creación" -- queda recortado e INVISIBLE, no
   sólo apretado. Bug real de pérdida de datos, no sólo estético. Selector amplio (cualquier
   celda con data-label, que es como MudBlazor marca las celdas en modo tarjeta) para cubrir las
   23 pantallas de una vez en vez de tocar cada Style="min-width" uno por uno. */
@media (max-width: 599px) {
    .mud-table-cell[data-label] {
        min-width: 0 !important;
        width: 100% !important;
        /* La celda en modo tarjeta es display:flex (MudBlazor la usa para poner la etiqueta
           a la izquierda y el valor a la derecha) con flex-wrap:nowrap de fábrica. Una columna
           con varios MudChip (ej. "Roles pendientes": Autoevaluación + Jefe Inmediato + Pares)
           es el mismo bug de arriba con otro disparador: los chips no encogen ni envuelven, así
           que siguen forzando la fila más allá del viewport aun con width:100% puesto arriba. */
        flex-wrap: wrap !important;
    }

    /* El width:100% de la celda sólo resuelve algo si el <table> mismo tiene un ancho real del
       que ser el 100% -- sin esto un <table> nunca lleva width propio (crece al tamaño de su
       contenido, "shrink-to-fit"), así que igual terminaba más ancho que .mud-table-container
       (verificado en vivo: tabla de 500px dentro de un contenedor de 267px) aunque cada <td>
       ya fuera 100% de ESE ancho equivocado. */
    .mud-table-root {
        width: 100% !important;
    }
}

/* =========================================================
   Cards & Papers
   ========================================================= */
.mud-paper {
    background-color: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
}

.mud-card {
    background-color: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

/* La plataforma es plana: el hover se acusa con el borde, no levantando la tarjeta.
   .pyx-card queda fuera — es superficie de lectura, no un control. */
.mud-card:not(.pyx-card):hover {
    border-color: var(--pyx-border-strong);
    box-shadow: 0 2px 8px -2px rgba(15, 27, 52, .10);
}

/* =========================================================
   Buttons - Contrast fixes for dark/light mode
   ========================================================= */
/* AB#23304: el texto sobre el botón primario sale del tema (blanco en claro, azul marino en oscuro: blanco sobre el
   azul claro del modo oscuro daba 3.3:1, bajo el 4.5:1 de AA). El hover aclara en vez de oscurecer para no perder contraste. */
.mud-button-filled.mud-button-filled-primary {
    background-color: var(--mud-palette-primary) !important;
    color: var(--mud-palette-primary-text) !important;
}

.mud-button-filled.mud-button-filled-primary:hover {
    background-color: color-mix(in srgb, var(--mud-palette-primary) 88%, white) !important;
    color: var(--mud-palette-primary-text) !important;
}

.mud-button-filled.mud-button-filled-primary .mud-button-label,
.mud-button-filled.mud-button-filled-primary span,
.mud-button-filled.mud-button-filled-primary .mud-icon-root {
    color: var(--mud-palette-primary-text) !important;
}

.mud-button-filled.mud-button-filled-secondary {
    background-color: var(--mud-palette-secondary) !important;
    color: white !important;
}

.mud-button-filled.mud-button-filled-secondary:hover {
    color: white !important;
}

.mud-button-filled.mud-button-filled-secondary .mud-button-label,
.mud-button-filled.mud-button-filled-secondary span,
.mud-button-filled.mud-button-filled-secondary .mud-icon-root {
    color: white !important;
}

/* Botón "Recordatorios de esta rama" en el desglose por organigrama (Progreso.razor):
   MudButton no forward-ea el atributo de scope de CSS aislado a su root, así que este
   override vive aquí en vez de en Progreso.razor.css. En mobile la fila del panel se apila
   en columna y el botón pasa a ancho completo -- sin esto la etiqueta larga fuerza un ancho
   mínimo que sigue desbordando el contenedor. */
.pyx-btn-recordatorios .mud-button-label {
    white-space: normal;
    text-align: left;
}

@media (max-width: 599px) {
    .pyx-btn-recordatorios {
        width: 100%;
        justify-content: flex-start;
    }
}

.mud-button-outlined.mud-button-outlined-primary {
    border-color: var(--mud-palette-primary) !important;
    color: var(--mud-palette-primary) !important;
}

.mud-button-outlined.mud-button-outlined-secondary {
    border-color: var(--mud-palette-lines-default) !important;
    color: var(--mud-palette-text-secondary) !important;
}

/* =========================================================
   Dialogs
   ========================================================= */

/* MudBlazor titula los diálogos con Typo.h6, que la escala de Identidad fija en 13.5px —el
   mismo cuerpo que el texto normal—. Un modal necesita un ancla tipográfica: se le da el
   tamaño de H4, que es el escalón de "título de bloque" en esta escala. */
.mud-dialog-title {
    color: var(--mud-palette-text-primary) !important;
    padding: var(--pyx-pad-card);
    border-bottom: 1px solid var(--pyx-border);
}

.mud-dialog-title .mud-typography {
    font-size: 17px;
    font-weight: 700;
    line-height: 1.3;
    letter-spacing: -.015em;
}

/* Ritmo interno del diálogo, alineado con el de las tarjetas. */
.mud-dialog-content {
    padding: var(--pyx-pad-card);
}

.mud-dialog-actions {
    padding: 14px 22px;
    border-top: 1px solid var(--pyx-border);
    gap: 8px;
}

.mud-dialog-content {
    color: var(--mud-palette-text-primary) !important;
}

/* =========================================================
   Alerts & Snackbar
   ========================================================= */
.mud-alert {
    background-color: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
}

/* =========================================================
   Loading Spinner
   ========================================================= */
.loading-progress {
    position: relative;
    display: block;
    width: 80px;
    height: 80px;
    margin: 20vh auto 20px auto;
}

.loading-progress circle {
    fill: none;
    stroke: var(--mud-palette-lines-default);
    stroke-width: 6;
    stroke-linecap: round;
    transform-origin: 50% 50%;
    animation: spin 1s infinite linear;
}

.loading-progress circle:last-child {
    stroke: var(--mud-palette-primary);
    stroke-dasharray: 40, 200;
    stroke-dashoffset: 0;
}

@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}

/* =========================================================
   Chips & Badges
   ========================================================= */
.mud-chip {
    font-weight: 500;
}

/* =========================================================
   Slider
   ========================================================= */
.mud-slider .mud-slider-value-label {
    color: var(--mud-palette-text-primary) !important;
    background-color: var(--mud-palette-surface) !important;
}

/* =========================================================
   Typography Helpers
   ========================================================= */
.text-primary-force {
    color: var(--mud-palette-text-primary) !important;
}

.text-secondary-force {
    color: var(--mud-palette-text-secondary) !important;
}

.text-disabled-force {
    color: var(--mud-palette-text-disabled) !important;
}

/* =========================================================
   Status Colors (semantic helpers)
   ========================================================= */
.status-success {
    color: var(--mud-palette-success) !important;
}

.status-warning {
    color: var(--mud-palette-warning) !important;
}

.status-error {
    color: var(--mud-palette-error) !important;
}

.status-info {
    color: var(--mud-palette-info) !important;
}

/* =========================================================
   Animations
   ========================================================= */
@keyframes fadeIn {
    from { opacity: 0; transform: translateY(8px); }
    to { opacity: 1; transform: translateY(0); }
}

.fade-in {
    animation: fadeIn 0.3s ease-out forwards;
}

.stagger-1 { animation-delay: 0.05s; }
.stagger-2 { animation-delay: 0.10s; }
.stagger-3 { animation-delay: 0.15s; }
.stagger-4 { animation-delay: 0.20s; }

/* =========================================================
   Componentes de identidad (antes en *.razor.css)
   Blazor aplica el atributo de scope a los elementos que escribe el propio
   componente, pero MudPaper/MudTable no lo propagan a su elemento raíz: toda regla
   que apunte a la raíz de un componente MudBlazor desde una hoja aislada NO se
   aplica nunca. Padding, flex y altura del Panorama y del encabezado de página
   llevaban así desde que se escribieron. Viven aquí, en global, donde sí empatan.
   ========================================================= */

/* ===== PageHeader ===== */
.pyx-page-head {
    padding: var(--pyx-pad-card);
    margin-bottom: var(--pyx-gap-block);
}

.pyx-page-head-row {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 20px;
    flex-wrap: wrap;
}

.pyx-page-head-text {
    min-width: 0;
}

.pyx-page-title {
    color: var(--mud-palette-text-primary);
}

.pyx-page-lede {
    color: var(--mud-palette-text-secondary);
    margin-top: 6px;
    max-width: 620px;
}

.pyx-page-head-actions {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
}

/* Indicador de autoguardado: punto verde + texto. No es un botón, es un acuse. */
.pyx-saved {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 5px 11px;
    border-radius: 999px;
    background: color-mix(in srgb, var(--pyx-positive) 12%, transparent);
    font-size: 11.5px;
    font-weight: 600;
    color: var(--pyx-positive);
    white-space: nowrap;
}

    .pyx-saved i {
        width: 7px;
        height: 7px;
        border-radius: 50%;
        background: var(--pyx-positive);
        animation: pyx-saved-pulse 2.4s ease-in-out infinite;
    }

@keyframes pyx-saved-pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: .35; }
}

@media (prefers-reduced-motion: reduce) {
    .pyx-saved i {
        animation: none;
    }
}

/* ===== AdequacyMap ===== */
.pyx-map {
    padding: var(--pyx-pad-card);
    display: flex;
    flex-direction: column;
    gap: 16px;
    height: 100%;
}

.pyx-map-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 18px;
    flex-wrap: wrap;
}

.pyx-muted {
    color: var(--pyx-ink-muted);
    max-width: 500px;
    margin-top: 4px;
}

.pyx-legend {
    display: flex;
    gap: 13px;
    flex-wrap: wrap;
}

.pyx-legend-item {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 11px;
    font-weight: 500;
    color: var(--pyx-ink-muted);
}

    .pyx-legend-item i {
        width: 9px;
        height: 9px;
        border-radius: 50%;
    }

.pyx-map-body {
    display: flex;
    gap: 12px;
    flex: 1;
    min-height: 0;
}

.pyx-axis-y {
    width: 16px;
    flex: none;
    position: relative;
}

    .pyx-axis-y span {
        position: absolute;
        left: 8px;
        top: 50%;
        transform: translate(-50%, -50%) rotate(-90deg);
        white-space: nowrap;
        font-family: var(--pyx-label);
        font-size: var(--pyx-font-2xs);
        font-weight: 600;
        letter-spacing: .1em;
        text-transform: uppercase;
        color: var(--pyx-ink-faint);
    }

.pyx-plot-wrap {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 9px;
}

.pyx-plot {
    position: relative;
    flex: 1;
    min-height: 360px;
    border: 1px solid var(--pyx-c-empty);
    border-radius: 10px;
    background: var(--mud-palette-background-gray);
    overflow: hidden;
}

.pyx-grid {
    position: absolute;
    inset: 0;
    background-image: linear-gradient(var(--pyx-divider) 1px, transparent 1px),
                      linear-gradient(90deg, var(--pyx-divider) 1px, transparent 1px);
    background-size: 25% 25%;
}

.pyx-threshold-v {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--pyx-border-strong);
}

.pyx-threshold-h {
    position: absolute;
    left: 0;
    right: 0;
    height: 1px;
    background: var(--pyx-border-strong);
}

.pyx-quad {
    position: absolute;
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-2xs);
    font-weight: 600;
    letter-spacing: .09em;
    text-transform: uppercase;
    background: color-mix(in srgb, var(--mud-palette-background-gray) 92%, transparent);
    padding: 3px 6px;
    border-radius: 5px;
}

.pyx-quad--tl {
    left: 12px;
    top: 11px;
    color: var(--pyx-ink-faint);
}

.pyx-quad--tr {
    right: 12px;
    top: 11px;
    color: oklch(0.5 0.09 162);
}

.pyx-quad--bl {
    left: 12px;
    bottom: 11px;
    color: oklch(0.55 0.14 28);
}

.pyx-quad--br {
    right: 12px;
    bottom: 11px;
    color: var(--pyx-blue);
}

.pyx-dot {
    position: absolute;
    transform: translate(-50%, 50%);
    width: 29px;
    height: 29px;
    border-radius: 50%;
    border: 1.5px solid;
    display: grid;
    place-items: center;
    font-size: 10.5px;
    font-weight: 700;
    text-decoration: none;
    cursor: pointer;
    transition: box-shadow .15s;
}

    .pyx-dot:hover {
        z-index: 9;
        box-shadow: 0 4px 14px rgba(15, 27, 52, .28);
    }

.pyx-axis-x {
    display: flex;
    justify-content: space-between;
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-2xs);
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--pyx-ink-faint);
}

.pyx-axis-x-title {
    font-size: 10.5px;
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
}

.pyx-map-warning {
    color: var(--pyx-ink-muted);
    font-size: 11.5px;
}

.pyx-map-divider {
    margin: 0;
}

.pyx-quad-counts {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 10px;
}

    .pyx-quad-counts b {
        display: block;
        font-size: 22px;
        line-height: 1;
        letter-spacing: -.02em;
        font-weight: 700;
    }

    .pyx-quad-counts span {
        display: block;
        margin-top: 5px;
        font-size: 11px;
        line-height: 1.35;
        font-weight: 500;
        color: var(--pyx-ink-muted);
    }

@media (max-width: 599px) {
    .pyx-quad-counts {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

/* ===== IdentityIndex ===== */
/* flex:none — dentro de .pyx-stack esta tarjeta se comprimía para igualar la altura del mapa,
   y al encogerse las filas el eje (margin-top negativo) se montaba sobre la última. */
.pyx-ident {
    padding: var(--pyx-pad-card);
    display: flex;
    flex-direction: column;
    gap: 15px;
    flex: none;
}

.pyx-ident-sub {
    color: var(--pyx-ink-muted);
    margin-top: 4px;
}

.pyx-ident-rows {
    display: flex;
    flex-direction: column;
    gap: 11px;
}

.pyx-ident-row {
    display: grid;
    grid-template-columns: minmax(84px, max-content) minmax(0, 1fr) 46px;
    gap: 10px;
    align-items: center;
}

/* Los nombres de nivel del catálogo son largos ("Mandos Medios"): con 84px se truncaban
   a "Mandos Medi…". La columna crece hasta donde haga falta sin comerse la barra. */
.pyx-ident-dot {
    display: inline-block;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    margin-right: 6px;
    vertical-align: baseline;
}

.pyx-ident-name {
    font-size: 11.5px;
    font-weight: 600;
    color: var(--pyx-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.pyx-ident-track {
    position: relative;
    height: 20px;
    background: var(--pyx-canvas-soft);
    border-radius: 5px;
    overflow: hidden;
}

.pyx-ident-zero {
    position: absolute;
    left: 50%;
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--pyx-border-strong);
}

.pyx-ident-bar {
    position: absolute;
    top: 3px;
    bottom: 3px;
    border-radius: 3px;
}

.pyx-ident-val {
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-sm);
    font-weight: 700;
    text-align: right;
    font-variant-numeric: tabular-nums;
}

.pyx-ident-axis {
    display: flex;
    justify-content: space-between;
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-2xs);
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--pyx-ink-faint);
}

.pyx-ident-note {
    background: var(--pyx-canvas-soft);
    border-color: var(--pyx-c-empty);
    font-size: 11.5px;
    line-height: 1.5;
    color: var(--pyx-ink-muted);
}

/* ===== ProfileHeatmap ===== */
.pyx-heat {
    padding: var(--pyx-pad-card);
    display: flex;
    flex-direction: column;
    gap: 16px;
    height: 100%;
}

.pyx-heat-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
}

.pyx-heat-sub {
    color: var(--pyx-ink-muted);
    max-width: 520px;
    margin-top: 4px;
}

.pyx-scale {
    display: flex;
    align-items: center;
    gap: 9px;
}

    .pyx-scale span {
        font-family: var(--pyx-label);
        font-size: var(--pyx-font-3xs);
        font-weight: 600;
        letter-spacing: .06em;
        color: var(--pyx-ink-faint);
    }

    .pyx-scale i {
        width: 100px;
        height: 9px;
        border-radius: 5px;
        background: linear-gradient(90deg, oklch(0.62 0.16 28), oklch(0.93 0.03 60), oklch(0.6 0.11 162));
    }

.pyx-heat-grid {
    flex: 1;
}

.pyx-heat-grid-scroll {
    display: flex;
    flex-direction: column;
    gap: 4px;
    overflow-x: auto;
}

/* La columna de etiquetas es flexible y las celdas tienen piso propio: si se fija en px,
   las celdas numéricas se quedan sin espacio y se recortan. El número de columnas viene del
   catálogo de niveles del tenant (--pyx-heat-cols), no está fijo en cinco como el prototipo. */
.pyx-heat-row {
    display: grid;
    grid-template-columns: minmax(120px, 1.7fr) repeat(var(--pyx-heat-cols, 5), minmax(44px, 1fr));
    gap: 4px;
    align-items: stretch;
    flex: 1;
    min-height: 32px;
}

.pyx-heat-row--head {
    flex: none;
}

.pyx-col {
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-2xs);
    font-weight: 600;
    letter-spacing: .05em;
    color: var(--pyx-ink-faint);
    text-align: center;
    padding-bottom: 3px;
}

.pyx-col--off {
    color: var(--pyx-border-strong);
}

.pyx-heat-label {
    font-size: 12px;
    color: var(--pyx-ink-muted);
    padding-right: 8px;
    display: flex;
    align-items: center;
    gap: 5px;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

    .pyx-heat-label b {
        font-weight: 700;
        color: var(--pyx-ink);
    }

.pyx-heat-cell {
    min-height: 32px;
    border-radius: 6px;
    display: grid;
    place-items: center;
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-sm);
    font-weight: 600;
}

.pyx-heat-cell--off {
    background: var(--pyx-canvas-soft);
    color: var(--pyx-border-strong);
}

.pyx-heat-notes {
    display: flex;
    gap: 22px;
    flex-wrap: wrap;
    font-size: 12px;
    line-height: 1.55;
    color: var(--pyx-ink-muted);
}

    .pyx-heat-notes > * {
        flex: 1;
        min-width: 200px;
    }

/* ===== Panorama (página) ===== */
.pyx-panorama {
    display: flex;
    flex-direction: column;
    gap: var(--pyx-gap-block);
    max-width: 1560px;
}

.pyx-filters {
    padding: var(--pyx-pad-card);
    display: flex;
    /* flex-start, no flex-end: un MudNumericField/MudSelect con HelperText siempre visible
       (ej. "Eventos" en ComparativaHistorica.razor, "Entre 2 y 20 eventos") es más alto que un
       vecino sin HelperText. Con flex-end esos vecinos alinean por ABAJO, así que la caja con
       borde del campo más alto (label + input) queda más ARRIBA que la de sus vecinos -- se ve
       "desalineado" aunque cada campo esté bien formado (feedback 2026-09-16, "input de eventos
       se ve mal"). Con flex-start todas las cajas alinean por el label/borde de arriba sin
       importar cuánto texto de ayuda tenga cada una debajo. */
    align-items: flex-start;
    gap: var(--pyx-gap-toolbar);
    flex-wrap: wrap;
}

/* Bug 22753: los filtros se movían de sitio y cambiaban de tamaño al elegir un valor. Dos causas
   apiladas, ambas encontradas por prueba y error:

   1) El buscador de texto era el único con flex-grow:1 mientras los selects tenian ancho fijo --
      cualquier cambio en un vecino (un select que gana/pierde la X de Clearable, o la barra de
      scroll de la pagina apareciendo/desapareciendo al cambiar el resultado filtrado) lo hacia
      crecer o encoger para repartirse el ancho sobrante, corriendo al resto de la fila.

   2) MAS IMPORTANTE: MudSelect envuelve su control en un <div class="mud-select"> EXTERIOR que es
      el verdadero hijo directo de ".pyx-filters" (el que de verdad participa del calculo flex) --
      el Class="pyx-filter" que cada pantalla declaraba en el MudSelect caía en un
      <div class="mud-input-control ... pyx-filter"> INTERIOR, un nieto de ".pyx-filters", no un
      hijo, y "flex-basis" sólo importa en el hijo DIRECTO de un contenedor flex. MudTextField/
      MudCheckBox no tienen este problema: su Class SI cae en el hijo directo.

   Arreglo original (temporal, retirado en la Fase 6 de componentización, 2026-09-15): fijar el
   ancho también en el hijo directo vía selector ":has()", ya que MudSelect no distinguía en ese
   div exterior el ancho normal del ancho "wide". Arreglo definitivo: MudSelect<T> ya trae su
   propio OuterClass, pensado exactamente para esto -- aplica directo al div exterior real. Todos
   los MudSelect de FiltroBar pasaron de Class="pyx-filter" a OuterClass="pyx-filter", así que el
   selector plano de abajo alcanza sin ":has()". Ancho fijo para TODO filtro: ninguno crece ya
   (flex-grow:0), así que ningún filtro cambia de tamaño cuando otro gana/pierde texto o un ícono.
   Todos encogen si el contenedor es más angosto (flex-shrink:1), nunca crecen. El espacio sobrante
   en la fila se reparte como gap extra entre los filtros (.pyx-filters usa
   justify-content:space-between, feedback 2026-09-15: "esparcirlos por todo el espacio... mismo
   espaciado para todos") en vez de quedar vacío al final -- como cada filtro sigue sin crecer,
   ninguno se mueve de ancho por eso; sólo cambia el gap entre ellos. */
.pyx-filter {
    min-width: 172px;
}

.pyx-filter--wide {
    min-width: 236px;
}

.pyx-filters > .pyx-filter {
    flex: 0 1 220px;
    max-width: 220px;
}

.pyx-filters > .pyx-filter--wide {
    flex: 0 1 300px;
    max-width: 300px;
}

.pyx-filter-actions {
    display: flex;
    align-items: center;
    /* Sin wrap, un grupo con botones de texto real -- "Importar Excel" + "Nuevo Cuestionario"
       en CatalogListShell, no sólo "Buscar"/"Limpiar" -- se sale del viewport en móvil en vez de
       apilarse: verificado en /catalogos/cuestionarios, 398px de contenido en un viewport de
       390px (feedback 2026-09-16, auditoría de las 23 pantallas con tabla). align-items:center
       (no stretch) cuando envuelve, para que los botones apilados no se estiren a la altura
       completa de la fila de filtros.
       flex-wrap solo no bastaba: con flex:none (flex-basis:auto) el navegador nunca le pide a
       este grupo que se achique -- se dimensiona por su contenido natural (352px) sin importar
       que su padre .pyx-filters mida menos (315px en el mismo caso, medido en vivo), así que
       "envolver" no tenía disparador: no hay wrap posible para un único bloque más ancho que su
       contenedor. max-width:100% es lo que fuerza esa restricción y hace que sus propios hijos
       (los botones) sí envuelvan entre ellos cuando no caben. */
    flex-wrap: wrap;
    max-width: 100%;
    gap: 12px;
    flex: none;
    /* Sin esto, este grupo de botones (34.8px, sin espacio reservado para label ni helper text)
       alinea por defecto con .pyx-filters (align-items:flex-start desde 2026-09-16) igual que
       los inputs, dejando un hueco ABAJO que lo hacía ver descentrado respecto a los inputs
       vecinos más altos (feedback 2026-09-15: "botones no centrados verticalmente").
       align-self:stretch lo estira a la altura completa de la fila -- la del input más alto,
       incluyendo el que tenga HelperText -- y el align-items:center de aquí arriba lo centra
       dentro de eso. Funciona igual sin importar si .pyx-filters alinea arriba o abajo. */
    align-self: stretch;
    /* Empuja el grupo de acciones (Buscar/Limpiar) al extremo derecho de SU fila cuando sobra
       espacio, sin usar justify-content:space-between en .pyx-filters -- ese space-between
       repartía el hueco sobrante TAMBIÉN entre los filtros, y como cada fila envuelta puede traer
       una cantidad distinta de filtros, el reparto salía diferente fila por fila y las columnas
       dejaban de alinearse entre la fila de arriba y la de abajo (feedback 2026-09-15: "columnas
       del filtro sin el mismo tamaño"). Con margin-left:auto sólo este grupo se corre; los
       filtros en sí quedan siempre pegados a la izquierda con el mismo gap fijo entre ellos, así
       que alinean en columna sea cual sea la fila. */
    margin-left: auto;
}

/* Corrección de estilo (2026-08-04), punto 33: variante plegable de FiltroBar. La caja externa
   no lleva el padding de .pyx-filters -- lo ponen el encabezado y el interior por separado, para
   que plegada quede sólo la altura de la fila del encabezado. */
.pyx-filters-box {
    padding: 0;
    overflow: hidden;
}

.pyx-filters-head {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px var(--pyx-pad-card-h);
    cursor: pointer;
    user-select: none;
    font-size: 13px;
    color: var(--mud-palette-text-secondary);
}

.pyx-filters-head:hover {
    background: var(--pyx-canvas-soft);
}

.pyx-filters-head-title {
    font-weight: 600;
    color: var(--mud-palette-text-primary);
}

/* Ya vive dentro de .pyx-filters-box, así que no repite borde ni fondo de tarjeta -- mismo
   padding que .pyx-filters (no plegable), sólo se le suma el borde de separación con el
   encabezado de arriba. */
.pyx-filters--interior {
    padding: var(--pyx-pad-card);
    border-top: 1px solid var(--pyx-divider);
}

/* En mobile cada filtro pasa a su propia fila (.pyx-filters ya hace flex-wrap), pero el
   max-width fijo de arriba (220px/300px, pensado para repartir varios filtros por fila en
   desktop) lo dejaba angosto y pegado a la izquierda en su fila propia, con todo el resto del
   ancho de la card vacío. .pyx-filter-actions (Buscar/Limpiar) queda fuera a propósito: son
   botones, no inputs, y ya se alinean bien a la derecha con margin-left:auto. */
@media (max-width: 599px) {
    .pyx-filters > .pyx-filter,
    .pyx-filters > .pyx-filter--wide {
        flex: 1 1 100%;
        max-width: 100%;
    }
}

/* Bug recurrente -- ya apareció 3 veces con nombres de ticket distintos (Bug 22753 en
   .pyx-filter arriba, AB#22842 en SeleccionarEvaluadosDialog, y de nuevo en AsignarTareaDialog
   sin ticket, sep 2026): dos o más MudSelect/MudAutocomplete/campos Clearable en la MISMA fila
   flex, cada uno con flex-basis:auto (el default de CSS) o sólo un min-width sin max-width.
   Ninguna de esas dos formas evita que el ancho dependa del CONTENIDO -- la X de Clearable
   apareciendo o desapareciendo, el texto elegido siendo más largo o más corto que el label
   vacío. Elegir un valor en CUALQUIERA de los campos de la fila recalcula cuánto ancho "pide"
   ese campo y desplaza a sus vecinos, que es justo el síntoma reportado ("el modal cambia de
   tamaño al seleccionar").

   .pyx-flex-equal fuerza flex-basis a 0% -- el reparto de espacio entre hermanos pasa a
   depender SOLO de flex-grow (una proporción fija), nunca del contenido de cada uno. Usarlo en
   el <div> ENVOLVENTE de cada campo, no en el Class del propio MudSelect/MudAutocomplete: su
   Class ata al div interior "mud-input-control", no al "mud-select"/"mud-autocomplete" exterior
   que es el hijo real del flex (el mismo hallazgo, documentado por separado en cada uno de los
   tres sitios de arriba). MudTextField/PyxDatePicker sí propagan Class al hijo real -- en esos
   casos alcanza con la Class directa, sin <div> envolvente.

   Un solo campo variable junto a hermanos de contenido FIJO (un botón "Agregar", un ícono) NO
   tiene este bug y no necesita esta clase -- ahí sólo el campo variable cambia, nunca sus
   vecinos, así que el problema no existe (varios sitios del app ya resuelven ese caso con
   flex-grow-1 simple, correctamente). El riesgo es específicamente CUANDO HAY DOS O MÁS CAMPOS
   VARIABLES compitiendo por el mismo espacio. */
.pyx-flex-equal {
    flex: 1 1 0%;
    min-width: 0;
}

.pyx-empty {
    padding: 56px 24px;
    text-align: center;
    display: flex;
    flex-direction: column;
    align-items: center;
}

.pyx-state-icon {
    font-size: 44px !important;
    color: var(--pyx-ink-faint);
}

.pyx-kpis {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(186px, 1fr));
    gap: 14px;
}

/* AB#23178: en el celular, 4 KPIs de una columna empujaban la tabla de Progreso fuera de la
   pantalla; con este modificador quedan en 2×2. */
@media (max-width: 599px) {
    .pyx-kpis.pyx-kpis--2col-movil {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: 10px;
    }
}

.pyx-kpi {
    padding: var(--pyx-pad-card);
}

    .pyx-kpi b {
        display: block;
        margin-top: 13px;
        font-size: 32px;
        line-height: 1;
        font-weight: 700;
        letter-spacing: -.03em;
    }

        /* AB#23304: el detalle ("créditos disponibles", "Participantes activos") iba en línea y, con cifras largas como
           494/500, se salía del borde de la tarjeta. Ahora baja a su propia línea, siempre dentro de la tarjeta. */
        .pyx-kpi b em {
            display: block;
            margin: 6px 0 0;
            font-family: var(--pyx-label);
            font-size: 13px;
            font-style: normal;
            font-weight: 500;
            line-height: 1.25;
            letter-spacing: 0;
            color: var(--pyx-ink-muted);
            overflow-wrap: anywhere;
        }

    .pyx-kpi span {
        display: block;
        margin-top: 7px;
        font-size: 11.5px;
        color: var(--pyx-ink-muted);
    }

.pyx-card--dark.pyx-kpi span,
.pyx-card--dark.pyx-kpi b em {
    color: rgba(255, 255, 255, .55);
}

.pyx-kpi-label {
    color: var(--pyx-ink-faint);
}

.pyx-kpi-label--dark {
    color: rgba(255, 255, 255, .5);
}

.pyx-kpi-head {
    display: flex;
    align-items: center;
    gap: 6px;
}

.pyx-kpi-icon {
    font-size: 15px !important;
    color: var(--pyx-ink-faint);
}

/* HU-4: ícono "i" opcional junto a la etiqueta de la tarjeta KPI, para explicar métricas
   ambiguas sin ocupar espacio permanente -- ver KpiCard.InfoText. */
.pyx-kpi-info {
    font-size: 13px !important;
    color: var(--pyx-ink-faint);
    cursor: help;
    flex: none;
}

.pyx-kpi-trend {
    display: flex;
    align-items: center;
    gap: 4px;
    margin-top: 6px;
    font-size: 11.5px;
    font-weight: 600;
}

    .pyx-kpi-trend .mud-icon-root {
        font-size: 15px;
    }

.pyx-kpi-trend--up {
    color: var(--pyx-positive);
}

.pyx-kpi-trend--down {
    color: var(--pyx-negative);
}

.pyx-kpi-trend--flat {
    color: var(--pyx-ink-muted);
}

.pyx-kpi-trend em {
    font-style: normal;
    color: var(--pyx-ink-faint);
    margin-left: 2px;
}

/* Dos columnas sólo cuando hay ancho real: si se fuerza 1.5fr/1fr, el mapa de calor queda
   por debajo de su ancho mínimo y se recorta. */
.pyx-row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(520px, 1fr));
    gap: 18px;
    align-items: stretch;
}

.pyx-stack {
    display: flex;
    flex-direction: column;
    gap: 18px;
    min-width: 0;
}

.pyx-sub {
    color: var(--pyx-ink-muted);
    margin-top: 4px;
}

.pyx-sub-dark {
    color: rgba(255, 255, 255, .58);
    margin-top: 4px;
}

.pyx-foot {
    margin-top: auto;
}

.pyx-dispersion {
    padding: var(--pyx-pad-card);
    display: flex;
    flex-direction: column;
    gap: 14px;
    flex: 1;
}

.pyx-table th {
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-3xs);
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--pyx-ink-faint);
}

.pyx-num {
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-sm);
    font-variant-numeric: tabular-nums;
}

.pyx-person b {
    display: block;
    font-size: 12.5px;
    font-weight: 600;
}

.pyx-person span {
    display: block;
    margin-top: 2px;
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-xs);
    color: var(--pyx-ink-faint);
}


/* ===== Cuestionario ===== */

/* Leyenda de la escala: qué significa cada valor, visible antes de elegir. */
.pyx-escala-leyenda {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    padding: 10px 14px;
    margin-bottom: 14px;
    border: 1px solid var(--pyx-border);
    border-radius: 10px;
    background: var(--pyx-canvas-soft);
}

.pyx-escala-op {
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
    font-size: 11.5px;
    color: var(--pyx-ink-muted);
}

.pyx-escala-op b {
    display: inline-grid;
    place-items: center;
    min-width: 18px;
    height: 18px;
    border-radius: 5px;
    background: var(--pyx-blue);
    color: #fff;
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-xs);
}

.pyx-escala-ayuda {
    margin-left: auto;
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-xs);
    color: var(--pyx-ink-faint);
}

/* "Teclas 1-N para responder, 0 para N/A" no significa nada en táctil y le roba alto a la
   pantalla justo donde más se necesita (arriba de un cuestionario de ~100 preguntas). El manejo
   de teclado en C# (OnTeclaCompetencia) no se toca -- sigue funcionando para quien conecte un
   teclado físico a su tableta o teléfono, sólo deja de anunciarse. */
@media (max-width: 959px) {
    .pyx-escala-ayuda {
        display: none;
    }
}

/* Una pregunta. El filete izquierdo marca sobre cuál se está registrando. scroll-margin-bottom
   evita que la barra inferior fija (.pyx-bottomnav) o el pie sticky de captura (.pyx-eval-
   acciones) tapen la pregunta recién enfocada: scrollElementIntoView (index.html) usa block:
   'nearest', que no sabe de elementos fijos -- se resuelve en CSS sin tocar el JS. */
.pyx-q {
    padding: 10px 14px;
    border-radius: 10px;
    border-left: 3px solid transparent;
    outline: none;
    scroll-margin-bottom: 90px;
    transition: background-color .15s ease, border-color .15s ease;
}

.pyx-q:hover {
    background: var(--pyx-canvas-soft);
}

.pyx-q--activa {
    background: var(--pyx-blue-soft);
    border-left-color: var(--pyx-blue);
}

/* Captura Manual (PBI 22327 §5): pregunta sin Puntaje ni EsNA. Puede coexistir con --activa (la
   pregunta con el foco); el filete queda en ámbar -- "aquí estás, y sigue sin responder" es más
   útil que el azul de --activa -- pero el fondo azul de --activa se conserva, porque esa regla no
   toca background. */
.pyx-q--pendiente {
    border-left-color: var(--mud-palette-warning);
}

/* Pregunta a la izquierda, estrellas al final de su misma línea. */
.pyx-q-fila {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 24px;
    flex-wrap: wrap;
}

.pyx-q-texto {
    flex: 1;
    min-width: 260px;
    font-weight: 600;
    color: var(--mud-palette-text-primary);
}

/* min-width:260px en el texto más una escala Grande de varias opciones (6 para una escala de 5,
   más para una de 10) suman más que el ancho útil de un teléfono -- con flex-wrap eso fuerza
   scroll horizontal real en vez de sólo partir la fila en dos líneas. Por debajo de 600px se
   apila en vez de esperar a que el navegador decida dónde cortar. */
@media (max-width: 599px) {
    .pyx-q-fila {
        flex-direction: column;
        align-items: stretch;
        gap: 10px;
    }

    .pyx-q-texto {
        min-width: 0;
    }
}

.pyx-q-control {
    display: flex;
    align-items: center;
    gap: 10px;
    flex: none;
}

.pyx-q-etiqueta {
    display: block;
    margin-top: 2px;
    color: var(--mud-palette-primary);
}

/* El valor acompaña a las estrellas: ancho fijo para que no baile al pasar de "—" a un número. */
.pyx-rating-valor {
    min-width: 34px;
    font-family: var(--pyx-label);
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* ===== Reporte Detallado ===== */

.pyx-rd-fichas {
    display: grid;
    /* min(360px, 100%) en vez de 360px seco: en un viewport angosto (iPhone SE y similares,
       <380px con el padding de la card ya descontado) el mínimo de 360px no podía encoger y
       forzaba scroll horizontal de toda la grilla aun con una sola columna. min() deja que el
       track baje hasta el 100% del contenedor cuando 360px no entra, sin tocar el layout de
       varias columnas en desktop (ahí 100% del contenedor sigue siendo mucho más que 360px). */
    grid-template-columns: repeat(auto-fit, minmax(min(360px, 100%), 1fr));
    gap: var(--pyx-gap-block);
    margin-bottom: var(--pyx-gap-block);
}

.pyx-rd-ficha {
    padding: var(--pyx-pad-card);
    display: flex;
    flex-direction: column;
    gap: 16px;
}

.pyx-rd-ficha-head {
    display: flex;
    align-items: center;
    gap: 12px;
}

.pyx-rd-kpis {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    gap: 12px;
}

    .pyx-rd-kpis b {
        display: block;
        margin-top: 5px;
        font-size: 22px;
        line-height: 1;
        font-weight: 700;
        letter-spacing: -.02em;
    }

    .pyx-rd-kpis em {
        display: block;
        margin-top: 4px;
        font-style: normal;
        font-size: 10.5px;
        color: var(--pyx-ink-muted);
    }

/* Un círculo evaluador con su promedio. */
.pyx-rd-circulos {
    display: flex;
    flex-wrap: wrap;
    gap: 8px;
}

.pyx-rd-circulo {
    display: flex;
    align-items: center;
    gap: 7px;
    padding: 7px 11px;
    border: 1px solid var(--pyx-border);
    border-radius: 999px;
    font-size: 11.5px;
}

.pyx-rd-circulo i {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    flex: none;
}

.pyx-rd-circulo b {
    font-family: var(--pyx-label);
    font-weight: 700;
    font-variant-numeric: tabular-nums;
}

.pyx-rd-circulo-n {
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-2xs);
    color: var(--pyx-ink-faint);
}

.pyx-rd-circulo--off {
    background: var(--pyx-canvas-soft);
    color: var(--pyx-ink-faint);
}

/* Tabla del reporte: ancha, con scroll propio para no empujar la página. */
.pyx-rd-scroll {
    overflow-x: auto;
}

.pyx-rd-tabla {
    width: 100%;
    border-collapse: collapse;
    font-size: 12px;
}

    .pyx-rd-tabla th {
        text-align: left;
        padding: 8px 10px;
        font-family: var(--pyx-label);
        font-size: var(--pyx-font-3xs);
        font-weight: 600;
        letter-spacing: .06em;
        text-transform: uppercase;
        color: var(--pyx-ink-faint);
        border-bottom: 1px solid var(--pyx-border);
        white-space: nowrap;
    }

    .pyx-rd-tabla td {
        padding: 7px 10px;
        border-bottom: 1px solid var(--pyx-divider);
    }

    .pyx-rd-tabla tbody tr:last-child td {
        border-bottom: none;
    }

/* La columna numérica alinea a la derecha CELDA Y ENCABEZADO. Sin la segunda regla,
   `.pyx-rd-tabla th` (0,1,1) le gana a `.pyx-rd-num` (0,1,0) por especificidad y el
   encabezado se quedaba a la izquierda mientras sus cifras iban a la derecha: "AUTOEVALUACIÓN"
   caía sobre la columna de Jefes y cada rótulo quedaba desplazado una columna respecto a
   los números que titulaba. */
.pyx-rd-num,
.pyx-rd-tabla th.pyx-rd-num {
    text-align: right;
    font-family: var(--pyx-label);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Ancho parejo para que las columnas no bailen entre una persona y otra. */
.pyx-rd-tabla th.pyx-rd-num,
.pyx-rd-tabla td.pyx-rd-num {
    min-width: 96px;
}

/* La competencia se queda con el espacio sobrante. */
.pyx-rd-comp {
    width: 100%;
    min-width: 220px;
}

.pyx-rd-comp b {
    font-weight: 700;
    color: var(--pyx-ink);
    margin-right: 5px;
}

.pyx-rd-resultado {
    font-weight: 700;
    color: var(--pyx-blue);
}

.pyx-rd-rango {
    color: var(--pyx-ink-faint);
}

/* Lectura de consenso. */
.pyx-rd-consenso {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
    gap: 24px;
}

.pyx-rd-brecha {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 52px 52px 58px;
    gap: 8px;
    align-items: center;
    padding: 5px 0;
    border-bottom: 1px solid var(--pyx-divider);
    font-size: 12px;
}

    .pyx-rd-brecha em,
    .pyx-rd-brecha strong {
        font-family: var(--pyx-label);
        font-style: normal;
        font-size: var(--pyx-font-sm);
        text-align: right;
    }

.pyx-rd-ok {
    color: var(--pyx-positive);
}

.pyx-rd-alerta {
    color: var(--pyx-negative);
}

/* Muestra de color del nivel en el catálogo. */
.pyx-nivel-color {
    display: inline-block;
    width: 12px;
    height: 12px;
    border-radius: 3px;
    border: 1px solid var(--pyx-border);
    flex: none;
}

/* ===== Acceso (portada) ===== */

/* auto-fit con piso de 420px: dos paneles sólo cuando hay ancho real; por debajo se apilan y
   el panel de marca queda arriba como cabecera. */
.pyx-access {
    min-height: 100vh;
    display: grid;
    grid-template-columns: 1fr 1fr;
}

.pyx-access-brand {
    background: var(--pyx-navy);
    padding: 64px 72px;
    display: flex;
    flex-direction: column;
    /* Punto 51: el panel ya sólo lleva el logo y la frase. Con space-between quedaban pegados
       arriba y abajo con un hueco enorme en medio; la frase se centra bajo el logotipo. */
    justify-content: center;
    gap: 40px;
    position: relative;
    overflow: hidden;
}

/* Isotipo grande como marca de agua del panel: grande y cargado a la derecha para que solo se
   vea la mitad, como en el mockup -- pero centrado en vertical (no con el top:104px exacto del
   mockup, que era para su frame fijo de 760px de alto y no cuadra contra el panel real a 100vh).
   pointer-events:none para que nunca robe clics al contenido que va encima. */
.pyx-access-iso {
    position: absolute;
    right: -180px;
    top: 50%;
    transform: translateY(-50%);
    width: 560px;
    height: auto;
    opacity: .15;
    pointer-events: none;
}

/* Wordmark de Acceso, calcado a mano del .dc.html (no usa el componente <Wordmark> compartido
   con el sidebar). El bloque 360° mide 14.3px = 42% de los 34px del logotipo, la misma regla de
   identidad que aplica ese componente, solo resuelta aquí en vez de vía su parámetro Height. */
.pyx-access-wordmark {
    display: inline-flex;
    align-items: flex-start;
    gap: 4px;
    position: relative;
}

    .pyx-access-wordmark img {
        display: block;
        height: 34px;
    }

.pyx-access-power {
    font: 700 14.3px/1 'Plus Jakarta Sans', sans-serif;
    color: #40a3fe;
    letter-spacing: -.01em;
}

    .pyx-access-power sup {
        font-size: .62em;
        vertical-align: .55em;
        letter-spacing: 0;
    }

.pyx-access-pitch {
    position: relative;
}

.pyx-access-pitch {
    max-width: 440px;
    display: flex;
    flex-direction: column;
    gap: 22px;
}

.pyx-access-form {
    display: grid;
    place-items: center;
    padding: 48px 32px;
    background: var(--mud-palette-surface);
}

/* Sustituto compacto del panel de marca cuando este se oculta (ver @media más abajo) --
   oculto por omisión porque en desktop la identidad ya la da .pyx-access-brand completo. */
.pyx-access-form-brand {
    display: none;
}

@media (max-width: 959px) {
    .pyx-access-form-brand {
        display: flex;
        justify-content: center;
        margin-bottom: 28px;
    }
}

/* Punto 52a: "que sea más pequeño". El título baja a H4 en AuthPageLayout y la tarjeta se
   angosta de 380 a 320. */
.pyx-access-card {
    width: 100%;
    max-width: 320px;
    background: transparent;
}

/* HU-2: apartado del logo del cliente (tenant), resuelto en cuanto el usuario escribe su cuenta.
   Posicionado en el propio panel del formulario -- a diferencia del "perdidillo" de Pixum
   Interactivo, aquí tiene su propio renglón con separación clara del título. */
.pyx-access-client-branding {
    display: flex;
    justify-content: center;
    margin-bottom: 20px;
}

/* Un poco más grande que antes: ahora es lo único que identifica al cliente en esta pantalla,
   ya no comparte renglón con el nombre de la empresa. */
.pyx-access-client-logo {
    max-height: 42px;
    max-width: 200px;
    object-fit: contain;
}

.pyx-access-sub {
    color: var(--mud-palette-text-secondary);
    margin-top: 6px;
    margin-bottom: 24px;
}

.pyx-access-copy {
    display: block;
    margin-top: 28px;
    padding-top: 16px;
    border-top: 1px solid var(--pyx-divider);
    color: var(--mud-palette-text-disabled);
}

/* Debe ir después de los selectores base .pyx-access/.pyx-access-brand de arriba: a igual
   especificidad, CSS resuelve por orden de aparición, así que puesta antes (como estaba en un
   intento previo) esta regla perdía contra display:flex/grid del selector base -- el panel de
   marca se seguía viendo en móvil pese al media query "correcto". Antes usaba
   repeat(auto-fit, minmax(420px, 1fr)) para .pyx-access: con un solo hijo visible (el panel se
   oculta aquí) ese minmax igual reservaba 420px mínimos, así que cualquier teléfono más angosto
   (390px, 375px, la mayoría) quedaba con scroll horizontal en toda la página. */
@media (max-width: 959px) {
    .pyx-access {
        display: block;
    }

    .pyx-access-brand {
        display: none;
    }
}

/* ===== Nodo del árbol de Estructura =====
   Global y no aislado: .pyx-node-name y .pyx-chip-mono van sobre MudText/MudChip,
   que no propagan el atributo de scope de Blazor a su elemento raíz. */

.pyx-node {
    flex: 1;
    min-width: 0;
    padding: 2px 0;
}

/* El tirador es la señal de que la fila se puede arrastrar; el arrastre sigue funcionando
   desde cualquier punto de la fila, como antes. */
.pyx-node-handle {
    font-family: var(--pyx-label);
    font-size: 13px;
    line-height: 1;
    color: var(--pyx-border-strong);
    cursor: grab;
    user-select: none;
    flex: none;
}

.pyx-node-handle:active {
    cursor: grabbing;
}

/* Azul = área (raíz), gris = subárea. El nivel se lee sin contar sangrías. */
.pyx-node-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--pyx-border-strong);
    flex: none;
}

.pyx-node-dot--area {
    background: var(--pyx-blue);
}

.pyx-node-name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.pyx-node-name--area {
    font-weight: 600;
}


/* ===== Registro de Personas — hoja de cálculo =====
   De "Identidad Pyxoom360" (mudblazor/Pages/Personas.razor). Global: todos los selectores
   cuelgan de MudTable, que no propaga el atributo de scope. */

.pyx-reg-table {
    border: 1px solid var(--pyx-border);
    border-radius: 10px;
    overflow: hidden;
}

/* Nueve columnas no caben cómodas por debajo de ~1290px: la tabla hace su propio scroll en
   vez de empujar la página entera. */
.pyx-reg-table .mud-table-container {
    overflow-x: auto;
}

/* El ancho y el reparto por columna se fijan más abajo, junto al resto del bloque de la hoja
   editable: con campos dentro de cada celda el reparto automático estrujaba Correo. */

.pyx-reg-table .mud-table-head .mud-table-cell {
    background: var(--pyx-canvas-soft);
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-3xs);
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--pyx-ink-faint) !important;
    padding: 8px;
    white-space: nowrap;
}

.pyx-reg-table .mud-table-body .mud-table-cell {
    padding: 4px 8px;
    border-bottom: 1px solid var(--pyx-divider);
}

/* Código y usuario en monoespaciada: son identificadores, se comparan en vertical. */
.pyx-mono-cell {
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-xs);
    font-weight: 500;
    color: var(--pyx-ink-muted);
    white-space: nowrap;
}

.pyx-mono-cell--faint {
    color: var(--pyx-ink-faint);
}

/* ===== Registro de Personas — hoja de cálculo editable =====
   De "Identidad Pyxoom360" (Pyxoom360 Administración.dc.html). Global: cuelga de MudTable. */

.pyx-reg { padding: 0; overflow: hidden; }

/* Fase 1 (observaciones 18/08): fila de filtros de Organigrama/Evento/Evaluado-Evaluador, bajo
   el MudToolBar de arriba -- mismo padding horizontal que .pyx-net-bar para que las dos filas se
   vean como una sola cabecera, con su propio borde inferior para separarlas de la tabla. */
.pyx-reg-filtros {
    padding: 4px 16px 12px;
    border-bottom: 1px solid var(--pyx-divider);
}

/* AB#23101: la barra de acciones masivas (PBI 22669) usaba esta clase sin que existiera ninguna
   regla: quedaba sin padding, pegada al borde izquierdo de la tarjeta, y la etiqueta flotante de
   los filtros de abajo se le encimaba. Fila propia con el mismo padding horizontal que
   .pyx-net-bar y .pyx-reg-filtros, fondo suave y borde inferior para que se lea como un estado
   de la selección y no como parte de los filtros. */
.pyx-reg-seleccion {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 8px 24px;
    padding: 10px 16px;
    background: var(--pyx-canvas-soft);
    border-bottom: 1px solid var(--pyx-divider);
}

/* AB#23189: dos grupos -- el estado de la selección a la izquierda y las acciones masivas al
   extremo derecho -- para que "Eliminar seleccionadas" no quede pegado al conteo. Con poco ancho
   el grupo de acciones baja entero a su propia línea en lugar de partirse. */
.pyx-reg-seleccion__info,
.pyx-reg-seleccion__acciones {
    display: flex;
    align-items: center;
    gap: 8px;
}

.pyx-reg-seleccion__acciones {
    flex-wrap: wrap;
    margin-left: auto;
}

.pyx-reg-seleccion__conteo {
    font-weight: 600;
    color: var(--pyx-ink);
    white-space: nowrap;
}

.pyx-reg-seleccion__divisor {
    align-self: stretch;
    width: 1px;
    margin: 2px 4px;
    background: var(--pyx-divider);
}

/* Mismo bug de fondo que .pyx-filter/.pyx-flex-equal (ver sus comentarios): los 5 MudSelect de
   esta fila declaran Class="pyx-filter" en el propio Razor, pero esa Class ata al div interior
   "mud-input-control", no al "mud-select" exterior que es el hijo real del flex -- así que
   .pyx-filter (min-width) nunca llegaba a aplicarse aquí, y como esta fila tampoco es
   ".pyx-filters" (es ".pyx-reg-filtros", su propio contenedor con su propio padding), tampoco
   heredaba el flex-basis fijo + max-width que sí protege a FiltroBar. Verificado en vivo:
   6-9px de corrimiento en los 5 filtros al elegir cualquiera de ellos. Selector estructural
   (":scope > .mud-select", el div real que MudBlazor pone) en vez de agregar la clase
   ".pyx-filters" al contenedor -- esa también trae su propio align-items:flex-start, que
   pisaría el align-items:center que ya usa esta fila. */
.pyx-reg-filtros > .mud-select {
    flex: 0 1 220px;
    max-width: 220px;
    min-width: 172px;
}

/* "Evento" (2º filtro) es el equivalente a pyx-filter--wide: nombres de evento más largos que
   el resto de las etiquetas. */
.pyx-reg-filtros > .mud-select:nth-child(2) {
    flex: 0 1 300px;
    max-width: 300px;
    min-width: 236px;
}

@media (max-width: 599px) {
    .pyx-reg-filtros > .mud-select,
    .pyx-reg-filtros > .mud-select:nth-child(2) {
        flex: 1 1 100%;
        max-width: 100%;
    }
}

/* AB#23263: Nivel y Perfil de la fila de filtros son selectores con busqueda (MudAutocomplete) envueltos en su propio
   div: la Class de un autocompletar cae en el div interior "mud-input-control", no en el hijo real del flex, asi que el
   ancho fijo va en el wrapper (mismos numeros que el resto de los filtros -> la fila no salta al elegir un valor).
   Doble clase para ganarle en especificidad a las reglas de MudBlazor. */
.pyx-reg-filtros > .pyx-reg-filtro-busqueda.pyx-reg-filtro-busqueda {
    flex: 0 1 220px;
    max-width: 220px;
    min-width: 172px;
}

@media (max-width: 599px) {
    .pyx-reg-filtros > .pyx-reg-filtro-busqueda.pyx-reg-filtro-busqueda {
        flex: 1 1 100%;
        max-width: 100%;
    }
}

/* AB#23263: leyenda «Mostrando 50 de N» al pie del desplegable de busqueda (Nivel / Perfil). */
.pyx-busqueda-leyenda.pyx-busqueda-leyenda {
    color: var(--mud-palette-text-secondary);
    border-top: 1px solid var(--pyx-divider);
}

/* Bug 22595: el identificador personal (18-20 chars mono, p.ej. "MALJ961208HCLRPR09") se
   recortaba a 130px. Sube a 175px para que abarque lo más posible de la columna; el texto se
   trunca con ellipsis dentro de la celda y el valor completo se ve en el MudTooltip. */
.pyx-reg-cod { width: 175px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Bug 22595: el span interno del MudTooltip hereda el truncado de la celda -- sin esto, el
   texto desbordaba la celda en vez de recortarse con ellipsis. */
.pyx-reg-cod-text {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* UX 22636: 70px no daban para dos botones de 34px en una línea (se apilaban en columna). */
.pyx-reg-acc { width: 90px; text-align: center; }

.pyx-reg-acc-cell {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 2px;
    white-space: nowrap;
}

.pyx-reg-acc-cell .mud-icon-button { padding: 5px; }

/* UX 22611 (Perfiles): columna Acciones de los catálogos genéricos (MudTable con anchos
   automáticos). Sin ancho propio, un texto largo en otra columna la estrechaba y los iconos se
   partían en dos filas. width:1% + nowrap la ajusta a su contenido y los botones no se parten. */
.pyx-cat-acc {
    width: 1%;
    white-space: nowrap;
    text-align: center;
}

.pyx-cat-acc-cell {
    display: flex;
    align-items: center;
    justify-content: center;
    flex-wrap: nowrap;
    gap: 2px;
}

/* UX 22610 (Niveles): textos sin espacios (nombre de 100, descripción de 500) no tienen punto
   de corte y ensanchan la tabla más allá del contenedor. overflow-wrap:anywhere deja partir
   dentro de la palabra sólo cuando no cabe; el ancho de Nombre evita que Descripción lo aplaste. */
.pyx-cat-wrap { overflow-wrap: anywhere; }

.pyx-cat-col-nombre { width: 28%; min-width: 160px; }

.pyx-cat-fit { width: 1%; white-space: nowrap; }

/* UX 22636: los títulos se veían corridos respecto al contenido porque el chip de Tipo, el badge
   de Estado y el botón "Restablecer" traen margen/padding propio a la izquierda, mientras que el
   título arranca en el padding de la celda (8px). Se quita ese margen interno para que texto y
   título compartan el mismo borde izquierdo, como en las columnas de campos editables. */
.pyx-reg-table .mud-table-body td:nth-last-child(4) .pyx-reg-pass { padding-left: 0; margin-left: 0; }
/* UX 22636 (re-abierto): al quitarle el padding izquierdo al botón, la llave de "Restablecer"
   perdía sus 2px de la izquierda: MudButton pone margin-left:-2px al ícono inicial (para
   compensar el padding que ya no está) y el botón recorta con overflow:hidden. */
.pyx-reg-table .mud-table-body td:nth-last-child(4) .pyx-reg-pass .mud-button-icon-start { margin-left: 0; }
.pyx-reg-table .mud-table-body td:nth-last-child(3) .mud-chip { margin-left: 0; }
/* UX 22636 (re-abierto): Estado seguía 4px corrido. MudChip 8.x envuelve el chip en un
   div.mud-chip-container con display:contents, así que "> *" le quitaba el margen al envoltorio
   y el chip de adentro conservaba sus 4px. Se apunta al chip, igual que en Tipo. */
.pyx-reg-table .mud-table-body td:nth-last-child(2) .mud-chip { margin-left: 0; }

/* Diez columnas con campos editables dentro no caben en pantallas medianas: la tabla lleva su
   propio scroll horizontal y cada columna un ancho fijo, porque el reparto automático estrujaba
   Correo hasta dejar dos letras visibles. Sólo por encima de sm: por debajo, MudTable ya colapsó
   a vista de tarjeta (Breakpoint.Sm) y forzar este ancho de tabla clásica la rompería.
   UX 22636 (re-abierto): el umbral era 600px, pero Breakpoint.Sm colapsa la tabla por DEBAJO de
   960px (clase mud-sm-table, medido a 768: celdas en flex y tabla de 2145px dentro de 706px), así
   que en tablet cada tarjeta medía 2145px de ancho con scroll horizontal vacío. */
@media (min-width: 960px) {
    /* Bug 22577: 1650px cubría hasta la columna Tipo (Fase 1), pero cuando se agregó Estado
       después nadie ajustó este min-width ni le dio ancho propio -- con table-layout:fixed el
       navegador no tenía margen para ella y comprimía Estado/Acciones.
       Bug 22595/22594: .pyx-reg-cod subió de 130→175 (delta +45), Tipo se mantiene en 150.
       1910+45 = ~1955. */
    /* UX 22641: Nivel 135→190 (+55), Perfil 135→200 (+65), Área 180→230 (+50) = +170.
       1955 + 170 = 2125. */
    /* UX 22636: Acciones 70→90 (+20). 2125 + 20 = 2145. */
    .pyx-reg-table table { min-width: 2145px; table-layout: fixed; }
}

/* Bug 22934: MultiSelection (PBI 22669) agrega una columna de checkbox al INICIO de cada fila y
   estas reglas seguían contando desde la izquierda sin ella: cada columna recibía el ancho de su
   vecina (Usuario los 120px de Contraseña, y el usuario se pisaba con el botón Restablecer).
   Se cuenta desde la DERECHA (Acciones = 1), que no cambia aunque aparezcan columnas al inicio. */
.pyx-reg-table th:nth-last-child(11), .pyx-reg-table td:nth-last-child(11) { width: 210px; }  /* Nombre   */
.pyx-reg-table th:nth-last-child(10), .pyx-reg-table td:nth-last-child(10) { width: 235px; }  /* Correo   */
/* UX 22641: con 135px el valor elegido se leía "Ejec..." / "Perfil..." y el desplegable, que
   hereda el ancho del campo, partía "Nivel descripcion 500" en tres líneas. */
.pyx-reg-table th:nth-last-child(9), .pyx-reg-table td:nth-last-child(9) { width: 190px; }  /* Nivel    */
.pyx-reg-table th:nth-last-child(8), .pyx-reg-table td:nth-last-child(8) { width: 200px; }  /* Perfil   */
.pyx-reg-table th:nth-last-child(7), .pyx-reg-table td:nth-last-child(7) { width: 175px; }  /* Estruct. */
.pyx-reg-table th:nth-last-child(6), .pyx-reg-table td:nth-last-child(6) { width: 230px; }  /* Área     */

/* UX 22641: desplegables de Nivel / Perfil / Estructura / Área (tabla y filtros). Con
   RelativeWidth=Adaptive el popover crece hasta el contenido, y esto evita que las opciones se
   partan en varias líneas ("SubDepartame- DC3-1 (Organigrama DC)") para que de verdad crezca.
   Global a propósito: el popover se pinta en el MudPopoverProvider, fuera de la tabla. */
.pyx-reg-popover .mud-list-item-text,
.pyx-reg-popover .mud-list-item-text .mud-typography {
    white-space: nowrap;
}

.pyx-reg-popover .mud-list {
    max-width: min(520px, 90vw);
}

.pyx-reg-popover .mud-list-item-text .mud-typography {
    overflow: hidden;
    text-overflow: ellipsis;
}

/* HU-19: select de área + botón de "rellenar hacia abajo" en la misma línea, sin ensanchar
   la columna más de lo necesario -- el select se encoge, el botón no. */
.pyx-reg-area-cell {
    display: flex;
    align-items: center;
    gap: 2px;
}

.pyx-reg-area-cell .mud-select {
    min-width: 0;
    flex: 1;
}

/* Bug 22596/22621: la grilla de corrección de importación (.ivg-table, en Personas.razor.css e
   ImportacionValidacionGrid.razor.css) intentaba forzar los inputs editables a ocupar el ancho de
   su celda con ".ivg-table .mud-input-control { width: 100% }" -- puesta en un .razor.css, esa
   regla NUNCA aplicó: el scope de Blazor no llega a la raíz que dibuja MudBlazor (mismo gotcha que
   Organigrama/Estructura documentado en CLAUDE.md). Va aquí, global, para que de verdad matchee. */
.ivg-table .mud-input-control {
    width: 100%;
}

.pyx-reg-area-cell .mud-icon-button-root {
    flex: none;
    padding: 4px;
}
.pyx-reg-table th:nth-last-child(5), .pyx-reg-table td:nth-last-child(5) { width: 230px; }  /* Usuario  */
.pyx-reg-table th:nth-last-child(4), .pyx-reg-table td:nth-last-child(4) { width: 120px; }  /* Contras. */
.pyx-reg-table th:nth-last-child(3), .pyx-reg-table td:nth-last-child(3) { width: 150px; } /* Rol      */
.pyx-reg-table th:nth-last-child(2), .pyx-reg-table td:nth-last-child(2) { width: 110px; } /* Estado   */

/* Bug 22577: el chip de Rol ("Evaluado"/"Evaluador") no tenía manejo de overflow -- ahora tiene
   white-space:nowrap como respaldo; la columna se amplió a 150px para acomodar "Evaluado". */
.pyx-reg-table td:nth-last-child(3) .mud-chip {
    max-width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    display: inline-block;
}

/* El candado va junto al usuario, en la misma línea; el correo se recorta con puntos suspensivos
   en vez de partir la fila en dos. */
.pyx-reg-table td:nth-last-child(5) .pyx-user-cell {
    display: flex;
    align-items: center;
    gap: 5px;
    min-width: 0;
}

.pyx-reg-table td:nth-last-child(5) .pyx-user-cell .mud-icon-root { font-size: 13px; flex: none; }

/* UX 23044: con un usuario largo, el texto se montaba sobre el candado. Causa: la regla del Bug
   22934 (recortar ".mud-tooltip-root" con min-width:0 + overflow:hidden para que el correo no
   invadiera la columna Contraseña) aplicaba a TODOS los tooltips de la celda, y el candado también
   iba dentro de un MudTooltip: al no caber el nombre, flex encogía el envoltorio del candado a
   ~5px y el texto arrancaba encima del icono. Sólo pasaba con nombres que no caben (recortados con
   "..."), por eso QA lo vio "al paginar": las páginas con cuentas largas.
   Regla nueva, por posición y no por clase: todo lo que preceda al usuario (candado, con o sin
   envoltorio de tooltip) mide lo suyo y no se encoge; sólo el ÚLTIMO hijo -- el usuario, esté
   envuelto o no -- absorbe el ancho que falta y se recorta. Así no depende de si el icono lleva
   MudTooltip (QA) o title (rama, AB#23045). */
.pyx-reg-table td:nth-last-child(5) .pyx-user-cell > * { flex: none; }

.pyx-reg-table td:nth-last-child(5) .pyx-user-cell > :last-child {
    flex: 1 1 auto;
    min-width: 0;
    max-width: 100%;
    overflow: hidden;
}

/* Bug 22934: el usuario se pisaba con la columna Contraseña. La regla apuntaba a "> span", pero
   el span iba dentro de un MudTooltip (div.mud-tooltip-root), así que nunca recortaba: el correo
   completo seguía de largo hasta la celda de al lado. Se recorta el span esté donde esté. */
.pyx-reg-table td:nth-last-child(5) .pyx-user-cell span {
    display: block;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Pedido del usuario (2026-10-01): el texto del chip de rol ("Limitado"/"Administrador") quedaba
   3.5px arriba del centro. La regla de arriba también alcanzaba al span interno del chip y lo
   volvía bloque, con la línea pegada arriba; se le devuelve el inline-flex centrado de MudBlazor. */
.pyx-reg-table td:nth-last-child(5) .pyx-user-cell span.mud-chip-content {
    display: inline-flex;
    overflow: visible;
}

/* Celda con el problema: fondo y borde rojos en el propio campo, no en toda la fila, para que
   se vea CUÁL corregir. */
.pyx-reg-cell--error .mud-input.mud-input-outlined {
    background: color-mix(in srgb, var(--mud-palette-error) 8%, transparent);
}

.pyx-reg-cell--error .mud-input.mud-input-outlined .mud-input-outlined-border {
    border-color: var(--mud-palette-error);
}

.pyx-reg-row--error td { background: color-mix(in srgb, var(--mud-palette-error) 6%, transparent); }
.pyx-reg-row--locked td { background: var(--pyx-canvas-soft); }

.pyx-reg-issue-row td {
    background: color-mix(in srgb, var(--mud-palette-error) 6%, transparent);
    border-bottom: 1px solid var(--pyx-divider);
}

.pyx-reg-issue {
    padding: 0 12px 7px !important;
    font-size: 10.5px;
    font-weight: 500;
    line-height: 1.3;
    color: var(--mud-palette-error);
}

/* Usuario que todavía no existe: se muestra en punteado para distinguir la propuesta de la
   cuenta real con la que ya se puede entrar. */
.pyx-user-propuesta {
    border-bottom: 1px dashed var(--pyx-border);
    color: var(--pyx-ink-faint);
    cursor: help;
}

/* Hallazgo (2026-09-28): etiqueta "(sugerido)" junto al nombre propuesto -- visible sin
   depender del tooltip al hover, que era la única pista de que la cuenta no existe aún. */
.pyx-user-propuesta-etiqueta {
    font-size: 10.5px;
    margin-left: 4px;
    cursor: help;
}

.pyx-reg-pass.mud-button-root { font-size: 10.5px; min-width: 0; padding: 0 8px; height: 24px; }

.pyx-reg-foot {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 16px;
    border-top: 1px solid var(--pyx-divider);
    background: var(--pyx-canvas-soft);
}

.pyx-reg-foot-note {
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-xs);
    color: var(--pyx-ink-faint);
}

/* ===== Estructura Organizacional — dos paneles =====
   De "Identidad Pyxoom360" (Pyxoom360 Administración.dc.html). Global: cuelga de MudPaper. */

.pyx-struct {
    display: grid;
    grid-template-columns: 280px minmax(0, 1fr);
    gap: var(--pyx-gap-block);
    align-items: start;
}

@media (max-width: 959px) {
    .pyx-struct { grid-template-columns: minmax(0, 1fr); }
}

.pyx-struct-list { padding: 0; overflow: hidden; }

.pyx-struct-list-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
    padding: 12px 14px;
    border-bottom: 1px solid var(--pyx-divider);
}

/* Corrección de estilo, punto 6: título + "Importar Excel" + "Nueva" no caben en una sola fila
   en el panel angosto de "Organización" -- se apilan en dos filas en vez de desbordar. */
.pyx-struct-list-head--stacked {
    flex-direction: column;
    align-items: stretch;
}

.pyx-struct-list-title { font-weight: 600; }

.pyx-struct-row {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 10px 14px;
    border-bottom: 1px solid var(--pyx-divider);
    cursor: pointer;
}

.pyx-struct-row:hover { background: var(--pyx-canvas-soft); }

/* La seleccionada se marca con una barra a la izquierda, no con un relleno fuerte:
   la lista tiene que seguir leyéndose como lista. */
.pyx-struct-row--on {
    background: var(--pyx-canvas-soft);
    box-shadow: inset 3px 0 0 var(--mud-palette-primary);
}

.pyx-struct-row-text { flex: 1; min-width: 0; }
.pyx-struct-row-name { font-weight: 600; line-height: 1.25; }

.pyx-struct-row-meta {
    display: block;
    margin-top: 2px;
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-2xs);
    letter-spacing: .04em;
    color: var(--pyx-ink-faint);
}

.pyx-chip-mono--uso {
    background: color-mix(in srgb, var(--pyx-caution) 18%, transparent);
    color: var(--pyx-caution);
}

.pyx-chip-mono--area {
    background: color-mix(in srgb, var(--mud-palette-primary) 12%, transparent);
    color: var(--mud-palette-primary);
}

.pyx-struct-list-note {
    display: block;
    padding: 10px 14px;
    color: var(--pyx-ink-faint);
    line-height: 1.4;
}

/* Bug 22578: overflow:hidden sin scroll recortaba las acciones (Agregar/Renombrar/Eliminar) de
   las filas más profundas o con nombres largos en organizaciones grandes, en vez de dejarlas
   alcanzables con scroll horizontal. */
.pyx-tree-card { padding: 0; overflow-x: auto; overflow-y: hidden; }

.pyx-tree-head {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    border-bottom: 1px solid var(--pyx-divider);
    flex-wrap: wrap;
}

.pyx-tree-title { font-weight: 600; }

.pyx-tree-meta {
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-2xs);
    letter-spacing: .08em;
    color: var(--pyx-ink-faint);
    white-space: nowrap;
}

.pyx-tree-search { width: 210px; flex: none; }
.pyx-tree-rename { width: 220px; flex: none; }

.pyx-tree-lock {
    border-radius: 0;
    background: color-mix(in srgb, var(--pyx-caution) 10%, transparent);
}

.pyx-tree-body { padding: 6px 4px; overflow-x: auto; }

/* Bug 22578 (retrabajo): el árbol crece con su contenido (width:max-content) para que, cuando
   las áreas/subáreas o la indentación por profundidad excedan el ancho del contenedor, aparezca
   el scroll horizontal de .pyx-tree-body/.pyx-tree-card en vez de recortar las filas. Sin esto,
   el contenido se encogía y nunca se podía desplazar hasta las filas más profundas. */
.pyx-tree-body .mud-treeview {
    width: max-content;
    min-width: 100%;
}

/* Bug 22578 (causa raíz): MudTreeViewItem no expone min-width y su contenido flex
   (.mud-treeview-item-content) usa min-width:auto -- la fila no se encogía y empujaba las
   acciones fuera del área visible en organizaciones grandes. Con min-width:0 el nombre se
   trunca con ellipsis y las acciones quedan siempre alcanzables. Va aquí (global) y no en un
   .razor.css porque el scope de Blazor no llega a lo que MudBlazor dibuja por su cuenta.
   Bug 22578 (retrabajo): faltaba .mud-treeview-item-label en esta cadena -- es el contenedor que
   MudBlazor pone alrededor del BodyContent (flex-grow:1, sin min-width propio), y sin él la
   cadena se cortaba ahí: .pyx-node-name podía truncar hacia adentro pero el label que lo envuelve
   seguía sin encogerse, así que un nombre larguísimo en el primer nivel (sin indentación de por
   medio) seguía empujando las acciones fuera de pantalla. */
.pyx-tree-body .mud-treeview-item,
.pyx-tree-body .mud-treeview-item-content,
.pyx-tree-body .mud-treeview-item-label {
    min-width: 0;
}

/* Bug 22578 (sexto retrabajo, causa raíz real): se intentó neutralizar overflow:hidden en cada
   capa que MudBlazor interpone entre la fila y .pyx-tree-body (.mud-treeview,
   .mud-treeview-item-content, .mud-collapse-wrapper, .mud-collapse-container) para que
   position:sticky alcanzara al verdadero contenedor con scroll. Descartado: esas mismas capas
   -- en particular .mud-collapse-wrapper/-container -- usan ESE overflow:hidden para ocultar
   nodos colapsados (altura animada a 0px + overflow:hidden = invisible; con overflow:visible el
   contenido de ramas colapsadas se dibuja igual y se monta encima de las filas sí visibles,
   verificado en vivo). position:sticky no puede atravesar esas capas sin romper el colapsado, así
   que .node-row-actions dejó de depender de sticky -- ahora se reposiciona con JavaScript
   (wwwroot/js/estructura-dragdrop.js, pyxFijarAccionesFila) via transform en cada scroll de
   .pyx-tree-body, que no necesita que ningún ancestro coopere con overflow. */

.pyx-tree-drop {
    display: flex;
    align-items: center;
    justify-content: center;
    margin: 0 14px 14px;
    padding: 12px;
    border: 2px dashed var(--pyx-border);
    border-radius: 10px;
    color: var(--pyx-ink-faint);
}

/* AB#23127: arrastre del organigrama. Global (no en EstructuraArbolItem.razor.css) porque las
   clases las pone estructura-dragdrop.js en tiempo de arrastre y la ficha cuelga de <body>, fuera
   del alcance del scope de Blazor.
   - .pyx-drop-hover: sólo el destino bajo el cursor (antes se resaltaban todas las filas a la vez).
   - .node-row-arrastrando: la fila de origen queda atenuada mientras se mueve.
   - .pyx-drag-ficha: la imagen que sigue al cursor -- sólo el nodo tomado, no la lista. */
.node-row.pyx-drop-hover,
.pyx-tree-drop.pyx-drop-hover {
    background-color: var(--mud-palette-primary-hover);
    outline: 2px dashed var(--mud-palette-primary);
    outline-offset: -2px;
    border-radius: 4px;
}

.pyx-tree-drop.pyx-drop-hover {
    border-color: transparent;
    border-radius: 10px;
    color: var(--mud-palette-primary);
}

.node-row-arrastrando {
    opacity: .45;
}

.pyx-drag-ficha {
    position: fixed;
    top: -1000px;
    left: -1000px;
    display: inline-flex;
    max-width: 360px;
    padding: 6px 12px;
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    border: 1px solid var(--mud-palette-primary);
    border-radius: 6px;
    pointer-events: none;
}

/* ===== Carga de Redes — grilla Persona × Rol =====
   De "Identidad Pyxoom360" (mudblazor/Pages/Redes.razor). Global: cuelga de MudTable. */

/* La tabla ya vive dentro de .pyx-card, que pone el borde: aquí sólo el scroll horizontal. */
.pyx-net-table {
    background: transparent;
}

.pyx-net-table .mud-table-container {
    overflow-x: auto;
}

/* Ancho que el diseño reserva para Persona + Auto + los cuatro círculos. Sólo por encima de sm:
   por debajo, MudTable ya colapsó a vista de tarjeta (Breakpoint.Sm). */
@media (min-width: 600px) {
    .pyx-net-table table {
        min-width: 1160px;
    }
}

/* La tarjeta recorta la barra y el pie contra sus esquinas redondeadas. */
.pyx-net {
    overflow: hidden;
    padding: 0;
}

.pyx-net-table .mud-table-head .mud-table-cell {
    background: var(--pyx-canvas-soft);
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-3xs);
    font-weight: 600;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--pyx-ink-faint) !important;
    padding: 8px;
    white-space: nowrap;
}

/* Las celdas crecen con el número de evaluadores: alineación arriba, alto libre. */
.pyx-net-table .mud-table-body .mud-table-cell {
    vertical-align: top;
    border-bottom: 1px solid var(--pyx-divider);
}

.pyx-net-cell {
    min-width: 190px;
    padding: 8px !important;
}

/* Barra de la tarjeta: título, contadores mono y el filtro por persona. */
.pyx-net-bar {
    height: auto;
    padding: 8px 16px;
    gap: 10px;
    flex-wrap: wrap;
    border-bottom: 1px solid var(--pyx-divider);
}

.pyx-net-title { font-weight: 600; }

.pyx-net-search { width: 240px; flex: none; }

/* UX 22624 (Personas): buscador + Exportar/Importar/Nueva Persona como un solo grupo que no
   se parte. El buscador cede ancho (hasta 160px) antes de que el grupo baje de línea entero. */
.pyx-net-actions {
    display: flex;
    align-items: center;
    flex-wrap: nowrap;
    gap: 8px;
    margin-left: auto;
    min-width: 0;
}

.pyx-net-actions .pyx-net-search {
    width: 240px;
    min-width: 160px;
    flex: 1 1 160px;
}

.pyx-net-actions .mud-button-root { white-space: nowrap; flex: none; }

/* En mobile, ni encogiendo el buscador a 160px caben los 3 botones al lado (Exportar +
   Importar Excel + Nueva Persona en Personas.razor) -- el flex-wrap:nowrap de arriba, pensado
   para tablet/desktop (ahí "el grupo baja de línea entero" alcanza), fuerza igual a todo el
   grupo a una sola línea que ya no cabe en un viewport angosto y se corta contra el borde.
   Bajo el breakpoint mobile se permite envolver: el buscador pasa a su propia fila de ancho
   completo y los botones se acomodan en las filas siguientes. */
@media (max-width: 599px) {
    .pyx-net-search {
        width: 100%;
    }

    .pyx-net-actions {
        flex-wrap: wrap;
    }

    .pyx-net-actions .pyx-net-search {
        flex: 1 1 100%;
        width: 100%;
    }
}

/* Antes había una segunda definición de .pyx-chip-mono más arriba (junto a .pyx-node), idéntica
   en los primeros 4 props y sin las de layout -- se fusionaron en esta, la única que queda,
   agregando height:20px de la que se quitó (ningún cambio visual: la cascada ya combinaba ambas). */
.pyx-chip-mono {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 2px 8px;
    border-radius: 999px;
    background: var(--pyx-canvas-soft);
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-2xs);
    font-weight: 600;
    letter-spacing: .05em;
    color: var(--pyx-ink-faint);
    white-space: nowrap;
    height: 20px;
}

.pyx-chip-mono--error {
    background: color-mix(in srgb, var(--mud-palette-error) 12%, transparent);
    color: var(--mud-palette-error);
}

.pyx-chip-mono--error .mud-icon-root { font-size: 12px; }

.pyx-net-person { width: 230px; padding: 10px 12px !important; }
.pyx-net-auto { width: 76px; }
.pyx-net-acc { width: 96px; text-align: center; }

.pyx-net-name {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    font-weight: 500;
}

.pyx-net-meta {
    display: block;
    margin-top: 3px;
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-xs);
    color: var(--pyx-ink-faint);
}

/* Doble clase: MudButton trae su propio font-size y gana por especificidad si sólo se usa .pyx-*. */
.pyx-net-toggle.mud-button-root,
span.pyx-net-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: 24px;
    min-width: 34px;
    padding: 0 11px;
    border-radius: 999px;
    font-size: 10.5px;
    font-weight: 700;
    text-transform: none;
}

/* Autoevaluación ya respondida: no se puede quitar, pero tiene que seguir leyéndose.
   Deshabilitar el MudButton la dejaba en gris claro sobre gris — texto invisible. */
span.pyx-net-toggle--fija {
    background: color-mix(in srgb, var(--mud-palette-success) 16%, transparent);
    color: var(--mud-palette-success);
    cursor: help;
}

/* Buscador de la celda: 24px de alto, para que la lista de asignados domine la celda. Nombre
   propio -- .pyx-net-cell ya lo usa el <MudTd> que lo envuelve (min-width/padding), y compartir
   el nombre entre padre e hijo hacía que las reglas de abajo (margin/height) también le
   aplicaran al <td>, sin que nadie lo hubiera notado porque el resultado visual coincidía. */
.pyx-net-cell-search { margin: 0; }

.pyx-net-cell-search .mud-input.mud-input-outlined {
    height: 24px;
    background: var(--pyx-canvas-soft);
}

.pyx-net-cell-search .mud-input-slot { padding: 0 7px; font-size: 11px; }
.pyx-net-cell-search input { height: 24px; }

/* Tres niveles de selector, no uno: la regla propia de MudBlazor para el borde del campo
   outlined gana con .pyx-net-cell-search a secas. */
.pyx-net-cell-search .mud-input.mud-input-outlined .mud-input-outlined-border {
    border-color: var(--pyx-divider);
}

.pyx-net-items {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: 5px;
}

.pyx-net-item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 4px;
    min-width: 0;
}

.pyx-net-item > span {
    overflow: hidden;
    font-size: 10.5px;
    line-height: 1.35;
    color: var(--pyx-ink-muted);
    text-overflow: ellipsis;
    white-space: nowrap;
}

.pyx-net-item .mud-icon-button { padding: 2px; }
.pyx-net-item-lock { font-size: 13px; color: var(--pyx-ink-faint); }

/* Punto 93 (corrección 19/08): lista de personas por vincular en el diálogo "Un grupo" -- antes
   sólo se veía el conteo. Alto acotado con scroll propio para no alargar un diálogo ya de por sí
   apretado (MaxWidth.Small) cuando el filtro matchea decenas de personas. */
.pyx-net-lista-vincular {
    max-height: 180px;
    overflow-y: auto;
    border: 1px solid var(--pyx-border);
    border-radius: 6px;
    padding: 6px 10px;
}

/* Fila de Virtualize con ItemSize fijo (Asignar.razor) -- el navegador posiciona cada fila a un
   múltiplo exacto de ItemSize, así que el contenido NO puede decidir la altura o las filas
   siguientes se pisarían visualmente. AB#22728 subió el renglón de una línea (ItemSize=27) a dos
   (ItemSize=40, height fijo + overflow:hidden aquí abajo) para sumar Nivel/Perfil sin romper esa
   invariante -- una persona sin Puesto igual mide 40px, sólo con la segunda línea vacía. */
.pyx-net-lista-vincular-item {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 2px;
    height: 40px;
    padding: 3px 0;
    min-width: 0;
    overflow: hidden;
}

.pyx-net-lista-vincular-fila-principal {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    min-width: 0;
}

.pyx-net-lista-vincular-nombre {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

/* AB#22728: Nivel/Perfil actuales de la persona -- para que se entienda qué va a quedar si el
   admin no elige nada en los combos de arriba (se conserva) o qué va a pisar si sí eligió
   (Ruling D-22728.2, "el elegido si se eligió; si no, el del catálogo"). */
.pyx-net-lista-vincular-nivel-perfil {
    flex: 0 0 auto;
    max-width: 45%;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    text-align: right;
}

.pyx-net-lista-vincular-puesto {
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
}

.pyx-net-lista-vincular-item + .pyx-net-lista-vincular-item {
    border-top: 1px solid var(--pyx-border);
}

/* El motivo del error va bajo el nombre, no en un tooltip: se corrigen varios de un vistazo. */
.pyx-net-issue {
    display: block;
    margin-top: 4px;
    font-size: 10.5px;
    font-weight: 500;
    line-height: 1.3;
    color: var(--mud-palette-error);
}

.pyx-net-row--locked td { background: var(--pyx-canvas-soft); }
.pyx-net-row--error td { background: color-mix(in srgb, var(--mud-palette-error) 7%, transparent); }

/* Mensaje compacto en línea (MudText suelto), no la tarjeta completa de .pyx-empty --
   nombre propio para no pisar en cascada el padding/estilo del estado vacío global. */
.pyx-net-empty { padding: 34px; color: var(--pyx-ink-faint); text-align: center; display: block; }

/* (phin-clear-btn eliminado — se usa flex con MudIconButton al lado del autocomplete) */

/* ── Editor de cuestionario (Pages/Catalogos/CuestionarioEditor.razor) ───────────────────────
   Corrección de estilo (2026-08-04), punto 35: "editar para que el texto/nombre de la
   competencia no esté replegado al marco/contorno". La píldora traía las clases de Bootstrap
   (p-2 border rounded, justify-content-between) y esta app no carga Bootstrap -- sólo MudBlazor
   y esta hoja -- así que ni el padding ni el borde ni la alineación se aplicaban nunca. */
/* UX 22634: sustituye a fw-bold de Bootstrap, que no se carga en esta app. */
.qe-bold { font-weight: 600; }

.qe-comp {
    margin: 6px 0;
    padding: 8px 12px;
    border: 1px solid var(--pyx-border);
    border-radius: 8px;
    background: var(--mud-palette-surface);
}

.qe-comp--usada {
    opacity: .55;
}

.qe-comp-fila {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.qe-comp-nombre {
    flex: 1 1 auto;
    min-width: 0;
    font-size: 13px;
    line-height: 1.35;
    /* Los nombres largos se envuelven en vez de desbordar la píldora. */
    overflow-wrap: anywhere;
}

/* Punto 84: círculo de evaluadores desplegado bajo el nombre del evaluado, en Avance del Evento.
   Va aquí y no en un .razor.css porque cuelga de una celda de MudTable, y el scope de Blazor no
   llega a lo que MudBlazor dibuja por su cuenta (ver la nota de aislamiento CSS en CLAUDE.md). */
.pyx-circulo-detalle {
    margin: 8px 0 12px 34px;
    padding: 10px 12px;
    border-left: 2px solid var(--mud-palette-primary);
    background: var(--mud-palette-background-grey);
    border-radius: 0 6px 6px 0;
}

/* Encabezado y filas comparten la MISMA retícula, que es lo que mantiene las columnas alineadas.
   Antes eran flex con anchos sueltos y cada fila caía donde le tocaba. La columna de acciones va
   al final y con ancho fijo para que los botones queden en la misma vertical. */
.pyx-circulo-tabla {
    display: flex;
    flex-direction: column;
}

.pyx-circulo-encabezado,
.pyx-circulo-fila {
    display: grid;
    grid-template-columns: minmax(160px, 1.6fr) minmax(120px, 1fr) 130px 96px;
    align-items: center;
    gap: 12px;
}

/* Las 4 columnas suman un mínimo duro de ~542px (160+120+130+96+3*12 de gap) sin encoger --
   no cabe en ningún celular. En mobile se apila cada celda en su propia fila; el encabezado
   (Evaluador/Rol/Evaluación/Acciones) se oculta porque apilado no queda alineado con nada, y
   cada acción ya tiene tooltip/aria-label propio que la identifica sin la columna. */
@media (max-width: 599px) {
    .pyx-circulo-fila {
        grid-template-columns: 1fr;
        gap: 4px;
    }

    .pyx-circulo-encabezado {
        display: none;
    }
}

.pyx-circulo-encabezado {
    padding: 0 0 6px;
    border-bottom: 1px solid var(--mud-palette-lines-default);
    font-size: 11px;
    font-weight: 600;
    letter-spacing: .03em;
    text-transform: uppercase;
    color: var(--mud-palette-text-secondary);
}

.pyx-circulo-fila {
    padding: 4px 0;
    font-size: 13px;
    border-bottom: 1px solid var(--mud-palette-lines-default);
}

    .pyx-circulo-fila:last-of-type {
        border-bottom: none;
    }

.pyx-circulo-nombre {
    font-weight: 500;
    color: var(--mud-palette-text-primary);
}

.pyx-circulo-rol {
    color: var(--mud-palette-text-secondary);
}

.pyx-circulo-vacio {
    padding: 10px 0;
    font-size: 13px;
    color: var(--mud-palette-text-secondary);
}

/* Los botones se alinean a la derecha, bajo su propio encabezado. */
.pyx-circulo-acciones-col {
    text-align: right;
}

.pyx-circulo-acciones {
    display: flex;
    justify-content: flex-end;
    gap: 2px;
}

/* La celda que aloja el círculo desplegado: sin relleno propio ni bordes, porque el marco ya lo
   pone .pyx-circulo-detalle y de lo contrario se ven dos recuadros encajados. */
.pyx-circulo-celda {
    padding: 0 !important;
    background: transparent;
}

/* El rol es editable mientras no haya respuestas: el selector va sin marco para que la celda siga
   leyéndose como una tabla y no como un formulario. Cuando no se puede cambiar, texto plano. */
.pyx-circulo-rol .mud-input-control {
    margin: 0;
}

.pyx-circulo-rol-fijo {
    color: var(--mud-palette-text-secondary);
}

/* Avance por evaluador. Al quitarse la etiqueta "Contestada" este es el contenido de la columna,
   asi que sube de tamano; el verde marca el 100 % sin necesidad de repetirlo con palabras. */
.pyx-circulo-avance {
    font-size: 12.5px;
    font-variant-numeric: tabular-nums;
    color: var(--mud-palette-text-secondary);
    white-space: nowrap;
}

.pyx-circulo-avance--completa {
    color: var(--mud-palette-success);
    font-weight: 500;
}

/* Corrección de estilo (QA visual, Fase F): diálogo "Perfilar" (Editar Evento → Perfiles del
   evento). Antes era un d-flex suelto por fila -- sin encabezados, el campo editable no quedaba
   alineado entre filas con nombres de competencia de distinto largo.
   Tres columnas: nombre / campo editable / acciones. La cuarta ("Catálogo sugiere") se retiró
   porque el valor de Catálogos es global y no conoce la escala del evento -- ver el comentario
   en Editar.razor, sección del diálogo Perfilar. */
.pyx-perfilar-grid {
    display: grid;
    grid-template-columns: 1fr 180px auto;
    column-gap: 12px;
    row-gap: 6px;
    align-items: center;
}

/* AB#23191: motivo del rechazo del servidor bajo la fila de la competencia; ocupa las 3 columnas
   del grid para no desacomodar nombre/campo/acciones de las filas siguientes. */
.pyx-perfilar-grid-error {
    grid-column: 1 / -1;
    margin-top: -2px;
}

/* Bug 22682: variante de 2 columnas para el alta (Pages/Eventos/Crear.razor) -- ahí no hay
   botones Guardar/Borrar por fila (el valor se guarda junto con todo el formulario), así que
   reusar .pyx-perfilar-grid (3 columnas) dejaba nombre y campo desalineados entre competencias
   consecutivas. */
.pyx-perfilar-grid-alta {
    display: grid;
    grid-template-columns: 1fr 180px;
    column-gap: 12px;
    row-gap: 6px;
    align-items: center;
}

.pyx-perfilar-grid-header {
    font-family: var(--pyx-label);
    font-size: var(--pyx-font-2xs);
    font-weight: 600;
    letter-spacing: .08em;
    text-transform: uppercase;
    color: var(--pyx-ink-faint);
    padding-bottom: 6px;
    border-bottom: 1px solid var(--pyx-divider);
}

/* ── Editor visual de plantillas de correo (Components/EditorCorreoHtml.razor) ───────────────
   Quill dibuja su propio DOM (barra + área editable), así que todo va en la hoja global; el
   scope de Blazor nunca lo alcanzaría. Colores sobre tokens --mud-palette-* para que el modo
   oscuro llegue solo (MudThemeProvider cambia los valores, no marca el DOM). */

.pyx-editor-correo .ql-toolbar.ql-snow,
.pyx-editor-correo .ql-container.ql-snow {
    border-color: var(--mud-palette-lines-default);
}

.pyx-editor-correo .ql-toolbar.ql-snow {
    border-radius: 8px 8px 0 0;
    background: var(--pyx-canvas-soft);
}

.pyx-editor-correo .ql-container.ql-snow {
    border-radius: 0 0 8px 8px;
    font-family: inherit;
    font-size: 13px;
}

.pyx-editor-correo .ql-editor {
    min-height: 220px;
    color: var(--mud-palette-text-primary);
}

.pyx-editor-correo .ql-editor.ql-blank::before {
    color: var(--pyx-ink-faint);
    font-style: normal;
}

/* Iconos y desplegables de la barra: Quill los pinta con grises fijos que desaparecen en
   modo oscuro. */
.pyx-editor-correo .ql-snow .ql-stroke,
.pyx-editor-correo .ql-snow .ql-stroke-miter {
    stroke: var(--mud-palette-text-primary);
}

.pyx-editor-correo .ql-snow .ql-fill {
    fill: var(--mud-palette-text-primary);
}

.pyx-editor-correo .ql-snow .ql-picker {
    color: var(--mud-palette-text-primary);
}

.pyx-editor-correo .ql-snow .ql-picker-options {
    background: var(--mud-palette-surface);
    border-color: var(--mud-palette-lines-default);
}

/* El globo de enlaces trae sus textos en inglés como content de CSS: se reescriben aquí. */
.pyx-editor-correo .ql-snow .ql-tooltip {
    background: var(--mud-palette-surface);
    border-color: var(--mud-palette-lines-default);
    color: var(--mud-palette-text-primary);
    box-shadow: none;
    z-index: 10;
}

.pyx-editor-correo .ql-snow .ql-tooltip::before {
    content: "Enlace:";
}

.pyx-editor-correo .ql-snow .ql-tooltip[data-mode="link"]::before {
    content: "Dirección del enlace:";
}

.pyx-editor-correo .ql-snow .ql-tooltip a.ql-action::after {
    content: "Editar";
}

.pyx-editor-correo .ql-snow .ql-tooltip.ql-editing a.ql-action::after {
    content: "Guardar";
}

.pyx-editor-correo .ql-snow .ql-tooltip a.ql-remove::before {
    content: "Quitar";
}

/* UX 22789: opción de MudSelect con ícono + texto (Rol en Crear usuario / Asignar rol). En el
   popover abierto el item mide lo que quiera, pero el valor CERRADO se pinta dentro de
   div.mud-input-slot, cuyo contenido mide 16px de alto (line-height) con overflow: hidden: un
   MudIcon de 24px con su texto se recortaba por abajo -- "el texto se ve demasiado abajo / un poco
   ocultado". Dentro del slot el ícono baja a 16px y el renglón se fija a esa altura; en la lista
   conserva su tamaño. Vive aquí y no en el .razor.css del diálogo porque el slot lo renderiza
   MudBlazor (ver "Blazor CSS isolation does not reach MudBlazor roots" en CLAUDE.md). */
.pyx-opcion-icono {
    /* flex y no inline-flex: como caja en línea, vertical-align la bajaba 2px respecto al
       renglón de 16px del slot y ese borde inferior seguía recortado (medido en local). */
    display: flex;
    align-items: center;
    gap: 8px;
    max-width: 100%;
}
.mud-input-slot .pyx-opcion-icono {
    height: 16px;
    line-height: 16px;
}
.mud-input-slot .pyx-opcion-icono .mud-icon-root {
    font-size: 16px;
}


/* Bug 22998: lista de errores de la importación de cuestionarios dentro del diálogo (antes iban
   concatenados al snackbar, que los truncaba). */
.pyx-lista-errores {
    margin: 6px 0 0;
    padding-left: 18px;
    /* Con muchos errores la lista se desplaza dentro de la alerta y los botones del diálogo
       siguen a la vista. */
    max-height: 220px;
    overflow-y: auto;
}

.pyx-lista-errores li {
    margin: 2px 0;
    line-height: 1.35;
}

/* GraficaComparativa (bugs 22909 / 22912): leyenda arriba, svg con scroll horizontal y burbuja de
   detalle. Global y no scoped: el svg llega como MarkupString y no lleva el atributo de ámbito. */
:root {
    --pyx-grafica-ink: var(--mud-palette-text-secondary);
}
.pyx-grafica__leyenda {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 18px;
    justify-content: center;
    margin-bottom: 10px;
    font-size: 12.5px;
    color: var(--mud-palette-text-primary);
}
.pyx-grafica__serie {
    display: inline-flex;
    align-items: center;
    gap: 7px;
}
.pyx-grafica__serie i,
.pyx-grafica__tip b i {
    display: inline-block;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    flex: none;
}
.pyx-grafica__scroll {
    position: relative;
    overflow-x: auto;
    overflow-y: hidden;
    padding-bottom: 6px;
}
.pyx-grafica__scroll svg {
    max-width: none;
}
.pyx-grafica__barra,
.pyx-grafica__punto {
    cursor: pointer;
}
.pyx-grafica__barra:hover {
    filter: brightness(0.85);
}
.pyx-grafica__tip {
    position: absolute;
    z-index: 5;
    pointer-events: none;
    min-width: 150px;
    max-width: 260px;
    padding: 8px 12px;
    border-radius: 8px;
    background: var(--mud-palette-surface);
    color: var(--mud-palette-text-primary);
    border: 1px solid var(--mud-palette-lines-default);
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.14);
    font-size: 12.5px;
    line-height: 1.35;
    display: flex;
    flex-direction: column;
    gap: 2px;
}
.pyx-grafica__tip[hidden] {
    display: none;
}
.pyx-grafica__tip b {
    display: inline-flex;
    align-items: center;
    gap: 7px;
}

/* Personas: administradores (FAD/FLI) con cuenta pero sin persona -- requerimiento 2026-09-30. */
.pyx-admins-pendientes {
    border: 1px solid var(--mud-palette-lines-default);
    border-radius: 8px;
    padding: 12px 16px;
    margin-bottom: var(--pyx-gap-block, 16px);
    background-color: var(--mud-palette-background-gray);
}
.pyx-admins-pendientes__tabla { background-color: transparent; }

/* AB#23154: encabezado centrado con MudTableSortLabel. El icono de orden ocupaba ancho en el flujo
   y corría el texto ~12px a la izquierda del centro real de la columna; se posiciona absoluto para
   que el texto quede centrado. Va aquí y no en .razor.css porque el aislamiento de CSS no llega a la
   raíz de los componentes MudBlazor. */
.pyx-th-centro .mud-table-sort-label { position: relative; }
.pyx-th-centro .mud-table-sort-label-icon { position: absolute; right: -1.4em; }

/* AB#23184 (Interactive, pregunta abierta): QA pidió la etiqueta más pequeña y la pregunta más
   grande. Va en el CSS global porque el aislamiento de Blazor no llega a la raíz de MudText; el
   doble selector (.mud-typography.x) gana a .mud-typography-body2/-caption sin !important.
   Colores derivados de --pyx-ink*, que siguen a --mud-palette-* y por eso funcionan en oscuro. */
.mud-typography.pyx-int-pregunta-label {
    display: block;
    font-size: 11.5px;
    font-weight: 600;
    letter-spacing: .04em;
    line-height: 1.3;
    color: var(--pyx-ink-muted);
}

.mud-typography.pyx-int-pregunta-texto {
    font-size: 15px;
    font-weight: 600;
    line-height: 1.4;
    color: var(--pyx-ink);
}


/* AB#23178: "Mis planes de acción" en Interactive > Tareas. Tarjetas apiladas a todo el ancho:
   desde que cada plan lleva sus tareas dentro, una rejilla de 280px dejaba la barra de avance
   reducida a un punto. Las reglas viven aquí porque apuntan a la raíz de MudPaper, a la que el
   aislamiento de CSS de Blazor no llega. */
.pyx-mis-planes {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--pyx-gap-block);
}
.pyx-mis-plan {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: var(--pyx-pad-card);
    /* AB#23178: borde y radio vienen de .pyx-card (la tarjeta lleva ambas clases). */
    min-width: 0;
}
.pyx-mis-plan__cabecera {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: 8px;
}
.pyx-mis-plan__titulo {
    display: flex;
    flex-direction: column;
    min-width: 0;
    overflow-wrap: anywhere;
}
.pyx-mis-plan__avance {
    display: flex;
    flex-direction: column;
    gap: 4px;
}
.pyx-mis-plan > .mud-button-root {
    align-self: flex-start;
}

/* AB#23178 (seguimiento): las tareas pendientes viven dentro de la tarjeta de su plan. Reglas
   globales porque apuntan a hijos de MudPaper/MudChip (el aislamiento de CSS no llega ahí).
   El editor de avance usa columnas fijas: teclear no cambia el ancho de la fila. */
.pyx-mis-plan__tareas {
    display: flex;
    flex-direction: column;
    min-width: 0;
}
.pyx-tarea-fila {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 10px 0;
    border-top: 1px solid var(--mud-palette-lines-default);
    min-width: 0;
    overflow-wrap: anywhere;
}
.pyx-tarea-fila__meta {
    display: flex;
    align-items: center;
    gap: 12px;
}
/* La competencia toma el espacio libre (y se parte en varias líneas si no cabe); prioridad y
   vencimiento quedan pegados al final, alineados entre tareas en escritorio y móvil. */
.pyx-tarea-fila__competencia {
    flex: 1 1 auto;
    min-width: 0;
    overflow-wrap: anywhere;
}
.pyx-tarea-fila__fin {
    display: flex;
    align-items: center;
    gap: 6px;
    flex: 0 0 auto;
    margin-left: auto;
}
.pyx-tarea-fila__sep {
    color: var(--mud-palette-text-secondary);
}
.pyx-tarea-avance {
    display: grid;
    /* La barra ocupa todo el ancho que deja el editor; el campo es angosto a propósito (el avance va
       de 0 a 100, tres cifras como máximo) y de ancho fijo: teclear no cambia el ancho de la fila. */
    grid-template-columns: minmax(0, 1fr) auto;
    align-items: center;
    column-gap: 16px;
    row-gap: 6px;
}
.pyx-tarea-avance__barra {
    display: flex;
    align-items: center;
    gap: 8px;
    min-width: 0;
}
.pyx-tarea-avance__barra .mud-progress-linear {
    flex: 1 1 auto;
    min-width: 0;
}
.pyx-tarea-avance__editor {
    display: flex;
    align-items: center;
    gap: 6px;
}
.pyx-tarea-avance__campo {
    flex: 0 0 72px;
    width: 72px;
}
.pyx-tarea-avance__pct {
    color: var(--mud-palette-text-secondary);
}
/* AB#23178: el texto de error propio del campo se partía letra por letra; se oculta y el aviso va en
   .pyx-tarea-avance__error, bajo el editor. */
.pyx-tarea-avance__campo .mud-input-helper-text,
.pyx-tarea-avance__campo .mud-input-control-helper-container {
    display: none;
}
.pyx-tarea-avance__error {
    grid-column: 2;
    font-size: 12px;
    color: var(--mud-palette-error);
}
@media (max-width: 599px) {
    /* Campo y botón bajan a su propia línea bajo la barra; el botón se estira hasta el ancho del
       contenedor y el campo sigue angosto. */
    .pyx-tarea-avance {
        grid-template-columns: minmax(0, 1fr);
    }
    .pyx-tarea-avance__editor > .mud-button-root {
        flex: 1 1 auto;
    }
    .pyx-tarea-avance__error {
        grid-column: 1;
    }
}

/* AB#23178: jerarquia de texto de Interactive. Typo.h6 mide lo mismo que el cuerpo (13.5px), asi que
   los titulos usan estas clases en vez de Typo.
   Decision de fondo: estas clases se aplican sobre MudText junto con .mud-typography-body1, y la hoja de
   MudBlazor carga DESPUES de app.css; con un solo selector de clase gana la de MudBlazor (13.5px/400).
   Se sube la especificidad con .mud-typography.<clase> (sin !important) y se conserva el selector
   simple para los <p>/<div> que no son MudText. */
.mud-typography.pyx-int-grupo,
.pyx-int-grupo {
    font-size: 15px;
    font-weight: 600;
    color: var(--mud-palette-text-primary);
    margin: 0;
}

.mud-typography.pyx-int-seccion,
.pyx-int-seccion {
    font-size: 17px;
    font-weight: 600;
    color: var(--mud-palette-text-primary);
    margin: 0;
}

.mud-typography.pyx-q-texto,
.pyx-q-texto {
    font-size: 15px;
    font-weight: 500;
    line-height: 1.4;
}

/* AB#23178: fila de "Mi equipo". Datos a la izquierda; "Evaluar ahora" + chevron agrupados a la
   derecha. Bajo 600px las acciones pasan debajo del nombre, sin desbordar. */
.pyx-int-persona-fila { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.pyx-int-persona-fila__datos { display: flex; align-items: center; flex: 1 1 auto; min-width: 0; }
.pyx-int-persona-fila__acciones { display: flex; align-items: center; justify-content: flex-end; gap: 4px; flex: 0 0 auto; }
@media (max-width: 599px) {
    .pyx-int-persona-fila { flex-wrap: wrap; }
    .pyx-int-persona-fila__datos { flex: 1 1 100%; }
    .pyx-int-persona-fila__acciones { flex: 1 1 100%; }
}

/* AB#23272: competencia del cuestionario con preguntas/elementos clave faltantes. */
.qe-card--problema { border: 1px solid var(--mud-palette-error); }

/* AB#23304 (pasada visual): Carga de redes en pantalla chica (tarjetas por persona). Los anchos fijos de escritorio
   (230/76/96/190 px) apretaban el nombre y mandaban a las personas elegidas al lado del buscador, y las celdas no decían a
   qué rol correspondían. Aquí cada celda ocupa todo el ancho y muestra el nombre del rol (data-label) arriba. */
@media (max-width: 959px) {
    .pyx-net-table .pyx-net-person,
    .pyx-net-table .pyx-net-auto,
    .pyx-net-table .pyx-net-acc { width: auto !important; }
    .pyx-net-table .pyx-net-cell { min-width: 0 !important; }
    .pyx-net-table .mud-table-cell { display: block !important; width: 100% !important; text-align: left !important; }
    .pyx-net-table .mud-table-cell[data-label]::before {
        display: block;
        font-weight: 600;
        font-size: var(--pyx-font-sm);
        margin-bottom: 4px;
        color: var(--pyx-ink-muted);
    }
}

/* AB#23304: en pantalla chica MudBlazor agrega arriba de la lista un selector de orden. En las tablas sin columnas
   ordenables sale vacío (sin etiqueta ni opciones) y confunde; sólo se muestra si trae etiqueta. */
.mud-table-smalldevices-sortselect:not(:has(.mud-input-label)) { display: none; }

/* AB#23304: la tabla "Eventos Activos" de Inicio quedaba cortada a la derecha (se perdía la columna Avance) porque cada
   celda mantenía el relleno amplio de MudBlazor; con relleno compacto las seis columnas caben en la tarjeta. */
@media (min-width: 960px) {
    .pyx-home-eventos .mud-table-cell { padding-left: 8px; padding-right: 8px; }
}

/* AB#23304: entre 960 y 1500 px la tarjeta de eventos no alcanza para las seis columnas; se omite "Fin" (la fecha de cierre
   se ve en Eventos) para que Estado, pendientes y Avance queden completos. */
@media (min-width: 960px) and (max-width: 1500px) {
    .pyx-home-eventos th:nth-child(3),
    .pyx-home-eventos td:nth-child(3) { display: none; }
}
