<?php
// Enable verbose PHP error output for development
@ini_set('display_errors', '1');
@ini_set('display_startup_errors', '1');
error_reporting(E_ALL);


define('BEEZUI_NAV_WIDTH', '60');


// Define project root constant if not already defined
if (!defined('BEEZUI_ROOT')) {
    define('BEEZUI_ROOT', __DIR__);
}

// Bootstrap core early so we can serve partials for HTMX
require_once BEEZUI_ROOT . '/components/bootstrap.php';

mh_define('BEEZUI_PRELOAD', mh_cfg('BEEZUI_PRELOAD', 'True') === 'True');

$tabParam = \Components\Router::tabParam();
$subtabParam = \Components\Router::subtabParam();
$pageParam = \Components\Router::pageParam();

// Detect partial request (HTMX or explicit flag)
$isPartial = (\Components\Support\Htmx::isRequest()) || (mh_get('partial', 0, 'int') == 1);

// Determine requested route name based on context
$routeName = null;
if ($isPartial) {
    $routeName = mh_get($pageParam, null, 'string');
    if ($routeName === null) {
        // Fallback for callers that still send the tab param
        $routeName = mh_get($tabParam, null, 'string');
    }
} else {
    $routeName = mh_get($tabParam, null, 'string');
    if ($routeName === null) {
        // Fallback for callers that only sent the page param
        $routeName = mh_get($pageParam, null, 'string');
    }
}
$routeName = $routeName ? preg_replace('/[^_a-z]/', '', strtolower((string)$routeName)) : 'dashboard';
$tab = $routeName;

// Resolve path from route name
$path = \Components\Router::path($routeName);
if (!$path) {
    // Fallback to conventional pages/<route>.php
    $candidate = 'pages/' . $routeName . '.php';
    $path = is_file(BEEZUI_ROOT . '/' . $candidate) ? $candidate : null;
}

// If this is an HTMX request (or explicit partial), capture and optimize the partial HTML (inline <svg> → CSS)
if ($isPartial) {
    if ($path && is_file(BEEZUI_ROOT . '/' . $path)) {

        // Start SVG optimizer buffering so we can transform the partial output
        $svgOpt = \Components\SvgCssOptimizer::begin([
                'cache_dir' => BEEZUI_ROOT . '/public/cache',
                'web_path' => (defined('BEEZUI_WEB_ROOT') ? BEEZUI_WEB_ROOT : '') . 'public/cache',
                'base64' => false,
                'class_prefix' => 'svg-',
                'size_var' => '--svg-size',
        ]);

        include BEEZUI_ROOT . '/' . $path;

        // Flush transformed content (injects a cache-busted CSS link for the found icons)
        $svgOpt->flush();
        exit;
    } else {
        http_response_code(404);
        echo 'Not found';
        exit;
    }
}
?>
<!DOCTYPE html>
<html class="h-full group/collapse" lang="<?= mh_session('language_code', 'en') ?>">
<head>
    <meta charset="UTF-8"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
    <title>MailBeez Admin</title>
    <!-- Dark mode bootstrap: prevent FOUC by setting class on <html> ASAP -->
    <script>
        try {
            if (localStorage.getItem('color-theme') === 'dark' || (!('color-theme' in localStorage) && window.matchMedia('(prefers-color-scheme: dark)').matches)) {
                // document.documentElement.classList.add('dark');
            } else {
                document.documentElement.classList.remove('dark');
            }
            document.documentElement.classList.remove('dark');
        } catch (e) {
            // no-op
        }
    </script>
    <script>
        // Apply persisted UI density asap to avoid FOUC
        try {
            var savedDensity = localStorage.getItem('ui-density') || 'normal';
            var htmlEl = document.documentElement;
            htmlEl.classList.remove('density-compact', 'density-very-compact');
            if (savedDensity === 'compact') htmlEl.classList.add('density-compact');
            else if (savedDensity === 'very-compact') htmlEl.classList.add('density-very-compact');
        } catch (e) { /* no-op */
        }
    </script>
    <!--    <link rel="stylesheet" href="https://rsms.me/inter/inter.css" />-->
    <link rel="stylesheet"
          href="<?php echo defined('BEEZUI_WEB_ROOT') ? BEEZUI_WEB_ROOT : ''; ?>appui/assets/css/tailwind.output.css?ver=<?= mh_cfg('MAILBEEZ_VERSION') ?>"/>
    <!-- Flag Icons for language flags in i18n fields: https://flagicons.lipis.dev/ -->
    <link rel="stylesheet"
          href="<?php echo defined('BEEZUI_WEB_ROOT') ? BEEZUI_WEB_ROOT : ''; ?>appui/assets/css/flag-icons.min.css?ver=<?= mh_cfg('MAILBEEZ_VERSION') ?>"/>

    <meta name="htmx-config" content='{"scrollBehavior":"smooth","historyCacheSize":5}'>

    <script>
        window.APP_LOCALE = '<?php echo htmlspecialchars(mh_session('language_code', 'en'), ENT_QUOTES); ?>';
        window.MB_MODAL_TYPE_ICONS = <?php echo json_encode(\Components\Modal::getTypeIconsMap()); ?>;
    </script>

    <script defer
            src="<?php echo defined('BEEZUI_WEB_ROOT') ? BEEZUI_WEB_ROOT : ''; ?>appui/assets/js/htmx.min.js?ver=<?= mh_cfg('MAILBEEZ_VERSION') ?>"></script>
    <script>
        document.addEventListener('htmx:beforeSwap', (event) => {
            const xhr = event.detail.xhr;
            const url = xhr.responseURL || '';

            if (url.indexOf('login.php') !== -1) {
                event.detail.shouldSwap = false;
                event.detail.isError = false;

                window.location.href = url;
            }
        });

        document.addEventListener('htmx:configRequest', (event) => {
            if (!event.detail) return;
            const verb = (event.detail.verb || event.detail.method || '').toUpperCase();
            if (verb === 'POST') {
                const tokenHtml = <?= json_encode(defined('MH_POST_TOKEN') ? MH_POST_TOKEN : '') ?>;
                if (tokenHtml) {
                    const template = document.createElement('template');
                    template.innerHTML = tokenHtml.trim();
                    const inputs = template.content.querySelectorAll('input');
                    inputs.forEach(input => {
                        if (input.name) {
                            event.detail.parameters[input.name] = input.value;
                        }
                    });
                }
            }
        });
    </script>
    <!--    <script defer src="-->
    <?php //echo defined('BEEZUI_WEB_ROOT') ? BEEZUI_WEB_ROOT : ''; ?><!--appui/assets/js/preload.min.js?ver=-->
    <?php //= mh_cfg('MAILBEEZ_VERSION') ?><!--"></script>-->
    <script defer src="<?php echo defined('BEEZUI_WEB_ROOT') ? BEEZUI_WEB_ROOT : ''; ?>appui/assets/js/mb-loader.js?ver=<?= mh_cfg('MAILBEEZ_VERSION') ?>"></script>
    <script defer src="<?php echo defined('BEEZUI_WEB_ROOT') ? BEEZUI_WEB_ROOT : ''; ?>appui/assets/js/mb-modal.js?ver=<?= mh_cfg('MAILBEEZ_VERSION') ?>"></script>
    <script defer
            src="<?php echo defined('BEEZUI_WEB_ROOT') ? BEEZUI_WEB_ROOT : ''; ?>public/js/app.js?ver=<?= mh_cfg('MAILBEEZ_VERSION') ?>"></script>

    <style>[x-cloak] {
            display: none !important;
        }</style>
    <script>
        // Global lightweight Alpine validation helper for forms rendered by Engine
        // Usage: Object.assign(existingXData, MBForm.validator(schema))
        window.MBForm = window.MBForm || {};
        window.MBForm.translations = <?php echo json_encode([
            'required' => \mh_lng('UI_VALIDATION_REQUIRED'),
            'email' => \mh_lng('UI_VALIDATION_EMAIL'),
            'min_length' => \mh_lng('UI_VALIDATION_MIN_LENGTH'),
            'max_length' => \mh_lng('UI_VALIDATION_MAX_LENGTH'),
            'integer' => \mh_lng('UI_VALIDATION_INTEGER'),
            'min_value' => \mh_lng('UI_VALIDATION_MIN_VALUE'),
            'max_value' => \mh_lng('UI_VALIDATION_MAX_VALUE'),
            'date' => \mh_lng('UI_VALIDATION_DATE'),
            'date_time' => \mh_lng('UI_VALIDATION_DATE_TIME'),
            'date_after' => \mh_lng('UI_VALIDATION_DATE_AFTER'),
            'date_before' => \mh_lng('UI_VALIDATION_DATE_BEFORE'),
            'compare' => \mh_lng('UI_VALIDATION_COMPARE'),
        ]); ?>;
        window.MBForm._t = function (key, ...args) {
            let s = this.translations[key] || key;
            args.forEach(arg => {
                s = s.replace(/%[ds]/, arg);
            });
            return s;
        };
        window.MBForm.validator = function (schema) {
            return {
                schema: schema || {},
                clientErrors: {},
                _getVal(name) {
                    let v = this.form ? this.form[name] : undefined;
                    if (v === undefined || v === null) return '';
                    if (typeof v === 'boolean') return v ? 'on' : '';
                    return v;
                },
                _isIntString(s) {
                    return /^-?\d+$/.test(String(s));
                },
                _parseDateToTs(val) {
                    if (val === undefined || val === null) return null;
                    const s = String(val).trim();
                    if (!s) return null;
                    if (/^\d{4}-\d{2}-\d{2}$/.test(s)) {
                        const d = new Date(s + 'T00:00:00');
                        if (!isNaN(d)) return Math.floor(d.getTime() / 1000);
                    }
                    const t = Date.parse(s);
                    if (!isNaN(t)) return Math.floor(t / 1000);
                    return null;
                },
                validateField(name) {
                    const r = this.schema[name];
                    const el = this.$root && this.$root.querySelector ? this.$root.querySelector('[name="' + (window.CSS && CSS.escape ? CSS.escape(name) : name) + '"]') : null;
                    if (!el) {
                        this.clientErrors[name] = [];
                        return true;
                    }
                    const v = this._getVal(name);
                    const errs = [];
                    if (!r) {
                        this.clientErrors[name] = [];
                        return true;
                    }
                    if (r.required && String(v).trim() === '') {
                        errs.push(window.MBForm._t('required'));
                    }
                    if (r.type === 'integer' && String(v).trim() !== '') {
                        if (!this._isIntString(v)) {
                            errs.push(window.MBForm._t('integer'));
                        }
                        const num = parseInt(v, 10);
                        if (r.min !== undefined && this._isIntString(r.min) && num < parseInt(r.min, 10)) {
                            errs.push(window.MBForm._t('min_value', r.min));
                        }
                        if (r.max !== undefined && this._isIntString(r.max) && num > parseInt(r.max, 10)) {
                            errs.push(window.MBForm._t('max_value', r.max));
                        }
                    }
                    if (Array.isArray(r.compare)) {
                        for (const cmp of r.compare) {
                            const otherName = cmp.field;
                            if (!otherName) continue;
                            const op = (cmp.op || 'gte').toLowerCase();
                            const offset = cmp.offset || 0;
                            let left = v;
                            let right = this._getVal(otherName);
                            let compared = false;
                            if (this._isIntString(left) && this._isIntString(right)) {
                                left = parseInt(left, 10);
                                right = parseInt(right, 10) + (this._isIntString(offset) ? parseInt(offset, 10) : 0);
                                compared = true;
                            } else {
                                const lTs = this._parseDateToTs(left);
                                const rTs = this._parseDateToTs(right);
                                if (lTs !== null && rTs !== null) {
                                    const days = this._isIntString(offset) ? parseInt(offset, 10) : 0;
                                    left = lTs;
                                    right = rTs + days * 86400;
                                    compared = true;
                                }
                            }
                            if (compared) {
                                let ok = true;
                                switch (op) {
                                    case 'gt':
                                    case '>':
                                        ok = (left > right);
                                        break;
                                    case 'gte':
                                    case '>=':
                                        ok = (left >= right);
                                        break;
                                    case 'lt':
                                    case '<':
                                        ok = (left < right);
                                        break;
                                    case 'lte':
                                    case '<=':
                                        ok = (left <= right);
                                        break;
                                    case 'eq':
                                    case '==':
                                        ok = (left == right);
                                        break;
                                    case 'ne':
                                    case '!=':
                                        ok = (left != right);
                                        break;
                                }
                                if (!ok) {
                                    errs.push(cmp.message || (window.MBForm._t('compare', op, otherName + (this._isIntString(offset) ? (' + ' + offset) : ''))));
                                }
                            }
                        }
                    }
                    this.clientErrors[name] = errs;
                    return errs.length === 0;
                },
                validateAll() {
                    let ok = true;
                    for (const name in this.schema) {
                        if (!this.validateField(name)) ok = false;
                    }
                    return ok;
                }
            };
        };
    </script>
    <script>
        // Sidebar collapse state using a data attribute consumed by Tailwind data-variants
        (function () {
            try {
                const saved = localStorage.getItem('sidebar-collapsed');
                if (saved === 'true') {
                    document.documentElement.setAttribute('data-collapsed', 'true');
                }
            } catch (e) { /* no-op */
            }
        })();
    </script>
