/* ============================================================
   EJN — Estilos de aplicación
   Se apoya en ejn-tokens.css (fuente de verdad de la marca).
   Regla del manual §04: el color del módulo es ACENTO,
   nunca color dominante. Base neutra siempre.
   ============================================================ */

/* Montserrat self-hosted — tipografía oficial del manual v4 §4.
   La CSP es `font-src 'self'`: no se puede tirar de Google Fonts, hay
   que dejar los .woff2 en /assets/fonts/. Si los archivos todavía no
   están, el navegador cae al fallback del sistema sin romper nada,
   pero el wordmark NO se ve como el manual.

   Archivos esperados (descargar de fonts.google.com/specimen/Montserrat):
     Montserrat-Regular.woff2   (400)
     Montserrat-Medium.woff2    (500)
     Montserrat-SemiBold.woff2  (600)
     Montserrat-Bold.woff2      (700) */
@font-face {
  font-family: 'Montserrat';
  src: url('/assets/fonts/Montserrat-Regular.woff2') format('woff2');
  font-weight: 400; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Montserrat';
  src: url('/assets/fonts/Montserrat-Medium.woff2') format('woff2');
  font-weight: 500; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Montserrat';
  src: url('/assets/fonts/Montserrat-SemiBold.woff2') format('woff2');
  font-weight: 600; font-style: normal; font-display: swap;
}
@font-face {
  font-family: 'Montserrat';
  src: url('/assets/fonts/Montserrat-Bold.woff2') format('woff2');
  font-weight: 700; font-style: normal; font-display: swap;
}

*, *::before, *::after { box-sizing: border-box; }

/* Reset de tipografia para controles de formulario (17/09/2026, reporte de
   Daniel: campos en investigacion.php con tipografia nativa del navegador
   pese a que el CSS de la plataforma ya define font-family en body.ejn --
   raiz del problema: <input>/<select>/<textarea>/<button> NO heredan
   font-family del documento por defecto en NINGUN navegador, la hoja de
   estilos del user-agent les fuerza su propia fuente de control nativo
   salvo que se pise explicitamente. Esto se venia parchando caso por caso
   cada vez que alguien lo notaba (fechas sueltas 14/09, RGRL 11/09,
   tabla-simple--formulario) -- pedido explicito de Daniel de "dejarlo
   resuelto para que no se repita". Este reset global, de especificidad
   minima (selectores de elemento sin clase), cierra el hueco de una vez
   para TODO campo -- actual y futuro, .campo o suelto, ERP o publico
   (ejn-app.css la cargan ambos) -- sin que ningun chat tenga que acordarse
   de repetir font-family campo por campo. No pisa ninguna regla mas
   especifica que ya lo fijaba a proposito (fechas, tabla-simple--formulario,
   etc.): esas siguen ganando por especificidad, esto solo cubre los casos
   que hoy no tenian nada. */
input, select, textarea, button { font-family: inherit; }

/* El reset de arriba fija font-family en el <input type="date"> anfitrion,
   pero los "segmentos" que se ven adentro (dd/mm/aaaa) los dibuja el propio
   navegador con un control interno (shadow DOM del user-agent) que en
   motores WebKit/Blink (Chrome, Edge, Safari -- que es como Daniel accede)
   NO siempre hereda el font-family del host aunque el CSS de afuera ya lo
   fije: hay que apuntarle explicitamente a esas partes internas via sus
   pseudo-elementos -webkit-. Encontrado 17/09/2026 (reporte de Daniel,
   captura): el campo "Fecha programada" de Agenda de visitas en
   investigacion.php seguia con tipografia nativa pese al reset de arriba --
   pero el problema es generico a CUALQUIER <input type="date"> de la
   plataforma, no puntual de esa pantalla, por eso el fix va aca y no en una
   vista puntual. Firefox no expone estos pseudo-elementos (los ignora sin
   romper nada) porque ahi el selector nativo de fecha ya hereda bien. */
input[type="date"]::-webkit-datetime-edit,
input[type="date"]::-webkit-datetime-edit-fields-wrapper,
input[type="date"]::-webkit-datetime-edit-text,
input[type="date"]::-webkit-datetime-edit-day-field,
input[type="date"]::-webkit-datetime-edit-month-field,
input[type="date"]::-webkit-datetime-edit-year-field {
  /* 17/09/2026, segundo reporte de Daniel (misma pantalla, seguia mal):
     "font-family: inherit" no alcanzo -- la hipotesis es que estos
     pseudo-elementos del shadow DOM del navegador no resuelven "inherit"
     ni una custom property (var(--font-family)) de forma confiable en
     todas las versiones de Chrome, porque ambos mecanismos dependen de
     la cascada de herencia normal, que el arbol de sombra del user-agent
     no siempre respeta igual que el DOM liviano. Se pisa con el valor
     final ya resuelto, sin depender de herencia ni de variables. */
  font-family: 'Montserrat', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}

/* [hidden] no siempre gana: cualquier contenedor con display explicito
   (grid/flex, ej. .fila) tiene la misma especificidad que el [hidden]
   del user-agent y, por venir de una hoja de autor, le gana -- el
   elemento queda oculto en el DOM pero sigue pintandose en pantalla.
   Encontrado 09/09/2026: iniciarCamposCondicionales() ocultaba
   correctamente el establecimiento en Tipo=in_itinere (hidden=true en
   el DOM) pero .fila{display:grid} lo mantenia visible igual. Regla
   global, no por componente -- cualquier data-visible-si futuro con
   display explicito tiene la misma falla si no esta esto. */
[hidden] { display: none !important; }

body.ejn {
  margin: 0;
  font-family: var(--font-family);
  font-size: var(--fs-body);
  color: var(--gray-900);
  background: var(--gray-50);
  line-height: var(--lh-normal);
  -webkit-font-smoothing: antialiased;
}

a { color: var(--ejn-blue-dark); }

/* ---------- Pantalla de acceso ---------- */

/* Selector con la misma especificidad que body.ejn, y posterior,
   para que gane el fondo navy de la pantalla de acceso. */
body.ejn-publico {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  /* 06/09/2026: manchas degradadas indigo/celeste detras de la tarjeta de
     vidrio (antes blanco plano) -- pedido de Daniel de estilo "Cristal /
     Vidrio Diurno". Fijo (no scrollea) porque la pantalla de acceso no
     scrollea en la practica. */
  background:
    radial-gradient(760px circle at 14% 10%, rgba(94,92,255,.20), transparent 60%),
    radial-gradient(820px circle at 88% 92%, rgba(143,217,255,.24), transparent 60%),
    var(--gray-50);
  background-attachment: fixed;
}

.acceso {
  flex: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-5);
}

.acceso__pie {
  text-align: center;
  padding: var(--space-4);
  font-size: var(--fs-small);
  color: var(--gray-400);
}

/* 06/09/2026: pedido nuevo de Daniel ("Cristal / Vidrio Diurno") reemplaza
   el pedido anterior de esta misma sesion (tarjeta blanca fundida con la
   pagina, sin sombra/borde). Ahora la tarjeta es vidrio real -- blur +
   traslucidez + sombra suave -- flotando sobre las manchas degradadas del
   fondo (ver body.ejn-publico). El logo no se toca. */
.tarjeta-acceso {
  width: 100%;
  max-width: 400px;
  background: var(--cristal-glass-bg-strong);
  backdrop-filter: blur(var(--cristal-blur)) saturate(160%);
  -webkit-backdrop-filter: blur(var(--cristal-blur)) saturate(160%);
  border: 1px solid var(--cristal-glass-border);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  box-shadow: var(--shadow-lg);
}

.tarjeta-acceso__marca {
  text-align: center;
  margin-bottom: var(--space-6);
}

/* El ícono va libre, sin contenedor. En el manual el aro circular es
   sólo la variante avatar (§5), no la identidad principal. */
.escudo-circulo {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}

/* «EJN» en bold y el nombre del producto en regular con su color,
   igual que el bloque de marca del manual. */
.wordmark {
  margin: var(--space-3) 0 0;
  font-size: 34px;
  font-weight: var(--fw-bold);
  letter-spacing: 3px;
  color: var(--ejn-navy);
}

.tagline {
  margin: 6px 0 0;
  font-size: 10.5px;
  font-weight: var(--fw-medium);
  letter-spacing: 2.4px;
  line-height: 1.7;
  text-transform: uppercase;
  color: var(--gray-600);
}

/* ---------- Formularios ---------- */

.formulario { display: flex; flex-direction: column; gap: var(--space-4); }

.campo { display: flex; flex-direction: column; gap: 6px; }

/* Lista compacta de checkboxes (08/09/2026, pedido de Daniel: el bloque
   "Contingencia, enfermedad y objeto reclamado" de Legales perdía mucho
   espacio vertical con un checkbox por línea). Gap chico a propósito --
   son opciones de un mismo grupo, no campos independientes como .campo. */
.lista-check { display: flex; flex-direction: column; gap: 2px; }

/* .campo--check con tipografía propia, en vez de depender de heredarla de
   ".campo label" (08/09/2026, bug reportado por Daniel: los 4 checkboxes de
   "Reclamo" se veían con otra tipografía que los de "Contingencia" -- los
   de Contingencia están anidados en un .campo y sí heredaban el estilo, los
   de Reclamo quedaron sueltos dentro de .lista-check al sacarlos de la
   fila--4 y no heredaban nada, mostrando el estilo default del navegador).
   Poniéndolo acá en vez de en .campo label, .campo--check se ve igual
   tenga o no un .campo como ancestro. */
.campo--check { font-size: var(--fs-small); font-weight: var(--fw-semibold); color: var(--gray-700); }

.campo label {
  font-size: var(--fs-small);
  font-weight: var(--fw-semibold);
  color: var(--gray-700);
}

.campo input {
  font-family: var(--font-family);
  font-size: var(--fs-body);
  padding: 10px 12px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm);
  background: var(--white);
  color: var(--gray-900);
  transition: border-color .15s, box-shadow .15s;
}

.campo input:focus {
  outline: none;
  border-color: var(--ejn-blue);
  box-shadow: 0 0 0 3px var(--ejn-blue-soft);
}

/* ---------- Desplegables y áreas de texto ----------
   Un desplegable es un campo de texto que además abre una lista: mismo trazo,
   mismo radio, mismo fondo y mismo foco. Se le saca la apariencia nativa del
   sistema operativo y se le dibuja la flecha, porque si no cada navegador lo
   pinta distinto y en la misma fila conviven dos alturas y dos tipografías.

   Vive acá y no en `ejn-erp.css` a propósito: las pantallas públicas —login y
   alta de canal— no cargan la hoja del ERP, y ahí los desplegables quedaban
   nativos al lado de campos de texto ya estilados. */
.campo select,
select {
  -webkit-appearance: none;
  -moz-appearance: none;
  appearance: none;
  font-family: var(--font-family);
  font-size: var(--fs-body);
  line-height: 1.4;
  padding: 10px 30px 10px 12px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm);
  background-color: var(--white);
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%234B5563' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  color: var(--gray-900);
  cursor: pointer;
  transition: border-color .15s, box-shadow .15s;
}

.campo textarea {
  font-family: var(--font-family);
  font-size: var(--fs-body);
  line-height: 1.5;
  padding: 10px 12px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm);
  background: var(--white);
  color: var(--gray-900);
  resize: vertical;
  transition: border-color .15s, box-shadow .15s;
}

select:hover { border-color: var(--gray-400); }

.campo select:focus,
.campo textarea:focus,
select:focus {
  outline: none;
  border-color: var(--ejn-blue);
  box-shadow: 0 0 0 3px var(--ejn-blue-soft);
}

.campo select:disabled,
.campo textarea:disabled,
.campo textarea[readonly],
select:disabled {
  background-color: var(--gray-50);
  color: var(--gray-600);
  cursor: default;
  opacity: 1;
}

