@charset "UTF-8";

/* Las fuentes van PRIMERO: un @font-face tiene que estar declarado antes de que
   lo use cualquier regla, y ademas asi el navegador empieza a pedirlas antes. */
@import "fonts.css?rev=1790257710";
@import "pico.css.php?rev=1789555797";
@import "mixin.css?rev=1758568260";
@import "login.css?rev=1790330958";

/**
 * Dolibarr WebPortal - Tailwind Integration Layer
 * Bridge Dolibarr dynamic theme colors to Tailwind CSS variables
 */

/* =====================================================================
 * SISTEMA DE DISEÑO EASYSOFT  (2026-09-24)
 *
 * Todo el portal se pinta desde estos tokens. La paleta es la de la web
 * corporativa (easysoft.es): grafito #08070D y amarillo #FCDD08, con
 * Inter y Space Grotesk.
 *
 * ⚠️ DOS REGLAS PARA NO ROMPER NADA:
 *
 *  1. El amarillo NUNCA se usa como color de texto sobre fondo claro: no
 *     llega a 4.5:1 ni de lejos. Es color de SUPERFICIE (botones, marcas,
 *     indicadores), y sobre el amarillo el texto va en grafito
 *     (--esp-accent-ink). Hay una excepcion: --esp-accent-text, un amarillo
 *     oscurecido que SI pasa contraste y se usa para enlaces y cifras sobre
 *     fondo oscuro.
 *
 *  2. Sobrescribir los tokens del tema del webportal (--primary-color-*,
 *     --tw-primary-*, y los de Pico que salen de ellos) es DELIBERADO y es
 *     la palanca del recoloreado: hay ~200 reglas que ya tiran de ellos, asi
 *     que cambiando aqui el triplete el portal entero cambia de color sin
 *     tocar una sola de esas reglas.
 * ===================================================================== */

:root {
	/* ---- Marca ---- */
	--esp-accent: #FCDD08;          /* amarillo easysoft.es (superficies) */
	--esp-accent-ink: #08070D;      /* texto SOBRE el amarillo */
	--esp-accent-strong: #E7C906;   /* hover del amarillo */
	--esp-accent-soft: rgba(252, 221, 8, 0.14);
	/* Amarillo legible sobre fondo oscuro (contraste >= 4.5:1 sobre grafito).
	   Es el que se usa para texto, enlaces y cifras, nunca el amarillo puro. */
	--esp-accent-text: #FCDD08;
	--esp-accent-text-dim: #D9BC05;

	/* ---- Amarillo de marca sobre SUPERFICIES ----
	   El amarillo #FCDD08 no se puede usar como color de TEXTO ni de icono sobre
	   fondo claro (1,4:1 sobre blanco) ni tampoco el blanco sobre el amarillo
	   oscurecido (2,33:1). Estos tres tokens resuelven los dos casos, medidos:
	     wash / wash-2    fondos tenidos de marca (bandeja de bienvenida,
	                      cabeceras de tarjeta)
	     on-surface       texto e iconos de marca sobre esos fondos
	   Claro: 5,86:1 sobre el wash y 5,83:1 sobre accent-soft.
	   Oscuro: 12,24:1 y 9,52:1. */
	--esp-accent-wash: #FFFAE4;
	--esp-accent-wash-2: #FEF3B0;
	--esp-accent-on-surface: #6F6200;

	/* ---- Neutros: TEMA CLARO (por defecto) ---- */
	--esp-bg: #F6F6F8;              /* fondo de la app */
	--esp-surface: #FFFFFF;         /* tarjetas, sidebar, tablas */
	--esp-surface-2: #F1F1F5;       /* filas alternas, cabeceras de tabla */
	--esp-surface-3: #E8E8EE;       /* hover de fila */
	--esp-ink: #14131A;             /* texto principal */
	--esp-ink-2: #55535F;           /* texto secundario */
	/* ⚠️ ink-3 es el tope para METADATOS con contraste AA. El #85838F de antes
	   daba 3,30:1 sobre blanco y 3,30 sobre las cabeceras de tabla: por debajo
	   del 4,5:1 que pide WCAG 2.2 (1.4.3 Contraste minimo, nivel AA) para texto
	   normal. Este da 4,50:1 en las tres superficies claras. Si alguien lo
	   aclara, que vuelva a medirlo. */
	--esp-ink-3: #6F6D79;           /* texto terciario / metadatos */
	--esp-line: #D9D8E0;            /* bordes */
	--esp-line-2: #EAE9EF;          /* bordes suaves */

	/* ---- Estados (fondo / texto) ---- */
	--esp-ok-bg: #E6F6EF;    --esp-ok-fg: #066C4A;
	--esp-warn-bg: #FEF3C7;  --esp-warn-fg: #92400E;
	--esp-bad-bg: #FDECEC;   --esp-bad-fg: #A01818;
	--esp-info-bg: #E9EEFE;  --esp-info-fg: #1B3FA8;
	--esp-neutral-bg: #EDEDF2; --esp-neutral-fg: #4A4854;

	/* ---- Cromo del LOGIN (la mitad del espacio): siempre noche cerrada ----
	   ⚠️ Estos NO son los del sidebar del panel. Aqui detras va el planeta y el
	   campo de estrellas, asi que el fondo tiene que ser casi negro o el shader
	   se ve lavado. El sidebar del panel usa los --esp-nav-* de abajo. */
	--esp-chrome-bg: #08070D;
	--esp-chrome-bg-2: #12101A;
	--esp-chrome-ink: #F5F4F8;
	--esp-chrome-ink-2: #9A98A8;
	--esp-chrome-line: rgba(255, 255, 255, 0.09);
	--esp-chrome-hover: rgba(255, 255, 255, 0.06);
	--esp-chrome-active: rgba(252, 221, 8, 0.14);

	/* ---- Sidebar del PANEL: oscuro SIEMPRE, tambien en tema claro ----
	   Tomado del sistema de diseño Horizon de easygestion
	   (D:\GIT\SAAS-EASYSOFTCLOUD, src/design-system/tokens/colors.css), que es
	   el esquema de color de la casa: alli el sidebar va con --h-navBg, un
	   oscuro calido, y el item activo con --h-navBg2 + texto blanco + una barra
	   de acento a la izquierda.

	   ⚠️ POR QUE OSCURO TAMBIEN EN CLARO: antes el sidebar era --esp-surface,
	   es decir BLANCO en tema claro e igual que las tarjetas del contenido en
	   tema oscuro. Sin diferencia entre el cromo y el contenido, el panel se lee
	   como una sola masa y no se distingue la navegacion de la pagina. Es lo que
	   pasaba en modo oscuro.

	   La matiz es calida (hue 60 en OKLCH) y no violeta: el violeta frio que
	   tenia antes es lo que hacia que el oscuro no cuadrara. */
	--esp-nav-bg: #191512;
	--esp-nav-bg-2: #25211D;
	--esp-nav-ink: #E9E3DF;
	--esp-nav-ink-2: #9E9791;
	/* Los rotulos de seccion ("DOCUMENTOS", "PROYECTOS") son navegacion, no
	   adorno: con el #69625D de antes daban 3,03:1 y no se leian. Ahora 4,56:1. */
	--esp-nav-ink-3: #877E77;
	--esp-nav-line: #322C28;

	/* ---- Formas y sombras ---- */
	--esp-radius-sm: 8px;
	--esp-radius: 12px;
	--esp-radius-lg: 18px;
	--esp-shadow-sm: 0 1px 2px rgba(16, 15, 25, 0.06);
	--esp-shadow: 0 1px 2px rgba(16, 15, 25, 0.05), 0 10px 28px -18px rgba(16, 15, 25, 0.28);
	--esp-shadow-lg: 0 24px 60px -30px rgba(16, 15, 25, 0.45);

	/* ---- Tipografia ---- */
	--esp-font: 'Inter', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', sans-serif;
	--esp-font-display: 'Space Grotesk', 'Inter', ui-sans-serif, system-ui, sans-serif;
	--esp-font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

	/* ---- Foco visible: grafito sobre claro, amarillo sobre oscuro ---- */
	--esp-focus: #08070D;

	color-scheme: light;
}

/* ---- Neutros: TEMA OSCURO ----
 *
 * Rampa calida del sistema Horizon (ver --esp-nav-* arriba), no el violeta frio
 * de antes: #0B0A11/#12101A tiraban a azul-violeta y el panel entero se veia
 * frio. Estos salen de oklch(L C 60) del SaaS, pasados a hex.
 */
[data-esp-theme="dark"] {
	--esp-bg: #100C0A;
	--esp-surface: #1A1512;
	/* ⚠️ surface-2 va POR ENCIMA de surface, no por debajo. La rampa de Horizon
	   pone panel2 mas oscuro que panel, pero eso vale para un panel lateral, no
	   para la cabecera de una tabla: usandola tal cual, la cabecera salia MAS
	   OSCURA que las filas y la tabla se leia como un agujero. Aqui la jerarquia
	   es siempre hacia arriba: bg < surface < surface-2 < surface-3. */
	--esp-surface-2: #221C18;
	--esp-surface-3: #2C2521;
	--esp-ink: #F5F2F0;
	--esp-ink-2: #B4AEA9;
	/* 4,55:1 sobre la superficie mas clara (la cabecera). Antes 4,22:1. */
	--esp-ink-3: #938C87;
	--esp-line: #36302A;
	--esp-line-2: #262019;

	/* El fondo tenido de marca en oscuro NO puede ser el amarillo al 90%: eso es
	   un diseño de tema claro metido en un panel oscuro, y es lo que hacia que la
	   bandeja de bienvenida y la tarjeta de Presupuestos fuesen manchas claras. */
	--esp-accent-wash: #241E0A;
	--esp-accent-wash-2: #2E2609;
	--esp-accent-on-surface: #FCDD08;

	--esp-ok-bg: #12301A;    --esp-ok-fg: #7BD18D;
	--esp-warn-bg: #33240C;  --esp-warn-fg: #E8AA4E;
	--esp-bad-bg: #33161A;   --esp-bad-fg: #EE8A8A;
	--esp-info-bg: #12222F;  --esp-info-fg: #7FB6E8;
	--esp-neutral-bg: #25211D; --esp-neutral-fg: #B3ADA8;

	/* El sidebar en oscuro va MAS OSCURO que el contenido, no igual: asi el
	   cromo se separa de la pagina por arriba en vez de por abajo. */
	--esp-nav-bg: #060403;
	--esp-nav-bg-2: #0E0A08;
	--esp-nav-ink: #EEEAE8;
	--esp-nav-ink-2: #A49E99;
	--esp-nav-ink-3: #7E756F;   /* 4,54:1 sobre el cromo; antes 3,12:1 */
	--esp-nav-line: #1E1A16;

	--esp-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4);
	--esp-shadow: 0 1px 2px rgba(0, 0, 0, 0.4), 0 12px 32px -20px rgba(0, 0, 0, 0.8);
	--esp-shadow-lg: 0 28px 70px -34px rgba(0, 0, 0, 0.9);

	/* Sobre fondo oscuro el foco va en amarillo: el grafito seria invisible */
	--esp-focus: var(--esp-accent);

	color-scheme: dark;
}

/* ===== Bridge Dolibarr theme colors to Tailwind-friendly variables =====
 *
 * EasySoft: el triplete del tema se FUERZA al amarillo de marca. El portal se
 * pintaba con el primario configurable del modulo webportal (WebPortalTheme),
 * que en esta instalacion daba un violeta azulado. Ahora manda la marca.
 * H=52 S=98% L=51% son los de #FCDD08. */
:root {
	--primary-color-hue: 52;
	--primary-color-saturation: 98%;
	--primary-color-lightness: 51%;

	--tw-primary-h: var(--primary-color-hue, 52);
	--tw-primary-s: var(--primary-color-saturation, 98%);
	--tw-primary-l: var(--primary-color-lightness, 51%);

	/* --- Correcciones a Pico, que asume un primario oscuro con texto blanco ---
	   Con amarillo, el texto blanco del boton primario es ilegible (~1.4:1):
	   Pico pinta los botones primarios con --primary-inverse. Se pasa a grafito.
	   Y --color (el color de texto base de Pico) NO puede ser el primario: el
	   amarillo sobre blanco no se lee. */
	--primary-inverse: var(--esp-accent-ink);
	--primary-hover: var(--esp-accent-strong);
	--primary-focus: var(--esp-accent-soft);
	--color: var(--esp-ink);
	--background-color: var(--esp-surface);
	--muted-color: var(--esp-ink-2);
	--muted-border-color: var(--esp-line);
	--card-background-color: var(--esp-surface);
	--card-border-color: var(--esp-line);
	--card-sectionning-background-color: var(--esp-surface-2);
	--table-border-color: var(--esp-line);
	--table-row-stripped-background-color: var(--esp-surface-2);
	--form-element-border-color: var(--esp-line);
	--form-element-color: var(--esp-ink);
	--form-element-background-color: var(--esp-surface);
	--form-element-active-border-color: var(--esp-ink);
	--form-element-focus-color: var(--esp-accent-soft);
	--secondary: var(--esp-ink-2);
	--h1-color: var(--esp-ink);
	--h2-color: var(--esp-ink);
	--h3-color: var(--esp-ink);
	--h4-color: var(--esp-ink);
	--h5-color: var(--esp-ink);
	--h6-color: var(--esp-ink);
}

/* Tipografia de marca. Inter y Space Grotesk van AUTOALOJADAS (ver
   public/lib/fonts/): la CSP es 'self', asi que un CDN de fuentes no carga. */
body {
	font-family: var(--esp-font);
	background-color: var(--esp-bg);
	color: var(--esp-ink);
}

h1, h2, h3, .esp-display {
	font-family: var(--esp-font-display);
	letter-spacing: -0.02em;
}

/* ===== Tailwind override layer - neutralize Pico on Tailwind-styled elements ===== */

/* Reset Pico nav styling when using Tailwind */
nav.tw-nav,
nav.tw-nav ul,
nav.tw-nav li,
nav.tw-nav a,
nav.tw-nav button {
	all: unset;
	display: revert;
	box-sizing: border-box;
	cursor: pointer;
}

/* Reset Pico article styling for Tailwind cards */
article.tw-card {
	all: unset;
	display: block;
	box-sizing: border-box;
}

/* Reset Pico section/main/footer styling for Tailwind sections */
/* NOTE: Do NOT use 'all: unset' here — it strips position/overflow needed by Tailwind */
section.tw-section,
main.tw-section,
footer.tw-section,
div.tw-section {
	margin: 0;
	padding: 0;
	display: block;
	box-sizing: border-box;
}

/* Reset Pico container */
.tw-container {
	max-width: none;
	margin: 0;
	padding: 0;
}

/* Prevent Pico from styling buttons inside Tailwind components */
.tw-btn {
	all: unset;
	cursor: pointer;
	display: inline-flex;
	align-items: center;
	box-sizing: border-box;
}

/* Ensure no-underline works against Pico */
.no-underline,
.no-underline:hover,
.no-underline:focus,
.no-underline:visited {
	text-decoration: none !important;
}

/* Smooth scrolling */
html {
	scroll-behavior: smooth;
}

/* ===== Utility classes ===== */

.maxwidthdate {
	max-width: 110px;
}

.nowraponall {
	white-space: nowrap;
}

.width150 {
	max-width: 150px;
}

/* ===== Sidebar layout ===== */

/* Base sidebar structure — Pico reset + layout */
aside.tw-sidebar {
	all: unset;
	display: flex;
	flex-direction: column;
	box-sizing: border-box;
	/* ⚠️ ANCHEADO AL TEXTO, NO AL REVES. Medido con el texto al 200% (el ajuste
	   de tamano de fuente del navegador, que es una forma real de ampliar):
	   - con los 260px fijos de antes, la etiqueta "Presupuestos" mide 197px y con
	     el icono y los rellenos se va a 316px, o sea 56px FUERA de la barra. Lo
	     mismo "Descargas", "Proyectos" y "Desconexion", y el nombre del tercero
	     salia cortado en "TER...". Todo eso es contenido perdido (WCAG 1.4.4).
	   - clamp(260px, 14rem, 100%) = nunca menos de 260px, y crece con la raiz.
	     OJO: a 1280px de ancho Pico pone la raiz en 17px (pico.css.php), asi que
	     14rem son 238px, por debajo del suelo de 260: a tamano normal la barra
	     sigue midiendo EXACTAMENTE los 260px de siempre. Solo crece cuando el
	     usuario amplia el texto. Comprobado a 100% y a 200%.
	   - El tope de 100% es para movil, donde la barra es un cajon fijo: con el
	     texto grande no se sale de la pantalla. */
	width: clamp(260px, 14rem, 100%);
	min-width: clamp(260px, 14rem, 100%);
	height: 100vh;
	/* Cromo del panel: oscuro en los DOS temas. Ver --esp-nav-bg. */
	background-color: var(--esp-nav-bg);
	border-right: 1px solid var(--esp-nav-line);

	/* ⚠️ REMAPEADO DE TOKENS. El sidebar de este portal lleva casi todos sus
	   colores en style="" en linea (menu.tpl.php), y son ~30 sitios. En vez de
	   reescribirlos uno a uno, aqui se redefinen los tokens DENTRO del sidebar:
	   las propiedades personalizadas de CSS se heredan, asi que un
	   style="color: var(--esp-ink)" escrito en la plantilla resuelve contra
	   este valor y no contra el del :root.

	   Con esto, un solo bloque pasa el sidebar entero al cromo oscuro, y
	   cualquier estilo en linea que se anada despues usa los tokens de siempre
	   y sale bien por defecto. La alternativa (reescribir las 30 lineas) se
	   desincroniza en cuanto alguien copie un estilo de otra plantilla. */
	--esp-ink: var(--esp-nav-ink);
	--esp-ink-2: var(--esp-nav-ink-2);
	--esp-ink-3: var(--esp-nav-ink-3);
	--esp-line: var(--esp-nav-line);
	--esp-surface: var(--esp-nav-bg-2);
	--esp-surface-2: var(--esp-nav-bg-2);
	--esp-surface-3: var(--esp-nav-line);
	/* El rojo de "Desconexion" del :root es para fondo CLARO (#A01818): sobre el
	   cromo oscuro no se lee. Aqui va uno claro, que vale para los dos temas
	   porque el cromo es oscuro en los dos. */
	--esp-bad-fg: #F0918E;
	--esp-ok-fg: #7BD18D;
	/* El selector de tema usa la variante --onDark, que va con los tokens del
	   cromo del login: se remapean tambien para que tome el matiz del sidebar. */
	--esp-chrome-ink: var(--esp-nav-ink);
	--esp-chrome-ink-2: var(--esp-nav-ink-2);

	/* ⚠️ color-scheme: dark DENTRO DEL SIDEBAR, aunque el tema de la pagina sea
	   claro. El cromo del panel es oscuro en los dos temas, pero un <select>
	   nativo (el del idioma) se pinta con el esquema del DOCUMENTO: en tema claro
	   salia un desplegable claro dentro del sidebar oscuro, con la lista de
	   idiomas en blanco. No es cosmetico: afecta a la lista desplegable, que es
	   lo que el usuario ve al elegir. Declararlo aqui arregla el control nativo
	   sin tener que rehacerlo a mano. */
	color-scheme: dark;
	position: sticky;
	top: 0;
	overflow-y: auto;
	flex-shrink: 0;
	z-index: 40;
	transition: transform 0.25s ease-in-out;
	scrollbar-width: thin;
	scrollbar-color: rgba(255, 255, 255, 0.12) transparent;
}