<!--    <style>-->
<!--        @keyframes pulse-highlight {-->
<!--            0%, 100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(59, 130, 246, 0); }-->
<!--            50% { transform: scale(1.02); box-shadow: 0 0 20px 5px rgba(59, 130, 246, 0.4); border-color: rgb(59, 130, 246); }-->
<!--        }-->
<!--        .step-highlight {-->
<!--            animation: pulse-highlight 2s ease-in-out 3;-->
<!--            position: relative;-->
<!--            z-index: 10;-->
<!--        }-->
<!--    </style>-->
</head>
<body class="scroll-smooth scrollbar-thin">
<div id="app-main" x-data="{ active: '<?php echo $tab; ?>', highlightId: null, openKey: null }"
     @set-active.window="active = $event.detail"
     @newsletter:newsletter_item:created.window="updateUrlForActive('newsletter', false)"
     @automation:newsletter_item:created.window="updateUrlForActive('trigger', false, { '<?= $subtabParam ?>': 'custom' })"
     @mb:open-module.window="openKey = $event.detail.module"
     @mb:open-item.window="openKey = $event.detail.key"
     @mb:open-newsletter.window="
        active = 'newsletter';
        openKey = $event.detail.openKey;
        const params = $event.detail.params || {};
        params.page_mode = 'newsletter';
        params.group = $event.detail.group || params.group;
        updateUrlForActive('newsletter', false, params);
        $dispatch('mb:open-item', { key: openKey, group: params.group, page_mode: 'newsletter' });
     "
     @mb:open-automation.window="
        active = 'trigger';
        openKey = $event.detail.openKey;
        const params = $event.detail.params || {};
        params.page_mode = 'automation';
        params['<?= $subtabParam ?>'] = 'custom';
        params.group = $event.detail.group || params.group;
        updateUrlForActive('trigger', false, params);
        $dispatch('mb:open-item', { key: openKey, group: params.group, page_mode: 'automation' });
     "
     @mb:navigate.window="
        if ($event.detail.active) {
            active = $event.detail.active;

            // close common modals if we are navigating away from a tool like template-usage
            const candidates = ['config-modal', 'config2-modal', 'confirm-modal', 'ai-modal', 'email-preview', 'web-preview', 'editor-inline-modal'];
            candidates.forEach(id => {
                const m = document.getElementById(id);
                if (m && typeof m.close === 'function') m.close();
            });

            const essential = $event.detail.params || {};
            if ($event.detail.highlightId) essential.highlight_step = $event.detail.highlightId;
            if ($event.detail.group) essential.group = $event.detail.group;

            updateUrlForActive(active, false, essential);
        }

        if ($event.detail.openKey) {
            const openKey = $event.detail.openKey;
            const type = $event.detail.type || '';
            const detail = {
                openKey: openKey,
                group: $event.detail.group,
                params: $event.detail.params || {}
            };

            if (type === 'newsletter' || (active === 'newsletter' && openKey.startsWith('newsletter-flow:'))) {
                $dispatch('mb:open-newsletter', detail);
            } else if (type === 'automation' || (active === 'trigger' && (openKey.startsWith('automation-flow:')))) {
                $dispatch('mb:open-automation', detail);
            } else {
                // Default handling
                if (active === 'trigger') {
                    if (openKey.startsWith('automation-flow:')) {
                        $dispatch('mb:open-automation', detail);
                    } else {
                        $dispatch('mb:open-module', { module: openKey });
                    }
                } else if (active === 'newsletter') {
                    $dispatch('mb:open-newsletter', detail);
                }
            }
        }
        if ($event.detail.highlightId) {
            highlightId = $event.detail.highlightId;
            const targetId = highlightId;
            const scroll = () => {
                if (highlightId !== targetId) return;
                const el = document.getElementById(targetId);
                if (el) {
                    el.scrollIntoView({ behavior: 'smooth', block: 'center' });
                    $dispatch('mb:highlight-step', { id: targetId, persist: true });
                }
            };
            scroll();
            setTimeout(scroll, 350); // wait for potential module expansion
            setTimeout(scroll, 800); // extra safety for slower loads
        }
     "
     @mb:highlight-step.window="
        const id = $event.detail.id;
        const persist = $event.detail.persist || false;
        if (persist) highlightId = id;
        document.querySelectorAll('.step-highlight').forEach(el => {
            if (!persist || el.id !== id) el.classList.remove('step-highlight');
        });
        if (id) {
            let target = document.getElementById(id);
            if (target) {
                // If the target is a flow card, try to highlight the first step inside it instead of the whole card
                if (id.startsWith('flow-newsletter-')) {
                    const stepId = id.replace('flow-newsletter-', 'flow-item-');
                    const step = document.getElementById(stepId);
                    if (step) target = step;
                } else if (id.startsWith('flow-automation-')) {
                    const mod = id.replace('flow-automation-', '');
                    // Handle both standard modules (key string) and custom items (numeric ID)
                    const stepId = isNaN(mod) ? 'step-' + mod + '-1' : 'flow-item-' + mod;
                    const step = document.getElementById(stepId);
                    if (step) target = step;
                }

                target.classList.add('step-highlight');
                if (persist) setTimeout(() => target.classList.remove('step-highlight'), 5000);
            }
        }
     "
     x-init="
        const urlParams = new URLSearchParams(window.location.search);
        highlightId = urlParams.get('highlight_step');
        if (highlightId) {
            setTimeout(() => {
                const el = document.getElementById(highlightId);
                if (el) {
                    el.scrollIntoView({ behavior: 'smooth', block: 'center' });
                    $dispatch('mb:highlight-step', { id: highlightId, persist: true });
                }
            }, 800);
        }

        document.addEventListener('htmx:afterSettle', (evt) => {
            if (highlightId) {
                const targetId = highlightId;
                const scroll = () => {
                    if (highlightId !== targetId) return;
                    const target = document.getElementById(targetId);
                    if (target && evt.target.contains(target)) {
                        target.scrollIntoView({ behavior: 'smooth', block: 'center' });
                        $dispatch('mb:highlight-step', { id: targetId, persist: true });
                    }
                };
                scroll();
                setTimeout(scroll, 100);
                setTimeout(scroll, 400);
            }
        });

        $watch('active', (nextActive, previousActive) => {
            if (!previousActive || previousActive === nextActive) return;
            if (typeof window.mbzTouchPageByKey === 'function') {
                window.mbzTouchPageByKey(nextActive);
                window.mbzTouchPageByKey(previousActive);
            }
            if (typeof window.mbzCancelOffloadByKey === 'function') {
                window.mbzCancelOffloadByKey(nextActive);
            }
            if (typeof window.mbzLoadPageByKey === 'function') {
                requestAnimationFrame(() => window.mbzLoadPageByKey(nextActive));
            }
            if (typeof window.mbzScheduleOffloadPageByKey === 'function') {
                requestAnimationFrame(() => window.mbzScheduleOffloadPageByKey(previousActive));
            }
        });
     "
