(function(){
  // Global modal controller. Initializes once and configures any modal markup on demand.
  // Exposes: window.MBModal with minimal API (initDialog, applyFromTrigger)
  if(window.MBModal) return;

  function getIds(modalId){
    return {
      dialogId: modalId,
      wrapperId: modalId + '-wrapper',
      panelId: modalId + '-panel',
      headerId: modalId + '-header',
      titleId: modalId + '-title',
      headerCloseId: modalId + '-header-close',
      headerIconId: modalId + '-header-icon',
      floatingCloseId: modalId + '-floating-close',
      bodyId: modalId + '-body',
      iframeId: modalId + '-iframe',
      footerId: modalId + '-footer',
      headerExtrasId: modalId + '-header-extras',
      headerToolbarId: modalId + '-header-toolbar',
      headerToolbarRightId: modalId + '-header-toolbar-right',
      loadingId: modalId + '-loading',
    };
  }

  function getById(id){ return id ? document.getElementById(id) : null; }
  function getAttr(el, /*names*/){
    for(var i=1;i<arguments.length;i++){
      var n = arguments[i];
      try{ var v = el.getAttribute(n); if(v !== null) return v; }catch(e){}
    }
    return null;
  }

  function withHeightTransition(panel, fn) {
    if (!panel || panel.offsetHeight === 0) {
      fn();
      return;
    }

    var startHeight = panel.offsetHeight;
    fn();

    var targetHeight = panel.style.height;
    if (!targetHeight || targetHeight === 'auto') {
      panel.style.height = 'auto';
    }
    var endHeight = panel.offsetHeight;

    if (startHeight === endHeight) {
      if (!targetHeight || targetHeight === 'auto') {
        panel.style.removeProperty('height');
      }
      return;
    }

    panel.style.height = startHeight + 'px';
    panel.offsetHeight; // force reflow

    panel.style.transition = 'height 300ms ease-in-out';
    panel.style.height = endHeight + 'px';

    var finished = false;
    var onEnd = function(e) {
      if (finished) return;
      if (e && e.propertyName && e.propertyName !== 'height') return;
      finished = true;
      panel.removeEventListener('transitionend', onEnd);
      if (!targetHeight || targetHeight === 'auto') {
        panel.style.removeProperty('height');
      } else {
        panel.style.height = targetHeight;
      }
      panel.style.removeProperty('transition');
    };
    panel.addEventListener('transitionend', onEnd);
    setTimeout(onEnd, 400); // safety fallback
  }

  /**
   * Configure a dialog element using attributes from a trigger element (btn),
   * falling back to the dialog's initial default attributes if not provided by the trigger.
   */
  function applyTriggerToDialog(dlg, btn) {
    var modalId = dlg.id;
    if (!dlg._mbDefaults) return;

    var getV = function(attr, attr2) {
      var v = getAttr(btn, attr, attr2);
      if (v === null) {
        var key = attr.replace('data-', '').replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); });
        if (attr === 'data-heigth') key = 'height';
        v = dlg._mbDefaults[key];
      }
      return v;
    };

    var url = getV('data-url') || '';
    var title = getV('data-title') || '';
    var icon = getV('data-icon') || '';
    var type = getV('data-type') || '';
    var iconClass = getV('data-icon-class') || '';
    var size = (getV('data-size') || '').trim();
    var width = (getV('data-width') || '').trim();
    var height = (getV('data-height', 'data-heigth') || '').trim();
    var closeAttr = getV('data-close-button');
    var closeEnabled = (closeAttr === null || closeAttr === '' || closeAttr === 'true');
    var dismissibleAttr = getV('data-dismissible');
    var dismissible = !(dismissibleAttr === 'false');
    var bodyClass = (getV('data-body-class') || '').trim();
    var popin = getV('data-popin');
    var scrollHints = getV('data-scroll-hints');
    var style = (getV('data-style') || '').trim();

    // Persist attributes to the dialog element. This allows other components and
    // mutation observers to react to the current configuration.
    var attrsToSet = {
      'data-style': style,
      'data-type': type,
      'data-title': title,
      'data-icon': icon,
      'data-icon-class': iconClass,
      'data-url': url,
      'data-size': size,
      'data-width': width,
      'data-height': height,
      'data-close-button': closeEnabled ? 'true' : 'false',
      'data-dismissible': dismissible ? 'true' : 'false',
      'data-body-class': bodyClass,
      'data-popin': popin,
      'data-scroll-hints': scrollHints
    };

    Object.keys(attrsToSet).forEach(function(k) {
      var val = attrsToSet[k];
      if (val !== null && val !== undefined && val !== '') {
        dlg.setAttribute(k, val);
      } else {
        dlg.removeAttribute(k);
      }
    });

    // Manually trigger updates to ensure immediate execution in correct order
    toggleHeaderAndClose(modalId, title, closeEnabled, icon, iconClass, type);
    applySize(modalId, size, width, height);
    applyBodyClass(modalId, bodyClass);
    applyPopin(modalId, popin);
    applyDismissible(modalId, dismissible);
    configureContentMode(modalId, url, btn);
  }

  function clearTeleportSlots(modalId, triggerBtn){
    var ids = getIds(modalId);
    [ids.footerId, ids.headerExtrasId, ids.headerToolbarId, ids.headerToolbarRightId].forEach(function(id){
      var el = getById(id);
      if(el){
        if (triggerBtn && el.contains(triggerBtn)) {
          setTimeout(function() {
            if (el.contains(triggerBtn)) { el.innerHTML = ''; }
          }, 0);
        } else {
          el.innerHTML = '';
        }
      }
    });
  }

  var sizeClassMap = {
    sm: { width: ['max-w-sm'], height: 'auto' },
    md: { width: ['max-w-md'], height: 'auto' },
    lg: { width: ['max-w-lg'], height: 'auto' },
    xl: { width: ['max-w-xl'], height: '60vh' },
    '2xl': { width: ['max-w-2xl'], height: '70vh' },
    '3xl': { width: ['max-w-3xl'], height: '80vh' },
    '4xl': { width: ['max-w-4xl'], height: '85vh' },
    '5xl': { width: ['max-w-5xl'], height: '95vh' },
    '6xl': { width: ['max-w-6xl'], height: '95vh' },
    full: { width: ['max-w-none', 'w-screen'], height: '100vh' },
    'full-margin': { width: ['max-w-none', 'w-screen'], height: '100vh' },
    'default': { width: ['max-w-2xl'], height: '70vh' },
    'confirm': { width: ['max-w-md'], height: 'auto' }
  };

  function applySize(modalId, size, width, height){
    var ids = getIds(modalId);
    var panel = getById(ids.panelId);
    if(!panel) return;

    withHeightTransition(panel, function() {
      var wrapper = getById(ids.wrapperId);
      var bodyEl = getById(ids.bodyId);
      var iframe = getById(ids.iframeId);
      var bodyContainer = bodyEl ? bodyEl.parentElement : null;

      var dlg = getById(modalId);
      var style = dlg ? (dlg.getAttribute('data-style') || '') : '';

      var all = new Set([]);
      Object.keys(sizeClassMap).forEach(function(k){ sizeClassMap[k].width.forEach(function(c){ all.add(c); }); });
      all.forEach(function(c){ panel.classList.remove(c); });
      panel.classList.remove('rounded-none', 'rounded-lg', 'rounded-xl', 'rounded-2xl');
      panel.classList.add(style === 'confirm' ? 'rounded-2xl' : 'rounded-2xl');
      panel.style.removeProperty('width');
      panel.style.removeProperty('maxWidth');
      panel.style.removeProperty('height');

      // Use 100vh for full screen modals, otherwise default to 95vh
      var isFull = (size === 'full' || (size && size.split('-')[0] === 'full'));
      panel.style.maxHeight = isFull ? '100vh' : '95vh';

      if(wrapper){
        wrapper.classList.remove('p-0');
        if(!wrapper.classList.contains('p-4')) wrapper.classList.add('p-4');
      }
      if(bodyContainer){
        bodyContainer.classList.remove('max-h-[90vh]', 'max-h-[95vh]');
        bodyContainer.classList.add('max-h-[95vh]');
        if(style !== 'confirm'){
          bodyContainer.classList.remove('flex-1','min-h-0');
        }
      }
      if(iframe){
        iframe.classList.remove('h-full');
        if(!iframe.classList.contains('h-[70vh]')) iframe.classList.add('h-[70vh]');
      }

      if(width){ panel.style.width = width; }

      // Parse size for suffixes (e.g. 2xl-sm)
      var baseSize = size || '';
      var sizeSuffix = '';
      if (size && !sizeClassMap[size] && size.indexOf('-') !== -1) {
        var parts = size.split('-');
        baseSize = parts[0];
        sizeSuffix = parts[1];
      }

      // Determine effective configuration
      var sizeCfg = sizeClassMap[size] || sizeClassMap[baseSize] || sizeClassMap[style] || sizeClassMap['default'];

      // Determine effective height
      var effHeight = height;
      if(!effHeight){
        effHeight = sizeCfg.height;
        if(sizeSuffix === 'sm'){
          effHeight = 'auto';
        }
      }

      if(effHeight && effHeight !== 'auto'){
        panel.style.height = effHeight;
        if(bodyContainer){
          bodyContainer.classList.remove('max-h-[90vh]', 'max-h-[95vh]');
          bodyContainer.classList.add('flex-1','min-h-0');
        }
        if(iframe){
          iframe.classList.remove('h-[70vh]');
          iframe.classList.add('h-full');
        }
      }

      // Apply width classes
      sizeCfg.width.forEach(function(c){ panel.classList.add(c); });

      var finalSize = sizeClassMap[size] ? size : baseSize;

      if(finalSize === 'full'){
        if(wrapper){
          wrapper.classList.remove('p-4');
          wrapper.classList.add('p-0');
          wrapper.classList.add('sm:p-0');
        }
        panel.classList.add('rounded-none');
      }

      if(finalSize === 'full-margin'){
        if(wrapper){
          wrapper.classList.remove('p-0');
          if(!wrapper.classList.contains('p-4')) wrapper.classList.add('p-4');
          wrapper.classList.remove('sm:p-0');
        }
        if(!height){
          panel.style.maxHeight = 'calc(100vh - 2rem)';
        }
      }
    });
  }

  // After HTMX content is loaded into the modal body, adopt the height to content again
  // This clears any preset fixed height so the panel can size naturally to its content,
  // while keeping a reasonable max height cap via the body container.
  function adoptHeightToContent(modalId){
    try{
      var ids = getIds(modalId);
      var dlg = getById(ids.dialogId);
      var panel = getById(ids.panelId);
      var bodyEl = getById(ids.bodyId);
      var iframe = getById(ids.iframeId);
      var bodyContainer = bodyEl ? bodyEl.parentElement : null;

      // If in iframe mode, do nothing here
      if(iframe && !iframe.classList.contains('hidden')) return;
      // Keep full-screen and large modals as-is to maintain their viewport-relative height
      var size = dlg ? (dlg.getAttribute('data-size') || '') : '';
      if(['full', 'full-margin', '5xl', '6xl'].indexOf(size) !== -1) return;

      withHeightTransition(panel, function() {
        if(panel){
          panel.style.removeProperty('height');
          panel.style.maxHeight = '95vh';
        }
        if(bodyContainer){
          // bodyContainer.classList.add('max-h-[80vh]');
          var style = dlg ? (dlg.getAttribute('data-style') || '') : '';
          if(style !== 'confirm'){
            bodyContainer.classList.remove('flex-1','min-h-0');
          }
        }
        if(iframe){
          iframe.classList.remove('h-full');
          if(!iframe.classList.contains('h-[70vh]')) iframe.classList.add('h-[70vh]');
        }
      });
    }catch(e){}
  }

  // Apply optional extra CSS classes to the modal body container while
  // preserving the base layout classes.
  //
  // The extra classes come from either a data-body-class attribute on the
  // trigger element (preferred) or directly on the <dialog> element. They
  // are appended to the body wrapper's existing classes and tracked in
  // data-mb-body-extra so we can remove only the previously applied extras
  // on subsequent openings, without touching the base classes.
  function applyBodyClass(modalId, bodyClass){
    try{
      var ids = getIds(modalId);
      var bodyEl = getById(ids.bodyId);
      if(!bodyEl || !bodyEl.parentElement) return;
      var bodyContainer = bodyEl.parentElement;

      // Remove any previously applied extra classes
      var prev = bodyContainer.getAttribute('data-mb-body-extra') || '';
      if(prev){
        var prevParts = prev.split(/\s+/);
        for(var i=0;i<prevParts.length;i++){
          var c = prevParts[i];
          if(!c) continue;
          try{ bodyContainer.classList.remove(c); }catch(eRem){}
        }
      }

      bodyClass = (bodyClass || '').trim();
      bodyContainer.setAttribute('data-mb-body-extra', bodyClass);

      if(bodyClass){
        var parts = bodyClass.split(/\s+/);
        for(var j=0;j<parts.length;j++){
          var c2 = parts[j];
          if(!c2) continue;
          try{ bodyContainer.classList.add(c2); }catch(eAdd){}
        }
      }
    }catch(e){}
  }

  function applyPopin(modalId, popin){
    try{
      var ids = getIds(modalId);
      var panel = getById(ids.panelId);
      if(!panel) return;
      if(popin === 'true' || popin === true){
        panel.classList.add('data-enter:animate-popin');
        panel.classList.remove('data-enter:animate-modal-overswing');
      } else {
        panel.classList.remove('data-enter:animate-popin');
        panel.classList.add('data-enter:animate-modal-overswing');
      }
    }catch(e){}
  }

  // A modal is "persistent" / "exclusive" when data-dismissible="false".
  // Such a modal cannot be dismissed via the Escape key or a backdrop click;
  // it can only be closed through an explicit close button / command.
  function isPersistent(dlg){
    return !!(dlg && dlg.getAttribute('data-dismissible') === 'false');
  }

  // Reflect the dismissible state onto the dialog. When supported, also set
  // the native `closedby="none"` attribute as a progressive enhancement; the
  // JS guards bound in initDialog remain the cross-browser fallback.
  function applyDismissible(modalId, dismissible){
    try{
      var ids = getIds(modalId);
      var dlg = getById(ids.dialogId);
      if(!dlg) return;
      if(dismissible === false || dismissible === 'false'){
        try{ dlg.setAttribute('closedby', 'none'); }catch(e){}
      } else {
        try{ dlg.removeAttribute('closedby'); }catch(e){}
      }
    }catch(e){}
  }

  function toggleHeaderAndClose(modalId, title, closeEnabled, icon, iconClass, type){
    var ids = getIds(modalId);
    var header = getById(ids.headerId);
    var dlg = getById(ids.dialogId);
    var floating = getById(ids.floatingCloseId);
    var headerClose = getById(ids.headerCloseId);
    var titleEl = getById(ids.titleId);
    var iconEl = getById(ids.headerIconId);
    var hasTitle = !!(title && String(title).trim());
    var style = dlg ? (dlg.getAttribute('data-style') || '') : '';

    if (type && !icon) {
      try {
        var typeIcons = window.MB_MODAL_TYPE_ICONS || {};
        if (typeIcons[type]) {
          icon = typeIcons[type];
        }
      } catch (e) {}
    }

    var hasIcon = !!(icon && String(icon).trim());

    if(titleEl){ titleEl.textContent = hasTitle ? title : ''; }

    if(iconEl){
      if(hasIcon){
        iconEl.innerHTML = icon;
        iconEl.className = (iconClass || '').trim();
        if (style === 'confirm') {
          iconEl.classList.add('mx-auto');
        }
        iconEl.classList.remove('hidden');
      } else {
        iconEl.innerHTML = '';
        iconEl.classList.add('hidden');
      }
    }

    if(hasTitle || hasIcon){
      if(header){
        header.classList.remove('hidden');
        if (style === 'confirm') {
          // Adjust top padding based on icon presence for a balanced, compact look
          if (hasIcon) {
            header.classList.add('pt-6');
            header.classList.remove('pt-4');
          } else {
            header.classList.add('pt-4');
            header.classList.remove('pt-6');
          }
        }
      }
      if(floating){ floating.classList.add('hidden'); }
      if(headerClose){ closeEnabled ? headerClose.classList.remove('hidden') : headerClose.classList.add('hidden'); }
    } else {
      if(header){ header.classList.add('hidden'); }
      if(floating){ closeEnabled ? floating.classList.remove('hidden') : floating.classList.add('hidden'); }
    }
  }

  function dispatchDialogEvent(modalId, name, detail) {
    var dlg = getById(modalId);
    if (!dlg) return;
    try {
      dlg.dispatchEvent(new CustomEvent(name, {detail: detail || {}, bubbles: true}));
    } catch (e) {
      // Fallback for older browsers without CustomEvent options
      var evt;
      try {
        evt = document.createEvent('CustomEvent');
        evt.initCustomEvent(name, true, false, detail || {});
      } catch (e2) {
        try {
          evt = new Event(name, {bubbles: true});
          evt.detail = detail || {};
        } catch (e3) {
          evt = null;
        }
      }
      if (evt) dlg.dispatchEvent(evt);
    }
  }

  // Parse HTMX trigger header payload (`HX-Trigger`, `HX-Trigger-After-Swap`, `HX-Trigger-After-Settle`).
  // Returns an array of { name: string, detail: any }.
  function parseHtmxTriggerHeader(value) {
    if (value === null || value === undefined) return [];
    var raw = String(value).trim();
    if (!raw) return [];

    var out = [];
    var push = function(name, detail) {
      name = (name || '').trim();
      if (!name) return;
      out.push({ name: name, detail: detail });
    };

    // JSON format (recommended by HTMX)
    if (raw[0] === '{' || raw[0] === '[') {
      try {
        var parsed = JSON.parse(raw);
        // Object form: {"event": true|{}|[]|...}
        if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
          Object.keys(parsed).forEach(function(k) {
            var v = parsed[k];
            if (v === false) return; // allow explicit suppression

            // `true`/`null` => event with empty detail
            if (v === true || v === null || v === undefined) {
              push(k, {});
              return;
            }

            // Array => trigger once per element (HTMX behavior)
            if (Array.isArray(v)) {
              if (v.length === 0) {
                push(k, {});
              } else {
                v.forEach(function(item) {
                  push(k, item);
                });
              }
              return;
            }

            // Object => use directly as detail
            if (v && typeof v === 'object') {
              push(k, v);
              return;
            }

            // Primitive => wrap to keep `detail` predictable
            push(k, { value: v });
          });
          return out;
        }

        // Array form: ["event1", "event2"]
        if (Array.isArray(parsed)) {
          parsed.forEach(function(item) {
            if (typeof item === 'string') push(item, {});
            else push(String(item), {});
          });
          return out;
        }
      } catch (e) {
        // Fall through to non-JSON parsing.
      }
    }

    // Plain format: "event" or "event1, event2"
    raw.split(',').forEach(function(part) {
      var name = String(part || '').trim();
      if (name) push(name, {});
    });
    return out;
  }

  function dispatchCustomEventSafe(target, name, detail) {
    if (!target) return;
    try {
      target.dispatchEvent(new CustomEvent(name, { detail: detail, bubbles: true }));
    } catch (e) {
      try {
        var evt = document.createEvent('CustomEvent');
        evt.initCustomEvent(name, true, false, detail);
        target.dispatchEvent(evt);
      } catch (e2) {
        try {
          var ev2 = new Event(name, { bubbles: true });
          ev2.detail = detail;
          target.dispatchEvent(ev2);
        } catch (e3) {}
      }
    }
  }

  // When legacy apps run inside an iframe modal, HTMX dispatches `HX-Trigger` events inside the
  // iframe document. BeezUI components (e.g. the newsletter CollectionList) listen on the parent
  // `document.body`, so we forward those triggers to the parent.
  function bindIframeHtmxTriggerForwarder(iframe) {
    if (!iframe) return;

    // Cleanup any previous binding (iframe navigations).
    try {
      if (iframe._mbHtmxForwarder && typeof iframe._mbHtmxForwarder.cleanup === 'function') {
        iframe._mbHtmxForwarder.cleanup();
      }
    } catch (e) {}

    var cw, cd, body;
    try {
      cw = iframe.contentWindow;
      cd = iframe.contentDocument;
      body = cd && cd.body ? cd.body : null;
    } catch (e2) {
      // Cross-origin or blocked access.
      return;
    }
    if (!cw || !cd || !body) return;

    // Only attach when HTMX is available inside the iframe; otherwise the headers won't be handled
    // there either, and we have nothing reliable to forward.
    if (!cw.htmx) return;

    // Avoid double-binding for the same iframe document.
    try {
      if (iframe._mbHtmxForwarder && iframe._mbHtmxForwarder.boundTo === body) return;
    } catch (e3) {}

    var parentBody = document && document.body ? document.body : null;
    if (!parentBody) return;

    var forwardFromHeader = function(evt, headerName) {
      try {
        if (!evt || !evt.detail || !evt.detail.xhr) return;
        var xhr = evt.detail.xhr;
        if (!xhr.getResponseHeader) return;
        var headerVal = xhr.getResponseHeader(headerName);
        var triggers = parseHtmxTriggerHeader(headerVal);
        if (!triggers || !triggers.length) return;
        triggers.forEach(function(t) {
          dispatchCustomEventSafe(parentBody, t.name, t.detail);
        });
      } catch (e) {}
    };

    var onAfterOnLoad = function(evt) { forwardFromHeader(evt, 'HX-Trigger'); };
    var onAfterSwap = function(evt) { forwardFromHeader(evt, 'HX-Trigger-After-Swap'); };
    var onAfterSettle = function(evt) { forwardFromHeader(evt, 'HX-Trigger-After-Settle'); };

    // Use capture to be resilient to stopPropagation in legacy apps.
    body.addEventListener('htmx:afterOnLoad', onAfterOnLoad, true);
    body.addEventListener('htmx:afterSwap', onAfterSwap, true);
    body.addEventListener('htmx:afterSettle', onAfterSettle, true);

    iframe._mbHtmxForwarder = {
      boundTo: body,
      cleanup: function() {
        try { body.removeEventListener('htmx:afterOnLoad', onAfterOnLoad, true); } catch (e1) {}
        try { body.removeEventListener('htmx:afterSwap', onAfterSwap, true); } catch (e2) {}
        try { body.removeEventListener('htmx:afterSettle', onAfterSettle, true); } catch (e3) {}
      }
    };
  }

  function showLoading(modalId, show) {
    var overlay = getById(getIds(modalId).loadingId);
    if (!overlay) return;
    if (show) {
      MBLoader && MBLoader.show(overlay);
      dispatchDialogEvent(modalId, 'mb:loading:show');
    } else {
      MBLoader && MBLoader.hide(overlay);
      dispatchDialogEvent(modalId, 'mb:loading:hide');
    }
  }

  function showIframe(modalId, url, triggerBtn){
    var ids = getIds(modalId);
    var iframe = getById(ids.iframeId);
    var body = getById(ids.bodyId);
    var overlay = getById(ids.loadingId);
    if(iframe){
      if(url){
        if(overlay && window.MBLoader){ MBLoader.scheduleShow(overlay, 350); }
        try{ iframe.src = 'about:blank'; }catch(e){}
        iframe.onload = function(){
          try{
            var isBlank = iframe.contentWindow && iframe.contentWindow.location && iframe.contentWindow.location.href === 'about:blank';
            if(overlay && window.MBLoader){ MBLoader.clearTimer(overlay, '_showTimer'); }
            if (!isBlank) {
              // Forward HTMX `HX-Trigger*` events from the iframe document to the parent.
              // This is crucial for BeezUI listeners that are scoped to `from:body`.
              bindIframeHtmxTriggerForwarder(iframe);
              if (overlay && !overlay.classList.contains('hidden') && window.MBLoader) {
                MBLoader.hide(overlay);
              }
              dispatchDialogEvent(modalId, 'mb:iframe:loaded', {url: url});
            }
          } catch (e) {
            if (overlay && window.MBLoader) {
              MBLoader.clearTimer(overlay, '_showTimer');
            }
            if (overlay && !overlay.classList.contains('hidden') && window.MBLoader) {
              MBLoader.hide(overlay);
            }
          }
        };
        iframe.src = url;
        dispatchDialogEvent(modalId, 'mb:iframe:loading', {url: url});
      }
      iframe.classList.remove('hidden');
    }
    if(body){
      body.classList.add('hidden');
      if (triggerBtn && body.contains(triggerBtn)) {
        setTimeout(function() {
          if (body.contains(triggerBtn)) { body.innerHTML = ''; }
        }, 0);
      } else {
        body.innerHTML = '';
      }
    }
  }

  function showBody(modalId){
    var ids = getIds(modalId);
    var iframe = getById(ids.iframeId);
    var body = getById(ids.bodyId);
    if(iframe){ iframe.classList.add('hidden'); try{ iframe.src = 'about:blank'; }catch(e){} }
    if(body){ body.classList.remove('hidden'); }
    // Do not hide loading overlay here; leave it to MBLoader/iframe handlers to avoid flashing old content
    //   showLoading(modalId, false); // re-added
    dispatchDialogEvent(modalId, 'mb:body:show');
  }

  function configureContentMode(modalId, url, triggerBtn){
    var ids = getIds(modalId);
    var dlg = getById(ids.dialogId);
    var style = dlg ? (dlg.getAttribute('data-style') || '') : '';

    // Special handling for embedded email previewer style
    if(style === 'email_preview'){
      // Always use body mode; forward url to page component via custom event
      showBody(modalId);
      try{
        var body = getById(ids.bodyId);
        // Support both legacy `data-email-preview-root` and the newer shared
        // `data-device-preview-root` used by `DevicePreviewer`.
        var root = body ? body.querySelector('[data-device-preview-root],[data-email-preview-root]') : null;
        if(root && url){
          var ev;
          try{ ev = new CustomEvent('mb:email-preview:set-url', { detail: { url: url }, bubbles: false }); }
          catch(e1){
            try{ ev = document.createEvent('CustomEvent'); ev.initCustomEvent('mb:email-preview:set-url', false, false, { url: url }); }
            catch(e2){ ev = null; }
          }
          if(ev) root.dispatchEvent(ev);
        }
      }catch(e){}
      return;
    }

    // Special handling for embedded web previewer style
    if(style === 'web_preview'){
      // Always use body mode; forward url to page component via custom event
      showBody(modalId);
      try{
        var bodyW = getById(ids.bodyId);
        var rootW = bodyW ? bodyW.querySelector('[data-device-preview-root]') : null;
        if(rootW && url){
          var evW;
          try{ evW = new CustomEvent('mb:web-preview:set-url', { detail: { url: url }, bubbles: false }); }
          catch(eW1){
            try{ evW = document.createEvent('CustomEvent'); evW.initCustomEvent('mb:web-preview:set-url', false, false, { url: url }); }
            catch(eW2){ evW = null; }
          }
          if(evW) rootW.dispatchEvent(evW);
        }
      }catch(eW){}
      return;
    }

    // Non-email_preview mode: clear teleport slots and body to ensure no leftover content from previous usage
    clearTeleportSlots(modalId, triggerBtn);
    var body = getById(ids.bodyId);
    if(body){
      // Lock current height if already open and in HTMX mode to avoid jump during content clear/load.
      // For iframes, we rely on the fixed heights set by applySize.
      var panel = getById(ids.panelId);
      if(!url && panel && panel.offsetHeight > 0 && !panel.style.height){
        panel.style.height = panel.offsetHeight + 'px';
      }
      
      if (triggerBtn && body.contains(triggerBtn)) {
        setTimeout(function() {
          if (body.contains(triggerBtn)) { body.innerHTML = ''; }
        }, 0);
      } else {
        body.innerHTML = '';
      }
    }

    if(url){
      showIframe(modalId, url, triggerBtn);
    } else {
      // HTMX mode: show a light overlay to avoid flashing stale DOM
      var overlay = getById(ids.loadingId);
      if(overlay && window.MBLoader){ MBLoader.show(overlay); }
      showBody(modalId);
    }
  }

  function bindHtmxOverlayOnce(modalId){
    try{
      var ids = getIds(modalId);
      if(window.MBLoader){ MBLoader.bindHtmxOverlay(ids.bodyId, ids.loadingId, 350); }
    }catch(e){}
  }

  function initDialog(modalId){
    if(!modalId) return;
    var ids = getIds(modalId);
    var dlg = getById(ids.dialogId);
    if(!dlg) return;

    // Capture initial attributes as defaults for this dialog instance
    if (!dlg._mbDefaults) {
      dlg._mbDefaults = {
        style: dlg.getAttribute('data-style'),
        size: dlg.getAttribute('data-size'),
        width: dlg.getAttribute('data-width'),
        height: dlg.getAttribute('data-height') || dlg.getAttribute('data-heigth'),
        type: dlg.getAttribute('data-type'),
        title: dlg.getAttribute('data-title'),
        icon: dlg.getAttribute('data-icon'),
        iconClass: dlg.getAttribute('data-icon-class'),
        closeButton: dlg.getAttribute('data-close-button'),
        dismissible: dlg.getAttribute('data-dismissible'),
        bodyClass: dlg.getAttribute('data-body-class'),
        popin: dlg.getAttribute('data-popin'),
        scrollHints: dlg.getAttribute('data-scroll-hints'),
        url: dlg.getAttribute('data-url')
      };
    }

    // Reset any pending optimistic close state when the dialog is initialized or re-opened
    if (dlg._mbCloseTimeout) {
      clearTimeout(dlg._mbCloseTimeout);
      dlg._mbCloseTimeout = null;
    }
    dlg.style.removeProperty('opacity');
    dlg.style.removeProperty('pointer-events');
    dlg.style.removeProperty('transition');

    window.__MBModalInited = window.__MBModalInited || {};
    if(window.__MBModalInited[modalId]) return;
    window.__MBModalInited[modalId] = true;

    // Observe slots for HTMX content via teleport
    try{
      var teleportSlots = [
        { id: ids.footerId, toggleVisible: true },
        { id: ids.headerExtrasId },
        { id: ids.headerToolbarId },
        { id: ids.headerToolbarRightId }
      ];

      teleportSlots.forEach(function(slotInfo){
        var el = getById(slotInfo.id);
        if(!el) return;
        var obs = new MutationObserver(function(){
          if(slotInfo.toggleVisible){
            if(el.innerHTML.trim() !== ''){
              el.classList.remove('hidden');
            } else {
              el.classList.add('hidden');
            }
          }
          try {
            if (window.htmx && typeof window.htmx.process === 'function') {
              window.htmx.process(el);
            }
          } catch (e) {}
        });
        obs.observe(el, { childList: true, subtree: true, characterData: true });
      });

      var obs = new MutationObserver(function(list){
        for(var i=0;i<list.length;i++){
          var m = list[i];
          if(m.type !== 'attributes') continue;
          var attr = m.attributeName;
          if(attr === 'data-size' || attr === 'data-width' || attr === 'data-heigth' || attr === 'data-height' || attr === 'data-style'){
            var size = getAttr(dlg,'data-size') || '';
            var style = getAttr(dlg,'data-style') || '';
            var width = getAttr(dlg,'data-width') || '';
            var height = getAttr(dlg,'data-height','data-heigth') || '';
            applySize(modalId, size, width, height);
          } else if(attr === 'data-url'){
            var url = getAttr(dlg,'data-url') || '';
            configureContentMode(modalId, url);
          } else if(attr === 'data-title' || attr === 'data-close-button' || attr === 'data-icon' || attr === 'data-icon-class' || attr === 'data-type'){
            var title = getAttr(dlg,'data-title') || '';
            var icon = getAttr(dlg,'data-icon') || '';
            var type = getAttr(dlg,'data-type') || '';
            var iconClass = getAttr(dlg,'data-icon-class') || '';
            var closeAttr = getAttr(dlg,'data-close-button');
            var closeEnabled = (closeAttr === null || closeAttr === '' || closeAttr === 'true');
            toggleHeaderAndClose(modalId, title, closeEnabled, icon, iconClass, type);
          } else if(attr === 'data-body-class'){
            var cls = getAttr(dlg,'data-body-class') || '';
            applyBodyClass(modalId, cls);
          } else if(attr === 'data-popin'){
            var popin = getAttr(dlg,'data-popin');
            applyPopin(modalId, popin);
          } else if(attr === 'data-dismissible'){
            var dismissibleAttr = getAttr(dlg,'data-dismissible');
            applyDismissible(modalId, !(dismissibleAttr === 'false'));
          } else if(attr === 'data-scroll-hints'){
            // Alpine will handle it via its own update() if we trigger something or just by next tick
          }
        }
      });
      obs.observe(dlg, { attributes: true });
    }catch(e){}

    // Initialize default header/close state
    try{
      var title = dlg.getAttribute('data-title') || '';
      var icon = dlg.getAttribute('data-icon') || '';
      var type = dlg.getAttribute('data-type') || '';
      var iconClass = dlg.getAttribute('data-icon-class') || '';
      toggleHeaderAndClose(modalId, title, true, icon, iconClass, type);
      var popin = dlg.getAttribute('data-popin');
      applyPopin(modalId, popin);
      var dismissibleAttr = dlg.getAttribute('data-dismissible');
      applyDismissible(modalId, !(dismissibleAttr === 'false'));
    }catch(e){}

    // Enforce persistent ("exclusive") behavior: when data-dismissible="false"
    // the modal must not close via the Escape key or a backdrop click. Explicit
    // close buttons (command="close" / [data-modal-close]) are unaffected.
    //
    // IMPORTANT: The Tailwind Plus <el-dialog> web component does NOT rely on
    // the native <dialog> light-dismiss. For both the Escape key and a backdrop
    // click it dispatches its own cancelable `cancel` event on the *<el-dialog>
    // wrapper element* (not on the inner <dialog>), roughly:
    //     if (elDialog.dispatchEvent(new Event('cancel', {cancelable:true}))) dialog.close();
    // i.e. calling preventDefault() on that `cancel` event stops the close.
    // Listening on the inner <dialog> therefore never fires; we must bind the
    // guard on the wrapper. We also keep guards on the <dialog> itself as a
    // belt-and-suspenders fallback for native light-dismiss behavior.
    try{
      var guardCancel = function(ev){
        if(isPersistent(dlg)){
          ev.preventDefault();
          if(typeof ev.stopImmediatePropagation === 'function'){ ev.stopImmediatePropagation(); }
        }
      };

      // (a) Tailwind Plus <el-dialog> wrapper: this is where the real close
      //     decision happens for Escape and backdrop clicks.
      var wrapper = (dlg.closest && dlg.closest('el-dialog')) || dlg.parentElement;
      if(wrapper && wrapper.localName === 'el-dialog'){
        wrapper.addEventListener('cancel', guardCancel);
      }

      // (b) Native <dialog> `cancel` (Escape) fallback for environments that
      //     use native light-dismiss instead of the web component.
      dlg.addEventListener('cancel', guardCancel);

      // (c) Backdrop click fallback -> a click on the backdrop, the dialog box
      //     itself, or the centering wrapper (everything inside the dialog that
      //     is not the panel). Stop it at the source in the capture phase.
      dlg.addEventListener('click', function(ev){
        if(!isPersistent(dlg)) return;
        var target = ev.target;
        if(!target) return;
        // A click inside the actual panel/content must never be blocked.
        var inPanel = false;
        try{
          inPanel = !!(target.closest && target.closest('el-dialog-panel'));
        }catch(e){}
        if(inPanel) return;
        ev.preventDefault();
        ev.stopPropagation();
        if(typeof ev.stopImmediatePropagation === 'function'){ ev.stopImmediatePropagation(); }
      }, true);
    }catch(e){}

    // Bind HTMX overlay for the modal
    bindHtmxOverlayOnce(modalId);

    // Height adoption for HTMX content swaps
    try {
      var bodyEl = getById(ids.bodyId);
      var panelEl = getById(ids.panelId);
      if (bodyEl && window.htmx) {
        bodyEl.addEventListener('htmx:beforeSwap', function() {
          if (panelEl && panelEl.offsetHeight > 0 && !panelEl.style.height) {
            panelEl.style.height = panelEl.offsetHeight + 'px';
          }
        });
        var afterSwap = function() {
          setTimeout(function() { adoptHeightToContent(modalId); }, 0);
        };
        bodyEl.addEventListener('htmx:afterSwap', afterSwap);
        bodyEl.addEventListener('htmx:afterSettle', afterSwap);
      }
    } catch (e) {}

    // Cleanup on close
    dlg.addEventListener('close', function(){
      if (dlg._mbCloseTimeout) {
        clearTimeout(dlg._mbCloseTimeout);
        dlg._mbCloseTimeout = null;
      }
      dlg.style.removeProperty('opacity');
      dlg.style.removeProperty('pointer-events');
      dlg.style.removeProperty('transition');

      try{ dlg.removeAttribute('data-url'); }catch(e){}
      var ids = getIds(modalId);
      var body = getById(ids.bodyId);
      var style = dlg ? (dlg.getAttribute('data-style') || '') : '';

      // Notify app-level cleaners (emoji picker, third-party overlays, etc.)
      // before clearing content so panel-scoped nodes/listeners can be disposed.
      dispatchDialogEvent(modalId, 'mb:modal:cleanup', {
        modalId: modalId,
        panelId: ids.panelId,
        bodyId: ids.bodyId,
        phase: 'close'
      });

      // Preserve embedded preview components across closes; other styles clear content
      if(style !== 'email_preview' && style !== 'web_preview'){
        if(body){ body.innerHTML = ''; }
        clearTeleportSlots(modalId);
      }

      showBody(modalId);
      // Ensure any loading overlay is hidden when the dialog is closed
      showLoading(modalId, false);
      // Reset any extra body classes so the next open starts from a clean base
      try{ applyBodyClass(modalId, ''); }catch(eReset){}
      try{ dlg.removeAttribute('data-body-class'); }catch(eAttr){}
    });
  }

  // Optimistic close enhancement: close a modal immediately on click.
  // Instead of closing immediately, we make it invisible so HTMX responses
  // can still be processed (e.g. scripts executed or content swapped)
  // while giving the user the impression that the modal is closed.
  document.addEventListener('click', function(ev){
    var t = ev.target;
    var btn = t && t.closest ? t.closest('[data-modal-close]') : null;
    if(btn){
      var modalId = btn.getAttribute('data-modal-close');
      var dlg = (modalId && modalId !== 'true') ? document.getElementById(modalId) : btn.closest('dialog');
      if(dlg && typeof dlg.close === 'function'){
        // Apply optimistic hide
        dlg.style.transition = 'opacity 250ms ease-out';
        dlg.style.opacity = '0';
        dlg.style.pointerEvents = 'none';

        // If the click initiates an HTMX request, keep the dialog technically open
        // (but hidden) until the request completes; otherwise HTMX may dispatch
        // `HX-Trigger` events on a detached element and they won't bubble to `body`.
        var hasHtmxRequest = false;
        try {
          hasHtmxRequest = !!(
            btn.getAttribute('hx-post') || btn.getAttribute('data-hx-post') ||
            btn.getAttribute('hx-get') || btn.getAttribute('data-hx-get') ||
            btn.getAttribute('hx-put') || btn.getAttribute('data-hx-put') ||
            btn.getAttribute('hx-patch') || btn.getAttribute('data-hx-patch') ||
            btn.getAttribute('hx-delete') || btn.getAttribute('data-hx-delete')
          );
        } catch (e0) {}

        var clearCloseTimeout = function() {
          try {
            if (dlg._mbCloseTimeout) {
              clearTimeout(dlg._mbCloseTimeout);
              dlg._mbCloseTimeout = null;
            }
          } catch (e1) {}
        };

        var doClose = function() {
          clearCloseTimeout();
          try {
            if (dlg.open) {
              dlg.close();
            }
          } catch (e2) {}
        };

        clearCloseTimeout();

        if (hasHtmxRequest && window.htmx) {
          // Close after HTMX completes (success or error); keep a long safety timeout.
          var eventNames = ['htmx:afterRequest', 'htmx:responseError', 'htmx:sendError', 'htmx:timeout'];
          var wrappers = {};
          var cleanup = function() {
            try {
              eventNames.forEach(function(n) {
                var w = wrappers[n];
                if (w) {
                  try { btn.removeEventListener(n, w, true); } catch (eR) {}
                }
              });
            } catch (eC) {}
          };

          eventNames.forEach(function(n) {
            var wrapped = function(evt) {
              cleanup();
              // Defer close to ensure HTMX has finished processing triggers/swaps.
              setTimeout(doClose, 0);
            };
            wrappers[n] = wrapped;
            try { btn.addEventListener(n, wrapped, true); } catch (eA) {}
          });

          dlg._mbCloseTimeout = setTimeout(doClose, 15000);
        } else {
          // Non-HTMX: keep original quick close.
          dlg._mbCloseTimeout = setTimeout(doClose, 1500);
        }
      }
    }
  }, true);

  // Configure modal before opening when clicking a trigger
  document.addEventListener('click', function(ev){
    // Guard: ignore clicks originating from elements marked to skip modal handling
    var t = ev.target;
    try{
      if(t && t.closest && (t.closest('[data-no-modal]') || t.closest('[data-modal-ignore]'))){ return; }
    }catch(e){}
    var btn = ev.target && ev.target.closest ? ev.target.closest('[command="show-modal"][commandfor]') : null;
    if(!btn) return;
    var modalId = getAttr(btn, 'commandfor');
    if(!modalId) return;
    initDialog(modalId);
    var dlg = getById(modalId);
    if(!dlg) return;

    applyTriggerToDialog(dlg, btn);

    // Add this to ensure non-standard triggers (like TR) also open the dialog
    var ln = btn.localName;
    var isStandardTrigger = ln === 'button' || (ln === 'input' && (btn.type === 'button' || btn.type === 'submit' || btn.type === 'image'));
    if (dlg && !dlg.open && !isStandardTrigger) {
      try {
        if (typeof dlg.show === 'function') dlg.show();
        else dlg.setAttribute('open', '');
      } catch (e) {
        try { dlg.setAttribute('open', ''); } catch (e2) {}
      }
    }
  }, true);

  window.MBModal = {
    initDialog: initDialog,
    applyFromTrigger: function(btn){
      if(!btn) return;
      var modalId = getAttr(btn, 'commandfor');
      if(!modalId) return;
      initDialog(modalId);
      var dlg = getById(modalId);
      if(!dlg) return;

      applyTriggerToDialog(dlg, btn);

      if (dlg.open) return;

      // When MBModal is driven programmatically (e.g. from the legacy visual
      // editor iframe via openEditorBox), Tailwind+ <el-dialog> logic that
      // normally opens the underlying <dialog> on user click is bypassed.
      // This means the iframe loads and mb:iframe:* events fire, but the
      // dialog never gains the `open` attribute and remains invisible.
      //
      // To mirror the behavior of a real click trigger, explicitly open the
      // <dialog> here after configuring its content.
      try{
        if(typeof dlg.show === 'function'){
          dlg.show();
        } else {
          dlg.setAttribute('open','');
        }
      }catch(e){
        try{ dlg.setAttribute('open',''); }catch(e2){}
      }
    }
  };
})();