/* Overrides puntuales sobre style.css (NexaDash). Ver docs/04-front-guidelines.md. */

/* El padding horizontal y superior del content-body debe ser igual al padding interno de las
   cards (--bs-card-spacer-x: 1rem), no el 1.875rem por defecto del template. Si no, el margen
   entre el borde de la página y las cards se ve mayor que el padding interno de cada card. */
.content-body .container-fluid,
.content-body .container-sm,
.content-body .container-md,
.content-body .container-lg,
.content-body .container-xl,
.content-body .container-xxl {
	padding-top: 1rem;
	padding-right: 1rem;
	padding-left: 1rem;
}

/* Formularios "sm" por defecto: inputs, selects y botones nacen pequeños sin tener que
   agregar .form-control-sm / .form-select-sm / .btn-sm a mano en cada vista. Los valores
   copian los de esas clases -sm del propio style.css para no inventar una escala nueva.
   Van con !important porque style.css redefine .form-control y .btn más abajo en el propio
   archivo con `height`/`padding` en valores literales (no basados en variables), pisando
   cualquier override que no use !important aunque se cargue después. */
.form-control,
.form-select {
	height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)) !important;
	min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)) !important;
	padding: 0.25rem 0.5rem !important;
	font-size: 0.76563rem !important;
	border-radius: var(--bs-border-radius-sm) !important;
}

.form-select {
	padding-top: 0.25rem !important;
	padding-bottom: 0.25rem !important;
	padding-left: 0.5rem !important;
}

/* Controles nativos de DataTables ("Mostrar _MENU_ registros" / "Buscar:"): el <select> y el
   <input> que genera el propio plugin no llevan clase .form-control/.form-select (no vendorizamos
   la integración datatables.bootstrap5), así que no heredan el override "sm" de arriba y quedan
   con el tamaño nativo del navegador, grande y desalineado del resto de inputs "sm" de la UI.
   Replicamos a mano los mismos valores para que se vean consistentes en cualquier tabla. */
/* Separación entre la tabla y la fila inferior de "Mostrando X de Y registros" +
   Anterior/Siguiente: por defecto quedan pegadas al borde de la tabla en toda la app. */
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_paginate {
	margin-top: 1rem;
}

/* Mismo borde/radio/sombra que .card (regla `body:not([data-theme-version="dark"]) .card`
   más abajo en este archivo), pero aplicado directo a la tabla: dentro del card-body la
   tabla queda "pegada" sin separación visual propia. `table.dataTable` es la clase que el
   propio plugin agrega a cualquier tabla que inicializa, así que aplica a toda la app sin
   tener que repetirlo tabla por tabla. border-collapse: separate es necesario para que el
   radio se note (con collapse, que es lo que trae DataTables, el borde no respeta el radio
   de las celdas). box-sizing es redundante con el reset global de Bootstrap (`*` ya es
   border-box) pero se deja explícito para no depender de que nadie lo cambie. */
table.dataTable {
	box-sizing: border-box;
	border-collapse: separate;
	border-spacing: 0;
	border: 1px solid rgb(235, 237, 243);
	border-radius: 5.25px;
	box-shadow: 0 1px 2px 0 rgba(16, 24, 40, 0.04),
		0 8px 24px -6px rgba(16, 24, 40, 0.10);
}

.dataTables_wrapper .dataTables_length select,
.dataTables_wrapper .dataTables_filter input {
	height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2));
	min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2));
	padding: 0.25rem 0.5rem;
	font-size: 0.76563rem;
	border-radius: var(--bs-border-radius-sm);
	border: var(--bs-border-width) solid var(--bs-border-color, #e4e4e4);
}

/* style.css fija line-height: 2.375rem para inputs type=file (pensado para su altura por
   defecto, 45px); con la altura "sm" de arriba ese line-height alto empuja el texto del
   pseudo-botón "Seleccionar archivo" fuera del input y lo solapa con lo que venga debajo. */
.form-control[type="file"] {
	line-height: 1.5 !important;
	padding-top: 0.25rem !important;
	padding-bottom: 0.25rem !important;
}

.btn,
.btn-group > .btn {
	padding: 0.25rem 0.5rem !important;
	font-size: 0.76563rem !important;
	border-radius: var(--bs-border-radius-sm) !important;
}

.form-label {
	font-size: 0.8125rem;
	margin-bottom: 0.25rem;
}

/* Ícono de mostrar/ocultar contraseña (resources/views/auth/{login,register}.blade.php):
   style.css posiciona `.eye` con `top: 38px` fijo, pensado para la altura grande original del
   input (form-control-lg). Con la altura "sm" de arriba ese valor fijo ya no coincide y el
   ícono queda descolocado por debajo del input. El markup ahora envuelve solo input+ícono en su
   propio `.position-relative` (sin el label), así el ícono se puede centrar con `top: 50%` +
   `translateY(-50%)`, independiente de cualquier altura de input. */
.position-relative > .show-pass.eye {
	top: 50%;
	right: 0.5rem;
	transform: translateY(-50%);
}

/* Selects potenciados por bootstrap-select (bootstrap-select.min.js) no heredan de
   .form-select/.form-control; su tamaño lo controla .bootstrap-select .dropdown-toggle. */
.bootstrap-select .dropdown-toggle {
	min-height: 2.5rem;
	padding: 0.25rem 1.563rem;
	font-size: 0.76563rem;
}

/* Modales: style.css redefine .modal-header/.modal-body/.modal-footer más abajo en el propio
   archivo con 1.875rem de padding (mismo problema que .form-control/.btn de arriba, valores
   literales que pisan cualquier override sin !important). Con controles "sm" adentro, ese
   padding queda desproporcionado — lo igualamos al 1rem del resto de la UI. */
.modal-header,
.modal-body,
.modal-footer {
	padding: 1rem !important;
}

/* Modales con `.modal-dialog-scrollable` (ej. #rolModal) cuyo `<form>` envuelve
   header/body/footer (patrón estándar del proyecto, ver `roles/_modales.blade.php`): el scroll
   interno de Bootstrap depende de que `.modal-header/.modal-body/.modal-footer` sean HIJOS
   DIRECTOS de `.modal-content` (que es `display:flex; flex-direction:column`) — así
   `.modal-header`/`.modal-footer` quedan fijos (`flex-shrink:0`) y `.modal-body` absorbe el resto
   con `flex:1 1 auto; overflow-y:auto`. El `<form>` de por medio rompe esa cadena: pasa a ser el
   único hijo (real) de `.modal-content`, y como es un bloque normal (no flex) sin `overflow`
   propio, simplemente crece con su contenido — el `.modal-body` de adentro nunca activa su
   scroll y el `.modal-footer` (Guardar/Cancelar) queda cortado por el `overflow:hidden` de
   `.modal-content`, sin forma de llegar a él. `display:contents` en el `<form>` lo saca de la
   caja (sigue funcionando igual para submit/eventos) y deja que sus hijos participen del flex de
   `.modal-content` como si el `<form>` no existiera en el layout. El `min-height:0` en
   `.modal-body` es el mismo arreglo que ya usa el menú del sidebar más abajo: un ítem flex no
   encoge por debajo de su contenido (min-height:auto por defecto) salvo que se lo digamos. */
.modal-dialog-scrollable .modal-content > form {
	display: contents;
}

.modal-dialog-scrollable .modal-body {
	min-height: 0;
}