/* Reset Pico de los hijos del sidebar.
   ⚠️ Este reset es (0,1,1) y le GANA a cualquier clase sola (0,1,0). Mientras el
   panel se pinto con style="" en linea no se notaba, porque un estilo en linea
   gana a todo; desde la 1.4.0 los estilos son clases, asi que TODO componente
   que viva dentro del sidebar tiene que llevar el prefijo `aside.tw-sidebar` en
   su selector o se queda con los valores del reset (sin fondo, sin padding, sin
   radio y sin display: flex).
   Paso de verdad: `.esp-brand-plate` (la placa clara del logo) perdio su fondo
   blanco al pasar de style= a clase y el logo, que es OSCURO, se quedo invisible
   sobre el cromo. Medido en produccion el 2026-09-24. */
aside.tw-sidebar nav,
aside.tw-sidebar ul,
aside.tw-sidebar li,
aside.tw-sidebar div,
aside.tw-sidebar span {
	all: unset;
	display: revert;
	box-sizing: border-box;
}

aside.tw-sidebar a,
aside.tw-sidebar button {
	all: unset;
	display: revert;
	box-sizing: border-box;
	cursor: pointer;
}

/* Hover de los enlaces del sidebar.
   ⚠️ El color de fondo lo lleva la plantilla en un style="" en linea, y un
   style en linea GANA a esta regla: por eso aqui solo se puede tocar con
   !important, o la plantilla tiene que dejar de pintarlo. Se usa !important a
   proposito y acotado al hover, que es lo unico que la plantilla no fija.
   (Antes era #f9fafb, un gris pensado para un sidebar BLANCO: sobre el cromo
   oscuro aquello ponia una barra clara al pasar el raton.) */
aside.tw-sidebar a:hover {
	background-color: rgba(255, 255, 255, 0.05) !important;
	color: #fff !important;
}

/* El sidebar tiene su propio scroll: la barra del navegador ahi sale blanca y
   canta mucho sobre el cromo oscuro. */
aside.tw-sidebar::-webkit-scrollbar {
	width: 8px;
}

aside.tw-sidebar::-webkit-scrollbar-track {
	background: transparent;
}

aside.tw-sidebar::-webkit-scrollbar-thumb {
	background: rgba(255, 255, 255, 0.12);
	border-radius: 999px;
}

aside.tw-sidebar:hover::-webkit-scrollbar-thumb {
	background: rgba(255, 255, 255, 0.28);
}

/* Sidebar overlay — hidden by default, shown via JS .active class */
#sidebar-overlay {
	display: none;
	position: fixed;
	top: 0;
	right: 0;
	bottom: 0;
	left: 0;
	background-color: rgba(0, 0, 0, 0.5);
	z-index: 49;
	backdrop-filter: blur(2px);
}

#sidebar-overlay.active {
	display: block;
}

/* Main content area — flex column, fills remaining width */
.tw-main-content {
	display: flex;
	flex-direction: column;
	flex: 1;
	min-width: 0;
	/* overflow-x:hidden convierte el elemento en scroll container (el otro eje
	   pasa a auto), y eso deja sin efecto el position:sticky del footer, que
	   pasa a pegarse a una caja que nunca scrollea. overflow-x:clip recorta
	   igual pero NO crea scroll container. El fallback va en @supports y no
	   como segunda declaracion: no son equivalentes (hidden+visible recomputa
	   el eje Y a auto), asi que solo se aplica si el navegador no tiene clip. */
	overflow-x: clip;
}

@supports not (overflow-x: clip) {
	.tw-main-content {
		overflow-x: hidden;
	}
}

/* ===== Mobile sidebar behavior (< 1024px) ===== */
@media (max-width: 1023px) {
	aside.tw-sidebar {
		position: fixed !important;
		top: 0;
		left: 0;
		height: 100vh !important;
		z-index: 50;
		/* -100% y no -260px: el ancho de la barra ya no es fijo (es min(16.25rem,
		   100%)), asi que un desplazamiento en px dejaria de esconderla entera en
		 cuanto cambiara. El porcentaje se calcula sobre el propio ancho. */
		transform: translateX(-100%);
	}

	aside.tw-sidebar.sidebar-open {
		transform: translateX(0);
	}

	/* lg:hidden utility: show on mobile */
	.lg\:hidden {
		display: flex !important;
	}
}

/* ===== Desktop sidebar behavior (>= 1024px) ===== */
@media (min-width: 1024px) {
	aside.tw-sidebar {
		position: sticky !important;
		transform: none !important;
	}

	/* lg:hidden utility: hide on desktop */
	.lg\:hidden {
		display: none !important;
	}
}

/* ===== List pages — layout & content area ===== */

/* Override Pico container inside main content — comfortable padding */
.tw-main-content > main,
.tw-main-content > main.container {
	max-width: none !important;
	padding: 0 2rem 2rem !important;
	margin: 0 !important;
	width: 100% !important;
	box-sizing: border-box !important;
	flex: 1;
}

@media (max-width: 640px) {
	.tw-main-content > main,
	.tw-main-content > main.container {
		padding: 0 1rem 1rem !important;
	}
}

/* List form — card wrapper with modern look */
.tw-main-content > main > form {
	background: #ffffff;
	border: 1px solid #e2e8f0;
	border-radius: 1rem;
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.02);
	overflow: hidden;
}

/* ===== Pagination nav — redesigned ===== */
.list-pagination-nav {
	padding: 1rem 1.5rem;
	border-bottom: 1px solid #f1f5f9;
	background: #fafbfc;
	display: flex;
	align-items: center;
	justify-content: space-between;
	flex-wrap: wrap;
	gap: 0.75rem;
}

.list-pagination-nav__info {
	display: flex;
	align-items: baseline;
	gap: 0.75rem;
}

.list-pagination-nav__title {
	font-size: 1.0625rem;
	font-weight: 700;
	color: #0f172a;
	margin: 0;
	padding: 0;
	line-height: 1.3;
}

.list-pagination-nav__count {
	font-size: 0.8125rem;
	color: #64748b;
	font-weight: 400;
}

.list-pagination-nav__controls {
	display: flex;
	align-items: center;
	gap: 1rem;
}

.list-pagination-nav__range {
	font-size: 0.8125rem;
	color: #64748b;
	white-space: nowrap;
}

.list-pagination-nav__buttons {
	display: flex;
	align-items: center;
	gap: 0.125rem;
}

.list-pagination-nav__btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2rem;
	height: 2rem;
	border-radius: 0.375rem;
	color: #475569;
	text-decoration: none !important;
	transition: background 0.15s, color 0.15s;
	cursor: pointer;
}

.list-pagination-nav__btn:hover {
	background: #e2e8f0;
	color: #0f172a;
}

.list-pagination-nav__btn.--disabled {
	color: #cbd5e1;
	cursor: default;
	pointer-events: none;
}

.list-pagination-nav__page {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 2rem;
	height: 2rem;
	padding: 0 0.375rem;
	border-radius: 0.375rem;
	font-size: 0.8125rem;
	font-weight: 500;
	color: #475569;
	text-decoration: none !important;
	transition: background 0.15s, color 0.15s;
}

.list-pagination-nav__page:hover {
	background: #e2e8f0;
	color: #0f172a;
}

.list-pagination-nav__page.--active {
	background: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 95%);
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 40%);
	font-weight: 600;
}

.list-pagination-nav__dots {
	color: #64748b;
	font-size: 0.75rem;
	padding: 0 0.25rem;
}

/* ===== Active filter chips ===== */
.list-filter-chips {
	padding: 0.625rem 1.5rem;
	border-bottom: 1px solid #f1f5f9;
	background: #ffffff;
	display: flex;
	align-items: center;
}

.list-filter-chips__inner {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 0.5rem;
}

.list-filter-chips__label {
	display: inline-flex;
	align-items: center;
	gap: 0.375rem;
	font-size: 0.75rem;
	font-weight: 600;
	color: #64748b;
	text-transform: uppercase;
	letter-spacing: 0.05em;
	flex-shrink: 0;
	margin-right: 0.25rem;
}

.list-filter-chip {
	display: inline-flex;
	align-items: center;
	gap: 0.25rem;
	padding: 0.25rem 0.5rem 0.25rem 0.625rem;
	background: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 96%);
	border: 1px solid hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 88%);
	border-radius: 9999px;
	font-size: 0.75rem;
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 35%);
	line-height: 1.4;
	transition: opacity 0.2s, transform 0.2s;
	max-width: 100%;
}

.list-filter-chip__label {
	font-weight: 600;
	white-space: nowrap;
}

.list-filter-chip__value {
	font-weight: 400;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	max-width: 12rem;
}

.list-filter-chip__remove {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 1.125rem;
	height: 1.125rem;
	border: none !important;
	background: none !important;
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 55%) !important;
	font-size: 0.875rem;
	line-height: 1;
	cursor: pointer;
	border-radius: 50%;
	padding: 0;
	flex-shrink: 0;
	transition: color 0.15s, background 0.15s;
}

.list-filter-chip__remove:hover {
	color: #dc2626 !important;
	background: rgba(220, 38, 38, 0.1) !important;
}

.list-filter-chips__clear-all {
	display: inline-flex;
	align-items: center;
	padding: 0.25rem 0.75rem;
	border: 1px solid #e2e8f0 !important;
	border-radius: 9999px;
	background: #ffffff !important;
	color: #64748b !important;
	font-size: 0.6875rem;
	font-weight: 500;
	cursor: pointer;
	transition: all 0.15s;
	white-space: nowrap;
	margin-left: 0.25rem;
}

.list-filter-chips__clear-all:hover {
	background: #fef2f2 !important;
	border-color: #fecaca !important;
	color: #dc2626 !important;
}

@media (max-width: 640px) {
	.list-filter-chips {
		padding: 0.5rem 1rem;
	}

	.list-filter-chip__value {
		max-width: 8rem;
	}
}

/* ===== Table figure wrapper (injected by theme.js) ===== */
.tw-main-content figure {
	overflow-x: auto;
	margin: 0;
	border: 1px solid #e2e8f0;
	border-radius: 0.75rem;
	background: #ffffff;
}

/* ===== Table — modern styling ===== */
.tw-main-content table {
	table-layout: auto;
	width: 100%;
	margin: 0 !important;
	border-collapse: separate !important;
	border-spacing: 0 !important;
	border: none !important;
}

/* First title row cell — rounded top-left corner */
.tw-main-content table[role="grid"] thead .list-titles-row th:first-child {
	border-top-left-radius: 0.75rem;
}
.tw-main-content table[role="grid"] thead .list-titles-row th:last-child {
	border-top-right-radius: 0.75rem;
}

/* Column headers — title row */
.tw-main-content table[role="grid"] thead .list-titles-row th {
	background: #ffffff !important;
	font-size: 0.75rem !important;
	font-weight: 700 !important;
	text-transform: uppercase !important;
	letter-spacing: 0.05em !important;
	color: #475569 !important;
	padding: 0.875rem 1rem 0.625rem !important;
	border-bottom: 1px solid #e2e8f0 !important;
	border-top: none !important;
	border-left: none !important;
	border-right: none !important;
	white-space: nowrap;
	position: sticky;
	top: 0;
	z-index: 10;
}

/* Header sort links — inline with icon */
.list-th-link {
	display: inline-flex;
	align-items: center;
	gap: 0.375rem;
	color: #475569 !important;
	text-decoration: none !important;
	font-size: inherit !important;
	font-weight: inherit !important;
	text-transform: inherit !important;
	letter-spacing: inherit !important;
	transition: color 0.15s;
}

a.list-th-link:hover {
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 40%) !important;
}

a.list-th-link:hover .list-sort-icon {
	opacity: 1;
}

.list-sort-icon {
	flex-shrink: 0;
	opacity: 0.6;
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 50%);
	transition: opacity 0.15s;
}

/* Active sort indicator — pill badge */
th[table-order] .list-th-link {
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 35%) !important;
}

th[table-order] .list-sort-icon {
	opacity: 1;
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 45%);
}

/* ===== Search filter row ===== */
.tw-main-content table[role="grid"] tr[role="search-row"] td {
	background: #f1f5f9 !important;
	padding: 0.5rem 0.75rem !important;
	border-bottom: 2px solid #e2e8f0 !important;
	border-top: none !important;
	border-left: none !important;
	border-right: none !important;
	vertical-align: middle;
}

/* Filter action buttons — container */
.list-filter-actions {
	white-space: nowrap;
}

.list-filter-actions__inner {
	display: flex;
	align-items: center;
	gap: 0.25rem;
}

.list-filter-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 2rem;
	height: 2rem;
	padding: 0;
	border-radius: 0.5rem;
	border: 1px solid transparent !important;
	background: transparent !important;
	color: #64748b !important;
	cursor: pointer;
	transition: all 0.15s;
	vertical-align: middle;
	flex-shrink: 0;
}

.list-filter-btn:hover {
	background: #e2e8f0 !important;
	color: #334155 !important;
}

.list-filter-btn--search {
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 45%) !important;
}

.list-filter-btn--search:hover {
	background: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 93%) !important;
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 35%) !important;
}

.list-filter-btn--clear:hover {
	background: #fef2f2 !important;
	color: #dc2626 !important;
}

/* Search inputs & selects inside filter row */
.tw-main-content table[role="grid"] tr[role="search-row"] input[type="text"],
.tw-main-content table[role="grid"] tr[role="search-row"] input[type="number"],
.tw-main-content table[role="grid"] tr[role="search-row"] input[type="date"],
.tw-main-content table[role="grid"] tr[role="search-row"] select {
	font-size: 0.8rem !important;
	padding: 0.375rem 0.5rem !important;
	border: 1px solid #cbd5e1 !important;
	border-radius: 0.375rem !important;
	background: #ffffff !important;
	color: #334155 !important;
	height: 2rem !important;
	line-height: 1.4 !important;
	box-shadow: none !important;
	transition: border-color 0.15s, box-shadow 0.15s !important;
	outline: none !important;
	width: 100%;
	max-width: 100%;
	box-sizing: border-box;
}

.tw-main-content table[role="grid"] tr[role="search-row"] input:focus,
.tw-main-content table[role="grid"] tr[role="search-row"] select:focus {
	border-color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 55%) !important;
	box-shadow: 0 0 0 2px hsla(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 55%, 0.12) !important;
}

.tw-main-content table[role="grid"] tr[role="search-row"] input::placeholder {
	color: #64748b;
	font-style: normal;
	font-size: 0.75rem;
}

/* Status select — compact */
.tw-main-content table[role="grid"] tr[role="search-row"] select {
	padding-right: 1.5rem !important;
	-webkit-appearance: none;
	appearance: none;
	background-image: url("data:image/svg+xml,%3Csvg width='10' height='6' viewBox='0 0 10 6' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 1L5 5L9 1' stroke='%2394a3b8' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: right 0.5rem center;
	background-size: 10px 6px;
}

/* ===== Data rows ===== */
.tw-main-content table[role="grid"] tbody td {
	padding: 0.875rem 1rem !important;
	font-size: 0.8125rem !important;
	color: #334155 !important;
	border-bottom: 1px solid #f1f5f9 !important;
	border-top: none !important;
	border-left: none !important;
	border-right: none !important;
	vertical-align: middle;
	transition: background 0.1s;
}

/* Row hover effect */
.tw-main-content table[role="grid"] tbody tr.list-data-row:hover td {
	background: #f8fafc !important;
}

/* Expandable row — cursor + chevron */
.list-data-row--expandable {
	cursor: pointer;
}

.list-data-row--expandable:hover td {
	background: #f0f4ff !important;
}

.list-row-toggle-cell {
	width: 2.5rem;
	text-align: center;
}

.list-row-chevron {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 1.5rem;
	height: 1.5rem;
	border-radius: 0.375rem;
	color: #64748b;
	transition: all 0.2s ease;
}

.list-data-row--expandable:hover .list-row-chevron {
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 50%);
	background: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 95%);
}

.list-data-row--expanded .list-row-chevron {
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 45%);
	background: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 93%);
	transform: rotate(90deg);
}

/* Expanded data row — highlight left border */
.list-data-row--expanded td {
	background: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 98%) !important;
	border-bottom-color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 90%) !important;
}

.list-data-row--expanded td:first-child {
	box-shadow: inset 3px 0 0 hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 50%);
}

/* Ref column — make it stand out as a link */
.tw-main-content table[role="grid"] tbody td[data-col="ref"] {
	font-weight: 600 !important;
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 42%) !important;
}

/* Price / number columns — right align */
.tw-main-content table[role="grid"] td.right,
.tw-main-content table[role="grid"] th.right {
	text-align: right !important;
}

/* "No records found" row */
.list-empty-cell {
	text-align: center !important;
	padding: 4rem 2rem !important;
	background: #fafbfc !important;
}

.list-empty-state {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
}

.list-empty-text {
	color: #64748b !important;
	font-size: 0.9375rem !important;
	font-style: normal;
	margin: 0;
}

/* ===== Detail / expandable row panel ===== */
.list-detail-row td {
	padding: 0 !important;
	border-bottom: 1px solid #e2e8f0 !important;
	border-top: none !important;
	border-left: none !important;
	border-right: none !important;
	background: #f8fafc !important;
}

.list-detail-cell {
	padding: 0 !important;
}

.list-detail-panel {
	overflow: hidden;
	transition: max-height 0.25s ease, opacity 0.2s ease;
	padding: 1rem 1.5rem 1.25rem;
	margin: 0 0.5rem;
}

.list-detail-header {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	margin-bottom: 0.75rem;
}

.list-detail-title {
	font-size: 0.8125rem;
	font-weight: 600;
	color: #334155;
}

.list-detail-count {
	font-size: 0.75rem;
	color: #64748b;
	background: #f1f5f9;
	padding: 0.125rem 0.5rem;
	border-radius: 9999px;
}

.list-detail-table-wrap {
	overflow-x: auto;
	border: 1px solid #e2e8f0;
	border-radius: 0.5rem;
	background: #ffffff;
}

.list-detail-table {
	width: 100%;
	border-collapse: collapse !important;
	border-spacing: 0 !important;
	border: none !important;
	margin: 0 !important;
}

.list-detail-th {
	font-size: 0.6875rem !important;
	font-weight: 600 !important;
	text-transform: uppercase !important;
	letter-spacing: 0.05em !important;
	color: #64748b !important;
	padding: 0.5rem 0.75rem !important;
	background: #fafbfc !important;
	border-bottom: 1px solid #e2e8f0 !important;
	border-top: none !important;
	border-left: none !important;
	border-right: none !important;
	white-space: nowrap;
}

