/* Fonts loaded via <link> tags in HTML - no @import needed */
@import url('./modules/oarclubdata-navbar.css');

/* =========================================================================
   LAYOUT ROOT — reglas globales para que `position: sticky` funcione
   =========================================================================
   Regla CSS: si un ancestro tiene overflow != visible en cualquier eje, la
   spec resuelve el otro eje a `auto`, convirtiéndolo en scroll-container y
   rompiendo el sticky de sus descendientes.

   Antes: cada `<body>` traía la utility class `overflow-x-hidden` para evitar
   scroll horizontal accidental. Eso rompía `position: sticky` de headers/tabs
   en todo el proyecto (el sticky "no anclaba").

   Ahora: la protección va en `<html>` (que sí puede ser scroll-container sin
   afectar a los sticky del body), y el body queda con overflow visible.
   Fuente única: no repetido en cada HTML, no hay valores mágicos.
   ========================================================================= */
html {
    overflow-x: clip;
    /* Offset ÚNICO bajo el que se anclan la navbar y los sub-headers sticky:
       altura del banner de licencia (--lwb-offset, 0 sin banner) + altura del
       navbar. Reactivo por CSS: si aparece el banner (o cambia su alto al
       envolver el texto en móvil), todo lo que use --nav-stick-top se reubica
       solo, sin quedar tapado al scrollear. La navbar en sí ancla sólo en
       --lwb-offset (va DEBAJO del banner, no de sí misma). */
    --nav-stick-top: calc(var(--lwb-offset, 0px) + var(--navbar-height, 100px));
    /* Cuando el browser hace scroll para poner algo en vista (auto-focus de
       inputs, hash navigation, scrollIntoView), respeta este padding para
       no dejar el target tapado por el chrome sticky. Fuente única. */
    scroll-padding-top: var(--nav-stick-top);
}
body {
    /* Fallback generoso hasta que el navbar reporte su altura real vía
       ResizeObserver (ver oarclubdata-navbar.js#_exposeHeightVar). El
       navbar en desktop mide ~96-100px (h-1.5 gradient + py-4 + logo h-10
       + label + border-b). Preferimos pasarnos un par de px que ver el
       sticky del profile-header tapado durante el primer paint. */
    --navbar-height: 100px;
}

/* =========================================================================
   SKELETON SHIMMER - sistema de precarga visual reutilizable
   =========================================================================
   Uso: agregar `data-loading="true"` al <body>. Cuando el módulo JS
   termina de renderizar, llama `document.body.removeAttribute('data-loading')`.
   Los bloques `.page-skeleton` se muestran solo durante la carga; el
   contenido real (`.page-content`) se oculta hasta que el loading termina.
   ========================================================================= */
body[data-loading] .page-skeleton { display: flex; flex-direction: column; gap: 1.5rem; }
body:not([data-loading]) .page-skeleton { display: none; }
body[data-loading] .page-content { display: none; }

.sk {
    background: #e5e7eb;
    border-radius: 8px;
    position: relative;
    overflow: hidden;
}
.dark .sk, [data-theme='dark'] .sk {
    background: #2a2a2a;
}
.sk::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.08) 50%, transparent 100%);
    animation: sk-shimmer 1.5s ease-in-out infinite;
}
.dark .sk::after, [data-theme='dark'] .sk::after {
    background: linear-gradient(90deg, transparent 0%, rgba(255,255,255,0.05) 50%, transparent 100%);
}
@keyframes sk-shimmer { 0% { transform: translateX(-100%); } 100% { transform: translateX(100%); } }

.sk-text { height: 14px; margin-bottom: 8px; }
.sk-text-lg { height: 20px; margin-bottom: 10px; }
.sk-text-xl { height: 32px; margin-bottom: 12px; }
.sk-card {
    border-radius: 12px;
    border: 1px solid #e5e7eb;
    padding: 1rem;
}
.dark .sk-card, [data-theme='dark'] .sk-card {
    border-color: #333;
}
.sk-row {
    height: 48px;
    margin-bottom: 4px;
    border-radius: 0;
}
.sk-circle {
    border-radius: 50%;
}
.sk-btn { height: 40px; width: 140px; border-radius: 8px; }
.sk-filter { height: 38px; border-radius: 8px; }
.sk-kpi { height: 100px; border-radius: 12px; }
.sk-chart { height: 250px; border-radius: 12px; }

/* Skeleton específico de attendance: refleja el layout real (scanner card +
   6 KPIs + tabs + filtro + tabla). Cada bloque adopta el mismo shape que
   su contraparte en `.page-content` para que la transición no dispare
   layout shift. */
body[data-loading] .page-skeleton.sk-attendance-layout {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
}
.sk-attendance-scanner {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1rem 1.25rem;
    border-radius: 16px;
    border: 1px solid var(--color-border, #e5e7eb);
    background: var(--color-surface, #ffffff);
    flex-wrap: wrap;
}
.dark .sk-attendance-scanner, [data-theme='dark'] .sk-attendance-scanner {
    background: #1a1a1a;
    border-color: #2a2a2a;
}
.sk-attendance-kpis {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1rem;
}
@media (min-width: 1024px) {
    .sk-attendance-kpis { grid-template-columns: repeat(6, 1fr); }
}

/* Skeleton específico de settings: refleja el layout real (sidebar + cards) */
body[data-loading] .page-skeleton.sk-settings-layout {
    display: grid;
    grid-template-columns: 240px 1fr;
    gap: 1.5rem;
    align-items: start;
}
.sk-settings-sidebar {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 12px;
    border-radius: 16px;
    background: var(--color-surface, #ffffff);
    border: 1px solid var(--color-border, #e5e5e5);
}
.dark .sk-settings-sidebar, [data-theme='dark'] .sk-settings-sidebar {
    background: #1a1a1a;
    border-color: #2a2a2a;
}
.sk-settings-nav { height: 36px; border-radius: 8px; }
.sk-settings-content {
    display: flex;
    flex-direction: column;
    gap: 1.5rem;
}
.sk-settings-card { height: 260px; border-radius: 16px; }
@media (max-width: 768px) {
    body[data-loading] .page-skeleton.sk-settings-layout {
        grid-template-columns: 1fr;
    }
    .sk-settings-sidebar { flex-direction: row; overflow-x: auto; }
    .sk-settings-nav { min-width: 100px; flex-shrink: 0; }
}

/* =========================================
   .consent-check - checkbox de consentimiento con texto multi-línea al lado.
   Usado por: DPA en superadmin (Nuevo Club), términos en login y saas-login.
   Centralizado acá para no repetir estilos inline y mantener un solo lugar
   de verdad para el patrón "acepto X".
   ========================================= */
.consent-check {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    font-size: 0.85rem;
    line-height: 1.5;
    cursor: pointer;
}
/* label.consent-check para elevar specificity (0,2,2) por sobre
   `.sa-modal input[type="checkbox"]` (0,2,1) que vive en <style> inline
   de superadmin.html. Sin esto, el margen top de 3px se perdía. */
label.consent-check > input[type="checkbox"] {
    margin: 3px 0 0 0;
    padding: 0;
    width: 1rem;
    height: 1rem;
    flex-shrink: 0;
    cursor: pointer;
    /* --consent-accent puede setearse por página (saas-login usa --sa-accent).
       Fallback a --color-primary del tema activo. */
    accent-color: var(--consent-accent, var(--color-primary));
}
label.consent-check > span { flex: 1; }

/* Checkboxes: el color de marca del tema activo — rojo en modo claro
   (--color-primary #dd0000), amarillo en modo oscuro (#facc15). Con un solo
   token, el accent nativo acompaña al tema sin reglas por-modo. */
input[type="checkbox"] {
    accent-color: var(--color-primary);
}

/* Barra de acciones masivas (arriba de la lista) — reutilizable por la lista de
   Socios y el panel de Solicitudes. Sticky bajo el navbar global para que NO se
   oculte al scrollear. La VISIBILIDAD la controla el JS con `display` (none/flex);
   esta clase sólo aporta look + posición (no fija display). --navbar-height la
   expone oarclubdata-navbar.js. */
.bulk-bar-top {
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    padding: 0.75rem 1.25rem;
    margin-bottom: 0.75rem;
    background: rgba(26, 26, 26, 0.97);
    -webkit-backdrop-filter: blur(12px);
    backdrop-filter: blur(12px);
    border: 1px solid rgba(234, 179, 8, 0.4);
    border-radius: 0.75rem;
    box-shadow: 0 4px 24px rgba(0, 0, 0, 0.4);
    position: sticky;
    top: var(--nav-stick-top);
    z-index: 30;
}

/* Filtros del panel de Solicitudes: buscador grande (izquierda) + select de
   estado chico (derecha), responsive. Clase propia para no depender de variantes
   Tailwind que pueden faltar en el CSS precompilado (p.ej. sm:w-52). */
.req-filters { display: flex; flex-direction: column; gap: 0.75rem; }
@media (min-width: 640px) {
    .req-filters { flex-direction: row; align-items: center; }
    .req-filters .req-search { flex: 1 1 auto; min-width: 0; }
    .req-filters .req-status { flex: 0 0 13rem; }
}

/* Opciones del modal "¿Cómo aplicar los datos?" (aceptar/actualizar una
   solicitud): dos tarjetas de acción con campo SÓLIDO desde los tokens de
   estado (danger = pisar, success = completar), mismo modelo solid-surface +
   herencia de foreground que las celdas del calendario y los chips/banners.
   Antes eran tintes rgba() HARDCODEADOS inline en member-requests-panel.js. */
.req-apply-opt {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.15rem;
    padding: 0.75rem 1rem;
    border: 1px solid transparent;
    border-radius: 0.5rem;
    cursor: pointer;
    text-align: left;
    transition: filter 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease;
}
.req-apply-opt:hover {
    filter: brightness(1.05);
    transform: translateY(-1px);
    box-shadow: 0 4px 12px rgba(0, 0, 0, 0.18);
}
.req-apply-opt__title { font-weight: 700; display: flex; align-items: center; gap: 0.4rem; }
.req-apply-opt__title .material-symbols-outlined { font-size: 1.1rem; color: inherit; }
/* Descripción: mismo foreground del token, atenuado, legible sobre el sólido. */
.req-apply-opt__desc { font-size: 0.78rem; color: inherit; opacity: 0.85; }

.req-apply-opt--overwrite {
    background: var(--badge-danger-bg);
    color: var(--badge-danger-fg);
    border-color: var(--badge-danger-bg);
}
.req-apply-opt--fill {
    background: var(--badge-success-bg);
    color: var(--badge-success-fg);
    border-color: var(--badge-success-bg);
}


/* Hide Material Symbols ligature text until the icon font is loaded.
   theme.js adds `html.fonts-ready` cargando la fuente EXPLÍCITAMENTE
   (document.fonts.load) y esperando su carga real (con red de seguridad de
   8s). La fuente está subseteada a ~260 KB (tools/subset-icons.py), así que
   llega en segundos. */
html:not(.fonts-ready) .material-symbols-outlined {
    visibility: hidden;
}

/* =========================================
   PURE CSS SCROLL LOCK
   Bloquea el scroll del body automáticamente si hay algún modal u overlay abierto.
   Esto reemplaza los "parches" en JavaScript (theme.js lockScroll, etc)
   ========================================= */
body:has(
    .modal-overlay.open, 
    .confirm-overlay.show, 
    .inv-modal-overlay.show, 
    .crew-modal-overlay.show, 
    .inv-modal-overlay.active, 
    .report-modal-overlay.show, 
    .backup-modal-overlay.show, 
    .settings-modal-overlay.show, 
    .camera-modal-overlay.show, 
    .overlay.show, 
    .modal.show:not(.inline-modal)
) {
    overflow: hidden !important;
}

/* =========================================
   LAYER: TOKENS
   Design tokens (CSS custom properties)
   ========================================= */
:root {
    /* Tailwind Dynamic Variables */
    --tw-color-primary: 221 0 0;
    --tw-color-primary-text: 255 255 255;

    /* Brand palette - LIGHT mode uses red as the accent color (in DARK mode
       these tokens flip to the system yellow via .dark overrides below).
       Use ONLY for branding: navbar active state, primary CTA buttons, focus
       rings, links. For danger/error signals use --badge-danger-* instead -
       those don't flip with theme and stay red-as-signal in both modes. */
    --color-brand: #dd0000;
    --z4-fg: #ffffff;
    --z4-dot: #ffffff;
    --color-primary: #dd0000;
    --color-accent-red: #dd0000;

    --color-sidebar: #0a0a0a;
    --color-bg: #fafafa;
    --color-surface: #ffffff;
    --color-surface-hover: #f5f5f5;
    --color-surface-recessed: #fafafa;
    --color-text-main: #111111;
    --color-text-muted: #525252;
    --color-border: #e5e5e5;

    /* Aliases cortos — mismos tokens con nombres de una palabra para consumo
       en componentes chicos (popover, tooltip, mini-modals). No es un set
       distinto: siempre resuelven al `--color-*-*` de arriba. Vive un solo
       token por concepto. Los overrides de .dark abajo cambian los
       `--color-*` y estos aliases se propagan solos. */
    --surface: var(--color-surface);
    --surface-hover: var(--color-surface-hover);
    --border: var(--color-border);
    --text: var(--color-text-main);
    --text-muted: var(--color-text-muted);
    --hover: var(--color-surface-hover);

    --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
    --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
    --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);

    --shadow-glow-red: 0 0 15px rgb(var(--tw-color-primary) / 0.3);

    --font-main: 'Outfit', sans-serif;
    --font-heading: 'Space Grotesk', sans-serif;

    --radius-md: 10px;
    --radius-lg: 16px;

    /* Semantic State Colors solid */
    --color-state-info: #1d4ed8;
    --color-state-info-bg: #dbeafe;
    --color-state-warning-bg: #fee2e2;
    --color-state-warning: var(--color-brand);
    --color-state-warning-border: #fca5a5;
    --color-badge-members: var(--color-brand);
    --color-badge-members-bg: #fee2e2;
    --color-sifen-query: #1d4ed8;
    --color-confirm-icon: #10b981;

    /* Status tokens - semantic single-color palette usada por cards, KPIs,
       indicadores y cualquier acento de estado. Consumido por dashboard.css,
       select-club.css y cards del sistema. Los cuatro estados canónicos. */
    --color-status-success: #10b981;
    --color-status-warning: var(--color-primary);
    --color-status-error: var(--color-brand);
    --color-status-info: var(--color-state-info);

    /* ── BADGE DESIGN TOKENS ── 4-color semantic system.
       Every badge/chip/pill/tooltip/banner/chart in the system resolves through
       these. Non-signal categories (info, primary, accent, muted) collapse to
       neutral by design - differentiation happens via icons + labels, not hue. */
    --badge-success-bg: #059669; --badge-success-fg: #ffffff;  /* emerald-600 - dinero + / OK / al día */
    --badge-danger-bg:  #b91c1c; --badge-danger-fg:  #ffffff;  /* red-700    - dinero - / error / deuda */
    --badge-warning-bg: #facc15; --badge-warning-fg: #111827;  /* brand yellow - atención / VIP / master */
    --badge-neutral-bg: #334155; --badge-neutral-fg: #ffffff;  /* slate-700  - todo lo demás */

    /* Warning como TEXTO/ÍCONO sobre una superficie (.text-warning, .icon-warning).
       Distinto de --badge-warning-bg (fondo de chip): el amarillo brand #facc15 es
       ilegible como texto sobre blanco. En claro se pide ROJO — lo tomamos del token
       de danger (una sola fuente del rojo, no un hex nuevo); en dark se sobreescribe
       al amarillo brand (que sí contrasta sobre negro). Los fondos de badge NO
       cambian: siguen con --badge-warning-bg. */
    --color-warning-text: var(--badge-danger-bg); /* rojo (danger) - legible sobre superficies claras */

    /* Aliases collapsed to neutral - kept for API stability across the codebase.
       If a specific accent color is needed later, only the alias line changes. */
    --badge-info-bg:    #2563eb; --badge-info-fg:    #ffffff;  /* blue-600   - zona agua / info contextual */
    --badge-primary-bg: var(--badge-neutral-bg); --badge-primary-fg: var(--badge-neutral-fg);
    --badge-accent-bg:  var(--badge-neutral-bg); --badge-accent-fg:  var(--badge-neutral-fg);
    --badge-muted-bg:   var(--badge-neutral-bg); --badge-muted-fg:   var(--badge-neutral-fg);

    /* Actividad deportiva: dos hues fijos usados en KPIs, barras, headers y dots
       del análisis de tiempo (salón vs agua). Un solo lugar donde tocar el color. */
    --color-salon: #10b981; /* emerald-500 */
    --color-water: #3b82f6; /* blue-500   */

    /* Role: TITULAR de plan grupal. En light mode contrasta mejor con las
       filas claras usando el rojo (danger). En dark mode dominamos con
       amarillo brand (warning). Vive como token propio para no arrastrar
       otras chip-warning al cambio, y para que un solo lugar controle el
       color del badge sin hardcodear en el componente. */
    --badge-titular-bg: var(--badge-danger-bg);
    --badge-titular-fg: var(--badge-danger-fg);

    /* Brand attention: badges/contadores que deben resaltar con el color
       principal del club (rojo en claro, amarillo en oscuro). Uso: contador
       de solicitudes pendientes, cualquier "hay algo nuevo" que reclame la
       atención con la marca. Token propio para que ni titular ni warning
       arrastren a este contador ante un cambio futuro. */
    --badge-brand-bg: var(--badge-danger-bg);
    --badge-brand-fg: var(--badge-danger-fg);

    /* Semantic single-color tokens (icons, dots, standalone text) reference badge palette. */
    --status-paid: var(--badge-success-bg);
    --status-paid-bg: var(--badge-success-bg);
    --status-paid-border: var(--badge-success-bg);
    --status-paid-text: var(--badge-success-bg);
    --status-debt: var(--badge-danger-bg);
    --status-debt-bg: var(--badge-danger-bg);
    --status-debt-border: var(--badge-danger-bg);
    --status-debt-text: var(--badge-danger-bg);
    --status-plan: var(--badge-neutral-bg);
    --status-attendance: var(--badge-neutral-bg);
    --status-category: var(--badge-warning-bg);
}

/* =========================================
   MODO OSCURO - Paleta basada en negros y grises puros
   Sin azules ni tonos fríos
   ========================================= */
.dark,
[data-theme='dark'] {
    --tw-color-primary: 250 204 21;
    --tw-color-primary-text: 10 10 10;

    /* Tailwind theme variable overrides - swap red → gold */
    --color-primary: #facc15;
    --color-accent-red: #facc15;

    --color-bg: #0a0a0a;
    --color-surface: #141414;
    --color-surface-hover: #1c1c1c;
    --color-surface-recessed: #1c1c1c;
    --color-text-main: #f5f5f5;
    --color-text-muted: #a3a3a3;
    --color-border: #262626;

    --color-brand: #facc15;
    --z4-fg: #000000;
    --z4-dot: #000000;
    --color-btn-text: #0a0a0a;

    /* Danger semántico en dark: rojo pleno (#dc2626 red-600) + fg blanco.
     * Mismo look que light (rojo intenso, texto blanco) — chips/banners/alerts
     * quedan totalmente rojos sin atenuar. Como color de texto (.text-danger,
     * .icon-danger) sobre negro mantiene contraste AA (~4.5:1 sobre #0a0a0a).
     * Alimenta a la vez: .text-danger, .chip-danger, .banner-danger,
     * .icon-danger, .menu-item.is-danger, .btn-link-danger, --status-debt-*,
     * .lb-info-bar--danger. */
    --badge-danger-bg: #dc2626;
    --badge-danger-fg: #ffffff;

    /* Warning como texto/ícono en dark: el amarillo brand contrasta bien sobre
       negro, así que .text-warning/.icon-warning vuelven al brand. Ver :root. */
    --color-warning-text: #facc15;

    /* Override del rol TITULAR: en dark queremos el amarillo brand (warning)
       porque contrasta mejor sobre negro que el rojo. Ver definición en :root. */
    --badge-titular-bg: var(--badge-warning-bg);
    --badge-titular-fg: var(--badge-warning-fg);

    /* Brand attention en dark: seguimos el mismo criterio que titular
       (amarillo brand sobre negro contrasta mejor que rojo). Ver :root. */
    --badge-brand-bg: var(--badge-warning-bg);
    --badge-brand-fg: var(--badge-warning-fg);

    --shadow-glow-red: 0 0 15px rgb(var(--tw-color-primary) / 0.3);
    --shadow-glow-hover: rgb(var(--tw-color-primary) / 0.4);

    /* Semantic State Colors - dark: gold accent for info/members, green for confirm */
    --color-state-info: var(--color-primary);
    --color-state-info-bg: rgba(250, 204, 21, 0.1);
    --color-state-warning-bg: #713f12;
    --color-state-warning: var(--color-primary);
    --color-state-warning-border: #a16207;
    --color-badge-members: var(--color-primary);
    --color-badge-members-bg: rgba(250, 204, 21, 0.1);
    --color-sifen-query: var(--color-primary);
    --color-confirm-icon: #10b981;

    /* Status tokens - dark: warning/error/info colapsan al primary (gold) igual
       que el resto del sistema; success sigue verde porque es señal semántica
       (dinero +, aprobado) que no debe leerse como el color de marca. */
    --color-status-success: #10b981;
    --color-status-warning: var(--color-primary);
    --color-status-error: var(--color-primary);
    --color-status-info: var(--color-primary);

    /* Semantic tokens inherit from :root (which references --badge-* single source). */

    --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.5);
    --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.4), 0 2px 4px -1px rgba(0, 0, 0, 0.3);
    --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.5);
}
/* =========================================
   GLOBAL COMPONENT + UTILITY STYLES
   Unlayered so they override Tailwind's base reset.
   Tailwind utilities (in @layer tw) still win over these
   by class order when intentionally used together.
   ========================================= */

