/* Estilos generales personalizados para la aplicación */

/* SweetAlert2: siempre por encima de modales de Bootstrap */
.swal2-container {
    z-index: 99999 !important;
}

/* ========== MOBILE-FIRST UTILITIES ========== */

/* Breakpoints estándar del proyecto (alineados a Bootstrap):
   mobile  → max-width: 575.98px
   tablet  → max-width: 767.98px / 991.98px
   Al tocar un módulo, normalizar sus @media a estos valores (evita gaps/solapes
   de 1px por mezclar 576/575/575.98 y 768/767.98). */

/* page-header-actions: contenedor de botones de acción en headers de página
   (ej. Movimientos: "Cuenta corriente" / "Resumen" / "Volver"). En desktop se
   comporta como un d-flex normal; en mobile permite wrap y se alinea a la
   izquierda para no empujar el ancho ni generar scroll lateral.
   Usar junto con labels `d-none d-sm-inline` para dejar los botones ícono-only
   en pantallas chicas. */
.page-header-actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
    justify-content: flex-end;
}
@media (max-width: 575.98px) {
    .page-header-actions {
        justify-content: flex-start;
    }
}

/* Footers de modal en mobile: botones apilados a ancho completo (tap cómodo
   con el pulgar). column-reverse deja la acción primaria (último botón del
   footer, por convención) arriba. Reemplaza los hacks por-modal anteriores.
   Solo <576px → en tablet/desktop el footer queda igual (botones inline). */
@media (max-width: 575.98px) {
    .modal-footer {
        flex-direction: column-reverse;
        align-items: stretch;
        gap: 0.5rem;
    }
    .modal-footer > .btn {
        width: 100%;
        margin: 0;
    }
    /* .modal-nav: fila de navegación (‹ contador ›) dentro de un footer de modal.
       Como el footer apila a ancho completo, la fila queda estirada: repartir sus
       tres partes a los extremos deja las flechas como blancos de toque cómodos en
       lugar de dos íconos chicos amontonados a la izquierda. */
    .modal-footer > .modal-nav {
        justify-content: space-between;
    }
}

/* .min-w-0: utilidad que Bootstrap 5 no trae. Permite que un flex item encoja
   por debajo del ancho de su contenido (sin esto, min-width:auto hace que el
   item crezca con el contenido —p. ej. un Select2 con carátula larga— y empuje
   el ancho generando scroll lateral). Varias vistas ya la usan asumiendo que existe. */
.min-w-0 {
    min-width: 0 !important;
}

/* .nav-scroll-x: solapas (tabs) que no entran en una fila en mobile → quedan en
   una sola fila y se deslizan en horizontal (swipe), en vez de envolver.
   Solo aplica en mobile/tablet; en desktop las solapas entran y no scrollean. */
@media (max-width: 767.98px) {
    .nav-scroll-x {
        flex-wrap: nowrap;
        overflow-x: auto;
        overflow-y: hidden;
        -webkit-overflow-scrolling: touch;
        scrollbar-width: none;
    }
    .nav-scroll-x::-webkit-scrollbar {
        display: none;
    }
    .nav-scroll-x .nav-item {
        flex: 0 0 auto;
    }
    .nav-scroll-x .nav-link {
        white-space: nowrap;
    }
}

/* Solapas de estado en listados (Activos / Finalizados / Archivados).
   En mobile las pestañas con ícono + texto no entran en el ancho del card
   y, al desbordar, ensanchan toda la página generando scroll lateral.
   Las contenemos al ancho del card: si no entran, hacen scroll horizontal
   interno en vez de empujar el ancho del documento.
   Aplica a Expediente/Index (#expedientesTabs) y Caso/Index (#casosTabs). */
#expedientesTabs,
#casosTabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
}
#expedientesTabs::-webkit-scrollbar,
#casosTabs::-webkit-scrollbar {
    display: none;
}
#expedientesTabs .nav-item,
#casosTabs .nav-item {
    flex: 0 0 auto;
}
#expedientesTabs .nav-link,
#casosTabs .nav-link {
    white-space: nowrap;
}

/* .btn-sm-mobile: aplica tamaño chico sólo en mobile (<576px).
   Usar en botones inline que en desktop deben ser tamaño normal pero
   en mobile rompen layouts (ej: botones en headers de cards). */
@media (max-width: 575.98px) {
    .btn-sm-mobile {
        padding: 0.25rem 0.625rem;
        font-size: 0.8125rem;
        line-height: 1.4;
    }
    .btn-sm-mobile i.bx-md { font-size: 1.25rem; }
}

/* Botón xs reutilizable (Sneat no define .btn-xs por defecto).
   Vive acá y no en un CSS de módulo porque lo usan varias pantallas: si sólo
   está en el CSS de un módulo, en el resto la clase no existe y el botón sale
   a tamaño normal (le pasaba a las acciones de las tablas de Contabilidad). */
.btn-xs {
    padding: 0.2rem 0.55rem;
    font-size: 0.75rem;
    line-height: 1.4;
}

