(function () {
  'use strict';

	var translatedMessages = window.MPAB_DATA && window.MPAB_DATA.messages ? window.MPAB_DATA.messages : {};

	function message(key, fallback, values) {
		var output = translatedMessages[key] || fallback;
		(values || []).forEach(function (value, index) {
			output = output.replace(new RegExp('%' + (index + 1) + '\\$[sd]|%[sd]'), String(value));
		});
		return output.replace(/%%/g, '%');
	}

  function money(value, currency) {
    var number = Number(value || 0);
    var formatted = Number.isInteger(number)
      ? String(number)
      : number.toFixed(2);
    return currency + formatted;
  }

	function roundMoney(value) {
		return Math.round((Number(value) || 0) * 100) / 100;
	}

  function selected(form, name) {
    return form.querySelector('input[name="' + name + '"]:checked');
  }

  function optionData(input) {
    if (!input) {
      return null;
    }
    return {
      id: input.value,
      label: input.getAttribute('data-label') || input.value,
      price: Number(input.getAttribute('data-price') || 0),
      spreadPrice: Number(input.getAttribute('data-spread-price') || 0),
			image: input.getAttribute('data-image') || ''
    };
  }

	function personalTextLines(form) {
		return Array.prototype.map.call(
			form.querySelectorAll('input[name="personal_text_lines[]"]:not(:disabled)'),
			function (field) { return field.value.trim(); }
		).filter(function (line) { return line !== ''; });
	}

  function rgbParts(value) {
    var hex;
    var match;
    if (!value) {
      return null;
    }
    hex = value.trim().replace('#', '');
    if (/^[0-9a-f]{3}$/i.test(hex)) {
      hex = hex.split('').map(function (part) { return part + part; }).join('');
    }
    if (/^[0-9a-f]{6}$/i.test(hex)) {
      return [
        parseInt(hex.slice(0, 2), 16),
        parseInt(hex.slice(2, 4), 16),
        parseInt(hex.slice(4, 6), 16)
      ];
    }
    match = value.match(/rgba?\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)/i);
    return match ? [Number(match[1]), Number(match[2]), Number(match[3])] : null;
  }

	function isVisibleColour(value) {
		var alpha;
		var match;
		if (!value || value.trim().toLowerCase() === 'transparent' || !rgbParts(value)) {
			return false;
		}
		match = value.match(/rgba\([^)]*,\s*([0-9.]+)\s*\)$/i);
		alpha = match ? Number(match[1]) : 1;
		return alpha > 0.05;
	}

	function inheritedTextColour(element, fallback) {
		var current = element;
		var colour;
		while (current) {
			colour = window.getComputedStyle(current).color;
			if (isVisibleColour(colour)) {
				return colour;
			}
			current = current.parentElement;
		}
		return isVisibleColour(fallback) ? fallback : '#222222';
	}

  function withAlpha(value, alpha, fallback) {
    var rgb = rgbParts(value);
    return rgb
      ? 'rgba(' + rgb[0] + ', ' + rgb[1] + ', ' + rgb[2] + ', ' + alpha + ')'
      : fallback;
  }

  function contrastText(value) {
    var rgb = rgbParts(value);
    var luminance;
    if (!rgb) {
      return '#ffffff';
    }
    luminance = ((rgb[0] * 299) + (rgb[1] * 587) + (rgb[2] * 114)) / 1000;
    return luminance > 160 ? '#111111' : '#ffffff';
  }

  function isUsableMarkColour(value) {
    var rgb = rgbParts(value);
    if (!rgb || /rgba\([^)]*,\s*0(?:\.0+)?\s*\)/i.test(value)) {
      return false;
    }
    return !(rgb[0] > 245 && rgb[1] > 245 && rgb[2] < 20);
  }

	function enhanceOptionThumbnails(builder) {
		Array.prototype.forEach.call(builder.querySelectorAll('.mpab-option input[type="radio"]'), function (input) {
			var body = input.nextElementSibling;
			var media;
			var image;
			var placeholder;
			var imageUrl;
			var variant;

			if (!body || body.querySelector('.mpab-option__media')) {
				return;
			}

			imageUrl = input.getAttribute('data-image') || '';
			variant = input.getAttribute('data-thumb') || '';
			if (!imageUrl && !variant) {
				return;
			}
			media = document.createElement('span');
			media.className = 'mpab-option__media';

			if (imageUrl) {
				image = document.createElement('img');
				image.className = 'mpab-option__image';
				image.alt = '';
				image.loading = 'lazy';
				image.decoding = 'sync';
				image.src = imageUrl;
				media.appendChild(image);
			} else {
				placeholder = document.createElement('span');
				placeholder.className = 'mpab-thumb mpab-thumb--' + variant;
				placeholder.setAttribute('aria-hidden', 'true');
				media.appendChild(placeholder);
			}

			body.insertBefore(media, body.firstChild);
			body.classList.add('mpab-option__body--visual');
		});
	}

	function wakeVisibleOptionImages(builder) {
		Array.prototype.forEach.call(builder.querySelectorAll('img.mpab-option__image'), function (image) {
			var source;
			if (image.closest('[hidden]') || (image.complete && image.naturalWidth > 0) || image.getAttribute('data-mpab-image-state') === 'loading') {
				return;
			}
			source = image.getAttribute('src') || image.getAttribute('data-mpab-image-source') || '';
			if (!source || image.getAttribute('data-mpab-image-state') === 'failed') {
				return;
			}
			image.setAttribute('data-mpab-image-source', source);
			image.setAttribute('data-mpab-image-state', 'loading');
			image.loading = 'eager';
			image.decoding = 'sync';
			image.addEventListener('load', function () {
				image.setAttribute('data-mpab-image-state', 'ready');
			}, { once: true });
			image.addEventListener('error', function () {
				image.setAttribute('data-mpab-image-state', 'failed');
			}, { once: true });
			image.removeAttribute('src');
			window.requestAnimationFrame(function () {
				image.setAttribute('src', source);
			});
		});
	}

	function setResponsiveChoiceCounts(builder) {
		var rules = [
			['.mpab-options--album-types', 2, 190],
			['.mpab-options--sizes', 4, 118],
			['.mpab-options--cover-materials', 4, 90],
			['.mpab-options--swatches', 6, 90],
			['.mpab-options--font-gallery', 6, 112],
			['.mpab-options--design-gallery', 8, 86],
			['.mpab-options--foil-colours', 6, 100],
			['.mpab-options--box-colours', 3, 110],
			['.mpab-options--personalisation-methods', 2, 170],
			['.mpab-options--presentation', 2, 170],
			['.mpab-options--two-by-two', 2, 170],
			['.mpab-options--three', 3, 110],
			['.mpab-options--four', 4, 100]
		];

		function matchingRule(grid) {
			for (var i = 0; i < rules.length; i += 1) {
				if (grid.matches(rules[i][0])) {
					return rules[i];
				}
			}
			return ['.mpab-options', 2, 160];
		}

		function update() {
			var builderWidth = builder.getBoundingClientRect().width;
			builder.setAttribute('data-mpab-compact', builderWidth > 0 && builderWidth <= 720 ? '1' : '0');
			Array.prototype.forEach.call(builder.querySelectorAll('.mpab-options'), function (grid) {
				var optionCount = Array.prototype.filter.call(grid.children, function (child) {
					return child.classList && child.classList.contains('mpab-option');
				}).length;
				var rule = matchingRule(grid);
				var width = grid.getBoundingClientRect().width;
				var gap;
				var columns;
				var minimumColumns;
				if (!optionCount || width < 1) {
					return;
				}
				gap = parseFloat(window.getComputedStyle(grid).columnGap) || 0;
				columns = Math.floor((width + gap) / (rule[2] + gap));
				minimumColumns = optionCount > 1 ? 2 : 1;
				columns = Math.max(minimumColumns, Math.min(rule[1], optionCount, columns));
				grid.style.setProperty('--mpab-columns', columns);
				grid.setAttribute('data-mpab-columns', String(columns));
			});
		}

		update();
		if (window.ResizeObserver) {
			new ResizeObserver(update).observe(builder);
		} else {
			window.addEventListener('resize', update);
		}
		return update;
	}

  function applyHostTheme(builder) {
    var host = builder.parentElement || document.body;
    var probe = document.createElement('div');
    var heading = document.createElement('h2');
    var link = document.createElement('a');
    var mark = document.createElement('mark');
    var button = document.createElement('button');
    var input = document.createElement('input');
    var hostStyle;
    var headingStyle;
    var linkStyle;
    var markStyle;
    var buttonStyle;
    var inputStyle;
    var accent;
    var customAccent;
		var textColour;

    probe.className = 'mpab-theme-probe';
    probe.setAttribute('aria-hidden', 'true');
    heading.textContent = 'Album heading';
    link.textContent = 'Album link';
    link.setAttribute('href', '#');
    mark.textContent = 'Album highlight';
    button.textContent = 'Album button';
    button.setAttribute('type', 'button');
    input.setAttribute('type', 'text');
    probe.appendChild(heading);
    probe.appendChild(link);
    probe.appendChild(mark);
    probe.appendChild(button);
    probe.appendChild(input);
    host.appendChild(probe);

    hostStyle = window.getComputedStyle(host);
    headingStyle = window.getComputedStyle(heading);
    linkStyle = window.getComputedStyle(link);
    markStyle = window.getComputedStyle(mark);
    buttonStyle = window.getComputedStyle(button);
    inputStyle = window.getComputedStyle(input);
		textColour = inheritedTextColour(host, inputStyle.color);

    builder.style.setProperty('--mpab-body-font', hostStyle.fontFamily);
    builder.style.setProperty('--mpab-heading-font', headingStyle.fontFamily || hostStyle.fontFamily);
    builder.style.setProperty('--mpab-heading-weight', headingStyle.fontWeight || 'inherit');
    builder.style.setProperty('--mpab-heading-style', headingStyle.fontStyle || 'normal');
    builder.style.setProperty('--mpab-heading-spacing', headingStyle.letterSpacing || 'normal');
    builder.style.setProperty('--mpab-text', textColour);
    builder.style.setProperty('--mpab-button-radius', buttonStyle.borderRadius || '0px');
    builder.style.setProperty('--mpab-field-radius', inputStyle.borderRadius || '0px');

		customAccent = builder.style.getPropertyValue('--mpab-accent').trim();
		if (customAccent) {
			accent = customAccent;
		} else if (isUsableMarkColour(markStyle.backgroundColor)) {
      accent = markStyle.backgroundColor;
    } else if (linkStyle.color && linkStyle.color !== hostStyle.color) {
      accent = linkStyle.color;
    } else {
      accent = hostStyle.color;
    }

    builder.style.setProperty('--mpab-accent', accent);
    builder.style.setProperty('--mpab-accent-contrast', contrastText(accent));
    builder.style.setProperty('--mpab-line', withAlpha(textColour, 0.22, 'rgba(127, 127, 127, 0.28)'));
    builder.style.setProperty('--mpab-muted', withAlpha(textColour, 0.68, textColour));
    builder.style.setProperty('--mpab-tint', withAlpha(accent, 0.07, 'transparent'));
    builder.style.setProperty('--mpab-selected-bg', withAlpha(accent, 0.13, 'rgba(127, 127, 127, 0.12)'));
    builder.style.setProperty('--mpab-selected-shadow', withAlpha(accent, 0.28, 'rgba(20, 20, 18, 0.2)'));

    probe.remove();
  }

  function initBuilder(builder) {
		enhanceOptionThumbnails(builder);
    applyHostTheme(builder);
		var updateResponsiveChoiceCounts = setResponsiveChoiceCounts(builder);
    var form = builder.querySelector('.mpab-form');
    var steps = Array.prototype.slice.call(builder.querySelectorAll('.mpab-step'));
    var backButton = builder.querySelector('.mpab-button--back');
    var nextButton = builder.querySelector('.mpab-button--next');
    var submitButton = builder.querySelector('.mpab-button--submit');
		var showDetailsButton = builder.querySelector('.mpab-button--show-details');
		var contactPanel = builder.querySelector('[data-mpab-contact]');
		var detailsRevealed = false;
		var addBasketButton = builder.querySelector('.mpab-button--add-basket');
		var quantityInput = form.elements.quantity;
		var basketSummary = builder.querySelector('[data-mpab-basket-summary]');
		var basket = window.WBStudioBasket;
		var idempotencyKey = '';
		var editingItemId = '';
    var consentCheckbox = form.querySelector('input[name="consent"]');
    var errorBox = builder.querySelector('.mpab-error');
    var progressText = builder.querySelector('.mpab-builder__progress-text');
    var progressFill = builder.querySelector('.mpab-builder__progress-track span');
    var summary = builder.querySelector('.mpab-summary');
		var albumSoFar = builder.querySelector('[data-mpab-album-so-far]');
		var progressShape = builder.querySelector('[data-mpab-progress-shape]');
		var progressAlbum = builder.querySelector('[data-mpab-progress-album]');
		var progressMaterial = builder.querySelector('[data-mpab-progress-material]');
		var progressColour = builder.querySelector('[data-mpab-progress-colour]');
		var progressSwatch = builder.querySelector('[data-mpab-progress-swatch]');
		var progressSpreads = builder.querySelector('[data-mpab-progress-spreads]');
		var progressPersonalisation = builder.querySelector('[data-mpab-progress-personalisation]');
		var reviewShape = builder.querySelector('[data-mpab-review-shape]');
		var reviewAlbum = builder.querySelector('[data-mpab-review-album]');
		var reviewCover = builder.querySelector('[data-mpab-review-cover]');
    var finalTotal = builder.querySelector('[data-mpab-final-total]');
		var normalTotalOutput = builder.querySelector('[data-mpab-normal-total]');
		var promotionSavingOutput = builder.querySelector('[data-mpab-promotion-saving]');
    var success = builder.querySelector('.mpab-success');
    var startedAt = form.querySelector('input[name="started_at"]');
    var currency = builder.getAttribute('data-currency') || '€';
		var promotionActive = builder.getAttribute('data-promotion-active') === '1';
		var promotionPercent = promotionActive ? Number(builder.getAttribute('data-promotion-percent') || 0) : 0;
		var promotionEndDate = builder.getAttribute('data-promotion-end-date') || '';
		var promotionEndLabel = builder.getAttribute('data-promotion-end-label') || '';
		var siteDate = builder.getAttribute('data-site-date') || '';
		var promotionBanner = builder.querySelector('.mpab-promotion');

		function syncSubmitAvailability() {
			if (submitButton) {
				submitButton.disabled = !(consentCheckbox && consentCheckbox.checked);
			}
		}
		function orderQuantity() {
			var value = Math.max(1, Math.min(20, Number(quantityInput ? quantityInput.value : 1) || 1));
			if (quantityInput) quantityInput.value = String(value);
			return value;
		}

		function updateAddBasketLabel() {
			if (addBasketButton) addBasketButton.textContent = editingItemId ? message('updateItem', 'Update item') : message('addBasket', 'Add to basket and order more');
		}

		function openAlbumPanel() {
			var studio = builder.closest('[data-wb-studio]');
			var panel = builder.closest('[data-wb-panel="albums"]');
			if (studio && panel && typeof studio.wbOpenPanel === 'function') studio.wbOpenPanel(panel);
		}
		if (promotionActive && promotionEndDate && siteDate) {
			if (promotionEndDate < siteDate) {
				promotionActive = false;
				promotionPercent = 0;
				promotionEndDate = '';
				promotionEndLabel = '';
				if (promotionBanner) {
					promotionBanner.hidden = true;
				}
				if (normalTotalOutput) {
					normalTotalOutput.hidden = true;
				}
				if (promotionSavingOutput) {
					promotionSavingOutput.hidden = true;
				}
			}
		}
    var startedAtMs = Date.now();
    var current = 0;
		var optionScrollPosition = null;
		var conditionalUiReady = false;
		var reduceMotion = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;

    startedAt.value = String(startedAtMs);

		function rememberOptionScrollPosition(event) {
			var option = event.target.closest ? event.target.closest('.mpab-option') : null;
			if (!option || !form.contains(option)) {
				return;
			}
			optionScrollPosition = {
				left: window.pageXOffset,
				top: window.pageYOffset
			};
		}

		function restoreOptionScrollPosition() {
			var position = optionScrollPosition;
			var restore;
			optionScrollPosition = null;
			if (!position) {
				return;
			}
			restore = function () {
				if (Math.abs(window.pageXOffset - position.left) > 1 || Math.abs(window.pageYOffset - position.top) > 1) {
					window.scrollTo(position.left, position.top);
				}
			};
			restore();
			window.requestAnimationFrame(restore);
		}

		function clearBranchMotion(branch) {
			branch.style.removeProperty('height');
			branch.style.removeProperty('margin-top');
			branch.style.removeProperty('opacity');
			branch.style.removeProperty('overflow');
			branch.style.removeProperty('transform');
		}

		function stopBranchMotion(branch) {
			if (branch._mpabMotion) {
				branch._mpabMotion.onfinish = null;
				branch._mpabMotion.cancel();
				branch._mpabMotion = null;
			}
			clearBranchMotion(branch);
		}

		function setBranchVisibility(branch, active, animate) {
			var motion;
			var currentlyVisible = !branch.hidden;

			if (active === currentlyVisible && !branch._mpabMotion) {
				return;
			}
			stopBranchMotion(branch);
			if (!animate || reduceMotion || typeof branch.animate !== 'function') {
				branch.hidden = !active;
				return;
			}

			if (active) {
				branch.hidden = false;
			}
			motion = branch.animate(
				active
					? [
						{ opacity: 0, transform: 'translateY(-8px)' },
						{ opacity: 1, transform: 'translateY(0)' }
					]
					: [
						{ opacity: 1, transform: 'translateY(0)' },
						{ opacity: 0, transform: 'translateY(-6px)' }
					],
				{ duration: active ? 280 : 220, easing: 'cubic-bezier(0.2, 0.72, 0.2, 1)', fill: 'both' }
			);
			branch._mpabMotion = motion;
			motion.onfinish = function () {
				if (!active) {
					branch.hidden = true;
				}
				branch._mpabMotion = null;
				clearBranchMotion(branch);
			};
		}

		function syncConditionalUi() {
			var branches = Array.prototype.slice.call(builder.querySelectorAll('[data-mpab-branch]'));
			branches.forEach(function (branch) {
				var controller = selected(form, branch.getAttribute('data-mpab-branch'));
				var active = controller && controller.value === branch.getAttribute('data-mpab-value');
				var fields = Array.prototype.slice.call(branch.querySelectorAll('input, select, textarea'));
				setBranchVisibility(branch, Boolean(active), conditionalUiReady);
				fields.forEach(function (field) {
					field.disabled = !active;
					if (!active && (field.type === 'radio' || field.type === 'checkbox')) {
						field.checked = false;
					}
				});
			});
			branches.filter(function (branch) {
				var controller = selected(form, branch.getAttribute('data-mpab-branch'));
				return !controller || controller.value !== branch.getAttribute('data-mpab-value');
			}).forEach(function (branch) {
				Array.prototype.forEach.call(branch.querySelectorAll('input, select, textarea'), function (field) {
					field.disabled = true;
				});
			});

			steps.forEach(function (step) {
				var condition = step.getAttribute('data-mpab-condition');
				var controller;
				var active;
				var fields;
				if (!condition) {
					return;
				}
				controller = selected(form, condition);
				active = controller && controller.value === step.getAttribute('data-mpab-value');
				fields = Array.prototype.slice.call(step.querySelectorAll('input, select, textarea'));
				fields.forEach(function (field) {
					field.disabled = !active;
					if (!active && (field.type === 'radio' || field.type === 'checkbox')) {
						field.checked = false;
					}
				});
			});
			conditionalUiReady = true;
		}

		function activeSteps() {
			return steps.filter(function (step) {
				var condition = step.getAttribute('data-mpab-condition');
				var controller;
				if (!condition) {
					return true;
				}
				controller = selected(form, condition);
				return controller && controller.value === step.getAttribute('data-mpab-value');
			});
		}

    function hideError() {
      errorBox.hidden = true;
      errorBox.textContent = '';
    }

    function showError(message) {
      errorBox.textContent = message;
      errorBox.hidden = false;
      errorBox.focus();
    }

		function albumRatio(label) {
			var match = String(label || '').match(/(\d+)\s*[x×]\s*(\d+)\s*cm/i);
			if (!match) {
				return 1;
			}
			return Math.max(0.65, Math.min(1.55, Number(match[1]) / Number(match[2])));
		}

		function applyAlbumShape(target, size, cover) {
			if (!target) {
				return;
			}
			target.style.aspectRatio = String(albumRatio(size ? size.label : ''));
			target.style.backgroundImage = cover && cover.image ? 'url("' + cover.image.replace(/"/g, '%22') + '")' : '';
			target.classList.toggle('has-cover-image', !!(cover && cover.image));
		}

		function updateAlbumProgress() {
			var albumType = optionData(selected(form, 'album_type'));
			var size = optionData(selected(form, 'size'));
			var coverMaterial = optionData(selected(form, 'cover_material'));
			var cover = optionData(selected(form, 'cover'));
			var personalisation = optionData(selected(form, 'personalisation'));
			var personalisationCategory = optionData(selected(form, 'personalisation_category'));
			var spreads = Number(form.elements.spreads ? form.elements.spreads.value : 0);
			var personalisationLabel = personalisation ? personalisation.label : message('notChosen', 'Not chosen yet');
			if (personalisation && personalisation.id !== 'none' && personalisationCategory) {
				personalisationLabel += ' · ' + personalisationCategory.label;
			}

			if (progressAlbum) {
				progressAlbum.textContent = albumType ? albumType.label + (size ? ' · ' + size.label : '') : message('chooseAlbum', 'Choose an album');
			}
			if (progressMaterial) { progressMaterial.textContent = coverMaterial ? coverMaterial.label : message('notChosen', 'Not chosen yet'); }
			if (progressColour) { progressColour.textContent = cover ? cover.label : message('notChosen', 'Not chosen yet'); }
			if (progressSpreads) { progressSpreads.textContent = spreads ? message('spreadsWithPages', '%1$d (%2$d pages)', [spreads, spreads * 2]) : message('notChosen', 'Not chosen yet'); }
			if (progressPersonalisation) { progressPersonalisation.textContent = personalisationLabel; }
			if (progressSwatch) {
				progressSwatch.hidden = !(cover && cover.image);
				progressSwatch.style.backgroundImage = cover && cover.image ? 'url("' + cover.image.replace(/"/g, '%22') + '")' : '';
			}
			applyAlbumShape(progressShape, size, cover);
			applyAlbumShape(reviewShape, size, cover);
			if (reviewAlbum) { reviewAlbum.textContent = albumType ? albumType.label + (size ? ' · ' + size.label : '') : message('yourAlbum', 'Your album'); }
			if (reviewCover) { reviewCover.textContent = coverMaterial && cover ? coverMaterial.label + ' · ' + cover.label : message('yourChosenCover', 'Your chosen cover'); }
		}

    function updateSummary() {
			var albumType = optionData(selected(form, 'album_type'));
      var size = optionData(selected(form, 'size'));
			var spreads = Number(form.elements.spreads ? form.elements.spreads.value : 0);
			var pages = spreads * 2;
			var extraSpreads = Math.max(0, spreads - 10);
			var extraSpreadTotal = size ? extraSpreads * size.spreadPrice : 0;
			var coverMaterial = optionData(selected(form, 'cover_material'));
      var cover = optionData(selected(form, 'cover'));
			var personalisation = optionData(selected(form, 'personalisation'));
			var personalisationCategory = optionData(selected(form, 'personalisation_category'));
			var personalisationFont = optionData(selected(form, 'personalisation_font'));
			var personalisationDesign = optionData(selected(form, 'personalisation_design'));
			var personalLines = personalTextLines(form);
			var personalText = personalLines.join(' · ');
			var foilColour = optionData(selected(form, 'foil_colour'));
      var box = optionData(selected(form, 'box'));
			var boxColour = optionData(selected(form, 'box_colour'));
			var boxTotal = box ? box.price : 0;
			var quantity = orderQuantity();
			var normalTotal = roundMoney(((size ? size.price : 0) + extraSpreadTotal + boxTotal) * quantity);
			var promotionDiscount = promotionActive ? roundMoney(normalTotal * (promotionPercent / 100)) : 0;
			var total = roundMoney(normalTotal - promotionDiscount);
      var rows = [
				[message('albumType', 'Album type'), albumType ? albumType.label : message('notSelected', 'Not selected'), 0],
				[message('size', 'Size'), size ? size.label : message('notSelected', 'Not selected'), 0],
				[message('spreads', 'Spreads'), spreads ? message('spreadsWithPages', '%1$d (%2$d pages)', [spreads, pages]) : message('notSelected', 'Not selected'), 0],
				[message('coverMaterial', 'Cover material'), coverMaterial ? coverMaterial.label : message('notSelected', 'Not selected'), 1],
        [message('cover', 'Cover'), cover ? cover.label : message('notSelected', 'Not selected'), 1],
				[message('personalisation', 'Personalisation'), personalisation ? personalisation.label : message('notSelected', 'Not selected'), 2]
      ];

			if (personalisation && personalisation.id !== 'none') {
				rows.push([message('personalisationStyle', 'Personalisation style'), personalisationCategory ? personalisationCategory.label : message('notSelected', 'Not selected'), 2]);
				if (personalisationCategory && personalisationCategory.id === 'text') {
					rows.push([message('wording', 'Wording'), personalText || message('notEntered', 'Not entered'), 2]);
					rows.push([message('font', 'Font'), personalisationFont ? personalisationFont.label : message('notSelected', 'Not selected'), 2]);
				}
				if (personalisationCategory && (personalisationCategory.id === 'wb-design' || personalisationCategory.id === 'full-cover')) {
					rows.push([message('design', 'Design'), personalisationDesign ? personalisationDesign.label : message('notSelected', 'Not selected'), 2]);
				}
				if (personalisation.id === 'foil') {
					rows.push([message('foilColour', 'Foil colour'), foilColour ? foilColour.label : message('notSelected', 'Not selected'), 2]);
				}
			}
			if (form.elements.box) {
				rows.push([message('presentation', 'Presentation'), box ? box.label : message('notSelected', 'Not selected'), 3]);
			}
			if (box && box.id === 'wooden') {
				rows.push([message('woodenBoxColour', 'Wooden box colour'), boxColour ? boxColour.label : message('notSelected', 'Not selected'), 3]);
			}
			rows.push([message('numberOfCopies', 'Number of copies'), String(quantity)]);

			var priceStart = rows.length;
			var promotionStart = -1;
			var albumSubtotal = ((size ? size.price : 0) + extraSpreadTotal) * quantity;
			rows.push([message('albumAndSpreads', 'Album · %d spreads', [spreads]), money(albumSubtotal, currency)]);
			if (boxTotal > 0) {
				rows.push([message('presentationBox', 'Presentation box'), money(boxTotal * quantity, currency)]);
			}
			if (promotionActive) {
				promotionStart = rows.length;
				rows.push([message('saving', '%d%% saving', [promotionPercent]), '−' + money(promotionDiscount, currency)]);
			}
			rows.push([message('total', 'Total'), money(total, currency)]);
			if (finalTotal) {
				finalTotal.textContent = money(total, currency);
			}
			if (normalTotalOutput) {
				normalTotalOutput.textContent = money(normalTotal, currency);
			}
			if (promotionSavingOutput) {
				promotionSavingOutput.textContent = message('youSave', 'You save') + ' ' + money(promotionDiscount, currency) + '.' + (promotionEndLabel ? ' ' + message('offerEnds', 'Offer ends') + ' ' + promotionEndLabel + '.' : '');
			}
			if (basket && basketSummary) {
				basket.render(basketSummary, {
					label: albumType ? albumType.label : message('yourAlbum', 'Your album'),
					detail: size ? size.label + (coverMaterial ? ' · ' + coverMaterial.label : '') : '',
					quantity: quantity,
					total: total,
					currency: currency
				});
			}

      summary.innerHTML = '';
      rows.forEach(function (row, index) {
        if (index === priceStart) {
          var sectionTitle = document.createElement('p');
          sectionTitle.className = 'mpab-summary__section';
					sectionTitle.textContent = message('priceBreakdown', 'Price breakdown');
          summary.appendChild(sectionTitle);
        }
        var item = document.createElement('div');
        var term = document.createElement('span');
        var description = document.createElement('strong');
				item.className = 'mpab-summary__row';
				if (index >= priceStart) {
					item.className += ' mpab-summary__price';
				}
				if (promotionStart >= 0 && index >= promotionStart) {
					item.className += ' mpab-summary__promotion';
				}
        term.textContent = row[0];
        description.textContent = row[1];
        item.appendChild(term);
        item.appendChild(description);
				if (typeof row[2] === 'number') {
					var edit = document.createElement('button');
					edit.type = 'button';
					edit.className = 'mpab-summary__edit';
					edit.setAttribute('data-mpab-edit-step', String(row[2]));
					edit.textContent = message('edit', 'Edit');
					item.appendChild(edit);
				}
        summary.appendChild(item);
      });
			updateAlbumProgress();
    }

    function updateStep() {
			syncConditionalUi();
			var availableSteps = activeSteps();
			current = Math.max(0, Math.min(current, availableSteps.length - 1));
      steps.forEach(function (step, index) {
				step.hidden = true;
      });
			availableSteps[current].hidden = false;
			updateAlbumProgress();
			if (promotionBanner) {
				promotionBanner.hidden = current !== 0 || !promotionActive;
			}
			if (albumSoFar) {
				albumSoFar.hidden = current === 0 || current === availableSteps.length - 1 || !selected(form, 'album_type');
			}
			var onReviewStep = current === availableSteps.length - 1;
			if (!onReviewStep) {
				detailsRevealed = false;
			}
			progressText.textContent = message('stepProgress', 'Step %1$d of %2$d', [current + 1, availableSteps.length]);
			progressFill.style.width = (((current + 1) / availableSteps.length) * 100) + '%';
      backButton.hidden = current === 0;
			nextButton.hidden = onReviewStep;
			if (contactPanel) contactPanel.hidden = !onReviewStep || !detailsRevealed;
			if (showDetailsButton) showDetailsButton.hidden = !onReviewStep || detailsRevealed;
			submitButton.hidden = !onReviewStep || !detailsRevealed;
			if (addBasketButton) addBasketButton.hidden = !onReviewStep;
      hideError();
			if (current === availableSteps.length - 1) {
        updateSummary();
      }
			window.requestAnimationFrame(function () {
				updateResponsiveChoiceCounts();
				wakeVisibleOptionImages(builder);
			});
    }

		if (showDetailsButton) {
			showDetailsButton.addEventListener('click', function () {
				detailsRevealed = true;
				if (contactPanel) contactPanel.hidden = false;
				showDetailsButton.hidden = true;
				submitButton.hidden = false;
				syncSubmitAvailability();
				if (contactPanel) {
					contactPanel.scrollIntoView({ behavior: reduceMotion ? 'auto' : 'smooth', block: 'start' });
					var firstField = contactPanel.querySelector('input:not([type="hidden"])');
					if (firstField) firstField.focus({ preventScroll: true });
				}
			});
		}

		form.addEventListener('click', function (event) {
			var edit = event.target.closest ? event.target.closest('[data-mpab-edit-step]') : null;
			var targetStep;
			var available;
			if (!edit) {
				return;
			}
			targetStep = edit.getAttribute('data-mpab-edit-step');
			available = activeSteps();
			current = available.findIndex(function (step) { return step.getAttribute('data-step') === targetStep; });
			current = Math.max(0, current);
			updateStep();
			builder.scrollIntoView({ behavior: reduceMotion ? 'auto' : 'smooth', block: 'start' });
		});

    function validateCurrentStep() {
			var step = activeSteps()[current];
      var required = Array.prototype.slice.call(step.querySelectorAll('[required]'));
      var checkedGroups = {};

      for (var i = 0; i < required.length; i += 1) {
        var field = required[i];
				if (field.disabled) {
					continue;
				}
        if (field.type === 'radio') {
          if (checkedGroups[field.name]) {
            continue;
          }
          checkedGroups[field.name] = true;
          if (!selected(form, field.name)) {
            showError(message('chooseBeforeContinuing', 'Choose one option before continuing.'));
            return false;
          }
        } else if (field.type === 'checkbox') {
          if (!field.checked) {
            showError(message('confirmBeforeSending', 'Please confirm that I may send these album choices.'));
            field.focus();
            return false;
          }
        } else if (!field.checkValidity()) {
					showError(field.name === 'spreads' ? message('chooseSpreads', 'Choose the number of spreads.') : (field.type === 'email' ? message('validEmail', 'Add a valid email address.') : message('completeDetails', 'Complete the required details.')));
          field.focus();
          return false;
        }
      }
      return true;
    }

    function payload() {
			if (!idempotencyKey) {
				var bytes = new Uint8Array(16);
				if (window.crypto && window.crypto.getRandomValues) {
					window.crypto.getRandomValues(bytes);
				} else {
					for (var byteIndex = 0; byteIndex < bytes.length; byteIndex += 1) bytes[byteIndex] = Math.floor(Math.random() * 256);
				}
				idempotencyKey = Array.prototype.map.call(bytes, function (value) { return value.toString(16).padStart(2, '0'); }).join('');
			}
			var albumType = optionData(selected(form, 'album_type'));
      var size = optionData(selected(form, 'size'));
			var coverMaterial = optionData(selected(form, 'cover_material'));
      var cover = optionData(selected(form, 'cover'));
			var personalisation = optionData(selected(form, 'personalisation'));
			var personalisationCategory = optionData(selected(form, 'personalisation_category'));
			var personalisationFont = optionData(selected(form, 'personalisation_font'));
			var personalisationDesign = optionData(selected(form, 'personalisation_design'));
			var personalLines = personalTextLines(form);
			var foilColour = optionData(selected(form, 'foil_colour'));
      var box = optionData(selected(form, 'box'));
			var boxColour = optionData(selected(form, 'box_colour'));
      return {
				 token: window.MPAB_DATA ? window.MPAB_DATA.token : '',
				locale: window.MPAB_DATA ? window.MPAB_DATA.locale : '',
				idempotency_key: idempotencyKey,
				album_type: albumType ? albumType.id : '',
        size: size ? size.id : '',
				spreads: Number(form.elements.spreads ? form.elements.spreads.value : 0),
				cover_material: coverMaterial ? coverMaterial.id : '',
        cover: cover ? cover.id : '',
				personalisation: personalisation ? personalisation.id : '',
				personalisation_category: personalisationCategory ? personalisationCategory.id : '',
				personalisation_font: personalisationFont ? personalisationFont.id : '',
				personalisation_design: personalisationDesign ? personalisationDesign.id : '',
				personal_text_lines: personalLines,
				personal_text: personalLines.join('\n'),
				foil_colour: foilColour ? foilColour.id : '',
        box: box ? box.id : '',
				box_colour: boxColour ? boxColour.id : '',
				promotion_percent: promotionActive ? promotionPercent : 0,
				promotion_end_date: promotionActive ? promotionEndDate : '',
				quantity: orderQuantity(),
        client_name: form.elements.client_name.value,
        client_email: form.elements.client_email.value,
				client_note: form.elements.client_note ? form.elements.client_note.value : '',
        consent: form.elements.consent.checked ? 1 : 0,
        website: form.elements.website.value,
        elapsed_ms: Date.now() - startedAtMs
      };
    }

		function albumEntry() {
			var orderPayload = payload();
			var albumType = optionData(selected(form, 'album_type'));
			var size = optionData(selected(form, 'size'));
			var coverMaterial = optionData(selected(form, 'cover_material'));
			var cover = optionData(selected(form, 'cover'));
			var spreads = Number(form.elements.spreads ? form.elements.spreads.value : 0);
			var box = optionData(selected(form, 'box'));
			var quantity = orderQuantity();
			var unitNormal = (size ? size.price : 0) + Math.max(0, spreads - 10) * (size ? size.spreadPrice : 0) + (box ? box.price : 0);
			var normal = roundMoney(unitNormal * quantity);
			var total = roundMoney(normal - (promotionActive ? roundMoney(normal * promotionPercent / 100) : 0));
			return {
				kind: 'album', label: albumType ? albumType.label : message('yourAlbum', 'Your album'),
				detail: size ? size.label + (coverMaterial ? ' · ' + coverMaterial.label : '') : '',
				quantity: quantity, total: total, currency: currency,
				image: cover && cover.image ? cover.image : '',
				edit: function (id) { restoreAlbumEntry(id, orderPayload); },
				submit: function (details) {
					orderPayload.client_name = details.name;
					orderPayload.client_email = details.email;
					orderPayload.consent = 1;
					orderPayload.elapsed_ms = Math.max(2001, Date.now() - startedAtMs);
					if (window.MPAB_DATA && window.MPAB_DATA.demo) {
						return Promise.resolve({ reference: '10', message: message('demoSaved', 'Your album choices have been saved.'), clientEmail: true, ownerEmail: true });
					}
					return fetch(window.MPAB_DATA.endpoint, { method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(orderPayload) })
						.then(function (response) { return response.json().then(function (data) { if (!response.ok) throw new Error(data.message || message('requestFailed', 'The request could not be sent.')); return data; }); });
				}
			};
		}

		function chooseField(name, value) {
			var fields = form.elements[name];
			var matched = null;
			if (!fields) return null;
			if (typeof fields.length === 'number' && !fields.tagName) {
				Array.prototype.forEach.call(fields, function (field) {
					field.checked = String(field.value) === String(value || '');
					if (field.checked) matched = field;
				});
			} else if (fields.type === 'radio' || fields.type === 'checkbox') {
				fields.checked = String(fields.value) === String(value || '');
				if (fields.checked) matched = fields;
			} else {
				fields.value = value == null ? '' : String(value);
				matched = fields;
			}
			if (matched) matched.dispatchEvent(new Event('change', { bubbles: true }));
			return matched;
		}

		function restoreAlbumEntry(id, snapshot) {
			var lines;
			form.reset();
			editingItemId = id;
			idempotencyKey = snapshot.idempotency_key || '';
			chooseField('album_type', snapshot.album_type);
			chooseField('size', snapshot.size);
			chooseField('spreads', snapshot.spreads);
			chooseField('cover_material', snapshot.cover_material);
			chooseField('cover', snapshot.cover);
			chooseField('personalisation', snapshot.personalisation);
			chooseField('personalisation_category', snapshot.personalisation_category);
			lines = form.querySelectorAll('input[name="personal_text_lines[]"]');
			Array.prototype.forEach.call(lines, function (field, index) { field.value = snapshot.personal_text_lines && snapshot.personal_text_lines[index] ? snapshot.personal_text_lines[index] : ''; });
			chooseField('personalisation_font', snapshot.personalisation_font);
			chooseField('personalisation_design', snapshot.personalisation_design);
			chooseField('foil_colour', snapshot.foil_colour);
			chooseField('box', snapshot.box);
			chooseField('box_colour', snapshot.box_colour);
			chooseField('quantity', snapshot.quantity || 1);
			current = Math.max(0, activeSteps().length - 1);
			success.hidden = true;
			form.hidden = false;
			openAlbumPanel();
			updateStep();
			updateAddBasketLabel();
			builder.scrollIntoView({ behavior: 'smooth', block: 'start' });
		}

		function returnToStudioStart() {
			form.reset(); editingItemId = ''; idempotencyKey = ''; startedAtMs = Date.now(); current = 0; success.hidden = true; form.hidden = false;
			updateStep();
			updateAddBasketLabel();
			var studio = builder.closest('[data-wb-studio]');
			if (studio && typeof studio.wbShowStart === 'function') studio.wbShowStart();
		}

		if (quantityInput) quantityInput.addEventListener('change', updateSummary);
		if (addBasketButton) addBasketButton.addEventListener('click', function () {
			if (!basket) return;
			addBasketButton.disabled = true;
			if (editingItemId) basket.replace(editingItemId, albumEntry()); else basket.add(albumEntry());
			returnToStudioStart();
			addBasketButton.disabled = false;
		});

		var albumPanel = builder.closest('[data-wb-panel]');
		if (albumPanel) albumPanel.addEventListener('wb:start-new-item', function () {
			if (!editingItemId) return;
			form.reset(); editingItemId = ''; idempotencyKey = ''; current = 0; updateStep(); updateAddBasketLabel();
		});
		updateAddBasketLabel();

    nextButton.addEventListener('click', function () {
      if (!validateCurrentStep()) {
        return;
      }
      current += 1;
      updateStep();
      builder.scrollIntoView({ behavior: 'smooth', block: 'start' });
    });

    backButton.addEventListener('click', function () {
      current = Math.max(0, current - 1);
      updateStep();
      builder.scrollIntoView({ behavior: 'smooth', block: 'start' });
    });

		form.addEventListener('pointerdown', rememberOptionScrollPosition, true);
		form.addEventListener('mousedown', rememberOptionScrollPosition, true);
		form.addEventListener('touchstart', rememberOptionScrollPosition, { capture: true, passive: true });
		form.addEventListener('keydown', function (event) {
			if (event.key === ' ' || event.key === 'Enter' || event.key.indexOf('Arrow') === 0) {
				rememberOptionScrollPosition(event);
			}
		}, true);
		form.addEventListener('click', function (event) {
			var option = event.target.closest ? event.target.closest('.mpab-option') : null;
			var input;
			var changed;
			if (!option || !form.contains(option)) {
				return;
			}
			input = option.querySelector('input[type="radio"]');
			if (!input || input.disabled) {
				return;
			}
			// Let native input and keyboard activation finish normally. The delegated
			// label handler below is only needed when the visible card itself is clicked.
			if (event.target === input) {
				return;
			}
			if (!optionScrollPosition) {
				rememberOptionScrollPosition(event);
			}
			event.preventDefault();
			changed = !input.checked;
			input.checked = true;
			if (input.focus) {
				try {
					input.focus({ preventScroll: true });
				} catch (error) {
					input.focus();
				}
			}
			if (changed) {
				input.dispatchEvent(new Event('change', { bubbles: true }));
			} else {
				restoreOptionScrollPosition();
			}
		});

    form.addEventListener('change', function (event) {
			if (event.target === consentCheckbox) {
				syncSubmitAvailability();
			}
			updateStep();
			if (event.target.matches('.mpab-option input')) {
				restoreOptionScrollPosition();
			}
    });

    form.addEventListener('submit', function (event) {
      event.preventDefault();
      if (!validateCurrentStep()) {
        return;
      }

      submitButton.disabled = true;
			submitButton.textContent = message('sending', 'Confirming your order…');
      hideError();

			var details = { name: form.elements.client_name.value, email: form.elements.client_email.value };
			var checkout = basket ? basket.checkout(albumEntry(), details) : albumEntry().submit(details).then(function (data) { return [data.reference]; });
			checkout.then(function (references) {
          form.hidden = true;
          builder.querySelector('.mpab-builder__progress').hidden = true;
          success.innerHTML = '';
						var icon = document.createElement('span');
          var eyebrow = document.createElement('span');
          var heading = document.createElement('h2');
						var reference = document.createElement('p');
						var emailNote = document.createElement('p');
						var next = document.createElement('p');
						var followUp = document.createElement('p');
						icon.className = 'mpab-success__icon';
						icon.setAttribute('aria-hidden', 'true');
						icon.textContent = '✓';
          eyebrow.className = 'mpab-success__eyebrow';
						eyebrow.textContent = message('submitted', 'Album choices submitted');
						heading.textContent = message('thankYou', 'Thank you. Your album choices are safely saved.');
						reference.className = 'mpab-success__reference';
						reference.textContent = references.length > 1
							? message('orderNumbers', 'Order numbers: %s', [references.join(', ')])
							: message('orderNumber', 'Order number: %s', [references[0] || '—']);
						emailNote.className = 'mpab-success__email';
						emailNote.textContent = message('confirmationSent', 'A confirmation has also been sent to your email address.');
						next.className = 'mpab-success__next';
						next.textContent = window.MPAB_DATA && window.MPAB_DATA.successContact
							? window.MPAB_DATA.successContact
							: 'I will be in touch to confirm production, delivery and payment.';
						followUp.className = 'mpab-success__follow-up';
						followUp.textContent = window.MPAB_DATA && window.MPAB_DATA.successFollowUp
							? window.MPAB_DATA.successFollowUp
							: 'If I have not been in touch within 48 hours, please email me.';
						success.appendChild(icon);
          success.appendChild(eyebrow);
          success.appendChild(heading);
						success.appendChild(reference);
						success.appendChild(emailNote);
						success.appendChild(next);
						success.appendChild(followUp);
          success.hidden = false;
          success.focus();
						builder.scrollIntoView({ behavior: 'smooth', block: 'start' });
        })
				.catch(function (error) {
          showError(error.message || message('tryAgain', 'Please refresh the page and try again.'));
				syncSubmitAvailability();
				submitButton.textContent = message('sendChoices', 'Send my album choices');
        });
    });

		function refreshHostTheme() {
			if (builder.isConnected) {
				applyHostTheme(builder);
			}
		}

		if (document.fonts && document.fonts.ready) {
			document.fonts.ready.then(refreshHostTheme);
		}
		window.addEventListener('load', refreshHostTheme, { once: true });
		window.setTimeout(refreshHostTheme, 800);

		syncSubmitAvailability();
    updateStep();
  }

  function boot() {
    Array.prototype.forEach.call(document.querySelectorAll('.mpab-builder'), initBuilder);
  }

  function bootWhenReady() {
    if (window.MPAB_CATALOGUE_READY && typeof window.MPAB_CATALOGUE_READY.then === 'function') {
      window.MPAB_CATALOGUE_READY.then(boot);
      return;
    }
    boot();
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', bootWhenReady);
  } else {
    bootWhenReady();
  }
}());
