<?php
if (!defined('ABSPATH')) {
    exit;
}

/**
 * Service for managing frontend URLs, routing, sticky navigation, auth UI, and breadcrumbs.
 */
class EEP_Frontend_Navigation_Service {

    /**
     * @var string
     */
    private $post_type;

    /**
     * Constructor.
     */
    public function __construct($post_type = 'nexdirectory_event') {
        $this->post_type = sanitize_key($post_type);
    }

    /**
     * Helper to reliably check boolean/toggle options.
     */
    public function is_option_enabled($option_key, $default = '0') {
        $raw = get_option($option_key, $default);
        if (is_bool($raw)) return $raw;
        if (is_numeric($raw)) return ((float) $raw) !== 0.0;
        
        $normalized = strtolower(trim((string) $raw));
        if ($normalized === '') {
            $normalized = strtolower(trim((string) $default));
        }
        return in_array($normalized, ['1', 'true', 'yes', 'on', 'enabled'], true);
    }

    /**
     * Shared brand-name fallback used by frontend navigation.
     */
    private function get_platform_brand_name() {
        $company_brand_name = sanitize_text_field((string) get_option('eep_company_brand_name', ''));
        if ($company_brand_name !== '') {
            return $company_brand_name;
        }

        if ($this->is_option_enabled('eep_white_label_enabled', '0')) {
            $white_label_name = sanitize_text_field((string) get_option('eep_white_label_plugin_name', ''));
            if ($white_label_name !== '') {
                return $white_label_name;
            }
        }

        $site_name = sanitize_text_field((string) get_option('blogname', get_bloginfo('name')));
        if ($site_name !== '') {
            return $site_name;
        }

        return __('nexdirectory', 'nexdirectory');
    }

    /**
     * Normalizes the global page side padding option.
     *
     * Keeps sticky navigation and page wrappers aligned to the same value.
     *
     * @param string $default
     * @return string
     */
    public function get_page_side_padding_value($default = '10%') {
        $normalized_default = $this->normalize_page_side_padding_value($default, '10%');
        return $this->normalize_page_side_padding_value(
            (string) get_option('eep_page_side_padding', $normalized_default),
            $normalized_default
        );
    }

    /**
     * Mobile padding value. Empty option falls back to desktop padding.
     *
     * @param string $default
     * @return string
     */
    public function get_page_side_padding_mobile_value($default = '2%') {
        $desktop = $this->get_page_side_padding_value('10%');
        $fallback = trim((string) $default) !== '' ? (string) $default : $desktop;
        $fallback = $this->normalize_page_side_padding_value($fallback, $desktop);

        $raw = trim((string) get_option('eep_page_side_padding_mobile', '2%'));
        if ($raw === '') {
            return $fallback;
        }

        return $this->normalize_page_side_padding_value($raw, $fallback);
    }

    /**
     * Desktop sticky navbar side padding.
     * Empty option inherits desktop page side padding.
     *
     * @param string $default
     * @return string
     */
    public function get_nav_side_padding_desktop_value($default = '0%') {
        $desktop = $this->get_page_side_padding_value('10%');
        $fallback = trim((string) $default) !== '' ? (string) $default : $desktop;
        $fallback = $this->normalize_page_side_padding_value($fallback, $desktop);

        $raw = trim((string) get_option('eep_nav_side_padding_desktop', '0%'));
        if ($raw === '') {
            return $desktop;
        }

        return $this->normalize_page_side_padding_value($raw, $fallback);
    }

    /**
     * Normalizes percent-based page padding values and clamps to 0..15%.
     *
     * @param string $value
     * @param string $fallback
     * @return string
     */
    private function normalize_page_side_padding_value($value, $fallback = '10%') {
        $value = strtolower(trim((string) $value));
        $fallback = strtolower(trim((string) $fallback));
        if ($fallback === '') {
            $fallback = '10%';
        }
        if ($value === '') {
            $value = $fallback;
        }
        if (is_numeric($value)) {
            $value .= '%';
        }

        if (!preg_match('/^(\d+(?:\.\d+)?)%$/', $value, $matches)) {
            $value = $fallback;
            if (!preg_match('/^(\d+(?:\.\d+)?)%$/', $value, $matches)) {
                $matches = ['10%', '10'];
            }
        }

        $padding = (float) ($matches[1] ?? 10);
        if ($padding < 0) {
            $padding = 0;
        } elseif ($padding > 15) {
            $padding = 15;
        }

        if (abs($padding - (float) ((int) $padding)) < 0.000001) {
            $normalized = (string) ((int) $padding);
        } else {
            $normalized = rtrim(rtrim(sprintf('%.6F', $padding), '0'), '.');
        }
        if ($normalized === '') {
            $normalized = '0';
        }

        return $normalized . '%';
    }

    /**
     * Retrieves the current safe URL.
     */
    public function current_url() {
        $request_uri = '/';
        if (isset($_SERVER['REQUEST_URI'])) {
            $candidate = trim((string) wp_unslash($_SERVER['REQUEST_URI']));
            if ($candidate !== '') $request_uri = $candidate;
        }
        if ($request_uri === '' || strpos($request_uri, '/') !== 0) {
            $request_uri = '/' . ltrim($request_uri, '/');
        }
        return esc_url_raw(home_url($request_uri));
    }

    /**
     * Returns a frontend login-required prompt with a safe login URL.
     *
     * This method is used by shortcode renderers that need to stop output for
     * unauthenticated users without hard-killing the request.
     *
     * @param string $message
     * @return string
     */
    public function redirect_to_login_screen($message = '') {
        $notice = sanitize_text_field((string) $message);
        if ($notice === '') {
            $notice = sanitize_text_field((string) __('Please log in to continue.', 'nexdirectory'));
        }

        $current = $this->current_url();
        $login_url = $this->get_platform_page_url(
            'eep_page_login_signup_url',
            ['auth', 'login-signup'],
            wp_login_url($current)
        );
        if (!is_string($login_url) || trim($login_url) === '') {
            $login_url = wp_login_url($current);
        } else {
            $login_url = eep_add_query_arg('redirect_to', $current, $login_url);
        }

        return '<section class="eep-page-banner"><h1>'
            . esc_html__('Login required', 'nexdirectory')
            . '</h1><p>'
            . esc_html($notice)
            . '</p></section><div class="eep-submit-wrap"><section class="eep-form-card"><p>'
            . esc_html__('Sign in to continue this action.', 'nexdirectory')
            . '</p><p><a class="eep-btn" data-eep-auth-open="1" data-eep-auth-tab="login" href="'
            . esc_url($login_url)
            . '">'
            . esc_html__('Go to Login / Signup', 'nexdirectory')
            . '</a></p></section></div>';
    }

    /**
     * Redirects to a URL and appends a normalized frontend notice payload.
     *
     * @param string $base_url
     * @param string $message
     * @param string $type
     * @return void
     */
    public function redirect_with_message($base_url, $message, $type = 'success') {
        $target = esc_url_raw((string) $base_url);
        if ($target === '') {
            $target = esc_url_raw(home_url('/'));
        }

        $notice = sanitize_text_field((string) $message);
        if ($notice === '') {
            $notice = sanitize_text_field((string) __('Action completed.', 'nexdirectory'));
        }

        $notice_type = sanitize_key((string) $type);
        if (!in_array($notice_type, ['success', 'error', 'warning', 'info', 'warn'], true)) {
            $notice_type = 'success';
        }
        if ($notice_type === 'warn') {
            $notice_type = 'warning';
        }

        $url = eep_add_query_arg(
            [
                'eep_notice' => $notice,
                'eep_notice_type' => $notice_type,
            ],
            $target
        );

        $is_ajax_notice = (string) wp_unslash($_REQUEST['eep_ajax'] ?? '') === '1'; // phpcs:ignore WordPress.Security.NonceVerification.Recommended
        if ($is_ajax_notice) {
            $payload = [
                'message' => $notice,
                'type' => $notice_type,
                'redirect' => $url,
            ];
            if (in_array($notice_type, ['error', 'warning'], true)) {
                wp_send_json_error($payload, $notice_type === 'error' ? 400 : 200);
            }
            wp_send_json_success($payload);
        }

        $this->safe_redirect_or_fallback($url);
    }

    /**
     * Renders redirect notice payload for frontend toaster consumption.
     *
     * @return string
     */
    public function render_notice() {
        if (empty($_GET['eep_notice'])) {
            return '';
        }

        $notice = sanitize_text_field(rawurldecode((string) wp_unslash($_GET['eep_notice'])));
        if ($notice === '') {
            return '';
        }

        $type = !empty($_GET['eep_notice_type']) ? sanitize_key((string) wp_unslash($_GET['eep_notice_type'])) : 'success';
        if ($type === 'warn') {
            $type = 'warning';
        }
        if (!in_array($type, ['success', 'error', 'warning', 'info'], true)) {
            $type = 'success';
        }

        return sprintf(
            '<div data-eep-toast-message="%1$s" data-eep-toast-type="%2$s" style="display:none !important;" aria-hidden="true"></div><noscript><div class="eep-notice eep-notice-%2$s">%3$s</div></noscript>',
            esc_attr($notice),
            esc_attr($type),
            esc_html($notice)
        );
    }

    /**
     * Safe redirect that falls back to JS/Meta refresh if headers are already sent.
     */
    public function safe_redirect_or_fallback($url, $status = 302) {
        $url = esc_url_raw((string) $url);
        if ($url === '') return;
        
        $status = (int) $status;
        if ($status < 300 || $status > 399) {
            $status = 302;
        }

        $target_host = strtolower((string) parse_url($url, PHP_URL_HOST));
        $site_host = strtolower((string) parse_url(home_url('/'), PHP_URL_HOST));
        $allowed_external_hosts = array_values(array_unique(array_filter(array_map('strtolower', (array) apply_filters(
            'eep_allowed_external_redirect_hosts',
            [
                'checkout.stripe.com',
                'pay.stripe.com',
                'hooks.stripe.com',
                'www.paypal.com',
                'paypal.com',
                'www.sandbox.paypal.com',
                'sandbox.paypal.com',
            ]
        )))));
        $is_external_gateway_redirect = $target_host !== '' && $site_host !== '' && $target_host !== $site_host && in_array($target_host, $allowed_external_hosts, true);

        if (!headers_sent()) {
            if ($is_external_gateway_redirect) {
                wp_redirect($url, $status);
                exit;
            }
            wp_safe_redirect($url, $status);
            exit;
        }

        echo '<script>window.location.replace(' . wp_json_encode($url) . ');</script>';
        echo '<noscript><meta http-equiv="refresh" content="0;url=' . esc_url($url) . '"></noscript>';
        exit;
    }

    /**
     * Generates a dynamic platform page URL, recovering via shortcodes if the slug is broken.
     */
    public function get_platform_page_url($option_key, $fallback_slug = '', $fallback_url = '') {
        $option_key = sanitize_key((string) $option_key);
        $fallback_slugs = is_array($fallback_slug) ? $fallback_slug : [$fallback_slug];
        $fallback_slugs = array_values(array_unique(array_filter(array_map('eep_sanitize_title', $fallback_slugs))));
        $preferred_slug = isset($fallback_slugs[0]) ? (string) $fallback_slugs[0] : '';
        $configured = $option_key !== '' ? esc_url_raw((string) get_option($option_key, '')) : '';
        $is_configured_root = $configured !== '' && $this->is_site_root_url($configured);

        // 1. Check explicitly configured page option
        if ($configured !== '' && !$is_configured_root) {
            $configured_page_id = absint((int) url_to_postid($configured));
            if ($configured_page_id > 0) {
                if ((string) get_post_status($configured_page_id) === 'publish') {
                    $configured_permalink = get_permalink($configured_page_id);
                    if (is_string($configured_permalink) && $configured_permalink !== '') {
                        return esc_url_raw((string) $configured_permalink);
                    }
                }
            }
            return $configured;
        }

        // 2. Check fallback slugs
        foreach ($fallback_slugs as $fallback_slug_item) {
            $fallback_slug_value = sanitize_title((string) $fallback_slug_item);
            if ($fallback_slug_value === '') continue;
            
            $page = get_page_by_path($fallback_slug_value);
            if ($page instanceof WP_Post && !is_wp_error($page) && (string) get_post_status((int) $page->ID) === 'publish') {
                $url = get_permalink((int) $page->ID);
                if (is_string($url) && $url !== '') {
                    return $url;
                }
            }
        }

        // 3. Database lookup for shortcode
        $shortcode_map = [
            'eep_page_account_url'         => 'eep_account',
            'eep_page_login_signup_url'    => 'eep_auth',
            'eep_page_submit_event_url'    => 'eep_submit',
            'eep_page_my_events_url'       => 'eep_my_listings',
            'eep_page_events_url'          => 'eep_listings_archive',
            'eep_page_events_map_url'      => 'eep_listings_map',
            'eep_page_events_calendar_url' => 'eep_listings_calendar',
            'eep_page_event_webapp_url'    => 'eep_event_webapp',
            'eep_page_boost_posts_url'     => 'eep_boost_posts',
            'eep_page_owner_analytics_url' => 'eep_owner_analytics',
            'eep_page_my_tickets_url'      => 'eep_saved_listings',
            'eep_page_whos_attending_url'  => 'eep_whos_attending',
            'eep_page_notifications_url'   => 'eep_notifications',
            'eep_page_whats_new_url'       => 'eep_whats_new',
            'eep_claim_listing_page_url'   => 'eep_claim_listing',
        ];
        
        $shortcode_tag = isset($shortcode_map[$option_key]) ? sanitize_key((string) $shortcode_map[$option_key]) : '';
        if ($shortcode_tag !== '') {
            global $wpdb;
            $like = '%' . $wpdb->esc_like('[' . $shortcode_tag) . '%';
            $shortcode_page_id = (int) $wpdb->get_var($wpdb->prepare(
                "SELECT ID FROM {$wpdb->posts} WHERE post_type = 'page' AND post_status = 'publish' AND post_content LIKE %s ORDER BY ID ASC LIMIT 1",
                $like
            ));
            if ($shortcode_page_id > 0) {
                $shortcode_url = get_permalink($shortcode_page_id);
                if (is_string($shortcode_url) && $shortcode_url !== '') {
                    return $shortcode_url;
                }
            }
        }

        // 4. Hard fallback
        $fallback_url = esc_url_raw((string) $fallback_url);
        return $fallback_url !== '' ? $fallback_url : home_url('/');
    }

    /**
     * Whether a URL points to the site root path ("/"), which is invalid for route options.
     *
     * @param string $url
     * @return bool
     */
    private function is_site_root_url($url) {
        $url = esc_url_raw((string) $url);
        if ($url === '') {
            return false;
        }
        $path = (string) wp_parse_url($url, PHP_URL_PATH);
        $path = trim($path);
        if ($path === '' || $path === '/') {
            return true;
        }
        return untrailingslashit($path) === '';
    }

    /**
     * Resolves the custom login page URL.
     */
    public function get_custom_login_page_url() {
        $configured = esc_url_raw((string) get_option('eep_page_login_signup_url', ''));
        if ($configured !== '') return $configured;

        $fallback_page = get_page_by_path('login-signup');
        if ($fallback_page instanceof WP_Post && (string) $fallback_page->post_status === 'publish') {
            $url = get_permalink((int) $fallback_page->ID);
            if (is_string($url) && $url !== '') {
                return esc_url_raw((string) $url);
            }
        }
        return '';
    }

    /**
     * Legacy-compatible helper for building login/signup URL with redirect target.
     */
    public function get_login_signup_url($redirect_to = '') {
        $redirect_to = esc_url_raw((string) $redirect_to);
        if ($redirect_to === '') {
            $redirect_to = $this->current_url();
        }

        $login_base = $this->get_custom_login_page_url();
        if ($login_base === '') {
            return wp_login_url($redirect_to);
        }

        return eep_add_query_arg('redirect_to', $redirect_to, $login_base);
    }

    /**
     * Replaces the native wp_login_url with the platform custom login page.
     */
    public function filter_wp_login_url($login_url, $redirect = '', $force_reauth = false) {
        $custom_login = $this->get_custom_login_page_url();
        if ($custom_login === '') return $login_url;

        $redirect = esc_url_raw((string) $redirect);
        if ($redirect === '') $redirect = $this->current_url();
        
        $args = ['redirect_to' => $redirect];
        if ($force_reauth) $args['reauth'] = '1';

        return eep_add_query_arg($args, $custom_login);
    }

    /**
     * Redirects users away from wp-login.php to the custom frontend login screen.
     */
    public function maybe_redirect_wp_login_screen() {
        $custom_login = $this->get_custom_login_page_url();
        if ($custom_login === '') return;
        if ((defined('REST_REQUEST') && REST_REQUEST) || wp_doing_ajax()) return;
        if (isset($_GET['eep_allow_wp_login']) && current_user_can('manage_options')) return;

        $action = isset($_REQUEST['action']) ? sanitize_key((string) wp_unslash($_REQUEST['action'])) : 'login';
        if (in_array($action, ['logout', 'lostpassword', 'retrievepassword', 'rp', 'resetpass', 'postpass'], true)) {
            return;
        }
        if (isset($_REQUEST['interim-login']) || isset($_REQUEST['checkemail'])) return;

        $redirect_to = isset($_REQUEST['redirect_to']) ? esc_url_raw((string) wp_unslash($_REQUEST['redirect_to'])) : '';
        $redirect_to = $redirect_to === '' ? $this->current_url() : $redirect_to;
        
        $this->safe_redirect_or_fallback(eep_add_query_arg('redirect_to', $redirect_to, $custom_login));
    }