/* Botones inline en mobile (sin clase de tamaño explícita): reducir un escalón
   para que no rompan layouts horizontales. Excluye btn-icon (acciones de tabla)
   y btn-lg/btn-sm explícitos. */
@media (max-width: 575.98px) {
    .btn:not(.btn-icon):not(.btn-sm):not(.btn-lg):not(.btn-fab) {
        padding: 0.4rem 0.85rem;
        font-size: 0.875rem;
    }
}

/* Legibilidad mínima en mobile: subir font-size del texto que cae <13px
   (0.8125rem) para no quedar por debajo del umbral de legibilidad cómoda.
   Aplica a badges con texto visible, .small, .text-muted, celdas de tabla
   y form-text. NO afecta a .badge-dot u otros indicadores sin texto. */
@media (max-width: 575.98px) {
    .badge:not(.badge-dot),
    .small,
    small,
    .text-muted.small,
    .form-text,
    table td,
    table th {
        font-size: max(0.8125rem, 1em);
    }
}

/* Tap targets ≥44px en mobile (Apple HIG / WCAG 2.5.5).
   Aplica a botones de icono, dropdowns de acciones de tabla y badges clickeables.
   Solo en mobile <768px — desktop conserva tamaños originales. */
@media (max-width: 767.98px) {
    .btn-icon,
    .table .dropdown-toggle,
    .dropdown-menu .dropdown-item,
    .pagination .page-link,
    a.badge[href],
    button.badge,
    .badge[role="button"],
    .badge[onclick] {
        min-width: 44px;
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    /* Iconos dentro de botones de acción: aumentar para que sean visibles
       en el tap target ampliado. */
    .btn-icon i.bx,
    .table .dropdown-toggle i.bx {
        font-size: 1.25rem;
    }
}

/* Fix for logo background */
.app-brand,
.app-brand-link,
.app-brand-logo,
.app-brand-logo img,
.auth-cover-brand {
    background-color: transparent !important;
}

/* Variables CSS para soporte de temas dark/light */
:root {
    --general-bg-light: rgba(255, 255, 255, 0.8);
    --general-bg-dark: rgba(40, 44, 52, 0.8);
    --general-border-light: #e5e7eb;
    --general-border-dark: #4b5563;
    --general-text-light: #374151;
    --general-text-dark: #d1d5db;
    --transition-theme: all 0.3s ease;
}

/* Transiciones suaves para cambio de tema */
html {
    transition: var(--transition-theme);
}

body,
.card,
.table,
.btn,
.form-control,
.dropdown-menu {
    transition: var(--transition-theme);
}

/* ========== SPINNER DE CARGA MEJORADO ========== */

#pageLoadingOverlay {
    position: fixed;
    inset: 0;
    width: 100vw;
    width: 100dvw;
    height: 100vh;
    height: 100dvh;
    background-color: rgba(var(--bs-body-bg-rgb), 0.95);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    z-index: 9999;
    display: flex;
    justify-content: center;
    align-items: center;
    transition: opacity 0.3s ease-out;
}

#pageLoadingSpinner {
    text-align: center;
}

#pageLoadingSpinner .spinner-border {
    width: 3.5rem;
    height: 3.5rem;
    border-width: 0.35em;
    color: var(--bs-primary);
}

#pageLoadingSpinner .loading-text {
    margin-top: 1rem;
    color: var(--bs-body-color);
    font-weight: 500;
    font-size: 0.875rem;
}

/* Animación suave de entrada/salida */
.page-content {
    opacity: 0;
    transition: opacity 0.3s ease-in-out;
}

.page-content.loaded {
    opacity: 1;
}

/* Spinner Global (estilo clase para reemplazar inline) */
.global-spinner-container {
    display: flex;
    justify-content: center;
    align-items: center;
    position: fixed;
    inset: 0;
    width: 100vw;
    width: 100dvw;
    height: 100vh;
    height: 100dvh;
    background-color: var(--bs-body-bg);
    z-index: 9999;
    transition: opacity 0.5s ease-out;
}

/* ========== TARJETAS ESTADÍSTICAS MEJORADAS ========== */

.stat-card {
    background: linear-gradient(135deg,
            rgba(var(--bs-primary-rgb), 0.05) 0%,
            rgba(var(--bs-primary-rgb), 0.02) 100%);
    border-left: 4px solid var(--bs-primary);
    border-radius: 12px;
    transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
    overflow: hidden;
    position: relative;
}

.stat-card::before {
    content: '';
    position: absolute;
    top: 0;
    right: 0;
    width: 100px;
    height: 100px;
    background: radial-gradient(circle, rgba(var(--bs-primary-rgb), 0.1) 0%, transparent 70%);
    border-radius: 50%;
    transform: translate(30%, -30%);
}

@media (hover: hover) {
    .stat-card:hover {
        transform: translateY(-5px);
        box-shadow: 0 12px 24px rgba(0, 0, 0, 0.15);
    }
}

.stat-card-primary {
    border-left-color: var(--bs-primary);
}

