/* =========================================================
   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;
    --pyx-blue-soft: #e6eefb;
    /* 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;
    --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);
    --pyx-ink-faint: var(--mud-palette-text-disabled);
    --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: 20px 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-gap-block: 12px;
        --pyx-gutter-top: 16px;
    }
}

body {
    -webkit-font-smoothing: antialiased;
    text-wrap: pretty;
}

/* Rótulo monoespaciado en versalitas que encabeza cada bloque de la plataforma. */
.pyx-eyebrow {
    font-family: var(--pyx-label);
    font-size: 9.5px;
    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;
}

/* 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. */
    .pyx-page:has(.pyx-bottomnav) {
        padding-bottom: calc(56px + env(safe-area-inset-bottom) + 12px);
    }
}

.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: 9.5px;
    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;
}

/* 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;
    gap: 8px;
    margin-bottom: 8px;
    font-size: 12.5px;
    font-weight: 600;
    color: var(--mud-palette-text-secondary);
}

/* 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;
}

@media (max-width: 599px) {
    .pyx-acciones-footer {
        flex-direction: column;
        align-items: stretch !important;
        gap: 10px;
    }

    .pyx-acciones-footer-grupo {
        flex-wrap: wrap;
        justify-content: flex-end;
    }
}

/* "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: 9px;
    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: 11.5px;
    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;
}

/* =========================================================
   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;
}

.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;
}

/* =========================================================
   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
   ========================================================= */
.mud-button-filled.mud-button-filled-primary {
    background-color: var(--mud-palette-primary) !important;
    color: white !important;
}

.mud-button-filled.mud-button-filled-primary:hover {
    background-color: var(--mud-palette-primary-darken) !important;
    color: white !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: white !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;
}

.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: 9.5px;
        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: 9.5px;
    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: 9.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: 10px;
    color: var(--pyx-ink-faint);
}

.pyx-axis-x-title {
    font-size: 9.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: 11.5px;
    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: 9.5px;
    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: 9px;
        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: 9.5px;
    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: 11px;
    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-head {
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    gap: 24px;
    flex-wrap: wrap;
}

.pyx-title {
    margin-top: 6px;
}

.pyx-lede {
    color: var(--pyx-ink-muted);
    margin-top: 7px;
    max-width: 620px;
}

.pyx-filters {
    padding: var(--pyx-pad-card);
    display: flex;
    align-items: flex-end;
    gap: var(--pyx-gap-toolbar);
    flex-wrap: wrap;
}

.pyx-filter {
    flex: 1;
    min-width: 172px;
}

.pyx-filter--wide {
    min-width: 236px;
}

/* Bug 22753: los SELECTS de filtro se movían de sitio y cambiaban de tamaño al elegir un valor.
   Con la clase a secas MudBlazor gana (.mud-input-control { flex: 1 1 auto } se carga después de
   este archivo con la misma especificidad), así que cada select crecía para repartirse el ancho
   sobrante y la X de Clearable o un texto más largo reacomodaba a los demás. Ancho fijo sólo para
   los selects (los campos de texto y casillas siguen rellenando la fila como antes): encogen si el
   contenedor es más angosto, nunca crecen para rellenar. */
.pyx-filters .mud-select.pyx-filter {
    flex: 0 1 220px;
    max-width: 220px;
}

.pyx-filters .mud-select.pyx-filter--wide {
    flex-basis: 300px;
    max-width: 300px;
}

.pyx-filter-actions {
    display: flex;
    align-items: center;
    gap: 12px;
    flex: none;
}

/* 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);
    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. */
.pyx-filters--interior {
    padding: 0 var(--pyx-pad-card) var(--pyx-pad-card);
    border-top: 1px solid var(--pyx-divider);
    padding-top: var(--pyx-pad-card);
}

.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;
}

.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;
    }

        .pyx-kpi b em {
            font-family: var(--pyx-label);
            font-size: 14px;
            font-style: normal;
            font-weight: 500;
            color: var(--pyx-ink-faint);
            margin-left: 5px;
        }

    .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: 9px;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: var(--pyx-ink-faint);
}

.pyx-num {
    font-family: var(--pyx-label);
    font-size: 11.5px;
    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: 10px;
    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: 10px;
}