/* Prevent transition animations on page load (FOUC) */
.preload *,
.preload,
/* Prevent transition animations during theme toggle (avoids animating
   hundreds of elements simultaneously on data-heavy pages like the
   161-member list). The class is added/removed by theme.js around the
   dark↔light class swap. */
.theme-switching *,
.theme-switching {
    transition: none !important;
}


body {
    background-color: var(--color-bg);
    color: var(--color-text-main);
    font-family: var(--font-main);
    transition:
        background-color 0.3s ease,
        color 0.3s ease;
}

:is(h1, h2, h3, h4, h5, h6),
.logo-container h1,
#page-title {
    font-family: var(--font-heading);
    letter-spacing: -0.02em;
}

/* Sidebar & Topbar */
.sidebar {
    background-color: var(--color-sidebar);
    border-right: 1px solid var(--color-border);
}
.sidebar .main-nav li a:hover,
.sidebar .main-nav li a.active {
    background-color: #1f1f1f;
    border-left-color: var(--color-brand);
}
.top-bar {
    background-color: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    color: var(--color-text-main);
}

/* Cards & Contenedores */
:is(.card, .dashboard-card, .data-card, .glass-card, .detail-card, .filter-section) {
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    color: var(--color-text-main);
    transition:
        transform 0.2s ease,
        box-shadow 0.2s ease;
}
:is(.card, .dashboard-card):hover {
    box-shadow: var(--shadow-md);
}

/* Modales */
.modal-content {
    background-color: var(--color-surface);
    color: var(--color-text-main);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
    font-family: var(--font-main);
}
.modal-header h2 {
    color: var(--color-text-main);
}
.close-modal {
    color: var(--color-text-muted);
}
.close-modal:hover {
    color: var(--color-brand);
}

/* ==========================================================================
   BASE LAYER: element-level defaults
   -------------------------------------------------------------------------
   Envueltos en @layer base para que utilities Tailwind (pl-*, text-*, etc.)
   puedan ganarles por cascade v4. Sin este layer, la regla element-selector
   gana siempre a la utility clase (aunque tenga más especificidad), porque
   un-layered > cualquier @layer. Ver [[project-oarclubdata-tailwind-layer-gotcha]].
   ========================================================================= */
@layer base {
    /* Inputs & Formularios */
    :is(input, select, textarea),
    .form-group :is(input, select) {
        background-color: var(--color-bg);
        color: var(--color-text-main);
        border: 1px solid var(--color-border);
        border-radius: var(--radius-md);
        font-family: var(--font-main);
        padding: 10px 14px;
        transition: all 0.2s ease;
    }

    /* Native select: single clean arrow, no double UA-arrow */
    select {
        appearance: none;
        -webkit-appearance: none;
        -moz-appearance: none;
        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%239ca3af'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E");
        background-position: right 0.75rem center;
        background-repeat: no-repeat;
        background-size: 1rem 1rem;
        padding-right: 2.5rem;
    }

    /* Dark mode native select arrow color adjustment */
    .dark select {
        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 24 24' stroke='%23d4d4d4'%3E%3Cpath stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M19 9l-7 7-7-7'/%3E%3C/svg%3E");
    }

    label {
        color: var(--color-text-main);
        font-weight: 500;
    }
}

/* FOUC prevention se queda un-layered - es contract con selects JS-upgraded */
select:not([data-custom-select]):not(.sr-only) {
    opacity: 0;
    pointer-events: none;
}

/* ── DATE/TIME NATIVE INDICATOR ──
   Confiamos en el indicator nativo del navegador (un solo ícono, click
   sólo sobre él abre el picker). Sólo ajustamos color-scheme para que
   Chromium/WebKit pinten el indicator legible en dark mode.

   Historia: había un hack que ocultaba el indicator OEM y superponía un
   pseudo-element con `position:absolute; inset:0` como hit-target. En
   modales el pseudo escapaba a un ancestro posicionado y capturaba clicks
   en otros campos (p. ej. click en "Monto" abría el calendario). Además
   producía doble ícono en navegadores donde el opacity:0 no ganaba.
   Solución de raíz: no manipular el indicator — ya funciona bien en
   Chromium mobile moderno. */
input[type='date'],
input[type='time'],
input[type='datetime-local'],
input[type='month'],
input[type='week'] {
    color-scheme: light;
}
html.dark input[type='date'],
html.dark input[type='time'],
html.dark input[type='datetime-local'],
html.dark input[type='month'],
html.dark input[type='week'] {
    color-scheme: dark;
}
input[type='date']::-webkit-calendar-picker-indicator,
input[type='time']::-webkit-calendar-picker-indicator,
input[type='datetime-local']::-webkit-calendar-picker-indicator,
input[type='month']::-webkit-calendar-picker-indicator,
input[type='week']::-webkit-calendar-picker-indicator {
    cursor: pointer;
}

/* Force screen-reader-only (sr-only) selects (used as hidden value stores for custom buttons)
   to be completely invisible and have 0px layout footprint, resolving overlapping double-arrows */
select.sr-only,
.sr-only select,
input.sr-only,
textarea.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border-width: 0;
    background: none;
    background-image: none;
    opacity: 0;
    pointer-events: none;
}

/* ── GATING POR ROL: config de API Key en el modal de test ──
   El botón "API Key" del modal de Test de Remo navega a Settings (Configurar
   API de Gemini), que el entrenador no puede acceder. Lo ocultamos para su rol
   en cualquier página que use el modal (perfil del socio y Performance Lab).
   role-preload.js/common-auth.js exponen data-club-role en <html>. El backend
   igual bloquea Settings; esto elimina la vía de confusión. */
:root[data-club-role='entrenador'] #btn-gemini-config {
    display: none !important;
}

/* Entrenador: gestión de datos (backup / importar / exportar) y configuración
   son administrativas y NO le corresponden en NINGÚN módulo que alcanza
   (Asistencia, Regatas, Inventario, Lab). En Socios los export/import ya se
   ocultan por applyListWriteGating. Mismo patrón declarativo por
   data-club-role; el backend es defensa en profundidad. */
:root[data-club-role='entrenador'] #btnOpenSettings,           /* Asistencia · Ajustes */
:root[data-club-role='entrenador'] #tabNotifications,          /* Asistencia · Notificaciones WA/TG */
:root[data-club-role='entrenador'] #btnOpenAttendanceBackup,   /* Asistencia · Backup */
:root[data-club-role='entrenador'] #btn-export-inv,            /* Inventario · Exportar/Importar */
:root[data-club-role='entrenador'] #lab-btn-export,            /* Lab · Exportar */
:root[data-club-role='entrenador'] #crew-btn-open-export-import, /* Regatas · Backups */
:root[data-club-role='entrenador'] #btn-export-champ,          /* Regatas · Exportar/Importar (campeonato) */
:root[data-club-role='entrenador'] #btn-export-date {          /* Regatas · Exportar/Importar (fecha) */
    display: none !important;
}

/* General utility bulletproofing for all sr-only elements */
.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border-width: 0;
}
/* Los workarounds .relative input.pl-10 / .pl-11 se eliminaron: con el input
   base ahora en @layer base, la utility Tailwind pl-10/pl-11 gana sola.
   El :focus también se movió al @layer base de arriba. */
@layer base {
    :is(input, select, textarea):focus {
        outline: none;
        border-color: var(--color-brand);
        box-shadow: 0 0 0 3px rgb(var(--tw-color-primary) / 0.15);
    }
}

/* =========================================================================
   BOTONES SISTEMA DE COMPONENTES
   -------------------------------------------------------------------------
   Reemplaza la "sopa de utilidades" (bg-primary + hover:opacity-90 +
   dark:bg-surface-dark + hover:bg-gray-50 + dark:hover:bg-gray-800 + ...)
   por clases semánticas que encapsulan estado base, hover, focus,
   disabled y contraste con la superficie del contenedor.

   Uso en HTML: <button class="btn btn-primary flex-1">Guardar</button>

   Variantes:
     · .btn geometría + tipografía + transición base
     · .btn-primary fondo sólido de marca (rojo/oro)
     · .btn-secondary outline: sin fondo, borde visible, hover con fill
     · .btn-emerald verde (Exportar/Confirmar), definido más abajo
     · .btn-ghost transparente sin borde, definido más abajo
   ========================================================================= */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.625rem 1.25rem;
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    font-family: var(--font-main);
    font-size: 0.875rem;
    font-weight: 700;
    line-height: 1;
    cursor: pointer;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
    transition:
        background-color 0.15s ease,
        border-color 0.15s ease,
        color 0.15s ease,
        filter 0.15s ease,
        box-shadow 0.15s ease,
        transform 0.08s ease;
}
.btn:disabled,
.btn[aria-disabled='true'] {
    opacity: 0.5;
    cursor: not-allowed;
    filter: none;
    pointer-events: none;
}
.btn:focus-visible {
    outline: 2px solid var(--color-brand);
    outline-offset: 2px;
}

/* Primary fondo sólido de marca. Hover con filter (nunca opacity ni
 * color-mix con transparent) para evitar bleed-through del backdrop.
 *
 * Decisión de diseño: hover = BRIGHTEN (nunca darken). El botón nunca
 * se oscurece al pasarle el cursor. El "press feedback" del :active sí
 * usa brightness<1 porque el click sí debe leerse como "hundido".
 * Válido para light y dark: `filter: brightness(1.08)` sube luminancia
 * de forma uniforme sobre cualquier color base. */