.stat-card-success {
    background: linear-gradient(135deg,
            rgba(var(--bs-success-rgb), 0.05) 0%,
            rgba(var(--bs-success-rgb), 0.02) 100%);
    border-left-color: var(--bs-success);
}

.stat-card-success::before {
    background: radial-gradient(circle, rgba(var(--bs-success-rgb), 0.1) 0%, transparent 70%);
}

.stat-card-warning {
    background: linear-gradient(135deg,
            rgba(var(--bs-warning-rgb), 0.05) 0%,
            rgba(var(--bs-warning-rgb), 0.02) 100%);
    border-left-color: var(--bs-warning);
}

.stat-card-warning::before {
    background: radial-gradient(circle, rgba(var(--bs-warning-rgb), 0.1) 0%, transparent 70%);
}

.stat-card-info {
    background: linear-gradient(135deg,
            rgba(var(--bs-info-rgb), 0.05) 0%,
            rgba(var(--bs-info-rgb), 0.02) 100%);
    border-left-color: var(--bs-info);
}

.stat-card-info::before {
    background: radial-gradient(circle, rgba(var(--bs-info-rgb), 0.1) 0%, transparent 70%);
}

.stat-card-secondary {
    background: linear-gradient(135deg,
            rgba(var(--bs-secondary-rgb), 0.05) 0%,
            rgba(var(--bs-secondary-rgb), 0.02) 100%);
    border-left-color: var(--bs-secondary);
}

.stat-card-secondary::before {
    background: radial-gradient(circle, rgba(var(--bs-secondary-rgb), 0.1) 0%, transparent 70%);
}

.stat-card-danger {
    background: linear-gradient(135deg,
            rgba(var(--bs-danger-rgb), 0.05) 0%,
            rgba(var(--bs-danger-rgb), 0.02) 100%);
    border-left-color: var(--bs-danger);
}

.stat-card-danger::before {
    background: radial-gradient(circle, rgba(var(--bs-danger-rgb), 0.1) 0%, transparent 70%);
}

.stat-card-danger .stat-icon-wrapper {
    background: var(--bs-danger);
    box-shadow: 0 4px 12px rgba(var(--bs-danger-rgb), 0.3);
}