.list-detail-td {
	font-size: 0.8125rem !important;
	color: #475569 !important;
	padding: 0.625rem 0.75rem !important;
	border-bottom: 1px solid #f1f5f9 !important;
	border-top: none !important;
	border-left: none !important;
	border-right: none !important;
	vertical-align: top;
}

.list-detail-data-row:last-child .list-detail-td {
	border-bottom: none !important;
}

.list-detail-product-ref {
	font-weight: 600;
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 42%);
	font-size: 0.8125rem;
}

.list-detail-product-label {
	color: #334155;
}

.list-detail-line-desc {
	color: #64748b;
	font-size: 0.75rem;
	line-height: 1.4;
}

/* Total row */
.list-total-row td {
	border-bottom: none !important;
	border-top: 2px solid #e2e8f0 !important;
	background: #fafbfc !important;
	padding: 0.75rem 1rem !important;
}

.list-total-label {
	font-weight: 700 !important;
	color: #0f172a !important;
	font-size: 0.8125rem !important;
}

.list-total-cell {
	font-weight: 700 !important;
	color: #0f172a !important;
	font-size: 0.8125rem !important;
	text-align: right !important;
}

/* ===== Status badges — aggressive normalization ===== */
.tw-main-content table[role="grid"] td[data-col="fk_statut"] {
	white-space: nowrap;
}

/* Target all spans inside status cells — covers every Dolibarr pattern */
.tw-main-content table[role="grid"] td[data-col="fk_statut"] > span,
.tw-main-content table[role="grid"] td[data-col="fk_statut"] .badge,
.tw-main-content table[role="grid"] td[data-col="fk_statut"] .badge-status {
	display: inline-flex !important;
	align-items: center !important;
	padding: 0.1875rem 0.625rem !important;
	border-radius: 9999px !important;
	font-size: 0.6875rem !important;
	font-weight: 600 !important;
	letter-spacing: 0.02em !important;
	line-height: 1.4 !important;
	border: none !important;
	text-decoration: none !important;
	gap: 0.3125rem !important;
}

/* Hide the dot element if badge-dot is used (we style the whole span instead) */
.tw-main-content table[role="grid"] td[data-col="fk_statut"] .badge-dot {
	display: none !important;
}

/* Status 0 — Draft (Gray) */
.tw-main-content table[role="grid"] td[data-col="fk_statut"] .badge-status0 {
	background-color: #f1f5f9 !important;
	color: #64748b !important;
}

/* Status 1 — Validated/Active (Primary/Blue) */
.tw-main-content table[role="grid"] td[data-col="fk_statut"] .badge-status1 {
	background-color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 95%) !important;
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 38%) !important;
}

/* Status 2 — Signed/Approved (Green) */
.tw-main-content table[role="grid"] td[data-col="fk_statut"] .badge-status2 {
	background-color: #dcfce7 !important;
	color: #166534 !important;
}

/* Status 3 — Billed/Closed (Teal) */
.tw-main-content table[role="grid"] td[data-col="fk_statut"] .badge-status3 {
	background-color: #e0f2fe !important;
	color: #0c4a6e !important;
}

/* Status 4 — Cancelled/Warning (Amber) */
.tw-main-content table[role="grid"] td[data-col="fk_statut"] .badge-status4 {
	background-color: #fef3c7 !important;
	color: #92400e !important;
}

/* Status 5 — Refused/Expired (Red) */
.tw-main-content table[role="grid"] td[data-col="fk_statut"] .badge-status5 {
	background-color: #fef2f2 !important;
	color: #991b1b !important;
}

/* Status 6 — Special (Purple) */
.tw-main-content table[role="grid"] td[data-col="fk_statut"] .badge-status6 {
	background-color: #f3e8ff !important;
	color: #6b21a8 !important;
}

/* Status 7+ — fallback (Slate) */
.tw-main-content table[role="grid"] td[data-col="fk_statut"] .badge-status7,
.tw-main-content table[role="grid"] td[data-col="fk_statut"] .badge-status8,
.tw-main-content table[role="grid"] td[data-col="fk_statut"] .badge-status9 {
	background-color: #f1f5f9 !important;
	color: #334155 !important;
}

/* Fallback for spans without specific badge-status class */
.tw-main-content table[role="grid"] td[data-col="fk_statut"] > span:not([class*="badge-status"]) {
	background-color: #f1f5f9 !important;
	color: #475569 !important;
}

/* Also normalize status inside detail/expandable rows */
.list-detail-td .badge,
.list-detail-td .badge-status,
.list-detail-td > span[class*="badge"] {
	display: inline-flex !important;
	align-items: center !important;
	padding: 0.125rem 0.5rem !important;
	border-radius: 9999px !important;
	font-size: 0.625rem !important;
	font-weight: 600 !important;
	letter-spacing: 0.02em !important;
	line-height: 1.4 !important;
	border: none !important;
}

/* Links inside table cells */
.tw-main-content table[role="grid"] tbody td a {
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 42%);
	text-decoration: none;
	font-weight: 500;
	transition: color 0.15s;
}

.tw-main-content table[role="grid"] tbody td a:hover {
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 32%);
	text-decoration: underline;
}

/* ===== Flatpickr custom theme ===== */

/* Flatpickr date filter container */
.flatpickr-date-filter {
	display: flex;
	flex-direction: column;
	gap: 0.375rem;
	position: relative;
}

/* Individual input wrapper — ensures each clear button is positioned relative to its own input */
.flatpickr-input-wrap {
	position: relative;
	display: flex;
	align-items: center;
}

/* Calendar icon inside input wrapper */
.flatpickr-calendar-icon {
	position: absolute;
	left: 0.5rem;
	top: 50%;
	transform: translateY(-50%);
	color: #64748b;
	pointer-events: none;
	z-index: 1;
	display: flex;
	align-items: center;
	line-height: 1;
}

.flatpickr-input-wrap:hover .flatpickr-calendar-icon,
.flatpickr-input-wrap:focus-within .flatpickr-calendar-icon {
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 50%);
}

.flatpickr-date-input {
	font-size: 0.8rem !important;
	padding: 0.375rem 1.625rem 0.375rem 1.75rem !important;
	border: 1px solid #cbd5e1 !important;
	border-radius: 0.375rem !important;
	background: #ffffff !important;
	color: #334155 !important;
	height: 2rem !important;
	line-height: 1.4 !important;
	box-shadow: none !important;
	transition: border-color 0.15s, box-shadow 0.15s !important;
	outline: none !important;
	width: 100%;
	box-sizing: border-box;
	cursor: pointer !important;
	pointer-events: auto !important;
	-webkit-user-select: none;
	user-select: none;
}

.flatpickr-date-input:focus,
.flatpickr-date-input.active {
	border-color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 55%) !important;
	box-shadow: 0 0 0 3px hsla(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 55%, 0.15) !important;
}

.flatpickr-date-input::placeholder {
	color: #64748b;
	font-style: normal;
	font-size: 0.75rem;
}

.flatpickr-clear-btn {
	position: absolute;
	right: 0.375rem;
	top: 50%;
	transform: translateY(-50%);
	width: 1.25rem;
	height: 1.25rem;
	border: none !important;
	background: none !important;
	color: #64748b !important;
	font-size: 1rem;
	line-height: 1;
	cursor: pointer;
	display: none;
	align-items: center;
	justify-content: center;
	border-radius: 50%;
	padding: 0;
	z-index: 2;
	transition: color 0.15s, background 0.15s;
}

/* Show clear button on hover or keyboard focus within the wrapper */
.flatpickr-input-wrap:hover .flatpickr-clear-btn,
.flatpickr-input-wrap:focus-within .flatpickr-clear-btn {
	display: inline-flex;
}

.flatpickr-clear-btn:hover {
	color: #dc2626 !important;
	background: #fef2f2 !important;
}

/* Flatpickr calendar dropdown — themed */
.flatpickr-calendar {
	border-radius: 0.75rem !important;
	box-shadow: 0 8px 30px rgba(0, 0, 0, 0.12), 0 2px 8px rgba(0, 0, 0, 0.06) !important;
	border: 1px solid #e2e8f0 !important;
	font-family: inherit !important;
	z-index: 99999 !important;
	pointer-events: auto !important;
}

/* Pico CSS aggressively overrides input states — force flatpickr inputs to remain interactive */
.flatpickr-date-input,
.flatpickr-date-input[readonly],
.flatpickr-date-input:read-only,
input.flatpickr-date-input {
	pointer-events: auto !important;
	cursor: pointer !important;
	opacity: 1 !important;
	-webkit-appearance: none !important;
	appearance: none !important;
}

.flatpickr-months {
	border-radius: 0.75rem 0.75rem 0 0;
	background: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), var(--tw-primary-l, 50%)) !important;
}

.flatpickr-months .flatpickr-month {
	color: #ffffff !important;
	fill: #ffffff !important;
	background: transparent !important;
}

.flatpickr-months .flatpickr-prev-month,
.flatpickr-months .flatpickr-next-month {
	color: #ffffff !important;
	fill: #ffffff !important;
}

.flatpickr-months .flatpickr-prev-month:hover,
.flatpickr-months .flatpickr-next-month:hover {
	color: #ffffff !important;
}

.flatpickr-months .flatpickr-prev-month svg,
.flatpickr-months .flatpickr-next-month svg {
	fill: #ffffff !important;
}

.flatpickr-current-month .cur-month,
.flatpickr-current-month .numInputWrapper {
	color: #ffffff !important;
}

.flatpickr-current-month input.cur-year {
	color: #ffffff !important;
}

span.flatpickr-weekday {
	color: #64748b !important;
	font-weight: 600 !important;
	font-size: 0.6875rem !important;
}

.flatpickr-day {
	border-radius: 0.375rem !important;
	font-size: 0.8125rem !important;
	color: #334155 !important;
}

.flatpickr-day:hover {
	background: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 95%) !important;
	border-color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 90%) !important;
}

.flatpickr-day.selected,
.flatpickr-day.selected:hover {
	background: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), var(--tw-primary-l, 50%)) !important;
	border-color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), var(--tw-primary-l, 50%)) !important;
	color: #ffffff !important;
}

.flatpickr-day.today {
	border-color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 70%) !important;
}

.flatpickr-day.today:hover {
	background: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 90%) !important;
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 30%) !important;
}

/* ===== Responsive: stack cells on small screens ===== */
@media (max-width: 768px) {
	.list-pagination-nav {
		flex-direction: column;
		align-items: flex-start;
		padding: 0.875rem 1rem;
	}

	.list-pagination-nav__controls {
		width: 100%;
		justify-content: space-between;
	}

	.tw-main-content table[role="grid"] thead {
		display: none;
	}

	.tw-main-content table[role="grid"] tbody tr.list-data-row {
		display: block;
		padding: 1rem;
		border-bottom: 1px solid #e2e8f0 !important;
		margin-bottom: 0;
	}

	.tw-main-content table[role="grid"] tbody tr.list-data-row:hover {
		background: #f8fafc;
	}

	.tw-main-content table[role="grid"] tbody tr.list-data-row td {
		display: flex !important;
		justify-content: space-between;
		align-items: center;
		padding: 0.5rem 0 !important;
		border-bottom: 1px solid #f8fafc !important;
		font-size: 0.8125rem !important;
	}

	.tw-main-content table[role="grid"] tbody td::before {
		content: attr(data-label);
		font-size: 0.6875rem;
		font-weight: 600;
		text-transform: uppercase;
		letter-spacing: 0.05em;
		color: #64748b;
		flex-shrink: 0;
		margin-right: 1rem;
	}

	.tw-main-content table[role="grid"] tbody td[data-col="row-checkbox"] {
		display: none !important;
	}

	/* Detail row on mobile */
	.tw-main-content table[role="grid"] tbody tr.list-detail-row {
		display: block !important;
	}

	.tw-main-content table[role="grid"] tbody tr.list-detail-row td {
		display: block !important;
		padding: 0 !important;
	}

	.list-detail-panel {
		padding: 0.75rem 0.75rem 1rem;
		margin: 0;
	}

	.list-detail-table-wrap {
		margin: 0 -0.25rem;
	}
}

/* ===== Dashboard stat cards ===== */
.dashboard-stat-card {
	transition: box-shadow 0.2s, transform 0.2s, border-color 0.2s;
}

.dashboard-stat-card:hover {
	box-shadow: 0 4px 16px rgba(0, 0, 0, 0.09);
	transform: translateY(-2px);
}

.dashboard-stat-card--primary:hover {
	border-color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 75%) !important;
}

.dashboard-stat-card--green:hover {
	border-color: #6ee7b7 !important;
}

.dashboard-stat-card--amber:hover {
	border-color: #fcd34d !important;
}

/* ===== Dashboard big cards hover effects ===== */
.dashboard-card {
	transition: box-shadow 0.25s, transform 0.25s, border-color 0.25s;
}

.dashboard-card:hover {
	box-shadow: 0 8px 28px rgba(0, 0, 0, 0.10);
	transform: translateY(-3px);
}

.dashboard-card--primary:hover {
	border-color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 75%) !important;
}

.dashboard-card--green:hover {
	border-color: #86efac !important;
}

.dashboard-card--amber:hover {
	border-color: #fde68a !important;
}

/* Welcome banner responsive — hide avatar on very small screens */
@media (max-width: 480px) {
	.dashboard-welcome-avatar {
		display: none !important;
	}
}

/* ===== Toast notification system ===== */
#toast-container {
	position: fixed;
	top: 1.25rem;
	right: 1.25rem;
	z-index: 10000;
	display: flex;
	flex-direction: column;
	gap: 0.625rem;
	max-width: 26rem;
	width: calc(100% - 2.5rem);
	pointer-events: none;
}

.toast {
	display: flex;
	align-items: flex-start;
	gap: 0.75rem;
	padding: 0.875rem 1rem;
	border-radius: 0.75rem;
	background: #ffffff;
	border: 1px solid #e2e8f0;
	box-shadow: 0 8px 30px rgba(0, 0, 0, 0.10), 0 2px 8px rgba(0, 0, 0, 0.06);
	position: relative;
	overflow: hidden;
	pointer-events: all;
	transform: translateX(110%);
	opacity: 0;
	transition: transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.3s ease;
}

.toast--visible {
	transform: translateX(0);
	opacity: 1;
}

.toast--dismissing {
	transform: translateX(110%);
	opacity: 0;
	transition: transform 0.25s ease-in, opacity 0.25s ease-in;
}

.toast__icon {
	flex-shrink: 0;
	margin-top: 0.0625rem;
}

.toast__message {
	flex: 1;
	font-size: 0.8125rem;
	line-height: 1.5;
	color: #334155;
	word-break: break-word;
	white-space: pre-line;
}

.toast__close {
	flex-shrink: 0;
	width: 1.5rem;
	height: 1.5rem;
	display: flex;
	align-items: center;
	justify-content: center;
	border: none !important;
	background: none !important;
	color: #64748b !important;
	font-size: 1.125rem;
	line-height: 1;
	cursor: pointer;
	border-radius: 0.25rem;
	padding: 0;
	transition: color 0.15s, background 0.15s;
}

.toast__close:hover {
	color: #475569 !important;
	background: #f1f5f9 !important;
}

.toast__progress {
	position: absolute;
	bottom: 0;
	left: 0;
	height: 3px;
	width: 100%;
	transform-origin: left;
	animation: toastProgress linear forwards;
}

@keyframes toastProgress {
	from { transform: scaleX(1); }
	to   { transform: scaleX(0); }
}

/* Toast type colors */
.toast--success { border-left: 4px solid #22c55e; }
.toast--success .toast__icon { color: #22c55e; }
.toast--success .toast__progress { background: #22c55e; }

.toast--warning { border-left: 4px solid #f59e0b; }
.toast--warning .toast__icon { color: #f59e0b; }
.toast--warning .toast__progress { background: #f59e0b; }

.toast--error { border-left: 4px solid #ef4444; }
.toast--error .toast__icon { color: #ef4444; }
.toast--error .toast__progress { background: #ef4444; }

.toast--info { border-left: 4px solid hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 50%); }
.toast--info .toast__icon { color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 50%); }
.toast--info .toast__progress { background: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 50%); }

@media (max-width: 640px) {
	#toast-container {
		top: 0.75rem;
		right: 0.75rem;
		max-width: none;
		width: calc(100% - 1.5rem);
	}
}

/* ===== Loading feedback — progress bar + table shimmer ===== */
#page-progress-bar {
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	height: 3px;
	z-index: 99999;
	pointer-events: none;
	opacity: 0;
	transition: opacity 0.2s;
}

#page-progress-bar.active {
	opacity: 1;
}

#page-progress-bar.active::before {
	content: '';
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 100%;
	background: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), var(--tw-primary-l, 50%));
	animation: progressBarIndeterminate 1.5s ease-in-out infinite;
	transform-origin: left;
}

@keyframes progressBarIndeterminate {
	0%   { transform: translateX(-100%) scaleX(0.3); }
	40%  { transform: translateX(25%) scaleX(0.5); }
	100% { transform: translateX(100%) scaleX(0.3); }
}

/* Table loading shimmer overlay */
.table-loading {
	position: relative;
	pointer-events: none;
}

.table-loading::after {
	content: '';
	position: absolute;
	top: 0;
	left: 0;
	right: 0;
	bottom: 0;
	background: rgba(255, 255, 255, 0.6);
	z-index: 5;
	animation: shimmerPulse 1.2s ease-in-out infinite;
}

@keyframes shimmerPulse {
	0%, 100% { opacity: 0.4; }
	50%      { opacity: 0.8; }
}

/* ===== Micro-interactions ===== */

/* Sidebar nav link hover background (smooth) */
aside.tw-sidebar a:hover {
	background-color: #f3f4f6;
	transition: background-color 0.15s ease;
}

/* Logout link hover */
aside.tw-sidebar a[href*="logout"]:hover {
	background-color: #fef2f2 !important;
}

/* Pagination active page — subtle press effect */
.list-pagination-nav__page:active,
.list-pagination-nav__btn:active:not(.--disabled) {
	transform: scale(0.93);
	transition: transform 0.08s ease;
}

/* Sidebar link active tap feedback */
aside.tw-sidebar a:active {
	transform: scale(0.98);
	transition: transform 0.08s ease;
}

/* Error modal fade-in (CSS only, for when modal is opened) */
dialog[open],
[role="dialog"][open] {
	animation: modalFadeIn 0.2s ease;
}

@keyframes modalFadeIn {
	from { opacity: 0; transform: translateY(-8px); }
	to   { opacity: 1; transform: translateY(0); }
}

/* Filter chip hover — slight lift */
.list-filter-chip:hover {
	box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
	transform: translateY(-1px);
	transition: box-shadow 0.15s, transform 0.15s;
}

/* Dashboard card focus visible outline */
.dashboard-stat-card:focus-visible,
.dashboard-card:focus-visible {
	outline: 2px solid hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 50%);
	outline-offset: 2px;
}

