simpleshop_theme/priv/static/demo.html

6278 lines
204 KiB
HTML
Raw Normal View History

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SimpleShop Theme Studio v28</title>
<!-- 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 &amp; 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>
<span class="browser-url-text"><span class="domain" id="browser-domain">botanicalstudio</span>.simpleshop.uk</span>
</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>