    /**
     * Renders frontend platform breadcrumbs.
     */
    public function render_platform_breadcrumbs($current_label = '', $plural_label = 'Items', $archive_url = '') {
        $items = [];
        $items[] = [
            'label' => __('Home', 'nexdirectory'),
            'url' => home_url('/'),
        ];

        $current_label = sanitize_text_field((string) $current_label);
        
        if (is_singular($this->post_type)) {
            $items[] = [
                'label' => $plural_label,
                'url' => $archive_url,
            ];
            $event_title = $current_label !== '' ? $current_label : get_the_title(get_the_ID());
            if ($event_title !== '') {
                $items[] = ['label' => sanitize_text_field((string) $event_title), 'url' => ''];
            }
        } elseif (is_page()) {
            $page_id = (int) get_queried_object_id();
            if ($page_id > 0) {
                $ancestors = array_reverse((array) get_post_ancestors($page_id));
                foreach ($ancestors as $ancestor_id) {
                    $ancestor_id = absint((int) $ancestor_id);
                    if ($ancestor_id <= 0) continue;
                    
                    $label = get_the_title($ancestor_id);
                    $url = get_permalink($ancestor_id);
                    if ($label !== '' && is_string($url) && $url !== '') {
                        $items[] = ['label' => sanitize_text_field((string) $label), 'url' => esc_url_raw((string) $url)];
                    }
                }
                $normalized_archive_url = esc_url_raw((string) $archive_url);
                if ($normalized_archive_url !== '' && $normalized_archive_url !== '#') {
                    $items[] = [
                        'label' => sanitize_text_field((string) $plural_label),
                        'url' => $normalized_archive_url,
                    ];
                }
                $page_title = $current_label !== '' ? $current_label : get_the_title($page_id);
                if ($page_title !== '') {
                    $items[] = ['label' => sanitize_text_field((string) $page_title), 'url' => ''];
                }
            }
        } else {
            $archive_label = $current_label;
            if ($archive_label === '' && function_exists('get_the_archive_title')) {
                $archive_label = wp_strip_all_tags((string) get_the_archive_title());
            }
            if ($archive_label !== '') {
                $items[] = ['label' => sanitize_text_field((string) $archive_label), 'url' => ''];
            }
        }

        if (count($items) < 2) return '';

        static $breadcrumb_style_printed = false;
        $breadcrumb_style = '';
        if (!$breadcrumb_style_printed) {
            $breadcrumb_style = '<style id="eep-platform-breadcrumb-style">'
                . '.eep-breadcrumb-shell{max-width:80rem;margin:0 auto;padding:1rem clamp(1rem,2.4vw,2rem) 0;}'
                . '@media (min-width:640px){.eep-breadcrumb-shell{padding-top:2rem;}}'
                . '.eep-breadcrumb-shell .eep-breadcrumbs{display:flex;flex-wrap:wrap;align-items:center;gap:0.35rem;margin:0 0 1.5rem 0;padding:0;max-width:none;color:#6b7280;font-size:.875rem;line-height:1.45;}'
                . '.eep-breadcrumb-shell .eep-breadcrumbs a{color:#4b5563;text-decoration:none;font-weight:500;transition:color .2s ease;}'
                . '.eep-breadcrumb-shell .eep-breadcrumbs a:hover,.eep-breadcrumb-shell .eep-breadcrumbs a:focus{color:var(--eep-primary,#f97316);text-decoration:none;}'
                . '.eep-breadcrumb-shell .eep-breadcrumbs .is-current{color:#111827;font-weight:600;}'
                . '.eep-breadcrumb-shell .eep-breadcrumbs .eep-breadcrumb-sep{color:#9ca3af;margin:0 .2rem;}'
                . '</style>';
            $breadcrumb_style_printed = true;
        }

        ob_start();
        echo $breadcrumb_style; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
        ?>
        <div class="eep-breadcrumb-shell">
            <nav class="eep-breadcrumbs" aria-label="<?php esc_attr_e('Breadcrumb', 'nexdirectory'); ?>">
                <?php $item_count = count($items); ?>
                <?php foreach ($items as $index => $item) : ?>
                    <?php
                    $label = sanitize_text_field((string) ($item['label'] ?? ''));
                    if ($label === '') continue;
                    $url = esc_url_raw((string) ($item['url'] ?? ''));
                    $is_last = ((int) $index === (int) ($item_count - 1));
                    ?>
                    <?php if (!$is_last && $url !== '') : ?>
                        <a href="<?php echo esc_url($url); ?>"><?php echo esc_html($label); ?></a>
                        <span class="eep-breadcrumb-sep" aria-hidden="true">/</span>
                    <?php else : ?>
                        <span class="is-current" aria-current="page"><?php echo esc_html($label); ?></span>
                        <?php if (!$is_last) : ?>
                            <span class="eep-breadcrumb-sep" aria-hidden="true">/</span>
                        <?php endif; ?>
                    <?php endif; ?>
                <?php endforeach; ?>
            </nav>
        </div>
        <?php
        return (string) ob_get_clean();
    }

    /**
     * Evaluates whether the sticky nav should be shown based on user state and settings.
     */
    public function should_show_frontend_sticky_nav_for_current_user() {
        if (!$this->is_option_enabled('eep_enable_frontend_sticky_nav', '1')) {
            return false;
        }
        if (is_user_logged_in()) {
            return $this->is_option_enabled('eep_frontend_sticky_nav_show_for_members', '1');
        }
        return $this->is_option_enabled('eep_frontend_sticky_nav_show_for_guests', '1');
    }

    /**
     * Determines if the sticky nav is already handling auth rendering.
     */
    public function sticky_nav_handles_auth_cta_for_current_user() {
        return $this->should_show_frontend_sticky_nav_for_current_user()
            && $this->is_option_enabled('eep_frontend_sticky_nav_include_auth_cta', '1');
    }

    /**
     * Extracts custom sticky nav links from plugin settings.
     */
    public function parse_frontend_sticky_nav_custom_links() {
        $raw = (string) get_option('eep_frontend_sticky_nav_custom_links', '');
        $lines = preg_split('/\r\n|\r|\n/', $raw) ?: [];
        $links = [];
        
        foreach ($lines as $line) {
            $line = trim((string) $line);
            if ($line === '' || strpos($line, '#') === 0) continue;
            
            $parts = array_map('trim', explode('|', $line));
            $label = sanitize_text_field((string) ($parts[0] ?? ''));
            $url = esc_url_raw((string) ($parts[1] ?? ''));
            $audience = sanitize_key((string) ($parts[2] ?? 'all'));
            $group = trim((string) str_replace('|', ' ', sanitize_text_field((string) ($parts[3] ?? __('More', 'nexdirectory')))));
            
            if ($group === '') $group = __('More', 'nexdirectory');
            if ($audience === 'loggedin') $audience = 'member';
            elseif ($audience === 'loggedout') $audience = 'guest';
            if (!in_array($audience, ['all', 'guest', 'member'], true)) $audience = 'all';
            
            if ($label === '' || $url === '') continue;
            
            $links[] = [
                'label' => $label,
                'url' => $url,
                'audience' => $audience,
                'group' => $group,
            ];
            
            if (count($links) >= 16) break;
        }
        return $links;
    }

    /**
     * Resolves selected WP menu ID for the current visitor state.
     *
     * Priority:
     * guest -> guest menu
     * admin -> admin menu
     * role override -> role-specific menu
     * member fallback -> member menu
     *
     * @return int
     */
    private function get_frontend_sticky_nav_wp_menu_id_for_current_user() {
        if (!$this->is_option_enabled('eep_frontend_sticky_nav_use_wp_menus', '0')) {
            return 0;
        }

        if (!is_user_logged_in()) {
            return absint((int) get_option('eep_frontend_sticky_nav_wp_menu_guest', 0));
        }

        $admin_menu_id = absint((int) get_option('eep_frontend_sticky_nav_wp_menu_admin', 0));
        if ($admin_menu_id > 0 && current_user_can('manage_options')) {
            return $admin_menu_id;
        }

        $role_menu_map = get_option('eep_frontend_sticky_nav_wp_menu_roles', []);
        if (is_array($role_menu_map)) {
            $user = wp_get_current_user();
            if ($user instanceof WP_User && !empty($user->roles) && is_array($user->roles)) {
                foreach ($user->roles as $role_slug_raw) {
                    $role_slug = sanitize_key((string) $role_slug_raw);
                    if ($role_slug === '') {
                        continue;
                    }
                    $role_menu_id = isset($role_menu_map[$role_slug]) ? absint((int) $role_menu_map[$role_slug]) : 0;
                    if ($role_menu_id > 0) {
                        return $role_menu_id;
                    }
                }
            }
        }

        return absint((int) get_option('eep_frontend_sticky_nav_wp_menu_member', 0));
    }

    /**
     * Normalizes WP menu URLs, handling relative links and special hash placeholders.
     *
     * @param string $raw_url
     * @return string
     */
    private function normalize_frontend_sticky_nav_wp_menu_url($raw_url) {
        $url = trim((string) $raw_url);
        if ($url === '') {
            return '';
        }
        if ($url === '#') {
            return '#';
        }
        if (strpos($url, '/') === 0) {
            return esc_url_raw(home_url($url));
        }
        if (preg_match('/^[a-z][a-z0-9+\-.]*:/i', $url)) {
            return esc_url_raw($url);
        }
        return esc_url_raw(home_url('/' . ltrim($url, '/')));
    }

    /**
     * Recursively flattens child items under a WP menu parent into dropdown links.
     *
     * @param int $parent_id
     * @param array<int, array<int, array<string, mixed>>> $children_map
     * @param int $depth
     * @param int $max_depth
     * @return array<int, array<string, string>>
     */
    private function flatten_frontend_sticky_nav_wp_menu_children($parent_id, $children_map, $depth = 0, $max_depth = 2) {
        if ($depth > $max_depth) {
            return [];
        }

        $parent_id = absint((int) $parent_id);
        $children = isset($children_map[$parent_id]) && is_array($children_map[$parent_id]) ? $children_map[$parent_id] : [];
        if (empty($children)) {
            return [];
        }

        $links = [];
        foreach ($children as $child) {
            if (!is_array($child)) {
                continue;
            }
            $label = sanitize_text_field((string) ($child['label'] ?? ''));
            $url = esc_url_raw((string) ($child['url'] ?? ''));
            $id = absint((int) ($child['id'] ?? 0));
            if ($id <= 0 || $label === '') {
                continue;
            }
            if ($url !== '' && $url !== '#') {
                if ($depth > 0) {
                    $label = str_repeat('-- ', $depth) . $label;
                }
                $links[] = [
                    'label' => $label,
                    'url' => $url,
                ];
            }
            $descendants = $this->flatten_frontend_sticky_nav_wp_menu_children($id, $children_map, $depth + 1, $max_depth);
            foreach ($descendants as $descendant_link) {
                if (!is_array($descendant_link)) {
                    continue;
                }
                $desc_label = sanitize_text_field((string) ($descendant_link['label'] ?? ''));
                $desc_url = esc_url_raw((string) ($descendant_link['url'] ?? ''));
                if ($desc_label === '' || $desc_url === '') {
                    continue;
                }
                $links[] = [
                    'label' => $desc_label,
                    'url' => $desc_url,
                ];
            }
        }

        return $links;
    }

    /**
     * Builds sticky nav menu groups from a selected WordPress menu.
     *
     * Top-level menu items become group labels when they have children.
     * Top-level standalone links are collected under a generic "Menu" group.
     *
     * @param int $menu_id
     * @return array<int, array<string, mixed>>
     */
    private function get_frontend_sticky_nav_menu_groups_from_wp_menu($menu_id) {
        $menu_id = absint((int) $menu_id);
        if ($menu_id <= 0) {
            return [];
        }

        $menu_obj = wp_get_nav_menu_object($menu_id);
        if (!$menu_obj) {
            return [];
        }

        $menu_items = wp_get_nav_menu_items($menu_id, ['update_post_term_cache' => false]);
        if (!is_array($menu_items) || empty($menu_items)) {
            return [];
        }

        $indexed_items = [];
        foreach ($menu_items as $menu_item) {
            if (!is_object($menu_item)) {
                continue;
            }
            $item_id = absint((int) ($menu_item->ID ?? 0));
            if ($item_id <= 0) {
                continue;
            }

            $item_label = sanitize_text_field((string) wp_strip_all_tags((string) ($menu_item->title ?? '')));
            if ($item_label === '') {
                continue;
            }

            $indexed_items[$item_id] = [
                'id' => $item_id,
                'label' => $item_label,
                'url' => $this->normalize_frontend_sticky_nav_wp_menu_url((string) ($menu_item->url ?? '')),
                'parent_id' => absint((int) ($menu_item->menu_item_parent ?? 0)),
                'menu_order' => (int) ($menu_item->menu_order ?? 0),
            ];
        }

        if (empty($indexed_items)) {
            return [];
        }

        uasort($indexed_items, static function ($a, $b) {
            $a_order = (int) ($a['menu_order'] ?? 0);
            $b_order = (int) ($b['menu_order'] ?? 0);
            if ($a_order === $b_order) {
                return ((int) ($a['id'] ?? 0)) <=> ((int) ($b['id'] ?? 0));
            }
            return $a_order <=> $b_order;
        });

        $children_map = [];
        foreach ($indexed_items as $item) {
            $parent_id = absint((int) ($item['parent_id'] ?? 0));
            if ($parent_id > 0 && isset($indexed_items[$parent_id])) {
                if (!isset($children_map[$parent_id]) || !is_array($children_map[$parent_id])) {
                    $children_map[$parent_id] = [];
                }
                $children_map[$parent_id][] = $item;
            }
        }
        foreach ($children_map as &$children) {
            if (!is_array($children) || count($children) < 2) {
                continue;
            }
            usort($children, static function ($a, $b) {
                $a_order = (int) ($a['menu_order'] ?? 0);
                $b_order = (int) ($b['menu_order'] ?? 0);
                if ($a_order === $b_order) {
                    return ((int) ($a['id'] ?? 0)) <=> ((int) ($b['id'] ?? 0));
                }
                return $a_order <=> $b_order;
            });
        }
        unset($children);

        $groups = [];
        $standalone_links = [];
        foreach ($indexed_items as $item) {
            $parent_id = absint((int) ($item['parent_id'] ?? 0));
            if ($parent_id !== 0) {
                continue;
            }

            $item_id = absint((int) ($item['id'] ?? 0));
            if ($item_id <= 0) {
                continue;
            }

            $child_links = $this->flatten_frontend_sticky_nav_wp_menu_children($item_id, $children_map, 0, 2);
            if (!empty($child_links)) {
                $group_links = [];
                $item_url = esc_url_raw((string) ($item['url'] ?? ''));
                if ($item_url !== '' && $item_url !== '#') {
                    $group_links[] = [
                        'label' => sanitize_text_field((string) ($item['label'] ?? '')),
                        'url' => $item_url,
                    ];
                }
                foreach ($child_links as $child_link) {
                    if (!is_array($child_link)) {
                        continue;
                    }
                    $child_label = sanitize_text_field((string) ($child_link['label'] ?? ''));
                    $child_url = esc_url_raw((string) ($child_link['url'] ?? ''));
                    if ($child_label === '' || $child_url === '') {
                        continue;
                    }
                    $group_links[] = [
                        'label' => $child_label,
                        'url' => $child_url,
                    ];
                }
                if (!empty($group_links)) {
                    $groups[] = [
                        'label' => sanitize_text_field((string) ($item['label'] ?? '')),
                        'links' => $group_links,
                    ];
                }
                continue;
            }

            $item_url = esc_url_raw((string) ($item['url'] ?? ''));
            if ($item_url === '' || $item_url === '#') {
                continue;
            }
            $standalone_links[] = [
                'label' => sanitize_text_field((string) ($item['label'] ?? '')),
                'url' => $item_url,
            ];
        }

        if (!empty($standalone_links)) {
            array_unshift($groups, [
                'label' => __('Menu', 'nexdirectory'),
                'links' => $standalone_links,
            ]);
        }

        return $this->filter_boost_links_from_menu_groups($groups);
    }

    /**
     * Checks whether current request is one of the plugin app pages (shortcode pages).
     *
     * @param WP_Post|int|null $post
     * @return bool
     */
    public function is_app_page($post = null) {
        $resolved_post = $this->resolve_app_page_post($post);
        if (!$resolved_post instanceof WP_Post) {
            return false;
        }

        $is_marked = (string) get_post_meta((int) $resolved_post->ID, '_eep_is_app_page', true);
        if ($is_marked === '1') {
            return true;
        }

        if ($this->get_app_view($resolved_post) !== '') {
            return true;
        }

        return false;
    }

    /**
     * Returns normalized app view key for a plugin app page, otherwise empty string.
     *
     * @param WP_Post|int|null $post
     * @return string
     */
    public function get_app_view($post = null) {
        $resolved_post = $this->resolve_app_page_post($post);
        if (!$resolved_post instanceof WP_Post) {
            return '';
        }

        $meta_view = sanitize_key((string) get_post_meta((int) $resolved_post->ID, '_eep_app_view', true));
        if ($meta_view !== '') {
            return $meta_view;
        }

        $content_view = $this->extract_app_view_from_content((string) $resolved_post->post_content);
        if ($content_view !== '') {
            return $content_view;
        }

        $platform_page_ids = $this->get_platform_page_ids_for_title_hiding();
        if (in_array((int) $resolved_post->ID, $platform_page_ids, true)) {
            return 'app';
        }

        return '';
    }

    /**
     * Resolves a page post instance for app-page checks.
     *
     * @param WP_Post|int|null $post
     * @return WP_Post|null
     */
    private function resolve_app_page_post($post = null) {
        if (is_admin() || (function_exists('wp_doing_ajax') && wp_doing_ajax())) {
            return null;
        }
        if (defined('REST_REQUEST') && REST_REQUEST) {
            return null;
        }

        if ($post instanceof WP_Post) {
            if ($post->post_type === 'page') {
                return $post;
            }
            return null;
        }

        if (is_numeric($post) && (int) $post > 0) {
            $candidate = get_post((int) $post);
            if ($candidate instanceof WP_Post && $candidate->post_type === 'page') {
                return $candidate;
            }
            return null;
        }

        if (!is_page()) {
            return null;
        }

        $queried = get_queried_object();
        if ($queried instanceof WP_Post && $queried->post_type === 'page') {
            return $queried;
        }

        $queried_id = (int) get_queried_object_id();
        if ($queried_id > 0) {
            $candidate = get_post($queried_id);
            if ($candidate instanceof WP_Post && $candidate->post_type === 'page') {
                return $candidate;
            }
        }

        return null;
    }