>
    <?php
    // SitePanel component logic can be handled via Alpine.data if we put it here
    ?>
    <?php
    // Autoload already required above, and $tab resolved
    // $tab is already set earlier; keep for clarity
    $tab = $tab ?? 'dashboard';

    // Navigation menu groups from Data Provider
    $menuGroups = \Components\Data\DataRegistry::navigation()->groups();

    // Flatten items for easier lookup and page rendering
    $flattenedItems = [];
    $topGroups = [];
    $bottomGroups = [];
    foreach ($menuGroups as $grp) {
        if (($grp['position'] ?? 'top') === 'bottom') {
            $bottomGroups[] = $grp;
        } else {
            $topGroups[] = $grp;
        }
        foreach ($grp['items'] ?? [] as $it) {
            $flattenedItems[] = $it;
            if (!empty($it['items'])) {
                foreach ($it['items'] as $subIt) {
                    $flattenedItems[] = $subIt;
                }
            }
        }
    }

    // Determine active item within grouped structure (optional)
    $activeItem = null;
    foreach ($flattenedItems as $it) {
        if ($it['key'] === $tab) {
            $activeItem = $it;
            break;
        }
    }


    ?>


    <div id="app-shell-topbar-wrap"
         class="fixed inset-x-0 top-0 z-[80] bg-muted transition-all duration-300 ease-in-out">
        <div class="flex items-stretch">
            <div id="sidebar-header"
                 class="flex w-<?= BEEZUI_NAV_WIDTH ?> items-center gap-3 px-6  h-12 py-2 mt-1 group-data-[collapsed=true]/collapse:w-20  text tracking-tight transition-[width] duration-300 ease-in-out [will-change:width] motion-reduce:transition-none">
                <div class="group-data-[collapsed=true]/collapse:hidden text-nowrap flex items-center space-x-1 h-12">
                    <h1 class="tracking-tighter font-medium opacity-70"> MailBeez </h1>
                    <div class="ml-1 ring ring-on-muted/70 rounded-lg px-1 text-xs text-on-muted ">beta 9</div>
                </div>
                <div class="group-data-[collapsed=true]/collapse:visible invisible min-w-12! ">
                    <div class="ring ring-on-muted/70 rounded-lg px-1 text-xs text-on-muted w-full text-nowrap  text-center">&beta; 9</div>
                </div>
            </div>
            <header id="app-shell-topbar"
                    class="flex min-w-0 flex-1 items-center gap-3 h-12 px-2 py-2 pl-0  group-data-[collapsed=true]/collapse:pl-2 transition-[padding-left] duration-300 ease-in-out [will-change:padding-left] motion-reduce:transition-none ">
                <?php include BEEZUI_ROOT . '/pages/components/top_bar.php'; ?>
            </header>
        </div>
    </div>

    <!-- Static sidebar for desktop (collapsible) -->

    <div id="app-sidebar"
         data-active="<?php echo \Components\Base::h($tab); ?>"
         class="fixed top-[3.25rem] bottom-0 z-[70] flex w-<?= BEEZUI_NAV_WIDTH ?>  group/sidebar group-data-[collapsed=true]/collapse:w-20 flex-col transition-[width] duration-300 ease-in-out [will-change:width] motion-reduce:transition-none">
        <div id="sidebar-pad"
             class="relative flex grow flex-col gap-y-5 overflow-y-auto overflow-x-hidden  bg-muted group-data-[collapsed=true]/collapse:bg-muted group-data-[collapsed=true]/collapse:border-0 pl-5 pr-7 transition-all duration-300 ease-in-out dark:before:pointer-events-none dark:before:absolute dark:before:inset-0 dark:before:bg-overlay/10">

            <!--
                        <div>
                            <span class="inline-flex items-center gap-1.5 rounded-md border border-emerald-600/60 text-emerald-700 px-2 py-0.5 text-xs font-semibold">
                            <span class="relative flex items-center justify-center">
                            <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
                                 stroke="currentColor" class="size-4">
                            <path stroke-linecap="round" stroke-linejoin="round"
                                  d="M9 12.75 11.25 15 15 9.75M21 12a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/></svg>
                            </span>
                            Production
                            </span>

                            <span class="inline-flex items-center gap-1.5 rounded-md border border-orange-500/60 text-orange-600 px-2 py-0.5 text-xs font-semibold">
                            <span class="relative flex items-center justify-center">
                            <span class="absolute inset-0 rounded-full border border-orange-500/30 border-dotted animate-pulse"></span>
                            <span class="absolute inset-[3px] rounded-full border border-orange-500/50 animate-pulse"></span>
                                <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
                                     stroke="currentColor"
                                     stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="size-4  animate-pulse"><path
                                            d="M14 2v6a2 2 0 0 0 .245.96l5.51 10.08A2 2 0 0 1 18 22H6a2 2 0 0 1-1.755-2.96l5.51-10.08A2 2 0 0 0 10 8V2"/><path
                                            d="M6.453 15h11.094"/><path d="M8.5 2h7"/></svg>
                            </span>
                            Simulation
                            </span>
                        </div>
            -->

            <div class="hidden bg-canvas rounded ring ring-divider px-3 py-2 text-xs text-secondary/70 dark:bg-canvas dark:text-white overflow-hidden">
                Search
            </div>

            <nav class="relative flex flex-1 flex-col transform-gpu transition-transform duration-300 ease-in-out will-change-transform motion-reduce:transition-none group-data-[collapsed=true]/collapse:translate-x-2!">
                <ul role="list" class="flex flex-1 flex-col gap-y-7">
                    <li>
                        <ul id="menu-list" role="list" class="-mx-2 transition-all  space-y-1 relative">
                            <?php foreach ($topGroups as $group): ?>
                                <?php include BEEZUI_ROOT . '/pages/components/sidebar_menu_group.php'; ?>
                            <?php endforeach; ?>
                        </ul>
                    </li>
                </ul>
            </nav>

            <nav class="transform-gpu transition-transform duration-300 ease-in-out will-change-transform motion-reduce:transition-none group-data-[collapsed=true]/collapse:translate-x-2!">
                <ul id="menu-list-bottom" role="list" class="-mx-2 transition-all space-y-1 relative">
                    <?php foreach ($bottomGroups as $group): ?>
                        <?php include BEEZUI_ROOT . '/pages/components/sidebar_menu_group.php'; ?>
                    <?php endforeach; ?>
                </ul>
                <div class="group-data-[collapsed=true]/collapse:hidden text-[10px] text-secondary/30">Version <?= mh_cfg('MAILBEEZ_VERSION'); ?></div>

            </nav>

            <div>

                <!--            <div class=" inline-flex items-center gap-x-1.5 rounded-md px-2 py-1 text-sm font-medium   inset-ring-gray-200 dark:text-white dark:inset-ring-white/10">-->
                <!--  <svg viewBox="0 0 6 6" aria-hidden="true" class="size-2 fill-green-500 dark:fill-green-400">-->
                <!--    <circle r="3" cx="3" cy="3" />-->
                <!--  </svg>-->
                <!--  Production-->
                <!--</div>-->


            </div>
            <!-- Frontend switcher -->
            <div id="frontend-switcher" class="hidden pb-2 group-data-[collapsed=true]/collapse:px-1.5">
                <div class="inline-flex items-center rounded-md overflow-hidden ring-1 ring-divider shadow-xs bg-elevated text-secondary group-data-[collapsed=true]/collapse:hidden">
                    <?php
                    echo \Components\Button::render(\mh_lng('UI_MAILBEEZ_V4', 'MailBeez V4'), [
                            'variant' => 'ghost',
                            'size' => 'sm',
                            'attrs' => [
                                    'hx-post' => \Components\Router::url('switch_to_legacy', ['partial' => 1]),
                                    'hx-target' => 'body',
                                    'hx-swap' => 'innerHTML',
                                    'title' => 'Disable BeezUI and return to the legacy MailBeez admin',
                            ],
                    ]);
                    ?>
                </div>
            </div>
            <div class="hidden">
                <?php
                // Plan/Subscription Information box (extracted component)
//                include BEEZUI_ROOT . '/pages/components/plan_info.php';
                ?>
            </div>

            <div>
                <?php
                // Developer diagnostics widget (DOM/pressure summary)