.stat-card-danger .stat-value {
    background: linear-gradient(135deg, var(--bs-danger), var(--bs-warning));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

.stat-icon-wrapper {
    width: 60px;
    height: 60px;
    border-radius: 16px;
    display: flex;
    align-items: center;
    justify-content: center;
    margin-bottom: 1rem;
    box-shadow: 0 4px 12px rgba(var(--bs-primary-rgb), 0.3);
}

.stat-card-primary .stat-icon-wrapper {
    background: var(--bs-primary);
}

.stat-card-success .stat-icon-wrapper {
    background: var(--bs-success);
    box-shadow: 0 4px 12px rgba(var(--bs-success-rgb), 0.3);
}

.stat-card-warning .stat-icon-wrapper {
    background: var(--bs-warning);
    box-shadow: 0 4px 12px rgba(var(--bs-warning-rgb), 0.3);
}

.stat-card-info .stat-icon-wrapper {
    background: var(--bs-info);
    box-shadow: 0 4px 12px rgba(var(--bs-info-rgb), 0.3);
}

.stat-card-secondary .stat-icon-wrapper {
    background: var(--bs-secondary);
    box-shadow: 0 4px 12px rgba(var(--bs-secondary-rgb), 0.3);
}

.stat-icon-wrapper i {
    font-size: 2rem;
    color: #fff;
}

.stat-content {
    position: relative;
    z-index: 1;
}

.stat-label {
    font-size: 0.875rem;
    color: var(--bs-secondary);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    display: block;
    margin-bottom: 0.5rem;
}

.stat-value-wrapper {
    display: flex;
    align-items: baseline;
    gap: 0.75rem;
    margin-bottom: 0.5rem;
    min-width: 0;
}

.stat-value {
    /* Fallback (sin container queries): escala con el viewport. Tope bajo para
       que montos de millones no se recorten contra el overflow:hidden de .stat-card. */
    font-size: clamp(1rem, 1.6vw, 1.6rem);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
    max-width: 100%;
    font-weight: 700;
    margin: 0;
    line-height: 1.1;
    background: linear-gradient(135deg, var(--bs-primary), var(--bs-info));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
    background-clip: text;
}

/* Donde el navegador soporta container queries, la fuente del monto escala
   según el ANCHO REAL de la tarjeta (no del viewport): así entra cualquier
   importe —decenas de millones— tanto en 2-up como en 4-up, sin recortarse. */
@supports (container-type: inline-size) {
    .stat-card {
        container-type: inline-size;
    }

    .stat-card .stat-value {
        font-size: clamp(0.9rem, 8cqi, 2.1rem);
    }
}

.stat-card-success .stat-value {
    background: linear-gradient(135deg, var(--bs-success), var(--bs-info));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
}

.stat-card-warning .stat-value {
    background: linear-gradient(135deg, var(--bs-warning), var(--bs-danger));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
}

.stat-card-secondary .stat-value {
    background: linear-gradient(135deg, var(--bs-secondary), var(--bs-dark));
    -webkit-background-clip: text;
    -webkit-text-fill-color: transparent;
}

.stat-trend {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    font-size: 0.875rem;
    color: var(--bs-success);
    font-weight: 500;
}

.stat-trend.down {
    color: var(--bs-danger);
}

.stat-footer {
    display: block;
    font-size: 0.75rem;
    color: var(--bs-secondary);
}

/* Animación de conteo */
@keyframes countUp {
    from {
        opacity: 0;
        transform: translateY(10px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.stat-value.updating {
    animation: countUp 0.5s ease-out;
}

/* Stat cards en mobile: el número de 2.5rem queda enorme cuando las cards
   van de a 2 por fila (col-6). Usamos clamp para que el tamaño se adapte
   al ancho real de la columna sin desbordar ni desfasarse. */
@media (max-width: 767.98px) {
    /* clamp(mín, preferido-relativo-al-viewport, máx)
       4vw ≈ 15px en 375px / 19px en 480px — caben hasta ~12 chars sin salirse */
    .stat-value {
        font-size: clamp(1rem, 4vw, 1.75rem);
        line-height: 1.15;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .stat-icon-wrapper {
        width: 44px;
        height: 44px;
        border-radius: 12px;
        margin-bottom: 0.75rem;
    }

    .stat-icon-wrapper i {
        font-size: 1.5rem;
    }

    .stat-label {
        font-size: 0.8125rem;
        margin-bottom: 0.375rem;
    }
}

/* === ESTILOS PARA BADGES DE ESTADO === */

/* Mejoras para badges de estado en ambos temas */
.badge-estado {
    font-weight: 600;
    font-size: 0.75rem;
    padding: 0.375rem 0.75rem;
    border-radius: 0.375rem;
    border: 1px solid transparent;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

/* Estilos específicos para tema claro */
:root {
    --badge-warning-bg: #fff3cd;
    --badge-warning-color: #856404;
    --badge-warning-border: #ffeaa7;

    --badge-info-bg: #d1ecf1;
    --badge-info-color: #0c5460;
    --badge-info-border: #bee5eb;

    --badge-success-bg: #d4edda;
    --badge-success-color: #155724;
    --badge-success-border: #c3e6cb;

    --badge-danger-bg: #f8d7da;
    --badge-danger-color: #721c24;
    --badge-danger-border: #f5c6cb;

    --badge-secondary-bg: #e2e3e5;
    --badge-secondary-color: #383d41;
    --badge-secondary-border: #d6d8db;
}

/* Estilos específicos para tema oscuro */
[data-bs-theme="dark"] {
    --badge-warning-bg: #664d03;
    --badge-warning-color: #ffda6a;
    --badge-warning-border: #997404;

    --badge-info-bg: #055160;
    --badge-info-color: #6edff6;
    --badge-info-border: #087990;

    --badge-success-bg: #0f5132;
    --badge-success-color: #75b798;
    --badge-success-border: #146c43;

    --badge-danger-bg: #842029;
    --badge-danger-color: #ea868f;
    --badge-danger-border: #a02834;

    --badge-secondary-bg: #41464b;
    --badge-secondary-color: #d3d3d4;
    --badge-secondary-border: #565e64;
}

/* Aplicar estilos a los badges */
.bg-label-warning.badge-estado {
    background-color: var(--badge-warning-bg) !important;
    color: var(--badge-warning-color) !important;
    border-color: var(--badge-warning-border) !important;
}

.bg-label-info.badge-estado {
    background-color: var(--badge-info-bg) !important;
    color: var(--badge-info-color) !important;
    border-color: var(--badge-info-border) !important;
}

.bg-label-success.badge-estado {
    background-color: var(--badge-success-bg) !important;
    color: var(--badge-success-color) !important;
    border-color: var(--badge-success-border) !important;
}

.bg-label-danger.badge-estado {
    background-color: var(--badge-danger-bg) !important;
    color: var(--badge-danger-color) !important;
    border-color: var(--badge-danger-border) !important;
}

.bg-label-secondary.badge-estado {
    background-color: var(--badge-secondary-bg) !important;
    color: var(--badge-secondary-color) !important;
    border-color: var(--badge-secondary-border) !important;
}

/* =========================================
   SKELETON LOADING (placeholders de carga)
   Estándar único de la app: markup en el partial _SkeletonStat.cshtml,
   toggle en window.Skeleton (Skeleton.global.js). Estos estilos viven
   SOLO acá (no duplicar en CSS de módulo).
   ========================================= */
@keyframes skeleton-loading {
    0% {
        background-position: 200% 0;
    }

    100% {
        background-position: -200% 0;
    }
}

.skeleton {
    background: #e0e0e0;
    background: linear-gradient(90deg, #e0e0e0 25%, #f0f0f0 50%, #e0e0e0 75%);
    background-size: 200% 100%;
    animation: skeleton-loading 1.5s infinite;
    border-radius: 4px;
    display: inline-block;
    /* Ensure it takes up space */
    min-height: 1em;
    /* Ensure minimum height */
}

/* Línea de texto placeholder (usar junto con .skeleton) */
.skeleton-text {
    height: 1em;
    width: 100%;
    margin-bottom: 0.5rem;
}

/* Valor de una stat-card (el número/fecha que todavía no llegó) */
.skeleton-stat {
    height: 2rem;
    width: 120px;
    max-width: 80%;
    margin-bottom: 0.25rem;
}

/* Contador inline dentro de una frase (ej. "X expedientes con cuenta corriente").
   Se aplica sobre el propio <span> del número; el JS le saca la clase al llegar el dato. */
.skeleton-inline {
    width: 2.5em;
    height: 1em;
    vertical-align: -0.15em;
}

/* Dark mode support for skeletons.
   Sneat marca el dark con la clase .dark-style en <html>; data-bs-theme='dark'
   solo existe en las páginas que cargan theme-integration.js — cubrir ambos. */
html.dark-style .skeleton,
[data-bs-theme='dark'] .skeleton {
    background: #3a3b55;
    background: linear-gradient(90deg, #3a3b55 25%, #494b6a 50%, #3a3b55 75%);
    background-size: 200% 100%;
}

/* =========================================
   INDICADOR DE ROBOTS EN NAVBAR
   Chips compactos PJN/MEV/SRT con punto de estado, a la izquierda del
   switcher de tema. Los pinta window.RobotStatus (RobotStatus.global.js)
   desde sessionStorage; el Dashboard refresca el cache con GetKPIs.
   Click → /Estudio/Usuario/Integraciones.
   ========================================= */
.robot-status-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.8125rem;
    font-weight: 500;
    line-height: 1;
}

.robot-status-chip .bx {
    font-size: 1.05rem;
}

.robot-status-dot {
    width: 0.55rem;
    height: 0.55rem;
    border-radius: 50%;
    display: inline-block;
    flex-shrink: 0;
}

/* ── Dropzone ───────────────────────────────────────────────────────────── */
.dropzone {
    background: transparent !important;
    border: 2px dashed var(--bs-border-color) !important;
    border-radius: var(--bs-border-radius) !important;
}
.dropzone:hover,
.dropzone.dz-drag-hover {
    border-color: #7367f0 !important;
    background: rgba(115, 103, 240, 0.06) !important;
}
/* Ocultar el pseudo-ícono del ::before (no lo necesitamos, el ícono va en el HTML) */
.dark-style .dropzone .dz-message::before,
.light-style .dropzone .dz-message::before {
    display: none !important;
}
/* Resetear el margen grande del tema (pensado para dropzones fullscreen) */
.dropzone .dz-message {
    margin: 1.5rem 0 !important;
}
.dropzone .dz-preview .dz-image {
    background: var(--bs-secondary-bg) !important;
}

/* ── Notificaciones dropdown ────────────────────────────────────────────── */
.dropdown-notifications-list.scrollable-container {
    max-height: 320px;
    overflow-y: auto;
}

.dropdown-notifications-item .notif-preview {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    line-height: 1.35;
    margin-bottom: 0.15rem !important;
}

.dropdown-notifications-item .d-flex {
    cursor: pointer;
}

/* ── Badge contador (número en lugar del puntito) ───────────────────────── */
.badge-notifications {
    position: absolute;
    top: -4px;
    right: -6px;
    min-width: 1.3rem;
    height: 1.3rem;
    font-size: 0.62rem;
    line-height: 1.3rem;
    padding: 0 0.3rem;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* ── Campana animada al recibir notificación ────────────────────────────── */
@keyframes bellShake {
    0%, 100% { transform: rotate(0deg); }
    10%       { transform: rotate(-18deg); }
    20%       { transform: rotate(18deg); }
    30%       { transform: rotate(-14deg); }
    40%       { transform: rotate(14deg); }
    50%       { transform: rotate(-8deg); }
    60%       { transform: rotate(8deg); }
    70%       { transform: rotate(-4deg); }
    80%       { transform: rotate(4deg); }
}

.bell-ring {
    animation: bellShake 0.8s ease;
    display: inline-block;
    transform-origin: top center;
}

/* ── Tostadas — ajuste visual compartido (toastr SignalR + Bootstrap Tostadas) ──
   Ambos sistemas comparten el contenedor #toast-container. Estas reglas genéricas
   (radios, sombra, ancho, tipografía) aplican a los dos. Lo específico de toastr
   (gutter del ícono, colores por tipo) va scopeado más abajo para NO filtrarse a
   las tostadas Bootstrap (.bs-toast). */
#toast-container > div {
    border-radius: 8px !important;
    box-shadow: 0 4px 18px rgba(0,0,0,0.18) !important;
    min-width: 300px;
    max-width: 380px;
    font-size: 0.875rem;
    opacity: 1 !important;
}

/* El gutter izquierdo de 50px es para el ícono sprite de toastr; no aplicarlo a las
   tostadas Bootstrap (.bs-toast), que ya traen su propio header/body con padding. */
#toast-container > div.toast:not(.bs-toast) {
    padding: 14px 16px 14px 50px !important;
}

#toast-container .toast-title {
    font-weight: 700;
    font-size: 0.875rem;
    margin-bottom: 3px;
}

#toast-container .toast-message {
    font-size: 0.8rem;
    line-height: 1.45;
    opacity: 0.92;
}

/* ── Utilidades genéricas ───────────────────────────────────────────────── */
.cursor-pointer {
    cursor: pointer;
}

.text-prewrap {
    white-space: pre-line;
}

/* Colores de toastr alineados a la paleta Sneat (mismos hex que bg-success/-danger/
   -warning/-info de core.css) para que la campanita SignalR se vea igual que las
   tostadas Bootstrap. Estas clases son exclusivas de toastr, así que no afectan a
   las tostadas Bootstrap (que colorean vía bg-* utilities). */
#toast-container .toast-success {
    background-color: #71dd37 !important;
}

#toast-container .toast-error {
    background-color: #ff3e1d !important;
}

#toast-container .toast-warning {
    background-color: #ffab00 !important;
}

#toast-container .toast-info {
    background-color: #03c3ec !important;
}