/* ===== Mobile filter toggle ===== */
.mobile-filter-toggle {
	display: none; /* Hidden on desktop */
}

@media (max-width: 768px) {
	.mobile-filter-toggle {
		display: inline-flex !important;
		align-items: center;
		gap: 0.5rem;
		padding: 0.5rem 1rem;
		margin: 0.75rem 1rem;
		border: 1px solid #e2e8f0 !important;
		border-radius: 0.5rem;
		background: #ffffff !important;
		color: #475569 !important;
		font-size: 0.8125rem;
		font-weight: 500;
		cursor: pointer;
		transition: all 0.15s;
	}

	.mobile-filter-toggle:hover,
	.mobile-filter-toggle.active {
		background: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 95%) !important;
		border-color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 75%) !important;
		color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 35%) !important;
	}

	/* When show-mobile-filters is active, reveal ONLY the search row */
	.tw-main-content table[role="grid"].show-mobile-filters thead {
		display: table-header-group !important;
	}

	.tw-main-content table[role="grid"].show-mobile-filters thead tr:not([role="search-row"]) {
		display: none !important;
	}

	/* Style the search row as a vertical stack on mobile */
	.tw-main-content table[role="grid"].show-mobile-filters tr[role="search-row"] {
		display: block !important;
		padding: 0.75rem 1rem;
		background: #f8fafc;
		border-bottom: 1px solid #e2e8f0;
	}

	.tw-main-content table[role="grid"].show-mobile-filters tr[role="search-row"] td {
		display: block !important;
		padding: 0.375rem 0 !important;
		background: transparent !important;
		border: none !important;
	}

	.tw-main-content table[role="grid"].show-mobile-filters tr[role="search-row"] td::before {
		content: attr(data-label);
		display: block;
		font-size: 0.6875rem;
		font-weight: 600;
		text-transform: uppercase;
		letter-spacing: 0.05em;
		color: #64748b;
		margin-bottom: 0.25rem;
	}

	.tw-main-content table[role="grid"].show-mobile-filters tr[role="search-row"] td.list-filter-actions {
		display: flex !important;
		gap: 0.5rem;
		padding-top: 0.625rem !important;
		justify-content: flex-end;
	}

	.tw-main-content table[role="grid"].show-mobile-filters tr[role="search-row"] td.list-filter-actions::before {
		display: none;
	}
}

/* ===================================================================
   FOOTER & BREADCRUMB LINK HOVER (CSP-safe, no inline handlers)
   =================================================================== */
.footer-link-dolibarr:hover,
.footer-link-dolibarr:focus-visible {
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 40%) !important;
}

.footer-link-top:hover,
.footer-link-top:focus-visible {
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 40%) !important;
}

.breadcrumb-home-link:hover,
.breadcrumb-home-link:focus-visible {
	color: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 40%) !important;
}

/* ===================================================================
   UTILIDADES QUE VENIAN DEL CDN DE TAILWIND
   -------------------------------------------------------------------
   El portal cargaba https://cdn.tailwindcss.com (el build JIT, que
   compila en el navegador) con un tailwind.config inline. Medido en el
   navegador: bloqueando ese CDN, las 174 cajas de la pagina salen con
   la MISMA geometria, 0 diferencias. No aportaba nada, y era
   JavaScript de un tercero sin SRI en el formulario de login de todos
   los clientes: quien controle ese CDN se queda con las contrasenas.
   Se quito de header.tpl.php y header_login.tpl.php; estas son las
   unicas utilidades que si se usaban. Valores = los de Tailwind por
   defecto, para que el aspecto no cambie ni un pixel.
   =================================================================== */
.min-h-screen { min-height: 100vh; }
.bg-slate-50 { background-color: #f8fafc; }
.bg-gray-50 { background-color: #f9fafb; }
.bg-white { background-color: #fff; }
.relative { position: relative; }
.z-10 { z-index: 10; }
.hidden { display: none; }
/* Estas dos las genera el core (webportalmember.class.php:322,483,
   html.formcardwebportal.class.php:453...) y en este vhost las definia
   Tailwind: las hojas de eldy dan 404 porque el root es la subcarpeta del
   modulo. Medido en la pagina de facturas: 4 divs con .inline-block que
   ahora salian como block. */
.inline-block { display: inline-block; }
.block { display: block; }
.no-underline { text-decoration-line: none; }
.rounded-2xl { border-radius: 1rem; }
.shadow-glass { box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.15); }
/* Con !important a proposito: los elementos que la llevan traen
   style="display: flex" en linea, que sin esto gana a la clase y la
   barra superior movil del login se colaria en escritorio. */
@media (min-width: 1024px) {
	.lg\:hidden { display: none !important; }
	/* Despues de .hidden a proposito: misma especificidad, decide el orden.
	   Es el panel decorativo del login (login.tpl.php:21, class="hidden
	   lg:flex"): oculto en movil, visible en escritorio. */
	.lg\:flex { display: flex; }
}

/* ===================================================================
   ACCESIBILIDAD — correcciones WCAG 2.2 AA
   =================================================================== */

/* --- Marcadores de lista fantasma (Pico) ------------------------------
   pico.css.php:933 pone `list-style: square` DIRECTAMENTE en el `li`. Una
   declaracion propia gana siempre a la herencia, asi que un `ul` con
   `list-style: none` seguia pintando el cuadradito: en el hero de descargas
   salia un punto suelto delante de cada chip. Hay que anularlo en el `li`.
   Ninguna lista del portal quiere marcador: las dos que hay (breadcrumb y
   chips) lo desactivan a proposito. */
li { list-style: none; }
li::marker { content: none; }

/* --- 2.4.7 Focus Visible / 1.4.11 -------------------------------------
   Pico pone `a { outline: none }` (pico.css.php:807) y varios componentes
   usan `all: unset`, que tambien borra el outline. Resultado: en casi todo
   el portal el foco de teclado era invisible. Esta regla va al final y con
   !important a proposito: es el unico caso en que hace falta, porque tiene
   que ganar a `all: unset` (que resetea outline a none) sin depender del
   orden de carga. #1d4ed8 sobre blanco = 6.70:1. */
a:focus-visible,
button:focus-visible,
[tabindex]:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
summary:focus-visible,
aside.tw-sidebar a:focus-visible,
aside.tw-sidebar button:focus-visible,
.tw-btn:focus-visible {
	/* El color sale del token: grafito sobre fondo claro y amarillo sobre fondo
	   oscuro. Antes era un azul fijo (#1d4ed8) que no era de la marca y que en
	   el tema oscuro quedaba casi invisible. */
	outline: 3px solid var(--esp-focus) !important;
	outline-offset: 2px !important;
}

/* El foco lo recibe el <a> que envuelve la tarjeta, no el <article>. */
.no-underline:focus-visible .dashboard-stat-card,
.no-underline:focus-visible .dashboard-card {
	outline: 3px solid #1d4ed8 !important;
	outline-offset: 2px !important;
}

@media (forced-colors: active) {
	:focus-visible {
		outline: 3px solid Highlight !important;
	}
}

/* --- 2.4.11 Focus Not Obscured ---------------------------------------
   El footer es sticky (abajo) y la barra movil tambien (arriba): al tabular,
   el navegador deja el elemento enfocado justo debajo del footer. */
html {
	scroll-padding-top: 4.5rem;
	scroll-padding-bottom: 5rem;
}

/* --- El footer sticky flota sobre el contenido ------------------------
   Al scrollear tapa ~56px de la parte baja (la ultima fila de la tabla, el
   ultimo boton Descargar). Se compensa en el padding inferior de main. */
.tw-main-content > main,
.tw-main-content > main.container {
	padding: 0 2rem calc(2rem + 3.5rem) !important;
}

/* --- Modo movil: el sidebar oculto no debe ser tabulable --------------
   Se ocultaba solo con transform, asi que sus enlaces seguian recibiendo el
   foco del teclado y los anunciaba el lector de pantalla. */
@media (max-width: 1023px) {
	aside.tw-sidebar {
		visibility: hidden;
		/* ⚠️ El retardo de 0.25s en `visibility` es SOLO para el cierre: asi el
		   cajon no desaparece antes de que acabe el deslizamiento. Al abrir va a
		   0s, y eso importa: con `visibility 0.25s` a secas, en el instante en que
		   se anade la clase el valor AUN es `hidden` (la interpolacion discreta
		   devuelve hidden en el punto 0), y un .focus() sobre un elemento que no
		   se ve NO hace nada. Medido: el foco se quedaba en la hamburguesa en vez
		   de entrar al cajon. */
		transition: transform 0.25s ease-in-out, visibility 0s linear 0.25s;
	}

	aside.tw-sidebar.sidebar-open {
		visibility: visible;
		transition: transform 0.25s ease-in-out, visibility 0s;
	}
}

/* --- En escritorio el overlay no debe existir -------------------------
   Si se abria el menu en movil y se cruzaba a escritorio, el overlay
   (z-index 49) se quedaba encima tapando tambien el sidebar (z-index 40) y
   con el body sin scroll: pantalla bloqueada. */
@media (min-width: 1024px) {
	#sidebar-overlay {
		display: none !important;
	}
}

/* --- Modo movil: las filas de detalle respetan su estado --------------
   El JS las colapsa con style="display:none", pero `display: block
   !important` le ganaba y TODAS salian expandidas. */
@media (max-width: 768px) {
	.tw-main-content table[role="grid"] tbody tr.list-detail-row[aria-hidden="true"] {
		display: none !important;
	}

	.tw-main-content table[role="grid"] tbody tr.list-detail-row[aria-hidden="false"] {
		display: block !important;
	}

	/* La fila de totales no lleva data-label: sin esto los importes salian
	   sin ninguna etiqueta que los explicase. */
	.tw-main-content table[role="grid"] tbody tr.list-total-row {
		display: block;
		padding: 0.75rem 1rem;
	}

	.tw-main-content table[role="grid"] tbody tr.list-total-row td {
		display: flex;
		justify-content: space-between;
		padding: 0.25rem 0 !important;
	}

	.tw-main-content table[role="grid"] tbody tr.list-total-row td:empty {
		display: none !important;
	}
}

/* --- 2.5.8 Target Size (minimo 24x24) --------------------------------- */
.list-filter-chip__remove {
	width: 1.5rem;
	height: 1.5rem;
}

.flatpickr-clear-btn {
	width: 1.75rem;
	height: 1.75rem;
	right: 0.125rem;
}

@media (pointer: coarse) {
	.list-filter-chip__remove {
		width: 1.75rem;
		height: 1.75rem;
	}
}

/* --- El toast tapaba la hamburguesa en movil --------------------------
   #toast-container iba a top con z-index 10000 y ancho completo, encima de
   la barra superior (z-index 30): con un aviso persistente el menu quedaba
   inaccesible. */
@media (max-width: 640px) {
	#toast-container {
		top: auto;
		bottom: calc(5rem + env(safe-area-inset-bottom, 0px));
	}
}

/* --- Doble marco en las tablas ---------------------------------------
   El <form> ya lleva borde y radio; el <figure> que inyecta theme.js ponia
   otro pegado por dentro. */
.tw-main-content > main > form figure {
	border: 0;
	border-radius: 0;
}

/* --- 2.5.8: enlace "volver arriba" con area pulsable ------------------ */
.footer-link-top {
	padding: 0.375rem 0.5rem;
	border-radius: 0.375rem;
	/* El resto venia en un `style=` del propio enlace. `display`, `gap`,
	   `font-size`, `color` y `font-weight` ya los pone el HTML con utilidades;
	   aqui quedan los dos que no tienen utilidad. */
	text-decoration: none;
	transition: color 0.15s;
}

/* --- Tabla: el orden de clasificacion se expone a lectores de pantalla -- */
.list-sort-icon {
	opacity: 1;
}

/* --- iOS: 100vh no descuenta la barra del navegador -------------------
   El sidebar dejaba el enlace de desconexion fuera del area visible. */
aside.tw-sidebar {
	height: 100vh;
	height: 100dvh;
}

/* ===================================================================
   HERO DE DESCARGAS (dashboard)
   La zona de descargas es la funcion que justifica el portal: ocupa toda
   la rejilla y tiene jerarquia propia en vez de ser una tarjeta mas.
   =================================================================== */
.esp-hero-link {
	display: block;
	text-decoration: none !important;
	border-radius: 1.25rem;
}

.esp-hero {
	position: relative;
	overflow: hidden;
	border-radius: 1.25rem;
	padding: 2rem 2.25rem;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 2rem;
	flex-wrap: wrap;
	background:
		radial-gradient(120% 160% at 100% 0%, hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 32%) 0%, transparent 60%),
		linear-gradient(135deg, #0b1220 0%, #101a2e 55%, #0d1526 100%);
	box-shadow: 0 18px 40px -18px rgba(2, 6, 23, 0.55);
	transition: transform 0.2s ease, box-shadow 0.2s ease;
}

.esp-hero-link:hover .esp-hero {
	transform: translateY(-2px);
	box-shadow: 0 24px 50px -18px rgba(2, 6, 23, 0.65);
}

/* Manchas decorativas (no aportan informacion) */
.esp-hero-orb {
	position: absolute;
	border-radius: 50%;
	pointer-events: none;
	filter: blur(2px);
}
.esp-hero-orb--a {
	top: -4rem;
	right: 12%;
	width: 12rem;
	height: 12rem;
	background: hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 55%);
	opacity: 0.16;
}
.esp-hero-orb--b {
	bottom: -6rem;
	left: 28%;
	width: 15rem;
	height: 15rem;
	background: hsl(calc(var(--tw-primary-h, 210) + 40), var(--tw-primary-s, 70%), 50%);
	opacity: 0.10;
}

.esp-hero-main {
	position: relative;
	z-index: 1;
	display: flex;
	align-items: center;
	gap: 1.25rem;
	min-width: 0;
	flex: 1 1 22rem;
}

.esp-hero-icon {
	flex: 0 0 auto;
	width: 4rem;
	height: 4rem;
	display: flex;
	align-items: center;
	justify-content: center;
	border-radius: 1rem;
	color: #fff;
	background: linear-gradient(140deg, hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 55%), hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 38%));
	box-shadow: 0 10px 24px -8px hsl(var(--tw-primary-h, 210), var(--tw-primary-s, 70%), 30%);
}
.esp-hero-icon svg { width: 2rem; height: 2rem; }

.esp-hero-body { min-width: 0; }

.esp-hero-eyebrow {
	margin: 0 0 0.25rem;
	font-size: 0.6875rem;
	font-weight: 700;
	letter-spacing: 0.12em;
	text-transform: uppercase;
	color: hsl(var(--tw-primary-h, 210), 70%, 74%);
}

.esp-hero-title {
	margin: 0 0 0.375rem;
	font-size: clamp(1.125rem, 1.6vw, 1.5rem);
	font-weight: 800;
	line-height: 1.2;
	letter-spacing: -0.02em;
	color: #ffffff;
	overflow-wrap: anywhere;
}

.esp-hero-desc {
	margin: 0;
	font-size: 0.875rem;
	line-height: 1.6;
	color: rgba(255, 255, 255, 0.78);
}
.esp-hero-sep { margin: 0 0.375rem; opacity: 0.5; }

.esp-hero-chips {
	display: flex;
	flex-wrap: wrap;
	gap: 0.375rem;
	margin: 0.875rem 0 0;
	padding: 0;
	list-style: none;
}
.esp-hero-chips li {
	padding: 0.25rem 0.625rem;
	border-radius: 999px;
	font-size: 0.75rem;
	font-weight: 600;
	color: rgba(255, 255, 255, 0.92);
	background: rgba(255, 255, 255, 0.10);
	border: 1px solid rgba(255, 255, 255, 0.14);
}
.esp-hero-chip--mas { color: rgba(255, 255, 255, 0.7) !important; }

.esp-hero-aside {
	position: relative;
	z-index: 1;
	display: flex;
	align-items: center;
	gap: 1.5rem;
	flex: 0 0 auto;
}

.esp-hero-count {
	margin: 0;
	display: flex;
	flex-direction: column;
	align-items: flex-end;
	line-height: 1;
}
.esp-hero-count-n {
	font-size: 3rem;
	font-weight: 900;
	letter-spacing: -0.05em;
	color: #ffffff;
}
.esp-hero-count-t {
	margin-top: 0.25rem;
	font-size: 0.6875rem;
	font-weight: 700;
	letter-spacing: 0.1em;
	text-transform: uppercase;
	color: rgba(255, 255, 255, 0.62);
}

.esp-hero-cta {
	display: inline-flex;
	align-items: center;
	gap: 0.5rem;
	min-height: 2.75rem;
	padding: 0.75rem 1.25rem;
	border-radius: 0.75rem;
	font-size: 0.875rem;
	font-weight: 700;
	color: #0b1220;
	background: #ffffff;
	white-space: nowrap;
	box-shadow: 0 8px 20px -10px rgba(0, 0, 0, 0.6);
}
.esp-hero-cta svg { width: 1rem; height: 1rem; flex: 0 0 auto; }

/* Foco visible en toda la tarjeta (es un enlace que la envuelve) */
.esp-hero-link:focus-visible { outline: 3px solid #ffffff !important; outline-offset: 3px; }

@media (max-width: 900px) {
	.esp-hero { padding: 1.75rem 1.5rem; gap: 1.5rem; }
	.esp-hero-aside { width: 100%; justify-content: space-between; }
	.esp-hero-count { align-items: flex-start; }
}

@media (max-width: 520px) {
	.esp-hero-main { flex-direction: column; align-items: flex-start; gap: 1rem; }
	.esp-hero-count-n { font-size: 2.25rem; }
	.esp-hero-cta { width: 100%; justify-content: center; }
}

@media (prefers-reduced-motion: reduce) {
	.esp-hero, .esp-hero-link:hover .esp-hero { transition: none !important; transform: none !important; }
}

@media (forced-colors: active) {
	.esp-hero { background: Canvas; border: 1px solid CanvasText; }
	.esp-hero-title, .esp-hero-count-n, .esp-hero-desc, .esp-hero-eyebrow { color: CanvasText; }
	.esp-hero-cta { background: ButtonFace; color: ButtonText; border: 1px solid ButtonText; }
	.esp-hero-chips li { border: 1px solid CanvasText; }
}

/* =====================================================================
 * COMPONENTES DEL SISTEMA (2026-09-24)
 *
 * Se definen aqui una sola vez y los usan el login, los listados y las
 * pantallas nuevas (presupuestos, proyectos). Sustituyen a los estilos en
 * linea que llenaban las plantillas: cuando un boton se define en un sitio,
 * los siguientes salen iguales sin copiar 12 atributos style.
 * ===================================================================== */

/* ---- Botones ----
   Objetivo tactil de 44px de alto como minimo (WCAG 2.5.5). */
.esp-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: 0.5rem;
	/* ⚠️ `width: auto` es OBLIGATORIO aqui. Pico trae `button { display: block;
	   width: 100% }` (pico.css.php:1034), asi que cualquier `.esp-btn` puesto
	   sobre un <button> —y no sobre un <a>— se estiraba a lo ancho del
	   contenedor. Le pasaba al boton "Borrar y empezar de nuevo" de la firma.
	   El ancho completo se pide con `.esp-btn--block`, que ya existe. Medido
	   en el navegador el 2026-09-25 comparando el ancho real con el natural. */
	width: auto;
	min-height: 44px;
	padding: 0.625rem 1.125rem;
	border: 1px solid transparent;
	border-radius: var(--esp-radius);
	font-family: var(--esp-font);
	font-size: 0.9375rem;
	font-weight: 600;
	line-height: 1.2;
	text-decoration: none;
	cursor: pointer;
	transition: background-color 0.18s ease, color 0.18s ease, border-color 0.18s ease, transform 0.18s ease;
}