//                include BEEZUI_ROOT . '/pages/components/dev_pressure_widget.php';
                ?>
            </div>

            <!-- Density switcher -->
            <div id="density-switcher"
                 class="hidden mt-2 px-2 pb-2 group-data-[collapsed=true]/collapse:px-1.5  group-data-[collapsed=true]/collapse:hidden">
                <div class="text-[11px] font-semibold uppercase tracking-wide text-secondary/60 mb-1 group-data-[collapsed=true]/collapse:hidden">
                    Density
                </div>
                <div class="inline-flex items-center rounded-md overflow-hidden ring-1 ring-divider shadow-xs bg-elevated text-secondary">
                    <button type="button" data-density="normal"
                            class="px-2 py-1 text-xs leading-5 hover:bg-muted data-[selected=true]:bg-muted data-[selected=true]:font-medium">
                        N
                    </button>
                    <div class="h-4 w-px bg-divider/70"></div>
                    <button type="button" data-density="compact"
                            class="px-2 py-1 text-xs leading-5 hover:bg-muted data-[selected=true]:bg-muted data-[selected=true]:font-medium">
                        C
                    </button>
                    <div class="h-4 w-px bg-divider/70"></div>
                    <button type="button" data-density="very-compact"
                            class="px-2 py-1 text-xs leading-5 hover:bg-muted data-[selected=true]:bg-muted data-[selected=true]:font-medium">
                        V
                    </button>
                </div>
            </div>


            <div id="sidebar-footer" class="relative flex h-10 transform-gpu transition-transform duration-300 ease-in-out will-change-transform motion-reduce:transition-none group-data-[collapsed=true]/collapse:translate-x-4!">
                <button type="button" onclick="toggleCollapsed()"
                        class="flex items-center gap-2 rounded-md text-xs/6 text-secondary/70 hover:bg-muted"
                >
                    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
                         stroke="currentColor" class="size-3 hidden group-data-[collapsed=true]/collapse:block">
                        <path stroke-linecap="round" stroke-linejoin="round" d="m8.25 4.5 7.5 7.5-7.5 7.5"/>
                    </svg>
                    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5"
                         stroke="currentColor" class="size-3 group-data-[collapsed=true]/collapse:hidden">
                        <path stroke-linecap="round" stroke-linejoin="round" d="M15.75 19.5 8.25 12l7.5-7.5"/>
                    </svg>
                    <span class="transition-all group-data-[collapsed=true]/collapse:hidden">Collapse</span>
                </button>
            </div>
        </div>
    </div>
    <div id="app-shell-site-body"
         class="relative mt-[3.25rem] ml-<?= BEEZUI_NAV_WIDTH ?> group-data-[collapsed=true]/collapse:ml-20 p-2 pl-0 pt-0 group-data-[collapsed=true]/collapse:pl-2 transition-[margin-left,padding-left] duration-300 ease-in-out [will-change:margin-left,padding-left] motion-reduce:transition-none bg-muted"
         style="height: calc(100vh - 3.25rem);">
        <div class="flex h-full min-h-0">

            <div id="app-shell-main"
                 class="relative flex min-h-0 flex-1 flex-col overflow-hidden rounded-2xl border border-divider/70 bg-canvas"
            >
                <!-- Global loading overlay for navigation/content loads -->
                <?php echo \Components\Base::loaderOverlay('app-loading', 'z-20 rounded-2xl'); ?>

                <div id="app-shell-main-content" class="min-h-0 flex-1 overflow-x-auto overflow-y-auto p-1 md:p-2">

                    <div class="min-w-2xl w-full h-full">

                    <?php
                    if (!function_exists('mbzRenderPageFallbackHtml')) {
                        function mbzRenderPageFallbackHtml(): string
                        {
                            ob_start();
                            ?>
                            <div data-mbz-page-fallback="1" class="w-full p-4 md:p-6">
                                <div class="w-full space-y-6">
                                    <div class="space-y-3">
                                        <div class="h-9 w-72 rounded-lg bg-muted animate-pulse"></div>
                                        <div class="h-6 w-full max-w-2xl rounded-md bg-muted animate-pulse"></div>
                                    </div>

                                    <div class="h-11 w-full max-w-md rounded-xl border border-divider/60 bg-muted animate-pulse"></div>

                                    <div class="flex flex-wrap gap-2">
                                        <div class="h-10 w-20 rounded-full bg-muted animate-pulse"></div>
                                        <div class="h-10 w-36 rounded-full bg-muted animate-pulse"></div>
                                        <div class="h-10 w-40 rounded-full bg-muted animate-pulse"></div>
                                        <div class="h-10 w-44 rounded-full bg-muted animate-pulse"></div>
                                    </div>

                                    <div class="space-y-3">
                                        <div class="h-24 w-full rounded-2xl border border-divider/60 bg-muted animate-pulse"></div>
                                        <div class="h-24 w-full rounded-2xl border border-divider/60 bg-muted animate-pulse"></div>
                                        <div class="h-24 w-full rounded-2xl border border-divider/60 bg-muted animate-pulse"></div>
                                        <div class="h-24 w-full rounded-2xl border border-divider/60 bg-muted animate-pulse"></div>
                                    </div>
                                </div>
                            </div>
                            <?php

                            return (string)ob_get_clean();
                        }
                    }

                    $pageFallbackMarkup = mbzRenderPageFallbackHtml();
                    $pageFallbackMarkupJs = json_encode(
                        $pageFallbackMarkup,
                        JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_AMP | JSON_HEX_QUOT
                    );
                    if ($pageFallbackMarkupJs === false) {
                        $pageFallbackMarkupJs = "''";
                    }
                    ?>

                    <?php foreach ($flattenedItems as $item):
                        $key = $item['key'];
                        $path = \Components\Router::path($key) ?: 'pages/' . $key . '.php';
                        if (!is_file(BEEZUI_ROOT . '/' . $path)) continue;
                        $isActive = ($key === $tab);
                        $componentUrl = \Components\Router::url($key, ['partial' => 0]);
                        $loading = (isset($item['loading']) && is_array($item['loading'])) ? $item['loading'] : [];
                        $preloadStrategy = strtolower((string)($loading['preload'] ?? ''));
                        if ($preloadStrategy === '') {
                            $preloadStrategy = (defined('BEEZUI_PRELOAD') && BEEZUI_PRELOAD) ? 'idle' : 'lazy';
                        }
                        $trigger = ($preloadStrategy === 'lazy') ? 'mbz:load-page' : 'mbz:load-page, preload once';
                        $offloadStrategy = strtolower((string)($loading['offload'] ?? 'retain'));
                        $shouldOffload = in_array($offloadStrategy, ['unload', 'offload', 'on_hide', 'onhide'], true);
                        $offloadDelayRaw = $loading['offload_delay'] ?? 0;
                        if (is_numeric($offloadDelayRaw)) {
                            $offloadDelay = (string)max(0, (int)$offloadDelayRaw);
                        } else {
                            $offloadDelayRaw = trim((string)$offloadDelayRaw);
                            $offloadDelay = preg_match('/^\d+(?:\.\d+)?(?:ms|s)$/i', $offloadDelayRaw) ? $offloadDelayRaw : '0';
                        }
                        ?>
                        <div id="page-<?php echo $key; ?>"
                             x-show="active === '<?php echo $key; ?>'"
                             <?php if (!$isActive) { ?>x-cloak<?php } ?>
                             data-page="<?php echo $key; ?>"
                             data-component-url="<?= $componentUrl; ?>"
                             data-hx-trigger="<?= $trigger; ?>"
                             data-offload="<?= $shouldOffload ? 'true' : 'false'; ?>"
                             data-offload-delay-ms="<?= htmlspecialchars($offloadDelay, ENT_QUOTES, 'UTF-8'); ?>"
                             data-loaded="<?= $isActive ? 'true' : 'false'; ?>"
                             data-loaded-at="<?= $isActive ? (time() * 1000) : '0'; ?>"
                             data-offloaded-at="0"
                             data-loading="false"
                             data-last-used="<?= $isActive ? time() * 1000 : '0'; ?>"
                             class="" <?php if (!$isActive) { ?> hx-get="<?= $componentUrl; ?>" hx-trigger="<?= $trigger ?>" hx-swap="innerHTML"<?php } ?>>
                            <?php
                            if ($isActive) {
                                include BEEZUI_ROOT . '/' . $path;
                            } else {
                                echo $pageFallbackMarkup;
                            }
                            ?>
                        </div>

                    <?php endforeach; ?>
                    </div>
                </div>

                <div id="app-shell-main-notification-area" class="pointer-events-none absolute inset-0 z-[100]"></div>
            </div>

            <!-- Site Panel next to app-shell-main -->
            <?= \Components\SitePanel::render([
                'collapsed' => true
            ]); ?>

        </div>
    </div>

    <script>
        // Centralized MBLoader binding for HTMX-loaded page fragments.
        // Avoids attaching one global listener per navigation item.
        (function () {
            function isElementVisible(el) {
                return !!(el && (el.offsetWidth || el.offsetHeight || el.getClientRects().length));
            }

            function shouldShowAppLoader(evt, boundId) {
                try {
                    if (!evt || !evt.detail) return false;

                    var elt = evt.detail.elt || null;
                    var target = evt.detail.target || null;

                    if (boundId && boundId.indexOf('page-') === 0) {
                        return isElementVisible(document.getElementById(boundId));
                    }

                    var pageCtx = null;
                    if (target && target.closest) pageCtx = target.closest('[id^="page-"]');
                    if (!pageCtx && elt && elt.closest) pageCtx = elt.closest('[id^="page-"]');
                    if (pageCtx) return isElementVisible(pageCtx);

                    return true;
                } catch (e) {
                    return false;
                }
            }

            window.mbzShouldShowAppLoader = shouldShowAppLoader;

            function bindOverlayForEl(el) {
                try {
                    if (!el || !el.id || !window.MBLoader) return;
                    if (el.id.indexOf('app-container-') === 0 || el.id.indexOf('page-') === 0) {
                        MBLoader.bindHtmxOverlay(el.id, 'app-loading', 350, {
                            filter: function (evt) {
                                return shouldShowAppLoader(evt, el.id);
                            }
                        });
                    }
                } catch (e) { /* no-op */
                }
            }

            function bindOverlayContext(root) {
                try {
                    if (!root || !window.MBLoader) return;
                    bindOverlayForEl(root);
                    if (root.closest) {
                        bindOverlayForEl(root.closest('[id^="app-container-"]'));
                        bindOverlayForEl(root.closest('[id^="page-"]'));
                    }
                } catch (e) { /* no-op */
                }
            }

            document.addEventListener('DOMContentLoaded', function () {
                try {
                    if (!window.MBLoader) return;
                    document.querySelectorAll('[id^="app-container-"], [id^="page-"]').forEach(bindOverlayForEl);
                } catch (e) { /* no-op */
                }
            });

            document.addEventListener('htmx:load', function (event) {
                bindOverlayContext(event && event.target ? event.target : null);
            });
        })();
    </script>


</div>

