/* ==========================================================================
   Sistema de Gestión de Laboratorios · Facultad de la Salud Humana · UNL
   Estructura, cabecera, navegación y páginas.
   ==========================================================================

   Arquitectura de la hoja de estilos (se cargan en este orden):

     1. `fonts.css`                    Montserrat, servida desde este servidor.
     2. `design-tokens.css`            color, tipografía, densidad, estructura.
     3. Bootstrap 5.3 (CDN)            base de componentes.
     4. `components.css`               tarjetas, tablas, formularios, botones…
     5. `app.css`  ← este archivo      shell, cabecera, barra lateral, páginas.
     6. `modules/operational-panel.css` panel operativo del técnico docente.

   Este archivo contiene lo **estructural y lo propio de página**: la cabecera,
   la barra lateral, el pie, el encabezado de página, el panel de bienvenida, la
   portada, el acceso y las páginas de error. Todo lo transversal —tarjetas,
   tablas, formularios, botones, distintivos, avisos, modales, estados vacíos—
   vive en `components.css`.

   Homologación visual con el sistema de Bienestar Universitario
   (`/var/www/ubu-laravel`, ver `docs/visual-parity-wellbeing-laboratories.md`):
   se adopta su gramática —cabecera blanca con filete rojo–azul, barra lateral
   azul petróleo con el elemento activo en rojo, portada dividida con fotografía
   y velo azul, panel con hero fotográfico— y se expresa con la paleta y la
   tipografía del Manual de Identidad Corporativa de la UNL. No es una copia:
   los colores, las fotografías y el contenido son de la Facultad de la Salud
   Humana.
   ========================================================================== */

/* ==========================================================================
   1. Base tipográfica
   ========================================================================== */
/* El salto a un ancla no debe dejar el destino debajo de la cabecera fija: se
   reserva su altura. Es el mismo token que usa el desplazamiento de la barra
   lateral, de modo que las tres piezas —cabecera, lateral y contenido— se
   alinean siempre con la misma medida. */
html { scroll-padding-top: var(--fsh-navbar-height); }

body {
    font-family: var(--fsh-font);
    color: var(--fsh-text);
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-regular);
    line-height: 1.55;
    min-height: 100vh;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    /* Montserrat es geométrica y de caja ancha: sin un poco de tracking
       negativo en cuerpo, los párrafos largos se leen sueltos. */
    letter-spacing: -.003em;
}

button, input, select, textarea, .btn, .badge, .alert, .table,
.breadcrumb, .modal, .dropdown-menu {
    font-family: var(--fsh-font);
}

/* Títulos: semibold, interlineado corto y tracking negativo creciente con el
   tamaño. En Montserrat, un título grande sin tracking negativo se ve disperso.
   El color pasa al azul institucional oscuro, como en la referencia: da
   jerarquía sin recurrir a más peso. */
h1, .h1, h2, .h2, h3, .h3, h4, .h4, h5, .h5, h6, .h6 {
    color: var(--fsh-blue-700);
    font-weight: var(--font-weight-bold);
    line-height: 1.22;
    letter-spacing: -.015em;
}
h1, .h1 { letter-spacing: -.022em; }

/* Título de página y subtítulo: una sola jerarquía en toda la aplicación. */
.page-title {
    font-size: var(--font-size-2xl);
    font-weight: var(--font-weight-bold);
    color: var(--fsh-blue-700);
    margin: 0;
    letter-spacing: -.022em;
}
.page-subtitle {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-medium);
    color: var(--fsh-text-muted);
    margin: .2rem 0 0;
}
/* Rótulo de sección: pequeño, en versalitas y con tracking abierto. El tracking
   es lo que lo convierte en rótulo y no en un texto pequeño cualquiera. */
.section-title {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-bold);
    text-transform: uppercase;
    letter-spacing: .13em;
    color: var(--fsh-blue-600);
    margin-bottom: var(--space-3);
}
.text-secondary { color: var(--fsh-text-muted) !important; }

/* Metadatos y textos de apoyo. */
.small, small { font-size: var(--font-size-sm); }
.fsh-meta { font-size: var(--font-size-xs); color: var(--fsh-text-muted); font-weight: var(--font-weight-medium); }

/* Números destacados de métricas. Tabulares para que no bailen al refrescar. */
.metric-value {
    font-size: 1.55rem;
    font-weight: var(--font-weight-bold);
    line-height: 1.1;
    letter-spacing: -.03em;
    font-variant-numeric: tabular-nums;
    color: var(--fsh-blue-700);
}
.metric-label {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    line-height: 1.35;
    color: var(--fsh-text-muted);
    text-transform: uppercase;
    letter-spacing: .06em;
}

/* ==========================================================================
   2. Foco visible — accesibilidad transversal
   ========================================================================== */
:focus-visible {
    outline: 3px solid rgba(0, 76, 123, .28);
    outline-offset: 2px;
    border-radius: var(--fsh-radius-sm);
}
/* Sobre azul oscuro el foco se marca en blanco: el rojo corporativo solo llega
   a 1.85:1 contra #003A5D y un indicador de foco necesita al menos 3:1. */
.app-sidebar :focus-visible {
    outline: 2px solid #fff;
    outline-offset: 1px;
}
.btn:focus-visible,
.form-control:focus-visible,
.form-select:focus-visible { outline-offset: 1px; }

/* Salto al contenido para navegación por teclado. */
.skip-link {
    position: absolute;
    left: -9999px;
    top: 0;
    z-index: 1090;
    background: var(--fsh-navy);
    color: #fff;
    padding: .65rem 1rem;
    border-radius: 0 0 var(--fsh-radius) 0;
    font-weight: var(--font-weight-semibold);
    text-decoration: none;
}
.skip-link:focus {
    left: 0;
    color: #fff;
}