.esp-btn:focus-visible {
	outline: 3px solid var(--esp-focus);
	outline-offset: 2px;
}

.esp-btn[disabled], .esp-btn[aria-disabled="true"] {
	opacity: 0.55;
	cursor: not-allowed;
	transform: none !important;
}

/* Primario: amarillo de marca con texto en grafito (contraste 15:1) */
.esp-btn--primary {
	background-color: var(--esp-accent);
	color: var(--esp-accent-ink);
}
.esp-btn--primary:hover:not([disabled]):not([aria-disabled="true"]) {
	background-color: var(--esp-accent-strong);
	transform: translateY(-1px);
}

/* Secundario: contorno, para acciones que no son la principal */
.esp-btn--outline {
	background-color: transparent;
	border-color: var(--esp-line);
	color: var(--esp-ink);
}
.esp-btn--outline:hover:not([disabled]) {
	border-color: var(--esp-ink);
	background-color: var(--esp-surface-2);
}

/* Discreto: enlaces de accion dentro de tarjetas y tablas */
.esp-btn--ghost {
	background-color: transparent;
	color: var(--esp-ink-2);
	padding-inline: 0.625rem;
}
.esp-btn--ghost:hover:not([disabled]) {
	background-color: var(--esp-surface-2);
	color: var(--esp-ink);
}

/* Peligro: rechazar un presupuesto, por ejemplo */
.esp-btn--danger {
	background-color: transparent;
	border-color: var(--esp-bad-fg);
	color: var(--esp-bad-fg);
}
.esp-btn--danger:hover:not([disabled]) {
	background-color: var(--esp-bad-bg);
}

.esp-btn--sm {
	min-height: 36px;
	padding: 0.375rem 0.75rem;
	font-size: 0.875rem;
}

.esp-btn--block {
	width: 100%;
}

/* ---- Tarjetas ---- */
.esp-card {
	background-color: var(--esp-surface);
	border: 1px solid var(--esp-line);
	border-radius: var(--esp-radius-lg);
	box-shadow: var(--esp-shadow-sm);
}

.esp-card__head {
	padding: 1.125rem 1.25rem;
	border-bottom: 1px solid var(--esp-line-2);
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 1rem;
	flex-wrap: wrap;
}

.esp-card__title {
	margin: 0;
	font-size: 1rem;
	font-weight: 650;
	color: var(--esp-ink);
}

.esp-card__body {
	padding: 1.25rem;
}

/* ---- Tarjetas de dato (cabecera de un detalle) ---- */
.esp-stats {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
	gap: 0.75rem;
}

.esp-stat {
	background-color: var(--esp-surface);
	border: 1px solid var(--esp-line);
	border-radius: var(--esp-radius);
	padding: 0.875rem 1rem;
}

.esp-stat__label {
	margin: 0 0 0.25rem;
	font-size: 0.75rem;
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--esp-ink-3);
}

.esp-stat__value {
	margin: 0;
	font-family: var(--esp-font-display);
	font-size: 1.5rem;
	font-weight: 700;
	line-height: 1.1;
	color: var(--esp-ink);
}

.esp-stat__hint {
	margin: 0.25rem 0 0;
	font-size: 0.8125rem;
	color: var(--esp-ink-2);
}

/* ---- Etiquetas de estado ----
   El color va en texto y borde suaves con fondo tenue: el estado se distingue
   sin depender solo del color (lleva texto siempre). */
.esp-badge {
	display: inline-flex;
	align-items: center;
	gap: 0.375rem;
	padding: 0.25rem 0.625rem;
	border-radius: 999px;
	font-size: 0.8125rem;
	font-weight: 600;
	line-height: 1.4;
	white-space: nowrap;
}
.esp-badge--ok      { background-color: var(--esp-ok-bg);      color: var(--esp-ok-fg); }
.esp-badge--warn    { background-color: var(--esp-warn-bg);    color: var(--esp-warn-fg); }
.esp-badge--bad     { background-color: var(--esp-bad-bg);     color: var(--esp-bad-fg); }
.esp-badge--info    { background-color: var(--esp-info-bg);    color: var(--esp-info-fg); }
.esp-badge--neutral { background-color: var(--esp-neutral-bg); color: var(--esp-neutral-fg); }
/* Pendiente de firma: es LA accion que espera el cliente, asi que se marca
   con el amarillo de marca en lugar de con un color de estado. */
.esp-badge--action  { background-color: var(--esp-accent-soft); color: var(--esp-ink); }

/* ---- Tablas de datos ---- */
.esp-table {
	width: 100%;
	border-collapse: collapse;
	font-size: 0.9375rem;
}

.esp-table caption {
	text-align: left;
	padding: 0 0 0.5rem;
	color: var(--esp-ink-2);
	font-size: 0.875rem;
}

.esp-table th {
	text-align: left;
	font-size: 0.75rem;
	font-weight: 700;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--esp-ink-3);
	background-color: var(--esp-surface-2);
	padding: 0.625rem 0.875rem;
	border-bottom: 1px solid var(--esp-line);
}

.esp-table td {
	padding: 0.75rem 0.875rem;
	border-bottom: 1px solid var(--esp-line-2);
	color: var(--esp-ink);
	vertical-align: middle;
}

.esp-table tbody tr:hover td {
	background-color: var(--esp-surface-2);
}

.esp-table .esp-num {
	text-align: right;
	font-variant-numeric: tabular-nums;
	white-space: nowrap;
}

.esp-table__wrap {
	overflow-x: auto;
}

/* ---- Buscador de listado (componente compartido) ----
   Nacio dentro del <style> de downloads.tpl.php con el prefijo .esp-dl-, y ahi
   se quedaba: las plantillas se cargan POR CONTROLADOR, asi que esas reglas solo
   existian en la pagina de descargas. Al añadir los listados de facturas y
   pedidos habia dos caminos: copiar el CSS a cada plantilla (tres copias que se
   desincronizan) o subirlo aqui. Va aqui, y descargas usa ya estas clases: una
   sola implementacion. */
.esp-search {
	display: flex;
	align-items: center;
	gap: 0.625rem;
	flex-wrap: wrap;
	background-color: var(--esp-surface);
	border: 1px solid var(--esp-line);
	border-radius: var(--esp-radius);
	padding: 0.75rem 1rem;
}

.esp-search__icon {
	width: 1.125rem;
	height: 1.125rem;
	color: var(--esp-ink-2);
	flex: 0 0 auto;
}

.esp-search__input {
	flex: 1 1 12rem;
	min-width: 0;
	min-height: 2.75rem;
	padding: 0.5rem 0.75rem;
	font-family: var(--esp-font);
	font-size: 0.9375rem;
	color: var(--esp-ink);
	background-color: var(--esp-surface);
	border: 1px solid var(--esp-line);
	border-radius: 0.5rem;
}

.esp-search__input::placeholder {
	color: var(--esp-ink-3);
	opacity: 1;
}

.esp-search__count {
	margin: 0;
	font-size: 0.8125rem;
	color: var(--esp-ink-2);
}

/* ---- Campos de formulario ---- */
.esp-field {
	display: block;
	margin-bottom: 1.25rem;
}

.esp-field__label {
	display: block;
	margin-bottom: 0.5rem;
	font-size: 0.875rem;
	font-weight: 600;
	color: var(--esp-ink);
}

.esp-field__hint {
	margin: 0.375rem 0 0;
	font-size: 0.8125rem;
	color: var(--esp-ink-2);
}

.esp-input {
	width: 100%;
	min-height: 52px;
	padding: 0 1rem;
	border: 1px solid var(--esp-line);
	border-radius: var(--esp-radius);
	background-color: var(--esp-surface);
	color: var(--esp-ink);
	font-family: var(--esp-font);
	font-size: 1rem;
	transition: border-color 0.18s ease, box-shadow 0.18s ease;
}

.esp-input::placeholder { color: var(--esp-ink-3); }

/* El borde se oscurece y aparece un halo amarillo tenue. El outline de foco lo
   sigue poniendo la capa de accesibilidad (var(--esp-focus) con !important),
   asi que en el login se ven las dos cosas: el contorno y el halo. */
.esp-input:focus,
.esp-input:focus-visible {
	border-color: var(--esp-ink);
	box-shadow: 0 0 0 3px var(--esp-accent-soft);
}

/* ---- Cabecera de pagina (h1 + entradilla + acciones) ---- */
.esp-page-head {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: 1rem;
	flex-wrap: wrap;
	margin-bottom: 1.25rem;
}

.esp-page-title {
	margin: 0;
	font-family: var(--esp-font-display);
	font-size: clamp(1.5rem, 2.5vw, 2rem);
	font-weight: 700;
	letter-spacing: -0.02em;
	color: var(--esp-ink);
}

.esp-page-sub {
	margin: 0.375rem 0 0;
	color: var(--esp-ink-2);
	font-size: 0.9375rem;
}

/* ---- Avisos ---- */
.esp-alert {
	display: flex;
	gap: 0.75rem;
	align-items: flex-start;
	padding: 0.875rem 1rem;
	border-radius: var(--esp-radius);
	font-size: 0.9375rem;
	border: 1px solid transparent;
}
.esp-alert--info { background-color: var(--esp-info-bg); color: var(--esp-info-fg); border-color: currentColor; }
.esp-alert--warn { background-color: var(--esp-warn-bg); color: var(--esp-warn-fg); border-color: currentColor; }
.esp-alert--ok   { background-color: var(--esp-ok-bg);   color: var(--esp-ok-fg);   border-color: currentColor; }
.esp-alert--bad  { background-color: var(--esp-bad-bg);  color: var(--esp-bad-fg);  border-color: currentColor; }
/* ⚠️ Sin esta regla, el `hidden` de un aviso NO esconde nada: el `display: flex`
   de arriba es hoja de AUTOR y gana al `[hidden]` del navegador, que es hoja del
   usuario-agente. Vale para todo lo que se destape por JS con el atributo. */
.esp-alert[hidden] { display: none; }

/* ---- Barra de progreso (tareas y proyectos) ---- */
.esp-bar {
	--esp-bar-value: 0%;
	position: relative;
	height: 8px;
	border-radius: 999px;
	background-color: var(--esp-surface-3);
	overflow: hidden;
	min-width: 80px;
}

.esp-bar__fill {
	position: absolute;
	inset: 0 auto 0 0;
	width: var(--esp-bar-value);
	background-color: var(--esp-accent);
	border-radius: inherit;
}

.esp-bar--done .esp-bar__fill { background-color: var(--esp-ok-fg); }

/* Alto contraste forzado: medido en navegador, el track y el relleno se fuerzan
   LOS DOS al mismo color (rgb(255,255,255)), con y sin --esp-bar-value: la barra
   desaparece y queda un rectangulo vacio, o sea que se pierde el porcentaje, que
   es informacion y no adorno (WCAG 1.4.11). Los colores de SISTEMA no se
   sobreescriben (los que no lo son, si): de ahi Highlight y CanvasText.
   El borde hace visible el track tambien al 0%, que es lo que distingue "sin
   avance" de "aqui no hay barra". box-sizing va explicito porque .esp-bar mide
   8px en content-box y el borde la dejaria en 10. */
@media (forced-colors: active) {
	.esp-bar {
		box-sizing: border-box;
		background-color: Canvas;
		border: 1px solid CanvasText;
	}
	.esp-bar__fill,
	.esp-bar--done .esp-bar__fill { background-color: Highlight; }
}

/* ---- Utilidades de texto ---- */

/* ---- Enlace de salto al contenido (WCAG 2.4.1, nivel A) ----
   Sin esto, quien navega con teclado tiene que pasar por los 14 elementos del
   menu lateral EN CADA PAGINA antes de llegar al contenido. Va oculto hasta que
   recibe el foco, y entonces aparece arriba a la izquierda.
   ⚠️ No vale con display:none ni visibility:hidden: un elemento asi NO recibe el
   foco y el enlace no serviria de nada. Se saca de la pantalla con position. */
.esp-skip-link {
	position: absolute;
	left: -9999px;
	top: 0;
	z-index: 100;
	padding: 0.75rem 1.25rem;
	background-color: var(--esp-accent);
	color: var(--esp-accent-ink);
	font-family: var(--esp-font);
	font-size: 0.9375rem;
	font-weight: 700;
	text-decoration: none;
	border-radius: 0 0 var(--esp-radius) 0;
}

.esp-skip-link:focus {
	left: 0;
	outline: 3px solid var(--esp-accent-ink);
	outline-offset: -3px;
}

/* Alto contraste forzado: el amarillo de marca se fuerza a Canvas y el texto a
   CanvasText, o sea que el enlace acaba del mismo color que lo que tapa (el
   lateral), sin separacion. Con colores de sistema el navegador los respeta, y
   el borde lo despega del fondo que queda debajo. */
@media (forced-colors: active) {
	.esp-skip-link,
	.esp-skip-link:focus {
		background-color: Canvas;
		color: CanvasText;
		border: 2px solid CanvasText;
	}
}

/* El <main> lleva tabindex="-1" para poder recibir el foco al saltar, pero no
   debe pintar el anillo de foco del navegador: no es un control. */
main[tabindex="-1"]:focus {
	outline: none;
}

/* Solo para lectores de pantalla. Estaba duplicada en dos plantillas con un
   <style> propio; vive aqui para no repetirla. */
.esp-sr-only {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* Referencias de documento (PR2609-0107): en monoespaciada y sin partirse. */
.esp-mono {
	font-family: var(--esp-font-mono);
	font-size: 0.875rem;
	white-space: nowrap;
}

.esp-hint-warn {
	font-size: 0.8125rem;
	color: var(--esp-warn-fg);
}

/* ---- Estado vacio ---- */
.esp-empty {
	text-align: center;
	padding: 2.5rem 1.5rem;
	color: var(--esp-ink-2);
}

.esp-empty__icon {
	width: 44px;
	height: 44px;
	margin: 0 auto 0.75rem;
	color: var(--esp-ink-3);
}

.esp-empty__title {
	margin: 0 0 0.375rem;
	font-weight: 650;
	color: var(--esp-ink);
}

/* ---- Selector de tema (claro / oscuro / dispositivo) ---- */
.esp-theme-switcher {
	display: inline-flex;
	align-items: center;
	gap: 2px;
	padding: 3px;
	border-radius: 999px;
	background-color: var(--esp-surface-2);
	border: 1px solid var(--esp-line);
}

.esp-theme-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 34px;
	height: 34px;
	padding: 0;
	border: none;
	border-radius: 999px;
	background: transparent;
	color: var(--esp-ink-2);
	cursor: pointer;
	transition: background-color 0.18s ease, color 0.18s ease;
}

.esp-theme-btn svg {
	width: 17px;
	height: 17px;
}

.esp-theme-btn:hover {
	color: var(--esp-ink);
}

.esp-theme-btn[aria-pressed="true"] {
	background-color: var(--esp-surface);
	color: var(--esp-ink);
	box-shadow: var(--esp-shadow-sm);
}

.esp-theme-btn:focus-visible {
	outline: 2px solid var(--esp-focus);
	outline-offset: 2px;
}

/* Variante para el login, que va sobre fondo oscuro */
.esp-theme-switcher--onDark {
	background-color: rgba(255, 255, 255, 0.08);
	border-color: rgba(255, 255, 255, 0.14);
}
/* Version del sidebar. Antes eran tres botones de 28 px pegados al rotulo
   "Tema": quedaban apretados y con un area de pulsacion pequeña. Ahora el
   selector ocupa el ancho del bloque y cada boton se reparte el espacio, con
   32 px de alto (WCAG 2.5.8 pide 24 px como minimo, y 2.5.5 recomienda 44). */
.esp-theme-switcher--sm {
	display: flex;
	width: 100%;
	max-width: 8.5rem;
	padding: 3px;
}

.esp-theme-switcher--sm .esp-theme-btn {
	flex: 1;
	width: auto;
	min-width: 32px;
	height: 32px;
}

.esp-theme-switcher--sm .esp-theme-btn svg {
	width: 16px;
	height: 16px;
}

.esp-theme-switcher--onDark .esp-theme-btn { color: var(--esp-chrome-ink-2); }
.esp-theme-switcher--onDark .esp-theme-btn:hover { color: var(--esp-chrome-ink); }
.esp-theme-switcher--onDark .esp-theme-btn[aria-pressed="true"] {
	background-color: rgba(255, 255, 255, 0.16);
	color: var(--esp-chrome-ink);
	box-shadow: none;
}
.esp-theme-switcher--onDark .esp-theme-btn:focus-visible { outline-color: var(--esp-accent); }

@media (prefers-reduced-motion: reduce) {
	.esp-btn, .esp-theme-btn { transition: none; }
	.esp-btn--primary:hover { transform: none; }
}

/* =====================================================================
 * PAGINA DE FIRMA (controllers/sign.controller.class.php)
 * ===================================================================== */

.esp-sign {
	display: grid;
	gap: 1.25rem;
	grid-template-columns: minmax(0, 1fr);
	max-width: 60rem;
}

/* La linea de nota de una linea de presupuesto (la etiqueta del producto
   cuando no es la propia descripcion). Las reglas de la cabecera vieja
   (__cabecera, __ref, __fecha, __importe, __acciones-secundarias) se fueron con
   la 1.5.3: la firma paso al esquema de la ficha de proyecto —.esp-pagebar,
   .esp-hero, .esp-totals— y dejaron de usarse. */
.esp-sign__linea-nota {
	font-size: 0.8125rem;
	color: var(--esp-ink-3);
}

.esp-sign__panel {
	padding: 1.5rem 1.625rem;
}

.esp-sign__titulo {
	margin: 0 0 0.375rem;
	font-family: var(--esp-font-display);
	font-size: 1.25rem;
	font-weight: 700;
	letter-spacing: -0.02em;
	color: var(--esp-ink);
}

.esp-sign__ayuda {
	margin: 0 0 1.25rem;
	font-size: 0.875rem;
	line-height: 1.55;
	color: var(--esp-ink-2);
}

/* El pad va SIEMPRE en blanco con tinta oscura, en los dos temas: es lo que se
   estampa luego en el PDF, que tambien es blanco. Un pad oscuro con trazo
   claro daria una firma invisible en el PDF. */