<script>

    // Density switcher bindings
    (function () {
        function applyDensity(mode) {
            try {
                var html = document.documentElement;
                html.classList.remove('density-compact', 'density-very-compact');
                if (mode === 'compact') html.classList.add('density-compact');
                else if (mode === 'very-compact') html.classList.add('density-very-compact');
                localStorage.setItem('ui-density', mode);
                var buttons = document.querySelectorAll('#density-switcher [data-density]');
                buttons.forEach(function (btn) {
                    var sel = btn.getAttribute('data-density');
                    btn.setAttribute('data-selected', (sel === mode) ? 'true' : 'false');
                });
            } catch (e) {
            }
        }

        window.applyDensity = applyDensity;
        document.addEventListener('DOMContentLoaded', function () {
            try {
                var saved = localStorage.getItem('ui-density') || 'normal';
                // set selected state based on saved
                var buttons = document.querySelectorAll('#density-switcher [data-density]');
                buttons.forEach(function (btn) {
                    var sel = btn.getAttribute('data-density');
                    btn.setAttribute('data-selected', (sel === saved) ? 'true' : 'false');
                });
                var container = document.getElementById('density-switcher');
                if (container) {
                    container.addEventListener('click', function (ev) {
                        var t = ev.target.closest('[data-density]');
                        if (!t) return;
                        ev.preventDefault();
                        applyDensity(t.getAttribute('data-density'));
                    });
                }
            } catch (e) {
            }
        });
    })();

    /**
     * Lazy noblock low priority pre-load all pages
     */
    (function () {
        document.addEventListener('DOMContentLoaded', function () {
            const hasNativeRequestIdle = ('requestIdleCallback' in window);
            const requestIdle = hasNativeRequestIdle
                ? function (cb, options) {
                    return window.requestIdleCallback(cb, options);
                }
                : function (cb, options) {
                    return window.setTimeout(function () {
                        cb({
                            // Fallback path has no real idle deadline; always allow progress.
                            didTimeout: true,
                            timeRemaining: function () {
                                return 0;
                            }
                        });
                    }, 1);
                };
            // Wait a bit after load to ensure everything is stable
            setTimeout(function () {
                // Preload all pages that have a preload trigger, filtering out the currently visible one
                const pagesToPreload = Array.from(document.querySelectorAll('[hx-trigger*="preload"]'))
                    .filter(function (el) {
                        return el && el.offsetParent === null;
                    });


                if (pagesToPreload.length === 0) return;

                let current = 0;
                function preloadNext() {
                    if (current >= pagesToPreload.length) return;

                    requestIdle(function (deadline) {
                        if (!hasNativeRequestIdle || deadline.timeRemaining() > 10 || deadline.didTimeout) {
                            const el = pagesToPreload[current];
                            if (el && window.htmx) {
                                window.htmx.trigger(el, 'preload');
                            }
                            current++;
                        }
                        setTimeout(preloadNext, 200);
                    }, {timeout: 2000});
                }
                preloadNext();
            }, 1000);
        });
    })();

    // Re-initialize after HTMX swaps
    // document.body.addEventListener('htmx:afterSwap', function (e) {
    //     try {
    //         var root = (e && e.detail && e.detail.target) ? e.detail.target : document.body;
    //         if (window.Alpine && typeof window.Alpine.initTree === 'function') {
    //             window.Alpine.initTree(root);
    //         }
    //     } catch (e3) {
    //     }
    // });

    function toggleCollapsed() {
        try {
            const html = document.documentElement;
            const isCollapsed = html.getAttribute('data-collapsed') === 'true';
            if (isCollapsed) {
                html.removeAttribute('data-collapsed');
                localStorage.setItem('sidebar-collapsed', 'false');
            } else {
                html.setAttribute('data-collapsed', 'true');
                localStorage.setItem('sidebar-collapsed', 'true');
            }
        } catch (e) { /* no-op */
        }
    }


    // Inform parent window when our (iframe) URL changes without reload (HTMX pushState/back/forward)
    (function () {
        function notifyParent() {
            try {
                if (window.parent && window.parent !== window) {
                    const params = new URLSearchParams(window.location.search || '');
                    const TAB_PARAM = <?php echo json_encode($tabParam); ?>;
                    const route = (params.get(TAB_PARAM) || 'dashboard').toLowerCase();
                    window.parent.postMessage({
                        type: 'iframe-location-update',
                        route: route,
                        search: window.location.search || ''
                    }, '*');
                }
            } catch (e) { /* no-op */
            }
        }

        // HTMX pushes into history when hx-push-url is used
        document.body.addEventListener('htmx:pushedIntoHistory', notifyParent);
        // Also capture browser navigation inside the iframe
        window.addEventListener('popstate', notifyParent);
    })();

    // Unified active menu setter used both by clicks and when parsing URL state
    const submenuMap = <?php
            $__map = [];
            foreach ($flattenedItems as $it) {
                $__map[$it['key']] = !empty($it['submenu']) ? $it['submenu'] : null;
            }
            echo json_encode($__map);
            ?>;

    function setActiveMenu(tab, submenuUrl) {
        try {
            if (!tab) return;
            // Toggle data-active on the menu items themselves
            const links = document.querySelectorAll('a[id^="desk-tab-"]');
            let activeLink = null;
            links.forEach((link) => {
                const key = link.id.replace('desk-tab-', '');
                if (key === String(tab).toLowerCase()) {
                    activeLink = link;
                    link.setAttribute('data-active', 'true');
                } else {
                    link.removeAttribute('data-active');
                }
            });

        } catch (e) { /* no-op */
        }
    }

    // Dynamically set the active menu item based on the current URL's ?<tab>= parameter
    function setActiveMenuFromURL() {
        try {
            const params = new URLSearchParams(window.location.search || '');
            const TAB_PARAM = <?php echo json_encode($tabParam); ?>;
            const tab = (params.get(TAB_PARAM) || '').toLowerCase();
            if (!tab) return;
            // Tell Alpine to switch the visible page
            window.dispatchEvent(new CustomEvent('set-active', {detail: tab}));
            const submenuUrl = submenuMap[tab] || null;
            setActiveMenu(tab, submenuUrl);
        } catch (e) { /* no-op */
        }
    }

    // Push/replace the URL for a given tab key, then notify existing listeners
    function updateUrlForActive(tabKey, replace, extraParams) {
        try {
            if (!tabKey) return;
            const TAB_PARAM = <?php echo json_encode($tabParam); ?>;
            const PAGE_PARAM = <?php echo json_encode($pageParam); ?>;
            const url = new URL(window.location.href);
            // Update params
            url.searchParams.set(TAB_PARAM, String(tabKey).toLowerCase());
            url.searchParams.delete('partial');
            if (PAGE_PARAM && PAGE_PARAM !== TAB_PARAM) url.searchParams.delete(PAGE_PARAM);

            // Clean up volatile params that should not persist across main tab changes
            const volatile = ['id', 'newsletter_campaign_id', 'item_dom_id', 'open_id', 'highlight_step', 'group', 'open_module', 'page_mode', '<?= $subtabParam ?>'];
            volatile.forEach(k => {
                if (!extraParams || !Object.prototype.hasOwnProperty.call(extraParams, k)) {
                    url.searchParams.delete(k);
                }
            });

            if (extraParams) {
                for (const key in extraParams) {
                    url.searchParams.set(key, extraParams[key]);
                }
            }

            const href = url.pathname + url.search + url.hash;
            const currentParams = new URLSearchParams(window.location.search || '');
            const currentTab = (currentParams.get(TAB_PARAM) || '').toLowerCase();


            if (replace || (currentTab === String(tabKey).toLowerCase() && !extraParams)) {
                history.replaceState({}, '', href);
            } else {
                history.pushState({}, '', href);
            }
            // Notify existing listeners (iframe parent + menu sync)
            document.body.dispatchEvent(new CustomEvent('htmx:pushedIntoHistory', {detail: {path: href}}));
        } catch (e) { /* no-op */
        }
    }

    function mbzIsPageVisible(pageEl) {
        return !!(pageEl && (pageEl.offsetWidth || pageEl.offsetHeight || pageEl.getClientRects().length));
    }

    function mbzApplyPageLoadingIndicator(pageEl) {
        if (!pageEl) return;
        if (mbzIsPageVisible(pageEl)) {
            pageEl.setAttribute('hx-indicator', '#app-loading');
        } else {
            pageEl.removeAttribute('hx-indicator');
        }
    }



    const MBZ_PRESSURE_ORDER = {
        ok: 0,
        watch: 1,
        high: 2,
        critical: 3,
    };

    const mbzOffloadState = {
        pressureLevel: 'ok',
        pressureLowSamples: 0,
        nextPressureEvictionAt: 0,
        lastOffloadAtByKey: Object.create(null),
        lastInteractionAt: 0,
    };

    function mbzCoercePressureLevel(level) {
        return Object.prototype.hasOwnProperty.call(MBZ_PRESSURE_ORDER, level) ? level : 'ok';
    }

    function mbzRuntimePressureScale() {
        let scale = 1;
        try {
            const deviceMemory = Number(navigator.deviceMemory || 0);
            const cores = Number(navigator.hardwareConcurrency || 0);

            if (deviceMemory > 0 && deviceMemory <= 4) scale *= 0.8;
            if (deviceMemory >= 12) scale *= 1.2;

            if (cores > 0 && cores <= 4) scale *= 0.9;
            if (cores >= 12) scale *= 1.1;
        } catch (e) { /* no-op */
        }

        return Math.min(1.35, Math.max(0.7, scale));
    }

    function mbzResolvePressureLevel(rawLevel) {
        const safeRawLevel = mbzCoercePressureLevel(rawLevel);
        const currentLevel = mbzCoercePressureLevel(mbzOffloadState.pressureLevel);
        const rawRank = MBZ_PRESSURE_ORDER[safeRawLevel];
        const currentRank = MBZ_PRESSURE_ORDER[currentLevel];

        if (rawRank > currentRank) {
            mbzOffloadState.pressureLevel = safeRawLevel;
            mbzOffloadState.pressureLowSamples = 0;
            return mbzOffloadState.pressureLevel;
        }

        if (rawRank < currentRank) {
            mbzOffloadState.pressureLowSamples += 1;
            if (mbzOffloadState.pressureLowSamples >= 3) {
                mbzOffloadState.pressureLevel = safeRawLevel;
                mbzOffloadState.pressureLowSamples = 0;
            }
            return mbzOffloadState.pressureLevel;
        }

        mbzOffloadState.pressureLowSamples = 0;
        mbzOffloadState.pressureLevel = safeRawLevel;

        return mbzOffloadState.pressureLevel;
    }

    function mbzGetActivePageKey() {
        const pages = document.querySelectorAll('[data-page]');
        for (const pageEl of pages) {
            if (mbzIsPageVisible(pageEl)) {
                return String(pageEl.dataset.page || '');
            }
        }
        return '';
    }

    window.mbzTouchPageByKey = function (pageKey) {
        const key = String(pageKey || '');
        if (!key) return;

        const pageEl = document.getElementById('page-' + key);
        if (!pageEl) return;

        pageEl.dataset.lastUsed = String(Date.now());
    };

    function mbzTouchActivePage() {
        const activePageKey = mbzGetActivePageKey();
        if (!activePageKey) return;
        if (typeof window.mbzTouchPageByKey === 'function') {
            window.mbzTouchPageByKey(activePageKey);
        }
    }

    function mbzCountPressureMetrics(scopeEl) {
        const scope = scopeEl || document;

        return {
            nodes: scope.getElementsByTagName('*').length,
            htmx: scope.querySelectorAll('[hx-get], [hx-post], [hx-trigger]').length,
            alpine: scope.querySelectorAll('[x-data]').length,
        };
    }

    function mbzDomPressure() {
        const domMetrics = mbzCountPressureMetrics(document);
        const nodes = domMetrics.nodes;
        const htmx = domMetrics.htmx;
        const alpine = domMetrics.alpine;
        const scale = mbzRuntimePressureScale();

        const heapMB = performance.memory
            ? Math.round(performance.memory.usedJSHeapSize / 1024 / 1024)
            : null;

        const watchNodes = Math.round(8000 * scale);
        const watchHtmx = Math.round(500 * scale);
        const watchAlpine = Math.round(150 * scale);
        const highNodes = Math.round(12000 * scale);
        const highHtmx = Math.round(700 * scale);
        const highAlpine = Math.round(200 * scale);
        const criticalNodes = Math.round(18000 * scale);
        const criticalHtmx = Math.round(1000 * scale);
        const criticalAlpine = Math.round(300 * scale);

        let rawLevel = 'ok';

        if (nodes > criticalNodes || htmx > criticalHtmx || alpine > criticalAlpine || (heapMB !== null && heapMB > 300)) {
            rawLevel = 'critical';
        } else if (nodes > highNodes || htmx > highHtmx || alpine > highAlpine || (heapMB !== null && heapMB > 150)) {
            rawLevel = 'high';
        } else if (nodes > watchNodes || htmx > watchHtmx || alpine > watchAlpine) {
            rawLevel = 'watch';
        }

        const level = mbzResolvePressureLevel(rawLevel);

        return { level, rawLevel, nodes, htmx, alpine, heapMB, scale };
    }

    function mbzDomPressureIdleLimitMs(level) {
        if (level === 'watch') return 10 * 60 * 1000;
        if (level === 'high') return 3 * 60 * 1000;
        if (level === 'critical') return 60 * 1000;

        return null;
    }

    function mbzDomPressureCycleMs(level) {
        if (level === 'watch') return 2 * 60 * 1000;
        if (level === 'high') return 60 * 1000;
        if (level === 'critical') return 30 * 1000;

        return 0;
    }

    function mbzDomPressureMinResidencyMs(level) {
        if (level === 'watch') return 4 * 60 * 1000;
        if (level === 'high') return 2 * 60 * 1000;
        if (level === 'critical') return 75 * 1000;

        return 0;
    }

    function mbzDomPressureOffloadCooldownMs(level) {
        if (level === 'watch') return 5 * 60 * 1000;
        if (level === 'high') return 3 * 60 * 1000;
        if (level === 'critical') return 2 * 60 * 1000;

        return 0;
    }

    function mbzPageHasProtectedState(pageEl) {
        if (!pageEl) return true;

        if (pageEl.querySelector('form[data-dirty="true"], [data-dirty="true"], [data-unsaved="true"], [data-editor-dirty="true"]')) {
            return true;
        }

        if (pageEl.querySelector('.htmx-request, [hx-request], [aria-busy="true"]')) {
            return true;
        }

        if (pageEl.querySelector('dialog[open], [data-modal-open="true"]')) {
            return true;
        }

        const activeEl = document.activeElement;
        if (activeEl && pageEl.contains(activeEl)) {
            const tag = String(activeEl.tagName || '').toLowerCase();
            if (tag === 'input' || tag === 'textarea' || tag === 'select' || activeEl.isContentEditable) {
                return true;
            }
        }

        return false;
    }

    function mbzPagePressureScore(pageEl, idleMs) {
        const nodes = pageEl.getElementsByTagName('*').length;
        const htmx = pageEl.querySelectorAll('[hx-get], [hx-post], [hx-trigger]').length;
        const alpine = pageEl.querySelectorAll('[x-data]').length;
        const weight = Math.max(1, nodes + (htmx * 20) + (alpine * 30));

        return idleMs * weight;
    }

    window.mbzDynamicOffload = function () {
        const pressure = mbzDomPressure();
        const idleLimitMs = mbzDomPressureIdleLimitMs(pressure.level);

        if (!idleLimitMs) return pressure;
        if (document.hidden) return pressure;

        const now = Date.now();
        if (now < mbzOffloadState.nextPressureEvictionAt) return pressure;

        const minResidencyMs = mbzDomPressureMinResidencyMs(pressure.level);
        const offloadCooldownMs = mbzDomPressureOffloadCooldownMs(pressure.level);
        const cycleMs = mbzDomPressureCycleMs(pressure.level);

        let bestCandidate = null;
        let bestScore = -1;

        document.querySelectorAll('[data-page]').forEach((pageEl) => {
            if (pageEl.dataset.offload !== 'true') return;
            if (pageEl.dataset.loaded !== 'true') return;
            if (pageEl.dataset.loading === 'true') return;
            if (mbzIsPageVisible(pageEl)) return;
            if (mbzPageHasProtectedState(pageEl)) return;

            const lastUsed = parseInt(pageEl.dataset.lastUsed || '0', 10);
            if (!lastUsed) return;

            const idleMs = now - lastUsed;
            if (idleMs < idleLimitMs) return;

            const loadedAt = parseInt(pageEl.dataset.loadedAt || '0', 10);
            if (loadedAt > 0 && (now - loadedAt) < minResidencyMs) return;

            const key = String(pageEl.dataset.page || '');
            if (!key) return;

            const lastOffloadAt = mbzOffloadState.lastOffloadAtByKey[key] || 0;
            if (lastOffloadAt > 0 && (now - lastOffloadAt) < offloadCooldownMs) return;

            const score = mbzPagePressureScore(pageEl, idleMs);
            if (score > bestScore) {
                bestScore = score;
                bestCandidate = pageEl;
            }
        });

        if (bestCandidate) {
            const key = String(bestCandidate.dataset.page || '');
            if (key) {
                window.mbzOffloadPageByKey?.(key);
                mbzOffloadState.lastOffloadAtByKey[key] = now;
                mbzOffloadState.nextPressureEvictionAt = now + cycleMs;
            }
        }

        return pressure;
    };

    window.mbzPagePressureReport = function () {
        const pressure = mbzDomPressure();
        const now = Date.now();

        const pages = [...document.querySelectorAll('[data-page]')].map((pageEl) => {
            const pageMetrics = mbzCountPressureMetrics(pageEl);

            return {
                key: pageEl.dataset.page,
                loaded: pageEl.dataset.loaded,
                loading: pageEl.dataset.loading,
                offload: pageEl.dataset.offload,
                visible: mbzIsPageVisible(pageEl),
                nodes: pageMetrics.nodes,
                htmx: pageMetrics.htmx,
                alpine: pageMetrics.alpine,
                idleSeconds: Math.round((now - parseInt(pageEl.dataset.lastUsed || '0', 10)) / 1000),
                loadedAt: parseInt(pageEl.dataset.loadedAt || '0', 10),
                protected: mbzPageHasProtectedState(pageEl),
            };
        });

        const pageTotals = pages.reduce((acc, row) => {
            acc.nodes += row.nodes;
            acc.htmx += row.htmx;
            acc.alpine += row.alpine;
            return acc;
        }, { nodes: 0, htmx: 0, alpine: 0 });

        const outsideTotals = {
            nodes: Math.max(0, pressure.nodes - pageTotals.nodes),
            htmx: Math.max(0, pressure.htmx - pageTotals.htmx),
            alpine: Math.max(0, pressure.alpine - pageTotals.alpine),
        };

        const summary = {
            ...pressure,
            pageNodes: pageTotals.nodes,
            pageHtmx: pageTotals.htmx,
            pageAlpine: pageTotals.alpine,
            outsidePageNodes: outsideTotals.nodes,
            outsidePageHtmx: outsideTotals.htmx,
            outsidePageAlpine: outsideTotals.alpine,
        };

        const scopes = [
            {
                scope: 'all_dom',
                nodes: pressure.nodes,
                htmx: pressure.htmx,
                alpine: pressure.alpine,
            },
            {
                scope: 'all_pages',
                nodes: pageTotals.nodes,
                htmx: pageTotals.htmx,
                alpine: pageTotals.alpine,
            },
            {
                scope: 'outside_pages',
                nodes: outsideTotals.nodes,
                htmx: outsideTotals.htmx,
                alpine: outsideTotals.alpine,
            },
        ];

        console.table(summary);
        console.table(scopes);
        console.table(pages);

        return { pressure: summary, scopes, pages };
    };

    document.addEventListener('DOMContentLoaded', function () {
        const touchActivePageThrottled = function (force) {
            const now = Date.now();
            if (!force && (now - mbzOffloadState.lastInteractionAt) < 3000) return;
            mbzOffloadState.lastInteractionAt = now;
            mbzTouchActivePage();
        };

        touchActivePageThrottled(true);

        ['pointerdown', 'keydown', 'input', 'focusin', 'scroll'].forEach((eventName) => {
            document.addEventListener(eventName, () => touchActivePageThrottled(false), { capture: true, passive: eventName === 'scroll' });
        });

        document.addEventListener('visibilitychange', () => {
            if (document.hidden) return;
            touchActivePageThrottled(true);
            window.mbzDynamicOffload?.();
        });

        setInterval(() => {
            if (document.hidden) return;
            window.mbzDynamicOffload?.();
        }, 30 * 1000);
    });
    const MBZ_PAGE_FALLBACK_MARKUP = <?php echo $pageFallbackMarkupJs; ?>;

    function mbzBuildPageFallbackMarkup() {
        return MBZ_PAGE_FALLBACK_MARKUP;
    }

    function mbzEnsurePageFallback(pageEl) {
        if (!pageEl) return;
        if (pageEl.querySelector('[data-mbz-page-fallback="1"]')) return;
        if (String(pageEl.innerHTML || '').trim() !== '') return;
        pageEl.innerHTML = mbzBuildPageFallbackMarkup();
    }

    function mbzPreparePageForLazyReload(pageEl) {
        if (!pageEl) return;
        let componentUrl = pageEl.getAttribute('data-component-url') || '';
        if (!componentUrl) return;

        // Append current browser URL parameters to the component URL
        const currentUrl = new URL(window.location.href);
        const compUrlObj = new URL(componentUrl, window.location.href);
        currentUrl.searchParams.forEach((value, key) => {
            if (!compUrlObj.searchParams.has(key)) {
                compUrlObj.searchParams.set(key, value);
            }
        });
        componentUrl = compUrlObj.pathname + compUrlObj.search;

        if (mbzIsPageVisible(pageEl)) return;
        if (pageEl.getAttribute('data-loading') === 'true') return;

        const trigger = pageEl.getAttribute('data-hx-trigger') || 'mbz:load-page';

        pageEl.dispatchEvent(new CustomEvent('mbz:destroy', { bubbles: true }));

        if (window.htmx && typeof window.htmx.trigger === 'function') {
            window.htmx.trigger(pageEl, 'htmx:beforeCleanupElement');
        }

        pageEl.innerHTML = mbzBuildPageFallbackMarkup();
        pageEl.setAttribute('data-loaded', 'false');
        pageEl.setAttribute('data-loaded-at', '0');
        pageEl.setAttribute('data-offloaded-at', String(Date.now()));
        pageEl.setAttribute('data-loading', 'false');
        pageEl.setAttribute('hx-get', componentUrl);
        pageEl.setAttribute('hx-trigger', trigger);
        pageEl.setAttribute('hx-swap', 'innerHTML');
        mbzApplyPageLoadingIndicator(pageEl);

        if (window.htmx && typeof window.htmx.process === 'function') {
            window.htmx.process(pageEl);
        }
    }

    window.mbzLoadPageByKey = function (pageKey) {
        try {
            if (!pageKey) return;
            const pageEl = document.getElementById('page-' + String(pageKey));
            if (!pageEl) return;
            if (pageEl.getAttribute('data-loaded') === 'true') return;
            if (pageEl.getAttribute('data-loading') === 'true') return;

            let componentUrl = pageEl.getAttribute('data-component-url') || '';
            if (!componentUrl) return;

            // Append current browser URL parameters to the component URL
            // to ensure context (like group, open_id) is preserved during lazy loading.
            const currentUrl = new URL(window.location.href);
            const compUrlObj = new URL(componentUrl, window.location.href);
            currentUrl.searchParams.forEach((value, key) => {
                // Do not override explicit component params, but pass through everything else
                if (!compUrlObj.searchParams.has(key)) {
                    compUrlObj.searchParams.set(key, value);
                }
            });
            componentUrl = compUrlObj.pathname + compUrlObj.search;

            mbzEnsurePageFallback(pageEl);
            pageEl.setAttribute('data-loading', 'true');
            pageEl.setAttribute('data-loaded-at', '0');
            pageEl.setAttribute('hx-get', componentUrl);
            pageEl.setAttribute('hx-trigger', pageEl.getAttribute('data-hx-trigger') || 'mbz:load-page');
            pageEl.setAttribute('hx-swap', 'innerHTML');
            mbzApplyPageLoadingIndicator(pageEl);

            if (window.htmx && typeof window.htmx.process === 'function') {
                window.htmx.process(pageEl);
            }
            if (window.htmx && typeof window.htmx.trigger === 'function') {
                window.htmx.trigger(pageEl, 'mbz:load-page');
            }
        } catch (e) { /* no-op */ }
    };

    const mbzOffloadTimers = Object.create(null);

    function mbzParseOffloadDelayMs(rawDelay) {
        const raw = String(rawDelay || '').trim().toLowerCase();
        if (!raw) return 0;
        if (/^\d+(?:\.\d+)?$/.test(raw)) {
            return Math.max(0, Math.round(parseFloat(raw)));
        }
        const match = raw.match(/^(\d+(?:\.\d+)?)(ms|s)$/);
        if (!match) return 0;
        const amount = parseFloat(match[1]);
        if (!Number.isFinite(amount)) return 0;
        return Math.max(0, Math.round(match[2] === 's' ? amount * 1000 : amount));
    }

    function mbzScheduleOffloadCheck(key, delayMs) {
        const waitMs = Math.max(0, Math.round(delayMs || 0));
        mbzOffloadTimers[key] = window.setTimeout(() => {
            delete mbzOffloadTimers[key];

            const pageEl = document.getElementById('page-' + key);
            if (!pageEl) return;
            if (pageEl.getAttribute('data-offload') !== 'true') return;
            if (pageEl.getAttribute('data-loaded') !== 'true') return;

            if (pageEl.getAttribute('data-loading') === 'true') {
                mbzScheduleOffloadCheck(key, Math.max(2000, waitMs || 0));
                return;
            }

            if (mbzIsPageVisible(pageEl)) {
                mbzScheduleOffloadCheck(key, Math.max(2000, waitMs || 0));
                return;
            }

            if (mbzPageHasProtectedState(pageEl)) {
                mbzScheduleOffloadCheck(key, 10000);
                return;
            }

            mbzPreparePageForLazyReload(pageEl);
        }, waitMs);
    }

    window.mbzCancelOffloadByKey = function (pageKey) {
        try {
            if (!pageKey) return;
            const key = String(pageKey);
            if (!Object.prototype.hasOwnProperty.call(mbzOffloadTimers, key)) return;
            clearTimeout(mbzOffloadTimers[key]);
            delete mbzOffloadTimers[key];
        } catch (e) { /* no-op */
        }
    };

    window.mbzScheduleOffloadPageByKey = function (pageKey) {
        try {
            if (!pageKey) return;
            const key = String(pageKey);
            if (typeof window.mbzTouchPageByKey === 'function') {
                window.mbzTouchPageByKey(key);
            }
            if (typeof window.mbzCancelOffloadByKey === 'function') {
                window.mbzCancelOffloadByKey(key);
            }
            const pageEl = document.getElementById('page-' + key);
            if (!pageEl) return;
            if (pageEl.getAttribute('data-offload') !== 'true') return;
            if (pageEl.getAttribute('data-loaded') !== 'true') return;

            const delayMs = mbzParseOffloadDelayMs(pageEl.getAttribute('data-offload-delay-ms'));
            mbzScheduleOffloadCheck(key, delayMs <= 0 ? 0 : delayMs);
        } catch (e) { /* no-op */
        }
    };

    window.mbzOffloadPageByKey = function (pageKey) {
        try {
            if (!pageKey) return;
            const key = String(pageKey);
            if (typeof window.mbzCancelOffloadByKey === 'function') {
                window.mbzCancelOffloadByKey(key);
            }
            const pageEl = document.getElementById('page-' + key);
            if (!pageEl) return;
            if (pageEl.getAttribute('data-offload') !== 'true') return;
            if (pageEl.getAttribute('data-loaded') !== 'true') return;
            if (pageEl.getAttribute('data-loading') === 'true') return;
            if (mbzIsPageVisible(pageEl)) return;
            if (mbzPageHasProtectedState(pageEl)) return;
            mbzOffloadState.lastOffloadAtByKey[key] = Date.now();
            mbzPreparePageForLazyReload(pageEl);
        } catch (e) { /* no-op */
        }
    };

    // Keep active state in sync for HTMX-driven navigation and browser back/forward
    document.body.addEventListener('htmx:pushedIntoHistory', setActiveMenuFromURL);
    document.body.addEventListener('htmx:afterOnLoad', setActiveMenuFromURL);
    window.addEventListener('popstate', setActiveMenuFromURL);

    // Sidebar navigation → push URL (Alpine toggles DOM instantly)
    document.addEventListener('DOMContentLoaded', function () {
        try {
            document.querySelectorAll('#menu-list, #menu-list-bottom').forEach(function(menu) {
                menu.addEventListener('click', function (e) {
                    var a = e.target && e.target.closest ? e.target.closest('a[id^="desk-tab-"]') : null;
                    if (!a) return;
                    var key = a.id.replace('desk-tab-', '');
                    updateUrlForActive(key, false);
                });
            });
            // Ensure URL reflects the initially active tab on first load (no duplicate history entry)
            try {
                var params = new URLSearchParams(window.location.search || '');
                var TAB_PARAM = <?php echo json_encode($tabParam); ?>;
                var initial = <?php echo json_encode($tab); ?>;
                var current = (params.get(TAB_PARAM) || '').toLowerCase();
                if (String(initial).toLowerCase() !== current) {
                    updateUrlForActive(initial, true);
                }
            } catch (e2) { /* no-op */
            }
        } catch (e1) { /* no-op */
        }

        // htmx.logAll();
    });
    document.addEventListener("htmx:load", () => {
        // Bind global HTMX loader for navigation/content loads targeting #app-container and #app-main
        try {
            if (window.MBLoader) {
                MBLoader.bindHtmxOverlay('app-main', 'app-loading', 350, {
                    filter: function (evt) {
                        try {
                            if (typeof window.mbzShouldShowAppLoader === 'function') {
                                return window.mbzShouldShowAppLoader(evt, 'app-main');
                            }
                            return true;
                        } catch (e) {
                            return false;
                        }
                    }
                });
            }
        } catch (e) { /* no-op */
        }
    });

    document.addEventListener('htmx:beforeRequest', (evt) => {
        try {
            const target = evt && evt.detail ? evt.detail.elt : null;
            if (target && target.id && target.id.indexOf('page-') === 0) {
                target.setAttribute('data-loading', 'true');
                if (typeof window.mbzTouchPageByKey === 'function') {
                    window.mbzTouchPageByKey(String(target.dataset.page || ''));
                }
            }
        } catch (e) { /* no-op */ }
    });

    document.addEventListener('htmx:afterRequest', (evt) => {
        try {
            const target = evt && evt.detail ? evt.detail.elt : null;
            if (target && target.id && target.id.indexOf('page-') === 0) {
                target.setAttribute('data-loading', 'false');
                if (evt.detail && evt.detail.successful === false) {
                    target.setAttribute('data-loaded', 'false');
                }
            }
        } catch (e) { /* no-op */ }
    });

    document.addEventListener('htmx:beforeSwap', (evt) => {
        // If we are swapping a whole page, start scroll at top; otherwise keep current position
        const targetId = (evt.detail.target && evt.detail.target.id) ? evt.detail.target.id : '';
        if (targetId.indexOf('page-') === 0) {
            window._scrollY = 0;
        } else {
            window._scrollY = window.scrollY;
        }

        try {
            if (window.parent && window.parent !== window) {
                window.parent._iframeScrollY = window.parent.scrollY;
            }
        } catch (e) {}
    });

    document.addEventListener('htmx:afterSwap', (evt) => {
        try {
            const swapTarget = evt && evt.detail ? evt.detail.target : null;
            if (swapTarget && swapTarget.id && swapTarget.id.indexOf('page-') === 0) {
                swapTarget.setAttribute('data-loaded', 'true');
                swapTarget.setAttribute('data-loading', 'false');
                swapTarget.setAttribute('data-loaded-at', String(Date.now()));
                swapTarget.setAttribute('data-offloaded-at', '0');
                if (typeof window.mbzTouchPageByKey === 'function') {
                    window.mbzTouchPageByKey(String(swapTarget.dataset.page || ''));
                }
            }
        } catch (e) { /* no-op */
        }

        requestAnimationFrame(() => {
            // Restore scroll inside iframe
            if (typeof window._scrollY === 'number') {
                window.scrollTo(0, window._scrollY);
            }

            // Restore scroll in parent document
            try {
                if (window.parent && window.parent !== window) {
                    if (typeof window.parent._iframeScrollY === 'number') {
                        window.parent.scrollTo(0, window.parent._iframeScrollY);
                    }
                }
            } catch (e) {}
        });
    });

    // --- Emoji picker bootstrap for HTMX + direct loads (EmailEditor, etc.) ---
    // Idempotent initializer keyed by data-init, e.g. data-init="emoji-subject-12" / "emoji-preheader-12".
    //
    // NOTE: When the EmailEditor is loaded inside a <dialog>-based modal, the browser
    // puts the dialog into the top layer above the regular document stacking
    // context. If the EmojiButton popover is attached to document.body it will
    // render *under* the dialog, regardless of z-index.
    //
    // To avoid layering issues *and* accidental modal closes when clicking inside
    // the picker, we:
    //   - Prefer attaching the picker to the closest <el-dialog-panel> ancestor,
    //     so it is treated as part of the modal content, not the backdrop.
    //   - Fall back to the closest <dialog> element, then to document.body.
    var mbzEmojiRegistry = window.__mbzEmojiRegistry;
    if (!mbzEmojiRegistry || typeof mbzEmojiRegistry !== 'object') {
        mbzEmojiRegistry = {
            byKey: Object.create(null),
            byButtonId: Object.create(null)
        };
        window.__mbzEmojiRegistry = mbzEmojiRegistry;
    }

    function cleanupEmojiEntry(entry) {
        if (!entry) return;

        try {
            if (entry.picker && typeof entry.picker.hidePicker === 'function') {
                entry.picker.hidePicker();
            }
        } catch (e1) { /* no-op */ }

        try {
            if (entry.picker && typeof entry.picker.destroy === 'function') {
                entry.picker.destroy();
            }
        } catch (e2) { /* no-op */ }

        try {
            if (entry.button && entry.onClick) {
                entry.button.removeEventListener('click', entry.onClick);
            }
        } catch (e3) { /* no-op */ }
    }

    function cleanupEmojiByKey(key) {
        try {
            if (!key) return;
            var entry = mbzEmojiRegistry.byKey[key];
            if (!entry) return;
            cleanupEmojiEntry(entry);
            if (entry.buttonId && mbzEmojiRegistry.byButtonId[entry.buttonId] === key) {
                delete mbzEmojiRegistry.byButtonId[entry.buttonId];
            }
            delete mbzEmojiRegistry.byKey[key];
        } catch (e) { /* no-op */ }
    }

    function registerEmojiEntry(key, entry) {
        if (!key || !entry) return;
        mbzEmojiRegistry.byKey[key] = entry;
        if (entry.buttonId) {
            mbzEmojiRegistry.byButtonId[entry.buttonId] = key;
        }
    }

    function mbzCleanupEmojiInScope(root) {
        try {
            var scope = root && root.querySelectorAll ? root : null;
            var keys = Object.keys(mbzEmojiRegistry.byKey);

            keys.forEach(function (key) {
                var entry = mbzEmojiRegistry.byKey[key];
                if (!entry) return;

                var button = entry.button;
                var field = entry.field;
                var pickerRoot = entry.rootEl;

                var disconnected = !button || !field || !button.isConnected || !field.isConnected;
                var withinScope = !!(
                    scope && (
                        (button && scope.contains(button)) ||
                        (field && scope.contains(field)) ||
                        (pickerRoot && scope.contains(pickerRoot))
                    )
                );

                if (disconnected || withinScope) {
                    cleanupEmojiByKey(key);
                }
            });

            if (scope) {
                scope.querySelectorAll('.emoji-picker, em-emoji-picker, [data-emoji-picker-root]').forEach(function (el) {
                    try { el.remove(); } catch (e4) { /* no-op */ }
                });
            }
        } catch (e) { /* no-op */ }
    }

    function resolveEmojiCleanupScopeFromEvent(event) {
        try {
            var detail = event && event.detail ? event.detail : null;
            if (detail && detail.panelId) {
                var panel = document.getElementById(String(detail.panelId));
                if (panel) return panel;
            }
            if (event && event.target && event.target.querySelectorAll) {
                return event.target;
            }
        } catch (e) { /* no-op */ }
        return null;
    }

    function initEmoji(key) {
        try {
            if (!key || typeof key !== 'string') return;

            // Expect pattern: "emoji-<fieldType>-<id>"
            var parts = key.split('-');
            if (parts.length < 3) return;

            var fieldType = parts[1]; // e.g. "subject" or "preheader"
            var id = parts.slice(2).join('-'); // support ids with dashes just in case

            if (!fieldType || !id) return;

            // Degrade gracefully if emoji bundle or helper is not present
            if (typeof EmojiButton === 'undefined' || !window.textFieldEdit) return;

            var buttonId = 'emoji-button-' + fieldType + '-' + id;
            var button = document.getElementById(buttonId);
            var field = document.querySelector('#' + fieldType + '-' + id);

            if (!button || !field) return;

            var existingByKey = mbzEmojiRegistry.byKey[key] || null;
            if (existingByKey && existingByKey.button === button && existingByKey.field === field) {
                return;
            }
            if (existingByKey) {
                cleanupEmojiByKey(key);
            }

            var existingKeyByButton = mbzEmojiRegistry.byButtonId[buttonId] || null;
            if (existingKeyByButton && existingKeyByButton !== key) {
                cleanupEmojiByKey(existingKeyByButton);
            }

            // Choose a root element for the picker that respects modal stacking
            // contexts and does not trigger outside-click-to-close on the
            // surrounding dialog. Mounting inside <el-dialog-panel> keeps clicks
            // within the modal content area.
            var rootEl = document.body;
            try {
                if (button.closest) {
                    // Prefer the dialog panel (modal content area)
                    var panelAncestor = button.closest('el-dialog-panel, [id$="-panel"]');
                    if (panelAncestor) {
                        rootEl = panelAncestor;
                    } else {
                        // Fallback: raw <dialog> element if present
                        var dialogAncestor = button.closest('dialog');
                        if (dialogAncestor) {
                            rootEl = dialogAncestor;
                        }
                    }
                }
            } catch (e) { /* no-op, fall back to body */ }

            var picker = new EmojiButton({
                position: 'bottom-end',
                rootElement: rootEl,
                autoHide: false,
                zIndex: 9999
            });

            picker.on('emoji', function (emoji) {
                try {
                    window.textFieldEdit.insert(field, emoji.emoji);
                    field.focus();
                } catch (e) { /* no-op */ }
            });

            var onButtonClick = function () {
                try {
                    picker.togglePicker(button);
                } catch (e) { /* no-op */ }
            };

            button.addEventListener('click', onButtonClick);

            registerEmojiEntry(key, {
                key: key,
                buttonId: buttonId,
                button: button,
                field: field,
                picker: picker,
                onClick: onButtonClick,
                rootEl: rootEl
            });
        } catch (eOuter) { /* no-op */ }
    }

    function scanEmojiInits(root) {
        try {
            var scope = root && root.querySelectorAll ? root : document.body;
            if (!scope) return;
            var nodes = scope.querySelectorAll('[data-init^="emoji-"]');
            if (!nodes || !nodes.length) return;
            nodes.forEach(function (el) {
                var key = el.getAttribute('data-init');
                if (!key) return;
                initEmoji(key);
                el.removeAttribute('data-init');
            });
        } catch (e) { /* no-op */ }
    }


    // HTMX partial loads (e.g. EmailEditor in a modal)
    document.body.addEventListener('htmx:load', function (e) {
        var target = e && e.target ? e.target : document.body;
        scanEmojiInits(target);
    });

    document.body.addEventListener('htmx:afterSettle', function (e) {
        var target = e && e.target ? e.target : document.body;
        scanEmojiInits(target);
    });

    // Centralized modal-scope cleanup for transient emoji picker UI mounted in modal panels.
    document.body.addEventListener('mb:modal:cleanup', function (e) {
        var scope = resolveEmojiCleanupScopeFromEvent(e);
        mbzCleanupEmojiInScope(scope);

        // Keep page offload heuristics up-to-date after modal teardown.
        try {
            if (typeof window.mbzDynamicOffload === 'function') {
                window.mbzDynamicOffload();
            }
        } catch (e2) { /* no-op */ }
    });

    // Handle close-config-modal trigger
    document.body.addEventListener('close-config-modal', function() {
        var m = document.getElementById('config-modal');
        if (m && typeof m.close === 'function') m.close();
    });

    // Listen for cross-frame requests coming from legacy iframes (modal/drawer)
    // Supported messages:
    //  - { type: 'mb:trigger', event: 'evt:name', detail?: {...} }
    //  - { type: 'mb:close', id?: 'element-id' }  → close a modal/drawer in the parent window
    window.addEventListener('message', function (event) {
        try {
            var data = event && event.data ? event.data : null;
            if (!data || !data.type) return;

            if (data.type === 'mb:trigger') {
                var ev = data.event || '';
                if (!ev) return;
                var detail = data.detail || {};
                try {
                    if (window.htmx && window.htmx.trigger) {
                        window.htmx.trigger(document.body, ev, detail);
                    } else if (document && document.body) {
                        document.body.dispatchEvent(new CustomEvent(ev, { detail: detail }));
                    }
                } catch (e3) { /* no-op */ }
                return;
            }

            if (data.type === 'mb:close') {
                try {
                    var id = data.id || null;
                    if (id) {
                        var el = document.getElementById(String(id));
                        if (el && typeof el.close === 'function') { el.close(); return; }
                    }
                    // Try common ids if none provided or element not found
                    var candidates = ['config-modal', 'config2-modal', 'confirm-modal', 'ai-modal', 'email-preview', 'config-drawer', 'editor-inline-modal'];
                    for (var i = 0; i < candidates.length; i++) {
                        var m = document.getElementById(candidates[i]);
                        if (m && typeof m.close === 'function') { m.close(); break; }
                    }
                } catch (e4) { /* no-op */ }
                return;
            }
        } catch (e) { /* no-op */ }
    });

    function mbzPageReport() {
        const pages = [...document.querySelectorAll('[data-page]')].map(page => ({
            id: page.id,
            nodes: page.getElementsByTagName('*').length,
            htmx: page.querySelectorAll('[hx-get], [hx-post], [hx-trigger]').length,
            alpine: page.querySelectorAll('[x-data]').length,
            visible: !!(page.offsetWidth || page.offsetHeight || page.getClientRects().length),
        }));

        console.table(pages);

        console.table({
            totalNodes: document.getElementsByTagName('*').length,
            totalHtmx: document.querySelectorAll('[hx-get], [hx-post], [hx-trigger]').length,
            totalAlpine: document.querySelectorAll('[x-data]').length,
            jsHeapMB: performance.memory
                ? Math.round(performance.memory.usedJSHeapSize / 1024 / 1024)
                : null,
        });
    }