/* ==========================================================================
   3. Cabecera institucional
   ==========================================================================
   Sustituye a la barra azul maciza anterior. Composición clara, como la del
   sistema de referencia: fondo blanco, logotipo oficial de la UNL a la
   izquierda, denominación institucional en texto, datos de la cuenta a la
   derecha con avatar de iniciales y rol, y un filete inferior rojo–azul de
   3 px que es el sello de marca de toda la aplicación.

   El azul no desaparece: se concentra donde estructura —barra lateral, títulos,
   acciones primarias— en lugar de teñir la pantalla entera.

   Tres decisiones estructurales sostienen el desplazamiento de la página:

     · **Altura fija, no mínima.** El desplazamiento de la barra lateral, el
       alto del shell y el salto a anclas se calculan con
       `--fsh-navbar-height`. Con `min-height`, la altura real podía separarse
       del token —basta un nombre de usuario a dos líneas o un control más
       alto— y esa diferencia se veía como una franja vacía bajo la cabecera al
       desplazar. Fijando la altura, el token es la medida real.
     · **Fondo opaco.** El blanco al 97 % con desenfoque de fondo obligaba al
       navegador a recomponer la cabecera en cada fotograma del desplazamiento,
       que es de donde salían las bandas claras. El contenido no tiene que
       transparentarse bajo la cabecera: tiene que quedar debajo.
     · **El filete es el borde.** Antes había un borde gris de 1 px y, encima,
       el filete de marca desplazado −1 px para taparlo. Dos elementos para una
       sola línea. Ahora el filete rojo–azul cierra la cabecera a ras, sin
       desbordarla, y no hay ningún desplazamiento que cuadrar. */
.app-navbar {
    position: sticky;
    top: 0;
    z-index: 1030;
    height: var(--fsh-navbar-height);
    padding: 0;
    background: rgba(255, 255, 255, 1);
    border-bottom: 0;
    box-shadow: 0 1px 2px rgba(33, 25, 21, .05);
}
/* La fila interior ocupa la cabecera completa: el centrado vertical es suyo y
   no depende de rellenos que haya que mantener a mano. */
.app-navbar > .app-container { height: 100%; }
.app-navbar::after {
    content: "";
    position: absolute;
    inset: auto 0 0 0;
    height: 3px;
    background: var(--rule-brand);
}

/* Marca: el logotipo oficial horizontal, sin caja, sin radio y sin sombra. El
   área de reserva del manual (≈1/7 de su alto) se respeta como margen propio,
   para que nunca quede pegado a un icono o a un texto. */
.app-navbar .navbar-brand {
    display: inline-flex;
    align-items: center;
    gap: .9rem;
    min-width: 0;
    padding: 0;
    margin: 0;
    color: var(--fsh-text);
    text-decoration: none;
}
.app-navbar .navbar-brand:hover { color: var(--fsh-text); }
.navbar-logo {
    height: 40px;
    width: auto;
    display: block;
    object-fit: contain;
    object-position: left center;
    margin-right: calc(40px / 7);
}
.navbar-brand-copy { min-width: 0; line-height: 1.2; }
.navbar-brand-title {
    display: block;
    font-size: .78rem;
    font-weight: var(--font-weight-bold);
    letter-spacing: .01em;
    text-transform: uppercase;
    color: var(--fsh-blue-700);
}
.navbar-brand-institution {
    display: block;
    margin-top: .12rem;
    color: var(--fsh-text-muted);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    letter-spacing: .01em;
}
/* Contexto de página: separa la marca del título de la pantalla con un filete
   vertical tenue, en lugar de dejarlo flotando. */
.navbar-context {
    color: var(--fsh-text-muted);
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    letter-spacing: .01em;
    padding-left: .95rem;
    margin-left: .3rem;
    border-left: 1px solid var(--fsh-border);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Botón de menú en móvil: control neutro sobre fondo claro. */
.app-navbar .navbar-toggle {
    width: 38px;
    height: 38px;
    flex: 0 0 38px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 1px solid var(--fsh-border);
    border-radius: 9px;
    background: var(--fsh-surface-muted);
    color: var(--fsh-blue-700);
}
.app-navbar .navbar-toggle:hover {
    background: var(--fsh-blue-050);
    border-color: var(--fsh-blue-400);
    color: var(--fsh-blue-700);
}

/* Zona de cuenta.
   ==========================================================================
   Composición fina, no cápsula. El bloque anterior se leía como un botón
   grande —caja, borde, relleno generoso— y competía con la marca al otro
   extremo de la cabecera. Aquí el orden es el de una ficha: primero quién
   (avatar de iniciales), después el nombre con el rol debajo y, al final, un
   galón discreto que anuncia el menú.

   La caja desaparece en reposo: el fondo azul muy claro solo aparece al pasar
   el puntero o con el menú desplegado. Un separador vertical tenue lo despega
   del resto de la barra sin dibujarle un contorno.

   El avatar de iniciales es el mismo recurso de la referencia: identifica al
   usuario sin depender de una fotografía que el sistema no tiene. Baja de 36 a
   34 px y pasa a circular, que es como se leen los avatares y lo distingue de
   los controles rectangulares de la aplicación. */
.navbar-account { flex: 0 0 auto; }
.app-navbar .navbar-account__toggle {
    display: flex;
    align-items: center;
    gap: .6rem;
    /* Sin altura de control: este no es un botón de trabajo. El objetivo táctil
       lo garantiza el propio avatar (34 px) más el relleno. */
    min-height: 0;
    padding: .3rem .45rem;
    border: 0;
    border-radius: var(--radius-control);
    color: var(--fsh-text);
    background: transparent;
    text-align: left;
    transition: background-color var(--fsh-transition);
}
/* Los botones de la aplicación se levantan 1 px al pasar el puntero; el de la
   cuenta no, porque no es una acción: es la identidad de la sesión. */
.app-navbar .navbar-account__toggle:hover,
.app-navbar .navbar-account__toggle:active { transform: none; }
.app-navbar .navbar-account__toggle:hover,
.app-navbar .navbar-account__toggle[aria-expanded="true"] {
    background: var(--fsh-blue-050);
    color: var(--fsh-text);
}
/* Separador de la misma familia que el del contexto de página: filete tenue,
   no contorno. Solo donde hay sitio para el nombre. */
@media (min-width: 768px) {
    .navbar-account::before {
        content: "";
        position: absolute;
        inset: .9rem auto .9rem 0;
        width: 1px;
        background: var(--fsh-border);
    }
    .navbar-account { position: relative; padding-left: .85rem; }
}
/* Indicador de despliegue: galón fino que gira al abrir. Sustituye al triángulo
   macizo de Bootstrap, que a este tamaño se veía tosco. */
.navbar-account__caret {
    flex: 0 0 auto;
    font-size: .7rem;
    line-height: 1;
    color: var(--fsh-text-muted);
    transition: transform var(--fsh-transition);
}
.navbar-account__toggle[aria-expanded="true"] .navbar-account__caret {
    transform: rotate(180deg);
}

/* Jerarquía del texto: el nombre manda, el rol acompaña. Interlineado corto
   para que las dos líneas formen un bloque compacto y bien centrado. */
.navbar-user-copy { min-width: 0; line-height: 1.25; }
.navbar-user-name {
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-bold);
    letter-spacing: -.005em;
    color: var(--fsh-blue-700);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 14rem;
}
.navbar-user-role {
    margin-top: .1rem;
    font-size: .625rem;
    font-weight: var(--font-weight-semibold);
    letter-spacing: .07em;
    text-transform: uppercase;
    color: var(--fsh-text-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 14rem;
}
.navbar-avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 34px;
    height: 34px;
    flex: 0 0 34px;
    border-radius: 50%;
    background: var(--fsh-blue-700);
    color: #fff;
    font-size: .7rem;
    font-weight: var(--font-weight-bold);
    letter-spacing: .04em;
}

