← back to Model Arena
data/artifacts/193230252710/grok.html
832 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Designer Wallcoverings — Mood Board</title>
<style>
:root {
--bg: #0d0d0f;
--surface: #17171c;
--ink: #1c1a17;
--text: #1c1a17;
--cream: #f5f2ea;
--muted: #8a8578;
--accent: #c9a961;
--accent-deep: #7d6a45;
--line: rgba(28, 26, 23, 0.12);
--line-light: rgba(245, 242, 234, 0.14);
--shadow: 0 12px 40px rgba(13, 13, 15, 0.18);
--shadow-soft: 0 4px 18px rgba(13, 13, 15, 0.08);
--ease: cubic-bezier(.22, .9, .24, 1);
--ease-scene: cubic-bezier(.77, 0, .18, 1);
--ease-hero: cubic-bezier(.22, 1, .36, 1);
--serif: 'Didot', 'Bodoni MT', 'Playfair Display', 'Cormorant Garamond', Georgia, 'Times New Roman', serif;
--sans: 'Avenir Next', 'Century Gothic', Futura, 'Segoe UI', 'Helvetica Neue', sans-serif;
--canvas-w: 1080px;
--canvas-h: 1350px;
}
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body {
min-height: 100%;
background: var(--bg);
color: var(--cream);
font-family: var(--sans);
font-size: 16px;
line-height: 1.55;
-webkit-font-smoothing: antialiased;
}
/* ——— Header / Wordmark ——— */
.site-header {
position: fixed;
top: 0; left: 0; right: 0;
z-index: 100;
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
padding: 18px 32px;
background: rgba(13, 13, 15, 0.92);
border-bottom: 1px solid var(--line-light);
backdrop-filter: blur(12px);
}
.wordmark {
font-family: var(--serif);
font-size: 42px;
font-weight: 400;
letter-spacing: 0.18em;
text-transform: uppercase;
color: var(--cream);
line-height: 1.15;
white-space: normal;
max-width: min(720px, 58vw);
}
.header-actions {
display: flex;
align-items: center;
gap: 10px;
flex-shrink: 0;
}
.theme-select {
appearance: none;
background: transparent;
border: 1px solid var(--line-light);
color: var(--cream);
font-family: var(--sans);
font-size: 11px;
font-weight: 500;
letter-spacing: 0.16em;
text-transform: uppercase;
padding: 10px 36px 10px 14px;
border-radius: 2px;
cursor: pointer;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath fill='%8a8578' d='M0 0l5 6 5-6z'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 12px center;
transition: border-color 0.35s var(--ease);
}
.theme-select:hover { border-color: var(--accent); }
.theme-select option { background: var(--surface); color: var(--cream); }
.btn {
font-family: var(--sans);
font-size: 11px;
font-weight: 500;
letter-spacing: 0.16em;
text-transform: uppercase;
padding: 10px 18px;
border: 1px solid var(--accent);
background: var(--accent);
color: var(--ink);
border-radius: 2px;
cursor: pointer;
transition: transform 0.4s var(--ease), background 0.35s var(--ease), box-shadow 0.35s var(--ease);
box-shadow: var(--shadow-soft);
}
.btn:hover {
background: transparent;
color: var(--accent);
transform: translateY(-1px);
}
.btn:active { transform: translateY(0); }
/* ——— Stage ——— */
.stage {
min-height: 100vh;
padding: 120px 24px 64px;
display: flex;
flex-direction: column;
align-items: center;
gap: 28px;
}
.meta-row {
width: min(100%, calc(var(--canvas-w) * 0.55));
display: flex;
justify-content: space-between;
align-items: baseline;
color: var(--muted);
font-size: 11px;
letter-spacing: 0.18em;
text-transform: uppercase;
}
.meta-row .theme-label {
color: var(--accent);
font-family: var(--serif);
font-size: 14px;
letter-spacing: 0.22em;
}
/* ——— Canvas ——— */
.canvas-wrap {
width: min(100%, 540px);
aspect-ratio: 1080 / 1350;
position: relative;
box-shadow: var(--shadow), 0 0 0 1px rgba(201, 169, 97, 0.15);
}
.board {
width: 100%;
height: 100%;
position: relative;
overflow: hidden;
background: var(--board-bg, #f3efe6);
color: var(--board-ink, #1c1a17);
transition: opacity 0.45s var(--ease-scene), transform 0.55s var(--ease-hero);
}
.board.is-exiting {
opacity: 0;
transform: scale(0.985) translateY(8px);
}
.board.is-entering {
opacity: 0;
transform: scale(1.015) translateY(-6px);
}
.board-inner {
position: absolute;
inset: 0;
display: grid;
grid-template-columns: 1.15fr 0.85fr;
grid-template-rows: 1.35fr 0.72fr 0.78fr;
gap: 14px;
padding: 28px;
}
/* cells */
.cell {
position: relative;
overflow: hidden;
border: 1px solid var(--board-line, rgba(28,26,23,0.1));
background: var(--board-surface, #ebe6db);
transition: opacity 0.6s var(--ease-hero), transform 0.7s var(--ease-hero);
}
.hero {
grid-column: 1 / 2;
grid-row: 1 / 2;
background: var(--board-surface, #ebe6db);
}
.chips {
grid-column: 2 / 3;
grid-row: 1 / 2;
display: flex;
flex-direction: column;
gap: 10px;
padding: 14px;
background: transparent;
border: none;
overflow: visible;
}
.chip {
flex: 1;
position: relative;
border: 1px solid var(--board-line, rgba(28,26,23,0.1));
min-height: 0;
box-shadow: var(--shadow-soft);
transition: transform 0.55s var(--ease);
}
.chip:hover { transform: translateY(-2px); }
.chip-name {
position: absolute;
left: 12px;
bottom: 10px;
font-family: var(--sans);
font-size: 9px;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--chip-label, rgba(245,242,234,0.92));
text-shadow: 0 1px 2px rgba(0,0,0,0.25);
pointer-events: none;
}
.type-pair {
grid-column: 1 / 2;
grid-row: 2 / 3;
display: flex;
flex-direction: column;
justify-content: center;
padding: 22px 26px;
background: var(--board-bg, #f3efe6);
gap: 8px;
}
.type-display {
font-family: var(--serif);
font-size: clamp(28px, 4.2vw, 42px);
line-height: 1.08;
letter-spacing: -0.02em;
color: var(--board-ink, #1c1a17);
}
.type-body {
font-family: var(--sans);
font-size: clamp(11px, 1.35vw, 13px);
letter-spacing: 0.04em;
color: var(--board-muted, #6e695c);
max-width: 36ch;
line-height: 1.55;
}
.type-meta {
margin-top: 6px;
font-size: 9px;
letter-spacing: 0.2em;
text-transform: uppercase;
color: var(--accent-deep);
}
.texture {
grid-column: 2 / 3;
grid-row: 2 / 3;
background: var(--board-surface, #ebe6db);
}
.footer-band {
grid-column: 1 / 3;
grid-row: 3 / 4;
display: grid;
grid-template-columns: 1fr 1.1fr;
gap: 14px;
border: none;
background: transparent;
overflow: visible;
}
.motif {
border: 1px solid var(--board-line, rgba(28,26,23,0.1));
background: var(--board-surface, #ebe6db);
overflow: hidden;
position: relative;
}
.caption-block {
border: 1px solid var(--board-line, rgba(28,26,23,0.1));
background: var(--board-bg, #f3efe6);
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 20px 22px;
gap: 6px;
}
.caption-kicker {
font-size: 9px;
letter-spacing: 0.22em;
text-transform: uppercase;
color: var(--accent-deep);
}
.caption-title {
font-family: var(--serif);
font-size: clamp(20px, 2.8vw, 28px);
line-height: 1.15;
color: var(--board-ink, #1c1a17);
letter-spacing: 0.01em;
}
.caption-sub {
font-size: 11px;
color: var(--board-muted, #6e695c);
letter-spacing: 0.06em;
max-width: 32ch;
}
/* brand stamp */
.stamp {
position: absolute;
top: 18px;
right: 22px;
font-family: var(--sans);
font-size: 8px;
letter-spacing: 0.28em;
text-transform: uppercase;
color: var(--board-muted, #6e695c);
opacity: 0.7;
z-index: 5;
pointer-events: none;
}
/* ——— Pattern fills (CSS / SVG via background) ——— */
.pat {
position: absolute;
inset: 0;
}
/* Classic damask-like diamonds */
.pat-damask {
background-color: var(--p-bg);
background-image:
radial-gradient(ellipse 40% 30% at 50% 50%, var(--p-fg) 0%, transparent 70%),
repeating-linear-gradient(45deg, transparent, transparent 18px, var(--p-line) 18px, var(--p-line) 19px),
repeating-linear-gradient(-45deg, transparent, transparent 18px, var(--p-line) 18px, var(--p-line) 19px);
background-size: 100% 100%, 36px 36px, 36px 36px;
}
/* Classic toile / medallion rings */
.pat-medallion {
background-color: var(--p-bg);
background-image:
radial-gradient(circle at 50% 50%, transparent 28%, var(--p-line) 28.5%, var(--p-line) 29.5%, transparent 30%),
radial-gradient(circle at 50% 50%, transparent 12%, var(--p-fg) 12.5%, var(--p-fg) 14%, transparent 14.5%),
radial-gradient(circle at 0% 0%, transparent 28%, var(--p-line) 28.5%, var(--p-line) 29.5%, transparent 30%),
radial-gradient(circle at 100% 0%, transparent 28%, var(--p-line) 28.5%, var(--p-line) 29.5%, transparent 30%),
radial-gradient(circle at 0% 100%, transparent 28%, var(--p-line) 28.5%, var(--p-line) 29.5%, transparent 30%),
radial-gradient(circle at 100% 100%, transparent 28%, var(--p-line) 28.5%, var(--p-line) 29.5%, transparent 30%);
background-size: 80px 80px;
}
/* Coastal wave stripes */
.pat-wave {
background-color: var(--p-bg);
background-image:
repeating-linear-gradient(
180deg,
transparent 0px,
transparent 10px,
var(--p-line) 10px,
var(--p-line) 11px,
transparent 11px,
transparent 22px,
var(--p-fg) 22px,
var(--p-fg) 24px
);
mask-image: radial-gradient(ellipse 120% 80% at 50% 100%, black 30%, transparent 75%);
}
.pat-coastal-geo {
background-color: var(--p-bg);
background-image:
linear-gradient(30deg, var(--p-line) 1px, transparent 1px),
linear-gradient(150deg, var(--p-line) 1px, transparent 1px),
linear-gradient(90deg, transparent 48%, var(--p-fg) 48%, var(--p-fg) 52%, transparent 52%);
background-size: 28px 48px, 28px 48px, 56px 100%;
}
/* Moody velvet rib */
.pat-velvet {
background-color: var(--p-bg);
background-image:
repeating-linear-gradient(
90deg,
transparent 0px,
transparent 3px,
var(--p-line) 3px,
var(--p-line) 4px
),
linear-gradient(180deg, var(--p-fg), transparent 60%);
}
.pat-moody-ornament {
background-color: var(--p-bg);
background-image:
radial-gradient(circle at 25% 25%, var(--p-fg) 0 2px, transparent 2.5px),
radial-gradient(circle at 75% 75%, var(--p-fg) 0 2px, transparent 2.5px),
radial-gradient(ellipse 50% 30% at 50% 50%, transparent 40%, var(--p-line) 41%, var(--p-line) 42%, transparent 43%),
linear-gradient(135deg, transparent 48%, var(--p-line) 49%, var(--p-line) 51%, transparent 52%);
background-size: 40px 40px, 40px 40px, 80px 80px, 40px 40px;
}
/* Botanical leaf lattice */
.pat-botanical {
background-color: var(--p-bg);
background-image:
radial-gradient(ellipse 18px 28px at 30% 40%, var(--p-fg) 0 40%, transparent 42%),
radial-gradient(ellipse 14px 22px at 70% 65%, var(--p-fg) 0 38%, transparent 40%),
radial-gradient(ellipse 10px 16px at 55% 25%, var(--p-line) 0 35%, transparent 38%),
radial-gradient(circle at 20% 80%, var(--p-line) 0 1.5px, transparent 2px);
background-size: 64px 72px;
}
.pat-vine {
background-color: var(--p-bg);
background-image:
repeating-linear-gradient(0deg, transparent 0 26px, var(--p-line) 26px 27px),
radial-gradient(ellipse 12px 8px at 50% 13px, var(--p-fg) 0 55%, transparent 58%),
radial-gradient(ellipse 8px 12px at 35% 40px, var(--p-fg) 0 50%, transparent 53%),
radial-gradient(ellipse 8px 12px at 65% 40px, var(--p-fg) 0 50%, transparent 53%);
background-size: 48px 54px;
}
/* Texture tiles */
.tex-linen {
background-color: var(--p-bg);
background-image:
repeating-linear-gradient(0deg, var(--p-line) 0 1px, transparent 1px 3px),
repeating-linear-gradient(90deg, var(--p-line) 0 1px, transparent 1px 4px);
opacity: 0.95;
}
.tex-silk {
background:
linear-gradient(115deg, transparent 40%, var(--p-fg) 50%, transparent 60%),
linear-gradient(180deg, var(--p-bg), color-mix(in srgb, var(--p-bg) 70%, #000 30%));
background-size: 40% 100%, 100% 100%;
}
.tex-grasscloth {
background-color: var(--p-bg);
background-image:
repeating-linear-gradient(90deg, transparent 0 2px, var(--p-line) 2px 3px),
repeating-linear-gradient(0deg, transparent 0 7px, var(--p-fg) 7px 8px);
}
.tex-plaster {
background-color: var(--p-bg);
background-image:
radial-gradient(circle at 20% 30%, var(--p-line) 0 1px, transparent 1.5px),
radial-gradient(circle at 70% 60%, var(--p-line) 0 0.8px, transparent 1.2px),
radial-gradient(circle at 40% 80%, var(--p-fg) 0 1.2px, transparent 1.8px),
radial-gradient(circle at 85% 25%, var(--p-line) 0 0.6px, transparent 1px);
background-size: 24px 24px, 18px 18px, 30px 30px, 16px 16px;
}
/* SVG overlay patterns for hero richness */
.hero-svg {
position: absolute;
inset: 0;
width: 100%;
height: 100%;
opacity: 0.55;
pointer-events: none;
}
.board-frame {
position: absolute;
inset: 12px;
border: 1px solid var(--board-frame, rgba(28,26,23,0.08));
pointer-events: none;
z-index: 4;
}
/* footer note */
.foot-note {
color: var(--muted);
font-size: 10px;
letter-spacing: 0.14em;
text-transform: uppercase;
text-align: center;
}
@media (max-width: 720px) {
.wordmark { font-size: 28px; letter-spacing: 0.14em; max-width: 48vw; }
.site-header { padding: 14px 16px; }
.stage { padding-top: 100px; }
}
</style>
</head>
<body>
<header class="site-header">
<div class="wordmark">Designer Wallcoverings</div>
<div class="header-actions">
<select class="theme-select" id="themeSelect" aria-label="Style theme">
<option value="Classic">Classic</option>
<option value="Coastal">Coastal</option>
<option value="Moody">Moody</option>
<option value="Botanical">Botanical</option>
</select>
<button class="btn" id="regenBtn" type="button">Regenerate</button>
</div>
</header>
<main class="stage">
<div class="meta-row">
<span>Mood Board · 1080×1350</span>
<span class="theme-label" id="themeLabel">Classic</span>
</div>
<div class="canvas-wrap">
<div class="board" id="board" aria-live="polite">
<!-- filled by JS -->
</div>
</div>
<p class="foot-note">Curated finishes · Share-ready composition</p>
</main>
<script>
(function () {
const THEMES = {
Classic: {
boardBg: '#f3efe6',
boardSurface: '#e8e2d4',
boardInk: '#1c1a17',
boardMuted: '#6e695c',
boardLine: 'rgba(28,26,23,0.10)',
boardFrame: 'rgba(28,26,23,0.08)',
accentDeep: '#7d6a45',
chips: [
{ c: '#1c1a17', n: 'Obsidian' },
{ c: '#c9a961', n: 'Gilded Leaf' },
{ c: '#8b7355', n: 'Antique Bronze' }
],
heroes: ['damask', 'medallion'],
textures: ['linen', 'silk'],
motifs: ['medallion', 'damask'],
typeDisplay: ['Heritage', 'Atelier', 'Estate', 'Regency'],
typeBody: [
'A refined damask rhythm for formal salons and paneled libraries.',
'Quiet ornament and warm ivory grounds for enduring interiors.',
'Classical proportion with a modern, restrained hand.'
],
titles: ['Gilded Salon', 'Ivory Estate', 'Panel & Plume', 'The Long Gallery'],
subs: ['Damask · Metallic ink', 'Handed repeat · Matte ground', 'Linen weave · Soft sheen'],
pHero: { bg: '#e8e2d4', fg: 'rgba(125,106,69,0.28)', line: 'rgba(28,26,23,0.10)' },
pTex: { bg: '#ddd4c2', fg: 'rgba(201,169,97,0.25)', line: 'rgba(28,26,23,0.08)' },
pMotif: { bg: '#1c1a17', fg: 'rgba(201,169,97,0.35)', line: 'rgba(245,242,234,0.12)' }
},
Coastal: {
boardBg: '#f2f4f1',
boardSurface: '#e4ebe6',
boardInk: '#1a2422',
boardMuted: '#5f6e68',
boardLine: 'rgba(26,36,34,0.10)',
boardFrame: 'rgba(26,36,34,0.08)',
accentDeep: '#3d6b6a',
chips: [
{ c: '#1a3a3a', n: 'Tidepool' },
{ c: '#c4a882', n: 'Driftwood' },
{ c: '#7a9e9a', n: 'Sea Glass' }
],
heroes: ['wave', 'coastal-geo'],
textures: ['grasscloth', 'linen'],
motifs: ['coastal-geo', 'wave'],
typeDisplay: ['Horizon', 'Seaglass', 'Harbor', 'Dune'],
typeBody: [
'Salt-air neutrals and soft geometry for light-filled rooms.',
'Woven grasscloth texture with a calm coastal cadence.',
'Breezy repeats that feel collected, never nautical-cliché.'
],
titles: ['Morning Harbor', 'Pale Current', 'Shore House', 'Glass & Linen'],
subs: ['Wave stripe · Mineral dye', 'Grasscloth · Open weave', 'Soft geometry · Mist ground'],
pHero: { bg: '#dfe8e4', fg: 'rgba(61,107,106,0.22)', line: 'rgba(26,36,34,0.10)' },
pTex: { bg: '#d2ddd6', fg: 'rgba(122,158,154,0.3)', line: 'rgba(26,36,34,0.09)' },
pMotif: { bg: '#1a3a3a', fg: 'rgba(196,168,130,0.35)', line: 'rgba(242,244,241,0.12)' }
},
Moody: {
boardBg: '#1a1816',
boardSurface: '#24201c',
boardInk: '#f0ebe3',
boardMuted: '#9a9286',
boardLine: 'rgba(240,235,227,0.10)',
boardFrame: 'rgba(240,235,227,0.08)',
accentDeep: '#c9a961',
chips: [
{ c: '#0d0d0f', n: 'Noir' },
{ c: '#6b2c3e', n: 'Burgundy Smoke' },
{ c: '#c9a961', n: 'Candlelight' }
],
heroes: ['velvet', 'moody-ornament'],
textures: ['silk', 'plaster'],
motifs: ['moody-ornament', 'velvet'],
typeDisplay: ['Nocturne', 'Velvet', 'Ember', 'After Dark'],
typeBody: [
'Deep grounds and candlelit metallics for intimate chambers.',
'A velvet rib and quiet ornament — drama without noise.',
'Shadowed palettes that flatter brass, stone, and low light.'
],
titles: ['Midnight Library', 'Ember Chamber', 'Low Light', 'Velvet Hour'],
subs: ['Ribbed velvet · Deep ground', 'Ornament · Soft gold', 'Plaster grain · Smoke'],
pHero: { bg: '#1f1b18', fg: 'rgba(201,169,97,0.18)', line: 'rgba(240,235,227,0.08)' },
pTex: { bg: '#2a2420', fg: 'rgba(107,44,62,0.35)', line: 'rgba(240,235,227,0.06)' },
pMotif: { bg: '#6b2c3e', fg: 'rgba(201,169,97,0.3)', line: 'rgba(240,235,227,0.1)' }
},
Botanical: {
boardBg: '#f4f1ea',
boardSurface: '#e7e4d8',
boardInk: '#1c2118',
boardMuted: '#6a705f',
boardLine: 'rgba(28,33,24,0.10)',
boardFrame: 'rgba(28,33,24,0.08)',
accentDeep: '#5c6b45',
chips: [
{ c: '#2f3b28', n: 'Boxwood' },
{ c: '#a67c52', n: 'Sienna Stem' },
{ c: '#c4b59a', n: 'Dried Fern' }
],
heroes: ['botanical', 'vine'],
textures: ['linen', 'grasscloth'],
motifs: ['vine', 'botanical'],
typeDisplay: ['Herbarium', 'Trellis', 'Orangery', 'Wildwood'],
typeBody: [
'Pressed-leaf motifs and garden greens for airy living spaces.',
'A living trellis — botanical without the cottage cliché.',
'Earthy pigments and linen grounds, collected from the orangery.'
],
titles: ['Glasshouse', 'Fern Study', 'Trellis Room', 'Orangery Wall'],
subs: ['Leaf lattice · Soft pigment', 'Vine repeat · Linen ground', 'Herbarium · Matte finish'],
pHero: { bg: '#e3e6d8', fg: 'rgba(92,107,69,0.28)', line: 'rgba(28,33,24,0.1)' },
pTex: { bg: '#d8d4c4', fg: 'rgba(166,124,82,0.25)', line: 'rgba(28,33,24,0.08)' },
pMotif: { bg: '#2f3b28', fg: 'rgba(196,181,154,0.35)', line: 'rgba(244,241,234,0.1)' }
}
};
const PAT_CLASS = {
damask: 'pat-damask',
medallion: 'pat-medallion',
wave: 'pat-wave',
'coastal-geo': 'pat-coastal-geo',
velvet: 'pat-velvet',
'moody-ornament': 'pat-moody-ornament',
botanical: 'pat-botanical',
vine: 'pat-vine',
linen: 'tex-linen',
silk: 'tex-silk',
grasscloth: 'tex-grasscloth',
plaster: 'tex-plaster'
};
function pick(arr) { return arr[Math.floor(Math.random() * arr.length)]; }
function heroSVG(kind, ink) {
const stroke = ink || 'rgba(28,26,23,0.18)';
if (kind === 'damask' || kind === 'medallion') {
return `<svg class="hero-svg" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice">
<defs>
<pattern id="hx" width="64" height="64" patternUnits="userSpaceOnUse">
<path d="M32 4 L52 20 L52 44 L32 60 L12 44 L12 20 Z" fill="none" stroke="${stroke}" stroke-width="0.8"/>
<circle cx="32" cy="32" r="6" fill="none" stroke="${stroke}" stroke-width="0.6"/>
<path d="M32 18 V46 M18 32 H46" stroke="${stroke}" stroke-width="0.4" opacity="0.7"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#hx)"/>
</svg>`;
}
if (kind === 'wave' || kind === 'coastal-geo') {
return `<svg class="hero-svg" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none">
<defs>
<pattern id="wv" width="120" height="40" patternUnits="userSpaceOnUse">
<path d="M0 20 Q30 0 60 20 T120 20" fill="none" stroke="${stroke}" stroke-width="1"/>
<path d="M0 28 Q30 12 60 28 T120 28" fill="none" stroke="${stroke}" stroke-width="0.5" opacity="0.6"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#wv)"/>
</svg>`;
}
if (kind === 'velvet' || kind === 'moody-ornament') {
return `<svg class="hero-svg" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice">
<defs>
<pattern id="mo" width="80" height="80" patternUnits="userSpaceOnUse">
<path d="M40 10 C55 25 55 55 40 70 C25 55 25 25 40 10Z" fill="none" stroke="${stroke}" stroke-width="0.7"/>
<circle cx="40" cy="40" r="3" fill="${stroke}"/>
<path d="M10 40 H30 M50 40 H70 M40 10 V30 M40 50 V70" stroke="${stroke}" stroke-width="0.4" opacity="0.5"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#mo)"/>
</svg>`;
}
// botanical / vine
return `<svg class="hero-svg" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid slice">
<defs>
<pattern id="bt" width="70" height="90" patternUnits="userSpaceOnUse">
<path d="M35 8 C48 28 48 50 35 82 C22 50 22 28 35 8Z" fill="none" stroke="${stroke}" stroke-width="0.8"/>
<path d="M35 30 C50 34 58 48 52 60" fill="none" stroke="${stroke}" stroke-width="0.55"/>
<path d="M35 42 C20 46 12 58 18 70" fill="none" stroke="${stroke}" stroke-width="0.55"/>
<circle cx="52" cy="62" r="2" fill="${stroke}" opacity="0.5"/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#bt)"/>
</svg>`;
}
function patternDiv(name, p) {
const cls = PAT_CLASS[name] || 'pat-damask';
return `<div class="pat ${cls}" style="--p-bg:${p.bg};--p-fg:${p.fg};--p-line:${p.line}"></div>`;
}
function chipLabelColor(hex) {
const c = hex.replace('#','');
const r = parseInt(c.slice(0,2),16), g = parseInt(c.slice(2,4),16), b = parseInt(c.slice(4,6),16);
const l = (0.299*r + 0.587*g + 0.114*b) / 255;
return l > 0.55 ? 'rgba(28,26,23,0.85)' : 'rgba(245,242,234,0.92)';
}
function buildBoard(themeName) {
const T = THEMES[themeName];
const hero = pick(T.heroes);
const tex = pick(T.textures);
const motif = pick(T.motifs.filter(m => m !== hero).concat(T.motifs));
const chips = T.chips;
// slight chip shuffle of order for variety while staying on-brand
const order = [0,1,2].sort(() => Math.random() - 0.5);
const orderedChips = order.map(i => chips[i]);
const display = pick(T.typeDisplay);
const body = pick(T.typeBody);
const title = pick(T.titles);
const sub = pick(T.subs);
const svgInk = themeName === 'Moody'
? 'rgba(201,169,97,0.22)'
: 'rgba(28,26,23,0.14)';
const root = document.getElementById('board');
root.style.setProperty('--board-bg', T.boardBg);
root.style.setProperty('--board-surface', T.boardSurface);
root.style.setProperty('--board-ink', T.boardInk);
root.style.setProperty('--board-muted', T.boardMuted);
root.style.setProperty('--board-line', T.boardLine);
root.style.setProperty('--board-frame', T.boardFrame);
root.style.setProperty('--accent-deep', T.accentDeep);
root.innerHTML = `
<div class="board-frame"></div>
<div class="stamp">DW · ${themeName}</div>
<div class="board-inner">
<div class="cell hero">
${patternDiv(hero, T.pHero)}
${heroSVG(hero, svgInk)}
</div>
<div class="chips">
${orderedChips.map(ch => `
<div class="chip" style="background:${ch.c}">
<span class="chip-name" style="color:${chipLabelColor(ch.c)}">${ch.n}</span>
</div>`).join('')}
</div>
<div class="cell type-pair">
<div class="type-display">${display}</div>
<div class="type-body">${body}</div>
<div class="type-meta">Serif display · Refined sans</div>
</div>
<div class="cell texture">
${patternDiv(tex, T.pTex)}
</div>
<div class="footer-band">
<div class="motif">
${patternDiv(motif, T.pMotif)}
</div>
<div class="caption-block">
<div class="caption-kicker">Collection</div>
<div class="caption-title">${title}</div>
<div class="caption-sub">${sub}</div>
</div>
</div>
</div>
`;
}
let busy = false;
const board = document.getElementById('board');
const themeSelect = document.getElementById('themeSelect');
const themeLabel = document.getElementById('themeLabel');
function regenerate(withMotion) {
if (busy) return;
const theme = themeSelect.value;
themeLabel.textContent = theme;
if (!withMotion) {
buildBoard(theme);
return;
}
busy = true;
board.classList.remove('is-entering');
board.classList.add('is-exiting');
window.setTimeout(() => {
buildBoard(theme);
board.classList.remove('is-exiting');
board.classList.add('is-entering');
requestAnimationFrame(() => {
requestAnimationFrame(() => {
board.classList.remove('is-entering');
board.style.opacity = '1';
board.style.transform = 'none';
busy = false;
});
});
}, 420);
}
document.getElementById('regenBtn').addEventListener('click', () => regenerate(true));
themeSelect.addEventListener('change', () => regenerate(true));
// initial
regenerate(false);
})();
</script>
</body>
</html>