/* Fondo saturado ⇒ texto blanco. El vendor de Sneat (toastr.css, scope .light-style)
   pinta el título/mensaje del MISMO color saturado que acá forzamos como fondo (su
   diseño es texto de color sobre toast blanco). Al invertir el fondo a ese color, el
   texto quedaba del mismo color que el fondo = invisible (bug: toast sin texto).
   .toast-title/.toast-message/.toast-close-button son clases exclusivas de toastr,
   así que esto NO afecta a las tostadas Bootstrap (.bs-toast, que usan .toast-body). */
#toast-container .toast-title,
#toast-container .toast-message,
#toast-container .toast-close-button {
    color: #fff !important;
}

#toast-container .toast-progress {
    opacity: 0.35;
    height: 3px;
}

/* ── Token Variables (Plantillas TinyMCE) ───────────────────────────────── */
.token-variable {
    display: inline-block;
    background-color: #7367f0;
    color: #fff !important;
    border-radius: 20px;
    padding: 2px 10px;
    font-size: 0.82em;
    font-weight: 500;
    user-select: none;
    cursor: default;
    white-space: nowrap;
    line-height: 1.6;
}

/* Toast container: en mobile reposicionar para no tapar la navbar superior
   ni dropdowns que se abren desde la esquina derecha. */