/* El gutter de Bootstrap por defecto (25px, ver .row en style.css) deja mucho aire entre
   columnas/filas de cards y de formularios, en cualquier .row del sitio, no solo en modales.
   Lo bajamos a 1rem (equivalente a `g-3`) en todos lados, para no depender de que cada vista
   nueva se acuerde de agregar la clase `g-3` a mano. --bs-gutter-y también, así las filas que
   wrappean (ej. las cards de métricas en 2 líneas en mobile) quedan separadas por lo mismo. */
.row {
	--bs-gutter-x: 1rem;
	--bs-gutter-y: 1rem;
}

/* .card trae su propio `margin-bottom` (más abajo en style.css, con el mismo patrón de valores
   literales que .form-control/.btn: 1.563rem, o sea 25px) para separar cards que están una
   debajo de la otra dentro de la misma columna, y usa `height: calc(100% - 25px)` para que,
   dentro de una fila de cards de igual altura, el margin no rompa el alto visual entre ellas.
   Se ve como "mucho aire" porque no está alineado con el resto del espaciado (1rem). Bajamos
   ambos valores juntos — si se cambia uno hay que cambiar el otro o las cards de una misma fila
   quedan de alturas distintas. */
.card {
	margin-bottom: 1rem !important;
	height: calc(100% - 1rem) !important;
}

/* ...pero ese margin-bottom de .card y el --bs-gutter-y de arriba se suman cuando una fila de
   cards wrappea (típico en mobile: las cards de métricas pasan de 3 en línea a 2 + 1), dejando
   2rem de aire vertical donde debería haber 1rem. En una fila de cards el espaciado ya lo pone
   la propia card, así que anulamos el gutter vertical ahí; las filas de formularios y demás
   contenido sin card lo conservan. */
.row:has(> * > .card) {
	--bs-gutter-y: 0;
}

/* Logo del tenant en el nav-header: style.css solo oculta .brand-title cuando el sidebar
   está colapsado (#main-wrapper.menu-toggle), pero deja .logo-abbr visible siempre a
   propósito, porque el combo por defecto del template es icono+texto conviviendo en el
   menú expandido. Con logos subidos por el tenant (uno para expandido, otro para
   comprimido) ese comportamiento se ve como "dos logos a la vez"; se quiere exclusivo:
   solo <img>.logo-abbr (el mini) cuando está colapsado, solo <img>.brand-title (el full)
   cuando está expandido. No toca los SVG por defecto (siguen combinados como diseñó el
   template) porque el selector apunta solo a <img>. */
#main-wrapper:not(.menu-toggle) .nav-header .brand-logo img.logo-abbr {
	display: none;
}

/* En mobile el sidebar siempre se ve como off-canvas (nunca "expandido" de verdad), así que
   la regla exclusiva de arriba dejaría los dos logos ocultos si #main-wrapper no tiene
   .menu-toggle. Por debajo del breakpoint donde style.css ya oculta .brand-title
   (@media max-width: 47.9375rem), forzamos el mini logo visible en los dos estados. */
@media (max-width: 47.9375rem) {
	#main-wrapper.menu-toggle .nav-header .brand-logo img.logo-abbr,
	#main-wrapper:not(.menu-toggle) .nav-header .brand-logo img.logo-abbr {
		display: inline-block;
	}
}

/* .nav-header .brand-logo trae justify-content: flex-start (con padding lateral) para el
   combo icono+texto por defecto, pensado para que el texto arranque pegado al icono. Con un
   único logo (imagen) ocupando todo el ancho del nav-header, ese layout lo deja pegado a la
   izquierda en vez de centrado. */
.nav-header .brand-logo {
	justify-content: center;
}

/* style.css fija margin-left: 0.6375rem en .brand-title, pensado para separarlo del icono
   .logo-abbr en el combo por defecto. Con el logo del tenant como imagen única y centrada,
   ese margen la descentra. */
.nav-header .brand-title {
	margin-left: 0;
}

/* Menú lateral: la fuente de los .nav-text (14px, ver .deznav .metismenu > li > a en style.css)
   queda un poco chica al lado de los lordicon de 30x30 de resources/views/partials/sidebar.blade.php. */
.deznav .metismenu > li > a .nav-text {
	font-size: 0.9375rem;
}

/* Bloque de usuario (foto + nombre + rol) en la cabecera del sidebar, con botón de cámara
   superpuesto para cambiar la foto sin salir del menú (ver partials/sidebar.blade.php). */
.sidebar-user-card {
	padding: 1.5rem 1rem 1rem;
}

/* Avatar editable (foto + botón de cámara superpuesto): componente compartido por el sidebar y
   el perfil, ver resources/views/components/avatar-editable.blade.php. El diámetro se pasa por
   la variable --avatar-editable-size desde el markup; el botón de cámara y su borde escalan a
   partir de ella, así que NO hay que ajustarlos por vista (era el error del bloque anterior: el
   tamaño vivía en dos sitios y el botón se salía del círculo si se tocaba solo uno). */
.avatar-editable {
	position: relative;
	display: inline-block;
	width: var(--avatar-editable-size, 96px);
	height: var(--avatar-editable-size, 96px);
	/* La forma vive en el contenedor (y la <img> la repite con `inherit`) para que el velo de
	   carga la respete: un ::before con `border-radius: inherit` sobre un contenedor sin radio
	   pintaría un cuadrado sobre una foto redonda. */
	border-radius: 50%;
}

