/* global cartcueAdmin, jQuery, wp */
(function () {
	'use strict';

	var BRAND = '#534AB7';

	var BLOCK_TYPES = {
		text:    { icon: 'ti-text-size',       cls: 'cti-text',    label: 'Text block' },
		button:  { icon: 'ti-hand-click',      cls: 'cti-button',  label: 'Button' },
		logo:    { icon: 'ti-brand-abstract',  cls: 'cti-logo',    label: 'Logo header' },
		image:   { icon: 'ti-photo',           cls: 'cti-image',   label: 'Image' },
		columns: { icon: 'ti-columns',         cls: 'cti-columns', label: '2 columns' },
		divider: { icon: 'ti-minus',           cls: 'cti-divider', label: 'Divider' },
		spacer:  { icon: 'ti-arrows-vertical', cls: 'cti-spacer',  label: 'Spacer' },
		billing_shipping: { icon: 'ti-map-pin', cls: 'cti-address', label: 'Addresses' }
	};

	var VAR_GROUPS = [
		{ name: 'Customer', cls: 'customer', tokens: ['first_name', 'last_name', 'email'] },
		{ name: 'Order',    cls: 'order',    tokens: ['number', 'total', 'items', 'tracking_link', 'billing_address', 'shipping_address'] },
		{ name: 'Cart',     cls: 'cart',     tokens: ['total', 'recover_url'] },
		{ name: 'Coupon',   cls: 'coupon',   tokens: ['code'] },
		{ name: 'Shop',     cls: 'shop',     tokens: ['name', 'url', 'logo_url', 'unsubscribe_url'] }
	];

	var FONT_OPTIONS = [
		'Arial, Helvetica, sans-serif',
		'Georgia, serif',
		'Verdana, Geneva, sans-serif',
		'Tahoma, sans-serif',
		'Times New Roman, Times, serif'
	];

	function defaultStyles() {
		return {
			branding_color: BRAND,
			text_color: '#333333',
			text_font: 'Arial, Helvetica, sans-serif',
			text_size: '15px',
			content_bg: '#ffffff',
			outer_bg: '#f5f5f5'
		};
	}

	function parseDocument(raw) {
		var value = (raw || '').trim();
		if (!value) {
			return { blocks: defaultBlocks(), styles: defaultStyles() };
		}
		if (value.indexOf('{') === 0) {
			try {
				var doc = JSON.parse(value);
				if (doc && Array.isArray(doc.blocks)) {
					return {
						blocks: doc.blocks.length ? doc.blocks : defaultBlocks(),
						styles: Object.assign(defaultStyles(), doc.styles || {})
					};
				}
			} catch (e) {
				return { blocks: defaultBlocks(), styles: defaultStyles() };
			}
		}
		return { blocks: parseLegacyBlocks(value), styles: defaultStyles() };
	}

	function parseLegacyBlocks(raw) {
		if (raw && (raw.indexOf('[{') === 0 || raw.indexOf('[ {') === 0)) {
			try {
				return JSON.parse(raw);
			} catch (e) {
				return defaultBlocks();
			}
		}
		if (raw) {
			return [{ type: 'text', content: raw, align: 'left' }];
		}
		return defaultBlocks();
	}

	function encodeDocument(blocks, styles) {
		return JSON.stringify({ v: 1, blocks: blocks, styles: styles });
	}

	function createBuilder(card) {
		var bodyTextarea = card.querySelector('textarea[name*="[body]"]');
		var modeInput = card.querySelector('input[name*="[body_mode]"]');
		if (!bodyTextarea || !modeInput || modeInput.value !== 'builder') {
			return null;
		}
		if (card.dataset.builderMounted === '1') {
			return null;
		}

		var initialDoc = parseDocument(bodyTextarea.value || '');
		var state = {
			blocks: initialDoc.blocks,
			styles: initialDoc.styles,
			selected: null,
			viewport: 'desktop',
			previewDebounce: null,
			history: [],
			historyIndex: -1,
			dragIdx: null
		};
		state.selected = state.blocks.length ? 0 : null;

		bodyTextarea.style.display = 'none';
		var builder = buildBuilderDOM();
		bodyTextarea.parentNode.insertBefore(builder, bodyTextarea.nextSibling);
		card.dataset.builderMounted = '1';

		var refs = {
			card: card,
			wrap: builder,
			bodyTextarea: bodyTextarea,
			canvas: builder.querySelector('.cb-canvas-blocks'),
			widgetsRail: builder.querySelector('.cb-widgets-rail'),
			editArea: builder.querySelector('.cb-edit-area'),
			editZoneText: builder.querySelector('.cb-edit-zone-text'),
			blockCount: builder.querySelector('.cb-block-count'),
			iframe: builder.querySelector('.cb-preview-frame'),
			previewWrap: builder.querySelector('.cb-preview-area'),
			newsletterShell: builder.querySelector('.cb-newsletter-shell')
		};

		refs.wrap.querySelectorAll('.cb-add-chip').forEach(function (chip) {
			chip.addEventListener('click', function () {
				var type = chip.dataset.addType || '';
				if (type) {
					addBlock(type);
				}
			});
		});
		refs.wrap.querySelectorAll('.cb-vp-btn').forEach(function (button) {
			button.addEventListener('click', function () {
				setViewport(button.dataset.vp || 'desktop');
			});
		});
		refs.wrap.querySelectorAll('.cb-view-btn').forEach(function (button) {
			button.addEventListener('click', function () {
				setBuilderView(button.dataset.view || 'list');
			});
		});

		var structureHeader = refs.wrap.querySelector('.cb-zone-header--structure');
		if (structureHeader) {
			var historyGroup = document.createElement('div');
			historyGroup.className = 'cb-history-group';
			var undoBtn = document.createElement('button');
			undoBtn.type = 'button';
			undoBtn.className = 'cb-ctrl-btn cb-undo-btn';
			undoBtn.title = 'Undo';
			undoBtn.innerHTML = '<i class="ti ti-arrow-back-up" aria-hidden="true"></i>';
			undoBtn.addEventListener('click', function () { restoreHistory(state.historyIndex - 1); });
			var redoBtn = document.createElement('button');
			redoBtn.type = 'button';
			redoBtn.className = 'cb-ctrl-btn cb-redo-btn';
			redoBtn.title = 'Redo';
			redoBtn.innerHTML = '<i class="ti ti-arrow-forward-up" aria-hidden="true"></i>';
			redoBtn.addEventListener('click', function () { restoreHistory(state.historyIndex + 1); });
			historyGroup.appendChild(undoBtn);
			historyGroup.appendChild(redoBtn);
			structureHeader.appendChild(historyGroup);
		}

		function persist() {
			refs.bodyTextarea.value = encodeDocument(state.blocks, state.styles);
			if (refs.newsletterShell) {
				refs.newsletterShell.style.background = state.styles.content_bg || '#ffffff';
			}
			if (window.jQuery) {
				window.jQuery(refs.bodyTextarea).trigger('input');
			} else if (typeof Event === 'function') {
				refs.bodyTextarea.dispatchEvent(new Event('input', { bubbles: true }));
			}
		}

		function syncExternalPreviewMode() {
			var hasExternal = card.classList.contains('ap-action-card--split');
			refs.wrap.classList.toggle('cartcue-builder--external-preview', hasExternal);
			if (hasExternal && refs.wrap.dataset.builderView === 'preview') {
				setBuilderView('list');
			}
		}

		function pushHistory() {
			var snapshot = encodeDocument(state.blocks, state.styles);
			if (state.historyIndex >= 0 && state.history[state.historyIndex] === snapshot) {
				return;
			}
			state.history = state.history.slice(0, state.historyIndex + 1);
			state.history.push(snapshot);
			if (state.history.length > 40) {
				state.history.shift();
			} else {
				state.historyIndex++;
			}
			updateHistoryButtons();
		}

		function updateHistoryButtons() {
			var undoBtn = refs.wrap.querySelector('.cb-undo-btn');
			var redoBtn = refs.wrap.querySelector('.cb-redo-btn');
			if (undoBtn) {
				undoBtn.disabled = state.historyIndex <= 0;
			}
			if (redoBtn) {
				redoBtn.disabled = state.historyIndex >= state.history.length - 1;
			}
		}

		function restoreHistory(index) {
			if (index < 0 || index >= state.history.length) {
				return;
			}
			state.historyIndex = index;
			try {
				var doc = parseDocument(state.history[index]);
				state.blocks = doc.blocks;
				state.styles = doc.styles;
			} catch (e) {
				return;
			}
			state.selected = state.blocks.length ? Math.min(state.selected || 0, state.blocks.length - 1) : null;
			renderCanvas();
			renderSidebar();
			renderStylesPanel();
			updateHistoryButtons();
		}

		function updateBlockPreview(idx) {
			var cardEl = refs.canvas && refs.canvas.querySelector('.cb-block-card[data-idx="' + idx + '"]');
			if (!cardEl) {
				return;
			}
			var block = state.blocks[idx];
			if (!block) {
				return;
			}
			var preview = cardEl.querySelector('.cb-card-body > div:last-child');
			if (!preview) {
				renderCanvas();
				return;
			}
			switch (block.type) {
				case 'button':
					preview.innerHTML = '<div class="cb-card-preview-btn" style="background:'
						+ esc(block.bg_color || BRAND) + ';color:'
						+ esc(block.text_color || '#fff') + '">'
						+ esc(block.text || 'Button') + '</div>';
					break;
				case 'text':
					preview.className = 'cb-card-preview';
					preview.textContent = stripTags(block.content || '').substring(0, 80);
					break;
				default:
					renderCanvas();
					return;
			}
			persist();
			schedulePreviewRefresh();
		}

		function schedulePreviewRefresh() {
			if (!refs.iframe) {
				return;
			}
			window.clearTimeout(state.previewDebounce);
			state.previewDebounce = window.setTimeout(refreshPreview, 350);
		}

		function refreshPreview() {
			if (!refs.iframe || !window.jQuery || !window.cartcueAdmin || !cartcueAdmin.nonces || !cartcueAdmin.nonces.builder_preview) {
				return;
			}
			var triggerSlugInput = document.querySelector('[name="trigger_slug"]:checked') || document.querySelector('[name="trigger_slug"]');
			var triggerSlug = triggerSlugInput ? triggerSlugInput.value : 'order_paid';
			jQuery.post(cartcueAdmin.ajaxUrl, {
				action: 'cartcue_builder_preview',
				nonce: cartcueAdmin.nonces.builder_preview,
				blocks: JSON.stringify(state.blocks),
				styles: JSON.stringify(state.styles),
				trigger_slug: triggerSlug
			}, function (res) {
				if (res && res.success && res.data && refs.iframe) {
					refs.iframe.srcdoc = res.data.html || '';
				}
			});
		}

		function setViewport(vp) {
			state.viewport = vp;
			if (refs.previewWrap) {
				refs.previewWrap.dataset.viewport = vp;
			}
			refs.wrap.querySelectorAll('.cb-vp-btn').forEach(function (btn) {
				btn.classList.toggle('is-active', btn.dataset.vp === vp);
			});
		}
		function setBuilderView(view) {
			refs.wrap.dataset.builderView = view;
			refs.wrap.querySelectorAll('.cb-view-btn').forEach(function (btn) {
				var isActive = btn.dataset.view === view;
				btn.classList.toggle('is-active', isActive);
				btn.setAttribute('aria-pressed', isActive ? 'true' : 'false');
			});
			if (view === 'preview') {
				schedulePreviewRefresh();
			}
		}

		syncExternalPreviewMode();
		if (typeof MutationObserver === 'function') {
			var splitObserver = new MutationObserver(syncExternalPreviewMode);
			splitObserver.observe(card, { attributes: true, attributeFilter: ['class'] });
		}

		function addBlock(type) {
			var defaults = {
				text:    { type: 'text',    content: '<p>New paragraph. Click to edit.</p>', align: 'left' },
				button:  { type: 'button',  text: 'Click here \u2192', url: '', bg_color: state.styles.branding_color || BRAND, text_color: '#ffffff', align: 'center' },
				logo:    { type: 'logo',    width: 140, align: 'left' },
				image:   { type: 'image',   src: '', alt: '', width: 300, align: 'center', link: '' },
				columns: { type: 'columns', cols: [ { content: '<p>Left text</p>' }, { content: '<p>Right text</p>' } ] },
				divider: { type: 'divider', color: '#e5e4f0' },
				spacer:  { type: 'spacer',  height: 24 },
				billing_shipping: {
					type: 'billing_shipping',
					billing: '{{order.billing_address}}',
					shipping: '{{order.shipping_address}}'
				}
			};
			state.blocks.push(defaults[type] || { type: type });
			state.selected = state.blocks.length - 1;
			pushHistory();
			renderCanvas();
			renderSidebar();
		}

		function duplicateBlock(idx) {
			var copy = JSON.parse(JSON.stringify(state.blocks[idx]));
			state.blocks.splice(idx + 1, 0, copy);
			state.selected = idx + 1;
			pushHistory();
			renderCanvas();
			renderSidebar();
		}

		function deleteBlock(idx) {
			if (state.blocks.length <= 1) {
				return;
			}
			state.blocks.splice(idx, 1);
			state.selected = state.blocks.length ? Math.min(idx, state.blocks.length - 1) : null;
			pushHistory();
			renderCanvas();
			renderSidebar();
		}

		function moveBlock(idx, dir) {
			var target = idx + dir;
			if (target < 0 || target >= state.blocks.length) {
				return;
			}
			var a = state.blocks[idx];
			state.blocks[idx] = state.blocks[target];
			state.blocks[target] = a;
			state.selected = target;
			pushHistory();
			renderCanvas();
			renderSidebar();
		}

		function selectBlock(idx) {
			state.selected = idx;
			renderCanvas();
			renderSidebar();
		}

		function buildBlockTools(idx) {
			var tools = el('div', { className: 'cb-block-tools' });
			var slider = el('div', { className: 'cb-block-tools-slider' });

			var editBtn = el('button', { type: 'button', className: 'cb-tool-btn', title: 'Edit block' });
			editBtn.innerHTML = '<i class="ti ti-settings" aria-hidden="true"></i>';
			editBtn.addEventListener('click', function (e) {
				e.stopPropagation();
				selectBlock(idx);
			});
			slider.appendChild(editBtn);

			var dupBtn = el('button', { type: 'button', className: 'cb-tool-btn', title: 'Duplicate' });
			dupBtn.innerHTML = '<i class="ti ti-copy" aria-hidden="true"></i>';
			dupBtn.addEventListener('click', function (e) {
				e.stopPropagation();
				duplicateBlock(idx);
			});
			slider.appendChild(dupBtn);

			var delBtn = el('button', { type: 'button', className: 'cb-tool-btn cb-tool-delete', title: 'Delete' });
			delBtn.innerHTML = '<i class="ti ti-trash" aria-hidden="true"></i>';
			delBtn.addEventListener('click', function (e) {
				e.stopPropagation();
				deleteBlock(idx);
			});
			slider.appendChild(delBtn);

			var dragBtn = el('span', { className: 'cb-tool-drag', title: 'Drag to move' });
			dragBtn.innerHTML = '<i class="ti ti-arrows-move" aria-hidden="true"></i>';
			slider.appendChild(dragBtn);

			tools.appendChild(slider);
			return tools;
		}

		function renderVisualBlockContent(block) {
			var inner = el('div', { className: 'cb-visual-inner' });
			var font = state.styles.text_font || 'Arial, Helvetica, sans-serif';
			var size = state.styles.text_size || '15px';
			var color = state.styles.text_color || '#333333';
			var brand = state.styles.branding_color || BRAND;

			switch (block.type) {
				case 'button':
					inner.style.textAlign = block.align || 'center';
					inner.innerHTML = '<span class="cb-visual-btn" style="background:'
						+ esc(block.bg_color || brand) + ';color:'
						+ esc(block.text_color || '#fff') + '">'
						+ esc(block.text || 'Button') + '</span>';
					break;
				case 'divider':
					inner.innerHTML = '<hr class="cb-visual-divider" style="border-color:'
						+ esc(block.color || '#e5e4f0') + '">';
					break;
				case 'spacer':
					inner.style.height = (block.height || 24) + 'px';
					inner.className += ' cb-visual-spacer';
					break;
				case 'image':
					if (block.src) {
						inner.style.textAlign = block.align || 'center';
						inner.innerHTML = '<img class="cb-visual-img" src="' + esc(block.src) + '" alt="'
							+ esc(block.alt || '') + '" style="max-width:'
							+ Math.min(block.width || 300, 560) + 'px">';
					} else {
						inner.className += ' cb-visual-placeholder';
						inner.textContent = 'Click to add an image';
					}
					break;
				case 'logo':
					inner.className += ' cb-visual-placeholder';
					inner.textContent = 'Store logo';
					inner.style.textAlign = block.align || 'left';
					break;
				case 'columns':
					var leftHtml  = ((block.cols || [{}])[0] || {}).content || '<p>Column 1</p>';
					var rightHtml = ((block.cols || [{}, {}])[1] || {}).content || '<p>Column 2</p>';
					inner.innerHTML = '<div class="cb-visual-columns">'
						+ '<div class="cb-visual-col">' + leftHtml + '</div>'
						+ '<div class="cb-visual-col">' + rightHtml + '</div>'
						+ '</div>';
					break;
				case 'billing_shipping':
					inner.className += ' cb-visual-placeholder cb-visual-address';
					inner.innerHTML = '<strong>Billing</strong> · <strong>Shipping</strong>'
						+ '<span>Order addresses (auto-filled when sent)</span>';
					break;
				default:
					inner.style.fontFamily = font;
					inner.style.fontSize = size;
					inner.style.color = color;
					inner.style.textAlign = block.align || 'left';
					inner.innerHTML = block.content || '<p>Text block</p>';
			}
			return inner;
		}

		function initDragDrop() {
			if (!refs.canvas) {
				return;
			}
			refs.canvas.addEventListener('dragover', function (e) {
				e.preventDefault();
				var target = e.target.closest('.cb-visual-block');
				refs.canvas.querySelectorAll('.cb-visual-block').forEach(function (node) {
					node.classList.remove('cb-drag-over');
				});
				if (target) {
					target.classList.add('cb-drag-over');
				}
			});
			refs.canvas.addEventListener('drop', function (e) {
				e.preventDefault();
				var from = state.dragIdx;
				var target = e.target.closest('.cb-visual-block');
				refs.canvas.querySelectorAll('.cb-visual-block').forEach(function (node) {
					node.classList.remove('cb-drag-over');
				});
				if (from === null || !target) {
					state.dragIdx = null;
					return;
				}
				var to = parseInt(target.dataset.idx, 10);
				if (isNaN(to) || from === to) {
					state.dragIdx = null;
					return;
				}
				var moved = state.blocks.splice(from, 1)[0];
				state.blocks.splice(to, 0, moved);
				state.selected = to;
				state.dragIdx = null;
				pushHistory();
				renderCanvas();
				renderSidebar();
			});
		}

		function renderCanvas() {
			if (!refs.canvas) {
				return;
			}
			refs.canvas.innerHTML = '';

			if (!state.blocks.length) {
				var empty = el('div', { className: 'cb-canvas-empty' });
				empty.innerHTML = '<i class="ti ti-inbox" aria-hidden="true"></i>'
					+ '<p>Drag a block from the sidebar or click Content to add your first section.</p>';
				refs.canvas.appendChild(empty);
				if (refs.blockCount) {
					refs.blockCount.textContent = '0 blocks';
				}
				persist();
				schedulePreviewRefresh();
				return;
			}

			state.blocks.forEach(function (block, idx) {
				var cfg = BLOCK_TYPES[block.type] || BLOCK_TYPES.text;
				var isSel = state.selected === idx;

				var blockEl = el('div', {
					className: 'cb-visual-block' + (isSel ? ' is-selected' : ''),
					draggable: true
				});
				blockEl.dataset.idx = String(idx);
				blockEl.setAttribute('aria-label', cfg.label + ', block ' + (idx + 1));

				blockEl.addEventListener('click', function () { selectBlock(idx); });
				blockEl.addEventListener('dragstart', function (e) {
					state.dragIdx = idx;
					blockEl.classList.add('cb-dragging');
					if (e.dataTransfer) {
						e.dataTransfer.effectAllowed = 'move';
					}
				});
				blockEl.addEventListener('dragend', function () {
					blockEl.classList.remove('cb-dragging');
					state.dragIdx = null;
				});

				blockEl.appendChild(buildBlockTools(idx));
				blockEl.appendChild(renderVisualBlockContent(block));
				refs.canvas.appendChild(blockEl);
			});

			if (refs.blockCount) {
				var n = state.blocks.length;
				refs.blockCount.textContent = n + ' block' + (n !== 1 ? 's' : '');
			}

			persist();
			schedulePreviewRefresh();
		}

		function renderSidebar() {
			if (!refs.editArea) {
				return;
			}

			if (state.selected === null || !state.blocks[state.selected]) {
				if (refs.editZoneText) {
					refs.editZoneText.textContent = 'Edit block';
				}
				refs.editArea.innerHTML = '<div class="cb-edit-empty">'
					+ '<i class="ti ti-cursor-text" aria-hidden="true"></i>'
					+ '<p>Click any block on the left to edit its content and settings here.</p>'
					+ '<span class="cb-edit-empty-hint">\u2190 select a block to start</span>'
					+ '</div>';
				return;
			}

			var block = state.blocks[state.selected];
			var cfg   = BLOCK_TYPES[block.type] || BLOCK_TYPES.text;

			if (refs.editZoneText) {
				refs.editZoneText.textContent = cfg.label;
			}

			refs.editArea.innerHTML = '';

			var identity = el('div', { className: 'cb-edit-block-id' });
			identity.innerHTML =
				'<div class="cb-edit-block-icon ' + cfg.cls + '">'
				+ '<i class="ti ' + cfg.icon + '" aria-hidden="true"></i></div>'
				+ '<div><div class="cb-edit-block-title">' + esc(cfg.label) + '</div>'
				+ '<div class="cb-edit-block-sub">Block '
				+ (state.selected + 1) + ' of ' + state.blocks.length + '</div></div>';
			refs.editArea.appendChild(identity);

			switch (block.type) {
				case 'text':
					renderSidebarText(block, state.selected);
					break;
				case 'button':
					renderSidebarButton(block, state.selected);
					break;
				case 'logo':
					renderSidebarLogo(block, state.selected);
					break;
				case 'image':
					renderSidebarImage(block, state.selected);
					break;
				case 'columns':
					renderSidebarColumns(block, state.selected);
					break;
				case 'divider':
					renderSidebarDivider(block, state.selected);
					break;
				case 'spacer':
					renderSidebarSpacer(block, state.selected);
					break;
				case 'billing_shipping':
					renderSidebarBillingShipping(block, state.selected);
					break;
			}
		}

		function renderSidebarText(block, idx) {
			function group(children) {
				var g = el('div', { className: 'cb-field-group' });
				children.forEach(function (c) { g.appendChild(c); });
				refs.editArea.appendChild(g);
			}

			var editor = el('div', {
				className: 'cb-rich-editor',
				contentEditable: true,
				innerHTML: block.content || ''
			});
			editor.addEventListener('input', function () {
				state.blocks[idx].content = editor.innerHTML;
				updateBlockPreview(idx);
			});
			group([editor]);

			var fmt = el('div', { className: 'cb-fmt-bar' });
			[
				{ cmd: 'bold',                icon: 'ti-bold',   title: 'Bold' },
				{ cmd: 'italic',              icon: 'ti-italic', title: 'Italic' },
				{ cmd: 'insertUnorderedList', icon: 'ti-list',   title: 'Bulleted list' },
				{ cmd: 'createLink',          icon: 'ti-link',   title: 'Add link', isLink: true }
			].forEach(function (f) {
				var b = el('button', { type: 'button', className: 'cb-fmt-btn' });
				b.setAttribute('aria-label', f.title);
				b.title = f.title;
				b.innerHTML = '<i class="ti ' + f.icon + '" aria-hidden="true"></i>';
				b.addEventListener('mousedown', function (e) {
					e.preventDefault();
					if (f.isLink) {
						var existingRow = refs.editArea.querySelector('.cb-link-input-row');
						if (existingRow) {
							existingRow.remove();
							return;
						}
						var linkRow = el('div', { className: 'cb-link-input-row' });
						var linkInput = el('input', {
							type: 'url',
							className: 'cb-input',
							placeholder: 'https://',
							value: 'https://'
						});
						var insertBtn = el('button', { type: 'button', className: 'cb-link-insert-btn', textContent: 'Insert' });
						var cancelBtn = el('button', { type: 'button', className: 'cb-link-cancel-btn', textContent: 'Cancel' });
						linkRow.appendChild(linkInput);
						linkRow.appendChild(insertBtn);
						linkRow.appendChild(cancelBtn);
						fmt.insertAdjacentElement('afterend', linkRow);
						linkInput.focus();
						linkInput.select();
						insertBtn.addEventListener('click', function () {
							var url = linkInput.value.trim();
							if (url) {
								editor.focus();
								document.execCommand('createLink', false, url);
								state.blocks[idx].content = editor.innerHTML;
								updateBlockPreview(idx);
							}
							linkRow.remove();
						});
						cancelBtn.addEventListener('click', function () {
							linkRow.remove();
						});
						linkInput.addEventListener('keydown', function (ev) {
							if (ev.key === 'Enter') { insertBtn.click(); }
							if (ev.key === 'Escape') { cancelBtn.click(); }
						});
						return;
					}
					document.execCommand(f.cmd);
					state.blocks[idx].content = editor.innerHTML;
					updateBlockPreview(idx);
				});
				fmt.appendChild(b);
			});
			refs.editArea.appendChild(fmt);
			refs.editArea.appendChild(varTokenPicker(editor, idx, 'content'));
			refs.editArea.appendChild(alignField(block, idx, 'Alignment'));
		}

		function renderSidebarButton(block, idx) {
			function appendGroup(labelText, child) {
				var g = el('div', { className: 'cb-field-group' });
				g.appendChild(fieldLabel(labelText));
				g.appendChild(child);
				refs.editArea.appendChild(g);
			}

			appendGroup('Button label', textField(block, idx, 'text', 'e.g. Recover my cart'));
			appendGroup('Link URL', textField(block, idx, 'url', 'https:// or {{cart.recover_url}}'));
			refs.editArea.appendChild(varTokenPicker(null, idx, 'url', ['cart', 'shop']));
			appendGroup('Button color', colorField(block, idx, 'bg_color'));
			appendGroup('Label color', colorField(block, idx, 'text_color'));
			refs.editArea.appendChild(alignField(block, idx, 'Alignment'));
		}

		function renderSidebarLogo(block, idx) {
			var info = el('div', { className: 'cb-info-box' });
			info.textContent = 'Uses your store logo from Settings \u2192 General automatically. No upload needed.';
			refs.editArea.appendChild(info);

			var sliderGroup = el('div', { className: 'cb-field-group' });
			sliderGroup.appendChild(fieldLabel('Logo width'));
			var sliderRow = el('div', { className: 'cb-slider-row' });
			var slider = el('input', { type: 'range', min: 60, max: 300, step: 10, value: block.width || 140 });
			var sliderVal = el('span', { className: 'cb-slider-val', textContent: (block.width || 140) + ' px' });
			slider.addEventListener('input', function () {
				state.blocks[idx].width = parseInt(slider.value, 10);
				sliderVal.textContent = slider.value + ' px';
				renderCanvas();
				schedulePreviewRefresh();
			});
			sliderRow.appendChild(slider);
			sliderRow.appendChild(sliderVal);
			sliderGroup.appendChild(sliderRow);
			refs.editArea.appendChild(sliderGroup);
			refs.editArea.appendChild(alignField(block, idx, 'Alignment'));
		}

		function renderSidebarImage(block, idx) {
			function appendFieldGroup(lbl, child) {
				var g = el('div', { className: 'cb-field-group' });
				g.appendChild(fieldLabel(lbl));
				g.appendChild(child);
				refs.editArea.appendChild(g);
			}

			if (window.wp && window.wp.media) {
				var mediaBtnGroup = el('div', { className: 'cb-field-group' });
				mediaBtnGroup.appendChild(fieldLabel('Image'));
				var mediaBtn = el('button', { type: 'button', className: 'cb-media-btn' });
				mediaBtn.innerHTML = '<i class="ti ti-upload" aria-hidden="true"></i> Choose from media library';
				mediaBtn.addEventListener('click', function () {
					var frame = wp.media({ title: 'Select image', button: { text: 'Use this image' }, multiple: false });
					frame.on('select', function () {
						var att = frame.state().get('selection').first().toJSON();
						state.blocks[idx].src   = att.url;
						state.blocks[idx].alt   = att.alt || att.title || '';
						state.blocks[idx].width = Math.min(att.width || 300, 600);
						renderCanvas();
						renderSidebar();
					});
					frame.open();
				});
				mediaBtnGroup.appendChild(mediaBtn);
				refs.editArea.appendChild(mediaBtnGroup);
			}

			appendFieldGroup('Image URL (or paste directly)', textField(block, idx, 'src', 'https://...'));
			appendFieldGroup('Alt text', textField(block, idx, 'alt', 'Describe the image for email clients'));
			appendFieldGroup('Link URL (optional)', textField(block, idx, 'link', 'https:// \u2014 makes image clickable'));

			var widthGroup = el('div', { className: 'cb-field-group' });
			widthGroup.appendChild(fieldLabel('Display width'));
			var widthSliderRow = el('div', { className: 'cb-slider-row' });
			var widthSlider = el('input', {
				type: 'range', min: 80, max: 600, step: 20,
				value: block.width || 300
			});
			widthSlider.style.flex = '1';
			var widthVal = el('span', {
				className: 'cb-slider-val',
				textContent: (block.width || 300) + ' px'
			});
			widthSlider.addEventListener('input', function () {
				var w = parseInt(widthSlider.value, 10);
				state.blocks[idx].width = w;
				widthVal.textContent = w + ' px';
				schedulePreviewRefresh();
			});
			widthSliderRow.appendChild(widthSlider);
			widthSliderRow.appendChild(widthVal);
			widthGroup.appendChild(widthSliderRow);
			refs.editArea.appendChild(widthGroup);

			refs.editArea.appendChild(alignField(block, idx, 'Alignment'));
		}

		function renderSidebarColumns(block, idx) {
			['Left column', 'Right column'].forEach(function (label, columnIndex) {
				var g = el('div', { className: 'cb-field-group' });
				g.appendChild(fieldLabel(label));
				var editor = el('div', {
					className: 'cb-rich-editor',
					contentEditable: true,
					innerHTML: ((block.cols || [{}, {}])[columnIndex] || {}).content || ''
				});
				editor.addEventListener('input', function () {
					if (!state.blocks[idx].cols) {
						state.blocks[idx].cols = [{}, {}];
					}
					state.blocks[idx].cols[columnIndex] = { content: editor.innerHTML };
					renderCanvas();
				});
				g.appendChild(editor);
				refs.editArea.appendChild(g);
			});
		}

		function renderSidebarDivider(block, idx) {
			var g = el('div', { className: 'cb-field-group' });
			g.appendChild(fieldLabel('Line color'));
			g.appendChild(colorField(block, idx, 'color'));
			refs.editArea.appendChild(g);

			var info = el('div', { className: 'cb-info-box' });
			info.textContent = 'A thin horizontal line that visually separates sections of your email.';
			refs.editArea.appendChild(info);
		}

		function renderSidebarSpacer(block, idx) {
			var g = el('div', { className: 'cb-field-group' });
			g.appendChild(fieldLabel('Gap size'));

			var sliderRow = el('div', { className: 'cb-slider-row' });
			var slider = el('input', { type: 'range', min: 4, max: 80, step: 4, value: block.height || 24 });
			var val = el('span', { className: 'cb-slider-val', textContent: (block.height || 24) + ' px' });
			slider.addEventListener('input', function () {
				var h = parseInt(slider.value, 10) || 24;
				state.blocks[idx].height = h;
				val.textContent = h + ' px';
				renderCanvas();
			});
			sliderRow.appendChild(slider);
			sliderRow.appendChild(val);
			g.appendChild(sliderRow);
			refs.editArea.appendChild(g);

			var info = el('div', { className: 'cb-info-box' });
			info.textContent = 'Adds empty vertical space between blocks without a visible line.';
			refs.editArea.appendChild(info);
		}

		function renderSidebarBillingShipping(block, idx) {
			var info = el('div', { className: 'cb-info-box' });
			info.innerHTML =
				'Renders the billing and shipping address from the order automatically.'
				+ '<br><br>Only shows in order-triggered automations (order paid, order cancelled, etc.).'
				+ ' Displays blank in test sends.';
			refs.editArea.appendChild(info);

			var g = el('div', { className: 'cb-field-group' });
			g.appendChild(fieldLabel('Billing token'));
			g.appendChild(textField(block, idx, 'billing', '{{order.billing_address}}'));
			refs.editArea.appendChild(g);

			var g2 = el('div', { className: 'cb-field-group' });
			g2.appendChild(fieldLabel('Shipping token'));
			g2.appendChild(textField(block, idx, 'shipping', '{{order.shipping_address}}'));
			refs.editArea.appendChild(g2);
		}

		function fieldLabel(text) {
			return el('label', { className: 'cb-field-label', textContent: text });
		}

		function textField(block, idx, key, placeholder) {
			var input = el('input', { type: 'text', className: 'cb-input', value: block[key] || '', placeholder: placeholder });
			input.addEventListener('input', function () {
				state.blocks[idx][key] = input.value;
				updateBlockPreview(idx);
			});
			return input;
		}

		function colorField(target, idx, key, onChange) {
			var wrap = el('div', { className: 'cb-color-wrap' });
			var raw = target[key] || BRAND;
			var current = /^#[0-9a-f]{6}$/i.test(raw) ? raw : BRAND;
			var picker = el('input', { type: 'color', className: 'cb-color-picker', value: current });
			var hex = el('input', { type: 'text', className: 'cb-input cb-input--hex', value: current, maxLength: 7 });
			var sync = function (value) {
				target[key] = value;
				picker.value = value;
				hex.value = value;
				if (typeof idx === 'number') {
					updateBlockPreview(idx);
				} else if (onChange) {
					onChange(value);
				}
			};
			picker.addEventListener('input', function () { sync(picker.value); });
			hex.addEventListener('change', function () {
				if (/^#[0-9a-f]{6}$/i.test(hex.value)) {
					sync(hex.value);
				}
			});
			wrap.appendChild(picker);
			wrap.appendChild(hex);
			return wrap;
		}

		function alignField(block, idx, labelText) {
			var wrap = el('div', { className: 'cb-field-group' });
			wrap.appendChild(fieldLabel(labelText || 'Alignment'));

			var row = el('div', { className: 'cb-align-row' });
			[
				{ value: 'left',   icon: 'ti-align-left',   label: 'Align left' },
				{ value: 'center', icon: 'ti-align-center', label: 'Align centre' },
				{ value: 'right',  icon: 'ti-align-right',  label: 'Align right' }
			].forEach(function (opt) {
				var btn = el('button', {
					type: 'button',
					className: 'cb-align-btn' + (block.align === opt.value ? ' is-active' : '')
				});
				btn.setAttribute('aria-label', opt.label);
				btn.title = opt.label;
				btn.innerHTML = '<i class="ti ' + opt.icon + '" aria-hidden="true"></i>';
				btn.addEventListener('click', function () {
					state.blocks[idx].align = opt.value;
					renderCanvas();
					renderSidebar();
				});
				row.appendChild(btn);
			});
			wrap.appendChild(row);
			return wrap;
		}

		function varTokenPicker(targetEditor, idx, field, onlyGroups) {
			var section = el('div', { className: 'cb-vars-section' });
			section.appendChild(el('div', {
				className: 'cb-vars-section-label',
				textContent: 'Add personalization'
			}));

			section.appendChild(el('div', {
				className: 'cb-vars-fallback-hint',
				textContent: 'Optional fallback: {{customer.first_name|there}} uses "there" when the name is empty.'
			}));

			var openGroups = {};
			VAR_GROUPS.forEach(function (g) {
				openGroups[g.cls] = (g.cls === 'customer');
			});

			VAR_GROUPS.forEach(function (group) {
				if (onlyGroups && onlyGroups.indexOf(group.cls) === -1) {
					return;
				}

				var groupDiv = el('div', { className: 'cb-var-group' });

				var header = el('button', {
					type: 'button',
					className: 'cb-var-group-header'
				});
				var isOpen = openGroups[group.cls];
				header.innerHTML =
					'<span class="cb-var-group-name">' + esc(group.name) + '</span>'
					+ '<i class="ti ti-chevron-' + (isOpen ? 'up' : 'down') + ' cb-var-group-chevron" aria-hidden="true"></i>';
				header.setAttribute('aria-expanded', String(isOpen));
				groupDiv.appendChild(header);

				var chips = el('div', { className: 'cb-var-chips' + (isOpen ? '' : ' cb-var-chips--collapsed') });
				group.tokens.forEach(function (token) {
					var fullToken = '{{' + group.cls + '.' + token + '}}';
					var fallbackToken = '{{' + group.cls + '.' + token + '|}}';
					var chip = el('button', {
						type: 'button',
						className: 'cb-var-chip cb-var-chip--' + group.cls,
						textContent: token.replace(/_/g, '\u00a0'),
						title: fullToken + ' (click) · ' + fallbackToken.replace('|}}', '|fallback}}')
					});
					chip.setAttribute('aria-label', 'Insert ' + fullToken);
					chip.addEventListener('click', function (e) {
						if (e.shiftKey) {
							fullToken = '{{' + group.cls + '.' + token + '|' + (token === 'first_name' ? 'there' : '') + '}}';
						}
						if (targetEditor) {
							targetEditor.focus();
							var sel = window.getSelection();
							if (sel && sel.rangeCount > 0 && targetEditor.contains(sel.anchorNode)) {
								document.execCommand('insertText', false, fullToken);
							} else {
								targetEditor.innerHTML += fullToken;
							}
							state.blocks[idx][field] = targetEditor.innerHTML;
						} else {
							state.blocks[idx][field] = (state.blocks[idx][field] || '') + fullToken;
						}
						renderCanvas();
						schedulePreviewRefresh();
						pushHistory();
					});
					chips.appendChild(chip);
				});
				groupDiv.appendChild(chips);

				header.addEventListener('click', function () {
					openGroups[group.cls] = !openGroups[group.cls];
					var nowOpen = openGroups[group.cls];
					chips.classList.toggle('cb-var-chips--collapsed', !nowOpen);
					header.setAttribute('aria-expanded', String(nowOpen));
					var chevron = header.querySelector('.cb-var-group-chevron');
					if (chevron) {
						chevron.className = 'ti ti-chevron-' + (nowOpen ? 'up' : 'down') + ' cb-var-group-chevron';
					}
				});

				section.appendChild(groupDiv);
			});

			return section;
		}

		function renderStylesPanel() {
			if (!refs.widgetsRail) {
				return;
			}
			var panel = refs.widgetsRail.querySelector('.cb-styles-panel-body');
			if (!panel) {
				return;
			}
			panel.innerHTML = '';

			function addStyleField(labelText, key, type) {
				var g = el('div', { className: 'cb-field-group cb-style-field' });
				g.appendChild(fieldLabel(labelText));
				if (type === 'color') {
					g.appendChild(colorField(state.styles, null, key, function () {
						pushHistory();
						renderCanvas();
						schedulePreviewRefresh();
					}));
				} else if (type === 'font') {
					var select = el('select', { className: 'cb-input' });
					FONT_OPTIONS.forEach(function (font) {
						var opt = el('option', { value: font, textContent: font.split(',')[0] });
						if (state.styles.text_font === font) {
							opt.selected = true;
						}
						select.appendChild(opt);
					});
					select.addEventListener('change', function () {
						state.styles.text_font = select.value;
						pushHistory();
						renderCanvas();
						schedulePreviewRefresh();
					});
					g.appendChild(select);
				} else if (type === 'size') {
					var sizeSelect = el('select', { className: 'cb-input cb-input--sm' });
					['13px', '14px', '15px', '16px', '18px'].forEach(function (sz) {
						var opt = el('option', { value: sz, textContent: sz });
						if (state.styles.text_size === sz) {
							opt.selected = true;
						}
						sizeSelect.appendChild(opt);
					});
					sizeSelect.addEventListener('change', function () {
						state.styles.text_size = sizeSelect.value;
						pushHistory();
						renderCanvas();
						schedulePreviewRefresh();
					});
					g.appendChild(sizeSelect);
				}
				panel.appendChild(g);
			}

			addStyleField('Branding color', 'branding_color', 'color');
			addStyleField('Text color', 'text_color', 'color');
			addStyleField('Font family', 'text_font', 'font');
			addStyleField('Font size', 'text_size', 'size');
			addStyleField('Content background', 'content_bg', 'color');
			addStyleField('Outer background', 'outer_bg', 'color');
		}

		function handlePresetChanged() {
			var doc = parseDocument(refs.bodyTextarea.value || '');
			state.blocks = doc.blocks;
			state.styles = doc.styles;
			state.selected = state.blocks.length ? 0 : null;
			renderCanvas();
			renderSidebar();
			renderStylesPanel();
		}

		var presetSelect = card.querySelector('select[name*="[email_preset]"]');
		if (presetSelect) {
			presetSelect.addEventListener('change', function (e) {
				if (this.value === '') {
					return;
				}
				if (state.blocks.length > 0) {
					var ok = window.confirm(
						'Load "' + this.options[this.selectedIndex].text + '" preset?\n'
						+ 'This will replace your current email content.'
					);
					if (!ok) {
						e.preventDefault();
						this.value = '';
						return;
					}
				}
				loadPresetIntoBuilder(this.value, state, refs, persist, renderCanvas, renderSidebar, card);
			});
		}

		initDragDrop();
		wireWidgetRegions();
		renderStylesPanel();
		renderCanvas();
		renderSidebar();
		pushHistory();
		setViewport('desktop');
		setBuilderView('list');
		schedulePreviewRefresh();

		function wireWidgetRegions() {
			if (!refs.widgetsRail) {
				return;
			}
			refs.widgetsRail.querySelectorAll('.cb-widget-region-header').forEach(function (header) {
				header.addEventListener('click', function () {
					var region = header.closest('.cb-widget-region');
					if (!region) {
						return;
					}
					region.classList.toggle('is-open');
					var expanded = region.classList.contains('is-open');
					header.setAttribute('aria-expanded', expanded ? 'true' : 'false');
				});
			});
		}

		return {
			refreshFromTextarea: handlePresetChanged
		};
	}

	function defaultBlocks() {
		return [
			{ type: 'text', content: '<p>Hi {{customer.first_name}},</p><p>Your message here.</p>', align: 'left' },
			{ type: 'button', text: 'Shop now \u2192', url: '{{shop.url}}', bg_color: BRAND, text_color: '#ffffff', align: 'center' },
			{ type: 'text', content: '<p>Thanks,<br>{{shop.name}}</p>', align: 'left' }
		];
	}

	function buildWidgetRegion(title, slug, openByDefault, items, extraClass) {
		var regionClass = 'cb-widget-region' + (openByDefault ? ' is-open' : '');
		if (extraClass) {
			regionClass += ' ' + extraClass;
		}
		var region = el('div', { className: regionClass });
		var header = el('button', {
			type: 'button',
			className: 'cb-widget-region-header',
			'aria-expanded': openByDefault ? 'true' : 'false'
		});
		header.innerHTML = '<span>' + esc(title) + '</span><i class="ti ti-chevron-down" aria-hidden="true"></i>';
		region.appendChild(header);
		var body = el('div', { className: 'cb-widget-region-body' });
		var chips = el('div', { className: 'cb-widget-chips' });
		items.forEach(function (item) {
			var chip = el('button', { type: 'button', className: 'cb-widget-chip cb-add-chip' });
			chip.innerHTML = '<span class="cb-widget-chip-add" aria-hidden="true">+</span>'
				+ '<i class="ti ' + item.icon + '" aria-hidden="true"></i>'
				+ '<span>' + esc(item.label) + '</span>';
			chip.dataset.addType = item.type;
			chip.title = 'Add ' + item.label + ' block';
			chips.appendChild(chip);
		});
		body.appendChild(chips);
		region.appendChild(body);
		return region;
	}

	function buildBuilderDOM() {
		var wrap = el('div', { className: 'cartcue-builder' });
		wrap.dataset.builderView = 'list';

		var toolbar = el('div', { className: 'cb-builder-toolbar' });
		var toolbarHint = el('span', { className: 'cb-toolbar-hint' });
		toolbarHint.innerHTML = '<strong>Build</strong> your email — add blocks from the left, click to edit, drag to reorder';
		toolbar.appendChild(toolbarHint);
		var viewGroup = el('div', { className: 'cb-view-group', role: 'group', 'aria-label': 'Builder view' });
		var builderViewBtn = el('button', { type: 'button', className: 'cb-view-btn is-active', textContent: 'Build' });
		builderViewBtn.dataset.view = 'list';
		builderViewBtn.setAttribute('aria-pressed', 'true');
		var previewViewBtn = el('button', { type: 'button', className: 'cb-view-btn', textContent: 'Preview' });
		previewViewBtn.dataset.view = 'preview';
		previewViewBtn.setAttribute('aria-pressed', 'false');
		viewGroup.appendChild(builderViewBtn);
		viewGroup.appendChild(previewViewBtn);
		toolbar.appendChild(viewGroup);
		wrap.appendChild(toolbar);

		var structureCol = el('div', { className: 'cb-structure-col' });
		var structureHeader = el('div', { className: 'cb-zone-header cb-zone-header--structure' });
		var structureLabel = el('div', { className: 'cb-zone-label' });
		structureLabel.innerHTML = '<i class="ti ti-mail" aria-hidden="true"></i> Email';
		var blockCount = el('span', { className: 'cb-block-count', textContent: '0 blocks' });
		structureHeader.appendChild(structureLabel);
		structureHeader.appendChild(blockCount);
		structureCol.appendChild(structureHeader);

		var structureBody = el('div', { className: 'cb-structure-body' });

		var widgetsRail = el('div', { className: 'cb-widgets-rail' });
		var railHead = el('div', { className: 'cb-widgets-rail-head' });
		railHead.innerHTML = '<span class="cb-widgets-rail-title">Add to email</span>'
			+ '<span class="cb-widgets-rail-hint">Click a block to insert it</span>';
		widgetsRail.appendChild(railHead);
		widgetsRail.appendChild(buildWidgetRegion('Blocks', 'content', true, [
			{ type: 'text',    icon: 'ti-text-size',       label: 'Text' },
			{ type: 'button',  icon: 'ti-hand-click',      label: 'Button' },
			{ type: 'logo',    icon: 'ti-brand-abstract',  label: 'Logo' },
			{ type: 'image',   icon: 'ti-photo',           label: 'Image' },
			{ type: 'divider', icon: 'ti-minus',           label: 'Divider' },
			{ type: 'spacer',  icon: 'ti-arrows-vertical', label: 'Spacer' }
		], 'cb-widget-region--blocks'));
		widgetsRail.appendChild(buildWidgetRegion('Layout', 'columns', false, [
			{ type: 'columns', icon: 'ti-columns', label: '2 columns' },
			{ type: 'billing_shipping', icon: 'ti-map-pin', label: 'Addresses' }
		], 'cb-widget-region--layout'));
		var stylesRegion = el('div', { className: 'cb-widget-region cb-widget-region--styles' });
		var stylesHeader = el('button', {
			type: 'button',
			className: 'cb-widget-region-header',
			'aria-expanded': 'false'
		});
		stylesHeader.innerHTML = '<span>Global styles</span><i class="ti ti-chevron-down" aria-hidden="true"></i>';
		stylesRegion.appendChild(stylesHeader);
		var stylesBody = el('div', { className: 'cb-widget-region-body cb-styles-panel-body' });
		stylesRegion.appendChild(stylesBody);
		widgetsRail.appendChild(stylesRegion);
		structureBody.appendChild(widgetsRail);

		var canvasWrap = el('div', { className: 'cb-canvas-wrap' });
		var newsletterShell = el('div', { className: 'cb-newsletter-shell' });
		newsletterShell.appendChild(el('div', { className: 'cb-canvas-blocks' }));
		canvasWrap.appendChild(newsletterShell);
		structureBody.appendChild(canvasWrap);
		structureCol.appendChild(structureBody);
		wrap.appendChild(structureCol);

		var editCol = el('div', { className: 'cb-edit-col' });
		var editHeader = el('div', { className: 'cb-zone-header cb-zone-header--edit' });
		var editLabel = el('div', { className: 'cb-zone-label' });
		editLabel.innerHTML = '<i class="ti ti-edit" aria-hidden="true"></i> <span class="cb-edit-zone-text">Edit block</span>';
		editHeader.appendChild(editLabel);
		editCol.appendChild(editHeader);

		var editArea = el('div', { className: 'cb-edit-area' });
		editArea.innerHTML = '<div class="cb-edit-empty">'
			+ '<i class="ti ti-cursor-text" aria-hidden="true"></i>'
			+ '<p>Click any block on the left to edit its content and settings here.</p>'
			+ '<span class="cb-edit-empty-hint">\u2190 select a block to start</span>'
			+ '</div>';
		editCol.appendChild(editArea);
		wrap.appendChild(editCol);

		var previewCol = el('div', { className: 'cb-preview-col' });
		var previewHeader = el('div', { className: 'cb-zone-header' });
		var previewLabel = el('div', { className: 'cb-zone-label' });
		previewLabel.innerHTML = '<i class="ti ti-eye" aria-hidden="true"></i> Preview';
		var vpGroup = el('div', { className: 'cb-vp-group' });
		['desktop', 'mobile'].forEach(function (vp) {
			var btn = el('button', {
				type: 'button',
				className: 'cb-vp-btn' + (vp === 'desktop' ? ' is-active' : ''),
				textContent: vp === 'desktop' ? 'Desktop' : 'Mobile'
			});
			btn.dataset.vp = vp;
			vpGroup.appendChild(btn);
		});
		var testLink = el('button', {
			type: 'button',
			className: 'cb-preview-test-btn'
		});
		testLink.innerHTML = '<i class="ti ti-send" aria-hidden="true"></i> Send test';
		testLink.addEventListener('click', function () {
			var mainTestBtn = document.getElementById('ap-test-btn');
			if (mainTestBtn) {
				mainTestBtn.click();
			}
		});

		previewHeader.appendChild(previewLabel);
		previewHeader.appendChild(testLink);
		previewHeader.appendChild(vpGroup);
		previewCol.appendChild(previewHeader);

		var previewWrap = el('div', { className: 'cb-preview-area' });
		previewWrap.dataset.viewport = 'desktop';
		var iframe = el('iframe', { className: 'cb-preview-frame', title: 'Email preview' });
		iframe.setAttribute('sandbox', 'allow-same-origin');
		previewWrap.appendChild(iframe);
		previewCol.appendChild(previewWrap);
		wrap.appendChild(previewCol);

		return wrap;
	}

	function loadPresetIntoBuilder(preset, state, refs, persist, renderCanvas, renderSidebar, card) {
		jQuery.post(cartcueAdmin.ajaxUrl, {
			action: 'cartcue_get_preset_blocks',
			nonce: cartcueAdmin.nonces.builder_preview,
			preset: preset
		}, function (res) {
			if (res && res.success && res.data && Array.isArray(res.data.blocks)) {
				state.blocks = res.data.blocks;
				state.selected = state.blocks.length ? 0 : null;
				var subjectInput = card.querySelector('[name*="[subject]"]');
				if (subjectInput && res.data.subject) {
					subjectInput.value = res.data.subject;
				} else if (subjectInput && window.cartcueAdmin && cartcueAdmin.emailPresets && cartcueAdmin.emailPresets[preset]) {
					subjectInput.value = cartcueAdmin.emailPresets[preset].subject || '';
				}
				persist();
				renderCanvas();
				renderSidebar();
				renderStylesPanel();
			}
		});
	}

	function mountCard(card) {
		if (!card) {
			return;
		}
		var typeSelect = card.querySelector('.ap-action-type');
		if (!typeSelect || typeSelect.value !== 'send_email') {
			delete card.dataset.builderMounted;
			return;
		}
		if (card.dataset.builderMounted === '1' && card.querySelector('.cartcue-builder')) {
			return;
		}
		delete card.dataset.builderMounted;

		var api = createBuilder(card);
		if (!api) {
			return;
		}
		api.refreshFromTextarea();
	}

	function initExistingCards() {
		document.querySelectorAll('#ap-actions-list .ap-action-card').forEach(mountCard);
	}

	function wireActionTypeChange() {
		document.addEventListener('change', function (event) {
			var target = event.target;
			if (!target || !target.classList || !target.classList.contains('ap-action-type')) {
				return;
			}
			window.setTimeout(initExistingCards, 0);
		});
	}

	function observeActionList() {
		var root = document.getElementById('ap-actions-list');
		if (!root || typeof MutationObserver === 'undefined') {
			return;
		}
		var observer = new MutationObserver(function () {
			initExistingCards();
		});
		observer.observe(root, { childList: true, subtree: true });
	}

	function el(tag, props) {
		var node = document.createElement(tag);
		Object.assign(node, props || {});
		return node;
	}

	function esc(str) {
		return String(str || '')
			.replace(/&/g, '&amp;')
			.replace(/"/g, '&quot;')
			.replace(/</g, '&lt;')
			.replace(/>/g, '&gt;');
	}

	function stripTags(html) {
		return String(html || '').replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim();
	}

	document.addEventListener('DOMContentLoaded', function () {
		initExistingCards();
		wireActionTypeChange();
		observeActionList();
	});
})();
