(function () {
  function safeParse(raw, fallback) {
    if (!raw || !String(raw).trim()) {
      return fallback;
    }
    try {
      return JSON.parse(raw);
    } catch (error) {
      return fallback;
    }
  }



  function safeObjectKeys(value) {
    if (!value || typeof value !== 'object' || Array.isArray(value)) {
      return [];
    }
    try {
      return Object.keys(value);
    } catch (error) {
      return [];
    }
  }

  function countFabricObjects(value) {
    if (!value || typeof value !== 'object') {
      return 0;
    }
    var total = 0;
    var visit = function (node) {
      if (!node || typeof node !== 'object') {
        return;
      }
      if (Array.isArray(node)) {
        node.forEach(visit);
        return;
      }
      if (node.type || node.objects) {
        total += node.type ? 1 : 0;
      }
      if (Array.isArray(node.objects)) {
        node.objects.forEach(visit);
      }
    };

    if (Array.isArray(value.objects)) {
      value.objects.forEach(visit);
    } else if (value.objects && typeof value.objects === 'object') {
      Object.keys(value.objects).forEach(function (key) { visit(value.objects[key]); });
    }
    return total;
  }

  function collectFabricObjectStats(value) {
    var stats = { total: 0, elements: 0, designs: 0, images: 0, texts: 0, groups: 0, types: {} };
    var visit = function (node) {
      if (!node || typeof node !== 'object') {
        return;
      }
      if (Array.isArray(node)) {
        node.forEach(visit);
        return;
      }
      if (node.type || node.objects) {
        var type = String(node.type || 'group');
        stats.total += 1;
        stats.types[type] = (stats.types[type] || 0) + 1;
        if (type === 'image') stats.images += 1;
        if (type === 'text' || type === 'i-text' || type === 'textbox' || type === 'Text' || type === 'IText' || type === 'Textbox') stats.texts += 1;
        if (type === 'group' || Array.isArray(node.objects)) stats.groups += 1;
        if (node._asukaElementId || node._asukaElementName || node._asukaElementFamilyId || node.itemType === 'element' || node.asukaElementId) stats.elements += 1;
        if (node._asukaDesignId || node._asukaDesignName || node.itemType === 'design' || node.asukaDesignId) stats.designs += 1;
      }
      if (Array.isArray(node.objects)) {
        node.objects.forEach(visit);
      }
    };
    if (value && typeof value === 'object') {
      if (Array.isArray(value.objects)) {
        value.objects.forEach(visit);
      } else if (value.objects && typeof value.objects === 'object') {
        Object.keys(value.objects).forEach(function (key) { visit(value.objects[key]); });
      }
    }
    return stats;
  }

  function getSnapshotsByView(value) {
    if (!value || typeof value !== 'object') {
      return {};
    }
    if (value.snapshotsByView && typeof value.snapshotsByView === 'object') return value.snapshotsByView;
    if (value.viewSnapshots && typeof value.viewSnapshots === 'object') return value.viewSnapshots;
    if (value.documentSnapshotsByView && typeof value.documentSnapshotsByView === 'object') return value.documentSnapshotsByView;
    if (value.document && typeof value.document === 'object') return getSnapshotsByView(value.document);
    if (value.initialDocument && typeof value.initialDocument === 'object') return getSnapshotsByView(value.initialDocument);
    return {};
  }

  function getObjectsByView(value) {
    if (!value || typeof value !== 'object') {
      return {};
    }
    if (value.objects && typeof value.objects === 'object' && !Array.isArray(value.objects) && value.objects.byView && typeof value.objects.byView === 'object') return value.objects.byView;
    if (value.objectsByView && typeof value.objectsByView === 'object') return value.objectsByView;
    if (value.documentObjectsByView && typeof value.documentObjectsByView === 'object') return value.documentObjectsByView;
    if (value.byView && typeof value.byView === 'object' && !Array.isArray(value.byView)) return value.byView;
    if (value.document && typeof value.document === 'object') return getObjectsByView(value.document);
    if (value.initialDocument && typeof value.initialDocument === 'object') return getObjectsByView(value.initialDocument);
    if (value.documentState && typeof value.documentState === 'object') return getObjectsByView(value.documentState);
    return {};
  }

  function countStateElements(value) {
    if (!value || typeof value !== 'object') {
      return 0;
    }
    var candidates = [
      value.elements,
      value.catalog && value.catalog.elements,
      value.document && value.document.elements,
      value.initialDocument && value.initialDocument.elements,
      value.legacy && value.legacy.state && value.legacy.state.elements,
      value.legacy && value.legacy.state && value.legacy.state.catalog && value.legacy.state.catalog.elements
    ];
    for (var i = 0; i < candidates.length; i += 1) {
      if (Array.isArray(candidates[i])) {
        return candidates[i].length;
      }
    }
    return 0;
  }

  function summarizeDesign(value) {
    if (!value || typeof value !== 'object') {
      return { hasValue: !!value, type: typeof value };
    }
    var views = Array.isArray(value.views) ? value.views : (value.views && Array.isArray(value.views.views) ? value.views.views : []);
    var snapshots = getSnapshotsByView(value);
    var objectsByView = getObjectsByView(value);
    var objectStats = collectFabricObjectStats(value);
    return {
      hasValue: true,
      type: 'object',
      topKeys: safeObjectKeys(value),
      viewsCount: views.length,
      firstViewIds: views.slice(0, 4).map(function (item) { return item && typeof item === 'object' ? String(item.id || item.name || '') : ''; }).filter(Boolean),
      activeViewId: value.activeViewId != null ? String(value.activeViewId) : (value.views && value.views.activeViewId != null ? String(value.views.activeViewId) : ''),
      snapshotsCount: safeObjectKeys(snapshots).length,
      snapshotViewIds: safeObjectKeys(snapshots).slice(0, 6),
      objectsByViewCount: safeObjectKeys(objectsByView).length,
      objectsByViewIds: safeObjectKeys(objectsByView).slice(0, 6),
      objectStats: objectStats,
      catalogElementsCount: countStateElements(value),
      drawingAreasCount: Array.isArray(value.drawingAreas) ? value.drawingAreas.length : (value.drawingAreas && Array.isArray(value.drawingAreas.zones) ? value.drawingAreas.zones.length : 0),
      drawingAreasByViewCount: value.drawingAreasByView && typeof value.drawingAreasByView === 'object' ? safeObjectKeys(value.drawingAreasByView).length : 0
    };
  }

  function isApdAdminSaveDebugEnabled() {
    try {
      var search = window.location && typeof window.location.search === 'string' ? window.location.search : '';
      if (search.indexOf('apd_debug_admin_save=0') !== -1 || search.indexOf('apd_admin_save_debug=0') !== -1) {
        return false;
      }
      if (search.indexOf('apd_debug_admin_save=1') !== -1 || search.indexOf('apd_admin_save_debug=1') !== -1) {
        return true;
      }
      if (window.localStorage) {
        var stored = String(window.localStorage.getItem('apd_debug_admin_save') || window.localStorage.getItem('apd_admin_save_debug') || '');
        if (stored === '0' || stored.toLowerCase() === 'false') return false;
        if (stored === '1' || stored.toLowerCase() === 'true') return true;
      }
      return false;
    } catch (error) {}
    return false;
  }

  function getApdAdminSaveDebugStore() {
    if (!window.__APD_ADMIN_SAVE_DEBUG_LOGS || !Array.isArray(window.__APD_ADMIN_SAVE_DEBUG_LOGS)) {
      window.__APD_ADMIN_SAVE_DEBUG_LOGS = [];
    }
    return window.__APD_ADMIN_SAVE_DEBUG_LOGS;
  }

  function shouldConsoleLogAdminSaveTag(tag) {
    tag = String(tag || '');
    return tag.indexOf('syncBuilderPage') !== -1
      || tag.indexOf('builder export') !== -1
      || tag.indexOf('productCanvas') !== -1
      || tag.indexOf('studio sync') !== -1
      || tag.indexOf('save-click') !== -1
      || tag.indexOf('form-submit') !== -1
      || tag.indexOf('error') !== -1
      || tag.indexOf('failed') !== -1;
  }

  function appendDebugLog(tag, data, root) {
    if (!isApdAdminSaveDebugEnabled()) {
      return;
    }
    var entry = {
      seq: Number(window.__APD_ADMIN_SAVE_DEBUG_SEQ || 0) + 1,
      at: new Date().toISOString(),
      tag: String(tag || ''),
      data: data || {},
      postId: (root && root.querySelector && root.querySelector('#post_ID') && root.querySelector('#post_ID').value) || (document.getElementById('post_ID') && document.getElementById('post_ID').value) || '',
      postType: (document.body && document.body.className.match(/post-type-([^\s]+)/) ? document.body.className.match(/post-type-([^\s]+)/)[1] : '')
    };
    window.__APD_ADMIN_SAVE_DEBUG_SEQ = entry.seq;
    var store = getApdAdminSaveDebugStore();
    store.push(entry);
    if (store.length > 300) {
      store.splice(0, store.length - 300);
    }
    if (!window.APDAdminSaveDebugDump) {
      window.APDAdminSaveDebugDump = function () {
        return getApdAdminSaveDebugStore();
      };
    }
    if (!window.APDAdminSaveDebugClear) {
      window.APDAdminSaveDebugClear = function () {
        window.__APD_ADMIN_SAVE_DEBUG_LOGS = [];
        window.__APD_ADMIN_SAVE_DEBUG_SEQ = 0;
      };
    }
  }

  function summarizeJsonField(root, name) {
    var field = findField(name, root || document);
    var value = field ? String(field.value || '') : '';
    return {
      exists: !!field,
      length: value.length,
      trimmedLength: value.trim().length,
      summary: value.trim() ? summarizeDesign(safeParse(value, null)) : { hasValue: false }
    };
  }

  function summarizeBuilderFields(root) {
    return {
      initialDocument: summarizeJsonField(root, '_apd_initial_document'),
      initialState: summarizeJsonField(root, '_apd_initial_state'),
      views: summarizeJsonField(root, '_apd_views'),
      drawingZones: summarizeJsonField(root, '_apd_drawing_zones'),
      layout: summarizeJsonField(root, '_apd_layout'),
      templateId: (findField('_apd_template_id', root || document) && findField('_apd_template_id', root || document).value) || ''
    };
  }

  function summarizeProductCanvasFields(container) {
    container = container || document;
    var root = container.parentElement || container;
    var documentField = root.querySelector ? root.querySelector('[name="_apd_product_canvas_document"]') : null;
    var stateField = root.querySelector ? root.querySelector('[name="_apd_product_canvas_state"]') : null;
    return {
      documentField: { exists: !!documentField, length: documentField ? String(documentField.value || '').length : 0, summary: documentField && documentField.value ? summarizeDesign(safeParse(documentField.value, null)) : { hasValue: false } },
      stateField: { exists: !!stateField, length: stateField ? String(stateField.value || '').length : 0, summary: stateField && stateField.value ? summarizeDesign(safeParse(stateField.value, null)) : { hasValue: false } }
    };
  }


  function adminLabel(key, fallback, replacements) {
    var labels = window.APDAdminConfig && window.APDAdminConfig.labels ? window.APDAdminConfig.labels : {};
    var value = Object.prototype.hasOwnProperty.call(labels, key) ? labels[key] : fallback;
    value = String(value == null ? '' : value);
    if (replacements && typeof replacements === 'object') {
      Object.keys(replacements).forEach(function (token) {
        value = value.replace(new RegExp('\\{' + token + '\\}', 'g'), String(replacements[token]));
      });
    }
    return value;
  }

  function getCreateFn() {
    if (window.createAsukaDesigner && typeof window.createAsukaDesigner === 'function') {
      return window.createAsukaDesigner;
    }
    if (window.AsukaDesigner && typeof window.AsukaDesigner.createAsukaDesigner === 'function') {
      return window.AsukaDesigner.createAsukaDesigner;
    }
    return null;
  }




  function normalizeApdMenuIconName(icon, fallbackKind) {
    var value = typeof icon === 'string' ? icon.trim() : '';
    if (!value && fallbackKind) {
      value = String(fallbackKind || '').trim();
    }
    if (!value) {
      return '';
    }
    if (value.indexOf('url:') === 0 || value.indexOf('svg:') === 0 || /^https?:\/\//i.test(value) || /^\/\//.test(value) || value.charAt(0) === '/') {
      return value;
    }
    var normalized = value.toLowerCase().replace(/\s+/g, '-').replace(/_/g, '-');
    if (['crop', 'drawingareas', 'drawing-area', 'drawing-areas'].indexOf(normalized) !== -1) {
      return 'drawing-areas';
    }
    return value;
  }

  function sanitizeObject(value, fallback) {
    return value && typeof value === 'object' && !Array.isArray(value) ? value : fallback;
  }

  function sanitizeArray(value, fallback) {
    return Array.isArray(value) ? value : fallback;
  }


  function apdClonePlainObject(value) {
    try {
      return JSON.parse(JSON.stringify(value));
    } catch (error) {
      if (Array.isArray(value)) {
        return value.map(function (item) { return apdClonePlainObject(item); });
      }
      if (value && typeof value === 'object') {
        var copy = {};
        Object.keys(value).forEach(function (key) {
          if (typeof value[key] !== 'function' && typeof value[key] !== 'undefined') {
            copy[key] = apdClonePlainObject(value[key]);
          }
        });
        return copy;
      }
      return value;
    }
  }

  function apdIsCanonicalStateEnvelope(value) {
    return !!(
      value
      && typeof value === 'object'
      && !Array.isArray(value)
      && value.format === 'asuka-state'
      && Number(value.schemaVersion) === 2
    );
  }

  function apdCanonicalDocumentBranch(value) {
    if (!apdIsCanonicalStateEnvelope(value)) {
      return null;
    }
    var documentValue = value.document && typeof value.document === 'object' && !Array.isArray(value.document)
      ? value.document
      : null;
    if (apdIsCanonicalStateEnvelope(documentValue)) {
      return apdCanonicalDocumentBranch(documentValue);
    }
    return documentValue;
  }

  function apdResolveCanonicalInitialState(stateValue, documentValue) {
    var state = apdIsCanonicalStateEnvelope(stateValue) ? apdClonePlainObject(stateValue) : null;
    var documentState = apdIsCanonicalStateEnvelope(documentValue) ? apdClonePlainObject(documentValue) : null;
    var documentBranch = apdCanonicalDocumentBranch(documentState);

    if (state && apdIsCanonicalStateEnvelope(state.document)) {
      state.document = apdCanonicalDocumentBranch(state.document) || {};
    }
    if (state && documentBranch) {
      state.document = apdClonePlainObject(documentBranch);
      if (documentState.catalog && typeof documentState.catalog === 'object') {
        state.catalog = Object.assign({}, sanitizeObject(state.catalog, {}), apdClonePlainObject(documentState.catalog));
      }
      state.scope = sanitizeObject(state.scope, {});
      state.scope.document = Object.assign({}, sanitizeObject(documentState.scope && documentState.scope.document, {}), sanitizeObject(state.scope.document, {}));
      if (documentState.scope && documentState.scope.catalog) {
        state.scope.catalog = Object.assign({}, sanitizeObject(state.scope.catalog, {}), sanitizeObject(documentState.scope.catalog, {}));
      }
    }

    if (state && payloadHasViews(state)) {
      return state;
    }
    if (documentState && payloadHasViews(documentState)) {
      return documentState;
    }
    return null;
  }

  function apdGetPayloadViews(value) {
    if (!value || typeof value !== 'object') {
      return [];
    }
    if (Array.isArray(value.views)) {
      return value.views;
    }
    if (value.views && typeof value.views === 'object' && Array.isArray(value.views.views)) {
      return value.views.views;
    }
    var nestedKeys = ['document', 'initialDocument', 'documentState'];
    for (var i = 0; i < nestedKeys.length; i += 1) {
      var nested = apdGetPayloadViews(value[nestedKeys[i]]);
      if (nested.length) {
        return nested;
      }
    }
    return [];
  }

  function apdGetPayloadActiveViewId(value, fallback) {
    if (!value || typeof value !== 'object') {
      return fallback || '';
    }
    if (value.activeViewId != null && String(value.activeViewId).trim()) {
      return String(value.activeViewId).trim();
    }
    if (value.views && typeof value.views === 'object' && value.views.activeViewId != null && String(value.views.activeViewId).trim()) {
      return String(value.views.activeViewId).trim();
    }
    var views = apdGetPayloadViews(value);
    if (views.length && views[0] && typeof views[0] === 'object') {
      return String(views[0].id || views[0].name || fallback || '').trim();
    }
    return fallback || '';
  }


  function apdExtractObjectsByView(value) {
    var objects = getObjectsByView(value || {});
    return objects && typeof objects === 'object' && !Array.isArray(objects) ? apdClonePlainObject(objects) : {};
  }

  function apdObjectListHasItems(list) {
    return Array.isArray(list) && list.length > 0;
  }

  function apdSetObjectsByViewOnDocumentPayload(payload, byView) {
    if (!payload || typeof payload !== 'object' || !byView || typeof byView !== 'object') {
      return payload;
    }
    var cloned = apdClonePlainObject(byView);
    payload.objects = payload.objects && typeof payload.objects === 'object' && !Array.isArray(payload.objects) ? payload.objects : {};
    payload.objects.byView = cloned;

    // Keep the legacy buckets in sync when they already existed. A few older
    // WordPress-side adapters still inspect those names while the official
    // JavaScript document contract reads objects.byView.
    if (payload.objectsByView && typeof payload.objectsByView === 'object' && !Array.isArray(payload.objectsByView)) {
      payload.objectsByView = apdClonePlainObject(cloned);
    }
    if (payload.documentObjectsByView && typeof payload.documentObjectsByView === 'object' && !Array.isArray(payload.documentObjectsByView)) {
      payload.documentObjectsByView = apdClonePlainObject(cloned);
    }
    return payload;
  }

  function apdReadRuntimeActiveViewId(instance, fallback) {
    var candidates = [];
    try {
      var state = instance && instance.util && typeof instance.util.getState === 'function' ? instance.util.getState() : null;
      if (state && state.activeViewId != null) {
        candidates.push(state.activeViewId);
      }
    } catch (error) {}
    candidates.push(fallback || '');
    for (var i = 0; i < candidates.length; i += 1) {
      var value = String(candidates[i] == null ? '' : candidates[i]).trim();
      if (value) return value;
    }
    return '';
  }

  function apdNormalizeDrawingAreaZone(zone, fallbackViewId) {
    if (!zone || typeof zone !== 'object') {
      return null;
    }
    var item = apdClonePlainObject(zone);
    var viewId = String(item.viewId || item.view || item.view_id || fallbackViewId || '').trim();
    if (viewId) {
      item.viewId = viewId;
    }
    if (!String(item.id || '').trim()) {
      item.id = (viewId ? viewId + '-' : 'drawing-area-') + String(Math.random()).slice(2, 10);
    }
    var left = Number(item.left != null ? item.left : item.x);
    var top = Number(item.top != null ? item.top : item.y);
    var width = Number(item.width);
    var height = Number(item.height);
    if (Number.isFinite(left)) {
      item.left = left;
      if (typeof item.x === 'undefined') item.x = left;
    }
    if (Number.isFinite(top)) {
      item.top = top;
      if (typeof item.y === 'undefined') item.y = top;
    }
    if (Number.isFinite(width) && width > 0) {
      item.width = width;
    }
    if (Number.isFinite(height) && height > 0) {
      item.height = height;
    }
    return item;
  }

  function apdPutDrawingZoneByView(byView, zone, fallbackViewId) {
    var item = apdNormalizeDrawingAreaZone(zone, fallbackViewId);
    if (!item) {
      return;
    }
    var viewId = String(item.viewId || fallbackViewId || '').trim();
    if (!viewId) {
      viewId = '__active__';
    }
    item.viewId = viewId;
    if (!Array.isArray(byView[viewId])) {
      byView[viewId] = [];
    }
    byView[viewId].push(item);
  }

  function apdMergeDrawingAreasByView(target, source) {
    if (!source || typeof source !== 'object') {
      return target;
    }
    target = target && typeof target === 'object' ? target : {};
    Object.keys(source).forEach(function (viewId) {
      var zones = Array.isArray(source[viewId]) ? source[viewId] : [];
      zones.forEach(function (zone) {
        apdPutDrawingZoneByView(target, zone, String(viewId));
      });
      if (!zones.length && !Array.isArray(target[viewId])) {
        target[viewId] = [];
      }
    });
    return target;
  }

  function apdExtractDrawingAreasByView(value, fallbackViewId) {
    var byView = {};
    if (!value || typeof value !== 'object') {
      return byView;
    }
    if (value.drawingAreasByView && typeof value.drawingAreasByView === 'object' && !Array.isArray(value.drawingAreasByView)) {
      apdMergeDrawingAreasByView(byView, value.drawingAreasByView);
    }
    if (value.drawingAreas && typeof value.drawingAreas === 'object') {
      if (value.drawingAreas.byView && typeof value.drawingAreas.byView === 'object') {
        apdMergeDrawingAreasByView(byView, value.drawingAreas.byView);
      }
      if (Array.isArray(value.drawingAreas.zones)) {
        var activeFromDrawingAreas = apdGetPayloadActiveViewId(value, fallbackViewId);
        value.drawingAreas.zones.forEach(function (zone) {
          apdPutDrawingZoneByView(byView, zone, zone && (zone.viewId || zone.view) ? '' : activeFromDrawingAreas);
        });
      }
    }
    if (Array.isArray(value.drawingAreas)) {
      var activeForArray = apdGetPayloadActiveViewId(value, fallbackViewId);
      value.drawingAreas.forEach(function (zone) {
        apdPutDrawingZoneByView(byView, zone, zone && (zone.viewId || zone.view) ? '' : activeForArray);
      });
    }
    if (Array.isArray(value.drawingZones)) {
      var activeForZones = apdGetPayloadActiveViewId(value, fallbackViewId);
      value.drawingZones.forEach(function (zone) {
        apdPutDrawingZoneByView(byView, zone, zone && (zone.viewId || zone.view) ? '' : activeForZones);
      });
    }
    ['document', 'initialDocument', 'documentState'].forEach(function (key) {
      if (value[key] && typeof value[key] === 'object') {
        apdMergeDrawingAreasByView(byView, apdExtractDrawingAreasByView(value[key], fallbackViewId));
      }
    });
    return byView;
  }

  function apdFlattenDrawingAreasByView(byView) {
    var zones = [];
    if (!byView || typeof byView !== 'object') {
      return zones;
    }
    Object.keys(byView).forEach(function (viewId) {
      (Array.isArray(byView[viewId]) ? byView[viewId] : []).forEach(function (zone) {
        var item = apdNormalizeDrawingAreaZone(zone, viewId === '__active__' ? '' : viewId);
        if (item) {
          zones.push(item);
        }
      });
    });
    return zones;
  }

  function apdApplyDrawingAreasToDocumentPayload(payload, snapshot) {
    if (!payload || typeof payload !== 'object') {
      return payload;
    }
    if (apdIsCanonicalStateEnvelope(payload)) {
      var canonicalDocument = apdCanonicalDocumentBranch(payload);
      if (canonicalDocument) {
        payload.document = apdApplyDrawingAreasToDocumentPayload(canonicalDocument, snapshot);
        delete payload.document.drawingAreasByView;
        delete payload.document.drawingZones;
      }
      payload.scope = sanitizeObject(payload.scope, {});
      payload.scope.document = sanitizeObject(payload.scope.document, {});
      payload.scope.document.drawingAreas = true;
      return payload;
    }
    var activeViewId = apdGetPayloadActiveViewId(payload, snapshot && snapshot.activeViewId ? String(snapshot.activeViewId) : '');
    var byView = apdExtractDrawingAreasByView(snapshot || null, activeViewId);
    apdMergeDrawingAreasByView(byView, apdExtractDrawingAreasByView(payload, activeViewId));
    var keys = safeObjectKeys(byView);
    if (!keys.length) {
      return payload;
    }
    var flat = apdFlattenDrawingAreasByView(byView);
    payload.drawingAreasByView = byView;
    payload.drawingZones = flat;
    payload.drawingAreas = payload.drawingAreas && typeof payload.drawingAreas === 'object' && !Array.isArray(payload.drawingAreas)
      ? payload.drawingAreas
      : {};
    payload.drawingAreas.zones = Array.isArray(byView[activeViewId]) ? byView[activeViewId] : (flat.length && keys.length === 1 ? flat : []);
    payload.drawingAreas.activeDrawingAreaId = (snapshot && snapshot.selection && snapshot.selection.activeDrawingAreaId) || payload.drawingAreas.activeDrawingAreaId || null;
    return payload;
  }

  function apdApplyDrawingAreasToStatePayload(payload, snapshot) {
    if (!payload || typeof payload !== 'object') {
      return payload;
    }
    if (apdIsCanonicalStateEnvelope(payload)) {
      return apdApplyDrawingAreasToDocumentPayload(payload, snapshot);
    }
    var activeViewId = apdGetPayloadActiveViewId(payload, snapshot && snapshot.activeViewId ? String(snapshot.activeViewId) : '');
    var byView = apdExtractDrawingAreasByView(snapshot || null, activeViewId);
    apdMergeDrawingAreasByView(byView, apdExtractDrawingAreasByView(payload, activeViewId));
    if (!safeObjectKeys(byView).length) {
      return payload;
    }
    payload.drawingAreasByView = byView;
    payload.drawingZones = apdFlattenDrawingAreasByView(byView);
    if (payload.document && typeof payload.document === 'object') {
      apdApplyDrawingAreasToDocumentPayload(payload.document, snapshot);
    } else if (payload.initialDocument && typeof payload.initialDocument === 'object') {
      apdApplyDrawingAreasToDocumentPayload(payload.initialDocument, snapshot);
    } else if (payload.documentState && typeof payload.documentState === 'object') {
      apdApplyDrawingAreasToDocumentPayload(payload.documentState, snapshot);
    } else if (payloadHasViews(payload)) {
      apdApplyDrawingAreasToDocumentPayload(payload, snapshot);
    }
    if (payload.scope && typeof payload.scope === 'object') {
      payload.scope.document = payload.scope.document && typeof payload.scope.document === 'object' ? payload.scope.document : {};
      payload.scope.document.drawingAreas = true;
    }
    return payload;
  }

  function apdCreateSnapshotFromDrawingZones(zones, views, fallbackActiveViewId) {
    var snapshot = {
      activeViewId: String(fallbackActiveViewId || '').trim(),
      objectsByView: {},
      drawingAreasByView: {},
      selection: { activeObjectIds: [], activeDrawingAreaId: null },
      viewportByView: {}
    };
    var normalizedViews = Array.isArray(views) ? views : [];
    if (!snapshot.activeViewId && normalizedViews.length) {
      snapshot.activeViewId = String((normalizedViews[0] && (normalizedViews[0].id || normalizedViews[0].name)) || '').trim();
    }
    normalizedViews.forEach(function (view) {
      var viewId = String((view && (view.id || view.name)) || '').trim();
      if (viewId) {
        snapshot.objectsByView[viewId] = [];
        snapshot.drawingAreasByView[viewId] = [];
      }
    });
    (Array.isArray(zones) ? zones : []).forEach(function (zone) {
      apdPutDrawingZoneByView(snapshot.drawingAreasByView, zone, zone && (zone.viewId || zone.view) ? '' : snapshot.activeViewId);
    });
    safeObjectKeys(snapshot.drawingAreasByView).forEach(function (viewId) {
      if (!Object.prototype.hasOwnProperty.call(snapshot.objectsByView, viewId)) {
        snapshot.objectsByView[viewId] = [];
      }
    });
    return snapshot;
  }

  function apdMergeTopLevelDrawingZonesIntoBootPayload(config) {
    if (!config || typeof config !== 'object' || !Array.isArray(config.drawingZones) || !config.drawingZones.length) {
      return config;
    }
    var views = Array.isArray(config.views) ? config.views : [];
    var snapshot = apdCreateSnapshotFromDrawingZones(config.drawingZones, views, '');
    if (config.initialState && typeof config.initialState === 'object') {
      apdApplyDrawingAreasToStatePayload(config.initialState, snapshot);
    }
    return config;
  }

  function apdCollectDrawingZonesForSave(design, state) {
    var activeViewId = apdGetPayloadActiveViewId(design || state || {}, '');
    var byView = {};
    apdMergeDrawingAreasByView(byView, apdExtractDrawingAreasByView(design || null, activeViewId));
    apdMergeDrawingAreasByView(byView, apdExtractDrawingAreasByView(state || null, activeViewId));
    var designDocument = apdCanonicalDocumentBranch(design);
    var stateDocument = apdCanonicalDocumentBranch(state);
    var known = safeObjectKeys(byView).length > 0
      || !!(designDocument && Object.prototype.hasOwnProperty.call(designDocument, 'drawingAreas'))
      || !!(stateDocument && Object.prototype.hasOwnProperty.call(stateDocument, 'drawingAreas'));
    return {
      known: known,
      zones: known ? apdFlattenDrawingAreasByView(byView) : []
    };
  }

  function apdUploadDebug(tag, payload) {
    return;
  }


  function apdResolveUploadResponseUrl(payload, uploadConfig) {
    try {
      if (uploadConfig && typeof uploadConfig.mapResponseToUrl === 'function') {
        var mapped = uploadConfig.mapResponseToUrl(payload);
        if (typeof mapped === 'string' && mapped) return mapped;
      }
    } catch (error) {}
    if (typeof payload === 'string' && payload) return payload;
    if (payload && typeof payload === 'object') {
      var directKeys = ['url', 'src', 'value', 'fileUrl', 'downloadUrl'];
      for (var i = 0; i < directKeys.length; i += 1) {
        var value = payload[directKeys[i]];
        if (typeof value === 'string' && value) return value;
      }
      var nested = [payload.data, payload.result];
      for (var j = 0; j < nested.length; j += 1) {
        var item = nested[j];
        if (item && typeof item === 'object') {
          for (var k = 0; k < directKeys.length; k += 1) {
            var nestedValue = item[directKeys[k]];
            if (typeof nestedValue === 'string' && nestedValue) return nestedValue;
          }
        }
      }
    }
    return null;
  }

  function apdParseUploadResponse(response) {
    var contentType = response && response.headers && typeof response.headers.get === 'function'
      ? String(response.headers.get('content-type') || '')
      : '';
    if (contentType.indexOf('application/json') !== -1) {
      return response.json();
    }
    return response.text().then(function (text) {
      try { return JSON.parse(text); } catch (error) { return text; }
    });
  }

  function apdUploadErrorMessage(payload, status) {
    function visit(value, depth) {
      if (depth > 5 || value == null) return '';
      if (typeof value === 'string') {
        var message = value.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
        return message && message.length <= 500 ? message : '';
      }
      if (typeof value !== 'object') return '';
      var keys = ['message', 'detail', 'error_description', 'error', 'data', 'result'];
      for (var index = 0; index < keys.length; index += 1) {
        var nested = visit(value[keys[index]], depth + 1);
        if (nested) return nested;
      }
      return '';
    }
    return visit(payload, 0) || ('Upload failed with status ' + status + '.');
  }

  function apdCloneUploadHeaders(headers) {
    var next = Object.assign({}, headers || {});
    Object.keys(next).forEach(function (key) {
      if (String(key).toLowerCase() === 'content-type') {
        delete next[key];
      }
    });
    return next;
  }

  function apdCreateWordPressUploadHandler(uploadConfig) {
    return function (file, context) {
      if (!file) {
        return Promise.reject(new Error('No file provided for upload.'));
      }
      var endpoint = String(uploadConfig.endpoint || '').trim();
      if (!endpoint) {
        return Promise.reject(new Error('Upload endpoint is not configured.'));
      }
      var body = new FormData();
      body.append(uploadConfig.fieldName || 'file', file, file.name || 'upload');
      if (uploadConfig.source) {
        body.append('apd_source', String(uploadConfig.source));
      }
      if (context && typeof context === 'object') {
        try { body.append('apd_context', JSON.stringify(context)); } catch (error) {}
        if (context.source) body.append('asuka_source', String(context.source));
        if (context.mode) body.append('asuka_mode', String(context.mode));
      }
      return fetch(endpoint, {
        method: uploadConfig.method || 'POST',
        body: body,
        headers: apdCloneUploadHeaders(uploadConfig.headers || {}),
        credentials: uploadConfig.credentials || 'same-origin'
      }).then(function (response) {
        return apdParseUploadResponse(response).then(function (payload) {
          if (!response.ok) {
            throw new Error(apdUploadErrorMessage(payload, response.status));
          }
          return payload;
        });
      }).then(function (payload) {
        var url = apdResolveUploadResponseUrl(payload, uploadConfig);
        if (!url) {
          throw new Error('The upload response did not contain a usable URL.');
        }
        return url;
      });
    };
  }

  function apdEnsureRuntimeUploadConfig(config, defaults) {
    if (!config || typeof config !== 'object') {
      return config;
    }
    defaults = sanitizeObject(defaults, {});
    var topUpload = sanitizeObject(config.upload, {});
    var moduleConfig = sanitizeObject(config.moduleConfig, {});
    var nestedUpload = sanitizeObject(moduleConfig.upload, {});
    var uploadConfig = Object.assign({}, defaults, nestedUpload, topUpload);
    uploadConfig.headers = Object.assign({}, sanitizeObject(defaults.headers, {}), sanitizeObject(nestedUpload.headers, {}), sanitizeObject(topUpload.headers, {}));
    if (!String(uploadConfig.endpoint || '').trim() && defaults.endpoint) {
      uploadConfig.endpoint = defaults.endpoint;
    }
    if (defaults.source && !uploadConfig.source) {
      uploadConfig.source = defaults.source;
    }
    if (defaults.credentials && !uploadConfig.credentials) {
      uploadConfig.credentials = defaults.credentials;
    }
    if (!Object.keys(uploadConfig).length) {
      return config;
    }
    if (!uploadConfig.fieldName) {
      uploadConfig.fieldName = 'file';
    }
    if ((defaults.endpoint && uploadConfig.strategy !== 'remote') || uploadConfig.endpoint || uploadConfig.strategy === 'remote') {
      uploadConfig.strategy = 'remote';
      uploadConfig.method = uploadConfig.method || 'POST';
    }
    if (uploadConfig.endpoint && typeof uploadConfig.handler !== 'function') {
      uploadConfig.handler = apdCreateWordPressUploadHandler(uploadConfig);
    }
    if ((uploadConfig.source === 'admin' || defaults.source === 'admin') && typeof uploadConfig.selectHandler !== 'function') {
      uploadConfig.selectHandler = apdCreateAdminUploadSelectHandler();
    }
    delete uploadConfig.picker;
    moduleConfig.upload = uploadConfig;
    config.moduleConfig = moduleConfig;
    config.upload = uploadConfig;
    return config;
  }


  function apdGetRuntimeStore(instance) {
    if (!instance || typeof instance !== 'object') {
      return null;
    }
    if (instance.store && typeof instance.store.getState === 'function') {
      return instance.store;
    }
    if (instance.__store && typeof instance.__store.getState === 'function') {
      return instance.__store;
    }
    if (instance.runtime && instance.runtime.store && typeof instance.runtime.store.getState === 'function') {
      return instance.runtime.store;
    }
    return null;
  }

  function apdMergeUploadRuntimeFunctions(target, source) {
    target = sanitizeObject(target, {});
    source = sanitizeObject(source, {});
    var next = Object.assign({}, target, source);
    if (typeof source.handler === 'function') {
      next.handler = source.handler;
    }
    if (typeof source.selectHandler === 'function') {
      next.selectHandler = source.selectHandler;
    }
    if (typeof source.mapResponseToUrl === 'function') {
      next.mapResponseToUrl = source.mapResponseToUrl;
    }
    delete next.picker;
    return next;
  }

  function apdApplyRuntimeUploadConfigToInstance(instance, uploadConfig) {
    uploadConfig = sanitizeObject(uploadConfig, {});
    if (!Object.keys(uploadConfig).length) {
      return false;
    }
    var store = apdGetRuntimeStore(instance);
    if (!store || typeof store.getState !== 'function') {
      return false;
    }
    try {
      var state = store.getState() || {};
      var currentModuleConfig = sanitizeObject(state.moduleConfig, {});
      var nextUpload = apdMergeUploadRuntimeFunctions(sanitizeObject(currentModuleConfig.upload, {}), uploadConfig);
      var nextModuleConfig = Object.assign({}, currentModuleConfig, { upload: nextUpload });
      if (state && typeof state.setModuleConfig === 'function') {
        state.setModuleConfig({ upload: nextUpload });
      } else if (typeof store.setState === 'function') {
        store.setState({ moduleConfig: nextModuleConfig });
      } else {
        return false;
      }
      return true;
    } catch (error) {
      return false;
    }
  }

  function apdKeepRuntimeUploadConfig(instance, uploadConfig) {
    uploadConfig = sanitizeObject(uploadConfig, {});
    if (!Object.keys(uploadConfig).length) {
      return;
    }
    [0, 16, 80, 220, 500, 1000, 1800].forEach(function (delay) {
      window.setTimeout(function () {
        apdApplyRuntimeUploadConfigToInstance(instance, uploadConfig);
      }, delay);
    });
  }

  function apdBuildAdminUploadConfig(overrides) {
    var restUrl = (window.APDAdminConfig && window.APDAdminConfig.restUploadUrl) || '';
    var nonce = (window.APDAdminConfig && window.APDAdminConfig.restNonce) || '';
    return Object.assign({
      strategy: 'remote',
      endpoint: restUrl,
      method: 'POST',
      fieldName: 'file',
      source: 'admin',
      credentials: 'same-origin',
      headers: { 'X-WP-Nonce': nonce }
    }, sanitizeObject(overrides, {}));
  }

  function apdUploadAdminFileToWordPress(file, context) {
    var uploadConfig = apdBuildAdminUploadConfig();
    var handler = apdCreateWordPressUploadHandler(uploadConfig);
    return handler(file, Object.assign({ source: 'admin' }, sanitizeObject(context, {})));
  }


  function apdNormalizeWordPressMediaItem(data) {
    data = data && typeof data === 'object' ? data : {};
    var url = apdResolveMediaAttachmentUrl(data);
    if (!url) return null;
    var id = data.id || data.attachmentId || undefined;
    return {
      url: url,
      name: data.filename || data.name || data.title || (url.split('/').pop() || 'wordpress-media'),
      filename: data.filename || data.name || '',
      id: id,
      source: 'wordpress-media',
      attachmentId: id,
      metadata: data
    };
  }

  function apdDedupeUploadSelectionItems(items) {
    var seen = {};
    return (Array.isArray(items) ? items : []).filter(function (item) {
      if (!item) return false;
      var id = item.attachmentId || item.id || (item.metadata && item.metadata.id) || '';
      var url = item.url || item.src || item.value || item.fileUrl || item.downloadUrl || '';
      var key = id ? ('id:' + String(id)) : (url ? ('url:' + String(url)) : '');
      if (!key) return true;
      if (seen[key]) return false;
      seen[key] = true;
      return true;
    });
  }

  function apdCollectWordPressMediaSelection(selection) {
    var attachments = [];
    if (selection && typeof selection.each === 'function') {
      selection.each(function (attachment) { attachments.push(attachment); });
    } else if (selection && typeof selection.map === 'function') {
      attachments = selection.map(function (attachment) { return attachment; });
    } else {
      var first = selection && typeof selection.first === 'function' ? selection.first() : null;
      if (first) attachments.push(first);
    }
    return apdDedupeUploadSelectionItems(attachments.map(function (attachment) {
      var data = attachment && typeof attachment.toJSON === 'function' ? attachment.toJSON() : (attachment || null);
      return apdNormalizeWordPressMediaItem(data);
    }).filter(Boolean));
  }

  function apdOpenAdminMediaPicker(options) {
    options = sanitizeObject(options, {});
    return new Promise(function (resolve, reject) {
      if (!window.wp || !window.wp.media) {
        reject(new Error('WordPress media uploader is not available.'));
        return;
      }
      var settled = false;
      var selected = false;
      var done = function (items) {
        if (settled) return;
        settled = true;
        items = apdDedupeUploadSelectionItems(Array.isArray(items) ? items : []);
        apdUploadDebug('wp.media normalized items', items);
        resolve(items);
      };
      var fail = function (error) {
        if (settled) return;
        settled = true;
        reject(error);
      };
      var labels = ((window.APDAdminConfig || {}).labels || {});
      var accept = String(options.accept || '').toLowerCase();
      var imageOnly = !accept || accept.indexOf('image') !== -1 || accept.indexOf('.png') !== -1 || accept.indexOf('.jpg') !== -1 || accept.indexOf('.jpeg') !== -1 || accept.indexOf('.gif') !== -1 || accept.indexOf('.webp') !== -1 || accept.indexOf('.svg') !== -1 || accept.indexOf('.avif') !== -1;
      var frame = window.wp.media({
        title: imageOnly ? (labels.mediaFrameTitleImage || 'Choose an image') : (labels.mediaFrameTitleFile || 'Choose a file'),
        button: { text: labels.mediaFrameButton || 'Use this file' },
        multiple: !!options.multiple,
        library: imageOnly ? { type: 'image' } : {}
      });
      frame.on('select', function () {
        selected = true;
        try {
          var selection = frame.state().get('selection');
          apdUploadDebug('wp.media select raw selection', selection);
          done(apdCollectWordPressMediaSelection(selection));
        } catch (error) {
          fail(error);
        }
      });
      frame.on('close', function () {
        window.setTimeout(function () {
          apdUploadDebug('wp.media close', { settled: settled, selected: selected });
          if (!settled && !selected) {
            done([]);
          }
        }, 50);
      });
      frame.open();
    });
  }

  window.openAdminMediaPicker = apdOpenAdminMediaPicker;

  function apdCreateAdminUploadSelectHandler() {
    var pendingPicker = null;
    return function (context) {
      context = sanitizeObject(context, {});
      apdUploadDebug('selectHandler called', context);
      if (pendingPicker) {
        return pendingPicker;
      }
      var picker = typeof window.openAdminMediaPicker === 'function' ? window.openAdminMediaPicker : apdOpenAdminMediaPicker;
      pendingPicker = Promise.resolve(picker({
        multiple: !!context.multiple,
        accept: context.accept || 'image/*',
        source: context.source || context.uploadContext || context.kind || 'admin',
        uploadContext: context.uploadContext || context.kind || context.source || 'admin'
      })).then(function (items) {
        apdUploadDebug('wp.media returned', items);
        if (!Array.isArray(items)) items = items ? [items] : [];
        var normalized = apdDedupeUploadSelectionItems(items.map(function (item) {
          if (typeof item === 'string') {
            return { url: item, name: item.split('/').pop() || 'wordpress-media', source: 'wordpress-media' };
          }
          item = item && typeof item === 'object' ? item : {};
          var metadata = sanitizeObject(item.metadata, sanitizeObject(item.data, item));
          var url = item.url || item.src || item.value || item.fileUrl || item.downloadUrl || apdResolveMediaAttachmentUrl(metadata);
          if (!url) return null;
          var id = item.attachmentId || item.id || metadata.attachmentId || metadata.id || undefined;
          return {
            url: url,
            name: item.name || item.filename || metadata.filename || metadata.name || metadata.title || (String(url).split('/').pop() || 'wordpress-media'),
            filename: item.filename || metadata.filename || metadata.name || '',
            id: id,
            source: item.source || 'wordpress-media',
            attachmentId: id,
            metadata: metadata
          };
        }).filter(Boolean));
        apdUploadDebug('selectHandler returns', normalized);
        return normalized;
      }).finally(function () {
        pendingPicker = null;
      });
      return pendingPicker;
    };
  }

  function apdResolveMediaAttachmentUrl(data) {
    if (!data || typeof data !== 'object') return '';
    var candidates = [
      data.url,
      data.source_url,
      data.fileUrl,
      data.downloadUrl,
      data.src,
      data.value,
      data.sizes && data.sizes.full && data.sizes.full.url,
      data.sizes && data.sizes.large && data.sizes.large.url,
      data.sizes && data.sizes.medium && data.sizes.medium.url,
      data.media_details && data.media_details.sizes && data.media_details.sizes.full && data.media_details.sizes.full.source_url,
      data.media_details && data.media_details.sizes && data.media_details.sizes.large && data.media_details.sizes.large.source_url,
      data.media_details && data.media_details.sizes && data.media_details.sizes.medium && data.media_details.sizes.medium.source_url
    ];
    for (var i = 0; i < candidates.length; i += 1) {
      var value = candidates[i];
      if (typeof value === 'string' && value.trim()) {
        return value.trim();
      }
    }
    return '';
  }

  function initAdminAsukaMediaUploader(scope) {
    // AsukaDesigner now calls moduleConfig.upload.selectHandler itself.
    // Keep this hook as a no-op so we do not hijack the upload module with a
    // separate DOM-level wp.media workflow.
    return scope;
  }


  function normalizeColorsConfig(value) {
        if (Array.isArray(value)) {
            return { scope: 'object', items: value };
        }
        if (!value || typeof value !== 'object') {
            return { scope: 'object', items: [] };
        }
        var scope = ['object', 'canvas', 'document'].indexOf(String(value.scope || 'object')) !== -1
            ? String(value.scope || 'object')
            : 'object';
        var items = [];
        if (Array.isArray(value.items)) {
            items = value.items;
        } else if (Array.isArray(value.colors)) {
            items = value.colors;
        }
        return { scope: scope, items: items };
  }

    function normalizeStringList(value) {
        if (typeof value === 'string') {
            value = value.split(/[\s,]+/);
        }
        if (!Array.isArray(value)) {
            return [];
        }
        var seen = {};
        var items = [];
        value.forEach(function (item) {
            item = String(item == null ? '' : item).trim();
            if (!item || seen[item]) {
                return;
            }
            seen[item] = true;
            items.push(item);
        });
        return items;
    }

    function normalizeFontItem(font, fallbackSource) {
        if (!font || typeof font !== 'object') {
            return null;
        }
        var family = String(font.family || font.label || font.name || '').trim();
        if (!family) {
            return null;
        }
        var source = String(font.source || fallbackSource || 'system').trim().toLowerCase();
        if (['system', 'google', 'custom'].indexOf(source) === -1) {
            source = 'system';
        }
        var id = String(font.id || family).trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '-').replace(/^-+|-+$/g, '') || family;
        var item = { id: id, family: family };
        if (source === 'google') {
            if (font.cssUrl) item.cssUrl = String(font.cssUrl);
            var weights = normalizeStringList(font.weights);
            if (weights.length) item.weights = weights;
            var styles = normalizeStringList(font.styles).filter(function (style) { return style === 'normal' || style === 'italic'; });
            if (styles.length) item.styles = styles;
            return { source: 'google', item: item };
        }
        if (source === 'custom') {
            var url = String(font.url || font.fileUrl || '').trim();
            var cssUrl = String(font.cssUrl || '').trim();
            if (url) item.url = url;
            if (cssUrl) item.cssUrl = cssUrl;
            if (font.format) item.format = String(font.format);
            if (font.weight != null && font.weight !== '') item.weight = font.weight;
            if (font.style === 'normal' || font.style === 'italic') item.style = font.style;
            if (url || cssUrl) {
                return { source: 'custom', item: item };
            }
        }
        return { source: 'system', item: item };
    }

    function normalizeFontsConfig(value) {
        var out = { system: [], google: [], custom: [] };
        var push = function (font, source) {
            var normalized = normalizeFontItem(font, source);
            if (!normalized) {
                return;
            }
            out[normalized.source].push(normalized.item);
        };
        if (Array.isArray(value)) {
            value.forEach(function (font) { push(font, font && font.source); });
        } else if (value && typeof value === 'object') {
            ['system', 'google', 'custom'].forEach(function (source) {
                if (Array.isArray(value[source])) {
                    value[source].forEach(function (font) { push(font, source); });
                }
            });
            if (Array.isArray(value.items)) {
                value.items.forEach(function (font) { push(font, font && font.source); });
            }
            if (Array.isArray(value.fonts)) {
                value.fonts.forEach(function (font) { push(font, font && font.source); });
            }
        }
        ['system', 'google', 'custom'].forEach(function (source) {
            var byId = {};
            out[source].forEach(function (font) {
                var id = String(font.id || font.family || '');
                if (!id) return;
                byId[id] = Object.assign({}, byId[id] || {}, font);
            });
            out[source] = Object.keys(byId).map(function (id) { return byId[id]; });
        });
        return out;
    }

    function hasFontsConfigItems(value) {
        return !!(value && typeof value === 'object' && (
            (Array.isArray(value.system) && value.system.length) ||
            (Array.isArray(value.google) && value.google.length) ||
            (Array.isArray(value.custom) && value.custom.length)
        ));
    }

    function normalizeColorItem(color, index) {
        if (!color || typeof color !== 'object') {
            return null;
        }
        var value = String(color.value || color.color || color.hex || '').trim();
        if (!value) {
            return null;
        }
        var id = String(color.id || '').trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '-').replace(/^-+|-+$/g, '') || ('color-' + index);
        var label = String(color.label || color.name || value).trim() || value;
        return Object.assign({}, color, { id: id, label: label, value: value, price: Number(color.price || 0) || 0 });
    }

    function normalizeColorItems(items) {
        if (!Array.isArray(items)) {
            if (items && typeof items === 'object' && Array.isArray(items.items)) {
                items = items.items;
            } else if (items && typeof items === 'object' && Array.isArray(items.colors)) {
                items = items.colors;
            } else {
                return [];
            }
        }
        return items.map(function (color, index) { return normalizeColorItem(color, index); }).filter(Boolean);
    }

    function normalizePrintingMethodColorItems(rawColors, colorItems) {
        var map = {};
        normalizeColorItems(colorItems || []).forEach(function (color) {
            if (color.id) map[String(color.id)] = color;
            if (color.value) map[String(color.value)] = color;
        });
        if (!Array.isArray(rawColors)) {
            rawColors = typeof rawColors === 'string' ? normalizeStringList(rawColors) : [];
        }
        return rawColors.map(function (entry, index) {
            if (entry && typeof entry === 'object') {
                return normalizeColorItem(entry, index);
            }
            var key = String(entry == null ? '' : entry).trim();
            if (!key) return null;
            return map[key] || map[key.toLowerCase()] || { id: key.toLowerCase().replace(/[^a-z0-9_-]+/g, '-') || ('color-' + index), label: key, value: key };
        }).filter(Boolean);
    }

    function normalizePrintingMethodsConfig(value, colorItems) {
        if (!Array.isArray(value)) {
            if (value && typeof value === 'object' && Array.isArray(value.items)) {
                value = value.items;
            } else if (value && typeof value === 'object' && Array.isArray(value.printingMethods)) {
                value = value.printingMethods;
            } else {
                return [];
            }
        }
        return value.map(function (method, index) {
            if (!method || typeof method !== 'object') {
                return null;
            }
            var label = String(method.label || method.name || method.id || '').trim();
            var id = String(method.id || label || ('printing-method-' + index)).trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '-').replace(/^-+|-+$/g, '') || ('printing-method-' + index);
            return Object.assign({}, method, {
                id: id,
                label: label || id,
                name: method.name || label || id,
                price: Number(method.price || 0) || 0,
                colors: normalizePrintingMethodColorItems(method.colors || [], colorItems || [])
            });
        }).filter(Boolean);
    }

    function normalizePricingHandler(handler) {
        var normalized = String(handler || '').trim().toLowerCase().replace(/[^a-z0-9]+/g, '');
        var map = {
            layersamount: 'layersAmount',
            textlength: 'textLength',
            textlines: 'textLines',
            usedcolorsamount: 'usedColorsAmount',
            colorslength: 'usedColorsAmount',
            scale: 'scale',
            layersize: 'layerSize',
            canvassize: 'canvasSize'
        };
        return map[normalized] || (handler ? String(handler) : 'layersAmount');
    }

    function normalizePricingScope(scope) {
        scope = String(scope || 'all').trim().toLowerCase();
        return scope === 'first' || scope === 'current' ? 'first' : 'all';
    }

    function normalizePricingTargetTypes(value) {
        return normalizeStringList(value).map(function (part) {
            var normalized = String(part || '').toLowerCase().replace(/[^a-z0-9]+/g, '');
            if (['text', 'textbox', 'itext'].indexOf(normalized) !== -1) return 'Text';
            if (['image', 'images'].indexOf(normalized) !== -1) return 'Image';
            if (['design', 'designs', 'group'].indexOf(normalized) !== -1) return 'Design';
            if (['tool', 'tools'].indexOf(normalized) !== -1) return 'Tools';
            return part;
        });
    }

    function normalizePricingRuleConfig(rule, index) {
        if (!rule || typeof rule !== 'object') {
            return null;
        }
        var conditions = [];
        if (Array.isArray(rule.conditions)) {
            conditions = rule.conditions.filter(function (item) { return item && typeof item === 'object'; });
        } else if (typeof rule.conditionsJson === 'string' && rule.conditionsJson.trim()) {
            try {
                var decoded = JSON.parse(rule.conditionsJson);
                if (Array.isArray(decoded)) {
                    conditions = decoded.filter(function (item) { return item && typeof item === 'object'; });
                }
            } catch (error) {}
        }
        var id = String(rule.id || rule.label || ('pricing-rule-' + index)).trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '-').replace(/^-+|-+$/g, '') || ('pricing-rule-' + index);
        var target = rule.target && typeof rule.target === 'object' && !Array.isArray(rule.target) ? rule.target : {};
        var targetTypes = normalizePricingTargetTypes(target.types || rule.targetTypes || []);
        var targetViews = normalizeStringList(target.view || rule.targetViews || []);
        var normalizedTarget = {};
        if (targetTypes.length) normalizedTarget.types = targetTypes;
        if (targetViews.length) normalizedTarget.view = targetViews;
        return Object.assign({}, rule, {
            id: id,
            label: String(rule.label || rule.name || id),
            handler: normalizePricingHandler(rule.handler),
            scope: normalizePricingScope(rule.scope),
            match: String(rule.match || 'all').toLowerCase() === 'any' ? 'any' : 'all',
            target: normalizedTarget,
            conditions: conditions,
            enabled: typeof rule.enabled === 'undefined' ? true : !!rule.enabled
        });
    }

    function normalizePricingRulesConfig(value) {
        var rules = extractPricingRules(value);
        return rules.map(function (rule, index) { return normalizePricingRuleConfig(rule, index); }).filter(Boolean);
    }

    function normalizeShapeAsset(shape, index) {
        if (!shape || typeof shape !== 'object') {
            return null;
        }
        var svgText = String(shape.svgText || shape.svg || '').trim();
        var svgPath = String(shape.svgPath || shape.path || '').trim();
        if (!svgText && svgPath) {
            var viewBox = String(shape.viewBox || '0 0 100 100');
            var fill = String(shape.fill || 'currentColor');
            var stroke = String(shape.stroke || '');
            svgText = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="' + viewBox.replace(/"/g, '&quot;') + '"><path d="' + svgPath.replace(/"/g, '&quot;') + '" fill="' + fill.replace(/"/g, '&quot;') + '"' + (stroke ? ' stroke="' + stroke.replace(/"/g, '&quot;') + '"' : '') + '/></svg>';
        }
        if (!svgText) {
            return null;
        }
        var label = String(shape.label || shape.name || ('Shape ' + (index + 1))).trim();
        var id = String(shape.id || label || ('shape-' + index)).trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '-').replace(/^-+|-+$/g, '') || ('shape-' + index);
        return Object.assign({}, shape, { id: id, name: label || id, label: label || id, svgText: svgText });
    }

    function normalizeModuleConfigConfig(value, catalog) {
        var moduleConfig = sanitizeObject(value, {});
        catalog = sanitizeObject(catalog, {});
        var hasCatalogDesigns = Array.isArray(catalog.designs);
        var designs = hasCatalogDesigns ? catalog.designs.slice() : [];
        if (!hasCatalogDesigns && moduleConfig.designs && typeof moduleConfig.designs === 'object' && Array.isArray(moduleConfig.designs.items)) {
            designs = moduleConfig.designs.items.slice();
        }
        if (hasCatalogDesigns || designs.length) {
            moduleConfig.designs = Object.assign({}, sanitizeObject(moduleConfig.designs, {}), { items: designs.filter(function (item) { return item && typeof item === 'object'; }) });
        }
        var hasCatalogShapes = Array.isArray(catalog.elements);
        var shapeAssets = hasCatalogShapes ? catalog.elements.slice() : [];
        if (!hasCatalogShapes && moduleConfig.shapes && typeof moduleConfig.shapes === 'object' && Array.isArray(moduleConfig.shapes.svgAssets)) {
            shapeAssets = moduleConfig.shapes.svgAssets.slice();
        }
        shapeAssets = shapeAssets.map(normalizeShapeAsset).filter(Boolean);
        if (hasCatalogShapes || shapeAssets.length) {
            moduleConfig.shapes = Object.assign({}, sanitizeObject(moduleConfig.shapes, {}), { svgAssets: shapeAssets });
        }
        return moduleConfig;
    }




    function extractPricingRules(container) {
        if (!container) {
            return [];
        }
        if (Array.isArray(container)) {
            return container.filter(function (item) { return item && typeof item === 'object'; });
        }
        if (typeof container !== 'object') {
            return [];
        }
        if (Array.isArray(container.pricingRules)) {
            return container.pricingRules.filter(function (item) { return item && typeof item === 'object'; });
        }
        if (Array.isArray(container.rules)) {
            return container.rules.filter(function (item) { return item && typeof item === 'object'; });
        }
        return [];
    }



  function payloadHasViews(payload) {
    if (!payload || typeof payload !== 'object') {
      return false;
    }
    if (Array.isArray(payload.views) && payload.views.length > 0) {
      return true;
    }
    if (payload.views && typeof payload.views === 'object' && Array.isArray(payload.views.views) && payload.views.views.length > 0) {
      return true;
    }
    var nestedKeys = ['document', 'initialDocument', 'state', 'legacy'];
    for (var i = 0; i < nestedKeys.length; i += 1) {
      var key = nestedKeys[i];
      if (payload[key] && payloadHasViews(payload[key])) {
        return true;
      }
    }
    return false;
  }

  function isImportableStatePayload(payload) {
    return apdIsCanonicalStateEnvelope(payload);
  }



  function summarizeI18nValue(i18n, key) {
    return getI18nTranslation(i18n, key) || '';
  }

  function collectLayoutMenuDebug(layout, i18n) {
    var rows = [];
    if (!layout || typeof layout !== 'object') {
      return rows;
    }
    ['top', 'left', 'right', 'bottom'].forEach(function (zone) {
      var zoneConfig = layout[zone];
      if (!zoneConfig || typeof zoneConfig !== 'object') {
        return;
      }
      var items = Array.isArray(zoneConfig.items) ? zoneConfig.items : (Array.isArray(zoneConfig) ? zoneConfig : null);
      if (!items) {
        return;
      }
      items.forEach(function (item) {
        if (!item || typeof item !== 'object') {
          return;
        }
        var id = item.id != null ? String(item.id) : '';
        var kind = item.kind != null ? String(item.kind) : '';
        rows.push({
          zone: zone,
          id: id,
          kind: kind,
          label: item.label != null ? String(item.label) : '',
          byId: id ? summarizeI18nValue(i18n, 'menu.' + id) : '',
          byKind: kind ? summarizeI18nValue(i18n, 'menu.' + kind) : ''
        });
      });
    });
    return rows;
  }

  function debugI18nPayload(source, rawI18n, finalI18n, layout) {
    return;
  }
  function getI18nTranslation(i18n, key) {
    if (!i18n || typeof i18n !== 'object' || !key) {
      return '';
    }

    var translations = i18n.translations && typeof i18n.translations === 'object' ? i18n.translations : {};
    var locale = typeof i18n.locale === 'string' ? i18n.locale : '';
    var fallbackLocale = typeof i18n.fallbackLocale === 'string' ? i18n.fallbackLocale : 'en';
    var languageLocale = locale ? String(locale).split(/[-_]/)[0].toLowerCase() : '';
    var order = [];

    [locale, languageLocale, fallbackLocale, 'en'].forEach(function (candidate) {
      if (!candidate) {
        return;
      }
      if (order.indexOf(candidate) === -1) {
        order.push(candidate);
      }
    });

    var segments = String(key).split('.');
    for (var i = 0; i < order.length; i += 1) {
      var node = translations[order[i]];
      for (var j = 0; node && j < segments.length; j += 1) {
        node = Object.prototype.hasOwnProperty.call(node, segments[j]) ? node[segments[j]] : undefined;
      }
      if (typeof node === 'string' && node !== '') {
        return node;
      }
    }

    return '';
  }

  function localizeLayoutMenus(layout, i18n) {
    if (!layout || typeof layout !== 'object') {
      return layout;
    }

    ['top', 'left', 'right', 'bottom'].forEach(function (zone) {
      var zoneConfig = layout[zone];
      if (!zoneConfig || typeof zoneConfig !== 'object') {
        return;
      }

      var items = Array.isArray(zoneConfig.items) ? zoneConfig.items : (Array.isArray(zoneConfig) ? zoneConfig : null);
      if (!items) {
        return;
      }

      items.forEach(function (item) {
        if (!item || typeof item !== 'object') {
          return;
        }

        var id = item.id != null ? String(item.id) : '';
        var kind = item.kind != null ? String(item.kind) : '';
        var translated = '';

        if (id) {
          translated = getI18nTranslation(i18n, 'menu.' + id);
        }
        if (!translated && kind) {
          translated = getI18nTranslation(i18n, 'menu.' + kind);
        }
        if (translated) {
          item.label = translated;
        }
      });
    });

    return layout;
  }



    function normalizeMenuToken(value) {
        return String(value == null ? '' : value)
            .trim()
            .toLowerCase()
            .replace(/&apos;|&#039;/g, '')
            .replace(/[^a-z0-9]+/g, '-')
            .replace(/^-+|-+$/g, '');
    }

    function collectMenuTranslationMap(i18n) {
        var map = {};
        if (!i18n || typeof i18n !== 'object') {
            return map;
        }
        var translations = i18n.translations && typeof i18n.translations === 'object' ? i18n.translations : {};
        var locale = typeof i18n.locale === 'string' ? i18n.locale : '';
        var fallbackLocale = typeof i18n.fallbackLocale === 'string' ? i18n.fallbackLocale : 'en';
        var languageLocale = locale ? String(locale).split(/[-_]/)[0].toLowerCase() : '';
        var order = [];
        ['en', fallbackLocale, languageLocale, locale].forEach(function (candidate) {
            if (!candidate || order.indexOf(candidate) !== -1) {
                return;
            }
            order.push(candidate);
        });
        order.forEach(function (candidate) {
            var node = translations[candidate];
            var menu = node && typeof node === 'object' && node.menu && typeof node.menu === 'object' ? node.menu : null;
            if (!menu) {
                return;
            }
            Object.keys(menu).forEach(function (key) {
                var value = menu[key];
                if (typeof value === 'string' && value !== '') {
                    map[String(key)] = value;
                }
            });
        });
        return map;
    }

    function getRenderedMenuElements(root) {
        if (!root || typeof root.querySelectorAll !== 'function') {
            return [];
        }
        var selectors = [
            '[data-asuka-block-id]',
            '.ask-theme-sidebar-item',
            '.ask-menu-item',
            '[class*="ask-theme-sidebar-item"]'
        ];
        var seen = [];
        var items = [];
        selectors.forEach(function (selector) {
            root.querySelectorAll(selector).forEach(function (node) {
                if (seen.indexOf(node) !== -1) {
                    return;
                }
                seen.push(node);
                items.push(node);
            });
        });
        return items;
    }

    function buildRenderedMenuRows(root, i18n) {
        var rows = [];
        var menuMap = collectMenuTranslationMap(i18n);
        getRenderedMenuElements(root).forEach(function (node, index) {
            if (!node || typeof node !== 'object') {
                return;
            }
            var labelNode = node.querySelector ? node.querySelector('.ask-theme-sidebar-item__label, .ask-nav__label, .ask-font-medium') : null;
            var visibleLabel = labelNode && labelNode.textContent ? String(labelNode.textContent).trim() : '';
            var title = node.getAttribute ? String(node.getAttribute('title') || node.getAttribute('aria-label') || '').trim() : '';
            var blockId = node.getAttribute ? String(node.getAttribute('data-asuka-block-id') || '').trim() : '';
            var kind = node.getAttribute ? String(node.getAttribute('data-kind') || node.getAttribute('data-module-kind') || '').trim() : '';
            var normalizedVisible = normalizeMenuToken(visibleLabel || title);
            var byId = blockId ? getI18nTranslation(i18n, 'menu.' + blockId) : '';
            var byKind = kind ? getI18nTranslation(i18n, 'menu.' + kind) : '';
            var byVisible = !byId && !byKind && normalizedVisible && Object.prototype.hasOwnProperty.call(menuMap, normalizedVisible) ? String(menuMap[normalizedVisible]) : '';
            rows.push({
                index: index,
                blockId: blockId,
                kind: kind,
                title: title,
                visibleLabel: visibleLabel,
                normalizedVisible: normalizedVisible,
                byId: byId,
                byKind: byKind,
                byVisible: byVisible
            });
        });
        return rows;
    }

    function patchRenderedMenuDom(root, i18n, source) {
        var patched = [];
        var rows = buildRenderedMenuRows(root, i18n);
        var nodes = getRenderedMenuElements(root);
        rows.forEach(function (row) {
            var node = nodes[row.index];
            if (!node) {
                return;
            }
            var translated = row.byId || row.byKind || row.byVisible || '';
            if (!translated) {
                return;
            }
            var labelNode = node.querySelector ? node.querySelector('.ask-theme-sidebar-item__label, .ask-nav__label, .ask-font-medium') : null;
            if (labelNode && String(labelNode.textContent || '').trim() !== translated) {
                labelNode.textContent = translated;
                patched.push({ target: 'text', blockId: row.blockId, kind: row.kind, from: row.visibleLabel, to: translated });
            }
            if (node.setAttribute) {
                var currentTitle = String(node.getAttribute('title') || '').trim();
                if (currentTitle && currentTitle !== translated) {
                    node.setAttribute('title', translated);
                    patched.push({ target: 'title', blockId: row.blockId, kind: row.kind, from: currentTitle, to: translated });
                }
                var currentAria = String(node.getAttribute('aria-label') || '').trim();
                if (currentAria && currentAria !== translated) {
                    node.setAttribute('aria-label', translated);
                    patched.push({ target: 'aria-label', blockId: row.blockId, kind: row.kind, from: currentAria, to: translated });
                }
            }
        });
        return rows;
    }

    function patchInstanceLayoutMenus(instance, i18n, source) {
        var results = [];
        if (!instance || !instance.layout || typeof instance.layout.updateItem !== 'function') {
            return results;
        }
        var menuMap = collectMenuTranslationMap(i18n);
        Object.keys(menuMap).forEach(function (key) {
            var translated = menuMap[key];
            if (!translated) {
                return;
            }
            try {
                instance.layout.updateItem(key, { label: translated });
                results.push({ target: key, label: translated, ok: true });
            } catch (error) {
                results.push({ target: key, label: translated, ok: false, error: error && error.message ? error.message : String(error) });
            }
        });
        return results;
    }

    function installRenderedMenuDebug(root, instance, i18n, source) {
        if (!root || !i18n || typeof i18n !== 'object') {
            return;
        }
        patchInstanceLayoutMenus(instance, i18n, source);
        if (root.__apdRenderedMenuObserver && typeof root.__apdRenderedMenuObserver.disconnect === 'function') {
            root.__apdRenderedMenuObserver.disconnect();
        }
        root.__apdRenderedMenuState = { signature: '' };
        var run = function () {
            var rows = patchRenderedMenuDom(root, i18n, source);
            var signature = rows.map(function (row) {
                return [row.blockId, row.kind, row.visibleLabel, row.title, row.byId, row.byKind, row.byVisible].join('|');
            }).join('||');
            if (root.__apdRenderedMenuState.signature === signature) {
                return;
            }
            root.__apdRenderedMenuState.signature = signature;
        };
        run();
        window.setTimeout(run, 80);
        window.setTimeout(run, 300);
        window.setTimeout(run, 900);
        if (typeof MutationObserver === 'function') {
            root.__apdRenderedMenuObserver = new MutationObserver(function () {
                if (root.__apdRenderedMenuTick) {
                    return;
                }
                root.__apdRenderedMenuTick = window.setTimeout(function () {
                    root.__apdRenderedMenuTick = null;
                    run();
                }, 30);
            });
            root.__apdRenderedMenuObserver.observe(root, {
                childList: true,
                subtree: true,
                attributes: true,
                characterData: false,
            });
        }
    }

    function getTopLevelPricingPayload(raw) {
        raw = sanitizeObject(raw, {});
        var nested = sanitizeObject(raw.pricing, {});
        var topLevelRules = normalizePricingRulesConfig(raw);
        var nestedRules = normalizePricingRulesConfig(nested);
        var rawPrice = typeof raw.price !== 'undefined' ? raw.price : undefined;
        if (typeof rawPrice === 'undefined') rawPrice = typeof raw.basePrice !== 'undefined' ? raw.basePrice : undefined;
        if (typeof rawPrice === 'undefined') rawPrice = typeof raw.designerBasePrice !== 'undefined' ? raw.designerBasePrice : undefined;
        if (typeof rawPrice === 'undefined') rawPrice = typeof nested.price !== 'undefined' ? nested.price : undefined;
        if (typeof rawPrice === 'undefined') rawPrice = typeof nested.basePrice !== 'undefined' ? nested.basePrice : undefined;
        if (typeof rawPrice === 'undefined') rawPrice = typeof nested.designerBasePrice !== 'undefined' ? nested.designerBasePrice : undefined;
        var priceFormat = sanitizeObject(raw.priceFormat, {});
        if (!Object.keys(priceFormat).length) {
            priceFormat = sanitizeObject(nested.priceFormat, {});
        }
        return {
            price: typeof rawPrice === 'undefined' ? undefined : (Number(rawPrice) || 0),
            pricingRules: topLevelRules.length ? topLevelRules : nestedRules,
            priceFormat: priceFormat
        };
    }



  function sanitizePreviewConfig(raw) {
    raw = sanitizeObject(raw, {});
    var topLevelPricing = getTopLevelPricingPayload(raw);
    var colorsConfig = normalizeColorsConfig(raw.colors);
    colorsConfig.items = normalizeColorItems(colorsConfig.items);
    var catalog = sanitizeObject(raw.catalog, {});
    catalog.colors = normalizeColorItems(catalog.colors || colorsConfig.items || []);
    if (!catalog.colors.length && colorsConfig.items.length) {
      catalog.colors = colorsConfig.items.slice();
    }
    catalog.printingMethods = normalizePrintingMethodsConfig(catalog.printingMethods || raw.printingMethods || [], catalog.colors || colorsConfig.items || []);
    if (Array.isArray(catalog.elements)) {
      catalog.elements = catalog.elements.map(normalizeShapeAsset).filter(Boolean);
    }
    var printingMethods = normalizePrintingMethodsConfig(raw.printingMethods || catalog.printingMethods || [], colorsConfig.items || catalog.colors || []);
    var moduleConfig = normalizeModuleConfigConfig(raw.moduleConfig, catalog);
    var fontsConfig = normalizeFontsConfig(raw.fonts);
    var pricingConfig = sanitizeObject(raw.pricing, {});
    if (topLevelPricing.pricingRules.length) {
      pricingConfig = Object.assign({}, pricingConfig, {
        rules: topLevelPricing.pricingRules,
        pricingRules: topLevelPricing.pricingRules
      });
    }
    var safe = {
      mode: typeof raw.mode === 'string' ? raw.mode : 'standalone',
      theme: sanitizeObject(raw.theme, {}),
      layout: sanitizeObject(raw.layout, {}),
      permissions: sanitizeObject(raw.permissions, {}),
      ui: sanitizeObject(raw.ui, {}),
      listeners: sanitizeObject(raw.listeners, {}),
      integrations: sanitizeObject(raw.integrations, {}),
      license: sanitizeObject(raw.license, {}),
      ai: sanitizeObject(raw.ai, {}),
      exports: sanitizeObject(raw.exports, {}),
      moduleConfig: moduleConfig,
      upload: sanitizeObject(raw.upload, {}),
      pricing: pricingConfig,
      price: topLevelPricing.price,
      pricingRules: topLevelPricing.pricingRules,
      priceFormat: sanitizeObject(topLevelPricing.priceFormat, {}),
      context: sanitizeObject(raw.context, {}),
      pdf: sanitizeObject(raw.pdf, {}),
      i18n: sanitizeObject(raw.i18n, {}),
      catalog: catalog,
      colors: colorsConfig,
      printingMethods: printingMethods,
      views: sanitizeArray(raw.views, []),
      drawingZones: sanitizeArray(raw.drawingZones, []),
      fonts: fontsConfig,
      initialState: apdResolveCanonicalInitialState(raw.initialState, raw.initialDocument)
    };

    if (!safe.initialState) delete safe.initialState;

    var hasStateBootPayload = !!(
      safe.initialState
      && typeof safe.initialState === 'object'
      && !Array.isArray(safe.initialState)
      && Object.keys(safe.initialState).length
    );

    var stateHasViews = payloadHasViews(safe.initialState);
    var stateCanBoot = isImportableStatePayload(safe.initialState);

    if (hasStateBootPayload && stateHasViews && stateCanBoot) {
      safe.initialState = apdClonePlainObject(safe.initialState);
    } else {
      delete safe.initialState;
      hasStateBootPayload = false;
    }

    if (hasStateBootPayload && stateHasViews) {
      apdMergeTopLevelDrawingZonesIntoBootPayload(safe);
      delete safe.views;
      delete safe.drawingZones;
    }

    if (!Array.isArray(safe.views) || !safe.views.length) delete safe.views;
    if (!Array.isArray(safe.drawingZones) || !safe.drawingZones.length) delete safe.drawingZones;
    if (!hasFontsConfigItems(safe.fonts)) delete safe.fonts;
    if (!Object.keys(safe.theme).length) delete safe.theme;
    if (!Object.keys(safe.layout).length) delete safe.layout;
    if (!Object.keys(safe.permissions).length) delete safe.permissions;
    if (!Object.keys(safe.listeners).length) delete safe.listeners;
    if (!Object.keys(safe.integrations).length) delete safe.integrations;
    if (!Object.keys(safe.license).length) delete safe.license;
    if (!Object.keys(safe.ai).length) delete safe.ai;
    if (!Object.keys(safe.exports).length) delete safe.exports;
    if (!Object.keys(safe.moduleConfig).length) delete safe.moduleConfig;
    if (!Object.keys(safe.upload).length) delete safe.upload;
    if (!Object.keys(safe.pricing).length) delete safe.pricing;
    if (typeof safe.price === 'undefined') delete safe.price;
    if (!Array.isArray(safe.pricingRules) || !safe.pricingRules.length) delete safe.pricingRules;
    if (!Object.keys(safe.priceFormat).length) delete safe.priceFormat;
    if (!Object.keys(safe.context).length) delete safe.context;
    if (!Object.keys(safe.pdf).length) delete safe.pdf;
    localizeLayoutMenus(safe.layout, safe.i18n);
    debugI18nPayload('admin-app:sanitizePreviewConfig', raw.i18n, safe.i18n, safe.layout);
    if (!Object.keys(safe.i18n).length) delete safe.i18n;
    if (!Object.keys(safe.catalog).length) delete safe.catalog;
    if (!Array.isArray(safe.printingMethods) || !safe.printingMethods.length) delete safe.printingMethods;

    return safe;
  }

  function findField(name, scope) {
    return (scope || document).querySelector('[name="' + name + '"]');
  }

  function activateTab(tabs, tabName) {
    if (!tabs) {
      return;
    }
    var buttons = tabs.querySelectorAll('.apd-tab-button');
    var panels = tabs.querySelectorAll('.apd-tab-panel');
    buttons.forEach(function (btn) {
      btn.classList.toggle('is-active', btn.getAttribute('data-tab') === tabName);
    });
    panels.forEach(function (panel) {
      panel.classList.toggle('is-active', panel.getAttribute('data-panel') === tabName);
    });
  }

  function initTabs(scope) {
    scope.querySelectorAll('.js-apd-tabs').forEach(function (tabs) {
      tabs.querySelectorAll('.apd-tab-button').forEach(function (button) {
        button.addEventListener('click', function () {
          activateTab(tabs, button.getAttribute('data-tab'));
        });
      });
    });
  }

  function autoresize(scope) {
    scope.querySelectorAll('.apd-codearea').forEach(function (textarea) {
      var resize = function () {
        textarea.style.height = 'auto';
        textarea.style.height = Math.max(textarea.scrollHeight + 2, 180) + 'px';
      };
      resize();
      textarea.addEventListener('input', resize);
    });
  }


  function initCatalogLibraryEnhancements(scope) {
    var sections = scope.querySelectorAll('[data-apd-catalog-section]');
    sections.forEach(function (section) {
      var key = section.getAttribute('data-apd-catalog-section');
      var search = section.querySelector('[data-apd-catalog-search="' + key + '"]');
      var category = section.querySelector('[data-apd-catalog-category="' + key + '"]');
      var count = section.querySelector('[data-apd-catalog-count="' + key + '"]');
      var list = section.querySelector('[data-apd-catalog-list="' + key + '"]');
      var panel = section.querySelector('[data-apd-import-panel="' + key + '"]');

      var applyFilter = function () {
        if (!list) {
          return;
        }
        var query = (search && search.value ? search.value : '').trim().toLowerCase();
        var categoryValue = category && category.value ? category.value : '';
        var visible = 0;
        list.querySelectorAll('[data-apd-search]').forEach(function (row) {
          var haystack = row.getAttribute('data-apd-search') || '';
          var rowCategory = row.getAttribute('data-apd-category') || '';
          var matchesQuery = !query || haystack.indexOf(query) !== -1;
          var matchesCategory = !categoryValue || rowCategory === categoryValue;
          var show = matchesQuery && matchesCategory;
          row.hidden = !show;
          if (show) {
            visible += 1;
          }
        });
        if (count) {
          count.textContent = String(visible);
        }
      };

      if (search) {
        search.addEventListener('input', applyFilter);
      }
      if (category) {
        category.addEventListener('change', applyFilter);
      }
      applyFilter();

      section.querySelectorAll('[data-apd-import-toggle="' + key + '"]').forEach(function (button) {
        button.addEventListener('click', function () {
          if (!panel) {
            return;
          }
          panel.classList.toggle('is-open');
        });
      });

      var mediaPanel = section.querySelector('[data-apd-media-import-panel="' + key + '"]');
      section.querySelectorAll('[data-apd-media-import-toggle="' + key + '"]').forEach(function (button) {
        button.addEventListener('click', function () {
          if (!mediaPanel) {
            return;
          }
          mediaPanel.classList.toggle('is-open');
        });
      });
    });
  }

  function renderMediaImportSelection(list, attachments) {
    if (!list) {
      return;
    }
    if (!attachments || !attachments.length) {
      list.innerHTML = '<p class="description">' + escapeHtml(adminLabel('mediaImportNoneSelected', 'No media selected yet.')) + '</p>';
      return;
    }
    var html = '<div class="apd-media-import-grid">';
    attachments.forEach(function (item) {
      var preview = '';
      var thumb = (item.thumbnail || item.url || item.icon || '');
      if (thumb) {
        preview = '<div class="apd-media-import-card__preview"><img src="' + escapeHtml(thumb) + '" alt=""></div>';
      }
      var title = item.title || item.filename || ('#' + String(item.id || ''));
      var meta = [];
      if (item.filename) {
        meta.push(item.filename);
      }
      if (item.mime) {
        meta.push(item.mime);
      }
      html += '<div class="apd-media-import-card">' + preview + '<div class="apd-media-import-card__body"><strong>' + escapeHtml(title) + '</strong>' + (meta.length ? '<small>' + escapeHtml(meta.join(' · ')) + '</small>' : '') + '</div></div>';
    });
    html += '</div>';
    list.innerHTML = html;
  }

  function initCatalogMediaImports(scope) {
    var config = window.APDAdminConfig || {};
    var labels = config.labels || {};
    scope.querySelectorAll('[data-apd-media-import-form]').forEach(function (form) {
      var key = form.getAttribute('data-apd-media-import-form');
      var idsField = form.querySelector('[data-apd-media-import-ids="' + key + '"]');
      var list = form.querySelector('[data-apd-media-import-list="' + key + '"]');
      var pickButtons = form.querySelectorAll('[data-apd-media-import-pick="' + key + '"]');
      var clearButtons = form.querySelectorAll('[data-apd-media-import-clear="' + key + '"]');
      var state = [];

      var sync = function () {
        if (idsField) {
          idsField.value = state.map(function (item) { return String(item.id || ''); }).filter(Boolean).join(',');
        }
        renderMediaImportSelection(list, state);
      };

      pickButtons.forEach(function (button) {
        button.addEventListener('click', function () {
          if (!window.wp || !window.wp.media) {
            return;
          }
          var frame = window.wp.media({
            title: labels.mediaImportTitle || 'Choose media items',
            button: { text: labels.mediaImportButton || 'Import selected media' },
            multiple: true,
            library: {}
          });
          frame.on('select', function () {
            var selection = frame.state().get('selection');
            state = selection.map(function (attachment) {
              var data = attachment.toJSON ? attachment.toJSON() : {};
              var thumb = '';
              if (data.sizes && data.sizes.thumbnail && data.sizes.thumbnail.url) {
                thumb = data.sizes.thumbnail.url;
              } else if (data.icon) {
                thumb = data.icon;
              } else if (data.url) {
                thumb = data.url;
              }
              return {
                id: data.id,
                title: data.title || '',
                filename: data.filename || '',
                mime: data.mime || data.subtype || '',
                url: data.url || '',
                thumbnail: thumb
              };
            }).filter(function (item) { return !!item.id; });
            sync();
          });
          frame.open();
        });
      });

      clearButtons.forEach(function (button) {
        button.addEventListener('click', function () {
          state = [];
          sync();
        });
      });

      form.addEventListener('submit', function (event) {
        if (!idsField || !idsField.value) {
          event.preventDefault();
          window.alert(labels.mediaImportEmpty || adminLabel('mediaImportEmpty', 'Please select at least one media item.'));
        }
      });

      sync();
    });
  }

  function initCopy(scope) {
    scope.querySelectorAll('.js-apd-copy-text, .js-apd-copy-sample').forEach(function (button) {
      button.addEventListener('click', function () {
        var text = button.getAttribute('data-copy-text') || '';
        if (!navigator.clipboard) {
          window.alert(adminLabel('copyFailedShort', 'Copy failed'));
          return;
        }
        navigator.clipboard.writeText(text).then(function () {
          var original = button.textContent;
          button.textContent = adminLabel('copiedShort', 'Copied');
          window.setTimeout(function () { button.textContent = original; }, 1400);
        }).catch(function () {
          window.alert(adminLabel('copyFailedShort', 'Copy failed'));
        });
      });
    });
  }

  function defaultCustomerModuleOpen(overrides) {
    return Object.assign({
      views: false,
      designs: true,
      text: true,
      image: true,
      images: true,
      shapes: true,
      layers: true,
      colors: true,
      zoom: true,
      magnifier: true,
      download: true,
      history: true,
      state: false,
      pricing: false,
      pricingRules: false,
      'pricing-rules': false,
      elements: false,
      'element-variation-picker': true,
      elementVariationPicker: true,
      variations: true,
      'product-parameters': false,
      assembly: false,
      materials: false,
      camera: false,
      modelOptimizer: false,
      license: false,
      drawingAreas: false,
      marker: false,
      'element-editor': false,
      elementEditor: false,
      studio: true,
      aiTextToImage: true,
      'image-ai-text-to-image': false,
      'image-upload': false,
      'image-qrcode': false,
      'image-facebook': false,
      'image-pixabay': false,
      'image-pexels': false,
      facebookImage: true,
      pixabayImage: true,
      pexelsImage: true
    }, overrides || {});
  }

  function defaultCustomerPermissions(overrides) {
    var safeOverrides = sanitizeObject(overrides, {});
    var moduleOverrides = sanitizeObject(safeOverrides.moduleOpen, {});
    try { delete safeOverrides.moduleOpen; } catch (error) {}
    return Object.assign({
      canOpenModule: true,
      canMovePanels: true,
      canCollapsePanels: true,
      canUseStateTools: false,
      canUseMagnifier: true,
      canUseZoom: true,
      canZoomIn: true,
      canZoomOut: true,
      canFitViewport: true,
      canResetViewport: true,
      canPanCanvas: true,
      canUndo: true,
      canRedo: true,
      canReset: true,
      canAddRect: true,
      canAddText: true,
      canAddImage: true,
      canAddShape: true,
      canDrawShape: true,
      canClearCanvas: true,
      canDuplicateObject: true,
      canDeleteObject: true,
      canMoveObject: true,
      canResizeObject: true,
      canRotateObject: true,
      canEditObjectColors: true,
      canEditObjectStudio: true,
      canEditObjectText: true,
      canSwitchView: true,
      canAddView: false,
      canViewViews: true,
      canCreateView: false,
      canEditView: false,
      canDeleteView: false,
      canViewLayers: true,
      canReorderLayers: true,
      canLockLayers: true,
      canHideLayers: true,
      canSelectLayers: true,
      canViewDesigns: true,
      canAddDesigns: true,
      canOpenImages: true,
      canUploadImage: true,
      canUseRemoteImages: true,
      canUseExternalImageLibraries: true,
      canUseFacebookImages: true,
      canUsePixabayImages: true,
      canUsePexelsImages: true,
      canEditText: true,
      canEditFontFamily: true,
      canEditTextFormatting: true,
      canEditTextCurve: true,
      canUseColors: true,
      canEditColors: true,
      canUseStudio: true,
      canApplyStudioEffects: true,
      canDownload: true,
      canDownloadCurrentView: true,
      canDownloadAllViews: true,
      canDownloadObjects: true,
      canDownloadDrawingZones: true,
      canExportJson: false,
      canImportJson: false,
      canViewDrawingAreas: true,
      canCreateDrawingAreas: false,
      canEditDrawingAreas: false,
      canDeleteDrawingAreas: false,
      canViewFixedSlots: true,
      canCreateFixedSlots: false,
      canEditFixedSlots: false,
      canDeleteFixedSlots: false,
      canViewPricing: false,
      canEditPricingBasePrice: false,
      canEditPricingRules: false,
      canAddPricingRule: false,
      canRemovePricingRule: false,
      canViewElements: true,
      canAddElements: false,
      canEditElements: false,
      canEditElementVariations: false,
      canDeleteElements: false,
      moduleOpen: defaultCustomerModuleOpen(moduleOverrides)
    }, safeOverrides);
  }

  function adminEditorPermissions(base) {
    var permissions = Object.assign({}, defaultCustomerPermissions(), sanitizeObject(base, {}));
    Object.keys(permissions).forEach(function (key) {
      if (typeof permissions[key] === 'boolean') {
        permissions[key] = true;
      }
    });
    permissions.canOpenModule = true;
    permissions.canUseStateTools = true;
    permissions.canImportJson = true;
    permissions.canExportJson = true;

    var moduleOpen = sanitizeObject(permissions.moduleOpen, {});
    Object.keys(moduleOpen).forEach(function (key) {
      moduleOpen[key] = true;
    });
    [
      'views', 'designs', 'text', 'image', 'images', 'shapes', 'layers',
      'elements', 'drawingAreas', 'drawing-areas', 'marker', 'fixedSlots',
      'fixed-slots', 'pricing', 'pricingRules', 'pricing-rules', 'colors',
      'download', 'state', 'history', 'zoom', 'ruler', 'magnifier', 'studio',
      'element-editor', 'elementEditor', 'aiTextToImage', 'ai-text-to-image',
      'facebookImage', 'facebook-image', 'pixabayImage', 'pixabay-image',
      'pexelsImage', 'pexels-image', 'element-variation-picker', 'variations',
      'product-parameters', 'productParameters', 'materials', 'camera',
      'assembly',
      'modelOptimizer', 'model-optimizer', 'license', 'image-upload',
      'upload-image', 'image-qrcode', 'qrcode-image', 'image-ai-text-to-image',
      'image-facebook', 'image-pixabay', 'image-pexels'
    ].forEach(function (key) {
      moduleOpen[key] = true;
    });
    permissions.moduleOpen = moduleOpen;

    return permissions;
  }

  function withAllBooleanPermissions(enabled, moduleEnabled) {
    var permissions = defaultCustomerPermissions();
    Object.keys(permissions).forEach(function (key) {
      if (typeof permissions[key] === 'boolean') {
        permissions[key] = !!enabled;
      }
    });
    permissions.moduleOpen = defaultCustomerModuleOpen();
    Object.keys(permissions.moduleOpen).forEach(function (key) {
      permissions.moduleOpen[key] = !!moduleEnabled;
    });
    return permissions;
  }

  function buildPermissionPreset(name) {
    if (name === 'full') {
      return withAllBooleanPermissions(true, true);
    }
    if (name === 'locked') {
      return withAllBooleanPermissions(false, false);
    }
    if (name === 'text-only') {
      return defaultCustomerPermissions({
        canMovePanels: false,
        canCollapsePanels: true,
        canAddRect: false,
        canAddImage: false,
        canAddShape: false,
        canDrawShape: false,
        canUploadImage: false,
        canUseRemoteImages: false,
        canUseExternalImageLibraries: false,
        canUseFacebookImages: false,
        canUsePixabayImages: false,
        canUsePexelsImages: false,
        canViewDesigns: false,
        canAddDesigns: false,
        canViewPricing: false,
        canEditPricingBasePrice: false,
        canEditPricingRules: false,
        canAddPricingRule: false,
        canRemovePricingRule: false,
        canAddElements: false,
        canEditObjectStudio: false,
        canUseStudio: false,
        canApplyStudioEffects: false,
        canViewDrawingAreas: true,
        canCreateDrawingAreas: false,
        canEditDrawingAreas: false,
        canDeleteDrawingAreas: false,
        canViewFixedSlots: true,
        canCreateFixedSlots: false,
        canEditFixedSlots: false,
        canDeleteFixedSlots: false,
        canViewElements: true,
        canEditElements: false,
        canEditElementVariations: false,
        canDeleteElements: false,
        moduleOpen: defaultCustomerModuleOpen({
          views: false,
          designs: false,
          image: false,
          images: false,
          shapes: false,
          pricing: false,
          pricingRules: false,
          elements: false,
          drawingAreas: false,
          marker: false,
          'element-editor': false,
          elementEditor: false,
          studio: false,
          aiTextToImage: false,
          facebookImage: false,
          pixabayImage: false,
          pexelsImage: false
        })
      });
    }
    return defaultCustomerPermissions();
  }

  function starterMap() {
    return {
      blank: {
        _apd_theme: { appearance: 'light' },
        _apd_layout: { height: '720px' },
        _apd_modules: { left: ['text', 'image'], right: ['layers', 'history'] },
        _apd_permissions: defaultCustomerPermissions(),
        _apd_ui: {},
        _apd_views: [],
        _apd_drawing_zones: []
      },
      tee: {
        _apd_theme: { appearance: 'light' },
        _apd_layout: { height: '780px' },
        _apd_modules: { top: ['views'], left: ['text', 'image', 'colors'], right: ['layers', 'history'] },
        _apd_permissions: defaultCustomerPermissions(),
        _apd_ui: {},
        _apd_views: [
          { id: 'front', name: 'Front', width: 1400, height: 1600 },
          { id: 'back', name: 'Back', width: 1400, height: 1600 }
        ],
        _apd_drawing_zones: [
          { id: 'front-main', viewId: 'front', left: 280, top: 260, width: 840, height: 980 },
          { id: 'back-main', viewId: 'back', left: 280, top: 260, width: 840, height: 980 }
        ],
        _apd_colors: [{ id: 'white', label: 'White', hex: '#FFFFFF' }, { id: 'navy', label: 'Navy', hex: '#223056' }]
      },
      poster: {
        _apd_theme: { appearance: 'light' },
        _apd_layout: { height: '820px' },
        _apd_modules: { left: ['text', 'image'], right: ['layers', 'history', 'download'] },
        _apd_permissions: defaultCustomerPermissions({ canSwitchView: false }),
        _apd_ui: {},
        _apd_views: [{ id: 'poster', name: 'Poster', width: 1200, height: 1800 }],
        _apd_drawing_zones: [{ id: 'poster-safe', viewId: 'poster', left: 120, top: 120, width: 960, height: 1560 }]
      }
    };
  }

  function applyStarterValue(value, scope) {
    var starters = starterMap();
    var selected = starters[value] || starters.blank;
    Object.keys(selected).forEach(function (fieldName) {
      var field = findField(fieldName, scope);
      if (!field) {
        return;
      }
      if (field.tagName === 'SELECT') {
        field.value = selected[fieldName];
      } else {
        field.value = JSON.stringify(selected[fieldName], null, 2);
        field.dispatchEvent(new Event('input', { bubbles: true }));
      }
    });

    var modeField = findField('_apd_mode', scope);
    if (modeField && !modeField.value) {
      modeField.value = 'standalone';
    }
  }

  function productConfigAdminLayout(height) {
    var resolvedHeight = height || '720px';
    return {
      height: resolvedHeight,
      left: {
        type: 'default',
        collapsed: false,
        collapsible: true,
        items: [
          { id: 'views', kind: 'views', label: 'Views', icon: 'views', zone: 'left', type: 'floating' },
          { id: 'designs', kind: 'designs', label: 'Designs', icon: 'brush', zone: 'left', type: 'floating' },
          { id: 'text', kind: 'text', label: 'Text', icon: 'text', zone: 'left', type: 'floating' },
          { id: 'image', kind: 'image', label: 'Images', icon: 'images', zone: 'left', type: 'floating' },
          { id: 'shapes', kind: 'shapes', label: 'Shapes', icon: 'customize', zone: 'left', type: 'floating' },
          { id: 'layers', kind: 'layers', label: 'Layers', icon: 'layers', zone: 'left', type: 'floating' },
          { id: 'elements', kind: 'elements', label: 'Elements', icon: 'puzzle', zone: 'left', type: 'floating' },
          { id: 'product-parameters', kind: 'product-parameters', label: 'Parameters', icon: 'product-parameters', zone: 'left', type: 'floating' },
          { id: 'assembly', kind: 'assembly', label: 'Assembly', icon: 'magnet', zone: 'left', type: 'floating' },
          { id: 'studio', kind: 'studio', label: 'Studio', icon: 'studio', zone: 'left', type: 'modal' },
          { id: 'drawingAreas', kind: 'drawingAreas', label: 'Drawing areas', icon: 'drawing-areas', zone: 'left', type: 'floating' },
          { id: 'marker', kind: 'marker', label: 'Markers', icon: 'pin', zone: 'left', type: 'floating' },
          { id: 'pricingRules', kind: 'pricingRules', label: 'Pricing rules', icon: 'pricing-rules', zone: 'left', type: 'floating' }
        ]
      },
      top: {
        type: 'default',
        collapsed: false,
        collapsible: true,
        items: [
          { id: 'history', kind: 'history', label: 'History', icon: 'history', zone: 'top', type: 'floating' },
          { id: 'zoom', kind: 'zoom', label: 'Zoom', icon: 'zoom', zone: 'top', type: 'floating' },
          { id: 'magnifier', kind: 'magnifier', label: 'Magnifier', icon: 'magnifying-glass', zone: 'top', type: 'floating' },
          { id: 'ruler', kind: 'ruler', label: 'Ruler', icon: 'ruler', zone: 'top', type: 'floating' },
          { id: 'camera', kind: 'camera', label: 'Camera', icon: 'camera', zone: 'top', type: 'floating' },
          { id: 'download', kind: 'download', label: 'Download', icon: 'download', zone: 'top', type: 'floating' },
          { id: 'pricing', kind: 'pricing', label: 'Pricing', icon: 'pricing', zone: 'top', type: 'floating' },
          { id: 'modelOptimizer', kind: 'modelOptimizer', label: 'GLB optimizer', icon: 'model-optimizer', zone: 'top', type: 'modal' },
          { id: 'state', kind: 'state', label: 'State', icon: 'state', zone: 'top', type: 'floating' }
        ]
      },
      right: {
        type: 'default',
        collapsed: false,
        collapsible: true,
        items: []
      },
      bottom: {
        type: 'default',
        collapsed: true,
        collapsible: true,
        items: []
      },
      selectionMenu: {
        type: 'popover',
        open: false,
        items: ['transform', 'colors', 'font-family', 'text-formatting', 'text-curve']
      }
    };
  }

  function clearBootLayout(payload) {
    if (!payload || typeof payload !== 'object') {
      return;
    }
    try { delete payload.layout; } catch (error) {}
    if (payload.ui && typeof payload.ui === 'object') {
      try { delete payload.ui.layout; } catch (error) {}
    }
    if (payload.scope && typeof payload.scope === 'object') {
      try { delete payload.scope.layout; } catch (error) {}
    }
    if (payload.legacy && typeof payload.legacy === 'object') {
      if (payload.legacy.state && typeof payload.legacy.state === 'object') {
        try { delete payload.legacy.state.layout; } catch (error) {}
        if (payload.legacy.state.ui && typeof payload.legacy.state.ui === 'object') {
          try { delete payload.legacy.state.ui.layout; } catch (error) {}
        }
      }
      if (payload.legacy.scope && typeof payload.legacy.scope === 'object') {
        try { delete payload.legacy.scope.layout; } catch (error) {}
      }
    }
  }

  function clearBootPermissions(payload) {
    if (!payload || typeof payload !== 'object') {
      return;
    }
    try { delete payload.permissions; } catch (error) {}
    try { delete payload.permissionsByView; } catch (error) {}
    try { delete payload.role; } catch (error) {}
    if (payload.ui && typeof payload.ui === 'object') {
      try { delete payload.ui.permissions; } catch (error) {}
      try { delete payload.ui.permissionsByView; } catch (error) {}
    }
    if (payload.scope && typeof payload.scope === 'object') {
      try { delete payload.scope.permissions; } catch (error) {}
      try { delete payload.scope.permissionsByView; } catch (error) {}
    }
    if (payload.legacy && typeof payload.legacy === 'object') {
      if (payload.legacy.state && typeof payload.legacy.state === 'object') {
        clearBootPermissions(payload.legacy.state);
      }
      if (payload.legacy.scope && typeof payload.legacy.scope === 'object') {
        try { delete payload.legacy.scope.permissions; } catch (error) {}
        try { delete payload.legacy.scope.permissionsByView; } catch (error) {}
      }
    }
    ['state', 'initialState', 'document', 'documentState', 'initialDocument'].forEach(function (key) {
      if (payload[key] && typeof payload[key] === 'object') {
        clearBootPermissions(payload[key]);
      }
    });
  }

  function forceProductConfigAdminLayout(config, height, layoutOverride) {
    if (!config || typeof config !== 'object') {
      return config;
    }
    var selectedLayout = sanitizeObject(layoutOverride, {});
    config.layout = Object.keys(selectedLayout).length
      ? Object.assign({}, selectedLayout, { height: height || selectedLayout.height || '720px' })
      : productConfigAdminLayout(height);
    config.permissions = adminEditorPermissions(config.permissions);
    config.context = sanitizeObject(config.context, {});
    config.context.isAdmin = true;
    config.context.apdAdminEditorPermissions = true;
    clearBootLayout(config.initialState);
    clearBootPermissions(config.initialState);
    if (config.initialState && typeof config.initialState === 'object') {
      if (config.initialState.document && typeof config.initialState.document === 'object') {
        clearBootLayout(config.initialState.document);
        clearBootPermissions(config.initialState.document);
      }
    }
    return config;
  }

  function getSelectedTemplatePayloadForBuilder(root) {
    if (!root || typeof root !== 'object') {
      return null;
    }
    if (root.__apdSelectedTemplatePayload && typeof root.__apdSelectedTemplatePayload === 'object') {
      return root.__apdSelectedTemplatePayload;
    }
    var picker = root.querySelector && root.querySelector('[data-apd-template-picker="1"]');
    var select = picker && picker.querySelector ? picker.querySelector('.js-apd-template-select') : null;
    var id = select && select.value ? String(select.value) : '0';
    if (!id || id === '0') {
      return null;
    }
    return getTemplatePayloadFromPicker(picker, id);
  }

  function readTemplateMetaJson(payload, key, fallback) {
    if (!payload || !payload.meta || !Object.prototype.hasOwnProperty.call(payload.meta, key)) {
      return fallback;
    }
    return safeParse(payload.meta[key], fallback);
  }

  function configHasBootViews(config) {
    return !!(config && typeof config === 'object' && (
      payloadHasViews(config.initialState) ||
      (Array.isArray(config.views) && config.views.length > 0)
    ));
  }

  function applyProductTemplateFallbackConfig(root, config) {
    var payload = getSelectedTemplatePayloadForBuilder(root);
    if (!payload || !payload.meta || configHasBootViews(config)) {
      return config;
    }

    var templateState = readTemplateMetaJson(payload, '_apd_initial_state', null);
    var templateDocument = readTemplateMetaJson(payload, '_apd_initial_document', null);
    var templateBootState = apdResolveCanonicalInitialState(templateState, templateDocument);
    if (templateBootState && payloadHasViews(templateBootState)) {
      config.initialState = templateBootState;
      return config;
    }

    var templateViews = readTemplateMetaJson(payload, '_apd_views', []);
    var templateZones = readTemplateMetaJson(payload, '_apd_drawing_zones', []);
    if ((!Array.isArray(config.views) || !config.views.length) && Array.isArray(templateViews) && templateViews.length) {
      config.views = templateViews;
    }
    if ((!Array.isArray(config.drawingZones) || !config.drawingZones.length) && Array.isArray(templateZones) && templateZones.length) {
      config.drawingZones = templateZones;
    }

    return config;
  }


  function keepAdminPreviewCanvasControlsVisible(ui) {
    var next = sanitizeObject(ui, {});
    if (Object.prototype.hasOwnProperty.call(next, 'viewsPagination')) {
      delete next.viewsPagination;
    }
    if (Object.prototype.hasOwnProperty.call(next, 'viewsPaginationMode')) {
      delete next.viewsPaginationMode;
    }
    if (Object.prototype.hasOwnProperty.call(next, 'viewsPaginationEnabled')) {
      delete next.viewsPaginationEnabled;
    }
    if (Object.prototype.hasOwnProperty.call(next, 'historyControls')) {
      delete next.historyControls;
    }
    return next;
  }

  function readLiveAssetCatalog(root) {
    var node = root && root.querySelector ? root.querySelector('[data-apd-live-asset-catalog]') : null;
    if (!node) {
      return null;
    }
    try {
      var payload = JSON.parse(node.textContent || '{}');
      return payload && typeof payload === 'object' ? payload : null;
    } catch (error) {
      return null;
    }
  }

  function applyLiveAssetCatalog(root, config) {
    var payload = readLiveAssetCatalog(root);
    if (!payload) {
      return config;
    }

    var liveCatalog = sanitizeObject(payload.catalog, {});
    var catalog = sanitizeObject(config.catalog, {});
    ['designs', 'elements', 'colors', 'printingMethods'].forEach(function (family) {
      catalog[family] = Array.isArray(liveCatalog[family]) ? liveCatalog[family].slice() : [];
    });
    config.catalog = catalog;

    var moduleConfig = sanitizeObject(config.moduleConfig, {});
    moduleConfig.designs = Object.assign({}, sanitizeObject(moduleConfig.designs, {}), {
      items: catalog.designs.slice()
    });
    moduleConfig.shapes = Object.assign({}, sanitizeObject(moduleConfig.shapes, {}), {
      svgAssets: catalog.elements.slice()
    });
    config.moduleConfig = moduleConfig;

    config.fonts = Array.isArray(payload.fonts) ? payload.fonts.slice() : [];
    config.colors = Object.assign({}, Array.isArray(config.colors) ? {} : sanitizeObject(config.colors, {}), {
      items: catalog.colors.slice(),
      colors: catalog.colors.slice(),
      printingMethods: catalog.printingMethods.slice()
    });
    config.printingMethods = catalog.printingMethods.slice();

    return config;
  }

  function buildConfig(preview) {
    var root = preview.closest('.apd-builder-page') || preview.closest('.js-apd-studio') || document;
    var heightField = preview.querySelector('.js-apd-preview-height');
    var modeField = findField('_apd_mode', root);
    var body = document.body;
    var isProductConfigEditor = !!(
      (body && body.classList && (body.classList.contains('post-type-apd_product_config') || body.classList.contains('post-type-apd_custom_product')))
      || (root && root.getAttribute && root.getAttribute('data-apd-product-config-editor') === '1')
    );
    var parsedLayout = safeParse(findField('_apd_layout', root) && findField('_apd_layout', root).value, {});
    var runtimeConfig = sanitizeObject((window.APDAdminConfig && window.APDAdminConfig.runtimeConfig) || {}, {});
    var adminDesigner = sanitizeObject((window.APDAdminConfig && window.APDAdminConfig.adminDesigner) || {}, {});
    var adminTemplate = sanitizeObject(adminDesigner.template, {});
    var storedDocumentState = safeParse(findField('_apd_initial_document', root) && findField('_apd_initial_document', root).value, null);
    var storedFullState = safeParse(findField('_apd_initial_state', root) && findField('_apd_initial_state', root).value, null);
    var config = {
      mode: modeField ? modeField.value : 'standalone',
      theme: safeParse(findField('_apd_theme', root) && findField('_apd_theme', root).value, { appearance: 'light' }),
      permissions: safeParse(findField('_apd_permissions', root) && findField('_apd_permissions', root).value, defaultCustomerPermissions()),
      ui: safeParse(findField('_apd_ui', root) && findField('_apd_ui', root).value, {}),
      moduleConfig: safeParse(findField('_apd_modules', root) && findField('_apd_modules', root).value, {}),
      views: safeParse(findField('_apd_views', root) && findField('_apd_views', root).value, []),
      drawingZones: safeParse(findField('_apd_drawing_zones', root) && findField('_apd_drawing_zones', root).value, []),
      fonts: safeParse(findField('_apd_fonts', root) && findField('_apd_fonts', root).value, []),
      upload: (function () {
        var uploadConfig = safeParse(findField('_apd_upload', root) && findField('_apd_upload', root).value, {});
        uploadConfig = uploadConfig && typeof uploadConfig === 'object' ? uploadConfig : {};
        uploadConfig.headers = Object.assign({}, uploadConfig.headers || {}, {
          'X-WP-Nonce': (window.APDAdminConfig && window.APDAdminConfig.restNonce) || ''
        });
        return Object.assign({
          strategy: 'remote',
          endpoint: (window.APDAdminConfig && window.APDAdminConfig.restUploadUrl) || '',
          method: 'POST',
          headers: {
            'X-WP-Nonce': (window.APDAdminConfig && window.APDAdminConfig.restNonce) || ''
          }
        }, uploadConfig);
      })(),
      license: sanitizeObject(runtimeConfig.license, {}),
      ai: sanitizeObject(runtimeConfig.ai, {}),
      exports: sanitizeObject(runtimeConfig.exports, {}),
      integrations: Object.assign({}, ((window.APDAdminConfig && window.APDAdminConfig.integrations) || {}), {
        selectionMenu: safeParse(findField('_apd_selection_menu', root) && findField('_apd_selection_menu', root).value, {}),
        frontBehavior: safeParse(findField('_apd_front_behavior', root) && findField('_apd_front_behavior', root).value, {}),
        mobileBehavior: safeParse(findField('_apd_mobile_behavior', root) && findField('_apd_mobile_behavior', root).value, {})
      }),
      pricing: {
        enabled: true,
        rules: safeParse(findField('_apd_pricing_rules', root) && findField('_apd_pricing_rules', root).value, [])
      },
      i18n: window.APDAdminConfig && window.APDAdminConfig.jsI18n ? window.APDAdminConfig.jsI18n : {},
      initialState: apdResolveCanonicalInitialState(storedFullState, storedDocumentState),
      context: {
        configId: preview.getAttribute('data-post-id') || root.getAttribute('data-config-id') || null,
        isWooCommerce: !!window.WooCommerce,
        isAdmin: true
      }
    };

    if (isProductConfigEditor) {
      applyProductTemplateFallbackConfig(root, config);
      if (adminDesigner.theme && typeof adminDesigner.theme === 'object') {
        config.theme = sanitizeObject(adminDesigner.theme, config.theme);
      }
      if (adminTemplate.configured === true) {
        config.moduleConfig = Object.assign({}, config.moduleConfig, sanitizeObject(adminTemplate.moduleConfig, {}));
        config.ui = Object.assign({}, config.ui, sanitizeObject(adminTemplate.ui, {}));
        config.integrations.selectionMenu = Object.assign(
          {},
          sanitizeObject(config.integrations.selectionMenu, {}),
          sanitizeObject(adminTemplate.selectionMenu, {})
        );
      }
    }
    config.layout = Object.assign({ height: (heightField && heightField.value) || '720px' }, sanitizeObject(parsedLayout, {}));
    if (isProductConfigEditor) {
      forceProductConfigAdminLayout(
        config,
        (heightField && heightField.value) || '720px',
        adminTemplate.configured === true ? adminTemplate.layout : null
      );
    }
    // These selectors are frontend/customer rules. Product and template
    // builders always keep view navigation and history actions available.
    config.ui = keepAdminPreviewCanvasControlsVisible(config.ui);
    if (isProductConfigEditor) {
      config.permissions = adminEditorPermissions(config.permissions);
      config.context = sanitizeObject(config.context, {});
      config.context.isAdmin = true;
      config.context.apdAdminEditorPermissions = true;
    }
    var colorsPayload = safeParse(findField('_apd_colors', root) && findField('_apd_colors', root).value, []);
    if (Array.isArray(colorsPayload)) {
      config.colors = colorsPayload;
    } else if (colorsPayload && typeof colorsPayload === 'object') {
      config.colors = colorsPayload;
      config.printingMethods = Array.isArray(colorsPayload.printingMethods) ? colorsPayload.printingMethods : [];
    }
    config = applyLiveAssetCatalog(root, config);
    return apdEnsureRuntimeUploadConfig(sanitizePreviewConfig(config), {
      strategy: 'remote',
      endpoint: (window.APDAdminConfig && window.APDAdminConfig.restUploadUrl) || '',
      method: 'POST',
      fieldName: 'file',
      source: 'admin',
      headers: {
        'X-WP-Nonce': (window.APDAdminConfig && window.APDAdminConfig.restNonce) || ''
      }
    });
  }

  function setStatus(preview, text) {
    var status = preview.querySelector('.js-apd-preview-status');
    if (status) {
      status.textContent = text;
    }
  }


  function mountPreview(preview) {
    var stage = preview.querySelector('.apd-preview-stage');
    if (!stage) {
      return;
    }
    stage.innerHTML = '';

    var config = buildConfig(preview);
    appendDebugLog('mountPreview config', {
      document: summarizeDesign(config && config.initialState ? config.initialState.document : null),
      state: summarizeDesign(config && config.initialState ? config.initialState : null)
    }, preview.closest('.apd-builder-page') || document);
    var createAsukaDesigner = getCreateFn();
    if (!createAsukaDesigner) {
      stage.innerHTML = '<div class="apd-preview-empty">' + adminLabel('previewMountLoading', 'AsukaDesigner is loading in this admin preview…') + '</div>';
      setStatus(preview, adminLabel('previewLoadingStatus', 'Preview loading'));
      window.setTimeout(function () { mountPreview(preview); }, 250);
      return;
    }

    try {
      debugI18nPayload('admin-app:mountPreview:beforeCreate', config.i18n, config.i18n, config.layout || {});
      var instance = createAsukaDesigner('#' + stage.id, config);
      apdKeepRuntimeUploadConfig(instance || null, config && config.moduleConfig ? config.moduleConfig.upload : null);
      window.__APD_ADMIN_LAST_DESIGNER_INSTANCE = instance || null;
      installRenderedMenuDebug(preview, instance || null, config.i18n || {}, 'admin-app:mountPreview');
      appendDebugLog('mountPreview success', { hasInstance: !!instance }, preview.closest('.apd-builder-page') || document);
      preview.__apdInstance = instance || null;
      installTemplateLayoutAutosync(preview, instance || null);
      setStatus(preview, adminLabel('previewRefreshedStatus', 'Preview refreshed'));
    } catch (error) {
      console.error('[APD] Admin preview mount failed.', error);
      appendDebugLog('mountPreview error', { message: error && error.message ? error.message : String(error) }, preview.closest('.apd-builder-page') || document);
      stage.innerHTML = '<div class="apd-preview-error">' + adminLabel('previewMountError', 'The preview could not be mounted with the current configuration. Check your JSON blocks and try again.') + '</div>';
      setStatus(preview, adminLabel('previewErrorStatus', 'Preview error'));
    }
  }

  function initPreview(scope) {
    scope.querySelectorAll('.js-apd-live-preview').forEach(function (preview) {
      var refresh = preview.querySelector('.js-apd-refresh-preview');
      var apply = preview.querySelector('.js-apd-apply-starter');
      if (refresh) {
        refresh.addEventListener('click', function () { mountPreview(preview); });
      }
      if (apply) {
        apply.addEventListener('click', function () {
          var preset = preview.querySelector('.js-apd-starter-template');
          applyStarterValue(preset ? preset.value : 'blank', preview.closest('.apd-config-layout') || document);
          mountPreview(preview);
        });
      }
      window.setTimeout(function () { mountPreview(preview); }, 120);
    });
  }

  function exportDesignFromInstance(instance, root) {
    if (!instance || !instance.util || !instance.util.state || typeof instance.util.state.exportPart !== 'function') {
      return null;
    }
    try {
      var payload = instance.util.state.exportPart('document');
      if (apdIsCanonicalStateEnvelope(payload)) {
        appendDebugLog('builder exportDocument success', summarizeDesign(payload), root || document);
        return payload;
      }
    } catch (error) {
      appendDebugLog('builder exportDocument error', { message: error && error.message ? error.message : String(error) }, root || document);
    }
    appendDebugLog('builder exportDocument failed', {}, root || document);
    return null;
  }

  function exportStateFromInstance(instance, root) {
    if (!instance || !instance.util || !instance.util.state || typeof instance.util.state.exportFull !== 'function') {
      return null;
    }
    try {
      var payload = instance.util.state.exportFull();
      if (apdIsCanonicalStateEnvelope(payload)) {
        appendDebugLog('builder exportState success', summarizeDesign(payload.document || payload), root || document);
        return payload;
      }
    } catch (error) {
      appendDebugLog('builder exportState error', { message: error && error.message ? error.message : String(error) }, root || document);
    }
    appendDebugLog('builder exportState failed', {}, root || document);
    return null;
  }

  function extractRuntimeLayoutFromState(state) {
    if (!state || typeof state !== 'object') {
      return null;
    }
    if (state.blocksByZone && typeof state.blocksByZone === 'object') {
      return state;
    }
    if (state.layout && typeof state.layout === 'object' && state.layout.blocksByZone && typeof state.layout.blocksByZone === 'object') {
      return state.layout;
    }
    if (state.ui && state.ui.layout && typeof state.ui.layout === 'object' && state.ui.layout.blocksByZone && typeof state.ui.layout.blocksByZone === 'object') {
      return state.ui.layout;
    }
    if (state.legacy && state.legacy.state && state.legacy.state.layout && typeof state.legacy.state.layout === 'object' && state.legacy.state.layout.blocksByZone && typeof state.legacy.state.layout.blocksByZone === 'object') {
      return state.legacy.state.layout;
    }
    return null;
  }

  function extractRuntimeLayoutFromStore(instance) {
    if (!instance) {
      return null;
    }
    try {
      var storeState = null;
      if (instance.util && typeof instance.util.getState === 'function') {
        storeState = instance.util.getState();
      }
      var layout = extractRuntimeLayoutFromState(storeState);
      if (layout) {
        return layout;
      }
      return null;
    } catch (error) {
      return null;
    }
  }

  function isTemplateEditorPage() {
    return !!(document.body && document.body.classList && document.body.classList.contains('post-type-apd_template'));
  }

  function findStructuredTemplateModulesPanel(page) {
    var scopes = [];
    var pushScope = function (scope) {
      if (!scope || !scope.querySelector || scopes.indexOf(scope) !== -1) {
        return;
      }
      scopes.push(scope);
    };
    pushScope(page);
    try {
      if (page && page.closest) {
        pushScope(page.closest('form#post'));
        pushScope(page.closest('.apd-builder-page'));
      }
    } catch (error) {}
    pushScope(document);

    for (var i = 0; i < scopes.length; i += 1) {
      var panel = scopes[i].querySelector('[data-apd-template-modules="1"]');
      if (panel && panel.querySelector('[data-apd-template-module-item]')) {
        return panel;
      }
    }
    return null;
  }

  function normalizeTemplateModuleKey(key) {
    var value = String(key || '').trim();
    if (!value) {
      return '';
    }
    var lower = value.toLowerCase();
    if (lower === 'images') {
      return 'image';
    }
    if (lower === 'fixedslots' || lower === 'fixed-slots' || lower === 'fixed_slots') {
      return 'marker';
    }
    if (lower === 'variations' || lower === 'elementvariationpicker' || lower === 'element-variation-picker' || lower === 'element_variation_picker') {
      return 'element-variation-picker';
    }
    if (lower === 'drawingareas' || lower === 'drawing-areas' || lower === 'drawing_areas') {
      return 'drawingAreas';
    }
    if (lower === 'pricingrules' || lower === 'pricing-rules' || lower === 'pricing_rules') {
      return 'pricingRules';
    }
    if (lower === 'uploadimage' || lower === 'upload-image' || lower === 'upload_image' || lower === 'image-upload') {
      return 'image-upload';
    }
    if (lower === 'pixabayimage' || lower === 'pixabay-image' || lower === 'pixabay_image' || lower === 'image-pixabay') {
      return 'image-pixabay';
    }
    if (lower === 'pexelsimage' || lower === 'pexels-image' || lower === 'pexels_image' || lower === 'image-pexels') {
      return 'image-pexels';
    }
    if (lower === 'facebookimage' || lower === 'facebook-image' || lower === 'facebook_image' || lower === 'image-facebook') {
      return 'image-facebook';
    }
    if (lower === 'qrcodeimage' || lower === 'qrcode-image' || lower === 'qrcode_image' || lower === 'image-qrcode') {
      return 'image-qrcode';
    }
    if (lower === 'aitexttoimage' || lower === 'ai-text-to-image' || lower === 'ai_text_to_image' || lower === 'image-ai-text-to-image') {
      return 'image-ai-text-to-image';
    }
    if (lower === 'elementeditor' || lower === 'element-editor' || lower === 'element_editor') {
      return 'element-editor';
    }
    if (lower === 'productparameters' || lower === 'product-parameters' || lower === 'product_parameters') {
      return 'product-parameters';
    }
    if (lower === 'modeloptimizer' || lower === 'model-optimizer' || lower === 'model_optimizer') {
      return 'modelOptimizer';
    }
    if (lower === 'fontfamily' || lower === 'font-family' || lower === 'font_family') {
      return 'font-family';
    }
    if (lower === 'textformatting' || lower === 'text-formatting' || lower === 'text_formatting') {
      return 'text-formatting';
    }
    if (lower === 'textcurve' || lower === 'text-curve' || lower === 'text_curve') {
      return 'text-curve';
    }
    return value;
  }

  function subscribeToInstanceStore(instance, callback) {
    if (!instance || typeof callback !== 'function') {
      return null;
    }
    var subscribe = null;
    if (instance.util && typeof instance.util.subscribe === 'function') {
      subscribe = instance.util.subscribe;
    }
    if (!subscribe) {
      return null;
    }
    try {
      return subscribe(function (state, previousState) {
        try { callback(state, previousState); } catch (error) {}
      });
    } catch (error) {
      return null;
    }
  }

  function layoutSignatureFromState(state) {
    if (!state || typeof state !== 'object') {
      return '';
    }
    try {
      var layout = extractRuntimeLayoutFromState(state) || state;
      return JSON.stringify({
        blocksByZone: layout.blocksByZone || {},
        zoneCollapsed: layout.zoneCollapsed || {},
        zoneCollapsible: layout.zoneCollapsible || {},
        zoneType: layout.zoneType || {}
      });
    } catch (error) {
      return '';
    }
  }

  function installTemplateLayoutAutosync(preview, instance) {
    if (!preview || !instance) {
      return;
    }
    if (typeof preview.__apdTemplateLayoutUnsubscribe === 'function') {
      try { preview.__apdTemplateLayoutUnsubscribe(); } catch (error) {}
      preview.__apdTemplateLayoutUnsubscribe = null;
    }
    var page = preview.closest('form#post') || preview.closest('.apd-builder-page') || document;
    var panel = findStructuredTemplateModulesPanel(page);
    if (!panel) {
      return;
    }

    var lastSignature = '';
    var syncTimer = null;
    var delayedSyncTimer = null;
    var cleanupFns = [];
    var runSync = function () {
      syncTemplateLayoutFromRuntime(page, instance, null);
      try {
        lastSignature = layoutSignatureFromState(instance.util && typeof instance.util.getState === 'function' ? instance.util.getState() : null);
      } catch (error) {}
    };
    var schedule = function (delay) {
      if (syncTimer) {
        window.clearTimeout(syncTimer);
      }
      syncTimer = window.setTimeout(function () {
        syncTimer = null;
        runSync();
      }, typeof delay === 'number' ? delay : 40);
    };
    var scheduleAfterRuntimeInteraction = function () {
      schedule(30);
      if (delayedSyncTimer) {
        window.clearTimeout(delayedSyncTimer);
      }
      delayedSyncTimer = window.setTimeout(function () {
        delayedSyncTimer = null;
        runSync();
      }, 180);
    };

    try {
      lastSignature = layoutSignatureFromState(instance.util && typeof instance.util.getState === 'function' ? instance.util.getState() : null);
    } catch (error) {}

    var unsubscribeStore = subscribeToInstanceStore(instance, function (state) {
      var signature = layoutSignatureFromState(state);
      if (signature && signature !== lastSignature) {
        lastSignature = signature;
        schedule();
      }
    });
    if (typeof unsubscribeStore === 'function') {
      cleanupFns.push(unsubscribeStore);
    }

    // Some layout interactions are triggered from inside the runtime UI. In that
    // path the hidden JSON was refreshed, but the visible admin panel could miss
    // the update when the store subscription did not fire in time. Wrap the
    // public layout mutators and listen to preview interactions so direct
    // collapse/drag actions always resync back into the right-side controls.
    var layout = instance.layout && typeof instance.layout === 'object' ? instance.layout : null;
    if (layout) {
      ['moveBlock', 'addItem', 'removeItem', 'updateItem', 'setZoneType', 'setZoneCollapsed', 'toggleZoneCollapsed', 'setZoneCollapsible', 'setBlockCollapsed', 'toggleBlockCollapsed', 'setBlockCollapsible'].forEach(function (methodName) {
        var original = layout[methodName];
        if (typeof original !== 'function' || original.__apdTemplateRuntimeAutosyncWrapped) {
          return;
        }
        var wrapped = function () {
          var result;
          try {
            result = original.apply(this, arguments);
          } finally {
            scheduleAfterRuntimeInteraction();
          }
          if (result && typeof result.then === 'function') {
            try { result.then(scheduleAfterRuntimeInteraction, scheduleAfterRuntimeInteraction); } catch (error) {}
          }
          return result;
        };
        wrapped.__apdTemplateRuntimeAutosyncWrapped = true;
        wrapped.__apdTemplateRuntimeAutosyncOriginal = original;
        try { layout[methodName] = wrapped; } catch (error) { return; }
        cleanupFns.push(function () {
          try {
            if (layout[methodName] === wrapped) {
              layout[methodName] = original;
            }
          } catch (error) {}
        });
      });
    }

    var stage = preview.querySelector('.apd-preview-stage');
    if (stage && stage.addEventListener) {
      var interactionHandler = function () {
        scheduleAfterRuntimeInteraction();
      };
      ['pointerup', 'mouseup', 'click', 'change', 'keyup'].forEach(function (eventName) {
        try {
          stage.addEventListener(eventName, interactionHandler, true);
          cleanupFns.push(function () {
            try { stage.removeEventListener(eventName, interactionHandler, true); } catch (error) {}
          });
        } catch (error) {}
      });
    }

    preview.__apdTemplateLayoutUnsubscribe = function () {
      if (syncTimer) {
        window.clearTimeout(syncTimer);
        syncTimer = null;
      }
      if (delayedSyncTimer) {
        window.clearTimeout(delayedSyncTimer);
        delayedSyncTimer = null;
      }
      cleanupFns.forEach(function (cleanup) {
        try { cleanup(); } catch (error) {}
      });
      cleanupFns = [];
    };

    runSync();
  }

  function normalizeTemplateZoneTypeValue(raw) {
    var value = typeof raw === 'string' ? raw.trim().toLowerCase() : '';
    if (value === 'inline') {
      return 'inline';
    }
    if (value === 'panel' || value === 'panels') {
      return 'panels';
    }
    return 'default';
  }

  function mapRuntimeZoneTypeToTemplateType(runtimeType, fallbackType) {
    var value = typeof runtimeType === 'string' ? runtimeType.trim().toLowerCase() : '';
    if (value === '') {
      return normalizeTemplateZoneTypeValue(fallbackType);
    }
    return normalizeTemplateZoneTypeValue(value);
  }

  function templateLayoutItemKey(item) {
    if (!item || typeof item !== 'object') {
      return '';
    }
    return String(item.kind || item.id || item.module || '').trim();
  }

  function templateLayoutItemOrder(item, fallbackIndex) {
    if (item && typeof item === 'object') {
      var candidates = [item.order, item.index, item.position];
      for (var i = 0; i < candidates.length; i += 1) {
        var parsed = Number(candidates[i]);
        if (Number.isFinite(parsed)) {
          return parsed;
        }
      }
    }
    return fallbackIndex;
  }

  function orderTemplateLayoutItemsByExistingLayout(items, currentLayout, zone) {
    var zoneConfig = currentLayout && currentLayout[zone] && typeof currentLayout[zone] === 'object' ? currentLayout[zone] : {};
    var existingItems = Array.isArray(zoneConfig.items) ? zoneConfig.items : (Array.isArray(currentLayout && currentLayout[zone]) ? currentLayout[zone] : []);
    var orderMap = {};
    existingItems.forEach(function (item, index) {
      var key = templateLayoutItemKey(item);
      if (!key || Object.prototype.hasOwnProperty.call(orderMap, key)) {
        return;
      }
      orderMap[key] = templateLayoutItemOrder(item, index);
    });

    return (Array.isArray(items) ? items : []).map(function (item, index) {
      var key = templateLayoutItemKey(item);
      var hasSavedOrder = key && Object.prototype.hasOwnProperty.call(orderMap, key);
      return {
        item: item,
        sourceIndex: index,
        order: hasSavedOrder ? orderMap[key] : (100000 + index)
      };
    }).sort(function (a, b) {
      if (a.order !== b.order) {
        return a.order - b.order;
      }
      return a.sourceIndex - b.sourceIndex;
    }).map(function (entry, index) {
      return Object.assign({}, entry.item, { order: index });
    });
  }

  function runtimeLayoutToTemplateLayout(runtimeLayout, fallbackLayout) {
    if (!runtimeLayout || typeof runtimeLayout !== 'object' || !runtimeLayout.blocksByZone || typeof runtimeLayout.blocksByZone !== 'object') {
      return null;
    }
    var fallback = fallbackLayout && typeof fallbackLayout === 'object' ? fallbackLayout : {};
    var blocksByZone = runtimeLayout.blocksByZone || {};
    var zoneCollapsed = runtimeLayout.zoneCollapsed && typeof runtimeLayout.zoneCollapsed === 'object' ? runtimeLayout.zoneCollapsed : {};
    var zoneCollapsible = runtimeLayout.zoneCollapsible && typeof runtimeLayout.zoneCollapsible === 'object' ? runtimeLayout.zoneCollapsible : {};
    var zoneType = runtimeLayout.zoneType && typeof runtimeLayout.zoneType === 'object' ? runtimeLayout.zoneType : {};
    var fallbackItemsByKey = getTemplateLayoutItemsByKey(fallback);
    var payload = {
      height: fallback.height || '820px'
    };
    ['left', 'right', 'top', 'bottom'].forEach(function (zone) {
      var fallbackZone = fallback[zone] && typeof fallback[zone] === 'object' ? fallback[zone] : {};
      var items = [];
      (Array.isArray(blocksByZone[zone]) ? blocksByZone[zone] : []).forEach(function (item, index) {
        if (!item || typeof item !== 'object') {
          return;
        }
        var id = String(item.id || item.kind || '').trim();
        var kind = String(item.kind || item.id || '').trim();
        if (!id) id = kind;
        if (!kind) kind = id;
        if (!id || !kind) {
          return;
        }
        var fallbackItem = fallbackItemsByKey[normalizeTemplateModuleKey(kind)] || null;
        var normalizedItem = {
          id: id,
          kind: kind,
          label: String((fallbackItem && fallbackItem.label) || item.label || kind),
          icon: fallbackItem && Object.prototype.hasOwnProperty.call(fallbackItem, 'icon') ? fallbackItem.icon : (item.icon || ''),
          zone: zone,
          type: fallbackItem && fallbackItem.type ? String(fallbackItem.type) : (item.type ? String(item.type) : 'floating'),
          collapsed: !!item.collapsed,
          collapsible: Object.prototype.hasOwnProperty.call(item, 'collapsible') ? !!item.collapsible : true,
          order: index
        };
        var runtimeMountTarget = typeof item.mountTarget === 'string' ? item.mountTarget.trim() : '';
        if (runtimeMountTarget) {
          normalizedItem.mountTarget = runtimeMountTarget;
        }
        if (item.renderMode === 'inline' || item.renderMode === 'portal') {
          normalizedItem.renderMode = item.renderMode;
        }
        items.push(normalizedItem);
      });
      payload[zone] = {
        items: items,
        collapsed: Object.prototype.hasOwnProperty.call(zoneCollapsed, zone)
          ? !!zoneCollapsed[zone]
          : (Object.prototype.hasOwnProperty.call(fallbackZone, 'collapsed') ? !!fallbackZone.collapsed : zone === 'top'),
        collapsible: Object.prototype.hasOwnProperty.call(zoneCollapsible, zone)
          ? !!zoneCollapsible[zone]
          : (Object.prototype.hasOwnProperty.call(fallbackZone, 'collapsible') ? !!fallbackZone.collapsible : true),
        type: mapRuntimeZoneTypeToTemplateType(zoneType[zone], fallbackZone.type || 'default')
      };
    });
    return payload;
  }

  function mapRuntimeItemTypeToTemplateType(runtimeType, fallbackType) {
    var value = typeof runtimeType === 'string' ? runtimeType.trim().toLowerCase() : '';
    var fallback = typeof fallbackType === 'string' && fallbackType ? fallbackType : 'floating';
    if (value.indexOf('panel-') === 0 || value === 'panel' || value === 'panels') {
      return 'panel';
    }
    if (['floating', 'popover', 'modal', 'inline', 'drawer', 'overlay'].indexOf(value) !== -1) {
      return value;
    }
    return fallback;
  }

  function looksLikeTemplateIconImage(value) {
    var icon = String(value || '').trim();
    if (!icon) {
      return false;
    }
    return icon.indexOf('http://') === 0
      || icon.indexOf('https://') === 0
      || icon.indexOf('/') === 0
      || icon.indexOf('data:image/') === 0;
  }

  function setInputValueIfChanged(field, value) {
    if (!field) {
      return;
    }
    var next = String(value == null ? '' : value);
    if (String(field.value || '') !== next) {
      field.value = next;
    }
  }

  function setCheckboxValueIfChanged(field, value) {
    if (!field) {
      return;
    }
    var next = !!value;
    if (!!field.checked !== next) {
      field.checked = next;
    }
  }

  function getTemplateLayoutItemsByKey(layout) {
    var itemsByKey = {};
    if (!layout || typeof layout !== 'object') {
      return itemsByKey;
    }
    ['left', 'right', 'top', 'bottom'].forEach(function (zone) {
      var zoneConfig = layout[zone] && typeof layout[zone] === 'object' ? layout[zone] : {};
      var items = Array.isArray(zoneConfig.items) ? zoneConfig.items : (Array.isArray(layout[zone]) ? layout[zone] : []);
      items.forEach(function (item, index) {
        if (!item || typeof item !== 'object') {
          return;
        }
        var key = normalizeTemplateModuleKey(item.kind || item.id || item.module || '');
        if (!key) {
          return;
        }
        itemsByKey[key] = Object.assign({}, item, { zone: zone, order: index });
      });
    });
    return itemsByKey;
  }

  function syncTemplateModulePanelFromLayout(page, panel, layout) {
    if (!panel || !layout || typeof layout !== 'object') {
      return;
    }
    if (panel.__apdTemplateApplyingRuntimeLayout) {
      return;
    }
    panel.__apdTemplateApplyingRuntimeLayout = true;
    try {
      ['left', 'right', 'top', 'bottom'].forEach(function (zone) {
        var card = panel.querySelector('[data-apd-template-zone-card="' + zone + '"]');
        if (!card) {
          return;
        }
        var zoneConfig = layout[zone] && typeof layout[zone] === 'object' ? layout[zone] : {};
        setInputValueIfChanged(card.querySelector('[data-apd-template-zone-type]'), mapRuntimeZoneTypeToTemplateType(zoneConfig.type, 'default'));
        setCheckboxValueIfChanged(card.querySelector('[data-apd-template-zone-collapsed]'), Object.prototype.hasOwnProperty.call(zoneConfig, 'collapsed') ? !!zoneConfig.collapsed : zone === 'top');
        setCheckboxValueIfChanged(card.querySelector('[data-apd-template-zone-collapsible]'), Object.prototype.hasOwnProperty.call(zoneConfig, 'collapsible') ? !!zoneConfig.collapsible : true);
      });

      var layoutItemsByKey = getTemplateLayoutItemsByKey(layout);
      panel.querySelectorAll('[data-apd-template-module-item]').forEach(function (row) {
        var rowKey = normalizeTemplateModuleKey(row.getAttribute('data-apd-template-module-item') || '');
        var item = rowKey ? layoutItemsByKey[rowKey] : null;
        var enabledField = row.querySelector('.js-apd-template-module-enabled');
        if (!item) {
          // The live preview is capability- and license-filtered. An enabled
          // template module can therefore be absent from the runtime layout
          // without being disabled in the saved template.
          return;
        }
        setCheckboxValueIfChanged(enabledField, true);
        var isPageContent = item.renderMode === 'portal';
        setCheckboxValueIfChanged(row.querySelector('[data-apd-template-module-layout-menu]'), !isPageContent);
        setCheckboxValueIfChanged(row.querySelector('[data-apd-template-module-page-content]'), isPageContent);
        var icon = String(item.icon || '').trim();
        var iconMode = looksLikeTemplateIconImage(icon) ? 'image' : 'text';
        setInputValueIfChanged(row.querySelector('[data-apd-template-module-zone]'), item.zone || 'left');
        setInputValueIfChanged(row.querySelector('[data-apd-template-module-label-input]'), item.label || item.kind || rowKey);
        setInputValueIfChanged(row.querySelector('[data-apd-template-module-type]'), mapRuntimeItemTypeToTemplateType(item.type, 'floating'));
        setCheckboxValueIfChanged(row.querySelector('[data-apd-template-module-collapsed]'), !!item.collapsed);
        setCheckboxValueIfChanged(row.querySelector('[data-apd-template-module-collapsible]'), Object.prototype.hasOwnProperty.call(item, 'collapsible') ? !!item.collapsible : true);
        var sharedPortalSelector = getTemplateSharedPortalSelector(panel);
        var itemMountTarget = typeof item.mountTarget === 'string' ? item.mountTarget.trim() : '';
        var currentPortalModeField = row.querySelector('[data-apd-template-module-portal-mode]');
        var currentPortalMode = currentPortalModeField && currentPortalModeField.value ? String(currentPortalModeField.value) : 'internal';
        var portalMode = !itemMountTarget
          ? (currentPortalMode === 'custom' || (currentPortalMode === 'shared' && !sharedPortalSelector) ? currentPortalMode : 'internal')
          : (itemMountTarget === 'body' || itemMountTarget === 'document.body'
            ? 'body'
            : (sharedPortalSelector && itemMountTarget === sharedPortalSelector ? 'shared' : 'custom'));
        setInputValueIfChanged(currentPortalModeField, portalMode);
        setInputValueIfChanged(row.querySelector('[data-apd-template-module-mount-target]'), portalMode === 'custom' ? itemMountTarget : '');
        setInputValueIfChanged(row.querySelector('[data-apd-template-module-icon-mode]'), iconMode);
        if (iconMode === 'image') {
          setInputValueIfChanged(row.querySelector('[data-apd-template-module-icon-image]'), icon);
        } else {
          setInputValueIfChanged(row.querySelector('[data-apd-template-module-icon-text]'), icon || 'puzzle');
        }
      });
      syncTemplateIconMode(panel);

      if (page) {
        try { setJsonFieldValue(page, '_apd_permissions', buildTemplatePermissionsFromPanel(page, panel)); } catch (error) {}
        try { setJsonFieldValue(page, '_apd_selection_menu', buildTemplateSelectionMenuFromPanel(page, panel)); } catch (error) {}
      }
    } finally {
      panel.__apdTemplateApplyingRuntimeLayout = false;
    }
  }

  function syncTemplateLayoutFromRuntime(page, instance, state) {
    if (!page) {
      return null;
    }
    var panel = findStructuredTemplateModulesPanel(page);
    if (!panel) {
      return null;
    }
    var layoutField = findField('_apd_layout', page);
    if (!layoutField) {
      return null;
    }
    var fallbackLayout = safeParse(layoutField.value, {});
    var runtimeLayout = extractRuntimeLayoutFromStore(instance) || extractRuntimeLayoutFromState(state);
    var layout = runtimeLayoutToTemplateLayout(runtimeLayout, fallbackLayout);
    if (!layout) {
      return null;
    }
    if (Object.keys(getTemplateLayoutItemsByKey(layout)).length === 0 && Object.keys(getTemplateLayoutItemsByKey(fallbackLayout)).length > 0) {
      appendDebugLog('builder template runtime layout skipped empty transient sync', {}, page);
      return null;
    }
    layoutField.value = JSON.stringify(layout, null, 2);
    layoutField.dispatchEvent(new Event('input', { bubbles: true }));
    syncTemplateModulePanelFromLayout(page, panel, layout);
    var preview = page.querySelector ? page.querySelector('.js-apd-live-preview') : null;
    if (preview) {
      var nextItems = collectTemplateLayoutState(panel);
      var nextMap = {};
      nextItems.forEach(function (item) { nextMap[item.key] = item; });
      preview.__apdTemplateModuleStateMap = nextMap;
      preview.__apdTemplateZoneSettings = readTemplateZoneSettings(panel);
      preview.__apdTemplateSelectionMenu = buildTemplateSelectionMenuFromPanel(page, panel);
    }
    appendDebugLog('builder template runtime layout synchronized', {
      left: layout.left && layout.left.items ? layout.left.items.map(function (item) { return item.kind; }) : [],
      right: layout.right && layout.right.items ? layout.right.items.map(function (item) { return item.kind; }) : [],
      top: layout.top && layout.top.items ? layout.top.items.map(function (item) { return item.kind; }) : [],
      bottom: layout.bottom && layout.bottom.items ? layout.bottom.items.map(function (item) { return item.kind; }) : []
    }, page);
    return layout;
  }

  function writeBuilderJsonField(field, value, page, label) {
    if (!field) {
      return false;
    }
    try {
      var json = JSON.stringify(value, null, 2);
      // Validate the exact string written into the hidden textarea. This catches
      // element payloads that would otherwise make the following WordPress save
      // keep the previous meta value.
      JSON.parse(json || 'null');
      field.value = json;
      field.dispatchEvent(new Event('input', { bubbles: true }));
      field.dispatchEvent(new Event('change', { bubbles: true }));
      appendDebugLog('syncBuilderPage:json-field-written', {
        field: label || field.name || '',
        length: json.length,
        escapedQuotes: (json.match(/\\"/g) || []).length,
        backslashes: (json.match(/\\/g) || []).length,
        validJson: true
      }, page || document);
      return true;
    } catch (error) {
      appendDebugLog('syncBuilderPage:json-field-error', {
        field: label || field.name || '',
        message: error && error.message ? error.message : String(error),
        valueType: value === null ? 'null' : typeof value
      }, page || document);
      return false;
    }
  }

  function syncBuilderPage(page, reason) {
    if (!page) {
      appendDebugLog('syncBuilderPage:missing-page', { reason: reason || '' }, document);
      return null;
    }
    var templatePanel = findStructuredTemplateModulesPanel(page);
    var preserveStructuredTemplateLayout = !!(
      templatePanel
      && (templatePanel.__apdTemplateSettingsDirty || templatePanel.__apdTemplateSyncTimer)
    ) || !!page.__apdPreserveStructuredTemplateLayout;
    if (templatePanel && preserveStructuredTemplateLayout) {
      page.__apdPreserveStructuredTemplateLayout = true;
      flushTemplateModuleSync(
        templatePanel.closest('form#post') || document,
        templatePanel,
        false
      );
      window.setTimeout(function () {
        page.__apdPreserveStructuredTemplateLayout = false;
        templatePanel.__apdTemplateSettingsDirty = false;
      }, 0);
    }
    var preview = page.classList && page.classList.contains('js-apd-live-preview') ? page : page.querySelector('.js-apd-live-preview');
    var instance = preview && preview.__apdInstance ? preview.__apdInstance : null;
    appendDebugLog('syncBuilderPage:start', {
      reason: reason || '',
      previewOnPage: !!preview,
      hasInstance: !!instance,
      beforeFields: summarizeBuilderFields(page)
    }, page);
    var design = exportDesignFromInstance(instance, page);
    var state = exportStateFromInstance(instance, page);
    var initialField = findField('_apd_initial_document', page);
    var stateField = findField('_apd_initial_state', page);
    var viewsField = findField('_apd_views', page);
    var zonesField = findField('_apd_drawing_zones', page);

    var drawingZonesExport = apdCollectDrawingZonesForSave(design, state);
    if (!preserveStructuredTemplateLayout) {
      syncTemplateLayoutFromRuntime(page, instance, state);
    }
    if (design && initialField) {
      writeBuilderJsonField(initialField, design, page, '_apd_initial_document');
    }
    if (state && stateField) {
      writeBuilderJsonField(stateField, state, page, '_apd_initial_state');
    }
    var exportedViews = apdGetPayloadViews(design);
    if (design && exportedViews.length && viewsField) {
      writeBuilderJsonField(viewsField, exportedViews, page, '_apd_views');
    }
    if (zonesField && drawingZonesExport && drawingZonesExport.known) {
      writeBuilderJsonField(zonesField, drawingZonesExport.zones, page, '_apd_drawing_zones');
    }
    appendDebugLog('syncBuilderPage:after-field-write', {
      reason: reason || '',
      exportedDocument: summarizeDesign(design),
      exportedStateDocument: summarizeDesign(state && typeof state === 'object' ? state.document : state),
      exportedState: summarizeDesign(state),
      afterFields: summarizeBuilderFields(page)
    }, page);
    return { design: design, state: state };
  }

  function initBuilderPages(scope) {
    scope.querySelectorAll('.apd-builder-page').forEach(function (page) {
      var preview = page.querySelector('.js-apd-live-preview');
      var refresh = page.querySelector('.js-apd-refresh-preview');
      var form = page.closest('form#post') || document.querySelector('form#post');
      if (refresh && preview) {
        refresh.addEventListener('click', function () { mountPreview(preview); });
      }
      if (form) {
        form.addEventListener('submit', function () {
          appendDebugLog('form-submit:builder-page', { fieldsBeforeSubmit: summarizeBuilderFields(page) }, page);
          syncBuilderPage(page, 'form-submit');
        });
      }
      // WordPress renders its Publish/Save controls outside the metabox that
      // contains `.apd-builder-page`. Bind against the parent post form so a
      // native `form.submit()` path cannot bypass the canvas export.
      (form || page).querySelectorAll('#publish, #save-post').forEach(function (button) {
        button.addEventListener('click', function () {
          appendDebugLog('save-click:builder-page', { buttonId: button.id || '', fieldsBeforeClick: summarizeBuilderFields(page) }, page);
          syncBuilderPage(page, button.id || 'save-click');
        });
      });
    });
  }


  function initStudio(scope) {
    scope.querySelectorAll('.js-apd-studio').forEach(function (studio) {
      var syncButton = studio.querySelector('[data-apd-sync-studio]');
      var captureButton = studio.querySelector('[data-apd-capture-document]');
      var presetButton = studio.querySelector('[data-apd-apply-studio-preset]');
      var openJsonButton = studio.querySelector('[data-apd-open-json-tab]');
      var presetSelect = studio.querySelector('[data-apd-studio-preset]');
      var shell = studio.querySelector('[data-apd-shell]');
      var tabs = studio.querySelector('.js-apd-tabs');
      var initialDocumentField = findField('_apd_initial_document', studio);

      if (presetButton && presetSelect) {
        presetButton.addEventListener('click', function () {
          applyStarterValue(presetSelect.value || 'blank', studio);
          if (window.APDRuntimeBridge && shell) {
            window.APDRuntimeBridge.mountAll();
          }
          activateTab(tabs, 'experience');
        });
      }

      if (openJsonButton) {
        openJsonButton.addEventListener('click', function () {
          activateTab(tabs, 'advanced');
        });
      }

      if (syncButton && shell) {
        syncButton.addEventListener('click', function () {
          if (!window.APDRuntimeBridge || typeof window.APDRuntimeBridge.syncShell !== 'function') {
            return;
          }
          var payload = window.APDRuntimeBridge.syncShell(shell);
        appendDebugLog('studio sync payload', summarizeDesign(payload && payload.design), studio);
          if (payload && payload.design && initialDocumentField && !String(initialDocumentField.value || '').trim()) {
            initialDocumentField.value = JSON.stringify(payload.design, null, 2);
            initialDocumentField.dispatchEvent(new Event('input', { bubbles: true }));
          }
        });
      }

      if (captureButton && shell && initialDocumentField) {
        captureButton.addEventListener('click', function () {
          if (!window.APDRuntimeBridge || typeof window.APDRuntimeBridge.syncShell !== 'function') {
            return;
          }
          var payload = window.APDRuntimeBridge.syncShell(shell);
        appendDebugLog('studio sync payload', summarizeDesign(payload && payload.design), studio);
          initialDocumentField.value = JSON.stringify((payload && payload.design) || {}, null, 2);
          initialDocumentField.dispatchEvent(new Event('input', { bubbles: true }));
          activateTab(tabs, 'canvas');
        });
      }
    });
  }





  function getCollectionEditor(collection) {
    return collection ? collection.querySelector('[data-apd-collection-editor]') : null;
  }

  function getCollectionEditorBody(collection) {
    var editor = getCollectionEditor(collection);
    return editor ? editor.querySelector('[data-apd-collection-editor-body]') : null;
  }

  function getRowBody(row) {
    return row ? row.querySelector('[data-apd-row-body]') : null;
  }

  function closeCollectionEditor(collection) {
    if (!collection) {
      return;
    }
    var editor = getCollectionEditor(collection);
    var bodyHost = getCollectionEditorBody(collection);
    var activeRow = collection.querySelector('.apd-collection-row.is-editing');
    if (activeRow && bodyHost) {
      var body = bodyHost.querySelector('[data-apd-row-body]');
      if (body) {
        activeRow.appendChild(body);
        body.hidden = true;
      }
      activeRow.classList.remove('is-editing');
    }
    if (editor) {
      editor.hidden = true;
    }
  }

  function openCollectionEditor(row) {
    if (!row) {
      return;
    }
    var collection = row.closest('[data-apd-collection]');
    if (!collection) {
      return;
    }
    closeCollectionEditor(collection);
    var editor = getCollectionEditor(collection);
    var bodyHost = getCollectionEditorBody(collection);
    var body = getRowBody(row);
    if (!editor || !bodyHost || !body) {
      return;
    }
    var title = editor.querySelector('[data-apd-editor-title]');
    var subtitle = editor.querySelector('[data-apd-editor-subtitle]');
    var label = getRowText(row, ['label', 'family', 'id']) || adminLabel('newItem', 'New item');
    var category = getRowText(row, ['category', 'group', 'source']) || adminLabel('updateSelectedEntrySavePage', 'Update the selected entry, then save the page.');
    if (title) {
      title.textContent = label;
    }
    if (subtitle) {
      subtitle.textContent = category;
    }
    row.classList.add('is-editing');
    editor.hidden = false;
    body.hidden = false;
    bodyHost.appendChild(body);
    if (typeof editor.scrollIntoView === 'function') {
      editor.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
    }
  }

  function getCollectionRows(collection) {
    return collection ? Array.prototype.slice.call(collection.querySelectorAll('.apd-collection-row')) : [];
  }

  function getRowField(row, name) {
    return row ? row.querySelector('[data-apd-field="' + name + '"]') : null;
  }

  function getFieldString(field) {
    if (!field) {
      return '';
    }
    if (field.type === 'checkbox') {
      return field.checked ? '1' : '';
    }
    return String(field.value || '').trim();
  }

  function getRowText(row, names) {
    for (var i = 0; i < names.length; i += 1) {
      var value = getFieldString(getRowField(row, names[i]));
      if (value) {
        return value;
      }
    }
    return '';
  }

  function buildShapePreview(row) {
    var svgPath = getRowText(row, ['svgPath']);
    var viewBox = getRowText(row, ['viewBox']) || '0 0 100 100';
    var fill = getRowText(row, ['fill']) || '#111827';
    var stroke = getRowText(row, ['stroke']) || 'none';
    if (!svgPath) {
      return '';
    }
    return '<svg viewBox="' + viewBox.replace(/"/g, '&quot;') + '" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><path d="' + svgPath.replace(/"/g, '&quot;') + '" fill="' + fill.replace(/"/g, '&quot;') + '" stroke="' + stroke.replace(/"/g, '&quot;') + '" /></svg>';
  }

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

  function refreshRowPreview(row) {
    if (!row) {
      return;
    }
    var preview = row.querySelector('[data-apd-asset-preview]');
    var title = row.querySelector('[data-apd-row-title]');
    var subtitle = row.querySelector('[data-apd-row-subtitle]');
    var collection = row.closest('[data-apd-collection]');
    var key = collection ? collection.getAttribute('data-apd-collection') : '';
    var label = getRowText(row, ['label', 'family', 'id']) || adminLabel('untitledItem', 'Untitled item');
    var category = getRowText(row, ['category', 'group', 'source']);
    var source = getRowText(row, ['url', 'previewUrl', 'sourceUrl', 'fileUrl', 'cssUrl']);
    var colorValue = getRowText(row, ['value', 'fill']);

    if (title) {
      title.textContent = label;
    }
    if (subtitle) {
      subtitle.textContent = category || source || '';
    }

    row.dataset.apdSearch = [
      getRowText(row, ['id']),
      label,
      category,
      source,
      getRowText(row, ['description']),
      getRowText(row, ['weights']),
      getRowText(row, ['colors'])
    ].join(' ').toLowerCase();
    row.dataset.apdCategory = category.toLowerCase();

    var swatch = row.querySelector('[data-apd-color-swatch]');
    if (swatch) {
      swatch.style.background = colorValue || '#ffffff';
    }

    if (!preview) {
      return;
    }

    if (key === 'colors') {
      preview.innerHTML = '<span class="apd-asset-preview__swatch" style="background:' + escapeHtml(colorValue || '#ffffff') + '"></span><em>' + escapeHtml(colorValue || '#ffffff') + '</em>';
      return;
    }

    if (key === 'fonts') {
      var family = getRowText(row, ['family']) || label;
      preview.innerHTML = '<div class="apd-font-preview" style="font-family:' + escapeHtml(family) + ', sans-serif;">Ag</div><em>' + escapeHtml(family) + '</em>';
      return;
    }

    if (key === 'shapes') {
      var shapeHtml = buildShapePreview(row);
      if (shapeHtml) {
        preview.innerHTML = '<div class="apd-shape-preview">' + shapeHtml + '</div>';
        return;
      }
    }

    if (source && /^(https?:|data:image|\/)/i.test(source)) {
      preview.innerHTML = '<img src="' + escapeHtml(source) + '" alt="" loading="lazy">';
      return;
    }

    preview.innerHTML = '<span>' + (((window.APDAdminConfig || {}).labels || {}).emptyPreview || 'No preview yet') + '</span>';
  }

  function refreshCollectionCategories(collection) {
    var container = collection.querySelector('[data-apd-category-filters]');
    if (!container) {
      return;
    }
    var rows = getCollectionRows(collection);
    var categories = [];
    rows.forEach(function (row) {
      var category = String(row.dataset.apdCategory || '').trim();
      if (category && categories.indexOf(category) === -1) {
        categories.push(category);
      }
    });
    categories.sort();
    var active = collection.dataset.apdActiveCategory || '';
    var labels = ((window.APDAdminConfig || {}).labels || {});
    var html = '<button type="button" class="button button-secondary ' + (!active ? 'is-active' : '') + '" data-apd-category="">' + escapeHtml(labels.allCategories || 'All categories') + '</button>';
    categories.forEach(function (category) {
      html += '<button type="button" class="button button-secondary ' + (active === category ? 'is-active' : '') + '" data-apd-category="' + escapeHtml(category) + '">' + escapeHtml(category) + '</button>';
    });
    container.innerHTML = html;
  }

  function applyCollectionFilters(collection) {
    var searchField = collection.querySelector('[data-apd-collection-search]');
    var search = String(searchField && searchField.value || '').trim().toLowerCase();
    var activeCategory = String(collection.dataset.apdActiveCategory || '').trim().toLowerCase();
    getCollectionRows(collection).forEach(function (row) {
      var haystack = row.dataset.apdSearch || '';
      var category = row.dataset.apdCategory || '';
      var matchesSearch = !search || haystack.indexOf(search) !== -1;
      var matchesCategory = !activeCategory || category === activeCategory;
      row.hidden = !(matchesSearch && matchesCategory);
    });
  }


  function rowHasMeaningfulData(row) {
    if (!row) {
      return false;
    }
    var fields = row.querySelectorAll('[data-apd-field]');
    for (var i = 0; i < fields.length; i += 1) {
      var field = fields[i];
      if (!field) {
        continue;
      }
      if (field.type === 'checkbox') {
        if (field.checked) {
          return true;
        }
        continue;
      }
      if (String(field.value || '').trim() !== '') {
        return true;
      }
    }
    return false;
  }

  function pruneEmptyRows(collection) {
    if (!collection) {
      return;
    }
    getCollectionRows(collection).forEach(function (row) {
      if (!rowHasMeaningfulData(row)) {
        if (row.classList.contains('is-editing')) {
          closeCollectionEditor(collection);
        }
        row.remove();
      }
    });
    renumberCollection(collection);
  }

  function syncCollection(collection) {
    getCollectionRows(collection).forEach(refreshRowPreview);
    refreshCollectionCategories(collection);
    applyCollectionFilters(collection);
  }

  function openMediaLibrary(button) {
    if (!window.wp || !window.wp.media) {
      return;
    }
    var fieldWrap = button.closest('.apd-media-field');
    var label = button.closest('.apd-field');
    var input = fieldWrap ? fieldWrap.querySelector('[data-apd-field]') : null;
    if (!input) {
      input = label ? label.querySelector('[data-apd-field]') : null;
    }
    if (!input) {
      return;
    }
    var mediaType = button.getAttribute('data-apd-media-type') || button.getAttribute('data-media-type') || 'file';
    var labels = ((window.APDAdminConfig || {}).labels || {});
    var frame = window.wp.media({
      title: mediaType === 'image' ? (labels.mediaFrameTitleImage || 'Choose an image') : (labels.mediaFrameTitleFile || 'Choose a file'),
      button: { text: labels.mediaFrameButton || 'Use this file' },
      multiple: false,
      library: mediaType === 'image' ? { type: 'image' } : {}
    });
    frame.on('select', function () {
      var attachment = frame.state().get('selection').first();
      var data = attachment ? attachment.toJSON() : null;
      if (!data) {
        return;
      }
      input.value = data.url || '';
      input.dispatchEvent(new Event('input', { bubbles: true }));
      var row = input.closest('.apd-collection-row');
      var collection = input.closest('[data-apd-collection]');
      refreshRowPreview(row);
      if (collection) {
        syncCollection(collection);
      }
    });
    frame.open();
  }

  function clearMediaField(button) {
    var fieldWrap = button.closest('.apd-media-field');
    var label = button.closest('.apd-field');
    var input = fieldWrap ? fieldWrap.querySelector('[data-apd-field]') : null;
    if (!input) {
      input = label ? label.querySelector('[data-apd-field]') : null;
    }
    if (!input) {
      return;
    }
    input.value = '';
    input.dispatchEvent(new Event('input', { bubbles: true }));
    var row = input.closest('.apd-collection-row');
    var collection = input.closest('[data-apd-collection]');
    refreshRowPreview(row);
    if (collection) {
      syncCollection(collection);
    }
  }

  function duplicateCollectionRow(row) {
    if (!row || !row.parentElement) {
      return;
    }
    var collection = row.closest('[data-apd-collection]');
    if (row.classList.contains('is-editing') && collection) {
      closeCollectionEditor(collection);
    }
    var clone = row.cloneNode(true);
    clone.querySelectorAll('input, textarea, select').forEach(function (field) {
      var name = field.getAttribute('name') || '';
      name = name.replace(/\[(\d+)\](?=\[[^\]]+\]$)/, function (_, index) { return '[' + String(parseInt(index, 10) + 1) + ']'; });
      field.setAttribute('name', name);
      if (field.type === 'checkbox') {
        field.checked = field.checked;
      } else {
        field.value = field.value;
      }
    });
    row.parentElement.insertBefore(clone, row.nextSibling);
    if (collection) {
      renumberCollection(collection);
      syncCollection(collection);
      openCollectionEditor(clone);
    }
  }

  function renumberCollection(collection) {
    getCollectionRows(collection).forEach(function (row, index) {
      var chip = row.querySelector('.apd-chip');
      if (chip) {
        chip.textContent = adminLabel('itemN', 'Item {n}', { n: index + 1 });
      }
      row.querySelectorAll('input[name], textarea[name], select[name]').forEach(function (field) {
        field.name = String(field.name).replace(/\[[0-9]+\](?=\[[^\]]+\]$)/, '[' + String(index) + ']');
      });
    });
  }

  function initCollections(scope) {
    scope.querySelectorAll('[data-apd-collection]').forEach(function (collection) {
      pruneEmptyRows(collection);
      syncCollection(collection);
      var searchField = collection.querySelector('[data-apd-collection-search]');
      if (searchField) {
        searchField.addEventListener('input', function () { applyCollectionFilters(collection); });
      }
    });

    scope.querySelectorAll('[data-apd-add-row]').forEach(function (button) {
      button.addEventListener('click', function () {
        var key = button.getAttribute('data-apd-add-row');
        var collection = scope.querySelector('[data-apd-collection="' + key + '"]');
        if (!collection) {
          return;
        }
        var rows = collection.querySelector('[data-apd-collection-list]');
        var template = collection.querySelector('[data-apd-row-template="' + key + '"]');
        if (!rows || !template) {
          return;
        }
        var index = rows.querySelectorAll('.apd-collection-row').length;
        var html = (template.innerHTML || '').replace(/__INDEX__/g, String(index));
        rows.insertAdjacentHTML('beforeend', html);
        var newRow = rows.querySelectorAll('.apd-collection-row')[index] || rows.lastElementChild;
        syncCollection(collection);
        openCollectionEditor(newRow);
      });
    });

    scope.addEventListener('input', function (event) {
      var field = event.target.closest('[data-apd-field]');
      if (!field) {
        return;
      }
      var row = field.closest('.apd-collection-row');
      var collection = field.closest('[data-apd-collection]');
      refreshRowPreview(row);
      if (row && row.classList.contains('is-editing')) {
        openCollectionEditor(row);
      }
      if (collection && (field.getAttribute('data-apd-field') === 'category' || field.getAttribute('data-apd-field') === 'group' || field.getAttribute('data-apd-field') === 'label' || field.getAttribute('data-apd-field') === 'family' || field.getAttribute('data-apd-field') === 'id' || field.getAttribute('data-apd-field') === 'source')) {
        syncCollection(collection);
      }
    });

    scope.addEventListener('click', function (event) {
      var mediaOpen = event.target.closest('[data-apd-media-open], .js-apd-media-pick');
      if (mediaOpen) {
        event.preventDefault();
        openMediaLibrary(mediaOpen);
        return;
      }
      var mediaClear = event.target.closest('[data-apd-media-clear], .js-apd-media-clear');
      if (mediaClear) {
        event.preventDefault();
        clearMediaField(mediaClear);
        return;
      }
      var edit = event.target.closest('[data-apd-row-edit]');
      if (edit) {
        event.preventDefault();
        openCollectionEditor(edit.closest('.apd-collection-row'));
        return;
      }
      var editorClose = event.target.closest('[data-apd-editor-close]');
      if (editorClose) {
        event.preventDefault();
        closeCollectionEditor(editorClose.closest('[data-apd-collection]'));
        return;
      }
      var duplicate = event.target.closest('[data-apd-row-duplicate]');
      if (duplicate) {
        event.preventDefault();
        duplicateCollectionRow(duplicate.closest('.apd-collection-row'));
        return;
      }
      var category = event.target.closest('[data-apd-category]');
      if (category) {
        var categoryFilters = category.closest('[data-apd-category-filters]');
        var collection = category.closest('[data-apd-collection]');
        if (!categoryFilters || !collection || !categoryFilters.contains(category)) {
          return;
        }
        event.preventDefault();
        collection.dataset.apdActiveCategory = category.getAttribute('data-apd-category') || '';
        refreshCollectionCategories(collection);
        applyCollectionFilters(collection);
        return;
      }
      var remove = event.target.closest('[data-apd-remove-row]');
      if (!remove) {
        return;
      }
      var row = remove.closest('.apd-collection-row');
      var rows = row && row.parentElement;
      var collectionForRemove = row && row.closest('[data-apd-collection]');
      if (!row || !rows) {
        return;
      }
      var wasEditing = row.classList.contains('is-editing');
      if (wasEditing && collectionForRemove) {
        closeCollectionEditor(collectionForRemove);
      }
      row.remove();
      if (collectionForRemove) {
        renumberCollection(collectionForRemove);
        syncCollection(collectionForRemove);
      }
    });

    scope.querySelectorAll('form').forEach(function (form) {
      form.addEventListener('submit', function () {
        scope.querySelectorAll('[data-apd-collection]').forEach(function (collection) {
          closeCollectionEditor(collection);
          pruneEmptyRows(collection);
          syncCollection(collection);
        });
      });
    });
  }


  function toPrettyJson(value) {
    try {
      return JSON.stringify(value, null, 2);
    } catch (error) {
      return '[]';
    }
  }

  function inferPricingBuilderFromJson(handlerField, modeField, operatorField, axisField, valueField, priceField, jsonField) {
    if (!jsonField || !jsonField.value || modeField.value === 'raw_json') {
      return;
    }
    var parsed = safeParse(jsonField.value, []);
    if (!Array.isArray(parsed) || !parsed.length || typeof parsed[0] !== 'object' || !parsed[0]) {
      return;
    }
    var first = parsed[0];
    var axis = Object.prototype.hasOwnProperty.call(first, 'width') ? 'width' : (Object.prototype.hasOwnProperty.call(first, 'height') ? 'height' : 'value');
    var rawValue = first[axis] != null ? first[axis] : first.value;
    var rawPrice = first.price != null ? String(first.price) : '';
    var isPercent = rawPrice.indexOf('%') !== -1;
    var inferredMode = first.operator === '%' ? (isPercent ? 'step_percent' : 'step_fixed') : (isPercent ? 'threshold_percent' : 'threshold_fixed');
    modeField.value = inferredMode;
    if (first.operator && first.operator !== '%') {
      operatorField.value = String(first.operator);
    }
    axisField.value = axis;
    valueField.value = rawValue != null ? String(rawValue) : '';
    priceField.value = rawPrice;
  }

  function syncPricingAxisOptions(axisField, isSizeHandler) {
    if (!axisField) {
      return;
    }
    Array.prototype.forEach.call(axisField.options, function (option) {
      var value = option.value;
      var allowed = isSizeHandler ? (value === 'width' || value === 'height') : value === 'value';
      option.hidden = !allowed;
      option.disabled = !allowed;
    });
    if (isSizeHandler) {
      if (axisField.value !== 'width' && axisField.value !== 'height') {
        axisField.value = 'width';
      }
    } else if (axisField.value !== 'value') {
      axisField.value = 'value';
    }
  }

  function ensurePricingSummary(form) {
    var summary = form.querySelector('[data-apd-pricing-builder-summary]');
    if (summary) {
      return summary;
    }
    var jsonField = form.querySelector('[data-apd-pricing-builder-json]');
    var wrapper = jsonField && jsonField.closest('.apd-field');
    if (!wrapper) {
      return null;
    }
    summary = document.createElement('div');
    summary.className = 'apd-pricing-builder-summary';
    summary.setAttribute('data-apd-pricing-builder-summary', '1');
    wrapper.parentNode.insertBefore(summary, wrapper);
    return summary;
  }

  function refreshPricingRuleBuilder(form) {
    if (!form) {
      return;
    }
    var handlerField = form.querySelector('[data-apd-pricing-handler]');
    var modeField = form.querySelector('[data-apd-pricing-builder-mode]');
    var operatorField = form.querySelector('[data-apd-pricing-builder-operator]');
    var axisField = form.querySelector('[data-apd-pricing-builder-axis]');
    var valueField = form.querySelector('[data-apd-pricing-builder-value]');
    var priceField = form.querySelector('[data-apd-pricing-builder-price]');
    var jsonField = form.querySelector('[data-apd-pricing-builder-json]');
    if (!handlerField || !modeField || !operatorField || !axisField || !valueField || !priceField || !jsonField) {
      return;
    }

    if (!form.dataset.apdPricingBuilderInitialized) {
      inferPricingBuilderFromJson(handlerField, modeField, operatorField, axisField, valueField, priceField, jsonField);
      form.dataset.apdPricingBuilderInitialized = '1';
    }

    var isSizeHandler = handlerField.value === 'layerSize' || handlerField.value === 'canvasSize';
    var rawMode = modeField.value === 'raw_json';
    var stepMode = modeField.value === 'step_fixed' || modeField.value === 'step_percent';
    syncPricingAxisOptions(axisField, isSizeHandler);

    var operatorWrapper = operatorField.closest('.apd-field');
    var axisWrapper = axisField.closest('.apd-field');
    var valueWrapper = valueField.closest('.apd-field');
    var priceWrapper = priceField.closest('.apd-field');
    var jsonWrapper = jsonField.closest('.apd-field');

    [operatorWrapper, axisWrapper, valueWrapper, priceWrapper].forEach(function (wrapper) {
      if (!wrapper) return;
      wrapper.classList.toggle('is-hidden', rawMode);
    });
    if (operatorWrapper) {
      operatorWrapper.classList.toggle('is-hidden', rawMode || stepMode);
    }

    jsonField.readOnly = !rawMode;
    if (jsonWrapper) {
      jsonWrapper.classList.toggle('apd-field-readonly', !rawMode);
    }

    var summary = ensurePricingSummary(form);
    if (rawMode) {
      if (summary) {
        summary.innerHTML = '<strong>' + escapeHtml(adminLabel('advancedJsonMode', 'Advanced JSON mode')) + '</strong><span>' + escapeHtml(adminLabel('advancedJsonModeHelp', 'You control the conditions array manually for this rule.')) + '</span>';
      }
      return;
    }

    var axisKey = axisField.value || (isSizeHandler ? 'width' : 'value');
    var axisLabel = axisKey === 'width' ? adminLabel('pricingAxisWidth', 'width') : (axisKey === 'height' ? adminLabel('pricingAxisHeight', 'height') : adminLabel('pricingAxisValue', 'value'));
    var rawValue = (valueField.value || '').trim();
    var rawPrice = (priceField.value || '').trim();
    var normalizedPrice = rawPrice;
    if ((modeField.value === 'threshold_percent' || modeField.value === 'step_percent') && normalizedPrice && normalizedPrice.indexOf('%') === -1) {
      normalizedPrice += '%';
    }

    var condition = {
      operator: stepMode ? '%' : operatorField.value,
      price: normalizedPrice || 0
    };
    if (rawValue !== '') {
      condition[axisKey] = Number(rawValue);
      if (!Number.isFinite(condition[axisKey])) {
        condition[axisKey] = rawValue;
      }
    }
    var jsonValue = rawValue !== '' ? [condition] : [];
    jsonField.value = toPrettyJson(jsonValue);

    if (summary) {
      var modeLabel = modeField.options[modeField.selectedIndex] ? modeField.options[modeField.selectedIndex].text : modeField.value;
      var operatorText = stepMode ? adminLabel('pricingEachStepOf', 'each step of') : operatorField.value;
      summary.innerHTML = '<strong>' + escapeHtml(modeLabel) + '</strong><span>' + escapeHtml(adminLabel('pricingWatching', 'Watching') + ' ' + axisLabel + ' ' + operatorText + ' ' + (rawValue || '0') + ' → ' + adminLabel('pricingAdds', 'adds') + ' ' + (normalizedPrice || '0') + '.') + '</span>';
    }
  }

  function initPricingRuleBuilders(scope) {
    scope.querySelectorAll('form').forEach(function (form) {
      if (!form.querySelector('[data-apd-pricing-handler]')) {
        return;
      }
      refreshPricingRuleBuilder(form);
      form.addEventListener('change', function (event) {
        if (!event.target.closest('[data-apd-pricing-handler], [data-apd-pricing-builder-mode], [data-apd-pricing-builder-operator], [data-apd-pricing-builder-axis], [data-apd-pricing-builder-value], [data-apd-pricing-builder-price], [data-apd-pricing-builder-json]')) {
          return;
        }
        refreshPricingRuleBuilder(form);
      });
      form.addEventListener('input', function (event) {
        if (!event.target.closest('[data-apd-pricing-builder-value], [data-apd-pricing-builder-price], [data-apd-pricing-builder-json]')) {
          return;
        }
        refreshPricingRuleBuilder(form);
      });
    });
  }


  function slugifyFontId(value) {
    return String(value || '')
      .toLowerCase()
      .trim()
      .replace(/[^a-z0-9]+/g, '-')
      .replace(/^-+|-+$/g, '');
  }

  function inferFontFormat(url) {
    var clean = String(url || '').split('?')[0].toLowerCase();
    if (/\.woff2$/.test(clean)) return 'woff2';
    if (/\.woff$/.test(clean)) return 'woff';
    if (/\.ttf$/.test(clean)) return 'truetype';
    if (/\.otf$/.test(clean)) return 'opentype';
    return '';
  }

  function ensureFontPreviewStyle(editor, family, cssUrl, fileUrl) {
    if (!editor || !family) {
      return family || 'inherit';
    }
    var safeFamily = String(family).replace(/"/g, '\"');
    var previewFamily = 'APDPreview_' + slugifyFontId(family || 'font');
    var head = document.head || document.documentElement;

    if (cssUrl) {
      var linkId = 'apd-font-css-' + slugifyFontId(cssUrl);
      if (!document.getElementById(linkId)) {
        var link = document.createElement('link');
        link.id = linkId;
        link.rel = 'stylesheet';
        link.href = cssUrl;
        head.appendChild(link);
      }
      return '"' + safeFamily + '", sans-serif';
    }

    if (fileUrl) {
      var styleId = 'apd-font-file-' + slugifyFontId(fileUrl + family);
      if (!document.getElementById(styleId)) {
        var style = document.createElement('style');
        style.id = styleId;
        var format = inferFontFormat(fileUrl);
        style.textContent = '@font-face{font-family:"' + previewFamily + '";src:url("' + String(fileUrl).replace(/"/g, '\"') + '")' + (format ? ' format("' + format + '")' : '') + ';font-display:swap;}';
        head.appendChild(style);
      }
      return '"' + previewFamily + '", sans-serif';
    }

    return '"' + safeFamily + '", sans-serif';
  }

  function adaptiveFontList(value) {
    return String(value || '').split(/[\s,]+/).map(function (part) {
      return String(part || '').trim();
    }).filter(Boolean);
  }

  function setAdaptiveFontField(editor, key, value, touched) {
    var field = editor.querySelector('[data-apd-font-sync="' + key + '"]');
    if (!field) return;
    field.value = Array.isArray(value) ? value.join(',') : String(value == null ? '' : value);
    if (touched) field.dataset.apdTouched = '1';
  }

  function refreshAdaptiveCustomDelivery(editor) {
    var control = editor.querySelector('[data-apd-custom-delivery]');
    if (!control) return;
    var delivery = control.getAttribute('data-apd-custom-delivery') || 'file';
    control.querySelectorAll('[data-apd-custom-delivery-option]').forEach(function (button) {
      button.classList.toggle('is-active', button.getAttribute('data-apd-custom-delivery-option') === delivery);
      button.setAttribute('aria-pressed', button.classList.contains('is-active') ? 'true' : 'false');
    });
    editor.querySelectorAll('[data-apd-custom-delivery-only]').forEach(function (panel) {
      var visible = panel.getAttribute('data-apd-custom-delivery-only') === delivery;
      panel.classList.toggle('is-hidden', !visible);
      panel.querySelectorAll('input, select, textarea').forEach(function (field) {
        field.disabled = !visible;
      });
    });
    if (delivery === 'stylesheet') {
      var details = editor.querySelector('.apd-advanced-details');
      if (details) details.open = true;
    }
  }

  function refreshAdaptiveFontEditor(editor) {
    if (!editor) return;
    var sourceField = editor.querySelector('[data-apd-font-source-input]');
    var source = sourceField ? (sourceField.value || 'google') : 'google';
    editor.dataset.apdFontSource = source;
    editor.querySelectorAll('[data-apd-font-source-option]').forEach(function (button) {
      var active = button.getAttribute('data-apd-font-source-option') === source;
      button.classList.toggle('is-active', active);
      button.setAttribute('aria-pressed', active ? 'true' : 'false');
    });
    editor.querySelectorAll('[data-apd-font-source-only]').forEach(function (node) {
      var allowed = String(node.getAttribute('data-apd-font-source-only') || '').split(/\s+/).filter(Boolean);
      node.classList.toggle('is-hidden', allowed.indexOf(source) === -1);
    });

    var labelField = editor.querySelector('[data-apd-font-sync="label"]');
    var idField = editor.querySelector('[data-apd-font-sync="id"]');
    var familyField = editor.querySelector('[data-apd-font-sync="family"]');
    var cssUrlField = editor.querySelector('[data-apd-font-sync="cssUrl"]');
    var fileUrlField = editor.querySelector('[data-apd-font-sync="fileUrl"]');
    var weightsField = editor.querySelector('[data-apd-font-sync="weights"]');
    var label = String(labelField && labelField.value || '').trim();
    var family = String(familyField && familyField.value || '').trim();
    var weights = source === 'google' ? String(weightsField && weightsField.value || '').trim() : '';
    var cssUrl = source === 'google' || source === 'custom' ? String(cssUrlField && cssUrlField.value || '').trim() : '';
    var fileUrl = source === 'custom' ? String(fileUrlField && fileUrlField.value || '').trim() : '';
    var previewLabel = editor.querySelector('.apd-font-preview-card__label');
    var previewMeta = editor.querySelector('[data-apd-font-preview-meta]');
    var previewSample = editor.querySelector('[data-apd-font-preview-sample]');
    var previewInput = editor.querySelector('[data-apd-font-preview-input]');
    var previewSource = editor.querySelector('[data-apd-font-preview-source]');
    if (previewLabel) previewLabel.textContent = label || adminLabel('untitledFont', 'Untitled font');
    if (previewMeta) previewMeta.textContent = (family || adminLabel('fontFamilyNotSetYet', 'Font family not set yet')) + (weights ? ' · ' + weights : '');
    if (previewSource) {
      var sourceTitle = editor.querySelector('[data-apd-font-source-option="' + source + '"] strong');
      previewSource.textContent = sourceTitle ? sourceTitle.textContent : source;
    }
    if (previewSample) {
      previewSample.textContent = previewInput && previewInput.value ? previewInput.value : adminLabel('fontPreviewSample', 'The quick brown fox jumps over the lazy dog');
      previewSample.style.fontFamily = ensureFontPreviewStyle(editor, family || label, cssUrl, fileUrl);
    }
    if (idField && !idField.dataset.apdTouched && label) idField.value = slugifyFontId(label);
    editor.querySelectorAll('[data-apd-system-font-option]').forEach(function (button) {
      button.classList.toggle('is-active', source === 'system' && button.getAttribute('data-family') === family);
    });
    refreshAdaptiveCustomDelivery(editor);
  }

  function adaptiveGoogleVariants(variants) {
    var weights = [];
    var styles = [];
    (Array.isArray(variants) ? variants : []).forEach(function (variant) {
      var value = String(variant || '').toLowerCase();
      var italic = value.indexOf('italic') !== -1;
      var weight = value.replace('italic', '');
      if (weight === 'regular' || !weight) weight = '400';
      if (/^\d{3}$/.test(weight) && weights.indexOf(weight) === -1) weights.push(weight);
      var style = italic ? 'italic' : 'normal';
      if (styles.indexOf(style) === -1) styles.push(style);
    });
    weights.sort(function (a, b) { return Number(a) - Number(b); });
    if (!weights.length) weights.push('400');
    if (!styles.length) styles.push('normal');
    return { weights: weights, styles: styles };
  }

  function buildAdaptiveGoogleCssUrl(editor) {
    var familyField = editor.querySelector('[data-apd-font-sync="family"]');
    var cssField = editor.querySelector('[data-apd-font-sync="cssUrl"]');
    var family = String(familyField && familyField.value || '').trim();
    if (!family || !cssField) return '';
    var weights = adaptiveFontList((editor.querySelector('[data-apd-font-sync="weights"]') || {}).value || '400');
    var styles = adaptiveFontList((editor.querySelector('[data-apd-font-sync="styles"]') || {}).value || 'normal');
    if (!weights.length) weights = ['400'];
    if (!styles.length) styles = ['normal'];
    var axis = '';
    if (styles.indexOf('italic') !== -1) {
      var tuples = [];
      ['normal', 'italic'].forEach(function (style) {
        if (styles.indexOf(style) === -1) return;
        weights.forEach(function (weight) {
          tuples.push((style === 'italic' ? '1' : '0') + ',' + weight);
        });
      });
      axis = ':ital,wght@' + tuples.join(';');
    } else {
      axis = ':wght@' + weights.join(';');
    }
    cssField.value = 'https://fonts.googleapis.com/css2?family=' + family.replace(/\s+/g, '+') + axis + '&display=swap';
    return cssField.value;
  }

  function renderAdaptiveFontChoices(editor, font, preserve) {
    var available = adaptiveGoogleVariants(font && font.variants);
    var selectedWeights = preserve ? adaptiveFontList((editor.querySelector('[data-apd-font-sync="weights"]') || {}).value) : [];
    var selectedStyles = preserve ? adaptiveFontList((editor.querySelector('[data-apd-font-sync="styles"]') || {}).value) : [];
    selectedWeights = selectedWeights.filter(function (weight) { return available.weights.indexOf(weight) !== -1; });
    selectedStyles = selectedStyles.filter(function (style) { return available.styles.indexOf(style) !== -1; });
    if (!selectedWeights.length) selectedWeights = [available.weights.indexOf('400') !== -1 ? '400' : available.weights[0]];
    if (!selectedStyles.length) selectedStyles = [available.styles.indexOf('normal') !== -1 ? 'normal' : available.styles[0]];
    setAdaptiveFontField(editor, 'weights', selectedWeights);
    setAdaptiveFontField(editor, 'styles', selectedStyles);

    function render(selector, values, selected, key) {
      var root = editor.querySelector(selector);
      if (!root) return;
      root.innerHTML = '';
      values.forEach(function (value) {
        var label = document.createElement('label');
        label.className = 'apd-choice-pill';
        var input = document.createElement('input');
        input.type = 'checkbox';
        input.value = value;
        input.checked = selected.indexOf(value) !== -1;
        input.addEventListener('change', function () {
          var checked = Array.prototype.slice.call(root.querySelectorAll('input:checked')).map(function (field) { return field.value; });
          if (!checked.length) {
            input.checked = true;
            checked = [input.value];
          }
          setAdaptiveFontField(editor, key, checked, true);
          buildAdaptiveGoogleCssUrl(editor);
          refreshAdaptiveFontEditor(editor);
        });
        var text = document.createElement('span');
        text.textContent = key === 'styles' ? value.charAt(0).toUpperCase() + value.slice(1) : value;
        label.appendChild(input);
        label.appendChild(text);
        root.appendChild(label);
      });
    }
    render('[data-apd-google-font-weights]', available.weights, selectedWeights, 'weights');
    render('[data-apd-google-font-styles]', available.styles, selectedStyles, 'styles');
    buildAdaptiveGoogleCssUrl(editor);
  }

  function selectAdaptiveGoogleFont(editor, font, preserve) {
    if (!font) return;
    var previous = String((editor.querySelector('[data-apd-font-sync="family"]') || {}).value || '').trim();
    setAdaptiveFontField(editor, 'family', font.family, true);
    var label = editor.querySelector('[data-apd-font-sync="label"]');
    if (label && (!label.value.trim() || label.value.trim() === previous)) label.value = font.family;
    var category = editor.querySelector('[data-apd-font-sync="category"]');
    if (category && (!category.value.trim() || category.dataset.apdGenerated === '1')) {
      category.value = String(font.category || '').replace(/-/g, ' ');
      category.dataset.apdGenerated = '1';
    }
    var search = editor.querySelector('[data-apd-google-font-search]');
    if (search) search.value = font.family;
    var selection = editor.querySelector('[data-apd-google-font-selection]');
    if (selection) selection.hidden = false;
    var familyNode = editor.querySelector('[data-apd-google-selected-family]');
    if (familyNode) familyNode.textContent = font.family;
    var categoryNode = editor.querySelector('[data-apd-google-selected-category]');
    if (categoryNode) categoryNode.textContent = String(font.category || '').replace(/-/g, ' ');
    renderAdaptiveFontChoices(editor, font, preserve);
    var results = editor.querySelector('[data-apd-google-font-results]');
    if (results) results.hidden = true;
    if (search) search.setAttribute('aria-expanded', 'false');
    refreshAdaptiveFontEditor(editor);
  }

  function renderAdaptiveGoogleResults(editor) {
    var search = editor.querySelector('[data-apd-google-font-search]');
    var results = editor.querySelector('[data-apd-google-font-results]');
    var fonts = Array.isArray(editor.__apdGoogleFonts) ? editor.__apdGoogleFonts : [];
    if (!search || !results || !fonts.length) return;
    var query = String(search.value || '').trim().toLowerCase();
    var matches = fonts.filter(function (font) {
      return !query || String(font.family || '').toLowerCase().indexOf(query) !== -1;
    }).sort(function (a, b) {
      var af = String(a.family || '').toLowerCase();
      var bf = String(b.family || '').toLowerCase();
      return ((query && af.indexOf(query) === 0) ? 0 : 1) - ((query && bf.indexOf(query) === 0) ? 0 : 1) || af.localeCompare(bf);
    }).slice(0, 60);
    results.innerHTML = '';
    if (!matches.length) {
      var empty = document.createElement('div');
      empty.className = 'apd-font-results__empty';
      empty.textContent = adminLabel('fontCatalogNoResults', 'No font family matches this search.');
      results.appendChild(empty);
    }
    matches.forEach(function (font) {
      var variants = adaptiveGoogleVariants(font.variants);
      var button = document.createElement('button');
      button.type = 'button';
      button.className = 'apd-font-result';
      button.setAttribute('role', 'option');
      button.innerHTML = '<span><strong>' + escapeHtml(font.family) + '</strong><small>' + escapeHtml(String(font.category || '').replace(/-/g, ' ')) + '</small></span><em>' + variants.weights.length + ' weights</em>';
      button.addEventListener('click', function () { selectAdaptiveGoogleFont(editor, font, false); });
      results.appendChild(button);
    });
    results.hidden = false;
    search.setAttribute('aria-expanded', 'true');
  }

  function loadAdaptiveGoogleCatalog(editor) {
    if (editor.__apdGoogleFontsLoading || Array.isArray(editor.__apdGoogleFonts)) {
      renderAdaptiveGoogleResults(editor);
      return;
    }
    var config = (window.APDAdminConfig || {}).fontCatalog || {};
    var status = editor.querySelector('[data-apd-google-font-status]');
    if (!config.hasApiKey) {
      if (status) status.innerHTML = escapeHtml(adminLabel('fontCatalogMissingKey', 'Add a Google Fonts API key in Settings to browse the complete live catalog.')) + (config.settingsUrl ? ' <a href="' + escapeHtml(config.settingsUrl) + '">Settings</a>' : '');
      return;
    }
    editor.__apdGoogleFontsLoading = true;
    if (status) {
      status.classList.add('is-loading');
      status.textContent = adminLabel('fontCatalogLoading', 'Loading the complete Google Fonts catalog...');
    }
    var body = new URLSearchParams();
    body.set('action', config.action || 'apd_google_fonts_catalog');
    body.set('nonce', config.nonce || '');
    fetch(config.endpoint || window.ajaxurl, {
      method: 'POST',
      credentials: 'same-origin',
      headers: { 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8' },
      body: body.toString()
    }).then(function (response) {
      return response.json().then(function (payload) {
        if (!response.ok || !payload || !payload.success) {
          throw new Error(payload && payload.data && payload.data.message || 'catalog_error');
        }
        return payload.data;
      });
    }).then(function (data) {
      editor.__apdGoogleFonts = Array.isArray(data.items) ? data.items : [];
      if (status) {
        status.classList.remove('is-loading', 'is-error');
        status.textContent = adminLabel('fontCatalogLoaded', '{count} Google Font families available.', { count: editor.__apdGoogleFonts.length });
      }
      var family = String((editor.querySelector('[data-apd-font-sync="family"]') || {}).value || '').trim();
      var current = editor.__apdGoogleFonts.find(function (font) { return font.family === family; });
      if (current) selectAdaptiveGoogleFont(editor, current, true);
      renderAdaptiveGoogleResults(editor);
    }).catch(function (error) {
      if (status) {
        status.classList.remove('is-loading');
        status.classList.add('is-error');
        status.textContent = error && error.message && error.message !== 'catalog_error' ? error.message : adminLabel('fontCatalogError', 'The Google Fonts catalog could not be loaded.');
      }
    }).finally(function () {
      editor.__apdGoogleFontsLoading = false;
    });
  }

  function initAdaptiveFontEditors(scope) {
    scope.querySelectorAll('.apd-catalog-editor-v2[data-apd-font-editor]').forEach(function (editor) {
      refreshAdaptiveFontEditor(editor);
      editor.querySelectorAll('[data-apd-font-source-option]').forEach(function (button) {
        button.addEventListener('click', function () {
          var source = button.getAttribute('data-apd-font-source-option') || 'google';
          var field = editor.querySelector('[data-apd-font-source-input]');
          if (field) field.value = source;
          refreshAdaptiveFontEditor(editor);
          if (source === 'google') loadAdaptiveGoogleCatalog(editor);
        });
      });
      editor.querySelectorAll('[data-apd-font-sync="id"], [data-apd-font-sync="label"], [data-apd-font-sync="category"]').forEach(function (field) {
        field.addEventListener('input', function () {
          if (field.getAttribute('data-apd-font-sync') !== 'label') field.dataset.apdTouched = '1';
          refreshAdaptiveFontEditor(editor);
        });
      });
      editor.querySelectorAll('[data-apd-font-sync], [data-apd-font-preview-input]').forEach(function (field) {
        field.addEventListener('input', function () { refreshAdaptiveFontEditor(editor); });
        field.addEventListener('change', function () { refreshAdaptiveFontEditor(editor); });
      });

      var googleSearch = editor.querySelector('[data-apd-google-font-search]');
      if (googleSearch) {
        googleSearch.addEventListener('focus', function () { loadAdaptiveGoogleCatalog(editor); });
        googleSearch.addEventListener('input', function () { renderAdaptiveGoogleResults(editor); });
        googleSearch.addEventListener('keydown', function (event) {
          var results = editor.querySelector('[data-apd-google-font-results]');
          var options = results ? Array.prototype.slice.call(results.querySelectorAll('.apd-font-result')) : [];
          var active = results && results.querySelector('.is-active');
          var index = options.indexOf(active);
          if (event.key === 'ArrowDown' || event.key === 'ArrowUp') {
            event.preventDefault();
            if (!options.length) return;
            if (active) active.classList.remove('is-active');
            index = event.key === 'ArrowDown' ? Math.min(index + 1, options.length - 1) : Math.max(index - 1, 0);
            options[index].classList.add('is-active');
            options[index].scrollIntoView({ block: 'nearest' });
          } else if (event.key === 'Enter' && active) {
            event.preventDefault();
            active.click();
          } else if (event.key === 'Escape' && results) {
            results.hidden = true;
            googleSearch.setAttribute('aria-expanded', 'false');
          }
        });
        googleSearch.addEventListener('blur', function () {
          window.setTimeout(function () {
            var results = editor.querySelector('[data-apd-google-font-results]');
            if (results) results.hidden = true;
            googleSearch.setAttribute('aria-expanded', 'false');
          }, 160);
        });
      }

      var systemSearch = editor.querySelector('[data-apd-system-font-search]');
      if (systemSearch) {
        systemSearch.addEventListener('input', function () {
          var query = String(systemSearch.value || '').trim().toLowerCase();
          editor.querySelectorAll('[data-apd-system-font-option]').forEach(function (button) {
            button.hidden = !!query && String(button.getAttribute('data-search') || '').indexOf(query) === -1;
          });
        });
      }
      editor.querySelectorAll('[data-apd-system-font-option]').forEach(function (button) {
        var availability = button.querySelector('[data-apd-system-font-availability]');
        if (availability && document.fonts && typeof document.fonts.check === 'function') {
          var buttonFamily = button.getAttribute('data-family') || '';
          var available = buttonFamily === 'system-ui' || document.fonts.check('16px "' + buttonFamily.replace(/"/g, '') + '"');
          availability.textContent = available ? adminLabel('fontAvailableHere', 'Available on this device') : adminLabel('fontFallbackHere', 'Uses the browser fallback on this device');
          availability.classList.toggle('is-available', available);
        }
        button.addEventListener('click', function () {
          var previous = String((editor.querySelector('[data-apd-font-sync="family"]') || {}).value || '');
          var family = button.getAttribute('data-family') || '';
          setAdaptiveFontField(editor, 'family', family, true);
          var label = editor.querySelector('[data-apd-font-sync="label"]');
          if (label && (!label.value.trim() || label.value.trim() === previous)) label.value = family;
          var category = editor.querySelector('[data-apd-font-sync="category"]');
          if (category && (!category.value.trim() || category.dataset.apdGenerated === '1')) {
            category.value = button.getAttribute('data-category') || '';
            category.dataset.apdGenerated = '1';
          }
          refreshAdaptiveFontEditor(editor);
        });
      });

      var customFamily = editor.querySelector('[data-apd-custom-font-family]');
      if (customFamily) {
        customFamily.addEventListener('input', function () {
          var previous = String((editor.querySelector('[data-apd-font-sync="family"]') || {}).value || '');
          setAdaptiveFontField(editor, 'family', customFamily.value, true);
          var label = editor.querySelector('[data-apd-font-sync="label"]');
          if (label && (!label.value.trim() || label.value.trim() === previous)) label.value = customFamily.value;
          refreshAdaptiveFontEditor(editor);
        });
      }
      editor.querySelectorAll('[data-apd-custom-delivery-option]').forEach(function (button) {
        button.addEventListener('click', function () {
          var control = button.closest('[data-apd-custom-delivery]');
          if (control) control.setAttribute('data-apd-custom-delivery', button.getAttribute('data-apd-custom-delivery-option') || 'file');
          refreshAdaptiveCustomDelivery(editor);
        });
      });
      if ((editor.querySelector('[data-apd-font-source-input]') || {}).value === 'google') loadAdaptiveGoogleCatalog(editor);
    });
  }

  function currentColorTypeValue(editor, type) {
    if (type === 'solid') {
      return String((editor.querySelector('[data-apd-solid-color-text]') || {}).value || '#223056').trim();
    }
    if (type === 'gradient') {
      return String((editor.querySelector('[data-apd-gradient-css]') || {}).value || '').trim();
    }
    var panel = editor.querySelector('[data-apd-color-type-only="' + type + '"]');
    return String((panel && panel.querySelector('[data-apd-color-media-url]') || {}).value || '').trim();
  }

  function setAdaptiveColorValue(editor, value) {
    var field = editor.querySelector('[data-apd-color-value]');
    if (field) field.value = String(value || '').trim();
  }

  function buildAdaptiveGradient(editor) {
    var angle = Math.max(0, Math.min(360, Number((editor.querySelector('[data-apd-gradient-angle]') || {}).value || 90)));
    var start = String((editor.querySelector('[data-apd-gradient-start]') || {}).value || '#2563eb');
    var end = String((editor.querySelector('[data-apd-gradient-end]') || {}).value || '#11bfae');
    var value = 'linear-gradient(' + angle + 'deg, ' + start + ', ' + end + ')';
    var cssField = editor.querySelector('[data-apd-gradient-css]');
    if (cssField) cssField.value = value;
    setAdaptiveColorValue(editor, value);
    return value;
  }

  function refreshAdaptiveColorEditor(editor) {
    var typeField = editor.querySelector('[data-apd-color-type-input]');
    var type = String(typeField && typeField.value || 'solid');
    var value = String((editor.querySelector('[data-apd-color-value]') || {}).value || '');
    editor.dataset.apdColorType = type;
    editor.querySelectorAll('[data-apd-color-type-option]').forEach(function (button) {
      var active = button.getAttribute('data-apd-color-type-option') === type;
      button.classList.toggle('is-active', active);
      button.setAttribute('aria-pressed', active ? 'true' : 'false');
    });
    editor.querySelectorAll('[data-apd-color-type-only]').forEach(function (panel) {
      panel.classList.toggle('is-hidden', panel.getAttribute('data-apd-color-type-only') !== type);
    });

    var labelField = editor.querySelector('[data-apd-color-sync="label"]');
    var idField = editor.querySelector('[data-apd-color-sync="id"]');
    var label = String(labelField && labelField.value || '').trim();
    if (idField && !idField.dataset.apdTouched && label) idField.value = slugifyFontId(label);
    var preview = editor.querySelector('[data-apd-color-preview]');
    if (preview) {
      preview.style.background = '';
      preview.style.backgroundImage = '';
      preview.style.backgroundSize = '';
      preview.style.backgroundRepeat = '';
      if (type === 'pattern' || type === 'image') {
        if (value) preview.style.backgroundImage = 'url("' + value.replace(/"/g, '\\"') + '")';
        preview.style.backgroundSize = type === 'pattern' ? 'auto' : 'cover';
        preview.style.backgroundRepeat = type === 'pattern' ? 'repeat' : 'no-repeat';
        preview.style.backgroundColor = '#eef4ff';
      } else {
        preview.style.background = value || '#eef4ff';
      }
    }
    var previewLabel = editor.querySelector('[data-apd-color-preview-label]');
    if (previewLabel) previewLabel.textContent = label || 'Untitled color';
    var previewValue = editor.querySelector('[data-apd-color-preview-value]');
    if (previewValue) previewValue.textContent = value || 'No value';
    var previewType = editor.querySelector('[data-apd-color-preview-type]');
    if (previewType) previewType.textContent = type.charAt(0).toUpperCase() + type.slice(1);

    var modeField = editor.querySelector('[data-apd-color-method-mode]');
    var mode = String(modeField && modeField.value || 'all');
    var modeRoot = editor.querySelector('[data-apd-relation-mode="color"]');
    if (modeRoot) {
      modeRoot.querySelectorAll('[data-apd-relation-mode-option]').forEach(function (button) {
        var active = button.getAttribute('data-apd-relation-mode-option') === mode;
        button.classList.toggle('is-active', active);
        button.setAttribute('aria-pressed', active ? 'true' : 'false');
      });
    }
    var methodOptions = editor.querySelector('[data-apd-color-method-options]');
    if (methodOptions) {
      methodOptions.classList.toggle('is-disabled', mode !== 'restricted');
      methodOptions.querySelectorAll('input').forEach(function (input) {
        input.disabled = mode !== 'restricted';
      });
    }
  }

  function initAdaptiveColorEditors(scope) {
    scope.querySelectorAll('[data-apd-color-editor]').forEach(function (editor) {
      refreshAdaptiveColorEditor(editor);
      editor.querySelectorAll('[data-apd-color-type-option]').forEach(function (button) {
        button.addEventListener('click', function () {
          var type = button.getAttribute('data-apd-color-type-option') || 'solid';
          var typeField = editor.querySelector('[data-apd-color-type-input]');
          if (typeField) typeField.value = type;
          var nextValue = currentColorTypeValue(editor, type);
          if (type === 'gradient' && !/^linear-gradient\(/i.test(nextValue)) nextValue = buildAdaptiveGradient(editor);
          setAdaptiveColorValue(editor, nextValue);
          refreshAdaptiveColorEditor(editor);
        });
      });
      var solidPicker = editor.querySelector('[data-apd-solid-color-picker]');
      var solidText = editor.querySelector('[data-apd-solid-color-text]');
      if (solidPicker && solidText) {
        solidPicker.addEventListener('input', function () {
          solidText.value = solidPicker.value;
          setAdaptiveColorValue(editor, solidPicker.value);
          refreshAdaptiveColorEditor(editor);
        });
        solidText.addEventListener('input', function () {
          if (/^#[0-9a-f]{6}$/i.test(solidText.value)) solidPicker.value = solidText.value;
          setAdaptiveColorValue(editor, solidText.value);
          refreshAdaptiveColorEditor(editor);
        });
      }
      editor.querySelectorAll('[data-apd-gradient-angle], [data-apd-gradient-start], [data-apd-gradient-end]').forEach(function (field) {
        field.addEventListener('input', function () {
          var colorKey = field.getAttribute('data-apd-gradient-color-picker');
          var colorText = colorKey && editor.querySelector('[data-apd-gradient-color-text="' + colorKey + '"]');
          if (colorText) colorText.value = field.value;
          buildAdaptiveGradient(editor);
          refreshAdaptiveColorEditor(editor);
        });
      });
      editor.querySelectorAll('[data-apd-gradient-color-text]').forEach(function (field) {
        var colorKey = field.getAttribute('data-apd-gradient-color-text') || '';
        var picker = editor.querySelector('[data-apd-gradient-color-picker="' + colorKey + '"]');
        field.addEventListener('input', function () {
          var value = String(field.value || '').trim();
          if (!/^#[0-9a-f]{6}$/i.test(value) || !picker) return;
          picker.value = value;
          buildAdaptiveGradient(editor);
          refreshAdaptiveColorEditor(editor);
        });
        field.addEventListener('blur', function () {
          if (picker && !/^#[0-9a-f]{6}$/i.test(String(field.value || '').trim())) field.value = picker.value;
        });
      });
      var gradientCss = editor.querySelector('[data-apd-gradient-css]');
      if (gradientCss) {
        gradientCss.addEventListener('input', function () {
          setAdaptiveColorValue(editor, gradientCss.value);
          var match = gradientCss.value.match(/linear-gradient\(\s*(-?\d+(?:\.\d+)?)deg\s*,\s*(#[0-9a-f]{6})\s*,\s*(#[0-9a-f]{6})\s*\)/i);
          if (match) {
            var angle = editor.querySelector('[data-apd-gradient-angle]');
            var start = editor.querySelector('[data-apd-gradient-start]');
            var end = editor.querySelector('[data-apd-gradient-end]');
            if (angle) angle.value = match[1];
            if (start) start.value = match[2];
            if (end) end.value = match[3];
          }
          refreshAdaptiveColorEditor(editor);
        });
      }
      editor.querySelectorAll('[data-apd-color-media-url]').forEach(function (field) {
        field.addEventListener('input', function () {
          var panel = field.closest('[data-apd-color-type-only]');
          var type = panel ? panel.getAttribute('data-apd-color-type-only') : '';
          var activeType = String((editor.querySelector('[data-apd-color-type-input]') || {}).value || '');
          if (type === activeType) {
            setAdaptiveColorValue(editor, field.value);
            refreshAdaptiveColorEditor(editor);
          }
        });
      });
      editor.querySelectorAll('[data-apd-color-sync]').forEach(function (field) {
        field.addEventListener('input', function () {
          if (field.getAttribute('data-apd-color-sync') === 'id') field.dataset.apdTouched = '1';
          refreshAdaptiveColorEditor(editor);
        });
      });
      var modeRoot = editor.querySelector('[data-apd-relation-mode="color"]');
      if (modeRoot) {
        modeRoot.querySelectorAll('[data-apd-relation-mode-option]').forEach(function (button) {
          button.addEventListener('click', function () {
            var field = editor.querySelector('[data-apd-color-method-mode]');
            if (field) field.value = button.getAttribute('data-apd-relation-mode-option') || 'all';
            refreshAdaptiveColorEditor(editor);
          });
        });
      }
    });
  }

  function refreshAdaptivePrintingMethodEditor(editor) {
    var modeField = editor.querySelector('[data-apd-palette-mode-input]');
    var mode = String(modeField && modeField.value || 'all');
    editor.dataset.apdPaletteMode = mode;
    var modeRoot = editor.querySelector('[data-apd-relation-mode="palette"]');
    if (modeRoot) {
      modeRoot.querySelectorAll('[data-apd-relation-mode-option]').forEach(function (button) {
        var active = button.getAttribute('data-apd-relation-mode-option') === mode;
        button.classList.toggle('is-active', active);
        button.setAttribute('aria-pressed', active ? 'true' : 'false');
      });
    }
    var optionsRoot = editor.querySelector('[data-apd-palette-color-options]');
    var inputs = optionsRoot ? Array.prototype.slice.call(optionsRoot.querySelectorAll('input[type="checkbox"]')) : [];
    if (optionsRoot) optionsRoot.classList.toggle('is-disabled', mode !== 'restricted');
    inputs.forEach(function (input) { input.disabled = mode !== 'restricted'; });
    var previewInputs = mode === 'all' ? inputs : (mode === 'none' ? [] : inputs.filter(function (input) { return input.checked; }));
    var swatches = editor.querySelector('[data-apd-method-preview-swatches]');
    if (swatches) {
      swatches.innerHTML = '';
      previewInputs.slice(0, 18).forEach(function (input) {
        var swatch = document.createElement('i');
        swatch.style.background = input.getAttribute('data-color-value') || '#dce6f5';
        swatch.title = input.getAttribute('data-color-label') || '';
        swatches.appendChild(swatch);
      });
      if (!previewInputs.length) {
        var empty = document.createElement('span');
        empty.textContent = adminLabel('paletteNoColors', 'No color palette');
        swatches.appendChild(empty);
      }
    }
    var count = editor.querySelector('[data-apd-palette-count]');
    if (count) {
      count.textContent = mode === 'all'
        ? adminLabel('paletteAllColors', 'All colors')
        : (previewInputs.length === 1
          ? adminLabel('paletteOneColor', '1 color')
          : adminLabel('paletteColorCount', '{count} colors', { count: previewInputs.length }));
    }
    var labelField = editor.querySelector('[data-apd-method-sync="label"]');
    var idField = editor.querySelector('[data-apd-method-sync="id"]');
    var label = String(labelField && labelField.value || '').trim();
    if (idField && !idField.dataset.apdTouched && label) idField.value = slugifyFontId(label);
    var previewLabel = editor.querySelector('[data-apd-method-preview-label]');
    if (previewLabel) previewLabel.textContent = label || 'Untitled method';
    var priceField = editor.querySelector('[data-apd-method-sync="price"]');
    var previewPrice = editor.querySelector('[data-apd-method-preview-price]');
    if (previewPrice) previewPrice.textContent = '+' + (String(priceField && priceField.value || '0').trim() || '0');
  }

  function initAdaptivePrintingMethodEditors(scope) {
    scope.querySelectorAll('[data-apd-printing-method-editor]').forEach(function (editor) {
      refreshAdaptivePrintingMethodEditor(editor);
      var modeRoot = editor.querySelector('[data-apd-relation-mode="palette"]');
      if (modeRoot) {
        modeRoot.querySelectorAll('[data-apd-relation-mode-option]').forEach(function (button) {
          button.addEventListener('click', function () {
            var field = editor.querySelector('[data-apd-palette-mode-input]');
            if (field) field.value = button.getAttribute('data-apd-relation-mode-option') || 'all';
            refreshAdaptivePrintingMethodEditor(editor);
          });
        });
      }
      editor.querySelectorAll('[data-apd-palette-color-options] input, [data-apd-method-sync]').forEach(function (field) {
        field.addEventListener('input', function () {
          if (field.getAttribute('data-apd-method-sync') === 'id') field.dataset.apdTouched = '1';
          refreshAdaptivePrintingMethodEditor(editor);
        });
        field.addEventListener('change', function () { refreshAdaptivePrintingMethodEditor(editor); });
      });
    });
  }



  function setJsonFieldValue(root, name, value) {
    var field = findField(name, root);
    if (!field) {
      return;
    }
    field.value = JSON.stringify(value, null, 2);
    field.dispatchEvent(new Event('input', { bubbles: true }));
  }

  function getTemplatePayloadFromPicker(picker, id) {
    if (!picker || !id || id === '0') {
      return null;
    }
    var node = picker.querySelector('[data-apd-template-payload="' + id + '"]');
    if (!node) {
      return null;
    }
    try {
      return JSON.parse(node.textContent || '{}');
    } catch (error) {
      console.error('[APD] Failed to read template payload.', error);
      return null;
    }
  }

  function shouldCopyTemplateMetaKey(key, isProductConfigEditor) {
    if (!key) {
      return false;
    }
    if (!isProductConfigEditor) {
      return true;
    }

    // Product-config editors keep their own saved canvas data. The selected
    // template is used as a starter/fallback in the preview and frontend, but
    // must not overwrite the product document/state or view fields when a
    // product already contains real canvas content.
    return ['_apd_layout', '_apd_views', '_apd_drawing_zones', '_apd_initial_document', '_apd_initial_state'].indexOf(key) === -1;
  }

  function applyTemplatePayload(root, payload) {
    if (!payload || !payload.meta) {
      return;
    }
    var body = document.body;
    var isProductConfigEditor = !!(
      (body && body.classList && (body.classList.contains('post-type-apd_product_config') || body.classList.contains('post-type-apd_custom_product')))
      || (root && root.getAttribute && root.getAttribute('data-apd-product-config-editor') === '1')
    );
    var preview = root.querySelector('.js-apd-live-preview');
    if (isProductConfigEditor) {
      root.__apdSelectedTemplatePayload = payload;
    }

    Object.keys(payload.meta).forEach(function (key) {
      if (!shouldCopyTemplateMetaKey(key, isProductConfigEditor)) {
        return;
      }
      var field = findField(key, root);
      if (!field) {
        return;
      }
      field.value = String(payload.meta[key] || '');
      field.dispatchEvent(new Event('input', { bubbles: true }));
    });

    if (preview) {
      mountPreview(preview);
    }
  }

  function initTemplatePickers(scope) {
    scope.querySelectorAll('[data-apd-template-picker]').forEach(function (picker) {
      var root = picker.closest('form#post') || document;
      var select = picker.querySelector('.js-apd-template-select');
      var applyButton = picker.querySelector('.js-apd-apply-template');
      var clearButton = picker.querySelector('.js-apd-clear-template');
      var applySelectedTemplate = function () {
        var id = select && select.value ? String(select.value) : '0';
        var payload = getTemplatePayloadFromPicker(picker, id);
        if (payload) {
          applyTemplatePayload(root, payload);
        }
      };
      if (applyButton) {
        applyButton.addEventListener('click', applySelectedTemplate);
      }
      if (select) {
        select.addEventListener('change', applySelectedTemplate);
      }
      if (clearButton) {
        clearButton.addEventListener('click', function () {
          root.__apdSelectedTemplatePayload = null;
          if (select) {
            select.value = '0';
            select.dispatchEvent(new Event('change', { bubbles: true }));
          }
        });
      }
    });
  }

  function getTemplateSharedPortalSelector(panel) {
    var field = panel && panel.querySelector('[data-apd-template-shared-portal]');
    return field ? String(field.value || '').trim() : '';
  }

  function resolveTemplateModuleMountTarget(row, panel) {
    if (!row) {
      return '';
    }
    var portalModeField = row.querySelector('[data-apd-template-module-portal-mode]');
    var portalMode = portalModeField && portalModeField.value ? String(portalModeField.value).trim() : 'internal';
    if (portalMode === 'shared') {
      return getTemplateSharedPortalSelector(panel || row.closest('[data-apd-template-modules]'));
    }
    if (portalMode === 'body') {
      return 'body';
    }
    if (portalMode === 'custom') {
      var customField = row.querySelector('[data-apd-template-module-mount-target]');
      return customField ? String(customField.value || '').trim() : '';
    }
    return '';
  }

  function readTemplateModuleRow(row, panel) {
    if (!row) {
      return null;
    }
    var key = row.getAttribute('data-apd-template-module-item') || '';
    if (!key) {
      return null;
    }
    var enabledField = row.querySelector('.js-apd-template-module-enabled');
    var enabled = !!(enabledField && enabledField.checked);
    var available = !(enabledField && enabledField.disabled);
    var zoneField = row.querySelector('[data-apd-template-module-zone]');
    var labelField = row.querySelector('[data-apd-template-module-label-input]');
    var typeField = row.querySelector('[data-apd-template-module-type]');
    var collapsedField = row.querySelector('[data-apd-template-module-collapsed]');
    var collapsibleField = row.querySelector('[data-apd-template-module-collapsible]');
    var layoutMenuField = row.querySelector('[data-apd-template-module-layout-menu]');
    var selectionMenuField = row.querySelector('[data-apd-template-module-selection-menu]');
    var pageContentField = row.querySelector('[data-apd-template-module-page-content]');
    var iconModeField = row.querySelector('[data-apd-template-module-icon-mode]');
    var iconTextField = row.querySelector('[data-apd-template-module-icon-text]');
    var iconImageField = row.querySelector('[data-apd-template-module-icon-image]');
    var iconMode = iconModeField && iconModeField.value === 'image' ? 'image' : 'text';
    var iconText = normalizeApdMenuIconName(iconTextField && iconTextField.value ? String(iconTextField.value).trim() : 'puzzle', key);
    var iconImage = iconImageField && iconImageField.value ? String(iconImageField.value).trim() : '';
    var portalModeField = row.querySelector('[data-apd-template-module-portal-mode]');
    var portalMode = portalModeField && portalModeField.value ? String(portalModeField.value).trim() : 'internal';
    var permissionRequirements = String(row.getAttribute('data-apd-template-permission-requirements') || '')
      .split('|')
      .map(function (permissionKey) { return permissionKey.trim(); })
      .filter(Boolean);
    return {
      key: key,
      label: labelField && labelField.value ? String(labelField.value).trim() : key,
      zone: zoneField && ['left', 'right', 'top', 'bottom'].indexOf(zoneField.value) !== -1 ? zoneField.value : 'left',
      type: typeField && typeField.value ? String(typeField.value).trim() : 'floating',
      collapsed: !!(collapsedField && collapsedField.checked),
      collapsible: !(collapsibleField && !collapsibleField.checked),
      iconMode: iconMode,
      iconText: iconText,
      iconImage: iconImage,
      icon: iconMode === 'image' && iconImage ? iconImage : (iconText || 'puzzle'),
      portalMode: portalMode,
      mountTarget: resolveTemplateModuleMountTarget(row, panel || row.closest('[data-apd-template-modules]')),
      enabled: enabled,
      available: available,
      layoutMenu: !!(layoutMenuField && layoutMenuField.checked),
      selectionMenu: !!(selectionMenuField && selectionMenuField.checked),
      pageContent: !!(pageContentField && pageContentField.checked),
      permissionRequirements: permissionRequirements
    };
  }

  function collectTemplateModuleState(panel) {
    var modules = [];
    panel.querySelectorAll('[data-apd-template-module-item]').forEach(function (row) {
      var item = readTemplateModuleRow(row, panel);
      if (item && item.enabled && item.available) {
        modules.push(item);
      }
    });
    return modules;
  }

  function collectTemplateLayoutState(panel) {
    return collectTemplateModuleState(panel).filter(function (item) {
      return item.layoutMenu || item.pageContent;
    });
  }

  function readTemplateZoneSettings(panel) {
    var settings = {
      left: { type: 'default', collapsed: false, collapsible: true },
      right: { type: 'default', collapsed: false, collapsible: true },
      top: { type: 'default', collapsed: true, collapsible: true },
      bottom: { type: 'default', collapsed: false, collapsible: true }
    };
    if (!panel) {
      return settings;
    }
    panel.querySelectorAll('[data-apd-template-zone-card]').forEach(function (card) {
      var zone = card.getAttribute('data-apd-template-zone-card') || '';
      if (!settings[zone]) {
        return;
      }
      var typeField = card.querySelector('[data-apd-template-zone-type]');
      var collapsedField = card.querySelector('[data-apd-template-zone-collapsed]');
      var collapsibleField = card.querySelector('[data-apd-template-zone-collapsible]');
      settings[zone] = {
        type: normalizeTemplateZoneTypeValue(typeField && typeField.value ? String(typeField.value).trim() : settings[zone].type),
        collapsed: !!(collapsedField && collapsedField.checked),
        collapsible: !(collapsibleField && !collapsibleField.checked)
      };
    });
    return settings;
  }


  function normalizeTemplateZoneRuntimeType(raw) {
    return normalizeTemplateZoneTypeValue(raw);
  }

  function setTemplateZoneCollapsibleRuntime(instance, zone, value) {
    if (!instance || !zone) {
      return false;
    }
    if (instance.layout && typeof instance.layout.setZoneCollapsible === 'function') {
      instance.layout.setZoneCollapsible(zone, !!value);
      return true;
    }
    if (instance.store && typeof instance.store.getState === 'function') {
      var storeState = instance.store.getState();
      if (storeState && typeof storeState.setZoneCollapsible === 'function') {
        storeState.setZoneCollapsible(zone, !!value);
        return true;
      }
    }
    return false;
  }

  function buildTemplateLayoutPayloadFromPanel(root, panel) {
    var currentLayout = safeParse(findField('_apd_layout', root) && findField('_apd_layout', root).value, {});
    var zoneSettings = readTemplateZoneSettings(panel);
    var layout = {
      height: currentLayout && currentLayout.height ? currentLayout.height : '820px',
      left: { items: [], type: zoneSettings.left.type, collapsed: zoneSettings.left.collapsed, collapsible: zoneSettings.left.collapsible },
      right: { items: [], type: zoneSettings.right.type, collapsed: zoneSettings.right.collapsed, collapsible: zoneSettings.right.collapsible },
      top: { items: [], type: zoneSettings.top.type, collapsed: zoneSettings.top.collapsed, collapsible: zoneSettings.top.collapsible },
      bottom: { items: [], type: zoneSettings.bottom.type, collapsed: zoneSettings.bottom.collapsed, collapsible: zoneSettings.bottom.collapsible }
    };
    collectTemplateLayoutState(panel).forEach(function (item) {
      var zone = ['left', 'right', 'top', 'bottom'].indexOf(item.zone) !== -1 ? item.zone : 'left';
      var layoutItem = { id: item.key, kind: item.key, label: item.label || item.key, icon: normalizeApdMenuIconName(item.icon || 'puzzle', item.key), zone: zone, type: resolveTemplateModuleRuntimeType(item), collapsed: !!item.collapsed, collapsible: item.collapsible !== false };
      if (item.pageContent) {
        layoutItem.renderMode = 'portal';
      }
      if (layoutItem.type !== 'inline' && item.mountTarget) {
        layoutItem.mountTarget = item.mountTarget;
      }
      layout[zone].items.push(layoutItem);
    });
    ['left', 'right', 'top', 'bottom'].forEach(function (zone) {
      layout[zone].items = orderTemplateLayoutItemsByExistingLayout(layout[zone].items, currentLayout, zone);
    });
    return layout;
  }

  function buildTemplatePermissionsFromPanel(root, panel) {
    var current = safeParse(findField('_apd_permissions', root) && findField('_apd_permissions', root).value, {});
    current = current && typeof current === 'object' ? current : {};
    var moduleOpen = current.moduleOpen && typeof current.moduleOpen === 'object' ? current.moduleOpen : {};
    panel.querySelectorAll('[data-apd-template-module-item]').forEach(function (row) {
      var item = readTemplateModuleRow(row, panel);
      if (item && item.key) {
        moduleOpen[item.key] = !!item.enabled;
        if (item.enabled) {
          item.permissionRequirements.forEach(function (permissionKey) {
            current[permissionKey] = true;
          });
        }
      }
    });
    current.canOpenModule = true;
    current.moduleOpen = moduleOpen;
    return current;
  }

  function buildTemplateSelectionMenuFromPanel(root, panel) {
    var current = safeParse(findField('_apd_selection_menu', root) && findField('_apd_selection_menu', root).value, {});
    current = current && typeof current === 'object' ? current : {};
    current.type = current.type || 'popover';
    current.open = false;
    current.items = collectTemplateModuleState(panel).filter(function (item) {
      return item.selectionMenu;
    }).map(function (item) { return item.key; });
    return current;
  }

  function templateItemsEqual(a, b) {
    if (!a || !b) {
      return false;
    }
    return a.key === b.key
      && a.label === b.label
      && a.zone === b.zone
      && a.type === b.type
      && a.icon === b.icon
      && String(a.mountTarget || '') === String(b.mountTarget || '')
      && (a.pageContent ? 'portal' : '') === (b.pageContent ? 'portal' : '')
      && !!a.collapsed === !!b.collapsed
      && (a.collapsible !== false) === (b.collapsible !== false);
  }

  function resolveTemplateModuleRuntimeType(item) {
    if (!item) {
      return 'floating';
    }
    var zone = ['left', 'right', 'top', 'bottom'].indexOf(item.zone) !== -1 ? item.zone : 'left';
    var type = item.type ? String(item.type).trim() : 'floating';
    if (type === 'panel') {
      return 'panel-' + zone;
    }
    if (['floating', 'popover', 'modal', 'inline', 'drawer', 'overlay', 'panel-left', 'panel-right', 'panel-top', 'panel-bottom'].indexOf(type) === -1) {
      return 'floating';
    }
    return type;
  }

  function syncTemplateIconMode(panel) {
    panel.querySelectorAll('[data-apd-template-module-item]').forEach(function (row) {
      var modeField = row.querySelector('[data-apd-template-module-icon-mode]');
      var enabledField = row.querySelector('.js-apd-template-module-enabled');
      var mode = modeField && modeField.value === 'image' ? 'image' : 'text';
      var enabled = !!(enabledField && enabledField.checked && !enabledField.disabled);
      var layoutMenuField = row.querySelector('[data-apd-template-module-layout-menu]');
      var selectionMenuField = row.querySelector('[data-apd-template-module-selection-menu]');
      var pageContentField = row.querySelector('[data-apd-template-module-page-content]');
      var inLayout = !!(layoutMenuField && layoutMenuField.checked);
      var inSelectionMenu = !!(selectionMenuField && selectionMenuField.checked);
      var inPageContent = !!(pageContentField && pageContentField.checked);
      if (inPageContent && inLayout && layoutMenuField) {
        layoutMenuField.checked = false;
        inLayout = false;
      }
      var typeField = row.querySelector('[data-apd-template-module-type]');
      var portalModeField = row.querySelector('[data-apd-template-module-portal-mode]');
      if (inPageContent && portalModeField && (!portalModeField.value || portalModeField.value === 'internal')) {
        portalModeField.value = 'body';
      }
      var portalMode = portalModeField && portalModeField.value ? String(portalModeField.value) : 'internal';
      var zoneField = row.querySelector('[data-apd-template-module-zone]');
      var zoneBadge = row.querySelector('[data-apd-template-zone-badge]');
      var selectionBadge = row.querySelector('[data-apd-template-selection-badge]');
      var pageBadge = row.querySelector('[data-apd-template-page-badge]');
      var portalBadge = row.querySelector('[data-apd-template-portal-badge]');
      var unplacedBadge = row.querySelector('[data-apd-template-unplaced-badge]');
      var layoutSettings = row.querySelector('[data-apd-template-layout-settings]');
      var menuPlacementSettings = row.querySelector('[data-apd-template-menu-placement-settings]');
      var panelBehavior = row.querySelector('[data-apd-template-panel-behavior]');
      var destinationTitle = row.querySelector('[data-apd-template-destination-title]');
      var destinationDescription = row.querySelector('[data-apd-template-destination-description]');
      var emptyPlacement = row.querySelector('[data-apd-template-placement-empty]');
      row.classList.toggle('is-icon-image', mode === 'image');
      row.classList.toggle('is-icon-text', mode !== 'image');
      row.classList.toggle('is-enabled', enabled);
      row.classList.toggle('is-disabled', !enabled);
      row.classList.toggle('is-in-layout', inLayout);
      row.classList.toggle('is-in-selection', inSelectionMenu);
      row.classList.toggle('is-in-page', inPageContent);
      row.classList.toggle('is-inline', !!(typeField && typeField.value === 'inline'));
      row.setAttribute('data-apd-template-portal-mode-current', portalMode);
      if (zoneBadge && zoneField && zoneField.options[zoneField.selectedIndex]) {
        zoneBadge.textContent = zoneField.options[zoneField.selectedIndex].textContent || zoneField.value;
        zoneBadge.hidden = !inLayout;
      }
      if (selectionBadge) {
        selectionBadge.hidden = !inSelectionMenu;
      }
      if (pageBadge) {
        pageBadge.hidden = !inPageContent;
      }
      if (portalBadge && portalModeField && portalModeField.options[portalModeField.selectedIndex]) {
        portalBadge.textContent = portalModeField.options[portalModeField.selectedIndex].textContent || portalMode;
        portalBadge.hidden = !(inLayout || inSelectionMenu || inPageContent);
      }
      if (unplacedBadge) {
        unplacedBadge.hidden = inLayout || inSelectionMenu || inPageContent;
      }
      if (layoutSettings) {
        layoutSettings.hidden = !(inLayout || inSelectionMenu || inPageContent);
      }
      if (menuPlacementSettings) {
        menuPlacementSettings.hidden = !inLayout;
      }
      if (panelBehavior) {
        panelBehavior.hidden = !inLayout;
      }
      if (destinationTitle) {
        destinationTitle.textContent = inPageContent
          ? adminLabel('templatePageDestinationTitle', 'Page content destination')
          : adminLabel('templateOverlayDestinationTitle', 'Opened module destination');
      }
      if (destinationDescription) {
        destinationDescription.textContent = inPageContent
          ? adminLabel('templatePageDestinationDescription', 'The module stays mounted in this page host.')
          : adminLabel('templateOverlayDestinationDescription', 'Choose where the module panel opens.');
      }
      if (emptyPlacement) {
        emptyPlacement.hidden = inLayout || inSelectionMenu || inPageContent;
      }
    });
  }

  function activateTemplateWorkspaceTab(panel, tabName) {
    var nextTab = tabName || 'modules';
    panel.querySelectorAll('[data-apd-template-tab]').forEach(function (button) {
      var active = button.getAttribute('data-apd-template-tab') === nextTab;
      button.classList.toggle('is-active', active);
      button.setAttribute('aria-selected', active ? 'true' : 'false');
    });
    panel.querySelectorAll('[data-apd-template-panel]').forEach(function (section) {
      var active = section.getAttribute('data-apd-template-panel') === nextTab;
      section.classList.toggle('is-active', active);
      section.hidden = !active;
    });
  }

  function updateTemplateModuleFilters(panel) {
    var searchField = panel.querySelector('[data-apd-template-search]');
    var categoryField = panel.querySelector('[data-apd-template-category-filter]');
    var activeOnlyField = panel.querySelector('[data-apd-template-active-filter]');
    var query = searchField ? String(searchField.value || '').trim().toLowerCase() : '';
    var category = categoryField ? String(categoryField.value || 'all') : 'all';
    var activeOnly = !!(activeOnlyField && activeOnlyField.checked);
    var totalActive = 0;
    var totalVisible = 0;

    panel.querySelectorAll('[data-apd-template-category]').forEach(function (group) {
      var groupCategory = group.getAttribute('data-apd-template-category') || '';
      var categoryMatches = category === 'all' || groupCategory === category;
      var groupActive = 0;
      var groupVisible = 0;

      group.querySelectorAll('[data-apd-template-module-item]').forEach(function (row) {
        var enabledField = row.querySelector('.js-apd-template-module-enabled');
        var enabled = !!(enabledField && enabledField.checked && !enabledField.disabled);
        var searchText = String(row.getAttribute('data-apd-template-search-text') || '').toLowerCase();
        var visible = categoryMatches
          && (!query || searchText.indexOf(query) !== -1)
          && (!activeOnly || enabled);
        if (enabled) {
          groupActive++;
          totalActive++;
        }
        if (visible) {
          groupVisible++;
          totalVisible++;
        }
        row.hidden = !visible;
      });

      group.hidden = groupVisible === 0;
      var count = group.querySelector('[data-apd-template-group-count]');
      if (count) {
        count.textContent = adminLabel('templateActiveCount', '{count} active', { count: groupActive });
      }
    });

    var activeCount = panel.querySelector('[data-apd-template-active-count]');
    if (activeCount) {
      activeCount.textContent = String(totalActive);
    }
    var empty = panel.querySelector('[data-apd-template-empty-filter]');
    if (empty) {
      empty.hidden = totalVisible !== 0;
    }
  }

  function getTemplatePortalExampleMarkup(selector) {
    var value = String(selector || '').trim();
    var idMatch = value.match(/^#([A-Za-z][A-Za-z0-9_:\-.]*)$/);
    if (idMatch) {
      return '<div id="' + idMatch[1] + '"></div>';
    }
    var classMatch = value.match(/^\.([A-Za-z][A-Za-z0-9_:\-.]*)$/);
    if (classMatch) {
      return '<div class="' + classMatch[1] + '"></div>';
    }
    var attributeMatch = value.match(/^\[([A-Za-z_:][A-Za-z0-9_:.\-]*)\]$/);
    if (attributeMatch) {
      return '<div ' + attributeMatch[1] + '></div>';
    }
    if (value === 'body' || value === 'document.body') {
      return '<!-- Modules open in document.body -->';
    }
    return '<div data-asuka-module-portal></div>';
  }

  function setTemplatePortalStatus(status, state, message) {
    if (!status) {
      return;
    }
    status.classList.remove('is-valid', 'is-warning', 'is-error');
    if (state) {
      status.classList.add('is-' + state);
    }
    status.textContent = message || '';
  }

  function updateTemplatePortalUi(panel) {
    var selector = getTemplateSharedPortalSelector(panel);
    var status = panel.querySelector('[data-apd-template-portal-status]');
    var code = panel.querySelector('[data-apd-template-portal-code]');
    if (code) {
      code.textContent = getTemplatePortalExampleMarkup(selector);
    }

    if (!selector) {
      setTemplatePortalStatus(status, 'warning', adminLabel('templatePortalMissing', 'Add a selector before assigning modules to the shared portal.'));
    } else {
      try {
        var match = document.querySelector(selector);
        setTemplatePortalStatus(
          status,
          match ? 'valid' : 'warning',
          match
            ? adminLabel('templatePortalFound', 'Portal found on this page.')
            : adminLabel('templatePortalValid', 'Valid selector. The portal will be resolved on the customer page.')
        );
      } catch (error) {
        setTemplatePortalStatus(status, 'error', adminLabel('templatePortalInvalid', 'This CSS selector is not valid.'));
      }
    }

    panel.querySelectorAll('[data-apd-template-portal-shared-note]').forEach(function (note) {
      var codeNode = note.querySelector('code');
      if (codeNode) {
        codeNode.textContent = selector || adminLabel('templatePortalNotConfigured', 'not configured');
      }
    });

    panel.querySelectorAll('[data-apd-template-module-item]').forEach(function (row) {
      var portalModeField = row.querySelector('[data-apd-template-module-portal-mode]');
      var customField = row.querySelector('[data-apd-template-module-mount-target]');
      var portalMode = portalModeField && portalModeField.value ? String(portalModeField.value) : 'internal';
      row.classList.remove('has-invalid-portal');
      if (portalMode === 'custom' && customField && String(customField.value || '').trim()) {
        try {
          document.querySelector(String(customField.value || '').trim());
        } catch (error) {
          row.classList.add('has-invalid-portal');
        }
      }
    });
  }

  function syncTemplateWorkspaceUi(panel) {
    syncTemplateIconMode(panel);
    updateTemplateModuleFilters(panel);
    updateTemplatePortalUi(panel);
  }

  function enforceTemplatePlacementChoice(row, changedField) {
    if (!row || !changedField || !changedField.checked) {
      return;
    }
    var layoutField = row.querySelector('[data-apd-template-module-layout-menu]');
    var pageField = row.querySelector('[data-apd-template-module-page-content]');
    if (changedField === pageField && layoutField) {
      layoutField.checked = false;
    } else if (changedField === layoutField && pageField) {
      pageField.checked = false;
    }
  }

  function copyTemplatePortalMarkup(panel, button) {
    var code = panel.querySelector('[data-apd-template-portal-code]');
    var value = code ? String(code.textContent || '') : '';
    if (!value) {
      return;
    }
    var complete = function () {
      var previous = button.textContent;
      button.textContent = adminLabel('copiedShort', 'Copied');
      window.setTimeout(function () { button.textContent = previous; }, 1400);
    };
    if (navigator.clipboard && typeof navigator.clipboard.writeText === 'function') {
      navigator.clipboard.writeText(value).then(complete).catch(function () {
        window.prompt(adminLabel('copyFailed', 'Unable to copy automatically. You can copy it manually from the document.'), value);
      });
      return;
    }
    window.prompt(adminLabel('copyFailed', 'Unable to copy automatically. You can copy it manually from the document.'), value);
  }

  function applyTemplateZoneRuntimeChange(root, panel, zone) {
    if (!zone) {
      return;
    }
    var preview = root.querySelector('.js-apd-live-preview');
    var instance = preview && preview.__apdInstance;
    var layout = instance && instance.layout;
    if (!preview || !layout) {
      return;
    }
    var zoneSettings = readTemplateZoneSettings(panel);
    var nextZone = zoneSettings[zone];
    if (!nextZone) {
      return;
    }
    var shouldRemount = false;
    var runtimeZoneType = normalizeTemplateZoneRuntimeType(nextZone.type);
    if (typeof layout.setZoneType === 'function') {
      try { layout.setZoneType(zone, runtimeZoneType); } catch (error) { shouldRemount = true; }
    } else {
      shouldRemount = true;
    }
    if (typeof layout.setZoneCollapsed === 'function') {
      try { layout.setZoneCollapsed(zone, !!nextZone.collapsed); } catch (error) { shouldRemount = true; }
    } else {
      shouldRemount = true;
    }
    try {
      if (!setTemplateZoneCollapsibleRuntime(instance, zone, nextZone.collapsible !== false)) {
        shouldRemount = true;
      }
    } catch (error) {
      shouldRemount = true;
    }
    var previousZoneSettings = preview.__apdTemplateZoneSettings && typeof preview.__apdTemplateZoneSettings === 'object' ? preview.__apdTemplateZoneSettings : {};
    preview.__apdTemplateZoneSettings = Object.assign({}, previousZoneSettings, (function () {
      var patch = {};
      patch[zone] = nextZone;
      return patch;
    })());
    if (shouldRemount) {
      mountPreview(preview);
    }
  }

  function applyTemplateItemRuntimeChange(root, panel, key) {
    if (!key) {
      return;
    }
    syncTemplateIconMode(panel);
    var preview = root.querySelector('.js-apd-live-preview');
    var instance = preview && preview.__apdInstance;
    var layout = instance && instance.layout;
    if (!preview || !layout || typeof layout.addItem !== 'function' || typeof layout.removeItem !== 'function') {
      return;
    }

    var nextItems = collectTemplateLayoutState(panel);
    var nextMap = {};
    nextItems.forEach(function (item) { nextMap[item.key] = item; });
    var previousMap = preview.__apdTemplateModuleStateMap && typeof preview.__apdTemplateModuleStateMap === 'object' ? preview.__apdTemplateModuleStateMap : {};
    var previousItem = previousMap[key] || null;
    var nextItem = nextMap[key] || null;
    var shouldRemount = false;
    var hasMoveBlock = typeof layout.moveBlock === 'function';
    var hasUpdateItem = typeof layout.updateItem === 'function';
    var hasSetBlockCollapsed = typeof layout.setBlockCollapsed === 'function';
    var hasSetBlockCollapsible = typeof layout.setBlockCollapsible === 'function';

    if (!nextItem) {
      try { layout.removeItem(key); } catch (error) { shouldRemount = true; }
    } else if (!previousItem) {
      try {
        layout.addItem(nextItem.zone, {
          id: nextItem.key,
          kind: nextItem.key,
          label: nextItem.label || nextItem.key,
          icon: nextItem.icon || 'puzzle',
          zone: nextItem.zone,
          type: resolveTemplateModuleRuntimeType(nextItem),
          mountTarget: nextItem.mountTarget || undefined,
          renderMode: nextItem.pageContent ? 'portal' : undefined,
          collapsed: !!nextItem.collapsed,
          collapsible: nextItem.collapsible !== false
        }, nextItems.findIndex(function (entry) { return entry.key === nextItem.key; }));
      } catch (error) { shouldRemount = true; }
    } else {
      var patch = {};
      if (previousItem.zone !== nextItem.zone) {
        if (hasMoveBlock) {
          try { layout.moveBlock(key, nextItem.zone); } catch (error) { shouldRemount = true; }
        } else {
          try { layout.removeItem(key); } catch (error) { shouldRemount = true; }
          try {
            layout.addItem(nextItem.zone, {
              id: nextItem.key,
              kind: nextItem.key,
              label: nextItem.label || nextItem.key,
              icon: nextItem.icon || 'puzzle',
              zone: nextItem.zone,
              type: resolveTemplateModuleRuntimeType(nextItem),
              mountTarget: nextItem.mountTarget || undefined,
              renderMode: nextItem.pageContent ? 'portal' : undefined,
              collapsed: !!nextItem.collapsed,
              collapsible: nextItem.collapsible !== false
            });
          } catch (error) { shouldRemount = true; }
        }
      }
      if (previousItem.label !== nextItem.label) {
        patch.label = nextItem.label || nextItem.key;
      }
      if (previousItem.icon !== nextItem.icon) {
        patch.icon = nextItem.icon || 'puzzle';
      }
      if (previousItem.type !== nextItem.type) {
        patch.type = resolveTemplateModuleRuntimeType(nextItem);
      }
      if (String(previousItem.mountTarget || '') !== String(nextItem.mountTarget || '')) {
        patch.mountTarget = nextItem.mountTarget || null;
      }
      if (!!previousItem.pageContent !== !!nextItem.pageContent) {
        patch.renderMode = nextItem.pageContent ? 'portal' : null;
      }
      if (Object.keys(patch).length) {
        if (hasUpdateItem) {
          try { layout.updateItem(key, patch); } catch (error) { shouldRemount = true; }
        } else {
          shouldRemount = true;
        }
      }
      if (!!previousItem.collapsed !== !!nextItem.collapsed) {
        if (hasSetBlockCollapsed) {
          try { layout.setBlockCollapsed(key, !!nextItem.collapsed); } catch (error) { shouldRemount = true; }
        } else {
          shouldRemount = true;
        }
      }
      if ((previousItem.collapsible !== false) !== (nextItem.collapsible !== false)) {
        if (hasSetBlockCollapsible) {
          try { layout.setBlockCollapsible(key, nextItem.collapsible !== false); } catch (error) { shouldRemount = true; }
        } else if (hasUpdateItem) {
          try { layout.updateItem(key, { collapsible: nextItem.collapsible !== false }); } catch (error) { shouldRemount = true; }
        } else {
          shouldRemount = true;
        }
      }
    }

    preview.__apdTemplateModuleStateMap = nextMap;
    if (shouldRemount) {
      mountPreview(preview);
    }
  }

  function syncTemplateModules(root, panel, allowMount) {
    var permissionsField = findField('_apd_permissions', root);
    var previousStoredPermissions = safeParse(permissionsField && permissionsField.value, {});
    var layout = buildTemplateLayoutPayloadFromPanel(root, panel);
    var permissions = buildTemplatePermissionsFromPanel(root, panel);
    var selectionMenu = buildTemplateSelectionMenuFromPanel(root, panel);
    var zoneSettings = readTemplateZoneSettings(panel);
    setJsonFieldValue(root, '_apd_layout', layout);
    setJsonFieldValue(root, '_apd_permissions', permissions);
    setJsonFieldValue(root, '_apd_selection_menu', selectionMenu);

    syncTemplateIconMode(panel);

    var preview = root.querySelector('.js-apd-live-preview');
    if (!preview) {
      return;
    }

    var nextItems = collectTemplateLayoutState(panel);
    var nextMap = {};
    nextItems.forEach(function (item) { nextMap[item.key] = item; });
    var previousMap = preview.__apdTemplateModuleStateMap && typeof preview.__apdTemplateModuleStateMap === 'object' ? preview.__apdTemplateModuleStateMap : {};
    var previousZoneSettings = preview.__apdTemplateZoneSettings && typeof preview.__apdTemplateZoneSettings === 'object' ? preview.__apdTemplateZoneSettings : {};
    var previousSelectionMenu = preview.__apdTemplateSelectionMenu && typeof preview.__apdTemplateSelectionMenu === 'object' ? preview.__apdTemplateSelectionMenu : {};
    var previousPermissions = preview.__apdTemplatePermissions && typeof preview.__apdTemplatePermissions === 'object'
      ? preview.__apdTemplatePermissions
      : previousStoredPermissions;
    var permissionsChanged = JSON.stringify(previousPermissions || {}) !== JSON.stringify(permissions || {});
    var instance = preview.__apdInstance;

    if (instance && instance.layout && typeof instance.layout.addItem === 'function' && typeof instance.layout.removeItem === 'function') {
      var shouldRemount = permissionsChanged;
      var hasUpdateItem = typeof instance.layout.updateItem === 'function';
      var hasSetBlockCollapsed = typeof instance.layout.setBlockCollapsed === 'function';
      var hasSetBlockCollapsible = typeof instance.layout.setBlockCollapsible === 'function';
      var hasSetZoneType = typeof instance.layout.setZoneType === 'function';
      var hasSetZoneCollapsed = typeof instance.layout.setZoneCollapsed === 'function';
      var hasSelectionMenuApi = !!(instance.layout.selectionMenu && typeof instance.layout.selectionMenu.setItems === 'function' && typeof instance.layout.selectionMenu.setType === 'function');
      var managedKeys = {};

      panel.querySelectorAll('[data-apd-template-module-item]').forEach(function (row) {
        var key = row.getAttribute('data-apd-template-module-item') || '';
        if (key) {
          managedKeys[key] = true;
        }
      });

      ['left', 'right', 'top', 'bottom'].forEach(function (zone) {
        var nextZone = zoneSettings[zone] || { type: 'default', collapsed: zone === 'top', collapsible: true };
        var previousZone = previousZoneSettings[zone] || {};
        var previousRuntimeZoneType = normalizeTemplateZoneRuntimeType(previousZone.type);
        var nextRuntimeZoneType = normalizeTemplateZoneRuntimeType(nextZone.type);
        if (previousRuntimeZoneType !== nextRuntimeZoneType) {
          if (hasSetZoneType) {
            try { instance.layout.setZoneType(zone, nextRuntimeZoneType); } catch (error) { shouldRemount = true; }
          } else {
            shouldRemount = true;
          }
        }
        if (!!previousZone.collapsed !== !!nextZone.collapsed) {
          if (hasSetZoneCollapsed) {
            try { instance.layout.setZoneCollapsed(zone, !!nextZone.collapsed); } catch (error) { shouldRemount = true; }
          } else {
            shouldRemount = true;
          }
        }
        if ((previousZone.collapsible !== false) !== (nextZone.collapsible !== false)) {
          try {
            if (!setTemplateZoneCollapsibleRuntime(instance, zone, nextZone.collapsible !== false)) {
              shouldRemount = true;
            }
          } catch (error) {
            shouldRemount = true;
          }
        }
      });

      if (hasSelectionMenuApi) {
        var nextSelectionItems = Array.isArray(selectionMenu.items) ? selectionMenu.items.slice() : [];
        var previousSelectionItems = Array.isArray(previousSelectionMenu.items) ? previousSelectionMenu.items.slice() : [];
        var selectionItemsChanged = nextSelectionItems.join('|') !== previousSelectionItems.join('|');
        if (selectionItemsChanged) {
          try { instance.layout.selectionMenu.setItems(nextSelectionItems); } catch (error) { shouldRemount = true; }
        }
        if ((previousSelectionMenu.type || 'popover') !== (selectionMenu.type || 'popover')) {
          try { instance.layout.selectionMenu.setType(selectionMenu.type || 'popover'); } catch (error) { shouldRemount = true; }
        }
        if (!!previousSelectionMenu.open !== !!selectionMenu.open) {
          try {
            if (selectionMenu.open && typeof instance.layout.selectionMenu.open === 'function') {
              instance.layout.selectionMenu.open();
            } else if (!selectionMenu.open && typeof instance.layout.selectionMenu.close === 'function') {
              instance.layout.selectionMenu.close();
            }
          } catch (error) { shouldRemount = true; }
        }
      } else if (Object.keys(previousSelectionMenu).length || Object.keys(selectionMenu).length) {
        shouldRemount = true;
      }

      Object.keys(managedKeys).forEach(function (key) {
        var previousItem = previousMap[key] || null;
        var nextItem = nextMap[key] || null;

        if (!nextItem) {
          try { instance.layout.removeItem(key); } catch (error) { shouldRemount = true; }
          return;
        }

        if (!previousItem) {
          try {
            instance.layout.addItem(nextItem.zone, {
              id: nextItem.key,
              kind: nextItem.key,
              label: nextItem.label || nextItem.key,
              icon: nextItem.icon || 'puzzle',
              zone: nextItem.zone,
              type: resolveTemplateModuleRuntimeType(nextItem),
              mountTarget: nextItem.mountTarget || undefined,
              renderMode: nextItem.pageContent ? 'portal' : undefined,
              collapsed: !!nextItem.collapsed,
              collapsible: nextItem.collapsible !== false
            }, nextItems.findIndex(function (entry) { return entry.key === nextItem.key; }));
          } catch (error) { shouldRemount = true; }
          return;
        }

        if (previousItem.zone !== nextItem.zone) {
          try { instance.layout.removeItem(key); } catch (error) { shouldRemount = true; }
          try {
            instance.layout.addItem(nextItem.zone, {
              id: nextItem.key,
              kind: nextItem.key,
              label: nextItem.label || nextItem.key,
              icon: nextItem.icon || 'puzzle',
              zone: nextItem.zone,
              type: resolveTemplateModuleRuntimeType(nextItem),
              mountTarget: nextItem.mountTarget || undefined,
              renderMode: nextItem.pageContent ? 'portal' : undefined,
              collapsed: !!nextItem.collapsed,
              collapsible: nextItem.collapsible !== false
            }, nextItems.findIndex(function (entry) { return entry.key === nextItem.key; }));
          } catch (error) { shouldRemount = true; }
          return;
        }

        if (hasUpdateItem) {
          var patch = {};
          if (previousItem.label !== nextItem.label) {
            patch.label = nextItem.label || nextItem.key;
          }
          if (previousItem.icon !== nextItem.icon) {
            patch.icon = nextItem.icon || 'puzzle';
          }
          if (previousItem.type !== nextItem.type) {
            patch.type = resolveTemplateModuleRuntimeType(nextItem);
          }
          if (String(previousItem.mountTarget || '') !== String(nextItem.mountTarget || '')) {
            patch.mountTarget = nextItem.mountTarget || null;
          }
          if (!!previousItem.pageContent !== !!nextItem.pageContent) {
            patch.renderMode = nextItem.pageContent ? 'portal' : null;
          }
          if (Object.keys(patch).length) {
            try { instance.layout.updateItem(key, patch); } catch (error) { shouldRemount = true; }
          }
        } else if (previousItem.label !== nextItem.label || previousItem.icon !== nextItem.icon || previousItem.type !== nextItem.type) {
          shouldRemount = true;
        }

        if (!!previousItem.collapsed !== !!nextItem.collapsed) {
          if (hasSetBlockCollapsed) {
            try { instance.layout.setBlockCollapsed(key, !!nextItem.collapsed); } catch (error) { shouldRemount = true; }
          } else {
            shouldRemount = true;
          }
        }

        if ((previousItem.collapsible !== false) !== (nextItem.collapsible !== false)) {
          if (hasSetBlockCollapsible) {
            try { instance.layout.setBlockCollapsible(key, nextItem.collapsible !== false); } catch (error) { shouldRemount = true; }
          } else if (hasUpdateItem) {
            try { instance.layout.updateItem(key, { collapsible: nextItem.collapsible !== false }); } catch (error) { shouldRemount = true; }
          } else {
            shouldRemount = true;
          }
        }

        if (!hasUpdateItem && !templateItemsEqual(previousItem, nextItem)) {
          shouldRemount = true;
        }
      });

      preview.__apdTemplateModuleStateMap = nextMap;
      preview.__apdTemplateZoneSettings = zoneSettings;
      preview.__apdTemplateSelectionMenu = selectionMenu;
      preview.__apdTemplatePermissions = permissions;

      if (shouldRemount && allowMount !== false) {
        mountPreview(preview);
      }
      return;
    }

    preview.__apdTemplateModuleStateMap = nextMap;
    preview.__apdTemplateZoneSettings = zoneSettings;
    preview.__apdTemplateSelectionMenu = selectionMenu;
    preview.__apdTemplatePermissions = permissions;
    if (allowMount !== false) {
      mountPreview(preview);
    }
  }

  function scheduleTemplateModuleSync(root, panel, allowMount) {
    if (!panel) {
      return;
    }
    panel.__apdTemplateSettingsDirty = true;
    if (panel.__apdTemplateSyncTimer) {
      window.clearTimeout(panel.__apdTemplateSyncTimer);
    }
    panel.__apdTemplateSyncTimer = window.setTimeout(function () {
      panel.__apdTemplateSyncTimer = null;
      syncTemplateModules(root, panel, allowMount !== false);
    }, 80);
  }

  function flushTemplateModuleSync(root, panel, allowMount) {
    if (!panel) {
      return;
    }
    if (panel.__apdTemplateSyncTimer) {
      window.clearTimeout(panel.__apdTemplateSyncTimer);
      panel.__apdTemplateSyncTimer = null;
    }
    syncTemplateModules(root, panel, allowMount !== false);
  }

  function initTemplateModules(scope) {
    scope.querySelectorAll('[data-apd-template-modules]').forEach(function (panel) {
      var root = panel.closest('form#post') || document;
      if (!panel.__apdTemplateWorkspaceInitialized) {
        panel.__apdTemplateWorkspaceInitialized = true;
        panel.querySelectorAll('[data-apd-template-tab]').forEach(function (button) {
          button.addEventListener('click', function () {
            activateTemplateWorkspaceTab(panel, button.getAttribute('data-apd-template-tab') || 'modules');
          });
        });
        var searchField = panel.querySelector('[data-apd-template-search]');
        var categoryField = panel.querySelector('[data-apd-template-category-filter]');
        var activeFilter = panel.querySelector('[data-apd-template-active-filter]');
        if (searchField) {
          searchField.addEventListener('input', function () { updateTemplateModuleFilters(panel); });
        }
        if (categoryField) {
          categoryField.addEventListener('change', function () { updateTemplateModuleFilters(panel); });
        }
        if (activeFilter) {
          activeFilter.addEventListener('change', function () { updateTemplateModuleFilters(panel); });
        }
        var sharedPortalField = panel.querySelector('[data-apd-template-shared-portal]');
        if (sharedPortalField) {
          sharedPortalField.addEventListener('input', function () {
            syncTemplateWorkspaceUi(panel);
            scheduleTemplateModuleSync(root, panel);
          });
          sharedPortalField.addEventListener('change', function () {
            syncTemplateWorkspaceUi(panel);
            scheduleTemplateModuleSync(root, panel);
          });
        }
        var copyPortalButton = panel.querySelector('[data-apd-template-copy-portal]');
        if (copyPortalButton) {
          copyPortalButton.addEventListener('click', function () {
            copyTemplatePortalMarkup(panel, copyPortalButton);
          });
        }
      }
      panel.querySelectorAll('[data-apd-template-module-item] input, [data-apd-template-module-item] select').forEach(function (field) {
        var row = field.closest('[data-apd-template-module-item]');
        var key = row ? (row.getAttribute('data-apd-template-module-item') || '') : '';
        var eventName = field.tagName === 'INPUT' && field.type === 'text' ? 'input' : 'change';
        field.addEventListener(eventName, function () {
          enforceTemplatePlacementChoice(row, field);
          syncTemplateWorkspaceUi(panel);
          applyTemplateItemRuntimeChange(root, panel, key);
          scheduleTemplateModuleSync(root, panel);
        });
        if (eventName !== 'change') {
          field.addEventListener('change', function () {
            enforceTemplatePlacementChoice(row, field);
            syncTemplateWorkspaceUi(panel);
            applyTemplateItemRuntimeChange(root, panel, key);
            scheduleTemplateModuleSync(root, panel);
          });
        }
      });
      panel.querySelectorAll('[data-apd-template-zone-type], [data-apd-template-zone-collapsed], [data-apd-template-zone-collapsible]').forEach(function (field) {
        var card = field.closest('[data-apd-template-zone-card]');
        var zone = card ? (card.getAttribute('data-apd-template-zone-card') || '') : '';
        field.addEventListener('change', function () {
          applyTemplateZoneRuntimeChange(root, panel, zone);
          scheduleTemplateModuleSync(root, panel, false);
        });
      });
      syncTemplateWorkspaceUi(panel);
      syncTemplateModules(root, panel, false);
    });
  }

  function splitPermissionModuleAliases(value) {
    return String(value || '').split('|').map(function (item) { return item.trim(); }).filter(Boolean);
  }

  function syncPermissionLinkedModules(box) {
    if (!box || !box.getAttribute) {
      return;
    }
    var aliases = splitPermissionModuleAliases(box.getAttribute('data-apd-permission-module-aliases') || '');
    if (!aliases.length) {
      return;
    }
    var panel = box.closest('.apd-structured-panel') || document;
    panel.querySelectorAll('[data-apd-module-permission-key]').forEach(function (moduleBox) {
      var key = moduleBox.getAttribute('data-apd-module-permission-key') || '';
      if (aliases.indexOf(key) !== -1) {
        moduleBox.checked = !!box.checked;
      }
    });
  }

  function syncModuleLinkedPermission(box) {
    if (!box || !box.getAttribute) {
      return;
    }
    var key = box.getAttribute('data-apd-module-permission-key') || '';
    if (!key) {
      return;
    }
    var panel = box.closest('.apd-structured-panel') || document;
    panel.querySelectorAll('[data-apd-permission-module-aliases]').forEach(function (permissionBox) {
      var aliases = splitPermissionModuleAliases(permissionBox.getAttribute('data-apd-permission-module-aliases') || '');
      if (aliases.indexOf(key) === -1) {
        return;
      }
      permissionBox.checked = !!box.checked;
      syncPermissionLinkedModules(permissionBox);
    });
  }

  function initAdvancedConfigPanels(scope) {
    scope.querySelectorAll('[data-apd-permission-preset]').forEach(function (button) {
      button.addEventListener('click', function () {
        var preset = button.getAttribute('data-apd-permission-preset') || '';
        var panel = button.closest('.apd-structured-panel') || document;
        var boxes = panel.querySelectorAll('[data-apd-permission-key]');
        var moduleBoxes = panel.querySelectorAll('[data-apd-module-permission-key]');
        var states = {
          default: buildPermissionPreset('default'),
          full: buildPermissionPreset('full'),
          'text-only': buildPermissionPreset('text-only'),
          locked: buildPermissionPreset('locked')
        };
        var state = states[preset] || null;
        if (!state) {
          return;
        }
        boxes.forEach(function (box) {
          var key = box.getAttribute('data-apd-permission-key') || '';
          if (Object.prototype.hasOwnProperty.call(state, key)) {
            box.checked = !!state[key];
          }
        });
        var moduleState = sanitizeObject(state.moduleOpen, {});
        moduleBoxes.forEach(function (box) {
          var key = box.getAttribute('data-apd-module-permission-key') || '';
          box.checked = Object.prototype.hasOwnProperty.call(moduleState, key) ? !!moduleState[key] : preset !== 'locked';
        });
        boxes.forEach(function (box) {
          syncPermissionLinkedModules(box);
        });
      });
    });

    scope.querySelectorAll('[data-apd-permission-module-aliases]').forEach(function (box) {
      box.addEventListener('change', function () {
        syncPermissionLinkedModules(box);
      });
      syncPermissionLinkedModules(box);
    });

    scope.querySelectorAll('[data-apd-module-permission-key]').forEach(function (box) {
      box.addEventListener('change', function () {
        syncModuleLinkedPermission(box);
      });
    });

    scope.querySelectorAll('[data-apd-filter-action]').forEach(function (button) {
      button.addEventListener('click', function () {
        var group = button.getAttribute('data-apd-filter-group') || '';
        var action = button.getAttribute('data-apd-filter-action') || '';
        var list = scope.querySelector('[data-apd-filter-list="' + group + '"]');
        if (!list) {
          return;
        }
        list.querySelectorAll('input[type="checkbox"]').forEach(function (box) {
          box.checked = action === 'all';
        });
      });
    });
  }


  function initTranslationsPage(scope) {
    scope.querySelectorAll('[data-apd-translations-page]').forEach(function (page) {
      var searchField = page.querySelector('[data-apd-translation-search]');
      var namespaceField = page.querySelector('[data-apd-translation-namespace]');
      var visibleCount = page.querySelector('[data-apd-translation-visible-count]');
      var rows = Array.prototype.slice.call(page.querySelectorAll('[data-apd-translation-row]'));
      if (!rows.length) {
        return;
      }

      function refresh() {
        var query = searchField ? String(searchField.value || '').trim().toLowerCase() : '';
        var namespace = namespaceField ? String(namespaceField.value || '').trim() : '';
        var count = 0;
        rows.forEach(function (row) {
          var haystack = [
            row.getAttribute('data-apd-key') || '',
            row.getAttribute('data-apd-default') || '',
            row.getAttribute('data-apd-translation') || ''
          ].join(' ').toLowerCase();
          var rowNamespace = row.getAttribute('data-apd-namespace') || '';
          var matchesQuery = !query || haystack.indexOf(query) !== -1;
          var matchesNamespace = !namespace || rowNamespace === namespace;
          var visible = matchesQuery && matchesNamespace;
          row.hidden = !visible;
          if (visible) {
            count += 1;
          }
        });
        if (visibleCount) {
          visibleCount.textContent = String(count);
        }
      }

      if (searchField) {
        searchField.addEventListener('input', refresh);
      }
      if (namespaceField) {
        namespaceField.addEventListener('change', refresh);
      }
      refresh();
    });
  }

  function initProductCanvas(scope) {
    scope.querySelectorAll('.js-apd-product-canvas').forEach(function (container) {
      var shell = container.querySelector('[data-apd-shell]');
      var syncButton = container.parentElement.querySelector('[data-apd-product-sync]');
      var captureButton = container.parentElement.querySelector('[data-apd-product-capture]');
      var storeField = container.parentElement.querySelector('[name="_apd_product_canvas_document"]');
      var stateField = container.parentElement.querySelector('[name="_apd_product_canvas_state"]');

      function sync(capture, reason) {
        appendDebugLog('productCanvas:sync:start', {
          reason: reason || '',
          capture: !!capture,
          hasShell: !!shell,
          hasBridge: !!(window.APDRuntimeBridge && typeof window.APDRuntimeBridge.syncShell === 'function'),
          beforeFields: summarizeProductCanvasFields(container)
        }, container);
        if (!shell || !window.APDRuntimeBridge || typeof window.APDRuntimeBridge.syncShell !== 'function') {
          appendDebugLog('productCanvas:sync:skipped', {
            reason: reason || '',
            hasShell: !!shell,
            hasBridge: !!(window.APDRuntimeBridge && typeof window.APDRuntimeBridge.syncShell === 'function')
          }, container);
          return;
        }
        var payload = window.APDRuntimeBridge.syncShell(shell);
        appendDebugLog('productCanvas:sync:payload', {
          reason: reason || '',
          document: summarizeDesign(payload && payload.design),
          stateDocument: summarizeDesign(payload && payload.state && typeof payload.state === 'object' ? (payload.state.initialDocument || payload.state.document || payload.state) : null),
          state: summarizeDesign(payload && payload.state)
        }, container);
        if (storeField) {
          storeField.value = JSON.stringify((payload && payload.design) || {}, null, 2);
          storeField.dispatchEvent(new Event('input', { bubbles: true }));
          storeField.dispatchEvent(new Event('change', { bubbles: true }));
        }
        if (stateField) {
          stateField.value = JSON.stringify((payload && payload.state) || {}, null, 2);
          stateField.dispatchEvent(new Event('input', { bubbles: true }));
          stateField.dispatchEvent(new Event('change', { bubbles: true }));
        }
        if (capture) {
          var initialField = shell.querySelector('[data-apd-design-json]');
          if (initialField && !initialField.value && storeField) {
            initialField.value = storeField.value;
          }
        }
        appendDebugLog('productCanvas:sync:after-field-write', {
          reason: reason || '',
          afterFields: summarizeProductCanvasFields(container)
        }, container);
      }

      if (syncButton) {
        syncButton.addEventListener('click', function () { sync(false, 'manual-sync-button'); });
      }
      if (captureButton) {
        captureButton.addEventListener('click', function () { sync(true, 'manual-capture-button'); });
      }

      var form = container.closest('form#post') || document.querySelector('form#post');
      if (form && !container.__apdProductCanvasFormSyncBound) {
        container.__apdProductCanvasFormSyncBound = true;
        form.addEventListener('submit', function () { sync(false, 'form-submit'); });
        form.querySelectorAll('#publish, #save-post, .save-variation-changes').forEach(function (button) {
          button.addEventListener('click', function () { sync(false, button.id || 'save-click'); });
        });
      }
    });
  }

  function initDocumentationSearch(scope) {
    var search = scope.querySelector('[data-apd-docs-search]');
    var library = scope.querySelector('[data-apd-docs-library]');
    if (!search || !library) {
      return;
    }

    var cards = Array.prototype.slice.call(library.querySelectorAll('[data-apd-docs-card]'));
    var groups = Array.prototype.slice.call(library.querySelectorAll('[data-apd-docs-group]'));
    var count = scope.querySelector('[data-apd-docs-result-count]');
    var empty = library.querySelector('[data-apd-docs-empty]');

    function refresh() {
      var query = String(search.value || '').trim().toLowerCase();
      var visibleCount = 0;

      cards.forEach(function (card) {
        var haystack = card.getAttribute('data-apd-docs-search-text') || '';
        var visible = !query || haystack.indexOf(query) !== -1;
        card.hidden = !visible;
        if (visible) {
          visibleCount += 1;
        }
      });

      groups.forEach(function (group) {
        var visibleInGroup = group.querySelectorAll('[data-apd-docs-card]:not([hidden])').length;
        var groupCount = group.querySelector('[data-apd-docs-group-count]');
        group.hidden = visibleInGroup === 0;
        if (groupCount) {
          var label = visibleInGroup === 1
            ? (groupCount.getAttribute('data-apd-docs-single-label') || 'guide')
            : (groupCount.getAttribute('data-apd-docs-plural-label') || 'guides');
          groupCount.textContent = String(visibleInGroup) + ' ' + label;
        }
      });

      if (count) {
        count.textContent = String(visibleCount);
      }
      if (empty) {
        empty.hidden = visibleCount !== 0;
      }
    }

    search.addEventListener('input', refresh);
    refresh();
  }

  function initDesignerThemeStudio(scope) {
    var studio = scope.querySelector('[data-apd-theme-studio]');
    if (!studio || studio.__apdThemeStudioReady) {
      return;
    }
    studio.__apdThemeStudioReady = true;

    var parseJsonNode = function (selector, fallback) {
      var node = studio.querySelector(selector);
      if (!node) return fallback;
      try {
        return JSON.parse(node.textContent || '');
      } catch (error) {
        return fallback;
      }
    };
    var labels = (window.APDAdminConfig && window.APDAdminConfig.labels) || {};
    var draftSource = parseJsonNode('[data-apd-theme-draft]', {});
    var publishedSource = parseJsonNode('[data-apd-theme-published]', {});
    var presets = parseJsonNode('[data-apd-theme-presets]', {});
    var presetIds = Object.keys(presets);
    var fallbackPreset = presetIds.indexOf('light') !== -1 ? 'light' : (presetIds[0] || 'light');
    var form = studio.closest('form') || studio.querySelector('[data-apd-theme-form]');
    var payloadField = studio.querySelector('[data-apd-theme-payload]');
    var nameField = studio.hasAttribute('data-apd-theme-cpt-editor')
      ? document.getElementById('title')
      : studio.querySelector('[data-apd-theme-name]');
    var preview = studio.querySelector('[data-apd-theme-preview]');
    var previewLabel = studio.querySelector('[data-apd-theme-preview-label]');
    var jsonField = studio.querySelector('[data-apd-theme-json]');
    var searchField = studio.querySelector('[data-apd-theme-search]');
    var searchClear = studio.querySelector('[data-apd-theme-search-clear]');
    var searchEmpty = studio.querySelector('[data-apd-theme-search-empty]');
    var activeSection = 'workspace';
    var comparingPublished = false;
    var history = [];
    var historyIndex = -1;
    var historyTimer = 0;
    var messageTimer = 0;

    function clone(value) {
      return JSON.parse(JSON.stringify(value || {}));
    }

    function normalizeState(value) {
      value = value && typeof value === 'object' ? value : {};
      var preset = presetIds.indexOf(String(value.preset || '')) !== -1 ? String(value.preset) : fallbackPreset;
      var presetColors = (presets[preset] && presets[preset].colors) || {};
      var colors = Object.assign({}, presetColors);
      if (value.colors && typeof value.colors === 'object') {
        Object.keys(presetColors).forEach(function (token) {
          var candidate = value.colors[token];
          if (typeof candidate === 'string' && candidate.trim()) colors[token] = candidate.trim();
        });
      }
      return {
        version: 1,
        name: String(value.name || 'Store theme').trim().slice(0, 80) || 'Store theme',
        preset: preset,
        colors: colors
      };
    }

    var state = normalizeState(draftSource);
    var published = normalizeState(publishedSource);

    function signature(value) {
      var normalized = normalizeState(value);
      return JSON.stringify({ name: normalized.name, preset: normalized.preset, colors: normalized.colors });
    }

    function colorIsValid(value) {
      value = String(value || '').trim();
      return !!value && (!window.CSS || typeof window.CSS.supports !== 'function' || window.CSS.supports('color', value));
    }

    function variableName(token) {
      return '--apd-designer-' + String(token).replace(/[^a-z0-9]+/gi, '-').replace(/^-|-$/g, '');
    }

    function readFields() {
      var next = normalizeState(state);
      next.name = String((nameField && nameField.value) || next.name).trim().slice(0, 80) || 'Store theme';
      var checkedPreset = studio.querySelector('[data-apd-theme-preset]:checked');
      next.preset = checkedPreset ? checkedPreset.value : next.preset;
      studio.querySelectorAll('[data-apd-theme-color]').forEach(function (field) {
        var token = field.getAttribute('data-apd-theme-color') || '';
        var value = String(field.value || '').trim();
        if (token && colorIsValid(value)) next.colors[token] = value;
      });
      return next;
    }

    function colorToRgba(value) {
      var probe = colorToRgba.probe;
      if (!probe) {
        probe = document.createElement('span');
        probe.hidden = true;
        studio.appendChild(probe);
        colorToRgba.probe = probe;
      }
      probe.style.color = '';
      probe.style.color = String(value || '');
      if (!probe.style.color) return null;
      var computed = window.getComputedStyle(probe).color;
      var match = computed.match(/^rgba?\(\s*([0-9.]+)[, ]+([0-9.]+)[, ]+([0-9.]+)(?:\s*[,\/]\s*([0-9.]+))?/i);
      if (!match) return null;
      return { r: Number(match[1]), g: Number(match[2]), b: Number(match[3]), a: match[4] === undefined ? 1 : Number(match[4]) };
    }

    function toHex(value) {
      var color = colorToRgba(value);
      if (!color) return '#2563eb';
      return '#' + [color.r, color.g, color.b].map(function (part) {
        return Math.max(0, Math.min(255, Math.round(part))).toString(16).padStart(2, '0');
      }).join('');
    }

    function relativeLuminance(color) {
      var channels = [color.r, color.g, color.b].map(function (value) {
        value = value / 255;
        return value <= 0.03928 ? value / 12.92 : Math.pow((value + 0.055) / 1.055, 2.4);
      });
      return channels[0] * 0.2126 + channels[1] * 0.7152 + channels[2] * 0.0722;
    }

    function composite(foreground, background) {
      var alpha = foreground.a === undefined ? 1 : foreground.a;
      return {
        r: foreground.r * alpha + background.r * (1 - alpha),
        g: foreground.g * alpha + background.g * (1 - alpha),
        b: foreground.b * alpha + background.b * (1 - alpha),
        a: 1
      };
    }

    function contrastRatio(foregroundValue, backgroundValue) {
      var foreground = colorToRgba(foregroundValue);
      var background = colorToRgba(backgroundValue);
      if (!foreground || !background) return null;
      background = composite(background, { r: 255, g: 255, b: 255, a: 1 });
      foreground = composite(foreground, background);
      var light = relativeLuminance(foreground);
      var dark = relativeLuminance(background);
      return (Math.max(light, dark) + 0.05) / (Math.min(light, dark) + 0.05);
    }

    function updateContrast(colors) {
      var checks = {
        content: contrastRatio(colors['text.primary'], colors['layout.panelBg']),
        primary: contrastRatio(colors['button.primary.text'], colors['button.primary.bg']),
        input: contrastRatio(colors['input.text'], colors['input.bg'])
      };
      Object.keys(checks).forEach(function (id) {
        var node = studio.querySelector('[data-apd-theme-contrast="' + id + '"]');
        if (!node) return;
        var ratio = checks[id];
        node.classList.remove('is-good', 'is-warning', 'is-poor');
        if (ratio === null) {
          node.querySelector('strong').textContent = '--';
          return;
        }
        node.querySelector('strong').textContent = ratio.toFixed(1) + ':1';
        node.classList.add(ratio >= 4.5 ? 'is-good' : (ratio >= 3 ? 'is-warning' : 'is-poor'));
      });
    }

    function applyPreview(value) {
      if (!preview) return;
      var colors = normalizeState(value).colors;
      Object.keys(colors).forEach(function (token) {
        preview.style.setProperty(variableName(token), colors[token]);
      });
      updateContrast(colors);
    }

    function countDifferences(left, right) {
      var count = left.name !== right.name || left.preset !== right.preset ? 1 : 0;
      Object.keys(left.colors).forEach(function (token) {
        if (String(left.colors[token]) !== String(right.colors[token])) count += 1;
      });
      return count;
    }

    function updateStatus() {
      state = readFields();
      var dirty = signature(state) !== signature(published);
      var count = countDifferences(state, published);
      studio.querySelectorAll('[data-apd-theme-status]').forEach(function (node) {
        node.classList.toggle('is-draft', dirty);
        node.classList.toggle('is-published', !dirty);
        var dot = node.querySelector('span');
        node.textContent = '';
        if (dot) node.appendChild(dot);
        else {
          dot = document.createElement('span');
          dot.setAttribute('aria-hidden', 'true');
          node.appendChild(dot);
        }
        node.appendChild(document.createTextNode(dirty ? (labels.themeUnpublished || 'Unpublished changes') : (labels.themePublished || 'Published')));
      });
      var summary = studio.querySelector('[data-apd-theme-change-summary]');
      if (summary && dirty) {
        var countTemplate = count === 1
          ? (labels.themeUnsavedOne || '%d token differs from the published theme.')
          : (labels.themeUnsavedMany || '%d tokens differ from the published theme.');
        summary.textContent = String(countTemplate).replace('%d', String(count));
      }
      if (jsonField) jsonField.value = JSON.stringify(state, null, 2);
      if (!comparingPublished) applyPreview(state);
    }

    function setFields(value) {
      state = normalizeState(value);
      if (nameField && !studio.hasAttribute('data-apd-theme-cpt-editor')) nameField.value = state.name;
      studio.querySelectorAll('[data-apd-theme-preset]').forEach(function (radio) {
        radio.checked = radio.value === state.preset;
        var card = radio.closest('[data-apd-theme-preset-card]');
        if (card) card.classList.toggle('is-active', radio.checked);
      });
      studio.querySelectorAll('[data-apd-theme-color]').forEach(function (field) {
        var token = field.getAttribute('data-apd-theme-color') || '';
        var valueForToken = state.colors[token] || '';
        field.value = valueForToken;
        field.classList.remove('is-invalid');
        field.removeAttribute('aria-invalid');
        var row = field.closest('[data-apd-theme-token-row]');
        var swatch = row && row.querySelector('.apd-theme-color-swatch');
        var picker = row && row.querySelector('[data-apd-theme-picker]');
        if (swatch) swatch.style.setProperty('--token-color', valueForToken);
        if (picker) picker.value = toHex(valueForToken);
      });
      comparingPublished = false;
      updateCompareState();
      updateStatus();
    }

    function updateHistoryButtons() {
      var undo = studio.querySelector('[data-apd-theme-undo]');
      var redo = studio.querySelector('[data-apd-theme-redo]');
      if (undo) undo.disabled = historyIndex <= 0;
      if (redo) redo.disabled = historyIndex >= history.length - 1;
    }

    function pushHistory() {
      state = readFields();
      var serialized = signature(state);
      if (historyIndex >= 0 && history[historyIndex].signature === serialized) return;
      history = history.slice(0, historyIndex + 1);
      history.push({ signature: serialized, state: clone(state) });
      if (history.length > 60) history.shift();
      historyIndex = history.length - 1;
      updateHistoryButtons();
      updateStatus();
    }

    function scheduleHistory() {
      window.clearTimeout(historyTimer);
      historyTimer = window.setTimeout(pushHistory, 260);
      updateStatus();
    }

    function goHistory(offset) {
      var next = historyIndex + offset;
      if (next < 0 || next >= history.length) return;
      historyIndex = next;
      setFields(history[historyIndex].state);
      updateHistoryButtons();
    }

    function flash(message, isError) {
      var node = studio.querySelector('[data-apd-theme-flash]');
      if (!node) {
        node = document.createElement('div');
        node.setAttribute('data-apd-theme-flash', '');
        node.className = 'apd-theme-flash';
        studio.appendChild(node);
      }
      node.textContent = message;
      node.classList.toggle('is-error', !!isError);
      node.classList.add('is-visible');
      window.clearTimeout(messageTimer);
      messageTimer = window.setTimeout(function () { node.classList.remove('is-visible'); }, 2600);
    }

    function selectSection(section) {
      activeSection = section;
      studio.querySelectorAll('[data-apd-theme-section-link]').forEach(function (button) {
        button.classList.toggle('is-active', button.getAttribute('data-apd-theme-section-link') === section);
      });
      if (searchField && searchField.value.trim()) return;
      studio.querySelectorAll('[data-apd-theme-section]').forEach(function (panel) {
        panel.hidden = panel.getAttribute('data-apd-theme-section') !== section;
      });
    }

    function filterTokens() {
      var query = String((searchField && searchField.value) || '').trim().toLowerCase();
      var visible = 0;
      if (searchClear) searchClear.hidden = !query;
      studio.querySelectorAll('[data-apd-theme-section]').forEach(function (panel) {
        panel.hidden = query ? false : panel.getAttribute('data-apd-theme-section') !== activeSection;
      });
      studio.querySelectorAll('[data-apd-theme-token-row]').forEach(function (row) {
        var match = !query || String(row.getAttribute('data-apd-theme-search-text') || '').indexOf(query) !== -1;
        row.hidden = !match;
        if (match && query) visible += 1;
      });
      studio.querySelectorAll('[data-apd-theme-token-group]').forEach(function (group) {
        group.hidden = !!query && group.querySelectorAll('[data-apd-theme-token-row]:not([hidden])').length === 0;
      });
      if (query) {
        studio.querySelectorAll('[data-apd-theme-section]').forEach(function (panel) {
          panel.hidden = panel.querySelectorAll('[data-apd-theme-token-row]:not([hidden])').length === 0;
        });
      }
      if (searchEmpty) searchEmpty.hidden = !query || visible > 0;
    }

    function setToken(token, value) {
      var field = studio.querySelector('[data-apd-theme-color="' + token + '"]');
      if (!field) return;
      field.value = value;
      field.classList.remove('is-invalid');
      field.removeAttribute('aria-invalid');
      var row = field.closest('[data-apd-theme-token-row]');
      var swatch = row && row.querySelector('.apd-theme-color-swatch');
      var picker = row && row.querySelector('[data-apd-theme-picker]');
      if (swatch) swatch.style.setProperty('--token-color', value);
      if (picker) picker.value = toHex(value);
    }

    function selectedPresetColors() {
      var selected = studio.querySelector('[data-apd-theme-preset]:checked');
      var id = selected ? selected.value : fallbackPreset;
      return clone((presets[id] && presets[id].colors) || {});
    }

    function updateCompareState() {
      var button = studio.querySelector('[data-apd-theme-compare]');
      if (button) {
        button.classList.toggle('is-active', comparingPublished);
        var icon = button.querySelector('.dashicons');
        button.textContent = '';
        if (icon) button.appendChild(icon);
        else {
          icon = document.createElement('span');
          icon.className = 'dashicons dashicons-visibility';
          icon.setAttribute('aria-hidden', 'true');
          button.appendChild(icon);
        }
        button.appendChild(document.createTextNode(comparingPublished ? (labels.themeReturnToDraft || 'Return to draft preview') : (labels.themeComparePublished || 'Compare published theme')));
      }
      if (previewLabel) previewLabel.textContent = comparingPublished ? (labels.themeComparingPublished || 'Comparing published') : (labels.themeEditingDraft || 'Editing draft');
      applyPreview(comparingPublished ? published : state);
    }

    studio.querySelectorAll('[data-apd-theme-section-link]').forEach(function (button) {
      button.addEventListener('click', function () { selectSection(button.getAttribute('data-apd-theme-section-link') || 'workspace'); });
    });

    studio.querySelectorAll('[data-apd-theme-preset]').forEach(function (radio) {
      radio.addEventListener('change', function () {
        if (!radio.checked || !presets[radio.value]) return;
        state.preset = radio.value;
        state.colors = clone(presets[radio.value].colors || {});
        setFields(state);
        pushHistory();
      });
    });

    studio.querySelectorAll('[data-apd-theme-color]').forEach(function (field) {
      field.addEventListener('input', function () {
        var value = String(field.value || '').trim();
        var valid = colorIsValid(value);
        field.classList.toggle('is-invalid', !valid);
        if (!valid) field.setAttribute('aria-invalid', 'true');
        else field.removeAttribute('aria-invalid');
        var row = field.closest('[data-apd-theme-token-row]');
        var swatch = row && row.querySelector('.apd-theme-color-swatch');
        var picker = row && row.querySelector('[data-apd-theme-picker]');
        if (valid && swatch) swatch.style.setProperty('--token-color', value);
        if (valid && picker) picker.value = toHex(value);
        scheduleHistory();
      });
      field.addEventListener('blur', function () {
        if (field.classList.contains('is-invalid')) flash(labels.themeColorInvalid || 'Enter a valid CSS color.', true);
      });
    });

    studio.querySelectorAll('[data-apd-theme-picker]').forEach(function (picker) {
      picker.addEventListener('input', function () {
        setToken(picker.getAttribute('data-apd-theme-picker') || '', picker.value);
        scheduleHistory();
      });
      picker.addEventListener('change', pushHistory);
    });

    if (nameField) nameField.addEventListener('input', scheduleHistory);
    if (searchField) searchField.addEventListener('input', filterTokens);
    if (searchClear) searchClear.addEventListener('click', function () { searchField.value = ''; filterTokens(); searchField.focus(); });
    var emptyClear = studio.querySelector('[data-apd-theme-search-empty-clear]');
    if (emptyClear) emptyClear.addEventListener('click', function () { searchField.value = ''; filterTokens(); searchField.focus(); });

    studio.querySelectorAll('[data-apd-theme-reset-token]').forEach(function (button) {
      button.addEventListener('click', function () {
        var token = button.getAttribute('data-apd-theme-reset-token') || '';
        var colors = selectedPresetColors();
        if (colors[token]) setToken(token, colors[token]);
        pushHistory();
      });
    });

    studio.querySelectorAll('[data-apd-theme-reset-section]').forEach(function (button) {
      button.addEventListener('click', function () {
        var section = button.getAttribute('data-apd-theme-reset-section') || '';
        var colors = selectedPresetColors();
        studio.querySelectorAll('[data-apd-theme-section="' + section + '"] [data-apd-theme-color]').forEach(function (field) {
          var token = field.getAttribute('data-apd-theme-color') || '';
          if (colors[token]) setToken(token, colors[token]);
        });
        pushHistory();
      });
    });

    var resetAll = studio.querySelector('[data-apd-theme-reset-all]');
    if (resetAll) resetAll.addEventListener('click', function () {
      state.colors = selectedPresetColors();
      setFields(state);
      pushHistory();
    });
    var restorePublished = studio.querySelector('[data-apd-theme-restore-published]');
    if (restorePublished) restorePublished.addEventListener('click', function () { setFields(published); pushHistory(); });

    var paletteButton = studio.querySelector('[data-apd-theme-generate-palette]');
    if (paletteButton) paletteButton.addEventListener('click', function () {
      var primaryField = studio.querySelector('[data-apd-theme-color="accent.primary"]');
      var primary = colorToRgba(primaryField && primaryField.value);
      if (!primary) {
        flash(labels.themeColorInvalid || 'Enter a valid CSS color.', true);
        return;
      }
      var rgb = [Math.round(primary.r), Math.round(primary.g), Math.round(primary.b)];
      var darken = function (amount) { return '#' + rgb.map(function (part) { return Math.max(0, Math.round(part * (1 - amount))).toString(16).padStart(2, '0'); }).join(''); };
      var luminance = relativeLuminance({ r: rgb[0], g: rgb[1], b: rgb[2] });
      var primaryHex = toHex('rgb(' + rgb.join(',') + ')');
      var values = {
        'accent.primary': primaryHex,
        'accent.primarySoft': 'rgba(' + rgb.join(', ') + ', 0.13)',
        'border.focus': primaryHex,
        'thumbnail.activeBorder': primaryHex,
        'thumbnail.activeBg': 'rgba(' + rgb.join(', ') + ', 0.10)',
        'layout.sidebarItemHoverBg': 'rgba(' + rgb.join(', ') + ', 0.05)',
        'layout.sidebarItemActiveBg': 'rgba(' + rgb.join(', ') + ', 0.09)',
        'button.primary.bg': primaryHex,
        'button.primary.border': primaryHex,
        'button.primary.hoverBg': darken(0.16),
        'button.primary.text': luminance > 0.48 ? '#0f172a' : '#ffffff',
        'input.focusRing': 'rgba(' + rgb.join(', ') + ', 0.20)'
      };
      Object.keys(values).forEach(function (token) { setToken(token, values[token]); });
      pushHistory();
    });

    studio.querySelectorAll('[data-apd-theme-preview-mode]').forEach(function (button) {
      button.addEventListener('click', function () {
        var mode = button.getAttribute('data-apd-theme-preview-mode') || 'desktop';
        studio.querySelectorAll('[data-apd-theme-preview-mode]').forEach(function (item) { item.classList.toggle('is-active', item === button); });
        if (preview) preview.classList.toggle('is-mobile', mode === 'mobile');
      });
    });
    var compareButton = studio.querySelector('[data-apd-theme-compare]');
    if (compareButton) compareButton.addEventListener('click', function () { comparingPublished = !comparingPublished; updateCompareState(); });

    var undoButton = studio.querySelector('[data-apd-theme-undo]');
    var redoButton = studio.querySelector('[data-apd-theme-redo]');
    if (undoButton) undoButton.addEventListener('click', function () { goHistory(-1); });
    if (redoButton) redoButton.addEventListener('click', function () { goHistory(1); });

    function exportState() {
      state = readFields();
      return JSON.stringify(state, null, 2);
    }
    var copyButton = studio.querySelector('[data-apd-theme-copy]');
    if (copyButton) copyButton.addEventListener('click', function () {
      var value = exportState();
      var done = function () { flash(labels.themeCopied || 'Theme JSON copied.'); };
      if (navigator.clipboard && navigator.clipboard.writeText) navigator.clipboard.writeText(value).then(done).catch(function () { if (jsonField) { jsonField.focus(); jsonField.select(); } });
      else if (jsonField) { jsonField.value = value; jsonField.focus(); jsonField.select(); document.execCommand('copy'); done(); }
    });
    var exportButton = studio.querySelector('[data-apd-theme-export]');
    if (exportButton) exportButton.addEventListener('click', function () {
      var blob = new Blob([exportState()], { type: 'application/json' });
      var url = URL.createObjectURL(blob);
      var anchor = document.createElement('a');
      anchor.href = url;
      anchor.download = (state.name || 'asuka-designer-theme').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '') + '.json';
      document.body.appendChild(anchor);
      anchor.click();
      anchor.remove();
      URL.revokeObjectURL(url);
    });

    function importPayload(value) {
      try {
        var parsed = typeof value === 'string' ? JSON.parse(value) : value;
        if (parsed && parsed.theme && typeof parsed.theme === 'object') parsed = parsed.theme;
        if (!parsed || typeof parsed !== 'object' || (!parsed.colors && !parsed.preset)) throw new Error('invalid theme');
        setFields(parsed);
        pushHistory();
        flash(labels.themeJsonApplied || 'Theme JSON applied to the draft.');
      } catch (error) {
        flash(labels.themeImportError || 'This file does not contain a valid AsukaDesigner theme.', true);
      }
    }
    var importInput = studio.querySelector('[data-apd-theme-import]');
    if (importInput) importInput.addEventListener('change', function () {
      var file = importInput.files && importInput.files[0];
      if (!file) return;
      file.text().then(importPayload).catch(function () { flash(labels.themeImportError || 'This file does not contain a valid AsukaDesigner theme.', true); });
      importInput.value = '';
    });
    var applyJson = studio.querySelector('[data-apd-theme-apply-json]');
    if (applyJson) applyJson.addEventListener('click', function () { importPayload(jsonField ? jsonField.value : ''); });

    if (form) form.addEventListener('submit', function (event) {
      var submitter = event.submitter;
      var confirmation = submitter && submitter.getAttribute('data-apd-confirm');
      if (confirmation && !window.confirm(confirmation)) {
        event.preventDefault();
        return;
      }
      var invalid = studio.querySelector('[data-apd-theme-color].is-invalid');
      if (invalid) {
        event.preventDefault();
        invalid.focus();
        flash(labels.themeColorInvalid || 'Enter a valid CSS color.', true);
        return;
      }
      state = readFields();
      if (payloadField) payloadField.value = JSON.stringify(state);
    });

    setFields(state);
    history = [{ signature: signature(state), state: clone(state) }];
    historyIndex = 0;
    updateHistoryButtons();
    selectSection(activeSection);
    filterTokens();
  }

  function parseThemePickerOptions(root, selector) {
    var node = root.querySelector(selector);
    if (!node) return [];
    try {
      var parsed = JSON.parse(node.textContent || '{}');
      return Array.isArray(parsed) ? parsed : Object.keys(parsed || {}).map(function (key) { return parsed[key]; });
    } catch (error) {
      return [];
    }
  }

  function applyThemePickerPreview(preview, nameNode, theme) {
    if (!preview || !theme) return;
    var colors = theme.colors && typeof theme.colors === 'object' ? theme.colors : {};
    preview.style.setProperty('--theme-bg', colors['layout.appBg'] || '#f5f7fb');
    preview.style.setProperty('--theme-panel', colors['layout.panelBg'] || '#ffffff');
    preview.style.setProperty('--theme-text', colors['text.primary'] || '#0f172a');
    preview.style.setProperty('--theme-accent', colors['accent.primary'] || '#2563eb');
    if (nameNode) nameNode.textContent = String(theme.name || 'Designer theme');
  }

  function initDefaultThemePickers(scope) {
    scope.querySelectorAll('[data-apd-default-theme-picker]').forEach(function (root) {
      if (root.__apdThemePickerReady) return;
      root.__apdThemePickerReady = true;
      var options = parseThemePickerOptions(root, '[data-apd-default-theme-options]');
      var select = root.querySelector('[data-apd-default-theme-select]');
      var preview = root.querySelector('[data-apd-default-theme-preview]');
      var name = root.querySelector('[data-apd-default-theme-name]');
      if (!select) return;
      select.addEventListener('change', function () {
        var theme = options.find(function (item) { return String(item.id || '') === String(select.value || ''); });
        applyThemePickerPreview(preview, name, theme);
      });
    });
  }

  function initProductThemePickers(scope) {
    scope.querySelectorAll('[data-apd-product-theme-picker]').forEach(function (root) {
      if (root.__apdThemePickerReady) return;
      root.__apdThemePickerReady = true;
      var options = parseThemePickerOptions(root, '[data-apd-product-theme-options]');
      var select = root.querySelector('[data-apd-product-theme-select]');
      var preview = root.querySelector('[data-apd-product-theme-preview]');
      var name = root.querySelector('[data-apd-product-theme-name]');
      var mode = root.querySelector('[data-apd-product-theme-mode]');
      var edit = root.querySelector('[data-apd-product-theme-edit]');
      if (!select) return;

      var refresh = function () {
        var selectedId = String(select.value || '__inherit');
        var theme = selectedId === '__inherit'
          ? options.find(function (item) { return item && item.isDefault; })
          : options.find(function (item) { return String(item.id || '') === selectedId; });
        if (theme) applyThemePickerPreview(preview, name, theme);
        if (mode) {
          mode.textContent = selectedId === '__inherit'
            ? (root.getAttribute('data-apd-theme-following-label') || 'Following store default')
            : (selectedId === '__embedded'
              ? (root.getAttribute('data-apd-theme-embedded-label') || 'Embedded custom theme')
              : (root.getAttribute('data-apd-theme-fixed-label') || 'Fixed assignment'));
        }
        if (edit) {
          edit.hidden = !theme;
          if (theme) {
            if (theme.editUrl) edit.href = String(theme.editUrl);
          }
        }
      };
      select.addEventListener('change', refresh);
      refresh();
    });
  }

  function normalizeTemplateEditorMetaBoxes(scope) {
    if (!document.body.classList.contains('post-type-apd_template')) {
      return;
    }
    var normalSortables = scope.querySelector('#normal-sortables');
    var modulesBox = scope.querySelector('#apd_template_modules');
    var previewBox = scope.querySelector('#apd_template_builder');
    if (!normalSortables || !modulesBox) {
      return;
    }
    if (modulesBox.parentElement !== normalSortables) {
      normalSortables.insertBefore(modulesBox, normalSortables.firstChild);
    }
    if (previewBox && previewBox.parentElement !== normalSortables) {
      normalSortables.appendChild(previewBox);
    }
  }

  document.addEventListener('DOMContentLoaded', function () {
    normalizeTemplateEditorMetaBoxes(document);
    initAdminAsukaMediaUploader(document);
    initTabs(document);
    autoresize(document);
    initCopy(document);
    initCatalogLibraryEnhancements(document);
    initCatalogMediaImports(document);
    initBuilderPages(document);
    initCollections(document);
    initPricingRuleBuilders(document);
    initAdaptiveFontEditors(document);
    initAdaptiveColorEditors(document);
    initAdaptivePrintingMethodEditors(document);
    initPreview(document);
    initStudio(document);
    if (window.APDRuntimeBridge && typeof window.APDRuntimeBridge.mountAll === 'function') {
      window.APDRuntimeBridge.mountAll();
    }
    initProductCanvas(document);
    initTemplatePickers(document);
    initTemplateModules(document);
    initAdvancedConfigPanels(document);
    initTranslationsPage(document);
    initDocumentationSearch(document);
    initDesignerThemeStudio(document);
    initDefaultThemePickers(document);
    initProductThemePickers(document);
  });
})();