    /**
     * Known app shortcode->view mapping.
     *
     * @return array<string, string>
     */
    private function get_app_shortcode_view_map() {
        return [
            'eep_events_archive' => 'events',
            'eep_listings_archive' => 'events',
            'eep_events_map' => 'map',
            'eep_listings_map' => 'map',
            'eep_events_calendar' => 'calendar',
            'eep_listings_calendar' => 'calendar',
            'eep_submit_event' => 'submit',
            'eep_submit' => 'submit',
            'eep_my_events' => 'my-events',
            'eep_my_listings' => 'my-events',
            'eep_boost_posts' => 'boost-posts',
            'eep_my_tickets' => 'my-tickets',
            'eep_saved_listings' => 'my-tickets',
            'eep_whos_attending' => 'whos-attending',
            'eep_attendees' => 'whos-attending',
            'eep_owner_analytics' => 'owner-analytics',
            'eep_notifications' => 'notifications',
            'eep_whats_new' => 'whats-new',
            'eep_account' => 'account',
            'eep_auth' => 'auth',
            'eep_user_management' => 'user-management',
            'eep_moderation_queue' => 'moderation-queue',
            'eep_organizers' => 'organizers',
            'eep_claim_listing' => 'claim-listing',
        ];
    }

    /**
     * Extracts app view key from page content shortcodes.
     *
     * @param string $content
     * @return string
     */
    private function extract_app_view_from_content($content) {
        $content = (string) $content;
        if ($content === '') {
            return '';
        }

        foreach ($this->get_app_shortcode_view_map() as $shortcode_tag => $view) {
            if (has_shortcode($content, $shortcode_tag) || has_shortcode($content, str_replace('eep_', 'nexdirectory_', $shortcode_tag))) {
                return sanitize_key((string) $view);
            }
        }

        return '';
    }

    /**
     * Checks whether the current frontend request belongs to platform UI pages.
     */
    private function is_platform_frontend_navigation_context() {
        if (is_admin() || (function_exists('wp_doing_ajax') && wp_doing_ajax())) {
            return false;
        }
        if (defined('REST_REQUEST') && REST_REQUEST) {
            return false;
        }
        $platform_taxonomies = $this->get_platform_taxonomy_slugs();
        if (!empty($platform_taxonomies) && is_tax($platform_taxonomies)) {
            return true;
        }
        if (is_category() && in_array('category', $platform_taxonomies, true)) {
            return true;
        }
        if (is_tag() && in_array('post_tag', $platform_taxonomies, true)) {
            return true;
        }
        if (is_singular($this->post_type) || is_post_type_archive($this->post_type)) {
            return true;
        }
        if ($this->is_app_page()) {
            return true;
        }
        return false;
    }

    /**
     * Returns taxonomy slugs registered for the platform post type.
     *
     * @return array<int, string>
     */
    private function get_platform_taxonomy_slugs() {
        static $cached = null;
        if (is_array($cached)) {
            return $cached;
        }

        $taxonomies = get_object_taxonomies($this->post_type, 'names');
        if (!is_array($taxonomies)) {
            $cached = [];
            return $cached;
        }

        $normalized = [];
        foreach ($taxonomies as $taxonomy) {
            $slug = sanitize_key((string) $taxonomy);
            if ($slug === '') {
                continue;
            }
            $normalized[$slug] = $slug;
        }

        $cached = array_values($normalized);
        return $cached;
    }

    /**
     * Returns sticky-nav behavior setting with safe fallback.
     */
    private function get_frontend_sticky_nav_behavior() {
        $behavior = sanitize_key((string) get_option('eep_frontend_sticky_nav_behavior', 'sticky'));
        if (!in_array($behavior, ['sticky', 'static', 'smart'], true)) {
            $behavior = 'sticky';
        }
        return $behavior;
    }