/* ==========================================================================
   4. Estructura general (shell)
   ========================================================================== */
.app-body {
    background-color: var(--fsh-content-bg);
    min-height: 100vh;
    min-height: 100dvh;
}
/* La altura útil se mide con `dvh` —el alto *visible*— y no con `vh`. En móvil,
   `100vh` cuenta la pantalla como si la barra del navegador no existiera: al
   desplazar, esa barra se retrae, la ventana crece y el shell y la lateral
   quedan calculados sobre una medida que ya no es la buena. De ahí venía el
   descuadre al desplazar en teléfono. `vh` se conserva como respaldo para
   navegadores sin `dvh`. */
.app-shell {
    display: flex;
    align-items: stretch;
    min-height: calc(100vh - var(--fsh-navbar-height));
    min-height: calc(100dvh - var(--fsh-navbar-height));
}
.app-content {
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    flex-direction: column;
    background: var(--fsh-content-bg);
}
/* Limita el ancho en monitores muy amplios sin estrechar el trabajo diario. */
.app-container {
    width: 100%;
    max-width: var(--fsh-content-max);
    margin-inline: auto;
}
/* Ancho cómodo de lectura para formularios y fichas de detalle. */
.content-narrow { max-width: 780px; }
.content-medium { max-width: 1100px; }

.app-footer {
    margin-top: auto;
    border-top: 1px solid var(--fsh-border);
    background: var(--fsh-surface);
    color: var(--fsh-text-muted);
    font-size: var(--font-size-xs);
}
.app-footer .footer-logo {
    height: 26px;
    width: auto;
    opacity: .85;
}

/* ==========================================================================
   5. Barra lateral
   ==========================================================================
   Azul petróleo institucional, como en la referencia: el degradado recorre el
   azul de la UNL entre sus profundidades —más claro arriba, junto a la
   cabecera; profundo abajo— sin introducir ningún tono ajeno.

   La identidad va arriba, con la **barra roja lateral** del sistema de
   referencia: un filete vertical de 4 px a la izquierda del bloque de texto.
   El nombre del sistema y la dependencia se escriben como texto institucional;
   no se crea ninguna marca gráfica nueva. */
.app-sidebar {
    position: relative;
    display: flex;
    flex-direction: column;
    overflow: hidden;
    background: linear-gradient(180deg,
        var(--unl-blue) 0%, var(--unl-blue-dark) 56%, var(--unl-blue-deep) 100%);
    color: #fff;
    width: var(--fsh-sidebar-width);
    border-right: 0;
}
@media (min-width: 992px) {
    .app-sidebar {
        flex: 0 0 var(--fsh-sidebar-width);
        /* La navegación acompaña al usuario en listados largos. Se ancla justo
           bajo la cabecera —misma medida que su altura fija— y ocupa el resto
           de la ventana visible: no queda franja entre una y otra al desplazar. */
        position: sticky;
        top: var(--fsh-navbar-height);
        height: calc(100vh - var(--fsh-navbar-height));
        height: calc(100dvh - var(--fsh-navbar-height));
    }
    /* Por encima del punto de ruptura, Bootstrap deja el cuerpo del offcanvas
       sin crecer (`flex-grow: 0`). Con la barra a altura de ventana, eso dejaba
       la identidad, la lista y el pie apilados arriba, con el pie flotando a
       media altura y la lista larga recortada por el `overflow: hidden` de la
       barra. Aquí el cuerpo ocupa la barra completa: la identidad y el pie
       quedan fijos en sus extremos y solo corre la lista de opciones. */
    .app-sidebar .offcanvas-body {
        display: flex;
        flex: 1 1 auto;
        min-height: 0;
        overflow: hidden;
    }
}
.app-sidebar .offcanvas-header {
    background: var(--unl-blue-dark);
    color: #fff;
    border-bottom: 1px solid rgba(255, 255, 255, .13);
}
.app-sidebar .offcanvas-header .btn-close {
    filter: invert(1) grayscale(100%) brightness(200%);
}

/* Identidad del sistema. El logotipo no se mete en ninguna caja gráfica nueva:
   sin borde redondeado, sin sombra y con el área de reserva del manual
   respetada como margen propio.

   Dos líneas, no tres: el rótulo «UNL · FSH» y el nombre del sistema. La
   denominación completa de la dependencia se lee en la cabecera; repetirla aquí
   —a un palmo de distancia y con el mismo peso— no reforzaba la identidad, la
   convertía en ruido. El bloque queda más corto, más limpio y con la barra roja
   de marca ganando presencia. */
.sidebar-identity {
    flex: 0 0 auto;
    padding: 1.05rem 1.05rem .95rem;
    border-bottom: 1px solid rgba(255, 255, 255, .13);
}
/* Barra roja lateral: el acento de marca en el punto de más jerarquía de la
   navegación. */
