(function () {
    var root = document.documentElement;

    // ---------------------------------------------------------------------
    // Global utilities
    // ---------------------------------------------------------------------
    function safeCall(fn, context) {
        try {
            return fn.call(context || window);
        } catch (err) {
            console.error('ICYLAB cleanup error:', err);
            return null;
        }
    }

    function escapeHtml(value) {
        return String(value || '').replace(/[&<>"']/g, function (ch) {
            return ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;' })[ch];
        });
    }

    function normalizeSearchText(value) {
        return String(value || '')
            .toLowerCase()
            .normalize('NFD')
            .replace(/[\u0300-\u036f]/g, '')
            .trim();
    }

    function normalizeRutSearch(value) {
        return normalizeSearchText(value).replace(/[^0-9k]/g, '');
    }

    window.ICY_NORMALIZE_SEARCH = normalizeSearchText;
    window.ICY_NORMALIZE_RUT_SEARCH = normalizeRutSearch;

    function enhanceModalFooterButtons(scope) {
        var rootScope = scope || document;
        var buttons = rootScope.querySelectorAll ? rootScope.querySelectorAll('.product-modal-footer .btn, .modal-footer-actions-right .btn') : [];
        buttons.forEach(function (button) {
            if (button.dataset.modalButtonEnhanced === '1') return;
            var existingText = (button.textContent || '').replace(/\s+/g, ' ').trim().toLowerCase();
            if (existingText.indexOf('eliminar') !== -1 || existingText.indexOf('borrar') !== -1) {
                button.classList.add('btn-delete-mobile');
            }
            if (button.querySelector('svg, i[data-lucide]')) {
                button.dataset.modalButtonEnhanced = '1';
                return;
            }

            var rawText = (button.textContent || '').replace(/\s+/g, ' ').trim();
            if (!rawText) return;

            var text = rawText;
            var icon = '';
            var lower = rawText.toLowerCase();

            if (lower.indexOf('cancelar') !== -1) { icon = 'x'; text = 'Cancelar'; }
            else if (lower.indexOf('cerrar') !== -1) { icon = 'x'; text = 'Cerrar'; }
            else if (lower.indexOf('eliminar') !== -1 || lower.indexOf('borrar') !== -1) { icon = 'trash-2'; text = 'Eliminar'; button.classList.add('btn-delete-mobile'); }
            else if (lower.indexOf('aplicar') !== -1) { icon = 'check-circle'; text = 'Aplicar'; }
            else if (lower.indexOf('guardar') !== -1 || lower.indexOf('actualizar') !== -1) { icon = 'save'; text = lower.indexOf('actualizar') !== -1 ? 'Actualizar' : 'Guardar'; }
            else if (lower.indexOf('crear') !== -1 || lower.indexOf('nuevo') !== -1) { icon = 'plus'; text = rawText.length > 16 ? 'Crear' : rawText; }

            if (!icon) return;
            button.innerHTML = '<i data-lucide="' + icon + '"></i><span>' + escapeHtml(text) + '</span>';
            button.dataset.modalButtonEnhanced = '1';
        });
        if (window.lucide) lucide.createIcons();
    }

    function isLeftPlainClick(event) {
        return event.button === 0 && !event.metaKey && !event.ctrlKey && !event.shiftKey && !event.altKey;
    }

    // ---------------------------------------------------------------------
    // Base UI boot
    // ---------------------------------------------------------------------
    var booted = false;

    function normalizeTheme(theme) {
        return theme === 'light' || theme === 'dark' ? theme : null;
    }

    function getSavedTheme() {
        try {
            return normalizeTheme(localStorage.getItem('icylab-theme'));
        } catch (err) {
            return null;
        }
    }

    function getAppliedTheme() {
        return normalizeTheme(root.getAttribute('data-theme')) || getSavedTheme() || 'dark';
    }

    function syncThemeControls(theme) {
        var appliedTheme = normalizeTheme(theme) || 'dark';
        var isDark = appliedTheme === 'dark';
        var themeIcon = document.getElementById('themeIcon');
        var themeText = document.getElementById('themeText');
        var themeToggle = document.getElementById('themeToggleBtn') || document.getElementById('themeToggle');
        var themeColor = document.querySelector('meta[name="theme-color"]');

        if (themeIcon) themeIcon.setAttribute('data-lucide', isDark ? 'moon' : 'sun');
        if (themeText) themeText.textContent = isDark ? 'Tema oscuro' : 'Tema claro';
        if (themeToggle) {
            themeToggle.setAttribute('aria-pressed', isDark ? 'true' : 'false');
            themeToggle.setAttribute('aria-label', isDark
                ? 'Tema actual: oscuro. Cambiar a tema claro.'
                : 'Tema actual: claro. Cambiar a tema oscuro.');
        }
        if (themeColor) themeColor.setAttribute('content', isDark ? '#090909' : '#F2F3F5');
    }

    function applyTheme(theme, persist) {
        var appliedTheme = normalizeTheme(theme) || 'dark';
        root.setAttribute('data-theme', appliedTheme);
        if (persist) {
            try {
                localStorage.setItem('icylab-theme', appliedTheme);
            } catch (err) {
                // El cambio visual sigue funcionando aunque el navegador bloquee localStorage.
            }
        }
        syncThemeControls(appliedTheme);
        return appliedTheme;
    }

    function injectRuntimeStyles() {
        if (document.getElementById('icy-runtime-styles')) return;
        var style = document.createElement('style');
        style.id = 'icy-runtime-styles';
        style.innerHTML = '' +
            '@keyframes spin { 100% { transform: rotate(360deg); } }' +
            '.spin-icon { animation: spin 1s linear infinite; }' +
            '.spa-progress { position: fixed; top: 0; left: 0; right: auto; width: 0; height: 3px; z-index: 3000; background: var(--color-primary, #00c853); opacity: 0; pointer-events: none; transition: width .28s ease, opacity .18s ease; }' +
            'body.is-spa-loading .spa-progress { width: 72%; opacity: 1; }' +
            'body.is-spa-loaded .spa-progress { width: 100%; opacity: 0; }' +
            'body.is-spa-loading .app-main { cursor: progress; }' +
            'body .product-modal-backdrop.show { display: flex !important; }' +
            'body .product-modal-box, body .quotes-modal-box { height: auto !important; min-height: 0 !important; overflow: hidden !important; }' +
            'body .product-modal-box > form { flex: 0 1 auto !important; max-height: inherit !important; min-height: 0 !important; overflow: hidden !important; }' +
            '@media (max-width: 760px) {' +
            'body .product-modal-backdrop { align-items: center !important; justify-content: center !important; padding: 12px !important; overflow: hidden !important; }' +
            'body .product-modal-box, body .quotes-modal-box { width: calc(100vw - 24px) !important; max-width: calc(100vw - 24px) !important; height: auto !important; min-height: 0 !important; max-height: calc(100dvh - 24px) !important; align-self: center !important; }' +
            'body .product-modal-header { padding: 16px 18px !important; }' +
            'body .product-modal-body { flex: 0 1 auto !important; min-height: 0 !important; max-height: calc(100dvh - 190px) !important; overflow-y: auto !important; overflow-x: hidden !important; -webkit-overflow-scrolling: touch; }' +
            'body .product-modal-footer { flex-shrink: 0 !important; padding: 12px 18px calc(12px + env(safe-area-inset-bottom)) !important; }' +
            '}';
        (document.body || document.head).appendChild(style);
    }

    function ensureProgressBar() {
        var bar = document.getElementById('spaProgress');
        if (bar) return bar;
        bar = document.createElement('div');
        bar.id = 'spaProgress';
        bar.className = 'spa-progress';
        document.body.appendChild(bar);
        return bar;
    }

    function boot() {
        if (booted) return;
        booted = true;

        injectRuntimeStyles();
        ensureProgressBar();

        applyTheme(getSavedTheme() || getAppliedTheme(), false);

        if (window.lucide) lucide.createIcons();
        enhanceModalFooterButtons(document);
        initNotifications();
        initOpportunityValidation();
        updateSidebarActiveLink(window.location.pathname);
    }

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', boot);
    } else {
        boot();
    }

    // ---------------------------------------------------------------------
    // SPA lifecycle hardening
    // ---------------------------------------------------------------------
    var SPA_CACHE_TTL = 30000;
    var SPA_MAX_CACHE_ITEMS = 18;
    var spaCache = {};
    var cleanupHandlers = [];
    var activeNavigationToken = 0;
    var activeNavigationController = null;
    var activeFormController = null;
    var navigationInFlight = false;
    var registeredModules = {};
    var activeModuleName = null;
    var activeModuleDestroy = null;


    function normalizeSpaPath(path) {
        try {
            var url = new URL(path, window.location.origin);
            return url.pathname + url.search + url.hash;
        } catch (err) {
            return path;
        }
    }

    function sameOriginUrl(value) {
        try {
            var url = new URL(value, window.location.origin);
            return url.origin === window.location.origin ? url : null;
        } catch (err) {
            return null;
        }
    }

    function pathHasBlockedExtension(pathname) {
        return /\.(pdf|csv|xlsx?|docx?|zip|rar|7z|png|jpe?g|webp|gif|svg|ico|txt|xml)$/i.test(pathname || '');
    }

    function isSpaNavigableUrl(url) {
        if (!url || url.origin !== window.location.origin) return false;

        var pathname = url.pathname || '';
        var lower = pathname.toLowerCase();

        if (lower === '/logout.php' || lower === '/login.php' || lower === '/login_process.php') return false;
        if (lower.indexOf('/api/') === 0 || lower.indexOf('/cron/') === 0) return false;
        if (lower.indexOf('/core/') === 0 || lower.indexOf('/services/') === 0) return false;
        if (pathHasBlockedExtension(pathname)) return false;

        // These views are intentionally excluded because they can be printed/opened
        // as isolated documents or previews.
        if (lower.indexOf('/quote_preview.php') !== -1) return false;
        if (lower.indexOf('/quote_view.php') !== -1) return false;

        return true;
    }

    function isSpaCacheable(path) {
        var url = sameOriginUrl(path);
        if (!url || !isSpaNavigableUrl(url)) return false;
        if (url.searchParams && url.searchParams.get('no_cache') === '1') return false;
        return true;
    }

    function pruneSpaCache() {
        var keys = Object.keys(spaCache);
        if (keys.length <= SPA_MAX_CACHE_ITEMS) return;
        keys.sort(function (a, b) { return (spaCache[a].ts || 0) - (spaCache[b].ts || 0); });
        while (keys.length > SPA_MAX_CACHE_ITEMS) {
            delete spaCache[keys.shift()];
        }
    }

    function clearSpaCache() {
        spaCache = {};
    }

    function registerSpaCleanup(handler) {
        if (typeof handler !== 'function') return function () {};
        cleanupHandlers.push(handler);
        return function unregisterSpaCleanup() {
            cleanupHandlers = cleanupHandlers.filter(function (candidate) {
                return candidate !== handler;
            });
        };
    }

    function runRegisteredCleanups() {
        if (!cleanupHandlers.length) return;
        var handlers = cleanupHandlers.slice();
        cleanupHandlers = [];
        handlers.forEach(function (handler) { safeCall(handler); });
    }

    // Compatibilidad con módulos que registran su limpieza en el contenedor
    // histórico. Vaciarlo permite reinicializarlos correctamente al volver.
    function runLegacyPageCleanups() {
        if (!Array.isArray(window.ICY_PAGE_CLEANUPS) || !window.ICY_PAGE_CLEANUPS.length) return;
        var handlers = window.ICY_PAGE_CLEANUPS.slice();
        window.ICY_PAGE_CLEANUPS.length = 0;
        handlers.forEach(function (handler) { safeCall(handler); });
    }

    function resolveModuleName(mainContainer, url) {
        if (mainContainer) {
            var explicit = mainContainer.getAttribute('data-spa-module') || mainContainer.getAttribute('data-module');
            if (explicit) return explicit;
            var child = mainContainer.querySelector('[data-spa-module], [data-module]');
            if (child) return child.getAttribute('data-spa-module') || child.getAttribute('data-module') || '';
        }
        try {
            var parsed = new URL(url || window.location.href, window.location.origin);
            var parts = parsed.pathname.split('/').filter(Boolean);
            if (parts[0] === 'modules' && parts[1]) {
                return parts[1] + (parts[2] ? ':' + parts[2].replace(/\.php$/i, '') : '');
            }
            return parsed.pathname || 'root';
        } catch (err) {
            return 'unknown';
        }
    }

    function destroyActiveModule() {
        if (typeof activeModuleDestroy === 'function') {
            safeCall(activeModuleDestroy);
        }
        activeModuleDestroy = null;
        activeModuleName = null;
    }

    function mountRegisteredModule(moduleName, context) {
        activeModuleName = moduleName || null;
        activeModuleDestroy = null;
        if (!moduleName || !registeredModules[moduleName] || typeof registeredModules[moduleName].mount !== 'function') return;
        var maybeDestroy = safeCall(function () {
            return registeredModules[moduleName].mount(context || {});
        });
        if (typeof maybeDestroy === 'function') {
            activeModuleDestroy = maybeDestroy;
        } else if (registeredModules[moduleName] && typeof registeredModules[moduleName].destroy === 'function') {
            activeModuleDestroy = function () { registeredModules[moduleName].destroy(context || {}); };
        }
    }

    function registerModule(name, handlers) {
        if (!name || !handlers) return;
        registeredModules[name] = handlers;
    }

    function registerAutoCleanupListener(target, eventName, handler, options) {
        if (!target || !eventName || typeof handler !== 'function') return function () {};
        target.addEventListener(eventName, handler, options || false);
        return registerSpaCleanup(function () {
            target.removeEventListener(eventName, handler, options || false);
        });
    }

    function registerAutoCleanupTimeout(callback, delay) {
        var id = window.setTimeout(callback, delay);
        registerSpaCleanup(function () { window.clearTimeout(id); });
        return id;
    }

    function registerAutoCleanupInterval(callback, delay) {
        var id = window.setInterval(callback, delay);
        registerSpaCleanup(function () { window.clearInterval(id); });
        return id;
    }

    function cleanupBeforeSpaNavigation() {
        document.dispatchEvent(new CustomEvent('icy:spa-before-cleanup', { detail: { module: activeModuleName } }));
        destroyActiveModule();
        runRegisteredCleanups();
        runLegacyPageCleanups();

        document.body.style.overflow = '';
        document.body.classList.remove('is-mobile-sidebar-open');
        document.body.classList.remove('is-modal-open');

        var sidebar = document.getElementById('sidebar');
        var backdrop = document.getElementById('sidebarBackdrop');
        if (sidebar) sidebar.classList.remove('is-open');
        if (backdrop) backdrop.classList.remove('is-open');

        closeNotificationsDropdown();
        closeNotificationPreferencesModal(true);
        closeNotificationGroupsModal(true);

        document.querySelectorAll('.custom-select.is-open').forEach(function (select) {
            select.classList.remove('is-open');
        });

        document.querySelectorAll('.product-modal-backdrop').forEach(function (modal) {
            modal.classList.remove('show');
            modal.setAttribute('aria-hidden', 'true');
            modal.style.display = 'none';
        });

        document.querySelectorAll('.quote-view-layer, .opp-view-layer').forEach(function (layer) {
            layer.classList.remove('active');
        });

        if (window.ICY_QUOTES_STATE) {
            ['noticeTimer', 'customerTimer', 'productTimer'].forEach(function (key) {
                if (window.ICY_QUOTES_STATE[key]) clearTimeout(window.ICY_QUOTES_STATE[key]);
            });
        }
    }

    function setSpaLoadingState(isLoading) {
        navigationInFlight = !!isLoading;
        ensureProgressBar();
        document.body.classList.toggle('is-spa-loading', !!isLoading);
        document.body.classList.remove('is-spa-loaded');

        var mainContainer = document.querySelector('.app-main');
        if (mainContainer) {
            mainContainer.setAttribute('aria-busy', isLoading ? 'true' : 'false');
            mainContainer.style.opacity = isLoading ? '0.58' : '';
        }
    }

    function finishSpaLoadingState() {
        navigationInFlight = false;
        document.body.classList.remove('is-spa-loading');
        document.body.classList.add('is-spa-loaded');
        var mainContainer = document.querySelector('.app-main');
        if (mainContainer) {
            mainContainer.setAttribute('aria-busy', 'false');
            mainContainer.style.opacity = '';
        }
        window.setTimeout(function () {
            document.body.classList.remove('is-spa-loaded');
        }, 240);
    }

    function abortActiveNavigation() {
        if (activeNavigationController && typeof activeNavigationController.abort === 'function') {
            activeNavigationController.abort();
        }
        activeNavigationController = null;
    }

    function abortActiveFormSubmission() {
        if (activeFormController && typeof activeFormController.abort === 'function') {
            activeFormController.abort();
        }
        activeFormController = null;
    }

    function createAbortController() {
        if (typeof AbortController === 'undefined') return null;
        return new AbortController();
    }

    function fetchSpaPage(path, options) {
        options = options || {};
        var key = normalizeSpaPath(path);
        var now = Date.now();
        var cached = spaCache[key];

        if (!options.force && isSpaCacheable(key) && cached && (now - cached.ts) < SPA_CACHE_TTL) {
            return Promise.resolve({
                html: cached.html,
                url: cached.url || key,
                title: cached.title || '',
                fromCache: true
            });
        }

        var fetchOptions = {
            headers: {
                'X-SPA-Request': 'true',
                'X-Requested-With': 'XMLHttpRequest'
            },
            credentials: 'same-origin',
            redirect: 'follow'
        };
        if (options.signal) fetchOptions.signal = options.signal;

        return fetch(path, fetchOptions).then(function (response) {
            if (!response.ok) {
                throw new Error('HTTP ' + response.status);
            }

            var finalUrl = response.url || key;
            var redirectedUrl = sameOriginUrl(finalUrl);
            if (response.redirected && redirectedUrl && !isSpaNavigableUrl(redirectedUrl)) {
                window.location.href = finalUrl;
                return null;
            }

            var newTitle = response.headers.get('X-Page-Title') || '';
            return response.text().then(function (html) {
                var payload = { html: html, url: finalUrl, title: newTitle, fromCache: false };
                if (isSpaCacheable(key)) {
                    spaCache[key] = { html: html, url: finalUrl, title: newTitle, ts: Date.now() };
                    pruneSpaCache();
                }
                return payload;
            });
        });
    }

    function prefetchSpaLink(link) {
        if (!link || link.dataset.spaPrefetched === '1') return;
        if (link.target === '_blank' || link.hasAttribute('data-no-spa') || link.hasAttribute('download')) return;
        var url = sameOriginUrl(link.href);
        if (!url || !isSpaNavigableUrl(url)) return;
        var path = url.pathname + url.search + url.hash;
        if (!isSpaCacheable(path)) return;

        link.dataset.spaPrefetched = '1';
        window.setTimeout(function () {
            fetchSpaPage(path, { prefetch: true }).catch(function () {
                link.dataset.spaPrefetched = '0';
            });
        }, 80);
    }

    function navigateTo(path, historyStrategy, options) {
        options = options || {};
        var targetUrl = sameOriginUrl(path);
        if (!targetUrl || !isSpaNavigableUrl(targetUrl)) {
            window.location.href = path;
            return Promise.resolve(false);
        }

        var normalizedPath = targetUrl.pathname + targetUrl.search + targetUrl.hash;
        var token = ++activeNavigationToken;

        abortActiveNavigation();
        abortActiveFormSubmission();
        activeNavigationController = createAbortController();

        cleanupBeforeSpaNavigation();
        setSpaLoadingState(true);

        return fetchSpaPage(normalizedPath, {
            force: !!options.force,
            signal: activeNavigationController ? activeNavigationController.signal : null
        })
        .then(function (data) {
            if (token !== activeNavigationToken) return false;
            if (!data || !data.html) return false;
            if (data.title) document.title = decodeURIComponent(data.title);
            return processSpaResponse(data.html, data.url || normalizedPath, historyStrategy || 'PUSH', token);
        })
        .catch(function (err) {
            if (err && err.name === 'AbortError') return false;
            console.error('Fallo de red en SPA:', err);
            window.location.href = normalizedPath;
            return false;
        })
        .finally(function () {
            if (token === activeNavigationToken) {
                finishSpaLoadingState();
                activeNavigationController = null;
            }
        });
    }

    function processSpaResponse(html, url, historyStrategy, token) {
        var temp = document.createElement('div');
        temp.innerHTML = html;
        var newMain = temp.querySelector('.app-main');

        if (!newMain) {
            window.location.href = url;
            return Promise.resolve(false);
        }

        // En respuestas SPA el encabezado y pie globales no se imprimen. Por
        // ello, los recursos que el módulo dejó después de </main> también
        // pertenecen a la vista y deben viajar con el nuevo contenedor.
        Array.prototype.slice.call(temp.querySelectorAll('style, link[rel~="stylesheet"], script')).forEach(function (asset) {
            if (!newMain.contains(asset)) newMain.appendChild(asset);
        });

        cleanupBeforeSpaNavigation();
        document.dispatchEvent(new CustomEvent('icy:spa-before-render', { detail: { url: url } }));

        if (historyStrategy === 'PUSH') {
            if (window.location.pathname + window.location.search + window.location.hash !== normalizeSpaPath(url)) {
                history.pushState({ spa: true }, '', url);
            }
        } else if (historyStrategy === 'REPLACE') {
            history.replaceState({ spa: true }, '', url);
        }

        var mainContainer = document.querySelector('.app-main');
        if (mainContainer) {
            mainContainer.replaceWith(newMain);
        } else {
            var shell = document.querySelector('.app-shell') || document.body;
            shell.appendChild(newMain);
        }

        updateSidebarActiveLink(window.location.pathname);
        closeMobileSidebar();

        var scrollTarget = newMain;
        if (scrollTarget && typeof scrollTarget.scrollTo === 'function') {
            scrollTarget.scrollTo({ top: 0, left: 0, behavior: 'auto' });
        }

        return executeScripts(newMain).then(function () {
            if (token && token !== activeNavigationToken) return false;
            if (window.lucide) lucide.createIcons();
            enhanceModalFooterButtons(newMain);
            initOpportunityValidation();
            initNotifications();
            var moduleName = resolveModuleName(newMain, url);
            var detail = { main: newMain, url: url, module: moduleName };
            mountRegisteredModule(moduleName, detail);
            document.dispatchEvent(new CustomEvent('icy:spa-render', { detail: detail }));
            document.dispatchEvent(new CustomEvent('icy:spa-after-render', { detail: detail }));
            return true;
        });
    }

    function shouldSkipExternalScript(src) {
        if (!src) return false;
        var lower = src.toLowerCase();
        if (lower.indexOf('cdn.jsdelivr.net/npm/chart.js') !== -1 && window.Chart) return true;
        if (lower.indexOf('unpkg.com/lucide') !== -1 && window.lucide) return true;
        if (lower.indexOf('accounts.google.com/gsi/client') !== -1 && window.google && window.google.accounts) return true;
        return false;
    }

    function executeOneScript(oldScript) {
        return new Promise(function (resolve) {
            try {
                var newScript = document.createElement('script');
                Array.prototype.slice.call(oldScript.attributes).forEach(function (attr) {
                    newScript.setAttribute(attr.name, attr.value);
                });

                if (oldScript.src) {
                    if (shouldSkipExternalScript(oldScript.src)) {
                        oldScript.parentNode.removeChild(oldScript);
                        resolve();
                        return;
                    }
                    newScript.onload = function () { resolve(); };
                    newScript.onerror = function () {
                        console.warn('No fue posible cargar script SPA:', oldScript.src);
                        resolve();
                    };
                    oldScript.parentNode.replaceChild(newScript, oldScript);
                } else {
                    newScript.text = oldScript.textContent || oldScript.innerText || '';
                    oldScript.parentNode.replaceChild(newScript, oldScript);
                    resolve();
                }
            } catch (err) {
                console.error('Error ejecutando script SPA:', err);
                resolve();
            }
        });
    }

    function executeScripts(container) {
        var scripts = Array.prototype.slice.call(container.querySelectorAll('script'));
        return scripts.reduce(function (chain, script) {
            return chain.then(function () { return executeOneScript(script); });
        }, Promise.resolve());
    }

    window.ICY_ENHANCE_MODAL_BUTTONS = enhanceModalFooterButtons;
    window.ICY_SPA_CLEANUP = cleanupBeforeSpaNavigation;
    window.ICY_SPA_REGISTER_CLEANUP = registerSpaCleanup;
    window.ICY_SPA_REGISTER_MODULE = registerModule;
    window.ICY_SPA_ON = registerAutoCleanupListener;
    window.ICY_SPA_SET_TIMEOUT = registerAutoCleanupTimeout;
    window.ICY_SPA_SET_INTERVAL = registerAutoCleanupInterval;
    window.ICY_SPA_CLEAR_CACHE = clearSpaCache;
    window.ICY_SPA_IS_NAVIGABLE = function (path) {
        var url = sameOriginUrl(path);
        return !!(url && isSpaNavigableUrl(url));
    };
    window.ICY_SPA_PREFETCH = function (path) { return fetchSpaPage(path).catch(function () {}); };
    window.ICY_SPA_NAVIGATE = function (path, options) {
        options = options || {};
        return navigateTo(path, options.replace ? 'REPLACE' : 'PUSH', options);
    };
    window.ICY_SPA_RELOAD = function (options) {
        options = options || {};
        options.force = true;
        return navigateTo(window.location.pathname + window.location.search + window.location.hash, 'REPLACE', options);
    };
    window.ICY_SPA_REFRESH = window.ICY_SPA_RELOAD;
    window.ICY_SPA_GO = function (path, options) {
        options = options || {};
        var url = sameOriginUrl(path);
        if (url && isSpaNavigableUrl(url)) {
            return navigateTo(url.pathname + url.search + url.hash, options.replace ? 'REPLACE' : 'PUSH', options);
        }
        window.location.href = path;
        return Promise.resolve(false);
    };


    // ---------------------------------------------------------------------
    // Fragment/modals shared by CRM pages
    // ---------------------------------------------------------------------
    window.ICY_EXECUTE_FRAGMENT_SCRIPTS = function (container) {
        if (!container) return Promise.resolve();
        var scripts = Array.prototype.slice.call(container.querySelectorAll('script'));
        return scripts.reduce(function (chain, sourceScript) {
            return chain.then(function () {
                return new Promise(function (resolve) {
                    try {
                        var transient = document.createElement('script');
                        transient.setAttribute('data-icy-fragment-script', 'true');
                        Array.prototype.slice.call(sourceScript.attributes).forEach(function (attr) {
                            if (attr.name !== 'id') transient.setAttribute(attr.name, attr.value);
                        });
                        if (sourceScript.src) {
                            transient.onload = function () { transient.remove(); resolve(); };
                            transient.onerror = function () { transient.remove(); resolve(); };
                            transient.src = sourceScript.src;
                            document.body.appendChild(transient);
                        } else {
                            transient.text = sourceScript.textContent || '';
                            document.body.appendChild(transient);
                            transient.remove();
                            resolve();
                        }
                        sourceScript.remove();
                    } catch (error) {
                        console.error('Error ejecutando script de fragmento:', error);
                        resolve();
                    }
                });
            });
        }, Promise.resolve());
    };

    window.ICY_OPEN_OPPORTUNITY_MODAL = function (event, opportunityId) {
        if (event) {
            if (window.kanbanDidPan || (event.target && event.target.closest && event.target.closest('.stage-menu-trigger, .stage-dropdown-menu, .crm-close-card'))) return Promise.resolve(false);
            event.preventDefault();
        }
        var id = parseInt(opportunityId, 10);
        if (!id) return Promise.resolve(false);

        var existing = document.getElementById('oppViewModalBackdrop');
        if (existing) existing.remove();
        document.querySelectorAll('script[data-icy-fragment-script="true"]').forEach(function (script) { script.remove(); });

        var loader = document.createElement('div');
        loader.id = 'oppViewModalBackdrop';
        loader.className = 'product-modal-backdrop show';
        loader.style.display = 'flex';
        loader.style.zIndex = '1040';
        loader.innerHTML = '<div style="color:var(--text-primary);display:flex;flex-direction:column;align-items:center;gap:12px"><i data-lucide="loader" class="spin-icon" style="width:32px;height:32px"></i><span>Cargando oportunidad...</span></div>';
        document.body.appendChild(loader);
        if (window.lucide) window.lucide.createIcons();

        return fetch('/modules/crm/opportunity.php?id=' + encodeURIComponent(id) + '&modal=1', {
            headers: { 'X-Requested-With': 'XMLHttpRequest' },
            credentials: 'same-origin'
        }).then(function (response) {
            if (!response.ok) throw new Error('No fue posible cargar la oportunidad.');
            return response.text();
        }).then(function (html) {
            if (!loader.isConnected) return false;
            loader.outerHTML = html;
            var modal = document.getElementById('oppViewModalBackdrop');
            return window.ICY_EXECUTE_FRAGMENT_SCRIPTS(modal).then(function () {
                if (window.lucide) window.lucide.createIcons();
                if (window.ICY_ENHANCE_MODAL_BUTTONS) window.ICY_ENHANCE_MODAL_BUTTONS(modal);
                return true;
            });
        }).catch(function (error) {
            if (loader && loader.isConnected) loader.remove();
            alert(error.message || 'Error al cargar la oportunidad.');
            return false;
        });
    };

    window.openOppViewModal = window.ICY_OPEN_OPPORTUNITY_MODAL;

    window.ICY_OPEN_CLOSE_OPPORTUNITY = function (event, opportunityId) {
        if (event) { event.preventDefault(); event.stopPropagation(); }
        var target = '/modules/crm/embudo.php?close_id=' + encodeURIComponent(parseInt(opportunityId, 10) || 0);
        if (window.ICY_SPA_GO) return window.ICY_SPA_GO(target, { force: true });
        window.location.href = target;
        return Promise.resolve(false);
    };
    window.openCloseOpportunityModal = window.ICY_OPEN_CLOSE_OPPORTUNITY;

    // ---------------------------------------------------------------------
    // Notifications
    // ---------------------------------------------------------------------
    var notificationsLoadedOnce = false;

    function formatNotificationDate(value) {
        if (!value) return '';
        var date = new Date(String(value).replace(' ', 'T'));
        if (isNaN(date.getTime())) return value;
        return date.toLocaleDateString('es-CL', { day: '2-digit', month: '2-digit' }) + ' ' + date.toLocaleTimeString('es-CL', { hour: '2-digit', minute: '2-digit' });
    }

    function notificationIcon(priority) {
        if (priority === 'critical') return 'circle-alert';
        if (priority === 'warning') return 'triangle-alert';
        if (priority === 'success') return 'circle-check';
        return 'bell';
    }

    function updateNotificationBadge(count) {
        var badge = document.getElementById('notificationCountBadge');
        var subtitle = document.getElementById('notificationDropdownSubtitle');
        count = parseInt(count || 0, 10);
        if (badge) {
            badge.textContent = count > 0 ? (count > 99 ? '99+' : String(count)) : '';
            badge.classList.toggle('is-visible', count > 0);
            badge.hidden = count <= 0;
            badge.setAttribute('aria-hidden', count > 0 ? 'false' : 'true');
        }
        if (subtitle) subtitle.textContent = count + ' no leídas';
    }

    function renderNotifications(items) {
        var list = document.getElementById('notificationDropdownList');
        if (!list) return;

        if (!items || !items.length) {
            list.innerHTML = '<div class="notification-empty">Sin notificaciones recientes.</div>';
            if (window.lucide) lucide.createIcons();
            return;
        }

        list.innerHTML = items.map(function (item) {
            var unread = !item.read_at;
            var priority = item.priority || 'info';
            var href = item.url || '/modules/notifications/';
            return '<a class="notification-item notification-priority-' + escapeHtml(priority) + (unread ? ' is-unread' : '') + '" href="' + escapeHtml(href) + '" data-notification-link data-notification-id="' + escapeHtml(item.id) + '">' +
                '<span class="notification-item-icon"><i data-lucide="' + notificationIcon(priority) + '"></i></span>' +
                '<span>' +
                    '<h4>' + escapeHtml(item.title) + '</h4>' +
                    (item.body ? '<p>' + escapeHtml(item.body) + '</p>' : '') +
                    '<time>' + escapeHtml(formatNotificationDate(item.created_at)) + '</time>' +
                '</span>' +
            '</a>';
        }).join('');

        if (window.lucide) lucide.createIcons();
    }

    function fetchNotifications() {
        var badge = document.getElementById('notificationCountBadge');
        if (!badge) return Promise.resolve();
        return fetch('/api/notifications/recent.php?limit=10', { credentials: 'same-origin' })
            .then(function (response) { return response.json(); })
            .then(function (data) {
                if (!data || !data.success) return;
                updateNotificationBadge(data.unread_count || 0);
                renderNotifications(data.notifications || []);
                notificationsLoadedOnce = true;
            })
            .catch(function () {
                var list = document.getElementById('notificationDropdownList');
                if (list) list.innerHTML = '<div class="notification-empty">No fue posible cargar notificaciones.</div>';
            });
    }

    function resetNotificationDropdownInlineStyles(dropdown) {
        if (!dropdown) return;
        dropdown.style.position = '';
        dropdown.style.top = '';
        dropdown.style.right = '';
        dropdown.style.left = '';
        dropdown.style.width = '';
        dropdown.style.maxHeight = '';
        dropdown.style.zIndex = '';
        dropdown.style.display = '';
    }

    function applyNotificationDropdownMobileFallback(dropdown) {
        if (!dropdown) return;
        if (window.matchMedia && window.matchMedia('(max-width: 760px)').matches) {
            var topbar = document.querySelector('.topbar');
            var top = topbar ? Math.max(60, Math.round(topbar.getBoundingClientRect().bottom + 8)) : 72;
            dropdown.style.position = 'fixed';
            dropdown.style.top = top + 'px';
            dropdown.style.left = '12px';
            dropdown.style.right = '12px';
            dropdown.style.width = 'auto';
            dropdown.style.maxHeight = 'calc(100dvh - ' + (top + 12) + 'px)';
            dropdown.style.zIndex = '9999';
            dropdown.style.display = 'flex';
        } else {
            resetNotificationDropdownInlineStyles(dropdown);
        }
    }

    function openNotificationsDropdown() {
        var dropdown = document.getElementById('notificationDropdown');
        var toggle = document.getElementById('notificationBellToggle');
        if (!dropdown || !toggle) return;
        closeUserDropdown();
        dropdown.hidden = false;
        dropdown.classList.add('is-open');
        document.body.classList.add('is-notification-dropdown-open');
        dropdown.setAttribute('aria-hidden', 'false');
        toggle.setAttribute('aria-expanded', 'true');
        applyNotificationDropdownMobileFallback(dropdown);
        if (!notificationsLoadedOnce) fetchNotifications();
    }

    function closeNotificationsDropdown() {
        var dropdown = document.getElementById('notificationDropdown');
        var toggle = document.getElementById('notificationBellToggle');
        if (!dropdown || !toggle) return;
        dropdown.classList.remove('is-open');
        document.body.classList.remove('is-notification-dropdown-open');
        dropdown.setAttribute('aria-hidden', 'true');
        toggle.setAttribute('aria-expanded', 'false');
        resetNotificationDropdownInlineStyles(dropdown);
        dropdown.hidden = true;
    }

    function closeUserDropdown() {
        var dropdown = document.getElementById('geminiUserDropdown') || document.getElementById('userDropdown');
        var toggle = document.getElementById('geminiUserToggle') || document.getElementById('userMenuToggle');
        if (dropdown) {
            dropdown.classList.remove('is-open');
            dropdown.setAttribute('aria-hidden', 'true');
        }
        if (toggle) toggle.setAttribute('aria-expanded', 'false');
    }

    function openUserDropdown() {
        var dropdown = document.getElementById('geminiUserDropdown') || document.getElementById('userDropdown');
        var toggle = document.getElementById('geminiUserToggle') || document.getElementById('userMenuToggle');
        if (!dropdown || !toggle) return;
        closeNotificationsDropdown();
        dropdown.classList.add('is-open');
        dropdown.setAttribute('aria-hidden', 'false');
        toggle.setAttribute('aria-expanded', 'true');
    }

    function openNotificationPreferencesModal() {
        var modal = document.getElementById('notificationPreferencesModal');
        if (!modal) return;
        modal.style.display = 'flex';
        modal.setAttribute('aria-hidden', 'false');
        window.requestAnimationFrame(function () { modal.classList.add('show'); });
        document.body.classList.add('is-modal-open');
        if (window.lucide) lucide.createIcons();
    }

    function closeNotificationPreferencesModal(immediate) {
        var modal = document.getElementById('notificationPreferencesModal');
        if (!modal) return;
        modal.classList.remove('show');
        modal.setAttribute('aria-hidden', 'true');
        if (immediate) {
            modal.style.display = 'none';
        } else {
            window.setTimeout(function () {
                if (!modal.classList.contains('show')) modal.style.display = 'none';
            }, 80);
        }
        document.body.classList.remove('is-modal-open');
    }

    function openNotificationGroupsModal() {
        var modal = document.getElementById('notificationGroupsModal');
        if (!modal) return;
        modal.style.display = 'flex';
        modal.setAttribute('aria-hidden', 'false');
        window.requestAnimationFrame(function () { modal.classList.add('show'); });
        document.body.classList.add('is-modal-open');
        if (window.lucide) lucide.createIcons();
    }

    function closeNotificationGroupsModal(immediate) {
        var modal = document.getElementById('notificationGroupsModal');
        if (!modal) return;
        modal.classList.remove('show');
        modal.setAttribute('aria-hidden', 'true');
        if (immediate) {
            modal.style.display = 'none';
        } else {
            window.setTimeout(function () {
                if (!modal.classList.contains('show')) modal.style.display = 'none';
            }, 80);
        }
        document.body.classList.remove('is-modal-open');
    }

    function markNotificationRead(id) {
        if (!id) return Promise.resolve();
        var form = new FormData();
        form.append('id', id);
        return fetch('/api/notifications/mark_read.php', { method: 'POST', body: form, credentials: 'same-origin' })
            .then(function (response) { return response.json(); })
            .then(function (data) {
                if (data && data.success) updateNotificationBadge(data.unread_count || 0);
            })
            .catch(function () {});
    }

    function markAllNotificationsRead() {
        return fetch('/api/notifications/mark_all_read.php', { method: 'POST', credentials: 'same-origin' })
            .then(function (response) { return response.json(); })
            .then(function (data) {
                if (data && data.success) {
                    updateNotificationBadge(0);
                    fetchNotifications();
                }
            })
            .catch(function () {});
    }

    function initNotifications() {
        if (document.getElementById('notificationCountBadge')) fetchNotifications();
    }

    window.addEventListener('resize', function () {
        var dropdown = document.getElementById('notificationDropdown');
        if (dropdown && dropdown.classList.contains('is-open')) applyNotificationDropdownMobileFallback(dropdown);
    });

    // ---------------------------------------------------------------------
    // Global UI event delegation
    // ---------------------------------------------------------------------
    function closeMobileSidebar() {
        var sidebar = document.getElementById('sidebar');
        var backdrop = document.getElementById('sidebarBackdrop');
        if (sidebar) sidebar.classList.remove('is-open');
        if (backdrop) backdrop.classList.remove('is-open');
        document.body.classList.remove('is-mobile-sidebar-open');
    }

    document.addEventListener('change', function (e) {
        var groupUserCheckbox = e.target.closest ? e.target.closest('.notification-group-user input[type="checkbox"]') : null;
        if (groupUserCheckbox) {
            var label = groupUserCheckbox.closest('.notification-group-user');
            if (label) label.classList.toggle('is-selected', groupUserCheckbox.checked);
        }
    });

    // La fase de captura garantiza el cierre aunque un módulo detenga el click.
    document.addEventListener('pointerdown', function (e) {
        var notificationMenu = document.getElementById('notificationMenu');
        var notificationDropdown = document.getElementById('notificationDropdown');
        if (notificationDropdown && notificationDropdown.classList.contains('is-open')
            && (!notificationMenu || !notificationMenu.contains(e.target))) {
            closeNotificationsDropdown();
        }

        var userDropdown = document.getElementById('geminiUserDropdown') || document.getElementById('userDropdown');
        var userToggle = document.getElementById('geminiUserToggle') || document.getElementById('userMenuToggle');
        if (userDropdown && userDropdown.classList.contains('is-open')
            && !userDropdown.contains(e.target)
            && (!userToggle || !userToggle.contains(e.target))) {
            closeUserDropdown();
        }

        document.querySelectorAll('details[data-close-outside][open]').forEach(function (menu) {
            if (!menu.contains(e.target)) menu.removeAttribute('open');
        });
    }, true);

    document.addEventListener('keydown', function (e) {
        if (e.key !== 'Escape') return;
        document.querySelectorAll('details[data-close-outside][open]').forEach(function (menu) {
            menu.removeAttribute('open');
        });
    });

    document.addEventListener('click', function (e) {
        var toggleBtn = e.target.closest ? e.target.closest('#sidebarToggle') : null;
        var sidebar = document.getElementById('sidebar');
        var backdrop = document.getElementById('sidebarBackdrop');

        if (toggleBtn) {
            e.preventDefault();
            if (sidebar) sidebar.classList.add('is-open');
            if (backdrop) backdrop.classList.add('is-open');
            document.body.classList.add('is-mobile-sidebar-open');
            return;
        }

        var closeBtn = e.target.closest ? e.target.closest('#mobileSidebarClose') : null;
        var isBackdrop = e.target === backdrop;
        if (closeBtn || isBackdrop) {
            e.preventDefault();
            closeMobileSidebar();
            return;
        }

        var notificationToggle = e.target.closest ? e.target.closest('#notificationBellToggle') : null;
        var notificationDropdown = document.getElementById('notificationDropdown');
        var notificationMenu = document.getElementById('notificationMenu');
        if (notificationToggle) {
            e.preventDefault();
            e.stopPropagation();
            if (notificationDropdown && notificationDropdown.classList.contains('is-open')) closeNotificationsDropdown();
            else openNotificationsDropdown();
            return;
        }

        var notificationMarkAll = e.target.closest ? e.target.closest('#notificationMarkAllRead') : null;
        if (notificationMarkAll) {
            e.preventDefault();
            e.stopPropagation();
            markAllNotificationsRead();
            return;
        }

        var notificationPreferencesOpen = e.target.closest ? e.target.closest('#notificationPreferencesOpen') : null;
        if (notificationPreferencesOpen) {
            e.preventDefault();
            openNotificationPreferencesModal();
            return;
        }

        var notificationPreferencesClose = e.target.closest ? (e.target.closest('#notificationPreferencesClose') || e.target.closest('[data-notification-preferences-close]')) : null;
        if (notificationPreferencesClose) {
            e.preventDefault();
            closeNotificationPreferencesModal(false);
            return;
        }

        var notificationPreferencesModal = document.getElementById('notificationPreferencesModal');
        if (notificationPreferencesModal && e.target === notificationPreferencesModal) {
            e.preventDefault();
            closeNotificationPreferencesModal(false);
            return;
        }

        var notificationGroupsOpen = e.target.closest ? e.target.closest('#notificationGroupsOpen') : null;
        if (notificationGroupsOpen) {
            e.preventDefault();
            openNotificationGroupsModal();
            return;
        }

        var notificationGroupsClose = e.target.closest ? (e.target.closest('#notificationGroupsClose') || e.target.closest('[data-notification-groups-close]')) : null;
        if (notificationGroupsClose) {
            e.preventDefault();
            closeNotificationGroupsModal(false);
            return;
        }

        var notificationGroupsModal = document.getElementById('notificationGroupsModal');
        if (notificationGroupsModal && e.target === notificationGroupsModal) {
            e.preventDefault();
            closeNotificationGroupsModal(false);
            return;
        }

        var notificationInlineRead = e.target.closest ? e.target.closest('[data-notification-mark-read]') : null;
        if (notificationInlineRead) {
            e.preventDefault();
            markNotificationRead(notificationInlineRead.getAttribute('data-notification-mark-read')).then(function () {
                var row = notificationInlineRead.closest('.notification-row');
                if (row) {
                    row.classList.remove('is-unread');
                    row.classList.add('is-read');
                    notificationInlineRead.remove();
                }
            });
            return;
        }

        var notificationLink = e.target.closest ? e.target.closest('[data-notification-link]') : null;
        if (notificationLink) {
            markNotificationRead(notificationLink.getAttribute('data-notification-id'));
            closeNotificationsDropdown();
        }

        if (notificationDropdown && notificationMenu && !notificationMenu.contains(e.target)) closeNotificationsDropdown();

        var desktopToggle = e.target.closest ? e.target.closest('#desktopSidebarToggle') : null;
        if (desktopToggle) {
            e.preventDefault();
            if (window.innerWidth > 760) {
                document.body.classList.toggle('is-sidebar-collapsed');
                localStorage.setItem('icylab_sidebar_collapsed', document.body.classList.contains('is-sidebar-collapsed'));
            }
            return;
        }

        var sidebarLogoLink = e.target.closest ? e.target.closest('#sidebarLogoLink') : null;
        if (sidebarLogoLink && document.body.classList.contains('is-sidebar-collapsed') && window.innerWidth > 760) {
            document.body.classList.remove('is-sidebar-collapsed');
            localStorage.setItem('icylab_sidebar_collapsed', 'false');
        }

        var userMenuToggle = e.target.closest ? (e.target.closest('#geminiUserToggle') || e.target.closest('#userMenuToggle')) : null;
        var userDropdown = document.getElementById('geminiUserDropdown') || document.getElementById('userDropdown');
        if (userMenuToggle && userDropdown) {
            e.preventDefault();
            e.stopPropagation();
            if (userDropdown.classList.contains('is-open')) closeUserDropdown();
            else openUserDropdown();
            return;
        } else if (userDropdown && !userDropdown.contains(e.target)) {
            closeUserDropdown();
        }

        var themeToggleBtn = e.target.closest ? (e.target.closest('#themeToggleBtn') || e.target.closest('#themeToggle')) : null;
        if (themeToggleBtn) {
            e.preventDefault();
            e.stopPropagation();
            var current = getAppliedTheme();
            var nextTheme = current === 'dark' ? 'light' : 'dark';
            applyTheme(nextTheme, true);
            if (window.lucide) lucide.createIcons();
            return;
        }

        var selectTrigger = e.target.closest ? e.target.closest('.custom-select-trigger') : null;
        var selectOption = e.target.closest ? e.target.closest('.custom-select-option') : null;
        var selectBox = e.target.closest ? e.target.closest('.custom-select') : null;

        if (!selectBox) {
            document.querySelectorAll('.custom-select.is-open').forEach(function (s) { s.classList.remove('is-open'); });
        } else {
            if (selectTrigger) {
                e.preventDefault();
                document.querySelectorAll('.custom-select.is-open').forEach(function (s) {
                    if (s !== selectBox) s.classList.remove('is-open');
                });
                selectBox.classList.toggle('is-open');
                return;
            }
            if (selectOption) {
                e.preventDefault();
                var value = selectOption.getAttribute('data-value');
                var label = selectOption.textContent.trim();
                var input = selectBox.querySelector('input[type="hidden"]');
                var labelTarget = selectBox.querySelector('.custom-select-label');
                if (input) input.value = value;
                if (labelTarget) labelTarget.textContent = label;
                selectBox.querySelectorAll('.custom-select-option').forEach(function (item) { item.classList.remove('is-selected'); });
                selectOption.classList.add('is-selected');
                selectBox.classList.remove('is-open');
                if (input) input.dispatchEvent(new Event('change', { bubbles: true }));
                return;
            }
        }

        var forceSyncFacto = e.target.closest ? e.target.closest('#forceSyncFacto') : null;
        if (forceSyncFacto) {
            e.preventDefault();
            var originalHtml = forceSyncFacto.innerHTML;
            forceSyncFacto.classList.add('is-loading');
            forceSyncFacto.innerHTML = '<i data-lucide="loader" class="spin-icon"></i> <span class="mobile-hidden">Sincronizando...</span>';
            if (window.lucide) lucide.createIcons();

            fetch('/api/dashboard/force_sync_facto.php', { method: 'POST', credentials: 'same-origin' })
                .then(function (response) {
                    return response.text().then(function (body) {
                        var data = null;
                        try {
                            data = body ? JSON.parse(body) : null;
                        } catch (parseError) {
                            var excerpt = (body || '').replace(/\s+/g, ' ').trim().slice(0, 500);
                            throw new Error(excerpt || ('Respuesta inválida del servidor (HTTP ' + response.status + ').'));
                        }

                        if (!response.ok || !data || !data.success) {
                            throw new Error((data && data.message) ? data.message : ('Error al sincronizar FACTO (HTTP ' + response.status + ').'));
                        }

                        return data;
                    });
                })
                .then(function () {
                    clearSpaCache();
                    window.ICY_SPA_RELOAD({ force: true });
                })
                .catch(function (error) {
                    console.error('ICYLAB FACTO sync error:', error);
                    alert((error && error.message) ? error.message : 'Error al ejecutar la sincronización.');
                })
                .finally(function () {
                    forceSyncFacto.classList.remove('is-loading');
                    forceSyncFacto.innerHTML = originalHtml;
                    if (window.lucide) lucide.createIcons();
                });
            return;
        }

        var googleAuthBtn = e.target.closest ? e.target.closest('#btn-google-auth') : null;
        if (googleAuthBtn) {
            e.preventDefault();
            if (typeof google === 'undefined' || !google.accounts) {
                alert('La libreria de Google aun no esta cargada. Por favor, recarga la pagina.');
                return;
            }

            var client = google.accounts.oauth2.initCodeClient({
                client_id: 'TU_GOOGLE_CLIENT_ID',
                scope: 'email profile https://www.googleapis.com/auth/calendar.events',
                ux_mode: 'popup',
                callback: function (response) {
                    if (!response.code) return;
                    var originalBtnText = googleAuthBtn.innerHTML;
                    googleAuthBtn.innerHTML = '<i data-lucide="loader" class="spin-icon"></i> Autenticando...';
                    googleAuthBtn.disabled = true;
                    if (window.lucide) lucide.createIcons();

                    fetch('/auth/google_callback.php', {
                        method: 'POST',
                        headers: { 'Content-Type': 'application/json' },
                        body: JSON.stringify({ code: response.code }),
                        credentials: 'same-origin'
                    })
                    .then(function (res) { return res.json(); })
                    .then(function (data) {
                        if (data.success) {
                            window.ICY_SPA_NAVIGATE('/modules/dashboard/', { replace: true, force: true });
                        } else {
                            alert(data.message || 'Error de validacion con Google.');
                            googleAuthBtn.innerHTML = originalBtnText;
                            googleAuthBtn.disabled = false;
                            if (window.lucide) lucide.createIcons();
                        }
                    })
                    .catch(function (err) {
                        console.error(err);
                        alert('Error de red al conectar con el servidor.');
                        googleAuthBtn.innerHTML = originalBtnText;
                        googleAuthBtn.disabled = false;
                        if (window.lucide) lucide.createIcons();
                    });
                }
            });
            client.requestCode();
            return;
        }

        var link = e.target.closest ? e.target.closest('a') : null;
        if (link && link.href && isLeftPlainClick(e)) {
            if (link.target === '_blank' || link.hasAttribute('data-no-spa') || link.hasAttribute('download')) return;
            if (link.href.indexOf('mailto:') === 0 || link.href.indexOf('tel:') === 0 || link.href.indexOf('javascript:') === 0) return;
            var url = sameOriginUrl(link.href);
            if (!url || !isSpaNavigableUrl(url)) return;
            if (url.pathname === window.location.pathname && url.search === window.location.search && url.hash) return;

            e.preventDefault();
            navigateTo(url.pathname + url.search + url.hash, 'PUSH');
        }
    });

    document.addEventListener('keydown', function (event) {
        if (event.key === 'Escape') {
            closeMobileSidebar();
            closeUserDropdown();
            closeNotificationsDropdown();
            closeNotificationPreferencesModal(false);
            closeNotificationGroupsModal(false);
            document.querySelectorAll('.custom-select.is-open').forEach(function (s) { s.classList.remove('is-open'); });
        }
    });

    // ---------------------------------------------------------------------
    // Opportunity form validation
    // ---------------------------------------------------------------------
    function initOpportunityValidation() {
        var form = document.getElementById('form-create-opportunity');
        if (!form || form.dataset.validationBound === '1') return;
        form.dataset.validationBound = '1';

        var btn = document.getElementById('saveOpportunityBtn');
        if (!btn) return;

        var requiredFields = ['title', 'stage', 'source', 'expected_close_date', 'next_action', 'next_action_date'];
        var requiredNewCustomerFields = ['new_customer_name', 'business_activity', 'country', 'address', 'district', 'city', 'region'];
        var customerIdField = form.querySelector('[name="customer_id"]');
        var newCustomerBlock = form.querySelector('#modalNewCustomerBlock, #newCustomerBlock');

        function hasValue(name) {
            var field = form.querySelector('[name="' + name + '"]');
            return !!field && String(field.value || '').trim() !== '';
        }

        function isNewCustomerMode() {
            return !!newCustomerBlock && window.getComputedStyle(newCustomerBlock).display !== 'none';
        }

        function validateForm() {
            var valid = true;
            requiredFields.forEach(function (name) {
                if (!hasValue(name)) valid = false;
            });

            var hasExistingCustomer = !!customerIdField && String(customerIdField.value || '').trim() !== '';
            var hasCompleteNewCustomer = isNewCustomerMode() && requiredNewCustomerFields.every(hasValue);
            valid = valid && (hasExistingCustomer || hasCompleteNewCustomer);

            btn.disabled = !valid;
            btn.setAttribute('aria-disabled', valid ? 'false' : 'true');
        }

        form.addEventListener('input', validateForm);
        form.addEventListener('change', validateForm);
        form.addEventListener('icy:opportunity-customer-mode', validateForm);
        validateForm();
    }

    // ---------------------------------------------------------------------
    // SPA form interception
    // ---------------------------------------------------------------------
    function submitSpaForm(form, event) {
        var formAction = form.getAttribute('action') || window.location.href;
        var url = sameOriginUrl(formAction);
        if (!url || !isSpaNavigableUrl(url)) {
            form.submit();
            return;
        }

        var method = (form.getAttribute('method') || 'GET').toUpperCase();
        var formData = new FormData(form);
        var submitter = event && event.submitter ? event.submitter : document.activeElement;
        if (submitter && submitter.name && !formData.has(submitter.name)) {
            formData.append(submitter.name, submitter.value || '');
        }

        if (method === 'GET') {
            var params = new URLSearchParams(formData);
            var getPath = url.pathname + (params.toString() ? '?' + params.toString() : '') + url.hash;
            navigateTo(getPath, 'PUSH', { force: true });
            return;
        }

        var submitBtn = form.querySelector('button[type="submit"], input[type="submit"]');
        var originalBtnHtml = '';
        if (submitBtn) {
            originalBtnHtml = submitBtn.innerHTML || submitBtn.value || '';
            submitBtn.disabled = true;
            if ('innerHTML' in submitBtn) submitBtn.innerHTML = '<i data-lucide="loader" class="spin-icon"></i> Procesando...';
            else submitBtn.value = 'Procesando...';
            if (window.lucide) lucide.createIcons();
        }

        var token = ++activeNavigationToken;
        abortActiveNavigation();
        abortActiveFormSubmission();
        activeFormController = createAbortController();
        clearSpaCache();
        cleanupBeforeSpaNavigation();
        setSpaLoadingState(true);

        var fetchOptions = {
            method: method,
            headers: {
                'X-SPA-Request': 'true',
                'X-Requested-With': 'XMLHttpRequest'
            },
            credentials: 'same-origin',
            redirect: 'follow',
            body: formData
        };
        if (activeFormController) fetchOptions.signal = activeFormController.signal;

        fetch(url.pathname + url.search + url.hash, fetchOptions)
            .then(function (response) {
                if (!response.ok) throw new Error('HTTP ' + response.status);
                var contentType = response.headers.get('content-type') || '';
                var finalUrl = response.url || url.href;
                var newTitle = response.headers.get('X-Page-Title');
                if (newTitle) document.title = decodeURIComponent(newTitle);

                if (contentType.indexOf('application/json') !== -1) {
                    return response.json().then(function (json) {
                        var jsonEvent = new CustomEvent('icy:spa-form-json', { cancelable: true, detail: { form: form, response: json, url: finalUrl } });
                        document.dispatchEvent(jsonEvent);
                        if (!jsonEvent.defaultPrevented) {
                            if (json && json.redirect) return navigateTo(json.redirect, 'REPLACE', { force: true });
                            if (json && json.redirect_url) return navigateTo(json.redirect_url, 'REPLACE', { force: true });
                            return window.ICY_SPA_RELOAD({ force: true });
                        }
                        return true;
                    });
                }

                return response.text().then(function (html) {
                    if (token !== activeNavigationToken) return false;
                    return processSpaResponse(html, finalUrl, 'REPLACE', token);
                });
            })
            .catch(function (err) {
                if (err && err.name === 'AbortError') return;
                console.error('Fallo de red en SPA Form:', err);
                form.submit();
            })
            .finally(function () {
                if (token === activeNavigationToken) finishSpaLoadingState();
                activeFormController = null;
                if (submitBtn) {
                    submitBtn.disabled = false;
                    if ('innerHTML' in submitBtn) submitBtn.innerHTML = originalBtnHtml;
                    else submitBtn.value = originalBtnHtml;
                    if (window.lucide) lucide.createIcons();
                }
            });
    }

    document.addEventListener('submit', function (e) {
        if (e.defaultPrevented) return;
        var form = e.target;
        if (!form || form.nodeName !== 'FORM') return;
        if (form.hasAttribute('data-no-spa') || form.target === '_blank') return;
        if (form.action && form.action.indexOf('quote_preview.php') !== -1) return;

        e.preventDefault();
        submitSpaForm(form, e);
    });

    document.addEventListener('pointerover', function (e) {
        var link = e.target.closest ? e.target.closest('a') : null;
        prefetchSpaLink(link);
    }, { passive: true });

    document.addEventListener('touchstart', function (e) {
        var link = e.target.closest ? e.target.closest('a') : null;
        prefetchSpaLink(link);
    }, { passive: true });

    document.addEventListener('icy:spa-render', function () {
        if (window.lucide) lucide.createIcons();
    });

    window.setInterval(function () {
        if (document.visibilityState === 'visible') fetchNotifications();
    }, 60000);

    window.addEventListener('popstate', function () {
        navigateTo(window.location.pathname + window.location.search + window.location.hash, 'NONE', { force: true });
    });

    function updateSidebarActiveLink(currentPath) {
        var links = document.querySelectorAll('.sidebar-link');
        links.forEach(function (link) { link.classList.remove('is-active'); });
        var containers = document.querySelectorAll('.sidebar-item-has-children');
        containers.forEach(function (c) { c.classList.remove('is-open'); });

        var bestMatch = null;
        var longestMatch = 0;
        links.forEach(function (link) {
            var href = link.getAttribute('href');
            if (href && currentPath.indexOf(href) === 0 && href !== '/' && href !== '/modules/') {
                if (href.length > longestMatch) {
                    longestMatch = href.length;
                    bestMatch = link;
                }
            }
        });

        if (bestMatch) {
            bestMatch.classList.add('is-active');
            var parentContainer = bestMatch.closest('.sidebar-item-has-children');
            if (parentContainer) parentContainer.classList.add('is-open');
        }
    }
})();