:is(.btn-primary, .btn-action) {
    background-color: var(--color-primary);
    color: var(--color-btn-text, #ffffff);
    border-color: transparent;
    /* Box-shadow compuesto: highlight blanco interno arriba + ring
     * blanco exterior + drop-shadow. Los blancos son SIEMPRE visibles
     * (no dependen del color base) el botón se lee brillante y
     * elevado ante cualquier render, compresión o gamut. */
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.45),
        0 0 0 1px rgba(255, 255, 255, 0.18),
        0 4px 12px -2px rgba(0, 0, 0, 0.5),
        0 2px 4px -1px rgba(0, 0, 0, 0.35);
}
.dark :is(.btn-primary, .btn-action),
[data-theme='dark'] :is(.btn-primary, .btn-action) {
    color: #0a0a0a; /* accent-black para contraste sobre el oro del tema dark */
}
:is(.btn-primary, .btn-action):hover:not(:disabled):not([aria-disabled='true']) {
    /* Feedback SIN tocar `background-color` ni `filter`. Sólo agrega un
     * ring blanco alrededor y sube la elevación del shadow el color
     * del fondo del botón queda idéntico entre base y hover. */
    box-shadow:
        0 0 0 2px rgba(255, 255, 255, 0.35),
        0 8px 20px -2px rgba(0, 0, 0, 0.5),
        0 4px 8px -2px rgba(0, 0, 0, 0.35);
}
:is(.btn-primary, .btn-action):active:not(:disabled):not([aria-disabled='true']) {
    /* Press feedback via encoger, no via brightness nunca oscurece. */
    transform: scale(0.98);
}

/* Botones dentro de cualquier modal del sistema: más compactos que en las
 * páginas principales. El modal es de decisión rápida — CTAs enormes
 * distraen más que ayudan. Cubrimos las dos convenciones que conviven en
 * el proyecto: .oarclubdata-modal (patrón compartido) y [role="dialog"]
 * (usado por overlays custom como .membership-confirm-dialog).
 *
 * Escape hatch: cualquier caller que quiera un botón grande dentro de un
 * modal puede pasar .btn-lg (esta regla solo pisa la variante default). */
:is(.oarclubdata-modal, [role='dialog'], .membership-confirm-overlay) .btn:not(.btn-lg):not(.btn-xl) {
    padding: 0.45rem 0.9rem;
    font-size: 0.8rem;
    min-width: 0;
}
:is(.oarclubdata-modal, [role='dialog'], .membership-confirm-overlay) .btn:not(.btn-lg):not(.btn-xl) .material-symbols-outlined {
    font-size: 16px;
}

/* ─── Selección multi-fila (row-click) + barra bulk ──────────────────────
 *
 * Click sobre cualquier zona "neutra" de la fila (no botones, no chips
 * interactivos) toggle la selección. El JS filtra el target antes de
 * disparar. Visualmente: cursor:pointer sutil vía CSS + highlight cuando
 * está seleccionada, con una barra vertical en el borde izquierdo. */
.member-list-item {
    position: relative;
    transition: background-color 0.1s ease;
}
/* Centrado vertical garantizado de TODA celda de la fila: sin esto quedan en la
   línea base (default), y en las filas que la altura la fija el avatar de 44px de
   la columna Socio el resto (badges, edad, íconos de acción) queda descentrado.
   En mobile la tabla pasa a cards flex (responsive.css) y esto es inocuo. */
.member-list-item > td {
    vertical-align: middle;
}
/* Badges un punto más compactos SOLO en la lista, para que entren (con holgura para
   centrar) en sus columnas de ancho fijo sin depender de pantallas anchas. Junto con
   el min-width de la tabla (+ overflow-x del wrapper), garantiza que el badge más
   ancho ("Por vencer -3d") nunca desborde su columna: en pantallas angostas la tabla
   scrollea en vez de amontonar. Scoped a .member-list-item → perfil/detalle intactos. */
.member-list-item .md-status-badge,
.member-list-item .chip,
.member-list-item .category-badge {
    padding: 0.3rem 0.5rem;
    font-size: 0.78rem;
}
.member-list-item .md-status-badge .material-symbols-outlined {
    font-size: 13px;
}
.member-list-item--selected {
    background-color: color-mix(in srgb, var(--color-primary) 8%, transparent);
}
.dark .member-list-item--selected,
[data-theme='dark'] .member-list-item--selected {
    background-color: color-mix(in srgb, var(--color-primary) 14%, transparent);
}
/* Barra vertical de 3px sobre el borde izquierdo indicando "seleccionada".
 * Va con box-shadow inset para no pelearse con el padding de las <td>. */
.member-list-item--selected > td:first-child {
    box-shadow: inset 3px 0 0 0 var(--color-primary);
}
/* Link "Seleccionar todos los visibles" dentro de la bulk bar. */
.bulk-actions-bar__select-all {
    background: transparent;
    border: 0;
    color: var(--color-primary);
    font-size: 0.8rem;
    font-weight: 600;
    cursor: pointer;
    padding: 0 0.25rem;
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
}
.bulk-actions-bar__select-all:hover {
    filter: brightness(1.15);
}

/* Barra contextual. Slide-down cuando aparece. `.hidden` (utilitario) gana
 * gracias al selector `:not(.hidden)`: la barra solo layouta cuando no está
 * marcada como oculta. Sin el `:not()` la especificidad de .bulk-actions-bar
 * y .hidden empatan y como esta regla está declarada después, ganaba
 * display:flex — la barra decía "0 socios seleccionados" en pantalla
 * aunque el JS ya la había marcado como hidden. */
