Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse varius enim in eros elementum tristique. Duis cursus, mi quis viverra ornare, eros dolor interdum nulla, ut commodo diam libero vitae erat. Aenean faucibus nibh et justo cursus id rutrum lorem imperdiet. Nunc ut sem vitae risus tristique posuere.
<div id="configurator-container-017e2aca">
<style>
#configurator-container-017e2aca {
max-width: 800px;
margin: 0 auto;
box-sizing: border-box;
text-align: left;
}
.selection-group {
margin-bottom: 20px;
padding: 15px;
border: 1px solid #eee;
border-radius: 5px;
background-color: #fdfdfd;
}
.selection-group p {
font-weight: 600;
margin-top: 0;
margin-bottom: 10px;
color: #333;
}
.options-grid {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.option-button {
display: inline-block;
box-sizing: border-box;
font-family: 'Poppins', sans-serif;
font-size: 14px;
font-weight: 500;
line-height: 20px;
min-width: 95px;
text-align: center;
cursor: pointer;
margin-bottom: 10px;
margin-right: 10px;
border-radius: 0;
-webkit-font-smoothing: antialiased;
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
transition: background-color 0.15s ease,
border-color 0.15s ease,
color 0.15s ease,
outline-color 0.15s ease,
box-shadow 0.15s ease;
color: rgb(73, 74, 75);
background-color: white;
border: 1px solid rgb(201, 201, 201);
outline: 1px solid transparent;
padding: 10px 15px;
box-shadow: none;
}
.option-button:hover:not(.active):not(.disabled) {
border-color: #999;
}
.option-button.active {
color: rgb(0, 0, 0);
background-color: rgb(238, 238, 238);
border: 2px solid rgb(0,0,0);
outline: 2px solid white;
outline-offset: -1px;
box-shadow: 0 0 0 1px black;
padding: 10px 15px;
font-weight: 500;
}
.option-button.disabled {
color: #aaa;
background-color: #f8f8f8;
border: 1px solid #e0e0e0;
outline: 1px solid transparent;
opacity: 0.6;
cursor: not-allowed;
padding: 10px 15px;
box-shadow: none;
}
#configurator-container-017e2aca #product-info {
margin-top: 20px;
padding: 15px;
border-radius: 5px;
border: 1px solid #ddd;
}
#configurator-container-017e2aca #product-info p {
margin: 8px 0;
}
.price-container {
display: flex;
align-items: center;
font-size: 16px;
}
.price-label {
margin-right: 8px;
}
.currency-symbol {
font-weight: 500;
color: #ff5a5f;
}
.actual-price {
font-weight: 500;
color: #ff5a5f;
}
#configurator-container-017e2aca a#create-button {
display: block;
width: 100%;
padding: 12px;
background-color: #4b5566;
color: white !important;
border: none;
border-radius: 5px;
font-size: 16px;
font-family: 'Poppins', sans-serif;
font-weight: 500;
cursor: pointer;
margin-top: 15px;
transition: background-color 0.3s;
text-decoration: none;
text-align: center;
}
#configurator-container-017e2aca a#create-button.disabled {
background-color: #ccc;
cursor: not-allowed;
pointer-events: none;
}
#configurator-container-017e2aca a#create-button:not(.disabled):hover {
background-color: #3a4250;
}
</style>
<script>
(function() {
const productsData = [{"design_id":4063,"design_slug":"a4-landscape-20mm-unmounted-caption-design-7","product_id":1156,"product_slug":"a4-landscape-20mm-unmounted-caption","product_name":"Framed Caption Print - A4 (30x21cm) - 20mm frame","size":"A4","frame_width":"20mm","orientation":"Landscape","price":25.0,"editor_url":"https://preview.post-snap.com/app/create/framed-caption-prints/a4-landscape-20mm-unmounted-caption/a4-landscape-20mm-unmounted-caption-design-7","preview_url":"https://eclipse-engine.s3.amazonaws.com/y/uploads/design_asset-74DE-D61A-2012-2505-7656.jpg"},{"design_id":4046,"design_slug":"a5-landscape-13mm-unmounted-caption-design-7","product_id":1151,"product_slug":"a5-landscape-13mm-unmounted-caption","product_name":"Framed Caption Print - A5 (20x15cm) - 13mm frame","size":"A5","frame_width":"13mm","orientation":"Landscape","price":15.0,"editor_url":"https://preview.post-snap.com/app/create/framed-caption-prints/a5-landscape-13mm-unmounted-caption/a5-landscape-13mm-unmounted-caption-design-7","preview_url":"https://eclipse-engine.s3.amazonaws.com/j/uploads/design_asset-3854-A99E-8DEC-24BC-1D15.jpg"},{"design_id":4061,"design_slug":"a4-landscape-13mm-unmounted-caption-design-7","product_id":1155,"product_slug":"a4-landscape-13mm-unmounted-caption","product_name":"Framed Caption Print - A4 (30x21cm) - 13mm frame","size":"A4","frame_width":"13mm","orientation":"Landscape","price":20.0,"editor_url":"https://preview.post-snap.com/app/create/framed-caption-prints/a4-landscape-13mm-unmounted-caption/a4-landscape-13mm-unmounted-caption-design-7","preview_url":"https://eclipse-engine.s3.amazonaws.com/y/uploads/design_asset-74DE-D61A-2012-2505-7656.jpg"},{"design_id":4043,"design_slug":"a4-portrait-20mm-unmounted-caption-design-7","product_id":1158,"product_slug":"a4-portrait-20mm-unmounted-caption","product_name":"Framed Caption Print - A4 (21x30cm) - 20mm frame","size":"A4","frame_width":"20mm","orientation":"Portrait","price":25.0,"editor_url":"https://preview.post-snap.com/app/create/framed-caption-prints/a4-portrait-20mm-unmounted-caption/a4-portrait-20mm-unmounted-caption-design-7","preview_url":"https://eclipse-engine.s3.amazonaws.com/t/uploads/design_asset-9F39-9CCB-2011-BA9A-039D.jpg"},{"design_id":4047,"design_slug":"a5-landscape-20mm-unmounted-caption-design-7","product_id":1152,"product_slug":"a5-landscape-20mm-unmounted-caption","product_name":"Framed Caption Print - A5 (20x15cm) - 20mm frame","size":"A5","frame_width":"20mm","orientation":"Landscape","price":18.0,"editor_url":"https://preview.post-snap.com/app/create/framed-caption-prints/a5-landscape-20mm-unmounted-caption/a5-landscape-20mm-unmounted-caption-design-7","preview_url":"https://eclipse-engine.s3.amazonaws.com/j/uploads/design_asset-3854-A99E-8DEC-24BC-1D15.jpg"},{"design_id":4039,"design_slug":"a3-portrait-13mm-unmounted-caption-design-7","product_id":1161,"product_slug":"a3-portrait-13mm-unmounted-caption","product_name":"Framed Caption Print - A3 (30x42cm) - 13mm frame","size":"A3","frame_width":"13mm","orientation":"Portrait","price":30.0,"editor_url":"https://preview.post-snap.com/app/create/framed-caption-prints/a3-portrait-13mm-unmounted-caption/a3-portrait-13mm-unmounted-caption-design-7","preview_url":"https://eclipse-engine.s3.amazonaws.com/x/uploads/design_asset-BB70-B56E-66D2-37A0-3A02.jpg"},{"design_id":4038,"design_slug":"a3-portrait-20mm-unmounted-caption-design-7","product_id":1162,"product_slug":"a3-portrait-20mm-unmounted-caption","product_name":"Framed Caption Print - A3 (30x42cm) - 20mm frame","size":"A3","frame_width":"20mm","orientation":"Portrait","price":35.0,"editor_url":"https://preview.post-snap.com/app/create/framed-caption-prints/a3-portrait-20mm-unmounted-caption/a3-portrait-20mm-unmounted-caption-design-7","preview_url":"https://eclipse-engine.s3.amazonaws.com/x/uploads/design_asset-BB70-B56E-66D2-37A0-3A02.jpg"},{"design_id":4050,"design_slug":"a3-landscape-20mm-unmounted-caption-design-7","product_id":1160,"product_slug":"a3-landscape-20mm-unmounted-caption","product_name":"Framed Caption Print - A3 (42x30cm) - 20mm frame","size":"A3","frame_width":"20mm","orientation":"Landscape","price":35.0,"editor_url":"https://preview.post-snap.com/app/create/framed-caption-prints/a3-landscape-20mm-unmounted-caption/a3-landscape-20mm-unmounted-caption-design-7","preview_url":"https://eclipse-engine.s3.amazonaws.com/y/uploads/design_asset-D1F5-6EE0-FFD9-BF86-580F.jpg"},{"design_id":4041,"design_slug":"a5-portrait-20mm-unmounted-caption-design-7","product_id":1154,"product_slug":"a5-portrait-20mm-unmounted-caption","product_name":"Framed Caption Print - A5 (15x20cm) - 20mm frame","size":"A5","frame_width":"20mm","orientation":"Portrait","price":18.0,"editor_url":"https://preview.post-snap.com/app/create/framed-caption-prints/a5-portrait-20mm-unmounted-caption/a5-portrait-20mm-unmounted-caption-design-7","preview_url":"https://eclipse-engine.s3.amazonaws.com/g/uploads/design_asset-F68D-3F2C-3642-6F48-7340.jpg"},{"design_id":4042,"design_slug":"a4-portrait-13mm-unmounted-caption-design-7","product_id":1157,"product_slug":"a4-portrait-13mm-unmounted-caption","product_name":"Framed Caption Print - A4 (21x30cm) - 13mm frame","size":"A4","frame_width":"13mm","orientation":"Portrait","price":20.0,"editor_url":"https://preview.post-snap.com/app/create/framed-caption-prints/a4-portrait-13mm-unmounted-caption/a4-portrait-13mm-unmounted-caption-design-7","preview_url":"https://eclipse-engine.s3.amazonaws.com/t/uploads/design_asset-9F39-9CCB-2011-BA9A-039D.jpg"},{"design_id":4044,"design_slug":"30-x-30-20mm-unmounted-caption-design-7","product_id":1164,"product_slug":"30-x-30-20mm-unmounted-caption","product_name":"Framed Caption Print - 30x30cm - 20mm frame","size":"30x30cm","frame_width":"20mm","orientation":"Square","price":27.0,"editor_url":"https://preview.post-snap.com/app/create/framed-caption-prints/30-x-30-20mm-unmounted-caption/30-x-30-20mm-unmounted-caption-design-7","preview_url":"https://eclipse-engine.s3.amazonaws.com/z/uploads/design_asset-6E20-86D6-E25F-678C-C3D0.jpg"},{"design_id":4052,"design_slug":"a3-landscape-13mm-unmounted-caption-design-7","product_id":1159,"product_slug":"a3-landscape-13mm-unmounted-caption","product_name":"Framed Caption Print - A3 (42x30cm) - 13mm frame","size":"A3","frame_width":"13mm","orientation":"Landscape","price":30.0,"editor_url":"https://preview.post-snap.com/app/create/framed-caption-prints/a3-landscape-13mm-unmounted-caption/a3-landscape-13mm-unmounted-caption-design-7","preview_url":"https://eclipse-engine.s3.amazonaws.com/y/uploads/design_asset-D1F5-6EE0-FFD9-BF86-580F.jpg"},{"design_id":4045,"design_slug":"30-x-30-13mm-unmounted-caption-design-7","product_id":1163,"product_slug":"30-x-30-13mm-unmounted-caption","product_name":"Framed Caption Print - 30x30cm - 13mm frame","size":"30x30cm","frame_width":"13mm","orientation":"Square","price":25.0,"editor_url":"https://preview.post-snap.com/app/create/framed-caption-prints/30-x-30-13mm-unmounted-caption/30-x-30-13mm-unmounted-caption-design-7","preview_url":"https://eclipse-engine.s3.amazonaws.com/z/uploads/design_asset-6E20-86D6-E25F-678C-C3D0.jpg"},{"design_id":4023,"design_slug":"a5-portrait-13mm-unmounted-caption-design-7","product_id":1153,"product_slug":"a5-portrait-13mm-unmounted-caption","product_name":"Framed Caption Print - A5 (15x20cm) - 13mm frame","size":"A5","frame_width":"13mm","orientation":"Portrait","price":15.0,"editor_url":"https://preview.post-snap.com/app/create/framed-caption-prints/a5-portrait-13mm-unmounted-caption/a5-portrait-13mm-unmounted-caption-design-7","preview_url":"https://eclipse-engine.s3.amazonaws.com/s/uploads/design_asset-E549-9A55-6289-64C6-4BDF.jpg"}];
const showOrientationSelector = true;
const sizesList = ["A5","A4","A3","30x30cm"];
const frameWidthsList = ["13mm","20mm"];
const updateMainImageEnabled = true;
const container = document.getElementById('configurator-container-017e2aca');
if (!container) return;
// Check URL parameters for pre-selection
const urlParams = new URLSearchParams(window.location.search);
const slugParam = urlParams.get('slug');
// Try to find product by slug
let preselectedProduct = null;
if (slugParam) {
preselectedProduct = productsData.find(p => p.product_slug === slugParam || p.design_slug === slugParam);
}
// Initialize state with smart defaults or from URL
let selectedOrientation = preselectedProduct ? preselectedProduct.orientation : 'Portrait';
// Filter products by orientation for initial setup
const productsForOrientation = (showOrientationSelector && selectedOrientation) ?
productsData.filter(p => p.orientation === selectedOrientation) :
productsData;
// Get available sizes for this orientation (ordered by sizesList)
const availableSizes = sizesList.filter(sz => productsForOrientation.some(p => p.size === sz));
// Default to first available size (or from URL preselection)
let selectedSize = preselectedProduct ? preselectedProduct.size : (availableSizes.length > 0 ? availableSizes[0] : null);
// Default to first available frame width for this size/orientation (or from URL preselection)
const availableFrameWidths = frameWidthsList.filter(fw =>
productsForOrientation.some(p => p.size === selectedSize && p.frame_width === fw)
);
let selectedFrameWidth = preselectedProduct ? preselectedProduct.frame_width : (availableFrameWidths.length > 0 ? availableFrameWidths[0] : null);
let selectedFrameColor = 'black';
function ensureValidSelection() {
const productsForOrientation = showOrientationSelector && selectedOrientation ?
productsData.filter(p => p.orientation === selectedOrientation) :
productsData;
// Ensure selected size is valid for this orientation
const firstAvailableSize = sizesList.find(sz => productsForOrientation.some(p => p.size === sz));
if (!selectedSize || !productsForOrientation.some(p => p.size === selectedSize)) {
selectedSize = firstAvailableSize || null;
}
// Ensure selected frame width is valid for size/orientation
const productsForSize = productsForOrientation.filter(p => p.size === selectedSize);
const firstAvailableFrameWidth = frameWidthsList.find(fw =>
productsForSize.some(p => p.frame_width === fw)
);
if (!selectedFrameWidth || !productsForSize.some(p => p.frame_width === selectedFrameWidth)) {
selectedFrameWidth = firstAvailableFrameWidth || null;
}
}
function init() {
ensureValidSelection();
updateUI();
attachEventListeners();
}
function updateUI() {
ensureValidSelection();
if (showOrientationSelector) {
updateOrientationButtons();
}
updateAvailability();
updatePrice();
updateMainImage();
updateCTA();
}
function updateOrientationButtons() {
container.querySelectorAll('[data-orientation]').forEach(btn => {
btn.classList.toggle('active', btn.dataset.orientation === selectedOrientation);
});
}
function updateAvailability() {
// Filter products by orientation first (if orientation selector is shown)
const productsForOrientation = showOrientationSelector ?
productsData.filter(p => p.orientation === selectedOrientation) :
productsData;
// Update size buttons - show/hide based on availability for this orientation
container.querySelectorAll('[data-size]').forEach(btn => {
const size = btn.dataset.size;
const hasSize = productsForOrientation.some(p => p.size === size);
btn.style.display = hasSize ? '' : 'none';
btn.disabled = !hasSize;
btn.classList.toggle('active', selectedSize === size);
btn.classList.toggle('disabled', !hasSize);
});
// Update frame width buttons
container.querySelectorAll('[data-frame-width]').forEach(btn => {
const fw = btn.dataset.frameWidth;
const hasFrameWidth = productsForOrientation.some(p => p.size === selectedSize && p.frame_width === fw);
btn.disabled = !hasFrameWidth;
btn.classList.toggle('active', selectedFrameWidth === fw);
btn.classList.toggle('disabled', !hasFrameWidth);
});
// Update color options
container.querySelectorAll('[data-color]').forEach(btn => {
btn.classList.toggle('active', btn.dataset.color === selectedFrameColor);
});
}
function updatePrice() {
const productsForOrientation = showOrientationSelector ?
productsData.filter(p => p.orientation === selectedOrientation) :
productsData;
const product = productsForOrientation.find(p =>
p.size === selectedSize && p.frame_width === selectedFrameWidth
);
const priceEl = container.querySelector('#actual-price');
if (priceEl) {
if (product) {
priceEl.textContent = product.price.toFixed(2);
} else {
priceEl.textContent = 'N/A';
}
}
}
function updateMainImage() {
if (!updateMainImageEnabled) return;
const productsForOrientation = showOrientationSelector ?
productsData.filter(p => p.orientation === selectedOrientation) :
productsData;
const product = productsForOrientation.find(p =>
p.size === selectedSize && p.frame_width === selectedFrameWidth
);
const mainImage = document.querySelector('#main-image');
console.log('[FCP] updateMainImage', {
mainImageFound: !!mainImage,
productFound: !!product,
previewUrl: product?.preview_url,
selectedOrientation,
selectedSize,
selectedFrameWidth
});
if (mainImage && product && product.preview_url) {
// Override any existing srcset/sizes so the preview URL is used immediately
mainImage.src = product.preview_url;
if (mainImage.hasAttribute('srcset')) mainImage.removeAttribute('srcset');
if (mainImage.hasAttribute('sizes')) mainImage.removeAttribute('sizes');
console.log('[FCP] Image updated to:', product.preview_url);
}
}
function updateCTA() {
const productsForOrientation = showOrientationSelector ?
productsData.filter(p => p.orientation === selectedOrientation) :
productsData;
const product = productsForOrientation.find(p =>
p.size === selectedSize && p.frame_width === selectedFrameWidth
);
const ctaBtn = container.querySelector('#create-button');
if (ctaBtn) {
if (!product) {
ctaBtn.classList.add('disabled');
ctaBtn.removeAttribute('href');
ctaBtn.onclick = (e) => e.preventDefault();
} else {
ctaBtn.classList.remove('disabled');
const url = product.editor_url + '?frame=' + selectedFrameColor;
ctaBtn.href = url;
// Navigate in same window - no onclick override needed
}
}
}
function attachEventListeners() {
// Orientation buttons
if (showOrientationSelector) {
container.querySelectorAll('[data-orientation]').forEach(btn => {
btn.addEventListener('click', function() {
selectedOrientation = this.dataset.orientation;
// Reset size and frame width to smart defaults for this orientation
const productsForOrientation = productsData.filter(p => p.orientation === selectedOrientation);
if (productsForOrientation.length > 0) {
// Keep current size if available, otherwise use first available based on sizesList ordering
const sizeAvailable = productsForOrientation.some(p => p.size === selectedSize);
if (!sizeAvailable) {
const firstAvailable = sizesList.find(sz => productsForOrientation.some(p => p.size === sz));
selectedSize = firstAvailable || productsForOrientation[0].size;
}
// Keep current frame width if available for this size/orientation
const frameAvailable = productsForOrientation.some(p =>
p.size === selectedSize && p.frame_width === selectedFrameWidth
);
if (!frameAvailable) {
const productsForSize = productsForOrientation.filter(p => p.size === selectedSize);
selectedFrameWidth = productsForSize.length > 0 ?
productsForSize[0].frame_width :
productsForOrientation[0].frame_width;
}
}
ensureValidSelection();
updateUI();
});
});
}
// Size buttons
container.querySelectorAll('[data-size]').forEach(btn => {
btn.addEventListener('click', function() {
if (this.disabled) return;
selectedSize = this.dataset.size;
// When size changes, ensure frame width is valid for this orientation/size
const productsForOrientation = showOrientationSelector ?
productsData.filter(p => p.orientation === selectedOrientation) :
productsData;
const productsForSize = productsForOrientation.filter(p => p.size === selectedSize);
if (productsForSize.length > 0) {
const frameValid = productsForSize.some(p => p.frame_width === selectedFrameWidth);
if (!frameValid) {
selectedFrameWidth = productsForSize[0].frame_width;
}
}
ensureValidSelection();
updateUI();
});
});
// Frame width buttons
container.querySelectorAll('[data-frame-width]').forEach(btn => {
btn.addEventListener('click', function() {
if (this.disabled) return;
selectedFrameWidth = this.dataset.frameWidth;
ensureValidSelection();
updateUI();
});
});
// Color options
container.querySelectorAll('[data-color]').forEach(btn => {
btn.addEventListener('click', function() {
selectedFrameColor = this.dataset.color;
updateUI();
});
});
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();
</script>
<div class="selection-group">
<p>Orientation:</p>
<div class="options-grid">
<button type="button" class="option-button" data-orientation="Portrait">Portrait</button>
<button type="button" class="option-button" data-orientation="Landscape">Landscape</button>
<button type="button" class="option-button" data-orientation="Square">Square</button>
</div>
</div>
<div class="selection-group">
<p>Size:</p>
<div class="options-grid">
<button type="button" class="option-button" data-size="A5">A5</button>
<button type="button" class="option-button" data-size="A4">A4</button>
<button type="button" class="option-button" data-size="A3">A3</button>
<button type="button" class="option-button" data-size="30x30cm">30x30cm</button>
</div>
</div>
<div class="selection-group">
<p>Frame Thickness:</p>
<div class="options-grid">
<button type="button" class="option-button" data-frame-width="13mm">13mm</button>
<button type="button" class="option-button" data-frame-width="20mm">20mm</button>
</div>
</div>
<div class="selection-group">
<p>Frame Colour:</p>
<div class="options-grid">
<button type="button" class="option-button" data-color="black">Black</button>
<button type="button" class="option-button" data-color="white">White</button>
<button type="button" class="option-button" data-color="natural">Natural</button>
</div>
</div>
<div id="product-info">
<p class="price-container">
<span class="price-label">Price:</span>
<span class="currency-symbol">£</span>
<span id="actual-price" class="actual-price">N/A</span>
</p>
<a id="create-button" href="#" role="button">Personalise Your Print</a>
</div>
</div>