(function ($) {
  'use strict';

  function getConfig(key, fallback) {
    if (window.nexdirectoryConfig && Object.prototype.hasOwnProperty.call(window.nexdirectoryConfig, key)) {
      return window.nexdirectoryConfig[key];
    }
    return fallback;
  }

  function getMessage(key, fallback) {
    const messages = getConfig('messages', {});
    if (messages && Object.prototype.hasOwnProperty.call(messages, key)) {
      return messages[key];
    }
    return fallback;
  }

  function toBool(value, fallback) {
    if (typeof value === 'boolean') {
      return value;
    }
    if (typeof value === 'number') {
      return value !== 0;
    }
    if (typeof value === 'string') {
      const normalized = String(value || '').trim().toLowerCase();
      if (['1', 'true', 'yes', 'on', 'enabled'].indexOf(normalized) >= 0) {
        return true;
      }
      if (['0', 'false', 'no', 'off', 'disabled', ''].indexOf(normalized) >= 0) {
        return false;
      }
    }
    return !!fallback;
  }

  let pagePreloaderController = null;
  let uiReadyTimer = 0;

  function isWpAdminSurface() {
    return !!(
      document.body &&
      (
        document.body.classList.contains('wp-admin') ||
        document.body.classList.contains('wp-core-ui') ||
        document.getElementById('wpadminbar') ||
        document.getElementById('adminmenu')
      )
    );
  }

  if (document.documentElement && !isWpAdminSurface()) {
    document.documentElement.classList.add('nexdirectory-ui-hydrating');
  }

  function markUiHydrating() {
    if (isWpAdminSurface()) {
      markUiReady(0);
      return;
    }
    if (!document.documentElement) {
      return;
    }
    if (uiReadyTimer) {
      window.clearTimeout(uiReadyTimer);
      uiReadyTimer = 0;
    }
    document.documentElement.classList.remove('nexdirectory-ui-ready');
    document.documentElement.classList.add('nexdirectory-ui-hydrating');
    uiReadyTimer = window.setTimeout(function () {
      markUiReady(0);
    }, 1800);
  }

  function markUiReady(delay) {
    const wait = Math.max(0, Number(delay) || 0);
    if (uiReadyTimer) {
      window.clearTimeout(uiReadyTimer);
      uiReadyTimer = 0;
    }
    uiReadyTimer = window.setTimeout(function () {
      if (!document.documentElement) {
        return;
      }
      document.documentElement.classList.add('nexdirectory-ui-ready');
      document.documentElement.classList.remove('nexdirectory-ui-hydrating');
      uiReadyTimer = 0;
    }, wait);
  }

  function initPagePreloader() {
    if (isWpAdminSurface()) {
      if (pagePreloaderController && typeof pagePreloaderController.hide === 'function') {
        pagePreloaderController.hide();
      }
      const staleOverlay = document.getElementById('nexdirectory-page-preloader');
      if (staleOverlay && staleOverlay.parentNode) {
        staleOverlay.parentNode.removeChild(staleOverlay);
      }
      if (document.documentElement) {
        document.documentElement.classList.remove('nexdirectory-preloader-active', 'nexdirectory-ui-hydrating');
        document.documentElement.classList.add('nexdirectory-ui-ready');
      }
      if (document.body) {
        document.body.classList.remove('nexdirectory-preloader-active');
      }
      return;
    }
    if (pagePreloaderController && typeof pagePreloaderController.syncFromConfig === 'function') {
      pagePreloaderController.syncFromConfig();
      return;
    }

    const clampNumber = function (value, min, max, fallback) {
      const numeric = Number(value);
      if (!Number.isFinite(numeric)) {
        return fallback;
      }
      if (numeric < min) {
        return min;
      }
      if (numeric > max) {
        return max;
      }
      return numeric;
    };

    const ensureStyleNode = function () {
      let styleNode = document.getElementById('nexdirectory-page-preloader-style');
      if (styleNode) {
        return styleNode;
      }
      styleNode = document.createElement('style');
      styleNode.id = 'nexdirectory-page-preloader-style';
      styleNode.type = 'text/css';
      styleNode.textContent = '' +
        '#nexdirectory-page-preloader{position:fixed;inset:0;z-index:2147483646;display:flex;align-items:center;justify-content:center;' +
        'background:var(--nexdirectory-preloader-bg,#fff);opacity:0;visibility:hidden;pointer-events:none;' +
        'transition:opacity var(--nexdirectory-preloader-fade,420ms) ease,visibility 0s linear var(--nexdirectory-preloader-fade,420ms);}' +
        '#nexdirectory-page-preloader.is-visible{opacity:1;visibility:visible;pointer-events:auto;transition-delay:0s;}' +
        '#nexdirectory-page-preloader .nexdirectory-page-preloader-stage{width:min(var(--nexdirectory-preloader-art-width,420px),86vw);max-height:72vh;display:flex;align-items:center;justify-content:center;}' +
        '#nexdirectory-page-preloader .nexdirectory-page-preloader-stage img,#nexdirectory-page-preloader .nexdirectory-page-preloader-stage svg{display:block;width:100%;height:auto;max-width:100%;}' +
        '#nexdirectory-page-preloader .nexdirectory-page-preloader-stage img{object-fit:contain;}' +
        'html.nexdirectory-preloader-active,body.nexdirectory-preloader-active{overflow:hidden !important;}' +
        '@media (max-width:767px){#nexdirectory-page-preloader .nexdirectory-page-preloader-stage{width:min(var(--nexdirectory-preloader-art-width-mobile,300px),82vw);}}';
      document.head.appendChild(styleNode);
      return styleNode;
    };

    const defaultSvg = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 200" width="100%" height="100%"><rect width="100%" height="100%" fill="#FFFFFF"/><text x="120" y="128" textLength="570" lengthAdjust="spacing" fill="#0A0A0A" style="font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Open Sans,Helvetica Neue,sans-serif;font-weight:850;font-size:66px;letter-spacing:0;">Nex Directory Free</text><circle cy="118" cx="90" r="14" fill="#FF5E14"></circle></svg>';

    let overlay = null;
    let stage = null;
    let minVisibleMs = 400;
    let fadeMs = 420;
    let showOnInitial = true;
    let showOnSpa = true;
    let showOnlyHome = false;
    let showOnce = false;
    let homePath = '/';
    let isHomePageRequest = false;
    let seenStorageKey = 'nexdirectory_preloader_seen';
    let disableOnMobile = false;
    let respectReducedMotion = true;
    let enabled = true;
    let seenInMemory = false;
    let visibleSince = 0;
    let hideTimer = 0;

    const clearHideTimer = function () {
      if (hideTimer) {
        window.clearTimeout(hideTimer);
        hideTimer = 0;
      }
    };

    const shouldDisable = function () {
      if (!enabled) {
        return true;
      }
      if (disableOnMobile && window.matchMedia && window.matchMedia('(max-width: 767px)').matches) {
        return true;
      }
      if (respectReducedMotion && window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
        return true;
      }
      return false;
    };

    const normalizePath = function (pathValue) {
      let 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 || '/';
    };

    const persistedState = (window.__nexdirectoryPreloaderPersist && typeof window.__nexdirectoryPreloaderPersist === 'object')
      ? window.__nexdirectoryPreloaderPersist
      : null;
    if (persistedState) {
      enabled = toBool(persistedState.enabled, enabled);
      minVisibleMs = clampNumber(persistedState.minVisibleMs, 0, 8000, minVisibleMs);
      fadeMs = clampNumber(persistedState.fadeMs, 80, 2000, fadeMs);
      showOnInitial = toBool(persistedState.showOnInitial, showOnInitial);
      showOnSpa = toBool(persistedState.showOnSpa, showOnSpa);
      showOnlyHome = toBool(persistedState.showOnlyHome, showOnlyHome);
      showOnce = toBool(persistedState.showOnce, showOnce);
      homePath = normalizePath(persistedState.homePath || homePath);
      isHomePageRequest = toBool(persistedState.isHomePageRequest, isHomePageRequest);
      seenStorageKey = String(persistedState.seenStorageKey || seenStorageKey).trim() || seenStorageKey;
      disableOnMobile = toBool(persistedState.disableOnMobile, disableOnMobile);
      respectReducedMotion = toBool(persistedState.respectReducedMotion, respectReducedMotion);
      seenInMemory = toBool(persistedState.seenInMemory, seenInMemory);
    }

    const resolvePathFromUrl = function (urlValue) {
      try {
        const parsed = new URL(String(urlValue || ''), window.location.origin);
        return normalizePath(parsed.pathname);
      } catch (error) {
        return normalizePath(window.location.pathname || '/');
      }
    };

    const currentPathIsHome = function () {
      const currentPath = normalizePath(window.location && window.location.pathname ? window.location.pathname : '/');
      return currentPath === homePath;
    };

    const getSafeLocalStorage = function () {
      try {
        return window.localStorage || null;
      } catch (error) {
        return null;
      }
    };

    const getSafeSessionStorage = function () {
      try {
        return window.sessionStorage || null;
      } catch (error) {
        return null;
      }
    };

    const getSeenCookieName = function () {
      return 'nexdirectory_seen_' + String(seenStorageKey || 'default').replace(/[^a-z0-9_-]/gi, '_');
    };

    const hasSeenCookie = function () {
      const cookieName = getSeenCookieName();
      const pairs = String(document.cookie || '').split(';');
      for (let i = 0; i < pairs.length; i++) {
        const pair = String(pairs[i] || '').trim();
        if (!pair) {
          continue;
        }
        const eqIndex = pair.indexOf('=');
        const key = eqIndex >= 0 ? pair.slice(0, eqIndex).trim() : pair;
        const value = eqIndex >= 0 ? pair.slice(eqIndex + 1).trim() : '';
        if (key === cookieName && value === '1') {
          return true;
        }
      }
      return false;
    };

    const setSeenCookie = function () {
      try {
        const cookieName = getSeenCookieName();
        const oneYearSeconds = 60 * 60 * 24 * 365;
        document.cookie = cookieName + '=1; path=/; max-age=' + String(oneYearSeconds) + '; SameSite=Lax';
      } catch (error) {
        // Ignore cookie write failures.
      }
    };

    const hasBeenSeen = function () {
      if (!showOnce) {
        return false;
      }
      if (seenInMemory) {
        return true;
      }
      const storage = getSafeLocalStorage();
      if (storage && seenStorageKey) {
        try {
          if (String(storage.getItem(seenStorageKey) || '') === '1') {
            return true;
          }
        } catch (error) {
          // Ignore storage read failures.
        }
      }
      const session = getSafeSessionStorage();
      if (session && seenStorageKey) {
        try {
          if (String(session.getItem(seenStorageKey) || '') === '1') {
            return true;
          }
        } catch (error) {
          // Ignore session read failures.
        }
      }
      return hasSeenCookie();
    };

    const markSeen = function () {
      if (!showOnce) {
        return;
      }
      seenInMemory = true;
      const storage = getSafeLocalStorage();
      if (!storage || !seenStorageKey) {
        const sessionFallback = getSafeSessionStorage();
        if (sessionFallback && seenStorageKey) {
          try {
            sessionFallback.setItem(seenStorageKey, '1');
          } catch (error) {
            // Ignore storage write failures.
          }
        }
        setSeenCookie();
        return;
      }
      try {
        storage.setItem(seenStorageKey, '1');
      } catch (error) {
        // Ignore storage write failures.
      }
      const session = getSafeSessionStorage();
      if (session && seenStorageKey) {
        try {
          session.setItem(seenStorageKey, '1');
        } catch (error) {
          // Ignore session write failures.
        }
      }
      setSeenCookie();
    };

    const canShowForReason = function (reason, contextUrl) {
      if (shouldDisable()) {
        return false;
      }
      if (String(reason || '') === 'initial' && !showOnInitial) {
        return false;
      }
      if (String(reason || '') === 'spa' && !showOnSpa) {
        return false;
      }
      if (showOnlyHome) {
        const contextPath = contextUrl
          ? resolvePathFromUrl(contextUrl)
          : normalizePath(window.location && window.location.pathname ? window.location.pathname : '/');
        if (contextPath !== homePath) {
          return false;
        }
      }
      if (showOnce && hasBeenSeen()) {
        return false;
      }
      return true;
    };

    const hydrateArtwork = function () {
      if (!stage) {
        return;
      }
      const source = String(getConfig('preloaderSource', 'svg') || 'svg').toLowerCase();
      const imageUrl = String(getConfig('preloaderImageUrl', '') || '').trim();
      const svgMarkup = String(getConfig('preloaderSvg', '') || '').trim();
      stage.replaceChildren();

      if (source === 'image' && imageUrl) {
        const img = document.createElement('img');
        img.src = imageUrl;
        img.alt = 'Loading';
        img.decoding = 'async';
        stage.appendChild(img);
        return;
      }

      try {
        const parser = new DOMParser();
        const doc = parser.parseFromString(svgMarkup || defaultSvg, 'image/svg+xml');
        const svg = doc && doc.documentElement && doc.documentElement.nodeName.toLowerCase() === 'svg'
          ? doc.documentElement
          : null;
        if (svg) {
          stage.appendChild(document.importNode(svg, true));
        }
      } catch (err) {
        stage.textContent = '';
      }
    };

    const syncFromConfig = function () {
      const hasConfigKey = function (key) {
        return !!(window.nexdirectoryConfig && Object.prototype.hasOwnProperty.call(window.nexdirectoryConfig, key));
      };

      if (hasConfigKey('preloaderEnabled')) {
        enabled = toBool(getConfig('preloaderEnabled', enabled), enabled);
      }
      if (hasConfigKey('preloaderMinDurationMs')) {
        minVisibleMs = clampNumber(getConfig('preloaderMinDurationMs', minVisibleMs), 0, 8000, minVisibleMs);
      }
      if (hasConfigKey('preloaderFadeDurationMs')) {
        fadeMs = clampNumber(getConfig('preloaderFadeDurationMs', fadeMs), 80, 2000, fadeMs);
      }
      if (hasConfigKey('preloaderShowOnInitial')) {
        showOnInitial = toBool(getConfig('preloaderShowOnInitial', showOnInitial), showOnInitial);
      }
      if (hasConfigKey('preloaderShowOnSpa')) {
        showOnSpa = toBool(getConfig('preloaderShowOnSpa', showOnSpa), showOnSpa);
      }
      if (hasConfigKey('preloaderShowOnlyHome')) {
        showOnlyHome = toBool(getConfig('preloaderShowOnlyHome', showOnlyHome), showOnlyHome);
      }
      if (hasConfigKey('preloaderShowOnce')) {
        showOnce = toBool(getConfig('preloaderShowOnce', showOnce), showOnce);
      }
      if (hasConfigKey('preloaderDisableOnMobile')) {
        disableOnMobile = toBool(getConfig('preloaderDisableOnMobile', disableOnMobile), disableOnMobile);
      }
      if (hasConfigKey('preloaderRespectReducedMotion')) {
        respectReducedMotion = toBool(getConfig('preloaderRespectReducedMotion', respectReducedMotion), respectReducedMotion);
      }
      if (hasConfigKey('preloaderIsHomePage')) {
        isHomePageRequest = toBool(getConfig('preloaderIsHomePage', isHomePageRequest), isHomePageRequest);
      }
      if (hasConfigKey('preloaderHomeUrl')) {
        const configuredHomeUrl = String(getConfig('preloaderHomeUrl', '/') || '/').trim() || '/';
        homePath = resolvePathFromUrl(configuredHomeUrl);
      }
      if (hasConfigKey('preloaderSeenStorageKey')) {
        seenStorageKey = String(getConfig('preloaderSeenStorageKey', seenStorageKey) || seenStorageKey).trim() || seenStorageKey;
      }

      const bgColor = String(getConfig('preloaderBackgroundColor', '#FFFFFF') || '#FFFFFF').trim() || '#FFFFFF';
      const artWidth = clampNumber(getConfig('preloaderLogoMaxWidth', 420), 120, 900, 420);
      ensureStyleNode();
      if (overlay) {
        overlay.style.setProperty('--nexdirectory-preloader-bg', bgColor);
        overlay.style.setProperty('--nexdirectory-preloader-fade', String(fadeMs) + 'ms');
        overlay.style.setProperty('--nexdirectory-preloader-art-width', String(artWidth) + 'px');
        overlay.style.setProperty('--nexdirectory-preloader-art-width-mobile', String(Math.max(160, Math.round(artWidth * 0.72))) + 'px');
      }
      hydrateArtwork();

      window.__nexdirectoryPreloaderPersist = {
        enabled: enabled,
        minVisibleMs: minVisibleMs,
        fadeMs: fadeMs,
        showOnInitial: showOnInitial,
        showOnSpa: showOnSpa,
        showOnlyHome: showOnlyHome,
        showOnce: showOnce,
        homePath: homePath,
        isHomePageRequest: isHomePageRequest,
        seenStorageKey: seenStorageKey,
        disableOnMobile: disableOnMobile,
        respectReducedMotion: respectReducedMotion,
        seenInMemory: seenInMemory
      };
    };

    const ensureOverlay = function () {
      if (overlay && document.body && document.body.contains(overlay)) {
        return overlay;
      }
      overlay = document.getElementById('nexdirectory-page-preloader');
      if (!overlay) {
        overlay = document.createElement('div');
        overlay.id = 'nexdirectory-page-preloader';
        overlay.setAttribute('aria-hidden', 'true');
        overlay.innerHTML = '<div class="nexdirectory-page-preloader-stage"></div>';
      }
      stage = overlay.querySelector('.nexdirectory-page-preloader-stage');
      if (!stage) {
        stage = document.createElement('div');
        stage.className = 'nexdirectory-page-preloader-stage';
        overlay.appendChild(stage);
      }
      if (document.body && !document.body.contains(overlay)) {
        document.body.appendChild(overlay);
      }
      syncFromConfig();
      return overlay;
    };

    const show = function (reason, contextUrl) {
      syncFromConfig();
      if (!canShowForReason(reason, contextUrl)) {
        return;
      }
      const node = ensureOverlay();
      if (!node) {
        return;
      }
      clearHideTimer();
      visibleSince = Date.now();
      markSeen();
      node.dataset.reason = String(reason || 'manual').trim();
      node.classList.add('is-visible');
      document.documentElement.classList.add('nexdirectory-preloader-active');
      if (document.body) {
        document.body.classList.add('nexdirectory-preloader-active');
      }
    };

    const hide = function () {
      if (!overlay || !overlay.classList.contains('is-visible')) {
        return;
      }
      clearHideTimer();
      const elapsed = visibleSince > 0 ? (Date.now() - visibleSince) : 0;
      const delay = Math.max(0, minVisibleMs - elapsed);
      hideTimer = window.setTimeout(function () {
        overlay.classList.remove('is-visible');
        document.documentElement.classList.remove('nexdirectory-preloader-active');
        if (document.body) {
          document.body.classList.remove('nexdirectory-preloader-active');
        }
        hideTimer = 0;
      }, delay);
    };

    pagePreloaderController = {
      show: show,
      hide: hide,
      syncFromConfig: syncFromConfig,
      setVisible: function (active, reason, contextUrl) {
        if (active) {
          show(reason, contextUrl);
        } else {
          hide();
        }
      }
    };
    window.__nexdirectoryPagePreloader = pagePreloaderController;

    syncFromConfig();
    if (canShowForReason('initial')) {
      show('initial');
      const dismissInitial = function () {
        hide();
      };
      if (document.readyState === 'complete') {
        window.setTimeout(dismissInitial, 0);
      } else {
        window.addEventListener('load', dismissInitial, { once: true });
      }
      window.setTimeout(dismissInitial, Math.max(minVisibleMs + fadeMs + 300, 2400));
    }
  }

  function buildMapTileLayerConfig() {
    const providerRaw = String(getConfig('mapProvider', 'openstreetmap') || 'openstreetmap').toLowerCase();
    const styleRaw = String(getConfig('mapStyle', 'osm-standard') || 'osm-standard').toLowerCase();
    const provider = ['openstreetmap', 'mapbox', 'google'].indexOf(providerRaw) >= 0 ? providerRaw : 'openstreetmap';
    const style = styleRaw || 'osm-standard';
    const googleKey = String(getConfig('googleMapsApiKey', '') || '').trim();
    const mapboxToken = String(getConfig('mapboxAccessToken', '') || '').trim();
    const mapboxStyleId = String(getConfig('mapboxStyleId', 'mapbox/streets-v12') || 'mapbox/streets-v12').trim().replace(/^\/+|\/+$/g, '');
    const customUrl = String(getConfig('mapCustomTileUrl', '') || '').trim();
    const customAttribution = String(getConfig('mapCustomTileAttribution', '') || '').trim() || '&copy; OpenStreetMap contributors';
    const subdomainsRaw = String(getConfig('mapCustomTileSubdomains', 'a,b,c') || 'a,b,c').trim();
    const customSubdomains = subdomainsRaw ? subdomainsRaw.split(',').map(function (item) { return String(item || '').trim(); }).filter(Boolean) : ['a', 'b', 'c'];
    const osmFallback = {
      url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
      options: {
        attribution: '&copy; OpenStreetMap contributors',
        className: 'nexdirectory-map-tiles'
      }
    };

    if (style === 'custom' && customUrl) {
      return {
        url: customUrl,
        options: {
          attribution: customAttribution,
          subdomains: customSubdomains,
          className: 'nexdirectory-map-tiles'
        }
      };
    }

    if (provider === 'mapbox' || style.indexOf('mapbox-') === 0) {
      if (mapboxToken !== '') {
        const styleByPreset = {
          'mapbox-streets': 'mapbox/streets-v12',
          'mapbox-outdoors': 'mapbox/outdoors-v12',
          'mapbox-satellite': 'mapbox/satellite-streets-v12'
        };
        const stylePath = styleByPreset[style] || mapboxStyleId || 'mapbox/streets-v12';
        return {
          url: 'https://api.mapbox.com/styles/v1/' + stylePath + '/tiles/{z}/{x}/{y}?access_token=' + encodeURIComponent(mapboxToken),
          options: {
            attribution: '&copy; <a href="https://www.mapbox.com/about/maps/">Mapbox</a> &copy; OpenStreetMap contributors',
            tileSize: 512,
            zoomOffset: -1,
            className: 'nexdirectory-map-tiles'
          }
        };
      }
      return osmFallback;
    }

    if (provider === 'google' || style.indexOf('google-') === 0) {
      const layerCode = style === 'google-satellite' ? 's' : 'm';
      const keyPart = googleKey ? '&key=' + encodeURIComponent(googleKey) : '';
      return {
        url: 'https://{s}.google.com/vt/lyrs=' + layerCode + '&x={x}&y={y}&z={z}' + keyPart,
        options: {
          attribution: '&copy; Google',
          subdomains: ['mt0', 'mt1', 'mt2', 'mt3'],
          maxZoom: 20,
          className: 'nexdirectory-map-tiles'
        }
      };
    }

    const skinMap = {
      'osm-standard': {
        url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
        attribution: '&copy; OpenStreetMap contributors'
      },
      'osm-hot': {
        url: 'https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png',
        attribution: '&copy; OpenStreetMap contributors, HOT'
      },
      'carto-light': {
        url: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',
        attribution: '&copy; OpenStreetMap contributors, &copy; CARTO'
      },
      'carto-dark': {
        url: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',
        attribution: '&copy; OpenStreetMap contributors, &copy; CARTO'
      },
      'topo': {
        url: 'https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png',
        attribution: '&copy; OpenStreetMap contributors, SRTM'
      }
    };
    const skin = skinMap[style] || skinMap['osm-standard'];
    return {
      url: skin.url,
      options: {
        attribution: skin.attribution,
        className: 'nexdirectory-map-tiles'
      }
    };
  }

  function getMapFocusPreset() {
    const focusRaw = String(getConfig('mapFocusRegion', 'global') || 'global').trim().toLowerCase();
    const focusPresets = {
      'uk': {
        center: [54.3, -3.2],
        zoom: 5,
        bounds: [[49.8, -11.5], [61.3, 2.8]]
      },
      'europe': {
        center: [53.5, 14.5],
        zoom: 4,
        bounds: [[34.0, -25.0], [72.5, 45.0]]
      },
      'north-america': {
        center: [46.0, -98.0],
        zoom: 3,
        bounds: [[7.0, -168.0], [83.0, -52.0]]
      },
      'america': {
        center: [46.0, -98.0],
        zoom: 3,
        bounds: [[7.0, -168.0], [83.0, -52.0]]
      },
      'americas': {
        center: [16.0, -90.0],
        zoom: 2,
        bounds: [[-56.0, -170.0], [83.0, -30.0]]
      },
      'south-america': {
        center: [-15.0, -60.0],
        zoom: 3,
        bounds: [[-57.0, -82.0], [14.0, -34.0]]
      },
      'africa': {
        center: [3.0, 20.0],
        zoom: 3,
        bounds: [[-36.0, -19.0], [38.0, 53.0]]
      },
      'middle-east': {
        center: [28.0, 45.0],
        zoom: 4,
        bounds: [[12.0, 25.0], [42.0, 64.0]]
      },
      'asia': {
        center: [35.0, 95.0],
        zoom: 2,
        bounds: [[-10.0, 25.0], [81.0, 180.0]]
      },
      'oceania': {
        center: [-24.0, 145.0],
        zoom: 3,
        bounds: [[-50.0, 108.0], [8.0, 180.0]]
      }
    };

    if (!Object.prototype.hasOwnProperty.call(focusPresets, focusRaw)) {
      return null;
    }
    return Object.assign({ key: focusRaw }, focusPresets[focusRaw]);
  }

  function escapeHtml(value) {
    return String(value || '').replace(/[&<>'\"]/g, function (char) {
      if (char === '&') return '&amp;';
      if (char === '<') return '&lt;';
      if (char === '>') return '&gt;';
      if (char === '"') return '&quot;';
      return '&#39;';
    });
  }

  function normalizePathname(input) {
    const raw = String(input || '').trim();
    let pathname = '/';
    if (raw) {
      try {
        pathname = String(new URL(raw, window.location.origin).pathname || '/');
      } catch (err) {
        pathname = raw;
      }
    }
    if (pathname.charAt(0) !== '/') {
      pathname = '/' + pathname;
    }
    pathname = pathname.replace(/\/{2,}/g, '/');
    if (pathname.length > 1) {
      pathname = pathname.replace(/\/+$/g, '');
    }
    return pathname || '/';
  }

  function appendSafeToastHtml(target, html) {
    const template = document.createElement('template');
    template.innerHTML = String(html || '');
    const walk = document.createTreeWalker(template.content, NodeFilter.SHOW_ELEMENT, null);
    const nodes = [];
    while (walk.nextNode()) {
      nodes.push(walk.currentNode);
    }
    nodes.forEach(function (node) {
      const tagName = String(node.tagName || '').toLowerCase();
      if (tagName !== 'a') {
        node.replaceWith(document.createTextNode(node.textContent || ''));
        return;
      }
      const href = String(node.getAttribute('href') || '').trim();
      let safeHref = '';
      try {
        const parsed = new URL(href, window.location.origin);
        if (parsed.protocol === 'http:' || parsed.protocol === 'https:' || parsed.protocol === 'mailto:') {
          safeHref = parsed.href;
        }
      } catch (err) {
        safeHref = '';
      }
      if (!safeHref) {
        node.replaceWith(document.createTextNode(node.textContent || ''));
        return;
      }
      Array.prototype.slice.call(node.attributes || []).forEach(function (attribute) {
        node.removeAttribute(attribute.name);
      });
      node.setAttribute('href', safeHref);
      node.setAttribute('target', '_blank');
      node.setAttribute('rel', 'noopener noreferrer nofollow ugc');
    });
    target.replaceChildren(template.content);
  }

  function syncEditors() {
    if (window.tinymce && typeof window.tinymce.triggerSave === 'function') {
      window.tinymce.triggerSave();
    }
  }

  function showToast(message, type, options) {
    const toastType = type || 'info';
    const text = String(message || '').trim();
    if (!text) {
      return;
    }
    const ensureToastStyles = function () {
      if (document.getElementById('nexdirectory-toast-inline-style')) {
        return;
      }
      const style = document.createElement('style');
      style.id = 'nexdirectory-toast-inline-style';
      style.textContent = [
        '#nexdirectory-toast-root{position:fixed;right:max(14px,env(safe-area-inset-right));bottom:max(14px,env(safe-area-inset-bottom));z-index:2147483647!important;display:grid;gap:10px;justify-items:end;pointer-events:none;}',
        '#nexdirectory-toast-root .nexdirectory-toast{opacity:0;transform:translateY(12px);transition:opacity .28s ease,transform .28s ease;pointer-events:auto;width:min(420px,calc(100vw - max(28px,env(safe-area-inset-left) + env(safe-area-inset-right) + 28px)));margin:0;border-radius:14px;padding:12px 13px;border:1px solid rgba(255,255,255,.14);background:linear-gradient(145deg,#0f172a,#1f2937);color:#fff;box-shadow:0 16px 34px rgba(15,23,42,.34);font-family:var(--nexdirectory-font,Inter,system-ui,-apple-system,sans-serif);line-height:1.4;}',
        '#nexdirectory-toast-root .nexdirectory-toast:not(.nexdirectory-toast-hide){opacity:1;transform:translateY(0);}',
        '#nexdirectory-toast-root .nexdirectory-toast.nexdirectory-toast-hide{opacity:0;transform:translateY(10px);}',
        '#nexdirectory-toast-root .nexdirectory-toast.nexdirectory-toast-success{background:linear-gradient(145deg,#14532d,#166534);}',
        '#nexdirectory-toast-root .nexdirectory-toast.nexdirectory-toast-error{background:linear-gradient(145deg,#7f1d1d,#991b1b);}',
        '#nexdirectory-toast-root .nexdirectory-toast.nexdirectory-toast-warning{background:linear-gradient(145deg,#92400e,#b45309);}',
        '#nexdirectory-toast-root .nexdirectory-toast.nexdirectory-toast-info{background:linear-gradient(145deg,#1e3a8a,#1d4ed8);}',
        '#nexdirectory-toast-root .nexdirectory-toast-head{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:3px;}',
        '#nexdirectory-toast-root .nexdirectory-toast-title{font-family:var(--nexdirectory-font-display,Inter,sans-serif);font-size:.8rem;font-weight:700;letter-spacing:.08em;text-transform:uppercase;}',
        '#nexdirectory-toast-root .nexdirectory-toast-subtitle{font-size:.74rem;font-weight:500;letter-spacing:.03em;opacity:.87;margin-bottom:6px;}',
        '#nexdirectory-toast-root .nexdirectory-toast-message{font-size:.94rem;font-weight:500;line-height:1.45;color:#f8fafc;}',
        '#nexdirectory-toast-root .nexdirectory-toast-message a{color:#fff;text-decoration:underline;text-underline-offset:2px;font-weight:700;}',
        '#nexdirectory-toast-root .nexdirectory-toast-message a:hover{opacity:.9;}',
        '#nexdirectory-toast-root .nexdirectory-toast-close{border:1px solid rgba(255,255,255,.25);border-radius:999px;width:24px;height:24px;padding:0;background:rgba(255,255,255,.12);color:#fff;font-size:18px;line-height:1;cursor:pointer;}',
        '#nexdirectory-toast-root .nexdirectory-toast-close:hover{background:rgba(255,255,255,.2);}',
        '#nexdirectory-toast-root .nexdirectory-toast.nexdirectory-toast-imported-wide{position:fixed!important;left:max(5vw,14px)!important;right:max(5vw,14px)!important;bottom:max(14px,env(safe-area-inset-bottom))!important;width:auto!important;max-width:none!important;margin:0 auto!important;}',
        '.nexdirectory-notice.nexdirectory-toasted{display:none!important;}',
        '@media (max-width:640px){#nexdirectory-toast-root{left:max(12px,env(safe-area-inset-left));right:max(12px,env(safe-area-inset-right));bottom:max(12px,env(safe-area-inset-bottom));justify-items:stretch;}#nexdirectory-toast-root .nexdirectory-toast{width:100%;max-width:100%;padding:12px;border-radius:13px;}#nexdirectory-toast-root .nexdirectory-toast-title{font-size:.78rem;}#nexdirectory-toast-root .nexdirectory-toast-message{font-size:.92rem;line-height:1.42;}}'
      ].join('');
      document.head.appendChild(style);
    };
    ensureToastStyles();
    const config = options && typeof options === 'object' ? options : {};
    const defaultTitle = (function () {
      if (toastType === 'success') return 'Success';
      if (toastType === 'error') return 'Action Needed';
      if (toastType === 'warning') return 'Heads Up';
      return 'Notice';
    })();
    const defaultSubtitle = (function () {
      if (toastType === 'success') return 'Your request has been completed.';
      if (toastType === 'error') return 'Please review the details below.';
      if (toastType === 'warning') return 'Please check this update.';
      return 'Platform update';
    })();
    const titleText = String(config.title || defaultTitle).trim();
    const subtitleText = String(config.subtitle || defaultSubtitle).trim();
    const duration = Math.max(3500, parseInt(config.duration, 10) || (toastType === 'error' ? 9800 : 7600));
    const allowHtml = config.allowHtml === true;
    const extraClassName = String(config.className || '').trim();
    const onClose = typeof config.onClose === 'function' ? config.onClose : null;

    let root = document.getElementById('nexdirectory-toast-root');
    if (!root) {
      root = document.createElement('div');
      root.id = 'nexdirectory-toast-root';
      document.body.appendChild(root);
    }

    const toast = document.createElement('div');
    toast.className = 'nexdirectory-toast nexdirectory-toast-' + toastType;
    if (extraClassName) {
      extraClassName.split(/\s+/).forEach(function (className) {
        if (/^[a-z0-9_-]+$/i.test(className)) {
          toast.classList.add(className);
        }
      });
    }
    toast.setAttribute('role', 'status');
    toast.setAttribute('aria-live', toastType === 'error' ? 'assertive' : 'polite');

    const head = document.createElement('div');
    head.className = 'nexdirectory-toast-head';
    if (titleText) {
      const titleNode = document.createElement('strong');
      titleNode.className = 'nexdirectory-toast-title';
      titleNode.textContent = titleText;
      head.appendChild(titleNode);
    }
    const closeBtn = document.createElement('button');
    closeBtn.type = 'button';
    closeBtn.className = 'nexdirectory-toast-close';
    closeBtn.setAttribute('aria-label', 'Dismiss notification');
    closeBtn.textContent = '×';
    head.appendChild(closeBtn);
    toast.appendChild(head);

    if (subtitleText) {
      const subtitleNode = document.createElement('div');
      subtitleNode.className = 'nexdirectory-toast-subtitle';
      subtitleNode.textContent = subtitleText;
      toast.appendChild(subtitleNode);
    }

    const messageNode = document.createElement('div');
    messageNode.className = 'nexdirectory-toast-message';
    if (allowHtml) {
      appendSafeToastHtml(messageNode, text);
    } else {
      messageNode.textContent = text;
    }
    toast.appendChild(messageNode);

    root.appendChild(toast);

    let isDismissed = false;
    const dismiss = function () {
      if (isDismissed) {
        return;
      }
      isDismissed = true;
      toast.classList.add('nexdirectory-toast-hide');
      if (onClose) {
        try {
          onClose();
        } catch (err) {
          // Ignore toast callback failures.
        }
      }
      window.setTimeout(function () {
        if (toast.parentNode) {
          toast.parentNode.removeChild(toast);
        }
      }, 300);
    };
    closeBtn.addEventListener('click', dismiss);

    window.setTimeout(dismiss, duration);
  }

  window.nexdirectoryShowToast = showToast;

  let spaRouterBound = false;
  let spaRouteSet = null;
  let spaSelector = '[data-nexdirectory-spa-main="1"]';
  let spaActiveController = null;
  let spaRequestSeq = 0;
  let spaFeedbackBootstrapped = false;
  const spaFeedbackStorageKey = 'nexdirectorySpaFeedbackStatus';
  let lastSpaFeedbackSignature = '';
  let lastSpaFeedbackAt = 0;

  function isSpaFeedbackBadgeEnabled() {
    return toBool(getConfig('spaFeedbackBadge', false), false);
  }

  function getSpaFeedbackNode() {
    const existing = document.getElementById('nexdirectory-spa-feedback');
    if (existing && existing.parentNode) {
      existing.parentNode.removeChild(existing);
    }
    return null;
  }

  function setSpaFeedbackStatus(message, tone, detail, title) {
    if (!isSpaFeedbackBadgeEnabled()) {
      return;
    }
    getSpaFeedbackNode();

    const toneRaw = String(tone || '').toLowerCase();
    const safeTone = ['ok', 'warn', 'error'].indexOf(toneRaw) >= 0 ? toneRaw : 'warn';

    const titleText = String(title || 'SPA Monitor').trim() || 'SPA Monitor';
    const messageText = String(message || '').trim() || 'Status update';
    const detailText = String(detail || '').trim();
    const signature = safeTone + '|' + titleText + '|' + messageText + '|' + detailText;
    const now = Date.now();
    if (signature === lastSpaFeedbackSignature && (now - lastSpaFeedbackAt) < 1800) {
      return;
    }
    lastSpaFeedbackSignature = signature;
    lastSpaFeedbackAt = now;

    const toastTypeMap = { ok: 'success', warn: 'warning', error: 'error' };
    showToast(messageText, {
      type: toastTypeMap[safeTone] || 'info',
      title: titleText,
      subtitle: detailText,
      duration: safeTone === 'error' ? 5200 : 4200
    });
  }

  function getSessionStorageSafe() {
    try {
      return window.sessionStorage || null;
    } catch (err) {
      return null;
    }
  }

  function queueSpaFeedbackForNextLoad(payload) {
    const storage = getSessionStorageSafe();
    if (!isSpaFeedbackBadgeEnabled() || !storage) {
      return;
    }
    const data = payload && typeof payload === 'object' ? payload : {};
    const toneRaw = String(data.tone || '').toLowerCase();
    const safeTone = ['ok', 'warn', 'error'].indexOf(toneRaw) >= 0 ? toneRaw : 'warn';
    const record = {
      message: String(data.message || 'Full page load').trim() || 'Full page load',
      tone: safeTone,
      detail: String(data.detail || '').trim(),
      title: String(data.title || 'SPA Monitor').trim() || 'SPA Monitor',
      target: String(data.target || '').trim(),
      queuedAt: Date.now()
    };
    try {
      storage.setItem(spaFeedbackStorageKey, JSON.stringify(record));
    } catch (err) {
      // ignore storage failures
    }
  }

  function consumeQueuedSpaFeedback() {
    const storage = getSessionStorageSafe();
    if (!storage) {
      return null;
    }
    let raw = '';
    try {
      raw = String(storage.getItem(spaFeedbackStorageKey) || '');
      storage.removeItem(spaFeedbackStorageKey);
    } catch (err) {
      return null;
    }
    if (!raw) {
      return null;
    }
    try {
      const parsed = JSON.parse(raw);
      if (!parsed || typeof parsed !== 'object') {
        return null;
      }
      return parsed;
    } catch (err) {
      return null;
    }
  }

  function initSpaFeedbackBadge() {
    const enabled = isSpaFeedbackBadgeEnabled();
    const existing = document.getElementById('nexdirectory-spa-feedback');
    if (!enabled) {
      if (existing && existing.parentNode) {
        existing.parentNode.removeChild(existing);
      }
      spaFeedbackBootstrapped = false;
      try {
        const storage = getSessionStorageSafe();
        if (storage) {
          storage.removeItem(spaFeedbackStorageKey);
        }
      } catch (err) {
        // ignore storage failures
      }
      return;
    }

    getSpaFeedbackNode();
    if (spaFeedbackBootstrapped) {
      return;
    }
    spaFeedbackBootstrapped = true;

    const queued = consumeQueuedSpaFeedback();
    if (queued) {
      setSpaFeedbackStatus(queued.message || 'Full page load', queued.tone || 'warn', queued.detail || '', queued.title || 'SPA Monitor');
      return;
    }

    if (!toBool(getConfig('spaEnabled', false), false)) {
      setSpaFeedbackStatus('SPA is disabled', 'warn', 'Using full page navigation mode');
      return;
    }

    setSpaFeedbackStatus('SPA monitor ready', 'ok', 'Waiting for route navigation events');
  }

  function restPost(endpoint, payload) {
    const url = String(endpoint || '').trim();
    if (!url) {
      return Promise.reject(new Error(getMessage('restActionFailed', 'Request failed. Please try again.')));
    }
    return fetch(url, {
      method: 'POST',
      credentials: 'same-origin',
      headers: {
        'Content-Type': 'application/json',
        'X-WP-Nonce': String(getConfig('restNonce', '') || '')
      },
      body: JSON.stringify(payload || {})
    })
      .then(async function (res) {
        let data = {};
        try {
          data = await res.json();
        } catch (err) {
          data = {};
        }
        if (!res.ok) {
          throw new Error(String((data && data.message) || getMessage('restActionFailed', 'Request failed. Please try again.')));
        }
        return data || {};
      });
  }

  function initEventAnalyticsTracking() {
    const endpoint = String(getConfig('analyticsTrackUrl', '') || '').trim();
    if (!endpoint) {
      return;
    }
    const sentKeys = new Set();
    const sendMetric = function (eventId, metric, source, dedupeSuffix) {
      const normalizedEventId = parseInt(String(eventId || '0'), 10);
      const normalizedMetric = String(metric || '').trim();
      const normalizedSource = String(source || 'single').trim();
      if (!Number.isFinite(normalizedEventId) || normalizedEventId <= 0 || !normalizedMetric) {
        return;
      }

      const dedupeKey = [normalizedMetric, normalizedEventId, normalizedSource, String(dedupeSuffix || '')].join('|');
      if (sentKeys.has(dedupeKey)) {
        return;
      }
      sentKeys.add(dedupeKey);

      fetch(endpoint, {
        method: 'POST',
        credentials: 'same-origin',
        keepalive: true,
        headers: {
          'Content-Type': 'application/json',
          'X-WP-Nonce': String(getConfig('restNonce', '') || '')
        },
        body: JSON.stringify({
          event_id: normalizedEventId,
          metric: normalizedMetric,
          source: normalizedSource
        })
      }).catch(function () {
        // Analytics must never interrupt the user journey.
      });
    };

    $('[data-nexdirectory-track-view="1"][data-nexdirectory-event-id]').each(function () {
      sendMetric(
        this.getAttribute('data-nexdirectory-event-id'),
        'listing_view',
        'single',
        'page'
      );
    });

    $(document)
      .off('click.nexdirectoryAnalyticsTrack', '[data-nexdirectory-event-id] a')
      .on('click.nexdirectoryAnalyticsTrack', '[data-nexdirectory-event-id] a', function () {
        const $anchor = $(this);
        const $eventRoot = $anchor.closest('[data-nexdirectory-event-id]');
        if (!$eventRoot.length) {
          return;
        }

        const href = String($anchor.attr('href') || '').trim();
        let metric = String($anchor.attr('data-nexdirectory-analytics-metric') || '').trim();
        if (!metric && /^tel:/i.test(href)) {
          metric = 'click_phone';
        }
        if (!metric && /(wa\.me|api\.whatsapp\.com|whatsapp)/i.test(href)) {
          metric = 'click_whatsapp';
        }
        if (!metric) {
          return;
        }

        sendMetric(
          $anchor.attr('data-nexdirectory-track-event-id') || $eventRoot.attr('data-nexdirectory-event-id'),
          metric,
          $anchor.attr('data-nexdirectory-track-source') || 'single',
          href
        );
      });
  }

  function uploadAssetFile(file, kind, target) {
    return new Promise(function (resolve, reject) {
      if (!file) {
        reject(new Error('missing_file'));
        return;
      }
      const endpoint = String(getConfig('uploadEndpoint', '/wp-admin/admin-post.php') || '');
      const nonce = String(getConfig('uploadNonce', '') || '').trim();
      if (!endpoint || !nonce) {
        reject(new Error('uploader_unavailable'));
        return;
      }

      const fd = new FormData();
      fd.append('action', 'nexdirectory_upload_image');
      fd.append('nexdirectory_upload_nonce', nonce);
      fd.append('nexdirectory_upload_kind', String(kind || 'image'));
      fd.append('nexdirectory_upload_target', String(target || 'gallery'));
      fd.append('file', file, file.name || 'upload');

      fetch(endpoint, {
        method: 'POST',
        body: fd,
        credentials: 'same-origin'
      })
        .then(async function (res) {
          let payload = null;
          try {
            payload = await res.json();
          } catch (err) {
            payload = null;
          }
          if (!res.ok || !payload || payload.success !== true || !payload.data || !payload.data.url) {
            throw new Error((payload && payload.data && payload.data.message) ? payload.data.message : 'upload_failed');
          }
          resolve(payload.data);
        })
        .catch(function (error) {
          reject(error || new Error('upload_failed'));
        });
    });
  }

  window.nexdirectorySubmitForm = function (initialState) {
    const initial = initialState && typeof initialState === 'object' ? initialState : {};
    const initialGallery = Array.isArray(initial.galleryImages) ? initial.galleryImages.map((row) => String(row || '').trim()).filter((row) => row !== '') : [];
    const initialVideos = Array.isArray(initial.videoEmbeds) ? initial.videoEmbeds.map((row) => String(row || '').trim()).filter((row) => row !== '') : [];
    const normalizeSession = (session) => ({
      title: String((session && session.title) || ''),
      start: String((session && session.start) || ''),
      end: String((session && session.end) || ''),
      location: String((session && session.location) || ''),
      speaker: String((session && session.speaker) || ''),
      day: String((session && session.day) || ''),
      notes: String((session && session.notes) || ''),
      attachments: Array.isArray(session && session.attachments)
        ? session.attachments.map((item) => String(item || '').trim()).filter((item) => item !== '').join('\n')
        : String((session && session.attachments) || ''),
      session_key: String((session && (session.session_key || session.id)) || '')
    });
    const initialSessions = Array.isArray(initial.sessions) ? initial.sessions.map(normalizeSession) : [];
    const initialKeySpeakers = Array.isArray(initial.keySpeakers) ? initial.keySpeakers.map((speaker) => ({
      name: String((speaker && speaker.name) || ''),
      profession: String((speaker && (speaker.profession || speaker.professional)) || ''),
      subject: String((speaker && speaker.subject) || ''),
      image: String((speaker && (speaker.image || speaker.image_url)) || ''),
      details: String((speaker && speaker.details) || '')
    })) : [];
    return {
      coverImage: String(initial.coverImage || ''),
      galleryImages: initialGallery,
      galleryTextarea: String(initialGallery.join('\n') || ''),
      videoUrlsTextarea: String(initial.videoUrlsTextarea || ''),
      videoEmbeds: initialVideos.length ? initialVideos : [''],
      sessions: initialSessions,
      keySpeakers: initialKeySpeakers,
      isRecurring: String(initial.isRecurring || '0') === '1' ? '1' : '0',
      recurrenceUntil: String(initial.recurrenceUntil || ''),
      streamUrl: String(initial.streamUrl || ''),
      modeOption: String(initial.modeOption || 'in-person'),
      youtubeIdOne: '',
      youtubeIdTwo: '',
      coverMaxBytes: parseInt(getConfig('uploadCoverMaxBytes', 512000), 10) || 512000,
      galleryMaxBytes: parseInt(getConfig('uploadGalleryMaxBytes', 2048000), 10) || 2048000,
      galleryMaxFiles: parseInt(getConfig('uploadGalleryMaxFiles', 10), 10) || 10,
      fileMaxBytes: parseInt(getConfig('uploadFileMaxBytes', 2097152), 10) || 2097152,
      titleValue: String(initial.titleValue || ''),
      excerptValue: String(initial.excerptValue || ''),
      metaTitleValue: String(initial.metaTitleValue || ''),
      metaDescriptionValue: String(initial.metaDescriptionValue || ''),
      serpUrl: String(initial.serpUrl || ''),
      excerptError: '',
      excerptMaxChars: parseInt(getConfig('excerptMaxChars', 220), 10) || 220,
      startValue: String(initial.startValue || ''),
      startTimeValue: String(initial.startTimeValue || ''),
      endValue: String(initial.endValue || ''),
      endTimeValue: String(initial.endTimeValue || ''),
      titleError: '',
      dateError: '',
      dateWarning: '',
      timeWarning: '',
      timezoneHint: '',
      cityValue: String(initial.cityValue || ''),
      countryValue: String(initial.countryValue || ''),
      latValue: String(initial.latValue || ''),
      lngValue: String(initial.lngValue || ''),
      autosavePostId: '',
      autosaveMessage: 'Autosave ready',
      autosaveQueuedTimer: null,
      autosaveBusy: false,
      autosaveEnabled: true,
      autosaveIntervalMs: 60000,
      autosaveLastRunAt: 0,
      autosaveLastSignature: '',
      autosavePendingAfterBusy: false,
      pastStartConfirmed: false,
      formStateStorageKey: 'nexdirectory_submit_form_state_v1',
      formStateTtlMs: 12 * 60 * 60 * 1000,
      init() {
        const form = document.getElementById('nexdirectory-submit-form');
        const isEditing = !!(form && String(form.getAttribute('data-nexdirectory-editing') || '') === '1');
        let forceFreshSubmit = false;
        try {
          const initialParams = new URLSearchParams(String(window.location.search || ''));
          forceFreshSubmit = String(initialParams.get('nexdirectory_new') || '') === '1';
        } catch (e) {
          forceFreshSubmit = false;
        }
        if (forceFreshSubmit) {
          try {
            window.localStorage.removeItem('nexdirectory_submit_draft');
          } catch (e) {}
          try {
            const cleanedUrl = new URL(window.location.href);
            cleanedUrl.searchParams.delete('nexdirectory_new');
            window.history.replaceState({}, '', cleanedUrl.toString());
          } catch (e) {}
        }
        if (form) {
          const readSeededValue = (field, fallback) => {
            const fallbackValue = String(fallback || '');
            if (!field) {
              return fallbackValue;
            }
            const liveValue = String(field.value || '');
            if (liveValue.trim() !== '') {
              return liveValue;
            }
            const defaultValue = String(field.defaultValue || field.getAttribute('value') || '');
            if (defaultValue.trim() !== '') {
              return defaultValue;
            }
            return fallbackValue;
          };
          const titleInput = form.querySelector('[name="post_title"]');
          const excerptInput = form.querySelector('[name="post_excerpt"]');
          const metaTitleInput = form.querySelector('[name="nexdirectory_meta_title"]');
          const metaDescriptionInput = form.querySelector('[name="nexdirectory_meta_description"]');
          const startInput = form.querySelector('[name="nexdirectory_start_date"]') || form.querySelector('[name="nexdirectory_start"]');
          const endInput = form.querySelector('[name="nexdirectory_end_date"]') || form.querySelector('[name="nexdirectory_end"]');
          const startTimeInput = form.querySelector('[name="nexdirectory_start_time"]');
          const endTimeInput = form.querySelector('[name="nexdirectory_end_time"]');
          const cityInput = form.querySelector('[name="nexdirectory_city"]');
          const countryInput = form.querySelector('[name="nexdirectory_country"]');
          const latInput = form.querySelector('[name="nexdirectory_lat"]');
          const lngInput = form.querySelector('[name="nexdirectory_lng"]');
          const streamInput = form.querySelector('[name="nexdirectory_event_stream_url"]');
          const videoTextarea = form.querySelector('[name="nexdirectory_video_urls"]');
          const coverInput = form.querySelector('[name="nexdirectory_cover_image_url"]');
          const galleryTextarea = form.querySelector('[name="nexdirectory_gallery_urls"]');
          const recurringChecked = form.querySelector('input[name="nexdirectory_is_recurring"]:checked');
          const modeChecked = form.querySelector('input[name="nexdirectory_event_mode_option"]:checked');
          this.titleValue = readSeededValue(titleInput, this.titleValue);
          this.excerptValue = readSeededValue(excerptInput, this.excerptValue);
          this.metaTitleValue = readSeededValue(metaTitleInput, this.metaTitleValue);
          this.metaDescriptionValue = readSeededValue(metaDescriptionInput, this.metaDescriptionValue);
          this.startValue = readSeededValue(startInput, this.startValue);
          this.endValue = readSeededValue(endInput, this.endValue);
          this.startTimeValue = readSeededValue(startTimeInput, this.startTimeValue);
          this.endTimeValue = readSeededValue(endTimeInput, this.endTimeValue);
          this.cityValue = readSeededValue(cityInput, this.cityValue);
          this.countryValue = readSeededValue(countryInput, this.countryValue);
          this.latValue = readSeededValue(latInput, this.latValue);
          this.lngValue = readSeededValue(lngInput, this.lngValue);
          this.streamUrl = readSeededValue(streamInput, this.streamUrl);
          this.coverImage = readSeededValue(coverInput, this.coverImage);
          const galleryTextValue = readSeededValue(galleryTextarea, this.galleryTextarea || '');
          if (galleryTextarea && String(galleryTextValue).trim() !== '') {
            const galleryRows = String(galleryTextValue).split(/\r?\n/).map((row) => String(row || '').trim()).filter((row) => row !== '');
            if (galleryRows.length) {
              this.galleryImages = Array.from(new Set(galleryRows));
            }
          }
          if (recurringChecked) {
            this.isRecurring = String(recurringChecked.value || '0');
          }
          if (modeChecked) {
            this.modeOption = String(modeChecked.value || 'in-person');
          }
          if (form.getAttribute('data-nexdirectory-field-highlight-bound') !== '1') {
            form.setAttribute('data-nexdirectory-field-highlight-bound', '1');
            const clearFieldHighlight = (event) => {
              const target = event && event.target ? event.target : null;
              this.clearFieldErrorState(target);
            };
            form.addEventListener('input', clearFieldHighlight, true);
            form.addEventListener('change', clearFieldHighlight, true);
          }
          if (startInput) {
            startInput.addEventListener('change', () => {
              this.pastStartConfirmed = false;
              this.validateDates();
            });
            startInput.addEventListener('input', () => {
              this.pastStartConfirmed = false;
            });
          }
          if (endInput) {
            endInput.addEventListener('change', () => {
              this.validateDates();
            });
          }
          const videoTextValue = readSeededValue(videoTextarea, this.videoUrlsTextarea || '');
          if (videoTextarea && String(videoTextValue).trim() !== '') {
            const rows = String(videoTextValue).split(/\r?\n/).map((row) => String(row || '').trim()).filter((row) => row !== '');
            if (rows.length) {
              this.videoEmbeds = rows;
            }
          }
          const sessionsRaw = String(form.getAttribute('data-nexdirectory-initial-sessions') || '').trim();
          if (sessionsRaw !== '') {
            try {
              const parsedSessions = JSON.parse(sessionsRaw);
              if (Array.isArray(parsedSessions) && parsedSessions.length) {
                this.sessions = parsedSessions.map(normalizeSession);
                this.isRecurring = '1';
              }
            } catch (err) {
              // ignore malformed prefill
            }
          }
          const recurrenceSeed = String(form.getAttribute('data-nexdirectory-recurrence-until') || '').trim();
          if (recurrenceSeed) {
            this.recurrenceUntil = recurrenceSeed;
          }
          if (form.getAttribute('data-nexdirectory-past-date-check-bound') !== '1') {
            form.setAttribute('data-nexdirectory-past-date-check-bound', '1');
            form.addEventListener('submit', (event) => {
              this.persistFormState(form);
              if (this.hasBlockingErrors(form)) {
                if (event && typeof event.preventDefault === 'function') {
                  event.preventDefault();
                }
                if (event && typeof event.stopImmediatePropagation === 'function') {
                  event.stopImmediatePropagation();
                }
                showToast('Please fix the highlighted fields before submitting.', 'error');
                return false;
              }
              if (!this.validateRequiredLocation(form, event)) {
                return false;
              }
              this.validateDates();
              if (!this.confirmPastStartDate(event)) {
                return false;
              }
              return true;
            }, true);
          }
        }

        const saved = (!isEditing && !forceFreshSubmit) ? window.localStorage.getItem('nexdirectory_submit_draft') : null;
        if (saved) {
          try {
            const parsed = JSON.parse(saved);
            if (String((parsed && parsed.sourceEditId) || '0') !== '0') {
              throw new Error('stale-edit-draft');
            }
            if (parsed.coverImage) this.coverImage = parsed.coverImage;
            if (Array.isArray(parsed.galleryImages)) this.galleryImages = parsed.galleryImages;
            if (Array.isArray(parsed.sessions)) {
              this.sessions = parsed.sessions.map(normalizeSession);
            }
            if (Array.isArray(parsed.keySpeakers)) {
              this.keySpeakers = parsed.keySpeakers.map((speaker) => ({
                name: String((speaker && speaker.name) || ''),
                profession: String((speaker && (speaker.profession || speaker.professional)) || ''),
                subject: String((speaker && speaker.subject) || ''),
                image: String((speaker && (speaker.image || speaker.image_url)) || ''),
                details: String((speaker && speaker.details) || '')
              }));
            }
            if (parsed.isRecurring === '1') this.isRecurring = '1';
            if (parsed.streamUrl) this.streamUrl = String(parsed.streamUrl);
            if (parsed.excerptValue) this.excerptValue = String(parsed.excerptValue);
            if (parsed.metaTitleValue) this.metaTitleValue = String(parsed.metaTitleValue);
            if (parsed.metaDescriptionValue) this.metaDescriptionValue = String(parsed.metaDescriptionValue);
            if (parsed.cityValue) this.cityValue = String(parsed.cityValue);
            if (parsed.countryValue) this.countryValue = String(parsed.countryValue);
            if (parsed.latValue) this.latValue = String(parsed.latValue);
            if (parsed.lngValue) this.lngValue = String(parsed.lngValue);
            if (parsed.youtubeIdOne) this.youtubeIdOne = String(parsed.youtubeIdOne);
            if (parsed.youtubeIdTwo) this.youtubeIdTwo = String(parsed.youtubeIdTwo);
            if (Array.isArray(parsed.videoEmbeds) && parsed.videoEmbeds.length) this.videoEmbeds = parsed.videoEmbeds;
            if (parsed.autosavePostId) this.autosavePostId = String(parsed.autosavePostId);
            this.syncGalleryTextarea();
            this.syncVideoUrls();
            this.syncRecurrenceMeta();
          } catch (e) {}
        }
        this.restoreFormState(form);

        this.autodetectTimezone();
        this.validateTitle();
        this.validateExcerpt();
        this.validateDates();
        if (this.modeOption !== 'video-stream') {
          this.streamUrl = '';
        }
        this.syncGalleryTextarea();
        this.syncVideoUrls();
        this.syncRecurrenceMeta();

        this.autosaveEnabled = toBool(getConfig('enableSubmitAutosave', true), true);
        if (this.autosaveEnabled) {
          const configuredAutosaveSeconds = parseInt(getConfig('autosaveInterval', 60), 10);
          const autosaveSeconds = Number.isFinite(configuredAutosaveSeconds)
            ? Math.max(15, Math.min(600, configuredAutosaveSeconds))
            : 60;
          this.autosaveIntervalMs = autosaveSeconds * 1000;
          this.bindAutosaveTriggers(form);
          this.autosaveMessage = 'Autosave active. Changes save after ' + String(autosaveSeconds) + ' seconds of inactivity.';
        } else {
          this.autosaveMessage = 'Autosave disabled by admin settings';
        }
      },
      bindAutosaveTriggers(form) {
        if (!form || form.getAttribute('data-nexdirectory-autosave-bound') === '1') {
          return;
        }
        form.setAttribute('data-nexdirectory-autosave-bound', '1');

        const shouldTrackField = (field) => {
          if (!field || typeof field !== 'object') {
            return false;
          }
          const tag = String(field.tagName || '').toLowerCase();
          if (['input', 'textarea', 'select'].indexOf(tag) === -1) {
            return false;
          }
          const type = String(field.type || '').toLowerCase();
          if (['hidden', 'submit', 'button', 'reset', 'file'].indexOf(type) >= 0) {
            return false;
          }
          return true;
        };

        form.addEventListener('focusout', (event) => {
          if (!this.autosaveEnabled || !shouldTrackField(event.target)) {
            return;
          }
          this.queueAutosave(420);
        }, true);

        form.addEventListener('input', (event) => {
          if (!this.autosaveEnabled || !shouldTrackField(event.target)) {
            return;
          }
          this.queueAutosave(260);
        }, true);

        form.addEventListener('change', (event) => {
          if (!this.autosaveEnabled || !shouldTrackField(event.target)) {
            return;
          }
          this.queueAutosave(320);
        }, true);

        if (window.tinymce && typeof window.tinymce.get === 'function') {
          const editor = window.tinymce.get('nexdirectory_post_content_editor');
          if (editor && editor.on) {
            editor.on('input keyup change', () => {
              this.queueAutosave(260);
            });
            editor.on('blur', () => {
              this.queueAutosave(320);
            });
          }
        }
      },
      queueAutosave(delayMs) {
        if (!this.autosaveEnabled) {
          return;
        }
        const debounceDelay = Number.isFinite(delayMs) ? Math.max(250, parseInt(delayMs, 10)) : 500;
        const now = Date.now();
        const throttleDelay = this.autosaveLastRunAt > 0
          ? Math.max(0, this.autosaveIntervalMs - (now - this.autosaveLastRunAt))
          : this.autosaveIntervalMs;
        const delay = Math.max(debounceDelay, throttleDelay);
        if (this.autosaveQueuedTimer) {
          window.clearTimeout(this.autosaveQueuedTimer);
        }
        this.autosaveQueuedTimer = window.setTimeout(() => {
          this.autosaveDraft();
        }, delay);
      },
      titleCounterText() {
        const len = String(this.titleValue || '').trim().length;
        return String(len) + ' chars';
      },
      excerptCounterText() {
        const chars = String(this.excerptValue || '').length;
        return String(chars) + ' / ' + String(this.excerptMaxChars) + ' chars';
      },
      seoTitleCount() {
        return String(this.metaTitleValue || '').trim().length;
      },
      seoDescriptionCount() {
        return String(this.metaDescriptionValue || '').trim().length;
      },
      seoTitleStatusText() {
        const count = this.seoTitleCount();
        if (count === 0) {
          return 'Optional';
        }
        if (count > 60) {
          return 'Too long';
        }
        if (count < 50) {
          return 'Add more detail';
        }
        return 'Ideal length';
      },
      seoTitleStatusClass() {
        const count = this.seoTitleCount();
        if (count === 0) {
          return 'text-gray-500';
        }
        if (count > 60) {
          return 'text-red-600';
        }
        if (count < 50) {
          return 'text-amber-600';
        }
        return 'text-green-600';
      },
      seoDescriptionStatusText() {
        const count = this.seoDescriptionCount();
        if (count === 0) {
          return 'Optional';
        }
        if (count > 158) {
          return 'Too long';
        }
        if (count < 120) {
          return 'Add more detail';
        }
        return 'Ideal length';
      },
      seoDescriptionStatusClass() {
        const count = this.seoDescriptionCount();
        if (count === 0) {
          return 'text-gray-500';
        }
        if (count > 158) {
          return 'text-red-600';
        }
        if (count < 120) {
          return 'text-amber-600';
        }
        return 'text-green-600';
      },
      stripHtml(value) {
        return String(value || '').replace(/<[^>]*>/g, ' ').replace(/\s+/g, ' ').trim();
      },
      truncateText(value, maxLength) {
        const text = String(value || '').trim();
        if (!text) {
          return '';
        }
        const limit = Math.max(1, parseInt(maxLength, 10) || 0);
        if (!limit || text.length <= limit) {
          return text;
        }
        return text.slice(0, Math.max(0, limit - 1)).trim() + '…';
      },
      readSerpDescriptionFallback() {
        const excerpt = String(this.excerptValue || '').trim();
        if (excerpt) {
          return excerpt;
        }
        const form = document.getElementById('nexdirectory-submit-form');
        if (!form) {
          return '';
        }
        const contentInput = form.querySelector('[name="post_content"]');
        if (contentInput && String(contentInput.value || '').trim() !== '') {
          return this.stripHtml(contentInput.value);
        }
        if (window.tinymce && typeof window.tinymce.get === 'function') {
          const editor = window.tinymce.get('nexdirectory_post_content_editor');
          if (editor && typeof editor.getContent === 'function') {
            const content = this.stripHtml(editor.getContent({ format: 'raw' }));
            if (content) {
              return content;
            }
          }
        }
        return '';
      },
      fillSeoFromContent() {
        const titleSeed = String(this.titleValue || '').trim();
        const descriptionSeed = this.readSerpDescriptionFallback();
        if (titleSeed) {
          this.metaTitleValue = this.truncateText(titleSeed, 60).replace(/…$/, '');
        }
        if (descriptionSeed) {
          this.metaDescriptionValue = this.truncateText(descriptionSeed, 158).replace(/…$/, '');
        }
        this.queueAutosave(180);
      },
      serpUrlPreview() {
        const seed = String(this.serpUrl || window.location.origin || '').trim();
        if (!seed) {
          return '';
        }
        return this.truncateText(seed.replace(/^https?:\/\//i, ''), 70);
      },
      serpTitlePreview() {
        const custom = String(this.metaTitleValue || '').trim();
        const fallback = String(this.titleValue || '').trim();
        const value = custom || fallback || 'Untitled Listing';
        return this.truncateText(value, 60);
      },
      serpDescriptionPreview() {
        const custom = String(this.metaDescriptionValue || '').trim();
        const fallback = this.readSerpDescriptionFallback();
        const value = custom || fallback || 'Add a concise summary to improve discovery in search results.';
        return this.truncateText(value, 158);
      },
      countWords(value) {
        const text = String(value || '').replace(/\s+/g, ' ').trim();
        if (!text) {
          return 0;
        }
        return text.split(' ').length;
      },
      validateExcerpt() {
        const chars = String(this.excerptValue || '').length;
        if (chars > this.excerptMaxChars) {
          this.excerptError = 'Short excerpt must be ' + String(this.excerptMaxChars) + ' characters or fewer.';
          return false;
        }
        this.excerptError = '';
        return true;
      },
      parseLocalDate(value, timeValue) {
        const dateText = String(value || '').trim();
        if (!dateText) {
          return null;
        }
        const timeText = String(timeValue || '').trim();
        const text = timeText ? (dateText + 'T' + timeText) : (dateText + 'T00:00');
        const parsed = new Date(text);
        if (!parsed || Number.isNaN(parsed.getTime())) {
          const fallback = new Date(dateText);
          if (!fallback || Number.isNaN(fallback.getTime())) {
            return null;
          }
          return fallback;
        }
        return parsed;
      },
      getMissingTimeWarning() {
        const startDate = String(this.startValue || '').trim();
        const startTime = String(this.startTimeValue || '').trim();
        const endDate = String(this.endValue || '').trim();
        const endTime = String(this.endTimeValue || '').trim();
        const messages = [];
        if (startDate && !startTime) {
          messages.push('No start time added. This will be shown as a date-only listing.');
        }
        if (endDate && !endTime) {
          messages.push('No end time added. End date will be shown without a time.');
        }
        return messages.join(' ');
      },
      validateTitle() {
        const len = String(this.titleValue || '').trim().length;
        if (len > 0 && len < 8) {
          this.titleError = 'Title should be at least 8 characters.';
          return false;
        }
        this.titleError = '';
        return true;
      },
      validateSeoFields() {
        const titleChars = this.seoTitleCount();
        const descChars = this.seoDescriptionCount();
        if (titleChars > 60 || descChars > 158) {
          return false;
        }
        return true;
      },
      validateDates() {
        const startDt = this.parseLocalDate(this.startValue, this.startTimeValue);
        const endDt = this.parseLocalDate(this.endValue, this.endTimeValue);
        this.dateWarning = '';
        this.timeWarning = this.getMissingTimeWarning();
        if (startDt && startDt.getTime() < (Date.now() - 60000)) {
          this.dateWarning = getMessage('pastDateWarning', 'Start date is in the past. You can still submit if this is intentional.');
        }
        if (startDt && endDt && endDt.getTime() < startDt.getTime()) {
          this.dateError = 'End date must be after start date.';
          return false;
        }
        if (
          startDt
          && endDt
          && endDt.getTime() === startDt.getTime()
          && String(this.startTimeValue || '').trim() !== ''
          && String(this.endTimeValue || '').trim() !== ''
        ) {
          this.dateError = 'End date must be after start date.';
          return false;
        }
        this.dateError = '';
        return true;
      },
      isStartDateInPast() {
        const startDt = this.parseLocalDate(this.startValue, this.startTimeValue);
        if (!startDt) {
          return false;
        }
        return startDt.getTime() < (Date.now() - 60000);
      },
      confirmPastStartDate(event) {
        if (!this.isStartDateInPast()) {
          this.pastStartConfirmed = false;
          return true;
        }
        if (this.pastStartConfirmed) {
          return true;
        }
        const message = getMessage('pastDateConfirm', 'The start date is in the past. Are you sure you want to continue?');
        const proceed = window.confirm(String(message || ''));
        if (!proceed) {
          if (event && typeof event.preventDefault === 'function') {
            event.preventDefault();
          }
          if (event && typeof event.stopImmediatePropagation === 'function') {
            event.stopImmediatePropagation();
          }
          const $form = $('#nexdirectory-submit-form');
          if ($form.length) {
            $form.data('nexdirectorySubmitting', false).removeClass('nexdirectory-is-submitting');
            $form.find('button[type="submit"], input[type="submit"]').prop('disabled', false).removeClass('nexdirectory-loading');
          }
          showToast(getMessage('pastDateCancelled', 'Submission cancelled. Adjust the date or submit again to confirm.'), 'error');
          return false;
        }
        this.pastStartConfirmed = true;
        return true;
      },
      isFieldVisible(field) {
        if (!field || typeof field.getClientRects !== 'function') {
          return false;
        }
        if (field.closest('[hidden], [aria-hidden="true"], .nexdirectory-is-hidden')) {
          return false;
        }
        const style = typeof window.getComputedStyle === 'function' ? window.getComputedStyle(field) : null;
        if (style && (style.display === 'none' || style.visibility === 'hidden')) {
          return false;
        }
        if (field.getClientRects().length === 0 && (!style || style.position !== 'fixed')) {
          return false;
        }
        return true;
      },
      isFieldEligibleForValidation(field) {
        if (!field || field.disabled) {
          return false;
        }
        const type = String(field.type || '').toLowerCase();
        if (['hidden', 'submit', 'button', 'reset', 'file'].indexOf(type) >= 0) {
          return false;
        }
        return this.isFieldVisible(field);
      },
      clearFieldErrorState(field) {
        if (!field || !field.classList) {
          return;
        }
        if (field.getAttribute('data-nexdirectory-invalid-flag') === '1') {
          field.classList.remove('nexdirectory-field-invalid');
          field.removeAttribute('data-nexdirectory-invalid-flag');
          field.removeAttribute('aria-invalid');
        }
        const group = field.closest('.nexdirectory-field-invalid-group[data-nexdirectory-invalid-flag-group="1"]');
        if (group) {
          group.classList.remove('nexdirectory-field-invalid-group');
          group.removeAttribute('data-nexdirectory-invalid-flag-group');
        }
      },
      clearValidationHighlights(form) {
        if (!form) {
          return;
        }
        const nodes = form.querySelectorAll('[data-nexdirectory-invalid-flag="1"]');
        nodes.forEach((node) => {
          if (!node || !node.classList) {
            return;
          }
          node.classList.remove('nexdirectory-field-invalid');
          node.removeAttribute('data-nexdirectory-invalid-flag');
          node.removeAttribute('aria-invalid');
        });
        const groups = form.querySelectorAll('.nexdirectory-field-invalid-group[data-nexdirectory-invalid-flag-group="1"]');
        groups.forEach((group) => {
          if (!group || !group.classList) {
            return;
          }
          group.classList.remove('nexdirectory-field-invalid-group');
          group.removeAttribute('data-nexdirectory-invalid-flag-group');
        });
      },
      markFieldInvalid(field) {
        if (!field || !field.classList) {
          return;
        }
        field.classList.add('nexdirectory-field-invalid');
        field.setAttribute('data-nexdirectory-invalid-flag', '1');
        field.setAttribute('aria-invalid', 'true');
        const group = field.closest('.nexdirectory-radio-group, .nexdirectory-checkbox-row, fieldset');
        if (group && group.classList) {
          group.classList.add('nexdirectory-field-invalid-group');
          group.setAttribute('data-nexdirectory-invalid-flag-group', '1');
        }
      },
      focusFirstInvalidField(fields) {
        if (!Array.isArray(fields) || !fields.length) {
          return;
        }
        const target = fields.find((field) => this.isFieldVisible(field)) || fields[0];
        if (!target) {
          return;
        }
        if (typeof target.scrollIntoView === 'function') {
          target.scrollIntoView({ behavior: 'smooth', block: 'center' });
        }
        if (typeof target.focus === 'function') {
          try {
            target.focus({ preventScroll: true });
          } catch (error) {
            target.focus();
          }
        }
      },
      collectRequiredFieldErrors(form) {
        if (!form) {
          return [];
        }
        const invalidFields = [];
        const requiredFields = Array.from(form.querySelectorAll('[required]')).filter((field) => this.isFieldEligibleForValidation(field));
        const radioHandled = {};

        const addInvalid = (field) => {
          if (!field || invalidFields.indexOf(field) >= 0) {
            return;
          }
          invalidFields.push(field);
          this.markFieldInvalid(field);
        };

        requiredFields.forEach((field) => {
          const type = String(field.type || '').toLowerCase();
          const tag = String(field.tagName || '').toLowerCase();

          if (type === 'radio') {
            const name = String(field.name || '');
            if (!name || radioHandled[name]) {
              return;
            }
            radioHandled[name] = true;
            const radioGroup = requiredFields.filter((candidate) => String(candidate.type || '').toLowerCase() === 'radio' && String(candidate.name || '') === name);
            const hasChecked = radioGroup.some((candidate) => !!candidate.checked);
            if (!hasChecked && radioGroup.length) {
              radioGroup.forEach((candidate) => this.markFieldInvalid(candidate));
              addInvalid(radioGroup[0]);
            }
            return;
          }

          if (type === 'checkbox') {
            if (!field.checked) {
              addInvalid(field);
            }
            return;
          }

          let isInvalid = false;
          if (typeof field.checkValidity === 'function') {
            isInvalid = !field.checkValidity();
          }

          const textLike = tag === 'textarea' || (tag === 'input' && ['text', 'search', 'email', 'url', 'tel', 'password', 'number'].indexOf(type) >= 0);
          if (!isInvalid && textLike && String(field.value || '').trim() === '') {
            isInvalid = true;
          }
          if (isInvalid) {
            addInvalid(field);
          }
        });

        return invalidFields;
      },
      collectBlockingFieldErrors(form) {
        if (!form) {
          return [];
        }
        this.clearValidationHighlights(form);
        const invalidFields = [];
        const addInvalid = (field) => {
          if (!field || invalidFields.indexOf(field) >= 0) {
            return;
          }
          invalidFields.push(field);
          this.markFieldInvalid(field);
        };

        this.collectRequiredFieldErrors(form).forEach((field) => addInvalid(field));

        if (!this.validateTitle()) {
          addInvalid(form.querySelector('[name="post_title"]'));
        }
        if (!this.validateExcerpt()) {
          addInvalid(form.querySelector('[name="post_excerpt"]'));
        }
        if (!this.validateDates()) {
          const dateField = form.querySelector('[name="nexdirectory_end_date"]')
            || form.querySelector('[name="nexdirectory_end"]')
            || form.querySelector('[name="nexdirectory_start_date"]')
            || form.querySelector('[name="nexdirectory_start"]')
            || form.querySelector('[name="nexdirectory_end_time"]')
            || form.querySelector('[name="nexdirectory_start_time"]');
          addInvalid(dateField);
        }
        if (!this.validateSeoFields()) {
          if (this.seoTitleCount() > 60) {
            addInvalid(form.querySelector('[name="nexdirectory_meta_title"]'));
          }
          if (this.seoDescriptionCount() > 158) {
            addInvalid(form.querySelector('[name="nexdirectory_meta_description"]'));
          }
        }

        return invalidFields;
      },
      hasBlockingErrors(form) {
        const invalidFields = this.collectBlockingFieldErrors(form);
        if (!invalidFields.length) {
          return false;
        }
        this.focusFirstInvalidField(invalidFields);
        return true;
      },
      validateRequiredLocation(form, event) {
        if (!form || String(form.getAttribute('data-nexdirectory-requires-address') || '0') !== '1') {
          return true;
        }
        const addressInput = form.querySelector('[name="nexdirectory_address"]');
        if (!addressInput) {
          return true;
        }
        const addressValue = String(addressInput.value || '').trim();
        if (addressValue !== '') {
          this.clearFieldErrorState(addressInput);
          return true;
        }
        if (event && typeof event.preventDefault === 'function') {
          event.preventDefault();
        }
        if (event && typeof event.stopImmediatePropagation === 'function') {
          event.stopImmediatePropagation();
        }
        this.markFieldInvalid(addressInput);
        this.focusFirstInvalidField([addressInput]);
        showToast('Address is required.', 'error');
        const $form = $('#nexdirectory-submit-form');
        if ($form.length) {
          $form.data('nexdirectorySubmitting', false).removeClass('nexdirectory-is-submitting');
          $form.find('button[type="submit"], input[type="submit"]').prop('disabled', false).removeClass('nexdirectory-loading');
        }
        return false;
      },
      buildFormStateSnapshot(form) {
        if (!form || !form.elements) {
          return null;
        }
        syncEditors();
        const editField = form.querySelector('[name="nexdirectory_edit_post_id"]');
        const snapshot = {
          v: 1,
          ts: Date.now(),
          path: String(window.location.pathname || ''),
          editEventId: String(editField && editField.value ? editField.value : '0'),
          fields: {},
          checkboxNames: [],
          radioNames: []
        };
        const elements = Array.from(form.elements || []);
        elements.forEach((el) => {
          if (!el || el.disabled || !el.name) {
            return;
          }
          const name = String(el.name || '').trim();
          if (!name) {
            return;
          }
          if (
            name === 'nexdirectory_nonce'
            || name === '_wp_http_referer'
            || name === 'nexdirectory_recaptcha_token'
            || name === 'action'
          ) {
            return;
          }
          const tag = String(el.tagName || '').toLowerCase();
          const type = String(el.type || '').toLowerCase();
          if (type === 'file' || type === 'submit' || type === 'button' || type === 'reset') {
            return;
          }
          if (type === 'checkbox') {
            if (snapshot.checkboxNames.indexOf(name) === -1) {
              snapshot.checkboxNames.push(name);
            }
            if (!el.checked) {
              return;
            }
            if (!Array.isArray(snapshot.fields[name])) {
              snapshot.fields[name] = [];
            }
            snapshot.fields[name].push(String(el.value || '1'));
            return;
          }
          if (type === 'radio') {
            if (snapshot.radioNames.indexOf(name) === -1) {
              snapshot.radioNames.push(name);
            }
            if (!el.checked) {
              return;
            }
            snapshot.fields[name] = [String(el.value || '')];
            return;
          }
          if (tag === 'select' && el.multiple) {
            const selected = Array.from(el.options || []).filter((opt) => opt && opt.selected).map((opt) => String(opt.value || ''));
            snapshot.fields[name] = selected;
            return;
          }
          snapshot.fields[name] = [String(el.value || '')];
        });
        return snapshot;
      },
      persistFormState(form) {
        if (!form) {
          return;
        }
        try {
          const snapshot = this.buildFormStateSnapshot(form);
          if (!snapshot) {
            return;
          }
          window.localStorage.setItem(this.formStateStorageKey, JSON.stringify(snapshot));
        } catch (e) {
          // ignore persistence errors
        }
      },
      restoreFormState(form) {
        if (!form) {
          return;
        }
        const isEditing = String(form.getAttribute('data-nexdirectory-editing') || '') === '1';
        // Never let stale browser snapshots overwrite server-hydrated values on edit screens.
        if (isEditing) {
          return;
        }
        let params;
        try {
          params = new URLSearchParams(String(window.location.search || ''));
        } catch (e) {
          return;
        }
        const noticeType = String(
          params.get('nexdirectory_notice_type')
          || window.__nexdirectoryNoticeType
          || ''
        ).toLowerCase();
        if (noticeType !== 'error') {
          return;
        }
        try {
          const raw = window.localStorage.getItem(this.formStateStorageKey);
          if (!raw) {
            return;
          }
          const parsed = JSON.parse(raw);
          if (!parsed || typeof parsed !== 'object') {
            return;
          }
          const ageMs = Date.now() - parseInt(parsed.ts || '0', 10);
          if (!Number.isFinite(ageMs) || ageMs < 0 || ageMs > this.formStateTtlMs) {
            window.localStorage.removeItem(this.formStateStorageKey);
            return;
          }
          if (String(parsed.path || '') !== String(window.location.pathname || '')) {
            return;
          }
          const currentEditField = form.querySelector('[name="nexdirectory_edit_post_id"]');
          const currentEditEventId = String(currentEditField && currentEditField.value ? currentEditField.value : '0');
          const savedEditEventId = String(parsed.editEventId || '0');
          if (savedEditEventId !== currentEditEventId) {
            return;
          }
          const elements = Array.from(form.elements || []);
          const checkboxNames = Array.isArray(parsed.checkboxNames) ? parsed.checkboxNames.map((n) => String(n || '')) : [];
          const radioNames = Array.isArray(parsed.radioNames) ? parsed.radioNames.map((n) => String(n || '')) : [];
          elements.forEach((el) => {
            if (!el || !el.name) {
              return;
            }
            const name = String(el.name || '');
            const type = String(el.type || '').toLowerCase();
            if (checkboxNames.indexOf(name) >= 0 && type === 'checkbox') {
              el.checked = false;
            }
            if (radioNames.indexOf(name) >= 0 && type === 'radio') {
              el.checked = false;
            }
          });
          const fields = (parsed.fields && typeof parsed.fields === 'object') ? parsed.fields : {};
          Object.keys(fields).forEach((name) => {
            const values = Array.isArray(fields[name]) ? fields[name].map((v) => String(v || '')) : [String(fields[name] || '')];
            if (!values.length) {
              return;
            }
            elements.forEach((el) => {
              if (!el || String(el.name || '') !== name) {
                return;
              }
              const tag = String(el.tagName || '').toLowerCase();
              const type = String(el.type || '').toLowerCase();
              if (type === 'checkbox' || type === 'radio') {
                el.checked = values.indexOf(String(el.value || '')) >= 0;
                return;
              }
              if (tag === 'select' && el.multiple) {
                Array.from(el.options || []).forEach((opt) => {
                  if (!opt) {
                    return;
                  }
                  opt.selected = values.indexOf(String(opt.value || '')) >= 0;
                });
                return;
              }
              el.value = String(values[0] || '');
            });
          });
          syncEditors();
          this.syncGalleryTextarea();
          this.syncVideoUrls();
          this.syncRecurrenceMeta();
          this.validateTitle();
          this.validateExcerpt();
          this.validateDates();
          showToast('Your previous form values were restored after validation error.', 'success');
        } catch (e) {
          // ignore malformed state
        }
      },
      autodetectTimezone() {
        const form = document.getElementById('nexdirectory-submit-form');
        if (!form || typeof Intl === 'undefined' || !Intl.DateTimeFormat) {
          return;
        }
        const tzSelect = form.querySelector('select[name="nexdirectory_timezone"]');
        if (!tzSelect || tzSelect.value) {
          return;
        }
        try {
          const guessed = Intl.DateTimeFormat().resolvedOptions().timeZone;
          if (!guessed) {
            return;
          }
          const hasOption = Array.from(tzSelect.options || []).some((opt) => opt.value === guessed);
          if (!hasOption) {
            return;
          }
          tzSelect.value = guessed;
          this.timezoneHint = 'Timezone auto-selected from your browser.';
        } catch (err) {
          // ignore
        }
      },
      getUploadNonce() {
        const nonceFromMedia = window.wp && window.wp.media && window.wp.media.model && window.wp.media.model.settings && window.wp.media.model.settings.post
          ? String(window.wp.media.model.settings.post.nonce || '')
          : '';
        if (nonceFromMedia) {
          return nonceFromMedia;
        }
        const fallback = window._wpPluploadSettings && window._wpPluploadSettings.defaults && window._wpPluploadSettings.defaults.multipart_params
          ? String(window._wpPluploadSettings.defaults.multipart_params._wpnonce || '')
          : '';
        return fallback;
      },
      openMedia(mode) {
        this.triggerFileInput(mode);
      },
      triggerFileInput(mode) {
        if (mode === 'cover' && this.$refs && this.$refs.coverFileInput) {
          this.$refs.coverFileInput.click();
          return;
        }
        if (mode === 'gallery' && this.$refs && this.$refs.galleryFileInput) {
          this.$refs.galleryFileInput.click();
        }
      },
      uploadFile(file, target, kind) {
        const uploadTarget = String(target || 'gallery');
        const uploadKind = String(kind || 'image');
        return uploadAssetFile(file, uploadKind, uploadTarget).then((payload) => {
          return payload && payload.url ? String(payload.url) : '';
        });
      },
      async uploadFiles(files, mode) {
        const incoming = Array.from(files || []);
        if (!incoming.length) {
          return;
        }

        const queue = mode === 'cover' ? [incoming[0]] : incoming;
        for (let i = 0; i < queue.length; i += 1) {
          const file = queue[i];
          if (!file) {
            continue;
          }
          if (mode === 'cover' && file.size > this.coverMaxBytes) {
            showToast('Cover image is too large.', 'error');
            continue;
          }
          if (mode === 'gallery' && file.size > this.galleryMaxBytes) {
            showToast('One of the gallery images is too large.', 'error');
            continue;
          }
          if (mode === 'gallery' && this.galleryImages.length >= this.galleryMaxFiles) {
            showToast('Gallery upload limit reached.', 'error');
            break;
          }
          try {
            const url = await this.uploadFile(file, mode, 'image');
            if (!url) {
              continue;
            }
            if (mode === 'cover') {
              this.coverImage = url;
            } else if (this.galleryImages.indexOf(url) === -1) {
              this.galleryImages.push(url);
            }
            this.syncGalleryTextarea();
            this.queueAutosave(160);
          } catch (error) {
            showToast(error && error.message ? error.message : 'Upload failed. Please try again.', 'error');
          }
        }
      },
      handleFileSelect(event, mode) {
        const files = event && event.target && event.target.files ? event.target.files : [];
        this.uploadFiles(files, mode);
        if (event && event.target) {
          event.target.value = '';
        }
      },
      handleDrop(event, mode) {
        const files = event.dataTransfer && event.dataTransfer.files ? event.dataTransfer.files : [];
        if (!files.length) {
          return;
        }
        this.uploadFiles(files, mode);
      },
      removeGallery(index) {
        this.galleryImages.splice(index, 1);
        this.syncGalleryTextarea();
        this.queueAutosave(160);
      },
      removeCover() {
        this.coverImage = '';
        this.queueAutosave(160);
      },
      syncGalleryTextarea() {
        this.galleryTextarea = this.galleryImages.join('\n');
      },
      extractYoutubeId(value) {
        const raw = String(value || '').trim();
        if (!raw) {
          return '';
        }
        const idPattern = /^[A-Za-z0-9_-]{6,32}$/;
        if (idPattern.test(raw) && raw.indexOf('http') !== 0) {
          return raw;
        }
        const ytMatch = raw.match(/[?&]v=([A-Za-z0-9_-]{6,32})/i)
          || raw.match(/youtu\.be\/([A-Za-z0-9_-]{6,32})/i)
          || raw.match(/youtube\.com\/embed\/([A-Za-z0-9_-]{6,32})/i)
          || raw.match(/youtube\.com\/shorts\/([A-Za-z0-9_-]{6,32})/i);
        return ytMatch && ytMatch[1] ? ytMatch[1] : '';
      },
      normalizeVideoValue(value) {
        const raw = String(value || '').trim();
        if (!raw) {
          return '';
        }
        if (/^https?:\/\//i.test(raw)) {
          return raw;
        }
        const id = this.extractYoutubeId(raw);
        return id || '';
      },
      syncVideoUrls() {
        const urls = [];
        const streamUrl = String(this.streamUrl || '').trim();
        if (streamUrl) {
          urls.push(streamUrl);
        }

        (Array.isArray(this.videoEmbeds) ? this.videoEmbeds : []).forEach((entry) => {
          const normalized = this.normalizeVideoValue(entry);
          if (!normalized) {
            return;
          }
          if (/^https?:\/\//i.test(normalized)) {
            urls.push(normalized);
            return;
          }
          urls.push('https://www.youtube.com/watch?v=' + normalized);
        });
        if (!Array.isArray(this.videoEmbeds) || !this.videoEmbeds.length) {
          this.videoEmbeds = [''];
        }

        const uniqueUrls = Array.from(new Set(urls.map((url) => String(url || '').trim()).filter((url) => url !== '')));
        this.videoUrlsTextarea = uniqueUrls.join('\n');

        const ytIds = uniqueUrls.map((url) => this.extractYoutubeId(url)).filter((id) => id !== '');
        this.youtubeIdOne = ytIds[0] || '';
        this.youtubeIdTwo = ytIds[1] || '';
      },
      addVideoEmbed() {
        if (!Array.isArray(this.videoEmbeds)) {
          this.videoEmbeds = [''];
        }
        this.videoEmbeds.push('');
        this.queueAutosave(220);
      },
      removeVideoEmbed(index) {
        if (!Array.isArray(this.videoEmbeds)) {
          this.videoEmbeds = [''];
          return;
        }
        this.videoEmbeds.splice(index, 1);
        if (!this.videoEmbeds.length) {
          this.videoEmbeds = [''];
        }
        this.syncVideoUrls();
        this.queueAutosave(220);
      },
      syncRecurrenceMeta() {
        if (this.isRecurring !== '1' || !Array.isArray(this.sessions) || !this.sessions.length) {
          this.recurrenceUntil = '';
          return;
        }
        let latest = '';
        this.sessions.forEach((session) => {
          const endValue = String((session && session.end) || '');
          const startValue = String((session && session.start) || '');
          const candidate = (endValue || startValue || '').trim();
          if (candidate && (latest === '' || candidate > latest)) {
            latest = candidate;
          }
        });
        this.recurrenceUntil = latest ? latest.slice(0, 10) : '';
      },
      getSessionAttachmentRows(index) {
        const session = Array.isArray(this.sessions) ? this.sessions[index] : null;
        const rawValue = String((session && session.attachments) || '');
        if (!rawValue.trim()) {
          return [];
        }
        return rawValue
          .split(/\r?\n/)
          .map((row) => String(row || '').trim())
          .filter((row) => row !== '');
      },
      normalizeSessionAttachmentUrl(value) {
        const raw = String(value || '').trim();
        if (!raw) {
          return '';
        }

        if (/^[A-Za-z0-9_-]{11}$/.test(raw)) {
          return 'https://www.youtube.com/watch?v=' + raw;
        }
        if (/^(?:youtube\.com|youtu\.be|vimeo\.com)\//i.test(raw)) {
          return 'https://' + raw;
        }
        if (/^[a-z0-9.-]+\.[a-z]{2,}(?:\/|$)/i.test(raw) && !/^[a-z]+:\/\//i.test(raw)) {
          return 'https://' + raw;
        }
        return raw;
      },
      appendSessionAttachmentUrl(index, rawUrl) {
        const normalizedUrl = this.normalizeSessionAttachmentUrl(rawUrl);
        if (!normalizedUrl) {
          return false;
        }
        if (!Array.isArray(this.sessions) || !this.sessions[index]) {
          return false;
        }

        const existing = this.getSessionAttachmentRows(index);
        if (existing.indexOf(normalizedUrl) !== -1) {
          return false;
        }

        existing.push(normalizedUrl);
        this.sessions[index].attachments = existing.join('\n');
        this.syncRecurrenceMeta();
        this.queueAutosave(180);
        return true;
      },
      triggerSessionAttachmentInput(index) {
        const input = document.getElementById('nexdirectory-session-attachment-upload-' + String(index));
        if (input && typeof input.click === 'function') {
          input.click();
        }
      },
      async handleSessionAttachmentUpload(event, index) {
        if (!Array.isArray(this.sessions) || !this.sessions[index]) {
          return;
        }
        const files = event && event.target && event.target.files ? Array.from(event.target.files) : [];
        if (!files.length) {
          return;
        }

        let addedCount = 0;
        for (let i = 0; i < files.length; i += 1) {
          const file = files[i];
          if (!file) {
            continue;
          }
          const mime = String(file.type || '').toLowerCase();
          const isImage = mime.indexOf('image/') === 0;
          const maxBytes = isImage ? this.galleryMaxBytes : this.fileMaxBytes;
          if (maxBytes > 0 && file.size > maxBytes) {
            showToast(isImage ? 'Image file is too large.' : 'File is too large.', 'error');
            continue;
          }

          try {
            const uploadTarget = isImage ? 'gallery' : 'file';
            const uploadKind = isImage ? 'image' : 'file';
            const uploadedUrl = await this.uploadFile(file, uploadTarget, uploadKind);
            if (this.appendSessionAttachmentUrl(index, uploadedUrl)) {
              addedCount += 1;
            }
          } catch (error) {
            showToast(error && error.message ? error.message : 'Upload failed. Please try again.', 'error');
          }
        }

        if (addedCount > 0) {
          showToast('Attachment added.', 'success');
        }
        if (event && event.target) {
          event.target.value = '';
        }
      },
      openSessionAttachmentMediaPicker(index) {
        if (!Array.isArray(this.sessions) || !this.sessions[index]) {
          return;
        }
        if (!window.wp || !window.wp.media) {
          showToast('Media library is unavailable right now.', 'error');
          return;
        }

        const frame = window.wp.media({
          title: 'Select Attachments',
          button: { text: 'Add attachments' },
          multiple: true
        });
        frame.on('select', () => {
          const selection = frame.state().get('selection');
          if (!selection || typeof selection.toJSON !== 'function') {
            return;
          }
          const items = selection.toJSON();
          if (!Array.isArray(items) || !items.length) {
            return;
          }
          let addedCount = 0;
          items.forEach((item) => {
            const candidateUrl = item && item.url ? String(item.url).trim() : '';
            if (this.appendSessionAttachmentUrl(index, candidateUrl)) {
              addedCount += 1;
            }
          });
          if (addedCount > 0) {
            showToast('Attachment added from media library.', 'success');
          }
        });
        frame.open();
      },
      triggerSpeakerImageInput(index) {
        const input = document.getElementById('nexdirectory-speaker-image-upload-' + String(index));
        if (input && typeof input.click === 'function') {
          input.click();
        }
      },
      async handleSpeakerImageUpload(event, index) {
        if (!Array.isArray(this.keySpeakers) || !this.keySpeakers[index]) {
          return;
        }
        const files = event && event.target && event.target.files ? Array.from(event.target.files) : [];
        if (!files.length) {
          return;
        }
        const file = files[0];
        const mime = String((file && file.type) || '').toLowerCase();
        if (mime.indexOf('image/') !== 0) {
          showToast('Please choose an image file.', 'error');
          if (event && event.target) {
            event.target.value = '';
          }
          return;
        }
        if (this.galleryMaxBytes > 0 && file.size > this.galleryMaxBytes) {
          showToast('Image file is too large.', 'error');
          if (event && event.target) {
            event.target.value = '';
          }
          return;
        }
        try {
          const uploadedUrl = await this.uploadFile(file, 'gallery', 'image');
          if (uploadedUrl) {
            this.keySpeakers[index].image = uploadedUrl;
            this.queueAutosave(180);
            showToast('Speaker image added.', 'success');
          }
        } catch (error) {
          showToast(error && error.message ? error.message : 'Upload failed. Please try again.', 'error');
        }
        if (event && event.target) {
          event.target.value = '';
        }
      },
      openSpeakerImageMediaPicker(index) {
        if (!Array.isArray(this.keySpeakers) || !this.keySpeakers[index]) {
          return;
        }
        if (!window.wp || !window.wp.media) {
          showToast('Media library is unavailable right now.', 'error');
          return;
        }
        const frame = window.wp.media({
          title: 'Select Speaker Image',
          button: { text: 'Use image' },
          multiple: false,
          library: { type: 'image' }
        });
        frame.on('select', () => {
          const selection = frame.state().get('selection');
          if (!selection || typeof selection.toJSON !== 'function') {
            return;
          }
          const items = selection.toJSON();
          const firstItem = Array.isArray(items) && items.length ? items[0] : null;
          const candidateUrl = firstItem && firstItem.url ? String(firstItem.url).trim() : '';
          if (!candidateUrl) {
            return;
          }
          this.keySpeakers[index].image = candidateUrl;
          this.queueAutosave(180);
          showToast('Speaker image selected.', 'success');
        });
        frame.open();
      },
      removeSpeakerImage(index) {
        if (!Array.isArray(this.keySpeakers) || !this.keySpeakers[index]) {
          return;
        }
        this.keySpeakers[index].image = '';
        this.queueAutosave(160);
      },
      addSession() {
        this.sessions.push({ title: '', start: '', end: '', location: '', speaker: '', day: '', notes: '', attachments: '', session_key: '' });
        this.isRecurring = '1';
        this.syncRecurrenceMeta();
        this.queueAutosave(220);
      },
      removeSession(index) {
        this.sessions.splice(index, 1);
        if (!this.sessions.length) {
          this.isRecurring = '0';
        }
        this.syncRecurrenceMeta();
        this.queueAutosave(220);
      },
      addKeySpeaker() {
        if (!Array.isArray(this.keySpeakers)) {
          this.keySpeakers = [];
        }
        this.keySpeakers.push({ name: '', profession: '', subject: '', image: '', details: '' });
        this.queueAutosave(220);
      },
      removeKeySpeaker(index) {
        if (!Array.isArray(this.keySpeakers)) {
          this.keySpeakers = [];
          return;
        }
        this.keySpeakers.splice(index, 1);
        this.queueAutosave(220);
      },
      autosaveDraft() {
        if (!this.autosaveEnabled) {
          return;
        }
        const form = document.getElementById('nexdirectory-submit-form');
        if (!form) {
          return;
        }
        if (this.autosaveBusy) {
          this.autosavePendingAfterBusy = true;
          return;
        }
        const titleInput = form.querySelector('[name="post_title"]');
        if (!titleInput || !titleInput.value.trim()) {
          return;
        }

        syncEditors();
        this.autosaveBusy = true;
        this.validateExcerpt();
        this.syncGalleryTextarea();
        this.syncVideoUrls();

        const fd = new FormData(form);
        fd.set('action', 'nexdirectory_autosave_event');
        fd.set('nonce', getConfig('autosaveNonce', ''));
        const signatureRows = [];
        fd.forEach((value, key) => {
          if (typeof File !== 'undefined' && value instanceof File) {
            return;
          }
          if (key === 'nonce') {
            return;
          }
          signatureRows.push([String(key), String(value)]);
        });
        signatureRows.sort((a, b) => {
          const keyCompare = a[0].localeCompare(b[0]);
          return keyCompare !== 0 ? keyCompare : a[1].localeCompare(b[1]);
        });
        const payloadSignature = JSON.stringify(signatureRows);
        if (payloadSignature === this.autosaveLastSignature) {
          this.autosaveMessage = 'All changes saved';
          return;
        }
        this.autosaveLastRunAt = Date.now();
        const editField = form.querySelector('[name="nexdirectory_edit_post_id"]');
        const sourceEditId = String(editField && editField.value ? editField.value : '0');

        fetch(getConfig('autosaveEndpoint', '/wp-admin/admin-post.php'), {
          method: 'POST',
          body: fd,
          credentials: 'same-origin'
        })
          .then((res) => res.json())
          .then((json) => {
            if (json && json.success) {
              this.autosaveLastSignature = payloadSignature;
              this.autosaveMessage = 'Autosaved at ' + new Date().toLocaleTimeString();
              this.autosavePostId = json.data && json.data.post_id ? String(json.data.post_id) : this.autosavePostId;
              const state = {
                coverImage: this.coverImage,
                galleryImages: this.galleryImages,
                sessions: this.sessions,
                keySpeakers: this.keySpeakers,
                isRecurring: this.isRecurring,
                streamUrl: this.streamUrl,
                excerptValue: this.excerptValue,
                metaTitleValue: this.metaTitleValue,
                metaDescriptionValue: this.metaDescriptionValue,
                cityValue: this.cityValue,
                countryValue: this.countryValue,
                latValue: this.latValue,
                lngValue: this.lngValue,
                youtubeIdOne: this.youtubeIdOne,
                youtubeIdTwo: this.youtubeIdTwo,
                videoEmbeds: this.videoEmbeds,
                autosavePostId: this.autosavePostId,
                sourceEditId: sourceEditId
              };
              if (sourceEditId === '0') {
                window.localStorage.setItem('nexdirectory_submit_draft', JSON.stringify(state));
              } else {
                window.localStorage.removeItem('nexdirectory_submit_draft');
              }
            } else {
              this.autosaveMessage = 'Autosave failed';
            }
          })
          .catch(() => {
            this.autosaveMessage = 'Autosave failed';
          })
          .finally(() => {
            this.autosaveBusy = false;
            if (this.autosavePendingAfterBusy) {
              this.autosavePendingAfterBusy = false;
              this.queueAutosave(500);
            }
          });
      }
    };
  };

  function initCardLinks() {
    $(document).off('click.nexdirectoryCardLink', '[data-nexdirectory-card-url]').on('click.nexdirectoryCardLink', '[data-nexdirectory-card-url]', function (event) {
      const $target = $(event.target);
      if ($target.closest('a, button, input, select, textarea, label, form').length) {
        return;
      }
      const url = String($(this).attr('data-nexdirectory-card-url') || '').trim();
      if (!url) {
        return;
      }
      window.location.href = url;
    });

    $(document).off('keydown.nexdirectoryCardLink', '[data-nexdirectory-card-url]').on('keydown.nexdirectoryCardLink', '[data-nexdirectory-card-url]', function (event) {
      if (event.key !== 'Enter' && event.key !== ' ') {
        return;
      }
      event.preventDefault();
      const url = String($(this).attr('data-nexdirectory-card-url') || '').trim();
      if (!url) {
        return;
      }
      window.location.href = url;
    });
  }

  let revealObserver = null;
  let revealMutationObserver = null;
  let revealRaf = null;

  function applyScrollReveal(scope) {
    const root = scope && scope.querySelectorAll ? scope : document;
    if (!root || !root.querySelectorAll) {
      return;
    }
    const selector = [
      '.nexdirectory-page-banner',
      '.nexdirectory-card',
      '.nexdirectory-account-panel',
      '.nexdirectory-auth-panel',
      '.nexdirectory-form-section',
      '.nexdirectory-event-section',
      '.nexdirectory-map-item-card',
      '.map-card',
      '.nexdirectory-empty-state',
      '.nexdirectory-calendar-cell',
      '.nexdirectory-admin-table tbody tr'
    ].join(', ');

    const nodes = root.querySelectorAll(selector);
    if (!nodes.length) {
      return;
    }

    let revealIndex = 0;
    nodes.forEach(function (node) {
      if (!node || node.classList.contains('nexdirectory-reveal-ready')) {
        return;
      }
      node.classList.add('nexdirectory-reveal', 'nexdirectory-reveal-ready');
      node.style.setProperty('--nexdirectory-reveal-delay', String(Math.min((revealIndex % 8) * 32, 224)) + 'ms');
      revealIndex += 1;

      if (!revealObserver) {
        node.classList.add('is-visible');
        return;
      }
      revealObserver.observe(node);
    });
  }

  function initScrollReveal() {
    const body = document.body;
    if (!body) {
      return;
    }

    if (!revealObserver && 'IntersectionObserver' in window) {
      revealObserver = new IntersectionObserver(function (entries) {
        entries.forEach(function (entry) {
          if (!entry || !entry.target || !entry.isIntersecting) {
            return;
          }
          entry.target.classList.add('is-visible');
          revealObserver.unobserve(entry.target);
        });
      }, {
        root: null,
        rootMargin: '0px 0px -8% 0px',
        threshold: 0.08
      });
    }

    applyScrollReveal(document);

    if (revealMutationObserver || !('MutationObserver' in window)) {
      return;
    }

    revealMutationObserver = new MutationObserver(function (mutations) {
      let shouldRefresh = false;
      for (let index = 0; index < mutations.length; index += 1) {
        const mutation = mutations[index];
        if (!mutation || !mutation.addedNodes || !mutation.addedNodes.length) {
          continue;
        }
        for (let nodeIndex = 0; nodeIndex < mutation.addedNodes.length; nodeIndex += 1) {
          const added = mutation.addedNodes[nodeIndex];
          if (added && added.nodeType === 1) {
            shouldRefresh = true;
            break;
          }
        }
        if (shouldRefresh) {
          break;
        }
      }

      if (!shouldRefresh) {
        return;
      }

      if (revealRaf) {
        window.cancelAnimationFrame(revealRaf);
      }
      revealRaf = window.requestAnimationFrame(function () {
        applyScrollReveal(document);
      });
    });

    revealMutationObserver.observe(body, {
      childList: true,
      subtree: true
    });
  }

  let smoothScrollEngineBound = false;
  let smoothScrollRafId = 0;
  let smoothScrollLenisInstance = null;

  function initSmoothScrollingEngine() {
    if (smoothScrollEngineBound) {
      return;
    }
    if (!toBool(getConfig('smoothScrolling', false), false)) {
      return;
    }
    if (!document.body || !document.documentElement) {
      return;
    }
    if (window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
      return;
    }
    if (typeof window.Lenis !== 'function') {
      return;
    }

    const clampNumber = function (value, min, max) {
      const numeric = Number(value);
      if (!Number.isFinite(numeric)) {
        return min;
      }
      if (numeric < min) {
        return min;
      }
      if (numeric > max) {
        return max;
      }
      return numeric;
    };

    const profileRaw = String(getConfig('smoothScrollProfile', 'balanced') || 'balanced').toLowerCase();
    const profile = ['gentle', 'balanced', 'snappy', 'custom'].indexOf(profileRaw) >= 0 ? profileRaw : 'balanced';
    const profileDefaults = {
      gentle: { duration: 1.45, multiplier: 0.92 },
      balanced: { duration: 1.2, multiplier: 1 },
      snappy: { duration: 0.9, multiplier: 1.12 },
      custom: { duration: 1.2, multiplier: 1 }
    };
    const fallbackProfile = profileDefaults.balanced;
    const profileConfig = Object.prototype.hasOwnProperty.call(profileDefaults, profile)
      ? profileDefaults[profile]
      : fallbackProfile;

    let duration = profileConfig.duration;
    let mouseMultiplier = profileConfig.multiplier;

    if (profile === 'custom') {
      duration = clampNumber(getConfig('smoothScrollEase', 1.2), 0.4, 3);
      mouseMultiplier = clampNumber(getConfig('smoothScrollWheelMultiplier', 1), 0.4, 3);
    }

    const touchpadModeRaw = String(getConfig('smoothScrollTouchpadBehavior', 'native') || 'native').toLowerCase();
    const touchpadMode = ['native', 'assist', 'force'].indexOf(touchpadModeRaw) >= 0 ? touchpadModeRaw : 'native';
    const hasTouchInput = (
      typeof window.matchMedia === 'function' &&
      (window.matchMedia('(pointer: coarse)').matches || window.matchMedia('(hover: none) and (pointer: coarse)').matches)
    );
    const enableOnTouch = toBool(getConfig('smoothScrollEnableOnTouch', false), false);
    if (hasTouchInput && !enableOnTouch) {
      return;
    }

    if (touchpadMode === 'assist') {
      mouseMultiplier = clampNumber(mouseMultiplier * 0.92, 0.4, 3);
    } else if (touchpadMode === 'force') {
      mouseMultiplier = clampNumber(mouseMultiplier * 1.1, 0.4, 3);
    }

    try {
      smoothScrollLenisInstance = new window.Lenis({
        duration: duration,
        easing: function (t) {
          return Math.min(1, 1.001 - Math.pow(2, -10 * t));
        },
        direction: 'vertical',
        gestureDirection: 'vertical',
        smooth: true,
        mouseMultiplier: mouseMultiplier,
        smoothTouch: enableOnTouch,
        prevent: function (node) {
          if (!node || typeof node.closest !== 'function') {
            return false;
          }
          return !!node.closest(
            '.leaflet-container, .leaflet-control-container, .leaflet-popup, [data-nexdirectory-smooth-ignore="1"], [data-no-smooth-scroll="1"], .no-smooth-scroll, input, textarea, select, option, [contenteditable=""], [contenteditable="true"]'
          );
        }
      });
    } catch (error) {
      smoothScrollLenisInstance = null;
      smoothScrollEngineBound = false;
      return;
    }

    smoothScrollEngineBound = true;
    window.__nexdirectoryLenis = smoothScrollLenisInstance;

    const raf = function (time) {
      if (!smoothScrollLenisInstance) {
        return;
      }
      smoothScrollLenisInstance.raf(time);
      smoothScrollRafId = window.requestAnimationFrame(raf);
    };
    smoothScrollRafId = window.requestAnimationFrame(raf);

    document.addEventListener('click', function (event) {
      const link = event && event.target && typeof event.target.closest === 'function'
        ? event.target.closest('a[href^="#"]')
        : null;
      if (!link || !smoothScrollLenisInstance) {
        return;
      }
      const href = String(link.getAttribute('href') || '');
      if (href.length < 2) {
        return;
      }
      const target = document.getElementById(href.slice(1));
      if (!target) {
        return;
      }
      smoothScrollLenisInstance.scrollTo(target, { duration: duration });
      event.preventDefault();
    }, true);

    window.addEventListener('beforeunload', function () {
      if (smoothScrollRafId) {
        window.cancelAnimationFrame(smoothScrollRafId);
        smoothScrollRafId = 0;
      }
      if (smoothScrollLenisInstance && typeof smoothScrollLenisInstance.destroy === 'function') {
        smoothScrollLenisInstance.destroy();
      }
      smoothScrollLenisInstance = null;
      smoothScrollEngineBound = false;
      window.__nexdirectoryLenis = null;
    }, { once: true });
  }

  function initSubmitNavSpy() {
    const nav = document.getElementById('nexdirectory-submit-nav');
    if (!nav) {
      return;
    }
    if (nav.getAttribute('data-nexdirectory-bound') === '1') {
      return;
    }
    nav.setAttribute('data-nexdirectory-bound', '1');
    const links = Array.from(nav.querySelectorAll('.nexdirectory-submit-nav-link[href^="#"]'));
    if (!links.length) {
      return;
    }

    const sectionMap = links.map((link) => {
      const targetId = String(link.getAttribute('href') || '').replace('#', '');
      const section = targetId ? document.getElementById(targetId) : null;
      return {
        link,
        targetId,
        section
      };
    }).filter((item) => item.section);
    if (!sectionMap.length) {
      return;
    }

    function setActive(targetId) {
      sectionMap.forEach((item) => {
        if (item.targetId === targetId) {
          item.link.classList.add('is-active');
          item.section.classList.add('is-current');
        } else {
          item.link.classList.remove('is-active');
          item.section.classList.remove('is-current');
        }
      });
    }

    sectionMap.forEach((item) => {
      item.link.addEventListener('click', function (event) {
        event.preventDefault();
        item.section.scrollIntoView({ behavior: 'smooth', block: 'start' });
        setActive(item.targetId);
      });
    });

    setActive(sectionMap[0].targetId);

    if ('IntersectionObserver' in window) {
      const observer = new IntersectionObserver(function (entries) {
        entries.forEach((entry) => {
          if (entry.isIntersecting && entry.target && entry.target.id) {
            setActive(String(entry.target.id));
          }
        });
      }, {
        root: null,
        rootMargin: '-35% 0px -55% 0px',
        threshold: [0.1, 0.3, 0.6]
      });
      sectionMap.forEach((item) => observer.observe(item.section));
      return;
    }

    window.addEventListener('scroll', function () {
      const anchor = window.scrollY + (window.innerHeight * 0.35);
      let activeId = sectionMap[0].targetId;
      sectionMap.forEach((item) => {
        if (!item.section) {
          return;
        }
        if (item.section.offsetTop <= anchor) {
          activeId = item.targetId;
        }
      });
      setActive(activeId);
    }, { passive: true });
  }

  function initUserManagementSearch() {
    const $form = $('.nexdirectory-user-search-form').first();
    const $input = $('#nexdirectory-user-search-input');
    const $tbody = $('#nexdirectory-user-management-body');
    const $count = $('#nexdirectory-user-search-count');
    const endpoint = String(getConfig('userSearchRestUrl', '') || '');
    if (!$form.length || !$input.length || !$tbody.length || !endpoint) {
      return;
    }

    let requestController = null;
    let debounceTimer = null;

    function setLoading(state) {
      $tbody.toggleClass('nexdirectory-loading', state === true);
      $form.toggleClass('nexdirectory-loading', state === true);
    }

    function runSearch(immediate) {
      const query = String($input.val() || '').trim();
      const limit = parseInt($form.attr('data-limit') || '100', 10);
      const safeLimit = Number.isFinite(limit) && limit > 0 ? Math.min(200, limit) : 100;

      if (requestController) {
        requestController.abort();
      }

      const execute = function () {
        const params = new URLSearchParams();
        params.set('q', query);
        params.set('limit', String(safeLimit));
        requestController = new AbortController();
        setLoading(true);
        fetch(endpoint + '?' + params.toString(), {
          credentials: 'same-origin',
          signal: requestController.signal,
          headers: {
            'X-WP-Nonce': String(getConfig('restNonce', '') || '')
          }
        })
          .then(async (res) => {
            const payload = await res.json();
            if (!res.ok) {
              throw new Error((payload && payload.message) ? payload.message : getMessage('userSearchFailed', 'Could not search users right now.'));
            }
            $tbody.html(payload && payload.html ? payload.html : '');
            if ($count.length) {
              const found = payload && Number.isFinite(parseInt(payload.count, 10)) ? parseInt(payload.count, 10) : 0;
              $count.text(String(found) + ' users loaded');
            }
          })
          .catch((err) => {
            if (err && err.name === 'AbortError') {
              return;
            }
            showToast(err && err.message ? err.message : getMessage('userSearchFailed', 'Could not search users right now.'), 'error');
          })
          .finally(() => {
            setLoading(false);
          });
      };

      if (immediate) {
        execute();
        return;
      }
      clearTimeout(debounceTimer);
      debounceTimer = window.setTimeout(execute, 260);
    }

    $form.off('submit.nexdirectoryUserSearch').on('submit.nexdirectoryUserSearch', function (event) {
      event.preventDefault();
      runSearch(true);
    });

    $input.off('input.nexdirectoryUserSearch').on('input.nexdirectoryUserSearch', function () {
      runSearch(false);
    });
  }

  function initNoticeToasts() {
    $('[data-nexdirectory-toast-message]').each(function () {
      const $node = $(this);
      const text = String($node.attr('data-nexdirectory-toast-message') || '').trim();
      if (!text) {
        return;
      }
      const rawType = String($node.attr('data-nexdirectory-toast-type') || 'success').trim().toLowerCase();
      const type = ['success', 'error', 'warning', 'info'].indexOf(rawType) >= 0 ? rawType : 'success';
      showToast(text, type);
      $node.attr('data-nexdirectory-toast-message', '');
    });

    $('.nexdirectory-notice').each(function () {
      const $notice = $(this);
      const text = String($notice.text() || '').trim();
      if (!text) {
        return;
      }
      const type = $notice.hasClass('nexdirectory-notice-error') ? 'error' : 'success';
      showToast(text, type);
      $notice.addClass('nexdirectory-toasted');
    });
  }

  function initUrlNoticeToast() {
    let url;
    try {
      url = new URL(window.location.href);
    } catch (error) {
      return;
    }

    const params = url.searchParams;
    const rawNotice = String(params.get('nexdirectory_notice') || '').trim();
    const rawType = String(params.get('nexdirectory_notice_type') || 'success').trim().toLowerCase();
    const hasNoticeParams = rawNotice !== '' || params.has('nexdirectory_notice_type');
    if (!hasNoticeParams) {
      window.__nexdirectoryNoticeType = '';
      return;
    }

    const validTypes = ['success', 'error', 'warning', 'info'];
    const noticeType = rawType === 'warn'
      ? 'warning'
      : (validTypes.indexOf(rawType) >= 0 ? rawType : 'success');

    window.__nexdirectoryNoticeType = noticeType;

    const inlineToastNode = Array.from(document.querySelectorAll('[data-nexdirectory-toast-message]')).find((node) => {
      return String(node.getAttribute('data-nexdirectory-toast-message') || '').trim() !== '';
    });
    const inlineNoticeNode = Array.from(document.querySelectorAll('.nexdirectory-notice')).find((node) => {
      return String((node.textContent || '')).trim() !== '';
    });
    const hasInlineNoticeNodes = !!inlineToastNode || !!inlineNoticeNode;
    if (rawNotice !== '' && !hasInlineNoticeNodes) {
      let noticeText = rawNotice;
      try {
        noticeText = decodeURIComponent(rawNotice.replace(/\+/g, '%20'));
      } catch (error) {
        noticeText = rawNotice;
      }
      noticeText = String(noticeText || '').trim();
      if (noticeText) {
        showToast(noticeText, noticeType);
      }
    }

    params.delete('nexdirectory_notice');
    params.delete('nexdirectory_notice_type');

    const nextUrl = url.pathname + (params.toString() ? ('?' + params.toString()) : '') + (url.hash || '');
    if (window.history && typeof window.history.replaceState === 'function') {
      window.history.replaceState(window.history.state || null, document.title, nextUrl);
    }
  }

  function resolveSpaRoot(preferredSelector, rootDocument) {
    const doc = (rootDocument && typeof rootDocument.querySelector === 'function') ? rootDocument : document;
    const normalizedPreferred = String(preferredSelector || '').trim() || '[data-nexdirectory-spa-main="1"]';
    let root = doc.querySelector(normalizedPreferred);
    if (root) {
      return {
        node: root,
        selector: normalizedPreferred
      };
    }

    const fallbackSelectors = [
      '[data-nexdirectory-spa-main="1"]',
      '.nexdirectory-spa-view',
      '[data-nexdirectory-uniform-page="1"]',
      '.nexdirectory-uniform-page-gutter'
    ];

    for (let i = 0; i < fallbackSelectors.length; i += 1) {
      const fallbackSelector = String(fallbackSelectors[i] || '').trim();
      if (!fallbackSelector) {
        continue;
      }
      root = doc.querySelector(fallbackSelector);
      if (!root) {
        continue;
      }
      if (!root.hasAttribute('data-nexdirectory-spa-main')) {
        root.setAttribute('data-nexdirectory-spa-main', '1');
      }
      return {
        node: root,
        selector: '[data-nexdirectory-spa-main="1"]'
      };
    }

    return null;
  }

  function initHybridSpaRouter() {
    if (spaRouterBound) {
      return;
    }
    if (!toBool(getConfig('spaEnabled', false), false)) {
      setSpaFeedbackStatus('SPA is disabled', 'warn', 'Enable SPA hybrid navigation in plugin settings');
      return;
    }
    if (typeof window.fetch !== 'function' || typeof window.DOMParser !== 'function' || !window.history || typeof window.history.pushState !== 'function') {
      setSpaFeedbackStatus('SPA is unavailable in this browser', 'error', 'Required fetch/history APIs are missing');
      return;
    }

    spaSelector = String(getConfig('spaSelector', '[data-nexdirectory-spa-main="1"]') || '[data-nexdirectory-spa-main="1"]').trim();
    if (!spaSelector) {
      spaSelector = '[data-nexdirectory-spa-main="1"]';
    }

    const configuredRoutes = getConfig('spaRoutes', []);
    const routeSet = new Set();
    if (Array.isArray(configuredRoutes)) {
      configuredRoutes.forEach(function (routeUrl) {
        const value = String(routeUrl || '').trim();
        if (!value) {
          return;
        }
        try {
          const parsed = new URL(value, window.location.origin);
          if (parsed.origin !== window.location.origin) {
            return;
          }
          routeSet.add(normalizePathname(parsed.pathname));
        } catch (err) {
          // ignore invalid route entries
        }
      });
    }
    const currentPath = normalizePathname(window.location.pathname);
    const currentPathConfigured = routeSet.has(currentPath);
    routeSet.add(currentPath);

    const spaRoot = resolveSpaRoot(spaSelector);
    if (!spaRoot) {
      if (!currentPathConfigured) {
        setSpaFeedbackStatus('SPA monitor ready', 'ok', 'Current page is outside SPA route scope');
        return;
      }
      setSpaFeedbackStatus('SPA container not found on this page', 'warn', 'Falling back to full page loads');
      return;
    }

    spaSelector = String(spaRoot.selector || '[data-nexdirectory-spa-main="1"]');
    if (!spaSelector) {
      spaSelector = '[data-nexdirectory-spa-main="1"]';
    }

    spaRouteSet = routeSet;
    spaRouterBound = true;
    setSpaFeedbackStatus('SPA router active', 'ok', 'Intercepting configured plugin routes');

    let announcer = document.getElementById('nexdirectory-spa-announcer');
    if (!announcer) {
      announcer = document.createElement('div');
      announcer.id = 'nexdirectory-spa-announcer';
      announcer.className = 'nexdirectory-spa-announcer';
      announcer.setAttribute('aria-live', 'polite');
      announcer.setAttribute('aria-atomic', 'true');
      document.body.appendChild(announcer);
    }

    const setLoadingState = function (active, targetUrl) {
      if (active === true) {
        markUiHydrating();
      } else {
        markUiReady(80);
      }
      const root = document.querySelector(spaSelector);
      if (root) {
        root.classList.toggle('nexdirectory-spa-loading', active === true);
      }
      document.documentElement.classList.toggle('nexdirectory-spa-loading', active === true);
      if (window.__nexdirectoryPagePreloader && typeof window.__nexdirectoryPagePreloader.setVisible === 'function') {
        window.__nexdirectoryPagePreloader.setVisible(active === true, 'spa', targetUrl);
      }
    };

    const announce = function (message) {
      if (!announcer) {
        return;
      }
      announcer.textContent = String(message || '').trim();
    };

    const shouldSkipSpaLink = function (link, url) {
      if (!link || !url) {
        return true;
      }
      if (link.getAttribute('data-nexdirectory-no-spa') === '1' || link.closest('[data-nexdirectory-no-spa="1"]')) {
        return true;
      }
      if (link.target && String(link.target).toLowerCase() === '_blank') {
        return true;
      }
      if (link.hasAttribute('download')) {
        return true;
      }
      if (url.origin !== window.location.origin) {
        return true;
      }
      if (url.protocol !== 'http:' && url.protocol !== 'https:') {
        return true;
      }
      if (url.pathname.indexOf('/wp-admin/') === 0 || /\/wp-login\.php$/i.test(url.pathname)) {
        return true;
      }
      if (url.searchParams.get('action') === 'logout') {
        return true;
      }
      if (url.hash && normalizePathname(url.pathname) === normalizePathname(window.location.pathname) && url.search === window.location.search) {
        return true;
      }
      return false;
    };

    const canHandleSpaLink = function (link, url) {
      const targetPath = normalizePathname(url.pathname);
      if (spaRouteSet && spaRouteSet.has(targetPath)) {
        return true;
      }
      return false;
    };

    const describeTarget = function (targetUrl) {
      try {
        const parsed = new URL(String(targetUrl || ''), window.location.origin);
        return normalizePathname(parsed.pathname) + String(parsed.search || '');
      } catch (err) {
        return String(targetUrl || '').trim();
      }
    };

    const hydrateSpaView = function (html, targetUrl, options) {
      const parser = new window.DOMParser();
      const doc = parser.parseFromString(String(html || ''), 'text/html');
      const nextResolved = resolveSpaRoot(spaSelector, doc);
      const currentResolved = resolveSpaRoot(spaSelector, document);
      if (!nextResolved || !currentResolved) {
        return false;
      }
      const nextRoot = nextResolved.node;
      const currentRoot = currentResolved.node;

      const importedRoot = document.importNode(nextRoot, true);
      currentRoot.replaceWith(importedRoot);
      importedRoot.classList.add('nexdirectory-spa-enter');
      window.setTimeout(function () {
        importedRoot.classList.remove('nexdirectory-spa-enter');
      }, 260);

      const nextTitle = doc.querySelector('title');
      const nextTitleText = nextTitle ? String(nextTitle.textContent || '').trim() : '';
      if (nextTitleText) {
        document.title = nextTitleText;
      }

      if (!options || options.push !== false) {
        window.history.pushState({ nexdirectorySpa: true }, '', targetUrl);
      }

      if (!options || options.scroll !== false) {
        window.scrollTo({ top: 0, left: 0, behavior: 'auto' });
      }

      document.body.classList.remove('nexdirectory-map-filters-open');
      runFrontendInitializers();
      const alpineHydrationFailed = importedRoot.getAttribute('data-nexdirectory-alpine-error') === '1'
        || !!importedRoot.querySelector('[data-nexdirectory-alpine-error="1"]');
      if (alpineHydrationFailed) {
        throw new Error('spa_alpine_hydration_failed');
      }
      try {
        document.dispatchEvent(new CustomEvent('nexdirectory:spa:navigated', {
          detail: {
            url: String(targetUrl || ''),
            selector: String(spaSelector || '')
          }
        }));
      } catch (error) {
        // Ignore event dispatch failures in very old browsers.
      }

      try {
        const parsed = new URL(String(targetUrl || ''), window.location.origin);
        if (spaRouteSet) {
          spaRouteSet.add(normalizePathname(parsed.pathname));
        }
      } catch (err) {
        // ignore parse failures
      }
      return true;
    };

    const navigateSpa = function (targetUrl, options) {
      const requestUrl = String(targetUrl || '').trim();
      if (!requestUrl) {
        return;
      }
      const targetLabel = describeTarget(requestUrl);

      if (spaActiveController) {
        spaActiveController.abort();
      }
      spaActiveController = new AbortController();
      const requestId = ++spaRequestSeq;
      const loadingMessage = String(getMessage('spaLoading', 'Loading page...') || 'Loading page...');
      const failMessage = String(getMessage('spaLoadFailed', 'Could not load that page. Opening full page view.') || 'Could not load that page. Opening full page view.');
      setSpaFeedbackStatus('SPA navigation in progress', 'warn', targetLabel);

	      let loadingShown = true;
	      setLoadingState(true, requestUrl);
	      announce(loadingMessage);
	      const clearLoading = function () {
	        if (loadingShown) {
	          setLoadingState(false, requestUrl);
	          announce('');
        }
      };

      fetch(requestUrl, {
        credentials: 'same-origin',
        signal: spaActiveController.signal,
        headers: {
          'X-Requested-With': 'XMLHttpRequest'
        }
      })
        .then(function (res) {
          if (!res.ok) {
            throw new Error('http_' + String(res.status || 0));
          }
          return res.text();
        })
        .then(function (html) {
          if (requestId !== spaRequestSeq) {
            return;
          }
          const hydrated = hydrateSpaView(html, requestUrl, options || {});
          if (!hydrated) {
            queueSpaFeedbackForNextLoad({
              tone: 'error',
              message: 'SPA fallback to full page load',
              detail: 'Could not locate SPA container in fetched response',
              target: targetLabel
            });
            window.location.href = requestUrl;
            return;
          }
          setSpaFeedbackStatus('SPA navigation completed', 'ok', targetLabel);
        })
        .catch(function (error) {
          if (error && error.name === 'AbortError') {
            return;
          }
          queueSpaFeedbackForNextLoad({
            tone: 'error',
            message: 'SPA request failed, full page load used',
            detail: targetLabel
          });
          showToast(failMessage, 'warning', {
            title: 'Notice',
            subtitle: 'Switching to full page load'
          });
          setSpaFeedbackStatus('SPA request failed', 'error', targetLabel);
          window.location.href = requestUrl;
        })
        .finally(function () {
          if (requestId !== spaRequestSeq) {
            return;
          }
          clearLoading();
          spaActiveController = null;
        });
    };

    document.addEventListener('click', function (event) {
      if (!event || event.defaultPrevented) {
        return;
      }
      if (event.button !== 0 || event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) {
        return;
      }
      const link = event.target && event.target.closest ? event.target.closest('a[href]') : null;
      if (!link) {
        return;
      }

      let url;
      try {
        const href = String(link.getAttribute('href') || '').trim();
        if (!href || href.indexOf('javascript:') === 0) {
          return;
        }
        url = new URL(href, window.location.href);
      } catch (err) {
        return;
      }
      if (shouldSkipSpaLink(link, url)) {
        return;
      }
      if (!canHandleSpaLink(link, url)) {
        queueSpaFeedbackForNextLoad({
          tone: 'warn',
          message: 'Full page load (route not registered for SPA)',
          detail: describeTarget(url.toString()),
          target: url.toString()
        });
        return;
      }

      event.preventDefault();
      navigateSpa(url.toString(), { push: true, scroll: true });
    });

    window.addEventListener('popstate', function () {
      let parsed;
      try {
        parsed = new URL(window.location.href, window.location.origin);
      } catch (err) {
        window.location.reload();
        return;
      }
      const targetPath = normalizePathname(parsed.pathname);
      if (!spaRouteSet || !spaRouteSet.has(targetPath)) {
        queueSpaFeedbackForNextLoad({
          tone: 'warn',
          message: 'Back/forward triggered full page load',
          detail: targetPath + String(parsed.search || ''),
          target: window.location.href
        });
        window.location.reload();
        return;
      }
      navigateSpa(window.location.href, { push: false, scroll: true });
    });
  }

  function initAuthTabs() {
    const shell = document.querySelector('.nexdirectory-auth-shell');
    if (!shell) {
      return;
    }
    if (shell.getAttribute('data-nexdirectory-bound') === '1') {
      return;
    }
    shell.setAttribute('data-nexdirectory-bound', '1');

    const tabs = Array.from(shell.querySelectorAll('[data-nexdirectory-auth-tab]'));
    const panels = Array.from(shell.querySelectorAll('[data-nexdirectory-auth-panel]'));
    if (!tabs.length || !panels.length) {
      return;
    }

    const activate = function (key) {
      tabs.forEach((tab) => {
        const isActive = String(tab.getAttribute('data-nexdirectory-auth-tab') || '') === key;
        tab.classList.toggle('is-active', isActive);
        tab.setAttribute('aria-selected', isActive ? 'true' : 'false');
      });
      panels.forEach((panel) => {
        const isActive = String(panel.getAttribute('data-nexdirectory-auth-panel') || '') === key;
        panel.classList.toggle('is-active', isActive);
        if (isActive) {
          panel.removeAttribute('hidden');
        } else {
          panel.setAttribute('hidden', 'hidden');
        }
      });
    };

    tabs.forEach((tab) => {
      tab.addEventListener('click', function () {
        const key = String(tab.getAttribute('data-nexdirectory-auth-tab') || '').trim();
        if (!key) {
          return;
        }
        activate(key);
      });
    });

    const initial = String(shell.getAttribute('data-nexdirectory-default-tab') || '').trim();
    const allowed = ['login', 'signup', 'forgot', 'reset'];
    activate(allowed.indexOf(initial) >= 0 ? initial : 'login');
  }

  function normalizeMapDateLabel(event) {
    const shortLabel = String((event && event.date_short) || '').trim();
    if (shortLabel) {
      return shortLabel;
    }

    const startRaw = String((event && event.start) || '').trim();
    if (startRaw) {
      const isoCandidate = startRaw.includes('T') ? startRaw : startRaw.replace(' ', 'T');
      const timestamp = Date.parse(isoCandidate);
      if (!Number.isNaN(timestamp)) {
        const dateText = new Date(timestamp).toLocaleDateString([], {
          year: 'numeric',
          month: 'long',
          day: 'numeric'
        });
        const timeText = new Date(timestamp).toLocaleTimeString([], {
          hour: 'numeric',
          minute: '2-digit'
        }).toLowerCase();
        return (dateText + ' • ' + timeText).trim();
      }
    }

    const fallback = String((event && event.date) || '').trim();
    if (!fallback) {
      return '';
    }
    return fallback
      .replace(/\s*\(.*\)\s*$/u, '')
      .replace(/\s*-\s*.*$/u, '')
      .replace(/\s{2,}/gu, ' ')
      .trim();
  }

  function decodeHtmlEntities(value) {
    const raw = String(value || '');
    if (!raw || raw.indexOf('&') === -1) {
      return raw;
    }
    let decoded = raw;
    const decoder = document.createElement('textarea');
    for (let index = 0; index < 3; index += 1) {
      decoder.innerHTML = decoded;
      const next = String(decoder.value || decoded);
      if (!next || next === decoded) {
        break;
      }
      decoded = next;
      if (decoded.indexOf('&') === -1) {
        break;
      }
    }
    return decoded
      .replace(/&#8211;?/gi, '–')
      .replace(/&ndash;/gi, '–');
  }

  function normalizeMapTitle(value) {
    return decodeHtmlEntities(value).replace(/\s+/g, ' ').trim();
  }

  function normalizeMapLocationLabel(event) {
    const city = String((event && event.city) || '').trim();
    if (city) {
      return decodeHtmlEntities(city);
    }
    const raw = String((event && event.location) || '').trim();
    if (!raw) {
      return '';
    }
    const parts = raw.split(',').map((part) => String(part || '').trim()).filter(Boolean);
    if (parts.length >= 3) {
      return decodeHtmlEntities(parts[parts.length - 2]);
    }
    if (parts.length >= 1) {
      return decodeHtmlEntities(parts[0]);
    }
    return decodeHtmlEntities(raw);
  }

  function getSearchRecoveryConfig($container) {
    if (!$container || !$container.length) {
      return {};
    }
    const raw = String($container.attr('data-nexdirectory-recovery') || '').trim();
    if (!raw) {
      return {};
    }
    try {
      const parsed = JSON.parse(raw);
      return parsed && typeof parsed === 'object' ? parsed : {};
    } catch (err) {
      return {};
    }
  }

  function getSearchRecoveryFieldValue($field, fallback) {
    const rawFallback = fallback == null ? '' : String(fallback).trim();
    if (!$field || !$field.length) {
      return rawFallback;
    }
    if ($field.is('select')) {
      const selectedText = String($field.find('option:selected').first().text() || '').trim();
      if (selectedText) {
        return selectedText;
      }
    }
    if ($field.is(':radio') || $field.is(':checkbox')) {
      const $checked = $field.filter(':checked').first();
      if ($checked.length) {
        const $label = $checked.closest('label');
        const labelText = String($label.text() || '').replace(/\s+/g, ' ').trim();
        if (labelText) {
          return labelText;
        }
      }
    }
    const fieldValue = String($field.first().val() == null ? '' : $field.first().val()).trim();
    return fieldValue || rawFallback;
  }

  function createSearchRecoveryShell($target, config, titleId, hasActiveFilters) {
    const recoveryConfig = config && typeof config === 'object' ? config : {};
    const title = String(recoveryConfig.title || getMessage('noMapResults', 'No matching results found.')).trim();
    const activeDescription = String(recoveryConfig.description || '').trim();
    const emptyDescription = String(recoveryConfig.noFiltersDescription || '').trim();
    const description = hasActiveFilters ? activeDescription : (emptyDescription || activeDescription);

    $target
      .empty()
      .addClass('nexdirectory-empty-state nexdirectory-search-recovery')
      .attr('aria-labelledby', titleId);

    const $icon = $('<span>', {
      class: 'nexdirectory-search-recovery__icon',
      'aria-hidden': 'true'
    });
    $icon.html(
      '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" aria-hidden="true">' +
        '<circle cx="11" cy="11" r="7" stroke-width="1.8"></circle>' +
        '<path d="m20 20-4.25-4.25" stroke-width="1.8" stroke-linecap="round"></path>' +
        '<path d="M8.5 11h5" stroke-width="1.8" stroke-linecap="round"></path>' +
      '</svg>'
    );
    $target.append($icon);
    $target.append($('<h3>', {
      id: titleId,
      class: 'nexdirectory-search-recovery__title',
      text: title
    }));
    if (description) {
      $target.append($('<p>', {
        class: 'nexdirectory-search-recovery__description',
        text: description
      }));
    }
    return $target;
  }

  function appendSearchRecoverySection($target, heading, contentClass) {
    const $section = $('<div>', { class: 'nexdirectory-search-recovery__section' });
    const headingText = String(heading || '').trim();
    if (headingText) {
      $section.append($('<span>', {
        class: 'nexdirectory-search-recovery__heading',
        text: headingText
      }));
    }
    const $content = $('<div>', { class: contentClass });
    $section.append($content);
    $target.append($section);
    return $content;
  }

  function createSearchRecoveryChip(filterKey, label, value, actionAttribute, removeLabel) {
    const labelText = String(label || '').trim();
    const valueText = String(value || '').trim();
    const accessibleText = [String(removeLabel || 'Remove filter').trim(), labelText, valueText].filter(Boolean).join(': ');
    const $button = $('<button>', {
      type: 'button',
      class: 'nexdirectory-search-recovery__chip',
      'aria-label': accessibleText
    });
    $button.attr(actionAttribute, String(filterKey || ''));
    $button.append($('<span>', {
      class: 'nexdirectory-search-recovery__chip-value',
      text: labelText && valueText ? labelText + ': ' + valueText : (valueText || labelText)
    }));
    $button.append($('<span>', {
      class: 'nexdirectory-search-recovery__chip-remove',
      'aria-hidden': 'true',
      text: '×'
    }));
    return $button;
  }

  function createSearchRecoveryButton(label, actionAttribute, actionValue, primary) {
    const $button = $('<button>', {
      type: 'button',
      class: 'nexdirectory-search-recovery__button' + (primary ? ' nexdirectory-search-recovery__button--primary' : ''),
      text: String(label || '')
    });
    $button.attr(actionAttribute, String(actionValue || ''));
    return $button;
  }

  function createSearchRecoveryCategory(category, config, actionAttribute) {
    const slug = String((category && category.slug) || '').trim();
    const name = String((category && category.name) || '').trim();
    if (!slug || !name) {
      return null;
    }
    const browseLabel = String((config && config.browseLabel) || 'Browse').trim();
    const $button = $('<button>', {
      type: 'button',
      class: 'nexdirectory-search-recovery__category',
      text: name,
      'aria-label': (browseLabel + ' ' + name).trim()
    });
    $button.attr(actionAttribute, slug);
    return $button;
  }

  const MAP_RESULTS_LIST_SELECTOR = '[data-map-results-list], #nexdirectory-map-list';
  const MAP_LOAD_MORE_WRAP_SELECTOR = '[data-map-load-more-wrap], #nexdirectory-map-load-more-wrap';
  const MAP_LOAD_MORE_BUTTON_SELECTOR = '[data-map-load-more], #nexdirectory-map-load-more';

  function getMapListElement() {
    return $(MAP_RESULTS_LIST_SELECTOR).first();
  }

  function renderMapList(items, options) {
    const $list = getMapListElement();
    if (!$list.length) {
      return;
    }
    $list.attr('role', 'list');
    $list.attr('aria-live', 'polite');
    $list.removeAttr('aria-busy');
    $list.empty();

    if (!items.length) {
      const renderOptions = options && typeof options === 'object' ? options : {};
      const filters = renderOptions.filters && typeof renderOptions.filters === 'object' ? renderOptions.filters : {};
      const config = getSearchRecoveryConfig($list);
      const filterLabels = config.filterLabels && typeof config.filterLabels === 'object' ? config.filterLabels : {};
      const fieldSelectors = {
        s: '#nexdirectory-map-search',
        type: '#nexdirectory-map-type',
        mode: '#nexdirectory-map-mode',
        tag: '#nexdirectory-map-tag',
        price: '#nexdirectory-map-price',
        official: '#nexdirectory-map-official',
        dateFrom: '#nexdirectory-map-date',
        distanceKm: '#nexdirectory-map-distance'
      };
      const activeFilters = [];
      Object.keys(fieldSelectors).forEach(function (key) {
        const rawValue = filters[key] == null ? '' : String(filters[key]).trim();
        if (!rawValue) {
          return;
        }
        activeFilters.push({
          key: key,
          label: String(filterLabels[key] || key),
          value: getSearchRecoveryFieldValue($(fieldSelectors[key]).first(), rawValue)
        });
      });
      const dynamicFilterGroups = {};
      (Array.isArray(filters.dynamic) ? filters.dynamic : []).forEach(function (field) {
        const name = String((field && field.name) || '').trim();
        const value = String((field && field.value) || '').trim();
        if (!name || !value || name.indexOf('nex_filter[') !== 0) {
          return;
        }
        if (!dynamicFilterGroups[name]) {
          dynamicFilterGroups[name] = [];
        }
        if (dynamicFilterGroups[name].indexOf(value) === -1) {
          dynamicFilterGroups[name].push(value);
        }
      });
      Object.keys(dynamicFilterGroups).forEach(function (name) {
        const $field = $('[name]').filter(function () {
          return String($(this).attr('name') || '') === name;
        }).first();
        const $wrapper = $field.closest('[data-nexdirectory-dynamic-filter]');
        const explicitLabel = String($field.attr('aria-label') || '').trim();
        let label = explicitLabel || String($wrapper.children('label').first().text() || '').trim() || 'Custom filter';
        if (!explicitLabel && name.slice(-5) === '[min]') {
          label += ' minimum';
        } else if (!explicitLabel && name.slice(-5) === '[max]') {
          label += ' maximum';
        }
        const values = dynamicFilterGroups[name];
        activeFilters.push({
          key: name,
          label: label,
          value: values.map(function (value) {
            return getSearchRecoveryFieldValue($field, value);
          }).join(', ')
        });
      });

      const $empty = $('<div>', {
        role: 'listitem',
        'data-nexdirectory-map-recovery': '1'
      });
      createSearchRecoveryShell($empty, config, 'nexdirectory-map-empty-title', activeFilters.length > 0);

      if (activeFilters.length) {
        const $chips = appendSearchRecoverySection(
          $empty,
          config.removeHeading || 'Try removing a filter',
          'nexdirectory-search-recovery__chips'
        );
        activeFilters.forEach(function (filter) {
          $chips.append(createSearchRecoveryChip(
            filter.key,
            filter.label,
            filter.value,
            'data-nexdirectory-map-remove-filter',
            config.removeLabel || 'Remove filter'
          ));
        });
      }

      const $actions = appendSearchRecoverySection(
        $empty,
        config.alternativesHeading || 'Try another approach',
        'nexdirectory-search-recovery__actions'
      );
      if (renderOptions.canWiden === true) {
        $actions.append(createSearchRecoveryButton(
          config.widerArea || 'Search a wider area',
          'data-nexdirectory-map-recovery-action',
          'widen',
          false
        ));
      }
      if (renderOptions.canUseLocation === true) {
        $actions.append(createSearchRecoveryButton(
          config.nearMe || 'Find near me instead',
          'data-nexdirectory-map-recovery-action',
          'nearby',
          false
        ));
      }
      if (activeFilters.length) {
        $actions.append(createSearchRecoveryButton(
          config.clearAll || 'Reset and show all',
          'data-nexdirectory-map-recovery-action',
          'clear',
          true
        ));
      }
      if (!$actions.children().length) {
        $actions.closest('.nexdirectory-search-recovery__section').remove();
      }

      const popularCategories = Array.isArray(config.popularCategories) ? config.popularCategories : [];
      if (popularCategories.length && $('#nexdirectory-map-type').length) {
        const $popular = appendSearchRecoverySection(
          $empty,
          config.popularHeading || 'Popular categories',
          'nexdirectory-search-recovery__popular'
        );
        popularCategories.forEach(function (category) {
          const $category = createSearchRecoveryCategory(category, config, 'data-nexdirectory-map-popular-category');
          if ($category) {
            $popular.append($category);
          }
        });
        if (!$popular.children().length) {
          $popular.closest('.nexdirectory-search-recovery__section').remove();
        }
      }

      $list.append($empty);
      return;
    }

    const fallbackThumb = String(getConfig('fallbackThumbUrl', '') || '').trim();

    items.forEach((event) => {
      const isBoosted = String(event.boosted || '') === '1' || event.boosted === 1 || event.boosted === true;
      const hasVerified = String(event.verified_badge || '') === '1' || event.verified_badge === 1 || event.verified_badge === true;
      const boostHighlight = String(event.boost_highlight || '') === '1' || event.boost_highlight === 1 || event.boost_highlight === true;
      const $item = $('<article>', {
        class: 'map-card group flex min-w-0 flex-col overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-sm transition duration-200 hover:-translate-y-0.5 hover:border-orange-300 hover:shadow-lg focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-orange-500'
          + (isBoosted && boostHighlight ? ' map-card--boosted ring-1 ring-orange-200' : '')
          + (hasVerified ? ' map-card--verified ring-1 ring-teal-200' : ''),
        role: 'listitem',
        tabindex: 0
      });
      const eventTitle = normalizeMapTitle(event.title || '');
      const eventUrl = String(event.url || '').trim();
      if (eventUrl) {
        $item.attr('data-nexdirectory-card-url', eventUrl);
        $item.addClass('cursor-pointer');
      }
      const thumbSrc = String(event.cover || '').trim() || fallbackThumb;
      if (thumbSrc) {
        const $thumb = $('<div>', { class: 'map-card-thumb relative h-44 w-full overflow-hidden bg-slate-100', 'aria-label': eventTitle });
        $thumb.append($('<img>', {
          src: thumbSrc,
          alt: event.title || '',
          loading: 'lazy',
          decoding: 'async',
          fetchpriority: 'low',
          class: 'h-full w-full object-cover transition duration-500 group-hover:scale-105'
        }));
        $item.append($thumb);
      } else {
        const initial = eventTitle ? eventTitle.charAt(0).toUpperCase() : 'E';
        const $thumbFallback = $('<div>', {
          class: 'map-card-thumb map-card-thumb--placeholder flex h-44 w-full items-center justify-center bg-slate-100 text-3xl font-bold text-slate-500',
          'aria-label': eventTitle
        });
        $thumbFallback.append($('<span>', { text: initial }));
        $item.append($thumbFallback);
      }

      const $body = $('<div>', { class: 'map-card-body flex min-h-0 flex-1 flex-col gap-2 p-4' });
      if (isBoosted) {
        $body.append($('<span>', {
          class: 'map-card-badge map-card-badge--featured inline-flex w-fit items-center rounded-full border border-orange-200 bg-orange-50 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-wide text-orange-700',
          text: String(event.boost_badge || getConfig('boostBadgeLabel', 'Featured'))
        }));
      }
      if (hasVerified) {
        $body.append($('<span>', {
          class: 'map-card-badge map-card-badge--verified inline-flex w-fit items-center rounded-full border border-teal-200 bg-teal-50 px-2.5 py-1 text-[10px] font-semibold uppercase tracking-wide text-teal-700',
          text: getMessage('verifiedLabel', 'Verified')
        }));
      }
      const $title = $('<h4>', { class: 'map-card-title m-0 text-base font-semibold leading-snug text-slate-900 break-words', text: eventTitle });
      $body.append($title);
      const eventDate = normalizeMapDateLabel(event);
      const eventLocation = normalizeMapLocationLabel(event);
      if (eventDate) {
        $body.append($('<p>', { class: 'map-card-date m-0 text-sm leading-snug text-slate-500', text: eventDate }));
      }
      if (eventLocation) {
        $body.append($('<p>', { class: 'map-card-location m-0 text-sm leading-snug text-slate-500', text: eventLocation }));
      }
      if (eventUrl) {
        $body.append($('<a>', {
          class: 'map-card-view mt-auto inline-flex min-h-10 w-full items-center justify-center rounded-xl border border-slate-200 bg-slate-50 px-3 py-2 text-sm font-semibold text-slate-700 no-underline transition hover:border-orange-300 hover:bg-orange-50 hover:text-orange-700',
          href: eventUrl,
          text: getMessage('viewDetails', 'View Details')
        }));
      }
      $item.append($body);
      $list.append($item);
    });

    initScrollReveal();
  }

  function initMap() {
    const $mapEl = $('#nexdirectory-events-map');
    const $listEl = getMapListElement();
    const hasMapContainer = $mapEl.length > 0;
    const canUseLeaflet = typeof window.L !== 'undefined' && typeof window.L.map === 'function';
    if (!hasMapContainer && !$listEl.length) {
      return;
    }
    if (hasMapContainer && !canUseLeaflet) {
      const $mapPanel = $mapEl.closest('[data-nexdirectory-map-panel]');
      if ($mapPanel.length) {
        $mapPanel.attr('hidden', 'hidden');
      } else {
        $mapEl.attr('hidden', 'hidden');
      }
    }

    const $layout = (hasMapContainer ? $mapEl.closest('[data-nexdirectory-map-layout="1"]') : $('[data-nexdirectory-map-layout="1"]').first());
    const filterPanel = document.getElementById('nexdirectory-map-sidebar-panel');
    const filterBackdrop = document.getElementById('nexdirectory-map-sidebar-backdrop');
    const filterOpenBtn = document.getElementById('nexdirectory-map-filters-open');
    const filterCloseBtn = document.getElementById('nexdirectory-map-filters-close');
    const filterApplyBtn = document.getElementById('nexdirectory-map-filters-apply');
    const mobileMediaQuery = window.matchMedia ? window.matchMedia('(max-width: 1023px)') : null;
    const drawerMode = !!($layout.length && String($layout.attr('data-nexdirectory-mobile-filter-mode') || '').trim() === 'drawer');
    function isMobileFilterMode() {
      return !!(drawerMode && mobileMediaQuery && mobileMediaQuery.matches);
    }
    function closeFilterPanel(force) {
      if (!filterPanel) {
        return;
      }
      if (!drawerMode) {
        return;
      }
      if (!force && !isMobileFilterMode()) {
        return;
      }
      filterPanel.classList.remove('is-open');
      window.dispatchEvent(new CustomEvent('nexdirectory-map-filters-close'));
      if (filterBackdrop) {
        filterBackdrop.classList.remove('is-open');
        filterBackdrop.setAttribute('hidden', 'hidden');
      }
      if (filterOpenBtn) {
        filterOpenBtn.setAttribute('aria-expanded', 'false');
      }
      filterPanel.setAttribute('aria-hidden', 'true');
      document.body.classList.remove('nexdirectory-map-filters-open');
    }
    function openFilterPanel() {
      if (!filterPanel || !isMobileFilterMode()) {
        return;
      }
      filterPanel.classList.add('is-open');
      filterPanel.setAttribute('aria-hidden', 'false');
      if (filterBackdrop) {
        filterBackdrop.removeAttribute('hidden');
        window.requestAnimationFrame(function () {
          filterBackdrop.classList.add('is-open');
        });
      }
      if (filterOpenBtn) {
        filterOpenBtn.setAttribute('aria-expanded', 'true');
      }
      document.body.classList.add('nexdirectory-map-filters-open');
    }
    function maybeCloseFilterPanel() {
      if (isMobileFilterMode()) {
        closeFilterPanel(true);
      }
    }
    if (filterPanel && filterPanel.getAttribute('data-nexdirectory-panel-bound') !== '1') {
      filterPanel.setAttribute('data-nexdirectory-panel-bound', '1');
      if (drawerMode) {
        filterPanel.setAttribute('aria-hidden', isMobileFilterMode() ? 'true' : 'false');
      }
      if (filterOpenBtn) {
        filterOpenBtn.addEventListener('click', function () {
          openFilterPanel();
        });
      }
      if (filterCloseBtn) {
        filterCloseBtn.addEventListener('click', function () {
          closeFilterPanel(true);
        });
      }
      if (filterBackdrop) {
        filterBackdrop.addEventListener('click', function () {
          closeFilterPanel(true);
        });
      }
      if (filterApplyBtn) {
        filterApplyBtn.addEventListener('click', function () {
          fetchEvents({ reset: true, closeFilters: true });
          closeFilterPanel(true);
        });
      }
      document.addEventListener('keydown', function (event) {
        if (event && event.key === 'Escape') {
          closeFilterPanel(true);
        }
      });
      if (mobileMediaQuery) {
        const mediaHandler = function (event) {
          if (!event.matches) {
            closeFilterPanel(true);
            if (filterPanel) {
              filterPanel.setAttribute('aria-hidden', 'false');
            }
          } else if (drawerMode && filterPanel) {
            filterPanel.setAttribute('aria-hidden', 'true');
          }
        };
        if (typeof mobileMediaQuery.addEventListener === 'function') {
          mobileMediaQuery.addEventListener('change', mediaHandler);
        } else if (typeof mobileMediaQuery.addListener === 'function') {
          mobileMediaQuery.addListener(mediaHandler);
        }
      }
    }
    const maxEvents = parseInt($layout.attr('data-nexdirectory-map-max-events') || '200', 10);
    const hasMap = hasMapContainer && canUseLeaflet;
    let map = null;
    let markerLayer = null;
    const mapFocusPreset = getMapFocusPreset();
    const defaultMapCenter = mapFocusPreset && Array.isArray(mapFocusPreset.center) && mapFocusPreset.center.length === 2
      ? { lat: Number(mapFocusPreset.center[0]), lng: Number(mapFocusPreset.center[1]) }
      : { lat: 20, lng: 0 };
    const defaultMapZoomRaw = mapFocusPreset ? Number(mapFocusPreset.zoom) : 2;
    const defaultMapZoom = Number.isFinite(defaultMapZoomRaw) ? defaultMapZoomRaw : 2;
    let mapFocusBounds = null;
    let mapFocusMinZoom = null;
    const pinUrl = String(getConfig('mapPinUrl', '') || '').trim();
    const markerOptions = {};
    let currentCenter = { lat: defaultMapCenter.lat, lng: defaultMapCenter.lng };

    function applyMapFocusConstraints(forceCenter) {
      if (!hasMap || !map || !mapFocusPreset || typeof window.L === 'undefined' || !Array.isArray(mapFocusPreset.bounds) || mapFocusPreset.bounds.length !== 2) {
        return;
      }
      if (!mapFocusBounds) {
        mapFocusBounds = L.latLngBounds(mapFocusPreset.bounds);
      }
      if (!mapFocusBounds || (typeof mapFocusBounds.isValid === 'function' && !mapFocusBounds.isValid())) {
        return;
      }

      if (typeof map.setMaxBounds === 'function') {
        map.setMaxBounds(mapFocusBounds);
      }
      if (typeof map.getBoundsZoom === 'function') {
        const minZoomCandidate = map.getBoundsZoom(mapFocusBounds, false);
        if (Number.isFinite(minZoomCandidate)) {
          mapFocusMinZoom = Math.max(1, Math.floor(minZoomCandidate));
          if (typeof map.setMinZoom === 'function') {
            map.setMinZoom(mapFocusMinZoom);
          }
          if (typeof map.getZoom === 'function' && typeof map.setZoom === 'function' && map.getZoom() < mapFocusMinZoom) {
            map.setZoom(mapFocusMinZoom);
          }
        }
      }
      if (forceCenter === true) {
        if (typeof map.panInsideBounds === 'function') {
          map.panInsideBounds(mapFocusBounds, { animate: false });
        }
      }
    }
    if (pinUrl && hasMap) {
      markerOptions.icon = L.icon({
        iconUrl: pinUrl,
        iconSize: [36, 48],
        iconAnchor: [18, 46],
        popupAnchor: [0, -42]
      });
    }
    if (hasMap) {
      const mapOptions = {};
      if (mapFocusPreset) {
        mapOptions.maxBoundsViscosity = 1.0;
      }
      map = L.map('nexdirectory-events-map', mapOptions).setView([defaultMapCenter.lat, defaultMapCenter.lng], defaultMapZoom);
      const mapContainer = map.getContainer ? map.getContainer() : document.getElementById('nexdirectory-events-map');
      if (mapContainer && mapContainer.getAttribute('data-nexdirectory-kbd-bound') !== '1') {
        mapContainer.setAttribute('data-nexdirectory-kbd-bound', '1');
        mapContainer.setAttribute('tabindex', mapContainer.getAttribute('tabindex') || '0');
        mapContainer.setAttribute('role', mapContainer.getAttribute('role') || 'region');
        if (!mapContainer.getAttribute('aria-label')) {
          mapContainer.setAttribute('aria-label', String(getMessage('mapAriaLabel', 'Locations map')));
        }
        mapContainer.addEventListener('keydown', function (event) {
          if (!event) {
            return;
          }
          const key = String(event.key || '');
          if (key === 'ArrowUp') {
            event.preventDefault();
            map.panBy([0, -120]);
            return;
          }
          if (key === 'ArrowDown') {
            event.preventDefault();
            map.panBy([0, 120]);
            return;
          }
          if (key === 'ArrowLeft') {
            event.preventDefault();
            map.panBy([-120, 0]);
            return;
          }
          if (key === 'ArrowRight') {
            event.preventDefault();
            map.panBy([120, 0]);
            return;
          }
          if (key === '+' || key === '=' || key === 'Add') {
            event.preventDefault();
            map.zoomIn();
            return;
          }
          if (key === '-' || key === '_' || key === 'Subtract') {
            event.preventDefault();
            map.zoomOut();
            return;
          }
          if (key === 'Home') {
            event.preventDefault();
            map.setView([defaultMapCenter.lat, defaultMapCenter.lng], defaultMapZoom);
            applyMapFocusConstraints(true);
          }
        });
      }
      const tileConfig = buildMapTileLayerConfig();
      L.tileLayer(tileConfig.url, tileConfig.options || {}).addTo(map);
      applyMapFocusConstraints(true);
      markerLayer = (typeof L.markerClusterGroup === 'function') ? L.markerClusterGroup() : L.layerGroup();
      markerLayer.addTo(map);
    }

    let hasInitialBounds = false;
    let fetchTimer = null;
    let loading = false;
    let pendingFetchOptions = null;
    let currentPage = 1;
    let hasMorePages = false;
    let aggregatedEvents = [];
    let lastResultTotal = 0;
    const perPageRaw = parseInt(getConfig('mapResultsPerPage', 120), 10);
    const perPage = Number.isFinite(perPageRaw) ? Math.max(12, Math.min(250, perPageRaw)) : 120;
    const $mapList = getMapListElement();
    const $mapSort = $('#nexdirectory-map-sort').first();
    const $resultsCount = $('#nexdirectory-map-results-count').first();
    const mobileSliderContainer = document.getElementById('nexdirectory-mobile-slider-container');
    let $loadMoreWrap = $(MAP_LOAD_MORE_WRAP_SELECTOR).first();
    let markerEntries = [];
    let activeMobileEventId = null;
    let mobileSliderProgrammaticScrollUntil = 0;
    let mobileSliderScrollRaf = 0;
    let suppressMapMoveFetchUntil = 0;
    let mapFocusAnimationToken = 0;
    let focusMapResultsAfterRecovery = false;
    let mobileSliderLoopState = {
      enabled: false,
      loopSize: 0,
      realCount: 0
    };
    if (!$loadMoreWrap.length && $mapList.length) {
      $loadMoreWrap = $('<div>', {
        id: 'map-load-more-wrap',
        class: 'mt-4 flex justify-center',
        'data-map-load-more-wrap': '1',
        hidden: 'hidden'
      });
      $loadMoreWrap.append($('<button>', {
        type: 'button',
        id: 'map-load-more',
        'data-map-load-more': '1',
        class: 'inline-flex min-w-[190px] items-center justify-center rounded-xl border border-gray-300 bg-white px-4 py-2.5 text-sm font-semibold text-gray-700 transition hover:bg-gray-50',
        text: getMessage('mapLoadMore', 'Load more results')
      }));
      $mapList.after($loadMoreWrap);
    }
    const $loadMoreBtn = $(MAP_LOAD_MORE_BUTTON_SELECTOR).first();

    function resetMobileSliderLoopState() {
      mobileSliderLoopState = {
        enabled: false,
        loopSize: 0,
        realCount: 0
      };
    }

    function getMobileSliderCards() {
      if (!mobileSliderContainer) {
        return [];
      }
      return Array.prototype.slice.call(
        mobileSliderContainer.querySelectorAll('[data-nexdirectory-mobile-card-index]')
      );
    }

    function getSliderContainerCenter() {
      if (!mobileSliderContainer) {
        return 0;
      }
      return mobileSliderContainer.scrollLeft + (mobileSliderContainer.clientWidth / 2);
    }

    function getCardCenter(card) {
      if (!card) {
        return 0;
      }
      return card.offsetLeft + (card.offsetWidth / 2);
    }

    function getClosestCardToCenter(cards) {
      const list = Array.isArray(cards) ? cards : [];
      if (!list.length) {
        return null;
      }
      const centerX = getSliderContainerCenter();
      let bestCard = null;
      let bestDistance = Number.POSITIVE_INFINITY;
      list.forEach(function (card) {
        const distance = Math.abs(getCardCenter(card) - centerX);
        if (distance < bestDistance) {
          bestDistance = distance;
          bestCard = card;
        }
      });
      return bestCard;
    }

    function centerMobileSliderOnCard(card, smooth) {
      if (!mobileSliderContainer || !card) {
        return;
      }
      const targetLeft = card.offsetLeft - Math.max(0, ((mobileSliderContainer.clientWidth - card.offsetWidth) / 2));
      if (smooth && typeof mobileSliderContainer.scrollTo === 'function') {
        mobileSliderContainer.scrollTo({
          left: targetLeft,
          behavior: 'smooth'
        });
        return;
      }
      mobileSliderContainer.scrollLeft = targetLeft;
    }

    function maybeRecenterMobileSliderLoop() {
      if (!mobileSliderContainer || !mobileSliderLoopState.enabled || mobileSliderLoopState.realCount < 2) {
        return false;
      }
      const cards = getMobileSliderCards();
      if (!cards.length) {
        return false;
      }
      const centeredCard = getClosestCardToCenter(cards);
      if (!centeredCard) {
        return false;
      }
      const centeredIndex = parseInt(centeredCard.getAttribute('data-nexdirectory-mobile-card-index') || '', 10);
      if (!Number.isFinite(centeredIndex)) {
        return false;
      }
      const firstRealIndex = mobileSliderLoopState.loopSize;
      const afterRealIndex = firstRealIndex + mobileSliderLoopState.realCount;
      let targetIndex = null;
      if (centeredIndex < firstRealIndex) {
        targetIndex = centeredIndex + mobileSliderLoopState.realCount;
      } else if (centeredIndex >= afterRealIndex) {
        targetIndex = centeredIndex - mobileSliderLoopState.realCount;
      }
      if (!Number.isFinite(targetIndex) || targetIndex < 0 || targetIndex >= cards.length) {
        return false;
      }
      const targetCard = cards[targetIndex];
      if (!targetCard) {
        return false;
      }
      mobileSliderProgrammaticScrollUntil = Date.now() + 140;
      centerMobileSliderOnCard(targetCard, false);
      return true;
    }

    function setActiveMobileSliderCard(eventId, options) {
      if (!mobileSliderContainer) {
        return;
      }
      const opts = options && typeof options === 'object' ? options : {};
      const shouldScrollIntoView = opts.scrollIntoView !== false;
      const idText = String(eventId || '').trim();
      activeMobileEventId = idText || null;
      mobileSliderContainer.querySelectorAll('[data-nexdirectory-mobile-event-id]').forEach(function (card) {
        const isActive = idText !== '' && String(card.getAttribute('data-nexdirectory-mobile-event-id') || '') === idText;
        card.classList.toggle('ring-2', isActive);
        card.classList.toggle('ring-brand-400', isActive);
        card.classList.toggle('border-brand-300', isActive);
      });
      if (!idText) {
        return;
      }
      const matchingCards = Array.prototype.slice.call(
        mobileSliderContainer.querySelectorAll('[data-nexdirectory-mobile-event-id="' + idText + '"]')
      );
      const activeCard = getClosestCardToCenter(matchingCards) || matchingCards[0] || null;
      if (shouldScrollIntoView && activeCard) {
        mobileSliderProgrammaticScrollUntil = Date.now() + 520;
        centerMobileSliderOnCard(activeCard, true);
      }
    }

    function animateMapFocusToEntry(entry, options) {
      if (!hasMap || !map || !entry || !entry.marker || typeof entry.marker.getLatLng !== 'function') {
        return;
      }
      const opts = options && typeof options === 'object' ? options : {};
      const token = ++mapFocusAnimationToken;
      const markerLatLng = entry.marker.getLatLng();
      if (!markerLatLng) {
        return;
      }
      const currentZoom = (typeof map.getZoom === 'function') ? Number(map.getZoom()) : 10;
      const mapMinZoom = (typeof map.getMinZoom === 'function') ? Number(map.getMinZoom()) : 1;
      const effectiveMinZoom = Number.isFinite(mapMinZoom) ? mapMinZoom : 1;
      const detailZoomRaw = Number(getConfig('mapDetailFocusZoom', 12));
      const detailZoom = Number.isFinite(detailZoomRaw) ? Math.max(effectiveMinZoom, detailZoomRaw) : Math.max(effectiveMinZoom, 12);
      const zoomOutBy = 1.5;
      const zoomOutTarget = Math.max(effectiveMinZoom, Math.min(currentZoom, detailZoom) - zoomOutBy);
      const shouldOpenPopup = opts.openPopup === true;
      suppressMapMoveFetchUntil = Date.now() + 1800;
      if (typeof map.closePopup === 'function') {
        map.closePopup();
      }
      if (typeof map.flyTo === 'function') {
        map.flyTo(map.getCenter(), zoomOutTarget, {
          animate: true,
          duration: 0.22,
          easeLinearity: 0.45
        });
      } else if (typeof map.setView === 'function') {
        map.setView(map.getCenter(), zoomOutTarget, { animate: true });
      }
      window.setTimeout(function () {
        if (token !== mapFocusAnimationToken) {
          return;
        }
        if (typeof map.flyTo === 'function') {
          map.flyTo(markerLatLng, detailZoom, {
            animate: true,
            duration: 0.42,
            easeLinearity: 0.3
          });
        } else if (typeof map.setView === 'function') {
          map.setView(markerLatLng, detailZoom, { animate: true });
        }
        if (shouldOpenPopup && typeof entry.marker.openPopup === 'function') {
          window.setTimeout(function () {
            if (token !== mapFocusAnimationToken) {
              return;
            }
            entry.marker.openPopup();
          }, 180);
        }
      }, 150);
    }

    function focusMarkerEntry(entry, options) {
      if (!entry || !entry.event) {
        return;
      }
      const opts = options && typeof options === 'object' ? options : {};
      const idText = String((entry.id != null ? entry.id : (entry.event && entry.event.id)) || '').trim();
      setActiveMobileSliderCard(idText);
      animateMapFocusToEntry(entry, opts);
    }

    function getCenteredMobileSliderEntry() {
      if (!mobileSliderContainer) {
        return null;
      }
      const centeredCard = getClosestCardToCenter(getMobileSliderCards());
      if (!centeredCard) {
        return null;
      }
      const bestId = String(centeredCard.getAttribute('data-nexdirectory-mobile-event-id') || '').trim();
      if (!bestId) {
        return null;
      }
      return markerEntries.find(function (entry) {
        const entryId = String((entry && (entry.id != null ? entry.id : (entry.event && entry.event.id))) || '').trim();
        return entryId === bestId;
      }) || null;
    }

    function bindMobileSliderFocusSync() {
      if (!mobileSliderContainer || mobileSliderContainer.getAttribute('data-nexdirectory-focus-sync-bound') === '1') {
        return;
      }
      mobileSliderContainer.setAttribute('data-nexdirectory-focus-sync-bound', '1');
      const clearProgrammaticWindow = function () {
        mobileSliderProgrammaticScrollUntil = 0;
      };
      mobileSliderContainer.addEventListener('touchstart', clearProgrammaticWindow, { passive: true });
      mobileSliderContainer.addEventListener('mousedown', clearProgrammaticWindow, { passive: true });
      const onScroll = function () {
        if (mobileSliderScrollRaf) {
          window.cancelAnimationFrame(mobileSliderScrollRaf);
        }
        mobileSliderScrollRaf = window.requestAnimationFrame(function () {
          const isProgrammatic = Date.now() < mobileSliderProgrammaticScrollUntil;
          if (isProgrammatic) {
            return;
          }
          if (maybeRecenterMobileSliderLoop()) {
            return;
          }
          if (hasMap && map && typeof map.closePopup === 'function') {
            map.closePopup();
          }
          const centeredEntry = getCenteredMobileSliderEntry();
          if (!centeredEntry) {
            return;
          }
          const centeredId = String((centeredEntry.id != null ? centeredEntry.id : (centeredEntry.event && centeredEntry.event.id)) || '').trim();
          if (!centeredId || centeredId === activeMobileEventId) {
            return;
          }
          activeMobileEventId = centeredId;
          setActiveMobileSliderCard(centeredId, { scrollIntoView: false });
          animateMapFocusToEntry(centeredEntry, { openPopup: false });
        });
      };
      mobileSliderContainer.addEventListener('scroll', onScroll, { passive: true });
    }

    function renderMobileSlider(entries) {
      if (!mobileSliderContainer) {
        return;
      }
      const rows = Array.isArray(entries) ? entries : [];
      mobileSliderContainer.innerHTML = '';
      resetMobileSliderLoopState();
      if (!rows.length) {
        const emptyCard = document.createElement('article');
        emptyCard.className = 'min-w-[84vw] sm:min-w-[320px] snap-center bg-white/95 shadow-lg rounded-2xl border border-gray-200 p-4';
        emptyCard.textContent = getMessage('noMapResults', 'No matching results found.');
        mobileSliderContainer.appendChild(emptyCard);
        return;
      }

      const fallbackThumb = String(getConfig('fallbackThumbUrl', '') || '').trim();
      let renderedRows = rows.slice();
      if (rows.length > 1) {
        const loopSize = Math.max(1, Math.min(3, rows.length));
        const lead = rows.slice(rows.length - loopSize);
        const trail = rows.slice(0, loopSize);
        renderedRows = lead.concat(rows, trail);
        mobileSliderLoopState = {
          enabled: true,
          loopSize: loopSize,
          realCount: rows.length
        };
      }

      renderedRows.forEach(function (entry, renderedIndex) {
        const event = entry && entry.event ? entry.event : null;
        if (!event) {
          return;
        }
        const eventId = String((entry.id != null ? entry.id : event.id) || '').trim();
        const eventTitle = normalizeMapTitle(event.title || '') || getMessage('untitledEvent', 'Untitled event');
        const eventDate = normalizeMapDateLabel(event);
        const eventLocation = normalizeMapLocationLabel(event);
        const thumbSrc = String(event.cover || '').trim() || fallbackThumb;

        const card = document.createElement('article');
        card.className = 'min-w-[84vw] sm:min-w-[320px] snap-center bg-white shadow-xl rounded-2xl overflow-hidden flex flex-row h-[130px] border border-gray-100 transition-all cursor-pointer';
        card.setAttribute('tabindex', '0');
        card.setAttribute('data-nexdirectory-mobile-card-index', String(renderedIndex));
        if (eventId) {
          card.setAttribute('data-nexdirectory-mobile-event-id', eventId);
        }
        if (
          mobileSliderLoopState.enabled &&
          (
            renderedIndex < mobileSliderLoopState.loopSize ||
            renderedIndex >= (mobileSliderLoopState.loopSize + mobileSliderLoopState.realCount)
          )
        ) {
          card.setAttribute('data-nexdirectory-mobile-clone', '1');
          card.setAttribute('aria-hidden', 'true');
          card.setAttribute('tabindex', '-1');
        }

        const mediaWrap = document.createElement('div');
        mediaWrap.className = 'w-2/5 h-full relative flex-shrink-0 bg-slate-100';
        if (thumbSrc) {
          const image = document.createElement('img');
          image.src = thumbSrc;
          image.alt = eventTitle;
          image.className = 'w-full h-full object-cover';
          image.loading = 'lazy';
          image.decoding = 'async';
          mediaWrap.appendChild(image);
        } else {
          const placeholder = document.createElement('div');
          placeholder.className = 'w-full h-full flex items-center justify-center text-3xl font-semibold text-slate-400';
          placeholder.textContent = eventTitle.charAt(0).toUpperCase();
          mediaWrap.appendChild(placeholder);
        }

        const content = document.createElement('div');
        content.className = 'w-3/5 p-3.5 flex flex-col justify-center';

        const title = document.createElement('h3');
        title.className = 'font-bold text-gray-900 text-[15px] leading-snug mb-1.5 line-clamp-2';
        title.textContent = eventTitle;
        content.appendChild(title);

        if (eventDate) {
          const date = document.createElement('p');
          date.className = 'text-[12px] mb-1 text-gray-500 truncate';
          date.textContent = eventDate;
          content.appendChild(date);
        }
        if (eventLocation) {
          const location = document.createElement('p');
          location.className = 'text-[12px] text-gray-500 truncate';
          location.textContent = eventLocation;
          content.appendChild(location);
        }

        card.appendChild(mediaWrap);
        card.appendChild(content);
        card.addEventListener('click', function () {
          focusMarkerEntry(entry, { openPopup: true });
        });
        card.addEventListener('keydown', function (eventObj) {
          if (!eventObj) {
            return;
          }
          if (eventObj.key === 'Enter' || eventObj.key === ' ') {
            eventObj.preventDefault();
            focusMarkerEntry(entry, { openPopup: true });
          }
        });

        mobileSliderContainer.appendChild(card);
      });

      if (mobileSliderLoopState.enabled) {
        const cards = getMobileSliderCards();
        const firstRealCard = cards[mobileSliderLoopState.loopSize] || null;
        if (firstRealCard) {
          centerMobileSliderOnCard(firstRealCard, false);
        }
      }

      if (activeMobileEventId) {
        setActiveMobileSliderCard(activeMobileEventId);
        const stillExists = !!mobileSliderContainer.querySelector('[data-nexdirectory-mobile-event-id="' + activeMobileEventId + '"]');
        if (!stillExists) {
          activeMobileEventId = null;
        }
      }
      if (!activeMobileEventId) {
        const initiallyCenteredEntry = getCenteredMobileSliderEntry() || rows[0] || null;
        if (initiallyCenteredEntry && initiallyCenteredEntry.event) {
          const firstId = String((initiallyCenteredEntry.id != null ? initiallyCenteredEntry.id : initiallyCenteredEntry.event.id) || '').trim();
          if (firstId) {
            activeMobileEventId = firstId;
            setActiveMobileSliderCard(firstId);
            animateMapFocusToEntry(initiallyCenteredEntry, { openPopup: false });
          }
        }
      }
      bindMobileSliderFocusSync();
    }

    function syncMobileSliderToMapBounds() {
      if (!mobileSliderContainer) {
        return;
      }
      renderMobileSlider(markerEntries);
    }

    function setLoading(state, preserveList) {
      loading = state;
      if (hasMapContainer) {
        $mapEl.toggleClass('opacity-70', state);
      }
      if ($mapList.length) {
        if (state) {
          $mapList.attr('aria-busy', 'true');
          if (preserveList !== true) {
            $mapList.empty().append($('<p>', {
              class: 'col-span-full m-0 rounded-xl border border-gray-200 bg-white p-4 text-sm text-gray-500',
              text: getMessage('mapLoading', 'Loading events...')
            }));
          }
        } else {
          $mapList.removeAttr('aria-busy');
        }
      }
      if ($loadMoreBtn.length) {
        $loadMoreBtn.prop('disabled', state).toggleClass('opacity-60 cursor-not-allowed', state);
      }
    }

    function updateLoadMoreUi() {
      if (!$loadMoreWrap.length) {
        return;
      }
      if (hasMorePages && aggregatedEvents.length > 0) {
        $loadMoreWrap.removeAttr('hidden');
        if ($loadMoreBtn.length) {
          $loadMoreBtn.text(getMessage('mapLoadMore', 'Load more results'));
        }
        return;
      }
      $loadMoreWrap.attr('hidden', 'hidden');
      if ($loadMoreBtn.length && aggregatedEvents.length > 0) {
        $loadMoreBtn.text(getMessage('mapLoadMoreDone', 'No more results'));
      }
    }

    function getDynamicMapFilterFields() {
      if (!filterPanel) {
        return $();
      }
      return $(filterPanel).find('[name^="nex_filter["]');
    }

    function collectDynamicMapFilters() {
      const fields = [];
      getDynamicMapFilterFields().each(function () {
        const $field = $(this);
        const name = String($field.attr('name') || '');
        if (!name || $field.prop('disabled')) {
          return;
        }
        if (($field.is(':checkbox') || $field.is(':radio')) && !$field.prop('checked')) {
          return;
        }
        const value = $field.val();
        const values = Array.isArray(value) ? value : [value];
        values.forEach(function (item) {
          const normalized = item == null ? '' : String(item).trim();
          if (normalized !== '') {
            fields.push({ name: name, value: normalized });
          }
        });
      });
      return fields;
    }

    function clearDynamicMapFilterValues() {
      getDynamicMapFilterFields().each(function () {
        const $field = $(this);
        if ($field.is(':checkbox') || $field.is(':radio')) {
          $field.prop('checked', false);
        } else if ($field.is('select[multiple]')) {
          $field.val([]);
        } else {
          $field.val('');
        }
      });
    }

    function clearDynamicMapFilterValue(name) {
      const normalized = String(name || '');
      if (normalized.indexOf('nex_filter[') !== 0) {
        return false;
      }
      const $matches = getDynamicMapFilterFields().filter(function () {
        return String($(this).attr('name') || '') === normalized;
      });
      if (!$matches.length) {
        return false;
      }
      $matches.each(function () {
        const $field = $(this);
        if ($field.is(':checkbox') || $field.is(':radio')) {
          $field.prop('checked', false);
        } else if ($field.is('select[multiple]')) {
          $field.val([]);
        } else {
          $field.val('');
        }
      });
      return true;
    }

    function collectFilters() {
      return {
        s: ($('#nexdirectory-map-search').val() || '').toString().trim(),
        type: ($('#nexdirectory-map-type').val() || '').toString().trim(),
        mode: ($('#nexdirectory-map-mode').val() || '').toString().trim(),
        tag: ($('#nexdirectory-map-tag').val() || '').toString().trim(),
        price: ($('#nexdirectory-map-price').val() || '').toString().trim(),
        official: ($('#nexdirectory-map-official').val() || '').toString().trim(),
        dateFrom: ($('#nexdirectory-map-date').val() || '').toString().trim(),
        distanceKm: ($('#nexdirectory-map-distance').val() || '').toString().trim(),
        dynamic: collectDynamicMapFilters()
      };
    }

    const mapFilterSelectors = {
      s: '#nexdirectory-map-search',
      type: '#nexdirectory-map-type',
      mode: '#nexdirectory-map-mode',
      tag: '#nexdirectory-map-tag',
      price: '#nexdirectory-map-price',
      official: '#nexdirectory-map-official',
      dateFrom: '#nexdirectory-map-date',
      distanceKm: '#nexdirectory-map-distance'
    };

    function setMapFilterValue(key, value) {
      const selector = mapFilterSelectors[key];
      if (!selector) {
        return;
      }
      const $field = $(selector).first();
      if ($field.length) {
        $field.val(value == null ? '' : String(value));
      }
    }

    function clearMapFilterValues() {
      Object.keys(mapFilterSelectors).forEach(function (key) {
        setMapFilterValue(key, '');
      });
      clearDynamicMapFilterValues();
    }

    function focusMapResultsRegion() {
      if (!$mapList.length) {
        return;
      }
      const node = $mapList.get(0);
      window.requestAnimationFrame(function () {
        if (!node || typeof node.focus !== 'function') {
          return;
        }
        try {
          node.focus({ preventScroll: true });
        } catch (err) {
          node.focus();
        }
      });
    }

    function buildMapRequestUrl(requestPage, includeUnlocated) {
      const base = String(getConfig('mapRestUrl', '') || '');
      if (!base) {
        return '';
      }
      const bounds = hasMap && map ? map.getBounds() : null;
      const center = hasMap && map ? map.getCenter() : currentCenter;
      const filters = collectFilters();
      const params = new URLSearchParams();
      if (bounds) {
        params.set('north', bounds.getNorth().toFixed(6));
        params.set('south', bounds.getSouth().toFixed(6));
        params.set('east', bounds.getEast().toFixed(6));
        params.set('west', bounds.getWest().toFixed(6));
        currentCenter = { lat: center.lat, lng: center.lng };
      } else {
        params.set('north', '85.000000');
        params.set('south', '-85.000000');
        params.set('east', '180.000000');
        params.set('west', '-180.000000');
      }
      params.set('max_events', String(Number.isFinite(maxEvents) ? Math.max(1, maxEvents) : 200));
      params.set('page', String(Math.max(1, parseInt(requestPage, 10) || 1)));
      params.set('per_page', String(perPage));
      if (includeUnlocated === true) params.set('include_unlocated', '1');
      if (filters.s) params.set('s', filters.s);
      if (filters.type) params.set('event_type', filters.type);
      if (filters.mode) params.set('event_mode', filters.mode);
      if (filters.tag) params.set('event_tag', filters.tag);
      if (filters.price) params.set('event_price', filters.price);
      if (filters.official) params.set('official', filters.official);
      if (filters.dateFrom) params.set('date_from', filters.dateFrom);
      filters.dynamic.forEach(function (field) {
        params.append(field.name, field.value);
      });
      if (filters.distanceKm) {
        const centerLatValue = Number(center && center.lat);
        const centerLngValue = Number(center && center.lng);
        const centerLat = Number.isFinite(centerLatValue) ? centerLatValue : Number(currentCenter.lat);
        const centerLng = Number.isFinite(centerLngValue) ? centerLngValue : Number(currentCenter.lng);
        params.set('distance_km', filters.distanceKm);
        params.set('center_lat', centerLat.toFixed(6));
        params.set('center_lng', centerLng.toFixed(6));
      }
      return base + (base.indexOf('?') === -1 ? '?' : '&') + params.toString();
    }

    function mergeEvents(existing, incoming) {
      const byId = {};
      (Array.isArray(existing) ? existing : []).forEach(function (event) {
        const id = parseInt(event && event.id, 10);
        if (Number.isFinite(id) && id > 0) {
          byId[id] = event;
        }
      });
      (Array.isArray(incoming) ? incoming : []).forEach(function (event) {
        const id = parseInt(event && event.id, 10);
        if (Number.isFinite(id) && id > 0) {
          byId[id] = event;
        }
      });
      return Object.keys(byId).map(function (key) {
        return byId[key];
      });
    }

    function sortMapEvents(events) {
      const rows = Array.isArray(events) ? events.slice() : [];
      const sortKey = String($mapSort.val() || 'nearest');
      const mapCenter = hasMap && map ? map.getCenter() : currentCenter;
      const centerLat = Number(mapCenter && mapCenter.lat);
      const centerLng = Number(mapCenter && mapCenter.lng);
      const distanceScore = function (event) {
        const lat = Number(event && event.lat);
        const lng = Number(event && event.lng);
        if (!Number.isFinite(lat) || !Number.isFinite(lng) || !Number.isFinite(centerLat) || !Number.isFinite(centerLng)) {
          return Number.POSITIVE_INFINITY;
        }
        const longitudeScale = Math.cos((centerLat * Math.PI) / 180);
        const latDelta = lat - centerLat;
        const lngDelta = (lng - centerLng) * longitudeScale;
        return (latDelta * latDelta) + (lngDelta * lngDelta);
      };
      rows.sort(function (left, right) {
        if (sortKey === 'title_asc') {
          return String((left && left.title) || '').localeCompare(String((right && right.title) || ''), undefined, {
            sensitivity: 'base',
            numeric: true
          });
        }
        if (sortKey === 'date_desc') {
          const leftTime = Date.parse(String((left && left.start) || '')) || 0;
          const rightTime = Date.parse(String((right && right.start) || '')) || 0;
          return rightTime - leftTime;
        }
        return distanceScore(left) - distanceScore(right);
      });
      return rows;
    }

    function updateMapResultsCount(total) {
      if (!$resultsCount.length) {
        return;
      }
      const count = Math.max(0, parseInt(total, 10) || 0);
      const singular = String($resultsCount.attr('data-singular') || getMessage('eventSingular', 'Event')).trim();
      const plural = String($resultsCount.attr('data-plural') || getMessage('eventPlural', 'Events')).trim();
      $resultsCount.text(String(count) + ' ' + (count === 1 ? singular : plural) + ' ' + (count === 1 ? 'found' : 'found'));
    }

    function drawEvents(events) {
      if (markerLayer && typeof markerLayer.clearLayers === 'function') {
        markerLayer.clearLayers();
      }
      markerEntries = [];
      activeMobileEventId = null;
      const list = [];
      const sortedEvents = sortMapEvents(events);

      sortedEvents.forEach((event) => {
        const lat = parseFloat(event.lat);
        const lng = parseFloat(event.lng);
        const eventId = parseInt(event && event.id, 10);
        const eventTitle = normalizeMapTitle(event && event.title ? event.title : '');
        list.push(event);
        if (hasMap) {
          if (!Number.isFinite(lat) || !Number.isFinite(lng)) {
            return;
          }
          const marker = L.marker([lat, lng], markerOptions);
          const popupUrl = String(event.url || '#').trim() || '#';
          const popupThumb = String(event.cover || '').trim() || String(getConfig('fallbackThumbUrl', '') || '').trim();
          const popupLocation = normalizeMapLocationLabel(event);
          const popupDate = normalizeMapDateLabel(event);
          const popupTitle = eventTitle || getMessage('untitledEvent', 'Untitled event');
          const popupParts = ['<article class="nexdirectory-map-popup-card">'];
          if (popupThumb) {
            popupParts.push(
              '<a class="nexdirectory-map-popup-image-link" href="' + escapeHtml(popupUrl) + '">' +
              '<img class="nexdirectory-map-popup-image" src="' + escapeHtml(popupThumb) + '" alt="' + escapeHtml(popupTitle) + '" loading="lazy" decoding="async" fetchpriority="low">' +
              '</a>'
            );
          }
          popupParts.push('<div class="nexdirectory-map-popup-body">');
          popupParts.push('<h3 class="nexdirectory-map-popup-title"><a href="' + escapeHtml(popupUrl) + '">' + escapeHtml(popupTitle) + '</a></h3>');
          if (popupDate || popupLocation) {
            popupParts.push('<div class="nexdirectory-map-popup-meta-row">');
            if (popupDate) {
              popupParts.push('<span class="nexdirectory-map-popup-meta-chip">' + escapeHtml(popupDate) + '</span>');
            }
            if (popupLocation) {
              popupParts.push('<span class="nexdirectory-map-popup-meta-chip">' + escapeHtml(popupLocation) + '</span>');
            }
            popupParts.push('</div>');
          }
          popupParts.push('<a class="nexdirectory-map-popup-cta" href="' + escapeHtml(popupUrl) + '" style="color:#ffffff !important;">' + escapeHtml(getMessage('viewDetails', 'View Details')) + '</a>');
          popupParts.push('</div></article>');
          marker.bindPopup(popupParts.join(''), {
            className: 'nexdirectory-map-popup-frame',
            minWidth: 260,
            maxWidth: 360
          });
          const entry = {
            id: Number.isFinite(eventId) ? eventId : null,
            event: event,
            marker: marker
          };
          marker.on('popupopen', function () {
            const entryId = entry.id != null ? entry.id : (entry.event && entry.event.id);
            setActiveMobileSliderCard(entryId);
          });
          markerEntries.push(entry);
          if (markerLayer) {
            markerLayer.addLayer(marker);
          }
        }
      });

      renderMapList(list, {
        filters: collectFilters(),
        canWiden: hasMap,
        canUseLocation: $('#nexdirectory-map-use-location').length > 0
      });
      syncMobileSliderToMapBounds();

      if (hasMap && list.length && !hasInitialBounds) {
        const bounds = markerLayer.getBounds && markerLayer.getBounds();
        if (bounds && bounds.isValid && bounds.isValid()) {
          map.fitBounds(bounds.pad(0.25));
          hasInitialBounds = true;
          window.requestAnimationFrame(syncMobileSliderToMapBounds);
        }
      }
    }

    function fetchEvents(options) {
      const opts = options && typeof options === 'object' ? options : {};
      const reset = opts.reset !== false;
      const closeFilters = opts.closeFilters === true;
      if (loading) {
        pendingFetchOptions = {
          reset: pendingFetchOptions ? pendingFetchOptions.reset !== false || reset : reset,
          closeFilters: pendingFetchOptions ? pendingFetchOptions.closeFilters === true || closeFilters : closeFilters,
          includeUnlocated: opts.includeUnlocated === true
        };
        return;
      }
      if (reset) {
        currentPage = 1;
      }
      const requestPage = Math.max(1, parseInt(currentPage, 10) || 1);
      const url = buildMapRequestUrl(requestPage, opts.includeUnlocated === true || !hasMap);
      if (!url) {
        return;
      }

      setLoading(true, !reset);
      fetch(url, {
        credentials: 'same-origin',
        headers: {
          'X-WP-Nonce': String(getConfig('restNonce', '') || '')
        }
      })
        .then(async (res) => {
          const payload = await res.json();
          if (!res.ok) {
            throw new Error((payload && payload.message) ? payload.message : getMessage('mapLoadFailed', 'Could not load map results.'));
          }
          const events = payload && Array.isArray(payload.events) ? payload.events : [];
          if (reset) {
            aggregatedEvents = events;
          } else {
            aggregatedEvents = mergeEvents(aggregatedEvents, events);
          }
          const pagination = payload && payload.pagination && typeof payload.pagination === 'object' ? payload.pagination : null;
          hasMorePages = !!(pagination && pagination.has_more === true);
          const filters = collectFilters();
          lastResultTotal = filters.distanceKm
            ? aggregatedEvents.length
            : Math.max(aggregatedEvents.length, parseInt(pagination && pagination.total, 10) || 0);
          drawEvents(aggregatedEvents);
          updateMapResultsCount(lastResultTotal);
          updateLoadMoreUi();
          if (focusMapResultsAfterRecovery) {
            focusMapResultsAfterRecovery = false;
            focusMapResultsRegion();
          }
        })
        .catch((err) => {
          showToast(err && err.message ? err.message : getMessage('mapLoadFailed', 'Could not load map results.'), 'error');
          if (reset) {
            aggregatedEvents = [];
            hasMorePages = false;
            lastResultTotal = 0;
            drawEvents([]);
            updateMapResultsCount(0);
          }
          updateLoadMoreUi();
          if (focusMapResultsAfterRecovery) {
            focusMapResultsAfterRecovery = false;
            focusMapResultsRegion();
          }
        })
        .finally(() => {
          setLoading(false, true);
          $('#nexdirectory-map-search-area').removeClass('ring-2 ring-orange-300 border-orange-300 text-orange-700 bg-orange-50');
          if (closeFilters) {
            maybeCloseFilterPanel();
          }
          if (pendingFetchOptions) {
            const nextOptions = pendingFetchOptions;
            pendingFetchOptions = null;
            fetchEvents(nextOptions);
          }
        });
    }

    function queueFetch(delay) {
      clearTimeout(fetchTimer);
      fetchTimer = window.setTimeout(function () {
        fetchEvents({ reset: true });
      }, delay || 250);
    }

    if ($('#nexdirectory-map-search').length) $('#nexdirectory-map-search').on('input', function () { queueFetch(300); });
    if ($('#nexdirectory-map-type').length) $('#nexdirectory-map-type').on('change', function () { queueFetch(200); maybeCloseFilterPanel(); });
    if ($('#nexdirectory-map-mode').length) $('#nexdirectory-map-mode').on('change', function () { queueFetch(200); maybeCloseFilterPanel(); });
    if ($('#nexdirectory-map-tag').length) $('#nexdirectory-map-tag').on('change', function () { queueFetch(200); maybeCloseFilterPanel(); });
    if ($('#nexdirectory-map-price').length) $('#nexdirectory-map-price').on('change', function () { queueFetch(200); maybeCloseFilterPanel(); });
    if ($('#nexdirectory-map-official').length) $('#nexdirectory-map-official').on('change', function () { queueFetch(200); maybeCloseFilterPanel(); });
    if ($('#nexdirectory-map-date').length) $('#nexdirectory-map-date').on('change', function () { queueFetch(200); maybeCloseFilterPanel(); });
    if ($('#nexdirectory-map-distance').length) $('#nexdirectory-map-distance').on('change', function () { queueFetch(200); maybeCloseFilterPanel(); });
    getDynamicMapFilterFields().on('change', function () { queueFetch(200); maybeCloseFilterPanel(); });
    if ($mapList.length) {
      $mapList
        .off('click.nexdirectoryMapRecovery')
        .on('click.nexdirectoryMapRecovery', '[data-nexdirectory-map-remove-filter]', function (event) {
          event.preventDefault();
          const key = String($(this).attr('data-nexdirectory-map-remove-filter') || '').trim();
          if (key.indexOf('nex_filter[') === 0) {
            if (!clearDynamicMapFilterValue(key)) {
              return;
            }
          } else if (mapFilterSelectors[key]) {
            setMapFilterValue(key, '');
          } else {
            return;
          }
          clearTimeout(fetchTimer);
          focusMapResultsAfterRecovery = true;
          fetchEvents({ reset: true });
        })
        .on('click.nexdirectoryMapRecovery', '[data-nexdirectory-map-popular-category]', function (event) {
          event.preventDefault();
          const category = String($(this).attr('data-nexdirectory-map-popular-category') || '').trim();
          if (!category || !$('#nexdirectory-map-type').length) {
            return;
          }
          clearTimeout(fetchTimer);
          clearMapFilterValues();
          setMapFilterValue('type', category);
          focusMapResultsAfterRecovery = true;
          fetchEvents({ reset: true });
        })
        .on('click.nexdirectoryMapRecovery', '[data-nexdirectory-map-recovery-action]', function (event) {
          event.preventDefault();
          const action = String($(this).attr('data-nexdirectory-map-recovery-action') || '').trim();
          clearTimeout(fetchTimer);
          if (action === 'nearby') {
            const $locationButton = $('#nexdirectory-map-use-location').first();
            if ($locationButton.length) {
              focusMapResultsAfterRecovery = true;
              $locationButton.trigger('click');
            }
            return;
          }
          if (action === 'widen' && hasMap && map) {
            focusMapResultsAfterRecovery = true;
            hasInitialBounds = true;
            suppressMapMoveFetchUntil = Date.now() + 1200;
            map.zoomOut(2, { animate: true });
            window.setTimeout(function () {
              fetchEvents({ reset: true });
            }, 260);
            return;
          }
          if (action === 'clear') {
            clearMapFilterValues();
            focusMapResultsAfterRecovery = true;
            hasInitialBounds = false;
            currentCenter = { lat: defaultMapCenter.lat, lng: defaultMapCenter.lng };
            if (hasMap && map) {
              suppressMapMoveFetchUntil = Date.now() + 1200;
              map.setView([defaultMapCenter.lat, defaultMapCenter.lng], defaultMapZoom, { animate: true });
              applyMapFocusConstraints(true);
              window.setTimeout(function () {
                fetchEvents({ reset: true });
              }, 260);
            } else {
              fetchEvents({ reset: true, includeUnlocated: true });
            }
          }
        });
    }
    if ($mapSort.length) {
      $mapSort.on('change', function () {
        drawEvents(aggregatedEvents);
        updateMapResultsCount(lastResultTotal);
      });
    }

    $('#nexdirectory-map-reset, #nexdirectory-map-reset-head').on('click', function () {
      clearMapFilterValues();
      queueFetch(80);
      maybeCloseFilterPanel();
    });

    $('#nexdirectory-map-apply').on('click', function () {
      fetchEvents({ reset: true, closeFilters: true });
      maybeCloseFilterPanel();
    });

    if ($loadMoreBtn.length) {
      $loadMoreBtn.on('click', function () {
        if (loading || !hasMorePages) {
          return;
        }
        currentPage += 1;
        fetchEvents({ reset: false, closeFilters: true });
      });
    }

    $('#nexdirectory-map-use-location').on('click', function () {
      const $btn = $(this);
      if (!$btn.length) {
        return;
      }
      if (!navigator.geolocation || typeof navigator.geolocation.getCurrentPosition !== 'function') {
        focusMapResultsAfterRecovery = false;
        showToast(getMessage('mapLocateUnsupported', 'Your browser does not support location services.'), 'error');
        return;
      }

      let $label = $btn.find('[data-nexdirectory-map-location-label]').first();
      if (!$label.length) {
        $btn.contents().filter(function () {
          return this.nodeType === 3 && String(this.nodeValue || '').trim() !== '';
        }).remove();
        $label = $('<span data-nexdirectory-map-location-label></span>').appendTo($btn);
      }
      const originalText = String($label.text() || '').trim() || getMessage('mapNearMeLabel', 'Find Near Me');
      const restoreLocationButton = function () {
        $btn.prop('disabled', false).removeClass('opacity-70 cursor-not-allowed');
        $label.text(originalText);
      };
      $btn.prop('disabled', true).addClass('opacity-70 cursor-not-allowed');
      $label.text(getMessage('mapLocateInProgress', 'Finding your location...'));
      navigator.geolocation.getCurrentPosition(function (pos) {
        if (!pos || !pos.coords) {
          focusMapResultsAfterRecovery = false;
          showToast(getMessage('mapLocateDenied', 'Could not access your location.'), 'error');
          restoreLocationButton();
          return;
        }
        const lat = parseFloat(pos.coords.latitude);
        const lng = parseFloat(pos.coords.longitude);
        if (!Number.isFinite(lat) || !Number.isFinite(lng)) {
          focusMapResultsAfterRecovery = false;
          showToast(getMessage('mapLocateDenied', 'Could not access your location.'), 'error');
          restoreLocationButton();
          return;
        }
        currentCenter = { lat: lat, lng: lng };
        hasInitialBounds = true;
        if (hasMap && map) {
          map.setView([lat, lng], 10);
          applyMapFocusConstraints(true);
        }
        queueFetch(150);
        maybeCloseFilterPanel();
        restoreLocationButton();
      }, function () {
        focusMapResultsAfterRecovery = false;
        showToast(getMessage('mapLocateDenied', 'Could not access your location.'), 'error');
        restoreLocationButton();
      }, {
        enableHighAccuracy: true,
        maximumAge: 60000,
        timeout: 10000
      });
    });

    $('#nexdirectory-map-search-area').on('click', function () {
      fetchEvents({ reset: true, closeFilters: true });
      maybeCloseFilterPanel();
    });

    if (hasMap && map) {
      map.on('moveend zoomend', function () {
        applyMapFocusConstraints(false);
        if (Date.now() < suppressMapMoveFetchUntil) {
          return;
        }
        if (getConfig('mapAutoSearch', true) === true) {
          queueFetch(500);
        } else {
          $('#nexdirectory-map-search-area').addClass('ring-2 ring-orange-300 border-orange-300 text-orange-700 bg-orange-50');
        }
      });

      // Handle map resize on window resize or potential tab switch
      window.addEventListener('resize', function() {
        map.invalidateSize();
        applyMapFocusConstraints(true);
      });
    }

    // Initial fetch
    fetchEvents({ reset: true });
  }

  function initGeocode() {
    const $btn = $('#nexdirectory-geocode-btn');
    if (!$btn.length) {
      return;
    }
    const $address = $('#nexdirectory-address-input');
    const $city = $('[name="nexdirectory_city"]');
    const $country = $('[name="nexdirectory_country"]');
    const $lat = $('#nexdirectory-lat-input');
    const $lng = $('#nexdirectory-lng-input');
    const endpoint = String(getConfig('geocodeRestUrl', '') || '');
    const suggestEndpoint = String(getConfig('addressSuggestRestUrl', '') || '');
    const btnDefaultText = ($btn.text() || '').trim() || 'Find Address On Map';
    let autoTimer = null;
    let lastAutoAddress = '';
    let suggestTimer = null;
    let suggestController = null;
    const suggestionMap = {};

    let previewMap = null;
    let previewMarker = null;
    const datalistId = 'nexdirectory-address-suggestions';
    let datalist = document.getElementById(datalistId);
    if (!datalist) {
      datalist = document.createElement('datalist');
      datalist.id = datalistId;
      document.body.appendChild(datalist);
    }
    if ($address.length) {
      $address.attr('list', datalistId);
    }

    function setSuggestions(items) {
      if (!datalist) {
        return;
      }
      datalist.innerHTML = '';
      Object.keys(suggestionMap).forEach(function (key) {
        delete suggestionMap[key];
      });
      (Array.isArray(items) ? items : []).forEach(function (item) {
        const label = String((item && item.label) || '').trim();
        if (!label) {
          return;
        }
        suggestionMap[label] = item;
        const option = document.createElement('option');
        option.value = label;
        datalist.appendChild(option);
      });
    }

    function fetchSuggestions(queryText) {
      const query = String(queryText || '').trim();
      if (query.length < 3 || !suggestEndpoint) {
        setSuggestions([]);
        return;
      }
      if (suggestController) {
        suggestController.abort();
      }
      suggestController = new AbortController();
      const qs = new URLSearchParams();
      qs.set('q', query);
      qs.set('limit', '6');
      fetch(suggestEndpoint + (suggestEndpoint.indexOf('?') === -1 ? '?' : '&') + qs.toString(), {
        credentials: 'same-origin',
        signal: suggestController.signal,
        headers: {
          'X-WP-Nonce': String(getConfig('restNonce', '') || '')
        }
      })
        .then(function (res) {
          return res.json().then(function (payload) {
            if (!res.ok) {
              throw new Error((payload && payload.message) ? payload.message : getMessage('addressSuggestFailed', 'Could not load address suggestions right now.'));
            }
            return payload;
          });
        })
        .then(function (payload) {
          setSuggestions(payload && Array.isArray(payload.suggestions) ? payload.suggestions : []);
        })
        .catch(function () {
          // silent - geocode still works without suggestions
        });
    }

    function updatePreviewMap(lat, lng) {
      if (typeof window.L === 'undefined' || !$('#nexdirectory-location-preview').length) {
        return;
      }
      const latNum = parseFloat(lat);
      const lngNum = parseFloat(lng);
      if (Number.isNaN(latNum) || Number.isNaN(lngNum)) {
        return;
      }
      if (!previewMap) {
        previewMap = L.map('nexdirectory-location-preview').setView([latNum, lngNum], 13);
        const previewTileConfig = buildMapTileLayerConfig();
        L.tileLayer(previewTileConfig.url, previewTileConfig.options || {}).addTo(previewMap);
        previewMarker = L.marker([latNum, lngNum], { draggable: true }).addTo(previewMap);
        previewMarker.on('dragend', function () {
          const pos = previewMarker.getLatLng();
          $lat.val(pos.lat.toFixed(6)).trigger('input');
          $lng.val(pos.lng.toFixed(6)).trigger('input');
        });
      } else {
        previewMap.setView([latNum, lngNum], 13);
        previewMarker.setLatLng([latNum, lngNum]);
      }
      window.setTimeout(function () {
        previewMap.invalidateSize();
      }, 50);
    }

    function applyGeocodePayload(payload) {
      if (!payload) {
        return;
      }
      if (Number.isFinite(parseFloat(payload.lat)) && Number.isFinite(parseFloat(payload.lng))) {
        $lat.val(String(payload.lat)).trigger('input');
        $lng.val(String(payload.lng)).trigger('input');
        updatePreviewMap(payload.lat, payload.lng);
      }
      if (payload.city) {
        $city.val(String(payload.city)).trigger('input');
      }
      if (payload.country) {
        $country.val(String(payload.country)).trigger('input');
      }
    }

    function geocodeAddress(address, silent) {
      if (!address) {
        if (!silent) {
          showToast(getMessage('enterAddress', 'Enter an address first.'), 'error');
        }
        return Promise.reject(new Error('missing_address'));
      }
      if (!endpoint) {
        if (!silent) {
          showToast(getMessage('geocodeFailed', 'Could not locate that address right now.'), 'error');
        }
        return Promise.reject(new Error('missing_endpoint'));
      }

      $btn.prop('disabled', true).addClass('nexdirectory-loading').text('Locating...');

      const qs = new URLSearchParams();
      qs.set('address', address);
      return fetch(endpoint + (endpoint.indexOf('?') === -1 ? '?' : '&') + qs.toString(), {
        credentials: 'same-origin',
        headers: {
          'X-WP-Nonce': String(getConfig('restNonce', '') || '')
        }
      })
        .then(async (res) => {
          const payload = await res.json();
          if (!res.ok) {
            throw new Error((payload && payload.message) ? payload.message : getMessage('geocodeNoResult', 'No location found for that address.'));
          }
          if (!payload || !Number.isFinite(parseFloat(payload.lat)) || !Number.isFinite(parseFloat(payload.lng))) {
            throw new Error(getMessage('geocodeNoResult', 'No location found for that address.'));
          }
          applyGeocodePayload(payload);
          return payload;
        })
        .catch((err) => {
          if (!silent) {
            showToast(err && err.message ? err.message : getMessage('geocodeFailed', 'Could not locate that address right now.'), 'error');
          }
          throw err;
        })
        .finally(() => {
          $btn.prop('disabled', false).removeClass('nexdirectory-loading').text(btnDefaultText);
        });
    }

    $lat.add($lng).on('change input', function () {
      updatePreviewMap($lat.val(), $lng.val());
    });

    $address.on('input', function () {
      clearTimeout(autoTimer);
      clearTimeout(suggestTimer);
      const current = String($address.val() || '').trim();
      suggestTimer = window.setTimeout(function () {
        fetchSuggestions(current);
      }, 180);
      if (current.length < 6) {
        return;
      }
      autoTimer = window.setTimeout(function () {
        if (current === lastAutoAddress) {
          return;
        }
        geocodeAddress(current, true)
          .then(() => {
            lastAutoAddress = current;
          })
          .catch(() => {});
      }, 550);
    });

    $address.on('blur', function () {
      const current = String($address.val() || '').trim();
      if (suggestionMap[current]) {
        const selected = suggestionMap[current];
        if (selected.city) {
          $city.val(String(selected.city)).trigger('input');
        }
        if (selected.country) {
          $country.val(String(selected.country)).trigger('input');
        }
        if (Number.isFinite(parseFloat(selected.lat)) && Number.isFinite(parseFloat(selected.lng))) {
          $lat.val(String(selected.lat)).trigger('input');
          $lng.val(String(selected.lng)).trigger('input');
          updatePreviewMap(selected.lat, selected.lng);
          lastAutoAddress = current;
          return;
        }
      }
      if (current.length < 6 || current === lastAutoAddress) {
        return;
      }
      geocodeAddress(current, true)
        .then(() => {
          lastAutoAddress = current;
        })
        .catch(() => {});
    });

    $address.on('change', function () {
      const current = String($address.val() || '').trim();
      if (!current) {
        return;
      }
      geocodeAddress(current, true)
        .then(() => {
          lastAutoAddress = current;
        })
        .catch(() => {});
    });

    $btn.on('click', function () {
      const address = ($address.val() || '').trim();
      geocodeAddress(address, false)
        .then(() => {
          lastAutoAddress = address;
        })
        .catch(() => {});
    });

    updatePreviewMap($lat.val(), $lng.val());
  }

  function initArchiveFiltersAjax() {
    const $form = $('.nexdirectory-archive-filter-form').first();
    const $results = $('#nexdirectory-archive-results');
    const $saveFiltersForm = $('.nexdirectory-save-filters-form').first();
    const $resetFilters = $form.find('.nexdirectory-reset-filters').first();
    const $saveFiltersLocal = $form.find('.nexdirectory-save-filters-local').first();
    const $useFiltersLocal = $form.find('.nexdirectory-use-filters-local').first();
    const endpoint = String(getConfig('archiveRestUrl', '') || '');
    const saveFiltersEndpoint = String(getConfig('saveFiltersRestUrl', '') || '');
    const currentUserId = parseInt(getConfig('currentUserId', 0), 10) || 0;
    if (!$form.length || !$results.length || !endpoint) {
      return;
    }

    let debounceTimer = null;
    let requestController = null;
    let focusArchiveResultsAfterRecovery = false;
    const localStorageKey = 'nexdirectory_archive_saved_filters';
    const archiveSearchQueryKey = 'nexdirectory_search';
    const filterKeys = ['s', 'event_type', 'event_mode', 'event_price', 'event_tag', 'city', 'date_from', 'official'];
    const selectDefaults = {};
    const $archivePage = $results.closest('.nexdirectory-archive-page');

    function isDynamicFilterKey(key) {
      return String(key || '').indexOf('nex_filter[') === 0;
    }

    $form.find('select[name]').each(function () {
      const key = String($(this).attr('name') || '');
      if (!key) {
        return;
      }
      const dataDefault = String($(this).attr('data-default-label') || '').trim();
      if (dataDefault) {
        selectDefaults[key] = dataDefault;
        return;
      }
      const firstText = String($(this).find('option').first().text() || '').trim();
      if (firstText) {
        selectDefaults[key] = firstText;
      }
    });

    function collectParams(extra) {
      const params = {};
      $form.serializeArray().forEach((field) => {
        const fieldName = String(field.name || '');
        const key = fieldName === archiveSearchQueryKey ? 's' : fieldName;
        const value = String(field.value || '');
        if (!key || value === '') {
          return;
        }
        params[key] = value;
      });
      const ppp = parseInt($results.attr('data-posts-per-page') || '12', 10);
      const organizer = parseInt($results.attr('data-organizer') || '0', 10);
      params.posts_per_page = Number.isFinite(ppp) && ppp > 0 ? ppp : 12;
      if (organizer > 0) {
        params.organizer = organizer;
      }
      if (!params.paged) {
        params.paged = 1;
      }
      if (extra && typeof extra === 'object') {
        Object.keys(extra).forEach((key) => {
          params[key] = extra[key];
        });
      }
      return params;
    }

    function hasActiveFilters(params) {
      const keysToCheck = ['s', 'event_type', 'event_mode', 'event_price', 'event_tag', 'city', 'date_from', 'official'];
      for (let i = 0; i < keysToCheck.length; i += 1) {
        const key = keysToCheck[i];
        if (String((params && params[key]) || '').trim() !== '') {
          return true;
        }
      }
      const dynamicKeys = Object.keys(params || {}).filter(isDynamicFilterKey);
      for (let i = 0; i < dynamicKeys.length; i += 1) {
        if (String(params[dynamicKeys[i]] || '').trim() !== '') {
          return true;
        }
      }
      return false;
    }

    function toggleHidden($el, hidden) {
      if (!$el || !$el.length) {
        return;
      }
      $el.toggleClass('nexdirectory-is-hidden', hidden === true);
      if (hidden) {
        $el.attr('aria-hidden', 'true');
      } else {
        $el.removeAttr('aria-hidden');
      }
    }

    function setArchiveFilterValue(key, value) {
      if (filterKeys.indexOf(key) === -1) {
        return;
      }
      const normalized = value == null ? '' : String(value);
      const fieldName = key === 's' ? archiveSearchQueryKey : key;
      const $scope = $archivePage.length ? $archivePage : $(document);
      $scope.find('[name="' + fieldName + '"]').each(function () {
        const $field = $(this);
        if ($field.is(':radio') || $field.is(':checkbox')) {
          $field.prop('checked', String($field.val() || '') === normalized);
          return;
        }
        $field.val(normalized);
      });
    }

    function clearDynamicArchiveFilterValues() {
      const $scope = $archivePage.length ? $archivePage : $form;
      $scope.find('[name^="nex_filter["]').each(function () {
        const $field = $(this);
        if ($field.is(':checkbox') || $field.is(':radio')) {
          $field.prop('checked', false);
        } else if ($field.is('select[multiple]')) {
          $field.val([]);
        } else {
          $field.val('');
        }
      });
    }

    function findArchiveFilterField(key) {
      const normalized = String(key || '');
      if (!normalized) {
        return $();
      }
      const $scope = $archivePage.length ? $archivePage : $form;
      return $scope.find('[name]').filter(function () {
        return String($(this).attr('name') || '') === normalized;
      }).first();
    }

    function clearDynamicArchiveFilterValue(key) {
      const $field = findArchiveFilterField(key);
      if (!$field.length) {
        return false;
      }
      const $scope = $archivePage.length ? $archivePage : $form;
      $scope.find('[name]').filter(function () {
        return String($(this).attr('name') || '') === String(key || '');
      }).each(function () {
        const $candidate = $(this);
        if ($candidate.is(':checkbox') || $candidate.is(':radio')) {
          $candidate.prop('checked', false);
        } else if ($candidate.is('select[multiple]')) {
          $candidate.val([]);
        } else {
          $candidate.val('');
        }
      });
      return true;
    }

    function getDynamicArchiveFilterLabel(key, $field) {
      const explicitLabel = String($field.attr('aria-label') || '').trim();
      if (explicitLabel) {
        return explicitLabel;
      }
      const $wrapper = $field.closest('[data-nexdirectory-dynamic-filter]');
      const wrapperLabel = String($wrapper.children('label').first().text() || '').trim();
      if (wrapperLabel) {
        if (String(key || '').slice(-5) === '[min]') {
          return wrapperLabel + ' minimum';
        }
        if (String(key || '').slice(-5) === '[max]') {
          return wrapperLabel + ' maximum';
        }
        return wrapperLabel;
      }
      return 'Custom filter';
    }

    function clearArchiveFilterValues() {
      filterKeys.forEach(function (key) {
        setArchiveFilterValue(key, '');
      });
      clearDynamicArchiveFilterValues();
      $form.find('input[name="paged"]').val('1');
    }

    function focusArchiveResultsRegion() {
      const node = $results.get(0);
      if (!node || typeof node.focus !== 'function') {
        return;
      }
      window.requestAnimationFrame(function () {
        try {
          node.focus({ preventScroll: true });
        } catch (err) {
          node.focus();
        }
      });
    }

    function enhanceArchiveEmptyState(params) {
      const $empty = $results.find('.nexdirectory-archive-empty-state').first();
      if (!$empty.length || $empty.attr('data-nexdirectory-recovery-enhanced') === '1') {
        return;
      }
      const config = getSearchRecoveryConfig($results);
      const filterLabels = config.filterLabels && typeof config.filterLabels === 'object' ? config.filterLabels : {};
      const activeFilters = [];
      filterKeys.forEach(function (key) {
        const rawValue = params && params[key] != null ? String(params[key]).trim() : '';
        if (!rawValue) {
          return;
        }
        const fieldName = key === 's' ? archiveSearchQueryKey : key;
        const $field = $form.find('[name="' + fieldName + '"]').first();
        activeFilters.push({
          key: key,
          label: String(filterLabels[key] || key),
          value: getSearchRecoveryFieldValue($field, rawValue)
        });
      });
      Object.keys(params || {}).filter(isDynamicFilterKey).forEach(function (key) {
        const rawValue = String(params[key] == null ? '' : params[key]).trim();
        if (!rawValue) {
          return;
        }
        const $field = findArchiveFilterField(key);
        activeFilters.push({
          key: key,
          label: getDynamicArchiveFilterLabel(key, $field),
          value: getSearchRecoveryFieldValue($field, rawValue)
        });
      });

      $empty.attr('data-nexdirectory-recovery-enhanced', '1');
      createSearchRecoveryShell($empty, config, 'nexdirectory-archive-empty-title', activeFilters.length > 0);

      if (activeFilters.length) {
        const $chips = appendSearchRecoverySection(
          $empty,
          config.removeHeading || 'Try removing a filter',
          'nexdirectory-search-recovery__chips'
        );
        activeFilters.forEach(function (filter) {
          $chips.append(createSearchRecoveryChip(
            filter.key,
            filter.label,
            filter.value,
            'data-nexdirectory-archive-remove-filter',
            config.removeLabel || 'Remove filter'
          ));
        });
      }

      const $actions = appendSearchRecoverySection(
        $empty,
        config.alternativesHeading || 'Broaden your search',
        'nexdirectory-search-recovery__actions'
      );
      if (params && String(params.city || '').trim()) {
        $actions.append(createSearchRecoveryButton(
          config.everywhere || 'Search every location',
          'data-nexdirectory-archive-recovery-action',
          'everywhere',
          false
        ));
      }
      if (params && String(params.date_from || '').trim()) {
        $actions.append(createSearchRecoveryButton(
          config.anyDate || 'Show any date',
          'data-nexdirectory-archive-recovery-action',
          'any-date',
          false
        ));
      }
      if (activeFilters.length) {
        $actions.append(createSearchRecoveryButton(
          config.clearAll || 'Clear filters and browse all',
          'data-nexdirectory-archive-recovery-action',
          'clear',
          true
        ));
      }
      if (!$actions.children().length) {
        $actions.closest('.nexdirectory-search-recovery__section').remove();
      }

      const popularCategories = Array.isArray(config.popularCategories) ? config.popularCategories : [];
      if (popularCategories.length && $form.find('[name="event_type"]').length) {
        const $popular = appendSearchRecoverySection(
          $empty,
          config.popularHeading || 'Popular categories',
          'nexdirectory-search-recovery__popular'
        );
        popularCategories.forEach(function (category) {
          const $category = createSearchRecoveryCategory(category, config, 'data-nexdirectory-archive-popular-category');
          if ($category) {
            $popular.append($category);
          }
        });
        if (!$popular.children().length) {
          $popular.closest('.nexdirectory-search-recovery__section').remove();
        }
      }
    }

    function syncFilterActionVisibility(params, serverActiveFlag) {
      const active = serverActiveFlag === true || hasActiveFilters(params);
      toggleHidden($resetFilters, !active);
      toggleHidden($saveFiltersForm, !active);
      toggleHidden($saveFiltersLocal, !active);
    }

    function syncSaveFiltersForm(params) {
      if (!$saveFiltersForm.length) {
        return;
      }
      ['s', 'event_type', 'event_mode', 'event_price', 'event_tag', 'city', 'date_from', 'official'].forEach((key) => {
        if (!$saveFiltersForm.find('input[name="' + key + '"]').length) {
          return;
        }
        $saveFiltersForm.find('input[name="' + key + '"]').val(String(params[key] || ''));
      });
    }

    function updateFilterSelect(name, options, params) {
      const $select = $form.find('select[name="' + name + '"]').first();
      if (!$select.length || !Array.isArray(options)) {
        return;
      }

      const defaultLabel = String($select.attr('data-default-label') || selectDefaults[name] || $select.find('option').first().text() || '').trim();
      const selectedValue = String((params && params[name]) || $select.val() || '').trim();
      const selectedText = String($select.find('option:selected').text() || '').trim();
      const seen = new Set();
      const prepared = [];

      options.forEach((option) => {
        const slug = String((option && option.slug) || '').trim();
        const label = String((option && option.name) || '').trim();
        const count = parseInt((option && option.count) || 0, 10);
        if (!slug || !label) {
          return;
        }
        if (count <= 0 && slug !== selectedValue) {
          return;
        }
        if (seen.has(slug)) {
          return;
        }
        seen.add(slug);
        prepared.push({ slug, label });
      });

      $select.empty();
      $select.append($('<option>', { value: '', text: defaultLabel || 'All' }));
      prepared.forEach((option) => {
        $select.append($('<option>', { value: option.slug, text: option.label }));
      });

      if (selectedValue) {
        if (seen.has(selectedValue)) {
          $select.val(selectedValue);
        } else {
          const fallbackText = selectedText || selectedValue;
          $select.append($('<option>', { value: selectedValue, text: fallbackText }));
          $select.val(selectedValue);
        }
      } else {
        $select.val('');
      }
    }

  function updateFilterOptions(payload, params) {
      if (!payload || typeof payload !== 'object' || !payload.filters || typeof payload.filters !== 'object') {
        return;
      }
      updateFilterSelect('event_type', payload.filters.event_type || [], params);
      updateFilterSelect('event_mode', payload.filters.event_mode || [], params);
      updateFilterSelect('event_price', payload.filters.event_price || [], params);
      updateFilterSelect('event_tag', payload.filters.event_tag || [], params);
    }

    function setLoading(state) {
      $results.toggleClass('nexdirectory-loading', state === true);
      if (state) {
        $results.attr('aria-busy', 'true');
      } else {
        $results.removeAttr('aria-busy');
      }
    }

    function decodeEscapedHtml(markup) {
      const source = String(markup || '');
      if (!source) {
        return '';
      }
      if (source.indexOf('<') !== -1) {
        return source;
      }
      if (source.indexOf('&lt;') === -1 && source.indexOf('&gt;') === -1) {
        return source;
      }
      const textarea = document.createElement('textarea');
      textarea.innerHTML = source;
      return String(textarea.value || source);
    }

    function updateUrl(params) {
      const current = new URL(window.location.href);
      ['s', archiveSearchQueryKey, 'event_type', 'event_mode', 'event_price', 'event_tag', 'city', 'date_from', 'official', 'paged', 'organizer', 'author'].forEach((key) => {
        current.searchParams.delete(key);
      });
      Array.from(new Set(Array.from(current.searchParams.keys()))).forEach((key) => {
        if (isDynamicFilterKey(key)) {
          current.searchParams.delete(key);
        }
      });
      Object.keys(params).forEach((key) => {
        if (!isDynamicFilterKey(key) && ['s', 'event_type', 'event_mode', 'event_price', 'event_tag', 'city', 'date_from', 'official', 'paged', 'organizer'].indexOf(key) === -1) {
          return;
        }
        const value = String(params[key] || '');
        if (!value || (key === 'paged' && value === '1')) {
          return;
        }
        current.searchParams.set(key === 's' ? archiveSearchQueryKey : key, value);
      });
      window.history.replaceState({}, '', current.toString());
    }

    function bindPagination() {
      $results.find('.nexdirectory-pagination a').off('click.nexdirectoryArchive').on('click.nexdirectoryArchive', function (e) {
        e.preventDefault();
        const href = String($(this).attr('href') || '');
        if (!href) {
          return;
        }
        const url = new URL(href, window.location.origin);
        let paged = parseInt(url.searchParams.get('paged') || '1', 10);
        if (!Number.isFinite(paged) || paged <= 1) {
          const match = url.pathname.match(/\/page\/(\d+)\/?$/i);
          if (match && match[1]) {
            paged = parseInt(match[1], 10);
          }
        }
        requestResults({ paged: Number.isFinite(paged) && paged > 0 ? paged : 1 });
      });
    }

    function requestResults(extra) {
      if (requestController) {
        requestController.abort();
      }
      const params = collectParams(extra);
      const query = new URLSearchParams();
      Object.keys(params).forEach((key) => {
        const value = String(params[key] || '');
        if (!value) {
          return;
        }
        query.set(key, value);
      });

      const currentController = new AbortController();
      requestController = currentController;
      setLoading(true);
      fetch(endpoint + '?' + query.toString(), {
        credentials: 'same-origin',
        signal: currentController.signal,
        headers: {
          'X-WP-Nonce': String(getConfig('restNonce', '') || '')
        }
      })
        .then(async (res) => {
          const payload = await res.json();
          if (!res.ok) {
            throw new Error((payload && payload.message) ? payload.message : getMessage('archiveLoadFailed', 'Could not update events list.'));
          }
          const rawHtml = payload && payload.html ? payload.html : '';
          $results.html(decodeEscapedHtml(rawHtml));
          const $visibleCount = $('[data-nexdirectory-archive-visible-count]').first();
          if ($visibleCount.length) {
            const found = payload && Number.isFinite(parseInt(payload.count, 10)) ? parseInt(payload.count, 10) : 0;
            $visibleCount.text(String(found));
          }
          document.dispatchEvent(new CustomEvent('nexdirectory:archiveResultsUpdated'));
          bindPagination();
          updateUrl(params);
          syncSaveFiltersForm(params);
          updateFilterOptions(payload, params);
          syncFilterActionVisibility(params, payload && payload.has_active_filters === true);
          enhanceArchiveEmptyState(params);
          initCardLinks();
          if (focusArchiveResultsAfterRecovery) {
            focusArchiveResultsAfterRecovery = false;
            focusArchiveResultsRegion();
          }
        })
        .catch((err) => {
          if (err && err.name === 'AbortError') {
            return;
          }
          showToast(err && err.message ? err.message : getMessage('archiveLoadFailed', 'Could not update events list.'), 'error');
        })
        .finally(() => {
          if (requestController === currentController) {
            requestController = null;
            setLoading(false);
          }
        });
    }

    $form.on('submit', function (e) {
      e.preventDefault();
      requestResults({ paged: 1 });
    });

    $form.on('change', 'select,input[type="date"],[name^="nex_filter["]', function () {
      requestResults({ paged: 1 });
    });

    $form.on('input', 'input[type="text"]', function () {
      clearTimeout(debounceTimer);
      debounceTimer = window.setTimeout(function () {
        requestResults({ paged: 1 });
      }, 260);
    });

    $resetFilters.on('click', function (e) {
      e.preventDefault();
      $form[0].reset();
      clearDynamicArchiveFilterValues();
      $form.find('input[name="paged"]').val('1');
      requestResults({ paged: 1 });
    });

    $results
      .off('click.nexdirectoryArchiveRecovery')
      .on('click.nexdirectoryArchiveRecovery', '[data-nexdirectory-archive-remove-filter]', function (event) {
        event.preventDefault();
        const key = String($(this).attr('data-nexdirectory-archive-remove-filter') || '').trim();
        if (isDynamicFilterKey(key)) {
          if (!clearDynamicArchiveFilterValue(key)) {
            return;
          }
        } else if (filterKeys.indexOf(key) !== -1) {
          setArchiveFilterValue(key, '');
        } else {
          return;
        }
        focusArchiveResultsAfterRecovery = true;
        requestResults({ paged: 1 });
      })
      .on('click.nexdirectoryArchiveRecovery', '[data-nexdirectory-archive-popular-category]', function (event) {
        event.preventDefault();
        const category = String($(this).attr('data-nexdirectory-archive-popular-category') || '').trim();
        if (!category || !$form.find('[name="event_type"]').length) {
          return;
        }
        clearArchiveFilterValues();
        setArchiveFilterValue('event_type', category);
        focusArchiveResultsAfterRecovery = true;
        requestResults({ paged: 1 });
      })
      .on('click.nexdirectoryArchiveRecovery', '[data-nexdirectory-archive-recovery-action]', function (event) {
        event.preventDefault();
        const action = String($(this).attr('data-nexdirectory-archive-recovery-action') || '').trim();
        if (action === 'everywhere') {
          setArchiveFilterValue('city', '');
        } else if (action === 'any-date') {
          setArchiveFilterValue('date_from', '');
        } else if (action === 'clear') {
          clearArchiveFilterValues();
        } else {
          return;
        }
        focusArchiveResultsAfterRecovery = true;
        requestResults({ paged: 1 });
      });

    if ($saveFiltersForm.length) {
      $saveFiltersForm.on('submit', function (e) {
        if (!saveFiltersEndpoint || currentUserId <= 0) {
          return;
        }
        e.preventDefault();
        const params = collectParams({ paged: 1 });
        if (!hasActiveFilters(params)) {
          showToast(getMessage('archiveNoFiltersToSave', 'Apply at least one filter before saving.'), 'error');
          return;
        }
        syncSaveFiltersForm(params);
        restPost(saveFiltersEndpoint, {
          s: String(params.s || ''),
          event_type: String(params.event_type || ''),
          event_mode: String(params.event_mode || ''),
          event_price: String(params.event_price || ''),
          event_tag: String(params.event_tag || ''),
          city: String(params.city || ''),
          date_from: String(params.date_from || ''),
          official: String(params.official || '')
        })
          .then(function (payload) {
            showToast((payload && payload.message) ? payload.message : getMessage('archiveFiltersSaved', 'Filters saved to your account.'), 'success');
          })
          .catch(function (err) {
            showToast(err && err.message ? err.message : getMessage('archiveSaveFailed', 'Could not save filters right now.'), 'error');
          })
          .finally(function () {
            $saveFiltersForm.data('nexdirectorySubmitting', false).removeClass('nexdirectory-is-submitting');
            $saveFiltersForm.find('button[type="submit"], input[type="submit"]').prop('disabled', false).removeClass('nexdirectory-loading');
          });
      });
    }

    if ($saveFiltersLocal.length) {
      $saveFiltersLocal.on('click', function (e) {
        e.preventDefault();
        const params = collectParams({ paged: 1 });
        if (!hasActiveFilters(params)) {
          showToast(getMessage('archiveNoFiltersToSave', 'Apply at least one filter before saving.'), 'error');
          return;
        }
        const payload = {};
        filterKeys.forEach((key) => {
          payload[key] = String(params[key] || '').trim();
        });
        try {
          window.localStorage.setItem(localStorageKey, JSON.stringify(payload));
          showToast(getMessage('archiveFiltersSaved', 'Filters saved on this device.'), 'success');
        } catch (err) {
          showToast(getMessage('archiveSaveFailed', 'Could not save filters on this device.'), 'error');
        }
      });
    }

    if ($useFiltersLocal.length) {
      $useFiltersLocal.on('click', function (e) {
        e.preventDefault();
        let payload = null;
        try {
          payload = JSON.parse(String(window.localStorage.getItem(localStorageKey) || '{}'));
        } catch (err) {
          payload = null;
        }
        if (!payload || typeof payload !== 'object') {
          showToast(getMessage('archiveNoSavedFilters', 'No saved filters found on this device.'), 'error');
          return;
        }
        filterKeys.forEach((key) => {
          const value = String(payload[key] || '');
          const $field = $form.find('[name="' + key + '"]').first();
          if ($field.length) {
            $field.val(value);
          }
        });
        requestResults({ paged: 1 });
      });
    }

    bindPagination();
    const initialParams = collectParams();
    if (new URL(window.location.href).searchParams.has('s')) {
      updateUrl(initialParams);
    }
    syncFilterActionVisibility(initialParams);
    enhanceArchiveEmptyState(initialParams);
  }

  function installAlpineDirectiveFallbacks(scope) {
    const alpineAvailable = !!(window.Alpine && typeof window.Alpine.initTree === 'function');
    const intersectAvailable = alpineAvailable && window.__nexdirectoryAlpineIntersectPluginLoaded === true;
    const collapseAvailable = alpineAvailable && window.__nexdirectoryAlpineCollapsePluginLoaded === true;
    const documentRoot = document.documentElement;

    if (documentRoot) {
      documentRoot.classList.toggle('nexdirectory-alpine-intersect-fallback', !intersectAvailable);
      documentRoot.classList.toggle('nexdirectory-alpine-collapse-fallback', !collapseAvailable);
    }

    if (intersectAvailable) {
      return;
    }

    const fallbackScope = scope && typeof scope.querySelectorAll === 'function' ? scope : document;
    const intersectNodes = [];
    if (
      fallbackScope.nodeType === 1
      && (fallbackScope.hasAttribute('x-intersect') || fallbackScope.hasAttribute('x-intersect.once'))
    ) {
      intersectNodes.push(fallbackScope);
    }
    fallbackScope.querySelectorAll('[x-intersect], [x-intersect\\.once]').forEach(function (node) {
      intersectNodes.push(node);
    });
    intersectNodes.forEach(function (node) {
      node.setAttribute('data-nexdirectory-intersect-fallback', '1');
      node.style.setProperty('opacity', '1', 'important');
      node.style.setProperty('transform', 'none', 'important');
    });
  }

  function reportAlpineHydrationFailure(root, error, context) {
    const safeContext = String(context || 'frontend').replace(/[^a-z0-9_-]/gi, '').slice(0, 40) || 'frontend';
    const safeMessage = 'An interactive view could not be initialized.';
    if (root && typeof root.setAttribute === 'function') {
      root.removeAttribute('data-nexdirectory-alpine-bound');
      root.setAttribute('data-nexdirectory-alpine-error', '1');
    }
    if (window.console && typeof window.console.error === 'function') {
      window.console.error('[NEXDIRECTORY Alpine] ' + safeContext + ' hydration failed', error);
    }
    try {
      window.dispatchEvent(new window.CustomEvent('nexdirectory:alpine:hydration-error', {
        detail: {
          context: safeContext,
          message: safeMessage
        }
      }));
    } catch (_err) {
      // The DOM marker and console entry still expose the failure in older browsers.
    }
  }

  function hydrateAlpineTree(root, context) {
    if (!root || !window.Alpine || typeof window.Alpine.initTree !== 'function') {
      return true;
    }
    if (root.__x || root._x_dataStack) {
      root.removeAttribute('data-nexdirectory-alpine-error');
      root.setAttribute('data-nexdirectory-alpine-bound', '1');
      return true;
    }
    try {
      window.Alpine.initTree(root);
      root.removeAttribute('data-nexdirectory-alpine-error');
      root.setAttribute('data-nexdirectory-alpine-bound', '1');
      return true;
    } catch (error) {
      reportAlpineHydrationFailure(root, error, context);
      return false;
    }
  }

  document.addEventListener('alpine:initialized', function () {
    installAlpineDirectiveFallbacks(document);
  }, { once: true });

  function ensureAlpineReady() {
    installAlpineDirectiveFallbacks(document);
    if (!window.Alpine || typeof window.Alpine.initTree !== 'function') {
      return true;
    }
    const roots = document.querySelectorAll('[x-data], #nexdirectory-submit-form');
    let ready = true;
    roots.forEach(function (root) {
      if (!root) {
        return;
      }
      if (root.getAttribute('data-nexdirectory-alpine-bound') === '1') {
        return;
      }
      if (root.__x || root._x_dataStack) {
        root.removeAttribute('data-nexdirectory-alpine-error');
        root.setAttribute('data-nexdirectory-alpine-bound', '1');
        return;
      }
      if (!hydrateAlpineTree(root, 'frontend-root')) {
        ready = false;
      }
    });
    return ready;
  }

  function initEventHeroParallax() {
    if (!getConfig('enableHeroParallax', true)) {
      return;
    }
    const heroes = document.querySelectorAll('.nexdirectory-event-hero-has-image');
    const archiveBanners = document.querySelectorAll('.nexdirectory-page-banner-has-image[data-nexdirectory-hero-parallax="1"]');
    if (!heroes.length && !archiveBanners.length) {
      return;
    }
    const mobileMedia = window.matchMedia ? window.matchMedia('(max-width: 900px)') : null;
    if (mobileMedia && mobileMedia.matches) {
      heroes.forEach(function (hero) {
        hero.style.setProperty('--nexdirectory-hero-bg-pos', 'center center');
      });
      archiveBanners.forEach(function (banner) {
        banner.style.setProperty('--nexdirectory-page-hero-bg-pos', 'center center');
      });
      return;
    }

    const updateParallax = function () {
      heroes.forEach(function (hero) {
        const rect = hero.getBoundingClientRect();
        const centerDelta = rect.top + rect.height / 2 - window.innerHeight / 2;
        const shift = Math.round(centerDelta * -0.08);
        hero.style.setProperty('--nexdirectory-hero-bg-pos', 'center calc(50% + ' + shift + 'px)');
      });
      archiveBanners.forEach(function (banner) {
        const rect = banner.getBoundingClientRect();
        const centerDelta = rect.top + rect.height / 2 - window.innerHeight / 2;
        const shift = Math.round(centerDelta * -0.06);
        banner.style.setProperty('--nexdirectory-page-hero-bg-pos', 'center calc(50% + ' + shift + 'px)');
      });
    };

    updateParallax();
    window.addEventListener('scroll', updateParallax, { passive: true });
    window.addEventListener('resize', updateParallax);
  }

  function initPageBannerHeroBackground() {
    const heroUrl = String(getConfig('fallbackHeroUrl', '') || '').trim();
    if (!heroUrl) {
      return;
    }
    const banners = document.querySelectorAll('.nexdirectory-page-banner');
    if (!banners.length) {
      return;
    }
    const escapedUrl = heroUrl.replace(/["\\]/g, '\\$&');
    banners.forEach(function (banner) {
      if (!(banner instanceof HTMLElement)) {
        return;
      }
      banner.classList.add('nexdirectory-page-banner-has-image');
      banner.style.setProperty('--nexdirectory-page-hero-image', 'url("' + escapedUrl + '")');
      if (!banner.hasAttribute('data-nexdirectory-hero-parallax')) {
        banner.setAttribute('data-nexdirectory-hero-parallax', '1');
      }
    });
  }

  function initAutogeneratedDisclaimer() {
    const disclaimers = document.querySelectorAll('[data-nexdirectory-autogenerated-disclaimer]');
    if (!disclaimers.length) {
      return;
    }

    disclaimers.forEach(function (banner) {
      if (!banner || banner.getAttribute('data-nexdirectory-disclaimer-bound') === '1') {
        return;
      }
      banner.setAttribute('data-nexdirectory-disclaimer-bound', '1');
      const eventId = String(banner.getAttribute('data-nexdirectory-event-id') || '0').trim() || '0';
      const storageKey = 'nexdirectory_disclaimer_dismissed_' + eventId;

      let isDismissed = false;
      try {
        isDismissed = window.localStorage.getItem(storageKey) === '1';
      } catch (err) {
        isDismissed = false;
      }
      if (isDismissed) {
        banner.setAttribute('hidden', 'hidden');
        return;
      }

      const messageText = String(banner.getAttribute('data-nexdirectory-disclaimer-message') || '').trim();
      if (!messageText) {
        banner.setAttribute('hidden', 'hidden');
        return;
      }

      const sourceUrlRaw = String(banner.getAttribute('data-nexdirectory-disclaimer-source-url') || '').trim();
      let sourceUrl = '';
      if (sourceUrlRaw) {
        try {
          const parsed = new URL(sourceUrlRaw, window.location.origin);
          if (parsed.protocol === 'http:' || parsed.protocol === 'https:') {
            sourceUrl = parsed.href;
          }
        } catch (err) {
          sourceUrl = '';
        }
      }
      const sourceHtml = sourceUrl
        ? ' <a href="' + escapeHtml(sourceUrl) + '" target="_blank" rel="noopener noreferrer nofollow ugc">Source</a>'
        : '';
      const messageHtml = escapeHtml(messageText) + sourceHtml;

      showToast(messageHtml, 'warning', {
        title: 'Imported Listing Notice',
        subtitle: 'Please verify listing details with the original host.',
        duration: 12000,
        className: 'nexdirectory-toast-imported-wide',
        allowHtml: true,
        onClose: function () {
          try {
            window.localStorage.setItem(storageKey, '1');
          } catch (err) {
            // Ignore storage failures in privacy-restricted contexts.
          }
        }
      });

      banner.setAttribute('hidden', 'hidden');
    });
  }

  function initEventGalleryLightbox() {
    const $lightbox = $('#nexdirectory-lightbox');
    const $image = $('#nexdirectory-lightbox-image');
    if (!$lightbox.length || !$image.length) {
      return;
    }
    const refreshMediaLightboxBodyState = function () {
      const hasOpenMediaLightbox = $('#nexdirectory-lightbox:not([hidden]), #nexdirectory-video-lightbox:not([hidden])').length > 0;
      $('body').toggleClass('nexdirectory-media-lightbox-open', hasOpenMediaLightbox);
    };
    let galleryItems = [];
    let galleryIndex = -1;
    let ignoreBackdropCloseUntil = 0;

    function parseGalleryItemsFromJsonAttr($container) {
      if (!$container || !$container.length) {
        return [];
      }
      const raw = String($container.attr('data-nexdirectory-gallery-items') || '').trim();
      if (!raw) {
        return [];
      }
      try {
        const parsed = JSON.parse(raw);
        if (!Array.isArray(parsed)) {
          return [];
        }
        return parsed.map(function (item) {
          return String(item || '').trim();
        }).filter(function (item) {
          return item !== '';
        });
      } catch (error) {
        return [];
      }
    }

    function gatherGalleryItems($scope) {
      if (!$scope || !$scope.length) {
        return [];
      }
      return $scope.find('[data-nexdirectory-lightbox]').map(function () {
        return String($(this).attr('data-nexdirectory-lightbox') || '').trim();
      }).get().filter(function (src) {
        return src !== '';
      });
    }

    function syncGalleryItems($trigger) {
      const $eventGallery = $trigger && $trigger.length
        ? $trigger.closest('[data-nexdirectory-gallery-grid], .nexdirectory-event-gallery-grid')
        : $();

      if ($eventGallery.length) {
        const fromJson = parseGalleryItemsFromJsonAttr($eventGallery.first());
        if (fromJson.length) {
          galleryItems = fromJson;
          return;
        }
        const fromEventGrid = gatherGalleryItems($eventGallery.first());
        if (fromEventGrid.length) {
          galleryItems = fromEventGrid;
          return;
        }
      }

      const $customGallery = $trigger && $trigger.length
        ? $trigger.closest('[data-nexdirectory-custom-gallery], .nexdirectory-custom-display-gallery')
        : $();
      if ($customGallery.length) {
        const fromCustom = gatherGalleryItems($customGallery.first());
        if (fromCustom.length) {
          galleryItems = fromCustom;
          return;
        }
      }

      galleryItems = $('[data-nexdirectory-lightbox]').map(function () {
        return String($(this).attr('data-nexdirectory-lightbox') || '').trim();
      }).get().filter(function (src) {
        return src !== '';
      });
    }

    function openAt(index) {
      if (!galleryItems.length) {
        return;
      }
      const size = galleryItems.length;
      galleryIndex = ((index % size) + size) % size;
      $image.attr('src', galleryItems[galleryIndex] || '');
      $lightbox.removeAttr('hidden');
      $('body').addClass('nexdirectory-lightbox-open');
      refreshMediaLightboxBodyState();
    }

    function move(step) {
      if (!galleryItems.length || galleryIndex < 0) {
        return;
      }
      openAt(galleryIndex + step);
    }

    function closeLightbox() {
      $lightbox.attr('hidden', 'hidden');
      $image.attr('src', '');
      galleryIndex = -1;
      $('body').removeClass('nexdirectory-lightbox-open');
      refreshMediaLightboxBodyState();
    }

    $(document).off('click.nexdirectoryGalleryLightbox', '.nexdirectory-gallery-item[data-nexdirectory-lightbox], [data-nexdirectory-gallery-grid] [data-nexdirectory-lightbox], [data-nexdirectory-custom-gallery] [data-nexdirectory-lightbox]').on('click.nexdirectoryGalleryLightbox', '.nexdirectory-gallery-item[data-nexdirectory-lightbox], [data-nexdirectory-gallery-grid] [data-nexdirectory-lightbox], [data-nexdirectory-custom-gallery] [data-nexdirectory-lightbox]', function (event) {
      if (event) {
        event.preventDefault();
        event.stopPropagation();
      }
      const $trigger = $(this);
      const src = String($trigger.attr('data-nexdirectory-lightbox') || '').trim();
      if (!src) {
        return;
      }
      syncGalleryItems($trigger);
      const explicitIndex = parseInt(String($trigger.attr('data-nexdirectory-gallery-index') || ''), 10);
      if (Number.isFinite(explicitIndex) && explicitIndex >= 0) {
        openAt(explicitIndex);
        return;
      }
      const clickedIndex = galleryItems.indexOf(src);
      openAt(clickedIndex >= 0 ? clickedIndex : 0);
    });

    $('#nexdirectory-lightbox-close').off('click.nexdirectoryGalleryLightboxClose').on('click.nexdirectoryGalleryLightboxClose', function () {
      closeLightbox();
    });
    $('#nexdirectory-lightbox-prev').off('click.nexdirectoryGalleryLightboxPrev').on('click.nexdirectoryGalleryLightboxPrev', function () {
      move(-1);
    });
    $('#nexdirectory-lightbox-next').off('click.nexdirectoryGalleryLightboxNext').on('click.nexdirectoryGalleryLightboxNext', function () {
      move(1);
    });

    $lightbox.off('click.nexdirectoryGalleryLightboxBackdrop').on('click.nexdirectoryGalleryLightboxBackdrop', function (e) {
      if (Date.now() < ignoreBackdropCloseUntil) {
        return;
      }
      if (e.target === this) {
        closeLightbox();
      }
    });

    const lightboxNode = $lightbox.get(0);
    if (lightboxNode) {
      bindHorizontalSwipe(lightboxNode, {
        boundKey: 'data-nexdirectory-gallery-swipe-bound',
        ignoreSelector: '#nexdirectory-lightbox-close, #nexdirectory-lightbox-prev, #nexdirectory-lightbox-next',
        isEnabled: function () {
          return $lightbox.is(':visible') && !$lightbox.attr('hidden');
        },
        beforeSwipe: function () {
          ignoreBackdropCloseUntil = Date.now() + 320;
        },
        onSwipeLeft: function () {
          move(1);
        },
        onSwipeRight: function () {
          move(-1);
        }
      });
    }

    $(document).off('keydown.nexdirectoryGalleryLightboxKeys').on('keydown.nexdirectoryGalleryLightboxKeys', function (e) {
      if ($lightbox.is(':visible') && !$lightbox.attr('hidden')) {
        if (e.key === 'ArrowLeft') {
          e.preventDefault();
          move(-1);
          return;
        }
        if (e.key === 'ArrowRight') {
          e.preventDefault();
          move(1);
          return;
        }
      }
      if (e.key === 'Escape') {
        closeLightbox();
      }
    });

    refreshMediaLightboxBodyState();
	  }

  function initMobileEventGallerySliders() {
    document.querySelectorAll('[data-nexdirectory-mobile-gallery-slider="1"]').forEach(function (slider) {
      if (!slider || slider.getAttribute('data-nexdirectory-mobile-gallery-bound') === '1') {
        return;
      }
      slider.setAttribute('data-nexdirectory-mobile-gallery-bound', '1');

      let scrollTimer = null;
      slider.addEventListener('scroll', function () {
        if (scrollTimer) {
          window.clearTimeout(scrollTimer);
        }
        scrollTimer = window.setTimeout(function () {
          const maxScrollLeft = Math.max(0, slider.scrollWidth - slider.clientWidth);
          if (maxScrollLeft > 10 && slider.scrollLeft >= maxScrollLeft - 10) {
            window.setTimeout(function () {
              if (typeof slider.scrollTo === 'function') {
                slider.scrollTo({ left: 0, behavior: 'smooth' });
              } else {
                slider.scrollLeft = 0;
              }
            }, 400);
          }
        }, 150);
      }, { passive: true });
    });
  }

  function bindHorizontalSwipe(node, options) {
    if (!node || !options || typeof options !== 'object') {
      return;
    }
    const onSwipeLeft = typeof options.onSwipeLeft === 'function' ? options.onSwipeLeft : null;
    const onSwipeRight = typeof options.onSwipeRight === 'function' ? options.onSwipeRight : null;
    if (!onSwipeLeft || !onSwipeRight) {
      return;
    }
    const boundKey = String(options.boundKey || 'data-nexdirectory-swipe-bound').trim() || 'data-nexdirectory-swipe-bound';
    if (node.getAttribute(boundKey) === '1') {
      return;
    }
    node.setAttribute(boundKey, '1');

    const threshold = Number(options.threshold) > 0 ? Number(options.threshold) : 52;
    const verticalThreshold = Number(options.verticalThreshold) > 0 ? Number(options.verticalThreshold) : 92;
    const maxDurationMs = Number(options.maxDurationMs) > 0 ? Number(options.maxDurationMs) : 900;
    const ignoreSelector = String(options.ignoreSelector || '').trim();
    const isEnabled = typeof options.isEnabled === 'function' ? options.isEnabled : function () {
      return true;
    };
    const beforeSwipe = typeof options.beforeSwipe === 'function' ? options.beforeSwipe : null;

    let startX = 0;
    let startY = 0;
    let startTime = 0;
    let tracking = false;

    const readPoint = function (event) {
      if (!event) {
        return null;
      }
      const touchList = event.changedTouches && event.changedTouches.length
        ? event.changedTouches
        : event.touches;
      if (!touchList || !touchList.length) {
        return null;
      }
      const point = touchList[0];
      return {
        x: Number(point.clientX || 0),
        y: Number(point.clientY || 0)
      };
    };

    const onStart = function (event) {
      if (!isEnabled()) {
        tracking = false;
        return;
      }
      if (ignoreSelector && event && event.target && $(event.target).closest(ignoreSelector).length) {
        tracking = false;
        return;
      }
      const point = readPoint(event);
      if (!point) {
        tracking = false;
        return;
      }
      startX = point.x;
      startY = point.y;
      startTime = Date.now();
      tracking = true;
    };

    const onMove = function (event) {
      if (!tracking) {
        return;
      }
      const point = readPoint(event);
      if (!point) {
        return;
      }
      const deltaX = point.x - startX;
      const deltaY = point.y - startY;
      if (Math.abs(deltaX) > 8 && Math.abs(deltaX) > Math.abs(deltaY) && event.cancelable) {
        event.preventDefault();
      }
    };

    const onEnd = function (event) {
      if (!tracking) {
        return;
      }
      tracking = false;
      if (!isEnabled()) {
        return;
      }
      const point = readPoint(event);
      if (!point) {
        return;
      }
      const elapsed = Date.now() - startTime;
      const deltaX = point.x - startX;
      const deltaY = point.y - startY;
      if (elapsed > maxDurationMs) {
        return;
      }
      if (Math.abs(deltaX) < threshold || Math.abs(deltaY) > verticalThreshold) {
        return;
      }
      if (beforeSwipe) {
        beforeSwipe();
      }
      if (deltaX < 0) {
        onSwipeLeft();
      } else {
        onSwipeRight();
      }
    };

    const onCancel = function () {
      tracking = false;
    };

    node.addEventListener('touchstart', onStart, { passive: true });
    node.addEventListener('touchmove', onMove, { passive: false });
    node.addEventListener('touchend', onEnd, { passive: true });
    node.addEventListener('touchcancel', onCancel, { passive: true });
  }

  function resolveVideoEmbedUrl(rawValue) {
    const raw = String(rawValue || '').trim();
    if (!raw) {
      return '';
    }

    let parsed = null;
    try {
      parsed = new URL(raw, window.location.origin);
    } catch (error) {
      return '';
    }
    if (!parsed) {
      return '';
    }

    const host = String(parsed.hostname || '').toLowerCase();
    const path = String(parsed.pathname || '').replace(/\/+$/g, '');
    const segments = path.replace(/^\/+/, '').split('/').filter(Boolean);
    const extension = (function () {
      const dotIndex = path.lastIndexOf('.');
      if (dotIndex < 0) {
        return '';
      }
      return String(path.slice(dotIndex + 1) || '').toLowerCase();
    })();

    if (['mp4', 'webm', 'ogg', 'mov', 'm4v'].indexOf(extension) >= 0) {
      return parsed.toString();
    }

    if (
      (host.indexOf('youtube.com') >= 0 || host.indexOf('youtube-nocookie.com') >= 0) &&
      segments[0] === 'embed' &&
      segments[1]
    ) {
      return 'https://www.youtube.com/embed/' + encodeURIComponent(segments[1]) + '?autoplay=1&rel=0';
    }

    if (host.indexOf('youtu.be') >= 0 && segments[0]) {
      return 'https://www.youtube.com/embed/' + encodeURIComponent(segments[0]) + '?autoplay=1&rel=0';
    }

    if (host.indexOf('youtube.com') >= 0 || host.indexOf('youtube-nocookie.com') >= 0) {
      const queryId = String(parsed.searchParams.get('v') || '').trim();
      if (queryId) {
        return 'https://www.youtube.com/embed/' + encodeURIComponent(queryId) + '?autoplay=1&rel=0';
      }
      if (segments.length >= 2 && ['shorts', 'live', 'v'].indexOf(segments[0]) >= 0) {
        return 'https://www.youtube.com/embed/' + encodeURIComponent(segments[1]) + '?autoplay=1&rel=0';
      }
    }

    if (host.indexOf('player.vimeo.com') >= 0 && segments[0] === 'video' && segments[1]) {
      return 'https://player.vimeo.com/video/' + encodeURIComponent(segments[1]) + '?autoplay=1';
    }

    if (host.indexOf('vimeo.com') >= 0 && segments.length) {
      let videoId = '';
      for (let i = segments.length - 1; i >= 0; i -= 1) {
        const candidate = String(segments[i] || '').trim();
        if (/^\d+$/.test(candidate)) {
          videoId = candidate;
          break;
        }
      }
      if (videoId) {
        return 'https://player.vimeo.com/video/' + encodeURIComponent(videoId) + '?autoplay=1';
      }
    }

    return '';
  }

  function initEventVideoLightbox() {
    const $lightbox = $('#nexdirectory-video-lightbox');
    const $frame = $('#nexdirectory-video-lightbox-iframe');
    if (!$lightbox.length || !$frame.length) {
      return;
    }
    const refreshMediaLightboxBodyState = function () {
      const hasOpenMediaLightbox = $('#nexdirectory-lightbox:not([hidden]), #nexdirectory-video-lightbox:not([hidden])').length > 0;
      $('body').toggleClass('nexdirectory-media-lightbox-open', hasOpenMediaLightbox);
    };
    let videoEmbeds = [];
    let videoIndex = -1;

    function syncVideoItems() {
      videoEmbeds = $('[data-nexdirectory-video-embed], [data-nexdirectory-video-url]').map(function () {
        const $item = $(this);
        const explicitEmbed = String($item.attr('data-nexdirectory-video-embed') || '').trim();
        const rawUrl = String($item.attr('data-nexdirectory-video-url') || '').trim();
        const embed = explicitEmbed || resolveVideoEmbedUrl(rawUrl);
        if (!embed) {
          return null;
        }
        return {
          embed: embed,
          url: rawUrl
        };
      }).get().filter(function (item) {
        return !!item && !!item.embed;
      });
    }

    function openVideoAt(index) {
      if (!videoEmbeds.length) {
        return;
      }
      const size = videoEmbeds.length;
      videoIndex = ((index % size) + size) % size;
      const current = videoEmbeds[videoIndex] || null;
      if (!current || !current.embed) {
        return;
      }
      $frame.attr('src', current.embed);
      $lightbox.removeAttr('hidden');
      $('body').addClass('nexdirectory-lightbox-open');
      refreshMediaLightboxBodyState();
    }

    function moveVideo(step) {
      if (!videoEmbeds.length || videoIndex < 0) {
        return;
      }
      openVideoAt(videoIndex + step);
    }

    let ignoreBackdropCloseUntil = 0;

    function closeVideoLightbox() {
      $lightbox.attr('hidden', 'hidden');
      $frame.attr('src', '');
      videoIndex = -1;
      $('body').removeClass('nexdirectory-lightbox-open');
      refreshMediaLightboxBodyState();
    }

    $(document).off('click.nexdirectoryVideoLightboxOpen', '[data-nexdirectory-video-embed], [data-nexdirectory-video-url]').on('click.nexdirectoryVideoLightboxOpen', '[data-nexdirectory-video-embed], [data-nexdirectory-video-url]', function (event) {
      if (event) {
        event.preventDefault();
        event.stopPropagation();
      }
      const $trigger = $(this);
      const explicitEmbed = String($trigger.attr('data-nexdirectory-video-embed') || '').trim();
      const rawUrl = String($trigger.attr('data-nexdirectory-video-url') || '').trim();
      const embedUrl = explicitEmbed || resolveVideoEmbedUrl(rawUrl);
      if (!embedUrl) {
        if (rawUrl) {
          window.open(rawUrl, '_blank', 'noopener,noreferrer');
        }
        return;
      }
      syncVideoItems();
      const clickedIndex = videoEmbeds.findIndex(function (item) {
        return !!item && String(item.embed || '') === embedUrl;
      });
      openVideoAt(clickedIndex >= 0 ? clickedIndex : 0);
    });

    $('#nexdirectory-video-lightbox-close').off('click.nexdirectoryVideoLightboxClose').on('click.nexdirectoryVideoLightboxClose', function () {
      closeVideoLightbox();
    });
    $('#nexdirectory-video-lightbox-prev').off('click.nexdirectoryVideoLightboxPrev').on('click.nexdirectoryVideoLightboxPrev', function () {
      moveVideo(-1);
    });
    $('#nexdirectory-video-lightbox-next').off('click.nexdirectoryVideoLightboxNext').on('click.nexdirectoryVideoLightboxNext', function () {
      moveVideo(1);
    });

    $lightbox.off('click.nexdirectoryVideoLightboxBackdrop').on('click.nexdirectoryVideoLightboxBackdrop', function (e) {
      if (Date.now() < ignoreBackdropCloseUntil) {
        return;
      }
      if (e.target === this) {
        closeVideoLightbox();
      }
    });

    const bindVideoSwipeOnNode = function (node) {
      bindHorizontalSwipe(node, {
        boundKey: 'data-nexdirectory-video-swipe-bound',
        ignoreSelector: '#nexdirectory-video-lightbox-close, #nexdirectory-video-lightbox-prev, #nexdirectory-video-lightbox-next',
        isEnabled: function () {
          return $lightbox.is(':visible') && !$lightbox.attr('hidden');
        },
        beforeSwipe: function () {
          ignoreBackdropCloseUntil = Date.now() + 320;
        },
        onSwipeLeft: function () {
          moveVideo(1);
        },
        onSwipeRight: function () {
          moveVideo(-1);
        }
      });
    };
    const lightboxNode = $lightbox.get(0);
    if (lightboxNode) {
      bindVideoSwipeOnNode(lightboxNode);
    }
    $lightbox.find('[data-nexdirectory-video-swipe-zone]').each(function () {
      bindVideoSwipeOnNode(this);
    });

    $(document).off('keydown.nexdirectoryVideoLightboxKeys').on('keydown.nexdirectoryVideoLightboxKeys', function (e) {
      if ($lightbox.is(':visible') && !$lightbox.attr('hidden')) {
        if (e.key === 'ArrowLeft') {
          e.preventDefault();
          moveVideo(-1);
          return;
        }
        if (e.key === 'ArrowRight') {
          e.preventDefault();
          moveVideo(1);
          return;
        }
      }
      if (e.key === 'Escape') {
        closeVideoLightbox();
      }
    });

    refreshMediaLightboxBodyState();
  }

  function initEventDocumentLightbox() {
    const $lightbox = $('#nexdirectory-doc-lightbox');
    const $frame = $('#nexdirectory-doc-lightbox-iframe');
    const $title = $('#nexdirectory-doc-lightbox-title');
    const $downloads = $('[data-nexdirectory-doc-download], #nexdirectory-doc-lightbox-download');
    if (!$lightbox.length || !$frame.length || !$downloads.length) {
      return;
    }

    let docs = [];
    let docIndex = -1;

    function resolveDocumentEmbedUrl(url, embedCandidate) {
      const rawUrl = String(url || '').trim();
      const explicitEmbed = String(embedCandidate || '').trim();
      if (explicitEmbed) {
        return explicitEmbed;
      }
      if (!rawUrl) {
        return '';
      }
      let path = '';
      try {
        path = String(new URL(rawUrl, window.location.origin).pathname || '');
      } catch (error) {
        path = rawUrl;
      }
      const dotIndex = path.lastIndexOf('.');
      const ext = dotIndex >= 0 ? String(path.slice(dotIndex + 1) || '').toLowerCase() : '';
      if (ext === 'pdf') {
        return rawUrl;
      }
      return 'https://docs.google.com/gview?embedded=1&url=' + encodeURIComponent(rawUrl);
    }

    function syncDocuments() {
      docs = $('[data-nexdirectory-doc-url]').map(function () {
        const $el = $(this);
        const url = String($el.attr('data-nexdirectory-doc-url') || '').trim();
        const embed = resolveDocumentEmbedUrl(url, $el.attr('data-nexdirectory-doc-embed'));
        const title = String($el.attr('data-nexdirectory-doc-title') || '').trim();
        if (!url || !embed) {
          return null;
        }
        return {
          url: url,
          embed: embed,
          title: title || 'Document'
        };
      }).get().filter(function (item) {
        return !!item;
      });
    }

    function renderDoc(index) {
      if (!docs.length) {
        return;
      }
      const size = docs.length;
      docIndex = ((index % size) + size) % size;
      const current = docs[docIndex] || null;
      if (!current) {
        return;
      }
      $frame.attr('src', current.embed);
      $downloads.attr('href', current.url);
      $title.text(current.title);
      $lightbox.removeAttr('hidden');
      $('body').addClass('nexdirectory-lightbox-open');
    }

    function move(step) {
      if (!docs.length || docIndex < 0) {
        return;
      }
      renderDoc(docIndex + step);
    }

    function closeDocLightbox() {
      $lightbox.attr('hidden', 'hidden');
      $frame.attr('src', '');
      $downloads.attr('href', '#');
      $title.text('');
      docIndex = -1;
      $('body').removeClass('nexdirectory-lightbox-open');
    }

    $(document).off('click.nexdirectoryDocLightbox', '[data-nexdirectory-doc-url]').on('click.nexdirectoryDocLightbox', '[data-nexdirectory-doc-url]', function (event) {
      if (event) {
        event.preventDefault();
        event.stopPropagation();
      }
      const $trigger = $(this);
      const docUrl = String($trigger.attr('data-nexdirectory-doc-url') || '').trim();
      if (!docUrl) {
        return;
      }
      syncDocuments();
      const explicitIndex = parseInt(String($trigger.attr('data-nexdirectory-doc-index') || ''), 10);
      if (Number.isFinite(explicitIndex) && explicitIndex >= 0) {
        renderDoc(explicitIndex);
        return;
      }
      const foundIndex = docs.findIndex(function (doc) {
        return String(doc && doc.url || '') === docUrl;
      });
      renderDoc(foundIndex >= 0 ? foundIndex : 0);
    });

    $('#nexdirectory-doc-lightbox-close').off('click.nexdirectoryDocLightboxClose').on('click.nexdirectoryDocLightboxClose', function () {
      closeDocLightbox();
    });
    $('#nexdirectory-doc-lightbox-prev').off('click.nexdirectoryDocLightboxPrev').on('click.nexdirectoryDocLightboxPrev', function () {
      move(-1);
    });
    $('#nexdirectory-doc-lightbox-next').off('click.nexdirectoryDocLightboxNext').on('click.nexdirectoryDocLightboxNext', function () {
      move(1);
    });
    $lightbox.off('click.nexdirectoryDocLightboxBackdrop').on('click.nexdirectoryDocLightboxBackdrop', function (event) {
      if (event.target === this) {
        closeDocLightbox();
      }
    });
    $(document).off('keydown.nexdirectoryDocLightboxKeys').on('keydown.nexdirectoryDocLightboxKeys', function (event) {
      if (!$lightbox.is(':visible') || !!$lightbox.attr('hidden')) {
        return;
      }
      if (event.key === 'ArrowLeft') {
        event.preventDefault();
        move(-1);
        return;
      }
      if (event.key === 'ArrowRight') {
        event.preventDefault();
        move(1);
        return;
      }
      if (event.key === 'Escape') {
        closeDocLightbox();
      }
    });
  }

  function initRsvpAjax() {
    const endpoint = String(getConfig('rsvpRestUrl', '') || '');
    if (!endpoint) {
      return;
    }

    $(document).off('submit.nexdirectoryRsvpAjax', 'form.nexdirectory-rsvp-form').on('submit.nexdirectoryRsvpAjax', 'form.nexdirectory-rsvp-form', function (event) {
      const $form = $(this);
      const $button = $form.find('button[type="submit"]').first();
      const postId = parseInt($form.find('input[name="post_id"]').val() || '0', 10);
      const intent = String($form.find('input[name="intent"]').val() || 'join').trim() || 'join';
      if (!postId) {
        return;
      }

      event.preventDefault();
      if ($button.length) {
        $button.prop('disabled', true).addClass('nexdirectory-loading');
      }

      restPost(endpoint, {
        post_id: postId,
        intent: intent
      })
        .then(function (payload) {
          const nextIntent = String((payload && payload.next_intent) || '').trim();
          const buttonLabel = String((payload && payload.button_label) || '').trim();
          const $formsForPost = $('form.nexdirectory-rsvp-form').filter(function () {
            const candidatePostId = parseInt($(this).find('input[name="post_id"]').val() || '0', 10);
            return Number.isFinite(candidatePostId) && candidatePostId === postId;
          });
          if (nextIntent) {
            $formsForPost.find('input[name="intent"]').val(nextIntent);
          }
          if (buttonLabel) {
            const $targetButtons = $formsForPost.length ? $formsForPost.find('button[type="submit"]') : $button;
            $targetButtons.each(function () {
              const $btn = $(this);
              const $label = $btn.find('[data-nexdirectory-rsvp-label]').first();
              if ($label.length) {
                $label.text(buttonLabel);
              } else {
                $btn.text(buttonLabel);
              }
            });
          }
          const rsvpCount = parseInt(payload && payload.rsvp_count, 10);
          const waitlistCount = parseInt(payload && payload.waitlist_count, 10);
          const $meta = $('.nexdirectory-rsvp-meta').filter(function () {
            const metaPostId = parseInt($(this).attr('data-post-id') || '0', 10);
            return !Number.isFinite(metaPostId) || metaPostId <= 0 || metaPostId === postId;
          });
          if ($meta.length && Number.isFinite(rsvpCount) && Number.isFinite(waitlistCount)) {
            $meta.each(function () {
              const $metaNode = $(this);
              const primaryLabel = String($metaNode.attr('data-primary-label') || 'RSVP').trim() || 'RSVP';
              const secondaryLabel = String($metaNode.attr('data-secondary-label') || '').trim();
              if (secondaryLabel) {
                $metaNode.text(primaryLabel + ': ' + String(rsvpCount) + ' | ' + secondaryLabel + ': ' + String(waitlistCount));
              } else {
                $metaNode.text(primaryLabel + ': ' + String(rsvpCount));
              }
              if ((rsvpCount + waitlistCount) > 0) {
                $metaNode.removeClass('hidden');
              } else {
                $metaNode.addClass('hidden');
              }
            });
          }
          const fallbackMessage = (function () {
            const primaryLabel = String(($meta.first().attr('data-primary-label') || '')).trim().toLowerCase();
            if (primaryLabel === 'saved') {
              return 'Saved list updated.';
            }
            return 'RSVP updated.';
          })();
          showToast((payload && payload.message) ? payload.message : fallbackMessage, 'success');
        })
        .catch(function (err) {
          showToast(err && err.message ? err.message : getMessage('restActionFailed', 'Request failed. Please try again.'), 'error');
        })
        .finally(function () {
          $form.data('nexdirectorySubmitting', false).removeClass('nexdirectory-is-submitting');
          if ($button.length) {
            $button.prop('disabled', false).removeClass('nexdirectory-loading');
          }
        });
    });
  }

  function renderCustomGalleryPreview(targetId) {
    const input = document.getElementById(targetId);
    if (!input) {
      return;
    }
    const preview = document.querySelector('[data-nexdirectory-custom-gallery-preview="' + targetId + '"]');
    if (!preview) {
      return;
    }
    const rows = String(input.value || '')
      .split(/\r?\n/)
      .map(function (row) { return String(row || '').trim(); })
      .filter(function (row) { return row !== ''; });
    preview.innerHTML = '';
    rows.forEach(function (url) {
      const img = document.createElement('img');
      img.src = url;
      img.alt = 'Gallery preview';
      img.loading = 'lazy';
      preview.appendChild(img);
    });
  }

  function initCustomFieldUploads() {
    $(document).off('click.nexdirectoryCustomUpload', '[data-nexdirectory-custom-upload-btn]').on('click.nexdirectoryCustomUpload', '[data-nexdirectory-custom-upload-btn]', function (event) {
      event.preventDefault();
      const targetId = String($(this).attr('data-nexdirectory-target-input') || '').trim();
      if (!targetId) {
        return;
      }
      const input = document.querySelector('[data-nexdirectory-custom-upload-input][data-nexdirectory-target-input="' + targetId + '"]');
      if (input) {
        input.click();
      }
    });

    $(document).off('change.nexdirectoryCustomUpload', '[data-nexdirectory-custom-upload-input]').on('change.nexdirectoryCustomUpload', '[data-nexdirectory-custom-upload-input]', async function () {
      const fileInput = this;
      const files = Array.from(fileInput.files || []);
      const targetId = String(fileInput.getAttribute('data-nexdirectory-target-input') || '').trim();
      const kind = String(fileInput.getAttribute('data-nexdirectory-kind') || 'image').trim();
      const target = String(fileInput.getAttribute('data-nexdirectory-target') || 'custom_gallery').trim();
      const multiple = String(fileInput.getAttribute('data-nexdirectory-multiple') || '0') === '1';
      const targetInput = document.getElementById(targetId);
      if (!files.length || !targetInput) {
        fileInput.value = '';
        return;
      }

      const galleryMaxBytes = parseInt(getConfig('uploadGalleryMaxBytes', 2048000), 10) || 2048000;
      const galleryMaxFiles = parseInt(getConfig('uploadGalleryMaxFiles', 10), 10) || 10;
      const fileMaxBytes = parseInt(getConfig('uploadFileMaxBytes', 2097152), 10) || 2097152;
      const coverMaxBytes = parseInt(getConfig('uploadCoverMaxBytes', 512000), 10) || 512000;
      const maxBytes = kind === 'file' ? fileMaxBytes : (target === 'custom_image' ? coverMaxBytes : galleryMaxBytes);

      const existing = String(targetInput.value || '')
        .split(/\r?\n/)
        .map(function (row) { return String(row || '').trim(); })
        .filter(function (row) { return row !== ''; });

      for (let i = 0; i < files.length; i += 1) {
        const file = files[i];
        if (!file) {
          continue;
        }
        if (maxBytes > 0 && file.size > maxBytes) {
          showToast('Selected file is too large.', 'error');
          continue;
        }
        try {
          const payload = await uploadAssetFile(file, kind, target);
          const uploadedUrl = payload && payload.url ? String(payload.url) : '';
          if (!uploadedUrl) {
            continue;
          }
          if (multiple) {
            if (galleryMaxFiles > 0 && existing.length >= galleryMaxFiles) {
              showToast('Gallery upload limit reached.', 'error');
              break;
            }
            if (existing.indexOf(uploadedUrl) === -1) {
              existing.push(uploadedUrl);
            }
            targetInput.value = existing.join('\n');
            renderCustomGalleryPreview(targetId);
          } else {
            targetInput.value = uploadedUrl;
          }
        } catch (error) {
          showToast(error && error.message ? error.message : 'Upload failed.', 'error');
        }
      }

      fileInput.value = '';
    });

    $(document).off('input.nexdirectoryCustomGalleryPreview', '[data-nexdirectory-custom-field][data-nexdirectory-field-type="gallery"] textarea')
      .on('input.nexdirectoryCustomGalleryPreview', '[data-nexdirectory-custom-field][data-nexdirectory-field-type="gallery"] textarea', function () {
        const id = String(this.id || '').trim();
        if (!id) {
          return;
        }
        renderCustomGalleryPreview(id);
      });

    $('[data-nexdirectory-custom-gallery-preview]').each(function () {
      const targetId = String($(this).attr('data-nexdirectory-custom-gallery-preview') || '').trim();
      if (targetId) {
        renderCustomGalleryPreview(targetId);
      }
    });
  }

  function initSubmitUploaderFallback() {
    const form = document.getElementById('nexdirectory-submit-form');
    if (!form) {
      return;
    }
    const fallbackRetryCount = parseInt(form.getAttribute('data-nexdirectory-fallback-retries') || '0', 10) || 0;
    if (window.Alpine && !form._x_dataStack && !form.__x && form.getAttribute('data-nexdirectory-alpine-bound') !== '1' && fallbackRetryCount < 4) {
      form.setAttribute('data-nexdirectory-fallback-retries', String(fallbackRetryCount + 1));
      window.setTimeout(function () {
        initSubmitUploaderFallback();
      }, 350);
      return;
    }
    const alpineActive = !!(window.Alpine && (form._x_dataStack || form.__x));
    if (alpineActive) {
      return;
    }
    if (form.getAttribute('data-nexdirectory-fallback-upload-bound') === '1') {
      return;
    }
    form.setAttribute('data-nexdirectory-fallback-upload-bound', '1');

    const coverInput = form.querySelector('input[name="nexdirectory_cover_file"]');
    const galleryInput = form.querySelector('input[name="nexdirectory_gallery_files[]"]');
    const coverField = form.querySelector('input[name="nexdirectory_cover_image_url"]');
    const galleryField = form.querySelector('textarea[name="nexdirectory_gallery_urls"]');
    const coverZone = form.querySelector('.nexdirectory-dropzone[data-nexdirectory-upload-target="cover"]');
    const galleryZone = form.querySelector('.nexdirectory-dropzone[data-nexdirectory-upload-target="gallery"]');
    const galleryPreview = form.querySelector('.nexdirectory-gallery');

    const coverMaxBytes = parseInt(getConfig('uploadCoverMaxBytes', 512000), 10) || 512000;
    const galleryMaxBytes = parseInt(getConfig('uploadGalleryMaxBytes', 2048000), 10) || 2048000;
    const galleryMaxFiles = parseInt(getConfig('uploadGalleryMaxFiles', 10), 10) || 10;

    function parseGalleryRows() {
      return String((galleryField && galleryField.value) || '')
        .split(/\r?\n/)
        .map(function (row) { return String(row || '').trim(); })
        .filter(function (row) { return row !== ''; });
    }

    function renderGalleryRows(rows) {
      if (galleryField) {
        galleryField.value = rows.join('\n');
      }
      if (!galleryPreview) {
        return;
      }
      galleryPreview.innerHTML = '';
      rows.forEach(function (url) {
        const item = document.createElement('div');
        const img = document.createElement('img');
        img.src = url;
        img.alt = 'Gallery preview';
        img.loading = 'lazy';
        item.appendChild(img);
        galleryPreview.appendChild(item);
      });
    }

    function renderCover(url) {
      if (!coverZone || !url) {
        return;
      }
      coverZone.innerHTML = '';
      const img = document.createElement('img');
      img.className = 'nexdirectory-upload-preview';
      img.src = url;
      img.alt = 'Cover preview';
      coverZone.appendChild(img);
    }

    async function uploadFiles(files, mode) {
      const queue = Array.from(files || []);
      if (!queue.length) {
        return;
      }
      let rows = parseGalleryRows();
      for (let i = 0; i < queue.length; i += 1) {
        const file = queue[i];
        if (!file) {
          continue;
        }
        if (mode === 'cover' && file.size > coverMaxBytes) {
          showToast('Cover image is too large.', 'error');
          continue;
        }
        if (mode === 'gallery' && file.size > galleryMaxBytes) {
          showToast('One of the gallery images is too large.', 'error');
          continue;
        }
        if (mode === 'gallery' && galleryMaxFiles > 0 && rows.length >= galleryMaxFiles) {
          showToast('Gallery upload limit reached.', 'error');
          break;
        }
        try {
          const payload = await uploadAssetFile(file, 'image', mode);
          const url = payload && payload.url ? String(payload.url) : '';
          if (!url) {
            continue;
          }
          if (mode === 'cover') {
            if (coverField) {
              coverField.value = url;
            }
            renderCover(url);
          } else if (rows.indexOf(url) === -1) {
            rows.push(url);
            renderGalleryRows(rows);
          }
        } catch (error) {
          showToast(error && error.message ? error.message : 'Upload failed. Please try again.', 'error');
        }
      }
    }

    if (coverZone && coverInput) {
      coverZone.addEventListener('click', function () {
        coverInput.click();
      });
      coverZone.addEventListener('dragover', function (event) {
        event.preventDefault();
      });
      coverZone.addEventListener('drop', function (event) {
        event.preventDefault();
        uploadFiles(event.dataTransfer && event.dataTransfer.files ? event.dataTransfer.files : [], 'cover');
      });
      coverInput.addEventListener('change', function () {
        uploadFiles(coverInput.files || [], 'cover');
        coverInput.value = '';
      });
    }

    if (galleryZone && galleryInput) {
      galleryZone.addEventListener('click', function () {
        galleryInput.click();
      });
      galleryZone.addEventListener('dragover', function (event) {
        event.preventDefault();
      });
      galleryZone.addEventListener('drop', function (event) {
        event.preventDefault();
        uploadFiles(event.dataTransfer && event.dataTransfer.files ? event.dataTransfer.files : [], 'gallery');
      });
      galleryInput.addEventListener('change', function () {
        uploadFiles(galleryInput.files || [], 'gallery');
        galleryInput.value = '';
      });
    }

    if (coverField && String(coverField.value || '').trim() !== '') {
      renderCover(String(coverField.value || '').trim());
    }
    renderGalleryRows(parseGalleryRows());
  }

  function ensureRecaptchaLoaded(siteKey, version, cb) {
    const callback = typeof cb === 'function' ? cb : function () {};
    const requiredMethod = version === 'v2' ? 'render' : 'execute';
    const hasRequiredApi = function () {
      return !!(window.grecaptcha && typeof window.grecaptcha[requiredMethod] === 'function');
    };
    if (!siteKey) {
      callback(false);
      return;
    }
    if (hasRequiredApi()) {
      callback(true);
      return;
    }

    let resolved = false;
    const done = function (ok) {
      if (resolved) {
        return;
      }
      resolved = true;
      callback(ok !== false);
    };
    const waitForApi = function () {
      if (hasRequiredApi()) {
        done(true);
        return;
      }
      let attempts = 0;
      const timer = window.setInterval(function () {
        attempts += 1;
        if (hasRequiredApi()) {
          window.clearInterval(timer);
          done(true);
          return;
        }
        if (attempts > 80) {
          window.clearInterval(timer);
          done(false);
        }
      }, 100);
    };

    const existing = document.getElementById('nexdirectory-recaptcha-script');
    if (existing) {
      if (hasRequiredApi()) {
        done(true);
        return;
      }
      existing.addEventListener('load', waitForApi, { once: true });
      existing.addEventListener('error', function () { done(false); }, { once: true });
      waitForApi();
      return;
    }
    const script = document.createElement('script');
    script.id = 'nexdirectory-recaptcha-script';
    if (version === 'v2') {
      script.src = 'https://www.google.com/recaptcha/api.js?render=explicit';
    } else {
      script.src = 'https://www.google.com/recaptcha/api.js?render=' + encodeURIComponent(siteKey);
    }
    script.async = true;
    script.defer = true;
    script.addEventListener('load', waitForApi, { once: true });
    script.addEventListener('error', function () { done(false); }, { once: true });
    document.head.appendChild(script);
  }

  function initRecaptchaForms() {
    const siteKey = String(getConfig('recaptchaSiteKey', '') || '').trim();
    const version = String(getConfig('recaptchaVersion', 'v3') || 'v3').toLowerCase() === 'v2' ? 'v2' : 'v3';
    const showSubmitField = !!getConfig('recaptchaShowSubmit', false);
    const bypassSubmitRecaptcha = !!getConfig('recaptchaBypassSubmitForPrivileged', false);
    if (!siteKey) {
      return;
    }

    let $forms = $('form').has('.nexdirectory-recaptcha-token');
    if (bypassSubmitRecaptcha) {
      $forms = $forms.filter(function () {
        return String(this.id || '') !== 'nexdirectory-submit-form';
      });
    }
    if (!$forms.length) {
      return;
    }
    const resetBlockedSubmitState = function ($form) {
      window.setTimeout(function () {
        $form.removeData('nexdirectorySubmitting').removeClass('nexdirectory-is-submitting');
        $form.find('button[type="submit"], input[type="submit"]').prop('disabled', false).removeAttr('aria-busy');
        window.dispatchEvent(new CustomEvent('nexdirectory-submit-reset'));
      }, 0);
    };

    if (version === 'v2') {
      ensureRecaptchaLoaded(siteKey, version, function (loaded) {
        if (!loaded || !window.grecaptcha || typeof window.grecaptcha.render !== 'function') {
          showToast(getMessage('recaptchaLoadFailed', 'reCAPTCHA failed to load.'), 'error');
          return;
        }
        $forms.each(function () {
          const $form = $(this);
          if (String($form.attr('data-nexdirectory-recaptcha-ready') || '') === '1') {
            return;
          }
          const $token = $form.find('.nexdirectory-recaptcha-token').first();
          if (!$token.length) {
            return;
          }
          const mustShow = true;
          let $slot = $form.find('.nexdirectory-recaptcha-v2-slot').first();
          if (!$slot.length) {
            $slot = $('<div class="nexdirectory-recaptcha-v2-slot"></div>');
            const $submitArea = $form.find('button[type="submit"], input[type="submit"]').first().closest('.nexdirectory-form-actions, .nexdirectory-actions, .nexdirectory-submit-actions, p, div');
            if ($submitArea.length) {
              $submitArea.before($slot);
            } else {
              $form.append($slot);
            }
          }
          if (!mustShow) {
            $slot.hide();
          }
          const widgetId = window.grecaptcha.render($slot.get(0), {
            sitekey: siteKey,
            callback: function (tokenValue) {
              $token.val(String(tokenValue || ''));
            },
            'expired-callback': function () {
              $token.val('');
            }
          });
          $form.attr('data-nexdirectory-recaptcha-widget-id', String(widgetId));
          $form.attr('data-nexdirectory-recaptcha-ready', '1');
        });
      });

      $forms.on('submit', function (e) {
        const $form = $(this);
        const $token = $form.find('.nexdirectory-recaptcha-token').first();
        if ($token.length && String($token.val() || '').trim() !== '') {
          return;
        }
        e.preventDefault();
        resetBlockedSubmitState($form);
        showToast(getMessage('recaptchaRequired', 'Please complete the reCAPTCHA challenge.'), 'error');
      });
      return;
    }

    if (showSubmitField) {
      const $submitForm = $forms.filter('#nexdirectory-submit-form');
      if ($submitForm.length && !$submitForm.find('.nexdirectory-recaptcha-v3-hint').length) {
        const $hint = $('<p class="nexdirectory-recaptcha-v3-hint">').text('Protected by reCAPTCHA v3');
        const $actions = $submitForm.find('.nexdirectory-form-actions, .nexdirectory-submit-actions').first();
        if ($actions.length) {
          $actions.before($hint);
        } else {
          $submitForm.append($hint);
        }
      }
    }

    $forms.on('submit', function (e) {
      const $form = $(this);
      const $token = $form.find('.nexdirectory-recaptcha-token');
      if ($token.val()) {
        return;
      }
      e.preventDefault();

      ensureRecaptchaLoaded(siteKey, version, function (loaded) {
        if (!loaded || !window.grecaptcha || typeof window.grecaptcha.execute !== 'function') {
          resetBlockedSubmitState($form);
          showToast(getMessage('recaptchaLoadFailed', 'reCAPTCHA failed to load.'), 'error');
          return;
        }
        const executeCaptcha = function () {
          window.grecaptcha.execute(siteKey, { action: 'submit' }).then(function (token) {
            $token.val(token || '');
            $form.trigger('submit');
          }).catch(function () {
            resetBlockedSubmitState($form);
            showToast(getMessage('recaptchaLoadFailed', 'reCAPTCHA failed to load.'), 'error');
          });
        };
        if (typeof window.grecaptcha.ready === 'function') {
          window.grecaptcha.ready(executeCaptcha);
        } else {
          executeCaptcha();
        }
      });
    });
  }

  function initFormLoadingStates() {
    $(document).off('click.nexdirectorySubmitterHint', 'form button[type="submit"], form input[type="submit"]').on('click.nexdirectorySubmitterHint', 'form button[type="submit"], form input[type="submit"]', function () {
      const $btn = $(this);
      const $form = $btn.closest('form');
      if (!$form.length) {
        return;
      }
      const name = String($btn.attr('name') || '').trim();
      const value = String($btn.val() || $btn.attr('value') || $btn.text() || '').trim();
      $form.data('nexdirectorySubmitterName', name);
      $form.data('nexdirectorySubmitterValue', value);
    });

    $('form').filter(function () {
      return $(this).closest('.nexdirectory-layout, .nexdirectory-submit-wrap, .nexdirectory-my-events, .nexdirectory-auth-grid, .nexdirectory-event-details').length > 0;
    }).off('submit.nexdirectoryFormLoading').on('submit.nexdirectoryFormLoading', function (e) {
      if (typeof e.isDefaultPrevented === 'function' && e.isDefaultPrevented()) {
        return;
      }
      const $form = $(this);
      if ($form.data('nexdirectorySubmitting')) {
        return;
      }

      syncEditors();
      $form.data('nexdirectorySubmitting', true);
      $form.addClass('nexdirectory-is-submitting');

      const submitterName = String($form.data('nexdirectorySubmitterName') || '').trim();
      const submitterValue = String($form.data('nexdirectorySubmitterValue') || '').trim();
      if (submitterName !== '') {
        const $existingMirror = $form.find('input[data-nexdirectory-submitter-clone="1"][name="' + submitterName + '"]');
        if (!$existingMirror.length) {
          $('<input>', {
            type: 'hidden',
            name: submitterName,
            value: submitterValue,
            'data-nexdirectory-submitter-clone': '1'
          }).appendTo($form);
        } else {
          $existingMirror.val(submitterValue);
        }
      }

      $form.find('button[type="submit"], input[type="submit"]').each(function () {
        const $btn = $(this);
        if ($btn.prop('disabled')) {
          return;
        }
        if (!$btn.data('nexdirectoryOriginalText')) {
          $btn.data('nexdirectoryOriginalText', $btn.text());
        }
        $btn.prop('disabled', true).addClass('nexdirectory-loading');
      });
    });
  }

  function initSubmitEventAjax() {
    const form = document.getElementById('nexdirectory-submit-form');
    if (!form || form.getAttribute('data-nexdirectory-submit-ajax-bound') === '1') {
      return;
    }
    form.setAttribute('data-nexdirectory-submit-ajax-bound', '1');

    const parseNoticeFromUrl = function (url) {
      try {
        const parsedUrl = new URL(String(url || ''), window.location.origin);
        const message = String(parsedUrl.searchParams.get('nexdirectory_notice') || '').trim();
        if (!message) {
          return null;
        }
        let type = String(parsedUrl.searchParams.get('nexdirectory_notice_type') || 'success').trim().toLowerCase();
        if (type === 'warn') {
          type = 'warning';
        }
        if (['success', 'error', 'warning', 'info'].indexOf(type) < 0) {
          type = 'success';
        }
        return {
          message: message,
          type: type
        };
      } catch (error) {
        return null;
      }
    };

    form.addEventListener('submit', function (event) {
      const isEditing = String(form.getAttribute('data-nexdirectory-editing') || '0') === '1';
      if (!isEditing) {
        return;
      }
      if (event.defaultPrevented) {
        return;
      }

      let intent = '';
      const submitter = event.submitter || null;
      if (submitter && String(submitter.name || '') === 'nexdirectory_intent') {
        intent = String(submitter.value || '').trim().toLowerCase();
      }
      if (!intent) {
        const $formData = $(form);
        const submitterName = String($formData.data('nexdirectorySubmitterName') || '').trim();
        const submitterValue = String($formData.data('nexdirectorySubmitterValue') || '').trim();
        if (submitterName === 'nexdirectory_intent') {
          intent = submitterValue.toLowerCase();
        }
      }
      if (!intent) {
        intent = 'pending';
      }
      if (intent !== 'pending') {
        return;
      }

      event.preventDefault();
      if (typeof event.stopImmediatePropagation === 'function') {
        event.stopImmediatePropagation();
      }
      if (form.getAttribute('data-nexdirectory-ajax-submit-pending') === '1') {
        return;
      }
      form.setAttribute('data-nexdirectory-ajax-submit-pending', '1');

      syncEditors();
      const $form = $(form);
      const $buttons = $form.find('button[type="submit"], input[type="submit"]');
      $form.data('nexdirectorySubmitting', true).addClass('nexdirectory-is-submitting');
      $buttons.prop('disabled', true).addClass('nexdirectory-loading');

      const formData = new FormData(form);
      formData.set('nexdirectory_ajax', '1');
      formData.set('nexdirectory_intent', 'pending');
      if (submitter && String(submitter.name || '') !== '') {
        formData.set(String(submitter.name), String(submitter.value || 'pending'));
      }

      const actionUrl = String(form.getAttribute('action') || '/wp-admin/admin-post.php').trim() || '/wp-admin/admin-post.php';
      fetch(actionUrl, {
        method: 'POST',
        body: formData,
        credentials: 'same-origin',
        headers: {
          Accept: 'application/json',
          'X-Requested-With': 'XMLHttpRequest'
        }
      })
        .then(function (response) {
          return response.text().then(function (text) {
            return { response: response, text: text };
          });
        })
        .then(function (result) {
          const response = result.response;
          const text = String(result.text || '');
          let parsed = null;
          try {
            parsed = JSON.parse(text);
          } catch (error) {
            parsed = null;
          }

          if (parsed && typeof parsed === 'object' && Object.prototype.hasOwnProperty.call(parsed, 'success')) {
            const payload = parsed && typeof parsed.data === 'object' && parsed.data ? parsed.data : {};
            const defaultMessage = parsed.success ? 'Listing updated.' : getMessage('restActionFailed', 'Request failed. Please try again.');
            const message = String(payload.message || defaultMessage).trim();
            if (parsed.success) {
              showToast(message || 'Listing updated.', 'success');
              const eventIdRaw = payload.post_id || payload.edit_event_id || payload.event_id || '';
              const eventId = parseInt(String(eventIdRaw || ''), 10);
              if (Number.isFinite(eventId) && eventId > 0) {
                const editField = form.querySelector('input[name="nexdirectory_edit_post_id"]');
                if (editField) {
                  editField.value = String(eventId);
                }
                const autosaveField = form.querySelector('input[name="nexdirectory_autosave_post_id"]');
                if (autosaveField) {
                  autosaveField.value = String(eventId);
                }
                if (window.Alpine && form._x_dataStack && form._x_dataStack.length > 0) {
                  const scope = form._x_dataStack[0];
                  if (scope && typeof scope === 'object') {
                    scope.autosavePostId = String(eventId);
                  }
                }
                try {
                  const nextUrl = new URL(window.location.href);
                  nextUrl.searchParams.set('nexdirectory_tab', 'submit');
                  nextUrl.searchParams.set('edit_event_id', String(eventId));
                  nextUrl.hash = 'submit';
                  window.history.replaceState(window.history.state || {}, '', nextUrl.toString());
                } catch (error) {
                  // ignore malformed URL state
                }
              }
            } else {
              let failType = String(payload.type || 'error').toLowerCase();
              if (['warning', 'info'].indexOf(failType) < 0) {
                failType = 'error';
              }
              showToast(message || getMessage('restActionFailed', 'Request failed. Please try again.'), failType);
            }
            return;
          }

          const notice = parseNoticeFromUrl(response && response.url ? response.url : '');
          if (notice && notice.message) {
            showToast(notice.message, notice.type || 'info');
            return;
          }

          if (response && response.ok) {
            showToast('Listing updated.', 'success');
            return;
          }

          showToast(getMessage('restActionFailed', 'Request failed. Please try again.'), 'error');
        })
        .catch(function (error) {
          showToast(error && error.message ? error.message : getMessage('restActionFailed', 'Request failed. Please try again.'), 'error');
        })
        .finally(function () {
          form.removeAttribute('data-nexdirectory-ajax-submit-pending');
          $form.data('nexdirectorySubmitting', false).removeClass('nexdirectory-is-submitting');
          $buttons.prop('disabled', false).removeClass('nexdirectory-loading');
        });
    }, false);
  }

  function initModerationActionsAjax() {
    const ajaxEnabled = !!getConfig('enableModerationAjax', false);
    if (!ajaxEnabled) {
      return;
    }
    const singleEndpoint = String(getConfig('moderationRestUrl', '') || '');
    const bulkEndpoint = String(getConfig('moderationBulkRestUrl', '') || '');

    $(document).off('click.nexdirectoryModerationAction', '[data-nexdirectory-moderation-submit]').on('click.nexdirectoryModerationAction', '[data-nexdirectory-moderation-submit]', function (event) {
      const $btn = $(this);
      const $form = $btn.closest('form.nexdirectory-moderation-row-form');
      if (!$form.length) {
        return;
      }
      if (String($form.attr('data-nexdirectory-bypass-rest') || '') === '1') {
        $form.removeAttr('data-nexdirectory-bypass-rest');
        return;
      }
      const decision = String($btn.attr('data-nexdirectory-moderation-submit') || '').trim();
      const $decisionInput = $form.find('input[name="decision"]').first();
      if ($decisionInput.length) {
        $decisionInput.val(decision);
      }
      if (!singleEndpoint) {
        return;
      }

      event.preventDefault();
      event.stopPropagation();

      const postId = parseInt($form.find('input[name="post_id"]').val() || $form.find('input[name="event_id"]').val() || '0', 10);
      const note = String($form.find('[name="moderator_note"]').val() || '').trim();
      if (!postId || !decision) {
        showToast(getMessage('restActionFailed', 'Request failed. Please try again.'), 'error');
        return;
      }

      const $row = $form.closest('tr');
      const $buttons = $form.find('[data-nexdirectory-moderation-submit]');
      $buttons.prop('disabled', true).addClass('nexdirectory-loading');

      restPost(singleEndpoint, {
        post_id: postId,
        event_id: postId,
        decision: decision,
        moderator_note: note
      })
        .then(function (payload) {
          showToast((payload && payload.message) ? payload.message : 'Moderation updated.', 'success');
          if ($row.length) {
            $row.fadeOut(180, function () {
              $row.remove();
            });
          }
        })
        .catch(function (err) {
          if ($form.length) {
            $form.attr('data-nexdirectory-bypass-rest', '1');
            if ($decisionInput.length) {
              $decisionInput.val(decision);
            }
            const formEl = $form.get(0);
            const btnEl = $btn.get(0);
            if (formEl && typeof formEl.requestSubmit === 'function' && btnEl) {
              formEl.requestSubmit(btnEl);
            } else if (formEl && typeof formEl.submit === 'function') {
              formEl.submit();
            }
            return;
          }
          showToast(err && err.message ? err.message : getMessage('restActionFailed', 'Request failed. Please try again.'), 'error');
        })
        .finally(function () {
          $buttons.prop('disabled', false).removeClass('nexdirectory-loading');
        });
    });

    $(document).off('submit.nexdirectoryModerationBulkAjax', '.nexdirectory-moderation-bulk-form').on('submit.nexdirectoryModerationBulkAjax', '.nexdirectory-moderation-bulk-form', function (event) {
      const $form = $(this);
      if (String($form.attr('data-nexdirectory-bypass-rest') || '') === '1') {
        $form.removeAttr('data-nexdirectory-bypass-rest');
        return;
      }
      if (!bulkEndpoint) {
        return;
      }
      event.preventDefault();

      const formId = String($form.attr('id') || '').trim();
      const $decisionField = $form.find('[name="bulk_decision"], [name="decision"]').first();
      const decision = String($decisionField.val() || '').trim();
      const note = String($form.find('[name="moderator_note"]').val() || '').trim();
      const selector = formId ? 'input[name="post_ids[]"][form="' + formId + '"]:checked' : 'input[name="post_ids[]"]:checked';
      const ids = $(selector).map(function () {
        return parseInt($(this).val() || '0', 10);
      }).get().filter(function (id) {
        return Number.isFinite(id) && id > 0;
      });

      if (!ids.length || !decision) {
        showToast('Select events and a valid action first.', 'error');
        return;
      }

      const $submit = $form.find('button[type="submit"]').first();
      if ($submit.length) {
        $submit.prop('disabled', true).addClass('nexdirectory-loading');
      }

      restPost(bulkEndpoint, {
        post_ids: ids,
        decision: decision,
        moderator_note: note
      })
        .then(function (payload) {
          const errors = Array.isArray(payload && payload.errors) ? payload.errors : [];
          const failedIds = {};
          errors.forEach(function (row) {
            const id = parseInt(row && row.post_id, 10);
            if (Number.isFinite(id) && id > 0) {
              failedIds[id] = true;
            }
          });

          ids.forEach(function (postId) {
            if (failedIds[postId]) {
              return;
            }
            const $checkbox = $('input[name="post_ids[]"][value="' + String(postId) + '"]');
            const $row = $checkbox.closest('tr');
            if ($row.length) {
              $row.fadeOut(180, function () {
                $row.remove();
              });
            }
          });

          showToast((payload && payload.message) ? payload.message : 'Bulk moderation complete.', (payload && payload.failed) ? 'error' : 'success');
        })
        .catch(function (err) {
          $form.attr('data-nexdirectory-bypass-rest', '1');
          const formEl = $form.get(0);
          if (formEl && typeof formEl.requestSubmit === 'function') {
            formEl.requestSubmit();
          } else if (formEl && typeof formEl.submit === 'function') {
            formEl.submit();
          } else {
            showToast(err && err.message ? err.message : getMessage('restActionFailed', 'Request failed. Please try again.'), 'error');
          }
        })
        .finally(function () {
          $form.data('nexdirectorySubmitting', false).removeClass('nexdirectory-is-submitting');
          if ($submit.length) {
            $submit.prop('disabled', false).removeClass('nexdirectory-loading');
          }
        });
    });
  }

  function initModerationBulkActions() {
    const syncMasterCheckbox = function (formId) {
      const selector = 'input[name="post_ids[]"][form="' + formId + '"]';
      const $rows = $(selector);
      const $master = $('[data-nexdirectory-check-all][data-nexdirectory-target-form="' + formId + '"]');
      if (!$rows.length || !$master.length) {
        return;
      }
      const total = $rows.length;
      const checked = $rows.filter(':checked').length;
      const masterEl = $master.get(0);
      if (!masterEl) {
        return;
      }
      masterEl.indeterminate = checked > 0 && checked < total;
      masterEl.checked = checked > 0 && checked === total;
    };

    $(document).off('change.nexdirectoryModerationAll', '[data-nexdirectory-check-all]').on('change.nexdirectoryModerationAll', '[data-nexdirectory-check-all]', function () {
      const formId = String($(this).attr('data-nexdirectory-target-form') || 'nexdirectory-moderation-bulk-form').trim();
      if (!formId) {
        return;
      }
      const checked = !!this.checked;
      $('input[name="post_ids[]"][form="' + formId + '"]').prop('checked', checked);
      syncMasterCheckbox(formId);
    });

    $(document).off('change.nexdirectoryModerationRow', 'input[name="post_ids[]"][form]').on('change.nexdirectoryModerationRow', 'input[name="post_ids[]"][form]', function () {
      const formId = String($(this).attr('form') || '').trim();
      if (!formId) {
        return;
      }
      syncMasterCheckbox(formId);
    });

    $('[data-nexdirectory-check-all]').each(function () {
      const formId = String($(this).attr('data-nexdirectory-target-form') || 'nexdirectory-moderation-bulk-form').trim();
      if (formId) {
        syncMasterCheckbox(formId);
      }
    });
  }

  function initMyEventsActionsAjax() {
    const $scope = $('.nexdirectory-my-events-actions, .nexdirectory-dashboard-listing-actions');
    if (!$scope.length) {
      return;
    }

    const setStatusPill = function ($row, status, statusLabel) {
      const $pill = $row.find('.nexdirectory-status-pill').first();
      if (!$pill.length) {
        return;
      }
      const pillEl = $pill.get(0);
      if (!pillEl) {
        return;
      }
      Array.from(pillEl.classList || []).forEach(function (className) {
        if (String(className || '').indexOf('nexdirectory-status-') === 0 && className !== 'nexdirectory-status-pill') {
          pillEl.classList.remove(className);
        }
      });
      const cleanStatus = String(status || '').trim().toLowerCase();
      if (cleanStatus) {
        pillEl.classList.add('nexdirectory-status-' + cleanStatus);
      }
      const nextLabel = String(statusLabel || '').trim();
      if (nextLabel) {
        $pill.text(nextLabel);
      }
    };

    const setPendingMeta = function ($form, pending) {
      let $meta = $form.find('.nexdirectory-live-toggle-meta').first();
      if (pending) {
        if (!$meta.length) {
          const metaNode = document.createElement('small');
          metaNode.className = 'nexdirectory-live-toggle-meta';
          metaNode.textContent = 'Pending moderation';
          const input = $form.find('[data-nexdirectory-live-toggle-input="1"]').first();
          if (input.length) {
            input.closest('.nexdirectory-live-toggle').after(metaNode);
          } else {
            $form.append(metaNode);
          }
          $meta = $(metaNode);
        } else {
          $meta.text('Pending moderation');
        }
      } else if ($meta.length) {
        $meta.remove();
      }
    };

    const submitNative = function (form) {
      form.setAttribute('data-nexdirectory-my-events-bypass', '1');
      if (typeof form.requestSubmit === 'function') {
        form.requestSubmit();
        return;
      }
      if (typeof form.submit === 'function') {
        form.submit();
      }
    };

    $(document)
      .off('submit.nexdirectoryMyEventsActionsAjax', '.nexdirectory-my-events-actions form, .nexdirectory-dashboard-listing-actions form')
      .on('submit.nexdirectoryMyEventsActionsAjax', '.nexdirectory-my-events-actions form, .nexdirectory-dashboard-listing-actions form', function (event) {
        const form = this;
        if (!form) {
          return;
        }
        if (String(form.getAttribute('data-nexdirectory-my-events-bypass') || '') === '1') {
          form.removeAttribute('data-nexdirectory-my-events-bypass');
          return;
        }

        const $form = $(form);
        const actionName = String($form.find('input[name="action"]').first().val() || '').trim();
        if (['nexdirectory_toggle_event_live_draft', 'nexdirectory_delete_event'].indexOf(actionName) < 0) {
          return;
        }

        if (actionName === 'nexdirectory_delete_event' && form.getAttribute('data-nexdirectory-delete-confirm') === '1') {
          const title = String(form.getAttribute('data-nexdirectory-delete-title') || '').trim();
          const message = String(form.getAttribute('data-nexdirectory-delete-message') || 'Delete this listing?').trim();
          const confirmText = title
            ? message + '\n\n' + 'This will move "' + title + '" to the trash.'
            : message + '\n\n' + 'This will move the listing to the trash.';
          if (!window.confirm(confirmText)) {
            event.preventDefault();
            return;
          }
        }

        event.preventDefault();
        if (form.getAttribute('data-nexdirectory-my-events-pending') === '1') {
          return;
        }
        form.setAttribute('data-nexdirectory-my-events-pending', '1');

        const $row = $form.closest('tr, .nexdirectory-dashboard-listing-card');
        const $controls = $form.find('button, input, select');
        const formData = new FormData(form);
        formData.set('nexdirectory_ajax', '1');
        $row.attr('aria-busy', 'true');
        $controls.prop('disabled', true).addClass('nexdirectory-loading');

        const liveToggleInput = form.querySelector('[data-nexdirectory-live-toggle-input="1"]');
        const previousChecked = !!(liveToggleInput && liveToggleInput.checked);

        const actionUrl = String(form.getAttribute('action') || '/wp-admin/admin-post.php').trim() || '/wp-admin/admin-post.php';
        fetch(actionUrl, {
          method: 'POST',
          body: formData,
          credentials: 'same-origin',
          headers: {
            Accept: 'application/json',
            'X-Requested-With': 'XMLHttpRequest'
          }
        })
          .then(function (response) {
            return response.text().then(function (text) {
              return { response: response, text: text };
            });
          })
          .then(function (result) {
            let parsed = null;
            try {
              parsed = JSON.parse(String(result && result.text ? result.text : ''));
            } catch (error) {
              parsed = null;
            }

            if (!parsed || typeof parsed !== 'object' || !Object.prototype.hasOwnProperty.call(parsed, 'success')) {
              submitNative(form);
              return;
            }

            const payload = parsed && typeof parsed.data === 'object' && parsed.data ? parsed.data : {};
            const message = String(payload.message || (parsed.success ? 'Saved.' : getMessage('restActionFailed', 'Request failed. Please try again.'))).trim();
            if (!parsed.success) {
              showToast(message || getMessage('restActionFailed', 'Request failed. Please try again.'), 'error');
              if (liveToggleInput) {
                liveToggleInput.checked = previousChecked;
              }
              return;
            }

            showToast(message || 'Saved.', 'success');

            if (actionName === 'nexdirectory_delete_event') {
              if ($row.length) {
                $row.addClass('is-removing').fadeOut(180, function () {
                  $row.remove();
                });
              }
              return;
            }

            if (actionName === 'nexdirectory_toggle_event_live_draft') {
              const nextStatus = String(payload.status || '').trim().toLowerCase();
              const nextStatusLabel = String(payload.status_label || '').trim();
              setStatusPill($row, nextStatus, nextStatusLabel);

              if (liveToggleInput) {
                if (Object.prototype.hasOwnProperty.call(payload, 'state_live')) {
                  liveToggleInput.checked = !!payload.state_live;
                } else {
                  liveToggleInput.checked = nextStatus === 'publish';
                }
              }

              setPendingMeta($form, !!payload.pending_moderation);
            }
          })
          .catch(function (error) {
            showToast(error && error.message ? error.message : getMessage('restActionFailed', 'Request failed. Please try again.'), 'error');
            if (liveToggleInput) {
              liveToggleInput.checked = previousChecked;
            }
          })
          .finally(function () {
            form.removeAttribute('data-nexdirectory-my-events-pending');
            $row.removeAttr('aria-busy');
            $controls.prop('disabled', false).removeClass('nexdirectory-loading');
          });
      });
  }

  function getInboxLastSeenStorageKey(userId) {
    return 'nexdirectoryInboxLastSeenMessageId:' + String(userId || 0);
  }

  function scrollInboxChatToBottom(scope) {
    const root = scope && typeof scope.querySelector === 'function' ? scope : document;
    const chatArea = root.querySelector('#nexdirectory-chat-scroll-area');
    if (chatArea) {
      chatArea.scrollTop = chatArea.scrollHeight;
      return;
    }

    const inboxMessages = root.querySelector('[data-nexdirectory-inbox-messages], .nexdirectory-inbox-messages');
    if (inboxMessages) {
      inboxMessages.scrollTop = inboxMessages.scrollHeight;
    }
  }

  function initInboxMessageFocus() {
    const highlighted = document.querySelector('.nexdirectory-inbox-message-highlight');
    if (highlighted && typeof highlighted.scrollIntoView === 'function') {
      window.setTimeout(function () {
        highlighted.scrollIntoView({ behavior: 'smooth', block: 'center' });
      }, 120);
      return;
    }

    window.setTimeout(function () {
      scrollInboxChatToBottom(document);
    }, 80);
  }

  function initInboxThreadNavigationAjax() {
    if (typeof window.fetch !== 'function' || typeof window.DOMParser !== 'function') {
      return;
    }

    const getInboxPanel = function (root) {
      if (!root || typeof root.querySelector !== 'function') {
        return null;
      }
      return root.querySelector('[data-nexdirectory-inbox-panel="1"], .nexdirectory-inbox-panel') || null;
    };

    const replaceInboxShellSections = function (currentShell, nextShell) {
      const hydratedNodes = [];
      if (!currentShell || !nextShell) {
        return hydratedNodes;
      }

      const currentPanel = getInboxPanel(currentShell);
      const nextPanel = getInboxPanel(nextShell);
      if (currentPanel && nextPanel) {
        const nextPanelClone = nextPanel.cloneNode(true);
        currentPanel.replaceWith(nextPanelClone);
        hydratedNodes.push(nextPanelClone);
      }

      return hydratedNodes;
    };

    const inboxShells = document.querySelectorAll('[data-nexdirectory-inbox-shell="1"]');
    if (!inboxShells.length) {
      return;
    }

    inboxShells.forEach(function (shell) {
      if (!shell || shell.getAttribute('data-nexdirectory-inbox-ajax-bound') === '1') {
        return;
      }
      shell.setAttribute('data-nexdirectory-inbox-ajax-bound', '1');

      shell.addEventListener('click', function (event) {
        const link = event.target && event.target.closest
          ? event.target.closest('a[data-nexdirectory-inbox-thread-link="1"][href]')
          : null;
        if (!link || !shell.contains(link)) {
          return;
        }
        if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey || event.button !== 0) {
          return;
        }

        const href = String(link.getAttribute('href') || link.href || '').trim();
        if (!href || href === '#') {
          return;
        }

        let requestUrl;
        try {
          requestUrl = new URL(href, window.location.href);
        } catch (_err) {
          return;
        }
        if (requestUrl.origin !== window.location.origin) {
          return;
        }
        try {
          const currentUrl = new URL(window.location.href);
          const threadValue = String(requestUrl.searchParams.get('thread') || '').trim();
          const messageValue = String(requestUrl.searchParams.get('message') || '').trim();
          const nextUrl = new URL(currentUrl.toString());
          if (threadValue !== '') {
            nextUrl.searchParams.set('thread', threadValue);
          } else {
            nextUrl.searchParams.delete('thread');
          }
          if (messageValue !== '') {
            nextUrl.searchParams.set('message', messageValue);
          } else {
            nextUrl.searchParams.delete('message');
          }
          nextUrl.searchParams.set('nexdirectory_tab', 'inbox');
          nextUrl.hash = 'inbox';
          requestUrl = nextUrl;
        } catch (_err) {
          return;
        }

        event.preventDefault();
        if (shell.getAttribute('data-nexdirectory-inbox-loading') === '1') {
          return;
        }
        shell.setAttribute('data-nexdirectory-inbox-loading', '1');
        shell.classList.add('is-loading');

        fetch(requestUrl.toString(), { credentials: 'same-origin' })
          .then(function (res) {
            if (!res.ok) {
              throw new Error('inbox_thread_fetch_failed');
            }
            return res.text();
          })
          .then(function (html) {
            const parser = new window.DOMParser();
            const doc = parser.parseFromString(String(html || ''), 'text/html');
            const nextShell = doc.querySelector('[data-nexdirectory-inbox-shell="1"]');
            const currentShell = shell && shell.isConnected
              ? shell
              : document.querySelector('[data-nexdirectory-inbox-shell="1"]');
            if (!nextShell || !currentShell) {
              throw new Error('inbox_thread_shell_missing');
            }

            const hydratedNodes = replaceInboxShellSections(currentShell, nextShell);
            if (!hydratedNodes.length) {
              throw new Error('inbox_thread_panel_missing');
            }

            if (window.history && typeof window.history.pushState === 'function') {
              window.history.pushState({}, '', requestUrl.toString());
            }

            let alpineHydrated = true;
            if (window.Alpine && typeof window.Alpine.initTree === 'function') {
              hydratedNodes.forEach(function (node) {
                if (
                  node
                  && typeof node.querySelector === 'function'
                  && !hydrateAlpineTree(node, 'inbox-thread')
                ) {
                  alpineHydrated = false;
                }
              });
            }
            if (!alpineHydrated) {
              throw new Error('inbox_thread_alpine_hydration_failed');
            }

            try {
              window.dispatchEvent(new window.CustomEvent('nexdirectory-inbox-open-thread'));
            } catch (_err) {
              // no-op
            }

            scrollInboxChatToBottom(document);
            initInboxMessageFocus();
            initFormLoadingStates();
            initInboxReplyAjax();
          })
          .catch(function () {
            window.location.href = requestUrl.toString();
          })
          .finally(function () {
            const activeShell = shell && shell.isConnected
              ? shell
              : document.querySelector('[data-nexdirectory-inbox-shell="1"]');
            if (activeShell) {
              activeShell.removeAttribute('data-nexdirectory-inbox-loading');
              activeShell.classList.remove('is-loading');
            }
          });
      });
    });
  }

  function initInboxReplyAjax() {
    if (typeof window.fetch !== 'function' || typeof window.DOMParser !== 'function') {
      return;
    }

    const getInboxPanel = function (root) {
      if (!root || typeof root.querySelector !== 'function') {
        return null;
      }
      return root.querySelector('[data-nexdirectory-inbox-panel="1"], .nexdirectory-inbox-panel') || null;
    };

    const getInboxThreadList = function (root) {
      if (!root || typeof root.querySelector !== 'function') {
        return null;
      }
      return root.querySelector('[data-nexdirectory-inbox-thread-list="1"], .nexdirectory-inbox-thread-list') || null;
    };

    const replaceInboxShellSections = function (currentShell, nextShell) {
      const hydratedNodes = [];
      if (!currentShell || !nextShell) {
        return hydratedNodes;
      }

      const currentPanel = getInboxPanel(currentShell);
      const nextPanel = getInboxPanel(nextShell);
      if (currentPanel && nextPanel) {
        const nextPanelClone = nextPanel.cloneNode(true);
        currentPanel.replaceWith(nextPanelClone);
        hydratedNodes.push(nextPanelClone);
      }

      const currentThreadList = getInboxThreadList(currentShell);
      const nextThreadList = getInboxThreadList(nextShell);
      if (currentThreadList && nextThreadList) {
        const nextThreadListClone = nextThreadList.cloneNode(true);
        currentThreadList.replaceWith(nextThreadListClone);
        hydratedNodes.push(nextThreadListClone);
      }

      return hydratedNodes;
    };

    $(document)
      .off('submit.nexdirectoryInboxReplyAjax', 'form[data-nexdirectory-inbox-reply-form="1"]')
      .on('submit.nexdirectoryInboxReplyAjax', 'form[data-nexdirectory-inbox-reply-form="1"]', function (event) {
        const form = this;
        if (!form) {
          return;
        }
        if (String(form.getAttribute('data-nexdirectory-inbox-reply-bypass') || '') === '1') {
          form.removeAttribute('data-nexdirectory-inbox-reply-bypass');
          return;
        }
        if (String(form.getAttribute('data-nexdirectory-inbox-reply-pending') || '') === '1') {
          event.preventDefault();
          return;
        }

        const shell = form.closest('[data-nexdirectory-inbox-shell="1"]');
        if (!shell) {
          return;
        }

        event.preventDefault();
        form.setAttribute('data-nexdirectory-inbox-reply-pending', '1');

        const $form = $(form);
        const $controls = $form.find('button, textarea');
        $controls.prop('disabled', true).addClass('nexdirectory-loading');

        const actionUrl = String(form.getAttribute('action') || '/wp-admin/admin-post.php').trim() || '/wp-admin/admin-post.php';
        const formData = new FormData(form);

        fetch(actionUrl, {
          method: 'POST',
          body: formData,
          credentials: 'same-origin',
          headers: {
            'X-Requested-With': 'XMLHttpRequest'
          }
        })
          .then(function (res) {
            return res.text().then(function (text) {
              return { response: res, text: text };
            });
          })
          .then(function (result) {
            const response = result && result.response ? result.response : null;
            const finalUrl = response && typeof response.url === 'string' ? response.url : '';
            const parser = new window.DOMParser();
            const doc = parser.parseFromString(String((result && result.text) || ''), 'text/html');
            const nextShell = doc.querySelector('[data-nexdirectory-inbox-shell="1"]');
            const currentShell = shell && shell.isConnected
              ? shell
              : document.querySelector('[data-nexdirectory-inbox-shell="1"]');

            if (nextShell && currentShell) {
              const hydratedNodes = replaceInboxShellSections(currentShell, nextShell);
              if (hydratedNodes.length) {
                if (finalUrl && window.history && typeof window.history.replaceState === 'function') {
                  window.history.replaceState({}, '', finalUrl);
                }
                let alpineHydrated = true;
                if (window.Alpine && typeof window.Alpine.initTree === 'function') {
                  hydratedNodes.forEach(function (node) {
                    if (
                      node
                      && typeof node.querySelector === 'function'
                      && !hydrateAlpineTree(node, 'inbox-reply')
                    ) {
                      alpineHydrated = false;
                    }
                  });
                }
                if (!alpineHydrated) {
                  window.location.href = finalUrl || window.location.href;
                  return;
                }
                try {
                  window.dispatchEvent(new window.CustomEvent('nexdirectory-inbox-open-thread'));
                } catch (_err) {
                  // no-op
                }
                const notice = parseNoticeFromUrl(finalUrl);
                if (notice && notice.message) {
                  showToast(notice.message, notice.type || 'success');
                } else {
                  showToast('Reply sent.', 'success');
                }
                scrollInboxChatToBottom(document);
                initInboxMessageFocus();
                initFormLoadingStates();
                return;
              }
            }

            if (finalUrl) {
              window.location.href = finalUrl;
              return;
            }

            window.location.reload();
          })
          .catch(function () {
            form.setAttribute('data-nexdirectory-inbox-reply-bypass', '1');
            if (typeof form.requestSubmit === 'function') {
              form.requestSubmit();
            } else if (typeof form.submit === 'function') {
              form.submit();
            }
          })
          .finally(function () {
            form.removeAttribute('data-nexdirectory-inbox-reply-pending');
            $controls.prop('disabled', false).removeClass('nexdirectory-loading');
          });
      });
  }

  let notificationFeedTimer = null;
  let notificationFeedBusy = false;
  let notificationFeedItems = [];
  let notificationFeedUnreadCount = 0;

  function getNotificationCenterRoots() {
    return Array.prototype.slice.call(document.querySelectorAll('[data-nexdirectory-notification-center]'));
  }

  function getNotificationTypeGlyph(type) {
    const glyphs = {
      message: 'M',
      claim_submitted: 'C',
      claim_status: 'C',
      moderation: '✓',
      team_invite: 'T',
      team_update: 'T',
      rsvp: 'R',
      waitlist: 'W',
      waitlist_promoted: 'W',
      system: '•'
    };
    return glyphs[String(type || '')] || glyphs.system;
  }

  function renderNotificationCenters() {
    const roots = getNotificationCenterRoots();
    roots.forEach(function (root) {
      const list = root.querySelector('[data-nexdirectory-notification-list]');
      const empty = root.querySelector('[data-nexdirectory-notification-empty]');
      const markAll = root.querySelector('[data-nexdirectory-notification-mark-all]');
      const badge = root.querySelector('[data-nexdirectory-notification-count]');
      if (badge) {
        badge.textContent = notificationFeedUnreadCount > 99 ? '99+' : String(notificationFeedUnreadCount);
        badge.hidden = notificationFeedUnreadCount <= 0;
      }
      if (markAll) {
        markAll.disabled = notificationFeedUnreadCount <= 0;
      }
      if (!list) {
        return;
      }

      list.textContent = '';
      list.setAttribute('aria-busy', 'false');
      if (!notificationFeedItems.length) {
        if (empty) {
          empty.hidden = false;
        }
        return;
      }
      if (empty) {
        empty.hidden = true;
      }

      const fragment = document.createDocumentFragment();
      notificationFeedItems.forEach(function (item) {
        const notificationId = parseInt(String((item && item.id) || '0'), 10) || 0;
        const type = String((item && item.type) || 'system').trim().toLowerCase();
        const url = String((item && item.url) || '').trim();
        const isUnread = !!(item && item.is_unread);
        const entry = document.createElement(url ? 'a' : 'article');
        entry.className = 'nexdirectory-notification-center__item nexdirectory-notification-center__item--' + type.replace(/_/g, '-');
        if (isUnread) {
          entry.classList.add('is-unread');
        }
        entry.setAttribute('data-nexdirectory-notification-id', String(notificationId));
        entry.setAttribute('aria-label', (isUnread ? String(getMessage('notificationUnread', 'Unread notification')) + ': ' : '') + String((item && item.title) || ''));
        if (url) {
          entry.href = url;
        }

        const icon = document.createElement('span');
        icon.className = 'nexdirectory-notification-center__icon';
        icon.setAttribute('aria-hidden', 'true');
        icon.textContent = getNotificationTypeGlyph(type);
        entry.appendChild(icon);

        const content = document.createElement('span');
        content.className = 'nexdirectory-notification-center__content';

        const meta = document.createElement('span');
        meta.className = 'nexdirectory-notification-center__meta';
        const typeLabel = document.createElement('span');
        typeLabel.textContent = String((item && item.type_label) || getMessage('notificationTitle', 'Notification'));
        meta.appendChild(typeLabel);
        const createdLabel = String((item && item.created_label) || '').trim();
        if (createdLabel) {
          const separator = document.createElement('span');
          separator.setAttribute('aria-hidden', 'true');
          separator.textContent = '•';
          meta.appendChild(separator);
          const created = document.createElement('time');
          created.dateTime = String((item && item.created_at) || '');
          created.textContent = createdLabel;
          meta.appendChild(created);
        }
        content.appendChild(meta);

        const title = document.createElement('strong');
        title.className = 'nexdirectory-notification-center__title';
        title.textContent = String((item && item.title) || getMessage('notificationTitle', 'Notification'));
        content.appendChild(title);

        const body = String((item && item.body) || '').trim();
        if (body) {
          const bodyNode = document.createElement('span');
          bodyNode.className = 'nexdirectory-notification-center__body';
          bodyNode.textContent = body;
          content.appendChild(bodyNode);
        }
        entry.appendChild(content);

        if (notificationId > 0 && isUnread) {
          entry.addEventListener('click', function (event) {
            const targetUrl = url;
            if (targetUrl) {
              event.preventDefault();
            }
            markNotificationsRead([notificationId]).finally(function () {
              if (targetUrl) {
                window.location.href = targetUrl;
              }
            });
          });
        }
        fragment.appendChild(entry);
      });
      list.appendChild(fragment);
    });
  }

  function requestNotificationFeed() {
    const endpoint = String(getConfig('notificationFeedRestUrl', '') || '').trim();
    const restNonce = String(getConfig('restNonce', '') || '').trim();
    const userId = parseInt(String(getConfig('currentUserId', 0) || '0'), 10) || 0;
    if (!endpoint || !restNonce || userId <= 0 || notificationFeedBusy || !getNotificationCenterRoots().length) {
      return Promise.resolve(null);
    }

    notificationFeedBusy = true;
    return fetch(endpoint + (endpoint.indexOf('?') === -1 ? '?' : '&') + 'per_page=24', {
      method: 'GET',
      credentials: 'same-origin',
      headers: {
        'X-WP-Nonce': restNonce
      }
    })
      .then(async function (response) {
        let payload = {};
        try {
          payload = await response.json();
        } catch (_err) {
          payload = {};
        }
        if (!response.ok) {
          throw new Error(String((payload && payload.message) || getMessage('notificationLoadFailed', 'Notifications could not be loaded right now.')));
        }
        return payload || {};
      })
      .then(function (payload) {
        notificationFeedItems = Array.isArray(payload.items) ? payload.items : [];
        notificationFeedUnreadCount = Math.max(0, parseInt(String(payload.unread_count || '0'), 10) || 0);
        renderNotificationCenters();
        return payload;
      })
      .catch(function () {
        getNotificationCenterRoots().forEach(function (root) {
          const list = root.querySelector('[data-nexdirectory-notification-list]');
          if (list) {
            list.setAttribute('aria-busy', 'false');
          }
        });
        return null;
      })
      .finally(function () {
        notificationFeedBusy = false;
      });
  }

  function markNotificationsRead(notificationIds) {
    const endpoint = String(getConfig('notificationFeedRestUrl', '') || '').trim();
    const restNonce = String(getConfig('restNonce', '') || '').trim();
    const ids = Array.isArray(notificationIds)
      ? notificationIds.map(function (id) { return parseInt(String(id || '0'), 10) || 0; }).filter(function (id) { return id > 0; })
      : [];
    if (!endpoint || !restNonce) {
      return Promise.resolve(null);
    }

    const action = ids.length ? 'mark_read' : 'mark_all_read';
    return fetch(endpoint, {
      method: 'POST',
      credentials: 'same-origin',
      headers: {
        'Content-Type': 'application/json',
        'X-WP-Nonce': restNonce
      },
      body: JSON.stringify({
        action: action,
        ids: ids
      })
    })
      .then(async function (response) {
        let payload = {};
        try {
          payload = await response.json();
        } catch (_err) {
          payload = {};
        }
        if (!response.ok) {
          throw new Error(String((payload && payload.message) || getMessage('restActionFailed', 'Request failed. Please try again.')));
        }
        return payload || {};
      })
      .then(function (payload) {
        if (ids.length) {
          notificationFeedItems = notificationFeedItems.map(function (item) {
            const itemId = parseInt(String((item && item.id) || '0'), 10) || 0;
            if (ids.indexOf(itemId) >= 0) {
              return Object.assign({}, item, { is_unread: false });
            }
            return item;
          });
        } else {
          notificationFeedItems = notificationFeedItems.map(function (item) {
            return Object.assign({}, item, { is_unread: false });
          });
        }
        notificationFeedUnreadCount = Math.max(0, parseInt(String((payload && payload.unread_count) || '0'), 10) || 0);
        renderNotificationCenters();
        return payload;
      })
      .catch(function (error) {
        showToast(
          String((error && error.message) || getMessage('restActionFailed', 'Request failed. Please try again.')),
          'error'
        );
        return null;
      });
  }

  function initNotificationCenter() {
    const roots = getNotificationCenterRoots();
    const endpoint = String(getConfig('notificationFeedRestUrl', '') || '').trim();
    const restNonce = String(getConfig('restNonce', '') || '').trim();
    const userId = parseInt(String(getConfig('currentUserId', 0) || '0'), 10) || 0;
    if (!roots.length || !endpoint || !restNonce || userId <= 0) {
      return;
    }

    roots.forEach(function (root) {
      if (root.getAttribute('data-nexdirectory-notification-bound') === '1') {
        return;
      }
      root.setAttribute('data-nexdirectory-notification-bound', '1');
      const toggle = root.querySelector('[data-nexdirectory-notification-toggle]');
      const panel = root.querySelector('[data-nexdirectory-notification-panel]');
      const markAll = root.querySelector('[data-nexdirectory-notification-mark-all]');

      if (toggle && panel) {
        const setOpen = function (isOpen) {
          const open = !!isOpen;
          toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
          panel.hidden = !open;
          if (open) {
            requestNotificationFeed();
          }
        };
        toggle.addEventListener('click', function (event) {
          event.preventDefault();
          event.stopPropagation();
          const nextOpen = toggle.getAttribute('aria-expanded') !== 'true';
          getNotificationCenterRoots().forEach(function (otherRoot) {
            if (otherRoot === root) {
              return;
            }
            const otherToggle = otherRoot.querySelector('[data-nexdirectory-notification-toggle]');
            const otherPanel = otherRoot.querySelector('[data-nexdirectory-notification-panel]');
            if (otherToggle && otherPanel) {
              otherToggle.setAttribute('aria-expanded', 'false');
              otherPanel.hidden = true;
            }
          });
          setOpen(nextOpen);
        });
        panel.addEventListener('click', function (event) {
          event.stopPropagation();
        });
      }

      if (markAll) {
        markAll.addEventListener('click', function (event) {
          event.preventDefault();
          if (!markAll.disabled) {
            markNotificationsRead([]);
          }
        });
      }
    });

    if (!document.documentElement.hasAttribute('data-nexdirectory-notification-events-bound')) {
      document.documentElement.setAttribute('data-nexdirectory-notification-events-bound', '1');
      document.addEventListener('click', function (event) {
        getNotificationCenterRoots().forEach(function (root) {
          if (root.contains(event.target)) {
            return;
          }
          const toggle = root.querySelector('[data-nexdirectory-notification-toggle]');
          const panel = root.querySelector('[data-nexdirectory-notification-panel]');
          if (toggle && panel) {
            toggle.setAttribute('aria-expanded', 'false');
            panel.hidden = true;
          }
        });
      });
      document.addEventListener('keydown', function (event) {
        if (event.key !== 'Escape') {
          return;
        }
        getNotificationCenterRoots().forEach(function (root) {
          const toggle = root.querySelector('[data-nexdirectory-notification-toggle]');
          const panel = root.querySelector('[data-nexdirectory-notification-panel]');
          if (toggle && panel && !panel.hidden) {
            toggle.setAttribute('aria-expanded', 'false');
            panel.hidden = true;
            toggle.focus();
          }
        });
      });
    }

    renderNotificationCenters();
    requestNotificationFeed();
    if (!notificationFeedTimer) {
      notificationFeedTimer = window.setInterval(function () {
        if (document.visibilityState === 'visible') {
          requestNotificationFeed();
        }
      }, 30000);
    }
  }

  let inboxPollTimer = null;
  let inboxPollBusy = false;
  let inboxPollSeeded = false;

  function initInboxPolling() {
    const endpoint = String(getConfig('inboxPollRestUrl', '') || '').trim();
    const restNonce = String(getConfig('restNonce', '') || '').trim();
    const userId = parseInt(String(getConfig('currentUserId', 0) || '0'), 10) || 0;
    if (!endpoint || !restNonce || userId <= 0) {
      return;
    }
    if (inboxPollTimer) {
      return;
    }

    const readStoredLastSeen = function () {
      let value = 0;
      try {
        value = parseInt(window.localStorage.getItem(getInboxLastSeenStorageKey(userId)) || '0', 10) || 0;
      } catch (_err) {
        value = 0;
      }
      return value > 0 ? value : 0;
    };

    const writeStoredLastSeen = function (messageId) {
      const cleanId = parseInt(String(messageId || '0'), 10) || 0;
      if (cleanId <= 0) {
        return;
      }
      try {
        window.localStorage.setItem(getInboxLastSeenStorageKey(userId), String(cleanId));
      } catch (_err) {
        // ignore storage failures
      }
    };

    let lastSeenMessageId = readStoredLastSeen();
    const poll = function () {
      if (inboxPollBusy) {
        return;
      }
      const syncedSeen = readStoredLastSeen();
      if (syncedSeen > lastSeenMessageId) {
        lastSeenMessageId = syncedSeen;
      }

      inboxPollBusy = true;
      const query = new URLSearchParams();
      if (lastSeenMessageId > 0) {
        query.set('last_seen_message_id', String(lastSeenMessageId));
      }
      const requestUrl = endpoint + (endpoint.indexOf('?') === -1 ? '?' : '&') + query.toString();
      fetch(requestUrl, {
        method: 'GET',
        credentials: 'same-origin',
        headers: {
          'X-WP-Nonce': restNonce
        }
      })
        .then(async function (res) {
          let payload = {};
          try {
            payload = await res.json();
          } catch (_err) {
            payload = {};
          }
          if (!res.ok) {
            throw new Error(String((payload && payload.message) || 'poll_failed'));
          }
          return payload || {};
        })
        .then(function (payload) {
          const latestMessageId = parseInt(String((payload && payload.latest_message_id) || '0'), 10) || 0;
          const hasNew = !!(payload && payload.has_new);
          const toastBody = String((payload && payload.toast_message) || getMessage('inboxToastBody', 'You have a new message in your inbox.')).trim();

          // Baseline unread state so existing unread history does not spam toasts on first page load.
          if (!inboxPollSeeded && lastSeenMessageId <= 0) {
            if (latestMessageId > 0) {
              lastSeenMessageId = latestMessageId;
              writeStoredLastSeen(lastSeenMessageId);
            }
            inboxPollSeeded = true;
            return;
          }

          inboxPollSeeded = true;
          if (latestMessageId > lastSeenMessageId) {
            if (hasNew) {
              showToast(toastBody, 'info', {
                title: String(getMessage('inboxToastTitle', 'New Inbox Message') || 'New Inbox Message').trim(),
                subtitle: String(getMessage('inboxToastSubtitle', 'Open your inbox to view and reply.') || 'Open your inbox to view and reply.').trim(),
                duration: 8500
              });
            }
            lastSeenMessageId = latestMessageId;
            writeStoredLastSeen(lastSeenMessageId);
          }
        })
        .catch(function () {
          // Silent failure to avoid noisy polling errors for users.
        })
        .finally(function () {
          inboxPollBusy = false;
        });
    };

    poll();
    inboxPollTimer = window.setInterval(poll, 25000);
  }

  function initElementorEventsAjaxSearch() {
    const restNonce = String(getConfig('restNonce', '') || '').trim();
    const fallbackError = String(getMessage('restActionFailed', 'Request failed. Please try again.') || '').trim();
    const noResultsLabel = String(getMessage('widgetSearchNoResults', 'No matches yet. Try different keywords.') || '').trim();

    $('[data-nexdirectory-widget-search]').each(function () {
      const $widget = $(this);
      if ($widget.data('nexdirectoryWidgetSearchBound') === 1) {
        return;
      }
      $widget.data('nexdirectoryWidgetSearchBound', 1);

      let config = {};
      const configRaw = String($widget.attr('data-nexdirectory-widget-config') || '').trim();
      if (configRaw) {
        try {
          config = JSON.parse(configRaw) || {};
        } catch (_err) {
          config = {};
        }
      }

      const endpoint = String(config.endpoint || '').trim();
      if (!endpoint) {
        return;
      }
      const $form = $widget.find('[data-nexdirectory-widget-search-form]').first();
      const $resultsWrap = $widget.find('[data-nexdirectory-widget-search-results]').first();
      const $count = $widget.find('[data-nexdirectory-widget-search-count]').first();
      const defaults = config.defaults && typeof config.defaults === 'object' ? config.defaults : {};
      const minChars = Math.max(0, parseInt(config.minChars || 2, 10) || 0);
      let timer = null;
      let activeController = null;

      const updateCount = function (value) {
        if (!$count.length) {
          return;
        }
        const countNum = Math.max(0, parseInt(value || 0, 10) || 0);
        const suffix = countNum === 1 ? 'result' : 'results';
        $count.text(String(countNum) + ' ' + suffix);
      };

      const serializePayload = function () {
        const payload = {
          template: String(config.template || '1'),
          view: String(config.view || 'search'),
          limit: Math.max(1, parseInt(config.limit || 8, 10) || 8),
          orderby: String(config.orderby || 'start'),
          order: String(config.order || 'ASC'),
          tax_relation: String(config.taxRelation || 'AND'),
          custom_taxonomy: String(config.customTaxonomy || ''),
          custom_terms: String(config.customTerms || ''),
          include_past: String(config.includePast || 'no'),
          show_excerpt: String(config.showExcerpt || 'no'),
          show_meta: String(config.showMeta || 'yes'),
          excerpt_length: Math.max(8, parseInt(config.excerptLength || 14, 10) || 14)
        };
        const fields = ['search', 'event_type', 'event_mode', 'event_tag', 'event_price', 'city', 'date_from', 'official'];
        fields.forEach(function (fieldName) {
          const $field = $form.find('[name="' + fieldName + '"]').first();
          payload[fieldName] = $field.length ? String($field.val() || '').trim() : String(defaults[fieldName] || '').trim();
        });
        return payload;
      };

      const renderNoResults = function () {
        const label = noResultsLabel || 'No matches yet. Try different keywords.';
        $resultsWrap.html('<div class="nexdirectory-widget-empty">' + escapeHtml(label) + '</div>');
      };

      const requestResults = function () {
        const payload = serializePayload();
        if (minChars > 0 && payload.search && payload.search.length < minChars) {
          updateCount(0);
          renderNoResults();
          return;
        }

        if (activeController && typeof activeController.abort === 'function') {
          activeController.abort();
        }
        activeController = new AbortController();
        $widget.addClass('is-loading');

        const endpointUrl = new URL(endpoint, window.location.origin);
        Object.keys(payload).forEach(function (key) {
          const value = payload[key];
          if (value === '' || value === null || typeof value === 'undefined') {
            return;
          }
          endpointUrl.searchParams.set(key, String(value));
        });

        fetch(endpointUrl.toString(), {
          method: 'GET',
          credentials: 'same-origin',
          headers: restNonce ? { 'X-WP-Nonce': restNonce } : {},
          signal: activeController.signal
        })
          .then(async function (res) {
            let data = {};
            try {
              data = await res.json();
            } catch (_err) {
              data = {};
            }
            if (!res.ok) {
              throw new Error(String((data && data.message) || fallbackError || 'Request failed.'));
            }
            return data || {};
          })
          .then(function (data) {
            const html = String((data && data.html) || '').trim();
            if (html) {
              $resultsWrap.html(html);
            } else {
              renderNoResults();
            }
            const found = parseInt((data && (data.found || data.count)) || 0, 10) || 0;
            updateCount(found);
          })
          .catch(function (error) {
            if (error && error.name === 'AbortError') {
              return;
            }
            showToast((error && error.message) ? error.message : (fallbackError || 'Request failed.'), 'error');
          })
          .finally(function () {
            $widget.removeClass('is-loading');
          });
      };

      const queueRequest = function (delay) {
        if (timer) {
          window.clearTimeout(timer);
        }
        timer = window.setTimeout(requestResults, Math.max(40, parseInt(delay || 160, 10) || 160));
      };

      $form.off('submit.nexdirectoryWidgetSearch').on('submit.nexdirectoryWidgetSearch', function (event) {
        event.preventDefault();
        requestResults();
      });

      $form.off('input.nexdirectoryWidgetSearch change.nexdirectoryWidgetSearch').on('input.nexdirectoryWidgetSearch change.nexdirectoryWidgetSearch', 'input, select', function () {
        const fieldName = String($(this).attr('name') || '').trim();
        if (fieldName === 'search') {
          queueRequest(300);
          return;
        }
        queueRequest(120);
      });

      $widget.find('[data-nexdirectory-widget-search-reset]').off('click.nexdirectoryWidgetSearch').on('click.nexdirectoryWidgetSearch', function () {
        ['search', 'event_type', 'event_mode', 'event_tag', 'event_price', 'city', 'date_from', 'official'].forEach(function (fieldName) {
          const $field = $form.find('[name="' + fieldName + '"]').first();
          if (!$field.length) {
            return;
          }
          $field.val(typeof defaults[fieldName] !== 'undefined' ? String(defaults[fieldName]) : '');
        });
        requestResults();
      });
    });
  }

	  function runFrontendInitializers() {
	    markUiHydrating();
	    const steps = [
      ['ensureAlpineReady', ensureAlpineReady],
      ['initUrlNoticeToast', initUrlNoticeToast],
      ['initSpaFeedbackBadge', initSpaFeedbackBadge],
      ['initPagePreloader', initPagePreloader],
      ['initSmoothScrollingEngine', initSmoothScrollingEngine],
      ['initHybridSpaRouter', initHybridSpaRouter],
      ['initCardLinks', initCardLinks],
      ['initNoticeToasts', initNoticeToasts],
      ['initAuthTabs', initAuthTabs],
      ['initSubmitNavSpy', initSubmitNavSpy],
      ['initUserManagementSearch', initUserManagementSearch],
      ['initArchiveFiltersAjax', initArchiveFiltersAjax],
      ['initMap', initMap],
      ['initGeocode', initGeocode],
      ['initCustomFieldUploads', initCustomFieldUploads],
      ['initSubmitUploaderFallback', initSubmitUploaderFallback],
      ['initPageBannerHeroBackground', initPageBannerHeroBackground],
      ['initEventHeroParallax', initEventHeroParallax],
      ['initAutogeneratedDisclaimer', initAutogeneratedDisclaimer],
      ['initEventGalleryLightbox', initEventGalleryLightbox],
      ['initMobileEventGallerySliders', initMobileEventGallerySliders],
      ['initEventVideoLightbox', initEventVideoLightbox],
      ['initEventDocumentLightbox', initEventDocumentLightbox],
      ['initRecaptchaForms', initRecaptchaForms],
      ['initFormLoadingStates', initFormLoadingStates],
      ['initSubmitEventAjax', initSubmitEventAjax],
      ['initRsvpAjax', initRsvpAjax],
      ['initEventAnalyticsTracking', initEventAnalyticsTracking],
      ['initModerationActionsAjax', initModerationActionsAjax],
      ['initModerationBulkActions', initModerationBulkActions],
      ['initMyEventsActionsAjax', initMyEventsActionsAjax],
      ['initInboxThreadNavigationAjax', initInboxThreadNavigationAjax],
      ['initInboxReplyAjax', initInboxReplyAjax],
      ['initInboxMessageFocus', initInboxMessageFocus],
      ['initNotificationCenter', initNotificationCenter],
      ['initInboxPolling', initInboxPolling],
      ['initElementorEventsAjaxSearch', initElementorEventsAjaxSearch],
      ['initScrollReveal', initScrollReveal]
    ];

    steps.forEach(function (step) {
      const name = Array.isArray(step) ? String(step[0] || '') : '';
      const fn = Array.isArray(step) ? step[1] : null;
      if (typeof fn !== 'function') {
        return;
      }
      try {
        fn();
      } catch (error) {
        if (window.console && typeof window.console.error === 'function') {
          window.console.error('[NEXDIRECTORY init] ' + name + ' failed', error);
	        }
	      }
	    });
	    markUiReady(120);
	  }

  $(document).ready(function () {
    runFrontendInitializers();
  });
})(jQuery);