.bulk-actions-bar:not(.hidden) {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    padding: 0.625rem 1rem;
    margin-bottom: 0.75rem;
    background-color: color-mix(in srgb, var(--color-primary) 10%, #ffffff);
    border: 1px solid color-mix(in srgb, var(--color-primary) 35%, transparent);
    border-radius: var(--radius-md);
    animation: bulk-bar-appear 0.18s ease-out;
    /* Sticky: se pega bajo el navbar global mientras el usuario scrollea la
     * lista. Mismo patrón que .profile-sticky-header — --navbar-height la
     * expone oarclubdata-navbar.js. z-index 30 (menor al del navbar=50).
     * Backdrop-filter suave para que las filas debajo se lean apagadas. */
    position: sticky;
    top: var(--nav-stick-top);
    z-index: 30;
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    box-shadow: 0 4px 12px -6px rgba(0, 0, 0, 0.15);
}
.dark .bulk-actions-bar:not(.hidden),
[data-theme='dark'] .bulk-actions-bar:not(.hidden) {
    background-color: color-mix(in srgb, var(--color-primary) 16%, #1a1a1a);
    border-color: color-mix(in srgb, var(--color-primary) 40%, #333333);
}
@keyframes bulk-bar-appear {
    from { opacity: 0; transform: translateY(-6px); }
    to   { opacity: 1; transform: translateY(0); }
}
.bulk-actions-bar__info {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--color-text-main);
    flex-shrink: 0;
}
.bulk-actions-bar__info .material-symbols-outlined {
    color: var(--color-primary);
    font-size: 20px;
}
.bulk-actions-bar__actions {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}
.bulk-actions-bar__divider {
    width: 1px;
    height: 24px;
    background-color: var(--color-border);
    margin: 0 0.25rem;
    flex-shrink: 0;
}
/* btn-sm compacto para la barra. Si ya existe .btn-sm en el proyecto la
 * variante convive; si no, este bloque le da tamaño razonable. */
.bulk-actions-bar .btn.btn-sm {
    padding: 0.375rem 0.75rem;
    font-size: 0.8rem;
}
.bulk-actions-bar .btn-icon.btn-sm,
.bulk-actions-bar .btn-icon {
    padding: 0.375rem;
}

/* Modal de confirmación para cambio de tipo de membresía. Reemplaza al
 * window.confirm nativo (que era una popup del sistema, ajena al tema).
 * Superficie theme-aware; los botones son .btn-primary/.btn-secondary del
 * sistema — heredan el color primary del tema (rojo en light, oro en dark). */
.membership-confirm-overlay {
    position: fixed;
    inset: 0;
    z-index: 9999;
    background: rgba(0, 0, 0, 0.55);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    animation: membership-confirm-fade 0.15s ease;
}
@keyframes membership-confirm-fade {
    from { opacity: 0; }
    to { opacity: 1; }
}
@keyframes membership-confirm-pop {
    from { opacity: 0; transform: translateY(8px) scale(0.98); }
    to { opacity: 1; transform: translateY(0) scale(1); }
}
.membership-confirm-dialog {
    width: 100%;
    max-width: 440px;
    background-color: #ffffff;
    color: var(--color-text-main);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg, 16px);
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.35);
    padding: 1.5rem;
    animation: membership-confirm-pop 0.18s ease-out;
}
.dark .membership-confirm-dialog,
[data-theme='dark'] .membership-confirm-dialog {
    background-color: #1a1a1a;
    border-color: #2f2f2f;
    color: #f5f5f5;
    box-shadow: 0 20px 60px rgba(0, 0, 0, 0.6);
}
.membership-confirm-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-bottom: 0.75rem;
}
.membership-confirm-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: 12px;
    background-color: color-mix(in srgb, var(--color-primary) 15%, transparent);
    color: var(--color-primary);
    flex-shrink: 0;
}
.membership-confirm-icon .material-symbols-outlined {
    font-size: 22px;
}
.membership-confirm-title {
    margin: 0;
    font-family: var(--font-main);
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1.25;
}
.membership-confirm-body {
    margin: 0 0 1.25rem;
    font-size: 0.875rem;
    line-height: 1.55;
    color: var(--color-text-secondary, #6b7280);
}
.dark .membership-confirm-body,
[data-theme='dark'] .membership-confirm-body {
    color: #b3b3b3;
}
.membership-confirm-body strong {
    color: var(--color-text-main);
    font-weight: 600;
}
.dark .membership-confirm-body strong,
[data-theme='dark'] .membership-confirm-body strong {
    color: #ffffff;
}
.membership-confirm-actions {
    display: flex;
    justify-content: center;
    gap: 0.5rem;
    flex-wrap: wrap;
}
/* Variante para modales con múltiples opciones. Los primarios se reparten
 * en fila (flex:1 con min-width 140px); Cancelar toma la fila completa
 * debajo. El sizing de los .btn ya lo cubre la regla global de modales. */
.membership-confirm-actions--stacked {
    display: flex;
    flex-wrap: wrap;
    gap: 0.5rem;
}
.membership-confirm-actions--stacked .btn {
    justify-content: center;
    flex: 1 1 140px;
}
.membership-confirm-actions--stacked .btn-secondary {
    flex: 1 1 100%;
}

/* Toggle segmentado de tipo de membresía (Plantel / Escuela). Vive en el
 * identity bar del formulario de socio. Un solo control, dos opciones
 * visibles la activa va en amarillo (marca), la inactiva en superficie
 * neutra pero legible en ambos temas. */
.membership-toggle {
    display: inline-flex;
    align-items: stretch;
    border-radius: var(--radius-md);
    background-color: #ececec;
    border: 1px solid var(--color-border);
    padding: 2px;
    gap: 2px;
    flex-shrink: 0;
}
.dark .membership-toggle,
[data-theme='dark'] .membership-toggle {
    background-color: #1e1e1e;
    border-color: #333333;
}
.membership-toggle__option {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    padding: 0.375rem 0.75rem;
    border: 0;
    background: transparent;
    color: var(--color-text-secondary, #6b7280);
    font-family: var(--font-main);
    font-size: 0.75rem;
    font-weight: 600;
    border-radius: calc(var(--radius-md) - 2px);
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
    white-space: nowrap;
}
.dark .membership-toggle__option,
[data-theme='dark'] .membership-toggle__option {
    color: #9ca3af;
}
.membership-toggle__option:hover:not([aria-selected='true']) {
    color: var(--color-text-main);
    background-color: rgba(0, 0, 0, 0.04);
}
.dark .membership-toggle__option:hover:not([aria-selected='true']),
[data-theme='dark'] .membership-toggle__option:hover:not([aria-selected='true']) {
    color: #ffffff;
    background-color: rgba(255, 255, 255, 0.06);
}
.membership-toggle__option[aria-selected='true'] {
    background-color: var(--color-primary);
    /* Mismo patrón que .btn-primary: default blanco (para light con primary rojo),
     * oscuro en dark (donde primary es amarillo y el texto oscuro contrasta). */
    color: var(--color-btn-text, #ffffff);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}
.dark .membership-toggle__option[aria-selected='true'],
[data-theme='dark'] .membership-toggle__option[aria-selected='true'] {
    color: #0a0a0a;
}
.membership-toggle__option:focus-visible {
    outline: 2px solid var(--color-brand);
    outline-offset: 1px;
}

/* Split-button: acción principal + trigger de menú viven en un solo elemento
 * visual. Un solo box-shadow/ring exterior (heredado de .btn-primary vía el
 * wrapper) y un divisor interno de 1px separa las dos zonas clickeables.
 *
 * Regla: el wrapper NO recibe .btn-primary (para no doblar el ring); en su
 * lugar replica el estilo primary con las mismas variables. Los <button>
 * hijos son transparentes con su propio hover-fill sutil. */
.btn-split {
    display: inline-flex;
    align-items: stretch;
    border-radius: var(--radius-md);
    background-color: var(--color-primary);
    color: var(--color-btn-text, #ffffff);
    font-family: var(--font-main);
    font-size: 0.875rem;
    font-weight: 700;
    line-height: 1;
    overflow: hidden;
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.45),
        0 0 0 1px rgba(255, 255, 255, 0.18),
        0 4px 12px -2px rgba(0, 0, 0, 0.5),
        0 2px 4px -1px rgba(0, 0, 0, 0.35);
    transition: box-shadow 0.15s ease, transform 0.08s ease;
}
.dark .btn-split,
[data-theme='dark'] .btn-split {
    color: #0a0a0a;
}
.btn-split:hover {
    box-shadow:
        0 0 0 2px rgba(255, 255, 255, 0.35),
        0 8px 20px -2px rgba(0, 0, 0, 0.5),
        0 4px 8px -2px rgba(0, 0, 0, 0.35);
}
.btn-split:active {
    transform: scale(0.98);
}
.btn-split > button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    background: transparent;
    color: inherit;
    border: 0;
    padding: 0.625rem 1.25rem;
    font: inherit;
    cursor: pointer;
    user-select: none;
    -webkit-tap-highlight-color: transparent;
    transition: background-color 0.15s ease;
}
.btn-split > button:hover:not(:disabled) {
    background-color: rgba(0, 0, 0, 0.08);
}
.btn-split > button:focus-visible {
    outline: 2px solid #ffffff;
    outline-offset: -3px;
    border-radius: 2px;
}
/* Divisor interno: 1px oscuro para separar visualmente las zonas sin partir
 * el botón. Sobre el amarillo funciona un tinte negro semi-opaco. */
.btn-split > .btn-split-divider {
    width: 1px;
    background-color: rgba(0, 0, 0, 0.22);
    align-self: stretch;
    flex: 0 0 auto;
}
/* Chevron: menos padding lateral que la acción principal (es un afford, no
 * un CTA con texto). */
.btn-split > .btn-split-chevron {
    padding-left: 0.5rem;
    padding-right: 0.5rem;
}

/* Secondary superficie sólida propia (nunca `transparent`).
 *
 * ¿Por qué NO `transparent`? En cualquier modal el shell es casi negro
 * (`dark:bg-surface-dark` = #141414). Un botón `background: transparent`
 * hereda esa negrura y visualmente se lee "botón negro" aunque el
 * componente esté correcto. Le damos una superficie propia con Δ
 * suficiente para despegarse del shell tanto en base como en hover.
 *
 * Colores plenos (no rgba/color-mix/alpha) para eliminar cualquier
 * dependencia del fondo del contenedor. */
:is(.btn-secondary, .btn-outline) {
    background-color: #f5f5f5;
    color: var(--color-text-main);
    border: 1px solid var(--color-border);
}
:is(.btn-secondary, .btn-outline):hover:not(:disabled):not([aria-disabled='true']) {
    /* Mismo efecto que .btn-primary y .btn-emerald: ring blanco alrededor
     * + shadow lifted. `background-color` NO se toca el fondo del
     * botón queda idéntico entre base y hover. */
    box-shadow:
        0 0 0 2px rgba(255, 255, 255, 0.35),
        0 8px 20px -2px rgba(0, 0, 0, 0.5),
        0 4px 8px -2px rgba(0, 0, 0, 0.35);
}
.dark :is(.btn-secondary, .btn-outline),
[data-theme='dark'] :is(.btn-secondary, .btn-outline) {
    /* Base #2f2f2f: Δ ≈ 27 sobre el shell #141414 el botón se lee
     * claramente como GRIS, no como "otro negro del modal". */
    background-color: #2f2f2f;
    border-color: #4a4a4a;
    color: #f5f5f5;
}
.dark :is(.btn-secondary, .btn-outline):hover:not(:disabled):not([aria-disabled='true']),
[data-theme='dark'] :is(.btn-secondary, .btn-outline):hover:not(:disabled):not([aria-disabled='true']) {
    /* Feedback en dark mode: sólo borde reforzado. El ring y el shadow
     * lift vienen del selector base de arriba (heredado en dark).
     * `background-color` NO cambia se mantiene la superficie #2f2f2f
     * de la base para que el fondo no se oscurezca ni se aclare. */
    border-color: #6a6a6a;
}
:is(.btn-secondary, .btn-outline):active:not(:disabled):not([aria-disabled='true']) {
    /* Press feedback via encoger, no via brightness nunca oscurece. */
    transform: scale(0.98);
}

/* .btn-flat modificador: botón SIN elevación. Combina con cualquier variante
 * (.btn-secondary, .btn-primary, …) y le quita el "efecto" de interacción del
 * sistema — el ring blanco + sombra levantada del hover y el encoger del
 * active. Conserva relleno, borde y cambios de color/borde de la variante base
 * (p.ej. el borde reforzado del hover en dark). Pensado para controles
 * utilitarios dentro de formularios (agregar/quitar fila) que no deben competir
 * con el CTA principal. Va DESPUÉS de las variantes para ganar por orden de
 * cascada con la misma especificidad — sin !important. */
.btn-flat:hover:not(:disabled):not([aria-disabled='true']),
.btn-flat:active:not(:disabled):not([aria-disabled='true']) {
    box-shadow: none;
    transform: none;
}

/* =========================================================================
   COMPAT: `inset` shorthand → top/right/bottom/left long-form
   -------------------------------------------------------------------------
   El shorthand CSS `inset` (equivalente a top/right/bottom/left) sólo llegó
   a Chromium 87 (sep 2020) / Firefox 66 (mar 2019) / Safari 14.1 (abr 2021).
   En navegadores anteriores — en particular versiones viejas de Opera que
   siguen instaladas — la property se ignora silenciosamente. Un `position:
   fixed` con `inset:0` PERO sin las 4 propiedades explícitas queda 0×0 y el
   elemento existe en el DOM pero es INVISIBLE — sin error de consola.
   .inset-0 es la clase Tailwind usada por todos los modales del sistema
   (oarclubdata-modal, req-detail-modal, etc.). Duplicando las 4 properties
   aquí, cualquiera de esas modales funciona también en Opera viejo. En un
   navegador moderno, `inset:0` de Tailwind y estas 4 líneas duplican el
   efecto — sin costo visual. Este archivo carga DESPUÉS de tailwind.css.
   ========================================================================= */
.inset-0 {
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
}

/* =========================================================================
   VALIDACIÓN INLINE POR CAMPO (.field-err)
   -------------------------------------------------------------------------
   Fuente ÚNICA del error inline compartida por el formulario público de
   registro y el perfil de socio. Un `.field` marcado `.invalid` muestra su
   `.field-err` (texto en el color de marca: rojo en claro, amarillo en oscuro
   vía --color-primary) y resalta el borde del control. La lógica de cuándo
   marcar `.invalid` vive en js/shared/date-validation.js.
   ========================================================================= */
.field-err {
    display: none;
    color: var(--color-primary);
    font-size: 12px;
    margin-top: 5px;
}
.field.invalid .field-err {
    display: block;
}
.field.invalid input,
.field.invalid select,
.field.invalid textarea {
    border-color: var(--color-primary);
}

/* .btn-submit alias legacy: aplica geometría base del sistema. */
.btn-submit {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.625rem 1.25rem;
    border-radius: var(--radius-md);
    font-family: var(--font-main);
    font-weight: 700;
    cursor: pointer;
}

/* Modificadores de tamaño se combinan con cualquier variante:
 * <button class="btn btn-primary btn-sm">Agregar</button> */
.btn-sm {
    padding: 0.375rem 0.75rem;
    font-size: 0.8125rem;
    gap: 0.375rem;
}
.btn-lg {
    padding: 0.75rem 1.5rem;
    font-size: 1rem;
    gap: 0.625rem;
}
/* .btn-tight padding mínimo para triggers compactos (avatar de
 * navbar, etc.) donde el contenido ya define su propia altura. */
.btn-tight {
    padding: 0.25rem;
}

/* Tamaño de iconos de Material Symbols dentro de otros componentes * reemplaza los `style="font-size:XXpx"` inline que quedaban en JS.
 * Escala coherente con `.btn-sm`, `.btn`, `.btn-lg`. */
.icon-xs { font-size: 14px; }
.icon-2xs { font-size: 10px; }
.icon-sm { font-size: 16px; }
.icon-md { font-size: 18px; }
.icon-lg { font-size: 20px; }
.icon-xl { font-size: 24px; }

/* Botón flotante circular (FAB) del speed-dial y "back to top". Antes
 * usaba `style="width:3.5rem;height:3.5rem"` inline en HTML. */
.fab {
    width: 3.5rem;
    height: 3.5rem;
    border-radius: 9999px;
    border: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    box-shadow: var(--shadow-lg);
    transition: box-shadow 0.2s ease, filter 0.15s ease, transform 0.15s ease;
}
.fab:hover:not(:disabled):not([aria-disabled='true']) {
    filter: brightness(1.1);
    box-shadow: 0 8px 20px -2px rgba(0, 0, 0, 0.4), 0 0 0 2px rgba(255, 255, 255, 0.2);
    transform: scale(1.05);
}
.fab:active:not(:disabled):not([aria-disabled='true']) { transform: scale(0.98); }

/* Alineación del contenido (por defecto centrado). `btn-align-start`
 * lo pega a la izquierda útil en pickers/listas donde el texto guía
 * la lectura (ej: "JSON (backup)", "CSV"). */
.btn-align-start {
    justify-content: flex-start;
    text-align: left;
}

/* .btn-icon botón cuadrado sin texto, para controles de icono
 * (limpiar filtro, editar, borrar, etc.). Se combina con variantes de
 * color: `.btn .btn-secondary .btn-icon`, `.btn .btn-ghost .btn-icon`.
 * Sizes: default 2.5rem, `.btn-icon.btn-sm` 2rem, `.btn-icon.btn-lg` 3rem. */
.btn-icon {
    width: 2.5rem;
    height: 2.5rem;
    padding: 0;
    gap: 0;
    flex-shrink: 0;
}
.btn-icon.btn-sm {
    width: 2rem;
    height: 2rem;
}
.btn-icon.btn-lg {
    width: 3rem;
    height: 3rem;
}

/* .btn-danger variante destructiva.
 * En todas sus formas (con o sin .btn-icon) tiene fondo rojo permanente
 * en reposo para que sea inequívocamente destructivo. Antes .btn-danger
 * sin .btn-icon solo tenía estilos de hover (transparente en reposo) —
 * un "Eliminar definitivamente" se veía como texto suelto sin fondo,
 * indistinguible de un botón de cancelar.
 * En estado :disabled el color base se atenúa vía opacity (utilidad
 * genérica de .btn) — no reescribimos background para que el usuario
 * siga viendo la intención destructiva incluso cuando está inactivo. */
.btn-danger {
    /* Deriva de la ÚNICA fuente de verdad de estado-danger (--badge-danger-*),
     * igual que .text-danger / chips / status-debt. Antes hardcodeaba #b91c1c/
     * #fff, duplicando el color fuera del token. */
    background-color: var(--badge-danger-bg);
    border-color: var(--badge-danger-bg);
    color: var(--badge-danger-fg);
}
.btn-danger:hover:not(:disabled):not([aria-disabled='true']) {
    /* Oscurecemos vía filter en vez de un segundo hex hardcodeado: el color
     * base sigue saliendo del token. */
    filter: brightness(0.9);
}
.btn-danger:disabled,
.btn-danger[aria-disabled='true'] {
    opacity: 0.5;
    cursor: not-allowed;
}

/* Tablas de datos */
:is(table, .data-table) {
    color: var(--color-text-main);
}
:is(th, .data-table th) {
    color: var(--color-text-muted);
    border-bottom: 2px solid var(--color-border);
    font-family: var(--font-heading);
    letter-spacing: 0.05em;
}
:is(td, .data-table td) {
    border-bottom: 1px solid var(--color-border);
}
tr:hover td {
    background-color: var(--color-surface-hover);
}

/* Theme Toggle Button */
.theme-toggle {
    background: none;
    border: 1px solid var(--color-border);
    border-radius: 9999px;
    padding: 8px;
    cursor: pointer;
    color: var(--color-text-muted);
    display: flex;
    align-items: center;
    justify-content: center;
    transition: all 0.2s ease;
    margin-left: 15px;
}
.theme-toggle:hover {
    background-color: var(--color-surface-hover);
    color: var(--color-brand);
}

/* Scrollbars */
::-webkit-scrollbar-track {
    background: var(--color-bg);
}
::-webkit-scrollbar-thumb {
    background: var(--color-border);
    border: 2px solid var(--color-bg);
}
::-webkit-scrollbar-thumb:hover {
    background: var(--color-brand);
}

/* Dark mode primary text */
.dark .bg-primary {
    color: rgb(var(--tw-color-primary-text));
}

/* =========================================
   GLOBAL UTILITIES
   Migrated from style.css (which no page loaded)
   ========================================= */

/* Accessibility */
:focus-visible {
    outline: 2px solid var(--color-brand);
    outline-offset: 3px;
    border-radius: 4px;
}
:focus:not(:focus-visible) {
    outline: none;
}

[aria-disabled='true'],
button:disabled,
input:disabled,
select:disabled {
    opacity: 0.5;
    cursor: not-allowed;
    pointer-events: none;
}

/* =========================================================================
   VARIANTES DE BOTÓN extensiones del sistema `.btn` definido arriba
   -------------------------------------------------------------------------
   `.btn-primary` y `.btn-secondary` viven junto a `.btn` en la sección
   principal del sistema de componentes. Aquí van sólo las variantes de
   color extra (emerald, ghost) que no encajan en primary/secondary.
   ========================================================================= */

/* Emerald acción de confirmación positiva (Exportar XLSX/JSON, Guardar
 * en formularios financieros, Confirmar). Color diferenciado de la marca
 * para señalar "confirmar/completar" cuando conviven con `.btn-primary`.
 * Misma regla que .btn-primary: hover = brighten, nunca darken. */
.btn-emerald {
    background-color: #10b981;
    color: #ffffff;
    border-color: transparent;
    box-shadow: var(--shadow-sm);
    transition: background-color 0.15s ease, filter 0.15s ease, box-shadow 0.15s ease;
}
.btn-emerald:hover:not(:disabled):not([aria-disabled='true']) {
    /* Mismo criterio que .btn-primary: hover NO toca background-color
     * ni filter. Sólo ring + elevación. */
    box-shadow:
        0 0 0 2px rgba(255, 255, 255, 0.35),
        0 8px 20px -2px rgba(0, 0, 0, 0.5),
        0 4px 8px -2px rgba(0, 0, 0, 0.35);
}
.btn-emerald:active:not(:disabled):not([aria-disabled='true']) { transform: scale(0.98); }

/* Ghost transparente sin borde, overlay sutil al hover. Ideal para
 * acciones terciarias (Cancelar en pickers pequeños, "Volver", etc.). */
.btn-ghost {
    background: transparent;
    border-color: transparent;
    color: var(--color-text-main);
    transition: background 0.15s ease;
}
.btn-ghost:hover:not(:disabled):not([aria-disabled='true']) { background: var(--color-surface-hover, rgba(0, 0, 0, 0.05)); }
.dark .btn-ghost:hover:not(:disabled):not([aria-disabled='true']),
[data-theme='dark'] .btn-ghost:hover:not(:disabled):not([aria-disabled='true']) { background: rgba(255, 255, 255, 0.05); }

/* =========================================================================
   COMPONENTES INTERACTIVOS NO-BOTÓN
   -------------------------------------------------------------------------
   Mismo criterio que `.btn-*`: colores sólidos, feedback nítido, sin
   depender de `hover:bg-gray-*` sueltos ni utilidades transparentes.
   ========================================================================= */

/* .menu-item item clickeable dentro de un dropdown / menú desplegable
 * (cambiar de club, cambiar contraseña, seleccionar formato, etc.).
 * Sin borde, con padding cómodo, hover con superficie sólida. */
.menu-item {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    width: 100%;
    padding: 0.625rem 0.875rem;
    font-family: var(--font-main);
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--color-text-main);
    background: transparent;
    border: none;
    border-radius: var(--radius-md);
    cursor: pointer;
    text-align: left;
    transition: background-color 0.12s ease, color 0.12s ease;
}
.menu-item:hover:not(:disabled):not([aria-disabled='true']),
.menu-item:focus-visible {
    background-color: #f0f0f0;
    color: var(--color-text-main);
    outline: none;
}
.dark .menu-item:hover:not(:disabled):not([aria-disabled='true']),
.dark .menu-item:focus-visible,
[data-theme='dark'] .menu-item:hover:not(:disabled):not([aria-disabled='true']),
[data-theme='dark'] .menu-item:focus-visible {
    background-color: #2a2a2a;
    color: #ffffff;
}
.menu-item:disabled,
.menu-item[aria-disabled='true'] {
    opacity: 0.5;
    cursor: not-allowed;
}
.menu-item.is-danger { color: var(--badge-danger-bg); }
.dark .menu-item.is-danger,
[data-theme='dark'] .menu-item.is-danger { color: #ef4444; }
/* .is-info para acciones informativas / superadmin (ej: "volver a
 * impersonar"), en violeta para no confundir con acciones destructivas. */
.menu-item.is-info { color: #9333ea; }
.dark .menu-item.is-info,
[data-theme='dark'] .menu-item.is-info { color: #a855f7; }
/* .is-strong item con peso "semibold" para marcar el default u opción
 * destacada dentro del menú (ej: "Todos" al comienzo de un filtro). */
.menu-item.is-strong { font-weight: 600; }
/* .has-arrow item con submenu: separa el label del arrow con
 * `space-between` para que el icono termine pegado a la derecha. */
.menu-item.has-arrow { justify-content: space-between; }
/* .is-selected item marcado como selección actual (default en filtro,
 * país actual en phone picker, etc.). Reemplaza el patrón anterior de
 * `bg-primary/10 dark:bg-primary/20` inline con alpha. */
.menu-item.is-selected {
    background-color: color-mix(in srgb, var(--color-primary) 12%, transparent);
    font-weight: 600;
}
.dark .menu-item.is-selected,
[data-theme='dark'] .menu-item.is-selected {
    background-color: color-mix(in srgb, var(--color-primary) 20%, transparent);
    color: var(--color-primary);
}

/* .row-hover filas de tabla, filas de lista o cards que reaccionan al
 * cursor (drill-down, selección, expandir). Cambia sólo el fondo el
 * layout (borders, grid, flex) lo pone el HTML. Sólido en ambos temas. */
.row-hover {
    transition: background-color 0.12s ease;
}
.row-hover:hover {
    background-color: #f5f5f5;
    cursor: pointer;
}
.dark .row-hover:hover,
[data-theme='dark'] .row-hover:hover {
    background-color: #1f1f1f;
}
.row-hover.is-selected {
    background-color: #ececec;
}
.dark .row-hover.is-selected,
[data-theme='dark'] .row-hover.is-selected {
    background-color: #262626;
}

/* .card-hover tarjeta clickeable con hover más marcado (bg + borde). */
.card-hover {
    transition: background-color 0.15s ease, border-color 0.15s ease, transform 0.15s ease;
    cursor: pointer;
}
.card-hover:hover {
    background-color: #f5f5f5;
    border-color: color-mix(in srgb, var(--color-border) 60%, var(--color-text-main));
}
.dark .card-hover:hover,
[data-theme='dark'] .card-hover:hover {
    background-color: #1f1f1f;
    border-color: #3a3a3a;
}

/* .radio-chip label envolvente de un radio dentro de un segmented
 * control (Permanente / Meses / Fecha en plan-config, Exento / Precio,
 * etc.). Padding compacto, hover con superficie sólida. */
.radio-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.25rem 0.375rem;
    border-radius: 0.375rem;
    cursor: pointer;
    transition: background-color 0.12s ease;
}
.radio-chip:hover { background-color: #f0f0f0; }
.dark .radio-chip:hover,
[data-theme='dark'] .radio-chip:hover { background-color: #2a2a2a; }

/* .th-sortable `<th>` clickeable para ordenar columnas de tabla.
 * Su fondo base viene del thead el hover añade una superficie sólida
 * suficiente para diferenciarse sin depender de alpha sobre el thead. */
.th-sortable {
    cursor: pointer;
    user-select: none;
    transition: background-color 0.12s ease;
}
.th-sortable:hover { background-color: #ececec; }
.dark .th-sortable:hover,
[data-theme='dark'] .th-sortable:hover { background-color: #262626; }

/* Drop zone (import de archivos): borde punteado que cambia a color primario
 * al hover. Antes cada zona repetía el mismo `onmouseover` inline. */
.oarclubdata-dropzone {
    border: 2px dashed var(--border-color, var(--color-border, #d1d5db));
    border-radius: 0.75rem;
    transition: border-color 0.2s ease;
    cursor: pointer;
}
.oarclubdata-dropzone:hover { border-color: var(--color-primary, #dd0000); }

.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border-width: 0;
}

/* Skeleton Loaders */
@keyframes skeleton-shimmer {
    0% {
        background-position: -200% 0;
    }
    100% {
        background-position: 200% 0;
    }
}
.skeleton {
    background: linear-gradient(
        90deg,
        rgba(229, 229, 229, 0.8) 25%,
        rgba(245, 245, 245, 0.9) 50%,
        rgba(229, 229, 229, 0.8) 75%
    );
    background-size: 200% 100%;
    animation: skeleton-shimmer 1.5s ease-in-out infinite;
    border-radius: 0.5rem;
}
.dark .skeleton {
    background: linear-gradient(90deg, rgba(20, 20, 20, 0.8) 25%, rgba(28, 28, 28, 0.9) 50%, rgba(20, 20, 20, 0.8) 75%);
    background-size: 200% 100%;
    animation: skeleton-shimmer 1.5s ease-in-out infinite;
}
.skeleton-text {
    height: 1rem;
    width: 70%;
    border-radius: 0.25rem;
    margin-bottom: 0.5rem;
}
.skeleton-title {
    height: 1.5rem;
    width: 40%;
    border-radius: 0.375rem;
    margin-bottom: 1rem;
}
.skeleton-avatar {
    height: 3rem;
    width: 3rem;
    border-radius: 9999px;
}
.skeleton-badge {
    height: 1.25rem;
    width: 5rem;
    border-radius: 9999px;
}
.skeleton-chart {
    height: 12rem;
    border-radius: 0.75rem;
}
.skeleton-btn {
    height: 2.5rem;
    width: 8rem;
    border-radius: 0.5rem;
}
.skeleton-row {
    height: 3.5rem;
    border-radius: 0.5rem;
    margin-bottom: 0.5rem;
}

/* Status Badges */
.status-badge {
    display: inline-block;
    padding: 4px 12px;
    border-radius: 20px;
    font-size: 0.8rem;
    font-weight: 600;
    text-align: center;
}
.status-badge.status-active { background-color: var(--badge-success-bg); color: var(--badge-success-fg); border: 1px solid var(--badge-success-bg); }
.status-badge.status-inactive { background-color: var(--badge-neutral-bg); color: var(--badge-neutral-fg); border: 1px solid var(--badge-neutral-bg); }
.status-badge.status-completed { background-color: var(--badge-muted-bg); color: var(--badge-muted-fg); border: 1px solid var(--badge-muted-bg); }

/* ── UTILITY CHIP CLASSES ──
   For inline HTML use (e.g. count pills, quick labels). Reference badge tokens.
   Fuera de @layer para que tengan prioridad normal en la cascada. */
.chip {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    padding: 0.25rem 0.625rem;
    border-radius: 9999px;
    font-size: 0.75rem;
    font-weight: 700;
    line-height: 1.2;
    white-space: nowrap;
    border: 1px solid transparent;
}
/* .chip está fuera de @layer utilities y sobreescribiría el `.hidden`
   (display:none) de Tailwind. Reglas defensivas para que hidden gane. */
.chip.hidden, .chip[hidden] { display: none; }
.chip-success { background: var(--badge-success-bg); color: var(--badge-success-fg); border-color: var(--badge-success-bg); }
.chip-danger { background: var(--badge-danger-bg); color: var(--badge-danger-fg); border-color: var(--badge-danger-bg); }
.chip-warning { background: var(--badge-warning-bg); color: var(--badge-warning-fg); border-color: var(--badge-warning-bg); }
.chip-info { background: var(--badge-info-bg); color: var(--badge-info-fg); border-color: var(--badge-info-bg); }
.chip-primary { background: var(--badge-primary-bg); color: var(--badge-primary-fg); border-color: var(--badge-primary-bg); }
.chip-accent { background: var(--badge-accent-bg); color: var(--badge-accent-fg); border-color: var(--badge-accent-bg); }
.chip-neutral { background: var(--badge-neutral-bg); color: var(--badge-neutral-fg); border-color: var(--badge-neutral-bg); }
.chip-muted { background: var(--badge-muted-bg); color: var(--badge-muted-fg); border-color: var(--badge-muted-bg); }
.chip-titular { background: var(--badge-titular-bg); color: var(--badge-titular-fg); border-color: var(--badge-titular-bg); }
.chip-brand { background: var(--badge-brand-bg); color: var(--badge-brand-fg); border-color: var(--badge-brand-bg); }

/* Badges ACCIONABLES de la lista de socios (Huellas → enrolar huella, Tarjetas →
   registrar tarjeta, Estado → cambiar estado). Afordancia de BOTÓN al pasar el
   cursor: elevación + sombra + brillo. Se aplica SOLO a roles que pueden
   accionarlos (member.list.write); el entrenador —que no puede— no recibe el
   hover, para no prometer una acción que su rol no tiene. La lógica del gating
   ya vive en los handlers; esto solo espeja la afordancia visual. */
.members-fp-tooltip,
.members-card-tooltip,
.members-status-tooltip {
    transition: transform 0.12s ease, box-shadow 0.12s ease, filter 0.12s ease;
}
:root:not([data-club-role='entrenador']) .members-fp-tooltip:hover,
:root:not([data-club-role='entrenador']) .members-card-tooltip:hover,
:root:not([data-club-role='entrenador']) .members-status-tooltip:hover {
    transform: translateY(-1px);
    box-shadow: 0 3px 10px rgba(0, 0, 0, 0.22);
    filter: brightness(1.06);
}

/* ─── Nombre de marca "OarClub Data" ───────────────────────────────────────
   Un solo contrato para el nombre a lo largo del sistema. Uso:
     <span class="brand-name"><span class="brand-name-word">OarClub</span> Data</span>
   - .brand-name          -> wrapper bold, "Data" hereda color del contexto.
   - .brand-name-word     -> "OarClub" en color de marca del modo activo.
   Paginas con identidad propia (superadmin, login hero, facturas-portal,
   consentimiento print) usan variables/clases especificas por decision
   de diseno -- no migrar. */
.brand-name       { font-weight: 700; }
.brand-name-word  { color: var(--color-primary); }
/* Cuando el brand-name vive sobre un fondo del propio color primario
   (ej. header .bg-primary en subir-test.html), el word quedaba invisible
   por color-blending. Heredar el color del contexto para mantener el
   contraste (text-white / dark:text-black del propio header). */
.bg-primary .brand-name-word,
[class*="bg-primary"] .brand-name-word {
    color: currentColor;
}

/* ─── Dropdown search input sticky ────────────────────────────────────────
   Cuando un dropdown scrolleable (max-height + overflow-y:auto) tiene un
   input de busqueda arriba, el input debe quedar sticky al top asi no se
   pierde al scrollear la lista. Uso: <input class="dropdown-search">.

   Bordes propios (4 lados) + margen contra el borde del dropdown: sin esto
   el input queda "pegado" al contenedor y el override global de
   `.dark input:focus` (que pinta border-color primary) pinta solo el
   border-bottom -> se ve como una linea amarilla suelta. Se define un
   focus propio (misma specificity, declarado despues) que gana el desempate. */
.dropdown-search {
    position: sticky;
    top: 0;
    z-index: 2;
    display: block;
    width: calc(100% - 16px);
    margin: 8px;
    padding: 8px 12px;
    font-size: 0.8125rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm, 6px);
    background: var(--color-bg);
    color: var(--color-text-main);
    outline: none;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.dropdown-search:focus,
.dark .dropdown-search:focus {
    border-color: var(--color-primary);
    box-shadow: 0 0 0 3px rgb(var(--tw-color-primary) / 0.15);
}

/* Size modifier - bump padding + font size for prominent chips (dashboard counters). */
.chip-lg {
    padding: 0.3rem 0.85rem;
    font-size: 0.8rem;
}

/* Size modifier - compact chips (badges secundarios inline junto a un nombre
   propio, ej. "Titular" al lado del titular del grupo familiar). El default
   .chip pisaba los utility classes de Tailwind porque vive fuera de @layer;
   este modificador es la forma correcta de bajar el tamaño sin !important. */
.chip-xs {
    padding: 0.075rem 0.35rem;
    font-size: 0.55rem;
    font-weight: 600;
    letter-spacing: 0.04em;
}

/* Chip children (icons, small text) inherit the chip foreground so nothing
   stays illegible against the solid background. */
.chip .material-symbols-outlined,
.chip small,
.chip p { color: inherit; }

/* KPI card - single source of truth for dashboard summary tiles.
   Uses design tokens for surface, border, muted label and main value. */
.kpi-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 0.75rem;
}
.kpi-card__label {
    color: var(--color-text-muted);
}
.kpi-card__value {
    color: var(--color-text-main);
}

/* Brand icon tile - subtle red tint in light, solid dark surface in dark.
   Layout (size, radius, flex) is handled by callers via Tailwind utilities. */
.icon-tile {
    background: color-mix(in srgb, var(--color-primary) 10%, transparent);
}
.dark .icon-tile {
    background: var(--color-bg); /* #0a0a0a en dark, ex-`accent-black` */
}

/* ── BANNER (large informational panel) ──
   Same semantic system as .chip but sized for a full-width alert/notice block. */
.banner {
    padding: 1rem 1.25rem;
    border-radius: 0.75rem;
    border: 1px solid transparent;
}
.banner-success { background: var(--badge-success-bg); color: var(--badge-success-fg); border-color: var(--badge-success-bg); }
.banner-danger { background: var(--badge-danger-bg); color: var(--badge-danger-fg); border-color: var(--badge-danger-bg); }
.banner-warning { background: var(--badge-warning-bg); color: var(--badge-warning-fg); border-color: var(--badge-warning-bg); }
.banner-info { background: var(--badge-info-bg); color: var(--badge-info-fg); border-color: var(--badge-info-bg); }
.banner-primary { background: var(--badge-primary-bg); color: var(--badge-primary-fg); border-color: var(--badge-primary-bg); }
.banner-accent { background: var(--badge-accent-bg); color: var(--badge-accent-fg); border-color: var(--badge-accent-bg); }
.banner-neutral { background: var(--badge-neutral-bg); color: var(--badge-neutral-fg); border-color: var(--badge-neutral-bg); }
/* Nested elements inherit foreground; icons/paragraph subcolors stay legible. */
.banner :is(.material-symbols-outlined, p, small, span, strong, b) { color: inherit; }

/* ── SEMANTIC ICON COLOR CLASSES ──
   For standalone decorator icons that need to signal semantic without a full
   chip background (e.g. leading icons in tooltip rows, list markers). */
.icon-success { color: var(--badge-success-bg); }
.icon-danger  { color: var(--badge-danger-bg); }
.icon-warning { color: var(--color-warning-text); }
.icon-neutral { color: var(--badge-neutral-bg); }
.icon-titular { color: var(--badge-titular-bg); }

/* Panel flotante genérico para menús dropdown que no viven en la lógica
   de `wireSimpleFilterDropdown` (ej. header del perfil de socios: Planes,
   Nuevo Socio). Antes se hardcodeaba background:#1e1e1e + textos gray-300,
   quedaban negros incluso en modo claro. Ahora consumen los tokens de
   surface/border/text — un solo lugar dicta el color para light y dark. */
.dropdown-panel {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 0.5rem;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.25);
    padding: 0.25rem 0;
}
.dropdown-panel-item {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.625rem 1rem;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--color-text-main);
    text-decoration: none;
    transition: background-color 120ms ease, color 120ms ease;
}
.dropdown-panel-item:hover {
    background: var(--color-surface-hover);
    color: var(--color-text-main);
}
.dropdown-panel-sep {
    height: 1px;
    background: var(--color-border);
    margin: 0.25rem 0.75rem;
}
.icon-info    { color: var(--badge-info-bg); }
.icon-primary { color: var(--badge-primary-bg); }
.icon-accent  { color: var(--badge-accent-bg); }
.icon-muted   { color: var(--badge-muted-bg); }

/* Semantic text-color utilities (for inline body text, table cells, etc.).
   .text-primary and .text-accent are intentionally omitted - they collide
   with Tailwind's generated utilities of the same name. Use .chip-primary /
   .chip-accent for badge-neutral coloring, or Tailwind's text-primary for
   the brand accent color. */
.text-success      { color: var(--badge-success-bg); }
.text-danger       { color: var(--badge-danger-bg); }
.text-warning      { color: var(--color-warning-text); }
.text-neutral      { color: var(--badge-neutral-bg); }
.text-info         { color: var(--badge-info-bg); }
.text-muted-fg     { color: var(--color-text-muted); }
.text-body         { color: var(--color-text-main); }

/* Marcador de campo obligatorio (el «*» de los labels). Usa el color de marca,
   que por las reglas de modo es rojo en claro (--color-brand #dd0000) y amarillo
   en oscuro (#facc15) — mismo criterio que el asterisco de facturación
   (.inv-field.required > label::after). Clase semántica propia para no colgar
   el significado del utility text-red-500 (que es rojo en ambos modos). */
.required-mark     { color: var(--color-brand); font-weight: 700; }

/* Ghost link-styled button that signals a destructive action. */
.btn-link-danger {
    color: var(--badge-danger-bg);
    font-size: 0.75rem;
    font-weight: 600;
    cursor: pointer;
    background: none;
    border: none;
    padding: 0;
}
.btn-link-danger:hover { text-decoration: underline; }

/* Money-value accent inside .fin-tooltip-detail-item (positive amounts). */
.fin-tooltip-detail-value--money { color: var(--badge-success-bg); font-weight: 600; }

/* ── MEMBER DETAIL - stats semantic variants (token-driven). */
.md-time-kpi--success .md-time-kpi-header,
.md-time-kpi--success .md-time-kpi-value { color: var(--badge-success-bg); }
.md-time-kpi--neutral .md-time-kpi-header,
.md-time-kpi--neutral .md-time-kpi-value { color: var(--badge-neutral-bg); }
.md-time-kpi--warning .md-time-kpi-header,
.md-time-kpi--warning .md-time-kpi-value { color: var(--badge-warning-bg); }
.md-time-kpi--water .md-time-kpi-header,
.md-time-kpi--water .md-time-kpi-value { color: var(--color-water); }

.md-time-comp-row-value--success { color: var(--badge-success-bg); }
.md-time-comp-row-value--neutral { color: var(--badge-neutral-bg); }
.md-time-comp-row-value--warning { color: var(--badge-warning-bg); }

.md-time-total-label {
    color: var(--color-text-muted);
    display: flex;
    align-items: center;
    gap: 4px;
}
.md-time-total-label .material-symbols-outlined { font-size: 12px; }

/* Titular indicator "(T)" - bold neutral flag next to name. */
.md-titular-flag {
    color: var(--badge-neutral-bg);
    font-weight: bold;
    margin-left: 4px;
}

/* Action Buttons (used in finance fixed-control) */
.action-btn {
    background: none;
    border: none;
    cursor: pointer;
    font-size: 1rem;
    color: var(--color-text-muted);
    transition: color 0.2s;
}
.edit-btn:hover {
    color: var(--color-text-main);
}
.delete-btn:hover {
    color: var(--color-brand);
}

.btn-icon.btn-edit,
.btn-icon.btn-delete {
    padding: 6px 10px;
    font-size: 0.9rem;
    border-radius: 6px;
}
.btn-icon.btn-edit:hover {
    background-color: #f5f5f5;
    color: var(--color-text-main);
}

/* Fixed Control Header (responsive) */
@media (max-width: 768px) {
    .fixed-control-header {
        display: none !important;
    }
}

/* Smooth theme transition - solo elementos interactivos (evita reflows en tablas masivas) */
body,
.card,
.sidebar,
.main-nav,
.btn,
button,
a,
input,
select,
textarea,
th,
td,
header,
footer,
nav,
.modal-content,
.toast,
.badge,
.chip,
[class*='bg-'],
[class*='surface'],
[class*='border-'] {
    transition-property: background-color, border-color, color;
    transition-duration: 150ms;
    transition-timing-function: ease;
}
.skeleton,
canvas,
[data-no-transition] {
    transition: none;
}

/* Custom Scrollbar */
::-webkit-scrollbar {
    width: 12px;
    height: 12px;
}
* {
    scrollbar-width: thin;
    scrollbar-color: var(--color-text-muted) var(--color-bg);
}

/* =========================================
   EMPTY STATE - Reusable class for empty table rows
   Replaces inline styles with across JS modules
   ========================================= */
.empty-state-cell {
    padding: 40px 20px;
    text-align: center;
    color: var(--color-text-muted);
    font-size: 0.9375rem;
}
.empty-state-cell.is-italic {
    font-style: italic;
}

/* Icon color override for forced-dark contexts (speed-dial, etc.) */
.icon-forced-dark {
    color: var(--color-brand-black, #0a0a0a);
}
.dark .icon-forced-dark {
    color: var(--color-text-main, #f5f5f5);
}

/* Disabled interactive element (replaces inline style.cursor/style.opacity) */
.is-field-locked {
    cursor: not-allowed;
    opacity: 0.7;
}

/* =========================================
   SPEED DIAL - Fixed action button positioning
   Moved from inline in finance.html
   ========================================= */
.speed-dial {
    position: fixed;
    bottom: 2rem;
    right: 2rem;
    left: auto;
    z-index: 10005;
    display: flex;
    flex-direction: column-reverse;
    align-items: flex-end;
    gap: 12px;
}

/* =========================================
   SPEED DIAL ANIMATIONS
   Staggered entry animation for speed dial options
   ========================================= */
@keyframes speedDialBackdropIn {
    from {
        opacity: 0;
    }
    to {
        opacity: 1;
    }
}

#speed-dial-backdrop {
    animation: speedDialBackdropIn 0.2s cubic-bezier(0.4, 0, 0.2, 1) both;
}

@keyframes speedDialItemIn {
    0% {
        opacity: 0;
        transform: translateY(12px) scale(0.9);
    }
    100% {
        opacity: 1;
        transform: translateY(0) scale(1);
    }
}

#speed-dial-options > div {
    opacity: 0;
    transform: translateY(12px) scale(0.9);
    will-change: transform, opacity;
}

body:has(#speed-dial) #speed-dial-options > div {
    animation: speedDialItemIn 0.28s cubic-bezier(0.34, 1.56, 0.64, 1) forwards;
}

body:has(#speed-dial) #speed-dial-options > div:nth-child(1) {
    animation-delay: 0.04s;
}

body:has(#speed-dial) #speed-dial-options > div:nth-child(2) {
    animation-delay: 0.1s;
}

/* =========================================
   AI CHAT FAB & PANEL POSITION OVERRIDES
   Avoids overlap by creating a single vertical stack in the corner
   ========================================= */
body:has(#speed-dial) .ai-chat-fab {
    right: 2rem; /* Align horizontally with the speed-dial column (32px) */
    bottom: 2rem; /* Position at the very bottom of the stack (32px) */
}
body:has(#speed-dial) .speed-dial {
    bottom: 100px; /* Stack speed-dial above the AI FAB (32px bottom + 56px height + 12px gap) */
}
body:has(#speed-dial) .ai-chat-panel {
    right: 100px; /* Aligned horizontally next to the FAB column (32px margin + 56px FAB + 12px gap) */
    bottom: 2rem; /* Position panel at the very bottom of the viewport when open (32px) */
}

/* =========================================
   FINANCE CARD COLORS - replaces .style.color in JS
   ========================================= */
.card-value-income {
    color: #10b981;
}
.card-value-expense {
    color: var(--color-brand);
}
.card-value-neutral {
    color: var(--color-text-main);
}

.card-emoji-income {
    color: rgba(16, 185, 129, 0.6);
}
.card-emoji-expense {
    color: rgb(var(--tw-color-primary) / 0.6);
}
.card-emoji-neutral {
    color: rgba(10, 10, 10, 0.6);
}
.dark .card-emoji-neutral {
    color: rgba(229, 229, 229, 0.6);
}

.card-border-neutral {
    background-color: #0a0a0a;
}
.dark .card-border-neutral {
    background-color: #e5e5e5;
}

/* ── Debt Detection Dialog (profile-groups.js) ── */
.debt-dialog-modal {
    max-width: 460px;
}
.debt-dialog-icon-bg {
    background: var(--color-state-warning-bg);
}

.debt-dialog-icon-color {
    color: var(--color-brand);
}
.debt-dialog-message {
    margin-bottom: 8px;
}
.debt-dialog-list {
    background: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: 8px;
    padding: 8px 12px;
    margin: 0 0 12px;
    max-height: 150px;
    overflow-y: auto;
}
.dark .debt-dialog-list {
    background: #1c1c1c;
    border-color: #262626;
}
.debt-dialog-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 6px 0;
    border-bottom: 1px solid var(--color-border);
}
.dark .debt-dialog-row {
    border-bottom-color: #262626;
}
.debt-dialog-name {
    font-weight: 500;
}
.debt-dialog-amount {
    color: var(--color-brand);
    font-weight: 600;
}
.debt-dialog-total-row {
    display: flex;
    justify-content: space-between;
    padding: 4px 0 12px;
    font-size: 0.85rem;
}
.debt-dialog-total-label {
    font-weight: 600;
}
.debt-dialog-total-value {
    color: var(--color-brand);
    font-weight: 700;
}
.debt-dialog-sub {
    margin-bottom: 14px;
}
.debt-dialog-actions {
    flex-direction: column;
    gap: 8px;
}
.debt-dialog-btn-full {
    width: 100%;
}
.debt-dialog-btn-icon {
    font-size: 16px;
    vertical-align: middle;
    margin-right: 4px;
}
.debt-dialog-hint {
    font-size: 0.7rem;
    color: var(--color-text-muted);
    margin-top: 10px;
    text-align: center;
}

/* ── Portal Copy Button State (invoicing.js) ── */
.portal-btn-copied {
    color: #059669;
    border-color: #10b981;
}

/* ── Attendance Status Messages (attendance.js) ── */
.att-status-success {
    color: #10b981;
}
.att-status-info {
    color: var(--color-state-info);
}
.att-status-error {
    color: var(--color-brand);
}

/* ── SIFEN Result Banner States (invoicing.js) ── */
.sifen-banner-ok {
    border: 1px solid #6ee7b7;
    background-color: #d1fae5;
}
.dark .sifen-banner-ok,
[data-theme='dark'] .sifen-banner-ok {
    border-color: #047857;
    background-color: #064e3b;
}
.sifen-banner-err {
    border: 1px solid #fca5a5;
    background-color: #fee2e2;
}
.dark .sifen-banner-err,
[data-theme='dark'] .sifen-banner-err {
    border-color: #b91c1c;
    background-color: #7f1d1d;
}
.sifen-banner-warn {
    border: 1px solid #fcd34d;
    background-color: #fef3c7;
}
.dark .sifen-banner-warn,
[data-theme='dark'] .sifen-banner-warn {
    border-color: #a16207;
    background-color: #713f12;
}
.sifen-color-ok {
    color: #10b981;
}
.sifen-color-err {
    color: var(--color-brand);
}
.sifen-color-warn {
    color: var(--color-primary);
}

/* ── Invoicing Environment Badge (invoicing.js) ── */
.inv-badge-prod {
    background: #10b981;
    color: #ffffff;
    border-color: #10b981;
}
.inv-badge-test {
    background: var(--color-state-warning-bg);
    color: var(--color-state-warning);
    border-color: var(--color-state-warning-border);
}

/* ── Invoicing Cert Status (invoicing.js) ── */
.inv-cert-loaded {
    border-color: var(--inv-success, #10b981);
}
.inv-cert-none {
    border-color: var(--inv-border, #e5e5e5);
}

/* ── Invoicing Table Button Colors (invoicing.js) ── */
.inv-btn-sifen-send {
    color: #10b981;
}
.inv-btn-sifen-query {
    color: var(--color-sifen-query);
}
.inv-btn-delete {
    color: var(--color-brand);
}

/* ── Invoicing Confirm Icon (invoicing.js) ── */
.inv-confirm-icon-default {
    color: var(--color-confirm-icon);
}

/* ── Plan Config Tab Active State (plan-config-modal.js) ── */
.plan-tab-active {
    border-color: var(--color-primary);
    color: var(--color-primary);
}

/* ── Finance Card Border States (finance.js) ── */
.card-border-income {
    background-color: #10b981;
}
.card-border-expense {
    background-color: var(--color-brand);
}

/* =========================================
   MOBILE FILTER IMPROVEMENTS
   Filters stack vertically on small screens
   ========================================= */
@media (max-width: 640px) {
    .filters-bar {
        flex-wrap: wrap;
    }
    .filters-bar > div {
        min-width: 100%;
        flex: 1 1 100%;
    }
    .filters-bar > div[style*='width: 40px'],
    .filters-bar > .flex.items-end[style*='width: 40px'] {
        min-width: auto;
        flex: 0 0 40px;
    }
}

/* =========================================
   EMPTY STATE - Enriched version for tables
   ========================================= */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    padding: 3rem 1.5rem;
    text-align: center;
    color: var(--color-text-muted);
}
.empty-state-icon {
    font-size: 3rem;
    color: var(--color-border);
    margin-bottom: 1rem;
}
.empty-state-title {
    font-family: var(--font-heading);
    font-size: 1.1rem;
    font-weight: 700;
    color: var(--color-text-main);
    margin-bottom: 0.5rem;
}
.empty-state-message {
    font-size: 0.875rem;
    color: var(--color-text-muted);
    max-width: 320px;
    line-height: 1.5;
    margin-bottom: 1rem;
}
.empty-state-action {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 16px;
    font-size: 0.85rem;
    font-weight: 600;
    border-radius: var(--radius-md);
    background-color: var(--color-brand);
    color: var(--color-btn-text, #ffffff);
    border: none;
    cursor: pointer;
    transition: all 0.2s ease;
}
.empty-state-action:hover {
    background-color: var(--color-brand-dark, var(--color-brand-dark));
    transform: translateY(-1px);
}

/* =========================================
   BREADCRUMB - Lightweight navigation aid
   ========================================= */
.breadcrumb {
    display: flex;
    align-items: center;
    gap: 6px;
    font-size: 0.8rem;
    color: var(--color-text-muted);
    margin-bottom: 0.5rem;
}
.breadcrumb a {
    color: var(--color-text-muted);
    text-decoration: none;
    transition: color 0.15s ease;
}
.breadcrumb a:hover {
    color: var(--color-brand);
}
.breadcrumb-separator {
    font-size: 0.7rem;
    color: var(--color-border);
}
.breadcrumb-current {
    color: var(--color-text-main);
    font-weight: 600;
}

/* =========================================
   KEYBOARD SHORTCUT HINTS
   ========================================= */
.kbd {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.5rem;
    height: 1.4rem;
    padding: 0 5px;
    font-family: var(--font-main);
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--color-text-muted);
    background: var(--color-surface-hover);
    border: 1px solid var(--color-border);
    border-radius: 4px;
    line-height: 1;
}

/* =========================================
   TAILWIND UTILITY COLOR STANDARDISATION
   Force standard colors on Tailwind utility classes
   ========================================= */

/* Red (Danger / Error / Debt) */
:is(.text-red-400, .text-red-500, .text-red-600, .text-red-700) {
    color: var(--status-debt);
}
:is(.bg-red-400, .bg-red-500, .bg-red-600, .bg-red-700) {
    background-color: var(--status-debt);
}
.hover\:text-red-700:hover {
    color: var(--status-debt);
}
.hover\:bg-red-50:hover {
    background-color: var(--color-state-warning-bg);
}
.dark :is(.dark\:text-red-400, .dark\:text-red-500),
[data-theme='dark'] :is(.dark\:text-red-400, .dark\:text-red-500) {
    color: var(--status-debt);
}
.dark .dark\:hover\:bg-red-900\/20:hover,
[data-theme='dark'] .dark\:hover\:bg-red-900\/20:hover {
    background-color: #7f1d1d;
}

/* Green & Emerald (Success / Paid / Active) */
:is(
    .text-green-400,
    .text-green-500,
    .text-green-600,
    .text-green-700,
    .text-green-800,
    .text-emerald-400,
    .text-emerald-500
) {
    color: var(--status-paid);
}
:is(.bg-green-400, .bg-green-500, .bg-green-600, .bg-green-700, .bg-emerald-500) {
    background-color: var(--status-paid);
}
.bg-green-100 {
    background-color: #d1fae5;
}
.dark .bg-green-100,
[data-theme='dark'] .bg-green-100 {
    background-color: #064e3b;
}
.dark :is(.dark\:text-green-400, .dark\:text-emerald-400),
[data-theme='dark'] :is(.dark\:text-green-400, .dark\:text-emerald-400) {
    color: var(--status-paid);
}

/* Yellow & Amber (Warning / Gold / Pending) */
:is(.text-yellow-600, .text-amber-500, .text-amber-600, .text-amber-700) {
    color: var(--status-category);
}
:is(.bg-yellow-500, .bg-amber-500) {
    background-color: var(--status-category);
}

/* Teal (Special Plans) */
:is(.text-teal-700, .text-teal-600) {
    color: var(--status-plan);
}
.dark :is(.dark\:text-teal-400),
[data-theme='dark'] :is(.dark\:text-teal-400) {
    color: var(--status-plan);
}

/* =========================================
   FINANCIAL TAB PREMIUM REDESIGN
   Impeccable style for Debt Hero and Month cells
   Ensures soft tinted colors and maximum visual quality
   ========================================= */

/* --- 1. HERO BANNER DEUDA (.fin-hero--debt) ---
   The banner is a solid danger surface; every descendant gets the danger
   foreground (white) by inheritance. Nested badges/icons live on a darker
   transparent overlay so they read as distinct chips on the red field. */
.fin-hero--debt {
    background: var(--badge-danger-bg);
    color: var(--badge-danger-fg);
    border: 1px solid var(--badge-danger-bg);
}
.dark .fin-hero--debt,
[data-theme='dark'] .fin-hero--debt {
    box-shadow: 0 4px 25px rgba(0, 0, 0, 0.4);
}

/* Every nested piece inherits the banner foreground automatically. */
.fin-hero--debt :is(.fin-hero__icon .material-symbols-outlined,
                    .fin-hero__balance,
                    .fin-hero__label,
                    .fin-hero__last-payment,
                    .fin-hero__amount-hint) {
    color: inherit;
}

/* Fecha y monto del último pago dentro de los banners de color: forzamos
   blanco y negrita para que resalten sobre el fondo rojo/verde (por defecto
   heredarían el color-text-main de members-profile.css, invisible en
   superficie danger/ok). */
.fin-hero--debt .fin-hero__last-payment strong,
.fin-hero--ok .fin-hero__last-payment strong,
.fin-hero--debt .fin-hero__amount-hint,
.fin-hero--ok .fin-hero__amount-hint {
    color: #ffffff;
    font-weight: 700;
}

/* Icon container: subtle inset overlay so the icon stands out on the red field. */
.fin-hero--debt .fin-hero__icon {
    background: rgba(0, 0, 0, 0.18);
}

/* Nested badges on the red banner: neutral dark chip with white text. */
.fin-hero--debt .fin-badge,
.fin-hero--debt .fin-badge--debt,
.fin-hero--debt .fin-badge--plan {
    background: var(--badge-neutral-bg);
    color: var(--badge-neutral-fg);
    border: 1px solid var(--badge-neutral-bg);
}

/* --- Mirror variant: HERO BANNER OK (.fin-hero--ok) --- */
.fin-hero--ok {
    background: var(--badge-success-bg);
    color: var(--badge-success-fg);
    border: 1px solid var(--badge-success-bg);
}
.dark .fin-hero--ok,
[data-theme='dark'] .fin-hero--ok {
    box-shadow: 0 4px 25px rgba(0, 0, 0, 0.4);
}

.fin-hero--ok :is(.fin-hero__icon .material-symbols-outlined,
                  .fin-hero__balance,
                  .fin-hero__label,
                  .fin-hero__last-payment,
                  .fin-hero__amount-hint) {
    color: inherit;
}

.fin-hero--ok .fin-hero__icon {
    background: rgba(0, 0, 0, 0.18);
}

.fin-hero--ok .fin-badge,
.fin-hero--ok .fin-badge--ok,
.fin-hero--ok .fin-badge--plan {
    background: var(--badge-neutral-bg);
    color: var(--badge-neutral-fg);
    border: 1px solid var(--badge-neutral-bg);
}

/* Standalone .fin-badge variants (outside the hero) reference tokens. */
.fin-badge--ok   { background: var(--badge-success-bg); color: var(--badge-success-fg); }
.fin-badge--debt { background: var(--badge-danger-bg);  color: var(--badge-danger-fg); }
.fin-badge--plan { background: var(--badge-neutral-bg); color: var(--badge-neutral-fg); }

/* --- 2. CELDAS MENSUALES PENDIENTES (.fin-month-cell--unpaid) ---
   Same solid-surface + inheritance model as .fin-hero--debt: red field,
   every text/icon/amount inside inherits white foreground. */
.fin-month-cell--unpaid {
    background: var(--badge-danger-bg);
    color: var(--badge-danger-fg);
    border: 1px solid var(--badge-danger-bg);
    transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

.fin-month-cell--unpaid :is(.fin-month-cell__name,
                            .fin-month-cell__icon,
                            .fin-month-cell__amount,
                            .fin-month-cell__plan,
                            .material-symbols-outlined) {
    color: inherit;
}

/* Pay button inside the red field: neutral dark chip for contrast. */
.fin-month-cell--unpaid .fin-month-cell__pay-btn {
    background: var(--badge-neutral-bg);
    color: var(--badge-neutral-fg);
}

.fin-month-cell--unpaid:hover {
    transform: translateY(-3px);
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
}
.dark .fin-month-cell--unpaid:hover,
[data-theme='dark'] .fin-month-cell--unpaid:hover {
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
}

/* --- Mirror variant: PAID cell (.fin-month-cell--paid) --- */
.fin-month-cell--paid {
    background: var(--badge-success-bg);
    color: var(--badge-success-fg);
    border: 1px solid var(--badge-success-bg);
    transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
}

.fin-month-cell--paid :is(.fin-month-cell__name,
                          .fin-month-cell__icon,
                          .fin-month-cell__amount,
                          .fin-month-cell__plan,
                          .material-symbols-outlined) {
    color: inherit;
}

.fin-month-cell--paid:hover {
    transform: translateY(-3px);
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
}
.dark .fin-month-cell--paid:hover,
[data-theme='dark'] .fin-month-cell--paid:hover {
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
}

/* --- Mirror variant: DUE cell (.fin-month-cell--due) --- Mes en curso impago
   pero AÚN EN PLAZO ("por vencer"): mismo modelo solid-surface que paid/unpaid,
   campo ámbar del token semántico --badge-warning (pagable, NO deuda → no rojo;
   accionable → no gris 'future'). fg oscuro porque el warning es amarillo
   brillante. Un solo lugar controla el color: el token, no un hardcode. */
.fin-month-cell--due {
    background: var(--badge-warning-bg);
    color: var(--badge-warning-fg);
    border: 1px solid var(--badge-warning-bg);
    transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
    cursor: pointer;
}

.fin-month-cell--due :is(.fin-month-cell__name,
                         .fin-month-cell__icon,
                         .fin-month-cell__amount,
                         .fin-month-cell__plan,
                         .material-symbols-outlined) {
    color: inherit;
}

/* Pay button sobre el campo amarillo: chip neutro oscuro para contraste. */
.fin-month-cell--due .fin-month-cell__pay-btn {
    background: var(--badge-neutral-bg);
    color: var(--badge-neutral-fg);
}

.fin-month-cell--due:hover {
    transform: translateY(-3px);
    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.25);
}
.dark .fin-month-cell--due:hover,
[data-theme='dark'] .fin-month-cell--due:hover {
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.4);
}

/* =========================================
   MEMBERS LIST - Debt Tooltip on "En deuda" badge
   ========================================= */
.members-debt-tooltip {
    cursor: pointer;
}

/* Tooltip sub-element styles (for the floating tooltip container) */
#members-debt-tooltip-container .fin-tooltip-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-family: var(--font-heading, 'Space Grotesk'), sans-serif;
    font-size: 0.6875rem;
    font-weight: 700;
    color: var(--color-text-muted);
    letter-spacing: 0.5px;
}

#members-debt-tooltip-container .fin-tooltip-divider {
    height: 1px;
    background: #1f1f1f;
    margin: 0.6rem 0;
}

#members-debt-tooltip-container .fin-tooltip-scrollable-months {
    scrollbar-width: thin;
    scrollbar-color: rgba(255, 255, 255, 0.15) transparent;
}

#members-debt-tooltip-container .fin-tooltip-scrollable-months::-webkit-scrollbar {
    width: 4px;
}

#members-debt-tooltip-container .fin-tooltip-scrollable-months::-webkit-scrollbar-track {
    background: transparent;
}

#members-debt-tooltip-container .fin-tooltip-scrollable-months::-webkit-scrollbar-thumb {
    background: #1f1f1f;
    border-radius: 20px;
}

#members-debt-tooltip-container .fin-tooltip-scrollable-months::-webkit-scrollbar-thumb:hover {
    background: #1f1f1f;
}