.esp-sign__pad-caja {
	border: 1px dashed var(--esp-line);
	border-radius: var(--esp-radius);
	background-color: #FFFFFF;
	overflow: hidden;
}

.esp-sign__pad {
	display: block;
	width: 100%;
	height: 11rem;
	cursor: crosshair;
	background-color: #FFFFFF;
}

.esp-sign__limpiar {
	margin-top: 0.5rem;
}

.esp-sign__botones {
	display: flex;
	gap: 0.625rem;
	flex-wrap: wrap;
	margin-top: 1.25rem;
}

.esp-sign__rechazo {
	margin-top: 1.75rem;
	padding-top: 1.25rem;
	border-top: 1px solid var(--esp-line-2);
}

.esp-sign__rechazo summary {
	cursor: pointer;
	font-size: 0.875rem;
	font-weight: 600;
	color: var(--esp-ink-2);
}

.esp-sign__rechazo summary:hover {
	color: var(--esp-ink);
}

.esp-sign__rechazo[open] summary {
	margin-bottom: 0.875rem;
}

.esp-sign__sello {
	margin: 0.5rem 0 0;
	font-size: 0.875rem;
}

@media (min-width: 900px) {
	.esp-sign {
		grid-template-columns: minmax(0, 1fr);
	}
}

/* =====================================================================
 * LAYOUT DEL CORE, RE-TEMATIZADO
 *
 * El portal es un fork del webportal del core, y su armazon (el body, el
 * contenedor de la aplicacion, el sidebar y el pie) venia con la paleta
 * slate/gray de Tailwind escrita a mano: `bg-gray-50` en el body, `#334155`
 * en los enlaces del menu, `background: white` en el pie... Con colores
 * fijos, al pasar a oscuro se oscurecian los componentes .esp-* pero el
 * marco se quedaba claro, y el resultado era una mezcla a medias.
 *
 * Los colores de las PLANTILLAS ya se cambiaron por tokens (var(--esp-*)).
 * Lo que queda aqui es lo que venia por CLASE de Tailwind, que no se puede
 * tocar desde la plantilla sin quitar la clase.
 * ===================================================================== */

/* `bg-gray-50` de Tailwind. La clase sola tiene especificidad (0,1,0) y esta
   regla (0,1,1), asi que gana sin necesidad de !important.
   El armazon del panel (`display:flex` + `min-height:100vh`) venia en un
   `style=` del <div id="app-layout">: va aqui, que el id ya era el gancho. */
body.bg-gray-50,
#app-layout {
	background-color: var(--esp-bg);
}

#app-layout {
	display: flex;
	min-height: 100vh;
}

/* El color de fondo del documento, para el rebote elastico del scroll */
html {
	background-color: var(--esp-bg);
}

/* =====================================================================
 * Utilidades del portal y escala tipografica
 * =====================================================================
 * Existen para que las plantillas NO lleven estilos en linea. El vocabulario no
 * es inventado: sale de medir los 206 `style=` de las plantillas VIVAS el
 * 2026-09-24. `display:flex` aparecia 37 veces, los iconos de 1.125rem 13, los
 * `font-weight:600` 17, `margin:0` 13... Eso es lo que se convierte en clase.
 *
 * Lo que NO se convierte: `padding` tenia 22 valores distintos en 37 usos. Eso
 * no es una utilidad, es un componente, y se queda con su nombre en la
 * plantilla que lo usa.
 *
 * ⚠️ Los valores son EXACTAMENTE los que estaban escritos a mano: este refactor
 * NO cambia el diseno, solo de donde sale el valor. Lo vigila `_qa-estilos.js`
 * + `_qa-diff-estilos.py`, que comparan el estilo COMPUTADO de las 20 vistas
 * antes y despues. Si un valor baila, sale con el elemento y la propiedad.
 * ===================================================================== */

/* --- Escala tipografica. Antes eran numeros sueltos por las plantillas. --- */
:root {
	--esp-fs-2xs: 0.6875rem;
	--esp-fs-xs: 0.75rem;
	--esp-fs-sm: 0.8125rem;
	--esp-fs-base: 0.875rem;
	--esp-fs-md: 1.0625rem;
	--esp-fs-lg: 1.25rem;
}

.esp-fs-2xs  { font-size: var(--esp-fs-2xs); }
.esp-fs-xs   { font-size: var(--esp-fs-xs); }
.esp-fs-sm   { font-size: var(--esp-fs-sm); }
.esp-fs-base { font-size: var(--esp-fs-base); }
.esp-fs-md   { font-size: var(--esp-fs-md); }
.esp-fs-lg   { font-size: var(--esp-fs-lg); }

.esp-fw-500 { font-weight: 500; }
.esp-fw-600 { font-weight: 600; }
.esp-fw-700 { font-weight: 700; }
.esp-fw-800 { font-weight: 800; }
.esp-fw-900 { font-weight: 900; }

.esp-lh-1    { line-height: 1; }
.esp-lh-12   { line-height: 1.2; }
.esp-lh-13   { line-height: 1.3; }
.esp-lh-16   { line-height: 1.6; }
.esp-lh-165  { line-height: 1.65; }

/* --- Color, siempre por token. El amarillo no vale como color de texto --- */
.esp-ink    { color: var(--esp-ink); }
.esp-ink-2  { color: var(--esp-ink-2); }
.esp-ink-3  { color: var(--esp-ink-3); }
.esp-nav-ink-2 { color: var(--esp-nav-ink-2); }
.esp-ok-fg     { color: var(--esp-ok-fg); }
.esp-accent-ink-color { color: var(--esp-accent-ink); }

/* --- Cajas y layout --- */
.esp-flex        { display: flex; }
.esp-inline-flex { display: inline-flex; }
.esp-block       { display: block; }
.esp-col         { display: flex; flex-direction: column; }
.esp-center      { display: flex; align-items: center; justify-content: center; }
.esp-row         { display: flex; align-items: center; }
.esp-between     { justify-content: space-between; }
.esp-wrap        { flex-wrap: wrap; }
.esp-shrink-0    { flex-shrink: 0; }
.esp-items-center { align-items: center; }
.esp-items-start  { align-items: flex-start; }
/* `min-width: 0` en un hijo flex: sin esto el hijo no se encoge nunca y empuja
   a los hermanos fuera de la caja (misma familia que el min-size automatico). */
.esp-min-0        { min-width: 0; }
.esp-m-0          { margin: 0; }
.esp-p-0          { padding: 0; }
.esp-list-none    { list-style: none; }
.esp-decoration-none { text-decoration: none; }
.esp-line-color   { color: var(--esp-line); }
.esp-m-0         { margin: 0; }
.esp-mx-auto     { margin-left: auto; margin-right: auto; }
.esp-text-center { text-align: center; }

.esp-gap-xs { gap: 0.375rem; }
.esp-gap-sm { gap: 0.5rem; }
.esp-gap-md { gap: 0.75rem; }
.esp-gap-lg { gap: 1rem; }

/* --- Iconos. `flex-shrink:0` va dentro: un svg dentro de un flex se aplasta --- */
.esp-icon        { width: 1.125rem; height: 1.125rem; flex-shrink: 0; }
.esp-icon--xs    { width: 0.75rem;  height: 0.75rem; }
.esp-icon--sm    { width: 1rem;     height: 1rem; }
.esp-icon--md    { width: 1.25rem;  height: 1.25rem; }
.esp-icon--lg    { width: 1.5rem;   height: 1.5rem; }
.esp-icon--xl    { width: 2.5rem;   height: 2.5rem; }

/* =====================================================================
 * Componentes de pagina
 * ===================================================================== */

/* --- Listado de proyectos --- */
.esp-project-link {
	text-decoration: none;
}
.esp-projects__group {
	margin: 0 0 0.75rem 0;
	font-size: var(--esp-fs-md);
	font-weight: 700;
	color: var(--esp-ink);
}
.esp-projects__group--sep {
	margin-top: 2rem;
}
.esp-projects__hint {
	margin: 0 0 1rem 0;
	font-size: var(--esp-fs-sm);
	color: var(--esp-ink-3);
}

/* --- Menu lateral ---
   ⚠️ ESPECIFICIDAD, y aqui es obligatoria: el sidebar lleva
   `aside.tw-sidebar a { all: unset; display: revert }`, que es (0,1,2) y barre
   TODAS las propiedades del enlace. El estilo en linea ganaba por ser en linea;
   una clase sola (0,1,0) no gana a `all: unset`. De ahi el selector largo.
   El estado activo NO lo decide PHP: el marcado ya trae `aria-current="page"`,
   asi que basta con colgar de ahi. Eso quita las variables $styleNavActive y
   $styleNavInactive, que se calculaban en el servidor para pintar lo mismo.

   ⚠️ Y no solo los enlaces: arriba hay `aside.tw-sidebar nav, ul, li, div, span
   { all: unset; display: revert }`, que es (0,1,1) y barre TAMBIEN los <div> y
   los <span> de las cabeceras de seccion. El comentario de esa regla ya lo
   decia — "inline styles take precedence over these resets" — y por eso todo el
   sidebar estaba hecho a base de style= en linea. De ahi que cada componente de
   aqui lleve el prefijo `aside.tw-sidebar`, que es lo unico que le gana. --- */
aside.tw-sidebar nav.esp-nav {
	flex: 1 1 auto;
	/* ⚠️ El `min-height` NO es decorativo: sin el, este nav se encoge a nada.
	   Un hijo flex con overflow distinto de `visible` tiene tamano minimo
	   automatico CERO, asi que con el texto al 200% se quedaba en 33px con
	   1022px de contenido y el menu desaparecia (medido). Con el suelo, el nav
	   se queda con lo que sobre y DESPLAZA por dentro, que es lo que se quiere:
	   el menu es lo unico que se mueve y la marca y el bloque de cuenta se
	   quedan fijos. 6rem = 96px, algo mas de tres entradas. */
	min-height: 6rem;
	padding: 0.75rem 0;
	overflow-y: auto;
	/* Que al llegar al final del menu el scroll no se encadene a la pagina. */
	overscroll-behavior: contain;
	scrollbar-width: thin;
	scrollbar-color: rgba(255, 255, 255, 0.12) transparent;
}
aside.tw-sidebar a.esp-nav-link {
	display: flex;
	align-items: center;
	gap: 0.75rem;
	padding: 0.6875rem 1rem;
	margin: 0.125rem 0.75rem;
	border-radius: 0.5rem;
	font-size: 0.9375rem;
	font-weight: 500;
	text-decoration: none;
	transition: background-color 0.15s ease, color 0.15s ease;
	border-left: 3px solid transparent;
	color: var(--esp-nav-ink);
}
aside.tw-sidebar a.esp-nav-link[aria-current="page"] {
	border-left-color: var(--esp-accent);
	background-color: var(--esp-nav-bg-2);
	color: #fff;
	font-weight: 600;
}
aside.tw-sidebar .esp-nav-section {
	padding: 0.875rem 1.5rem 0.375rem;
}
aside.tw-sidebar .esp-nav-section__rule {
	border-top: 1px solid var(--esp-surface-2);
	margin-bottom: 0.625rem;
}
aside.tw-sidebar .esp-nav-section__label {
	font-size: var(--esp-fs-xs);
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.08em;
	color: var(--esp-ink-3);
	display: block;
}

/* =====================================================================
 * Inicio (el panel del cliente)
 * =====================================================================
 * Es la pantalla con mas deuda: 94 estilos en linea. Cuatro tarjetas de cifra y
 * tres tarjetas grandes con la MISMA estructura, y de distinto solo el color.
 *
 * La forma vive aqui; el color de cada tarjeta lo pone ella con una variable
 * local (--k-*), asi que la variante se elige en una linea del marcado y no
 * repitiendo veinte declaraciones cuatro veces.
 *
 * ⚠️ La CUARTA tarjeta de cifra usa colores CALCULADOS por PHP ($pending_bg,
 * $pending_color, $pending_border). Esos se quedan en linea: son datos, no
 * diseno, y sacarlos a una clase obligaria a generar CSS en el servidor.
 *
 * ⚠️ Los head de las tarjetas verdes y ambar son `linear-gradient(X, X)` de un
 * solo color, que es un fondo liso escrito como degradado. Se DEJA como estaba:
 * cambiarlo a `background` a secas cambiaria el valor computado de
 * background-image y el diff de estilos lo cazaria. Si algun dia se limpia, es
 * un cambio de diseno, no de deuda.
 * ===================================================================== */
main.esp-home { flex: 1; }
.esp-home__body { padding: 0 0 3rem; }

.esp-welcome {
	background: linear-gradient(135deg, var(--esp-accent-wash) 0%, var(--esp-accent-wash-2) 60%, var(--esp-surface) 100%);
	border-radius: 1.25rem;
	border: 1px solid var(--esp-accent-wash-2);
	padding: 2rem 2.5rem;
	margin-bottom: 2rem;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 2rem;
	position: relative;
	overflow: hidden;
}
.esp-welcome__blob {
	position: absolute;
	top: -3rem;
	right: 8rem;
	width: 10rem;
	height: 10rem;
	border-radius: 50%;
	background: var(--esp-accent);
	opacity: 0.3;
	pointer-events: none;
}
.esp-welcome__blob--b {
	position: absolute;
	bottom: -4rem;
	right: -2rem;
	width: 13rem;
	height: 13rem;
	border-radius: 50%;
	background: var(--esp-accent);
	opacity: 0.2;
	pointer-events: none;
}
.esp-welcome__body { min-width: 0; position: relative; z-index: 1; }
.esp-welcome__eyebrow { margin: 0 0 0.5rem; font-size: var(--esp-fs-base); color: var(--esp-accent-on-surface); font-weight: 500; }
.esp-welcome__title {
	margin: 0 0 0.5rem;
	font-size: clamp(1.5rem, 3vw, 2.25rem);
	font-weight: 800;
	color: var(--esp-ink);
	line-height: 1.15;
	word-break: break-word;
}
.esp-welcome__sub { margin: 0; font-size: var(--esp-fs-sm); color: var(--esp-ink-3); text-transform: capitalize; }
.esp-welcome__avatar {
	width: 5.5rem;
	height: 5.5rem;
	border-radius: 1.25rem;
	background: var(--esp-accent);
	color: var(--esp-accent-ink);
	font-size: 2.5rem;
	font-weight: 800;
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
	box-shadow: 0 8px 28px rgba(252, 221, 8, 0.35);
	letter-spacing: -0.05em;
	position: relative;
	z-index: 1;
}

/* --- Tarjetas de cifra --- */
.esp-stats-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
	gap: 1rem;
	margin-bottom: 2rem;
}
.esp-cardlink { text-decoration: none; }
/* ⚠️ `article.esp-statcard` y no `.esp-statcard`: el <article> lleva `tw-card`, y
   arriba hay `article.tw-card { all: unset }`, que es (0,1,1) y barre TODO. El
   estilo en linea le ganaba; una clase sola no. Misma familia que el reset del
   sidebar y el de `footer.tw-section`. */
article.esp-statcard {
	background: var(--esp-surface);
	border-radius: 0.875rem;
	border: 1px solid var(--esp-line);
	padding: 1.25rem 1.125rem;
	display: flex;
	align-items: center;
	gap: 0.875rem;
	cursor: pointer;
}
.esp-statcard__icon {
	width: 2.875rem;
	height: 2.875rem;
	border-radius: 0.75rem;
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
}
.esp-statcard__icon--accent { background: var(--esp-accent-soft); }
.esp-statcard__icon--ok     { background: var(--esp-ok-bg); }
.esp-statcard__icon--warn   { background: var(--esp-warn-bg); }
.esp-statcard__glyph { width: 1.625rem; height: 1.625rem; }
.esp-statcard__glyph--accent { color: var(--esp-accent-on-surface); }
.esp-statcard__glyph--ok     { color: var(--esp-ok-fg); }
.esp-statcard__glyph--warn   { color: var(--esp-warn-fg); }
.esp-statcard__value { font-size: 2.25rem; font-weight: 800; color: var(--esp-ink); line-height: 1; letter-spacing: -0.03em; }
/* La tarjeta de importe lleva el numero mas pequeño y con su unidad al lado */
.esp-statcard__value--money {
	font-size: 1.25rem;
	line-height: 1.1;
	letter-spacing: -0.025em;
}
.esp-statcard__unit { font-size: 0.7em; font-weight: 700; opacity: 0.75; }
.esp-statcard__label {
	font-size: var(--esp-fs-2xs);
	font-weight: 600;
	color: var(--esp-ink-3);
	margin-top: 0.3rem;
	text-transform: uppercase;
	letter-spacing: 0.05em;
}

/* --- Cabecera de seccion --- */
.esp-sechead { display: flex; align-items: center; gap: 1rem; margin-bottom: 1.5rem; }
.esp-sechead__title { margin: 0; font-size: 0.9375rem; font-weight: 700; color: var(--esp-ink); white-space: nowrap; }
.esp-sechead__rule { flex: 1; height: 1px; background: var(--esp-line); }

/* --- Tarjetas grandes --- */
.esp-cards-grid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(min(280px, 100%), 1fr));
	gap: 1.5rem;
}
.esp-full { grid-column: 1 / -1; }
/* Por lo mismo que arriba: el <article> lleva `tw-card`. */
article.esp-dashcard {
	background: var(--esp-surface);
	border-radius: 1.125rem;
	border: 1px solid var(--esp-line);
	overflow: hidden;
	cursor: pointer;
}
.esp-dashcard__head {
	background: var(--k-head);
	padding: 2rem 1.75rem;
	display: flex;
	align-items: center;
	justify-content: space-between;
	height: 9.5rem;
	position: relative;
	overflow: hidden;
}
.esp-dashcard__head--accent { --k-head: linear-gradient(135deg, var(--esp-accent-wash) 0%, var(--esp-accent-wash-2) 100%); }
.esp-dashcard__head--ok     { --k-head: linear-gradient(135deg, var(--esp-ok-bg) 0%, var(--esp-ok-bg) 100%); }
.esp-dashcard__head--warn   { --k-head: linear-gradient(135deg, var(--esp-warn-bg) 0%, var(--esp-warn-bg) 100%); }

