/**
 * Booking Form Builder — Admin JS
 */
/* global formulatePricingBuilder, Sortable, jQuery */
(function ($) {
	'use strict';

	// ── State ────────────────────────────────────────────────────────────────
	var state = {
		formId:          0,
		title:           'New Form',
		config:          null,
		currentStep:     0,
		selectedFieldId: null,
		activeTab:       'field',
		isDirty:         false,
		sortable:        null,
		rowSortables:    [],
	};

	// ── Field type registry ──────────────────────────────────────────────────
	var FIELD_TYPES = {
		text:             { label: 'Text',           icon: 'T',   color: '#6366f1', hasOptions: false },
		email:            { label: 'Email',          icon: '✉',   color: '#0891b2', hasOptions: false },
		phone:            { label: 'Phone',          icon: '☎',   color: '#0891b2', hasOptions: false },
		number:           { label: 'Number',         icon: '#',   color: '#7c3aed', hasOptions: false },
		textarea:         { label: 'Textarea',       icon: '¶',   color: '#6366f1', hasOptions: false },
		url:              { label: 'URL',            icon: '🔗',  color: '#0891b2', hasOptions: false },
		date:             { label: 'Date',           icon: '📅',  color: '#059669', hasOptions: false },
		time:             { label: 'Time',           icon: '🕐',  color: '#059669', hasOptions: false },
		hidden:           { label: 'Hidden',         icon: '◌',   color: '#9ca3af', hasOptions: false },
		radio:            { label: 'Single Choice',  icon: '◉',   color: '#d97706', hasOptions: true  },
		checkbox:         { label: 'Multi Choice',   icon: '☑',   color: '#d97706', hasOptions: true  },
		select:           { label: 'Dropdown',       icon: '▾',   color: '#7c3aed', hasOptions: true  },
		card_choice:      { label: 'Card Choice',    icon: '🃏',  color: '#db2777', hasOptions: true  },
		booking_date:     { label: 'Booking Date',   icon: '📆',  color: '#059669', hasOptions: false },
		booking_timeslot: { label: 'Time Slot',      icon: '⏰',  color: '#059669', hasOptions: true  },
		booking_postcode: { label: 'Postcode',       icon: '📍',  color: '#0891b2', hasOptions: false },
		heading:          { label: 'Heading',        icon: 'H',   color: '#374151', hasOptions: false },
		paragraph:        { label: 'Paragraph',      icon: '¶',   color: '#6b7280', hasOptions: false },
		divider:          { label: 'Divider',        icon: '—',   color: '#9ca3af', hasOptions: false },
	};

	var WIDTH_OPTIONS = [
		{ val: '25',  label: '¼'    },
		{ val: '33',  label: '⅓'    },
		{ val: '50',  label: '½'    },
		{ val: '66',  label: '⅔'    },
		{ val: '75',  label: '¾'    },
		{ val: '100', label: 'Full' },
	];

	// ── Style presets ─────────────────────────────────────────────────────────
	// Each preset has a `light` and `dark` variant.
	// Primary-derived values (button_bg, card_selected_*) are filled by applyPreset().
	var STYLE_PRESETS = {
		// ── Light variants keep a solid form background; text/inputs suit a light page.
		// ── Dark variants set bg_color:'transparent' so the form floats on the page's
		//    dark background without imposing its own dark card. Input fields get a
		//    subtle frosted-glass treatment and text is light for contrast.
		classic: {
			light: { bg_color:'#ffffff',     text_color:'#111827', label_color:'#374151', input_bg:'#ffffff',              input_border:'#d1d5db',              input_border_width:'1', border_radius:'4',  button_border_radius:'6',  font_size:'15', form_padding:'24', field_gap:'16', card_bg:'#ffffff',              card_border:'#d1d5db'              },
			dark:  { bg_color:'transparent', text_color:'#f9fafb', label_color:'#e5e7eb', input_bg:'rgba(255,255,255,0.07)', input_border:'rgba(255,255,255,0.18)', input_border_width:'1', border_radius:'4',  button_border_radius:'6',  font_size:'15', form_padding:'24', field_gap:'16', card_bg:'rgba(255,255,255,0.07)', card_border:'rgba(255,255,255,0.15)' },
		},
		modern: {
			light: { bg_color:'#ffffff',     text_color:'#111827', label_color:'#374151', input_bg:'#f9fafb',              input_border:'#f3f4f6',              input_border_width:'1', border_radius:'12', button_border_radius:'12', font_size:'15', form_padding:'28', field_gap:'18', card_bg:'#f9fafb',              card_border:'#e5e7eb'              },
			dark:  { bg_color:'transparent', text_color:'#f1f5f9', label_color:'#cbd5e1', input_bg:'rgba(255,255,255,0.07)', input_border:'rgba(255,255,255,0.10)', input_border_width:'1', border_radius:'12', button_border_radius:'12', font_size:'15', form_padding:'28', field_gap:'18', card_bg:'rgba(255,255,255,0.07)', card_border:'rgba(255,255,255,0.10)' },
		},
		professional: {
			light: { bg_color:'#ffffff',     text_color:'#0f172a', label_color:'#1e293b', input_bg:'#f8fafc',              input_border:'#e2e8f0',              input_border_width:'1', border_radius:'6',  button_border_radius:'6',  font_size:'14', form_padding:'24', field_gap:'14', card_bg:'#f8fafc',              card_border:'#e2e8f0'              },
			dark:  { bg_color:'transparent', text_color:'#e2e8f0', label_color:'#cbd5e1', input_bg:'rgba(255,255,255,0.06)', input_border:'rgba(255,255,255,0.14)', input_border_width:'1', border_radius:'6',  button_border_radius:'6',  font_size:'14', form_padding:'24', field_gap:'14', card_bg:'rgba(255,255,255,0.06)', card_border:'rgba(255,255,255,0.12)' },
		},
		outline: {
			light: { bg_color:'#ffffff',     text_color:'#111827', label_color:'#374151', input_bg:'transparent',           input_border:'#9ca3af',              input_border_width:'2', border_radius:'8',  button_border_radius:'8',  font_size:'15', form_padding:'24', field_gap:'16', card_bg:'transparent',           card_border:'#d1d5db'              },
			dark:  { bg_color:'transparent', text_color:'#f3f4f6', label_color:'#d1d5db', input_bg:'transparent',           input_border:'rgba(255,255,255,0.30)', input_border_width:'2', border_radius:'8',  button_border_radius:'8',  font_size:'15', form_padding:'24', field_gap:'16', card_bg:'transparent',           card_border:'rgba(255,255,255,0.25)' },
		},
		soft: {
			light: { bg_color:'#f8faff',     text_color:'#1e1b4b', label_color:'#312e81', input_bg:'#eef2ff',              input_border:'transparent',          input_border_width:'0', border_radius:'16', button_border_radius:'20', font_size:'15', form_padding:'28', field_gap:'18', card_bg:'#eef2ff',              card_border:'transparent'          },
			dark:  { bg_color:'transparent', text_color:'#ede9fe', label_color:'#c4b5fd', input_bg:'rgba(255,255,255,0.08)', input_border:'transparent',          input_border_width:'0', border_radius:'16', button_border_radius:'20', font_size:'15', form_padding:'28', field_gap:'18', card_bg:'rgba(255,255,255,0.08)', card_border:'transparent'          },
		},
	};

	// Quick-pick primary color swatches
	var COLOR_SWATCHES = ['#2563eb','#7c3aed','#db2777','#059669','#d97706','#dc2626','#0891b2','#374151'];

	/**
	 * Lighten a hex color by mixing with white at the given ratio (0–1).
	 * Used to compute tinted card-selected-bg from primary.
	 */
	function hexTint(hex, ratio) {
		hex = hex.replace('#', '');
		if (hex.length === 3) { hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2]; }
		var r = parseInt(hex.substring(0,2),16);
		var g = parseInt(hex.substring(2,4),16);
		var b = parseInt(hex.substring(4,6),16);
		r = Math.round(r + (255-r) * ratio);
		g = Math.round(g + (255-g) * ratio);
		b = Math.round(b + (255-b) * ratio);
		return '#' + [r,g,b].map(function(v){ return ('0'+v.toString(16)).slice(-2); }).join('');
	}

	/**
	 * Apply a style preset, merging primary color + bg context.
	 * Updates all [data-style] inputs and state.config.styling.
	 */
	function applyPreset(presetKey, bgContext, primaryColor) {
		var preset = STYLE_PRESETS[presetKey];
		if (!preset) { return; }
		var variant = preset[bgContext] || preset['light'];
		primaryColor = primaryColor || '#2563eb';

		// Merge preset values into state.config.styling
		var s = state.config.styling;
		$.extend(s, variant);
		s.active_preset  = presetKey;
		s.bg_context     = bgContext;
		s.primary_color  = primaryColor;
		s.button_bg      = primaryColor;
		s.button_text_color = (bgContext === 'dark' || presetKey === 'soft') ? '#ffffff' : '#ffffff';

		// Card selected colours: tint from primary
		s.card_selected_border = primaryColor;
		s.card_selected_bg     = hexTint(primaryColor, bgContext === 'dark' ? 0.8 : 0.92);

		// Step / progress track colours derived from context (not preset-specific)
		if (bgContext === 'dark') {
			s.step_track = 'rgba(255,255,255,0.18)';
			s.step_text  = 'rgba(255,255,255,0.50)';
		} else {
			s.step_track = '#e5e7eb';
			s.step_text  = '#6b7280';
		}

		// Push to all visible style inputs
		$('[data-style]').each(function () {
			var k = $(this).data('style');
			if (s[k] !== undefined) {
				$(this).val(s[k]);
			}
		});

		// Sync primary pickers
		$('#bf-primary-color-picker').val(primaryColor);
		$('#bf-primary-color-hex').val(primaryColor);

		// Highlight active preset tile
		$('.bf-preset-tile').removeClass('active');
		$('[data-preset="' + presetKey + '"]').addClass('active');

		// Highlight active bg button
		$('.bf-bg-ctx-btn').removeClass('active');
		$('[data-bg="' + bgContext + '"]').addClass('active');

		markDirty();
	}

	function currentPreset()   { return state.config.styling.active_preset  || 'classic'; }
	function currentBgContext(){ return state.config.styling.bg_context      || 'light'; }
	function currentPrimary()  { return state.config.styling.primary_color   || '#2563eb'; }

	// ── Helpers ──────────────────────────────────────────────────────────────
	function uid()        { return 'f_' + Math.random().toString(36).slice(2, 9); }
	function esc(s)       { return $('<div>').text(String(s || '')).html(); }
	function clone(o)     { return JSON.parse(JSON.stringify(o)); }
	function markDirty()  { state.isDirty = true; $('.bf-unsaved-dot').show(); $('.bf-save-status').text(''); }
	function markClean()  { state.isDirty = false; $('.bf-unsaved-dot').hide(); }

	function currentFields() {
		return state.config.steps[state.currentStep].fields;
	}

	function findField(fieldId) {
		for (var si = 0; si < state.config.steps.length; si++) {
			var fields = state.config.steps[si].fields;
			for (var fi = 0; fi < fields.length; fi++) {
				if (fields[fi].id === fieldId) {
					return { field: fields[fi], si: si, fi: fi };
				}
			}
		}
		return null;
	}

	// ── Initialise ───────────────────────────────────────────────────────────
	function init() {
		state.formId = formulatePricingBuilder.formId;
		state.title  = formulatePricingBuilder.formTitle;
		state.config = clone(formulatePricingBuilder.formConfig);

		renderStepTabs();
		renderCanvas();
		syncSettingPanels();
		bindEvents();

		// Prime the preview URL if we already have a form ID
		if (state.formId) {
			updatePreviewLink(state.formId);
		}

		window.addEventListener('beforeunload', function (e) {
			if (state.isDirty) { e.preventDefault(); e.returnValue = formulatePricingBuilder.strings.confirmLeave; }
		});
	}

	// ── Step tabs ────────────────────────────────────────────────────────────
	function renderStepTabs() {
		var $tabs = $('#bf-steps-tabs').empty();
		state.config.steps.forEach(function (step, si) {
			var $tab = $('<div>', { 'class': 'bf-step-tab' + (si === state.currentStep ? ' active' : ''), 'data-step': si });

			var $name = $('<span>', {
				'class': 'bf-step-tab-name',
				contenteditable: si === state.currentStep,
				spellcheck: false,
				text: step.title || ('Step ' + (si + 1)),
			});
			$name.on('input', function () {
				state.config.steps[si].title = $(this).text().trim() || ('Step ' + (si + 1));
				markDirty();
			}).on('keydown', function (e) {
				if (e.key === 'Enter') { e.preventDefault(); $(this).blur(); }
			});

			$tab.append($name);

			if (state.config.steps.length > 1) {
				var $del = $('<button>', { 'class': 'bf-step-tab-delete', html: '&times;', title: 'Delete step' });
				$del.on('click', function (e) {
					e.stopPropagation();
					if (!confirm('Delete this step and all its fields?')) { return; }
					state.config.steps.splice(si, 1);
					state.currentStep = Math.min(state.currentStep, state.config.steps.length - 1);
					markDirty();
					renderStepTabs();
					renderCanvas();
					showFieldSettings(null);
				});
				$tab.append($del);
			}

			$tab.on('click', function (e) {
				if ($(e.target).is('.bf-step-tab-delete')) { return; }
				// If already on this step and clicking the name span, let the
				// browser handle focus/cursor so the user can type to rename it.
				if (si === state.currentStep && $(e.target).is('.bf-step-tab-name')) { return; }
				switchStep(si);
			});

			$tabs.append($tab);
		});
	}

	function switchStep(si) {
		state.currentStep   = si;
		state.selectedFieldId = null;
		renderStepTabs();
		renderCanvas();
		showFieldSettings(null);
	}

	// ── Canvas ───────────────────────────────────────────────────────────────

	/**
	 * Group a flat array of fields into rows.
	 * A new row starts whenever the next field's width would push the total over 100%.
	 */
	function computeRows(fields) {
		var rows = [];
		var row  = [];
		var rowW = 0;

		fields.forEach(function (f) {
			var w = parseInt(f.width || '100', 10);
			if (row.length > 0 && rowW + w > 100) {
				rows.push(row);
				row  = [f];
				rowW = w;
			} else {
				row.push(f);
				rowW += w;
			}
		});
		if (row.length > 0) { rows.push(row); }
		return rows;
	}

	function renderCanvas() {
		var fields  = currentFields();
		var $wrap   = $('#bf-canvas-fields-wrap').empty();
		var $page   = $('#bf-canvas-page');
		var $empty  = $('#bf-canvas-empty');

		if (fields.length === 0) {
			$empty.show();
			$page.hide();
		} else {
			$empty.hide();
			$page.show();

			var rows = computeRows(fields);
			rows.forEach(function (rowFields, ri) {
				var multiCol = rowFields.length > 1;
				var $row = $('<div>', {
					'class':    'bf-canvas-row' + (multiCol ? ' is-multi-col' : ''),
					'data-row': ri,
				});
				if (multiCol) {
					$row.append($('<span>', {
						'class': 'bf-canvas-row-label',
						text: rowFields.length + ' columns',
					}));
				}
				rowFields.forEach(function (f) { $row.append(buildFieldCard(f)); });
				$wrap.append($row);
			});
		}

		initSortable();
	}

	function buildFieldCard(field) {
		var type  = field.type || 'text';
		var def   = FIELD_TYPES[type] || { label: type, icon: '?', color: '#6b7280' };
		var width = field.width || '100';
		var label = field.label || def.label;
		var isActive = state.selectedFieldId === field.id;

		var $field = $('<div>', {
			'class':       'bf-canvas-field' + (isActive ? ' active' : ''),
			'data-field-id': field.id,
			'data-width':  width,
		});

		var $inner = $('<div>', { 'class': 'bf-cf-inner' });

		// ── Toolbar ──
		var $toolbar = $('<div>', { 'class': 'bf-cf-toolbar' });

		$toolbar.append($('<span>', {
			'class': 'bf-cf-drag',
			html: '&#x2630;',
			title: 'Drag to reorder',
		}));

		$toolbar.append($('<span>', {
			'class': 'bf-cf-type-chip',
			html: '<span class="bf-cf-type-chip-icon">' + esc(def.icon) + '</span><span>' + esc(def.label) + '</span>',
		}).css('color', def.color).css('background', def.color + '18'));

		$toolbar.append($('<span>', { 'class': 'bf-cf-label' }).append(
			$('<span>').text(label),
			field.required ? $('<span>', { 'class': 'bf-cf-required-star', text: ' *' }) : null
		));

		$toolbar.append($('<span>', { 'class': 'bf-cf-badge', text: width + '%' }));

		var $actions = $('<div>', { 'class': 'bf-cf-actions' });
		var $dup = $('<button>', {
			'class': 'bf-cf-action-btn is-dup',
			title: 'Duplicate',
			html: '<i class="hgi-stroke hgi-copy-01"></i>',
		});
		var $del = $('<button>', {
			'class': 'bf-cf-action-btn',
			title: 'Delete',
			html: '<i class="hgi-stroke hgi-delete-02"></i>',
		});
		$dup.on('click', function (e) { e.stopPropagation(); duplicateField(field.id); });
		$del.on('click', function (e) { e.stopPropagation(); deleteField(field.id); });
		$actions.append($dup, $del);
		$toolbar.append($actions);
		$inner.append($toolbar);

		// ── Preview ──
		var $preview = $('<div>', { 'class': 'bf-cf-preview' });
		$preview.append(buildFieldPreview(field));
		$inner.append($preview);

		// ── Width bar ──
		var $wBar = $('<div>', { 'class': 'bf-cf-width-bar' });
		$wBar.append($('<div>', { 'class': 'bf-cf-width-fill', css: { width: width + '%' } }));
		$inner.append($wBar);

		$field.append($inner);

		$field.on('click', function (e) {
			if ($(e.target).closest('.bf-cf-actions').length) { return; }
			selectField(field.id);
		});

		return $field;
	}

	function buildFieldPreview(field) {
		var type = field.type || 'text';
		var placeholder = field.placeholder || '';
		var opts = field.options || [];

		switch (type) {
			case 'text':
			case 'email':
			case 'phone':
			case 'url':
			case 'number':
			case 'booking_postcode':
				return $('<div>', { 'class': 'bf-cf-mock-input', text: placeholder || 'Type here…' });

			case 'textarea':
				return $('<div>', { 'class': 'bf-cf-mock-textarea', text: placeholder || 'Type here…' });

			case 'date':
			case 'booking_date':
				return $('<div>', { 'class': 'bf-cf-mock-date' }).append(
					$('<span>').text('📅'),
					$('<span>').text('dd / mm / yyyy')
				);

			case 'time':
				return $('<div>', { 'class': 'bf-cf-mock-date' }).append(
					$('<span>').text('🕐'),
					$('<span>').text('hh : mm')
				);

			case 'select':
				return $('<div>', { 'class': 'bf-cf-mock-select', text: placeholder || (opts[0] && opts[0].label) || 'Select…' });

			case 'radio':
			case 'booking_timeslot': {
				var $rg = $('<div>', { 'class': 'bf-cf-mock-options' });
				(opts.slice(0, 3)).forEach(function (o) {
					var lbl = (typeof o === 'object' ? o.label : o) || '';
					$rg.append(
						$('<div>', { 'class': 'bf-cf-mock-option' }).append(
							$('<span>', { 'class': 'bf-cf-mock-radio' }),
							$('<span>').text(lbl || 'Option')
						)
					);
				});
				if (!opts.length) {
					['Option 1', 'Option 2'].forEach(function (t) {
						$rg.append($('<div>', { 'class': 'bf-cf-mock-option' }).append($('<span>', { 'class': 'bf-cf-mock-radio' }), $('<span>').text(t)));
					});
				}
				return $rg;
			}

			case 'checkbox': {
				var $cg = $('<div>', { 'class': 'bf-cf-mock-options' });
				(opts.slice(0, 3)).forEach(function (o) {
					var lbl = (typeof o === 'object' ? o.label : o) || '';
					$cg.append(
						$('<div>', { 'class': 'bf-cf-mock-option' }).append(
							$('<span>', { 'class': 'bf-cf-mock-check' }),
							$('<span>').text(lbl || 'Option')
						)
					);
				});
				if (!opts.length) {
					['Option 1', 'Option 2'].forEach(function (t) {
						$cg.append($('<div>', { 'class': 'bf-cf-mock-option' }).append($('<span>', { 'class': 'bf-cf-mock-check' }), $('<span>').text(t)));
					});
				}
				return $cg;
			}

			case 'card_choice': {
				var $cards = $('<div>', { 'class': 'bf-cf-mock-cards' });
				var previewOpts = opts.slice(0, 3);
				if (!previewOpts.length) previewOpts = [{ label: 'A' }, { label: 'B' }, { label: 'C' }];
				previewOpts.forEach(function (o) {
					var lbl = (typeof o === 'object' ? o.label : o) || '';
					$cards.append($('<div>', { 'class': 'bf-cf-mock-card', text: lbl.slice(0, 8) }));
				});
				return $cards;
			}

			case 'heading':
				return $('<div>', { 'class': 'bf-cf-mock-heading', text: field.label || 'Section Heading' });

			case 'paragraph':
				return $('<div>', { 'class': 'bf-cf-mock-paragraph', text: (field.content || 'Paragraph text…').slice(0, 80) });

			case 'divider':
				return $('<div>', { 'class': 'bf-cf-mock-divider' });

			case 'hidden':
				return $('<div>', { 'class': 'bf-cf-mock-input', text: 'Hidden field' }).css({ opacity: .5, fontStyle: 'italic' });

			default:
				return $('<div>', { 'class': 'bf-cf-mock-input', text: placeholder || 'Value…' });
		}
	}

	function selectField(fieldId) {
		state.selectedFieldId = fieldId;
		$('.bf-canvas-field').removeClass('active');
		$('.bf-canvas-field[data-field-id="' + fieldId + '"]').addClass('active');
		showFieldSettings(fieldId);
		switchTab('field');
	}

	function addField(type) {
		var def   = FIELD_TYPES[type] || { label: type };
		var field = {
			id:          uid(),
			type:        type,
			label:       def.label,
			placeholder: '',
			required:    false,
			width:       '100',
			help_text:   '',
			options:     [],
		};

		// Type-specific defaults
		if (type === 'radio' || type === 'checkbox' || type === 'select') {
			field.options = [
				{ label: 'Option 1', value: 'option_1', price: '' },
				{ label: 'Option 2', value: 'option_2', price: '' },
			];
		}
		if (type === 'card_choice') {
			field.multi_select = false;
			field.options = [
				{ label: 'Option A', value: 'a', icon: '⭐', description: '', price: '' },
				{ label: 'Option B', value: 'b', icon: '⚡', description: '', price: '' },
			];
		}
		if (type === 'booking_timeslot') {
			field.options = [
				{ label: '08:00 – 10:00', value: '08:00-10:00', price: '' },
				{ label: '10:00 – 12:00', value: '10:00-12:00', price: '' },
				{ label: '12:00 – 14:00', value: '12:00-14:00', price: '' },
				{ label: '14:00 – 16:00', value: '14:00-16:00', price: '' },
			];
		}
		if (type === 'heading')   { field.label = 'Section Heading'; field.heading_level = 'h3'; }
		if (type === 'paragraph') { field.label = ''; field.content = 'Enter your text here.'; }

		currentFields().push(field);
		markDirty();
		renderCanvas();
		selectField(field.id);
		$('#bf-canvas').scrollTop($('#bf-canvas')[0].scrollHeight);
	}

	function deleteField(fieldId) {
		var fields = currentFields();
		var idx    = fields.findIndex(function (f) { return f.id === fieldId; });
		if (idx === -1) { return; }
		fields.splice(idx, 1);
		if (state.selectedFieldId === fieldId) {
			state.selectedFieldId = null;
			showFieldSettings(null);
		}
		markDirty();
		renderCanvas();
	}

	function duplicateField(fieldId) {
		var fields = currentFields();
		var idx    = fields.findIndex(function (f) { return f.id === fieldId; });
		if (idx === -1) { return; }
		var copy   = clone(fields[idx]);
		copy.id    = uid();
		copy.label = (copy.label || '') + ' (Copy)';
		fields.splice(idx + 1, 0, copy);
		markDirty();
		renderCanvas();
		selectField(copy.id);
	}

	// ── Sortable ─────────────────────────────────────────────────────────────

	/**
	 * Read the current field order from the DOM (across all rows) and
	 * sync it back to state, then re-render so row grouping is recomputed.
	 */
	function syncFieldOrderFromDOM() {
		var newFields = [];
		$('#bf-canvas-fields-wrap .bf-canvas-field').each(function () {
			var id    = $(this).data('field-id');
			var found = findField(id);
			if (found) { newFields.push(found.field); }
		});
		state.config.steps[state.currentStep].fields = newFields;
		markDirty();
		renderCanvas();
		// Restore selection highlight after re-render
		if (state.selectedFieldId) {
			$('[data-field-id="' + state.selectedFieldId + '"]').addClass('active');
		}
	}

	function initSortable() {
		if (typeof Sortable === 'undefined') { return; }

		// Destroy previous sortables
		if (state.sortable)     { state.sortable.destroy();          state.sortable     = null; }
		if (state.rowSortables) { state.rowSortables.forEach(function (s) { s.destroy(); }); }
		state.rowSortables = [];

		// Apply cross-row sortable to every .bf-canvas-row
		$('#bf-canvas-fields-wrap .bf-canvas-row').each(function () {
			var s = Sortable.create(this, {
				group:      'bf-fields',
				animation:  150,
				handle:     '.bf-cf-drag',
				draggable:  '.bf-canvas-field',
				ghostClass: 'sortable-ghost',
				onEnd:      function () { syncFieldOrderFromDOM(); },
			});
			state.rowSortables.push(s);
		});
	}

	// ── Field settings panel ─────────────────────────────────────────────────
	function showFieldSettings(fieldId) {
		var $inner = $('.bf-field-settings-inner');
		var $empty = $('.bf-no-field-selected');

		if (!fieldId) {
			$inner.hide().empty();
			$empty.show();
			return;
		}

		// Auto-open the field accordion when a field is selected
		openAccordion('field');

		var found = findField(fieldId);
		if (!found) { return; }
		var field = found.field;
		var type  = field.type || 'text';
		var def   = FIELD_TYPES[type] || {};

		$empty.hide();
		$inner.show().empty();

		var $wrap = $('<div>');

		// Field type header
		$wrap.append(
			$('<div>', {
				css: {
					display: 'flex', alignItems: 'center', gap: '8px',
					padding: '0 0 12px', marginBottom: '12px',
					borderBottom: '1px solid #f1f3f8',
				},
			}).append(
				$('<span>', {
					text: def.icon || '?',
					css: {
						width: '30px', height: '30px', background: (def.color || '#6366f1') + '18',
						borderRadius: '7px', display: 'flex', alignItems: 'center',
						justifyContent: 'center', fontSize: '15px',
					},
				}),
				$('<div>').append(
					$('<div>', { text: def.label || type, css: { fontWeight: '700', fontSize: '13px' } }),
					$('<div>', { text: 'Field ID: ' + field.id, css: { fontSize: '10px', color: '#9ca3af', fontFamily: 'monospace' } })
				)
			)
		);

		// Label
		var labelKey  = (type === 'paragraph') ? 'content' : 'label';
		var labelText = (type === 'paragraph') ? 'Content' : 'Label';
		if (type !== 'divider') {
			$wrap.append(makeFsRow(labelText, makeFsField((type === 'paragraph') ? 'textarea' : 'text', field[labelKey] || '', labelKey)));
		}

		// Placeholder
		var noPlaceholder = ['radio','checkbox','card_choice','heading','paragraph','divider','date','time','booking_date','booking_timeslot','booking_postcode','hidden'];
		if (!noPlaceholder.includes(type)) {
			$wrap.append(makeFsRow('Placeholder', makeFsField('text', field.placeholder || '', 'placeholder')));
		}

		// Heading level
		if (type === 'heading') {
			var $hl = $('<select>', { 'class': 'bf-fs-input', 'data-fs-key': 'heading_level' });
			['h1','h2','h3','h4','h5','h6'].forEach(function (h) {
				$hl.append($('<option>', { value: h, text: h.toUpperCase(), selected: field.heading_level === h }));
			});
			$wrap.append(makeFsRow('Heading Level', $hl));
		}

		// Card multi-select
		if (type === 'card_choice') {
			$wrap.append(makeFsToggle('Allow Multiple Selections', !!field.multi_select, 'multi_select'));
		}

		// Required
		if (!['heading','paragraph','divider'].includes(type)) {
			$wrap.append(makeFsToggle('Required', !!field.required, 'required'));
		}

		// Width picker
		$wrap.append(makeWidthPicker(field.width || '100'));

		// Help text
		$wrap.append(makeFsRow('Help Text (below field)', makeFsField('text', field.help_text || '', 'help_text')));

		// Number min/max
		if (type === 'number') {
			var $row = $('<div>', { css: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '8px' } });
			$row.append(makeFsRow('Min', makeFsField('number', field.min || '', 'min')));
			$row.append(makeFsRow('Max', makeFsField('number', field.max || '', 'max')));
			$wrap.append($row);
		}

		// Date min/max
		if (type === 'date' || type === 'booking_date') {
			$wrap.append(makeFsRow('Min Date', makeFsField('date', field.min_date || '', 'min_date')));
			$wrap.append(makeFsRow('Max Date', makeFsField('date', field.max_date || '', 'max_date')));
		}

		// Textarea rows
		if (type === 'textarea') {
			$wrap.append(makeFsRow('Rows', makeFsField('number', field.rows || 4, 'rows')));
		}

		// Options
		if (def.hasOptions) {
			$wrap.append($('<div>', { 'class': 'bf-section-heading', text: 'Options', css: { marginTop: '14px', marginBottom: '6px' } }));
			$wrap.append(buildOptionsEditor(field, type));
		}

		$inner.append($wrap);

		// Bind inputs
		bindFsInputs($inner, fieldId);
	}

	function makeFsRow(labelText, $el) {
		return $('<label>', { 'class': 'bf-fs-label' }).append(
			$('<span>', { text: labelText }),
			$el
		);
	}

	function makeFsField(type, value, key) {
		if (type === 'textarea') {
			return $('<textarea>', { 'class': 'bf-fs-input', 'data-fs-key': key, rows: 2 }).val(value);
		}
		return $('<input>', { type: type, 'class': 'bf-fs-input', 'data-fs-key': key, value: value });
	}

	function makeFsToggle(labelText, checked, key) {
		return $('<label>', { 'class': 'bf-sl bf-sl-toggle' }).append(
			$('<span>', { text: labelText }),
			$('<input>', { type: 'checkbox', 'class': 'bf-stoggle', 'data-fs-key': key, checked: checked })
		);
	}

	function makeWidthPicker(currentWidth) {
		var $row    = $('<div>', { 'class': 'bf-fs-label' }).append($('<span>', { text: 'Field Width' }));
		var $picker = $('<div>', { 'class': 'bf-width-picker' });

		WIDTH_OPTIONS.forEach(function (opt) {
			var $o = $('<div>', {
				'class': 'bf-width-opt' + (opt.val === String(currentWidth) ? ' active' : ''),
				text:    opt.label,
				title:   opt.val + '%',
				'data-width': opt.val,
			});
		$o.on('click', function () {
			$picker.find('.bf-width-opt').removeClass('active');
			$(this).addClass('active');
			var w = $(this).data('width');
			var f = findField(state.selectedFieldId);
			if (!f) { return; }
			f.field.width = String(w);
			markDirty();
			// Re-render canvas so computeRows() regroups fields into correct rows.
			// Keep the field selected so settings panel stays open.
			var selId = state.selectedFieldId;
			renderCanvas();
			// Re-highlight without reopening the settings panel
			$('[data-field-id="' + selId + '"]').addClass('active');
		});
			$picker.append($o);
		});

		return $row.append($picker);
	}

	function bindFsInputs($container, fieldId) {
		$container.find('[data-fs-key]').on('input change', function () {
			var key = $(this).data('fs-key');
			var val = $(this).is('[type="checkbox"]') ? this.checked : $(this).val();
			var f   = findField(state.selectedFieldId || fieldId);
			if (!f) { return; }
			f.field[key] = val;

			// Live canvas updates
			if (key === 'label' || key === 'content') {
				$('.bf-canvas-field[data-field-id="' + f.field.id + '"] .bf-cf-label').html(esc(val) + (f.field.required ? ' <span class="bf-cf-required-star">*</span>' : ''));
				// Rebuild preview
				$('.bf-canvas-field[data-field-id="' + f.field.id + '"] .bf-cf-preview').empty().append(buildFieldPreview(f.field));
			}
			if (key === 'required') {
				var $card = $('.bf-canvas-field[data-field-id="' + f.field.id + '"]');
				$card.find('.bf-cf-required-star').remove();
				if (val) { $card.find('.bf-cf-label').append('<span class="bf-cf-required-star"> *</span>'); }
			}
			if (key === 'placeholder') {
				$('.bf-canvas-field[data-field-id="' + f.field.id + '"] .bf-cf-preview').empty().append(buildFieldPreview(f.field));
			}
			markDirty();
		});
	}

	// ── Options editor ───────────────────────────────────────────────────────
	function buildOptionsEditor(field, type) {
		var hasIcon  = (type === 'card_choice');
		var hasDesc  = (type === 'card_choice');
		var $list    = $('<div>', { 'class': 'bf-options-list' });

		function rebuild() {
			$list.empty();
			(field.options || []).forEach(function (opt, idx) {
				$list.append(buildOptionRow(opt, idx, field, hasIcon, hasDesc, rebuild));
			});
			if (typeof Sortable !== 'undefined') {
				Sortable.create($list[0], {
					handle: '.bf-option-drag',
					animation: 120,
					onEnd: function (evt) {
						var moved = field.options.splice(evt.oldIndex, 1)[0];
						field.options.splice(evt.newIndex, 0, moved);
						markDirty();
						rebuild();
					},
				});
			}
		}

		rebuild();

		var $add = $('<button>', { 'class': 'bf-add-option-btn', text: '+ Add Option' });
		$add.on('click', function () {
			var n = field.options.length + 1;
			var newOpt = { label: 'Option ' + n, value: 'option_' + n, price: '' };
			if (hasIcon) { newOpt.icon = ''; newOpt.description = ''; }
			field.options.push(newOpt);
			markDirty();
			rebuild();
			// rebuild canvas preview
			$('.bf-canvas-field[data-field-id="' + field.id + '"] .bf-cf-preview').empty().append(buildFieldPreview(field));
		});

		return $('<div>').append($list, $add);
	}

	function buildOptionRow(opt, idx, field, hasIcon, hasDesc, rebuild) {
		var $item   = $('<div>', { 'class': 'bf-option-item' });
		$item.append($('<span>', { 'class': 'bf-option-drag', html: '&#x2630;' }));
		var $fields = $('<div>', { 'class': 'bf-option-fields' });

		function addInput(placeholder, key) {
			var $i = $('<input>', { type: 'text', placeholder: placeholder, value: opt[key] || '' });
			$i.on('input', function () {
				opt[key] = $(this).val();
				markDirty();
				$('.bf-canvas-field[data-field-id="' + field.id + '"] .bf-cf-preview').empty().append(buildFieldPreview(field));
			});
			$fields.append($i);
		}

		addInput('Label', 'label');
		addInput('Value (slug)', 'value');

		var $price = $('<input>', { type: 'number', placeholder: 'Cart price (hidden on form)', value: opt.price || '', step: '0.01', min: '0', title: 'Used for WooCommerce total only — not shown on the form' });
		$price.on('input', function () { opt.price = $(this).val(); markDirty(); });
		$fields.append($price);

	if (hasIcon) { addInput('Icon (emoji)', 'icon'); }
	if (hasDesc) {
		var $descWrap = $('<div>', { 'class': 'bf-option-desc-wrap' });
		var $descTa   = $('<textarea>', {
			'class':       'bf-option-desc-ta',
			placeholder:   'Short description (HTML allowed, e.g. <b>bold</b>)',
			rows:          2,
			text:          opt.description || '',
		});
		$descTa.on('input', function () {
			opt.description = $(this).val();
			markDirty();
			$('.bf-canvas-field[data-field-id="' + field.id + '"] .bf-cf-preview').empty().append(buildFieldPreview(field));
		});
		var $descHint = $('<span>', { 'class': 'bf-option-desc-hint', text: 'HTML tags allowed' });
		$descWrap.append($descTa, $descHint);
		$fields.append($descWrap);
	}

		var $del = $('<button>', { 'class': 'bf-option-del', html: '&times;', title: 'Remove' });
		$del.on('click', function () {
			field.options.splice(idx, 1);
			markDirty();
			$('.bf-canvas-field[data-field-id="' + field.id + '"] .bf-cf-preview').empty().append(buildFieldPreview(field));
			rebuild();
		});

		$item.append($fields, $del);
		return $item;
	}

	// ── Sync settings panels with config ─────────────────────────────────────
	function syncSettingPanels() {
		var settings = state.config.settings || {};
		var styling  = state.config.styling  || {};

		$('[data-setting]').each(function () {
			var key = $(this).data('setting');
			var val = settings[key];
			if (val === undefined) { return; }
			$(this).is('[type="checkbox"]') ? $(this).prop('checked', !!val) : $(this).val(val);
		});

		$('[data-style]').each(function () {
			var key = $(this).data('style');
			if (styling[key] !== undefined) { $(this).val(styling[key]); }
		});

		// Restore preset UI state
		var ap = styling.active_preset || 'classic';
		var bg = styling.bg_context    || 'light';
		var pc = styling.primary_color || '#2563eb';
		$('.bf-preset-tile').removeClass('active');
		$('[data-preset="' + ap + '"]').addClass('active');
		$('.bf-bg-ctx-btn').removeClass('active');
		$('[data-bg="' + bg + '"]').addClass('active');
		$('#bf-primary-color-picker').val(pc);
		$('#bf-primary-color-hex').val(pc);

		// If no preset saved yet, apply Classic/Light as default silently (no dirty mark).
		if (!styling.active_preset) {
			applyPreset('classic', 'light', pc);
			markClean();
		}

		// Open field accordion by default on load
		openAccordion('field');

		// Sync WC product card UI
		syncWcProductSection();

		// Sync notifications panel
		syncNotifyPanel();
	}

	// ── Accordion toggle ──────────────────────────────────────────────────────
	function openAccordion(name) {
		var $sec = $('#bf-acc-' + name);
		if ($sec.hasClass('open')) { return; }
		$sec.addClass('open');
	}

	function toggleAccordion(name) {
		$('#bf-acc-' + name).toggleClass('open');
	}

	// ── Flush all settings panels into state.config (shared by save + preview) ─
	function flushPanels() {
		$('#bf-spane-form [data-setting], #bf-spane-wc [data-setting]').each(function () {
			var key = $(this).data('setting');
			state.config.settings[key] = $(this).is('[type="checkbox"]') ? this.checked : $(this).val();
		});
		$('#bf-spane-style [data-style]').each(function () {
			state.config.styling[$(this).data('style')] = $(this).val();
		});
		if (!state.config.notifications) { state.config.notifications = {}; }
		$('#bf-spane-notify [data-notif]').each(function () {
			var key = $(this).data('notif');
			state.config.notifications[key] = $(this).is('[type="checkbox"]') ? this.checked : $(this).val();
		});
	}

	// ── Save ──────────────────────────────────────────────────────────────────
	function save() {
		flushPanels();

		var title = $('#bf-form-title').val().trim() || 'New Form';
		state.title = title;

		var $btn = $('#bf-save-btn');
		$btn.prop('disabled', true);
		$('.bf-save-status').text(formulatePricingBuilder.strings.saving || 'Saving…');

		$.post(formulatePricingBuilder.ajaxUrl, {
			action:               'formulate_pricing_save_form',
			nonce:                formulatePricingBuilder.nonce,
			formulate_pricing_form_id: state.formId,
			form_id:              state.formId,
			title:                title,
			form_config:          JSON.stringify(state.config),
		}, function (res) {
			$btn.prop('disabled', false);
			if (res.success) {
				var savedId = res.data.form_id;
				if (savedId) {
					assignFormId(savedId, res.data.preview_nonce);
				}
				markClean();
				$('.bf-save-status').text('✓ Saved').css('color', '#6ee7b7');
				setTimeout(function () { $('.bf-save-status').text('').css('color', ''); }, 3000);
				showToast('Form saved successfully!');
			} else {
				$('.bf-save-status').text(res.data.message || 'Save failed.').css('color', '#fca5a5');
			}
		}).fail(function () {
			$btn.prop('disabled', false);
			$('.bf-save-status').text('Server error.').css('color', '#fca5a5');
		});
	}

	/** Keep JS state + URL in sync after the server assigns or confirms a form ID. */
	function assignFormId(id, previewNonce) {
		id = parseInt(id, 10);
		if (!id) { return; }
		state.formId = id;
		formulatePricingBuilder.formId = id;
		var editUrl = formulatePricingBuilder.editBaseUrl + id;
		if (window.location.href.indexOf('form_id=') === -1) {
			history.replaceState(null, '', editUrl);
		}
		updateShortcode(id);
		updatePreviewLink(id, previewNonce);
	}

	function updateShortcode(id) {
		var sc = '[formulate_pricing_form id="' + id + '"]';
		if ($('#bf-shortcode-display').length) {
			$('#bf-shortcode-display').text(sc);
			$('.bf-copy-sc').attr('data-sc', sc);
		} else {
			var $pill = $('<div>', { 'class': 'bf-shortcode-pill' }).append(
				$('<code>', { id: 'bf-shortcode-display', text: sc }),
			$('<button>', {
				'class': 'bf-copy-sc',
				type: 'button',
				title: 'Copy shortcode',
				'data-sc': sc,
				html: '<i class="hgi-stroke hgi-copy-01"></i>',
			})
			);
			$('.bf-topbar-right').prepend($pill);
		}
	}

	function updatePreviewLink(id, previewNonce) {
		if (previewNonce) {
			formulatePricingBuilder.previewNonce = previewNonce;
		}
		var nonce = formulatePricingBuilder.previewNonce || '';
		var url = formulatePricingBuilder.ajaxUrl + '?action=formulate_pricing_preview_form&form_id=' + id + '&nonce=' + encodeURIComponent(nonce);
		$('.bf-preview-btn').data('preview-url', url);
		$('#bf-preview-open-tab').attr('href', url);
	}

	// ── Preview overlay ────────────────────────────────────────────────────────
	function openPreview() {
		// Mark that user has requested a preview open; closePreviewOverlay() clears this
		state.previewPending = true;

		// If the form has never been saved yet or is dirty, save first
		if ( state.isDirty || ! state.formId ) {
			var $btn = $('#bf-preview-btn');
			$btn.prop('disabled', true);
			$btn.append($('<span>', { class: 'bf-preview-saving-msg', text: ' Saving…' }));

			flushPanels();
			var title = $('#bf-form-title').val().trim() || 'New Form';
			state.title = title;

			$.post(formulatePricingBuilder.ajaxUrl, {
				action:               'formulate_pricing_save_form',
				nonce:                formulatePricingBuilder.nonce,
				formulate_pricing_form_id: state.formId,
				form_id:              state.formId,
				title:                title,
				form_config:          JSON.stringify(state.config),
			}, function (res) {
				$btn.prop('disabled', false);
				$('.bf-preview-saving-msg').remove();
				if (res.success) {
					var savedId = res.data.form_id;
					if (savedId) {
						assignFormId(savedId, res.data.preview_nonce);
					}
					markClean();
					$('.bf-save-status').text('✓ Saved').css('color', '#6ee7b7');
					setTimeout(function () { $('.bf-save-status').text('').css('color', ''); }, 3000);
					// Only open the overlay if the user hasn't already closed it
					if (state.previewPending) {
						openPreviewOverlay(state.formId);
					}
				} else {
					state.previewPending = false;
					$('.bf-save-status').text('Save failed.').css('color', '#fca5a5');
				}
			}).fail(function () {
				$btn.prop('disabled', false);
				state.previewPending = false;
				$('.bf-preview-saving-msg').remove();
				$('.bf-save-status').text('Server error.').css('color', '#fca5a5');
			});

		} else {
			openPreviewOverlay(state.formId);
		}
	}

	function openPreviewOverlay(formId) {
		var nonce = formulatePricingBuilder.previewNonce || formulatePricingBuilder.nonce;
		var url   = formulatePricingBuilder.ajaxUrl + '?action=formulate_pricing_preview_form&form_id=' + formId + '&nonce=' + encodeURIComponent(nonce);

		// Update "open in tab" link
		$('#bf-preview-open-tab').attr('href', url);

		// Set form name label
		$('#bf-preview-form-name').text($('#bf-form-title').val().trim() || 'Form Preview');

		// Show overlay, hide iframe, show spinner
		var $overlay = $('#bf-preview-overlay');
		var $iframe  = $('#bf-preview-iframe');
		var $loading = $('#bf-preview-loading');

		$loading.show();
		$iframe.hide().attr('src', 'about:blank');
		$overlay.show();

		// Set default device (desktop = full width)
		setPreviewDevice($('.bf-dev-btn.active').data('device') || 'desktop');

		// Load iframe
		$iframe.one('load', function () {
			$loading.hide();
			$iframe.show();
		});
		$iframe.attr('src', url);

		// Trap Escape key to close
		$(document).one('keydown.bf-preview', function (e) {
			if (e.key === 'Escape') { closePreviewOverlay(); }
		});
	}

	function closePreviewOverlay() {
		state.previewPending = false;
		$('#bf-preview-overlay').hide();
		$('#bf-preview-iframe').attr('src', 'about:blank');
		$(document).off('keydown.bf-preview');
	}

	function setPreviewDevice(device) {
		var widths  = { desktop: '100%', tablet: '768px', mobile: '390px' };
		var $body   = $('#bf-preview-body');
		var $wrap   = $('#bf-preview-iframe-wrap');
		var w = widths[device] || '100%';

		if ( device === 'desktop' ) {
			$body.removeClass('is-device-frame');
			$wrap.css({ 'max-width': '', 'margin': '' });
		} else {
			$body.addClass('is-device-frame');
			$wrap.css({ 'max-width': w, 'margin': '0 auto' });
		}
	}

	// ── Toast ─────────────────────────────────────────────────────────────────
	function showToast(msg) {
		$('.bf-toast').remove();
		var $t = $('<div>', { 'class': 'bf-toast', text: msg });
		$('body').append($t);
		setTimeout(function () { $t.fadeOut(300, function () { $t.remove(); }); }, 2500);
	}

	// ── Event bindings ────────────────────────────────────────────────────────
	// ── Notifications panel helpers ───────────────────────────────────────────

	function getEmailFields() {
		var fields = [];
		(state.config.steps || []).forEach(function (step) {
			(step.fields || []).forEach(function (f) {
				if (f.type === 'email') {
					fields.push({ id: f.id, label: f.label || 'Email' });
				}
			});
		});
		return fields;
	}

	function getAllFields() {
		var fields = [];
		(state.config.steps || []).forEach(function (step) {
			(step.fields || []).forEach(function (f) {
				if (f.label) { fields.push({ id: f.id, label: f.label }); }
			});
		});
		return fields;
	}

	function syncNotifyPanel() {
		var n = state.config.notifications || {};

		// Populate email-field dropdowns
		var emailFields  = getEmailFields();
		var allFields    = getAllFields();

		var $userEmail   = $('#bf-notif-user-email-field').empty().append('<option value="">' + '— Select email field —' + '</option>');
		var $replyTo     = $('#bf-notif-admin-replyto').empty().append('<option value="">' + '— None —' + '</option>');

		emailFields.forEach(function (f) {
			$userEmail.append($('<option>', { value: f.id, text: f.label }));
		});
		allFields.forEach(function (f) {
			$replyTo.append($('<option>', { value: f.id, text: f.label }));
		});

		// Restore values
		$('#bf-spane-notify [data-notif]').each(function () {
			var key = $(this).data('notif');
			var val = n[key];
			if (val === undefined) { return; }
			$(this).is('[type="checkbox"]') ? $(this).prop('checked', !!val) : $(this).val(val);
		});

		// Show/hide sub-sections
		$('#bf-notif-admin-fields').toggle(!!n.admin_enabled);
		$('#bf-notif-user-fields').toggle(!!n.user_enabled);
	}

	// ── WC product card helpers ───────────────────────────────────────────────

	function showLinkedProduct(id, name, editUrl) {
		$('#bf-wc-product-id-input').val(id);
		state.config.settings.wc_product_id = String(id);
		$('#bf-product-name').text(name);
		$('#bf-product-edit-link').attr('href', editUrl || '#');
		$('#bf-wc-no-product').hide();
		$('#bf-wc-linked-product').show();
	}

	function showNoProduct() {
		$('#bf-wc-product-id-input').val(0);
		state.config.settings.wc_product_id = '0';
		$('#bf-wc-no-product').show();
		$('#bf-wc-linked-product').hide();
		// Reset the Select2 picker
		var $sel = $('#bf-manual-product-select');
		if (typeof $.fn.select2 !== 'undefined' && $sel.data('select2')) {
			$sel.val(null).trigger('change');
		} else {
			$sel.val('');
		}
	}

	function syncWcProductSection() {
		var linked = formulatePricingBuilder.linkedProduct;
		var pid    = state.config.settings.wc_product_id;
		var isEnabled = !!state.config.settings.wc_enabled;

		$('#bf-wc-product-section').toggle(isEnabled);

		if (linked && linked.id) {
			showLinkedProduct(linked.id, linked.name, linked.edit_url);
		} else if (pid && pid !== '0' && parseInt(pid, 10) > 0) {
			// Product ID is known but no name — use a placeholder
			$('#bf-wc-product-id-input').val(pid);
			$('#bf-product-name').text('#' + pid);
			$('#bf-product-edit-link').attr('href', '#');
			$('#bf-wc-no-product').hide();
			$('#bf-wc-linked-product').show();
		} else {
			showNoProduct();
		}
	}

	function bindEvents() {
		// Save
		$('#bf-save-btn').on('click', save);
		$(document).on('keydown', function (e) {
			if ((e.ctrlKey || e.metaKey) && e.key === 's') { e.preventDefault(); save(); }
		});

		// Style preset tiles
		$(document).on('click', '.bf-preset-tile', function () {
			applyPreset($(this).data('preset'), currentBgContext(), currentPrimary());
		});

		// Background context toggle
		$(document).on('click', '.bf-bg-ctx-btn', function () {
			applyPreset(currentPreset(), $(this).data('bg'), currentPrimary());
		});

		// Primary color picker (native color input)
		$(document).on('input', '#bf-primary-color-picker', function () {
			var c = $(this).val();
			$('#bf-primary-color-hex').val(c);
			applyPreset(currentPreset(), currentBgContext(), c);
		});

		// Primary color hex input
		$(document).on('change', '#bf-primary-color-hex', function () {
			var v = $(this).val().trim();
			if (/^#[0-9a-fA-F]{6}$/.test(v)) {
				$('#bf-primary-color-picker').val(v);
				applyPreset(currentPreset(), currentBgContext(), v);
			}
		});

		// Color swatches
		$(document).on('click', '.bf-color-swatch', function () {
			var c = $(this).data('color');
			$('#bf-primary-color-picker').val(c);
			$('#bf-primary-color-hex').val(c);
			applyPreset(currentPreset(), currentBgContext(), c);
		});

		// Preview
		$('#bf-preview-btn').on('click', function (e) {
			e.preventDefault();
			openPreview();
		});
		$('#bf-preview-close').on('click', closePreviewOverlay);

		// Device switcher
		$(document).on('click', '.bf-dev-btn', function () {
			$('.bf-dev-btn').removeClass('active');
			$(this).addClass('active');
			setPreviewDevice($(this).data('device'));
		});

		// Add step
		$('#bf-add-step-btn').on('click', function () {
			var n = state.config.steps.length + 1;
			state.config.steps.push({ id: 'step_' + uid(), title: 'Step ' + n, fields: [] });
			state.currentStep = state.config.steps.length - 1;
			state.config.settings.multi_step = true;
			$('[data-setting="multi_step"]').prop('checked', true);
			markDirty();
			renderStepTabs();
			renderCanvas();
			showFieldSettings(null);
		});

		// Field type buttons
		$(document).on('click', '.bf-field-type-btn', function () {
			addField($(this).data('type'));
		});

		// Field search
		$('#bf-field-search').on('input', function () {
			var q = $(this).val().toLowerCase();
			$('.bf-field-type-btn').each(function () {
				$(this).toggleClass('hidden', !!q && $(this).data('label').toLowerCase().indexOf(q) === -1);
			});
			$('.bf-field-group').each(function () {
				$(this).toggle(!q || $(this).find('.bf-field-type-btn:not(.hidden)').length > 0);
			});
		});

		// Accordion toggle
		$(document).on('click', '.bf-acc-header', function () {
			var name = $(this).data('acc');
			toggleAccordion(name);
			// Populate notify dropdowns lazily when that section opens
			if (name === 'notify' && $('#bf-acc-notify').hasClass('open')) {
				syncNotifyPanel();
			}
		});

		// Title
		$('#bf-form-title').on('input', function () { state.title = $(this).val().trim(); markDirty(); });

		// Deselect when clicking canvas bg
		$('#bf-canvas').on('click', function (e) {
			if ($(e.target).is('#bf-canvas, .bf-canvas-scroll, .bf-canvas-page, .bf-canvas-fields-wrap')) {
				state.selectedFieldId = null;
				$('.bf-canvas-field').removeClass('active');
				showFieldSettings(null);
			}
		});

		// Copy shortcode
		$(document).on('click', '.bf-copy-sc', function (e) {
			e.preventDefault();
			var text = ($(this).data('sc') || $('#bf-shortcode-display').text() || '').trim();
			if (!text) {
				return;
			}

			function onCopied() {
				showToast('Shortcode copied!');
			}

			function fallbackCopy(str, callback) {
				var $tmp = $('<textarea>').val(str).css({ position: 'fixed', left: '-9999px', top: 0 }).appendTo('body');
				$tmp[0].focus();
				$tmp[0].select();
				try {
					document.execCommand('copy');
					callback();
				} catch (err) { /* ignore */ }
				$tmp.remove();
			}

			if (navigator.clipboard && window.isSecureContext) {
				navigator.clipboard.writeText(text).then(onCopied).catch(function () {
					fallbackCopy(text, onCopied);
				});
			} else {
				fallbackCopy(text, onCopied);
			}
		});

		// Settings changes → mark dirty
		$(document).on('input change', '[data-setting], [data-style]', markDirty);

		// WC enabled toggle → show/hide product section
		$(document).on('change', '#bf-wc-enabled-toggle', function () {
			$('#bf-wc-product-section').toggle(this.checked);
		});

		// Notification toggles → show/hide sub-sections
		$(document).on('change', '#bf-notif-admin-toggle', function () {
			$('#bf-notif-admin-fields').toggle(this.checked);
		});
		$(document).on('change', '#bf-notif-user-toggle', function () {
			$('#bf-notif-user-fields').toggle(this.checked);
		});

		// (notify panel is synced in the accordion open handler above)

		// Notification inputs → mark dirty
		$(document).on('input change', '#bf-spane-notify [data-notif]', markDirty);

		// Create product for this form
		$(document).on('click', '#bf-create-product-btn', function () {
			var $btn = $(this);
			$btn.prop('disabled', true).text('Creating…');
			$.post(formulatePricingBuilder.ajaxUrl, {
				action:     'formulate_pricing_create_product_ajax',
				nonce:      formulatePricingBuilder.nonce,
				form_title: $('#bf-form-title').val().trim() || state.title || 'Booking Form',
			}, function (res) {
				$btn.prop('disabled', false).html(
					'<i class="hgi-stroke hgi-package-add"></i> Create Product for This Form'
				);
				if (res.success && res.data.product_id) {
					formulatePricingBuilder.linkedProduct = { id: res.data.product_id, name: res.data.name, edit_url: res.data.edit_url };
					showLinkedProduct(res.data.product_id, res.data.name, res.data.edit_url);
					markDirty();
					showToast('Product "' + res.data.name + '" created and linked!');
				} else {
					showToast((res.data && res.data.message) ? res.data.message : 'Could not create product.');
				}
			});
		});

		// Link an existing product via Select2 dropdown
		$(document).on('click', '#bf-set-manual-product', function () {
			var $sel = $('#bf-manual-product-select');
			var pid  = parseInt($sel.val(), 10);
			if (!pid || pid < 1) { showToast('Select a product first.'); return; }
			// Strip the " #123" suffix we appended to the option text
			var name = $sel.find('option:selected').text().replace(/\s+#\d+\s*$/, '').trim();
			var editUrl = '';
			if (window.location && window.location.origin) {
				editUrl = window.location.origin + '/wp-admin/post.php?post=' + pid + '&action=edit';
			}
			formulatePricingBuilder.linkedProduct = { id: pid, name: name, edit_url: editUrl };
			showLinkedProduct(pid, name, editUrl);
			$sel.val(null).trigger('change'); // reset dropdown
			markDirty();
		});

		// Unlink product → go back to no-product state
		$(document).on('click', '#bf-unlink-product-btn', function () {
			formulatePricingBuilder.linkedProduct = null;
			showNoProduct();
			markDirty();
		});
	}

	// ── Boot ──────────────────────────────────────────────────────────────────
	$(document).ready(function () {
		if (typeof formulatePricingBuilder === 'undefined') { return; }
		init();

		// Initialise Select2 on the product picker (after DOM is ready)
		if (typeof $.fn.select2 !== 'undefined') {
			$('#bf-manual-product-select').select2({
				placeholder:        'Search products…',
				allowClear:         true,
				dropdownParent:     $('#bf-wc-no-product'),
				width:              '100%',
				theme:              'classic',
			});
		}
	});

}(jQuery));