/* =========================================
   MEMBERS LIST - Rowing Category Badges Styling
   ========================================= */
.category-badge {
    display: inline-flex;
    align-items: center;
    padding: 0.3rem 0.75rem;
    border-radius: 8px;
    font-size: 0.825rem;
    font-weight: 800;
    white-space: nowrap;
    border: 1px solid transparent;
    transition: all 0.2s ease;
}

/* Category badges — FEPARE 2026. Solo tokens del sistema, sin hex sueltos. */

/* Formativas: Sub 11 → Sub 17 — todas comparten el token success (verde) */
.category-badge.sub-11,
.category-badge.sub-13,
.category-badge.sub-15,
.category-badge.sub-17 { background-color: var(--badge-success-bg); color: var(--badge-success-fg); border-color: var(--badge-success-bg); }

/* Oficiales */
.category-badge.juvenil  { background-color: var(--badge-neutral-bg);  color: var(--badge-neutral-fg);  border-color: var(--badge-neutral-bg); }
.category-badge.senior-b,
.category-badge.senior-a { background-color: var(--badge-info-bg);     color: var(--badge-info-fg);     border-color: var(--badge-info-bg); }
.category-badge.master   { background-color: var(--badge-warning-bg);  color: var(--badge-warning-fg);  border-color: var(--badge-warning-bg); }
.category-badge.novicio  { background-color: var(--badge-muted-bg);    color: var(--badge-muted-fg);    border-color: var(--badge-muted-bg); }
/* Sized-down variant para uso en headers de tooltip. */
.category-badge--sm { padding: 2px 6px; border-radius: 4px; font-size: 0.65rem; letter-spacing: 0.05em; }

