← 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>