    /**
     * Resolves operational pages (notifications, moderation, user management) by slug/shortcode.
     *
     * @param array<int, string> $fallback_slugs
     * @param array<int, string> $shortcode_tags
     * @return string
     */
    private function get_frontend_ops_page_url(array $fallback_slugs = [], array $shortcode_tags = []) {
        foreach ($fallback_slugs as $slug) {
            $slug = sanitize_title((string) $slug);
            if ($slug === '') {
                continue;
            }
            $page = get_page_by_path($slug);
            if (!$page instanceof WP_Post || is_wp_error($page)) {
                continue;
            }
            if ((string) get_post_status((int) $page->ID) !== 'publish') {
                continue;
            }
            $url = get_permalink((int) $page->ID);
            if (is_string($url) && $url !== '') {
                return esc_url_raw((string) $url);
            }
        }

        global $wpdb;
        if ($wpdb instanceof wpdb) {
            foreach ($shortcode_tags as $shortcode_tag) {
                $shortcode_tag = sanitize_key((string) $shortcode_tag);
                if ($shortcode_tag === '') {
                    continue;
                }
                $like = '%' . $wpdb->esc_like('[' . $shortcode_tag) . '%';
                $page_id = (int) $wpdb->get_var(
                    $wpdb->prepare(
                        "SELECT ID FROM {$wpdb->posts} WHERE post_type = 'page' AND post_status = 'publish' AND post_content LIKE %s ORDER BY ID ASC LIMIT 1",
                        $like
                    )
                ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching
                if ($page_id <= 0) {
                    continue;
                }
                $url = get_permalink($page_id);
                if (is_string($url) && $url !== '') {
                    return esc_url_raw((string) $url);
                }
            }
        }

        return '';
    }

    /**
     * Resolves notifications center URL.
     */
    private function get_notifications_page_url() {
        $configured = esc_url_raw((string) get_option('eep_page_notifications_url', ''));
        if ($configured !== '') {
            return $configured;
        }
        $resolved = $this->get_frontend_ops_page_url(
            ['notifications', 'inbox'],
            ['eep_notifications', 'nexdirectory_notifications', 'eep_inbox', 'nexdirectory_inbox']
        );
        if ($resolved !== '') {
            return $resolved;
        }
        return esc_url_raw(home_url('/notifications/'));
    }

    /**
     * Appends a nav link while preventing duplicates.
     *
     * @param array<int, array<string, string>> $bucket
     * @param array<string, bool> $seen
     * @return void
     */
    private function add_unique_nav_link(array &$bucket, array &$seen, $label, $url) {
        $label = sanitize_text_field((string) $label);
        $url = esc_url_raw((string) $url);
        if ($label === '' || $url === '') {
            return;
        }
        if ($this->should_hide_boost_nav_link($label, $url)) {
            return;
        }
        $path = '/' . ltrim((string) wp_parse_url($url, PHP_URL_PATH), '/');
        if ($path !== '/') {
            $path = untrailingslashit($path);
        }
        $query = (string) wp_parse_url($url, PHP_URL_QUERY);
        if ($query !== '') {
            parse_str($query, $query_args);
            if (is_array($query_args)) {
                ksort($query_args);
                $query = http_build_query($query_args);
            }
        }
        $key = strtolower($label . '|' . $path . ($query !== '' ? '?' . $query : ''));
        if (isset($seen[$key])) {
            return;
        }
        $seen[$key] = true;
        $bucket[] = [
            'label' => $label,
            'url' => $url,
        ];
    }

    /**
     * Returns true when a nav link should be hidden from frontend menus.
     *
     * @param string $label
     * @param string $url
     * @return bool
     */
    private function should_hide_boost_nav_link($label, $url) {
        $label_value = strtolower(trim(sanitize_text_field((string) $label)));
        $url_value = esc_url_raw((string) $url);

        $link_path = '/' . ltrim((string) wp_parse_url($url_value, PHP_URL_PATH), '/');
        if ($link_path !== '/') {
            $link_path = untrailingslashit($link_path);
        }
        $link_path_lc = strtolower($link_path);

        if (!is_user_logged_in()) {
            $configured_submit_url = esc_url_raw((string) get_option('eep_page_submit_event_url', ''));
            $configured_submit_path = '';
            if ($configured_submit_url !== '') {
                $configured_submit_path = '/' . ltrim((string) wp_parse_url($configured_submit_url, PHP_URL_PATH), '/');
                if ($configured_submit_path !== '/') {
                    $configured_submit_path = untrailingslashit($configured_submit_path);
                }
                $configured_submit_path = strtolower($configured_submit_path);
            }
            $is_submit_link = (
                strpos($label_value, 'add new') !== false
                || strpos($label_value, 'submit') !== false
                || ($configured_submit_path !== '' && $configured_submit_path !== '/' && $configured_submit_path === $link_path_lc)
                || strpos($link_path_lc, '/submit') !== false
                || strpos($link_path_lc, '/add-new-event') !== false
            );
            if ($is_submit_link) {
                return true;
            }
        }

        if ($label_value !== '' && strpos($label_value, 'boost') !== false) {
            return true;
        }

        $configured_boost_url = esc_url_raw((string) get_option('eep_page_boost_posts_url', ''));
        if ($configured_boost_url !== '') {
            $configured_path = '/' . ltrim((string) wp_parse_url($configured_boost_url, PHP_URL_PATH), '/');
            if ($configured_path !== '/') {
                $configured_path = untrailingslashit($configured_path);
            }
            if ($configured_path !== '/' && strtolower($configured_path) === $link_path_lc) {
                return true;
            }
        }

        if ($link_path_lc !== '/' && (strpos($link_path_lc, '/boost') !== false || strpos($link_path_lc, '/boost-post') !== false)) {
            return true;
        }

        return false;
    }

    /**
     * Removes blocked links (e.g., Boost) from a prepared nav menu group structure.
     *
     * @param array<int, array<string, mixed>> $groups
     * @return array<int, array<string, mixed>>
     */
    private function filter_boost_links_from_menu_groups(array $groups) {
        $filtered_groups = [];

        foreach ($groups as $group) {
            if (!is_array($group)) {
                continue;
            }

            $group_label = sanitize_text_field((string) ($group['label'] ?? ''));
            if ($group_label !== '' && strpos(strtolower($group_label), 'boost') !== false) {
                continue;
            }

            $group_links = isset($group['links']) && is_array($group['links']) ? $group['links'] : [];
            $filtered_links = [];
            foreach ($group_links as $link) {
                if (!is_array($link)) {
                    continue;
                }
                $link_label = sanitize_text_field((string) ($link['label'] ?? ''));
                $link_url = esc_url_raw((string) ($link['url'] ?? ''));
                if ($link_label === '' || $link_url === '') {
                    continue;
                }
                if ($this->should_hide_boost_nav_link($link_label, $link_url)) {
                    continue;
                }
                $filtered_links[] = [
                    'label' => $link_label,
                    'url' => $link_url,
                ];
            }

            if (!empty($filtered_links)) {
                $filtered_groups[] = [
                    'label' => $group_label,
                    'links' => $filtered_links,
                ];
            }
        }

        return $filtered_groups;
    }

    /**
     * Builds grouped sticky-nav menu links from routed pages and custom settings.
     *
     * @return array<int, array<string, mixed>>
     */
    private function get_frontend_sticky_nav_menu_groups() {
        $wp_menu_id = $this->get_frontend_sticky_nav_wp_menu_id_for_current_user();
        if ($wp_menu_id > 0) {
            $wp_menu_groups = $this->get_frontend_sticky_nav_menu_groups_from_wp_menu($wp_menu_id);
            if (!empty($wp_menu_groups)) {
                return $wp_menu_groups;
            }
        }

        $is_member = is_user_logged_in();
        $can_manage = current_user_can('manage_options');
        $can_moderate = $can_manage || current_user_can('edit_others_posts');
        $can_manage_users = $can_manage || current_user_can('list_users');
        $platform_mode = sanitize_key((string) get_option('eep_platform_mode', 'user_generated'));
        $single_org_non_admin = $platform_mode === 'single_organisation' && !$can_manage;
        $can_submit_from_nav = !$single_org_non_admin;

        $seen = [];
        $explore_links = [];
        $workspace_links = [];
        $publish_links = [];
        $admin_links = [];

        $this->add_unique_nav_link(
            $explore_links,
            $seen,
            __('Explore', 'nexdirectory'),
            $this->get_platform_page_url('eep_page_events_url', ['events', 'listings'], home_url('/events/'))
        );
        $this->add_unique_nav_link(
            $explore_links,
            $seen,
            __('Map', 'nexdirectory'),
            $this->get_platform_page_url('eep_page_events_map_url', ['map', 'events-map'], home_url('/map/'))
        );
        $this->add_unique_nav_link(
            $explore_links,
            $seen,
            __('Calendar', 'nexdirectory'),
            $this->get_platform_page_url('eep_page_events_calendar_url', ['calendar', 'events-calendar'], home_url('/calendar/'))
        );
        $this->add_unique_nav_link(
            $explore_links,
            $seen,
            __('What\'s New', 'nexdirectory'),
            $this->get_platform_page_url('eep_page_whats_new_url', ['whats-new'], home_url('/whats-new/'))
        );

        if ($is_member && $can_submit_from_nav) {
            $this->add_unique_nav_link(
                $publish_links,
                $seen,
                __('Add New Event', 'nexdirectory'),
                $this->get_platform_page_url('eep_page_submit_event_url', ['submit', 'add-new-event'], home_url('/submit/'))
            );
        }

        if ($is_member) {
            if ($single_org_non_admin) {
                $this->add_unique_nav_link(
                    $workspace_links,
                    $seen,
                    __('Dashboard', 'nexdirectory'),
                    $this->get_platform_page_url('eep_page_account_url', ['account', 'my-account'], home_url('/account/'))
                );
            } else {
                $this->add_unique_nav_link(
                    $workspace_links,
                    $seen,
                    __('Workspace', 'nexdirectory'),
                    $this->get_platform_page_url('eep_page_my_events_url', ['my-events', 'workspace'], home_url('/my-events/'))
                );
            }
            $this->add_unique_nav_link(
                $workspace_links,
                $seen,
                __('Saved & Attending', 'nexdirectory'),
                $this->get_platform_page_url('eep_page_my_tickets_url', ['saved', 'attending', 'my-tickets'], home_url('/my-tickets/'))
            );
            $this->add_unique_nav_link(
                $workspace_links,
                $seen,
                __('Messages', 'nexdirectory'),
                $this->get_notifications_page_url()
            );
            $this->add_unique_nav_link(
                $workspace_links,
                $seen,
                __('Account Settings', 'nexdirectory'),
                $this->get_platform_page_url('eep_page_account_url', ['account', 'my-account'], home_url('/account/'))
            );
            $this->add_unique_nav_link(
                $workspace_links,
                $seen,
                __('Analytics', 'nexdirectory'),
                $this->get_platform_page_url('eep_page_owner_analytics_url', ['analytics', 'owner-analytics'], home_url('/analytics/'))
            );
        }

        if ($can_moderate) {
            $moderation_url = $this->get_frontend_ops_page_url(
                ['moderation-queue'],
                ['eep_moderation_queue', 'nexdirectory_moderation_queue']
            );
            if ($moderation_url === '' && $can_manage) {
                $moderation_url = esc_url_raw(admin_url('admin.php?page=eep-moderation'));
            }
            $this->add_unique_nav_link($admin_links, $seen, __('Moderation Queue', 'nexdirectory'), $moderation_url);
        }
        if ($can_manage_users) {
            $user_management_url = $this->get_frontend_ops_page_url(
                ['user-management'],
                ['eep_user_management', 'nexdirectory_user_management']
            );
            if ($user_management_url === '' && $can_manage) {
                $user_management_url = esc_url_raw(admin_url('users.php'));
            }
            $this->add_unique_nav_link($admin_links, $seen, __('User Management', 'nexdirectory'), $user_management_url);
            $this->add_unique_nav_link(
                $admin_links,
                $seen,
                __('Organizers', 'nexdirectory'),
                $this->get_frontend_ops_page_url(['organizers'], ['eep_organizers', 'nexdirectory_organizers'])
            );
        }
        if ($can_manage) {
            $this->add_unique_nav_link($admin_links, $seen, __('Platform Settings', 'nexdirectory'), admin_url('admin.php?page=eep-settings'));
        }

        $custom_group_links = [];
        foreach ($this->parse_frontend_sticky_nav_custom_links() as $custom_link) {
            $audience = sanitize_key((string) ($custom_link['audience'] ?? 'all'));
            if ($audience === 'member' && !$is_member) {
                continue;
            }
            if ($audience === 'guest' && $is_member) {
                continue;
            }
            $group = sanitize_text_field((string) ($custom_link['group'] ?? __('More', 'nexdirectory')));
            if ($group === '') {
                $group = __('More', 'nexdirectory');
            }
            if (!isset($custom_group_links[$group]) || !is_array($custom_group_links[$group])) {
                $custom_group_links[$group] = [];
            }
            $this->add_unique_nav_link(
                $custom_group_links[$group],
                $seen,
                (string) ($custom_link['label'] ?? ''),
                (string) ($custom_link['url'] ?? '')
            );
        }

        $groups = [];
        if (!empty($explore_links)) {
            $groups[] = ['label' => __('Explore', 'nexdirectory'), 'links' => array_values($explore_links)];
        }
        if ($is_member && !empty($workspace_links)) {
            $groups[] = ['label' => __('Workspace', 'nexdirectory'), 'links' => array_values($workspace_links)];
        }
        if (!empty($publish_links)) {
            $groups[] = ['label' => $is_member ? __('Publish', 'nexdirectory') : __('Get Started', 'nexdirectory'), 'links' => array_values($publish_links)];
        }
        if (!empty($admin_links)) {
            $groups[] = ['label' => __('Admin', 'nexdirectory'), 'links' => array_values($admin_links)];
        }
        foreach ($custom_group_links as $group_label => $group_links) {
            if (empty($group_links)) {
                continue;
            }
            $groups[] = ['label' => sanitize_text_field((string) $group_label), 'links' => array_values($group_links)];
        }

        return $groups;
    }

    /**
     * Evaluates whether the platform footer should be shown on the current request.
     *
     * @return bool
     */
    public function should_show_frontend_footer_for_current_user() {
        if (!$this->is_option_enabled('eep_enable_frontend_footer', '1')) {
            return false;
        }
        if (!$this->is_platform_frontend_navigation_context()) {
            return false;
        }
        return true;
    }

    /**
     * Returns normalized footer desktop menu column count (1..5).
     *
     * @return int
     */
    private function get_frontend_footer_menu_count() {
        $count = absint((int) get_option('eep_frontend_footer_menu_count', 3));
        if ($count < 1) {
            $count = 1;
        } elseif ($count > 5) {
            $count = 5;
        }
        return $count;
    }

    /**
     * Returns selected footer WP menu IDs (deduped, max 5).
     *
     * @return array<int, int>
     */
    private function get_frontend_footer_wp_menu_ids() {
        $raw_ids = get_option('eep_frontend_footer_wp_menus', []);
        if (!is_array($raw_ids)) {
            return [];
        }

        $menu_ids = [];
        foreach ($raw_ids as $raw_id) {
            $menu_id = absint((int) $raw_id);
            if ($menu_id <= 0) {
                continue;
            }
            if (isset($menu_ids[$menu_id])) {
                continue;
            }
            $menu_obj = wp_get_nav_menu_object($menu_id);
            if (!$menu_obj instanceof WP_Term) {
                continue;
            }
            $menu_ids[$menu_id] = $menu_id;
            if (count($menu_ids) >= 5) {
                break;
            }
        }

        return array_values($menu_ids);
    }

    /**
     * Builds a flat footer link list from one WP menu.
     *
     * @param int $menu_id
     * @param int $max_links
     * @return array<int, array<string, string>>
     */
    private function get_frontend_footer_links_from_wp_menu($menu_id, $max_links = 16) {
        $menu_id = absint((int) $menu_id);
        if ($menu_id <= 0) {
            return [];
        }

        $menu_items = wp_get_nav_menu_items($menu_id, ['update_post_term_cache' => false]);
        if (!is_array($menu_items) || empty($menu_items)) {
            return [];
        }

        $indexed = [];
        foreach ($menu_items as $menu_item) {
            if (!is_object($menu_item)) {
                continue;
            }
            $item_id = absint((int) ($menu_item->ID ?? 0));
            if ($item_id <= 0) {
                continue;
            }
            $label = sanitize_text_field((string) wp_strip_all_tags((string) ($menu_item->title ?? '')));
            if ($label === '') {
                continue;
            }
            $url = $this->normalize_frontend_sticky_nav_wp_menu_url((string) ($menu_item->url ?? ''));
            if ($url === '' || $url === '#') {
                continue;
            }
            $indexed[$item_id] = [
                'id' => $item_id,
                'label' => $label,
                'url' => $url,
                'parent_id' => absint((int) ($menu_item->menu_item_parent ?? 0)),
                'menu_order' => (int) ($menu_item->menu_order ?? 0),
            ];
        }

        if (empty($indexed)) {
            return [];
        }

        uasort($indexed, static function ($a, $b) {
            $a_order = (int) ($a['menu_order'] ?? 0);
            $b_order = (int) ($b['menu_order'] ?? 0);
            if ($a_order === $b_order) {
                return ((int) ($a['id'] ?? 0)) <=> ((int) ($b['id'] ?? 0));
            }
            return $a_order <=> $b_order;
        });

        $links = [];
        $seen = [];
        foreach ($indexed as $item) {
            $label = sanitize_text_field((string) ($item['label'] ?? ''));
            $url = esc_url_raw((string) ($item['url'] ?? ''));
            if ($label === '' || $url === '') {
                continue;
            }
            $path = '/' . ltrim((string) wp_parse_url($url, PHP_URL_PATH), '/');
            if ($path !== '/') {
                $path = untrailingslashit($path);
            }
            $query = (string) wp_parse_url($url, PHP_URL_QUERY);
            $dedupe_key = strtolower($label . '|' . $path . ($query !== '' ? ('?' . $query) : ''));
            if (isset($seen[$dedupe_key])) {
                continue;
            }
            $seen[$dedupe_key] = true;
            $links[] = [
                'label' => $label,
                'url' => $url,
            ];
            if (count($links) >= $max_links) {
                break;
            }
        }

        return $links;
    }

    /**
     * Builds footer columns from selected WP menus.
     *
     * @param array<int, int> $menu_ids
     * @param int $max_columns
     * @return array<int, array<string, mixed>>
     */
    private function get_frontend_footer_columns_from_wp_menus(array $menu_ids, $max_columns = 3) {
        $max_columns = max(1, min(5, absint((int) $max_columns)));
        if (empty($menu_ids)) {
            return [];
        }

        $columns = [];
        foreach ($menu_ids as $menu_id_raw) {
            $menu_id = absint((int) $menu_id_raw);
            if ($menu_id <= 0) {
                continue;
            }
            $menu_obj = wp_get_nav_menu_object($menu_id);
            if (!$menu_obj instanceof WP_Term) {
                continue;
            }
            $links = $this->get_frontend_footer_links_from_wp_menu($menu_id, 16);
            if (empty($links)) {
                continue;
            }
            $columns[] = [
                'label' => sanitize_text_field((string) $menu_obj->name),
                'links' => $links,
            ];
            if (count($columns) >= $max_columns) {
                break;
            }
        }

        return $columns;
    }

    /**
     * Resolves footer columns from WP menu source or auto-generated nav groups.
     *
     * @return array<int, array<string, mixed>>
     */
    private function get_frontend_footer_columns() {
        $menu_count = $this->get_frontend_footer_menu_count();
        $columns = [];

        if ($this->is_option_enabled('eep_frontend_footer_use_wp_menus', '0')) {
            $columns = $this->get_frontend_footer_columns_from_wp_menus(
                $this->get_frontend_footer_wp_menu_ids(),
                $menu_count
            );
        }

        $menu_groups = $this->get_frontend_sticky_nav_menu_groups();
        if (!empty($menu_groups) && count($columns) < $menu_count) {
            $existing_labels = [];
            foreach ($columns as $column) {
                $existing_label = sanitize_key((string) ($column['label'] ?? ''));
                if ($existing_label !== '') {
                    $existing_labels[$existing_label] = true;
                }
            }

            foreach ($menu_groups as $group) {
                if (!is_array($group)) {
                    continue;
                }
                $label = sanitize_text_field((string) ($group['label'] ?? ''));
                $label_key = sanitize_key($label);
                $links_raw = isset($group['links']) && is_array($group['links']) ? $group['links'] : [];
                if ($label === '' || $label_key === '' || isset($existing_labels[$label_key]) || empty($links_raw)) {
                    continue;
                }
                $links = [];
                foreach ($links_raw as $link) {
                    if (!is_array($link)) {
                        continue;
                    }
                    $link_label = sanitize_text_field((string) ($link['label'] ?? ''));
                    $link_url = esc_url_raw((string) ($link['url'] ?? ''));
                    if ($link_label === '' || $link_url === '') {
                        continue;
                    }
                    $links[] = [
                        'label' => $link_label,
                        'url' => $link_url,
                    ];
                    if (count($links) >= 12) {
                        break;
                    }
                }
                if (empty($links)) {
                    continue;
                }
                $columns[] = [
                    'label' => $label,
                    'links' => $links,
                ];
                $existing_labels[$label_key] = true;
                if (count($columns) >= $menu_count) {
                    break;
                }
            }
        }

        if (empty($columns)) {
            $columns[] = [
                'label' => __('Explore', 'nexdirectory'),
                'links' => [
                    [
                        'label' => __('Events', 'nexdirectory'),
                        'url' => $this->get_platform_page_url('eep_page_events_url', ['events', 'listings'], home_url('/events/')),
                    ],
                    [
                        'label' => __('Map', 'nexdirectory'),
                        'url' => $this->get_platform_page_url('eep_page_events_map_url', ['events-map', 'map'], home_url('/events-map/')),
                    ],
                    [
                        'label' => __('Calendar', 'nexdirectory'),
                        'url' => $this->get_platform_page_url('eep_page_events_calendar_url', ['events-calendar', 'calendar'], home_url('/events-calendar/')),
                    ],
                ],
            ];
        }

        return array_slice($columns, 0, $menu_count);
    }

    /**
     * Renders the frontend platform footer.
     *
     * @return string
     */
    public function render_frontend_platform_footer() {
        if (!$this->should_show_frontend_footer_for_current_user()) {
            return '';
        }

        $show_logo = $this->is_option_enabled('eep_frontend_footer_show_logo', '1');
        $show_privacy_link = $this->is_option_enabled('eep_frontend_footer_show_privacy_link', '1');
        $brand_name = $this->get_platform_brand_name();
        $logo_url = esc_url((string) get_option('eep_frontend_footer_logo_url', ''));
        if ($logo_url === '') {
            $logo_url = esc_url((string) get_option('eep_frontend_sticky_nav_logo_url', ''));
        }
        $logo_link_url = esc_url((string) get_option('eep_frontend_footer_logo_link_url', home_url('/')));
        if ($logo_link_url === '') {
            $logo_link_url = esc_url(home_url('/'));
        }
        $bottom_text = sanitize_text_field((string) get_option('eep_frontend_footer_bottom_text', ''));
        $columns = $this->get_frontend_footer_columns();
        $column_count = count($columns);
        if ($column_count < 1) {
            $column_count = 1;
        }

        $events_url = $this->get_platform_page_url('eep_page_events_url', ['events', 'listings'], home_url('/events/'));
        $privacy_url = function_exists('get_privacy_policy_url') ? esc_url_raw((string) get_privacy_policy_url()) : '';
        $year_label = wp_date('Y');
        if ($year_label === '') {
            $year_label = gmdate('Y');
        }

        static $footer_style_printed = false;
        $footer_style = '';
        if (!$footer_style_printed) {
            $footer_style = '<style id="eep-platform-footer-style">'
                . 'body.eep-plugin-page .eep-platform-footer{display:block !important;width:100vw !important;max-width:100vw !important;margin-left:calc(50% - 50vw) !important;margin-right:calc(50% - 50vw) !important;}'
                . 'body.eep-plugin-page #colophon,'
                . 'body.eep-plugin-page .site-footer,'
                . 'body.eep-plugin-page footer.site-footer,'
                . 'body.eep-plugin-page #footer,'
                . 'body.eep-plugin-page .footer,'
                . 'body.eep-plugin-page .footer-widgets,'
                . 'body.eep-plugin-page .site-info,'
                . 'body.eep-plugin-page .elementor-location-footer,'
                . 'body.eep-plugin-page .ast-footer-overlay,'
                . 'body.eep-plugin-page .ast-footer-wrap{display:none !important;}'
                . '.eep-platform-footer-inner{padding-left:1rem;padding-right:1rem;}'
                . '@media (min-width:1024px){.eep-platform-footer-inner{padding-left:1.5rem;padding-right:1.5rem;}}'
                . '.eep-platform-footer-menus{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:1.5rem;}'
                . '@media (min-width:1024px){.eep-platform-footer-menus{grid-template-columns:repeat(var(--eep-footer-menu-count,3),minmax(0,1fr));gap:2rem;}}'
                . '.eep-platform-footer a{color:#374151;text-decoration:none;}'
                . '.eep-platform-footer a:hover{color:var(--eep-primary,#FF5E14);}'
                . '.eep-platform-footer .eep-footer-link{display:inline-flex;align-items:center;gap:.4rem;line-height:1.45;font-size:.93rem;font-weight:500;color:#4b5563;transition:color .2s ease;}'
                . '.eep-platform-footer .eep-footer-legal-link{font-size:.84rem;font-weight:500;color:#6b7280;transition:color .2s ease;text-decoration:none;}'
                . '.eep-platform-footer .eep-footer-legal-link:hover{color:var(--eep-primary,#FF5E14);}'
                . '.eep-platform-footer .eep-footer-col-title{font-size:.72rem;font-weight:800;letter-spacing:.07em;text-transform:uppercase;color:#94a3b8;margin:0 0 .9rem;}'
                . '</style>';
            $footer_style_printed = true;
        }

        ob_start();
        echo $footer_style; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
        ?>
        <footer class="eep-platform-footer w-full border-t border-gray-200/70 bg-white/95 backdrop-blur supports-[backdrop-filter]:bg-white/90 mt-8">
            <div class="eep-platform-footer-inner mx-auto w-full py-10 md:py-12">
                <div class="grid gap-8 lg:grid-cols-[minmax(240px,1.2fr)_minmax(0,1fr)] items-start">
                    <?php if ($show_logo) : ?>
                        <section class="space-y-4">
                            <a href="<?php echo esc_url($logo_link_url); ?>" class="inline-flex items-center max-w-full">
                                <?php if ($logo_url !== '') : ?>
                                    <img src="<?php echo esc_url($logo_url); ?>" alt="<?php echo esc_attr($brand_name); ?>" loading="lazy" decoding="async" class="h-10 w-auto max-w-[220px] object-contain">
                                <?php else : ?>
                                    <span class="text-2xl font-black tracking-tight text-gray-900"><?php echo esc_html($brand_name); ?></span>
                                <?php endif; ?>
                            </a>
                            <p class="text-sm text-gray-500 leading-relaxed">
                                <?php
                                echo esc_html(
                                    sprintf(
                                        __('Discover, manage, and publish %s with confidence.', 'nexdirectory'),
                                        strtolower($this->post_type === 'nexdirectory_event' ? __('events', 'nexdirectory') : __('listings', 'nexdirectory'))
                                    )
                                );
                                ?>
                            </p>
                        </section>
                    <?php endif; ?>

                    <div class="eep-platform-footer-menus" style="--eep-footer-menu-count: <?php echo esc_attr((string) $column_count); ?>;">
                        <?php foreach ($columns as $column) : ?>
                            <?php
                            $column_label = sanitize_text_field((string) ($column['label'] ?? ''));
                            $column_links = isset($column['links']) && is_array($column['links']) ? $column['links'] : [];
                            if ($column_label === '' || empty($column_links)) {
                                continue;
                            }
                            ?>
                            <nav aria-label="<?php echo esc_attr($column_label); ?>">
                                <h3 class="eep-footer-col-title"><?php echo esc_html($column_label); ?></h3>
                                <ul class="space-y-2.5 list-none m-0 p-0">
                                    <?php foreach ($column_links as $column_link) : ?>
                                        <?php
                                        $link_label = sanitize_text_field((string) ($column_link['label'] ?? ''));
                                        $link_url = esc_url((string) ($column_link['url'] ?? ''));
                                        if ($link_label === '' || $link_url === '') {
                                            continue;
                                        }
                                        ?>
                                        <li>
                                            <a href="<?php echo esc_url($link_url); ?>" class="eep-footer-link">
                                                <?php echo esc_html($link_label); ?>
                                            </a>
                                        </li>
                                    <?php endforeach; ?>
                                </ul>
                            </nav>
                        <?php endforeach; ?>
                    </div>
                </div>

                <div class="mt-8 pt-5 border-t border-gray-200/70 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
                    <p class="text-xs sm:text-sm text-gray-500 m-0">
                        &copy; <?php echo esc_html((string) $year_label); ?> <?php echo esc_html($brand_name); ?>.
                        <?php if ($bottom_text !== '') : ?>
                            <span><?php echo esc_html($bottom_text); ?></span>
                        <?php endif; ?>
                    </p>
                    <div class="flex flex-wrap items-center gap-3 text-xs sm:text-sm">
                        <?php if ($show_privacy_link && $privacy_url !== '') : ?>
                            <a href="<?php echo esc_url($privacy_url); ?>" class="eep-footer-legal-link"><?php esc_html_e('Privacy Policy', 'nexdirectory'); ?></a>
                        <?php endif; ?>
                        <a href="<?php echo esc_url($events_url); ?>" class="eep-footer-legal-link"><?php esc_html_e('Explore Events', 'nexdirectory'); ?></a>
                    </div>
                </div>
            </div>
        </footer>
        <?php

        return (string) ob_get_clean();
    }

    /**
     * Detects unusable nav menu output (e.g. only one home/root URL link).
     *
     * @param array<int, array<string, mixed>> $menu_groups
     * @return bool
     */
    private function frontend_nav_menu_groups_need_fallback($menu_groups) {
        if (!is_array($menu_groups) || empty($menu_groups)) {
            return true;
        }

        $total_links = 0;
        $non_root_links = 0;
        foreach ($menu_groups as $group) {
            if (!is_array($group)) {
                continue;
            }
            $group_links = isset($group['links']) && is_array($group['links']) ? $group['links'] : [];
            foreach ($group_links as $group_link) {
                if (!is_array($group_link)) {
                    continue;
                }
                $url = esc_url_raw((string) ($group_link['url'] ?? ''));
                if ($url === '') {
                    continue;
                }
                $total_links++;
                if (!$this->is_site_root_url($url)) {
                    $non_root_links++;
                }
            }
        }

        if ($total_links === 0) {
            return true;
        }
        if ($total_links <= 1) {
            return true;
        }
        return $non_root_links === 0;
    }

    /**
     * Renders the frontend sticky navigation bar.
     *
     * @return string
     */
    /**
     * Renders the frontend sticky navigation bar.
     *
     * @return string
     */
    public function render_frontend_sticky_nav() {
        if (!$this->should_show_frontend_sticky_nav_for_current_user()) {
            return '';
        }
        if (!$this->is_platform_frontend_navigation_context()) {
            return '';
        }

        $behavior = $this->get_frontend_sticky_nav_behavior();
        $show_logo = $this->is_option_enabled('eep_frontend_sticky_nav_show_logo', '1');
        $include_auth_cta = $this->is_option_enabled('eep_frontend_sticky_nav_include_auth_cta', '1');
        $logo_url = esc_url((string) get_option('eep_frontend_sticky_nav_logo_url', ''));
        $logo_link_url = esc_url((string) get_option('eep_frontend_sticky_nav_logo_link_url', home_url('/')));
        $logo_size_raw = strtolower(trim((string) get_option('eep_frontend_sticky_nav_logo_size', '66px')));
        $logo_size_value = '66px';
        if (preg_match('/^(\d+(?:\.\d+)?)(px|rem|em)?$/', $logo_size_raw, $logo_size_matches)) {
            $logo_size_amount = (float) $logo_size_matches[1];
            if ($logo_size_amount < 18) {
                $logo_size_amount = 18;
            }
            if ($logo_size_amount > 96) {
                $logo_size_amount = 96;
            }
            $logo_size_unit = (isset($logo_size_matches[2]) && $logo_size_matches[2] !== '') ? (string) $logo_size_matches[2] : 'px';
            $logo_size_value = rtrim(rtrim(number_format($logo_size_amount, 2, '.', ''), '0'), '.') . $logo_size_unit;
        }
        $logo_max_width_value = '190px';
        if (preg_match('/^(\d+(?:\.\d+)?)(px|rem|em)$/', $logo_size_value, $logo_size_parsed)) {
            $logo_max_multiplier = 5.3;
            $logo_max_width_value = 'calc(' . $logo_size_parsed[1] . $logo_size_parsed[2] . ' * ' . $logo_max_multiplier . ')';
        }
        if ($logo_link_url === '') {
            $logo_link_url = esc_url(home_url('/'));
        }

        $menu_groups = $this->get_frontend_sticky_nav_menu_groups();
        $prepared_groups = [];
        foreach ((array) $menu_groups as $group) {
            $group_label = sanitize_text_field((string) ($group['label'] ?? ''));
            $group_links_raw = isset($group['links']) && is_array($group['links']) ? $group['links'] : [];
            $group_links = [];
            foreach ($group_links_raw as $group_link) {
                $link_label = sanitize_text_field((string) ($group_link['label'] ?? ''));
                $link_url = esc_url_raw((string) ($group_link['url'] ?? ''));
                if ($link_label === '' || $link_url === '') {
                    continue;
                }
                $group_links[] = [
                    'label' => $link_label,
                    'url' => $link_url,
                ];
            }
            if ($group_label === '' || empty($group_links)) {
                continue;
            }
            $prepared_groups[] = [
                'label' => $group_label,
                'links' => $group_links,
            ];
        }
        $menu_groups = $prepared_groups;
        if ($this->frontend_nav_menu_groups_need_fallback($menu_groups)) {
            $fallback_links = [];
            $fallback_events_url = $this->get_platform_page_url('eep_page_events_url', ['events', 'listings'], home_url('/events/'));
            $fallback_map_url = $this->get_platform_page_url('eep_page_events_map_url', ['map', 'events-map'], home_url('/map/'));
            $fallback_calendar_url = $this->get_platform_page_url('eep_page_events_calendar_url', ['calendar', 'events-calendar'], home_url('/calendar/'));
            $fallback_submit_url = $this->get_platform_page_url('eep_page_submit_event_url', ['submit', 'add-new-event'], home_url('/submit/'));
            $platform_mode = sanitize_key((string) get_option('eep_platform_mode', 'user_generated'));
            $allow_submit_link = is_user_logged_in() && !($platform_mode === 'single_organisation' && !current_user_can('manage_options'));

            $fallback_map = [
                ['label' => __('Explore', 'nexdirectory'), 'url' => $fallback_events_url],
                ['label' => __('Map', 'nexdirectory'), 'url' => $fallback_map_url],
                ['label' => __('Calendar', 'nexdirectory'), 'url' => $fallback_calendar_url],
            ];
            if ($allow_submit_link) {
                $fallback_map[] = ['label' => __('Add New Event', 'nexdirectory'), 'url' => $fallback_submit_url];
            }

            foreach ($fallback_map as $fallback_link) {
                $link_label = sanitize_text_field((string) ($fallback_link['label'] ?? ''));
                $link_url = esc_url_raw((string) ($fallback_link['url'] ?? ''));
                if ($link_label === '' || $link_url === '' || $this->is_site_root_url($link_url)) {
                    continue;
                }
                $fallback_links[] = [
                    'label' => $link_label,
                    'url' => $link_url,
                ];
            }
            if (!empty($fallback_links)) {
                $menu_groups[] = [
                    'label' => __('Menu', 'nexdirectory'),
                    'links' => $fallback_links,
                ];
            }
        }

        // Final safety fallback: never render an empty mobile/desktop nav.
        if (empty($menu_groups)) {
            $safe_links = [];
            $safe_links[] = [
                'label' => __('Explore', 'nexdirectory'),
                'url' => esc_url_raw(home_url('/events/')),
            ];
            $safe_links[] = [
                'label' => __('Map', 'nexdirectory'),
                'url' => esc_url_raw(home_url('/events-map/')),
            ];
            $safe_links[] = [
                'label' => __('Calendar', 'nexdirectory'),
                'url' => esc_url_raw(home_url('/events-calendar/')),
            ];
            if (is_user_logged_in()) {
                $safe_links[] = [
                    'label' => __('Workspace', 'nexdirectory'),
                    'url' => esc_url_raw(home_url('/my-events/')),
                ];
            }
            $platform_mode = sanitize_key((string) get_option('eep_platform_mode', 'user_generated'));
            if (is_user_logged_in() && !($platform_mode === 'single_organisation' && !current_user_can('manage_options'))) {
                $safe_links[] = [
                    'label' => __('Add New Event', 'nexdirectory'),
                    'url' => esc_url_raw(home_url('/submit/')),
                ];
            }
            $menu_groups[] = [
                'label' => __('Menu', 'nexdirectory'),
                'links' => $safe_links,
            ];
        }

        $brand_name = $this->get_platform_brand_name();

        $current_url = (string) $this->current_url();
        $login_base = $this->get_custom_login_page_url();
        if ($login_base === '') {
            $login_url = wp_login_url($current_url);
        } else {
            $login_url = eep_add_query_arg('redirect_to', $current_url, $login_base);
        }
        $signup_url = eep_add_query_arg('eep_auth', 'signup', $login_url);
        $account_url = $this->get_platform_page_url('eep_page_account_url', ['account', 'my-account'], home_url('/account/'));
        $logout_url = wp_logout_url($login_url);
        $is_member = is_user_logged_in();

        if (empty($menu_groups) && !$include_auth_cta && !$show_logo) {
            return '';
        }

        $position_class = $behavior === 'static' ? 'relative' : 'sticky top-0';
        static $nav_padding_style_printed = false;
        $nav_padding_style = '';
        if (!$nav_padding_style_printed) {
            $nav_padding_style = '<style id="eep-platform-nav-padding-style">'
                . '.eep-platform-nav-inner{padding-left:var(--eep-nav-side-padding-mobile, var(--eep-page-side-padding-mobile, 2%));padding-right:var(--eep-nav-side-padding-mobile, var(--eep-page-side-padding-mobile, 2%));}'
                . '@media (min-width:1024px){.eep-platform-nav-inner{padding-left:var(--eep-nav-side-padding-desktop, var(--eep-page-side-padding, 10%));padding-right:var(--eep-nav-side-padding-desktop, var(--eep-page-side-padding, 10%));}}'
                . '</style>';
            $nav_padding_style_printed = true;
        }
        
        static $nav_mobile_style_printed = false;
        $nav_mobile_style = '';
        if (!$nav_mobile_style_printed) {
            // Added app-like entrance animations for dropdowns and mobile staggers
            $nav_mobile_style = '<style id="eep-platform-nav-mobile-style">'
                . '.eep-platform-nav [data-eep-nav-summary]::-webkit-details-marker{display:none;}'
                . '.eep-platform-nav [data-eep-nav-summary]{list-style:none;}'
                . '.eep-platform-nav .eep-nav-mobile-shell{padding-bottom:env(safe-area-inset-bottom,0px);}'
                . '.eep-platform-nav [data-eep-nav-mobile-group]{transition:background-color .26s ease, border-color .26s ease,box-shadow .26s ease,transform .26s cubic-bezier(0.34, 1.56, 0.64, 1);}'
                . '.eep-platform-nav [data-eep-nav-mobile-group].is-open{background-color: #ffffff; border-color:rgba(var(--eep-primary-rgb,255,94,20),.25);box-shadow:0 8px 20px -6px rgba(15,23,42,.08); transform: translateY(-2px);}'
                . '.eep-platform-nav [data-eep-nav-mobile-group-toggle]{transition:color .2s ease;}'
                . '.eep-platform-nav [data-eep-nav-mobile-group].is-open [data-eep-nav-mobile-group-toggle]{color:var(--eep-primary,#FF5E14);}'
                . '.eep-platform-nav [data-eep-nav-mobile-group-panel]{overflow:hidden;max-height:0;opacity:0;transition:max-height .34s cubic-bezier(.22,1,.36,1),opacity .24s ease;}'
                . '.eep-platform-nav [data-eep-nav-mobile-group].is-open [data-eep-nav-mobile-group-panel]{opacity:1;}'
                . '@keyframes eepNavDropdownIn { 0% { opacity: 0; transform: translateY(6px) scale(0.98); } 100% { opacity: 1; transform: translateY(0) scale(1); } }'
                . '.eep-nav-dropdown-anim:not(.hidden) { animation: eepNavDropdownIn 0.2s cubic-bezier(0.16, 1, 0.3, 1) forwards; }'
                . '@keyframes eepNavItemIn { 0% { opacity: 0; transform: translateX(12px); } 100% { opacity: 1; transform: translateX(0); } }'
                . '.eep-platform-nav [data-eep-nav-mobile-shell]:not(.hidden) .eep-mobile-stagger { animation: eepNavItemIn 0.35s cubic-bezier(0.16, 1, 0.3, 1) forwards; opacity: 0; }'
                . '.eep-platform-nav a.eep-nav-logout-link,.eep-platform-nav a.eep-nav-logout-link:visited,.eep-platform-nav a.eep-nav-logout-link:hover,.eep-platform-nav a.eep-nav-logout-link:focus,.eep-platform-nav a.eep-nav-logout-link:active,.eep-platform-nav a.eep-nav-signup-link,.eep-platform-nav a.eep-nav-signup-link:visited,.eep-platform-nav a.eep-nav-signup-link:hover,.eep-platform-nav a.eep-nav-signup-link:focus,.eep-platform-nav a.eep-nav-signup-link:active{color:#fff!important;-webkit-text-fill-color:#fff!important;}'
                . '</style>';
            $nav_mobile_style_printed = true;
        }
        
        static $nav_script_printed = false;
        $nav_script = '';
        if (!$nav_script_printed) {
            $nav_script = '<script id="eep-platform-nav-script">(function(){'
                . 'if(window.__eepPlatformNavInit){return;}window.__eepPlatformNavInit=true;'
                . 'var DESKTOP_BREAKPOINT=1024;'
                . 'function isDesktop(){return window.innerWidth>=DESKTOP_BREAKPOINT;}'
                . 'function initRoot(root){if(!root||root.__eepNavBound){return;}root.__eepNavBound=true;'
                . 'var behavior=(root.getAttribute("data-eep-nav-behavior")||"sticky").toLowerCase();'
                . 'var mobileToggle=root.querySelector("[data-eep-nav-toggle]");'
                . 'var mobileShell=root.querySelector("[data-eep-nav-mobile-shell]");'
                . 'var mobilePanel=root.querySelector("[data-eep-nav-mobile-panel]");'
                . 'var openIcon=root.querySelector("[data-eep-nav-icon-open]");'
                . 'var closeIcon=root.querySelector("[data-eep-nav-icon-close]");'
                . 'var mobileOpen=false;var mobileCloseTimer=null;'
                . 'function syncIcons(){if(!openIcon||!closeIcon){return;}openIcon.classList.toggle("hidden",mobileOpen);closeIcon.classList.toggle("hidden",!mobileOpen);}'
                . 'function setMobileOpen(next){next=!!next;if(!mobileShell||!mobilePanel){mobileOpen=false;syncIcons();return;}'
                . 'window.clearTimeout(mobileCloseTimer);mobileOpen=next;'
                . 'if(next){mobileShell.classList.remove("hidden");requestAnimationFrame(function(){mobileShell.classList.remove("opacity-0","pointer-events-none");mobileShell.classList.add("opacity-100","pointer-events-auto");mobilePanel.classList.remove("translate-x-full","opacity-0");mobilePanel.classList.add("translate-x-0","opacity-100");window.setTimeout(function(){root.querySelectorAll("[data-eep-nav-mobile-group]").forEach(function(group){var trigger=group.querySelector("[data-eep-nav-mobile-group-toggle]");var panel=group.querySelector("[data-eep-nav-mobile-group-panel]");if(!trigger||!panel||trigger.getAttribute("aria-expanded")!=="true"){return;}panel.classList.remove("hidden");panel.style.maxHeight=panel.scrollHeight+"px";panel.style.opacity="1";});},40);});document.documentElement.classList.add("overflow-hidden");}'
                . 'else{mobileShell.classList.add("opacity-0","pointer-events-none");mobileShell.classList.remove("opacity-100","pointer-events-auto");mobilePanel.classList.add("translate-x-full","opacity-0");mobilePanel.classList.remove("translate-x-0","opacity-100");document.documentElement.classList.remove("overflow-hidden");mobileCloseTimer=window.setTimeout(function(){if(!mobileOpen){mobileShell.classList.add("hidden");}},340);}'
                . 'if(mobileToggle){mobileToggle.setAttribute("aria-expanded",next?"true":"false");}syncIcons();}'
                . 'if(mobileToggle){mobileToggle.addEventListener("click",function(e){e.preventDefault();setMobileOpen(!mobileOpen);});}'
                . 'root.querySelectorAll("[data-eep-nav-close]").forEach(function(btn){btn.addEventListener("click",function(e){e.preventDefault();setMobileOpen(false);});});'
                . 'root.querySelectorAll("[data-eep-nav-close-link]").forEach(function(link){link.addEventListener("click",function(){setMobileOpen(false);});});'
                . 'var mobileGroups=Array.prototype.slice.call(root.querySelectorAll("[data-eep-nav-mobile-group]"));'
                . 'function setMobileGroupState(group,nextExpanded,closeOthers){if(!group){return;}var trigger=group.querySelector("[data-eep-nav-mobile-group-toggle]");var panel=group.querySelector("[data-eep-nav-mobile-group-panel]");if(!trigger||!panel){return;}var expanded=!!nextExpanded;if(closeOthers&&expanded){mobileGroups.forEach(function(other){if(other!==group){setMobileGroupState(other,false,false);}});}trigger.setAttribute("aria-expanded",expanded?"true":"false");var chev=trigger.querySelector("[data-eep-nav-mobile-group-chevron]");if(chev){chev.classList.toggle("rotate-180",expanded);}group.classList.toggle("is-open",expanded);panel.classList.remove("hidden");panel.style.overflow="hidden";panel.style.willChange="max-height,opacity";if(expanded){panel.style.maxHeight=panel.scrollHeight+"px";panel.style.opacity="1";window.setTimeout(function(){if(trigger.getAttribute("aria-expanded")==="true"){panel.style.maxHeight=panel.scrollHeight+"px";}},40);}else{panel.style.maxHeight="0px";panel.style.opacity="0";window.setTimeout(function(){if(trigger.getAttribute("aria-expanded")!=="true"){panel.classList.add("hidden");}},360);}}'
                . 'var firstOpenSet=false;mobileGroups.forEach(function(group){var trigger=group.querySelector("[data-eep-nav-mobile-group-toggle]");if(!trigger){return;}var expanded=trigger.getAttribute("aria-expanded")==="true";if(expanded){if(firstOpenSet){expanded=false;}else{firstOpenSet=true;}}setMobileGroupState(group,expanded,false);trigger.addEventListener("click",function(){var isOpen=trigger.getAttribute("aria-expanded")==="true";setMobileGroupState(group,!isOpen,true);});});'
                . 'if(!firstOpenSet&&mobileGroups.length){setMobileGroupState(mobileGroups[0],true,false);}'
                . 'var desktopGroups=Array.prototype.slice.call(root.querySelectorAll("[data-eep-nav-desktop-group]"));'
                . 'function closeDesktopGroups(except){desktopGroups.forEach(function(group){if(except&&group===except){return;}var trigger=group.querySelector("[data-eep-nav-desktop-trigger]");var menu=group.querySelector("[data-eep-nav-desktop-menu]");if(trigger){trigger.setAttribute("aria-expanded","false");}if(menu){menu.classList.add("hidden");}var chev=group.querySelector("[data-eep-nav-desktop-chevron]");if(chev){chev.classList.remove("rotate-180","text-brand-600");}});}'
                . 'desktopGroups.forEach(function(group){var trigger=group.querySelector("[data-eep-nav-desktop-trigger]");var menu=group.querySelector("[data-eep-nav-desktop-menu]");var chev=group.querySelector("[data-eep-nav-desktop-chevron]");if(!trigger||!menu){return;}var closeTimer=null;'
                . 'function openThis(){if(!isDesktop()){return;}closeDesktopGroups(group);menu.classList.remove("hidden");trigger.setAttribute("aria-expanded","true");if(chev){chev.classList.add("rotate-180","text-brand-600");}}'
                . 'function closeThis(){menu.classList.add("hidden");trigger.setAttribute("aria-expanded","false");if(chev){chev.classList.remove("rotate-180","text-brand-600");}}'
                . 'function cancelClose(){if(closeTimer){window.clearTimeout(closeTimer);closeTimer=null;}}'
                . 'function scheduleClose(){if(!isDesktop()){return;}cancelClose();closeTimer=window.setTimeout(function(){closeThis();},220);}'
                . 'trigger.addEventListener("click",function(e){if(!isDesktop()){return;}e.preventDefault();var isOpen=trigger.getAttribute("aria-expanded")==="true";if(isOpen){closeThis();}else{openThis();}});'
                . 'group.addEventListener("mouseenter",function(){cancelClose();openThis();});group.addEventListener("mouseleave",scheduleClose);menu.addEventListener("mouseenter",cancelClose);menu.addEventListener("mouseleave",scheduleClose);trigger.addEventListener("focus",function(){cancelClose();openThis();});trigger.addEventListener("blur",scheduleClose);});'
                . 'document.addEventListener("click",function(e){if(!root.contains(e.target)){closeDesktopGroups();setMobileOpen(false);}});'
                . 'window.addEventListener("resize",function(){if(isDesktop()){setMobileOpen(false);closeDesktopGroups();}mobileGroups.forEach(function(group){var trigger=group.querySelector("[data-eep-nav-mobile-group-toggle]");var panel=group.querySelector("[data-eep-nav-mobile-group-panel]");if(!trigger||!panel){return;}if(trigger.getAttribute("aria-expanded")==="true"){panel.style.maxHeight=panel.scrollHeight+"px";}});});'
                . 'if(behavior==="smart"){var lastScrollY=Math.max(window.scrollY||0,0);window.addEventListener("scroll",function(){if(mobileOpen){lastScrollY=Math.max(window.scrollY||0,0);return;}var nextY=Math.max(window.scrollY||0,0);if(nextY<=16||nextY<lastScrollY-6){root.classList.remove("-translate-y-full");root.classList.add("shadow-sm");}else if(nextY>lastScrollY+6&&nextY>92){root.classList.add("-translate-y-full");}if(nextY===0){root.classList.remove("shadow-sm");}lastScrollY=nextY;},{passive:true});}'
                . 'syncIcons();setMobileOpen(false);closeDesktopGroups();}'
                . 'function boot(){document.querySelectorAll("[data-eep-nav-root]").forEach(initRoot);}'
                . 'if(document.readyState==="loading"){document.addEventListener("DOMContentLoaded",boot,{once:true});}else{boot();}'
                . 'document.addEventListener("eep:spa:navigated",boot);})();</script>';
            $nav_script_printed = true;
        }
        $mobile_menu_id = 'eep-platform-mobile-menu-' . wp_rand(1000, 9999);
        $nav_side_padding_desktop = $this->get_nav_side_padding_desktop_value('0%');
        $nav_side_padding_mobile = $this->get_page_side_padding_mobile_value('2%');

        ob_start();
        ?>
        <?php echo $nav_padding_style; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
        <?php echo $nav_mobile_style; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
        <?php echo $nav_script; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped ?>
        <nav
            class="eep-platform-nav <?php echo esc_attr($position_class); ?> w-full border-b border-gray-200/60 bg-white/80 backdrop-blur-xl supports-[backdrop-filter]:bg-white/70 transition-all duration-300 shadow-[0_1px_3px_0_rgba(0,0,0,0.02)]"
            style="z-index:1100;"
            aria-label="<?php esc_attr_e('Platform navigation', 'nexdirectory'); ?>"
            data-eep-nav-root="1"
            data-eep-nav-behavior="<?php echo esc_attr($behavior); ?>"
        >
            <div class="eep-platform-nav-inner mx-auto w-full" style="--eep-nav-side-padding-desktop: <?php echo esc_attr($nav_side_padding_desktop); ?>; --eep-nav-side-padding-mobile: <?php echo esc_attr($nav_side_padding_mobile); ?>;">
                <div class="h-16 sm:h-[4.5rem] flex items-center justify-between gap-4">
                    <?php if ($show_logo) : ?>
                        <a href="<?php echo esc_url($logo_link_url); ?>" class="eep-platform-nav-brand inline-flex items-center min-w-0 shrink-0 group active:scale-95 transition-transform duration-200 origin-left">
                            <?php if ($logo_url !== '') : ?>
                                <img src="<?php echo esc_url($logo_url); ?>" alt="<?php echo esc_attr($brand_name); ?>" loading="lazy" decoding="async" class="w-auto object-contain transition-transform duration-300 group-hover:scale-105" style="height: <?php echo esc_attr($logo_size_value); ?>; max-width: <?php echo esc_attr($logo_max_width_value); ?>;">
                            <?php else : ?>
                                <span class="eep-platform-nav-brand-text text-lg font-black tracking-tight text-gray-900 group-hover:text-brand-600 transition-colors"><?php echo esc_html($brand_name); ?></span>
                            <?php endif; ?>
                        </a>
                    <?php endif; ?>

                    <div class="hidden lg:flex items-center justify-center gap-2 min-w-0 flex-1 flex-wrap <?php echo $show_logo ? 'lg:ml-4' : ''; ?>">
                        <?php $desktop_group_index = 0; ?>
                        <?php foreach ($menu_groups as $group) : ?>
                            <?php
                            $group_label = sanitize_text_field((string) ($group['label'] ?? ''));
                            $group_links = isset($group['links']) && is_array($group['links']) ? $group['links'] : [];
                            if ($group_label === '' || empty($group_links)) {
                                continue;
                            }
                            $desktop_group_key = 'eep-group-' . $desktop_group_index;
                            $desktop_dropdown_id = 'eep-platform-nav-dropdown-' . $desktop_group_index . '-' . wp_rand(1000, 9999);
                            $desktop_group_index++;
                            ?>
                            <div class="relative group/nav" data-eep-nav-desktop-group>
                                <button
                                    type="button"
                                    class="eep-platform-nav-trigger inline-flex h-10 items-center gap-1.5 rounded-full px-4 py-2 text-[14px] font-semibold text-gray-600 hover:text-gray-900 hover:bg-gray-100/80 active:scale-95 active:bg-gray-200/60 transition-all duration-200 whitespace-nowrap outline-none focus-visible:ring-2 focus-visible:ring-brand-500/50"
                                    data-eep-nav-desktop-trigger
                                    aria-expanded="false"
                                    aria-haspopup="menu"
                                    aria-controls="<?php echo esc_attr($desktop_dropdown_id); ?>"
                                >
                                    <?php echo esc_html($group_label); ?>
                                    <svg class="h-4 w-4 text-gray-400 group-hover/nav:text-gray-600 transition-transform duration-300 ease-out" data-eep-nav-desktop-chevron fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
                                        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M19 9l-7 7-7-7"></path>
                                    </svg>
                                </button>
                                <div
                                    id="<?php echo esc_attr($desktop_dropdown_id); ?>"
                                    data-eep-nav-desktop-menu
                                    class="hidden absolute left-1/2 -translate-x-1/2 top-[calc(100%+0.5rem)] z-[120] min-w-[260px] rounded-2xl border border-gray-100 bg-white/95 backdrop-blur-xl p-2 shadow-[0_20px_40px_-15px_rgba(0,0,0,0.1)] eep-nav-dropdown-anim"
                                    role="menu"
                                >
                                    <div class="grid grid-cols-1 gap-1">
                                        <?php foreach ($group_links as $group_link) : ?>
                                            <?php
                                            $group_link_label = sanitize_text_field((string) ($group_link['label'] ?? ''));
                                            $group_link_url = esc_url((string) ($group_link['url'] ?? '#'));
                                            if ($group_link_label === '' || $group_link_url === '') {
                                                continue;
                                            }
                                            ?>
                                            <a
                                                href="<?php echo $group_link_url; ?>"
                                                class="eep-platform-nav-link flex items-center justify-between rounded-xl px-4 py-3 text-[14px] font-medium text-gray-700 hover:bg-brand-50/50 hover:text-brand-700 active:scale-[0.98] transition-all duration-200 group/link"
                                                role="menuitem"
                                            >
                                                <?php echo esc_html($group_link_label); ?>
                                                <svg class="w-4 h-4 opacity-0 -translate-x-2 group-hover/link:opacity-100 group-hover/link:translate-x-0 transition-all duration-300 text-brand-500" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>
                                            </a>
                                        <?php endforeach; ?>
                                    </div>
                                </div>
                            </div>
                        <?php endforeach; ?>
                    </div>

                    <?php if ($include_auth_cta) : ?>
                        <div class="hidden lg:flex items-center gap-3 shrink-0">
                            <?php if ($is_member) : ?>
                                <a href="<?php echo esc_url($account_url); ?>" class="eep-platform-nav-cta-link inline-flex h-10 items-center justify-center rounded-full border border-gray-200 bg-white px-4 text-sm font-bold text-gray-800 hover:border-gray-300 hover:bg-gray-50 active:scale-95 transition-all shadow-sm shadow-gray-200/20"><?php esc_html_e('My Account', 'nexdirectory'); ?></a>
                                <a href="<?php echo esc_url($logout_url); ?>" class="eep-platform-nav-cta-link eep-nav-logout-link inline-flex h-10 items-center justify-center rounded-full border border-transparent bg-[var(--eep-primary)] px-4 text-sm font-bold text-white hover:bg-[var(--eep-primary-hover)] active:scale-95 transition-all shadow-md shadow-brand-500/20"><?php esc_html_e('Logout', 'nexdirectory'); ?></a>
                            <?php else : ?>
                                <a href="<?php echo esc_url($login_url); ?>" data-eep-auth-open="1" data-eep-auth-tab="login" class="eep-platform-nav-cta-link inline-flex h-10 items-center justify-center rounded-full px-4 text-sm font-bold text-gray-600 hover:text-gray-900 hover:bg-gray-100 active:scale-95 transition-all"><?php esc_html_e('Log in', 'nexdirectory'); ?></a>
                                <a href="<?php echo esc_url($signup_url); ?>" data-eep-auth-open="1" data-eep-auth-tab="signup" class="eep-platform-nav-cta-link eep-nav-signup-link inline-flex h-10 items-center justify-center rounded-full border border-transparent bg-[var(--eep-primary)] px-5 text-sm font-bold text-white hover:bg-[var(--eep-primary-hover)] active:scale-95 transition-all shadow-md shadow-brand-500/20"><?php esc_html_e('Sign Up', 'nexdirectory'); ?></a>
                            <?php endif; ?>
                        </div>
                    <?php endif; ?>

                    <button
                        type="button"
                        class="lg:hidden inline-flex h-11 w-11 items-center justify-center rounded-full bg-gray-100/80 text-gray-700 hover:bg-gray-200 hover:text-gray-900 active:scale-90 transition-all duration-300 ml-auto"
                        data-eep-nav-toggle
                        aria-expanded="false"
                        aria-controls="<?php echo esc_attr($mobile_menu_id); ?>"
                    >
                        <span class="screen-reader-text"><?php esc_html_e('Toggle menu', 'nexdirectory'); ?></span>
                        <svg class="h-5 w-5 transition-transform duration-300" data-eep-nav-icon-open fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path></svg>
                        <svg class="h-5 w-5 hidden transition-transform duration-300" data-eep-nav-icon-close fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"></path></svg>
                    </button>
                </div>
            </div>

            <div class="eep-nav-mobile-shell lg:hidden hidden opacity-0 pointer-events-none transition-opacity duration-300" data-eep-nav-mobile-shell style="position:fixed;left:0;right:0;top:0;height:100dvh;z-index:1200;">
                <div class="absolute inset-0 bg-gray-900/60 backdrop-blur-sm transition-opacity duration-300" data-eep-nav-close></div>
                <aside id="<?php echo esc_attr($mobile_menu_id); ?>" data-eep-nav-mobile-panel class="absolute inset-y-0 right-0 w-[90%] max-w-[380px] bg-[#f8fafc] shadow-[0_24px_80px_rgba(0,0,0,0.3)] flex flex-col translate-x-full opacity-0 transition-all duration-[350ms] ease-[cubic-bezier(0.2,0.8,0.2,1)] rounded-l-[2rem] overflow-hidden" style="z-index:1201;">
                    
                    <div class="px-6 pt-safe pb-4 bg-white/80 backdrop-blur-md border-b border-gray-200/50 sticky top-0 z-10 flex items-center justify-between" style="padding-top: max(1.5rem, env(safe-area-inset-top));">
                        <div>
                            <strong class="text-xl font-black tracking-tight text-gray-900"><?php echo esc_html($brand_name); ?></strong>
                        </div>
                        <button type="button" class="inline-flex h-10 w-10 items-center justify-center rounded-full bg-gray-100 text-gray-600 hover:bg-gray-200 hover:text-gray-900 active:scale-90 transition-all duration-200" data-eep-nav-close>
                            <span class="screen-reader-text"><?php esc_html_e('Close menu', 'nexdirectory'); ?></span>
                            <svg class="h-5 w-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M6 18L18 6M6 6l12 12"></path></svg>
                        </button>
                    </div>
                    
                    <div class="flex-1 overflow-y-auto px-4 py-6 space-y-4">
                        <?php $mobile_group_index = 0; ?>
                        <?php foreach ($menu_groups as $group) : ?>
                            <?php
                            $group_label = sanitize_text_field((string) ($group['label'] ?? ''));
                            $group_links = isset($group['links']) && is_array($group['links']) ? $group['links'] : [];
                            if ($group_label === '' || empty($group_links)) {
                                continue;
                            }
                            $mobile_group_open = $mobile_group_index === 0;
                            // Stagger animation timing
                            $stagger_delay = ($mobile_group_index * 60) . 'ms';
                            ?>
                            <section class="rounded-2xl bg-gray-100/50 border border-gray-200/60 overflow-hidden eep-mobile-stagger" style="animation-delay: <?php echo esc_attr($stagger_delay); ?>;" data-eep-nav-mobile-group>
                                <button type="button" class="eep-platform-nav-mobile-trigger w-full px-5 py-4 text-[13px] font-bold uppercase tracking-wider text-gray-500 flex items-center justify-between text-left active:bg-gray-100/50 transition-colors focus:outline-none" data-eep-nav-mobile-group-toggle aria-expanded="<?php echo $mobile_group_open ? 'true' : 'false'; ?>">
                                    <span><?php echo esc_html($group_label); ?></span>
                                    <div class="w-8 h-8 rounded-full bg-white shadow-sm flex items-center justify-center shrink-0">
                                        <svg class="h-4 w-4 text-gray-500 transition-transform duration-[350ms] ease-out <?php echo $mobile_group_open ? 'rotate-180 text-brand-600' : ''; ?>" data-eep-nav-mobile-group-chevron fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
                                            <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2.5" d="M19 9l-7 7-7-7"></path>
                                        </svg>
                                    </div>
                                </button>
                                <div class="px-2 pb-2 space-y-1 <?php echo $mobile_group_open ? '' : 'hidden'; ?>" data-eep-nav-mobile-group-panel>
                                    <?php foreach ($group_links as $group_link) : ?>
                                        <a href="<?php echo esc_url((string) ($group_link['url'] ?? '#')); ?>" data-eep-nav-close-link class="eep-platform-nav-mobile-link flex items-center justify-between rounded-xl px-4 py-3.5 text-[15px] font-semibold text-gray-700 bg-transparent hover:bg-white active:bg-white active:scale-[0.98] transition-all duration-200 group/moblink">
                                            <?php echo esc_html((string) ($group_link['label'] ?? '')); ?>
                                            <svg class="w-4 h-4 text-gray-300 group-hover/moblink:text-brand-500 group-hover/moblink:translate-x-1 transition-all duration-300" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"></path></svg>
                                        </a>
                                    <?php endforeach; ?>
                                </div>
                            </section>
                            <?php $mobile_group_index++; ?>
                        <?php endforeach; ?>
                    </div>

                    <?php if ($include_auth_cta) : ?>
                        <div class="p-5 pb-safe bg-white border-t border-gray-200/60 shadow-[0_-4px_16px_rgba(0,0,0,0.02)] eep-mobile-stagger" style="animation-delay: <?php echo esc_attr(($mobile_group_index * 60) + 100); ?>ms;">
                            <div class="grid grid-cols-1 gap-3">
                                <?php if ($is_member) : ?>
                                    <a href="<?php echo esc_url($account_url); ?>" data-eep-nav-close-link class="eep-platform-nav-mobile-cta-link inline-flex h-12 w-full items-center justify-center rounded-xl border-2 border-gray-100 bg-gray-50 text-[15px] font-bold text-gray-800 active:scale-95 transition-all"><?php esc_html_e('My Account', 'nexdirectory'); ?></a>
                                    <a href="<?php echo esc_url($logout_url); ?>" data-eep-nav-close-link class="eep-platform-nav-mobile-cta-link eep-nav-logout-link inline-flex h-12 w-full items-center justify-center rounded-xl bg-[var(--eep-primary)] text-[15px] font-bold text-white shadow-lg shadow-brand-500/25 active:scale-95 transition-all"><?php esc_html_e('Logout', 'nexdirectory'); ?></a>
                                <?php else : ?>
                                    <a href="<?php echo esc_url($login_url); ?>" data-eep-nav-close-link data-eep-auth-open="1" data-eep-auth-tab="login" class="eep-platform-nav-mobile-cta-link inline-flex h-12 w-full items-center justify-center rounded-xl border-2 border-gray-100 bg-gray-50 text-[15px] font-bold text-gray-800 hover:border-gray-200 active:scale-95 transition-all"><?php esc_html_e('Log In', 'nexdirectory'); ?></a>
                                    <a href="<?php echo esc_url($signup_url); ?>" data-eep-nav-close-link data-eep-auth-open="1" data-eep-auth-tab="signup" class="eep-platform-nav-mobile-cta-link eep-nav-signup-link inline-flex h-12 w-full items-center justify-center rounded-xl bg-[var(--eep-primary)] text-[15px] font-bold text-white shadow-lg shadow-brand-500/25 active:scale-95 transition-all"><?php esc_html_e('Sign Up', 'nexdirectory'); ?></a>
                                <?php endif; ?>
                            </div>
                        </div>
                    <?php endif; ?>
                </aside>
            </div>
        </nav>
        <?php
        return (string) ob_get_clean();
    }

    /**
     * Filters body classes to hide native theme page titles on platform pages if configured.
     */
    public function filter_body_class_for_plugin_pages($classes) {
        if (!is_array($classes)) $classes = [];

        $is_app_page = $this->is_app_page();
        if (!$this->is_platform_frontend_navigation_context()) {
            return array_values(array_unique($classes));
        }

        $classes[] = 'eep-plugin-page';
        if ($is_app_page) {
            $classes[] = 'eep-app-page';
        }
        $has_platform_sticky_nav = $this->is_platform_frontend_navigation_context()
            && $this->should_show_frontend_sticky_nav_for_current_user();
        if ($has_platform_sticky_nav) {
            $classes[] = 'eep-has-platform-sticky-nav';
            $classes[] = 'eep-theme-override-hide-header';
        }
        if ($is_app_page) {
            $app_view = $this->get_app_view();
            if ($app_view !== '' && $app_view !== 'app') {
                $classes[] = 'eep-view-' . sanitize_html_class($app_view);
            }
        }
        if ($this->should_hide_theme_page_titles_now()) {
            $classes[] = 'eep-hide-theme-page-title';
        }
        if ($this->is_theme_override_enabled('full_width')) {
            $classes[] = 'eep-theme-override-full-width';
        }
        if ($this->is_theme_override_enabled('remove_padding')) {
            $classes[] = 'eep-theme-override-no-padding';
        }
        if ($this->is_theme_override_enabled('hide_comments')) {
            $classes[] = 'eep-theme-override-hide-comments';
        }
        if ($this->is_theme_override_enabled('hide_header')) {
            $classes[] = 'eep-theme-override-hide-header';
        }
        if ($this->is_theme_override_enabled('reset_theme_styles')) {
            $classes[] = 'eep-theme-override-reset';
        }

        return array_values(array_unique($classes));
    }

    /**
     * Enqueues scoped app-page layout isolation styles only on plugin app pages.
     *
     * @return void
     */
    public function enqueue_app_page_assets() {
        if (is_admin() || (function_exists('wp_doing_ajax') && wp_doing_ajax())) {
            return;
        }
        if (!$this->is_app_page()) {
            return;
        }

        $css_relative = 'assets/css/eep-app-page.css';
        $css_path = plugin_dir_path(EEP_PLUGIN_FILE) . $css_relative;
        if (!is_readable($css_path)) {
            return;
        }

        wp_enqueue_style(
            'eep-app-page',
            plugins_url($css_relative, EEP_PLUGIN_FILE),
            [],
            (string) filemtime($css_path)
        );

        if ($this->is_option_enabled('eep_enable_smooth_scrolling', '0')) {
            // Keep CSS scroll behavior on auto; wheel/touch smoothing is handled by JS runtime.
            wp_add_inline_style('eep-app-page', 'html,body{scroll-behavior:auto;}');
        }
    }

    /**
     * Forces plugin app pages through a lightweight plugin template shell.
     *
     * @param string $template
     * @return string
     */
    public function filter_template_include_for_app_pages($template) {
        if (!$this->is_app_page()) {
            return (string) $template;
        }
        if (!$this->is_theme_override_master_enabled()) {
            return (string) $template;
        }

        $override_template = plugin_dir_path(EEP_PLUGIN_FILE) . 'templates/eep-app-page.php';
        if (is_readable($override_template)) {
            return $override_template;
        }

        return (string) $template;
    }

    /**
     * Removes theme-level featured image output on platform frontend pages.
     *
     * Platform templates already handle hero imagery, so this prevents duplicate
     * oversized thumbnails from rendering above the sticky nav.
     *
     * @param string $html
     * @param int $post_id
     * @param int $post_thumbnail_id
     * @param string|array<int, int>|array<string, string> $size
     * @param string|array<string, string> $attr
     * @return string
     */
    public function filter_platform_featured_image_html($html, $post_id = 0, $post_thumbnail_id = 0, $size = 'post-thumbnail', $attr = '') {
        if (is_admin() || (function_exists('wp_doing_ajax') && wp_doing_ajax())) {
            return (string) $html;
        }
        if (defined('REST_REQUEST') && REST_REQUEST) {
            return (string) $html;
        }
        if (!$this->is_platform_frontend_navigation_context()) {
            return (string) $html;
        }
        if (!$this->is_theme_override_enabled('hide_featured_image')) {
            return (string) $html;
        }

        return '';
    }

    /**
     * Disables comments on plugin pages when theme override asks for it.
     *
     * @param bool $open
     * @param int $post_id
     * @return bool
     */
    public function filter_comments_open_for_plugin_pages($open, $post_id = 0) {
        if (is_admin() || (function_exists('wp_doing_ajax') && wp_doing_ajax())) {
            return (bool) $open;
        }
        if (!$this->is_platform_frontend_navigation_context()) {
            return (bool) $open;
        }
        if ($this->is_theme_override_enabled('hide_comments')) {
            return false;
        }
        return (bool) $open;
    }

    /**
     * Injects a compatibility CSS reset layer for plugin pages.
     *
     * @return void
     */
    public function output_plugin_page_theme_override_css() {
        if (is_admin() || (function_exists('wp_doing_ajax') && wp_doing_ajax())) {
            return;
        }
        if (!$this->is_platform_frontend_navigation_context()) {
            return;
        }

        $full_width = $this->is_theme_override_enabled('full_width');
        $remove_padding = $this->is_theme_override_enabled('remove_padding');
        $hide_title = $this->should_hide_theme_page_titles_now();
        $hide_comments = $this->is_theme_override_enabled('hide_comments');
        $hide_header = $this->is_theme_override_enabled('hide_header');
        $reset_theme_styles = $this->is_theme_override_enabled('reset_theme_styles');
        if (!$full_width && !$remove_padding && !$hide_title && !$hide_comments && !$hide_header && !$reset_theme_styles) {
            return;
        }

        $css = '';
        if ($full_width) {
            $css .= '
body.eep-plugin-page .site,
body.eep-plugin-page #page,
body.eep-plugin-page #content,
body.eep-plugin-page #main,
body.eep-plugin-page main,
body.eep-plugin-page .site-content,
body.eep-plugin-page .content-area,
body.eep-plugin-page .site-main,
body.eep-plugin-page .entry-content,
body.eep-plugin-page .entry,
body.eep-plugin-page .post,
body.eep-plugin-page article.page,
body.eep-plugin-page article.post,
body.eep-plugin-page .container,
body.eep-plugin-page .container-fluid,
body.eep-plugin-page .wrap,
body.eep-plugin-page .site-inner,
body.eep-plugin-page .ast-container,
body.eep-plugin-page .ast-article-single,
body.eep-plugin-page .inside-article,
body.eep-plugin-page #primary,
body.eep-plugin-page .primary,
body.eep-plugin-page .main-content {
    width: 100% !important;
    max-width: none !important;
    margin-left: 0 !important;
    margin-right: 0 !important;
}';
        }
        if ($remove_padding) {
            $css .= '
body.eep-plugin-page .site-content,
body.eep-plugin-page .content-area,
body.eep-plugin-page .site-main,
body.eep-plugin-page #content,
body.eep-plugin-page #main,
body.eep-plugin-page main,
body.eep-plugin-page article.page,
body.eep-plugin-page article.post,
body.eep-plugin-page .entry-content,
body.eep-plugin-page .container,
body.eep-plugin-page .container-fluid,
body.eep-plugin-page .wrap,
body.eep-plugin-page .site-inner,
body.eep-plugin-page .ast-container,
body.eep-plugin-page .inside-article {
    padding-top: 0 !important;
    padding-bottom: 0 !important;
    padding-left: 0 !important;
    padding-right: 0 !important;
    margin-left: 0 !important;
    margin-right: 0 !important;
}';
        }
        if ($hide_title) {
            $css .= '
body.eep-plugin-page .entry-title,
body.eep-plugin-page .page-title,
body.eep-plugin-page .wp-block-post-title,
body.eep-plugin-page .entry-meta,
body.eep-plugin-page .post-meta,
body.eep-plugin-page .breadcrumbs,
body.eep-plugin-page .breadcrumb,
body.eep-plugin-page .yoast-breadcrumb,
body.eep-plugin-page .entry-header,
body.eep-plugin-page .page-header,
body.eep-plugin-page .ast-single-post-header,
body.eep-plugin-page .post-thumbnail,
body.eep-plugin-page .featured-image,
body.eep-plugin-page .entry-media,
body.eep-hide-theme-page-title .entry-header,
body.eep-hide-theme-page-title .page-header {
    display: none !important;
}';
        }
        if ($hide_comments) {
            $css .= '
body.eep-plugin-page #comments,
body.eep-plugin-page .comments-area,
body.eep-plugin-page .comment-respond,
body.eep-plugin-page .post-comments {
    display: none !important;
}';
        }
        if ($hide_header) {
            $css .= '
body.eep-plugin-page #header,
body.eep-plugin-page .site-header,
body.eep-plugin-page #masthead,
body.eep-plugin-page #site-header,
body.eep-plugin-page .ast-header-wrap,
body.eep-plugin-page .main-header-bar,
body.eep-plugin-page .ast-mobile-header-wrap,
body.eep-plugin-page .ast-desktop-header-content,
body.eep-plugin-page .header-main-layout-1,
body.eep-plugin-page .elementor-location-header,
body.eep-plugin-page .site-branding,
body.eep-plugin-page .site-title,
body.eep-plugin-page .wp-block-site-title {
    display: none !important;
}';
        }
        if ($reset_theme_styles) {
            $css .= '
body.eep-plugin-page .hentry,
body.eep-plugin-page article.page,
body.eep-plugin-page article.post,
body.eep-plugin-page .entry-content,
body.eep-plugin-page .page-content,
body.eep-plugin-page .widget-area,
body.eep-plugin-page #secondary,
body.eep-plugin-page .sidebar {
    background: transparent !important;
    border: 0 !important;
    box-shadow: none !important;
}';
            $css .= '
body.eep-plugin-page .widget-area,
body.eep-plugin-page #secondary,
body.eep-plugin-page .sidebar {
    display: none !important;
}';
            $css .= '
body.eep-plugin-page .elementor-section.elementor-section-boxed > .elementor-container,
body.eep-plugin-page .elementor-container {
    max-width: 100% !important;
}';
            $css .= '
body.eep-plugin-page header.site-header .site-branding,
body.eep-plugin-page .site-header .custom-logo-link + .site-title,
body.eep-plugin-page .site-header .site-title,
body.eep-plugin-page .site-header .site-description {
    display: none !important;
}';
        }

        if ($css === '') {
            return;
        }

        echo '<style id="eep-plugin-theme-override">' . trim((string) $css) . '</style>'; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
    }

    private function is_theme_override_master_enabled() {
        return $this->is_option_enabled('eep_theme_override_all_plugin_pages', '1');
    }

    private function is_theme_override_enabled($feature_key) {
        if ($this->is_theme_override_master_enabled() && $feature_key !== 'hide_header') {
            return true;
        }

        $map = [
            'full_width' => 'eep_theme_override_full_width',
            'remove_padding' => 'eep_theme_override_remove_padding',
            'hide_title' => 'eep_theme_override_hide_title',
            'hide_comments' => 'eep_theme_override_hide_comments',
            'hide_featured_image' => 'eep_theme_override_hide_featured_image',
            'hide_header' => 'eep_theme_override_hide_header',
            'reset_theme_styles' => 'eep_theme_override_reset_theme_styles',
        ];
        $option_key = isset($map[$feature_key]) ? (string) $map[$feature_key] : '';
        if ($option_key === '') {
            return false;
        }

        return $this->is_option_enabled($option_key, '1');
    }

    private function should_hide_theme_page_titles_now() {
        if (!$this->is_theme_override_enabled('hide_title') && !$this->is_option_enabled('eep_hide_theme_titles_on_plugin_pages', '0')) return false;
        if (is_admin() || wp_doing_ajax() || (defined('REST_REQUEST') && REST_REQUEST)) return false;
        if (!is_singular()) return false;

        $queried_id = absint((int) get_queried_object_id());
        if ($queried_id > 0) {
            $platform_page_ids = $this->get_platform_page_ids_for_title_hiding();
            if (in_array($queried_id, $platform_page_ids, true)) {
                return true;
            }
        }

        return $this->current_page_uses_platform_shortcode();
    }

    private function get_platform_page_ids_for_title_hiding() {
        // Omitting full map array here for brevity, assumes lookup of core page configs (account, submit, dashboard, etc.)
        // Returns an array of mapped Page IDs configured in options.
        static $cached_ids = null;
        if (is_array($cached_ids)) return $cached_ids;
        
        $ids = [];
        $options = [
            'eep_page_events_url', 'eep_page_events_map_url', 'eep_page_events_calendar_url', 
            'eep_page_submit_event_url', 'eep_page_my_events_url', 'eep_page_boost_posts_url',
            'eep_page_owner_analytics_url', 'eep_page_my_tickets_url', 'eep_page_whos_attending_url',
            'eep_page_event_webapp_url',
            'eep_page_whats_new_url', 'eep_page_notifications_url', 'eep_page_account_url', 'eep_page_login_signup_url'
        ];
        
        foreach ($options as $opt) {
            $url = esc_url_raw((string) get_option($opt, ''));
            if ($url !== '') {
                $page_id = absint((int) url_to_postid($url));
                if ($page_id > 0) $ids[$page_id] = $page_id;
            }
        }
        
        $cached_ids = array_values($ids);
        return $cached_ids;
    }

    private function current_page_uses_platform_shortcode() {
        $post = $this->resolve_app_page_post();
        if (!$post instanceof WP_Post) {
            return false;
        }

        if ((string) get_post_meta((int) $post->ID, '_eep_is_app_page', true) === '1') {
            return true;
        }

        return $this->extract_app_view_from_content((string) $post->post_content) !== '';
    }

    /**
     * Renders top-right auth/account links when sticky nav is not handling them.
     *
     * @return string
     */
    public function render_frontend_auth_links() {
        if (!$this->is_option_enabled('eep_show_frontend_auth_links', '1')) {
            return '';
        }
        if ($this->sticky_nav_handles_auth_cta_for_current_user()) {
            return '';
        }

        $wrapper_class = 'eep-page-auth-links';
        $redirect_to = $this->current_url();
        $login_base = $this->get_custom_login_page_url();
        if ($login_base === '') {
            $login_url = wp_login_url($redirect_to);
        } else {
            $login_url = eep_add_query_arg('redirect_to', $redirect_to, $login_base);
        }

        if (is_user_logged_in()) {
            $account_url = $this->get_platform_page_url('eep_page_account_url', ['account', 'my-account'], home_url('/'));
            $logout_url = wp_logout_url($login_url);
            return '<div class="' . esc_attr($wrapper_class) . '" role="navigation" aria-label="' . esc_attr__('Account actions', 'nexdirectory') . '">'
                . '<a class="eep-page-auth-link" href="' . esc_url($account_url) . '">' . esc_html__('My Account', 'nexdirectory') . '</a>'
                . '<a class="eep-page-auth-link eep-page-auth-link-primary" href="' . esc_url($logout_url) . '">' . esc_html__('Logout', 'nexdirectory') . '</a>'
                . '</div>';
        }

        $signup_url = eep_add_query_arg('eep_auth', 'signup', $login_url);
        return '<div class="' . esc_attr($wrapper_class) . '" role="navigation" aria-label="' . esc_attr__('Authentication', 'nexdirectory') . '">'
            . '<a class="eep-page-auth-link" data-eep-auth-open="1" data-eep-auth-tab="login" href="' . esc_url($login_url) . '">' . esc_html__('Login', 'nexdirectory') . '</a>'
            . '<a class="eep-page-auth-link eep-page-auth-link-primary" data-eep-auth-open="1" data-eep-auth-tab="signup" href="' . esc_url($signup_url) . '">' . esc_html__('Sign Up', 'nexdirectory') . '</a>'
            . '</div>';
    }

    /**
     * Renders global auth modal so login/signup can happen without leaving the page.
     *
     * @return string
     */
    private function render_frontend_auth_modal() {
        if (is_user_logged_in()) {
            return '';
        }

        static $printed = false;
        if ($printed) {
            return '';
        }
        $printed = true;

        $redirect_to = $this->current_url();
        $login_base = $this->get_custom_login_page_url();
        if (!is_string($login_base) || trim($login_base) === '') {
            $login_base = home_url('/login-signup/');
        }
        $login_path = (string) wp_parse_url($login_base, PHP_URL_PATH);
        $login_path = $login_path !== '' ? untrailingslashit($login_path) : '';
        if ($login_path === '') {
            $login_path = '/login-signup';
        }

        $claim_page_url = $this->get_platform_page_url(
            'eep_claim_listing_page_url',
            ['claim-listing', 'claim'],
            home_url('/claim-listing/')
        );
        $claim_path = (string) wp_parse_url((string) $claim_page_url, PHP_URL_PATH);
        $claim_path = $claim_path !== '' ? untrailingslashit($claim_path) : '';

        ob_start();
        ?>
        <style id="eep-global-auth-modal-style">
            #eep-global-auth-modal[hidden] { display: none !important; }
            #eep-global-auth-modal {
                position: fixed;
                inset: 0;
                z-index: 1500;
                display: flex;
                align-items: center;
                justify-content: center;
                padding: 1rem;
                background: rgba(15, 23, 42, 0.48);
                backdrop-filter: blur(4px);
                -webkit-backdrop-filter: blur(4px);
                opacity: 0;
                pointer-events: none;
                transition: opacity .24s ease;
            }
            #eep-global-auth-modal.is-open {
                opacity: 1;
                pointer-events: auto;
            }
            .eep-auth-modal-open {
                overflow: hidden !important;
            }
            #eep-global-auth-modal .eep-global-auth-modal__dialog {
                width: min(560px, 96vw);
                max-height: calc(100dvh - 2rem);
                overflow: auto;
                background: #fff;
                border: 1px solid #e5e7eb;
                border-radius: 1.25rem;
                box-shadow: 0 28px 84px rgba(2, 6, 23, .42);
                transform: translateY(16px) scale(.98);
                transition: transform .26s cubic-bezier(.22,1,.36,1);
            }
            #eep-global-auth-modal.is-open .eep-global-auth-modal__dialog {
                transform: translateY(0) scale(1);
            }
            .eep-global-auth-modal__head {
                display: flex;
                align-items: center;
                justify-content: space-between;
                gap: .75rem;
                padding: 1rem 1.1rem .8rem;
                border-bottom: 1px solid #f3f4f6;
                position: sticky;
                top: 0;
                background: #fff;
                z-index: 2;
            }
            .eep-global-auth-modal__title {
                margin: 0;
                font-size: 1.05rem;
                font-weight: 800;
                color: #111827;
            }
            .eep-global-auth-modal__close {
                border: 1px solid #e5e7eb;
                background: #f8fafc;
                color: #374151;
                width: 2.1rem;
                height: 2.1rem;
                border-radius: 999px;
                cursor: pointer;
                font-size: 1.2rem;
                line-height: 1;
            }
            .eep-global-auth-modal__body {
                padding: 1rem 1.1rem 1.2rem;
            }
            .eep-global-auth-tabs {
                display: grid;
                grid-template-columns: repeat(2, minmax(0, 1fr));
                gap: .4rem;
                padding: .25rem;
                border-radius: .85rem;
                background: #f3f4f6;
                margin-bottom: .9rem;
            }
            .eep-global-auth-tab {
                border: 0;
                border-radius: .65rem;
                background: transparent;
                color: #6b7280;
                font-size: .86rem;
                font-weight: 700;
                padding: .55rem .65rem;
                cursor: pointer;
            }
            .eep-global-auth-tab.is-active {
                background: #fff;
                color: #111827;
                box-shadow: 0 1px 3px rgba(0, 0, 0, .08);
            }
            .eep-global-auth-panel[hidden] { display: none !important; }
            .eep-global-auth-form { display: grid; gap: .72rem; }
            .eep-global-auth-form label {
                font-size: .72rem;
                font-weight: 800;
                text-transform: uppercase;
                letter-spacing: .05em;
                color: #6b7280;
                display: block;
            }
            .eep-global-auth-form input[type="text"],
            .eep-global-auth-form input[type="email"],
            .eep-global-auth-form input[type="password"] {
                width: 100%;
                border: 1px solid #d1d5db;
                background: #f9fafb;
                color: #111827;
                border-radius: .75rem;
                min-height: 2.75rem;
                padding: .65rem .85rem;
                font-size: .95rem;
                outline: none;
            }
            .eep-global-auth-form input:focus {
                border-color: var(--eep-primary, #ff5e14);
                box-shadow: 0 0 0 3px rgba(var(--eep-primary-rgb,255,94,20), .14);
                background: #fff;
            }
            .eep-global-auth-submit {
                border: 0;
                border-radius: .75rem;
                min-height: 2.75rem;
                padding: .65rem .85rem;
                background: var(--eep-primary, #ff5e14);
                color: #fff !important;
                -webkit-text-fill-color: #fff !important;
                font-size: .95rem;
                font-weight: 800;
                cursor: pointer;
            }
            .eep-global-auth-submit:hover {
                background: var(--eep-primary-hover, #ea580c);
            }
            .eep-global-auth-minor {
                border: 0;
                background: transparent;
                color: #4b5563;
                font-size: .85rem;
                font-weight: 600;
                padding: .2rem;
                text-decoration: underline;
                cursor: pointer;
                justify-self: start;
            }
            .eep-global-auth-check {
                display: flex;
                align-items: flex-start;
                gap: .5rem;
                font-size: .79rem;
                color: #4b5563;
                line-height: 1.35;
            }
            .eep-global-auth-check input {
                margin-top: .08rem;
                accent-color: var(--eep-primary, #ff5e14);
            }
        </style>
        <div
            id="eep-global-auth-modal"
            class="eep-global-auth-modal"
            hidden
            data-login-path="<?php echo esc_attr($login_path); ?>"
            data-claim-path="<?php echo esc_attr($claim_path); ?>"
            data-default-redirect="<?php echo esc_attr($redirect_to); ?>"
        >
            <div class="eep-global-auth-modal__dialog" role="dialog" aria-modal="true" aria-labelledby="eep-global-auth-title">
                <div class="eep-global-auth-modal__head">
                    <h2 class="eep-global-auth-modal__title" id="eep-global-auth-title"><?php esc_html_e('Sign In Or Join', 'nexdirectory'); ?></h2>
                    <button type="button" class="eep-global-auth-modal__close" data-eep-auth-close aria-label="<?php esc_attr_e('Close', 'nexdirectory'); ?>">&times;</button>
                </div>
                <div class="eep-global-auth-modal__body">
                    <div class="eep-global-auth-tabs" role="tablist" aria-label="<?php esc_attr_e('Authentication tabs', 'nexdirectory'); ?>">
                        <button type="button" class="eep-global-auth-tab is-active" data-eep-auth-tab-trigger="login" role="tab" aria-selected="true"><?php esc_html_e('Log In', 'nexdirectory'); ?></button>
                        <button type="button" class="eep-global-auth-tab" data-eep-auth-tab-trigger="signup" role="tab" aria-selected="false"><?php esc_html_e('Sign Up', 'nexdirectory'); ?></button>
                    </div>

                    <section class="eep-global-auth-panel" data-eep-auth-panel="login">
                        <form class="eep-global-auth-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                            <input type="hidden" name="action" value="eep_auth_login">
                            <input type="hidden" name="redirect_to" class="eep-global-auth-redirect" value="<?php echo esc_attr($redirect_to); ?>">
                            <input type="hidden" name="_wpnonce" value="<?php echo esc_attr(wp_create_nonce('eep_auth_login_nonce')); ?>">
                            <input type="hidden" name="eep_website_auth" class="eep-honeypot" tabindex="-1" autocomplete="off" aria-hidden="true">
                            <input type="hidden" name="eep_recaptcha_token" class="eep-recaptcha-token" value="">
                            <?php wp_nonce_field('eep_auth_login_nonce', 'eep_auth_login_nonce'); ?>
                            <p>
                                <label><?php esc_html_e('Username or Email', 'nexdirectory'); ?></label>
                                <input type="text" name="log" required autocomplete="username">
                            </p>
                            <p>
                                <label><?php esc_html_e('Password', 'nexdirectory'); ?></label>
                                <input type="password" name="pwd" required autocomplete="current-password">
                            </p>
                            <button type="submit" class="eep-global-auth-submit"><?php esc_html_e('Log In', 'nexdirectory'); ?></button>
                            <button type="button" class="eep-global-auth-minor" data-eep-auth-switch="forgot"><?php esc_html_e('Forgot your password?', 'nexdirectory'); ?></button>
                        </form>
                    </section>

                    <section class="eep-global-auth-panel" data-eep-auth-panel="signup" hidden>
                        <form class="eep-global-auth-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                            <input type="hidden" name="action" value="eep_auth_signup">
                            <input type="hidden" name="redirect_to" class="eep-global-auth-redirect" value="<?php echo esc_attr($redirect_to); ?>">
                            <input type="hidden" name="_wpnonce" value="<?php echo esc_attr(wp_create_nonce('eep_auth_signup_nonce')); ?>">
                            <input type="hidden" name="eep_website_auth" class="eep-honeypot" tabindex="-1" autocomplete="off" aria-hidden="true">
                            <input type="hidden" name="eep_recaptcha_token" class="eep-recaptcha-token" value="">
                            <?php wp_nonce_field('eep_auth_signup_nonce', 'eep_auth_signup_nonce'); ?>
                            <p>
                                <label><?php esc_html_e('Username', 'nexdirectory'); ?></label>
                                <input type="text" name="user_login" required autocomplete="username">
                            </p>
                            <p>
                                <label><?php esc_html_e('Email', 'nexdirectory'); ?></label>
                                <input type="email" name="user_email" required autocomplete="email">
                            </p>
                            <p>
                                <label><?php esc_html_e('Password', 'nexdirectory'); ?></label>
                                <input type="password" name="user_pass" required autocomplete="new-password">
                            </p>
                            <label class="eep-global-auth-check"><input type="checkbox" name="eep_gdpr_accept" value="1" required><span><?php esc_html_e('I agree to GDPR/privacy processing and essential service emails required for account operations.', 'nexdirectory'); ?></span></label>
                            <label class="eep-global-auth-check"><input type="checkbox" name="eep_gdpr_marketing_opt_in" value="1" checked><span><?php esc_html_e('I agree to receive organizer announcements and platform news emails (optional).', 'nexdirectory'); ?></span></label>
                            <button type="submit" class="eep-global-auth-submit"><?php esc_html_e('Create Account', 'nexdirectory'); ?></button>
                        </form>
                    </section>

                    <section class="eep-global-auth-panel" data-eep-auth-panel="forgot" hidden>
                        <form class="eep-global-auth-form" method="post" action="<?php echo esc_url(admin_url('admin-post.php')); ?>">
                            <input type="hidden" name="action" value="eep_auth_forgot_password">
                            <input type="hidden" name="redirect_to" class="eep-global-auth-redirect" value="<?php echo esc_attr($redirect_to); ?>">
                            <input type="hidden" name="eep_website_auth" class="eep-honeypot" tabindex="-1" autocomplete="off" aria-hidden="true">
                            <?php wp_nonce_field('eep_auth_forgot_password_nonce', 'eep_auth_forgot_password_nonce'); ?>
                            <p>
                                <label><?php esc_html_e('Username or Email', 'nexdirectory'); ?></label>
                                <input type="text" name="user_login_email" required autocomplete="username email">
                            </p>
                            <button type="submit" class="eep-global-auth-submit"><?php esc_html_e('Send Reset Link', 'nexdirectory'); ?></button>
                            <button type="button" class="eep-global-auth-minor" data-eep-auth-switch="login"><?php esc_html_e('Back to log in', 'nexdirectory'); ?></button>
                        </form>
                    </section>
                </div>
            </div>
        </div>
        <script id="eep-global-auth-modal-script">
            (function () {
                if (window.__eepGlobalAuthModalInit) {
                    return;
                }
                window.__eepGlobalAuthModalInit = true;

                var modal = document.getElementById('eep-global-auth-modal');
                if (!modal) {
                    return;
                }
                var dialog = modal.querySelector('.eep-global-auth-modal__dialog');
                var tabButtons = Array.prototype.slice.call(modal.querySelectorAll('[data-eep-auth-tab-trigger]'));
                var panels = Array.prototype.slice.call(modal.querySelectorAll('[data-eep-auth-panel]'));
                var redirectInputs = Array.prototype.slice.call(modal.querySelectorAll('.eep-global-auth-redirect'));
                var loginPath = String(modal.getAttribute('data-login-path') || '').trim();
                var claimPath = String(modal.getAttribute('data-claim-path') || '').trim();
                var defaultRedirect = String(modal.getAttribute('data-default-redirect') || window.location.href).trim();
                var closeTimer = null;
                var isOpen = false;

                function normalizePath(pathValue) {
                    var path = String(pathValue || '').trim();
                    if (!path) {
                        return '';
                    }
                    if (path.charAt(0) !== '/') {
                        path = '/' + path;
                    }
                    path = path.replace(/\/{2,}/g, '/');
                    if (path.length > 1) {
                        path = path.replace(/\/+$/, '');
                    }
                    return path;
                }

                loginPath = normalizePath(loginPath);
                claimPath = normalizePath(claimPath);

                function parseSafeUrl(input) {
                    try {
                        var parsed = new URL(String(input || ''), window.location.href);
                        if (parsed.origin !== window.location.origin) {
                            return null;
                        }
                        return parsed;
                    } catch (error) {
                        return null;
                    }
                }

                function setTab(nextTab) {
                    var allowed = ['login', 'signup', 'forgot'];
                    var tab = allowed.indexOf(String(nextTab || '').trim()) >= 0 ? String(nextTab || '').trim() : 'login';
                    tabButtons.forEach(function (button) {
                        var key = String(button.getAttribute('data-eep-auth-tab-trigger') || '').trim();
                        var active = key === tab;
                        button.classList.toggle('is-active', active);
                        button.setAttribute('aria-selected', active ? 'true' : 'false');
                    });
                    panels.forEach(function (panel) {
                        var key = String(panel.getAttribute('data-eep-auth-panel') || '').trim();
                        if (key === tab) {
                            panel.removeAttribute('hidden');
                        } else {
                            panel.setAttribute('hidden', 'hidden');
                        }
                    });
                }

                function setRedirect(redirectTo) {
                    var parsed = parseSafeUrl(redirectTo);
                    var value = parsed ? parsed.toString() : window.location.href;
                    redirectInputs.forEach(function (input) {
                        input.value = value;
                    });
                }

                function openModal(tab, redirectTo) {
                    window.clearTimeout(closeTimer);
                    setTab(tab);
                    setRedirect(redirectTo || defaultRedirect);
                    modal.hidden = false;
                    requestAnimationFrame(function () {
                        modal.classList.add('is-open');
                    });
                    document.documentElement.classList.add('eep-auth-modal-open');
                    document.body.classList.add('eep-auth-modal-open');
                    isOpen = true;
                    window.setTimeout(function () {
                        var field = modal.querySelector('[data-eep-auth-panel]:not([hidden]) input[type="text"], [data-eep-auth-panel]:not([hidden]) input[type="email"]');
                        if (field && typeof field.focus === 'function') {
                            field.focus();
                        }
                    }, 80);
                }

                function closeModal() {
                    if (!isOpen) {
                        return;
                    }
                    modal.classList.remove('is-open');
                    document.documentElement.classList.remove('eep-auth-modal-open');
                    document.body.classList.remove('eep-auth-modal-open');
                    isOpen = false;
                    closeTimer = window.setTimeout(function () {
                        if (!isOpen) {
                            modal.hidden = true;
                        }
                    }, 240);
                }

                window.eepOpenAuthModal = function (options) {
                    var opts = options && typeof options === 'object' ? options : {};
                    openModal(String(opts.tab || 'login'), String(opts.redirectTo || defaultRedirect));
                };

                modal.querySelectorAll('[data-eep-auth-close]').forEach(function (button) {
                    button.addEventListener('click', function (event) {
                        event.preventDefault();
                        closeModal();
                    });
                });

                tabButtons.forEach(function (button) {
                    button.addEventListener('click', function () {
                        setTab(String(button.getAttribute('data-eep-auth-tab-trigger') || 'login'));
                    });
                });

                modal.querySelectorAll('[data-eep-auth-switch]').forEach(function (button) {
                    button.addEventListener('click', function () {
                        setTab(String(button.getAttribute('data-eep-auth-switch') || 'login'));
                    });
                });

                modal.addEventListener('click', function (event) {
                    if (event.target === modal) {
                        closeModal();
                    }
                });

                if (dialog) {
                    dialog.addEventListener('click', function (event) {
                        event.stopPropagation();
                    });
                }

                document.addEventListener('keydown', function (event) {
                    if (event.key === 'Escape') {
                        closeModal();
                    }
                });

                document.addEventListener('click', function (event) {
                    var directTrigger = event.target.closest('[data-eep-auth-open="1"]');
                    var anchor = event.target.closest('a[href]');
                    var trigger = directTrigger || anchor;
                    if (!trigger) {
                        return;
                    }
                    if (event.defaultPrevented || event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
                        return;
                    }
                    if (anchor && (anchor.hasAttribute('download') || String(anchor.getAttribute('target') || '') === '_blank')) {
                        return;
                    }

                    var href = String((trigger.getAttribute('href') || '')).trim();
                    var parsed = parseSafeUrl(href);
                    if (!parsed) {
                        return;
                    }

                    var path = normalizePath(parsed.pathname);
                    var isWpLogin = /\/wp-login\.php$/i.test(path);
                    var isLoginRoute = !!(loginPath && path === loginPath);
                    var isClaimRoute = !!(!document.body.classList.contains('logged-in') && claimPath && path === claimPath);
                    var shouldIntercept = !!directTrigger || isWpLogin || isLoginRoute || isClaimRoute;
                    if (!shouldIntercept) {
                        return;
                    }

                    event.preventDefault();
                    var tab = String(trigger.getAttribute('data-eep-auth-tab') || parsed.searchParams.get('eep_auth') || 'login').trim();
                    if (tab !== 'signup' && tab !== 'forgot' && tab !== 'login') {
                        tab = 'login';
                    }
                    var redirectTo = String(trigger.getAttribute('data-eep-auth-redirect') || parsed.searchParams.get('redirect_to') || '').trim();
                    if (!redirectTo) {
                        redirectTo = isClaimRoute ? parsed.toString() : window.location.href;
                    }
                    openModal(tab, redirectTo);
                });
            })();
        </script>
        <?php
        return (string) ob_get_clean();
    }

    /**
     * Outputs global auth links in footer if not already rendered.
     *
     * @return void
     */
    public function output_frontend_auth_links_global() {
        if (is_admin()) {
            return;
        }
        if ($this->is_platform_frontend_navigation_context() && did_action('eep_frontend_sticky_nav_rendered') === 0) {
            $sticky_markup = $this->render_frontend_sticky_nav();
            if ($sticky_markup !== '') {
                echo $sticky_markup; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
                do_action('eep_frontend_sticky_nav_rendered');
            }
        }
        if (did_action('eep_frontend_auth_links_rendered') === 0) {
            $auth_markup = $this->render_frontend_auth_links();
            if ($auth_markup !== '') {
                echo $auth_markup; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
                do_action('eep_frontend_auth_links_rendered');
            }
        }
        $auth_modal_markup = $this->render_frontend_auth_modal();
        if ($auth_modal_markup !== '') {
            echo $auth_modal_markup; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
        }
        if ($this->is_platform_frontend_navigation_context() && did_action('eep_frontend_footer_rendered') === 0) {
            $footer_markup = $this->render_frontend_platform_footer();
            if ($footer_markup !== '') {
                echo $footer_markup; // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped
                do_action('eep_frontend_footer_rendered');
            }
        }
    }
}