/* El input de archivo no se puede estilar por dentro: se estila el botón. */
.campo input[type="file"] { padding: 7px 10px; cursor: pointer; }

.campo input[type="file"]::file-selector-button {
  font-family: var(--font-family);
  font-size: var(--fs-small);
  font-weight: var(--fw-semibold);
  margin-right: 10px;
  padding: 5px 12px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm);
  background: var(--gray-50);
  color: var(--gray-700);
  cursor: pointer;
}

.campo input[type="file"]::file-selector-button:hover {
  border-color: var(--ejn-blue);
  color: var(--ejn-blue);
}

/* Bug real (06/09/2026): un input[type=file] disabled se veia identico al
   habilitado -- el boton nativo no hereda ningun estilo del :disabled del
   input padre por default. Sin esto, el click no abria el picker y no
   habia ninguna pista visual de por que. */
.campo input[type="file"]:disabled {
  cursor: default;
}

.campo input[type="file"]:disabled::file-selector-button {
  cursor: default;
  background-color: var(--gray-50);
  color: var(--gray-400);
  border-color: var(--gray-200);
}

/* El transition faltaba sólo en el input: se unifica para los tres. */
.campo input { transition: border-color .15s, box-shadow .15s; }

/* Campos con error de validación (08/09/2026, pedido de Daniel): al fallar
   un alta/edición se resalta en rojo el campo puntual que causó el rechazo,
   sin perder lo tipeado -- primera implementación en Legales/estudios
   jurídicos, ver docs/PENDIENTES_PLATAFORMA.md para la regla general que se
   pide aplicar a toda la plataforma. Reusa los mismos tokens que ya pintan
   de rojo .badge--dang/.aviso-barra--dang, no crea colores nuevos. */
.campo--error label { color: var(--ejn-red-dark); }

.campo--error input,
.campo--error select,
.campo--error textarea {
  border-color: var(--ejn-red);
  background: var(--ejn-red-soft);
}

.campo--error input:focus,
.campo--error select:focus,
.campo--error textarea:focus {
  border-color: var(--ejn-red);
  box-shadow: 0 0 0 3px var(--ejn-red-soft);
}

/* ---------- Legajo: un archivo por requisito ----------
   Se sube de a uno porque un error en el último documento no debe obligar a
   volver a elegir los seis anteriores. */
/* Texto que el sistema le va a mostrar a otro tal cual está escrito. Se
   destaca para que quien decide vea exactamente qué va a leer el otro. */
.cita {
  margin: 8px 0 0;
  padding: 10px 14px;
  border-left: 3px solid var(--modulo-color, var(--gray-400));
  background: var(--gray-50);
  color: var(--gray-700);
  font-size: var(--fs-small);
  font-style: italic;
}

/* Avisos propios del canal. No son la banda de gestión de la aseguradora:
   ésos hablan de web services e intercambios y el canal no los ve. */
.avisos { list-style: none; margin: 0 0 var(--space-3); padding: 0; display: flex; flex-direction: column; gap: 8px; }