.sidebar-identity > span {
    display: block;
    min-width: 0;
    line-height: 1.3;
    padding-left: .85rem;
    border-left: 4px solid var(--unl-red);
}
.sidebar-identity__kicker {
    display: block;
    color: rgba(255, 255, 255, .66);
    font-size: .5875rem;
    font-style: normal;
    font-weight: var(--font-weight-bold);
    letter-spacing: .19em;
    text-transform: uppercase;
}
.sidebar-identity strong {
    display: block;
    margin-top: .35rem;
    color: #fff;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-bold);
    letter-spacing: -.01em;
}

/* Zona desplazable: la identidad y el pie quedan fijos; solo corre la lista de
   opciones, que es lo que crece con el rol del usuario. */
.app-sidebar .offcanvas-body { flex-direction: column; padding: 0; }
.app-sidebar-nav {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    padding: .55rem .7rem 1rem;
    width: 100%;
    scrollbar-width: thin;
    scrollbar-color: rgba(255, 255, 255, .22) transparent;
}
.app-sidebar-nav::-webkit-scrollbar { width: 8px; }
.app-sidebar-nav::-webkit-scrollbar-thumb {
    background: rgba(255, 255, 255, .2);
    border-radius: 4px;
}
.app-sidebar-nav > .sidebar-section-title:first-child { padding-top: .35rem; }

/* Rótulo de grupo: separa módulos sin recargar. */
.sidebar-section-title {
    padding: .95rem .55rem .35rem;
    color: rgba(255, 255, 255, .55);
    font-size: .5875rem;
    font-weight: var(--font-weight-bold);
    letter-spacing: .16em;
    text-transform: uppercase;
}

.sidebar-link {
    display: flex;
    align-items: center;
    gap: .65rem;
    min-height: 40px;
    margin-bottom: 2px;
    padding: .5rem .65rem;
    border: 0;
    border-radius: 9px;
    color: rgba(255, 255, 255, .84);
    text-decoration: none;
    font-size: var(--font-size-sm);
    font-weight: var(--font-weight-semibold);
    letter-spacing: -.005em;
    line-height: 1.3;
    transition: background-color var(--fsh-transition), color var(--fsh-transition);
}
.sidebar-link > span:not(.badge) {
    /* La etiqueta ocupa el espacio disponible y se ajusta por PALABRAS, nunca
       letra por letra: `anywhere` rompía «Gestión de prácticas» en vertical
       cuando la columna se estrechaba. `break-word` solo parte una palabra si es
       más larga que la línea (excepcional). */
    flex: 1 1 auto;
    min-width: 0;
    white-space: normal;
    word-break: normal;
    overflow-wrap: break-word;
}
.sidebar-link i {
    font-size: 1rem;
    flex: 0 0 auto;
    width: 1.2rem;
    text-align: center;
    color: rgba(255, 255, 255, .72);
    transition: color var(--fsh-transition);
}
.sidebar-link:hover {
    background-color: rgba(255, 255, 255, .11);
    color: #fff;
}
.sidebar-link:hover i { color: #fff; }
.sidebar-link:focus-visible {
    background-color: rgba(255, 255, 255, .12);
    color: #fff;
}
/* Estado activo: rojo institucional macizo, como en el sistema de referencia.
   Es el único lugar de la navegación donde el rojo ocupa superficie, y por eso
   funciona: marca una sola opción entre veinte. El texto en blanco sobre
   #BF0811 alcanza 6.45:1, por encima del mínimo AA. */
.sidebar-link.active {
    background: var(--unl-red);
    color: #fff;
    font-weight: var(--font-weight-bold);
    box-shadow: 0 8px 20px rgba(0, 0, 0, .18);
}
.sidebar-link.active i { color: #fff; }
.sidebar-link.disabled {
    color: rgba(255, 255, 255, .45);
    cursor: default;
}
.sidebar-link.disabled:hover { background-color: transparent; }
.sidebar-link .badge { flex: 0 0 auto; font-size: .625rem; margin-left: auto; }

/* Contadores del menú: sólidos, porque compiten con el fondo azul profundo. */
.sidebar-link .badge.text-bg-danger {
    background-color: #fff !important;
    color: var(--unl-red) !important;
    box-shadow: none;
}
.sidebar-link.active .badge.text-bg-danger {
    background-color: rgba(255, 255, 255, .95) !important;
    color: var(--unl-red-dark) !important;
}
/* Advertencia: ámbar semántico con texto negro corporativo. No es un color
   corporativo UNL y no se presenta como tal; es el color de interfaz que el
   sistema ya usaba para «atención» y que la paleta publicada no cubre. */
.sidebar-link .badge.text-bg-warning {
    background-color: #F2C14E !important;
    color: var(--unl-black) !important;
    box-shadow: none;
}
.sidebar-link .badge.text-bg-light {
    background-color: rgba(255, 255, 255, .16) !important;
    color: rgba(255, 255, 255, .85) !important;
    box-shadow: none;
}

/* Pie de la barra lateral: estable, discreto, sin competir con la navegación.
   Solo la versión. La denominación de la dependencia que antes lo encabezaba se
   ha retirado: era la tercera aparición de «Facultad de la Salud Humana» en la
   misma pantalla. */
.sidebar-footer {
    flex: 0 0 auto;
    border-top: 1px solid rgba(255, 255, 255, .13);
    color: rgba(255, 255, 255, .62);
    font-size: .625rem;
    font-weight: var(--font-weight-semibold);
    letter-spacing: .06em;
    text-transform: uppercase;
}

/* ==========================================================================
   6. Encabezado de página y migas
   ========================================================================== */
.page-header {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: space-between;
    gap: var(--space-3);
    padding: .15rem 0 var(--space-md);
    margin-bottom: var(--space-lg);
    border-bottom: 1px solid var(--fsh-border);
}
.page-header__text { min-width: 0; }
/* Filete rojo corto: marca institucional discreta, sin ocupar espacio. */
.page-header__text .page-title::before {
    content: "";
    display: block;
    width: 34px;
    height: 3px;
    background: var(--unl-red);
    border-radius: 2px;
    margin-bottom: .5rem;
}
.page-header__actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-sm);
}
/* Variante estrecha: para pantallas cuya identidad ya la da el bloque que sigue.
   Repetir aquí el código, el ciclo y el laboratorio que la ficha vuelve a
   mostrar cuesta una franja de alto y no añade nada, así que la cabecera se
   queda con el título y cede el sitio. */