</script>


<script language="javascript"
        src="<?php echo mh_cfg('MH_CATALOG_SERVER') . mh_cfg('MH_DIR_WS_CATALOG') . mh_cfg('MH_ROOT_PATH'); ?>common/js/jquery-1.11.3.min.js?ver=<?php mh_cfg('MAILBEEZ_VERSION', time()) ?>"></script>
<script type="text/javascript"
        src="<?php echo mh_cfg('MH_CATALOG_SERVER') . mh_cfg('MH_DIR_WS_CATALOG') . mh_cfg('MH_ROOT_PATH'); ?>configbeez/config_editor/js/editor.js?ver=<?php mh_cfg('MAILBEEZ_VERSION', time()) ?>"></script>

<?php if (mh_cfg('MAILBEEZ_EMOJI_SUPPORT')) { ?>
    <script type="text/javascript"
            src="<?php echo mh_cfg('MH_CATALOG_SERVER') . mh_cfg('MH_DIR_WS_CATALOG') . mh_cfg('MH_ROOT_PATH'); ?>common/js/emoji-bundle/emoji-button-bundle.js?ver=<?php mh_cfg('MAILBEEZ_VERSION', time()) ?>"></script>
<?php } ?>
<script type="text/javascript"
        src="<?php echo mh_cfg('MH_CATALOG_SERVER') . mh_cfg('MH_DIR_WS_CATALOG') . mh_cfg('MH_ROOT_PATH'); ?>common/js/text-field-edit.js?ver=<?php mh_cfg('MAILBEEZ_VERSION', time()) ?>"></script>


<script src="<?php echo defined('BEEZUI_WEB_ROOT') ? BEEZUI_WEB_ROOT : ''; ?>appui/assets/js/html2canvas-pro.min.js?ver=<?= mh_cfg('MAILBEEZ_VERSION') ?>"></script>

<?= \Components\Drawer::render(['style' => 'floating']); ?>
<?= \Components\Modal::render(); ?>
<?= \Components\Modal::render(['id' => 'editor-inline-modal',  'style' => 'editor_inline']); ?>
<?= \Components\Modal::render(['id' => 'confirm-modal', 'style' => 'confirm', 'close_button' => false, 'popin' => true]); ?>
<?= \Components\Modal::render(['id' => 'ai-modal', 'style' => 'ai']); ?>
<?= \Components\Modal::render(['id' => 'email-preview', 'style' => 'email_preview']); ?>
<?= \Components\Modal::render(['id' => 'web-preview', 'style' => 'web_preview']); ?>
    <!-- Async license check to ensure status is updated in background without blocking TTFB -->
    <div hx-get="<?= \Components\Router::url('license_check', ['partial' => 1]) ?>" hx-trigger="load delay:1s" class="hidden"></div>
</body>
</html>