@media (max-width: 575.98px) {
    #toast-container.toast-container.position-fixed {
        top: auto !important;
        bottom: 1rem !important;
        left: 0.5rem !important;
        right: 0.5rem !important;
        max-width: none !important;
    }
    #toast-container .toast {
        width: 100%;
        max-width: none;
    }
}

/* ========== ACCESIBILIDAD: REDUCED MOTION ==========
   Respetar la preferencia del usuario por menos movimiento.
   Aplica globalmente: animaciones casi instantáneas, transiciones mínimas.
   Cubre pulse, ring, scale, translate y cualquier transition de los customs. */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.01ms !important;
        scroll-behavior: auto !important;
    }
}

/* ========== SELECT2 VARIANTE "SM" ==========
   Iguala la altura/tipografía del control select2 con los .form-control-sm
   cuando conviven en una misma fila compacta (p.ej. las filas de Matrículas en
   Estudio/Usuario/Configuracion, donde Jurisdicción es un select2 y Nro. de
   matrícula / Colegio son inputs -sm). Se activa pasando
   containerCssClass: 'select2-sm' al inicializar select2.
   Métricas espejadas de Bootstrap .form-control-sm: font-size .8125rem,
   line-height 1.5, padding-y .25rem (=> .5rem total), borde 2px. */
.light-style .select2-container--default.select2-sm .select2-selection--single,
.light-style .select2-container--default.select2-sm .select2-selection--single .select2-selection__arrow {
    height: calc(1.5 * 0.8125rem + 0.5rem + 2px);
}
.light-style .select2-container--default.select2-sm .select2-selection--single .select2-selection__rendered {
    font-size: 0.8125rem;
    line-height: calc(1.5 * 0.8125rem + 0.5rem);
}
.light-style .select2-container--default:not([dir=rtl]).select2-sm .select2-selection--single .select2-selection__rendered {
    padding-left: 0.5rem;
}

/* ========== Modal "Vincular expediente" (Caso/Detalle) ========== */
/* Un poco más ancho que el default (500px) para que las carátulas largas
   no se corten tanto, y altura suficiente para que el desplegable de
   select2 (dropdownParent = .modal-content) no se salga del modal. */
#modalVincular .modal-dialog {
    max-width: 620px;
}
#modalVincular .modal-body {
    min-height: 300px;
}
#modalVincular .select2-results__options {
    max-height: 220px;
}
/* =============================================================================
   Calculadora de Plazos — días inhábiles pintados como fondo en el calendario
   de tareas. El fondo es informativo y NO bloquea la creación de eventos.
   ============================================================================= */