.page-header--slim {
    padding-bottom: var(--space-sm);
    margin-bottom: var(--space-3);
}

/* Migas de pan discretas: contexto, no protagonismo. */
.breadcrumb {
    font-size: var(--font-size-sm);
    margin-bottom: var(--space-md);
    padding: .38rem .75rem;
    width: fit-content;
    border: 1px solid var(--fsh-border);
    border-radius: 999px;
    background: var(--fsh-surface);
}
.breadcrumb-item a {
    color: var(--fsh-text-muted);
    text-decoration: none;
}
.breadcrumb-item a:hover { color: var(--fsh-blue-600); text-decoration: underline; }
.breadcrumb-item.active { color: var(--fsh-blue-700); font-weight: var(--font-weight-semibold); }

/* ==========================================================================
   7. Panel: hero institucional
   ==========================================================================
   El patrón del sistema de referencia: fotografía institucional, velo azul que
   garantiza el contraste del texto blanco, línea lateral roja, saludo grande,
   subtítulo y distintivos del rol. No es decoración: el velo es lo que hace
   legible el texto sobre una fotografía de luminosidad variable.

   La fotografía es la del campus de la Universidad Nacional de Loja, un activo
   propio del proyecto (`static/img/auth/campus-unl.webp`). No se reutiliza
   ninguna imagen del sistema de Bienestar Universitario. */
.welcome-panel {
    position: relative;
    overflow: hidden;
    min-height: 190px;
    display: flex;
    flex-direction: column;
    justify-content: flex-end;
    border: 1px solid var(--unl-blue-40);
    border-radius: 14px;
    background-color: var(--unl-blue-dark);
    background-image:
        linear-gradient(105deg,
            rgba(0, 42, 68, .96) 0%,
            rgba(0, 76, 123, .82) 54%,
            rgba(0, 42, 68, .38) 100%),
        url("../img/auth/campus-unl.67d82ff5ed76.webp");
    background-size: cover;
    background-position: center;
    color: #fff;
    padding: 1.6rem 1.75rem 1.5rem;
    box-shadow: 0 18px 46px rgba(33, 25, 21, .12);
    margin-bottom: var(--space-lg);
}
/* Línea lateral roja: el acento de marca en el bloque de más jerarquía. */
.welcome-panel::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 5px;
    background: var(--unl-red);
}
.welcome-panel__kicker {
    margin: 0 0 .45rem;
    color: rgba(255, 255, 255, .74);
    font-size: .625rem;
    font-weight: var(--font-weight-bold);
    letter-spacing: .19em;
    text-transform: uppercase;
}
.welcome-panel h1 {
    color: #fff;
    font-size: clamp(1.35rem, 2.6vw, 2rem);
    font-weight: var(--font-weight-bold);
    letter-spacing: -.025em;
    margin-bottom: .55rem;
}
.welcome-panel .welcome-meta {
    color: rgba(255, 255, 255, .88);
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    line-height: 1.6;
}
.welcome-panel .welcome-meta i { color: rgba(255, 255, 255, .75); }
/* Distintivos sobre la fotografía: cristal tenue con borde, como en la
   referencia. Informan del rol y del alcance sin competir con el título. */
.welcome-panel .badge.role-badge {
    background: rgba(255, 255, 255, .14);
    color: #fff;
    border: 1px solid rgba(255, 255, 255, .24);
    font-weight: var(--font-weight-semibold);
    font-size: .625rem;
    letter-spacing: .04em;
    padding: .34em .7em;
    border-radius: var(--radius-sm);
    backdrop-filter: blur(5px);
}
/* El rol principal se distingue con el rojo corporativo sobre blanco: es el
   dato que el usuario busca primero. */
.welcome-panel .badge.role-badge--rol {
    background: #fff;
    color: var(--unl-red);
    border-color: #fff;
}
.welcome-panel .badge.role-badge i { margin-right: .25rem; font-size: .9em; }

/* ==========================================================================
   8. Portada pública
   ========================================================================== */
.public-hero {
    position: relative;
    overflow: hidden;
    background-color: var(--unl-blue-dark);
    /* Versión ligera en móvil; la grande solo se descarga en pantallas que la
       aprovechan. El velo azul asegura el contraste del texto. */
    background-image:
        linear-gradient(105deg,
            rgba(0, 42, 68, .96) 0%,
            rgba(0, 76, 123, .84) 56%,
            rgba(0, 42, 68, .42) 100%),
        url("../img/auth/campus-unl-sm.d7bc89b571f0.webp");
    background-size: cover;
    background-position: center;
    color: #fff;
    border-radius: 14px;
    padding: 2.5rem 1.75rem;
}
.public-hero::before {
    content: "";
    position: absolute;
    inset: 0 auto 0 0;
    width: 5px;
    background: var(--unl-red);
}
@media (min-width: 768px) {
    .public-hero {
        padding: 3.5rem 3rem;
        background-image:
            linear-gradient(105deg,
                rgba(0, 42, 68, .96) 0%,
                rgba(0, 76, 123, .84) 56%,
                rgba(0, 42, 68, .42) 100%),
            url("../img/auth/campus-unl.67d82ff5ed76.webp");
    }
}
.public-hero__eyebrow {
    display: flex;
    align-items: center;
    gap: .7rem;
    font-size: var(--font-size-xs);
    text-transform: uppercase;
    letter-spacing: .19em;
    color: rgba(255, 255, 255, .78);
    font-weight: var(--font-weight-bold);
}
.public-hero__eyebrow::before {
    content: "";
    width: 34px;
    height: 4px;
    border-radius: 99px;
    background: var(--unl-red);
    flex: 0 0 auto;
}
.public-hero__title {
    color: #fff;
    font-size: clamp(1.75rem, 4vw, 2.6rem);
    font-weight: var(--font-weight-bold);
    line-height: 1.1;
    letter-spacing: -.028em;
    margin: .8rem 0 .5rem;
}
.public-hero__text {
    color: rgba(255, 255, 255, .9);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-medium);
    max-width: 56ch;
}
/* El logotipo oficial sobre la fotografía: sin caja, sin radio y sin sombra. */
.public-hero__logo {
    height: 54px;
    width: auto;
    background: rgba(255, 255, 255, .96);
    border-radius: var(--radius-sm);
    padding: .5rem .7rem;
}
.public-feature {
    display: flex;
    gap: .85rem;
    align-items: flex-start;
}
.public-feature__icon {
    flex: 0 0 auto;
    width: 40px; height: 40px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--fsh-border);
    border-radius: 10px;
    background: var(--fsh-blue-050);
    color: var(--fsh-blue-600);
    font-size: 1.1rem;
}
.public-feature__title {
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-bold);
    color: var(--fsh-blue-700);
    margin-bottom: .15rem;
}
.public-feature__text { font-size: var(--font-size-sm); color: var(--fsh-text-muted); margin: 0; line-height: 1.6; }

