2026-01-01 16:17:05 +00:00
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< meta name = "viewport" content = "width=device-width, initial-scale=1.0" >
2026-02-18 21:23:15 +00:00
< title > Berrypod Theme Studio v28< / title >
2026-01-01 16:17:05 +00:00
<!-- Google Fonts -->
< link rel = "preconnect" href = "https://fonts.googleapis.com" >
< link rel = "preconnect" href = "https://fonts.gstatic.com" crossorigin >
< link href = "https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,400;9..144,500;9..144,600;9..144,700&family=Inter:wght@400;500;600;700&family=Libre+Baskerville:wght@400;700&family=Nunito:wght@400;600;700&family=Nunito+Sans:opsz,wght@6..12,300;6..12,400;6..12,500;6..12,600&family=Outfit:wght@300;400;500;600&family=Source+Sans+3:wght@400;500;600&family=Space+Grotesk:wght@400;500;600&display=swap" rel = "stylesheet" >
< style >
/* ========================================
LAYER 1: PRIMITIVES
======================================== */
:root {
--p-space-1: 0.25rem;
--p-space-2: 0.5rem;
--p-space-3: 0.75rem;
--p-space-4: 1rem;
--p-space-6: 1.5rem;
--p-space-8: 2rem;
--p-space-12: 3rem;
--p-space-16: 4rem;
--p-space-24: 6rem;
--p-radius-none: 0;
--p-radius-sm: 0.25rem;
--p-radius-md: 0.5rem;
--p-radius-lg: 0.75rem;
--p-radius-xl: 1rem;
--p-radius-full: 9999px;
--p-font-inter: 'Inter', system-ui, sans-serif;
--p-font-fraunces: 'Fraunces', serif;
--p-font-source: 'Source Sans 3', system-ui, sans-serif;
--p-font-space: 'Space Grotesk', system-ui, sans-serif;
--p-font-baskerville: 'Libre Baskerville', Georgia, serif;
--p-font-nunito: 'Nunito', system-ui, sans-serif;
--p-font-outfit: 'Outfit', system-ui, sans-serif;
--p-font-avenir: 'Nunito Sans', 'Avenir Next', 'Avenir', system-ui, sans-serif;
--p-text-xs: 0.75rem;
--p-text-sm: 0.875rem;
--p-text-base: 1rem;
--p-text-lg: 1.125rem;
--p-text-xl: 1.25rem;
--p-text-2xl: 1.5rem;
--p-text-3xl: 1.875rem;
--p-text-4xl: 2.25rem;
--p-duration-fast: 0.1s;
--p-duration-normal: 0.2s;
--p-duration-slow: 0.35s;
--p-ease-out: cubic-bezier(0.25, 0.46, 0.45, 0.94);
--p-ease-out-back: cubic-bezier(0.34, 1.56, 0.64, 1);
--p-shadow-color: 0 0% 0%;
--p-shadow-strength: 0.06;
}
/* ========================================
LAYER 2: THEME TOKENS
======================================== */
.preview-frame {
--t-surface-base: #ffffff;
--t-surface-raised: #ffffff;
--t-surface-sunken: #f5f5f5;
--t-surface-overlay: rgba(255, 255, 255, 0.95);
--t-text-primary: #171717;
--t-text-secondary: #525252;
--t-text-tertiary: #a3a3a3;
--t-text-inverse: #ffffff;
--t-border-default: #e5e5e5;
--t-border-subtle: #f0f0f0;
}
.preview-frame[data-mood="warm"] {
--t-surface-base: #fdf8f3;
--t-surface-raised: #fffcf8;
--t-surface-sunken: #f5ebe0;
--t-text-primary: #1c1917;
--t-text-secondary: #57534e;
--t-text-tertiary: #a8a29e;
--t-border-default: #e7e0d8;
--t-border-subtle: #f0ebe4;
}
.preview-frame[data-mood="cool"] {
--t-surface-base: #f4f7fb;
--t-surface-raised: #f8fafc;
--t-surface-sunken: #e8eff7;
--t-text-primary: #0f172a;
--t-text-secondary: #475569;
--t-text-tertiary: #94a3b8;
--t-border-default: #d4dce8;
--t-border-subtle: #e8eff5;
}
.preview-frame[data-mood="dark"] {
--t-surface-base: #0a0a0a;
--t-surface-raised: #171717;
--t-surface-sunken: #000000;
--t-surface-overlay: rgba(23, 23, 23, 0.95);
--t-text-primary: #fafafa;
--t-text-secondary: #a3a3a3;
--t-text-tertiary: #737373;
--t-text-inverse: #171717;
--t-border-default: #262626;
--t-border-subtle: #1c1c1c;
--p-shadow-strength: 0.25;
}
/* Typography */
.preview-frame {
--t-font-heading: var(--p-font-inter);
--t-font-body: var(--p-font-inter);
--t-heading-weight: 600;
--t-heading-tracking: -0.025em;
}
.preview-frame[data-typography="editorial"] {
--t-font-heading: var(--p-font-fraunces);
--t-font-body: var(--p-font-source);
--t-heading-weight: 600;
--t-heading-tracking: -0.02em;
}
.preview-frame[data-typography="modern"] {
--t-font-heading: var(--p-font-space);
--t-font-body: var(--p-font-space);
--t-heading-weight: 500;
--t-heading-tracking: -0.03em;
}
.preview-frame[data-typography="classic"] {
--t-font-heading: var(--p-font-baskerville);
--t-font-body: var(--p-font-source);
--t-heading-weight: 400;
--t-heading-tracking: 0;
}
.preview-frame[data-typography="friendly"] {
--t-font-heading: var(--p-font-nunito);
--t-font-body: var(--p-font-nunito);
--t-heading-weight: 700;
--t-heading-tracking: -0.01em;
}
.preview-frame[data-typography="minimal"] {
--t-font-heading: var(--p-font-outfit);
--t-font-body: var(--p-font-outfit);
--t-heading-weight: 300;
--t-heading-tracking: 0;
}
.preview-frame[data-typography="impulse"] {
--t-font-heading: var(--p-font-avenir);
--t-font-body: var(--p-font-avenir);
--t-heading-weight: 300;
--t-heading-tracking: 0.02em;
}
/* Shape */
.preview-frame {
--t-radius-sm: var(--p-radius-sm);
--t-radius-md: var(--p-radius-md);
--t-radius-lg: var(--p-radius-lg);
--t-radius-button: var(--p-radius-md);
--t-radius-card: var(--p-radius-lg);
--t-radius-input: var(--p-radius-md);
--t-radius-image: var(--p-radius-md);
}
.preview-frame[data-shape="sharp"] {
--t-radius-sm: 0;
--t-radius-md: 0;
--t-radius-lg: 0;
--t-radius-button: 0;
--t-radius-card: 0;
--t-radius-input: 0;
--t-radius-image: 0;
}
.preview-frame[data-shape="round"] {
--t-radius-sm: var(--p-radius-md);
--t-radius-md: var(--p-radius-lg);
--t-radius-lg: var(--p-radius-xl);
--t-radius-button: var(--p-radius-lg);
--t-radius-card: var(--p-radius-xl);
--t-radius-input: var(--p-radius-lg);
--t-radius-image: var(--p-radius-lg);
}
.preview-frame[data-shape="pill"] {
--t-radius-sm: var(--p-radius-full);
--t-radius-md: var(--p-radius-full);
--t-radius-lg: var(--p-radius-xl);
--t-radius-button: var(--p-radius-full);
--t-radius-card: var(--p-radius-xl);
--t-radius-input: var(--p-radius-full);
--t-radius-image: var(--p-radius-lg);
}
/* Density */
.preview-frame {
--t-density: 1;
}
.preview-frame[data-density="spacious"] {
--t-density: 1.25;
}
.preview-frame[data-density="compact"] {
--t-density: 0.85;
}
/* ========================================
LAYER 3: SEMANTIC TOKENS
======================================== */
.preview-frame {
--t-accent-h: 24;
--t-accent-s: 95%;
--t-accent-l: 53%;
--t-accent: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l));
--t-accent-hover: hsl(var(--t-accent-h) var(--t-accent-s) calc(var(--t-accent-l) - 8%));
--t-accent-subtle: hsl(var(--t-accent-h) 40% 95%);
--t-accent-ring: hsl(var(--t-accent-h) var(--t-accent-s) var(--t-accent-l) / 0.4);
--t-secondary-accent: #ea580c;
--t-sale-color: #dc2626;
--t-font-size-scale: 1;
--t-heading-weight: 600;
--t-layout-max-width: 1400px;
--t-button-style: filled;
--t-card-shadow: none;
--t-product-text-align: left;
--color-page: var(--t-surface-base);
--color-card: var(--t-surface-raised);
--color-input: var(--t-surface-raised);
--color-heading: var(--t-text-primary);
--color-body: var(--t-text-secondary);
--color-caption: var(--t-text-tertiary);
--color-button-primary: var(--t-accent);
--color-button-primary-hover: var(--t-secondary-accent);
--color-button-primary-text: var(--t-text-inverse);
--color-border: var(--t-border-default);
--font-heading: var(--t-font-heading);
--font-body: var(--t-font-body);
--weight-heading: var(--t-heading-weight);
--tracking-heading: var(--t-heading-tracking);
--space-xs: calc(var(--p-space-2) * var(--t-density));
--space-sm: calc(var(--p-space-3) * var(--t-density));
--space-md: calc(var(--p-space-4) * var(--t-density));
--space-lg: calc(var(--p-space-6) * var(--t-density));
--space-xl: calc(var(--p-space-8) * var(--t-density));
--space-2xl: calc(var(--p-space-12) * var(--t-density));
--radius-button: var(--t-radius-button);
--radius-card: var(--t-radius-card);
--radius-input: var(--t-radius-input);
--radius-image: var(--t-radius-image);
--shadow-sm:
0 1px 2px hsl(var(--p-shadow-color) / calc(var(--p-shadow-strength) * 0.5)),
0 1px 3px hsl(var(--p-shadow-color) / var(--p-shadow-strength));
--shadow-md:
0 2px 4px hsl(var(--p-shadow-color) / calc(var(--p-shadow-strength) * 0.5)),
0 4px 8px hsl(var(--p-shadow-color) / var(--p-shadow-strength)),
0 8px 16px hsl(var(--p-shadow-color) / calc(var(--p-shadow-strength) * 0.5));
--transition-fast: var(--p-duration-fast) var(--p-ease-out);
--transition-normal: var(--p-duration-normal) var(--p-ease-out);
--transition-bounce: var(--p-duration-normal) var(--p-ease-out-back);
}
.preview-frame[data-mood="dark"] {
--t-accent-subtle: hsl(var(--t-accent-h) 30% 15%);
}
/* ========================================
PAGE LAYOUT
======================================== */
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html {
font-size: 16px;
}
body {
font-family: var(--p-font-inter);
background: #f5f5f5;
height: 100vh;
overflow: hidden;
color: #171717;
}
.page-layout {
display: grid;
grid-template-columns: 380px 1fr;
height: 100vh;
}
@media (max-width: 1000px) {
.page-layout {
grid-template-columns: 1fr;
}
}
/* ========================================
CONTROLS PANEL
======================================== */
.controls-panel {
background: #ffffff;
border-right: 1px solid #e5e5e5;
padding: var(--p-space-6);
overflow-y: auto;
max-height: 100vh;
position: sticky;
top: 0;
}
.controls-header {
margin-bottom: var(--p-space-6);
}
.controls-header h1 {
font-size: var(--p-text-xl);
font-weight: 600;
margin-bottom: var(--p-space-2);
letter-spacing: -0.025em;
}
.controls-header p {
font-size: var(--p-text-sm);
color: #737373;
line-height: 1.5;
}
.control-section {
margin-bottom: var(--p-space-6);
}
.control-label {
font-size: var(--p-text-xs);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #737373;
margin-bottom: var(--p-space-3);
display: block;
}
/* Customise accordion */
.customise-section {
border-top: 1px solid #e5e5e5;
margin-top: var(--p-space-6);
padding-top: var(--p-space-4);
}
.customise-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--p-space-3) 0;
cursor: pointer;
user-select: none;
}
.customise-header:hover .customise-title {
color: #171717;
}
.customise-title {
font-size: var(--p-text-sm);
font-weight: 600;
color: #525252;
transition: color 0.15s ease;
}
.customise-icon {
width: 20px;
height: 20px;
color: #a3a3a3;
transition: transform 0.2s ease;
}
.customise-section.open .customise-icon {
transform: rotate(180deg);
}
.customise-content {
display: none;
padding-top: var(--p-space-4);
}
.customise-section.open .customise-content {
display: block;
}
/* Control groups within customise */
.control-group {
margin-bottom: var(--p-space-6);
padding-bottom: var(--p-space-6);
border-bottom: 1px solid #f0f0f0;
}
.control-group:last-child {
margin-bottom: 0;
padding-bottom: 0;
border-bottom: none;
}
.control-group-header {
display: flex;
align-items: center;
gap: var(--p-space-2);
margin-bottom: var(--p-space-4);
}
.control-group-icon {
width: 16px;
height: 16px;
color: #a3a3a3;
}
.control-group-title {
font-size: var(--p-text-sm);
font-weight: 600;
color: #171717;
}
.control-group .control-section {
margin-bottom: var(--p-space-4);
}
.control-group .control-section:last-child {
margin-bottom: 0;
}
/* Text input */
.text-input {
width: 100%;
padding: var(--p-space-3) var(--p-space-4);
border: 1px solid #e5e5e5;
border-radius: var(--p-radius-md);
font-size: var(--p-text-base);
font-family: inherit;
transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.text-input:focus {
outline: none;
border-color: #171717;
box-shadow: 0 0 0 3px rgba(0,0,0,0.05);
}
/* Logo/Header section */
.branding-section {
background: #f9f9f9;
border-radius: var(--p-radius-lg);
padding: var(--p-space-4);
margin-bottom: var(--p-space-6);
}
.branding-section .control-label {
margin-bottom: var(--p-space-4);
}
.logo-mode-options {
display: flex;
flex-direction: column;
gap: var(--p-space-2);
margin-bottom: var(--p-space-4);
}
.logo-mode-option {
display: flex;
align-items: center;
gap: var(--p-space-3);
padding: var(--p-space-3);
background: #ffffff;
border: 2px solid transparent;
border-radius: var(--p-radius-md);
cursor: pointer;
transition: all 0.15s ease;
}
.logo-mode-option:hover {
border-color: #e5e5e5;
}
.logo-mode-option.active {
border-color: #171717;
}
.logo-mode-option input[type="radio"] {
display: none;
}
.logo-mode-radio {
width: 18px;
height: 18px;
border: 2px solid #d4d4d4;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
transition: all 0.15s ease;
}
.logo-mode-option.active .logo-mode-radio {
border-color: #171717;
}
.logo-mode-radio::after {
content: '';
width: 8px;
height: 8px;
background: #171717;
border-radius: 50%;
opacity: 0;
transform: scale(0);
transition: all 0.15s ease;
}
.logo-mode-option.active .logo-mode-radio::after {
opacity: 1;
transform: scale(1);
}
.logo-mode-content {
flex: 1;
}
.logo-mode-title {
font-size: var(--p-text-sm);
font-weight: 500;
margin-bottom: 2px;
}
.logo-mode-desc {
font-size: var(--p-text-xs);
color: #737373;
}
/* Upload areas */
.upload-section {
margin-top: var(--p-space-4);
padding-top: var(--p-space-4);
border-top: 1px solid #e5e5e5;
}
.upload-row {
display: none;
margin-bottom: var(--p-space-3);
}
.upload-row.visible {
display: block;
}
.upload-label {
font-size: var(--p-text-xs);
font-weight: 500;
color: #525252;
margin-bottom: var(--p-space-2);
display: block;
}
.upload-area {
display: flex;
align-items: center;
gap: var(--p-space-3);
}
.upload-btn {
flex: 1;
background: #ffffff;
border: 1px dashed #d4d4d4;
border-radius: var(--p-radius-md);
padding: var(--p-space-3);
font-size: var(--p-text-sm);
color: #737373;
cursor: pointer;
transition: all 0.15s ease;
text-align: center;
}
.upload-btn:hover {
border-color: #a3a3a3;
color: #525252;
}
.upload-btn input {
display: none;
}
.upload-preview {
width: 64px;
height: 40px;
background: #ffffff;
border: 1px solid #e5e5e5;
border-radius: var(--p-radius-md);
display: none;
align-items: center;
justify-content: center;
overflow: hidden;
position: relative;
}
.upload-preview.has-image {
display: flex;
}
.upload-preview img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
}
.upload-preview-wide {
width: 100%;
height: 60px;
margin-top: var(--p-space-2);
}
.upload-preview-wide img {
width: 100%;
height: 100%;
object-fit: cover;
}
.remove-upload {
position: absolute;
top: -6px;
right: -6px;
width: 18px;
height: 18px;
background: #171717;
color: white;
border: none;
border-radius: 50%;
font-size: 12px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
line-height: 1;
}
/* Size control slider */
.size-control {
margin-top: var(--p-space-3);
display: none;
}
.size-control.visible {
display: block;
}
.size-control-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--p-space-2);
}
.size-control-label {
font-size: var(--p-text-xs);
font-weight: 500;
color: #525252;
}
.size-control-value {
font-family: 'SF Mono', Monaco, monospace;
font-size: var(--p-text-xs);
color: #737373;
}
.size-slider {
width: 100%;
height: 6px;
border-radius: 3px;
background: #e5e5e5;
outline: none;
-webkit-appearance: none;
appearance: none;
}
.size-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 18px;
height: 18px;
border-radius: 50%;
background: #171717;
cursor: pointer;
transition: transform 0.1s ease;
}
.size-slider::-webkit-slider-thumb:hover {
transform: scale(1.1);
}
.size-slider::-moz-range-thumb {
width: 18px;
height: 18px;
border-radius: 50%;
background: #171717;
cursor: pointer;
border: none;
}
/* Header image controls */
.header-image-controls {
margin-top: var(--p-space-3);
display: none;
flex-direction: column;
gap: var(--p-space-3);
}
.header-image-controls.visible {
display: flex;
}
.control-row {
display: flex;
flex-direction: column;
gap: var(--p-space-2);
}
#header-position-x-row {
display: none;
}
#header-position-x-row.visible {
display: flex;
}
/* Logo color control */
.logo-color-control {
margin-top: var(--p-space-3);
display: none;
}
.logo-color-control.visible {
display: block;
}
.logo-color-toggle {
margin-bottom: var(--p-space-2);
}
.toggle-label {
display: flex;
align-items: center;
gap: var(--p-space-2);
cursor: pointer;
}
.toggle-label input[type="checkbox"] {
display: none;
}
.toggle-switch {
width: 36px;
height: 20px;
background: #e5e5e5;
border-radius: 10px;
position: relative;
transition: background 0.2s ease;
}
.toggle-switch::after {
content: '';
position: absolute;
width: 16px;
height: 16px;
background: white;
border-radius: 50%;
top: 2px;
left: 2px;
transition: transform 0.2s ease;
box-shadow: 0 1px 3px rgba(0,0,0,0.2);
}
.toggle-label input:checked + .toggle-switch {
background: #171717;
}
.toggle-label input:checked + .toggle-switch::after {
transform: translateX(16px);
}
.toggle-text {
font-size: var(--p-text-sm);
color: #525252;
}
.toggle-list {
display: flex;
flex-direction: column;
gap: var(--p-space-2);
}
.toggle-list .toggle-label {
padding: var(--p-space-2) 0;
}
.logo-color-picker-row {
display: none;
align-items: center;
gap: var(--p-space-3);
margin-top: var(--p-space-2);
}
.logo-color-picker-row.visible {
display: flex;
}
.color-picker-small {
width: 36px;
height: 36px;
}
/* Preset grid */
.preset-grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--p-space-2);
margin-bottom: var(--p-space-6);
}
.preset-button {
background: #f5f5f5;
border: 2px solid transparent;
border-radius: var(--p-radius-md);
padding: var(--p-space-3);
cursor: pointer;
text-align: left;
transition: all var(--p-duration-normal) var(--p-ease-out);
font-family: inherit;
}
.preset-button:hover {
background: #ebebeb;
}
.preset-button.active {
border-color: #171717;
background: #ffffff;
}
.preset-button .preset-name {
font-size: var(--p-text-sm);
font-weight: 600;
margin-bottom: 2px;
}
.preset-button .preset-desc {
font-size: var(--p-text-xs);
color: #737373;
}
/* Option buttons */
.option-group {
display: flex;
flex-wrap: wrap;
gap: var(--p-space-2);
}
.option-button {
background: #f5f5f5;
border: 2px solid transparent;
border-radius: var(--p-radius-md);
padding: var(--p-space-2) var(--p-space-3);
cursor: pointer;
font-size: var(--p-text-sm);
font-family: inherit;
transition: all var(--p-duration-normal) var(--p-ease-out);
}
.option-button:hover {
background: #ebebeb;
}
.option-button.active {
border-color: #171717;
background: #ffffff;
}
/* Color picker */
.color-picker-wrapper {
display: flex;
align-items: center;
gap: var(--p-space-3);
}
.color-picker {
width: 48px;
height: 48px;
border: none;
border-radius: var(--p-radius-md);
cursor: pointer;
padding: 0;
overflow: hidden;
}
.color-picker::-webkit-color-swatch-wrapper {
padding: 0;
}
.color-picker::-webkit-color-swatch {
border: 2px solid rgba(0,0,0,0.1);
border-radius: var(--p-radius-md);
}
.color-value {
font-family: 'SF Mono', Monaco, monospace;
font-size: var(--p-text-sm);
color: #525252;
}
/* Combo display */
.combo-display {
background: #f5f5f5;
border-radius: var(--p-radius-md);
padding: var(--p-space-4);
margin-top: var(--p-space-6);
}
.combo-display .combo-label {
font-size: var(--p-text-xs);
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #737373;
margin-bottom: var(--p-space-2);
}
.combo-display .combo-value {
font-size: var(--p-text-sm);
color: #171717;
line-height: 1.6;
}
.combo-display .combo-count {
font-size: var(--p-text-xs);
color: #a3a3a3;
margin-top: var(--p-space-2);
}
/* ========================================
PREVIEW AREA
======================================== */
.preview-area {
padding: var(--p-space-6);
display: flex;
flex-direction: column;
overflow: hidden;
height: 100%;
}
.preview-container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
display: flex;
flex-direction: column;
flex: 1;
min-height: 0;
overflow: hidden;
}
.page-toggle {
display: flex;
gap: var(--p-space-1);
margin-bottom: var(--p-space-3);
background: #e5e5e5;
padding: 4px;
border-radius: var(--p-radius-md);
width: fit-content;
flex-shrink: 0;
}
.page-toggle-btn {
padding: var(--p-space-2) var(--p-space-4);
border: none;
background: transparent;
border-radius: var(--p-radius-sm);
font-family: inherit;
font-size: var(--p-text-sm);
font-weight: 500;
color: #525252;
cursor: pointer;
transition: all 0.15s ease;
}
.page-toggle-btn:hover {
color: #171717;
}
.page-toggle-btn.active {
background: #ffffff;
color: #171717;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}
/* Browser Chrome Mockup - macOS style */
.browser-chrome {
display: flex;
align-items: center;
background: linear-gradient(180deg, #e8e8e8 0%, #d8d8d8 100%);
border: 1px solid #c0c0c0;
border-bottom: 1px solid #b0b0b0;
border-radius: 10px 10px 0 0;
padding: 10px 14px;
gap: 14px;
flex-shrink: 0;
}
.browser-traffic-lights {
display: flex;
gap: 8px;
flex-shrink: 0;
}
.traffic-light {
width: 12px;
height: 12px;
border-radius: 50%;
border: 1px solid rgba(0,0,0,0.1);
}
.traffic-light.red { background: #ff5f57; border-color: #e14640; }
.traffic-light.yellow { background: #ffbd2e; border-color: #dfa123; }
.traffic-light.green { background: #28c940; border-color: #1aab29; }
.browser-nav {
display: flex;
gap: 6px;
flex-shrink: 0;
}
.browser-nav-btn {
width: 24px;
height: 24px;
border-radius: 4px;
background: transparent;
border: none;
color: #999;
display: flex;
align-items: center;
justify-content: center;
cursor: default;
}
.browser-nav-btn svg {
width: 14px;
height: 14px;
}
.browser-url-bar {
flex: 1;
display: flex;
align-items: center;
gap: 8px;
background: #ffffff;
border: 1px solid #c0c0c0;
border-radius: 6px;
padding: 5px 12px;
min-width: 0;
}
.browser-url-icon {
width: 14px;
height: 14px;
color: #28c940;
flex-shrink: 0;
}
.browser-favicon {
width: 16px;
height: 16px;
border-radius: 3px;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
flex-shrink: 0;
background: var(--t-accent, #e85d04);
}
.browser-favicon.has-logo {
background: transparent;
}
.favicon-letter {
font-family: system-ui, sans-serif;
font-size: 10px;
font-weight: 600;
color: white;
}
.browser-favicon.has-logo .favicon-letter {
display: none;
}
.favicon-logo {
width: 100%;
height: 100%;
object-fit: contain;
}
.browser-url-text {
font-family: system-ui, sans-serif;
font-size: 12px;
color: #333;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.browser-url-text .domain {
color: #111;
}
.browser-actions {
display: flex;
gap: 8px;
flex-shrink: 0;
}
.browser-action-btn {
width: 24px;
height: 24px;
border-radius: 4px;
background: transparent;
border: none;
color: #666;
display: flex;
align-items: center;
justify-content: center;
cursor: default;
}
.browser-action-btn svg {
width: 16px;
height: 16px;
}
.preview-frame {
width: 100%;
flex: 1;
min-height: 0;
background: var(--color-page);
border-radius: 0 0 var(--p-radius-lg) var(--p-radius-lg);
border: 1px solid #c0c0c0;
border-top: none;
box-shadow:
0 4px 6px rgba(0,0,0,0.05),
0 10px 20px rgba(0,0,0,0.05),
0 20px 40px rgba(0,0,0,0.05);
overflow-y: auto;
overflow-x: hidden;
transition: background-color var(--p-duration-slow) var(--p-ease-out);
}
/* Styled scrollbar for the preview */
.preview-frame::-webkit-scrollbar {
width: 10px;
}
.preview-frame::-webkit-scrollbar-track {
background: rgba(0,0,0,0.05);
}
.preview-frame::-webkit-scrollbar-thumb {
background: rgba(0,0,0,0.2);
border-radius: 5px;
}
.preview-frame::-webkit-scrollbar-thumb:hover {
background: rgba(0,0,0,0.3);
}
/* Announcement bar */
.announcement-bar {
background: var(--t-accent);
color: var(--t-text-inverse);
text-align: center;
padding: var(--space-xs) var(--space-md);
font-family: var(--font-body);
font-size: var(--p-text-sm);
transition: all var(--transition-normal);
}
.announcement-bar.hidden {
display: none;
}
.announcement-bar p {
margin: 0;
}
/* Sticky header */
.preview-frame[data-sticky-header="true"] .shop-header {
position: sticky;
top: 0;
z-index: 100;
box-shadow: var(--shadow-sm);
}
.preview-frame[data-sticky-header="true"][data-has-announcement="true"] .shop-header {
top: 0;
}
/* ========================================
SHOP HEADER MODES
======================================== */
.shop-header {
padding: var(--space-md) var(--space-lg);
border-bottom: 1px solid var(--color-border);
display: flex;
align-items: center;
justify-content: space-between;
transition: all var(--transition-normal);
position: relative;
background: var(--t-surface-base);
}
.shop-header[data-header="centered"] {
flex-direction: column;
gap: var(--space-sm);
text-align: center;
}
.shop-header[data-header="minimal"] .shop-nav {
display: none;
}
/* Header with background image */
.shop-header[data-logo-mode="header-image"] {
min-height: 160px;
padding: var(--space-lg);
border-bottom: none;
background-size: cover;
background-position: center;
}
.shop-header[data-logo-mode="header-image"]::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(to bottom, rgba(0,0,0,0.3), rgba(0,0,0,0.5));
pointer-events: none;
}
.shop-header[data-logo-mode="header-image"] .shop-logo,
.shop-header[data-logo-mode="header-image"] .shop-nav a,
.shop-header[data-logo-mode="header-image"] .shop-cart {
position: relative;
z-index: 1;
color: #ffffff;
}
.shop-header[data-logo-mode="header-image"] .shop-logo-text {
color: #ffffff;
text-shadow: 0 1px 3px rgba(0,0,0,0.3);
}
.shop-header[data-logo-mode="header-image"] .cart-icon {
stroke: #ffffff;
}
/* Header with logo + background image */
.shop-header[data-logo-mode="logo-header-image"] {
min-height: 160px;
padding: var(--space-lg);
border-bottom: none;
background-size: cover;
background-position: center;
}
.shop-header[data-logo-mode="logo-header-image"]::before {
content: '';
position: absolute;
inset: 0;
background: linear-gradient(to bottom, rgba(0,0,0,0.3), rgba(0,0,0,0.5));
pointer-events: none;
}
.shop-header[data-logo-mode="logo-header-image"] .shop-logo,
.shop-header[data-logo-mode="logo-header-image"] .shop-nav a,
.shop-header[data-logo-mode="logo-header-image"] .shop-cart {
position: relative;
z-index: 1;
color: #ffffff;
}
.shop-header[data-logo-mode="logo-header-image"] .cart-icon {
stroke: #ffffff;
}
/* Logo display */
.shop-logo {
display: flex;
align-items: center;
gap: var(--space-sm);
transition: all var(--transition-normal);
}
.shop-logo-text {
font-family: var(--font-heading);
font-weight: var(--weight-heading);
font-size: var(--p-text-xl);
letter-spacing: var(--tracking-heading);
color: var(--color-heading);
transition: all var(--transition-normal);
}
.shop-logo-image {
height: 36px;
width: auto;
display: none;
flex-shrink: 0;
}
.shop-logo-image.visible {
display: block;
}
.shop-logo-image img {
height: 100%;
width: auto;
object-fit: contain;
}
.shop-nav {
display: flex;
gap: var(--space-lg);
}
.shop-nav a {
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-body);
text-decoration: none;
transition: color var(--transition-fast);
}
.shop-nav a:hover {
color: var(--color-heading);
}
.shop-cart {
display: flex;
align-items: center;
gap: var(--space-xs);
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-body);
cursor: pointer;
}
.cart-count {
background: var(--t-accent);
color: var(--t-text-inverse);
font-size: 11px;
font-weight: 600;
width: 18px;
height: 18px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
/* Hero */
.shop-hero {
padding: var(--space-2xl) var(--space-lg);
text-align: center;
transition: all var(--transition-normal);
background: var(--t-surface-base);
}
.shop-hero h1 {
font-family: var(--font-heading);
font-weight: var(--weight-heading);
font-size: var(--p-text-4xl);
letter-spacing: var(--tracking-heading);
color: var(--color-heading);
margin-bottom: var(--space-sm);
transition: all var(--transition-normal);
}
/* Minimal/Impulse style - much larger, lighter hero text */
.preview-frame[data-typography="minimal"] .shop-hero {
padding: var(--space-2xl) var(--space-lg) calc(var(--space-2xl) * 1.5);
}
.preview-frame[data-typography="minimal"] .shop-hero h1 {
font-size: clamp(2.5rem, 5vw, 4rem);
font-weight: 300;
letter-spacing: -0.02em;
margin-bottom: var(--space-md);
}
.preview-frame[data-typography="minimal"] .shop-hero p {
font-weight: 300;
font-size: var(--p-text-base);
color: var(--color-caption);
}
.shop-hero p {
font-family: var(--font-body);
font-size: var(--p-text-lg);
color: var(--color-body);
max-width: 500px;
margin: 0 auto var(--space-lg);
line-height: 1.6;
transition: all var(--transition-normal);
}
/* Buttons */
.btn {
display: inline-flex;
align-items: center;
justify-content: center;
font-family: var(--font-body);
font-weight: 500;
font-size: var(--p-text-sm);
padding: var(--space-sm) var(--space-lg);
border-radius: var(--radius-button);
border: none;
cursor: pointer;
transition:
transform var(--transition-bounce),
box-shadow var(--transition-normal),
background-color var(--transition-fast);
}
.btn:hover {
transform: translateY(-1px);
box-shadow: var(--shadow-md);
}
.btn:active {
transform: translateY(0);
transition-duration: 0.05s;
}
.btn-primary {
background: var(--color-button-primary);
color: var(--color-button-primary-text);
}
.btn-primary:hover {
background: var(--color-button-primary-hover);
}
/* Button style variations */
.preview-frame[data-button-style="outline"] .btn-primary {
background: transparent;
color: var(--color-button-primary);
border: 2px solid var(--color-button-primary);
}
.preview-frame[data-button-style="outline"] .btn-primary:hover {
background: var(--color-button-primary);
color: var(--color-button-primary-text);
}
.preview-frame[data-button-style="soft"] .btn-primary {
background: var(--t-accent-subtle);
color: var(--color-button-primary);
}
.preview-frame[data-button-style="soft"] .btn-primary:hover {
background: var(--color-button-primary);
color: var(--color-button-primary-text);
}
/* Layout width variations - controls content width within the full-width browser */
.preview-frame[data-layout-width="contained"] .shop-hero,
.preview-frame[data-layout-width="contained"] .product-section,
.preview-frame[data-layout-width="contained"] .home-categories,
.preview-frame[data-layout-width="contained"] .home-about,
.preview-frame[data-layout-width="contained"] .pdp-container,
.preview-frame[data-layout-width="contained"] .pdp-reviews,
.preview-frame[data-layout-width="contained"] .pdp-related,
.preview-frame[data-layout-width="contained"] .cart-container,
.preview-frame[data-layout-width="contained"] .content-page,
.preview-frame[data-layout-width="contained"] .error-container,
.preview-frame[data-layout-width="contained"] .collection-header,
.preview-frame[data-layout-width="contained"] .collection-grid-container {
max-width: 800px;
margin-left: auto;
margin-right: auto;
padding-left: var(--space-lg);
padding-right: var(--space-lg);
}
.preview-frame[data-layout-width="wide"] .shop-hero,
.preview-frame[data-layout-width="wide"] .product-section,
.preview-frame[data-layout-width="wide"] .home-categories,
.preview-frame[data-layout-width="wide"] .home-about,
.preview-frame[data-layout-width="wide"] .pdp-container,
.preview-frame[data-layout-width="wide"] .pdp-reviews,
.preview-frame[data-layout-width="wide"] .pdp-related,
.preview-frame[data-layout-width="wide"] .cart-container,
.preview-frame[data-layout-width="wide"] .content-page,
.preview-frame[data-layout-width="wide"] .error-container,
.preview-frame[data-layout-width="wide"] .collection-header,
.preview-frame[data-layout-width="wide"] .collection-grid-container {
max-width: 1000px;
margin-left: auto;
margin-right: auto;
}
/* Full width has no max-width constraint */
.preview-frame[data-layout-width="full"] .shop-hero,
.preview-frame[data-layout-width="full"] .product-section,
.preview-frame[data-layout-width="full"] .home-categories,
.preview-frame[data-layout-width="full"] .home-about,
.preview-frame[data-layout-width="full"] .pdp-container,
.preview-frame[data-layout-width="full"] .pdp-reviews,
.preview-frame[data-layout-width="full"] .pdp-related,
.preview-frame[data-layout-width="full"] .cart-container,
.preview-frame[data-layout-width="full"] .content-page,
.preview-frame[data-layout-width="full"] .error-container,
.preview-frame[data-layout-width="full"] .collection-header,
.preview-frame[data-layout-width="full"] .collection-grid-container {
max-width: none;
}
/* Font size scale */
.preview-frame[data-font-size="small"] {
font-size: 14px;
}
.preview-frame[data-font-size="medium"] {
font-size: 16px;
}
.preview-frame[data-font-size="large"] {
font-size: 18px;
}
/* Heading weight */
.preview-frame[data-heading-weight="regular"] {
--weight-heading: 400;
}
.preview-frame[data-heading-weight="medium"] {
--weight-heading: 500;
}
.preview-frame[data-heading-weight="bold"] {
--weight-heading: 700;
}
/* Impulse typography special styling */
.preview-frame[data-typography="impulse"] {
--weight-heading: 300;
}
.preview-frame[data-typography="impulse"] .shop-hero h1 {
font-size: 3.5rem;
font-weight: 300;
letter-spacing: 0.04em;
text-transform: uppercase;
line-height: 1.1;
}
.preview-frame[data-typography="impulse"] .shop-hero p {
font-size: 1.125rem;
font-weight: 300;
letter-spacing: 0.02em;
max-width: 500px;
}
.preview-frame[data-typography="impulse"] .section-title {
font-size: 1.5rem;
font-weight: 400;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.preview-frame[data-typography="impulse"] .product-title {
font-weight: 400;
letter-spacing: 0.02em;
}
.preview-frame[data-typography="impulse"] .product-price {
font-weight: 300;
letter-spacing: 0.01em;
}
.preview-frame[data-typography="impulse"] .btn-primary {
font-weight: 500;
letter-spacing: 0.1em;
text-transform: uppercase;
font-size: 0.75rem;
padding: 1rem 2rem;
}
.preview-frame[data-typography="impulse"] .shop-nav a {
font-weight: 400;
letter-spacing: 0.04em;
text-transform: uppercase;
font-size: 0.75rem;
}
.preview-frame[data-typography="impulse"] .announcement-bar {
font-weight: 400;
letter-spacing: 0.06em;
text-transform: uppercase;
font-size: 0.7rem;
}
.preview-frame[data-typography="impulse"] .category-name {
font-weight: 400;
letter-spacing: 0.04em;
text-transform: uppercase;
font-size: 0.75rem;
}
/* Product grid */
.product-section {
padding: var(--space-xl) var(--space-lg);
transition: all var(--transition-normal);
background: var(--t-surface-sunken);
}
.section-title {
font-family: var(--font-heading);
font-weight: var(--weight-heading);
font-size: var(--p-text-2xl);
letter-spacing: var(--tracking-heading);
color: var(--color-heading);
margin-bottom: var(--space-lg);
transition: all var(--transition-normal);
}
.product-grid {
display: grid;
gap: var(--space-lg);
transition: all var(--transition-normal);
}
.product-grid[data-grid="2"] { grid-template-columns: repeat(2, 1fr); }
.product-grid[data-grid="3"] { grid-template-columns: repeat(3, 1fr); }
.product-grid[data-grid="4"] { grid-template-columns: repeat(4, 1fr); }
.product-card {
background: var(--t-surface-raised);
border-radius: var(--radius-card);
overflow: hidden;
cursor: pointer;
}
/* Card shadow variations */
.preview-frame[data-card-shadow="subtle"] .product-card {
box-shadow: 0 1px 3px rgba(0,0,0,0.08), 0 2px 8px rgba(0,0,0,0.06);
}
.preview-frame[data-card-shadow="pronounced"] .product-card {
box-shadow: 0 4px 12px rgba(0,0,0,0.1), 0 8px 24px rgba(0,0,0,0.08);
}
/* Product text alignment */
.preview-frame[data-product-text="center"] .product-info {
text-align: center;
}
/* Show/hide prices */
.preview-frame[data-show-prices="false"] .product-price {
display: none;
}
transition:
transform var(--transition-normal),
box-shadow var(--transition-normal);
cursor: pointer;
box-shadow: var(--shadow-sm);
position: relative;
}
.product-card:hover {
transform: translateY(-4px);
box-shadow: var(--shadow-md);
}
.product-image {
aspect-ratio: 1;
overflow: hidden;
border-radius: var(--radius-image) var(--radius-image) 0 0;
position: relative;
}
.product-image-primary,
.product-image-hover {
position: absolute;
inset: 0;
background-size: cover;
background-position: center;
transition: opacity 0.3s ease;
}
.product-image-hover {
opacity: 0;
}
.preview-frame[data-hover-image="true"] .product-card:hover .product-image-hover {
opacity: 1;
}
.quick-add-btn {
position: absolute;
bottom: var(--space-sm);
left: var(--space-sm);
right: var(--space-sm);
background: var(--t-surface-raised);
color: var(--color-heading);
border: none;
padding: var(--space-xs) var(--space-sm);
border-radius: var(--radius-button);
font-family: var(--font-body);
font-size: var(--p-text-sm);
font-weight: 500;
cursor: pointer;
opacity: 0;
transform: translateY(8px);
transition: all 0.2s ease;
box-shadow: var(--shadow-sm);
}
.preview-frame[data-quick-add="true"] .product-card:hover .quick-add-btn {
opacity: 1;
transform: translateY(0);
}
.quick-add-btn:hover {
background: var(--color-heading);
color: var(--t-text-inverse);
}
/* Product badges */
.product-badge {
position: absolute;
top: var(--space-sm);
left: var(--space-sm);
padding: 4px 10px;
border-radius: var(--radius-button);
font-family: var(--font-body);
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.5px;
z-index: 1;
}
.badge-new {
background: var(--t-accent);
color: var(--t-text-inverse);
}
.badge-sale {
background: var(--t-sale-color);
color: white;
}
.badge-sold {
background: var(--t-surface-raised);
color: var(--color-body);
border: 1px solid var(--color-border);
}
.price-was {
text-decoration: line-through;
color: var(--color-caption);
margin-right: 4px;
}
/* Aspect ratios */
.preview-frame[data-aspect="square"] .product-image {
aspect-ratio: 1;
}
.preview-frame[data-aspect="portrait"] .product-image {
aspect-ratio: 3 / 4;
}
.preview-frame[data-aspect="landscape"] .product-image {
aspect-ratio: 4 / 3;
}
/* Header icons */
.header-icon-btn {
background: none;
border: none;
padding: var(--space-xs);
cursor: pointer;
color: var(--color-body);
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--radius-button);
transition: all 0.15s ease;
position: relative;
}
.header-icon-btn:hover {
color: var(--color-heading);
background: var(--t-surface-sunken);
}
.shop-cart {
display: flex;
align-items: center;
gap: var(--space-xs);
}
.cart-count {
position: absolute;
top: -2px;
right: -2px;
background: var(--t-accent);
color: var(--t-text-inverse);
font-size: 10px;
font-weight: 600;
min-width: 16px;
height: 16px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.shop-header[data-logo-mode="header-image"] .header-icon-btn,
.shop-header[data-logo-mode="logo-header-image"] .header-icon-btn {
color: #ffffff;
}
.shop-header[data-logo-mode="header-image"] .header-icon-btn:hover,
.shop-header[data-logo-mode="logo-header-image"] .header-icon-btn:hover {
background: rgba(255,255,255,0.1);
}
.product-info {
padding: var(--space-md);
transition: all var(--transition-normal);
}
.product-title {
font-family: var(--font-body);
font-weight: 500;
font-size: var(--p-text-sm);
color: var(--color-heading);
margin-bottom: var(--space-xs);
transition: all var(--transition-normal);
}
.product-price {
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-body);
font-variant-numeric: tabular-nums;
transition: all var(--transition-normal);
}
/* Minimal/Impulse style product cards - cleaner, lighter */
.preview-frame[data-typography="minimal"] .product-info {
padding: var(--space-sm) 0;
}
.preview-frame[data-typography="minimal"] .product-title {
font-weight: 400;
font-size: var(--p-text-base);
letter-spacing: 0;
}
.preview-frame[data-typography="minimal"] .product-price {
font-weight: 300;
color: var(--color-caption);
}
.preview-frame[data-typography="minimal"] .product-card {
background: transparent;
}
.preview-frame[data-typography="minimal"] .quick-add-btn {
opacity: 0;
transition: opacity 0.2s ease;
}
.preview-frame[data-typography="minimal"] .product-card:hover .quick-add-btn {
opacity: 1;
}
/* Minimal section titles */
.preview-frame[data-typography="minimal"] .section-title {
font-weight: 300;
font-size: var(--p-text-xl);
letter-spacing: 0.05em;
text-transform: uppercase;
}
/* Footer */
.shop-footer {
padding: var(--space-xl) var(--space-lg);
border-top: 1px solid var(--color-border);
transition: all var(--transition-normal);
background: var(--t-surface-base);
}
.footer-content {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-xl);
margin-bottom: var(--space-lg);
padding-bottom: var(--space-lg);
border-bottom: 1px solid var(--color-border);
}
.footer-newsletter {
max-width: 320px;
}
.footer-heading {
font-family: var(--font-heading);
font-weight: var(--weight-heading);
font-size: var(--p-text-lg);
color: var(--color-heading);
margin-bottom: var(--space-xs);
}
.footer-text {
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-body);
margin-bottom: var(--space-sm);
line-height: 1.5;
}
.newsletter-form {
display: flex;
gap: var(--space-xs);
}
.newsletter-input {
flex: 1;
padding: var(--space-xs) var(--space-sm);
border: 1px solid var(--color-border);
border-radius: var(--radius-input);
font-family: var(--font-body);
font-size: var(--p-text-sm);
background: var(--t-surface-raised);
color: var(--color-heading);
}
.newsletter-input::placeholder {
color: var(--color-caption);
}
.newsletter-btn {
padding: var(--space-xs) var(--space-md);
white-space: nowrap;
}
.footer-links {
display: flex;
gap: var(--space-xl);
justify-content: flex-end;
}
.footer-column {
display: flex;
flex-direction: column;
gap: var(--space-xs);
}
.footer-column-title {
font-family: var(--font-body);
font-weight: 600;
font-size: var(--p-text-sm);
color: var(--color-heading);
margin-bottom: var(--space-xs);
}
.footer-column a {
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-body);
text-decoration: none;
transition: color 0.15s ease;
}
.footer-column a:hover {
color: var(--color-heading);
}
.footer-bottom {
display: flex;
justify-content: space-between;
align-items: center;
}
.footer-bottom p {
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-caption);
margin: 0;
}
.footer-social {
display: flex;
gap: var(--space-sm);
}
.social-link {
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
color: var(--color-body);
border-radius: var(--radius-button);
transition: all 0.15s ease;
}
.social-link:hover {
color: var(--color-heading);
background: var(--t-surface-sunken);
}
.social-link svg {
width: 20px;
height: 20px;
}
/* Product images - using picsum.photos with alt views */
.product-image-primary.img-1 {
background-color: #e8e4df;
background-image: url('https://picsum.photos/seed/fern/400/400');
}
.product-image-hover.img-1-alt {
background-color: #e8e4df;
background-image: url('https://picsum.photos/seed/fern-alt/400/400');
}
.product-image-primary.img-2 {
background-color: #f5e6e0;
background-image: url('https://picsum.photos/seed/roses/400/400');
}
.product-image-hover.img-2-alt {
background-color: #f5e6e0;
background-image: url('https://picsum.photos/seed/roses-alt/400/400');
}
.product-image-primary.img-3 {
background-color: #e0e8e4;
background-image: url('https://picsum.photos/seed/morning/400/400');
}
.product-image-hover.img-3-alt {
background-color: #e0e8e4;
background-image: url('https://picsum.photos/seed/morning-alt/400/400');
}
.product-image-primary.img-4 {
background-color: #e4e8e0;
background-image: url('https://picsum.photos/seed/oak/400/400');
}
.product-image-hover.img-4-alt {
background-color: #e4e8e0;
background-image: url('https://picsum.photos/seed/oak-alt/400/400');
}
.product-image-primary.img-5 {
background-color: #f0e8e4;
background-image: url('https://picsum.photos/seed/bloom/400/400');
}
.product-image-hover.img-5-alt {
background-color: #f0e8e4;
background-image: url('https://picsum.photos/seed/bloom-alt/400/400');
}
.product-image-primary.img-6 {
background-color: #e8e0f0;
background-image: url('https://picsum.photos/seed/lavender/400/400');
}
.product-image-hover.img-6-alt {
background-color: #e8e0f0;
background-image: url('https://picsum.photos/seed/lavender-alt/400/400');
}
.icon {
width: 20px;
height: 20px;
}
.preview-frame ::selection {
background: var(--t-accent-subtle);
}
/* Page views */
.page-view {
display: none;
}
.page-view.active {
display: block;
}
/* PDP Styles */
.pdp-container {
padding: var(--space-lg);
background: var(--t-surface-base);
}
.pdp-breadcrumb {
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-caption);
margin-bottom: var(--space-lg);
display: flex;
align-items: center;
gap: var(--space-xs);
}
.pdp-breadcrumb a {
color: var(--color-body);
text-decoration: none;
}
.pdp-breadcrumb a:hover {
color: var(--color-heading);
}
.breadcrumb-sep {
color: var(--color-caption);
}
.pdp-layout {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-xl);
}
/* Gallery position */
.preview-frame[data-gallery-position="right"] .pdp-layout {
direction: rtl;
}
.preview-frame[data-gallery-position="right"] .pdp-layout > * {
direction: ltr;
}
/* PDP section visibility */
.preview-frame[data-pdp-trust="false"] .pdp-trust-badges {
display: none;
}
.preview-frame[data-pdp-reviews="false"] .pdp-reviews {
display: none;
}
.preview-frame[data-pdp-related="false"] .pdp-related {
display: none;
}
.pdp-gallery {
display: flex;
flex-direction: column;
gap: var(--space-sm);
}
.pdp-main-image {
aspect-ratio: 1;
border-radius: var(--radius-image);
overflow: hidden;
}
.pdp-thumbnails {
display: flex;
gap: var(--space-xs);
}
.pdp-thumb {
width: 64px;
height: 64px;
border-radius: var(--t-radius-sm);
cursor: pointer;
opacity: 0.6;
transition: opacity 0.15s ease;
border: 2px solid transparent;
}
.pdp-thumb:hover {
opacity: 0.8;
}
.pdp-thumb.active {
opacity: 1;
border-color: var(--color-heading);
}
/* PDP images */
.pdp-main-image {
background-color: #e8e4df;
background-size: cover;
background-position: center;
}
.pdp-thumb {
background-color: #e8e4df;
background-size: cover;
background-position: center;
}
.pdp-thumb[data-image*="fern/"] { background-image: url('https://picsum.photos/seed/fern/100/100'); }
.pdp-thumb[data-image*="fern-alt"] { background-image: url('https://picsum.photos/seed/fern-alt/100/100'); }
.pdp-thumb[data-image*="fern-detail"] { background-image: url('https://picsum.photos/seed/fern-detail/100/100'); }
.pdp-details {
display: flex;
flex-direction: column;
gap: var(--space-md);
}
.pdp-title {
font-family: var(--font-heading);
font-weight: var(--weight-heading);
font-size: var(--p-text-3xl);
letter-spacing: var(--tracking-heading);
color: var(--color-heading);
margin: 0;
}
.pdp-price {
font-family: var(--font-body);
font-size: var(--p-text-xl);
color: var(--color-heading);
font-variant-numeric: tabular-nums;
margin: 0;
}
.pdp-description {
font-family: var(--font-body);
font-size: var(--p-text-base);
color: var(--color-body);
line-height: 1.6;
}
.pdp-description p {
margin: 0;
}
.pdp-options {
display: flex;
flex-direction: column;
gap: var(--space-md);
padding: var(--space-md) 0;
border-top: 1px solid var(--color-border);
border-bottom: 1px solid var(--color-border);
}
.pdp-option-group {
display: flex;
flex-direction: column;
gap: var(--space-xs);
}
.pdp-option-label {
font-family: var(--font-body);
font-size: var(--p-text-sm);
font-weight: 500;
color: var(--color-heading);
}
.pdp-option-buttons {
display: flex;
flex-wrap: wrap;
gap: var(--space-xs);
}
.pdp-option-btn {
padding: var(--space-xs) var(--space-sm);
border: 1px solid var(--color-border);
border-radius: var(--radius-button);
background: var(--t-surface-raised);
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-body);
cursor: pointer;
transition: all 0.15s ease;
}
.pdp-option-btn:hover {
border-color: var(--color-heading);
color: var(--color-heading);
}
.pdp-option-btn.active {
border-color: var(--color-heading);
background: var(--color-heading);
color: var(--t-text-inverse);
}
.pdp-add-btn {
width: 100%;
padding: var(--space-md);
font-size: var(--p-text-base);
}
/* PDP Expandable Details */
.pdp-details-sections {
display: flex;
flex-direction: column;
border-top: 1px solid var(--color-border);
}
.pdp-details {
border-bottom: 1px solid var(--color-border);
}
.pdp-details-summary {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--space-md) 0;
cursor: pointer;
font-family: var(--font-body);
font-size: var(--p-text-sm);
font-weight: 500;
color: var(--color-heading);
list-style: none;
}
.pdp-details-summary::-webkit-details-marker {
display: none;
}
.pdp-details-summary::marker {
display: none;
}
.pdp-details-icon {
width: 18px;
height: 18px;
color: var(--color-caption);
transition: transform 0.2s ease;
}
.pdp-details[open] .pdp-details-icon {
transform: rotate(180deg);
}
.pdp-details-content {
padding-bottom: var(--space-md);
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-body);
line-height: 1.6;
}
.size-table {
width: 100%;
border-collapse: collapse;
font-size: var(--p-text-sm);
}
.size-table th,
.size-table td {
padding: var(--space-xs) var(--space-sm);
text-align: left;
border-bottom: 1px solid var(--color-border);
}
.size-table th {
font-weight: 500;
color: var(--color-heading);
background: var(--t-surface-sunken);
}
.size-table td {
color: var(--color-body);
}
.details-list {
margin: 0;
padding-left: var(--space-md);
}
.details-list li {
margin-bottom: var(--space-xs);
}
.details-list li:last-child {
margin-bottom: 0;
}
.pdp-meta {
display: flex;
flex-direction: column;
gap: var(--space-sm);
padding-top: var(--space-sm);
}
.pdp-meta-item {
display: flex;
align-items: center;
gap: var(--space-sm);
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-body);
}
.pdp-meta-icon {
width: 18px;
height: 18px;
color: var(--color-caption);
}
.pdp-trust-badges {
display: flex;
gap: var(--space-md);
padding-top: var(--space-md);
border-top: 1px solid var(--color-border);
flex-wrap: wrap;
}
.trust-badge {
display: flex;
align-items: center;
gap: var(--space-xs);
font-family: var(--font-body);
font-size: var(--p-text-xs);
color: var(--color-caption);
}
.trust-badge-icon {
width: 16px;
height: 16px;
}
.pdp-related {
padding: var(--space-xl) 0;
border-top: 1px solid var(--color-border);
margin-top: var(--space-xl);
}
/* Reviews Section */
.pdp-reviews {
padding: var(--space-xl) var(--space-lg);
background: var(--t-surface-base);
}
.reviews-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
flex-wrap: wrap;
gap: var(--space-md);
margin-bottom: var(--space-lg);
}
.reviews-summary {
display: flex;
align-items: center;
gap: var(--space-sm);
}
.reviews-stars,
.review-stars {
display: flex;
gap: 2px;
}
.star {
width: 16px;
height: 16px;
color: var(--color-border);
}
.star.filled {
color: #f59e0b;
}
.reviews-count {
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-caption);
}
.reviews-list {
display: flex;
flex-direction: column;
gap: var(--space-lg);
margin-bottom: var(--space-lg);
}
.review {
padding-bottom: var(--space-lg);
border-bottom: 1px solid var(--color-border);
}
.review:last-child {
border-bottom: none;
}
.review-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--space-xs);
}
.review-date {
font-family: var(--font-body);
font-size: var(--p-text-xs);
color: var(--color-caption);
}
.review-title {
font-family: var(--font-body);
font-size: var(--p-text-base);
font-weight: 600;
color: var(--color-heading);
margin: 0 0 var(--space-xs);
}
.review-text {
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-body);
line-height: 1.6;
margin: 0 0 var(--space-sm);
}
.review-author {
display: flex;
align-items: center;
gap: var(--space-sm);
}
.review-name {
font-family: var(--font-body);
font-size: var(--p-text-sm);
font-weight: 500;
color: var(--color-heading);
}
.review-verified {
font-family: var(--font-body);
font-size: var(--p-text-xs);
color: var(--color-caption);
background: var(--t-surface-sunken);
padding: 2px 8px;
border-radius: var(--radius-button);
}
.reviews-load-more {
display: block;
margin: 0 auto;
}
.related-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-md);
}
/* Section CTA */
.section-cta {
text-align: center;
margin-top: var(--space-lg);
}
.btn-secondary {
background: transparent;
color: var(--color-heading);
border: 1px solid var(--color-heading);
}
.btn-secondary:hover {
background: var(--color-heading);
color: var(--t-text-inverse);
}
/* Home About Section */
.home-about {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-xl);
padding: var(--space-2xl) var(--space-lg);
background: var(--t-surface-base);
}
.home-about-image {
height: 300px;
border-radius: var(--radius-image);
background: url('https://picsum.photos/seed/studio/600/400') center/cover;
}
.home-about-content {
display: flex;
flex-direction: column;
justify-content: center;
}
.home-about-content h2 {
font-family: var(--font-heading);
font-weight: var(--weight-heading);
font-size: var(--p-text-2xl);
letter-spacing: var(--tracking-heading);
color: var(--color-heading);
margin: 0 0 var(--space-md);
}
.home-about-content p {
font-family: var(--font-body);
font-size: var(--p-text-base);
color: var(--color-body);
line-height: 1.7;
margin: 0 0 var(--space-md);
}
.text-link {
font-family: var(--font-body);
font-size: var(--p-text-sm);
font-weight: 500;
color: var(--t-accent);
text-decoration: none;
}
.text-link:hover {
text-decoration: underline;
}
/* Home Categories */
.home-categories {
padding: var(--space-xl) var(--space-lg);
background: var(--t-surface-base);
}
.category-links {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-md);
max-width: 800px;
margin: 0 auto;
}
.category-link {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--space-sm);
text-decoration: none;
padding: var(--space-md);
border-radius: var(--radius-card);
transition: all 0.2s ease;
}
.category-link:hover {
background: var(--t-surface-sunken);
}
.category-link:focus {
outline: 2px solid var(--t-accent);
outline-offset: 2px;
}
.category-image {
width: 100px;
height: 100px;
border-radius: 50%;
background-size: cover;
background-position: center;
transition: transform 0.2s ease;
}
.category-link:hover .category-image {
transform: scale(1.05);
}
.cat-img-1 { background-image: url('https://picsum.photos/seed/botanicals/200/200'); }
.cat-img-2 { background-image: url('https://picsum.photos/seed/florals/200/200'); }
.cat-img-3 { background-image: url('https://picsum.photos/seed/landscapes/200/200'); }
.category-name {
font-family: var(--font-body);
font-size: var(--p-text-sm);
font-weight: 500;
color: var(--color-heading);
}
/* Utility: visually hidden but accessible */
.visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
/* Collection Page */
.collection-header {
padding: var(--space-xl) var(--space-lg) var(--space-md);
background: var(--t-surface-base);
text-align: center;
}
.collection-title {
font-family: var(--font-heading);
font-weight: var(--weight-heading);
font-size: var(--p-text-3xl);
letter-spacing: var(--tracking-heading);
color: var(--color-heading);
margin: 0 0 var(--space-xs);
}
.collection-count {
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-caption);
margin: 0;
}
.collection-toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--space-lg);
gap: var(--space-md);
flex-wrap: wrap;
}
.collection-filter {
display: flex;
gap: var(--space-xs);
}
.filter-btn {
padding: var(--space-xs) var(--space-md);
border: 1px solid var(--color-border);
border-radius: var(--radius-button);
background: var(--t-surface-raised);
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-body);
cursor: pointer;
transition: all 0.15s ease;
}
.filter-btn:hover {
border-color: var(--color-heading);
color: var(--color-heading);
}
.filter-btn.active {
background: var(--color-heading);
border-color: var(--color-heading);
color: var(--t-text-inverse);
}
.sort-select {
padding: var(--space-xs) var(--space-md);
border: 1px solid var(--color-border);
border-radius: var(--radius-button);
background: var(--t-surface-raised);
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-heading);
cursor: pointer;
}
@media (max-width: 1000px) {
.controls-panel {
position: relative;
max-height: none;
border-right: none;
border-bottom: 1px solid #e5e5e5;
}
.preview-area {
padding: var(--p-space-4);
}
.product-grid[data-grid="4"] {
grid-template-columns: repeat(2, 1fr);
}
.pdp-layout {
grid-template-columns: 1fr;
}
.related-grid {
grid-template-columns: repeat(2, 1fr);
}
.footer-content {
grid-template-columns: 1fr;
gap: var(--space-lg);
}
.footer-links {
justify-content: flex-start;
}
.cart-layout {
grid-template-columns: 1fr;
}
.contact-layout {
grid-template-columns: 1fr;
}
.home-about {
grid-template-columns: 1fr;
}
.home-about-image {
height: 200px;
}
.category-links {
grid-template-columns: repeat(3, 1fr);
gap: var(--space-sm);
}
.category-image {
width: 70px;
height: 70px;
}
}
/* Cart Page */
.cart-container {
padding: var(--space-lg);
background: var(--t-surface-base);
min-height: 60vh;
}
.cart-header-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: var(--space-lg);
}
.cart-title {
font-family: var(--font-heading);
font-weight: var(--weight-heading);
font-size: var(--p-text-3xl);
color: var(--color-heading);
margin: 0;
}
.cart-state-toggle {
background: none;
border: none;
font-family: var(--font-body);
font-size: var(--p-text-xs);
color: var(--color-caption);
cursor: pointer;
text-decoration: underline;
padding: var(--space-xs);
}
.cart-state-toggle:hover {
color: var(--color-body);
}
.cart-state[hidden] {
display: none;
}
/* Empty State */
.empty-state {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
padding: var(--space-2xl) var(--space-lg);
min-height: 40vh;
}
.empty-state-illustration {
width: 120px;
height: 120px;
margin-bottom: var(--space-lg);
color: var(--color-caption);
}
.empty-state-illustration svg {
width: 100%;
height: 100%;
}
.empty-state-title {
font-family: var(--font-heading);
font-weight: var(--weight-heading);
font-size: var(--p-text-2xl);
color: var(--color-heading);
margin: 0 0 var(--space-sm);
}
.empty-state-message {
font-family: var(--font-body);
font-size: var(--p-text-base);
color: var(--color-body);
max-width: 400px;
margin: 0 0 var(--space-lg);
line-height: 1.6;
}
/* Error Page */
.error-container {
display: flex;
align-items: center;
justify-content: center;
min-height: 70vh;
padding: var(--space-xl) var(--space-lg);
background: var(--t-surface-base);
}
.error-content {
text-align: center;
max-width: 500px;
}
.error-illustration {
width: 200px;
height: 160px;
margin: 0 auto var(--space-xl);
color: var(--t-accent);
}
.error-illustration svg {
width: 100%;
height: 100%;
}
.error-title {
font-family: var(--font-heading);
font-weight: var(--weight-heading);
font-size: var(--p-text-3xl);
letter-spacing: var(--tracking-heading);
color: var(--color-heading);
margin: 0 0 var(--space-sm);
}
.error-message {
font-family: var(--font-body);
font-size: var(--p-text-base);
color: var(--color-body);
line-height: 1.6;
margin: 0 0 var(--space-xl);
}
.error-actions {
display: flex;
gap: var(--space-sm);
justify-content: center;
flex-wrap: wrap;
}
.cart-layout {
display: grid;
grid-template-columns: 1fr 360px;
gap: var(--space-xl);
align-items: start;
}
.cart-items {
display: flex;
flex-direction: column;
gap: var(--space-md);
}
.cart-item {
display: flex;
align-items: center;
gap: var(--space-md);
padding: var(--space-md);
background: var(--t-surface-raised);
border-radius: var(--radius-card);
border: 1px solid var(--color-border);
}
.cart-item-image {
width: 80px;
height: 80px;
border-radius: var(--radius-image);
background-size: cover;
background-position: center;
flex-shrink: 0;
}
.cart-item-image.img-1 { background-image: url('https://picsum.photos/seed/fern/200/200'); }
.cart-item-image.img-2 { background-image: url('https://picsum.photos/seed/roses/200/200'); }
.cart-item-details {
flex: 1;
}
.cart-item-title {
font-family: var(--font-body);
font-weight: 500;
font-size: var(--p-text-base);
color: var(--color-heading);
margin: 0 0 4px;
}
.cart-item-variant {
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-caption);
margin: 0 0 4px;
}
.cart-item-price {
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-heading);
margin: 0;
font-variant-numeric: tabular-nums;
}
.cart-item-quantity {
display: flex;
align-items: center;
gap: var(--space-xs);
}
.qty-btn {
width: 32px;
height: 32px;
border: 1px solid var(--color-border);
border-radius: var(--radius-button);
background: var(--t-surface-raised);
color: var(--color-heading);
font-size: 16px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.15s ease;
}
.qty-btn:hover {
border-color: var(--color-heading);
}
.qty-value {
font-family: var(--font-body);
font-size: var(--p-text-base);
color: var(--color-heading);
min-width: 24px;
text-align: center;
}
.cart-item-remove {
background: none;
border: none;
padding: var(--space-xs);
color: var(--color-caption);
cursor: pointer;
transition: color 0.15s ease;
}
.cart-item-remove:hover {
color: #dc2626;
}
.cart-item-remove svg {
width: 18px;
height: 18px;
}
.cart-summary {
padding: var(--space-lg);
background: var(--t-surface-sunken);
border-radius: var(--radius-card);
}
.cart-summary-row {
display: flex;
justify-content: space-between;
padding: var(--space-sm) 0;
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-body);
border-bottom: 1px solid var(--color-border);
}
.cart-summary-row.cart-total {
font-weight: 600;
font-size: var(--p-text-base);
color: var(--color-heading);
border-bottom: none;
padding-top: var(--space-md);
}
.cart-checkout-btn {
width: 100%;
margin-top: var(--space-md);
padding: var(--space-md);
}
.cart-secure-note {
display: flex;
align-items: center;
justify-content: center;
gap: var(--space-xs);
margin-top: var(--space-md);
font-family: var(--font-body);
font-size: var(--p-text-xs);
color: var(--color-caption);
}
.cart-secure-note svg {
width: 14px;
height: 14px;
}
/* Content Pages (About/Contact) */
.content-page {
background: var(--t-surface-base);
}
.content-hero {
padding: var(--space-2xl) var(--space-lg);
text-align: center;
background: var(--t-surface-sunken);
}
.content-title {
font-family: var(--font-heading);
font-weight: var(--weight-heading);
font-size: var(--p-text-4xl);
letter-spacing: var(--tracking-heading);
color: var(--color-heading);
margin: 0 0 var(--space-xs);
}
.content-subtitle {
font-family: var(--font-body);
font-size: var(--p-text-lg);
color: var(--color-body);
margin: 0;
}
.content-body {
padding: var(--space-xl) var(--space-lg);
max-width: 800px;
margin: 0 auto;
}
.content-image {
width: 100%;
height: 300px;
border-radius: var(--radius-image);
margin-bottom: var(--space-lg);
background-size: cover;
background-position: center;
}
.about-image {
background-image: url('https://picsum.photos/seed/studio/800/400');
}
.content-text {
font-family: var(--font-body);
color: var(--color-body);
line-height: 1.7;
}
.content-text p {
margin: 0 0 var(--space-md);
}
.content-text .lead-text {
font-size: var(--p-text-lg);
color: var(--color-heading);
}
.content-text h2 {
font-family: var(--font-heading);
font-weight: var(--weight-heading);
font-size: var(--p-text-xl);
color: var(--color-heading);
margin: var(--space-lg) 0 var(--space-sm);
}
/* Contact Page */
.contact-layout {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-xl);
padding: var(--space-xl) var(--space-lg);
max-width: 1000px;
margin: 0 auto;
}
.contact-info {
display: flex;
flex-direction: column;
gap: var(--space-lg);
}
.contact-block h3 {
font-family: var(--font-heading);
font-weight: var(--weight-heading);
font-size: var(--p-text-lg);
color: var(--color-heading);
margin: 0 0 var(--space-xs);
}
.contact-block p {
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-body);
margin: 0 0 var(--space-sm);
line-height: 1.5;
}
.contact-link {
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--t-accent);
text-decoration: none;
}
.contact-link:hover {
text-decoration: underline;
}
.contact-social {
display: flex;
gap: var(--space-sm);
}
.contact-form {
display: flex;
flex-direction: column;
gap: var(--space-md);
}
.form-group {
display: flex;
flex-direction: column;
gap: var(--space-xs);
}
.form-label {
font-family: var(--font-body);
font-size: var(--p-text-sm);
font-weight: 500;
color: var(--color-heading);
}
.form-input {
padding: var(--space-sm);
border: 1px solid var(--color-border);
border-radius: var(--radius-input);
font-family: var(--font-body);
font-size: var(--p-text-base);
background: var(--t-surface-raised);
color: var(--color-heading);
transition: border-color 0.15s ease, box-shadow 0.15s ease;
}
.form-input:focus {
outline: none;
border-color: var(--t-accent);
box-shadow: 0 0 0 3px var(--t-accent-ring);
}
.form-input::placeholder {
color: var(--color-caption);
}
.form-select {
cursor: pointer;
}
.form-textarea {
resize: vertical;
min-height: 120px;
}
.form-submit {
align-self: flex-start;
padding: var(--space-sm) var(--space-xl);
}
/* Cart Drawer */
.cart-drawer {
position: fixed;
top: 0;
right: -400px;
width: 400px;
max-width: 90vw;
height: 100%;
background: var(--t-surface-raised);
z-index: 1000;
display: flex;
flex-direction: column;
transition: right 0.3s ease;
box-shadow: -4px 0 20px rgba(0,0,0,0.15);
}
.cart-drawer.open {
right: 0;
}
.cart-drawer-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: var(--space-md) var(--space-lg);
border-bottom: 1px solid var(--color-border);
}
.cart-drawer-header h2 {
font-family: var(--font-heading);
font-weight: var(--weight-heading);
font-size: var(--p-text-lg);
color: var(--color-heading);
margin: 0;
}
.cart-drawer-close {
background: none;
border: none;
padding: var(--space-xs);
cursor: pointer;
color: var(--color-body);
}
.cart-drawer-close svg {
width: 20px;
height: 20px;
}
.cart-drawer-items {
flex: 1;
overflow-y: auto;
padding: var(--space-md);
}
.cart-drawer-item {
display: flex;
gap: var(--space-sm);
padding: var(--space-sm) 0;
border-bottom: 1px solid var(--color-border);
}
.cart-drawer-item-image {
width: 60px;
height: 60px;
border-radius: var(--radius-image);
background-size: cover;
background-position: center;
flex-shrink: 0;
}
.cart-drawer-item-image.img-1 { background-image: url('https://picsum.photos/seed/fern/120/120'); }
.cart-drawer-item-image.img-2 { background-image: url('https://picsum.photos/seed/roses/120/120'); }
.cart-drawer-item-details h3 {
font-family: var(--font-body);
font-size: var(--p-text-sm);
font-weight: 500;
color: var(--color-heading);
margin: 0 0 2px;
}
.cart-drawer-item-details p {
font-family: var(--font-body);
font-size: var(--p-text-xs);
color: var(--color-caption);
margin: 0;
}
.cart-drawer-item-price {
color: var(--color-heading) !important;
font-weight: 500;
margin-top: 4px !important;
}
.cart-drawer-footer {
padding: var(--space-md) var(--space-lg);
border-top: 1px solid var(--color-border);
background: var(--t-surface-sunken);
}
.cart-drawer-total {
display: flex;
justify-content: space-between;
font-family: var(--font-body);
font-size: var(--p-text-base);
font-weight: 600;
color: var(--color-heading);
margin-bottom: var(--space-md);
}
.cart-drawer-checkout {
width: 100%;
margin-bottom: var(--space-sm);
}
.cart-drawer-link {
display: block;
text-align: center;
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-body);
text-decoration: underline;
}
.cart-drawer-overlay {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.4);
z-index: 999;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}
.cart-drawer-overlay.open {
opacity: 1;
visibility: visible;
}
/* Search Modal */
.search-modal {
position: fixed;
inset: 0;
background: rgba(0,0,0,0.5);
z-index: 1001;
display: flex;
align-items: flex-start;
justify-content: center;
padding-top: 100px;
opacity: 0;
visibility: hidden;
transition: opacity 0.2s ease, visibility 0.2s ease;
}
.search-modal.open {
opacity: 1;
visibility: visible;
}
.search-modal-content {
background: var(--t-surface-raised);
border-radius: var(--radius-card);
width: 100%;
max-width: 600px;
margin: 0 var(--space-md);
box-shadow: 0 20px 40px rgba(0,0,0,0.2);
overflow: hidden;
}
.search-input-wrapper {
display: flex;
align-items: center;
padding: var(--space-md);
gap: var(--space-sm);
border-bottom: 1px solid var(--color-border);
}
.search-icon {
width: 20px;
height: 20px;
color: var(--color-caption);
flex-shrink: 0;
}
.search-input {
flex: 1;
border: none;
background: none;
font-family: var(--font-body);
font-size: var(--p-text-lg);
color: var(--color-heading);
outline: none;
}
.search-input::placeholder {
color: var(--color-caption);
}
.search-close {
background: none;
border: none;
padding: var(--space-xs);
cursor: pointer;
color: var(--color-caption);
}
.search-close:hover {
color: var(--color-heading);
}
.search-close svg {
width: 20px;
height: 20px;
}
.search-results {
padding: var(--space-lg);
}
.search-hint {
font-family: var(--font-body);
font-size: var(--p-text-sm);
color: var(--color-caption);
text-align: center;
margin: 0;
}
/* Zoom button on PDP image */
.pdp-main-image {
position: relative;
cursor: pointer;
}
.zoom-btn {
position: absolute;
bottom: var(--space-sm);
right: var(--space-sm);
width: 40px;
height: 40px;
background: var(--t-surface-raised);
border: none;
border-radius: var(--radius-button);
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: var(--color-heading);
box-shadow: var(--shadow-sm);
transition: all 0.15s ease;
opacity: 0;
}
.pdp-main-image:hover .zoom-btn,
.zoom-btn:focus {
opacity: 1;
}
.zoom-btn:hover {
background: var(--color-heading);
color: var(--t-text-inverse);
}
.zoom-btn svg {
width: 20px;
height: 20px;
}
/* Lightbox - using native dialog */
.lightbox {
position: fixed;
inset: 0;
width: 100%;
height: 100%;
max-width: 100%;
max-height: 100%;
background: rgba(0, 0, 0, 0.95);
border: none;
padding: 0;
z-index: 1002;
display: flex;
align-items: center;
justify-content: center;
}
.lightbox::backdrop {
background: rgba(0, 0, 0, 0.95);
}
.lightbox:not([open]) {
display: none;
}
.lightbox-content {
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.lightbox-close {
position: absolute;
top: var(--space-md);
right: var(--space-md);
width: 44px;
height: 44px;
background: transparent;
border: none;
cursor: pointer;
color: white;
display: flex;
align-items: center;
justify-content: center;
border-radius: var(--radius-button);
transition: background 0.15s ease;
z-index: 1;
}
.lightbox-close:hover {
background: rgba(255, 255, 255, 0.1);
}
.lightbox-close svg {
width: 24px;
height: 24px;
}
.lightbox-nav {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 50px;
height: 50px;
background: rgba(255, 255, 255, 0.1);
border: none;
cursor: pointer;
color: white;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
transition: background 0.15s ease;
}
.lightbox-nav:hover {
background: rgba(255, 255, 255, 0.2);
}
.lightbox-nav svg {
width: 24px;
height: 24px;
}
.lightbox-prev {
left: var(--space-md);
}
.lightbox-next {
right: var(--space-md);
}
.lightbox-image-container {
max-width: 90vw;
max-height: 75vh;
display: flex;
align-items: center;
justify-content: center;
}
.lightbox-image {
max-width: 100%;
max-height: 75vh;
object-fit: contain;
border-radius: var(--radius-image);
}
.lightbox-figure {
margin: 0;
display: flex;
flex-direction: column;
align-items: center;
gap: var(--space-md);
}
.lightbox-caption {
color: rgba(255, 255, 255, 0.8);
font-family: var(--font-body);
font-size: var(--p-text-sm);
text-align: center;
max-width: 500px;
line-height: 1.5;
}
.lightbox-counter {
position: absolute;
bottom: var(--space-md);
left: 50%;
transform: translateX(-50%);
color: rgba(255, 255, 255, 0.7);
font-family: var(--font-body);
font-size: var(--p-text-sm);
}
< / style >
< / head >
< body >
< div class = "page-layout" >
<!-- Controls Panel -->
< div class = "controls-panel" >
< div class = "controls-header" >
< h1 > Theme Studio< / h1 >
< p > One theme, infinite possibilities. Every combination is designed to work beautifully.< / p >
< / div >
<!-- Shop Name -->
< div class = "control-section" >
< label class = "control-label" > Shop name< / label >
< input type = "text" class = "text-input" id = "shop-name" value = "Botanical Studio" placeholder = "Your shop name" >
< / div >
<!-- Branding Section -->
< div class = "branding-section" >
< label class = "control-label" > Logo & header< / label >
< div class = "logo-mode-options" >
< label class = "logo-mode-option active" data-mode = "text-only" >
< input type = "radio" name = "logo-mode" value = "text-only" checked >
< span class = "logo-mode-radio" > < / span >
< div class = "logo-mode-content" >
< div class = "logo-mode-title" > Shop name only< / div >
< div class = "logo-mode-desc" > Your name in the heading font< / div >
< / div >
< / label >
< label class = "logo-mode-option" data-mode = "logo-and-text" >
< input type = "radio" name = "logo-mode" value = "logo-and-text" >
< span class = "logo-mode-radio" > < / span >
< div class = "logo-mode-content" >
< div class = "logo-mode-title" > Logo + shop name< / div >
< div class = "logo-mode-desc" > Your logo image with name beside it< / div >
< / div >
< / label >
< label class = "logo-mode-option" data-mode = "logo-only" >
< input type = "radio" name = "logo-mode" value = "logo-only" >
< span class = "logo-mode-radio" > < / span >
< div class = "logo-mode-content" >
< div class = "logo-mode-title" > Logo only< / div >
< div class = "logo-mode-desc" > Just your logo (with text built in)< / div >
< / div >
< / label >
< label class = "logo-mode-option" data-mode = "header-image" >
< input type = "radio" name = "logo-mode" value = "header-image" >
< span class = "logo-mode-radio" > < / span >
< div class = "logo-mode-content" >
< div class = "logo-mode-title" > Header background< / div >
< div class = "logo-mode-desc" > Full-width image with name overlay< / div >
< / div >
< / label >
< label class = "logo-mode-option" data-mode = "logo-header-image" >
< input type = "radio" name = "logo-mode" value = "logo-header-image" >
< span class = "logo-mode-radio" > < / span >
< div class = "logo-mode-content" >
< div class = "logo-mode-title" > Logo + header background< / div >
< div class = "logo-mode-desc" > Your logo (use white/light version) on a full-width image< / div >
< / div >
< / label >
< / div >
< div class = "upload-section" >
<!-- Logo upload (for logo - and - text and logo - only modes) -->
< div class = "upload-row" id = "logo-upload-row" >
< span class = "upload-label" id = "logo-upload-label" > Upload logo (SVG or PNG)< / span >
< div class = "upload-area" >
< label class = "upload-btn" >
< span > Choose file...< / span >
< input type = "file" id = "logo-upload" accept = ".svg,.png,.jpg,.jpeg,.webp" >
< / label >
< div class = "upload-preview" id = "logo-preview" >
< img id = "logo-preview-img" src = "" alt = "Logo preview" >
< button class = "remove-upload" id = "remove-logo" > × < / button >
< / div >
< / div >
< div class = "size-control" id = "logo-size-control" >
< div class = "size-control-header" >
< span class = "size-control-label" > Logo size< / span >
< span class = "size-control-value" id = "logo-size-value" > 36px< / span >
< / div >
< input type = "range" class = "size-slider" id = "logo-size-slider" min = "24" max = "120" value = "36" >
< / div >
< div class = "logo-color-control" id = "logo-color-control" >
< div class = "logo-color-toggle" >
< label class = "toggle-label" >
< input type = "checkbox" id = "recolor-logo-checkbox" >
< span class = "toggle-switch" > < / span >
< span class = "toggle-text" > Recolour logo< / span >
< / label >
< / div >
< div class = "logo-color-picker-row" id = "logo-color-picker-row" >
< input type = "color" class = "color-picker color-picker-small" id = "logo-color-picker" value = "#171717" >
< span class = "color-value" id = "logo-color-value" > #171717< / span >
< / div >
< / div >
< / div >
<!-- Header background upload -->
< div class = "upload-row" id = "header-upload-row" >
< span class = "upload-label" > Upload header image< / span >
< div class = "upload-area" >
< label class = "upload-btn" >
< span > Choose file...< / span >
< input type = "file" id = "header-upload" accept = ".jpg,.jpeg,.png,.webp" >
< / label >
< / div >
< div class = "upload-preview upload-preview-wide" id = "header-preview" >
< img id = "header-preview-img" src = "" alt = "Header preview" >
< button class = "remove-upload" id = "remove-header" > × < / button >
< / div >
< div class = "header-image-controls" id = "header-image-controls" >
< div class = "control-row" >
< div class = "size-control-header" >
< span class = "size-control-label" > Zoom< / span >
< span class = "size-control-value" id = "header-zoom-value" > 100%< / span >
< / div >
< input type = "range" class = "size-slider" id = "header-zoom-slider" min = "100" max = "200" value = "100" >
< / div >
< div class = "control-row" id = "header-position-x-row" >
< div class = "size-control-header" >
< span class = "size-control-label" > Horizontal position< / span >
< span class = "size-control-value" id = "header-position-x-value" > 50%< / span >
< / div >
< input type = "range" class = "size-slider" id = "header-position-x-slider" min = "0" max = "100" value = "50" >
< / div >
< div class = "control-row" >
< div class = "size-control-header" >
< span class = "size-control-label" > Vertical position< / span >
< span class = "size-control-value" id = "header-position-value" > 50%< / span >
< / div >
< input type = "range" class = "size-slider" id = "header-position-slider" min = "0" max = "100" value = "50" >
< / div >
< / div >
< / div >
< / div >
< / div >
<!-- Presets -->
< div class = "control-section" >
< label class = "control-label" > Start with a preset< / label >
< div class = "preset-grid" id = "preset-grid" >
< button class = "preset-button active" data-preset = "gallery" >
< div class = "preset-name" > Gallery< / div >
< div class = "preset-desc" > Warm & spacious< / div >
< / button >
< button class = "preset-button" data-preset = "studio" >
< div class = "preset-name" > Studio< / div >
< div class = "preset-desc" > Clean & balanced< / div >
< / button >
< button class = "preset-button" data-preset = "boutique" >
< div class = "preset-name" > Boutique< / div >
< div class = "preset-desc" > Classic & refined< / div >
< / button >
< button class = "preset-button" data-preset = "bold" >
< div class = "preset-name" > Bold< / div >
< div class = "preset-desc" > Sharp & modern< / div >
< / button >
< button class = "preset-button" data-preset = "playful" >
< div class = "preset-name" > Playful< / div >
< div class = "preset-desc" > Friendly & round< / div >
< / button >
< button class = "preset-button" data-preset = "minimal" >
< div class = "preset-name" > Minimal< / div >
< div class = "preset-desc" > Cool & focused< / div >
< / button >
< button class = "preset-button" data-preset = "night" >
< div class = "preset-name" > Night< / div >
< div class = "preset-desc" > Dark & premium< / div >
< / button >
< button class = "preset-button" data-preset = "classic" >
< div class = "preset-name" > Classic< / div >
< div class = "preset-desc" > Timeless elegance< / div >
< / button >
< button class = "preset-button" data-preset = "impulse" >
< div class = "preset-name" > Impulse< / div >
< div class = "preset-desc" > Fashion editorial< / div >
< / button >
< / div >
< / div >
<!-- Accent Color - stays in essentials -->
< div class = "control-section" >
< label class = "control-label" > Accent colour< / label >
< div class = "color-picker-wrapper" >
< input type = "color" class = "color-picker" id = "accent-picker" value = "#f97316" >
< span class = "color-value" id = "color-value" > #f97316< / span >
< / div >
< / div >
<!-- Customise Section (collapsible) -->
< div class = "customise-section" id = "customise-section" >
< div class = "customise-header" id = "customise-header" >
< span class = "customise-title" > Customise< / span >
< svg class = "customise-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< polyline points = "6 9 12 15 18 9" > < / polyline >
< / svg >
< / div >
< div class = "customise-content" >
<!-- Typography Group -->
< div class = "control-group" >
< div class = "control-group-header" >
< svg class = "control-group-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< polyline points = "4 7 4 4 20 4 20 7" > < / polyline >
< line x1 = "9" y1 = "20" x2 = "15" y2 = "20" > < / line >
< line x1 = "12" y1 = "4" x2 = "12" y2 = "20" > < / line >
< / svg >
< span class = "control-group-title" > Typography< / span >
< / div >
< div class = "control-section" >
< label class = "control-label" > Font style< / label >
< div class = "option-group" id = "typography-options" >
< button class = "option-button active" data-typography = "clean" > Clean< / button >
< button class = "option-button" data-typography = "editorial" > Editorial< / button >
< button class = "option-button" data-typography = "modern" > Modern< / button >
< button class = "option-button" data-typography = "classic" > Classic< / button >
< button class = "option-button" data-typography = "friendly" > Friendly< / button >
< button class = "option-button" data-typography = "minimal" > Minimal< / button >
< / div >
< / div >
< div class = "control-section" >
< label class = "control-label" > Font size< / label >
< div class = "option-group" id = "font-size-options" >
< button class = "option-button" data-font-size = "small" > Small< / button >
< button class = "option-button active" data-font-size = "medium" > Medium< / button >
< button class = "option-button" data-font-size = "large" > Large< / button >
< / div >
< / div >
< div class = "control-section" >
< label class = "control-label" > Heading weight< / label >
< div class = "option-group" id = "heading-weight-options" >
< button class = "option-button" data-heading-weight = "regular" > Regular< / button >
< button class = "option-button" data-heading-weight = "medium" > Medium< / button >
< button class = "option-button active" data-heading-weight = "bold" > Bold< / button >
< / div >
< / div >
< / div >
<!-- Colours Group -->
< div class = "control-group" >
< div class = "control-group-header" >
< svg class = "control-group-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< circle cx = "12" cy = "12" r = "10" > < / circle >
< circle cx = "12" cy = "12" r = "3" > < / circle >
< / svg >
< span class = "control-group-title" > Colours< / span >
< / div >
< div class = "control-section" >
< label class = "control-label" > Colour mood< / label >
< div class = "option-group" id = "mood-options" >
< button class = "option-button" data-mood = "warm" > Warm< / button >
< button class = "option-button active" data-mood = "neutral" > Neutral< / button >
< button class = "option-button" data-mood = "cool" > Cool< / button >
< button class = "option-button" data-mood = "dark" > Dark< / button >
< / div >
< / div >
< div class = "control-section" >
< label class = "control-label" > Hover colour< / label >
< div class = "color-picker-wrapper" >
< input type = "color" class = "color-picker" id = "secondary-accent-picker" value = "#ea580c" >
< span class = "color-value" id = "secondary-color-value" > #ea580c< / span >
< / div >
< / div >
< div class = "control-section" >
< label class = "control-label" > Sale badge colour< / label >
< div class = "color-picker-wrapper" >
< input type = "color" class = "color-picker" id = "sale-color-picker" value = "#dc2626" >
< span class = "color-value" id = "sale-color-value" > #dc2626< / span >
< / div >
< / div >
< / div >
<!-- Layout Group -->
< div class = "control-group" >
< div class = "control-group-header" >
< svg class = "control-group-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< rect x = "3" y = "3" width = "18" height = "18" rx = "2" ry = "2" > < / rect >
< line x1 = "3" y1 = "9" x2 = "21" y2 = "9" > < / line >
< line x1 = "9" y1 = "21" x2 = "9" y2 = "9" > < / line >
< / svg >
< span class = "control-group-title" > Layout< / span >
< / div >
< div class = "control-section" >
< label class = "control-label" > Layout width< / label >
< div class = "option-group" id = "layout-width-options" >
< button class = "option-button" data-layout-width = "contained" > Contained< / button >
< button class = "option-button active" data-layout-width = "wide" > Wide< / button >
< button class = "option-button" data-layout-width = "full" > Full width< / button >
< / div >
< / div >
< div class = "control-section" >
< label class = "control-label" > Density< / label >
< div class = "option-group" id = "density-options" >
< button class = "option-button" data-density = "spacious" > Spacious< / button >
< button class = "option-button active" data-density = "balanced" > Balanced< / button >
< button class = "option-button" data-density = "compact" > Compact< / button >
< / div >
< / div >
< div class = "control-section" >
< label class = "control-label" > Corner style< / label >
< div class = "option-group" id = "shape-options" >
< button class = "option-button" data-shape = "sharp" > Sharp< / button >
< button class = "option-button active" data-shape = "soft" > Soft< / button >
< button class = "option-button" data-shape = "round" > Round< / button >
< button class = "option-button" data-shape = "pill" > Pill< / button >
< / div >
< / div >
< div class = "control-section" >
< label class = "control-label" > Button style< / label >
< div class = "option-group" id = "button-style-options" >
< button class = "option-button active" data-button-style = "filled" > Filled< / button >
< button class = "option-button" data-button-style = "outline" > Outline< / button >
< button class = "option-button" data-button-style = "soft" > Soft< / button >
< / div >
< / div >
< / div >
<!-- Product Cards Group -->
< div class = "control-group" >
< div class = "control-group-header" >
< svg class = "control-group-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< rect x = "2" y = "3" width = "20" height = "14" rx = "2" ry = "2" > < / rect >
< line x1 = "8" y1 = "21" x2 = "16" y2 = "21" > < / line >
< line x1 = "12" y1 = "17" x2 = "12" y2 = "21" > < / line >
< / svg >
< span class = "control-group-title" > Product cards< / span >
< / div >
< div class = "control-section" >
< label class = "control-label" > Products per row< / label >
< div class = "option-group" id = "grid-options" >
< button class = "option-button" data-grid = "2" > 2< / button >
< button class = "option-button" data-grid = "3" > 3< / button >
< button class = "option-button active" data-grid = "4" > 4< / button >
< / div >
< / div >
< div class = "control-section" >
< label class = "control-label" > Image ratio< / label >
< div class = "option-group" id = "aspect-options" >
< button class = "option-button active" data-aspect = "square" > Square< / button >
< button class = "option-button" data-aspect = "portrait" > Portrait< / button >
< button class = "option-button" data-aspect = "landscape" > Landscape< / button >
< / div >
< / div >
< div class = "control-section" >
< label class = "control-label" > Text alignment< / label >
< div class = "option-group" id = "product-text-options" >
< button class = "option-button active" data-product-text = "left" > Left< / button >
< button class = "option-button" data-product-text = "center" > Center< / button >
< / div >
< / div >
< div class = "control-section" >
< label class = "control-label" > Card shadow< / label >
< div class = "option-group" id = "card-shadow-options" >
< button class = "option-button active" data-card-shadow = "none" > None< / button >
< button class = "option-button" data-card-shadow = "subtle" > Subtle< / button >
< button class = "option-button" data-card-shadow = "pronounced" > Pronounced< / button >
< / div >
< / div >
< div class = "control-section" >
< label class = "control-label" > Features< / label >
< div class = "toggle-list" >
< label class = "toggle-label" >
< input type = "checkbox" id = "hover-image-toggle" checked >
< span class = "toggle-switch" > < / span >
< span class = "toggle-text" > Second image on hover< / span >
< / label >
< label class = "toggle-label" >
< input type = "checkbox" id = "quick-add-toggle" checked >
< span class = "toggle-switch" > < / span >
< span class = "toggle-text" > Quick add button< / span >
< / label >
< label class = "toggle-label" >
< input type = "checkbox" id = "show-prices-toggle" checked >
< span class = "toggle-switch" > < / span >
< span class = "toggle-text" > Show prices< / span >
< / label >
< / div >
< / div >
< / div >
<!-- Product Page Group -->
< div class = "control-group" >
< div class = "control-group-header" >
< svg class = "control-group-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< path d = "M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" > < / path >
< polyline points = "14 2 14 8 20 8" > < / polyline >
< / svg >
< span class = "control-group-title" > Product page< / span >
< / div >
< div class = "control-section" >
< label class = "control-label" > Gallery position< / label >
< div class = "option-group" id = "gallery-position-options" >
< button class = "option-button active" data-gallery-position = "left" > Left< / button >
< button class = "option-button" data-gallery-position = "right" > Right< / button >
< / div >
< / div >
< div class = "control-section" >
< label class = "control-label" > Sections< / label >
< div class = "toggle-list" >
< label class = "toggle-label" >
< input type = "checkbox" id = "pdp-trust-toggle" checked >
< span class = "toggle-switch" > < / span >
< span class = "toggle-text" > Trust badges< / span >
< / label >
< label class = "toggle-label" >
< input type = "checkbox" id = "pdp-reviews-toggle" checked >
< span class = "toggle-switch" > < / span >
< span class = "toggle-text" > Reviews section< / span >
< / label >
< label class = "toggle-label" >
< input type = "checkbox" id = "pdp-related-toggle" checked >
< span class = "toggle-switch" > < / span >
< span class = "toggle-text" > Related products< / span >
< / label >
< / div >
< / div >
< / div >
<!-- Header Group -->
< div class = "control-group" >
< div class = "control-group-header" >
< svg class = "control-group-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< rect x = "3" y = "3" width = "18" height = "18" rx = "2" ry = "2" > < / rect >
< line x1 = "3" y1 = "9" x2 = "21" y2 = "9" > < / line >
< / svg >
< span class = "control-group-title" > Header< / span >
< / div >
< div class = "control-section" >
< label class = "control-label" > Layout< / label >
< div class = "option-group" id = "header-options" >
< button class = "option-button active" data-header = "standard" > Standard< / button >
< button class = "option-button" data-header = "centered" > Centered< / button >
< button class = "option-button" data-header = "minimal" > Minimal< / button >
< / div >
< / div >
< div class = "control-section" >
< label class = "control-label" > Features< / label >
< div class = "toggle-list" >
< label class = "toggle-label" >
< input type = "checkbox" id = "announcement-bar-toggle" checked >
< span class = "toggle-switch" > < / span >
< span class = "toggle-text" > Announcement bar< / span >
< / label >
< label class = "toggle-label" >
< input type = "checkbox" id = "sticky-header-toggle" >
< span class = "toggle-switch" > < / span >
< span class = "toggle-text" > Sticky header< / span >
< / label >
< / div >
< / div >
< / div >
< / div >
< / div >
<!-- Current combination -->
< div class = "combo-display" >
< div class = "combo-label" > Current combination< / div >
< div class = "combo-value" id = "combo-value" > Neutral · Clean · Soft · Balanced · 4-up · Standard< / div >
< div class = "combo-count" > One of 100,000+ possible combinations< / div >
< / div >
< / div >
<!-- Preview Area -->
< div class = "preview-area" >
< div class = "preview-container" >
< div class = "page-toggle" >
< button class = "page-toggle-btn active" data-page = "home" > Home< / button >
< button class = "page-toggle-btn" data-page = "collection" > Collection< / button >
< button class = "page-toggle-btn" data-page = "pdp" > Product< / button >
< button class = "page-toggle-btn" data-page = "cart" > Cart< / button >
< button class = "page-toggle-btn" data-page = "about" > About< / button >
< button class = "page-toggle-btn" data-page = "contact" > Contact< / button >
< button class = "page-toggle-btn" data-page = "error" > Error< / button >
< / div >
< div class = "browser-chrome" >
< div class = "browser-traffic-lights" >
< span class = "traffic-light red" > < / span >
< span class = "traffic-light yellow" > < / span >
< span class = "traffic-light green" > < / span >
< / div >
< div class = "browser-nav" >
< button class = "browser-nav-btn" aria-label = "Back" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" > < path d = "M15 18l-6-6 6-6" / > < / svg >
< / button >
< button class = "browser-nav-btn" aria-label = "Forward" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" > < path d = "M9 18l6-6-6-6" / > < / svg >
< / button >
< / div >
< div class = "browser-url-bar" >
< svg class = "browser-url-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< rect x = "3" y = "11" width = "18" height = "11" rx = "2" ry = "2" / >
< path d = "M7 11V7a5 5 0 0 1 10 0v4" / >
< / svg >
< div class = "browser-favicon" id = "browser-favicon" >
< span class = "favicon-letter" id = "favicon-letter" > B< / span >
< / div >
2026-02-18 21:23:15 +00:00
< span class = "browser-url-text" > < span class = "domain" id = "browser-domain" > botanicalstudio< / span > .berrypod.uk< / span >
2026-01-01 16:17:05 +00:00
< / div >
< div class = "browser-actions" >
< button class = "browser-action-btn" aria-label = "Share" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< path d = "M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8" / >
< polyline points = "16 6 12 2 8 6" / >
< line x1 = "12" y1 = "2" x2 = "12" y2 = "15" / >
< / svg >
< / button >
< / div >
< / div >
< div class = "preview-frame" id = "preview-frame"
data-mood="neutral"
data-typography="clean"
data-shape="soft"
data-density="balanced">
<!-- Announcement Bar -->
< div class = "announcement-bar" id = "announcement-bar" >
< p > Free UK shipping on orders over £50 ✨< / p >
< / div >
<!-- Shop Header -->
< header class = "shop-header" id = "shop-header" data-header = "standard" data-logo-mode = "text-only" >
< div class = "shop-logo" id = "shop-logo" >
< div class = "shop-logo-image" id = "shop-logo-image" >
< img id = "shop-logo-img" src = "" alt = "" >
< / div >
< span class = "shop-logo-text" id = "shop-logo-text" > Botanical Studio< / span >
< / div >
< nav class = "shop-nav" >
< a href = "#" data-nav = "home" > Home< / a >
< a href = "#" data-nav = "collection" > Shop All< / a >
< a href = "#" data-nav = "about" > About< / a >
< a href = "#" data-nav = "contact" > Contact< / a >
< / nav >
< div class = "shop-cart" >
< button class = "header-icon-btn search-btn" id = "search-btn" aria-label = "Search" >
< svg class = "icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< circle cx = "11" cy = "11" r = "8" > < / circle >
< path d = "M21 21l-4.35-4.35" > < / path >
< / svg >
< / button >
< button class = "header-icon-btn cart-btn" id = "cart-btn" aria-label = "Cart" >
< svg class = "icon cart-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< path d = "M6 2L3 6v14a2 2 0 002 2h14a2 2 0 002-2V6l-3-4z" > < / path >
< line x1 = "3" y1 = "6" x2 = "21" y2 = "6" > < / line >
< path d = "M16 10a4 4 0 01-8 0" > < / path >
< / svg >
< span class = "cart-count" > 2< / span >
< / button >
< / div >
< / header >
<!-- Home View -->
< div class = "page-view home-view active" id = "home-view" >
<!-- Hero -->
< section class = "shop-hero" >
< h1 id = "hero-title" > Nature-inspired art prints< / h1 >
< p > Original botanical illustrations, printed on premium archival paper. Each piece brings a little bit of the outside, inside.< / p >
< button class = "btn btn-primary" id = "shop-collection-btn" data-nav = "collection" > Shop the collection< / button >
< / section >
<!-- Categories -->
< section class = "home-categories" >
< nav class = "category-links" aria-label = "Product categories" >
< a href = "#" class = "category-link" data-nav = "collection" >
< div class = "category-image cat-img-1" > < / div >
< span class = "category-name" > Botanicals< / span >
< / a >
< a href = "#" class = "category-link" data-nav = "collection" >
< div class = "category-image cat-img-2" > < / div >
< span class = "category-name" > Florals< / span >
< / a >
< a href = "#" class = "category-link" data-nav = "collection" >
< div class = "category-image cat-img-3" > < / div >
< span class = "category-name" > Landscapes< / span >
< / a >
< / nav >
< / section >
<!-- Featured Products -->
< section class = "product-section" >
< h2 class = "section-title" > Featured prints< / h2 >
< div class = "product-grid" id = "product-grid" data-grid = "4" >
< article class = "product-card" >
< div class = "product-image" >
< span class = "product-badge badge-new" > New< / span >
< div class = "product-image-primary img-1" > < / div >
< div class = "product-image-hover img-1-alt" > < / div >
< button class = "quick-add-btn" > Quick add< / button >
< / div >
< div class = "product-info" >
< h3 class = "product-title" > Autumn Fern< / h3 >
< p class = "product-price" > £24.00< / p >
< / div >
< / article >
< article class = "product-card" >
< div class = "product-image" >
< span class = "product-badge badge-sale" > Sale< / span >
< div class = "product-image-primary img-2" > < / div >
< div class = "product-image-hover img-2-alt" > < / div >
< button class = "quick-add-btn" > Quick add< / button >
< / div >
< div class = "product-info" >
< h3 class = "product-title" > Wild Roses< / h3 >
< p class = "product-price" > < span class = "price-was" > £35.00< / span > £28.00< / p >
< / div >
< / article >
< article class = "product-card" >
< div class = "product-image" >
< div class = "product-image-primary img-3" > < / div >
< div class = "product-image-hover img-3-alt" > < / div >
< button class = "quick-add-btn" > Quick add< / button >
< / div >
< div class = "product-info" >
< h3 class = "product-title" > Morning Dew< / h3 >
< p class = "product-price" > £24.00< / p >
< / div >
< / article >
< article class = "product-card" >
< div class = "product-image" >
< div class = "product-image-primary img-5" > < / div >
< div class = "product-image-hover img-5-alt" > < / div >
< button class = "quick-add-btn" > Quick add< / button >
< / div >
< div class = "product-info" >
< h3 class = "product-title" > Spring Bloom< / h3 >
< p class = "product-price" > £28.00< / p >
< / div >
< / article >
< / div >
< div class = "section-cta" >
< button class = "btn btn-secondary" data-nav = "collection" > View all products< / button >
< / div >
< / section >
<!-- About Snippet -->
< section class = "home-about" >
< div class = "home-about-image" > < / div >
< div class = "home-about-content" >
< h2 > Made with care in Yorkshire< / h2 >
< p > Every illustration starts as a pencil sketch, inspired by the plants and flowers found in British woodlands, meadows, and gardens. Printed on museum-quality archival paper using pigment-based inks that will last a lifetime.< / p >
< a href = "#" class = "text-link" data-nav = "about" > Learn more about the studio →< / a >
< / div >
< / section >
<!-- Footer -->
< footer class = "shop-footer" id = "home-footer" >
< div class = "footer-content" >
< div class = "footer-newsletter" >
< h3 class = "footer-heading" > Join the studio< / h3 >
< p class = "footer-text" > Get 10% off your first order and be the first to know about new prints.< / p >
< form class = "newsletter-form" >
< input type = "email" class = "newsletter-input" placeholder = "your@email.com" >
< button type = "submit" class = "btn btn-primary newsletter-btn" > Subscribe< / button >
< / form >
< / div >
< div class = "footer-links" >
< div class = "footer-column" >
< h4 class = "footer-column-title" > Shop< / h4 >
< a href = "#" data-nav = "collection" > All products< / a >
< a href = "#" data-nav = "collection" > New arrivals< / a >
< a href = "#" data-nav = "collection" > Best sellers< / a >
< / div >
< div class = "footer-column" >
< h4 class = "footer-column-title" > Help< / h4 >
< a href = "#" data-nav = "contact" > Shipping< / a >
< a href = "#" data-nav = "contact" > Returns< / a >
< a href = "#" data-nav = "contact" > Contact< / a >
< / div >
< / div >
< / div >
< div class = "footer-bottom" >
< p id = "footer-text" > © 2025 Botanical Studio< / p >
< div class = "footer-social" >
< a href = "#" class = "social-link" aria-label = "Instagram" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< rect x = "2" y = "2" width = "20" height = "20" rx = "5" ry = "5" > < / rect >
< path d = "M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z" > < / path >
< line x1 = "17.5" y1 = "6.5" x2 = "17.51" y2 = "6.5" > < / line >
< / svg >
< / a >
< a href = "#" class = "social-link" aria-label = "Pinterest" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< circle cx = "12" cy = "12" r = "10" > < / circle >
< path d = "M8 12c0-2.2 1.8-4 4-4s4 1.8 4 4-1.8 4-4 4" > < / path >
< line x1 = "12" y1 = "16" x2 = "9" y2 = "21" > < / line >
< / svg >
< / a >
< / div >
< / div >
< / footer >
< / div >
<!-- Collection View -->
< div class = "page-view collection-view" id = "collection-view" >
< div class = "collection-header" >
< h1 class = "collection-title" > Shop All< / h1 >
< p class = "collection-count" > 12 products< / p >
< / div >
< section class = "product-section" >
< div class = "collection-toolbar" >
< div class = "collection-filter" role = "group" aria-label = "Filter by category" >
< button class = "filter-btn active" > All< / button >
< button class = "filter-btn" > Botanicals< / button >
< button class = "filter-btn" > Florals< / button >
< button class = "filter-btn" > Landscapes< / button >
< / div >
< div class = "collection-sort" >
< label for = "sort-select" class = "visually-hidden" > Sort products< / label >
< select id = "sort-select" class = "sort-select" >
< option > Featured< / option >
< option > Price: Low to high< / option >
< option > Price: High to low< / option >
< option > Newest< / option >
< / select >
< / div >
< / div >
< div class = "product-grid" data-grid = "4" >
< article class = "product-card" >
< div class = "product-image" >
< span class = "product-badge badge-new" > New< / span >
< div class = "product-image-primary img-1" > < / div >
< div class = "product-image-hover img-1-alt" > < / div >
< button class = "quick-add-btn" > Quick add< / button >
< / div >
< div class = "product-info" >
< h3 class = "product-title" > Autumn Fern< / h3 >
< p class = "product-price" > £24.00< / p >
< / div >
< / article >
< article class = "product-card" >
< div class = "product-image" >
< span class = "product-badge badge-sale" > Sale< / span >
< div class = "product-image-primary img-2" > < / div >
< div class = "product-image-hover img-2-alt" > < / div >
< button class = "quick-add-btn" > Quick add< / button >
< / div >
< div class = "product-info" >
< h3 class = "product-title" > Wild Roses< / h3 >
< p class = "product-price" > < span class = "price-was" > £35.00< / span > £28.00< / p >
< / div >
< / article >
< article class = "product-card" >
< div class = "product-image" >
< div class = "product-image-primary img-3" > < / div >
< div class = "product-image-hover img-3-alt" > < / div >
< button class = "quick-add-btn" > Quick add< / button >
< / div >
< div class = "product-info" >
< h3 class = "product-title" > Morning Dew< / h3 >
< p class = "product-price" > £24.00< / p >
< / div >
< / article >
< article class = "product-card" >
< div class = "product-image" >
< span class = "product-badge badge-sold" > Sold out< / span >
< div class = "product-image-primary img-4" > < / div >
< div class = "product-image-hover img-4-alt" > < / div >
< / div >
< div class = "product-info" >
< h3 class = "product-title" > Oak Leaves< / h3 >
< p class = "product-price" > £32.00< / p >
< / div >
< / article >
< article class = "product-card" >
< div class = "product-image" >
< div class = "product-image-primary img-5" > < / div >
< div class = "product-image-hover img-5-alt" > < / div >
< button class = "quick-add-btn" > Quick add< / button >
< / div >
< div class = "product-info" >
< h3 class = "product-title" > Spring Bloom< / h3 >
< p class = "product-price" > £28.00< / p >
< / div >
< / article >
< article class = "product-card" >
< div class = "product-image" >
< div class = "product-image-primary img-6" > < / div >
< div class = "product-image-hover img-6-alt" > < / div >
< button class = "quick-add-btn" > Quick add< / button >
< / div >
< div class = "product-info" >
< h3 class = "product-title" > Lavender Fields< / h3 >
< p class = "product-price" > £24.00< / p >
< / div >
< / article >
< / div >
< / section >
< footer class = "shop-footer" id = "collection-footer" >
< div class = "footer-bottom" >
< p id = "footer-text-collection" > © 2025 Botanical Studio< / p >
< / div >
< / footer >
< / div >
<!-- PDP View -->
< div class = "page-view pdp-view" id = "pdp-view" >
< div class = "pdp-container" >
< nav class = "pdp-breadcrumb" >
< a href = "#" data-nav = "home" > Home< / a >
< span class = "breadcrumb-sep" > › < / span >
< a href = "#" data-nav = "collection" > Shop All< / a >
< span class = "breadcrumb-sep" > › < / span >
< span > Autumn Fern< / span >
< / nav >
< div class = "pdp-layout" >
< div class = "pdp-gallery" >
< div class = "pdp-main-image" id = "pdp-main-image" style = "background-image: url('https://picsum.photos/seed/fern/600/600')" >
< button class = "zoom-btn" id = "zoom-btn" aria-label = "View larger image" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< circle cx = "11" cy = "11" r = "8" > < / circle >
< path d = "M21 21l-4.35-4.35" > < / path >
< line x1 = "11" y1 = "8" x2 = "11" y2 = "14" > < / line >
< line x1 = "8" y1 = "11" x2 = "14" y2 = "11" > < / line >
< / svg >
< / button >
< / div >
< div class = "pdp-thumbnails" >
< div class = "pdp-thumb active"
data-image="https://picsum.photos/seed/fern/600/600"
data-alt="Autumn Fern botanical illustration - front view"
data-description="Delicate hand-drawn fern fronds in graphite and watercolour, showing the full composition">< / div >
< div class = "pdp-thumb"
data-image="https://picsum.photos/seed/fern-alt/600/600"
data-alt="Autumn Fern botanical illustration - detail view"
data-description="Close-up detail showing the intricate vein patterns and subtle colour gradations">< / div >
< div class = "pdp-thumb"
data-image="https://picsum.photos/seed/fern-detail/600/600"
data-alt="Autumn Fern botanical illustration - framed example"
data-description="Example of the print displayed in an oak frame, showing scale and presentation">< / div >
< / div >
< / div >
< div class = "pdp-details" >
< h1 class = "pdp-title" > Autumn Fern< / h1 >
< p class = "pdp-price" > £24.00< / p >
< div class = "pdp-description" >
< p > A delicate botanical illustration capturing the intricate fronds of an autumn fern. Each piece is printed on museum-quality 300gsm cotton rag paper using archival inks.< / p >
< / div >
< div class = "pdp-options" >
< div class = "pdp-option-group" >
< label class = "pdp-option-label" > Size< / label >
< div class = "pdp-option-buttons" >
< button class = "pdp-option-btn active" > A5< / button >
< button class = "pdp-option-btn" > A4< / button >
< button class = "pdp-option-btn" > A3< / button >
< / div >
< / div >
< div class = "pdp-option-group" >
< label class = "pdp-option-label" > Frame< / label >
< div class = "pdp-option-buttons" >
< button class = "pdp-option-btn active" > Unframed< / button >
< button class = "pdp-option-btn" > Oak< / button >
< button class = "pdp-option-btn" > Black< / button >
< button class = "pdp-option-btn" > White< / button >
< / div >
< / div >
< / div >
< div class = "pdp-details-sections" >
< details class = "pdp-details" >
< summary class = "pdp-details-summary" >
< span > Size guide< / span >
< svg class = "pdp-details-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< polyline points = "6 9 12 15 18 9" > < / polyline >
< / svg >
< / summary >
< div class = "pdp-details-content" >
< table class = "size-table" >
< thead >
< tr >
< th > Size< / th >
< th > Dimensions< / th >
< th > Best for< / th >
< / tr >
< / thead >
< tbody >
< tr >
< td > A5< / td >
< td > 14.8 × 21 cm< / td >
< td > Desks, shelves< / td >
< / tr >
< tr >
< td > A4< / td >
< td > 21 × 29.7 cm< / td >
< td > Small wall spaces< / td >
< / tr >
< tr >
< td > A3< / td >
< td > 29.7 × 42 cm< / td >
< td > Statement pieces< / td >
< / tr >
< / tbody >
< / table >
< / div >
< / details >
< details class = "pdp-details" >
< summary class = "pdp-details-summary" >
< span > Materials & quality< / span >
< svg class = "pdp-details-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< polyline points = "6 9 12 15 18 9" > < / polyline >
< / svg >
< / summary >
< div class = "pdp-details-content" >
< ul class = "details-list" >
< li > Printed on 300gsm museum-quality cotton rag paper< / li >
< li > Archival pigment inks rated 100+ years< / li >
< li > FSC-certified sustainable paper< / li >
< li > Each print is hand-checked before shipping< / li >
< / ul >
< / div >
< / details >
< details class = "pdp-details" >
< summary class = "pdp-details-summary" >
< span > Shipping & returns< / span >
< svg class = "pdp-details-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< polyline points = "6 9 12 15 18 9" > < / polyline >
< / svg >
< / summary >
< div class = "pdp-details-content" >
< ul class = "details-list" >
< li > Free UK shipping on orders over £50< / li >
< li > Dispatched within 2-3 business days< / li >
< li > Plastic-free packaging< / li >
< li > 30-day returns for unused items< / li >
< / ul >
< / div >
< / details >
< / div >
< button class = "btn btn-primary pdp-add-btn" > Add to basket< / button >
< div class = "pdp-meta" >
< div class = "pdp-meta-item" >
< svg class = "pdp-meta-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< path d = "M12 22s-8-4.5-8-11.8A8 8 0 0 1 12 2a8 8 0 0 1 8 8.2c0 7.3-8 11.8-8 11.8z" / >
< circle cx = "12" cy = "10" r = "3" / >
< / svg >
< span > Printed in the UK< / span >
< / div >
< div class = "pdp-meta-item" >
< svg class = "pdp-meta-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< rect x = "1" y = "3" width = "15" height = "13" > < / rect >
< polygon points = "16 8 20 8 23 11 23 16 16 16 16 8" > < / polygon >
< circle cx = "5.5" cy = "18.5" r = "2.5" > < / circle >
< circle cx = "18.5" cy = "18.5" r = "2.5" > < / circle >
< / svg >
< span > Free UK shipping over £50< / span >
< / div >
< / div >
< div class = "pdp-trust-badges" >
< div class = "trust-badge" >
< svg class = "trust-badge-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< path d = "M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" > < / path >
< polyline points = "9 12 11 14 15 10" > < / polyline >
< / svg >
< span > Secure checkout< / span >
< / div >
< div class = "trust-badge" >
< svg class = "trust-badge-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< path d = "M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0 1 18 0z" > < / path >
< circle cx = "12" cy = "10" r = "3" > < / circle >
< / svg >
< span > UK based< / span >
< / div >
< div class = "trust-badge" >
< svg class = "trust-badge-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< polyline points = "20 6 9 17 4 12" > < / polyline >
< / svg >
< span > Quality guaranteed< / span >
< / div >
< / div >
< / div >
< / div >
<!-- Reviews Section -->
< div class = "pdp-reviews" >
< div class = "reviews-header" >
< h2 class = "section-title" > Customer reviews< / h2 >
< div class = "reviews-summary" >
< div class = "reviews-stars" >
< svg class = "star filled" viewBox = "0 0 20 20" > < path d = "M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" fill = "currentColor" / > < / svg >
< svg class = "star filled" viewBox = "0 0 20 20" > < path d = "M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" fill = "currentColor" / > < / svg >
< svg class = "star filled" viewBox = "0 0 20 20" > < path d = "M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" fill = "currentColor" / > < / svg >
< svg class = "star filled" viewBox = "0 0 20 20" > < path d = "M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" fill = "currentColor" / > < / svg >
< svg class = "star filled" viewBox = "0 0 20 20" > < path d = "M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" fill = "currentColor" / > < / svg >
< / div >
< span class = "reviews-count" > Based on 24 reviews< / span >
< / div >
< / div >
< div class = "reviews-list" >
< article class = "review" >
< div class = "review-header" >
< div class = "review-stars" >
< svg class = "star filled" viewBox = "0 0 20 20" > < path d = "M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" fill = "currentColor" / > < / svg >
< svg class = "star filled" viewBox = "0 0 20 20" > < path d = "M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" fill = "currentColor" / > < / svg >
< svg class = "star filled" viewBox = "0 0 20 20" > < path d = "M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" fill = "currentColor" / > < / svg >
< svg class = "star filled" viewBox = "0 0 20 20" > < path d = "M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" fill = "currentColor" / > < / svg >
< svg class = "star filled" viewBox = "0 0 20 20" > < path d = "M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" fill = "currentColor" / > < / svg >
< / div >
< span class = "review-date" > 2 weeks ago< / span >
< / div >
< h3 class = "review-title" > Absolutely beautiful< / h3 >
< p class = "review-text" > The quality exceeded my expectations. The colours are vibrant and the paper feels premium. It's now pride of place in my living room.< / p >
< div class = "review-author" >
< span class = "review-name" > Sarah M.< / span >
< span class = "review-verified" > Verified purchase< / span >
< / div >
< / article >
< article class = "review" >
< div class = "review-header" >
< div class = "review-stars" >
< svg class = "star filled" viewBox = "0 0 20 20" > < path d = "M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" fill = "currentColor" / > < / svg >
< svg class = "star filled" viewBox = "0 0 20 20" > < path d = "M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" fill = "currentColor" / > < / svg >
< svg class = "star filled" viewBox = "0 0 20 20" > < path d = "M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" fill = "currentColor" / > < / svg >
< svg class = "star filled" viewBox = "0 0 20 20" > < path d = "M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" fill = "currentColor" / > < / svg >
< svg class = "star" viewBox = "0 0 20 20" > < path d = "M10 15l-5.878 3.09 1.123-6.545L.489 6.91l6.572-.955L10 0l2.939 5.955 6.572.955-4.756 4.635 1.123 6.545z" fill = "currentColor" / > < / svg >
< / div >
< span class = "review-date" > 1 month ago< / span >
< / div >
< h3 class = "review-title" > Great gift< / h3 >
< p class = "review-text" > Bought this as a gift and it arrived beautifully packaged. Fast shipping too. Would definitely order again.< / p >
< div class = "review-author" >
< span class = "review-name" > James T.< / span >
< span class = "review-verified" > Verified purchase< / span >
< / div >
< / article >
< / div >
< button class = "btn btn-secondary reviews-load-more" > Load more reviews< / button >
< / div >
<!-- Related Products -->
< div class = "pdp-related" >
< h2 class = "section-title" > You might also like< / h2 >
< div class = "related-grid" >
< article class = "product-card" >
< div class = "product-image" >
< div class = "product-image-primary img-2" > < / div >
< div class = "product-image-hover img-2-alt" > < / div >
< button class = "quick-add-btn" > Quick add< / button >
< / div >
< div class = "product-info" >
< h3 class = "product-title" > Wild Roses< / h3 >
< p class = "product-price" > £28.00< / p >
< / div >
< / article >
< article class = "product-card" >
< div class = "product-image" >
< div class = "product-image-primary img-3" > < / div >
< div class = "product-image-hover img-3-alt" > < / div >
< button class = "quick-add-btn" > Quick add< / button >
< / div >
< div class = "product-info" >
< h3 class = "product-title" > Morning Dew< / h3 >
< p class = "product-price" > £24.00< / p >
< / div >
< / article >
< article class = "product-card" >
< div class = "product-image" >
< div class = "product-image-primary img-4" > < / div >
< div class = "product-image-hover img-4-alt" > < / div >
< button class = "quick-add-btn" > Quick add< / button >
< / div >
< div class = "product-info" >
< h3 class = "product-title" > Oak Leaves< / h3 >
< p class = "product-price" > £32.00< / p >
< / div >
< / article >
< / div >
< / div >
< / div >
<!-- Footer -->
< footer class = "shop-footer" id = "pdp-footer" >
< div class = "footer-content" >
< div class = "footer-newsletter" >
< h3 class = "footer-heading" > Join the studio< / h3 >
< p class = "footer-text" > Get 10% off your first order and be the first to know about new prints.< / p >
< form class = "newsletter-form" >
< input type = "email" class = "newsletter-input" placeholder = "your@email.com" >
< button type = "submit" class = "btn btn-primary newsletter-btn" > Subscribe< / button >
< / form >
< / div >
< div class = "footer-links" >
< div class = "footer-column" >
< h4 class = "footer-column-title" > Shop< / h4 >
< a href = "#" > All prints< / a >
< a href = "#" > New arrivals< / a >
< a href = "#" > Best sellers< / a >
< / div >
< div class = "footer-column" >
< h4 class = "footer-column-title" > Help< / h4 >
< a href = "#" > Shipping< / a >
< a href = "#" > Returns< / a >
< a href = "#" > Contact< / a >
< / div >
< / div >
< / div >
< div class = "footer-bottom" >
< p id = "footer-text-pdp" > © 2025 Botanical Studio< / p >
< div class = "footer-social" >
< a href = "#" class = "social-link" aria-label = "Instagram" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< rect x = "2" y = "2" width = "20" height = "20" rx = "5" ry = "5" > < / rect >
< path d = "M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z" > < / path >
< line x1 = "17.5" y1 = "6.5" x2 = "17.51" y2 = "6.5" > < / line >
< / svg >
< / a >
< a href = "#" class = "social-link" aria-label = "Pinterest" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< circle cx = "12" cy = "12" r = "10" > < / circle >
< path d = "M8 12c0-2.2 1.8-4 4-4s4 1.8 4 4-1.8 4-4 4" > < / path >
< line x1 = "12" y1 = "16" x2 = "9" y2 = "21" > < / line >
< / svg >
< / a >
< / div >
< / div >
< / footer >
< / div >
<!-- Cart Page -->
< div class = "page-view cart-view" id = "cart-view" >
< div class = "cart-container" >
< div class = "cart-header-row" >
< h1 class = "cart-title" > Your basket< / h1 >
< button class = "cart-state-toggle" id = "cart-state-toggle" > Preview empty state< / button >
< / div >
<!-- Full Cart State -->
< div class = "cart-state cart-state-full" id = "cart-full" >
< div class = "cart-layout" >
< div class = "cart-items" >
< div class = "cart-item" >
< div class = "cart-item-image img-1" > < / div >
< div class = "cart-item-details" >
< h3 class = "cart-item-title" > Autumn Fern< / h3 >
< p class = "cart-item-variant" > A4 / Unframed< / p >
< p class = "cart-item-price" > £24.00< / p >
< / div >
< div class = "cart-item-quantity" >
< button class = "qty-btn" > − < / button >
< span class = "qty-value" > 1< / span >
< button class = "qty-btn" > +< / button >
< / div >
< button class = "cart-item-remove" aria-label = "Remove" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< line x1 = "18" y1 = "6" x2 = "6" y2 = "18" > < / line >
< line x1 = "6" y1 = "6" x2 = "18" y2 = "18" > < / line >
< / svg >
< / button >
< / div >
< div class = "cart-item" >
< div class = "cart-item-image img-2" > < / div >
< div class = "cart-item-details" >
< h3 class = "cart-item-title" > Wild Roses< / h3 >
< p class = "cart-item-variant" > A3 / Oak frame< / p >
< p class = "cart-item-price" > £48.00< / p >
< / div >
< div class = "cart-item-quantity" >
< button class = "qty-btn" > − < / button >
< span class = "qty-value" > 1< / span >
< button class = "qty-btn" > +< / button >
< / div >
< button class = "cart-item-remove" aria-label = "Remove" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< line x1 = "18" y1 = "6" x2 = "6" y2 = "18" > < / line >
< line x1 = "6" y1 = "6" x2 = "18" y2 = "18" > < / line >
< / svg >
< / button >
< / div >
< / div >
< div class = "cart-summary" >
< div class = "cart-summary-row" >
< span > Subtotal< / span >
< span > £72.00< / span >
< / div >
< div class = "cart-summary-row" >
< span > Shipping< / span >
< span > Calculated at checkout< / span >
< / div >
< div class = "cart-summary-row cart-total" >
< span > Total< / span >
< span > £72.00< / span >
< / div >
< button class = "btn btn-primary cart-checkout-btn" > Checkout< / button >
< p class = "cart-secure-note" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< rect x = "3" y = "11" width = "18" height = "11" rx = "2" ry = "2" > < / rect >
< path d = "M7 11V7a5 5 0 0 1 10 0v4" > < / path >
< / svg >
Secure checkout powered by Stripe
< / p >
< / div >
< / div >
< / div >
<!-- Empty Cart State -->
< div class = "cart-state cart-state-empty" id = "cart-empty" hidden >
< div class = "empty-state" >
< div class = "empty-state-illustration" >
< svg viewBox = "0 0 120 120" fill = "none" xmlns = "http://www.w3.org/2000/svg" >
<!-- Soft background -->
< circle cx = "60" cy = "60" r = "48" fill = "currentColor" opacity = "0.04" / >
<!-- Simple elegant shopping bag -->
< rect x = "32" y = "44" width = "56" height = "52" rx = "4"
stroke="currentColor" stroke-width="2" fill="none" opacity="0.6"/>
<!-- Bag handles -->
< path d = "M44 44 C44 44 44 32 60 32 C76 32 76 44 76 44"
stroke="currentColor" stroke-width="2" fill="none"
stroke-linecap="round" opacity="0.6"/>
<!-- Simple fold line -->
< line x1 = "32" y1 = "52" x2 = "88" y2 = "52"
stroke="currentColor" stroke-width="1" opacity="0.2"/>
< / svg >
< / div >
< h2 class = "empty-state-title" > Your basket is empty< / h2 >
< p class = "empty-state-message" > Looks like you haven't added anything yet. Start exploring our collection to find something you'll love.< / p >
< button class = "btn btn-primary" data-nav = "collection" > Continue shopping< / button >
< / div >
< / div >
< / div >
< footer class = "shop-footer" id = "cart-footer" >
< div class = "footer-bottom" >
< p id = "footer-text-cart" > © 2025 Botanical Studio< / p >
< / div >
< / footer >
< / div >
<!-- About Page -->
< div class = "page-view about-view" id = "about-view" >
< div class = "content-page" >
< div class = "content-hero" >
< h1 class = "content-title" > About the studio< / h1 >
< p class = "content-subtitle" > Nature-inspired art, made with care< / p >
< / div >
< div class = "content-body" >
< div class = "content-image about-image" > < / div >
< div class = "content-text" >
< p class = "lead-text" > Botanical Studio was born from a love of the natural world and a desire to bring its beauty indoors.< / p >
< p > Every illustration starts as a pencil sketch, inspired by the plants and flowers found in British woodlands, meadows, and gardens. These sketches are then refined and printed on museum-quality archival paper using pigment-based inks that will last a lifetime.< / p >
< p > Based in the Yorkshire countryside, I work from a small garden studio surrounded by the very nature that inspires each piece. Every print is checked by hand before being carefully packaged and sent on its way.< / p >
< h2 > Sustainability< / h2 >
< p > I believe beautiful art shouldn't cost the earth. All prints are produced on FSC-certified paper, shipped in plastic-free packaging, and printed locally to reduce transport emissions.< / p >
< h2 > The process< / h2 >
< p > Each botanical illustration takes between 20-40 hours to complete, from initial field sketches through to the final digital refinement. I work primarily in graphite and watercolour, which are then scanned and carefully colour-corrected to ensure the prints match the original artwork.< / p >
< / div >
< / div >
< / div >
< footer class = "shop-footer" id = "about-footer" >
< div class = "footer-bottom" >
< p id = "footer-text-about" > © 2025 Botanical Studio< / p >
< / div >
< / footer >
< / div >
<!-- Contact Page -->
< div class = "page-view contact-view" id = "contact-view" >
< div class = "content-page" >
< div class = "content-hero" >
< h1 class = "content-title" > Get in touch< / h1 >
< p class = "content-subtitle" > I'd love to hear from you< / p >
< / div >
< div class = "contact-layout" >
< div class = "contact-info" >
< div class = "contact-block" >
< h3 > General enquiries< / h3 >
< p > For questions about prints, shipping, or custom orders.< / p >
< a href = "mailto:hello@botanicalstudio.com" class = "contact-link" > hello@botanicalstudio.com< / a >
< / div >
< div class = "contact-block" >
< h3 > Response time< / h3 >
< p > I typically respond within 1-2 business days. For urgent order queries, please include your order number.< / p >
< / div >
< div class = "contact-block" >
< h3 > Follow along< / h3 >
< p > See works in progress and behind-the-scenes on Instagram.< / p >
< div class = "contact-social" >
< a href = "#" class = "social-link" aria-label = "Instagram" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< rect x = "2" y = "2" width = "20" height = "20" rx = "5" ry = "5" > < / rect >
< path d = "M16 11.37A4 4 0 1 1 12.63 8 4 4 0 0 1 16 11.37z" > < / path >
< line x1 = "17.5" y1 = "6.5" x2 = "17.51" y2 = "6.5" > < / line >
< / svg >
< / a >
< a href = "#" class = "social-link" aria-label = "Pinterest" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< circle cx = "12" cy = "12" r = "10" > < / circle >
< path d = "M8 12c0-2.2 1.8-4 4-4s4 1.8 4 4-1.8 4-4 4" > < / path >
< line x1 = "12" y1 = "16" x2 = "9" y2 = "21" > < / line >
< / svg >
< / a >
< / div >
< / div >
< / div >
< form class = "contact-form" >
< div class = "form-group" >
< label class = "form-label" for = "contact-name" > Name< / label >
< input type = "text" id = "contact-name" class = "form-input" placeholder = "Your name" >
< / div >
< div class = "form-group" >
< label class = "form-label" for = "contact-email" > Email< / label >
< input type = "email" id = "contact-email" class = "form-input" placeholder = "your@email.com" >
< / div >
< div class = "form-group" >
< label class = "form-label" for = "contact-subject" > Subject< / label >
< select id = "contact-subject" class = "form-input form-select" >
< option > General enquiry< / option >
< option > Order question< / option >
< option > Custom order< / option >
< option > Wholesale / Trade< / option >
< option > Press / Collaboration< / option >
< / select >
< / div >
< div class = "form-group" >
< label class = "form-label" for = "contact-message" > Message< / label >
< textarea id = "contact-message" class = "form-input form-textarea" rows = "5" placeholder = "How can I help?" > < / textarea >
< / div >
< button type = "submit" class = "btn btn-primary form-submit" > Send message< / button >
< / form >
< / div >
< / div >
< footer class = "shop-footer" id = "contact-footer" >
< div class = "footer-bottom" >
< p id = "footer-text-contact" > © 2025 Botanical Studio< / p >
< / div >
< / footer >
< / div >
<!-- Error Page -->
< div class = "page-view error-view" id = "error-view" >
< div class = "error-container" >
< div class = "error-content" >
< div class = "error-illustration" >
< svg viewBox = "0 0 200 160" fill = "none" xmlns = "http://www.w3.org/2000/svg" >
<!-- Soft background -->
< circle cx = "100" cy = "75" r = "55" fill = "currentColor" opacity = "0.04" / >
<!-- Magnifying glass - universal "not found" symbol -->
< circle cx = "90" cy = "70" r = "32"
stroke="currentColor" stroke-width="2.5" fill="none" opacity="0.5"/>
< line x1 = "113" y1 = "93" x2 = "140" y2 = "120"
stroke="currentColor" stroke-width="2.5" stroke-linecap="round" opacity="0.5"/>
<!-- Question mark inside magnifying glass -->
< path d = "M82 60 C82 52 88 48 95 48 C102 48 108 52 108 60 C108 66 102 68 95 72"
stroke="currentColor" stroke-width="2" fill="none"
stroke-linecap="round" opacity="0.4"/>
< circle cx = "95" cy = "82" r = "2.5" fill = "currentColor" opacity = "0.4" / >
<!-- Small decorative dots suggesting scattered/lost -->
< circle cx = "45" cy = "45" r = "3" fill = "currentColor" opacity = "0.15" / >
< circle cx = "155" cy = "55" r = "2" fill = "currentColor" opacity = "0.1" / >
< circle cx = "160" cy = "100" r = "2.5" fill = "currentColor" opacity = "0.12" / >
< circle cx = "40" cy = "110" r = "2" fill = "currentColor" opacity = "0.1" / >
< / svg >
< / div >
< h1 class = "error-title" > Something went wrong< / h1 >
< p class = "error-message" > We couldn't find what you were looking for. The page may have moved, or perhaps it never existed in the first place.< / p >
< div class = "error-actions" >
< button class = "btn btn-primary" data-nav = "home" > Go to homepage< / button >
< button class = "btn btn-secondary" data-nav = "collection" > Browse products< / button >
< / div >
< / div >
< / div >
< footer class = "shop-footer" id = "error-footer" >
< div class = "footer-bottom" >
< p id = "footer-text-error" > © 2025 Botanical Studio< / p >
< / div >
< / footer >
< / div >
<!-- Cart Drawer -->
< div class = "cart-drawer" id = "cart-drawer" >
< div class = "cart-drawer-header" >
< h2 > Your basket< / h2 >
< button class = "cart-drawer-close" id = "cart-drawer-close" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< line x1 = "18" y1 = "6" x2 = "6" y2 = "18" > < / line >
< line x1 = "6" y1 = "6" x2 = "18" y2 = "18" > < / line >
< / svg >
< / button >
< / div >
< div class = "cart-drawer-items" >
< div class = "cart-drawer-item" >
< div class = "cart-drawer-item-image img-1" > < / div >
< div class = "cart-drawer-item-details" >
< h3 > Autumn Fern< / h3 >
< p > A4 / Unframed< / p >
< p class = "cart-drawer-item-price" > £24.00< / p >
< / div >
< / div >
< div class = "cart-drawer-item" >
< div class = "cart-drawer-item-image img-2" > < / div >
< div class = "cart-drawer-item-details" >
< h3 > Wild Roses< / h3 >
< p > A3 / Oak frame< / p >
< p class = "cart-drawer-item-price" > £48.00< / p >
< / div >
< / div >
< / div >
< div class = "cart-drawer-footer" >
< div class = "cart-drawer-total" >
< span > Subtotal< / span >
< span > £72.00< / span >
< / div >
< button class = "btn btn-primary cart-drawer-checkout" > Checkout< / button >
< a href = "#" class = "cart-drawer-link" id = "view-cart-link" > View basket< / a >
< / div >
< / div >
< div class = "cart-drawer-overlay" id = "cart-drawer-overlay" > < / div >
<!-- Search Modal -->
< div class = "search-modal" id = "search-modal" >
< div class = "search-modal-content" >
< div class = "search-input-wrapper" >
< svg class = "search-icon" viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< circle cx = "11" cy = "11" r = "8" > < / circle >
< path d = "M21 21l-4.35-4.35" > < / path >
< / svg >
< input type = "text" class = "search-input" placeholder = "Search products..." autofocus >
< button class = "search-close" id = "search-close" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< line x1 = "18" y1 = "6" x2 = "6" y2 = "18" > < / line >
< line x1 = "6" y1 = "6" x2 = "18" y2 = "18" > < / line >
< / svg >
< / button >
< / div >
< div class = "search-results" >
< p class = "search-hint" > Try searching for "fern", "roses", or "botanical"< / p >
< / div >
< / div >
< / div >
<!-- Image Lightbox -->
< dialog class = "lightbox" id = "lightbox" aria-label = "Product image gallery" >
< div class = "lightbox-content" >
< button class = "lightbox-close" id = "lightbox-close" aria-label = "Close gallery" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< line x1 = "18" y1 = "6" x2 = "6" y2 = "18" > < / line >
< line x1 = "6" y1 = "6" x2 = "18" y2 = "18" > < / line >
< / svg >
< / button >
< button class = "lightbox-nav lightbox-prev" id = "lightbox-prev" aria-label = "Previous image" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< polyline points = "15 18 9 12 15 6" > < / polyline >
< / svg >
< / button >
< figure class = "lightbox-figure" >
< div class = "lightbox-image-container" >
< img class = "lightbox-image" id = "lightbox-image" src = "" alt = "" >
< / div >
< figcaption class = "lightbox-caption" id = "lightbox-caption" > < / figcaption >
< / figure >
< button class = "lightbox-nav lightbox-next" id = "lightbox-next" aria-label = "Next image" >
< svg viewBox = "0 0 24 24" fill = "none" stroke = "currentColor" stroke-width = "2" >
< polyline points = "9 18 15 12 9 6" > < / polyline >
< / svg >
< / button >
< div class = "lightbox-counter" id = "lightbox-counter" aria-live = "polite" > 1 / 3< / div >
< / div >
< / dialog >
< / div >
< / div >
< / div >
< script >
// Presets configuration
const presets = {
gallery: {
mood: 'warm',
typography: 'editorial',
shape: 'soft',
density: 'spacious',
grid: '3',
header: 'centered',
accent: '#e85d04'
},
studio: {
mood: 'neutral',
typography: 'clean',
shape: 'soft',
density: 'balanced',
grid: '4',
header: 'standard',
accent: '#3b82f6'
},
boutique: {
mood: 'warm',
typography: 'classic',
shape: 'soft',
density: 'balanced',
grid: '3',
header: 'centered',
accent: '#b45309'
},
bold: {
mood: 'neutral',
typography: 'modern',
shape: 'sharp',
density: 'compact',
grid: '4',
header: 'standard',
accent: '#dc2626'
},
playful: {
mood: 'neutral',
typography: 'friendly',
shape: 'pill',
density: 'balanced',
grid: '4',
header: 'standard',
accent: '#8b5cf6'
},
minimal: {
mood: 'cool',
typography: 'clean',
shape: 'sharp',
density: 'spacious',
grid: '2',
header: 'minimal',
accent: '#171717'
},
night: {
mood: 'dark',
typography: 'modern',
shape: 'soft',
density: 'balanced',
grid: '4',
header: 'standard',
accent: '#f97316'
},
classic: {
mood: 'warm',
typography: 'classic',
shape: 'soft',
density: 'spacious',
grid: '3',
header: 'standard',
accent: '#166534'
},
impulse: {
mood: 'neutral',
typography: 'impulse',
shape: 'sharp',
density: 'spacious',
grid: '3',
header: 'centered',
accent: '#000000',
// Additional Impulse-specific settings
fontSize: 'medium',
headingWeight: 'regular',
layoutWidth: 'full',
buttonStyle: 'filled',
cardShadow: 'none',
productText: 'center'
}
};
// State
let currentSettings = {
mood: 'neutral',
typography: 'clean',
shape: 'soft',
density: 'balanced',
grid: '4',
header: 'standard',
accent: '#f97316'
};
let shopName = 'Botanical Studio';
let logoMode = 'text-only';
let logoUrl = null;
let logoSvgContent = null; // Store original SVG content
let logoIsSvg = false;
let headerImageUrl = null;
let logoSize = 36;
let headerPositionX = 50; // percentage 0-100
let headerPositionY = 50; // percentage 0-100
let headerZoom = 100;
let recolorLogo = false;
let logoColor = '#171717';
// Feature toggles
let showAnnouncementBar = true;
let stickyHeader = false;
let hoverImage = true;
let quickAdd = true;
let aspectRatio = 'square';
// DOM elements
const previewFrame = document.getElementById('preview-frame');
const shopHeader = document.getElementById('shop-header');
const productGrid = document.getElementById('product-grid');
const accentPicker = document.getElementById('accent-picker');
const colorValue = document.getElementById('color-value');
const comboValue = document.getElementById('combo-value');
const shopNameInput = document.getElementById('shop-name');
const logoUploadRow = document.getElementById('logo-upload-row');
const headerUploadRow = document.getElementById('header-upload-row');
const logoUpload = document.getElementById('logo-upload');
const headerUpload = document.getElementById('header-upload');
const logoPreview = document.getElementById('logo-preview');
const logoPreviewImg = document.getElementById('logo-preview-img');
const headerPreview = document.getElementById('header-preview');
const headerPreviewImg = document.getElementById('header-preview-img');
const shopLogoImage = document.getElementById('shop-logo-image');
const shopLogoImg = document.getElementById('shop-logo-img');
const shopLogoText = document.getElementById('shop-logo-text');
const logoSizeControl = document.getElementById('logo-size-control');
const logoSizeSlider = document.getElementById('logo-size-slider');
const logoSizeValue = document.getElementById('logo-size-value');
const logoColorControl = document.getElementById('logo-color-control');
const recolorLogoCheckbox = document.getElementById('recolor-logo-checkbox');
const logoColorPickerRow = document.getElementById('logo-color-picker-row');
const logoColorPicker = document.getElementById('logo-color-picker');
const logoColorValue = document.getElementById('logo-color-value');
const headerImageControls = document.getElementById('header-image-controls');
const headerPositionXRow = document.getElementById('header-position-x-row');
const headerPositionXSlider = document.getElementById('header-position-x-slider');
const headerPositionXValue = document.getElementById('header-position-x-value');
const headerPositionSlider = document.getElementById('header-position-slider');
const headerPositionValue = document.getElementById('header-position-value');
const headerZoomSlider = document.getElementById('header-zoom-slider');
const headerZoomValue = document.getElementById('header-zoom-value');
// Recolor SVG by replacing fill and stroke colors
function recolorSvg(svgContent, newColor) {
if (!svgContent) return svgContent;
// Replace fill attributes (but not fill="none")
let recolored = svgContent.replace(/fill="(?!none)[^"]*"/gi, `fill="${newColor}"`);
// Replace stroke attributes (but not stroke="none")
recolored = recolored.replace(/stroke="(?!none)[^"]*"/gi, `stroke="${newColor}"`);
// Also handle style attributes with fill/stroke
recolored = recolored.replace(/fill:\s*(?!none)[^;}"']+/gi, `fill: ${newColor}`);
recolored = recolored.replace(/stroke:\s*(?!none)[^;}"']+/gi, `stroke: ${newColor}`);
return recolored;
}
// Update the logo image in the preview
function updateLogoImage() {
if (logoIsSvg & & logoSvgContent) {
let svgToUse = logoSvgContent;
if (recolorLogo) {
svgToUse = recolorSvg(logoSvgContent, logoColor);
}
logoUrl = 'data:image/svg+xml,' + encodeURIComponent(svgToUse);
// Also update the preview thumbnail
logoPreviewImg.src = logoUrl;
}
}
// Helper: Convert hex to HSL
function hexToHSL(hex) {
let r = parseInt(hex.slice(1, 3), 16) / 255;
let g = parseInt(hex.slice(3, 5), 16) / 255;
let b = parseInt(hex.slice(5, 7), 16) / 255;
let max = Math.max(r, g, b), min = Math.min(r, g, b);
let h, s, l = (max + min) / 2;
if (max === min) {
h = s = 0;
} else {
let d = max - min;
s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
switch (max) {
case r: h = ((g - b) / d + (g < b ? 6 : 0 ) ) / 6 ; break ;
case g: h = ((b - r) / d + 2) / 6; break;
case b: h = ((r - g) / d + 4) / 6; break;
}
}
return {
h: Math.round(h * 360),
s: Math.round(s * 100),
l: Math.round(l * 100)
};
}
// Update logo/header mode display
function updateLogoDisplay() {
// Update upload row visibility
const needsLogo = logoMode === 'logo-and-text' || logoMode === 'logo-only' || logoMode === 'logo-header-image';
const needsHeader = logoMode === 'header-image' || logoMode === 'logo-header-image';
logoUploadRow.classList.toggle('visible', needsLogo);
headerUploadRow.classList.toggle('visible', needsHeader);
// Show size control only when logo is uploaded and mode uses a logo
const hasLogo = logoUrl || (logoIsSvg & & logoSvgContent);
const showSizeControl = needsLogo & & hasLogo;
logoSizeControl.classList.toggle('visible', showSizeControl);
// Show color control only for SVG logos
const showColorControl = needsLogo & & logoIsSvg & & logoSvgContent;
logoColorControl.classList.toggle('visible', showColorControl);
// Show header image controls when header image is uploaded
const showHeaderControls = needsHeader & & headerImageUrl;
headerImageControls.classList.toggle('visible', showHeaderControls);
// Only show horizontal position when zoomed in
if (showHeaderControls) {
headerPositionXRow.classList.toggle('visible', headerZoom > 100);
}
// Update logo upload label based on mode
const logoUploadLabel = document.getElementById('logo-upload-label');
if (logoMode === 'logo-header-image') {
logoUploadLabel.textContent = 'Upload logo (use white/light version)';
} else {
logoUploadLabel.textContent = 'Upload logo (SVG or PNG)';
}
// Update preview header
shopHeader.dataset.logoMode = logoMode;
// Apply logo size
shopLogoImage.style.height = logoSize + 'px';
// Update logo visibility and header background
if (logoMode === 'text-only') {
shopLogoImage.classList.remove('visible');
shopLogoText.style.display = 'block';
shopHeader.style.backgroundImage = '';
shopHeader.style.backgroundPosition = '';
shopHeader.style.backgroundSize = '';
} else if (logoMode === 'logo-and-text') {
if (logoUrl) {
shopLogoImage.classList.add('visible');
shopLogoImg.src = logoUrl;
} else {
shopLogoImage.classList.remove('visible');
}
shopLogoText.style.display = 'block';
shopHeader.style.backgroundImage = '';
shopHeader.style.backgroundPosition = '';
shopHeader.style.backgroundSize = '';
} else if (logoMode === 'logo-only') {
if (logoUrl) {
shopLogoImage.classList.add('visible');
shopLogoImg.src = logoUrl;
} else {
shopLogoImage.classList.remove('visible');
}
shopLogoText.style.display = 'none';
shopHeader.style.backgroundImage = '';
shopHeader.style.backgroundPosition = '';
shopHeader.style.backgroundSize = '';
} else if (logoMode === 'header-image') {
shopLogoImage.classList.remove('visible');
shopLogoText.style.display = 'block';
if (headerImageUrl) {
shopHeader.style.backgroundImage = `url(${headerImageUrl})`;
shopHeader.style.backgroundPosition = `${headerPositionX}% ${headerPositionY}%`;
shopHeader.style.backgroundSize = `${headerZoom}% auto`;
} else {
// Default placeholder gradient
shopHeader.style.backgroundImage = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
shopHeader.style.backgroundPosition = 'center center';
shopHeader.style.backgroundSize = 'cover';
}
} else if (logoMode === 'logo-header-image') {
if (logoUrl) {
shopLogoImage.classList.add('visible');
shopLogoImg.src = logoUrl;
} else {
shopLogoImage.classList.remove('visible');
}
shopLogoText.style.display = 'none';
if (headerImageUrl) {
shopHeader.style.backgroundImage = `url(${headerImageUrl})`;
shopHeader.style.backgroundPosition = `${headerPositionX}% ${headerPositionY}%`;
shopHeader.style.backgroundSize = `${headerZoom}% auto`;
} else {
// Default placeholder gradient
shopHeader.style.backgroundImage = 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)';
shopHeader.style.backgroundPosition = 'center center';
shopHeader.style.backgroundSize = 'cover';
}
}
}
// Apply settings to preview
function applySettings() {
// Apply data attributes
previewFrame.dataset.mood = currentSettings.mood;
previewFrame.dataset.typography = currentSettings.typography;
previewFrame.dataset.shape = currentSettings.shape;
previewFrame.dataset.density = currentSettings.density;
shopHeader.dataset.header = currentSettings.header;
productGrid.dataset.grid = currentSettings.grid;
// Apply Impulse-specific settings if present
if (currentSettings.fontSize) {
previewFrame.dataset.fontSize = currentSettings.fontSize;
document.querySelectorAll('#font-size-options .option-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.fontSize === currentSettings.fontSize);
});
}
if (currentSettings.headingWeight) {
previewFrame.dataset.headingWeight = currentSettings.headingWeight;
document.querySelectorAll('#heading-weight-options .option-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.headingWeight === currentSettings.headingWeight);
});
}
if (currentSettings.layoutWidth) {
previewFrame.dataset.layoutWidth = currentSettings.layoutWidth;
document.querySelectorAll('#layout-width-options .option-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.layoutWidth === currentSettings.layoutWidth);
});
}
if (currentSettings.buttonStyle) {
previewFrame.dataset.buttonStyle = currentSettings.buttonStyle;
document.querySelectorAll('#button-style-options .option-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.buttonStyle === currentSettings.buttonStyle);
});
}
if (currentSettings.cardShadow) {
previewFrame.dataset.cardShadow = currentSettings.cardShadow;
document.querySelectorAll('#card-shadow-options .option-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.cardShadow === currentSettings.cardShadow);
});
}
if (currentSettings.productText) {
previewFrame.dataset.productText = currentSettings.productText;
document.querySelectorAll('#product-text-options .option-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.productText === currentSettings.productText);
});
}
// Apply accent color
const hsl = hexToHSL(currentSettings.accent);
previewFrame.style.setProperty('--t-accent-h', hsl.h);
previewFrame.style.setProperty('--t-accent-s', hsl.s + '%');
previewFrame.style.setProperty('--t-accent-l', hsl.l + '%');
// Update color picker and display
accentPicker.value = currentSettings.accent;
colorValue.textContent = currentSettings.accent;
// Update shop name in preview
shopLogoText.textContent = shopName;
const footerTexts = ['footer-text', 'footer-text-collection', 'footer-text-pdp', 'footer-text-cart', 'footer-text-about', 'footer-text-contact', 'footer-text-error'];
footerTexts.forEach(id => {
const el = document.getElementById(id);
if (el) el.textContent = `© 2025 ${shopName}`;
});
// Update browser tab
document.getElementById('favicon-letter').textContent = shopName.charAt(0).toUpperCase();
document.getElementById('browser-domain').textContent = shopName.toLowerCase().replace(/\s+/g, '');
// Update logo display
updateLogoDisplay();
// Update combo display
const labels = {
mood: { warm: 'Warm', neutral: 'Neutral', cool: 'Cool', dark: 'Dark' },
typography: { clean: 'Clean', editorial: 'Editorial', modern: 'Modern', classic: 'Classic', friendly: 'Friendly', minimal: 'Minimal', impulse: 'Impulse' },
shape: { sharp: 'Sharp', soft: 'Soft', round: 'Round', pill: 'Pill' },
density: { spacious: 'Spacious', balanced: 'Balanced', compact: 'Compact' },
header: { standard: 'Standard', centered: 'Centered', minimal: 'Minimal' }
};
comboValue.textContent = [
labels.mood[currentSettings.mood],
labels.typography[currentSettings.typography],
labels.shape[currentSettings.shape],
labels.density[currentSettings.density],
currentSettings.grid + '-up',
labels.header[currentSettings.header]
].join(' · ');
updateActiveStates();
}
// Update active states on buttons
function updateActiveStates() {
document.querySelectorAll('#mood-options .option-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.mood === currentSettings.mood);
});
document.querySelectorAll('#typography-options .option-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.typography === currentSettings.typography);
});
document.querySelectorAll('#shape-options .option-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.shape === currentSettings.shape);
});
document.querySelectorAll('#density-options .option-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.density === currentSettings.density);
});
document.querySelectorAll('#grid-options .option-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.grid === currentSettings.grid);
});
document.querySelectorAll('#header-options .option-button').forEach(btn => {
btn.classList.toggle('active', btn.dataset.header === currentSettings.header);
});
}
// Preset buttons
document.querySelectorAll('.preset-button').forEach(btn => {
btn.addEventListener('click', () => {
const presetName = btn.dataset.preset;
const preset = presets[presetName];
currentSettings = { ...preset };
document.querySelectorAll('.preset-button').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
applySettings();
});
});
// Individual option buttons
function setupOptionGroup(groupId, settingKey) {
document.querySelectorAll(`#${groupId} .option-button`).forEach(btn => {
btn.addEventListener('click', () => {
currentSettings[settingKey] = btn.dataset[settingKey];
document.querySelectorAll('.preset-button').forEach(b => b.classList.remove('active'));
applySettings();
});
});
}
setupOptionGroup('mood-options', 'mood');
setupOptionGroup('typography-options', 'typography');
setupOptionGroup('shape-options', 'shape');
setupOptionGroup('density-options', 'density');
setupOptionGroup('grid-options', 'grid');
setupOptionGroup('header-options', 'header');
// Accent color picker
accentPicker.addEventListener('input', (e) => {
currentSettings.accent = e.target.value;
document.querySelectorAll('.preset-button').forEach(b => b.classList.remove('active'));
applySettings();
});
// Secondary accent (hover) color picker
const secondaryAccentPicker = document.getElementById('secondary-accent-picker');
const secondaryColorValue = document.getElementById('secondary-color-value');
secondaryAccentPicker.addEventListener('input', (e) => {
previewFrame.style.setProperty('--t-secondary-accent', e.target.value);
secondaryColorValue.textContent = e.target.value;
});
// Sale color picker
const saleColorPicker = document.getElementById('sale-color-picker');
const saleColorValue = document.getElementById('sale-color-value');
saleColorPicker.addEventListener('input', (e) => {
previewFrame.style.setProperty('--t-sale-color', e.target.value);
saleColorValue.textContent = e.target.value;
});
// Font size options
document.querySelectorAll('#font-size-options .option-button').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('#font-size-options .option-button').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
previewFrame.dataset.fontSize = btn.dataset.fontSize;
});
});
// Heading weight options
document.querySelectorAll('#heading-weight-options .option-button').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('#heading-weight-options .option-button').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
previewFrame.dataset.headingWeight = btn.dataset.headingWeight;
});
});
// Layout width options
document.querySelectorAll('#layout-width-options .option-button').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('#layout-width-options .option-button').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
previewFrame.dataset.layoutWidth = btn.dataset.layoutWidth;
});
});
// Button style options
document.querySelectorAll('#button-style-options .option-button').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('#button-style-options .option-button').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
previewFrame.dataset.buttonStyle = btn.dataset.buttonStyle;
});
});
// Card shadow options
document.querySelectorAll('#card-shadow-options .option-button').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('#card-shadow-options .option-button').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
previewFrame.dataset.cardShadow = btn.dataset.cardShadow;
});
});
// Product text alignment options
document.querySelectorAll('#product-text-options .option-button').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('#product-text-options .option-button').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
previewFrame.dataset.productText = btn.dataset.productText;
});
});
// Gallery position options
document.querySelectorAll('#gallery-position-options .option-button').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('#gallery-position-options .option-button').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
previewFrame.dataset.galleryPosition = btn.dataset.galleryPosition;
});
});
// Show prices toggle
document.getElementById('show-prices-toggle').addEventListener('change', (e) => {
previewFrame.dataset.showPrices = e.target.checked;
});
// PDP toggles
document.getElementById('pdp-trust-toggle').addEventListener('change', (e) => {
previewFrame.dataset.pdpTrust = e.target.checked;
});
document.getElementById('pdp-reviews-toggle').addEventListener('change', (e) => {
previewFrame.dataset.pdpReviews = e.target.checked;
});
document.getElementById('pdp-related-toggle').addEventListener('change', (e) => {
previewFrame.dataset.pdpRelated = e.target.checked;
});
// Shop name input
shopNameInput.addEventListener('input', (e) => {
shopName = e.target.value || 'My Shop';
applySettings();
});
// Logo mode options
document.querySelectorAll('.logo-mode-option').forEach(option => {
option.addEventListener('click', () => {
const mode = option.dataset.mode;
logoMode = mode;
document.querySelectorAll('.logo-mode-option').forEach(opt => {
opt.classList.toggle('active', opt.dataset.mode === mode);
});
updateLogoDisplay();
});
});
// Logo upload
logoUpload.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
const browserFavicon = document.getElementById('browser-favicon');
logoIsSvg = file.type === 'image/svg+xml' || file.name.toLowerCase().endsWith('.svg');
if (logoIsSvg) {
// Read SVG as text so we can manipulate it
const textReader = new FileReader();
textReader.onload = (event) => {
logoSvgContent = event.target.result;
updateLogoImage();
logoPreviewImg.src = 'data:image/svg+xml,' + encodeURIComponent(logoSvgContent);
logoPreview.classList.add('has-image');
updateLogoDisplay();
// Update favicon with logo
browserFavicon.classList.add('has-logo');
let faviconImg = browserFavicon.querySelector('.favicon-logo');
if (!faviconImg) {
faviconImg = document.createElement('img');
faviconImg.className = 'favicon-logo';
browserFavicon.appendChild(faviconImg);
}
faviconImg.src = 'data:image/svg+xml,' + encodeURIComponent(logoSvgContent);
};
textReader.readAsText(file);
} else {
// Read as data URL for non-SVG images
logoSvgContent = null;
const reader = new FileReader();
reader.onload = (event) => {
logoUrl = event.target.result;
logoPreviewImg.src = logoUrl;
logoPreview.classList.add('has-image');
updateLogoDisplay();
// Update favicon with logo
browserFavicon.classList.add('has-logo');
let faviconImg = browserFavicon.querySelector('.favicon-logo');
if (!faviconImg) {
faviconImg = document.createElement('img');
faviconImg.className = 'favicon-logo';
browserFavicon.appendChild(faviconImg);
}
faviconImg.src = logoUrl;
};
reader.readAsDataURL(file);
}
});
// Header upload
headerUpload.addEventListener('change', (e) => {
const file = e.target.files[0];
if (!file) return;
const reader = new FileReader();
reader.onload = (event) => {
headerImageUrl = event.target.result;
headerPreviewImg.src = headerImageUrl;
headerPreview.classList.add('has-image');
updateLogoDisplay();
};
reader.readAsDataURL(file);
});
// Remove logo
document.getElementById('remove-logo').addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
logoUrl = null;
logoSvgContent = null;
logoIsSvg = false;
recolorLogo = false;
recolorLogoCheckbox.checked = false;
logoColorPickerRow.classList.remove('visible');
logoPreview.classList.remove('has-image');
logoUpload.value = '';
updateLogoDisplay();
});
// Remove header
document.getElementById('remove-header').addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
headerImageUrl = null;
headerPreview.classList.remove('has-image');
headerUpload.value = '';
updateLogoDisplay();
});
// Logo size slider
logoSizeSlider.addEventListener('input', (e) => {
logoSize = parseInt(e.target.value);
logoSizeValue.textContent = logoSize + 'px';
updateLogoDisplay();
});
// Recolor logo toggle
recolorLogoCheckbox.addEventListener('change', (e) => {
recolorLogo = e.target.checked;
logoColorPickerRow.classList.toggle('visible', recolorLogo);
updateLogoImage();
updateLogoDisplay();
});
// Logo color picker
logoColorPicker.addEventListener('input', (e) => {
logoColor = e.target.value;
logoColorValue.textContent = logoColor;
updateLogoImage();
updateLogoDisplay();
});
// Header horizontal position slider
headerPositionXSlider.addEventListener('input', (e) => {
headerPositionX = parseInt(e.target.value);
headerPositionXValue.textContent = headerPositionX + '%';
updateLogoDisplay();
});
// Header vertical position slider
headerPositionSlider.addEventListener('input', (e) => {
headerPositionY = parseInt(e.target.value);
headerPositionValue.textContent = headerPositionY + '%';
updateLogoDisplay();
});
// Header zoom slider
headerZoomSlider.addEventListener('input', (e) => {
headerZoom = parseInt(e.target.value);
headerZoomValue.textContent = headerZoom + '%';
// Only show horizontal position when zoomed in
headerPositionXRow.classList.toggle('visible', headerZoom > 100);
updateLogoDisplay();
});
// Initialize with Gallery preset
document.querySelector('[data-preset="gallery"]').click();
// Page view toggle
document.querySelectorAll('.page-toggle-btn').forEach(btn => {
btn.addEventListener('click', () => {
const page = btn.dataset.page;
document.querySelectorAll('.page-toggle-btn').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
document.querySelectorAll('.page-view').forEach(view => view.classList.remove('active'));
document.getElementById(page + '-view').classList.add('active');
});
});
// Feature toggles
document.getElementById('announcement-bar-toggle').addEventListener('change', (e) => {
showAnnouncementBar = e.target.checked;
document.getElementById('announcement-bar').classList.toggle('hidden', !showAnnouncementBar);
});
document.getElementById('sticky-header-toggle').addEventListener('change', (e) => {
stickyHeader = e.target.checked;
previewFrame.dataset.stickyHeader = stickyHeader;
});
document.getElementById('hover-image-toggle').addEventListener('change', (e) => {
hoverImage = e.target.checked;
previewFrame.dataset.hoverImage = hoverImage;
});
document.getElementById('quick-add-toggle').addEventListener('change', (e) => {
quickAdd = e.target.checked;
previewFrame.dataset.quickAdd = quickAdd;
});
// Initialize feature data attributes
previewFrame.dataset.hoverImage = hoverImage;
previewFrame.dataset.quickAdd = quickAdd;
previewFrame.dataset.aspect = aspectRatio;
previewFrame.dataset.fontSize = 'medium';
previewFrame.dataset.headingWeight = 'bold';
previewFrame.dataset.layoutWidth = 'wide';
previewFrame.dataset.buttonStyle = 'filled';
previewFrame.dataset.cardShadow = 'none';
previewFrame.dataset.productText = 'left';
previewFrame.dataset.galleryPosition = 'left';
previewFrame.dataset.showPrices = 'true';
previewFrame.dataset.pdpTrust = 'true';
previewFrame.dataset.pdpReviews = 'true';
previewFrame.dataset.pdpRelated = 'true';
// Aspect ratio options
document.querySelectorAll('#aspect-options .option-button').forEach(btn => {
btn.addEventListener('click', () => {
aspectRatio = btn.dataset.aspect;
previewFrame.dataset.aspect = aspectRatio;
document.querySelectorAll('#aspect-options .option-button').forEach(b => b.classList.remove('active'));
btn.classList.add('active');
});
});
// Customise section toggle
const customiseSection = document.getElementById('customise-section');
const customiseHeader = document.getElementById('customise-header');
customiseHeader.addEventListener('click', () => {
customiseSection.classList.toggle('open');
});
// Cart drawer
const cartDrawer = document.getElementById('cart-drawer');
const cartDrawerOverlay = document.getElementById('cart-drawer-overlay');
const cartBtn = document.getElementById('cart-btn');
const cartDrawerClose = document.getElementById('cart-drawer-close');
const viewCartLink = document.getElementById('view-cart-link');
function openCartDrawer() {
cartDrawer.classList.add('open');
cartDrawerOverlay.classList.add('open');
}
function closeCartDrawer() {
cartDrawer.classList.remove('open');
cartDrawerOverlay.classList.remove('open');
}
cartBtn.addEventListener('click', openCartDrawer);
cartDrawerClose.addEventListener('click', closeCartDrawer);
cartDrawerOverlay.addEventListener('click', closeCartDrawer);
viewCartLink.addEventListener('click', (e) => {
e.preventDefault();
closeCartDrawer();
document.querySelector('[data-page="cart"]').click();
});
// Quick add buttons open cart drawer
document.querySelectorAll('.quick-add-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
e.stopPropagation();
openCartDrawer();
});
});
// Search modal
const searchModal = document.getElementById('search-modal');
const searchBtn = document.getElementById('search-btn');
const searchClose = document.getElementById('search-close');
searchBtn.addEventListener('click', () => {
searchModal.classList.add('open');
searchModal.querySelector('.search-input').focus();
});
searchClose.addEventListener('click', () => {
searchModal.classList.remove('open');
});
searchModal.addEventListener('click', (e) => {
if (e.target === searchModal) {
searchModal.classList.remove('open');
}
});
// Escape key closes modals (dialog handles its own escape)
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
closeCartDrawer();
searchModal.classList.remove('open');
}
});
// PDP thumbnail gallery
const pdpMainImage = document.getElementById('pdp-main-image');
document.querySelectorAll('.pdp-thumb').forEach(thumb => {
thumb.addEventListener('click', () => {
// Update main image
const imageUrl = thumb.dataset.image;
pdpMainImage.style.backgroundImage = `url('${imageUrl}')`;
// Update active state
document.querySelectorAll('.pdp-thumb').forEach(t => t.classList.remove('active'));
thumb.classList.add('active');
});
});
// Lightbox - using native dialog
const lightbox = document.getElementById('lightbox');
const lightboxImage = document.getElementById('lightbox-image');
const lightboxCaption = document.getElementById('lightbox-caption');
const lightboxCounter = document.getElementById('lightbox-counter');
const lightboxClose = document.getElementById('lightbox-close');
const lightboxPrev = document.getElementById('lightbox-prev');
const lightboxNext = document.getElementById('lightbox-next');
const zoomBtn = document.getElementById('zoom-btn');
// Get all product images with metadata from thumbnails
function getLightboxImages() {
return Array.from(document.querySelectorAll('.pdp-thumb')).map(thumb => ({
src: thumb.dataset.image,
alt: thumb.dataset.alt || 'Product image',
description: thumb.dataset.description || ''
}));
}
let currentLightboxIndex = 0;
function openLightbox(startIndex = 0) {
const images = getLightboxImages();
currentLightboxIndex = startIndex;
updateLightboxImage(images);
lightbox.showModal();
}
function closeLightbox() {
lightbox.close();
zoomBtn.focus();
}
function updateLightboxImage(images) {
const current = images[currentLightboxIndex];
lightboxImage.src = current.src;
lightboxImage.alt = current.alt;
lightboxCaption.textContent = current.description;
lightboxCounter.textContent = `${currentLightboxIndex + 1} / ${images.length}`;
}
function nextImage() {
const images = getLightboxImages();
currentLightboxIndex = (currentLightboxIndex + 1) % images.length;
updateLightboxImage(images);
}
function prevImage() {
const images = getLightboxImages();
currentLightboxIndex = (currentLightboxIndex - 1 + images.length) % images.length;
updateLightboxImage(images);
}
// Open lightbox from zoom button or clicking main image
zoomBtn.addEventListener('click', (e) => {
e.stopPropagation();
const activeThumb = document.querySelector('.pdp-thumb.active');
const thumbs = Array.from(document.querySelectorAll('.pdp-thumb'));
const startIndex = thumbs.indexOf(activeThumb);
openLightbox(startIndex >= 0 ? startIndex : 0);
});
pdpMainImage.addEventListener('click', () => {
const activeThumb = document.querySelector('.pdp-thumb.active');
const thumbs = Array.from(document.querySelectorAll('.pdp-thumb'));
const startIndex = thumbs.indexOf(activeThumb);
openLightbox(startIndex >= 0 ? startIndex : 0);
});
lightboxClose.addEventListener('click', closeLightbox);
lightboxNext.addEventListener('click', nextImage);
lightboxPrev.addEventListener('click', prevImage);
// Close on clicking backdrop (native dialog handles escape key)
lightbox.addEventListener('click', (e) => {
if (e.target === lightbox) {
closeLightbox();
}
});
// Arrow key navigation
lightbox.addEventListener('keydown', (e) => {
if (e.key === 'ArrowRight') {
nextImage();
} else if (e.key === 'ArrowLeft') {
prevImage();
}
});
// Internal navigation links
function navigateToPage(pageId) {
document.querySelectorAll('.page-toggle-btn').forEach(b => b.classList.remove('active'));
document.querySelector(`[data-page="${pageId}"]`).classList.add('active');
document.querySelectorAll('.page-view').forEach(view => view.classList.remove('active'));
document.getElementById(pageId + '-view').classList.add('active');
}
document.querySelectorAll('[data-nav]').forEach(link => {
link.addEventListener('click', (e) => {
e.preventDefault();
navigateToPage(link.dataset.nav);
});
});
// Product cards link to PDP
document.querySelectorAll('.product-card').forEach(card => {
card.addEventListener('click', (e) => {
// Don't navigate if clicking quick-add button
if (e.target.closest('.quick-add-btn')) return;
navigateToPage('pdp');
});
});
// PDP Add to basket button opens cart drawer
document.querySelector('.pdp-add-btn').addEventListener('click', openCartDrawer);
// Cart state toggle (full/empty)
const cartStateToggle = document.getElementById('cart-state-toggle');
const cartFull = document.getElementById('cart-full');
const cartEmpty = document.getElementById('cart-empty');
let isCartEmpty = false;
cartStateToggle.addEventListener('click', () => {
isCartEmpty = !isCartEmpty;
cartFull.hidden = isCartEmpty;
cartEmpty.hidden = !isCartEmpty;
cartStateToggle.textContent = isCartEmpty ? 'Preview full cart' : 'Preview empty state';
});
< / script >
< / body >
< / html >