/* ── MEMBER-DETAIL FINANCIAL STATUS BADGE ──
   Single source of truth (previously duplicated in members-profile.css and member-detail.css). */
.md-status-badge {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.3rem 0.75rem;
    border-radius: 8px;
    font-size: 0.825rem;
    font-weight: 800;
    letter-spacing: 0.01em;
    white-space: nowrap;
    transition: all 0.2s ease;
}
.md-status-badge .material-symbols-outlined {
    font-size: 14px;
    font-weight: 800;
    font-variation-settings: 'FILL' 1;
}
/* Estado + su marcador de días como UNA unidad inline: el texto del estado y el
   sufijo <small> (días) comparten la MISMA línea base (flujo inline dentro del
   label), en vez de que el <small> sea un flex-item aparte que align-items:center
   levantaba respecto del texto. Fuente única del estilo del sufijo (antes iba
   inline repetido en cada estado del JS). */
.md-status-badge__label { display: inline; }
.md-status-badge__label small {
    font-size: 0.65rem;
    font-weight: 700;
    opacity: 0.8;
    margin-left: 0.3rem;
}
.md-status-badge--ok { background: var(--badge-success-bg); color: var(--badge-success-fg); border: 1px solid var(--badge-success-bg); }
.md-status-badge--debt { background: var(--badge-danger-bg); color: var(--badge-danger-fg); border: 1px solid var(--badge-danger-bg); }
.md-status-badge--near-due { background: var(--badge-warning-bg); color: var(--badge-warning-fg); border: 1px solid var(--badge-warning-bg); }
.md-status-badge--neutral { background: var(--badge-neutral-bg); color: var(--badge-neutral-fg); border: 1px solid var(--badge-neutral-bg); }
.md-status-badge--warning { background: var(--badge-warning-bg); color: var(--badge-warning-fg); border: 1px solid var(--badge-warning-bg); }