.avisos__item {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 12px 14px;
  border: 1px solid var(--ejn-amber-300, #E8C77A);
  border-left-width: 3px;
  border-radius: var(--radius-sm);
  background: var(--ejn-amber-50, #FDF8EC);
}

/* Acción que existe pero todavía no corresponde. Se muestra apagada en lugar
   de esconderla: el usuario tiene que saber que el paso está y por qué no. */
.btn--inhabilitado {
  opacity: .45;
  cursor: not-allowed;
}

.avisos__cuerpo { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.avisos__titulo { font-size: var(--fs-small); font-weight: var(--fw-semibold); color: var(--gray-800); }
.avisos__texto  { font-size: var(--fs-small); color: var(--gray-700); }
.avisos__pie    { font-size: 11px; color: var(--gray-500); }

.legajo { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }

.legajo__fila {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
  background: var(--white);
}

.legajo__fila--ok { border-color: var(--gray-300); background: var(--gray-50); }

.legajo__datos  { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.legajo__nombre { font-size: var(--fs-small); font-weight: var(--fw-semibold); color: var(--gray-800); }
.legajo__obl    { color: var(--ejn-red, #C94434); font-weight: var(--fw-bold); }
.legajo__ayuda  { font-size: 11px; color: var(--gray-500); }
.legajo__archivo{ font-size: 11px; color: var(--gray-600); font-family: var(--font-mono, monospace); }

.legajo__forma  { display: flex; align-items: center; gap: 8px; }
.legajo__forma .campo { gap: 0; }
.legajo__forma input[type="file"] { font-size: 11.5px; padding: 4px 8px; max-width: 260px; }

@media (max-width: 720px) {
  .legajo__fila  { grid-template-columns: 1fr; }
  .legajo__forma { flex-wrap: wrap; }
}

.btn--bloque { width: 100%; justify-content: center; }

.nota {
  margin: var(--space-5) 0 0;
  font-size: var(--fs-small);
  color: var(--gray-600);
  text-align: center;
}
/* Secreto/clave manual de TOTP: monospace, se puede cortar en cualquier
   punto para no desbordar la tarjeta angosta de auth. */
.nota--codigo { font-family: monospace; letter-spacing: .1em; word-break: break-all; }
/* Lista de códigos de recuperación 2FA: sin viñetas, un código por línea. */
.nota--codigos-lista { font-family: monospace; font-size: 1.1em; line-height: 2; list-style: none; padding: 0; }

/* Wrapper del QR de configuración TOTP. */
.totp-qr { text-align: center; margin: 1rem 0; }
.totp-qr img { max-width: 100%; }

/* Franja decorativa con la familia de módulos. */
.familia-modulos {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--space-4);
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--gray-100);
}

.familia-modulos img {
  opacity: .55;
  transition: opacity .2s;
}

.tarjeta-acceso:hover .familia-modulos img { opacity: .85; }

/* ---------- Alertas ---------- */

.alerta {
  margin: 0 0 var(--space-4);
  padding: 10px 14px;
  border-radius: var(--radius-sm);
  font-size: var(--fs-small);
  border: 1px solid transparent;
}

.alerta--error       { background: var(--ejn-red-soft);   color: var(--ejn-red-dark);   border-color: #F1C4C4; }
.alerta--info        { background: var(--ejn-blue-soft);  color: var(--ejn-blue-dark);  border-color: #C7E0F2; }
.alerta--advertencia { background: var(--ejn-gold-soft);  color: var(--ejn-gold-dark);  border-color: #EFD9A0; }

/* ---------- Layout del panel ---------- */

/* 12/09/2026: el fondo tornasolado (ver historial arriba en el commit
   de esta misma fecha) quedaba lindo pero Daniel prefirio volver a
   fondo blanco plano y usar sombra en las tarjetas/formularios en su
   lugar (.tarjeta, ejn-erp.css) para dar sensacion de profundidad.
   .app es el que realmente pinta el fondo visible (cubre 100vh encima
   de <body>), por eso el valor vive aca. */
.app {
  display: flex; height: 100vh; overflow: hidden;
  background: var(--white);
}

.barra-lateral {
  width: var(--sidebar-width);
  flex-shrink: 0;
  background: var(--sidebar-bg);
  backdrop-filter: blur(var(--cristal-blur));
  -webkit-backdrop-filter: blur(var(--cristal-blur));
  border-right: 1px solid var(--sidebar-borde);
  box-shadow: var(--shadow-sm);
  color: var(--sidebar-texto);
  display: flex;
  flex-direction: column;
  padding: var(--space-5) var(--space-4);
}  /* 06/09/2026: vidrio (blur+sombra suave) -- pedido de Daniel "Cristal / Vidrio Diurno" */

.barra-lateral__marca {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-bottom: var(--space-5);
  border-bottom: 1px solid var(--sidebar-borde);
  text-decoration: none;
  color: var(--ejn-navy);
}

/* Wordmark: «EJN» en bold y el descriptor en regular, como manda el
   manual. El descriptor toma el color del módulo activo. */
.barra-lateral__wordmark {
  font-size: 19px;
  font-weight: var(--fw-regular);
  letter-spacing: .3px;
  line-height: 1.1;
  color: var(--ejn-navy);
}

.barra-lateral__wordmark b {
  font-weight: var(--fw-bold);
}

.barra-lateral__wordmark em {
  font-style: normal;
  color: var(--module-accent, var(--ejn-teal));
}

/* Wordmark RixART trazado (05/09/2026): reemplaza el texto "EJN ART" a
   nivel de marca madre (sin módulo activo) por el SVG real de la placa,
   fiel al manual -- nunca texto con fuente del sistema. currentColor
   toma el color ya definido por el contenedor (.wordmark o
   .barra-lateral__wordmark). */
.wordmark-rixart { display: block; width: auto; }
.wordmark .wordmark-rixart { height: 34px; }
.barra-lateral__wordmark .wordmark-rixart { height: 16px; }

/* Con el menu desplegado se ve solo el wordmark "RIXART"; el isologo (R
   sobre placa navy) sólo aparece cuando el menu esta contraido -- ahí no
   entra texto y hace falta un simbolo (pedido de Daniel, 06/09/2026). */
.barra-lateral__marca .marca-escudo { display: none; }
.app--plegado .barra-lateral__marca .marca-escudo { display: block; }

.barra-lateral__nav {
  flex: 1;
  padding-top: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: 2px;
  overflow-y: auto;
}

.nav-titulo {
  margin: var(--space-4) 0 6px;
  padding: 0 12px;
  font-size: var(--fs-caption);
  font-weight: var(--fw-semibold);
  letter-spacing: .6px;
  text-transform: uppercase;
  color: var(--gray-400);
}

.nav-item {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 9px 12px;
  border-radius: var(--radius-sm);
  color: var(--sidebar-texto);
  text-decoration: none;
  font-size: var(--fs-body);
  border-left: 3px solid transparent;
}

a.nav-item:hover { background: var(--gray-100); color: var(--ejn-navy); }

/* Punto con el color del módulo: identifica cada entrada de un vistazo. */
.nav-item__punto {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
  background: var(--module-accent, var(--gray-300));
}

/* El ítem activo lleva el acento del módulo en curso. */
.nav-item--activo {
  background: var(--module-accent-soft, var(--ejn-azul-acento-soft));
  color: var(--ejn-navy);
  font-weight: var(--fw-semibold);
  border-left-color: var(--module-accent, var(--ejn-azul-acento));
}

/* Módulo sin permiso: visible pero inerte. */
.nav-item--bloqueado {
  color: var(--gray-400);
  cursor: not-allowed;
}

.nav-item--bloqueado .nav-item__punto { background: var(--gray-300); }

.barra-lateral__pie {
  padding-top: var(--space-4);
  border-top: 1px solid var(--sidebar-borde);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.usuario__nombre { font-size: var(--fs-small); color: var(--gray-600); }

/* Columna derecha: barra superior fija + contenido desplazable. */
.marco {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.barra-superior {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-4);
  height: var(--topbar-height);
  padding: 0 var(--space-6);
  background: var(--white);
  border-bottom: 1px solid var(--gray-200);
  flex-shrink: 0;
}

.usuario-chip {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 0;
}

/* Ahora es un enlace a /mi-cuenta (06/09/2026) -- necesita su propia
   senal de que se puede clicar, texto sin subrayado por defecto. */
.usuario-chip--clicable {
  text-decoration: none;
  border-radius: var(--radius-sm);
  padding: 4px 8px;
  margin: -4px -8px;
  transition: background-color .15s ease;
}
.usuario-chip--clicable:hover,
.usuario-chip--clicable:focus-visible {
  background-color: var(--gray-100);
}

.usuario-chip__nombre {
  font-size: var(--fs-small);
  color: var(--gray-900);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ---------- Selector de color de tema (Mi cuenta, 07/09/2026) ----------
   Cada boton es un <form> propio que postea a /mi-cuenta/tema -- clic en
   el color = aplicado, sin paso intermedio de "Guardar" (mismo criterio
   que el selector de color de acento de macOS en Preferencias del
   Sistema, que fue justamente la referencia que pidio Daniel).

   Sin display:inline-block en el <form> a proposito: .selector-temas ya
   es flex, y un contenedor flex "blockifica" a sus hijos directos para
   el layout sin importar el display propio de cada uno -- no hace falta
   forzarlo, y forzarlo por style="" inline no funcionaria igual (ver
   nota mas abajo).

   El color de cada bolita NO se manda por style="--swatch: #hex" inline
   -- se probo así primero y quedaban todas blancas/sin color: el sitio
   tiene CSP style-src 'self' (public/.htaccess, endurecido por el chat
   de Seguridad) sin 'unsafe-inline', asi que el navegador ignora
   CUALQUIER atributo style="" del HTML, no solo este. Por eso cada
   color es una clase modificadora fija (.tema-swatch--lila, etc.) con
   el hex hardcodeado -- mismo valor que --ejn-azul-acento de esa
   paleta en ejn-tokens.css y que CuentaRepo::TEMAS[x]['muestra']; si se
   agrega un color nuevo hay que tocar las 3 puntas a mano (documentado
   en docs/ESTADO.md). Hay ~27 archivos en app/Views que todavia usan
   style="" inline en otras pantallas -- funcionan por casualidad si
   nunca se probaron con el CSP real activo; queda como P-89 en
   PENDIENTES_PLATAFORMA.md, fuera de alcance de este cambio. */
.selector-temas__ayuda {
  margin-bottom: 12px;
}
.selector-temas {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}
.tema-swatch {
  width: 36px;
  height: 36px;
  border-radius: 50%;
  border: none;
  cursor: pointer;
  padding: 0;
  box-shadow: var(--shadow-sm);
  transition: transform .15s ease, box-shadow .15s ease;
}
.tema-swatch:hover {
  transform: scale(1.08);
}
.tema-swatch:focus-visible {
  outline: 2px solid var(--gray-900);
  outline-offset: 2px;
}
.tema-swatch--activo {
  box-shadow: 0 0 0 2px var(--white), 0 0 0 4px var(--gray-900), var(--shadow-sm);
}
.tema-swatch--lila    { background: #5E5CFF; }
.tema-swatch--celeste { background: #3E9BE0; }
.tema-swatch--menta   { background: #2FBF9C; }
.tema-swatch--rosa    { background: #F0629A; }
.tema-swatch--durazno { background: #F0985A; }
.tema-swatch--grafito { background: #64748B; }

/* Botón de icono (campana, cerrar). */
.btn-icono {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--gray-300);
  background: var(--white);
  color: var(--gray-700);
  cursor: pointer;
  flex-shrink: 0;
  transition: background .15s, border-color .15s, color .15s;
}

.btn-icono:hover {
  background: var(--module-accent-soft, var(--ejn-blue-soft));
  border-color: var(--module-accent, var(--ejn-blue));
  color: var(--module-accent, var(--ejn-blue-dark));
}

.btn-icono:focus-visible {
  outline: 2px solid var(--module-accent, var(--ejn-blue));
  outline-offset: 2px;
}

.btn-icono[aria-expanded="true"] {
  background: var(--module-accent-soft, var(--ejn-blue-soft));
  border-color: var(--module-accent, var(--ejn-blue));
}

.btn-icono--claro {
  border-color: transparent;
  background: transparent;
  color: var(--gray-600);
}

/* Contador de avisos pendientes. */
.btn-icono__contador {
  position: absolute;
  top: -6px;
  right: -6px;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: 9px;
  background: var(--ejn-red);
  color: var(--white);
  font-size: 10px;
  font-weight: var(--fw-bold);
  line-height: 18px;
  text-align: center;
}

/* Texto solo para lectores de pantalla. */
.visualmente-oculto {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.contenido { flex: 1; padding: var(--space-6); min-width: 0; overflow-y: auto; }

/* ---------- Panel lateral de avisos ----------
   Flotante y superpuesto: NO desplaza ni comprime el contenido.
   Preparado para alertas, notificaciones, advertencias y registros. */

.avisos-fondo {
  position: fixed;
  inset: 0;
  background: rgba(31, 41, 55, .25);
  opacity: 0;
  transition: opacity .2s ease;
  z-index: 40;
}

.avisos-fondo.esta-abierto { opacity: 1; }

.avisos {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  width: 360px;
  max-width: 90vw;
  display: flex;
  flex-direction: column;
  background: var(--white);
  border-left: 3px solid var(--module-accent, var(--ejn-blue));
  box-shadow: var(--shadow-lg);
  transform: translateX(100%);
  transition: transform .2s ease;
  z-index: 50;
}

.avisos.esta-abierto { transform: translateX(0); }

.avisos__cabecera {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--gray-200);
  flex-shrink: 0;
}

.avisos__titulo {
  margin: 0;
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
  color: var(--ejn-navy);
}

.avisos__cuerpo {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-4) var(--space-5);
}

.avisos__pie {
  padding: var(--space-3) var(--space-5);
  border-top: 1px solid var(--gray-200);
  flex-shrink: 0;
}

.aviso-grupo { margin-bottom: var(--space-5); }
.aviso-grupo:last-child { margin-bottom: 0; }

.aviso-grupo__titulo {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0 0 6px;
  font-size: var(--fs-caption);
  font-weight: var(--fw-semibold);
  letter-spacing: .5px;
  text-transform: uppercase;
  color: var(--gray-600);
}

.aviso-grupo__vacio {
  margin: 0;
  font-size: var(--fs-small);
  color: var(--gray-400);
  line-height: var(--lh-normal);
}

@media (max-width: 860px) {
  .barra-superior { padding: 0 var(--space-4); }
  .usuario-chip .caption { display: none; }

  /* 11/09/2026 (pedido de Daniel): en celular angosto el icono de
     plegar/las migas se superponian con el nombre de usuario. Causa
     real: .barra-superior__der no tenia min-width:0 -- un flex item
     sin eso nunca se achica por debajo del ancho de su contenido, asi
     que el elipsis ya definido en .usuario-chip__nombre nunca llegaba
     a activarse. Se oculta ademas el badge de rol (ej.
     "ADMIN_PLATAFORMA"): en celular alcanza con el nombre, el rol se
     ve completo en /mi-cuenta. Las migas (titulo de pantalla) tambien
     podian desbordar sin achicarse -- se acotan igual. */
  .barra-superior__der,
  .usuario-chip { min-width: 0; }
  .usuario-chip__nombre { max-width: 84px; }
  .usuario-chip .badge { display: none; }
  .migas b,
  .migas a {
    display: inline-block;
    max-width: 130px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* Quien pidió menos animación, no la recibe. */
@media (prefers-reduced-motion: reduce) {
  .avisos, .avisos-fondo, .modulo, .tarjeta { transition: none; }
}

/* ---------- Encabezado de página ---------- */

.encabezado {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: var(--space-5);
  margin-bottom: var(--space-6);
  flex-wrap: wrap;
}

.encabezado h1 { margin: 0; font-size: var(--fs-h1); font-weight: var(--fw-bold); }

.encabezado__sub { margin: 4px 0 0; color: var(--gray-600); font-size: var(--fs-small); }

.encabezado__usuario { text-align: right; display: flex; flex-direction: column; gap: 4px; align-items: flex-end; }

.caption {
  font-size: var(--fs-caption);
  font-weight: var(--fw-medium);
  letter-spacing: .5px;
  text-transform: uppercase;
  color: var(--gray-400);
}

.panel-inicio .encabezado { margin-bottom: var(--space-4); }

/* ---------- Grilla de módulos ----------
   Los 7 módulos (Core/Poliza/Business/Care/Work/Finance/SCI, éste último
   agregado 07/09/2026 -- migración 099) entran en una sola fila; las
   tarjetas se achicaron un poco (ver .modulo* más abajo) para que entren
   sin apretarse. Por debajo de 1280 px se reacomodan solos para no
   comprimir el contenido. */

.grilla-modulos {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: var(--space-2);
  align-items: stretch;
}

@media (max-width: 1280px) {
  .grilla-modulos { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (max-width: 720px) {
  .grilla-modulos { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Telefonos angostos (07/09/2026): 2 columnas con 5 chips de resumen por
   tarjeta queda apretado por debajo de 480px -- una columna se lee mejor. */
@media (max-width: 480px) {
  .grilla-modulos { grid-template-columns: 1fr; }
}

/* ============================================================
   Red RIXART: grafico central del panel de inicio
   Pedido de Daniel (06/09/2026): reemplaza el SVG estatico anterior
   por una red animada -- anillos, particulas, halos -- con los 6 nodos
   representando los modulos reales (mismos iconos que el sidebar,
   ver core/helpers.php::icono()), con resaltado cruzado tarjeta<->nodo
   y respeto a prefers-reduced-motion. Sin JS: el cruce tarjeta<->nodo
   se resuelve con :has() (soportado en navegadores actuales).
   ============================================================ */

/* 07/09/2026 (noche), pedido de Daniel: el grafico ocupaba todo el alto de
   la pantalla, lejos de las tarjetas de arriba. Menos margen superior y
   menos ancho maximo (el alto escala con el ancho por el viewBox fijo). */
.panel-hub {
  margin: var(--space-3) auto 0;
  max-width: 820px;
  width: 100%;
  /* 09/09/2026, pedido de Daniel ("que se convierta en una imagen 3D
     tipo JARVIS de Iron Man"): perspective aca para que el <svg> hijo
     pueda inclinarse en 3D (rotateX/rotateY) -- ver la seccion "Hub:
     holograma 3D" mas abajo y iniciarHubHolograma() en ejn-app.js.
     perspective-origin cerca del centro real del nucleo (325/770 del
     viewBox = 42% de alto) para que la inclinacion pivotee ahi. */
  perspective: 1400px;
  perspective-origin: 50% 42%;
  filter: drop-shadow(0 30px 46px rgba(20, 91, 239, .14));
}

.hub-svg {
  display: block;
  width: 100%;
  height: auto;
  overflow: visible;
  transform-origin: center center;
  animation: rixart-hub-flotar 11s ease-in-out infinite;
  will-change: transform;
}

/* ---------- Halo central ---------- */
.hub-halo {
  transform-box: fill-box;
  transform-origin: center;
  animation: rixart-halo 4s ease-in-out infinite;
}

/* Segunda capa de halo, mas grande y difuminada con feGaussianBlur real
   (no solo gradiente) -- pedido de Daniel (06/09/2026): el centro se
   sentia vacio comparado con su mockup, esto le da mas volumen/efecto
   sin agregar JS ni cambiar la geometria de nodos/conexiones. Late a
   otro ritmo que el halo interior (delay negativo) para que no
   respiren identicas -- mas organico. */
.hub-halo-outer {
  transform-box: fill-box;
  transform-origin: center;
  animation: rixart-halo 7s ease-in-out infinite;
  animation-delay: -2.3s;
}

/* Polvo decorativo: puntos esparcidos dentro y fuera del anillo
   exterior, estaticos (sin animacion -- evita sobrecargar de movimiento
   un fondo que ya tiene 3 anillos rotando + particulas viajando). */
.hub-polvo { fill: var(--rixart-blue-2); }
.hub-polvo--lejos { fill: var(--rixart-blue); }

/* Opacidad individual de cada partícula de polvo (antes venía inline por
   círculo -- CSP style-src la bloqueaba en silencio desde el endurecimiento
   de Seguridad, P-89, 07/09/2026). Son 24 círculos estáticos en un orden
   fijo dentro de .hub-decor, siempre los mismos -- por eso nth-of-type es
   exacto acá y no una aproximación. */
.hub-decor circle:nth-of-type(1)  { opacity: .22; }
.hub-decor circle:nth-of-type(2)  { opacity: .20; }
.hub-decor circle:nth-of-type(3)  { opacity: .28; }
.hub-decor circle:nth-of-type(4)  { opacity: .29; }
.hub-decor circle:nth-of-type(5)  { opacity: .18; }
.hub-decor circle:nth-of-type(6)  { opacity: .21; }
.hub-decor circle:nth-of-type(7)  { opacity: .20; }
.hub-decor circle:nth-of-type(8)  { opacity: .33; }
.hub-decor circle:nth-of-type(9)  { opacity: .29; }
.hub-decor circle:nth-of-type(10) { opacity: .30; }
.hub-decor circle:nth-of-type(11) { opacity: .19; }
.hub-decor circle:nth-of-type(12) { opacity: .18; }
.hub-decor circle:nth-of-type(13) { opacity: .17; }
.hub-decor circle:nth-of-type(14) { opacity: .24; }
.hub-decor circle:nth-of-type(15) { opacity: .20; }
.hub-decor circle:nth-of-type(16) { opacity: .25; }
.hub-decor circle:nth-of-type(17) { opacity: .15; }
.hub-decor circle:nth-of-type(18) { opacity: .29; }
.hub-decor circle:nth-of-type(19) { opacity: .20; }
.hub-decor circle:nth-of-type(20) { opacity: .18; }
.hub-decor circle:nth-of-type(21) { opacity: .28; }
.hub-decor circle:nth-of-type(22) { opacity: .22; }
.hub-decor circle:nth-of-type(23) { opacity: .24; }
.hub-decor circle:nth-of-type(24) { opacity: .20; }

/* ---------- Anillos tecnicos ---------- */
.hub-anillo {
  fill: none;
  stroke: var(--rixart-blue-2);
  stroke-opacity: .28;
  stroke-width: 1.6;
  transform-box: fill-box;
  transform-origin: center;
}
.hub-anillo--1 { animation: rixart-rotate 32s linear infinite; }
.hub-anillo--2 { stroke-opacity: .4; animation: rixart-rotate-reverse 24s linear infinite; }
.hub-anillo--3 { stroke-opacity: .5; animation: rixart-pulse 4s ease-in-out infinite; }

.hub-tic {
  stroke: var(--rixart-blue-2);
  stroke-opacity: .22;
  stroke-width: 2;
}
.hub-tic--mayor { stroke-opacity: .4; stroke-width: 2.6; }

/* ---------- Conexiones ---------- */
.hub-conexion {
  fill: none;
  stroke: var(--rixart-blue-2);
  stroke-width: 2;
  opacity: .55;
  transition: opacity .25s ease, stroke-width .25s ease;
}

.hub-conexion__punto {
  fill: var(--rixart-blue-2);
  opacity: .7;
  transition: opacity .25s ease;
}

.hub-particula {
  fill: var(--rixart-blue);
  filter: drop-shadow(0 0 3px rgba(20, 91, 239, .65));
}

/* ---------- Logo central (fijo, nunca rota) ---------- */
.hub-logo {
  color: var(--rixart-navy);
}

/* ---------- Nodos ---------- */
.hub-nodo {
  cursor: pointer;
  transform-box: fill-box;
  transform-origin: center;
  transition: transform .2s ease, filter .25s ease;
  outline: none;
}

/* La animacion de "flote" (continua) vive en un <g> interno, separado
   del <g> que recibe el hover -- si el scale de :hover fuera al mismo
   elemento que anima transform en loop infinito, la animacion lo pisa
   cada frame y el crecimiento nunca se llega a ver (pedido de Daniel,
   06/09/2026: "aumenten un poco mas el tamaño" al pasar el mouse). */
.hub-nodo__inner {
  animation: rixart-node-in .5s ease both, rixart-node-float 4s ease-in-out infinite;
  transform-box: fill-box;
  transform-origin: center;
}

.hub-nodo__halo {
  fill: var(--rixart-blue-light);
  opacity: .85;
  transition: opacity .25s ease, r .25s ease;
}

.hub-nodo__cuerpo {
  fill: var(--rixart-white);
  stroke: var(--rixart-blue-2);
  stroke-width: 2;
  transition: fill .25s ease, stroke .25s ease;
}

.hub-nodo__anillo {
  fill: none;
  stroke: var(--rixart-blue-light);
  stroke-width: 2;
}

.hub-nodo__icono { color: var(--rixart-blue); transition: color .25s ease; }
.hub-nodo__icono path, .hub-nodo__icono circle, .hub-nodo__icono rect { stroke-width: 2.4; }

.hub-nodo__indicador {
  fill: var(--rixart-blue-2);
  animation: rixart-pulse-dot 2.4s ease-in-out infinite;
}

/* Ruta invisible (sin trazo propio) que el nombre del módulo sigue con
   <textPath> -- pedido de Daniel (07/09/2026): "que el nombre del módulo
   se muestre por el contorno del círculo como si siguiera la línea". Arco
   superior del anillo (r=58, entre __anillo r=43 y __halo r=62), de punta
   a punta en sentido horario -- así el texto queda derecho, curvado hacia
   afuera como un sello, centrado arriba del nodo (startOffset 50%). */
.hub-nodo__ruta-nombre { fill: none; stroke: none; }

.hub-nodo__nombre {
  font-size: 11px;
  font-weight: var(--fw-extrabold);
  letter-spacing: 1.5px;
  fill: var(--ejn-azul-acento);
  opacity: 0;
  transition: opacity .25s ease;
  pointer-events: none;
}

.hub-nodo:hover,
.hub-nodo:focus-visible {
  transform: scale(1.12);
  filter: drop-shadow(0 0 14px rgba(20, 91, 239, .28));
}
.hub-nodo:hover .hub-nodo__cuerpo,
.hub-nodo:focus-visible .hub-nodo__cuerpo {
  /* Pintar el fondo del círculo con el color del estilo activo (tema por
     usuario -- lila por defecto) y el ícono en blanco, pedido de Daniel
     (07/09/2026). */
  fill: var(--ejn-azul-acento);
  stroke: var(--ejn-azul-acento-dark);
}
.hub-nodo:hover .hub-nodo__icono,
.hub-nodo:focus-visible .hub-nodo__icono { color: var(--rixart-white); }
.hub-nodo:hover .hub-nodo__halo,
.hub-nodo:focus-visible .hub-nodo__halo { opacity: .95; r: 62; }
.hub-nodo:hover .hub-nodo__nombre,
.hub-nodo:focus-visible .hub-nodo__nombre { opacity: 1; }

/* El delay de cada nodo (entrada + "flote", para que no se muevan juntos)
   venía inline por nodo desde el propio SVG generado (style="animation-
   delay:..."). Roto en silencio por CSP style-src desde el endurecimiento
   de Seguridad (P-89, 07/09/2026): los 6 nodos flotaban todos sincronizados
   en vez de desfasados. Cada .hub-nodo tiene su data-module en el padre, así
   que el delay se resuelve por atributo -- mismo mecanismo que ya usan
   .hub-anillos/.hub-conexiones/.hub-logo un poco más abajo. */
[data-module="core"]     .hub-nodo__inner { animation-delay: 0s; }
[data-module="poliza"]   .hub-nodo__inner { animation-delay: .5s; }
[data-module="business"] .hub-nodo__inner { animation-delay: 1s; }
[data-module="care"]     .hub-nodo__inner { animation-delay: 1.5s; }
[data-module="work"]     .hub-nodo__inner { animation-delay: 2s; }
[data-module="finance"]  .hub-nodo__inner { animation-delay: 2.5s; }
[data-module="sci"]      .hub-nodo__inner { animation-delay: 3s; }  /* 07/09/2026: 7º nodo, abajo-centrado */

/* ---------- Entrada inicial escalonada ---------- */
.hub-halo, .hub-anillos, .hub-conexiones, .hub-logo {
  animation: rixart-fade-in .6s ease both;
}
.hub-anillos    { animation-delay: .05s; }
.hub-conexiones { animation-delay: .15s; }
.hub-logo       { animation-delay: .35s; }
.hub-particulas { animation: rixart-fade-in .6s ease .5s both; }

/* ---------- Resaltado cruzado tarjeta <-> nodo (sin JS, via :has()) ---------- */

.panel-inicio:has([data-module="core"]:hover) .modulo[data-module="core"] {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(20, 91, 239, .10);
  border-color: rgba(20, 91, 239, .35);
}
.panel-inicio:has([data-module="core"]:hover) .hub-nodo[data-module="core"] {
  filter: drop-shadow(0 0 14px rgba(20, 91, 239, .28));
}
.panel-inicio:has([data-module="core"]:hover) .hub-nodo[data-module="core"] .hub-nodo__cuerpo {
  stroke: var(--rixart-blue);
}
.panel-inicio:has([data-module="core"]:hover) .hub-nodo[data-module="core"] .hub-nodo__halo {
  opacity: .95;
}
.panel-inicio:has([data-module="core"]:hover) .hub-conexion[data-module="core"] {
  stroke: var(--rixart-blue);
  opacity: 1;
  stroke-width: 2.6;
}
.panel-inicio:has([data-module="core"]:hover) .hub-conexion__punto[data-module="core"] {
  opacity: 1;
}
.panel-inicio:has([data-module="poliza"]:hover) .modulo[data-module="poliza"] {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(20, 91, 239, .10);
  border-color: rgba(20, 91, 239, .35);
}
.panel-inicio:has([data-module="poliza"]:hover) .hub-nodo[data-module="poliza"] {
  filter: drop-shadow(0 0 14px rgba(20, 91, 239, .28));
}
.panel-inicio:has([data-module="poliza"]:hover) .hub-nodo[data-module="poliza"] .hub-nodo__cuerpo {
  stroke: var(--rixart-blue);
}
.panel-inicio:has([data-module="poliza"]:hover) .hub-nodo[data-module="poliza"] .hub-nodo__halo {
  opacity: .95;
}
.panel-inicio:has([data-module="poliza"]:hover) .hub-conexion[data-module="poliza"] {
  stroke: var(--rixart-blue);
  opacity: 1;
  stroke-width: 2.6;
}
.panel-inicio:has([data-module="poliza"]:hover) .hub-conexion__punto[data-module="poliza"] {
  opacity: 1;
}
.panel-inicio:has([data-module="business"]:hover) .modulo[data-module="business"] {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(20, 91, 239, .10);
  border-color: rgba(20, 91, 239, .35);
}
.panel-inicio:has([data-module="business"]:hover) .hub-nodo[data-module="business"] {
  filter: drop-shadow(0 0 14px rgba(20, 91, 239, .28));
}
.panel-inicio:has([data-module="business"]:hover) .hub-nodo[data-module="business"] .hub-nodo__cuerpo {
  stroke: var(--rixart-blue);
}
.panel-inicio:has([data-module="business"]:hover) .hub-nodo[data-module="business"] .hub-nodo__halo {
  opacity: .95;
}
.panel-inicio:has([data-module="business"]:hover) .hub-conexion[data-module="business"] {
  stroke: var(--rixart-blue);
  opacity: 1;
  stroke-width: 2.6;
}
.panel-inicio:has([data-module="business"]:hover) .hub-conexion__punto[data-module="business"] {
  opacity: 1;
}
.panel-inicio:has([data-module="care"]:hover) .modulo[data-module="care"] {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(20, 91, 239, .10);
  border-color: rgba(20, 91, 239, .35);
}
.panel-inicio:has([data-module="care"]:hover) .hub-nodo[data-module="care"] {
  filter: drop-shadow(0 0 14px rgba(20, 91, 239, .28));
}
.panel-inicio:has([data-module="care"]:hover) .hub-nodo[data-module="care"] .hub-nodo__cuerpo {
  stroke: var(--rixart-blue);
}
.panel-inicio:has([data-module="care"]:hover) .hub-nodo[data-module="care"] .hub-nodo__halo {
  opacity: .95;
}
.panel-inicio:has([data-module="care"]:hover) .hub-conexion[data-module="care"] {
  stroke: var(--rixart-blue);
  opacity: 1;
  stroke-width: 2.6;
}
.panel-inicio:has([data-module="care"]:hover) .hub-conexion__punto[data-module="care"] {
  opacity: 1;
}
.panel-inicio:has([data-module="work"]:hover) .modulo[data-module="work"] {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(20, 91, 239, .10);
  border-color: rgba(20, 91, 239, .35);
}
.panel-inicio:has([data-module="work"]:hover) .hub-nodo[data-module="work"] {
  filter: drop-shadow(0 0 14px rgba(20, 91, 239, .28));
}
.panel-inicio:has([data-module="work"]:hover) .hub-nodo[data-module="work"] .hub-nodo__cuerpo {
  stroke: var(--rixart-blue);
}
.panel-inicio:has([data-module="work"]:hover) .hub-nodo[data-module="work"] .hub-nodo__halo {
  opacity: .95;
}
.panel-inicio:has([data-module="work"]:hover) .hub-conexion[data-module="work"] {
  stroke: var(--rixart-blue);
  opacity: 1;
  stroke-width: 2.6;
}
.panel-inicio:has([data-module="work"]:hover) .hub-conexion__punto[data-module="work"] {
  opacity: 1;
}
.panel-inicio:has([data-module="finance"]:hover) .modulo[data-module="finance"] {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(20, 91, 239, .10);
  border-color: rgba(20, 91, 239, .35);
}
.panel-inicio:has([data-module="finance"]:hover) .hub-nodo[data-module="finance"] {
  filter: drop-shadow(0 0 14px rgba(20, 91, 239, .28));
}
.panel-inicio:has([data-module="finance"]:hover) .hub-nodo[data-module="finance"] .hub-nodo__cuerpo {
  stroke: var(--rixart-blue);
}
.panel-inicio:has([data-module="finance"]:hover) .hub-nodo[data-module="finance"] .hub-nodo__halo {
  opacity: .95;
}
.panel-inicio:has([data-module="finance"]:hover) .hub-conexion[data-module="finance"] {
  stroke: var(--rixart-blue);
  opacity: 1;
  stroke-width: 2.6;
}
.panel-inicio:has([data-module="finance"]:hover) .hub-conexion__punto[data-module="finance"] {
  opacity: 1;
}
.panel-inicio:has([data-module="sci"]:hover) .modulo[data-module="sci"] {
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(20, 91, 239, .10);
  border-color: rgba(20, 91, 239, .35);
}
.panel-inicio:has([data-module="sci"]:hover) .hub-nodo[data-module="sci"] {
  filter: drop-shadow(0 0 14px rgba(20, 91, 239, .28));
}
.panel-inicio:has([data-module="sci"]:hover) .hub-nodo[data-module="sci"] .hub-nodo__cuerpo {
  stroke: var(--rixart-blue);
}
.panel-inicio:has([data-module="sci"]:hover) .hub-nodo[data-module="sci"] .hub-nodo__halo {
  opacity: .95;
}
.panel-inicio:has([data-module="sci"]:hover) .hub-conexion[data-module="sci"] {
  stroke: var(--rixart-blue);
  opacity: 1;
  stroke-width: 2.6;
}
.panel-inicio:has([data-module="sci"]:hover) .hub-conexion__punto[data-module="sci"] {
  opacity: 1;
}

/* ---------- Keyframes ---------- */
@keyframes rixart-rotate         { from { transform: rotate(0deg); }   to { transform: rotate(360deg); } }
@keyframes rixart-rotate-reverse { from { transform: rotate(360deg); } to { transform: rotate(0deg); } }

@keyframes rixart-pulse {
  0%, 100% { opacity: .35; transform: scale(.98); }
  50%      { opacity: .7;  transform: scale(1.03); }
}

@keyframes rixart-halo {
  0%, 100% { opacity: .25; transform: scale(.98); }
  50%      { opacity: .55; transform: scale(1.03); }
}

@keyframes rixart-pulse-dot {
  0%, 100% { opacity: .5; }
  50%      { opacity: 1; }
}

@keyframes rixart-node-float {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-3px); }
}

@keyframes rixart-node-in {
  from { opacity: 0; transform: scale(.75); }
  to   { opacity: 1; transform: scale(1); }
}

@keyframes rixart-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ---------- prefers-reduced-motion: estados estaticos ---------- */
@media (prefers-reduced-motion: reduce) {
  .hub-halo,
  .hub-anillo,
  .hub-nodo,
  .hub-nodo__indicador,
  .hub-halo, .hub-anillos, .hub-conexiones, .hub-logo,
  .hub-svg,
  .hub-sweep-grupo {
    animation: none !important;
  }
  /* 09/09/2026: el holograma 3D (tilt + parallax por capa) tambien se
     apaga entero -- iniciarHubHolograma() en ejn-app.js ya ni siquiera
     engancha los listeners bajo esta preferencia, esto cubre el estado
     visual en caso de que --hub-tilt-x/y/--hub-par-x/y hayan quedado
     con un valor de una sesion anterior (custom properties persisten
     en el elemento, no se resetean solas). */
  .hub-svg,
  .hub-anillos,
  .hub-conexiones,
  .hub-nodos {
    transform: none !important;
  }
  .hub-particulas,
  .hub-sweep-grupo { display: none; }
  .panel-hub { filter: none; }
}

/* ---------- Responsive ---------- */
@media (max-width: 860px) {
  .panel-hub { margin-top: var(--space-4); }
}

@media (max-width: 640px) {
  .hub-nodo__icono { transform-origin: center; }
}

/* ============================================================
   Hub: holograma 3D estilo "JARVIS" (pedido de Daniel, 09/09/2026)
   "la imagen central de la plataforma que esta animada timidamente,
   que se convierta en una imagen 3D tipo JARVIS de Iron Man". Sin
   WebGL/Three.js -- esta app no tiene build step ni bundler, y sumar
   un motor 3D real para un widget decorativo del panel de inicio es
   sobreingenieria frente a lo que CSS 3D puro ya puede lograr. El
   efecto "3D" es: el <svg> entero se inclina en el espacio
   (perspective en .panel-hub + rotateX/rotateY en .hub-svg,
   reaccionando al mouse via iniciarHubHolograma() en ejn-app.js, que
   escribe --hub-tilt-x/--hub-tilt-y/--hub-par-x/--hub-par-y como
   custom properties -- permitido por CSP style-src 'self': la
   restriccion es sobre style="" y <style> en el HTML, no sobre
   asignaciones JS al CSSOM, mismo principio que ya documentaba el fix
   de P-89 para animation-delay). Sin mouse (touch, o antes del primer
   movimiento) el <svg> igual "flota" solo por el keyframe de reposo
   -- nunca queda estatico, que era justamente la queja de Daniel.
   Profundidad simulada sin transform-style:preserve-3d anidado (poco
   confiable entre navegadores dentro de <svg> real): cada capa
   (anillos, conexiones, nodos) se corre en 2D una distancia distinta
   segun --hub-par-x/y (posicion del mouse normalizada -1..1) -- los
   nodos (mas "cerca" del usuario) se mueven mas que los anillos (mas
   "atras"). El nucleo (halo + logo RIXART) queda fijo a proposito: es
   el punto de anclaje del holograma, todo lo demas gira/flota
   alrededor. Respeta prefers-reduced-motion (bloque de arriba).
   ============================================================ */

/* Profundidad por capa. .hub-anillos/.hub-conexiones ya tenian su
   propio animation (rixart-fade-in, solo opacity) -- este transform
   estatico no choca, son propiedades distintas del mismo elemento. */
.hub-anillos {
  transform: translate(calc(var(--hub-par-x, 0) * 4px), calc(var(--hub-par-y, 0) * 4px));
}
.hub-conexiones {
  transform: translate(calc(var(--hub-par-x, 0) * 8px), calc(var(--hub-par-y, 0) * 8px));
}
.hub-nodos {
  transform: translate(calc(var(--hub-par-x, 0) * 15px), calc(var(--hub-par-y, 0) * 15px));
}

/* ---------- Barrido de radar ---------- */
.hub-sweep { opacity: .85; }
.hub-sweep-grupo {
  animation: rixart-rotate 5.5s linear infinite;
  transform-box: view-box;
  transform-origin: 500px 325px;
}

/* ---------- Keyframe del holograma (reposo autonomo + tilt del mouse
   combinados -- ver comentario largo arriba) ---------- */
@keyframes rixart-hub-flotar {
  0%, 100% {
    transform: rotateX(calc(1.6deg + var(--hub-tilt-x, 0deg))) rotateY(calc(-2.6deg + var(--hub-tilt-y, 0deg)));
  }
  50% {
    transform: rotateX(calc(-1.6deg + var(--hub-tilt-x, 0deg))) rotateY(calc(2.6deg + var(--hub-tilt-y, 0deg)));
  }
}

.modulo {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  background: var(--cristal-glass-bg-strong);
  backdrop-filter: blur(var(--cristal-blur));
  -webkit-backdrop-filter: blur(var(--cristal-blur));
  border: 1px solid var(--cristal-glass-border);
  border-radius: var(--radius-md);
  padding: var(--space-3);
  overflow: hidden;
  text-decoration: none;
  color: inherit;
  box-shadow: var(--shadow-sm);
  transition: box-shadow .15s, transform .15s;
}  /* 06/09/2026: vidrio (antes blanco solido + borde duro) -- pedido de Daniel "Cristal / Vidrio Diurno" */

/* Solo las tarjetas accesibles reaccionan al mouse. Sombra mas marcada en
   vez de cambiar el borde -- "sombras suaves en vez de bordes duros". */
a.modulo:hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-2px) scale(1.025);
}

a.modulo:focus-visible {
  outline: 2px solid var(--module-accent, var(--ejn-azul-acento));
  outline-offset: 2px;
}

/* Bloqueado: sin color, sin cursor, sin interacción. */
.modulo--bloqueado {
  background: var(--gray-50);
  cursor: not-allowed;
  opacity: .6;
}

.modulo--bloqueado .modulo__escudo { filter: grayscale(1); }
.modulo--bloqueado .modulo__marca  { background: var(--gray-300); }
.modulo--bloqueado .modulo__nombre { color: var(--gray-600); }

/* Banda superior con el color del módulo: acento, no fondo.
   El color llega por [data-module="..."] desde ejn-tokens.css. */
.modulo__marca {
  position: absolute;
  inset: 0 0 auto 0;
  height: 4px;
  background: var(--module-accent, var(--ejn-blue));
}

.modulo__escudo {
  flex-shrink: 0;
  margin-bottom: var(--space-2);
}

/* Azul de acento electrico (pedido de Daniel, 06/09/2026, sobre su
   mockup de referencia) -- ya no navy. Un solo color fijo para los 6
   modulos (no depende de --module-accent, que ademas cambio en este
   mismo commit): contraste verificado sobre blanco, ratio ~4.57:1,
   cumple AA. */
.modulo__nombre {
  margin: 6px 0 4px;
  font-size: 14px;  /* 07/09/2026: antes --fs-h3 (16px) -- con 7 módulos en
                        una fila (SCI agregado) la tarjeta se achicó y
                        BUSINESS/FINANCE quedaban al límite del ancho */
  font-weight: var(--fw-extrabold);
  text-transform: uppercase;
  letter-spacing: 1px;
  color: var(--ejn-azul-acento);
  line-height: var(--lh-tight);
}

.modulo__desc {
  margin: 0 0 var(--space-3);
  font-size: var(--fs-small);
  color: var(--gray-600);
  flex: 1;
}

/* Badge "Ingresar": azul de acento fijo e igual en los 6 módulos (ya no
   depende de --module-accent, que unificamos a navy hace dos pasadas --
   quedaba grisáceo). Pedido de Daniel a partir de su mockup de
   referencia (06/09/2026). */
.badge-modulo {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  background: var(--ejn-azul-acento-soft);
  color: var(--ejn-azul-acento);
}

/* Resumen de gestión bajo "Ingresar" (pedido de Daniel, 07/09/2026):
   vencimientos/observaciones/errores/pendientes/mensajes de
   PanelResumenRepo. Los chips reusan .badge--ok/--warn/--dang/--info/--neu
   de ejn-erp.css (mismo tamaño 10.5px que el resto de la plataforma) --
   acá sólo se resuelve el layout de fila, no el color. */
.modulo__resumen {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 6px;
  width: 100%;
}

.modulo__chip { white-space: nowrap; }

/* En 0 se apaga a gris neutro (ya lo pinta badge--neu) y baja opacidad --
   la fila siempre tiene 5 chips ahora (pedido de Daniel, 07/09/2026,
   "mostralo en todas, lo vamos a ir mejorando"), y lo que sí importa no
   puede competir visualmente con una fila de ceros. */
.modulo__chip--vacio { opacity: .55; }

/* ---------- Portada de módulo ---------- */

.encabezado--modulo { align-items: center; }

.encabezado__titulo {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.alerta--modulo {
  background: var(--module-accent-soft, var(--ejn-blue-soft));
  color: var(--gray-700);
  border-color: var(--module-accent, var(--ejn-blue));
  border-left-width: 3px;
}

.grilla-secciones {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: var(--space-4);
}

.seccion {
  position: relative;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-left: 3px solid var(--module-accent, var(--ejn-blue));
  border-radius: var(--radius-md);
  padding: var(--space-4);
}

.seccion__nombre {
  margin: 0 0 6px;
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
  color: var(--ejn-navy);
}

.seccion__desc {
  margin: 0 0 var(--space-4);
  font-size: var(--fs-small);
  color: var(--gray-600);
}

/* ---------- Responsive ---------- */

@media (max-width: 860px) {
  .app { flex-direction: column; height: auto; }

  /* Pedido de Daniel (07/09/2026): el grafico decorativo del panel de
     inicio no debe verse en celular -- ahi importa llegar rapido a las
     tarjetas de modulos, no una animacion. */
  .panel-hub { display: none; }

  .barra-lateral {
    position: relative;
    width: 100%;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    padding: var(--space-3) var(--space-4);
  }

  .barra-lateral__marca,
  .barra-lateral__pie { border: none; padding: 0; }

  .barra-lateral__nav {
    display: none;
    position: absolute;
    top: 100%; left: 0; right: 0;
    z-index: 40;
    flex-direction: column;
    max-height: calc(100vh - 64px);
    overflow-y: auto;
    background: var(--white);
    border-top: 1px solid var(--gray-200);
    box-shadow: var(--shadow-lg, 0 8px 24px rgba(10, 31, 68, .18));
    padding: var(--space-3) var(--space-4);
  }

  .app--nav-movil .barra-lateral__nav { display: flex; }

  /* Fondo fijo detras del desplegable de celular (11/09/2026): sin esto,
     en algunas pantallas (ej. Poliza/Tablero, con tarjetas .kpi) el
     contenido de la pagina se veia por encima del menu -- .barra-lateral
     tiene backdrop-filter, que crea su propio contexto de apilamiento y
     puede confundir el orden de pintado en algunos motores moviles. El
     div vive fuera de #app (ver privado.php) con position:fixed, asi que
     no depende de ese contexto -- cubre TODA la pantalla siempre. */
  .nav-movil-fondo {
    position: fixed;
    inset: 0;
    background: rgba(31, 41, 55, .25);
    z-index: 39;
  }

  .nav-item { padding: 10px 8px; }
  .pag, .btn-mini, .btn-icono { min-height: 34px; }

  .contenido { padding: var(--space-4); }

  .grilla__buscar { width: 100%; }
  .grilla__buscar input { width: 100%; max-width: none; }
}

/* ============================================================
   UTILIDADES — reemplazo de los `style="..."` en línea
   ============================================================

   La CSP de EJN es `style-src 'self'`, que descarta **también** el
   atributo `style`. Igual que con el `<script>` embebido (ver
   ejn-app.js), el navegador no dice nada: el margen simplemente no
   se aplica y la pantalla se ve "casi bien".

   El 19/08/2026 había 188 atributos `style` repartidos en 72 vistas
   de los seis módulos, y 665 anchos de columna de grilla. Ninguno
   hacía efecto.

   Estas clases cubren exactamente los valores que estaban en uso.
   No es un framework de utilidades: es la traducción uno a uno de
   lo que ya existía. Antes de agregar una clase nueva acá, revisar
   si el caso no merece una clase con nombre propio en el bloque
   que corresponda.
   ============================================================ */

/* --- Márgenes superiores --- */
.mt-0   { margin-top: 0; }
.mt-2   { margin-top: var(--space-2); }
.mt-3   { margin-top: var(--space-3); }
.mt-4   { margin-top: var(--space-4); }
.mt-06r { margin-top: .6rem; }
.mt-08r { margin-top: .8rem; }

/* --- Márgenes inferiores --- */
.mb-0   { margin-bottom: 0; }
.mb-6   { margin-bottom: 6px; }
.mb-2   { margin-bottom: var(--space-2); }
.mb-3   { margin-bottom: var(--space-3); }
.mb-4   { margin-bottom: var(--space-4); }
.mb-04r { margin-bottom: .4rem; }
.mb-08r { margin-bottom: .8rem; }

/* --- Margen completo --- */
.m-0    { margin: 0; }
.m-t2   { margin: var(--space-2) 0 0; }
.m-t3   { margin: var(--space-3) 0 0; }
.m-t4   { margin: var(--space-4) 0 0; }
.m-y3   { margin: var(--space-3) 0; }
.m-b3   { margin: 0 0 var(--space-3); }

/* --- Grilla de formulario --- */
.col-2  { grid-column: span 2; }
.cols-2 { grid-template-columns: repeat(2, 1fr); }
.cols-3 { grid-template-columns: repeat(3, 1fr); }

/* --- Flex --- */
.fila-fin   { display: flex; align-items: flex-end; align-self: flex-end; } /* align-self: alinea este item contra el borde inferior de la fila --grid--, no solo su propio contenido; sin esto un boton sin etiqueta quedaba a la altura del rotulo del campo vecino (mismo bug de fondo que P-54) */
.just-fin   { justify-content: flex-end; }
.just-entre { justify-content: space-between; }
.flex-1     { flex: 1; }

/* --- Texto --- */
.txt-izq        { text-align: left; }
.txt-der        { text-align: right; }
.fw-normal      { font-weight: 400; }
.lh-19          { line-height: 1.9; }
.romper-palabra { word-break: break-all; }

.fs-105   { font-size: 10.5px; }
.fs-13    { font-size: 13px; }
.fs-14    { font-size: 14px; }
.fs-15    { font-size: 15px; }
.fs-16    { font-size: 16px; }
.fs-105pt { font-size: 10.5pt; }

/* --- Otros --- */
.borde-acento { border-color: var(--module-accent); }
.minw-280 { min-width: 280px; }
.minw-420 { min-width: 420px; }

/* --- Anchos de columna de grilla ---
   `componentes/grilla.php` recibe el ancho por configuración y antes lo
   escribía como `style="width:120px"`. Ahora usa `w-120`, y el ancho se
   redondea al múltiplo de 5 más cercano para que **siempre** caiga en una
   clase existente: un ancho suelto como 107px no puede quedar sin efecto
   en silencio, que es exactamente el error que estamos corrigiendo. */
/* Anchos chicos: columnas de selección e íconos. */
.w-30 { width: 30px; }
.w-35 { width: 35px; }
.w-40 { width: 40px; }
.w-45 { width: 45px; }
.w-50 { width: 50px; }
.w-55 { width: 55px; }
.w-60 { width: 60px; }
.w-65 { width: 65px; }
.w-70 { width: 70px; }
.w-75 { width: 75px; }
.w-80 { width: 80px; }
.w-85 { width: 85px; }
.w-90 { width: 90px; }
.w-95 { width: 95px; }
.w-100 { width: 100px; }
.w-105 { width: 105px; }
.w-110 { width: 110px; }
.w-115 { width: 115px; }
.w-120 { width: 120px; }
.w-125 { width: 125px; }
.w-130 { width: 130px; }
.w-135 { width: 135px; }
.w-140 { width: 140px; }
.w-145 { width: 145px; }
.w-150 { width: 150px; }
.w-155 { width: 155px; }
.w-160 { width: 160px; }
.w-165 { width: 165px; }
.w-170 { width: 170px; }
.w-175 { width: 175px; }
.w-180 { width: 180px; }
.w-185 { width: 185px; }
.w-190 { width: 190px; }
.w-195 { width: 195px; }
.w-200 { width: 200px; }
.w-205 { width: 205px; }
.w-210 { width: 210px; }
.w-215 { width: 215px; }
.w-220 { width: 220px; }
.w-225 { width: 225px; }
.w-230 { width: 230px; }
.w-235 { width: 235px; }
.w-240 { width: 240px; }
.w-245 { width: 245px; }
.w-250 { width: 250px; }
.w-255 { width: 255px; }
.w-260 { width: 260px; }
.w-265 { width: 265px; }
.w-270 { width: 270px; }
.w-275 { width: 275px; }
.w-280 { width: 280px; }
.w-285 { width: 285px; }
.w-290 { width: 290px; }
.w-295 { width: 295px; }
.w-300 { width: 300px; }
.w-305 { width: 305px; }
.w-310 { width: 310px; }
.w-315 { width: 315px; }
.w-320 { width: 320px; }
.w-325 { width: 325px; }
.w-330 { width: 330px; }
.w-335 { width: 335px; }
.w-340 { width: 340px; }
.w-345 { width: 345px; }
.w-350 { width: 350px; }
.w-355 { width: 355px; }
.w-360 { width: 360px; }
.w-365 { width: 365px; }
.w-370 { width: 370px; }
.w-375 { width: 375px; }
.w-380 { width: 380px; }
.w-385 { width: 385px; }
.w-390 { width: 390px; }
.w-395 { width: 395px; }
.w-400 { width: 400px; }

/* ---------- Marca v3: wordmark del producto ----------
   «EJN ART»: EJN en tinta, ART en el turquesa de marca — como lo compone
   el manual v3 (EJN_Soft_Manual_de_Marca_v3.png). */
.wordmark__acento { color: var(--ejn-teal); }

/* Logo completo RixART (placa + wordmark en un solo grafico) en las
   pantallas de auth -- reemplaza el icono chico + texto separados
   (06/09/2026, pedido de Daniel: "quiero que muestre el logo tal como
   te adjunto la imagen"). */
.wordmark-full { margin: 0 0 var(--space-2); display: flex; justify-content: center; }
.logo-rixart-full { display: block; width: min(280px, 80%); height: auto; }

/* Boton "Ingresar" del login con el azul electrico de marca -- scopeado
   solo a esta pantalla. .btn-primary sin scope sigue en --color-primary
   (navy) para el resto de la plataforma, no se toca. */
.tarjeta-acceso .btn-primary { background: var(--ejn-azul-acento); }
.tarjeta-acceso .btn-primary:hover { background: var(--ejn-azul-acento-dark); }

/* ---------- Ayuda contextual ----------
   Panel flotante del botón «?». Reutiliza la estructura de .avisos;
   es más ancho porque adentro viven los circuitos explicados, que son
   franjas horizontales de 5 o 6 pasos. */
.avisos--ayuda { width: 640px; max-width: 94vw; }

.ayuda-bloque { margin-bottom: var(--space-5); }
.ayuda-bloque:last-child { margin-bottom: 0; }

.ayuda-bloque__titulo {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  margin: 0 0 var(--space-3);
  font-size: var(--fs-h3);
  font-weight: var(--fw-semibold);
  color: var(--ejn-navy);
}

/* Lo que la vista mandó al panel puede traer sus propias tarjetas con
   sombra y borde: acá adentro se aplanan para no encajonar cajas. */
.ayuda-bloque__cuerpo > .panel,
.ayuda-bloque__cuerpo > section {
  border: none;
  box-shadow: none;
  padding: 0;
  margin: 0;
}

/* ---------- Consulta rápida ----------
   Panel de la lupa (Ctrl/Cmd+K): dato puntual de otro módulo sin
   abandonar el formulario en curso. */
.avisos--consulta { width: 460px; max-width: 92vw; }

/* ---------- Panel lateral ancho (tablas con muchas columnas) ----------
   Pedido de Daniel (11/09/2026): el detalle de un trabajador (Poliza,
   pestaña Trabajadores) y el detalle de un período de Emisión muestran
   la nómina real de ARCA con 13 y 21 columnas respectivamente -- en los
   360px de .avisos de base quedaban ilegibles (.grilla__marco las hace
   scrollear, pero casi no entraba ninguna columna antes de tener que
   scrollear). La vista que necesita el ancho extra marca su fragmento
   con <span data-panel-ancho hidden></span>; ejn-app.js lo detecta
   después de cargar el contenido y alterna esta clase en el drawer
   compartido #panel-lateral -- no se ensancha el panel para todo el
   resto de usos (movimiento antes/después, ayuda, consulta rápida),
   sólo para el contenido que lo pide. */
.avisos--ancho { width: 1100px; max-width: 96vw; }

.consulta__buscador {
  padding: var(--space-4) var(--space-5) var(--space-3);
  border-bottom: 1px solid var(--gray-200);
  flex-shrink: 0;
}

.consulta__entrada {
  width: 100%;
  font-family: var(--font-family);
  font-size: var(--fs-body);
  padding: 10px 12px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm);
}

.consulta__entrada:focus {
  outline: none;
  border-color: var(--ejn-navy);
  box-shadow: 0 0 0 3px var(--ejn-navy-soft);
}

.consulta__pista { margin: 8px 0 0; }

.consulta__item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: 9px 10px;
  border: 1px solid var(--gray-200);
  border-left: 3px solid var(--module-accent, var(--gray-300));
  border-radius: var(--radius-sm);
  margin-bottom: 8px;
  background: var(--white);
}

.consulta__cuerpo { min-width: 0; display: flex; flex-direction: column; gap: 2px; }

.consulta__titulo {
  font-size: var(--fs-small);
  color: var(--gray-900);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.consulta__linea { font-size: var(--fs-caption); color: var(--gray-600); }

.consulta__acciones { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }

.consulta__copiar {
  font-family: var(--font-family);
  font-size: var(--fs-caption);
  font-weight: var(--fw-semibold);
  padding: 4px 9px;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-sm);
  background: var(--gray-50);
  color: var(--gray-700);
  cursor: pointer;
  white-space: nowrap;
}

.consulta__copiar:hover { border-color: var(--ejn-navy); color: var(--ejn-navy); }

.consulta__copiar--ok {
  border-color: var(--ejn-green);
  background: var(--ejn-green-soft);
  color: var(--ejn-green-dark);
}

.consulta__abrir {
  font-size: var(--fs-caption);
  font-weight: var(--fw-semibold);
  color: var(--module-accent, var(--ejn-navy));
  text-decoration: none;
  white-space: nowrap;
}

/* ---------- Borradores (P-61) ----------
   Barra que ofrece restaurar un borrador guardado. Aparece arriba del
   formulario y desaparece al elegir; nunca pisa nada sin preguntar. */
.borrador-barra {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: 9px 14px;
  margin-bottom: var(--space-3);
  border: 1px solid var(--ejn-gold);
  border-left-width: 3px;
  border-radius: var(--radius-sm);
  background: var(--ejn-gold-soft);
}

.borrador-barra__texto {
  flex: 1;
  font-size: var(--fs-small);
  color: var(--gray-700);
}

.borrador-barra__accion {
  font-family: var(--font-family);
  font-size: var(--fs-small);
  font-weight: var(--fw-semibold);
  padding: 5px 12px;
  border: 1px solid var(--ejn-gold-dark);
  border-radius: var(--radius-sm);
  background: var(--white);
  color: var(--ejn-gold-dark);
  cursor: pointer;
}

.borrador-barra__accion:hover { background: var(--ejn-gold-soft); }

.borrador-barra__accion--sec {
  border-color: var(--gray-300);
  color: var(--gray-600);
}

/* ============================================================
   Panel de inicio: dashboard interactivo (11/09/2026)
   Reemplaza al holograma SVG (.panel-hub/.hub-* mas arriba, que
   queda sin uso -- pendiente de limpieza, ver ESTADO.md). Mockup
   de layout combinando 3 referencias que le gustaron a Daniel
   (Youcare/Finnova/Ultraleads): tarjetas KPI con anillo, area
   degradada, dona/gauge, barras con una resaltada y una lista con
   avatar+titulo+subtitulo+valor. Un solo acento (--ejn-azul-acento,
   paleta "Cristal") en vez de un color por modulo, siguiendo la
   unificacion ya vigente en el resto de la plataforma (ver
   ejn-tokens.css, "Acento por modulo"). Valores ilustrativos --
   el contenido real por perfil de usuario es la proxima iteracion.
   ============================================================ */
.panel-dashboard {
  margin: var(--space-5) auto 0;
  max-width: 1100px;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.panel-dashboard__cab {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-3); flex-wrap: wrap;
}
.panel-dashboard__cab h2 {
  margin: 0; font-size: var(--fs-h3); font-weight: var(--fw-semibold); color: var(--ejn-navy);
}

/* ---- Fila de KPIs con anillo ---- */
.kpi-fila {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--space-3);
}
@media (max-width: 720px) { .kpi-fila { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 480px) { .kpi-fila { grid-template-columns: 1fr; } }

.kpi-tarjeta {
  background: var(--gray-50);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  display: flex; align-items: center; gap: var(--space-3);
  box-shadow: var(--shadow-sm);
}
.kpi-tarjeta__anillo { position: relative; width: 52px; height: 52px; flex-shrink: 0; }
.kpi-tarjeta__anillo svg { transform: rotate(-90deg); }
.kpi-anillo__fondo { fill: none; stroke: var(--gray-200); stroke-width: 6; }
.kpi-anillo__valor {
  fill: none; stroke: var(--ejn-azul-acento); stroke-width: 6; stroke-linecap: round;
  stroke-dasharray: calc(var(--valor, 0) / 100 * 169.65) 169.65;
}
.kpi-tarjeta__icono {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  color: var(--ejn-azul-acento);
}
.kpi-tarjeta__texto { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.kpi-tarjeta__valor { font-size: var(--fs-h3); font-weight: var(--fw-bold); color: var(--ejn-navy); line-height: 1; }
.kpi-tarjeta__etiqueta {
  font-size: var(--fs-small); color: var(--gray-600);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* ---- Filas de paneles (area/dona, barras/lista) ---- */
.dash-fila { display: grid; gap: var(--space-4); align-items: stretch; }
.dash-fila--2 { grid-template-columns: 2fr 1fr; }
@media (max-width: 860px) { .dash-fila--2 { grid-template-columns: 1fr; } }

.dash-panel {
  background: var(--gray-50);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-md);
  padding: var(--space-4);
  display: flex; flex-direction: column; gap: var(--space-3);
  box-shadow: var(--shadow-sm);
}
.dash-panel__cab { display: flex; align-items: center; justify-content: space-between; gap: var(--space-2); flex-wrap: wrap; }
.dash-panel__cab h3 { margin: 0; font-size: var(--fs-h3); font-weight: var(--fw-semibold); color: var(--ejn-navy); }

/* Area degradada */
.dash-area { width: min(100%, 500px); height: auto; aspect-ratio: 480 / 180; display: block; margin: 0 auto; }
.dash-area__linea { stroke: var(--ejn-azul-acento); stroke-width: 2.5; }
.dash-area__punto { fill: var(--white); stroke: var(--ejn-azul-acento); stroke-width: 2.5; }
.dash-area__meses {
  display: flex; justify-content: space-between;
  font-size: var(--fs-caption); color: var(--gray-400); padding: 0 2px;
  max-width: 500px; margin: 0 auto;
}

/* Dona / gauge */
.dash-panel--donut { align-items: center; text-align: center; }
.dash-donut { width: 140px; height: 140px; }
.dash-donut__fondo { fill: none; stroke: var(--gray-200); stroke-width: 10; }
.dash-donut__valor {
  fill: none; stroke: var(--ejn-azul-acento); stroke-width: 10; stroke-linecap: round;
  transform-origin: 60px 60px; transform: rotate(-90deg);
  stroke-dasharray: calc(var(--valor, 0) / 100 * 314.16) 314.16;
}
.dash-donut__num { font: var(--fw-bold) 22px var(--font-family); fill: var(--ejn-navy); }
.dash-donut__sub { font-size: 11px; fill: var(--gray-600); }
.dash-panel__cta { align-self: center; }

/* Barras, una resaltada */
.dash-barras { display: flex; align-items: flex-end; gap: var(--space-3); height: 120px; padding-top: var(--space-2); }
.dash-barra { display: flex; flex-direction: column; align-items: center; gap: var(--space-1); flex: 1 1 0; height: 100%; justify-content: flex-end; }
.dash-barra__col { display: block; width: 100%; max-width: 28px; border-radius: 6px 6px 0 0; background: var(--gray-200); height: calc(var(--alto, 0) * 1%); }
.dash-barra--activa .dash-barra__col { background: var(--ejn-azul-acento); }
.dash-barra__etiqueta { font-size: var(--fs-caption); color: var(--gray-400); }

/* Lista con avatar + titulo/subtitulo + valor */
.dash-lista { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.dash-lista__fila { display: flex; align-items: center; gap: var(--space-3); }
.dash-lista__avatar {
  width: 34px; height: 34px; border-radius: 50%; flex-shrink: 0;
  background: var(--ejn-azul-acento-soft); color: var(--ejn-azul-acento);
  display: flex; align-items: center; justify-content: center;
}
.dash-lista__texto { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.dash-lista__titulo {
  font-size: var(--fs-body); font-weight: var(--fw-medium); color: var(--ejn-navy);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dash-lista__subtitulo { font-size: var(--fs-caption); color: var(--gray-400); }
.dash-lista__valor { font-size: var(--fs-small); color: var(--gray-600); flex-shrink: 0; }

/* ============================================================
   Panel de inicio: columnas Usuarios y Resumen general (12/09/2026)
   Pedido de Daniel: agregar 2 columnas a la derecha de la fila
   Actividad por modulo / Ultimos movimientos. A diferencia del resto
   del dashboard (todavia ilustrativo), estas 2 SI son datos reales:
   Usuarios sale de sys_usuario (PanelController::inicio()), Resumen
   general suma $m['resumen'] que PanelResumenRepo ya calcula por
   modulo (mismo dato que los chips de cada tarjeta mas arriba).
   ============================================================ */
.dash-fila--4 { grid-template-columns: 1.3fr 1fr 0.9fr 1fr; }
@media (max-width: 1100px) { .dash-fila--4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .dash-fila--4 { grid-template-columns: 1fr; } }

/* Usuarios: 2 cifras apiladas + icono */
.dash-panel--usuarios { align-items: center; text-align: center; justify-content: center; }
.dash-usuarios { display: flex; flex-direction: column; align-items: center; gap: var(--space-2); }
.dash-usuarios__icono { color: var(--ejn-azul-acento); }
.dash-usuarios__cifra { display: flex; flex-direction: column; gap: 2px; }
.dash-usuarios__numero { font-size: 28px; font-weight: var(--fw-bold); color: var(--ejn-navy); line-height: 1; }
.dash-usuarios__cifra--sec .dash-usuarios__numero { font-size: var(--fs-h3); color: var(--ejn-azul-acento); }
.dash-usuarios__etiqueta { font-size: var(--fs-caption); color: var(--gray-600); }

/* Resumen general: una fila por categoria (punto de color + etiqueta + numero + %) */
.dash-resumen { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.dash-resumen__fila { display: flex; align-items: center; gap: var(--space-2); }
.dash-resumen__punto { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
.dash-resumen__punto--ok   { background: var(--ejn-green-dark); }
.dash-resumen__punto--warn { background: var(--ejn-gold-dark); }
.dash-resumen__punto--dang { background: var(--ejn-red-dark); }
.dash-resumen__punto--info { background: var(--ejn-blue-dark); }
.dash-resumen__punto--neu  { background: var(--gray-400); }
.dash-resumen__etiqueta {
  flex: 1 1 auto; min-width: 0; font-size: var(--fs-small); color: var(--ejn-navy);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dash-resumen__numero { font-size: var(--fs-small); font-weight: var(--fw-semibold); color: var(--ejn-navy); flex-shrink: 0; }
.dash-resumen__pct { font-size: var(--fs-caption); color: var(--gray-600); min-width: 32px; text-align: right; flex-shrink: 0; }

/* ---------- Tabla compacta dentro del panel lateral ancho ----------
   Pedido de Daniel (12/09/2026): la nomina real de ARCA que trae el panel
   de un trabajador (13 columnas) o de un periodo de Emision (21 columnas,
   ver .avisos--ancho mas arriba) seguia necesitando la barra de scroll
   horizontal incluso a 1100px -- pidio achicar la letra de la grilla para
   que entre todo sin scroll. Scopeado bajo .avisos--ancho .grilla__tabla
   a proposito: son las UNICAS 2 vistas que usan ese modificador de ancho
   (trabajador_lateral.php, emision_periodo_lateral.php), asi que esto no
   toca el tamano de letra de ninguna otra grilla de la plataforma -- ni
   siquiera el resto de los paneles laterales (.avisos base, ej. el de
   movimiento), que no tienen este problema de columnas. */
.avisos--ancho .grilla__tabla { font-size: 11px; }
.avisos--ancho .grilla__tabla thead th { padding: 5px 6px; }
.avisos--ancho .grilla__tabla tbody td { padding: 3px 6px; }
.avisos--ancho .grilla__tabla .badge { padding: 1px 6px; font-size: 10.5px; }

/* ============================================================
   Panel de inicio: dashboard a todo el ancho (12/09/2026, ajustado
   el mismo dia -- la primera pasada con clamp()/vw se fue al otro
   extremo: la dona y el anillo terminaron ocupando cientos de
   pixeles y el area degradada mas de 500px de alto en pantallas
   anchas, mas un bug real (el selector ".kpi-tarjeta__anillo svg"
   con combinador descendiente tambien agrandaba el icono del modulo
   anidado adentro, no solo el aro de progreso -- se corrige con
   combinador directo "> svg"). Vuelve a tamanos fijos moderados
   (sin vw): el dashboard sigue a todo el ancho (max-width:none,
   igual que la grilla de modulos), pero el contenido interno no
   escala con el viewport -- crece un poco respecto del diseno
   original (52->64px el anillo, 140->170px la dona) y se queda ahi,
   sin depender del ancho de pantalla. La grilla de KPIs sigue en
   minmax(0,1fr) por columna: en 4 columnas, cada tarjeta usa el
   espacio que le toca y el contenido interno no crece con el ancho,
   asi que no fuerza el layout a desbordar. */
.panel-dashboard { max-width: none; }
.kpi-tarjeta, .dash-panel { min-width: 0; }

.kpi-tarjeta__anillo { width: 64px; height: 64px; }
.kpi-tarjeta__anillo > svg { width: 100%; height: 100%; }
.kpi-tarjeta__valor { font-size: 20px; }

.dash-donut { width: 170px; height: 170px; }

.dash-barras { height: 140px; }

.dash-lista__avatar { width: 38px; height: 38px; }

/* 12/09/2026: el height:220px fijo que iba aca se saca -- era la causa
   real de la deformacion del area "Tendencia general" (ver nota junto a
   la regla base .dash-area, mas arriba en este archivo). aspect-ratio ya
   mantiene la proporcion correcta en cualquier ancho, sin necesidad de
   fijar un alto en px. */
.dash-fila--2 { grid-template-columns: 2fr 1fr; }

/* ============================================================
   Panel de inicio: indicadores laterales (12/09/2026)
   Grid de 3 columnas alrededor del panel-dashboard existente:
   izquierda 2 tarjetas (canales por zona / prestadores por
   especializacion, dato real), derecha 4 tarjetas (cotizaciones
   vs polizas -- real -- y 3 ilustrativas: siniestros/trabajadores,
   cobranza/emision, polizas por programa especial -- sin fuente
   de dato real confirmada, ver docs/ESTADO.md). Fijo en px, no
   vw/clamp -- leccion del ajuste de tamaño anterior en esta misma
   sesion (ver seccion de arriba, todo el ancho).
   ============================================================ */
.panel-dashboard-grid {
  display: grid;
  grid-template-columns: 240px minmax(0, 1fr) 240px;
  gap: var(--space-4);
  align-items: stretch;
  width: 100%;
  margin-top: var(--space-4);
}
.panel-dashboard-centro { min-width: 0; }
.panel-dashboard-centro .panel-dashboard { margin-top: 0; }
.panel-dashboard-lateral { display: flex; flex-direction: column; gap: var(--space-4); min-width: 0; }
.panel-dashboard-lateral > .dash-panel { flex: 1 1 0; }

@media (max-width: 1100px) {
  .panel-dashboard-grid { grid-template-columns: 1fr; }
  .panel-dashboard-centro { order: 1; }
  .panel-dashboard-lateral--izq { order: 2; }
  .panel-dashboard-lateral--der { order: 3; }
  .panel-dashboard-lateral { flex-direction: row; flex-wrap: wrap; }
  .panel-dashboard-lateral > .dash-panel { flex: 1 1 260px; }
}

/* Ranking horizontal (etiqueta + barra proporcional + numero) */
.dash-ranking { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.dash-ranking__fila { display: flex; align-items: center; gap: var(--space-2); }
.dash-ranking__etiqueta {
  font-size: var(--fs-small); color: var(--gray-600);
  flex: 0 0 92px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.dash-ranking__barra { flex: 1 1 auto; height: 8px; border-radius: 4px; background: var(--gray-200); overflow: hidden; }
.dash-ranking__barra span { display: block; height: 100%; border-radius: 4px; background: var(--ejn-azul-acento); width: calc(var(--alto, 0) * 1%); }
.dash-ranking__numero { font-size: var(--fs-small); font-weight: var(--fw-semibold); color: var(--ejn-navy); flex-shrink: 0; min-width: 20px; text-align: right; }
.dash-ranking__vacio { font-size: var(--fs-small); color: var(--gray-400); text-align: center; padding: var(--space-3) 0; }

/* Comparacion de 2 valores en barras verticales */
.dash-comparacion { display: flex; align-items: flex-end; gap: var(--space-4); height: 90px; padding-top: var(--space-2); }
.dash-comparacion__item { display: flex; flex-direction: column; align-items: center; gap: var(--space-1); flex: 1 1 0; height: 100%; justify-content: flex-end; }
.dash-comparacion__barra { display: flex; align-items: flex-end; width: 100%; max-width: 34px; height: 100%; }
.dash-comparacion__barra span { display: block; width: 100%; border-radius: 6px 6px 0 0; background: var(--ejn-azul-acento); height: calc(var(--alto, 0) * 1%); }
.dash-comparacion__item:nth-child(2) .dash-comparacion__barra span { background: var(--gray-300); }
.dash-comparacion__numero { font-size: var(--fs-small); font-weight: var(--fw-semibold); color: var(--ejn-navy); }
.dash-comparacion__etiqueta { font-size: var(--fs-caption); color: var(--gray-400); text-align: center; }

/* ============================================================
   Bienvenida — intro animada post-login (pedido de Daniel, 14/09/2026)
   Pantalla de transición pura entre el login y /panel, inspirada
   conceptualmente en el efecto de apertura de Netflix (logo con una
   animación breve) pero con 100% identidad RixART -- mismo wordmark y
   misma paleta que ya usa app/Views/auth/login.php. No es una pantalla
   de navegación: sin menú, sin acciones -- sólo se desvanece hacia /panel.
   Ver app/Views/panel/bienvenida.php y public/assets/js/ejn-bienvenida.js.
   ============================================================ */
.bienvenida-intro {
  position: fixed;
  inset: 0;
  z-index: 999;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  background: var(--ejn-navy);
}
.bienvenida-intro__fondo {
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 50% 42%, var(--ejn-navy-dark, #051122) 0%, var(--ejn-navy) 55%, #030b1a 100%);
  opacity: 0;
  animation: bienvenida-fondo-in 1.1s ease-out forwards;
}
.bienvenida-intro__contenido {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1.15rem;
  padding: 0 1.5rem;
  text-align: center;
}
.bienvenida-intro__logo {
  width: min(78vw, 480px);
  opacity: 0;
  transform: scale(.86);
  animation: bienvenida-logo-in .9s cubic-bezier(.16,.9,.3,1) .35s forwards;
}
.bienvenida-intro__logo svg {
  width: 100%;
  height: auto;
  display: block;
}
.bienvenida-intro__tagline {
  margin: 0;
  color: #cfe0ff;
  font-size: .95rem;
  letter-spacing: .04em;
  opacity: 0;
  animation: bienvenida-fade-in .7s ease-out 1s forwards;
}
.bienvenida-intro__barra {
  width: min(220px, 40vw);
  height: 2px;
  border-radius: 2px;
  background: rgba(255,255,255,.14);
  overflow: hidden;
  opacity: 0;
  animation: bienvenida-fade-in .5s ease-out 1.15s forwards;
}
.bienvenida-intro__barra span {
  display: block;
  height: 100%;
  width: 40%;
  border-radius: 2px;
  background: var(--ejn-azul-acento);
  animation: bienvenida-barra 1.5s ease-in-out 1.2s infinite;
}
.bienvenida-intro__saltar {
  position: absolute;
  right: 1.25rem;
  bottom: 1.25rem;
  z-index: 2;
  color: rgba(255,255,255,.55);
  font-size: .8rem;
  text-decoration: none;
  opacity: 0;
  animation: bienvenida-fade-in .5s ease-out 1.4s forwards;
}
.bienvenida-intro__saltar:hover,
.bienvenida-intro__saltar:focus-visible {
  color: #fff;
}
.bienvenida-intro.es-salida {
  animation: bienvenida-salida .4s ease-in forwards;
}

@keyframes bienvenida-fondo-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes bienvenida-logo-in { from { opacity: 0; transform: scale(.86); } to { opacity: 1; transform: scale(1); } }
@keyframes bienvenida-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes bienvenida-barra {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(350%); }
}
@keyframes bienvenida-salida { from { opacity: 1; } to { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .bienvenida-intro__fondo,
  .bienvenida-intro__logo,
  .bienvenida-intro__tagline,
  .bienvenida-intro__barra,
  .bienvenida-intro__saltar {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
  .bienvenida-intro__barra span {
    animation: none !important;
    width: 100%;
  }
}
/* ============================================================
   Bloquear pantalla — overlay sobre la página actual (14/09/2026)
   Reusa .bienvenida-intro/.es-salida (el intro de /bienvenida, ver más
   arriba) sin modificarlas -- este bloque sólo agrega el modificador
   --bloqueo y la pista de texto. No navega a ningún lado: se muestra y
   se oculta con un clic, tapando/destapando la página tal cual estaba.
   Ver app/Views/layouts/privado.php (id="bloqueo-pantalla") y
   iniciarBloqueoPantalla() en ejn-app.js.
   ============================================================ */
.bienvenida-intro--bloqueo {
  z-index: 2000;
  cursor: pointer;
}
.bloqueo-pantalla__pista {
  margin: 0;
  color: rgba(255,255,255,.6);
  font-size: .85rem;
}

@media (prefers-reduced-motion: reduce) {
  .bienvenida-intro.es-salida {
    animation: none !important;
    opacity: 0 !important;
  }
}