/* ==========================================================================
   9. Acceso institucional
   ==========================================================================
   Composición dividida del sistema de referencia: a la izquierda la fotografía
   institucional con velo azul, el logotipo oficial en blanco y el mensaje del
   sistema; a la derecha la tarjeta blanca de acceso, con su filete superior
   rojo–azul, sobre un fondo fotográfico secundario muy atenuado.

   El panel izquierdo es complementario: todo lo que dice se repite en la
   columna del formulario, de modo que ocultarlo en móvil no pierde nada. */
.auth-body {
    min-height: 100vh;
    background-color: var(--fsh-content-bg);
    display: flex;
    flex-direction: column;
}
.auth-layout {
    position: relative;
    flex: 1 1 auto;
    display: grid;
    grid-template-columns: 1fr;
    min-height: 100vh;
    overflow: hidden;
}
@media (min-width: 992px) {
    .auth-layout { grid-template-columns: minmax(0, 3fr) minmax(420px, 2fr); }
}

.auth-aside {
    display: none;
    position: relative;
    background-color: var(--unl-blue-dark);
    background-image:
        linear-gradient(100deg,
            rgba(0, 32, 52, .96) 0%,
            rgba(0, 42, 68, .88) 45%,
            rgba(0, 76, 123, .62) 100%),
        url("../img/auth/campus-unl.67d82ff5ed76.webp");
    background-size: cover;
    background-position: center;
    color: #fff;
    padding: 3.25rem clamp(1.75rem, 5vw, 4.75rem) 2.6rem;
    flex-direction: column;
    justify-content: space-between;
}
@media (min-width: 992px) { .auth-aside { display: flex; } }
.auth-aside__brand { display: flex; align-items: center; gap: 1rem; }
/* El logotipo oficial horizontal, sin recolorear ni deformar. Se apoya sobre
   una placa blanca porque la marca institucional no puede perder su color
   sobre una fotografía oscura. */
.auth-aside__logo {
    height: 58px;
    width: auto;
    background: rgba(255, 255, 255, .96);
    border-radius: var(--radius-sm);
    padding: .55rem .75rem;
}
.auth-aside__institution { font-size: var(--font-size-sm); line-height: 1.4; color: rgba(255, 255, 255, .88); }
.auth-aside__institution strong {
    display: block;
    font-size: var(--font-size-base);
    font-weight: var(--font-weight-bold);
    color: #fff;
    text-transform: uppercase;
    letter-spacing: .02em;
}
.auth-aside__title {
    font-size: clamp(1.9rem, 3.4vw, 2.9rem);
    font-weight: var(--font-weight-bold);
    color: #fff;
    line-height: 1.08;
    letter-spacing: -.03em;
    max-width: 15ch;
}
/* El filete rojo va **antes** del título, como marca de apertura. */
.auth-aside__title::before {
    content: "";
    display: block;
    width: 46px; height: 4px;
    background: var(--unl-red);
    border-radius: 99px;
    margin-bottom: 1.1rem;
}
.auth-aside__text {
    color: rgba(255, 255, 255, .88);
    font-size: var(--font-size-lg);
    font-weight: var(--font-weight-medium);
    max-width: 46ch;
    margin-top: 1.1rem;
}
.auth-aside__foot {
    font-size: var(--font-size-xs);
    font-weight: var(--font-weight-semibold);
    letter-spacing: .04em;
    color: rgba(255, 255, 255, .72);
}

/* Columna del formulario, sobre fondo fotográfico secundario muy atenuado. */
.auth-main {
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    padding: 2.25rem 1.25rem;
    overflow: hidden;
}
.auth-main::before {
    content: "";
    position: absolute;
    inset: 0;
    background:
        linear-gradient(rgba(244, 246, 248, .93), rgba(244, 246, 248, .93)),
        url("../img/auth/campus-unl-sm.d7bc89b571f0.webp") center / cover;
}
.auth-wrapper { position: relative; width: 100%; max-width: 440px; }

/* Tarjeta blanca de acceso con la línea superior rojo–azul. */
.auth-card {
    position: relative;
    overflow: hidden;
    background: var(--fsh-surface);
    border: 1px solid var(--fsh-border);
    border-radius: var(--fsh-radius-lg);
    box-shadow: var(--fsh-shadow-lg);
    padding: 1.9rem 1.85rem 1.75rem;
}
.auth-card::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 5px;
    background: var(--rule-brand-card);
}
/* Marca visible en móvil, donde el panel lateral no se muestra. */
.auth-brand-mobile { text-align: center; margin-bottom: 1.5rem; }
.auth-brand-mobile img { height: 46px; width: auto; }
.auth-title {
    font-size: 1.4rem;
    font-weight: var(--font-weight-bold);
    color: var(--fsh-blue-700);
    letter-spacing: -.025em;
    margin-bottom: .3rem;
}
.auth-subtitle { color: var(--fsh-text-muted); font-size: var(--font-size-sm); }
.auth-footer {
    position: relative;
    border-top: 1px solid var(--fsh-border);
    color: var(--fsh-text-muted);
    font-size: var(--font-size-xs);
    text-align: center;
    padding: 1rem;
    background: var(--fsh-surface);
}
.auth-footer img { height: 30px; width: auto; opacity: .85; margin-inline: auto; }
/* Los campos del acceso son más altos que los del resto del sistema: es una
   pantalla de un solo propósito y el objetivo táctil manda sobre la densidad. */