/* ── TOOLTIP STATUS PILL ──
   Single source of truth (previously duplicated in members-profile.css, member-detail.css, financial-tooltip.css). */
.fin-tooltip-status {
    padding: 2px 6px;
    border-radius: 4px;
    border: 1px solid;
    font-size: 0.65rem;
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}
.fin-tooltip-status--paid,
.fin-tooltip-status--success { background: var(--badge-success-bg); color: var(--badge-success-fg); border-color: var(--badge-success-bg); }
.fin-tooltip-status--unpaid,
.fin-tooltip-status--danger  { background: var(--badge-danger-bg); color: var(--badge-danger-fg); border-color: var(--badge-danger-bg); }
.fin-tooltip-status--warning { background: var(--badge-warning-bg); color: var(--badge-warning-fg); border-color: var(--badge-warning-bg); }
.fin-tooltip-status--info    { background: var(--badge-info-bg); color: var(--badge-info-fg); border-color: var(--badge-info-bg); }
.fin-tooltip-status--future,
.fin-tooltip-status--disabled,
.fin-tooltip-status--neutral,
.fin-tooltip-status--muted   { background: var(--badge-muted-bg); color: var(--badge-muted-fg); border-color: var(--badge-muted-bg); }

/* ── Tooltip layout modifiers (token-driven, replace inline style="..."). */
.fin-tooltip-detail-item--row   { justify-content: space-between; width: 100%; display: flex; gap: 8px; }
.fin-tooltip-detail-item--stack { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; }
.fin-tooltip-detail-item--desc  { display: flex; align-items: flex-start; gap: 8px; margin-bottom: 8px; }
.fin-tooltip-detail-value--danger { color: var(--badge-danger-bg); font-weight: 700; }
.fin-tooltip-detail-value--lg     { font-size: 0.85rem; }
.fin-tooltip-empty {
    color: var(--color-text-muted);
    font-size: 0.72rem;
    text-align: center;
    padding: 6px 0;
}
.fin-tooltip-section-label {
    font-size: 0.65rem;
    font-weight: 700;
    text-transform: uppercase;
    color: var(--color-text-muted);
    letter-spacing: 0.05em;
    margin-bottom: 6px;
}
/* Icon-* semantic classes must win over the generic tooltip icon color. */
.fin-tooltip-detail-item .material-symbols-outlined[class*="icon-"] { color: inherit; }
.fin-tooltip-detail-item :is(.icon-success, .icon-danger, .icon-warning, .icon-neutral, .icon-info, .icon-primary, .icon-accent, .icon-muted) {
    color: revert;
}

/* -- CATEGORY DROPDOWN SUBMENU -- */
.cat-submenu-trigger {
    position: relative;
}

/* Submenu portalizado a document.body al abrir para escapar del clip del
   menú padre (overflow-y: auto) y de cualquier ancestor con transform/filter
   que cambie el containing block de position: fixed. Como el submenu ya no
   vive dentro del trigger, la regla NO se puede escopear a
   .cat-submenu-trigger - debe aplicar al .cat-submenu esté donde esté. */
.cat-submenu {
    display: none;
    position: fixed;
    z-index: 9999;
}

/* Estado abierto: JS agrega .open al .cat-submenu al portalizarlo. */
.cat-submenu.open {
    display: block;
}

/* Variante lab-* (mismo comportamiento, selector separado por historia del
   módulo laboratorio; ambos usan wireCategoryDropdown y portal a body). */
.lab-cat-submenu-trigger {
    position: relative;
}
.lab-cat-submenu {
    display: none;
    position: fixed;
    z-index: 9999;
}
.lab-cat-submenu.open {
    display: block;
}

/* -- SCROLL EN DROPDOWNS DE FILTRO --
   Cualquier contenedor de dropdown (por convención: id/clase termina en
   "-menu") o el submenu .cat-submenu/.lab-cat-submenu que contenga ítems
   tipo menu-item debe acotar su alto al viewport y scrollear internamente
   en lugar de desbordarse por debajo del fold. Regla única para no tener
   que añadir max-height ad-hoc en cada HTML. */
[id$="-menu"]:has(> .menu-item),
[class*="-menu"]:has(> .menu-item),
.cat-submenu:has(> .menu-item),
.lab-cat-submenu:has(> .menu-item) {
    max-height: min(50vh, 20rem);
    overflow-y: auto;
    overscroll-behavior: contain;
    /* Firefox: scrollbar siempre visible (no auto-hide) */
    scrollbar-width: thin;
    scrollbar-color: var(--color-text-muted, #888) transparent;
}

/* WebKit (Chrome/Safari/Edge): scrollbar visible dentro del dropdown.
   El default global es 12px pero sobre menús flotantes con esquinas
   redondeadas queda mejor un track angosto y siempre presente. */
[id$="-menu"]:has(> .menu-item)::-webkit-scrollbar,
[class*="-menu"]:has(> .menu-item)::-webkit-scrollbar,
.cat-submenu:has(> .menu-item)::-webkit-scrollbar,
.lab-cat-submenu:has(> .menu-item)::-webkit-scrollbar {
    width: 8px;
}
[id$="-menu"]:has(> .menu-item)::-webkit-scrollbar-thumb,
[class*="-menu"]:has(> .menu-item)::-webkit-scrollbar-thumb,
.cat-submenu:has(> .menu-item)::-webkit-scrollbar-thumb,
.lab-cat-submenu:has(> .menu-item)::-webkit-scrollbar-thumb {
    background: var(--color-text-muted, #888);
    border-radius: 4px;
}
[id$="-menu"]:has(> .menu-item)::-webkit-scrollbar-track,
[class*="-menu"]:has(> .menu-item)::-webkit-scrollbar-track,
.cat-submenu:has(> .menu-item)::-webkit-scrollbar-track,
.lab-cat-submenu:has(> .menu-item)::-webkit-scrollbar-track {
    background: transparent;
}

/* ==========================================================================
   PREMIUM CUSTOM SELECTS - Global Upgraded Dropdowns
   ========================================================================== */

.custom-select-wrapper {
    position: relative;
    width: 100%;
}
.custom-select-wrapper.hidden {
    display: none !important;
}

.custom-select-trigger {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    padding: 10px 14px;
    font-family: var(--font-main), system-ui, sans-serif;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--color-text-main);
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    cursor: pointer;
    text-align: left;
    transition:
        border-color 0.2s ease,
        box-shadow 0.2s ease,
        background-color 0.2s ease;
    outline: none;
    box-sizing: border-box;
}

.custom-select-trigger:hover {
    border-color: var(--color-brand);
    background-color: var(--color-surface-hover);
}

.custom-select-trigger:focus-within,
.custom-select-trigger:focus {
    border-color: var(--color-brand);
    box-shadow: 0 0 0 3px rgb(var(--tw-color-primary) / 0.15);
}

.dark .custom-select-trigger:focus {
    border-color: var(--color-primary);
    box-shadow: 0 0 0 3px rgb(var(--tw-color-primary) / 0.15);
}

.custom-select-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding-right: 0.5rem;
}