.fc .fc-event-inhabil.fc-bg-event .fc-inhabil-label {
    font-size: 0.7rem;
    font-weight: 600;
    line-height: 1.1;
    padding: 2px 4px;
    /* El evento de fondo llena toda la celda y su label arrancaba arriba-izquierda,
       pisando el número del día. Lo bajamos debajo de la fila del número. */
    margin-top: 1.5rem;
    color: #4b4b4b;
    pointer-events: none;
}

/* =============================================================================
   PWA — botón flotante "Instalar app" (Android prompt nativo / iOS instructivo).
   Lo controla js/pwa-install.js; sólo aparece cuando la app es instalable y no
   está ya instalada.
   ============================================================================= */
.gerux-pwa-fab-wrapper {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 1080;
    display: inline-flex;
    align-items: flex-start;
}
.gerux-pwa-fab {
    border-radius: 2rem;
    padding-inline: 1rem;
}
.gerux-pwa-fab-close {
    margin-left: -0.5rem;
    margin-top: -0.5rem;
    background-color: #1B2B4D;
    border-radius: 50%;
    padding: 0.35rem;
    font-size: 0.6rem;
    opacity: 1;
    box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.2);
}
/* El FAB de ayuda (#helpFab, help-center.css) ocupa la misma esquina inferior
   derecha. Donde exista, el pill de instalación se apila encima en vez de
   superponerse; en las páginas sin ayuda (login, landing) queda abajo de todo. */
body:has(#helpFab) .gerux-pwa-fab-wrapper {
    bottom: 5rem;
}
@media (max-width: 575.98px) {
    body:has(#helpFab) .gerux-pwa-fab-wrapper {
        bottom: 8.25rem;
    }
}

/* Los dos flotantes (FAB de ayuda + pill de instalación) viven pegados al viewport,
   así que al llegar al final del scroll se comen la última franja de la página: en el
   celular tapaban el botón "Nueva tarea" / "Nuevo evento" al pie de los widgets de la
   ficha. Se le da al contenido una zona muerta abajo del alto del stack flotante, para
   que lo último de la página se pueda subir por encima de los botones.
   Sólo mientras los flotantes existen: la pill se cierra o no aparece (app ya instalada
   o navegador que no la ofrece) y ahí el colchón vuelve al del FAB solo. */
@media (max-width: 575.98px) {
    body:has(#helpFab) .content-wrapper {
        padding-bottom: 7.5rem;
    }

    body:has(#helpFab):has(.gerux-pwa-fab-wrapper) .content-wrapper {
        padding-bottom: 11rem;
    }
}

/* =============================================================================
   Navbar fijo: banda opaca detrás, para que el contenido no se vea pasar.

   El navbar de Sneat es fixed y además "detached": flota como pastilla dejando
   1rem de aire arriba y 1rem/1.625rem a los costados (.navbar-detached, core.css),
   y encima su fondo es semitransparente (.bg-navbar-theme = rgba(255,255,255,.88)).
   Resultado: al scrollear, el contenido se ve pasar por el aire de arriba Y en
   fantasma a través de la pastilla — en la ficha del expediente se leía la carátula
   flotando sobre la barra, encimada con los iconos del navbar.

   La banda tapa los 4.875rem de arriba (1rem de aire + 3.875rem de navbar) con el
   fondo del body, que es exactamente lo que se ve ahí cuando la página está arriba
   de todo: no cambia el aspecto en reposo, sólo corta lo que pasaba por detrás.

   Va montada en .content-wrapper y no en el propio navbar a propósito: un pseudo del
   navbar con z-index negativo se pinta ARRIBA del fondo del navbar (orden de pintado
   de un stacking context), y le comía el blanco a la pastilla.

   z-index 1074 = uno menos que el navbar (1075 en este layout), así queda por detrás
   de la barra y por delante del contenido. El body:not(.modal-open) copia el criterio
   que ya usa Sneat para el navbar: con un modal abierto la barra baja de z-index y la
   banda tiene que bajar con ella, si no taparía el modal.
   ============================================================================= */
.layout-navbar-fixed body:not(.modal-open) .layout-content-navbar .content-wrapper::before {
    content: "";
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 4.875rem;
    background-color: var(--bs-body-bg);
    z-index: 1074;
    pointer-events: none;
}

/* =============================================================================
   Texto Proveído del modal de detalle de movimiento. Por defecto va compacto
   con scroll; el botón "Ampliar" (js en _DetalleMovimientoModal.cshtml) agrega
   .proveido-ampliado para leerlo sin tope de altura y con fuente más grande.
   ============================================================================= */
.proveido-texto {
    max-height: 180px;
    overflow-y: auto;
    white-space: pre-wrap;
    line-height: 1.6;
}
.proveido-texto.proveido-ampliado {
    max-height: none;
    font-size: 0.95rem;
    line-height: 1.7;
}

/* =============================================================================
   session-monitor.js — modal "Sesión por expirar".
   Estilos movidos aquí desde el <style> inline del JS (regla del proyecto: sin
   bloques <style> ni estilos inline). Look Sneat/GERUX: card sin bordes duros,
   avatar label-warning y círculo de cuenta regresiva con la marca (violeta →
   navy), que vira a rojo cuando el JS le agrega .is-urgent (≤30 s restantes).
   ============================================================================= */