.pyx-escala-ayuda {
    margin-left: auto;
    font-family: var(--pyx-label);
    font-size: 10.5px;
    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;
    grid-template-columns: repeat(auto-fit, minmax(360px, 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: 9.5px;
    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: 9px;
        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: 11.5px;
        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;
}

.pyx-chip-mono {
    font-family: var(--pyx-label);
    font-size: 9.5px;
    font-weight: 600;
    letter-spacing: .05em;
    height: 20px;
}

/* ===== 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: 9.5px;
    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: 10.5px;
    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);
}

/* 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-child(9) .pyx-reg-pass { padding-left: 0; margin-left: 0; }
.pyx-reg-table .mud-table-body td:nth-child(10) .mud-chip { margin-left: 0; }
.pyx-reg-table .mud-table-body td:nth-child(11) > * { 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. */
@media (min-width: 600px) {
    /* 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; }
}

.pyx-reg-table th:nth-child(2), .pyx-reg-table td:nth-child(2) { width: 210px; }  /* Nombre   */
.pyx-reg-table th:nth-child(3), .pyx-reg-table td:nth-child(3) { 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-child(4), .pyx-reg-table td:nth-child(4) { width: 190px; }  /* Nivel    */
.pyx-reg-table th:nth-child(5), .pyx-reg-table td:nth-child(5) { width: 200px; }  /* Perfil   */
.pyx-reg-table th:nth-child(6), .pyx-reg-table td:nth-child(6) { width: 175px; }  /* Estruct. */
.pyx-reg-table th:nth-child(7), .pyx-reg-table td:nth-child(7) { 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-child(8), .pyx-reg-table td:nth-child(8) { width: 230px; }  /* Usuario  */
.pyx-reg-table th:nth-child(9), .pyx-reg-table td:nth-child(9) { width: 120px; }  /* Contras. */
.pyx-reg-table th:nth-child(10), .pyx-reg-table td:nth-child(10) { width: 150px; } /* Rol      */
.pyx-reg-table th:nth-child(11), .pyx-reg-table td:nth-child(11) { 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-child(10) .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-child(8) .pyx-user-cell {
    display: flex;
    align-items: center;
    gap: 5px;
    min-width: 0;
}

.pyx-reg-table td:nth-child(8) .pyx-user-cell .mud-icon-root { font-size: 13px; flex: none; }

.pyx-reg-table td:nth-child(8) .pyx-user-cell > span {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* 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: 9.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;
}

.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: 10px;
    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: 9.5px;
    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: 9.5px;
    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);
}

/* ===== 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: 9.5px;
    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; }

.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: 9.5px;
    font-weight: 600;
    letter-spacing: .05em;
    color: var(--pyx-ink-faint);
    white-space: nowrap;
}

.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: 10px;
    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 y el ámbar nunca llegaba a pintarse. */
.pyx-net-cell-search .mud-input.mud-input-outlined .mud-input-outlined-border {
    border-color: var(--pyx-divider);
}

/* Bajo el mínimo de anonimato: ámbar en el BORDE del buscador, no un relleno de toda la celda.
   Advierte sin bloquear — el círculo se guarda así, sólo saldrá N/A al reportar. */
.pyx-net-cell-search--caution .mud-input.mud-input-outlined .mud-input-outlined-border {
    border-color: var(--pyx-caution);
}

.pyx-net-min {
    display: block;
    margin-top: 4px;
    font-family: var(--pyx-label);
    font-size: 9px;
    font-weight: 600;
    letter-spacing: .04em;
    color: var(--pyx-caution);
    cursor: help;
}

.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;
}

.pyx-net-lista-vincular-item {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    padding: 3px 0;
}

.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: 9.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); }

.pyx-net-foot {
    padding: 10px 16px;
    border-top: 1px solid var(--pyx-divider);
    background: var(--pyx-canvas-soft);
    font-family: var(--pyx-label);
    font-size: 11px;
    color: var(--pyx-ink-faint);
}

/* 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; }

/* ── 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,
.pyx-circulo-alta {
    display: grid;
    grid-template-columns: minmax(160px, 1.6fr) minmax(120px, 1fr) 130px 96px;
    align-items: center;
    gap: 12px;
}

.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;
}

.pyx-circulo-alta {
    padding-top: 10px;
    margin-top: 6px;
    border-top: 1px solid var(--mud-palette-lines-default);
}

/* 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;
}

/* 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: 9.5px;
    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";
}