.esp-dashcard__blob {
	position: absolute;
	top: -2rem;
	right: -2rem;
	width: 7rem;
	height: 7rem;
	border-radius: 50%;
	background: var(--k-tile);
	opacity: var(--k-blob-a-op);
	pointer-events: none;
}
.esp-dashcard__blob--b {
	position: absolute;
	bottom: -2.5rem;
	left: 2rem;
	width: 8rem;
	height: 8rem;
	border-radius: 50%;
	background: var(--k-blob-b);
	opacity: var(--k-blob-b-op);
	pointer-events: none;
}
.esp-dashcard__icon {
	width: 5rem;
	height: 5rem;
	border-radius: 1.25rem;
	background: var(--k-tile);
	display: flex;
	align-items: center;
	justify-content: center;
	box-shadow: var(--k-shadow);
	position: relative;
	z-index: 1;
	flex-shrink: 0;
}
.esp-dashcard__glyph { width: 2.5rem; height: 2.5rem; color: var(--esp-accent-ink); }
.esp-dashcard__countbox { position: relative; z-index: 1; text-align: right; }
.esp-dashcard__count {
	font-size: 2.75rem;
	font-weight: 900;
	color: var(--k-fg);
	line-height: 1;
	letter-spacing: -0.04em;
	opacity: 0.85;
}
.esp-dashcard__countlabel {
	font-size: var(--esp-fs-xs);
	font-weight: 600;
	color: var(--k-fg);
	text-transform: uppercase;
	letter-spacing: 0.05em;
}
.esp-dashcard__body { padding: 1.5rem 1.75rem 1.25rem; }
.esp-dashcard__title { margin: 0 0 0.5rem; font-size: var(--esp-fs-md); font-weight: 700; color: var(--esp-ink); line-height: 1.3; }
.esp-dashcard__desc { margin: 0; font-size: var(--esp-fs-sm); color: var(--esp-ink-3); line-height: 1.65; }
.esp-dashcard__foot {
	padding: 0.875rem 1.75rem;
	border-top: 1px solid var(--esp-surface-2);
	display: flex;
	align-items: center;
	justify-content: space-between;
}
.esp-dashcard__footlabel { font-size: var(--esp-fs-sm); font-weight: 600; color: var(--k-fg); }
.esp-dashcard__footsvg { width: 1.125rem; height: 1.125rem; color: var(--k-fg); flex-shrink: 0; }

/* Los tres juegos de variables de color, uno por tarjeta. */
/* ⚠️ Las dos opacidades y el color del SEGUNDO blob cambian en cada tarjeta:
   no se pueden fijar en la regla comun. Medido: el blob de arriba va a 0.4 / 0.45
   / 0.45 y el de abajo a 0.2 / 0.3 / 0.35. */
.esp-dashcard--accent { --k-tile: var(--esp-accent);    --k-fg: var(--esp-accent-on-surface); --k-shadow: 0 8px 24px rgba(252, 221, 8, 0.4); --k-blob-b: var(--esp-accent);    --k-blob-a-op: 0.4;  --k-blob-b-op: 0.2; }
.esp-dashcard--ok     { --k-tile: var(--esp-ok-fg);     --k-fg: var(--esp-ok-fg);             --k-shadow: 0 8px 24px rgba(5, 150, 105, 0.4); --k-blob-b: var(--esp-ok-bg);     --k-blob-a-op: 0.45; --k-blob-b-op: 0.3; }
.esp-dashcard--warn   { --k-tile: var(--esp-warn-fg);   --k-fg: var(--esp-warn-fg);           --k-shadow: 0 8px 24px rgba(217, 119, 6, 0.4); --k-blob-b: var(--esp-warn-bg);   --k-blob-a-op: 0.45; --k-blob-b-op: 0.35; }

/* --- Sin nada que enseñar --- */
.esp-emptystate {
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	padding: 4rem 2rem;
	text-align: center;
	max-width: 28rem;
	margin: 0 auto;
}
.esp-emptystate__icon {
	width: 4rem;
	height: 4rem;
	border-radius: 9999px;
	background: var(--esp-surface-2);
	display: flex;
	align-items: center;
	justify-content: center;
	margin-bottom: 1.25rem;
}
.esp-emptystate__title { margin: 0 0 0.5rem; font-size: 1.125rem; font-weight: 700; color: var(--esp-ink); }
.esp-emptystate__text { margin: 0; font-size: var(--esp-fs-base); color: var(--esp-ink-3); line-height: 1.6; }

/* --- Resto del sidebar: marca, ficha de usuario, tema, idioma y pie ---
   ⚠️ Todo con el prefijo `aside.tw-sidebar` por el `all: unset` de arriba: una
   clase sola (0,1,0) NO le gana, y por eso estas zonas estaban a base de
   `style=` en linea. Dentro del sidebar NO valen las utilidades .esp-* por el
   mismo motivo: van a componente con nombre.
   Los valores estan COPIADOS del style= que llevaban, no reescritos: los genero
   un script leyendo el propio atributo. */

aside.tw-sidebar .esp-sidebrand {
	padding: 1.5rem 1.25rem;
	border-bottom: 1px solid var(--esp-line);
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.625rem;
	flex-shrink: 0;
}
/* El logo, grande y centrado. La marca es lo primero que se mira del panel y a
   2rem se quedaba en un sello; a 3rem el PNG de la empresa (866x288) sale a
   153x51 y se lee la razon social entera. El `align-items: center` del bloque es
   lo que lo centra: antes iba todo a la izquierda. */
aside.tw-sidebar .esp-sidebrand__logo {
	height: 3rem;
	width: auto;
	max-width: 100%;
	object-fit: contain;
	display: block;
}
aside.tw-sidebar .esp-sidebrand__mark {
	width: 2.5rem;
	height: 2.5rem;
	border-radius: 0.5rem;
	background: var(--esp-accent);
	display: flex;
	align-items: center;
	justify-content: center;
	flex-shrink: 0;
}
aside.tw-sidebar .esp-sidebrand__label {
	font-size: 0.75rem;
	text-transform: uppercase;
	letter-spacing: 0.075em;
	color: var(--esp-ink-3);
	font-weight: 600;
	text-align: center;
}
/* El bloque de cuenta (usuario, ajustes y acciones).
   `flex: 0 0 auto` lo deja FUERA del reparto: quien se encoge y desplaza es el
   nav, no esto. Antes el sidebar entero desplazaba y este bloque se iba con el,
   que es justo lo que no se quiere: los ajustes de la cuenta tienen que estar
   siempre a mano. */
aside.tw-sidebar .esp-nav-rule {
	border-top: 1px solid var(--esp-line);
	flex: 0 0 auto;
}
aside.tw-sidebar .esp-user {
	padding: 0.5rem 0.75rem;
	display: flex;
	align-items: center;
	gap: 0.5rem;
}
aside.tw-sidebar .esp-user__avatar {
	width: 1.75rem;
	height: 1.75rem;
	border-radius: 9999px;
	background: var(--esp-accent);
	color: var(--esp-accent-ink);
	display: flex;
	align-items: center;
	justify-content: center;
	font-size: 0.6875rem;
	font-weight: 700;
	flex-shrink: 0;
	letter-spacing: 0.02em;
}
aside.tw-sidebar .esp-user__info {
	min-width: 0;
	flex: 1;
}
aside.tw-sidebar .esp-user__name {
	font-size: 0.75rem;
	font-weight: 600;
	color: var(--esp-ink);
	line-height: 1.3;
	overflow-wrap: break-word;
}
aside.tw-sidebar .esp-user__sub {
	font-size: 0.6875rem;
	color: var(--esp-ink-3);
	line-height: 1.3;
	margin-top: 0.0625rem;
}
aside.tw-sidebar .esp-sidepad {
	padding: 0.0625rem 0.5rem;
}
/* Una sola fila: el selector de tema a la izquierda, las acciones a la derecha.
   Sustituye a las cuatro filas con rotulo que hacian este bloque de 300px.
   ⚠️ CON PREFIJO las dos, `.esp-toolrow` y `.esp-tools`. Sin el, el reset de los
   hijos del sidebar las deja en `display: block` y todo se apila en vertical:
   medido, la fila salia de 89px en vez de 32 (el selector arriba y los dos
   botones uno debajo del otro). */
aside.tw-sidebar .esp-toolrow {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.5rem;
	padding: 0 0.25rem;
}
aside.tw-sidebar .esp-tools {
	display: flex;
	align-items: center;
	gap: 2px;
}
/* ⚠️ CON PREFIJO, como todo lo que vive dentro del sidebar: `.esp-iconbtn` sola
   es (0,1,0) y el reset `aside.tw-sidebar a, button { all: unset }` (0,1,1) le
   borra el tamano, el color y el display, o sea que el boton desaparece. Paso en
   el tanteo de este mismo cambio. */
aside.tw-sidebar a.esp-iconbtn,
aside.tw-sidebar button.esp-iconbtn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 26px;
	height: 26px;
	border-radius: 0.375rem;
	color: var(--esp-nav-ink-2);
	cursor: pointer;
	transition: background-color 0.15s ease, color 0.15s ease;
}
aside.tw-sidebar a.esp-iconbtn:hover,
aside.tw-sidebar button.esp-iconbtn:hover {
	background: rgba(255, 255, 255, 0.08);
	color: var(--esp-nav-ink);
}
aside.tw-sidebar a.esp-iconbtn:focus-visible,
aside.tw-sidebar button.esp-iconbtn:focus-visible {
	outline: 2px solid var(--esp-focus);
	outline-offset: 1px;
}
aside.tw-sidebar a.esp-iconbtn--bad,
aside.tw-sidebar button.esp-iconbtn--bad {
	color: var(--esp-bad-fg);
}
aside.tw-sidebar a.esp-iconbtn svg,
aside.tw-sidebar button.esp-iconbtn svg {
	width: 16px;
	height: 16px;
}
/* El rotulo, en micro: es lo unico que distingue un <select> de un boton, asi que
   no se cae (hubo que anadirlo a proposito), pero baja a 11px en mayusculas. */
aside.tw-sidebar .esp-set__label {
	font-size: 0.6875rem;
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--esp-nav-ink-2);
}
/* El selector de tema, DENTRO del sidebar.
   ⚠️ Aqui se vuelven a declarar las propiedades de `.esp-theme-switcher`,
   `.esp-theme-switcher--onDark` y `.esp-theme-switcher--sm` juntas, porque las
   tres son de una sola clase (0,1,0) y el reset de los hijos del sidebar
   (0,1,1) les borraba el fondo, el borde, el radio, el padding y el flex. Medido
   en produccion el 2026-09-24: el contenedor salia `display: block` y
   transparente, y medía 96px (los tres botones pegados) en vez de los 136px del
   ancho del bloque con los botones repartidos, que es lo que dicen las reglas
   de arriba. Los botones si se veian bien porque su regla es de dos clases
   (0,2,0) y esa le gana al reset.
   El sidebar usa SIEMPRE la variante onDark + sm, asi que sobra una sola regla. */
aside.tw-sidebar .esp-theme-switcher {
	display: flex;
	align-items: center;
	gap: 1px;
	padding: 2px;
	border-radius: 999px;
	background-color: rgba(255, 255, 255, 0.08);
	border: 1px solid rgba(255, 255, 255, 0.14);
	width: auto;
	flex: 0 0 auto;
}
/* Y los botones de dentro, por lo mismo: `.esp-theme-btn` es (0,1,0) y el reset
   les borraba el radio (el boton activo salia CUADRADO en vez de en pastilla).
   No se declara `background` a proposito: sin el, el estado activo lo sigue
   pintando `.esp-theme-switcher--onDark .esp-theme-btn[aria-pressed="true"]`
   (0,3,0), que gana; declararlo aqui con (0,2,1) se lo comeria. */
aside.tw-sidebar .esp-theme-switcher .esp-theme-btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 26px;
	height: 26px;
	border-radius: 999px;
	transition: background-color 0.18s ease, color 0.18s ease;
}
aside.tw-sidebar .esp-theme-switcher .esp-theme-btn svg {
	width: 14px;
	height: 14px;
}
/* .esp-sidepad--solo se fue con la fila unica (era el envoltorio de "Cambiar
   contraseña" cuando era una fila de texto). */
aside.tw-sidebar .esp-langbox__form {
	margin: 0;
	display: flex;
	flex-direction: column;
	gap: 0;
}
/* El rotulo de idioma comparte fila con el desplegable y usa `.esp-set__label`
   (micro, en mayusculas). Ocupaba una linea entera para el solo y el bloque se
   iba a 300px de alto. */
aside.tw-sidebar .esp-langbox__row {
	display: flex;
	align-items: center;
	gap: 0.25rem;
	padding-left: 0.25rem;
}
aside.tw-sidebar .esp-langbox__select {
	flex: 1;
	min-width: 0;
	height: 1.75rem;
	padding: 0 0.375rem;
	border: 1px solid var(--esp-nav-line);
	border-radius: 0.375rem;
	background: var(--esp-nav-bg-2);
	color: var(--esp-nav-ink);
	font-family: inherit;
	font-size: 0.75rem;
	font-weight: 500;
}
aside.tw-sidebar .esp-langbox__btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 1.75rem;
	height: 1.75rem;
	flex-shrink: 0;
	padding: 0;
	border: 1px solid var(--esp-nav-line);
	border-radius: 0.375rem;
	background: var(--esp-nav-bg-2);
	color: var(--esp-nav-ink);
	cursor: pointer;
}
/* .esp-sidepad--last y .esp-footlink se fueron con la fila unica: la desconexion
   ya no es una fila de texto sino un boton de icono dentro de `.esp-tools`. */
.esp-topbar {
	display: flex;
	align-items: center;
	gap: 1rem;
	padding: 0.875rem 1rem;
	background-color: var(--esp-surface);
	border-bottom: 1px solid var(--esp-line);
	position: sticky;
	top: 0;
	z-index: 30;
	flex-shrink: 0;
}
.esp-topbar__btn {
	padding: 0.5rem;
	border-radius: 0.375rem;
	color: var(--esp-ink);
	background: none;
	border: none;
	cursor: pointer;
}
.esp-topbar__title {
	font-weight: 700;
	font-size: 1rem;
	color: var(--esp-ink);
}

/* .esp-nav-link--slim se fue con la fila unica: "Cambiar contraseña" ya no es
   una fila de texto sino un boton de icono (`.esp-iconbtn`). */

/* --- Barra de la ficha (proyecto y presupuesto) ---
   Nacio como `.esp-project__lead` dentro de la ficha de proyecto y la firma
   necesitaba exactamente lo mismo: volver a la lista a la izquierda y las
   acciones a la derecha. Se generaliza el nombre en vez de duplicar la regla
   (o de que la firma use una clase que dice "project"). */
.esp-pagebar  {
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 0.75rem;
	flex-wrap: wrap;
	margin: 0 0 1.25rem 0;
}
.esp-pagebar__actions { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; }
.esp-project__block { margin-bottom: 1.5rem; }
.esp-project__alert { margin-bottom: 1.25rem; }
.esp-project__h2 {
	margin: 0 0 0.875rem;
	font-size: var(--esp-fs-md);
	font-weight: 700;
	color: var(--esp-ink);
}

/* ── El hero: el dato que resume la ficha, en grande ─────────────────────
   En un proyecto es el anillo de avance con la banda temporal; en un
   presupuesto, el importe con el anillo de validez. Comparten rejilla para que
   las dos fichas empiecen igual: un dato grande a la izquierda y su contexto a
   la derecha. */
.esp-hero {
	display: grid;
	grid-template-columns: 11rem minmax(0, 1fr);
	gap: 2rem;
	align-items: center;
	padding: 1.5rem;
	border: 1px solid var(--esp-line);
	border-radius: 1rem;
	background: var(--esp-surface);
}
@media (max-width: 900px) {
	.esp-hero { grid-template-columns: 1fr; gap: 1.25rem; }
}
/* Sin dato que poner en el anillo (un presupuesto sin fecha de validez), el
   hero se queda con una columna: no se reserva el hueco de algo que no hay. */
.esp-hero--solo { grid-template-columns: minmax(0, 1fr); }

.esp-hero__gauge {
	position: relative;
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: 0.625rem;
}
.esp-ring {
	width: 9.25rem;
	height: 9.25rem;
	/* El SVG empieza a las 3 en punto: sin girarlo, el avance arranca a la
	   derecha en vez de arriba. */
	transform: rotate(-90deg);
}
.esp-ring__track { fill: none; stroke: var(--esp-line); stroke-width: 9; }
/* El anillo que se esta acabando (plazo a 3 dias o vencido) se pone en aviso:
   el amarillo de marca dice "accion pendiente", no "date prisa". */
.esp-ring__value--warn { stroke: var(--esp-warn-fg); }
.esp-ring__value {
	fill: none;
	stroke: var(--esp-accent);
	stroke-width: 9;
	stroke-linecap: round;
	transition: stroke-dashoffset 0.6s ease;
}
.esp-ring__label {
	position: absolute;
	top: 0;
	left: 0;
	width: 100%;
	height: 9.25rem;
	display: flex;
	flex-direction: column;
	align-items: center;
	justify-content: center;
	pointer-events: none;
}
.esp-ring__pct {
	font-size: 2.25rem;
	font-weight: 800;
	line-height: 1;
	letter-spacing: -0.02em;
	color: var(--esp-ink);
}
.esp-ring__sign { font-size: 1.125rem; font-weight: 700; margin-left: 0.0625rem; }
.esp-ring__caption {
	max-width: 7.5rem;
	font-size: var(--esp-fs-2xs);
	line-height: 1.25;
	text-align: center;
	color: var(--esp-ink-3);
}
.esp-hero__state { margin: 0; }

/* --- El cuerpo del hero cuando el dato es un NUMERO (presupuestos) --- */
.esp-hero__body { min-width: 0; }
.esp-hero__amount-label {
	display: block;
	margin: 0 0 0.375rem;
	font-size: var(--esp-fs-2xs);
	font-weight: 600;
	letter-spacing: 0.06em;
	text-transform: uppercase;
	color: var(--esp-ink-3);
}
.esp-hero__amount {
	font-size: 2.75rem;
	font-weight: 800;
	line-height: 1;
	letter-spacing: -0.02em;
	/* Cifras de ancho fijo: con proporcionales, el numero baila al comparar. */
	font-variant-numeric: tabular-nums;
	color: var(--esp-ink);
}
.esp-hero__meta {
	margin: 0.875rem 0 0;
	font-size: var(--esp-fs-sm);
	color: var(--esp-ink-2);
}
.esp-hero__meta strong { color: var(--esp-ink); font-weight: 600; }
.esp-hero__meta-sep { color: var(--esp-ink-3); padding: 0 0.375rem; }

.esp-gantt { min-width: 0; }
.esp-gantt__title { margin: 0 0 0.75rem; font-size: var(--esp-fs-sm); font-weight: 700; color: var(--esp-ink); }
.esp-gantt__scale {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	margin-bottom: 0.375rem;
	font-size: var(--esp-fs-2xs);
	color: var(--esp-ink-3);
}
.esp-gantt__rows {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: 0.4375rem;
}
.esp-gantt__row { min-width: 0; }
.esp-gantt__track {
	position: relative;
	height: 0.875rem;
	border-radius: 999px;
	background: var(--esp-surface-2);
	overflow: hidden;
}
/* La barra son DOS colores en un solo elemento: lo hecho y lo que queda. Se
   hace con un degradado duro en `--esp-bar-value` en vez de con un hijo dentro,
   porque el hijo tendria que posicionarse sobre un ancho que ya es variable. */