.auth-card .form-control { min-height: 46px; }
.auth-card .btn-lg { min-height: 48px; }

/* Mostrar/ocultar contraseña. El botón se apoya sobre el campo sin alterarlo:
   ocupa su alto completo (top/bottom en 0, sin altura propia) y el hueco lo
   reserva el relleno derecho del input, de modo que el texto nunca queda
   debajo. El campo conserva su borde, su fondo y su estado de foco. */
.auth-password { position: relative; }
.auth-password .form-control { padding-right: 3rem; }
.auth-password__toggle {
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;
    width: 3rem;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    background: transparent;
    color: var(--fsh-text-muted);
    line-height: 1;
    cursor: pointer;
    border-radius: var(--fsh-radius);
}
.auth-password__toggle:hover { color: var(--fsh-blue-700); }
.auth-password__toggle:focus-visible {
    outline: 2px solid var(--fsh-blue);
    outline-offset: -3px;
    color: var(--fsh-blue-700);
}
.auth-password__toggle .bi { font-size: 1.05rem; }
/* Edge dibuja su propio ojo dentro del campo: serían dos controles para lo
   mismo, y el nuestro es el que anuncia su estado a los lectores de pantalla. */
.auth-password .form-control::-ms-reveal,
.auth-password .form-control::-ms-clear { display: none; }

/* ==========================================================================
   10. Páginas de error
   ========================================================================== */
.error-body {
    min-height: 100vh;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--fsh-content-bg);
    background-image:
        linear-gradient(rgba(244, 246, 248, .93), rgba(244, 246, 248, .93)),
        url("../img/auth/campus-unl-sm.d7bc89b571f0.webp");
    background-size: cover;
    background-position: center;
    padding: 1.5rem;
}
.error-wrapper {
    max-width: 520px;
    width: 100%;
}
.error-wrapper .card { box-shadow: var(--fsh-shadow-lg); }
.error-code {
    font-size: clamp(3.5rem, 12vw, 5.5rem);
    font-weight: var(--font-weight-bold);
    line-height: 1;
    letter-spacing: -.06em;
    color: var(--fsh-blue-600);
    font-variant-numeric: tabular-nums;
}
/* Las plantillas de error distinguen el tipo con un modificador propio, no con
   las utilidades de color de Bootstrap: aquellas no alcanzan AA sobre blanco
   (el ámbar queda en 1.6:1) y, al llevar `!important`, obligaban a responder
   con otro `!important`. Con clase propia basta declarar el color.
   El significado no depende del color: el encabezado lo dice con palabras. */
.error-code--warning { color: var(--fsh-warning); }
.error-code--danger  { color: var(--fsh-danger); }
.error-code--info    { color: var(--fsh-blue-600); }

/* ==========================================================================
   11. Utilidades
   ========================================================================== */
.gold-rule {
    width: 40px; height: 3px;
    background: var(--fsh-gold);
    border-radius: 2px;
    border: 0;
    opacity: 1;
    margin: .5rem 0;
}
.text-institutional { color: var(--fsh-blue-600); }
.bg-surface { background-color: var(--fsh-surface); }
/* No hay `.border-institutional`: el color de borde institucional ya viaja en
   `--bs-border-color`, así que la utilidad `border` de Bootstrap lo aplica sola. */

/* ==========================================================================
   12. Responsive
   ========================================================================== */
/* Tablet y móvil: la cabecera recupera la marca compacta y el menú se abre
   como panel lateral. La altura de la cabecera baja en el token (ver
   `design-tokens.css`), de modo que el shell y el desplazamiento siguen
   midiéndose con la altura real. */
@media (max-width: 991.98px) {
    .navbar-logo { height: 34px; }

    /* Cabecera: respeta el notch/isla (área segura superior) creciendo hacia
       arriba; su fondo cubre esa franja y el contenido queda debajo. */
    .app-navbar {
        height: auto;
        min-height: var(--fsh-navbar-height);
        padding-top: var(--safe-top);
        padding-left: var(--safe-left);
        padding-right: var(--safe-right);
    }

    /* Contenido a ancho completo del teléfono (el sidebar es off-canvas, no
       ocupa columna). El área segura lateral se aplica al `main`, por FUERA del
       relleno del contenedor: en vertical vale 0 y no molesta; en horizontal
       evita que el contenido quede bajo el notch. */
    .app-content { width: 100%; max-width: 100%; }
    .app-content, .app-footer {
        padding-left: var(--safe-left);
        padding-right: var(--safe-right);
    }
    .app-footer { padding-bottom: calc(.75rem + var(--safe-bottom)); }

    /* ── Drawer lateral móvil ──────────────────────────────────────────────
       Panel real: ancho cómodo, alto de viewport visible, y el scroll vive SOLO
       en la lista de opciones (nada de scrolls anidados que en iOS se «traban»).
       El bloqueo del fondo lo aporta el off-canvas de Bootstrap (backdrop). */
    .app-sidebar {
        /* CAUSA RAÍZ del «el menú empuja el contenido y deja hueco blanco al
           cerrar»: la regla base `.app-sidebar{position:relative}` (para el
           filete rojo del escritorio) anulaba el `position:fixed` del
           off-canvas de Bootstrap en móvil, dejando la barra DENTRO del flujo
           (ocupaba columna y empujaba el `main`; al ocultarla con translateX
           quedaba su hueco). Se restaura el posicionamiento fijo del drawer. */
        position: fixed;
        top: 0;
        left: 0;
        z-index: 1045;
        width: var(--sidebar-mobile-width);
        max-width: 92vw;
        /* `bottom:auto` para que mande `height:100dvh` (Bootstrap fija top:0 y
           bottom:0, que daría el viewport de layout, no el visible). */
        bottom: auto;
        height: 100dvh;
        overflow: hidden;
    }
    .app-sidebar .offcanvas-body {
        display: flex;
        flex-direction: column;
        min-height: 0;
        overflow: hidden;
        padding: 0;
    }
    .app-sidebar .offcanvas-header { padding-top: calc(.6rem + var(--safe-top)); }
    .sidebar-identity { padding-top: calc(1.05rem + var(--safe-top)); }
    .app-sidebar-nav {
        flex: 1 1 auto;
        min-height: 0;
        overflow-y: auto;
        overscroll-behavior: contain;
        -webkit-overflow-scrolling: touch;
        padding-bottom: calc(1rem + var(--safe-bottom));
    }
    /* Área táctil cómoda por opción y sin partir etiquetas. */
    .sidebar-link { min-height: 44px; }
}