.custom-select-arrow {
    font-size: 18px;
    color: var(--color-text-muted);
    transition: transform 0.2s ease;
}

.custom-select-wrapper.open .custom-select-arrow {
    transform: rotate(180deg);
}

/* Floating custom dropdown menu */
.custom-select-dropdown {
    position: absolute;
    z-index: 99999;
    margin-top: 6px;
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    box-shadow:
        var(--shadow-lg),
        0 10px 30px rgba(0, 0, 0, 0.08);
    max-height: 300px;
    /* Layout flex vertical: search fijo arriba, options scrolleable abajo.
       El scroll vive en .custom-select-options-list, NO en el dropdown, para
       que el search-wrap nunca se corte al scrollear (más robusto que sticky
       en dark-mode con backdrop-filter). */
    display: flex;
    flex-direction: column;
    overflow: hidden;
    opacity: 0;
    transform: translateY(8px);
    pointer-events: none;
    transition:
        opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1),
        transform 0.2s cubic-bezier(0.4, 0, 0.2, 1);
    box-sizing: border-box;
}
/* Cerrado (sin .open): aunque es invisible (opacity:0), su caja de 300px seguía
   ocupando lugar al final del <body> y estiraba el documento — muy notorio en
   formularios con muchos selects (registro público sumaba ~314px de vacío).
   Colapsado a 0 mientras está cerrado no aporta alto; al abrir (.open) recupera
   el max-height que fija el JS. */
.custom-select-dropdown:not(.open) {
    max-height: 0 !important;
}

.custom-select-options-list {
    overflow-y: auto;
    flex: 1 1 auto;
    min-height: 0;
}

.custom-select-search-wrap {
    padding: 8px;
    flex-shrink: 0;
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
}
.dark .custom-select-search-wrap {
    background: rgb(20, 20, 20);
    backdrop-filter: blur(16px);
}
.custom-select-search-input {
    width: 100%;
    padding: 6px 12px;
    font-size: 0.8125rem;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-sm);
    background: var(--color-bg);
    color: var(--color-text-main);
    outline: none;
    transition: border-color 0.2s ease;
}
.custom-select-search-input:focus {
    border-color: var(--color-brand);
}

.custom-select-dropdown.open {
    opacity: 1;
    transform: translateY(0);
    pointer-events: auto;
}

/* Custom Scrollbar for Dropdown */
.custom-select-dropdown::-webkit-scrollbar {
    width: 6px;
}
.custom-select-dropdown::-webkit-scrollbar-track {
    background: transparent;
}
.custom-select-dropdown::-webkit-scrollbar-thumb {
    background: #f5f5f5;
    border-radius: 3px;
}
.dark .custom-select-dropdown::-webkit-scrollbar-thumb {
    background: #1f1f1f;
}

/* Glassmorphism for Dark Mode Dropdown */
.dark .custom-select-dropdown {
    background-color: rgb(20, 20, 20);
    backdrop-filter: blur(16px);
    border-color: rgba(255, 255, 255, 0.08);
    box-shadow:
        0 20px 40px rgba(0, 0, 0, 0.5),
        0 0 1px rgba(255, 255, 255, 0.1);
}

/* Custom Option Item */
.custom-select-option {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 14px;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--color-text-main);
    cursor: pointer;
    transition:
        background-color 0.15s ease,
        color 0.15s ease;
}

.custom-select-option:hover {
    background-color: color-mix(in srgb, var(--color-brand) 8%, transparent);
    color: var(--color-text-main);
}

.dark .custom-select-option:hover {
    background-color: color-mix(in srgb, var(--color-brand) 12%, transparent);
    color: #ffffff;
}

.custom-select-option.selected {
    background-color: color-mix(in srgb, var(--color-brand) 12%, transparent);
    color: var(--color-brand);
    font-weight: 600;
}

.dark .custom-select-option.selected {
    background-color: color-mix(in srgb, var(--color-brand) 20%, transparent);
    color: var(--color-primary);
    font-weight: 600;
}

/* ==========================================================================
   AUTOFILL OVERRIDES - Keep mode-appropriate appearance on autofilled fields
   ========================================================================== */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active {
    -webkit-box-shadow: 0 0 0 30px var(--color-bg) inset;
    -webkit-text-fill-color: var(--color-text-main);
    transition: background-color 5000s ease-in-out 0s;
}

/* ==========================================================================
   PORTAL - Toolbar search field override (fix invisible input)
   ========================================================================== */
#module-tab-portal .search-wrap {
    position: relative;
    flex: 1;
    min-width: 200px;
}
#module-tab-portal .search-wrap svg {
    position: absolute;
    left: 12px;
    top: 50%;
    transform: translateY(-50%);
    width: 15px;
    height: 15px;
    color: var(--color-text-muted);
    pointer-events: none;
}
#module-tab-portal .toolbar-input {
    width: 100%;
    padding: 9px 14px 9px 36px;
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: 10px;
    font-family: inherit;
    font-size: 0.84rem;
    color: var(--color-text-main);
    outline: none;
    transition:
        border-color 0.2s,
        box-shadow 0.2s;
}
#module-tab-portal .toolbar-input:focus {
    border-color: var(--color-primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 15%, transparent);
}
#module-tab-portal .toolbar-input::placeholder {
    color: var(--color-text-muted);
}

/* ==========================================================================
   PORTAL - Create Access Modal (premium design)
   ========================================================================== */

/* Modal shell */
.portal-create-modal {
    max-width: 480px;
    padding: 0;
    overflow: visible;
}

/* ── Header ── */
.portal-create-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 20px 24px 16px;
}
.portal-create-title {
    display: flex;
    align-items: center;
    gap: 10px;
}
.portal-create-title svg {
    width: 20px;
    height: 20px;
    color: var(--color-gold);
}
.portal-create-title h3 {
    margin: 0;
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--color-text-main);
}

/* ── Search zone - adapts to light/dark ── */
.portal-search-zone {
    background: var(--color-bg, #fafafa);
    border-top: 1px solid var(--color-border, #e5e5e5);
    border-bottom: 1px solid var(--color-border, #e5e5e5);
    padding: 20px 24px 14px;
}
.dark .portal-search-zone {
    background: #0a0a0a;
    border-color: rgba(255, 255, 255, 0.06);
}
.portal-search-box {
    position: relative;
    display: flex;
    align-items: center;
    background: var(--color-surface, #ffffff);
    border: 1px solid var(--color-border, #e5e5e5);
    border-radius: 10px;
    transition:
        border-color 0.2s,
        box-shadow 0.2s;
}
.dark .portal-search-box {
    background: #161616;
    border-color: rgba(255, 255, 255, 0.1);
}
.portal-search-box:focus-within {
    border-color: var(--color-primary);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 15%, transparent);
}
.portal-search-icon {
    width: 18px;
    height: 18px;
    margin-left: 14px;
    color: var(--color-text-muted);
    flex-shrink: 0;
    transition: color 0.2s;
}
.dark .portal-search-icon {
    color: rgba(255, 255, 255, 0.3);
}
.portal-search-box:focus-within .portal-search-icon {
    color: var(--color-primary);
}
.portal-search-box > input {
    flex: 1;
    background: transparent;
    border: none;
    outline: none;
    padding: 14px 16px;
    font-size: 0.9rem;
    color: var(--color-text-main, #111);
    font-family: inherit;
}
.dark .portal-search-box > input {
    color: #fff;
}
.portal-search-box > input::placeholder {
    color: var(--color-text-muted);
}
.dark .portal-search-box > input::placeholder {
    color: rgba(255, 255, 255, 0.28);
}
.portal-search-hint {
    display: block;
    margin-top: 8px;
    font-size: 0.72rem;
    color: var(--color-text-muted);
    letter-spacing: 0.02em;
}
.dark .portal-search-hint {
    color: rgba(255, 255, 255, 0.3);
}

/* ── Dropdown results ── */
.member-search-dropdown {
    /* NO usar !important: los callers muestran/ocultan con style.display inline
     * (invocing-portal, manual-attendance-modal). Con !important el inline no
     * puede prender el dropdown → nunca se abría al tipear en el buscador. */
    display: none;
    position: absolute;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    z-index: 200;
    max-height: 280px;
    overflow-y: auto;
    background: var(--color-surface, #ffffff);
    border: 1px solid var(--color-border, #e5e5e5);
    border-radius: 10px;
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.15);
    scrollbar-width: thin;
    scrollbar-color: var(--color-border) transparent;
}
.dark .member-search-dropdown {
    background: #1a1a1a;
    border-color: rgba(255, 255, 255, 0.08);
    box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
    scrollbar-color: rgba(255, 255, 255, 0.1) transparent;
}
.member-search-item {
    padding: 12px 16px;
    cursor: pointer;
    display: flex;
    flex-direction: column;
    gap: 3px;
    border-bottom: 1px solid var(--color-border, #e5e5e5);
    transition:
        background 0.15s,
        padding-left 0.15s;
}
.dark .member-search-item {
    border-bottom-color: rgba(255, 255, 255, 0.05);
}
.member-search-item:last-child {
    border-bottom: none;
}
.member-search-item:hover {
    background: color-mix(in srgb, var(--color-primary) 8%, transparent);
    padding-left: 20px;
}
.member-search-item strong {
    font-size: 0.85rem;
    font-weight: 600;
    color: var(--color-text-main, #111);
}
.dark .member-search-item strong {
    color: #f0f0f0;
}
.member-search-item:hover strong {
    color: var(--color-primary);
}
.member-search-detail {
    font-size: 0.72rem;
    color: var(--color-text-muted);
    font-family: 'JetBrains Mono', 'Fira Code', monospace;
    letter-spacing: 0.03em;
}
.dark .member-search-detail {
    color: rgba(255, 255, 255, 0.4);
}
.member-search-empty {
    cursor: default;
    text-align: center;
    color: var(--color-text-muted);
    font-size: 0.82rem;
    padding: 24px 16px;
}
.dark .member-search-empty {
    color: rgba(255, 255, 255, 0.3);
}
.member-search-empty:hover {
    background: transparent;
    padding-left: 16px;
}

/* ── Selected member card ── */
.portal-selected-card {
    display: none !important;
    margin: 0 24px 6px;
    padding: 14px 18px;
    border-radius: 10px;
    border: 1px solid color-mix(in srgb, var(--color-primary) 25%, transparent);
    background: color-mix(in srgb, var(--color-primary) 4%, transparent);
    align-items: center;
    gap: 14px;
    animation: portalCardIn 0.25s ease-out;
}
.portal-selected-card[style*='display: flex'],
.portal-selected-card[style*='display:flex'] {
    display: flex;
}
@keyframes portalCardIn {
    from {
        opacity: 0;
        transform: translateY(-6px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}
.portal-selected-avatar {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    background: linear-gradient(135deg, var(--color-gold), #e6a800);
    color: #000;
    font-weight: 800;
    font-size: 0.85rem;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
    text-transform: uppercase;
}
.portal-selected-info {
    flex: 1;
    min-width: 0;
}
.portal-selected-name {
    font-weight: 700;
    font-size: 0.88rem;
    color: var(--color-text-main);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.portal-selected-detail {
    font-size: 0.74rem;
    color: var(--color-text-muted);
    font-family: 'JetBrains Mono', 'Fira Code', monospace;
    margin-top: 1px;
}
.portal-selected-check {
    width: 22px;
    height: 22px;
    color: #22c55e;
    flex-shrink: 0;
}

/* ── Footer ── */
.portal-create-footer {
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    padding: 16px 24px 20px;
}


/* ==========================================================================
   PORTAL - Modals & Overlay Style Fixes (prevents layout disassembly)
   ========================================================================== */
.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.6);
    backdrop-filter: blur(5px);
    z-index: 10000;
    display: none;
    align-items: center;
    justify-content: center;
    opacity: 0;
    transition: opacity 0.25s ease;
}

.modal-overlay.open {
    display: flex;
    opacity: 1;
}

/* General overlay modal window sizing and shadows */
.modal-overlay .modal {
    background: var(--color-surface, #ffffff);
    border: 1px solid var(--color-border, #e5e5e5);
    border-radius: var(--radius-lg, 16px);
    width: 95%;
    max-width: 480px;
    max-height: 90vh;
    display: flex;
    flex-direction: column;
    box-shadow: 0 25px 60px rgba(0, 0, 0, 0.25);
    color: var(--color-text-main);
    box-sizing: border-box;
    /* overflow: visible permite que popovers absolute-positioned (dropdowns
       de búsqueda de socio, autocompletes, tooltips) escapen del modal en
       vez de quedar clipeados. Si un modal específico necesita scroll
       interno, el body del modal debe manejarlo con su propio overflow-y:
       auto, no clipear a nivel del contenedor externo. */
    overflow: visible;
    animation: portalModalSlideIn 0.3s ease;
}

.dark .modal-overlay .modal {
    background: #141414;
    color: #e5e5e5;
    border-color: rgba(255, 255, 255, 0.08);
    box-shadow: 0 25px 60px rgba(0, 0, 0, 0.5);
}

@keyframes portalModalSlideIn {
    from {
        transform: translateY(15px);
        opacity: 0;
    }
    to {
        transform: translateY(0);
        opacity: 1;
    }
}

/* Modal close button styling and SVG constraint */
.modal-close {
    background: none;
    border: none;
    cursor: pointer;
    padding: 6px;
    color: var(--color-text-muted);
    display: flex;
    align-items: center;
    justify-content: center;
    border-radius: 50%;
    transition: all 0.2s ease;
}

.modal-close:hover {
    color: var(--color-brand, var(--color-brand));
    background: var(--color-surface-hover, rgba(0, 0, 0, 0.05));
}

.modal-close svg {
    width: 20px;
    height: 20px;
}

/* Portal token-copy icon button: hereda `.btn .btn-secondary .btn-icon`
 * del sistema de componentes (definidos arriba). Sólo se ajusta el SVG
 * interno para que se vea consistente con Material Symbols. */
.btn-icon svg {
    width: 18px;
    height: 18px;
}

.token-box {
    display: flex;
    align-items: center;
    justify-content: space-between;
    background: var(--color-bg, #fafafa);
    border: 1px solid var(--color-border, #e5e5e5);
    border-radius: var(--radius-md, 8px);
    padding: 10px 14px;
    gap: 12px;
}

.dark .token-box {
    background: #0a0a0a;
    border-color: rgba(255, 255, 255, 0.08);
}

.token-url {
    font-family: 'JetBrains Mono', 'Fira Code', monospace;
    font-size: 0.8rem;
    color: var(--color-text-main);
    word-break: break-all;
    user-select: all;
}