.esp-gantt__bar {
	position: absolute;
	top: 0;
	bottom: 0;
	border-radius: 999px;
	background: linear-gradient(to right, var(--esp-gantt-on, var(--esp-accent)) 0 var(--esp-bar-value, 0%), var(--esp-gantt-off, var(--esp-accent-wash)) var(--esp-bar-value, 0%) 100%);
}
.esp-gantt__bar--ok      { --esp-gantt-on: var(--esp-ok-fg);   --esp-gantt-off: var(--esp-ok-bg); }
.esp-gantt__bar--action  { --esp-gantt-on: var(--esp-accent);  --esp-gantt-off: var(--esp-accent-wash); }
.esp-gantt__bar--neutral { --esp-gantt-on: var(--esp-ink-3);   --esp-gantt-off: var(--esp-surface-2); }
.esp-gantt__today {
	position: absolute;
	top: 0;
	bottom: 0;
	width: 2px;
	background: var(--esp-ink);
	opacity: 0.5;
}
.esp-gantt__meta {
	display: flex;
	align-items: baseline;
	justify-content: space-between;
	gap: 0.75rem;
	margin-top: 0.1875rem;
	min-width: 0;
}
.esp-gantt__name {
	font-size: var(--esp-fs-xs);
	color: var(--esp-ink-2);
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	min-width: 0;
}
.esp-gantt__dates { font-size: var(--esp-fs-2xs); color: var(--esp-ink-3); white-space: nowrap; }
.esp-gantt__legend {
	display: flex;
	align-items: center;
	gap: 0.375rem;
	margin: 0.75rem 0 0;
	font-size: var(--esp-fs-2xs);
	color: var(--esp-ink-3);
}
.esp-gantt__key--today { width: 2px; height: 0.875rem; background: var(--esp-ink); opacity: 0.5; }
.esp-gantt__nodates { margin: 0.5rem 0 0; font-size: var(--esp-fs-2xs); color: var(--esp-ink-3); }

/* ── Totales ─────────────────────────────────────────────────────────────
   Banda con separadores finos y no una fila de tarjetas: son cuatro datos del
   MISMO proyecto, no cuatro cosas sueltas. La tarjeta repetida cuatro veces es
   el patron por defecto y no dice nada. */
.esp-totals {
	display: grid;
	grid-template-columns: repeat(4, minmax(0, 1fr));
	border: 1px solid var(--esp-line);
	border-radius: 1rem;
	background: var(--esp-surface);
	overflow: hidden;
}
@media (max-width: 1000px) { .esp-totals { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 560px)  { .esp-totals { grid-template-columns: 1fr; } }
.esp-totals__item { min-width: 0; padding: 1rem 1.125rem; border-left: 1px solid var(--esp-line); }
.esp-totals__item:first-child { border-left: 0; }
@media (max-width: 1000px) {
	.esp-totals__item:nth-child(odd) { border-left: 0; }
	.esp-totals__item:nth-child(n+3) { border-top: 1px solid var(--esp-line); }
}
@media (max-width: 560px) {
	.esp-totals__item { border-left: 0; border-top: 1px solid var(--esp-line); }
	.esp-totals__item:first-child { border-top: 0; }
}
.esp-totals__label {
	margin: 0 0 0.25rem;
	font-size: var(--esp-fs-2xs);
	font-weight: 600;
	text-transform: uppercase;
	letter-spacing: 0.06em;
	color: var(--esp-ink-3);
}
.esp-totals__value { margin: 0; font-size: 1.5rem; font-weight: 800; line-height: 1.1; color: var(--esp-ink); }
.esp-totals__of { font-size: var(--esp-fs-sm); font-weight: 600; color: var(--esp-ink-3); }
.esp-totals__value--late { font-size: 1.125rem; color: var(--esp-warn-fg); }
.esp-totals__value--text { font-size: var(--esp-fs-md); font-weight: 600; color: var(--esp-ink-2); }
.esp-totals__hint { margin: 0.25rem 0 0; font-size: var(--esp-fs-2xs); color: var(--esp-ink-3); }
.esp-totals__bar { margin-top: 0.5rem; }

/* ── Las tareas, en filas ────────────────────────────────────────────────
   En filas y no en tabla: con la descripcion dentro, una columna de texto largo
   en una tabla obliga a leer de lado. */
.esp-tasks { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.75rem; }
.esp-task {
	display: flex;
	flex-direction: column;
	gap: 0.625rem;
	padding: 1rem 1.125rem;
	border: 1px solid var(--esp-line);
	border-radius: 0.875rem;
	background: var(--esp-surface);
}
.esp-task__head { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; flex-wrap: wrap; }
.esp-task__id { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; min-width: 0; }
.esp-task__dot { width: 0.5rem; height: 0.5rem; border-radius: 999px; flex-shrink: 0; background: var(--esp-ink-3); }
.esp-task__dot--ok     { background: var(--esp-ok-fg); }
.esp-task__dot--action { background: var(--esp-accent); }
.esp-task__name { font-weight: 600; color: var(--esp-ink); }
.esp-task__desc { font-size: var(--esp-fs-sm); line-height: 1.6; color: var(--esp-ink-2); }
.esp-task__desc > :first-child { margin-top: 0; }
.esp-task__desc > :last-child  { margin-bottom: 0; }
.esp-task__bar { display: flex; align-items: center; gap: 0.625rem; }
.esp-task__bar .esp-bar { flex: 1; }
.esp-task__pct { min-width: 2.5rem; text-align: right; font-size: var(--esp-fs-2xs); font-weight: 700; color: var(--esp-ink-2); }
.esp-task__meta { display: flex; flex-wrap: wrap; gap: 0.5rem 1.75rem; margin: 0; }
.esp-task__meta-item { display: flex; flex-direction: column; gap: 0.0625rem; min-width: 0; }
.esp-task__meta dt { font-size: var(--esp-fs-2xs); color: var(--esp-ink-3); }
.esp-task__meta dd { margin: 0; font-size: var(--esp-fs-sm); color: var(--esp-ink); }

/* --- Tareas agrupadas por estado y su filtro (1.5.2) ---
   El grupo lleva el titulo con su punto de color y su cuenta, y debajo la
   lista. `:first-of-type` quita el aire de arriba del PRIMER grupo: si no, la
   lista arranca con un hueco que no separa nada. */
.esp-taskgroup__head {
	display: flex;
	align-items: center;
	gap: 0.5rem;
	margin: 1.5rem 0 0.625rem 0;
	font-size: var(--esp-fs-sm);
	font-weight: 700;
	color: var(--esp-ink);
}
.esp-taskgroup__head:first-of-type { margin-top: 0; }
.esp-taskgroup__n {
	font-weight: 400;
	font-variant-numeric: tabular-nums;
	color: var(--esp-ink-3);
}
/* ⚠️ Sin esta regla el `hidden` del filtro NO esconde nada en cuanto el
   navegador aplique `display` desde otra hoja (pico trae su propio reset). */
.esp-taskgroup__head[hidden],
.esp-tasks[hidden] { display: none; }

.esp-taskfilter {
	display: flex;
	align-items: center;
	flex-wrap: wrap;
	gap: 0.375rem;
	margin: 0 0 0.25rem 0;
}
.esp-chip {
	display: inline-flex;
	align-items: center;
	gap: 0.375rem;
	/* ⚠️ `width: auto` NO es decorativo. Pico trae `button { display: block;
	   width: 100% }` (pico.css.php:1034) y un boton sin `width` propio hereda
	   ese 100%: los cuatro chips se apilaban uno por fila, cada uno del ancho
	   del contenedor. Medido en el navegador, no deducido. */
	width: auto;
	min-height: 28px;
	padding: 0.1875rem 0.625rem;
	border: 1px solid var(--esp-line);
	border-radius: 999px;
	background-color: transparent;
	color: var(--esp-ink-2);
	font-family: inherit;
	/* Un paso por encima de `2xs` (que es lo que usan las insignias): un chip
	   es un CONTROL que el cliente tiene que leer para saber que filtra, no un
	   metadato que solo se consulta. */
	font-size: var(--esp-fs-xs);
	font-weight: 600;
	cursor: pointer;
	transition: background-color .15s ease, color .15s ease, border-color .15s ease;
}
.esp-chip:hover { background-color: var(--esp-surface-2); color: var(--esp-ink); }
/* Sin regla de `:focus-visible` propia A PROPOSITO: la global (global.css.php,
   `a/button/[tabindex]/…:focus-visible`) ya pone el anillo con `!important`, asi
   que una aqui no se aplicaria nunca y solo serviria para que el siguiente que
   la lea crea que el anillo es de otro color. */
/* El activo se marca por `aria-pressed`, no por una clase: es el mismo dato
   que lee el lector de pantalla, asi que no pueden discrepar. */
.esp-chip[aria-pressed="true"] {
	background-color: var(--esp-ink);
	border-color: var(--esp-ink);
	color: var(--esp-surface);
}
.esp-chip__n {
	font-weight: 400;
	font-variant-numeric: tabular-nums;
	opacity: .75;
}

.esp-project__note  { margin: 1rem 0 0 0; }
.esp-project__desc {
	margin: 0;
	font-size: 0.9375rem;
	color: var(--esp-ink-2);
	line-height: 1.65;
	/* ⚠️ SIN `white-space: pre-line`. Estaba aqui como apaño para que los saltos
	   se vieran cuando la plantilla escapaba la descripcion con
	   dol_escape_htmltag(); ahora la plantilla usa dolPrintHTML(), que ya mete
	   los <br>, y con pre-line cada salto se pintaba DOS veces. Medido: la
	   descripcion de prueba pasaba de 184px a la mitad al quitarlo. */
}
/* Dentro de una tarjeta de estadistica se enseña una FECHA, no un numero
   grande: el tamaño de la clase base es de cifra. */
.esp-stat__value--text {
	font-size: 1rem;
}

/* --- Cabecera de pagina. Estaba entera en estilos en linea.
   ⚠️ Se llama `esp-pagehead` y NO `esp-hero`: `.esp-hero` YA EXISTIA en esta
   hoja con 35 reglas (el banner con degradado de Inicio), y reutilizar el nombre
   le colgaba a este <div> el degradado y a sus hijos los margenes negativos del
   otro componente. Medido: margin-left/right del <ol> pasaba de 0 a -17px.
   Antes de inventar un nombre de clase, comprobarlo contra la hoja previa.
   ⚠️ Y `div.esp-pagehead`, no `.esp-pagehead`: el <div> lleva `tw-section`, que
   es (0,1,1) y gana a una clase sola. Ver la nota del pie. --- */
div.esp-pagehead {
	padding: 2rem 2rem 0;
}
/* ⚠️ Pico mete margen negativo a los <ol>/<ul> de dentro de un <nav>:
   `nav ol:first-of-type { margin-left: calc(var(--nav-element-spacing-horizontal) * -1) }`,
   que es (0,1,2) y GANA a `.esp-m-0` (0,1,0) y a `.esp-p-0`. Antes no se veia
   porque el `style=` en linea ganaba a todo. El selector de aqui empata en
   especificidad (0,1,2) y va despues, asi que manda. Medido: el margen del
   breadcrumb pasaba de 0 a -17px. */
nav ol.esp-pagehead__list,
nav ul.esp-pagehead__list {
	margin: 0;
	padding: 0;
	list-style: none;
}

.esp-pagehead__head {
	margin-bottom: 1.5rem;
}
.esp-pagehead__crumbs {
	margin-bottom: 0.5rem;
}
.esp-pagehead__title {
	margin: 0 0 0.375rem 0;
	font-size: 1.75rem;
	font-weight: 800;
	color: var(--esp-ink);
	line-height: 1.2;
	letter-spacing: -0.02em;
}
.esp-pagehead__desc {
	margin: 0;
	font-size: var(--esp-fs-base);
	color: var(--esp-ink-3);
	line-height: 1.6;
}

/* --- Pie. Estaba entero en el `style=` del <footer>: 8 declaraciones.
   ⚠️ El selector es `footer.esp-footer` y NO `.esp-footer`, y no es un capricho:
   el <footer> lleva la clase `tw-section`, y arriba hay una regla de reset —
   `footer.tw-section { margin:0; padding:0; display:block }` — con especificidad
   (0,1,1), que GANA a una clase sola (0,1,0). Con `.esp-footer` el pie se
   quedaba con padding 0 y el contenido pegado a los bordes. Es la MISMA familia
   de trampa que el `a:link` del tema de eldy, que ya se documento en el README:
   antes ganaba el estilo en linea, al pasarlo a clase hay que igualar
   especificidad. Aplica a todo componente que se ponga sobre un `tw-section`. --- */
footer.esp-footer {
	margin-top: auto;
	position: sticky;
	bottom: 0;
	z-index: 20;
	padding: 0.875rem 2rem;
	border-top: 1px solid var(--esp-line);
	background: var(--esp-surface);
	box-shadow: 0 -1px 3px rgba(15, 23, 42, 0.06);
	/* Hueco para la barra del iPhone, que se come los ultimos 34px */
	padding-bottom: calc(0.875rem + env(safe-area-inset-bottom, 0px));
}
.esp-footer__logo {
	height: 1.75rem;
	width: auto;
	max-width: 10rem;
	object-fit: contain;
	display: block;
}

/* Aviso sin toast disponible: se pinta como HTML para no perderlo. */
.esp-alert-bar {
	padding: 1rem 2rem;
}

/* --- 404. Los valores son los que llevaba en linea; ver el comentario del
   propio 404.tpl.php, que explica por que el numero NO es un degradado. --- */
.esp-404 {
	min-height: 60vh;
	display: flex;
	align-items: center;
	justify-content: center;
	padding: 2rem 0;
}
.esp-404__box   { text-align: center; max-width: 28rem; margin: 0 auto; }
.esp-404__num   { font-size: 6rem; font-weight: 900; line-height: 1; letter-spacing: -0.04em; margin: 0 0 1rem 0; color: var(--esp-accent-on-surface); }
.esp-404__title { font-size: var(--esp-fs-lg); font-weight: 700; color: var(--esp-ink); margin: 0 0 0.5rem 0; }
.esp-404__text  { font-size: var(--esp-fs-base); color: var(--esp-ink-2); margin: 0 0 1.5rem 0; line-height: 1.6; }

/* --- Radios --- */
.esp-round { border-radius: 50%; }
.esp-r-sm  { border-radius: 0.5rem; }
.esp-r-md  { border-radius: 0.75rem; }
.esp-r-lg  { border-radius: 0.875rem; }
.esp-r-xl  { border-radius: 1.125rem; }
.esp-r-2xl { border-radius: 1.25rem; }

/* La placa clara detras del logo del sidebar. Es BLANCA A PROPOSITO y en los
   dos temas: el logo es una imagen con sus colores y no se puede invertir, asi
   que se le pone el mismo fondo claro que usan los paneles oscuros de verdad.
   Va aqui y con clase (antes era un style= en linea) para que el barrido de QA
   pueda reconocerla como superficie clara deliberada y no confundirla con el
   fallo que persigue, que es una caja clara que NADIE ha decidido.

   ⚠️ CON PREFIJO `aside.tw-sidebar`. Sin el es (0,1,0) y el reset de los hijos
   del sidebar (0,1,1) le borra el fondo, el padding, el radio y el flex: la
   placa desaparece y el logo negro se queda sobre el cromo oscuro. Estuvo roto
   desde que el style= en linea paso a clase (1.4.0) y se vio en produccion el
   2026-09-24. Es la misma trampa que documentan `nav.esp-nav` y
   `a.esp-nav-link` mas arriba. */
aside.tw-sidebar .esp-brand-plate {
	background: #FFFFFF;
	border-radius: 0.625rem;
	padding: 0.4375rem 0.75rem;
	display: flex;
	align-items: center;
	justify-content: center;
	max-width: 100%;
}

/* =====================================================================
 * "Mis Tarjetas": los colores del modulo Redsys
 * =====================================================================
 * Esa pagina no la pinta el portal: la pinta el modulo Redsys por el hook
 * PrintPageView, con los colores ESCRITOS EN LINEA y pensados para un tema
 * claro (leidos en su codigo el 2026-09-24: fondo #f9f9f9, texto #666, boton
 * #27ae60 con letra blanca). Dentro del portal, que puede ir en tema oscuro,
 * eso da dos fallos de contraste AA medidos en navegador (2,87:1 el boton y
 * 3,39:1 el parrafo) y una isla clara en mitad de la pagina.
 *
 * ⚠️ NO se toca el modulo. Es de terceros y cualquier cambio suyo se pierde en
 * su siguiente actualizacion, que es la leccion de contasol. El color se
 * corrige desde aqui, que es lo unico que sobrevive.
 *
 * Los selectores van por el LITERAL del color en linea porque el modulo no le
 * pone clase a esos elementos: es lo unico que los distingue. Es fragil a
 * proposito y sin riesgo — si el modulo cambia un literal, la regla deja de
 * aplicar y no rompe nada; y el contraste de esta pagina lo vigila el barrido
 * del portal, que no perdona un fallo.
 *
 * ⚠️ `[style*="#27ae60"]` casa con el boton Y con el texto del mismo verde, asi
 * que van separados por `background:` y `color:`. Y `#fff` va con el punto y
 * coma: sin el, casaria tambien con `#fff3cd`.
 * ===================================================================== */

/* Texto neutro de tema claro, ilegible sobre grafito. */
#esp-main [style*="color:#666"]     { color: var(--esp-ink-2) !important; }
#esp-main [style*="color:#555"]     { color: var(--esp-ink-2) !important; }
#esp-main [style*="color:#7f8c8d"]  { color: var(--esp-ink-3) !important; }
#esp-main [style*="color:#2c3e50"]  { color: var(--esp-ink) !important; }

/* Superficies claras: tarjetas y recuadros que en oscuro dejan un agujero. */
#esp-main [style*="background:#f9f9f9"] { background: var(--esp-surface-2) !important; border-color: var(--esp-line) !important; }
#esp-main [style*="background:#fff;"]   { background: var(--esp-surface) !important; }
#esp-main [style*="background:#fff3cd"] { background: var(--esp-warn-bg) !important; border-color: var(--esp-warn-fg) !important; color: var(--esp-warn-fg) !important; }
#esp-main [style*="#eaf2f8"]            { background: var(--esp-info-bg) !important; border-color: var(--esp-line) !important; }
#esp-main [style*="color:#856404"]      { color: var(--esp-warn-fg) !important; }
#esp-main [style*="color:#1a5276"]      { color: var(--esp-info-fg) !important; }
#esp-main [style*="color:#e67e22"]      { color: var(--esp-warn-fg) !important; }
#esp-main [style*="color:#e74c3c"]      { color: var(--esp-bad-fg) !important; }
#esp-main [style*="color:#27ae60"]      { color: var(--esp-ok-fg) !important; }
#esp-main [style*="border-left:4px solid #3498db"] { border-left-color: var(--esp-info-fg) !important; }

/* Botones del modulo. Se visten con el acento del portal para que no parezcan
   de otra aplicacion: letra oscura sobre amarillo, el par ya verificado. */
#esp-main [style*="background:#27ae60"],
#esp-main [style*="background:#2e86c1"] {
	background: var(--esp-accent) !important;
	color: var(--esp-accent-ink) !important;
	border: 1px solid var(--esp-accent) !important;
}