@media (max-width: 767.98px) {
    .navbar-brand-title { font-size: .7rem; }
    .navbar-logo { height: 30px; }
    .app-navbar .navbar-brand { gap: .6rem; }
    /* Sin nombre ni rol a la vista, el bloque de cuenta se reduce al avatar y
       al galón: no necesita separador ni relleno lateral. */
    .app-navbar .navbar-account__toggle { gap: .35rem; padding-inline: .3rem; }
}

/* Ajustes de pantallas pequeñas (360–575 px). */
@media (max-width: 575.98px) {
    .page-title { font-size: 1.25rem; }
    .metric-value { font-size: 1.5rem; }
    .card-body, .data-panel__body { padding: .9rem; }
    .page-header { gap: var(--space-sm); }
    .page-header__actions { width: 100%; }
    .page-header__actions > .btn { flex: 1 1 auto; }
    .form-actions > .btn { width: 100%; }
    .welcome-panel { padding: 1.25rem 1.1rem 1.1rem; min-height: 160px; }
    .public-hero { padding: 2rem 1.25rem; }
    .auth-card { padding: 1.5rem 1.25rem 1.4rem; }
    /* Los títulos largos se ajustan en lugar de desbordar. */
    h1, .h1, h2, .h2, .page-title { overflow-wrap: anywhere; }
}

/* ==========================================================================
   12 bis. Aviso de nueva versión desplegada
   ==========================================================================
   Lo inserta `js/core/app-version.js` cuando el servidor informa de un
   despliegue nuevo. Franja discreta y anclada abajo: informa sin tapar el
   contenido ni interrumpir lo que el usuario esté haciendo, porque la recarga
   la decide él. Respeta el área segura de iOS para no quedar bajo la barra
   inferior de Safari. */
.app-update {
    position: fixed;
    left: calc(var(--safe-left) + var(--space-sm));
    right: calc(var(--safe-right) + var(--space-sm));
    bottom: calc(var(--safe-bottom) + var(--space-sm));
    z-index: 1080;                       /* sobre el contenido, bajo los modales */
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-sm);
    max-width: 46rem;
    margin-inline: auto;
    padding: var(--space-sm) var(--space-md);
    border-radius: var(--radius-md);
    background: var(--unl-blue);
    color: #fff;
    box-shadow: var(--shadow-lg);
    font-size: var(--font-size-sm);
}
.app-update__text { font-weight: 600; }
/* El motivo por el que aún no conviene recargar (escaneo, cola, formulario). */
.app-update__hint {
    flex: 1 1 100%;
    order: 3;
    opacity: .85;
    font-size: var(--font-size-xs);
}
.app-update__hint:empty { display: none; }
.app-update__btn { margin-left: auto; white-space: nowrap; }
.app-update__close {
    border: 0;
    background: none;
    color: inherit;
    font-size: 1.25rem;
    line-height: 1;
    padding: 0 var(--space-xs);
    opacity: .8;
    cursor: pointer;
}
.app-update__close:hover, .app-update__close:focus-visible { opacity: 1; }

/* ==========================================================================
   13. Impresión
   ==========================================================================
   Sin navegación ni acciones; el contenido ocupa la página. */
@media print {
    .app-sidebar, .app-navbar, .app-footer, .page-header__actions,
    .filter-bar, .action-group, .ajax-loader, .app-update { display: none !important; }
    .app-shell { display: block; }
    .card, .data-panel, .table-panel { border: 1px solid #999; box-shadow: none; }
    .welcome-panel, .public-hero {
        background: none !important;
        color: #000 !important;
        border: 1px solid #999;
    }
    .welcome-panel h1, .public-hero__title { color: #000 !important; }
    body { background: #fff; }
}

/* ---------------------------------------------------------------------------
   Aviso de actualización automática
   ---------------------------------------------------------------------------
   Solo aparece cuando la conexión lleva caída un rato: un indicador permanente
   de «conectado» no aporta nada y compite por atención con el contenido. Se
   sitúa abajo a la derecha, por encima de la barra inferior en móvil, y nunca
   bloquea la interacción.
--------------------------------------------------------------------------- */
.fsh-realtime-aviso {
    position: fixed;
    right: 1rem;
    bottom: calc(1rem + env(safe-area-inset-bottom, 0px));
    z-index: 1080;
    max-width: min(22rem, calc(100vw - 2rem));
    padding: 0.6rem 0.9rem;
    border-radius: var(--fsh-radius-sm, 0.5rem);
    border: 1px solid var(--fsh-color-border, #d8dde3);
    background: var(--fsh-color-surface, #fff);
    color: var(--fsh-color-text, #21252b);
    box-shadow: 0 0.5rem 1.25rem rgba(0, 0, 0, 0.14);
    font-size: 0.875rem;
    line-height: 1.35;
    opacity: 0;
    transform: translateY(0.5rem);
    pointer-events: none;
    visibility: hidden;
    transition: opacity 0.18s ease, transform 0.18s ease, visibility 0s linear 0.18s;
}

.fsh-realtime-aviso.is-visible {
    opacity: 1;
    transform: translateY(0);
    visibility: visible;
    transition-delay: 0s;
}

.fsh-realtime-aviso.is-warn {
    border-left: 3px solid var(--fsh-color-warning, #b8860b);
}

.fsh-realtime-aviso.is-ok {
    border-left: 3px solid var(--fsh-color-success, #1a7f4b);
}

@media (prefers-reduced-motion: reduce) {
    .fsh-realtime-aviso { transition: none; }
}