.avatar-editable img {
	width: 100%;
	height: 100%;
	border-radius: inherit;
	object-fit: cover;
	border: 3px solid var(--rgba-primary-1, #fff);
}

/* Subida en curso: velo + spinner encima de la foto (public/js/avatar-upload.js pone
   .is-uploading en TODOS los avatares de la página, porque todos muestran la misma imagen).
   El disparador es un <input type="file">, no un botón, así que no aplica withButtonLoading;
   esto cumple la misma regla de "nunca dejar una petición en vuelo sin feedback".
   `border-radius: inherit` para que el velo respete la forma del avatar en cada vista (círculo
   en el sidebar, esquinas redondeadas en el perfil). */
.avatar-editable.is-uploading::before,
.avatar-editable.is-uploading::after {
	content: "";
	position: absolute;
	pointer-events: none;
}

.avatar-editable.is-uploading::before {
	inset: 0;
	border-radius: inherit;
	background: rgba(255, 255, 255, 0.65);
}

.avatar-editable.is-uploading::after {
	/* Centrado y proporcional al avatar, con un piso para que se vea en el sidebar comprimido. */
	width: max(20px, calc(var(--avatar-editable-size, 96px) * 0.3));
	height: max(20px, calc(var(--avatar-editable-size, 96px) * 0.3));
	top: 50%;
	left: 50%;
	transform: translate(-50%, -50%);
	border: 3px solid color-mix(in srgb, var(--primary, #1D69D6) 25%, transparent);
	border-top-color: var(--primary, #1D69D6);
	border-radius: 50%;
	animation: avatar-editable-spin 700ms linear infinite;
}

@keyframes avatar-editable-spin {
	to {
		transform: translate(-50%, -50%) rotate(360deg);
	}
}

@media (prefers-reduced-motion: reduce) {
	.avatar-editable.is-uploading::after {
		animation-duration: 2.5s;
	}
}

/* Mientras sube no tiene sentido ofrecer el botón de cambiar foto (su input está deshabilitado). */
.avatar-editable.is-uploading .avatar-editable-edit {
	opacity: 0.5;
	pointer-events: none;
}

.avatar-editable-edit {
	position: absolute;
	right: 0;
	bottom: 0;
	/* Proporcional al avatar, con un piso de 28px para que siga siendo tocable en el tamaño
	   chico del sidebar colapsado. */
	width: max(28px, calc(var(--avatar-editable-size, 96px) * 0.28));
	height: max(28px, calc(var(--avatar-editable-size, 96px) * 0.28));
	display: flex;
	align-items: center;
	justify-content: center;
	background: var(--primary);
	color: #fff;
	border-radius: 50%;
	border: 2px solid #fff;
	cursor: pointer;
	font-size: max(0.75rem, calc(var(--avatar-editable-size, 96px) * 0.1));
	margin: 0;
}
.sidebar-user-name {
	margin-top: 0.75rem;
	font-weight: 600;
	font-size: 0.9375rem;
}

.sidebar-user-role {
	font-size: 0.8125rem;
	color: #6f767e;
}

/* Sidebar comprimido (estilo "mini" fijo, o "full" colapsado con el botón toggle vía
   body.menu-toggle): igual que el template oculta .header-profile .header-info en ese modo,
   ocultamos nombre y rol y dejamos solo el avatar, más chico, para que quepa en el ancho de
   5rem que le da style.css. */
[data-sidebar-style="mini"] .sidebar-user-name,
[data-sidebar-style="mini"] .sidebar-user-role,
[data-sidebar-style="full"][data-layout="vertical"] .menu-toggle .sidebar-user-name,
[data-sidebar-style="full"][data-layout="vertical"] .menu-toggle .sidebar-user-role {
	display: none;
}

[data-sidebar-style="mini"] .sidebar-user-card,
[data-sidebar-style="full"][data-layout="vertical"] .menu-toggle .sidebar-user-card {
	padding: 1rem 0.5rem;
}

/* Basta con pisar la variable: el componente deriva de ella el alto, el ancho y el botón
   de cámara (que mantiene su piso de 28px para seguir siendo tocable). */
[data-sidebar-style="mini"] .sidebar-user-card .avatar-editable,
[data-sidebar-style="full"][data-layout="vertical"] .menu-toggle .sidebar-user-card .avatar-editable {
	--avatar-editable-size: 3rem;
}

/* Ítem activo del menú lateral (li.mm-active, clase que metismenu asigna al link de la
   pestaña actual). El fondo del link activo es transparente por defecto (igual que el resto
   de .deznav, que es blanco): una sombra sola no se nota sin relleno propio contra el que
   contrastar, así que se le da un fondo blanco explícito + sombra para que la pill quede
   "levantada" sobre el sidebar en vez de plana. */
.deznav .metismenu > li.mm-active > a,
.deznav .metismenu > li > a:hover {
	background-color: #fff;
	box-shadow: 0 2px 5px rgba(0, 0, 0, 0.08);
}

/* Layout del sidebar: la tarjeta de usuario queda arriba, el bloque help-desk + mode-btn
   pegado abajo, y el menú (<ul#menu>) al medio ocupando el espacio disponible con scroll
   vertical propio.
   Por defecto style.css hace que TODO el .deznav-scroll scrollee como un bloque único
   (height:100% + overflow-y:scroll) y PerfectScrollbar (js/custom.js) se engancha a ese mismo
   elemento. Lo convertimos en una columna flex sin scroll propio: user-card y help-desk/mode-btn
   quedan a tamaño fijo (flex:0 0 auto) arriba y abajo, y el menú toma el resto (flex:1) con su
   propio overflow-y (min-height:0 es necesario para que un ítem flex pueda encoger y scrollear).
   PerfectScrollbar deja de mostrar riel porque el contenedor ya no desborda. Scope a layout
   vertical para no pisar el `.grid-menu` horizontal (que style.css pone en flex-direction: row). */
[data-layout="vertical"] .deznav .deznav-scroll.grid-menu {
	display: flex;
	flex-direction: column;
	overflow: hidden;
}

[data-layout="vertical"] .deznav .deznav-scroll.grid-menu > .sidebar-user-card,
[data-layout="vertical"] .deznav .deznav-scroll.grid-menu > .help-desk,
[data-layout="vertical"] .deznav .deznav-scroll.grid-menu > .mode-btn {
	flex: 0 0 auto;
}

[data-layout="vertical"] .deznav .deznav-scroll.grid-menu > #menu {
	flex: 1 1 auto;
	min-height: 0;
	overflow-y: auto;
}

/* Banda de marca sobre la imagen del login (resources/views/auth/login.blade.php): título +
   redes sociales del tenant, centrados verticalmente sobre la imagen con una franja
   translúcida detrás del bloque de redes. Reemplaza el bloque `.text-white` simple que traía
   el template por defecto. */
.login-brand-band {
	position: absolute;
	left: 0;
	right: 0;
	top: 50%;
	transform: translateY(-50%);
}

.login-brand-title {
	color: #fff;
	font-weight: 700;
	font-size: 2.5rem;
	text-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
	margin-bottom: 1rem;
}

.login-brand-socials {
	display: flex;
	align-items: center;
	justify-content: center;
	gap: 0.75rem;
	padding: 0.75rem 0;
	background: rgba(255, 255, 255, 0.25);
	backdrop-filter: blur(2px);
}

.login-brand-socials a {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 2rem;
	height: 2rem;
	border-radius: 0.25rem;
	background: var(--primary, #1D69D6);
	color: #fff;
	font-size: 1rem;
}

/* ============================================================================
   Calendario de fichajes (feature 026). Mapa de clases `cal-veredicto-*` emitido
   por el backend (VeredictoCumplimiento::clase(), D6): tinte de fondo por día +
   etiqueta de texto accesible (nunca solo color). Variables por veredicto para
   poder ajustar light/dark en un solo lugar.
   ========================================================================== */

:root {
	--cal-ausencia: 220, 53, 69;    /* danger */
	--cal-retraso: 255, 159, 0;     /* warning ámbar */
	--cal-parcial: 253, 126, 20;    /* naranja, distinto de retraso */
	--cal-cumplido: 34, 172, 147;   /* success del template */
	--cal-exceso: 33, 150, 243;     /* info */
	--cal-libre: 137, 150, 163;     /* secondary */
	--cal-incidencia: 55, 60, 70;   /* dark */
	--cal-tinte: 0.16;
	--cal-texto: #1f2937;
}

[data-theme-version="dark"] {
	--cal-tinte: 0.28;
	--cal-texto: #e5e7eb;
}

/* Fondo del día en la vista mensual (eventos display:background). */
.fc .fc-bg-event.cal-veredicto-ausencia { background: rgba(var(--cal-ausencia), var(--cal-tinte)); opacity: 1; }
.fc .fc-bg-event.cal-veredicto-retraso { background: rgba(var(--cal-retraso), var(--cal-tinte)); opacity: 1; }
.fc .fc-bg-event.cal-veredicto-parcial { background: rgba(var(--cal-parcial), var(--cal-tinte)); opacity: 1; }
.fc .fc-bg-event.cal-veredicto-cumplido { background: rgba(var(--cal-cumplido), var(--cal-tinte)); opacity: 1; }
.fc .fc-bg-event.cal-veredicto-exceso { background: rgba(var(--cal-exceso), var(--cal-tinte)); opacity: 1; }
.fc .fc-bg-event.cal-veredicto-libre { background: rgba(var(--cal-libre), calc(var(--cal-tinte) * 0.6)); opacity: 1; }

/* Etiqueta de veredicto inyectada dentro del evento de fondo (eventDidMount). */
.fc .fc-bg-event .cal-etiqueta {
	position: absolute;
	left: 0.25rem;
	bottom: 0.25rem;
	font-size: 0.6875rem;
	font-weight: 600;
	line-height: 1.2;
	color: var(--cal-texto);
	pointer-events: none;
}

/* Leyenda y badges de veredicto (leyenda de la card, modal de día, modal de equipo). */
.cal-leyenda {
	display: flex;
	flex-wrap: wrap;
	gap: 0.375rem;
}

.cal-leyenda-item {
	display: inline-block;
	padding: 0.125rem 0.5rem;
	border-radius: 1rem;
	font-size: 0.75rem;
	font-weight: 600;
	color: var(--cal-texto);
	background: rgba(var(--cal-libre), 0.15);
	border-left: 3px solid rgb(var(--cal-libre));
}

.cal-leyenda-item.cal-veredicto-ausencia { background: rgba(var(--cal-ausencia), var(--cal-tinte)); border-left-color: rgb(var(--cal-ausencia)); }
.cal-leyenda-item.cal-veredicto-retraso { background: rgba(var(--cal-retraso), var(--cal-tinte)); border-left-color: rgb(var(--cal-retraso)); }
.cal-leyenda-item.cal-veredicto-parcial { background: rgba(var(--cal-parcial), var(--cal-tinte)); border-left-color: rgb(var(--cal-parcial)); }
.cal-leyenda-item.cal-veredicto-cumplido { background: rgba(var(--cal-cumplido), var(--cal-tinte)); border-left-color: rgb(var(--cal-cumplido)); }
.cal-leyenda-item.cal-veredicto-exceso { background: rgba(var(--cal-exceso), var(--cal-tinte)); border-left-color: rgb(var(--cal-exceso)); }
.cal-leyenda-item.cal-veredicto-libre { background: rgba(var(--cal-libre), var(--cal-tinte)); border-left-color: rgb(var(--cal-libre)); }
.cal-leyenda-item.cal-veredicto-incidencia { background: rgba(var(--cal-incidencia), var(--cal-tinte)); border-left-color: rgb(var(--cal-incidencia)); }
.cal-leyenda-item.cal-previsto { background: rgba(var(--cal-libre), 0.12); border-left: 3px dashed rgb(var(--cal-exceso)); }
.cal-leyenda-item.cal-real { background: rgba(var(--cal-exceso), 0.9); border-left-color: rgb(var(--cal-exceso)); color: #fff; }

/* Vistas semana/día (US2): tramo previsto como bloque de fondo punteado, intervalo real
   como bloque sólido superpuesto — distinguibles sin depender solo del color. */
.fc .fc-bg-event.cal-previsto {
	background: rgba(var(--cal-libre), 0.12);
	border: 1px dashed rgba(var(--cal-exceso), 0.7);
	opacity: 1;
}

.fc .cal-previsto-etiqueta {
	position: absolute;
	top: 0.125rem;
	right: 0.25rem;
	font-size: 0.625rem;
	font-weight: 600;
	color: var(--cal-texto);
	opacity: 0.7;
	pointer-events: none;
}

.fc .fc-event.cal-real {
	background: rgba(var(--cal-exceso), 0.85);
	border: 1px solid rgb(var(--cal-exceso));
}

.fc .cal-real-contenido {
	font-size: 0.6875rem;
	font-weight: 600;
	color: #fff;
	padding: 0 0.25rem;
}

/* Vista de equipo (US3): recuentos por día. */
.cal-resumen {
	display: flex;
	flex-wrap: wrap;
	gap: 0.25rem;
	padding: 0.125rem;
}

.cal-resumen-badge {
	display: inline-block;
	padding: 0.0625rem 0.375rem;
	border-radius: 1rem;
	font-size: 0.6875rem;
	font-weight: 600;
	color: var(--cal-texto);
}

.cal-resumen-badge.cal-veredicto-ausencia { background: rgba(var(--cal-ausencia), 0.35); }
.cal-resumen-badge.cal-veredicto-retraso { background: rgba(var(--cal-retraso), 0.35); }
.cal-resumen-badge.cal-veredicto-incidencia { background: rgba(var(--cal-incidencia), 0.3); }

/* El evento contenedor del resumen de equipo no debe verse como "pastilla" azul default. */
.fc .fc-event.cal-resumen-equipo {
	background: transparent;
	border: 0;
}

/* ============================================================================
   Ayuda contextual (mini-tutorial por pantalla)
   Trigger en el sidebar (.ayuda-trigger) + modal global (.ayuda-modal).
   Ver docs/04-front-guidelines.md, sección "Ayuda contextual".
   ============================================================================ */

/* --- Trigger en el sidebar --- */
.help-desk .ayuda-trigger {
	display: flex;
	align-items: center;
	gap: 0.625rem;
	width: 100%;
	padding: 0.625rem 0.75rem;
	border: 1px solid color-mix(in srgb, var(--primary, #1D69D6) 18%, transparent);
	border-radius: 0.75rem;
	background: color-mix(in srgb, var(--primary, #1D69D6) 7%, transparent);
	color: inherit;
	text-align: left;
	cursor: pointer;
	transition: transform 160ms cubic-bezier(0.23, 1, 0.32, 1),
		background-color 160ms ease, border-color 160ms ease;
}

.help-desk .ayuda-trigger:hover {
	background: color-mix(in srgb, var(--primary, #1D69D6) 12%, transparent);
	border-color: color-mix(in srgb, var(--primary, #1D69D6) 32%, transparent);
}

.help-desk .ayuda-trigger:active {
	transform: scale(0.98);
}

.ayuda-trigger-icon {
	display: flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 2rem;
	width: 2rem;
	height: 2rem;
	line-height: 0;
	border-radius: 0.5rem;
	background: color-mix(in srgb, var(--primary, #1D69D6) 15%, transparent);
}

.ayuda-trigger-text {
	display: flex;
	flex-direction: column;
	min-width: 0;
	flex: 1 1 auto;
}

.ayuda-trigger-title {
	font-size: 0.8125rem;
	font-weight: 600;
	line-height: 1.2;
	color: var(--primary, #1D69D6);
}

.ayuda-trigger-sub {
	font-size: 0.6875rem;
	line-height: 1.2;
	color: #8a94a6;
	white-space: nowrap;
	overflow: hidden;
	text-overflow: ellipsis;
}

.ayuda-trigger-chevron {
	flex: 0 0 auto;
	font-size: 0.625rem;
	color: color-mix(in srgb, var(--primary, #1D69D6) 55%, transparent);
	transition: transform 160ms cubic-bezier(0.23, 1, 0.32, 1);
}

.help-desk .ayuda-trigger:hover .ayuda-trigger-chevron {
	transform: translateX(2px);
}

/* Sidebar comprimido (toggle sobre `full`, o `mini`): el template solo contempla el `.help-btn`
   original del banco (`.help-desk .help-btn span { display: none }`), no este trigger custom, así
   que el texto seguía ahí y se partía en varias líneas dentro de los 5rem de ancho. En comprimido
   el botón queda cuadrado, centrado y solo con el icono; el nombre lo da el `title`/`aria-label`
   del propio botón. `icon-hover` no necesita nada: style.css ya oculta el bloque entero. */
[data-sidebar-style="full"][data-layout="vertical"] .menu-toggle .help-desk .ayuda-trigger,
[data-sidebar-style="mini"] .deznav .help-desk .ayuda-trigger {
	justify-content: center;
	gap: 0;
	padding: 0.5rem;
}

[data-sidebar-style="full"][data-layout="vertical"] .menu-toggle .help-desk .ayuda-trigger-text,
[data-sidebar-style="full"][data-layout="vertical"] .menu-toggle .help-desk .ayuda-trigger-chevron,
[data-sidebar-style="mini"] .deznav .help-desk .ayuda-trigger-text,
[data-sidebar-style="mini"] .deznav .help-desk .ayuda-trigger-chevron {
	display: none;
}

/* --- Modal --- */
.ayuda-modal .ayuda-content {
	position: relative;
	border: 0;
	border-radius: 1rem;
	overflow: hidden;
	box-shadow: 0 24px 60px -12px rgba(16, 24, 40, 0.28),
		0 0 0 1px rgba(16, 24, 40, 0.04);
}

/* Entrada un pelín más marcada que el default de Bootstrap: escala desde 0.96
   (nunca desde 0) con una curva ease-out fuerte. Modal centrado → origen centro. */
.ayuda-modal.fade .modal-dialog {
	transform: scale(0.96);
	transition: transform 220ms cubic-bezier(0.23, 1, 0.32, 1);
}

.ayuda-modal.show .modal-dialog {
	transform: scale(1);
}

@media (prefers-reduced-motion: reduce) {
	.ayuda-modal.fade .modal-dialog {
		transition: none;
	}
	.help-desk .ayuda-trigger,
	.ayuda-trigger-chevron {
		transition: none;
	}
}

.ayuda-close {
	position: absolute;
	top: 0.875rem;
	right: 0.875rem;
	z-index: 2;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 1.875rem;
	height: 1.875rem;
	padding: 0;
	border: 0;
	border-radius: 50%;
	background: rgba(16, 24, 40, 0.05);
	color: #667085;
	font-size: 0.8125rem;
	cursor: pointer;
	transition: background-color 140ms ease, color 140ms ease, transform 140ms ease;
}

.ayuda-close:hover {
	background: rgba(16, 24, 40, 0.1);
	color: #1f2937;
}

.ayuda-close:active {
	transform: scale(0.94);
}

.ayuda-head {
	display: flex;
	align-items: center;
	gap: 0.875rem;
	padding: 1.375rem 1.5rem 1.125rem;
	background: linear-gradient(180deg,
		color-mix(in srgb, var(--primary, #1D69D6) 9%, transparent),
		transparent);
	border-bottom: 1px solid rgba(16, 24, 40, 0.06);
}

.ayuda-head-icon {
	display: flex;
	align-items: center;
	justify-content: center;
	flex: 0 0 3rem;
	width: 3rem;
	height: 3rem;
	line-height: 0;
	border-radius: 0.875rem;
	background: color-mix(in srgb, var(--primary, #1D69D6) 14%, transparent);
	box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--primary, #1D69D6) 18%, transparent);
}

.ayuda-eyebrow {
	display: block;
	font-size: 0.6875rem;
	font-weight: 700;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--primary, #1D69D6);
}

.ayuda-title {
	margin: 0.0625rem 0 0;
	font-size: 1.0625rem;
	font-weight: 600;
	line-height: 1.2;
}

.ayuda-body {
	padding: 1.25rem 1.5rem 1.5rem;
	font-size: 0.875rem;
	line-height: 1.55;
	color: #475467;
	max-height: min(65vh, 32rem);
	overflow-y: auto;
}

.ayuda-body > p:first-child {
	margin-top: 0;
}

.ayuda-body p:last-child {
	margin-bottom: 0;
}

.ayuda-body strong {
	color: #1f2937;
	font-weight: 600;
}

/* Lista de pasos: números en círculo con el color del tenant. */
.ayuda-body ol {
	counter-reset: ayuda-paso;
	list-style: none;
	margin: 0.75rem 0 0.875rem;
	padding: 0;
}

.ayuda-body ol > li {
	position: relative;
	padding-left: 2.125rem;
	margin-bottom: 0.75rem;
}

.ayuda-body ol > li:last-child {
	margin-bottom: 0;
}

.ayuda-body ol > li::before {
	counter-increment: ayuda-paso;
	content: counter(ayuda-paso);
	position: absolute;
	left: 0;
	top: -0.0625rem;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 1.5rem;
	height: 1.5rem;
	border-radius: 50%;
	background: color-mix(in srgb, var(--primary, #1D69D6) 12%, transparent);
	color: var(--primary, #1D69D6);
	font-size: 0.75rem;
	font-weight: 700;
	line-height: 1;
}

.ayuda-body ul {
	padding-left: 1.125rem;
	margin: 0.5rem 0;
}

.ayuda-body ul > li {
	margin-bottom: 0.375rem;
}

/* Nota final (error común a evitar): callout suave, ícono de info implícito. */
.ayuda-body .ayuda-nota,
.ayuda-body > p.text-muted:last-child {
	margin-top: 1rem;
	padding: 0.625rem 0.75rem 0.625rem 2.25rem;
	position: relative;
	border-radius: 0.625rem;
	background: color-mix(in srgb, var(--primary, #1D69D6) 6%, transparent);
	color: #667085;
	font-size: 0.8125rem;
}

.ayuda-body .ayuda-nota::before,
.ayuda-body > p.text-muted:last-child::before {
	content: "\f05a";
	font-family: "Font Awesome 6 Free";
	font-weight: 900;
	position: absolute;
	left: 0.75rem;
	top: 0.6875rem;
	color: color-mix(in srgb, var(--primary, #1D69D6) 70%, #667085);
}

.ayuda-empty {
	padding: 0.5rem 0;
	color: #667085;
}

/* --- Dark mode --- */
[data-theme-version="dark"] .ayuda-trigger-sub {
	color: #9aa4b8;
}

[data-theme-version="dark"] .ayuda-modal .ayuda-content {
	box-shadow: 0 24px 60px -12px rgba(0, 0, 0, 0.55),
		0 0 0 1px rgba(255, 255, 255, 0.06);
}

[data-theme-version="dark"] .ayuda-close {
	background: rgba(255, 255, 255, 0.08);
	color: #c2c9d6;
}

[data-theme-version="dark"] .ayuda-close:hover {
	background: rgba(255, 255, 255, 0.16);
	color: #fff;
}

[data-theme-version="dark"] .ayuda-head {
	border-bottom-color: rgba(255, 255, 255, 0.08);
}

[data-theme-version="dark"] .ayuda-body {
	color: #b3bccb;
}

[data-theme-version="dark"] .ayuda-body strong {
	color: #f0f2f5;
}

/* ==========================================================================
   Cards informativas de métricas — "rail de acento"
   --------------------------------------------------------------------------
   Se aplican solas a cualquier .card que contenga un [data-metric] (selector
   :has()), así que no hay que tocar el markup de las ~20 vistas que ya usan
   el patrón. El color del rail lo decide la clase de color del propio número
   (.text-success / .text-danger); sin clase de color usa el primario del
   tenant. Ver docs/04-front-guidelines.md, "Icono flotante en cards
   informativas (métricas)".
   ========================================================================== */

.card:has([data-metric]) {
	--metric-accent: var(--primary);
	--metric-accent-soft: var(--rgba-primary-1);
	position: relative;
	overflow: hidden;
	transition: transform 200ms cubic-bezier(0.23, 1, 0.32, 1),
		box-shadow 200ms cubic-bezier(0.23, 1, 0.32, 1);
}

.card:has([data-metric].text-success) {
	--metric-accent: var(--bs-success);
	--metric-accent-soft: rgba(var(--bs-success-rgb), 0.08);
}

.card:has([data-metric].text-danger) {
	--metric-accent: var(--bs-danger);
	--metric-accent-soft: rgba(var(--bs-danger-rgb), 0.08);
}

/* Rail izquierdo. Se dibuja a ancho final y se comprime con scaleX para que
   el hover anime solo transform (sin layout ni paint). */
.card:has([data-metric])::before {
	content: '';
	position: absolute;
	top: 0;
	bottom: 0;
	left: 0;
	width: 5px;
	background: var(--metric-accent);
	transform: scaleX(0.6);
	transform-origin: left center;
	transition: transform 200ms cubic-bezier(0.23, 1, 0.32, 1);
	pointer-events: none;
	z-index: 1;
}

/* Tinte lateral del mismo acento, apagado en reposo. */
.card:has([data-metric])::after {
	content: '';
	position: absolute;
	inset: 0;
	background: linear-gradient(90deg, var(--metric-accent-soft), transparent 60%);
	opacity: 0;
	transition: opacity 200ms cubic-bezier(0.23, 1, 0.32, 1);
	pointer-events: none;
}

.card:has([data-metric]) .card-body {
	position: relative;
	z-index: 1;
}

/* El número no debe "bailar" mientras cuenta: cifras de ancho fijo. */
[data-metric] {
	font-variant-numeric: tabular-nums;
	font-feature-settings: 'tnum' 1;
}

@media (hover: hover) and (pointer: fine) {
	.card:has([data-metric]):hover {
		transform: translateY(-2px);
		box-shadow: 0 12px 24px -12px rgba(16, 24, 40, 0.18);
	}

	.card:has([data-metric]):hover::before {
		transform: scaleX(1);
	}

	.card:has([data-metric]):hover::after {
		opacity: 1;
	}
}

/* Entrada escalonada de la fila de métricas. */
.card:has([data-metric]) {
	animation: metricCardIn 420ms cubic-bezier(0.23, 1, 0.32, 1) both;
}

.row > *:nth-child(1) .card:has([data-metric]) { animation-delay: 0ms; }
.row > *:nth-child(2) .card:has([data-metric]) { animation-delay: 60ms; }
.row > *:nth-child(3) .card:has([data-metric]) { animation-delay: 120ms; }
.row > *:nth-child(4) .card:has([data-metric]) { animation-delay: 180ms; }
.row > *:nth-child(5) .card:has([data-metric]) { animation-delay: 240ms; }
.row > *:nth-child(6) .card:has([data-metric]) { animation-delay: 300ms; }

@keyframes metricCardIn {
	from {
		opacity: 0;
		transform: translateY(8px);
	}

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

[data-theme-version="dark"] .card:has([data-metric]):hover {
	box-shadow: 0 12px 24px -12px rgba(0, 0, 0, 0.5);
}

@media (prefers-reduced-motion: reduce) {
	.card:has([data-metric]) {
		animation: none;
	}

	.card:has([data-metric]):hover {
		transform: none;
	}

	.card:has([data-metric])::before {
		transform: scaleX(1);
	}
}

/* ==========================================================================
   Fondo de la app y elevación de las superficies
   --------------------------------------------------------------------------
   El template deja el body en #FCFCFC (casi blanco), o sea que las cards —que
   son #fff— no se despegan del fondo y su sombra no se lee. Bajamos el fondo a
   un gris azulado y subimos la sombra de todo lo que "flota" encima: cards,
   sidebar y cabecera.

   Las sombras van en dos capas (una corta y cerrada para el contacto, otra
   larga y difusa para la elevación): una sola capa grande se ve como una
   mancha gris, dos capas se leen como profundidad real.
   ========================================================================== */

body:not([data-theme-version="dark"]) {
	background-color: #f3f5f8;
}

body:not([data-theme-version="dark"]) .card {
	box-shadow: 0 1px 2px 0 rgba(16, 24, 40, 0.04),
		0 8px 24px -6px rgba(16, 24, 40, 0.10);
}

body:not([data-theme-version="dark"]) .deznav {
	/* Sombra proyectada hacia la derecha: el sidebar se apoya sobre el contenido. */
	box-shadow: 4px 0 24px -6px rgba(16, 24, 40, 0.10),
		1px 0 2px 0 rgba(16, 24, 40, 0.04);
}

body:not([data-theme-version="dark"]) .header {
	box-shadow: 0 1px 2px 0 rgba(16, 24, 40, 0.04),
		0 6px 18px -8px rgba(16, 24, 40, 0.10);
}

/* El hover de las cards de métricas tiene que seguir leyéndose por encima de
   la nueva sombra en reposo (ver bloque "Cards informativas de métricas"). */
@media (hover: hover) and (pointer: fine) {
	body:not([data-theme-version="dark"]) .card:has([data-metric]):hover {
		box-shadow: 0 2px 4px 0 rgba(16, 24, 40, 0.05),
			0 16px 32px -8px rgba(16, 24, 40, 0.18);
	}
}

/* ==========================================================================
   Cabecera de las DataTables en color de marca
   --------------------------------------------------------------------------
   El <thead> de los listados va con el primario del tenant y texto de
   contraste. Solo se pinta `table.dataTable`, no `.table` a secas: las tablas
   embebidas en documentos (líneas de factura, presupuestos, POS) tienen que
   seguir leyéndose como papel, no como listado.

   El color del texto sale de --primary-contraste, que calcula
   AparienciaTenant::contraste() por luminancia WCAG: blanco sobre marcas
   oscuras, gris oscuro sobre marcas claras. Dar por sentado "letra blanca"
   dejaría ilegible cualquier tenant con un amarillo o un celeste claro. El
   fallback #FFFFFF cubre el primario por defecto del template (#1D69D6).
   ========================================================================== */

/* Especificidad: style.css declara `table.dataTable > thead > tr > th` (0,1,4) con
   `background-color: transparent`. Una regla `table.dataTable thead th` (0,1,3) pierde
   aunque cargue después — el fondo quedaba transparente mientras el color sí se
   aplicaba, o sea letra blanca sobre card blanca. Hay que igualar la forma del
   selector, no subir el peso con !important. */
table.dataTable > thead > tr > th,
table.dataTable > thead > tr > td {
	background-color: var(--primary);
	color: var(--primary-contraste, #FFFFFF);
	border-bottom: 0;
	border-top: 0;
	border-color: transparent;
	/* style.css da 1rem de padding vertical a la cabecera: demasiado aire para
	   una banda de color. Se baja a .5rem sin tocar el horizontal (0.9375rem,
	   que style.css fija aparte con !important en el izquierdo). */
	padding-top: 0.5rem;
	padding-bottom: 0.5rem;
}

/* Las esquinas superiores acompañan el radio de la card que contiene la tabla:
   sin esto la banda de color se ve como un bloque pegado. */
table.dataTable > thead > tr > th:first-child,
table.dataTable > thead > tr > td:first-child {
	border-top-left-radius: 0.375rem;
}

table.dataTable > thead > tr > th:last-child,
table.dataTable > thead > tr > td:last-child {
	border-top-right-radius: 0.375rem;
}

/* Las flechas de orden son iconos de fuente: heredan currentColor, así que ya
   salen del color de contraste. Solo hay que subirles la opacidad, pensada
   para gris sobre blanco. */
table.dataTable thead > tr > th.sorting::after,
table.dataTable thead > tr > th.sorting_asc::after,
table.dataTable thead > tr > th.sorting_desc::after {
	opacity: 0.65;
}

table.dataTable thead > tr > th.sorting_asc::after,
table.dataTable thead > tr > th.sorting_desc::after {
	opacity: 1;
}

@media (hover: hover) and (pointer: fine) {
	table.dataTable thead > tr > th.sorting:hover::after {
		opacity: 1;
	}
}

[data-theme-version="dark"] table.dataTable > thead > tr > th,
[data-theme-version="dark"] table.dataTable > thead > tr > td {
	background-color: var(--primary);
	color: var(--primary-contraste, #FFFFFF);
}

/* ==========================================================================
   Badge "light" secundario — corrección de contraste
   --------------------------------------------------------------------------
   En style.css toda la familia `.light.badge-*` sigue el mismo patrón: fondo
   pálido teñido + texto en el color saturado (success = #cefff6 sobre #01BD9B,
   danger = #fbe7e7 sobre #E55555, etc.). `secondary` es la única excepción y
   está mal: fondo gris medio (#868999) con texto casi negro (#1F2025). Queda
   turbio e ilegible, sobre todo en una celda de DataTable.

   Se alinea con el resto de la familia: fondo gris muy claro, texto oscuro.
   Afecta a los tres usos actuales — "Simple" (POS/facturas simplificadas),
   "Inactivo" (horarios) y "Baja" (miembros del equipo).
   ========================================================================== */

.light.badge-secondary {
	background-color: #ECEDF0;
	border-color: #ECEDF0;
	color: #33363F;
}

/* ==========================================================================
   Contorno de los campos de formulario
   --------------------------------------------------------------------------
   El template deja `.form-control` en blanco con borde #E9ECEF: 1.2:1 de
   contraste contra el fondo blanco de la card o del modal, o sea que el campo
   prácticamente no tiene contorno y el formulario se lee como texto suelto.

   Se sube el borde a #CBD2DD (~1.9:1) y se le da al foco un anillo del color
   de marca. Se corrige el campo, no el fondo del modal: los formularios viven
   tanto en modales como en cards, y tintar solo el modal arreglaría la mitad
   del problema mientras aplana la jerarquía de elevación (body = suelo,
   card/modal = superficie levantada).

   Nota honesta sobre el número: la WCAG 1.4.11 pide 3:1 para el contorno de un
   control, y eso exigiría un borde ~#949494, que se ve pesado y sucio en un
   formulario largo. #CBD2DD es el valor que usan la mayoría de los design
   systems. Si alguna vez se audita accesibilidad en serio, este es el punto a
   revisar, y está identificado a propósito.
   ========================================================================== */

.form-control,
.form-select {
	border-color: #CBD2DD;
}

.form-control:hover,
.form-select:hover {
	border-color: #B4BDCC;
}

/* El template pone `box-shadow: none` en :hover/:focus/.active con la misma
   especificidad, así que el anillo hay que declararlo acá para que gane por
   orden de cascada. */
.form-control:focus,
.form-select:focus {
	border-color: var(--primary);
	box-shadow: 0 0 0 3px var(--rgba-primary-2, rgba(29, 105, 214, 0.2));
}

/* Deshabilitado: el borde baja de intensidad para que el campo se lea como no
   editable sin depender solo del color de fondo. */
.form-control:disabled,
.form-control[readonly],
.form-select:disabled {
	border-color: #E4E7EC;
}

/* ==========================================================================
   PRUEBA (2026-08-10) — cards de métricas con el ícono a la izquierda
   --------------------------------------------------------------------------
   Layout alternativo pedido para evaluar: ícono primero (en una placa
   redondeada con el tinte del acento), y a la derecha el número grande con la
   etiqueta debajo.

   Se hace 100% por CSS sobre el markup que ya existe (`.d-flex` con el bloque
   de texto y el del ícono): `row-reverse` invierte el orden visual sin tocar
   ninguna de las ~20 vistas, y `column-reverse` sube el número por encima de
   la etiqueta. Para volver atrás alcanza con borrar este bloque entero — no
   hay nada que revertir en las vistas ni en el JS.

   El contenedor del ícono se estiliza acá, sin usar `.icon-box bg-primary-light`
   del template (ver "Icono flotante en cards informativas"): esas clases traen
   2.5rem fijos con !important y recortarían el <lord-icon> de 50px.
   ========================================================================== */

.card:has([data-metric]) .card-body > .d-flex {
	flex-direction: row-reverse;
	gap: 1rem;
}

/* Placa del ícono. El tinte es el mismo acento semántico del rail, así que la
   card sigue leyéndose como una sola pieza. */
/* `:not(:first-child)`: hay cards de métrica sin ícono (dashboard: "Pendiente de cobro",
   "Facturas emitidas", "Ventas POS"). Sin este guard, el único <div> de esas cards
   sería a la vez :first-child y :last-child y se convertiría en una placa vacía. */
.card:has([data-metric]) .card-body > .d-flex > div:last-child:not(:first-child) {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 4rem;
	height: 4rem;
	border-radius: 1rem;
	background-color: var(--metric-accent-soft);
}

/* Número arriba, etiqueta debajo. */
/* `flex: 1 1 auto` en vez de pelear el `justify-content-between` de la vista: esa
   utilidad de Bootstrap lleva !important, así que no se puede sobreescribir sin otro
   !important. Haciendo crecer el bloque de texto no queda espacio libre que repartir
   y los dos elementos terminan pegados, que es el efecto buscado. */
.card:has([data-metric]) .card-body > .d-flex > div:first-child {
	display: flex;
	flex: 1 1 auto;
	flex-direction: column;
	align-items: flex-start;
	min-width: 0;
}

/* Orden explícito en vez de `column-reverse`: varias cards del dashboard traen un
   <small> con la variación vs. el periodo anterior, y invertir la columna entera lo
   mandaría arriba de todo. Con `order` cada pieza queda donde corresponde sin
   importar cuántas haya. */
.card:has([data-metric]) .card-body > .d-flex > div:first-child > h3 {
	order: 1;
	margin-bottom: 0.125rem;
	line-height: 1.15;
}

.card:has([data-metric]) .card-body > .d-flex > div:first-child > h6 {
	order: 2;
	margin-bottom: 0;
	font-weight: 400;
	color: var(--text-gray, #6c757d);
}

.card:has([data-metric]) .card-body > .d-flex > div:first-child > small {
	order: 3;
	margin-top: 0.25rem;
}

/* ==========================================================================
   Filtro de un solo valor como control segmentado (.filtro-segmentado)
   --------------------------------------------------------------------------
   El markup de partida es un `.btn-group` de Bootstrap — con radios
   `.btn-check` + `label.btn` (patrón dashboard/informes-comerciales/cobros,
   rango de fechas) o con `<button>` sueltos que un JS alterna con `.active`
   (patrón facturas/leads/cobros, filtro por tipo o estado): en ambos casos,
   rectángulos contorneados pegados que pesan más que los datos que filtran.

   `.filtro-segmentado` reestiliza CUALQUIERA de los dos como control
   segmentado (una pista gris con el activo en blanco "levantado"), que es
   el patrón habitual para elegir UNA opción de un set corto y se lee de un
   vistazo. Agregar la clase al `.btn-group` es todo lo que hace falta —
   nunca crear un filtro de un solo valor con `.btn-group` a secas.

   Ver docs/04-front-guidelines.md, sección "Filtro de un solo valor:
   control segmentado (.filtro-segmentado)" — toda vista nueva con este tipo
   de filtro debe usar esta clase, no reinventar el estilo. */

.filtro-segmentado {
	display: inline-flex;
	gap: 0.125rem;
	padding: 0.1875rem;
	background-color: #EDEFF3;
	border-radius: 999px;
}

/* style.css define `label { margin-bottom: 0.5rem }` global; acá los <label> son los
   propios botones (patrón .btn-check + label.btn), no campos de formulario, así que ese
   margen deja un hueco debajo del control. */
.filtro-segmentado label.btn {
	margin-bottom: 0;
}

/* `!important` en radius y padding, a regañadientes: más arriba en este mismo archivo
   hay un `.btn, .btn-group > .btn { padding / font-size / border-radius !important }`
   global (compactado de botones), y contra un !important solo gana otro !important. La
   clase gana a ese !important sin ampliar el alcance a ningún otro botón de la app. */
.filtro-segmentado > .btn {
	margin: 0;
	border: 0;
	border-radius: 999px !important;
	padding: 0.375rem 0.9375rem !important;
	font-weight: 500;
	color: #5A6172;
	background-color: transparent;
	box-shadow: none;
	transition: background-color 160ms ease, color 160ms ease, transform 160ms cubic-bezier(0.23, 1, 0.32, 1);
}

@media (hover: hover) and (pointer: fine) {
	.filtro-segmentado > .btn:hover {
		color: #1F2937;
		background-color: rgba(255, 255, 255, 0.6);
	}
}

/* Feedback de pulsación: el control tiene que responder al dedo/click. */
.filtro-segmentado > .btn:active {
	transform: scale(0.97);
}

/* El seleccionado es el "thumb": blanco y levantado sobre la pista. Cubre los dos
   mecanismos de selección que conviven en la app: radios .btn-check (rango de fechas)
   y botones sueltos que un JS alterna con .active (filtro por tipo/estado). */
.filtro-segmentado .btn-check:checked + .btn,
.filtro-segmentado > .btn.active {
	color: var(--primary);
	background-color: #fff;
	box-shadow: 0 1px 2px rgba(16, 24, 40, 0.06), 0 1px 3px rgba(16, 24, 40, 0.12);
}

/* Foco de teclado: los radios son invisibles, así que el anillo tiene que
   pintarse sobre el label o el control queda inoperable a ciegas. */
.filtro-segmentado .btn-check:focus-visible + .btn {
	outline: 2px solid var(--primary);
	outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
	.filtro-segmentado > .btn {
		transition: none;
	}

	.filtro-segmentado > .btn:active {
		transform: none;
	}
}

/* ==========================================================================
   Placa de ícono (.icono-placa)
   --------------------------------------------------------------------------
   Contenedor reutilizable para un <lord-icon>: cuadrado redondeado con el
   tinte del color de marca. Es el mismo tratamiento que llevan las cards de
   métricas, extraído a una clase para poder usarlo en cabeceras (perfil de
   cliente) sin duplicar valores.

   No usa `.icon-box bg-primary-light` del template a propósito: esas clases
   traen 2.5rem fijos con !important y recortan el ícono (ver "Icono flotante
   en cards informativas").
   ========================================================================== */

.icono-placa {
	flex: 0 0 auto;
	display: flex;
	align-items: center;
	justify-content: center;
	width: 4rem;
	height: 4rem;
	border-radius: 1rem;
	background-color: var(--rgba-primary-1, rgba(29, 105, 214, 0.1));
}

/* Botonera del card-header en móvil: los headers de listado son `title + <div class="d-flex gap-2">`
   con filtros y botones. En pantallas chicas ese grupo se comprime en columnas angostas y los
   botones quedan de anchos distintos y con el texto partido en varias líneas ("Importar
   document/o"). Por debajo de 576px la botonera pasa a ocupar el ancho completo y apila sus
   controles uno debajo del otro, cada uno a w-100 (el `!important` es para ganarle a los
   `style="width: auto"` inline de los selects de filtro). Se aplica solo a los grupos que
   contienen controles, para no tocar headers con otro tipo de contenido en flex. */
@media only screen and (max-width: 35.9375rem) {
	.card-header:has(> .d-flex .btn) {
		flex-wrap: wrap;
	}

	.card-header > .d-flex:has(.btn, .form-control, .form-select, .dropdown) {
		width: 100%;
		flex-direction: column;
		align-items: stretch;
		gap: 0.5rem;
	}

	.card-header > .d-flex:has(.btn, .form-control, .form-select, .dropdown) > * {
		width: 100% !important;
	}
}

/* Avatar de "Mi perfil": usa el mismo componente editable que el sidebar, con su propio tamaño.
   Reemplaza a `.profile-overview .profile-avatar` de style.css (160/100/80px según ancho), que
   solo dimensionaba la <img>: acá el tamaño va en el contenedor vía la variable, si no el botón
   de cámara queda descolocado. Un escalón más grande que el del template en cada breakpoint. */
.profile-avatar-editor .avatar-editable {
	--avatar-editable-size: 200px;
	/* Equivalente a `rounded-4` de Bootstrap, en el contenedor: la <img> lo hereda y el velo de
	   carga también, así el perfil mantiene su avatar cuadrado de esquinas redondeadas. */
	border-radius: var(--bs-border-radius-xl, 1rem);
}

@media only screen and (max-width: 61.9375rem) {
	.profile-avatar-editor .avatar-editable {
		--avatar-editable-size: 160px;
	}
}

@media only screen and (max-width: 35.9375rem) {
	.profile-avatar-editor .avatar-editable {
		--avatar-editable-size: 130px;
	}
}

/* ==========================================================================
   Iconos de la barra superior
   --------------------------------------------------------------------------
   style.css maquetaba cada `.notification_dropdown .nav-link` como una caja de
   45x45 centrada con `line-height: 25px` + `text-align: center`, no con flex:
   un <svg> dentro no se centra vertical con line-height, así que quedaban
   descolgados y cada uno a distinta altura según su alto propio (24, 19, 25px).
   Además los SVG traían `fill="black"` a mano, que sobre un topbar de marca
   oscuro se ve casi invisible.

   Acá: caja cuadrada real con flex, todos los iconos al mismo tamaño, y el
   color heredado del header (--topbar-contraste, calculado por luminancia en
   AparienciaTenant) vía currentColor — legible con topbar clara u oscura.
   ========================================================================== */
.header-right .notification_dropdown .nav-link {
	display: flex;
	align-items: center;
	justify-content: center;
	width: 42px;
	height: 42px;
	margin: 0 2px;
	padding: 0;
	line-height: 1;
	color: inherit;
	transition: background-color 160ms ease;
}

.header-right .notification_dropdown .nav-link svg {
	width: 22px;
	height: 22px;
	display: block;
}

/* El realce del hover se construye con el color del propio contenido, así que
   funciona igual sobre una barra oscura (aclara) que sobre una clara (oscurece),
   sin fijar un blanco o un negro que solo sirve para la mitad de los tenants. */
.header-right .notification_dropdown .nav-link:hover,
.header-right .notification_dropdown .nav-link:focus-visible,
.header-right .notification_dropdown .show > .nav-link {
	background-color: color-mix(in srgb, currentColor 15%, transparent);
	color: inherit;
}

@media (prefers-reduced-motion: reduce) {
	.header-right .notification_dropdown .nav-link {
		transition: none;
	}
}

@media only screen and (max-width: 35.9375rem) {
	.header-right .notification_dropdown .nav-link {
		width: 36px;
		height: 36px;
	}

	.header-right .notification_dropdown .nav-link svg {
		width: 20px;
		height: 20px;
	}
}