.session-warning-modal {
    border: none;
    border-radius: var(--bs-border-radius-lg, 0.625rem);
    box-shadow: 0 0.75rem 2rem rgba(0, 0, 0, 0.18);
}

.session-warning-avatar {
    width: 72px;
    height: 72px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: rgba(var(--bs-warning-rgb), 0.16);
    color: var(--bs-warning);
}

.session-warning-avatar i {
    font-size: 2.5rem;
}

.session-countdown-container {
    display: flex;
    justify-content: center;
}

.session-countdown-circle {
    width: 120px;
    height: 120px;
    border-radius: 50%;
    background: linear-gradient(135deg, #7367f0 0%, #1B2B4D 100%);
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    color: #fff;
    box-shadow: 0 0.5rem 1.25rem rgba(115, 103, 240, 0.4);
    animation: sessionCountdownPulse 1s infinite;
}

/* Estado de urgencia: el JS agrega .is-urgent cuando quedan ≤30 s */
.session-countdown-circle.is-urgent {
    background: linear-gradient(135deg, #ff9800 0%, #f44336 100%);
    box-shadow: 0 0.5rem 1.25rem rgba(244, 67, 54, 0.4);
}

.session-countdown-number {
    font-size: 2.5rem;
    font-weight: 700;
    line-height: 1;
}

.session-countdown-label {
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.5px;
    color: rgba(255, 255, 255, 0.9);
}

@keyframes sessionCountdownPulse {
    0%, 100% { transform: scale(1); }
    50% { transform: scale(1.03); }
}


/* ============================================================================
   Modal en modo solo-lectura (patrón compartido: Personas, Usuarios).
   Atenúa los inputs y oculta el botón de guardar cuando el modal se abre
   para "ver" en vez de editar. Movido desde los <style> embebidos de
   Persona/Index.cshtml y Usuario/Index.cshtml.
   ============================================================================ */
.modal-readonly .form-control,
.modal-readonly .form-select {
    background-color: #f8f9fa;
    border-color: #dee2e6;
    color: #6c757d;
}

.modal-readonly .btn-primary {
    display: none;
}


/* ============================================================================
   Campos obligatorios (estándar — ver CLAUDE.md "Campos obligatorios en
   formularios"). El label lleva class="form-label required" y el asterisco
   rojo lo pone este CSS: nunca escribir <span class="text-danger">*</span>
   a mano (además, los limpiadores de errores tipo $('.text-danger').text('')
   los borraban). El input lleva el atributo `required`, fuente de verdad
   que valida window.Validacion (Validacion.global.js).
   ============================================================================ */
.form-label.required::after {
    content: " *";
    color: var(--bs-danger);
}

/* Borde rojo en el widget de select2 cuando el <select> real quedó marcado
   con .is-invalid (select2 oculta el select original, el borde se ve acá). */
select.is-invalid + .select2 .select2-selection {
    border-color: var(--bs-form-invalid-border-color, #ff3e1d);
}

/* ============================================================================
   Navbar de páginas públicas (.landing-navbar, compartido por landing,
   precios y centro de ayuda).
   En la franja lg (992–1199px) el menú completo + theme switcher + botones
   "Ingresar"/"Probar gratis" quedaban justos de ancho y los botones se
   apilaban uno arriba del otro. Además del flex-nowrap en el markup
   (_NavbarFront.cshtml) y del container-xxl, compactamos los botones solo
   en esa franja para que toda la fila entre cómoda.
   ============================================================================ */
@media (min-width: 992px) and (max-width: 1199.98px) {
    .navbar.landing-navbar .navbar-nav .btn {
        padding-left: 0.875rem;
        padding-right: 0.875rem;
        font-size: 0.875rem;
    }
}

/* ============================================================================
   Campo secreto que NO es una contraseña de GERUX: credenciales de portales
   judiciales (PJN, MEV, SRT, JUSCABA).

   Chrome/Edge ofrecen guardar en su gestor CUALQUIER <input type="password">,
   sin importar de qué sea la clave. Al aceptar, la credencial del portal
   quedaba guardada contra el origen de GERUX y después aparecía autocompletada
   en el login de GERUX como si fuera un usuario de la app (bug reportado con
   JUSCABA; pasaba con los cuatro portales, el modal es el mismo).

   Fix: estos campos son <input type="text"> —los gestores de contraseñas solo
   miran type="password"— y el enmascarado lo hace este CSS. El botón de ojito
   togglea .input-secreto-visible en vez de cambiar el `type`.

   NO usar esta clase en contraseñas propias de GERUX (login, registro, cambio
   de contraseña): ahí ofrecer guardarlas es el comportamiento correcto.
   ============================================================================ */
.input-secreto {
    -webkit-text-security: disc;
    text-security: disc;
}

.input-secreto.input-secreto-visible {
    -webkit-text-security: none;
    text-security: none;
}
