← back to Brand Selector Viewer
initial snapshot — gitify all builds (CLAUDE.md rule 2026-05-06)
75494628b52e61b1e0c7784aa845b4360ef8ad2c · 2026-05-06 10:20:33 -0700 · Steve
Files touched
A .gitignoreA horsemen.htmlA index.htmlA magic-app.htmlA outputs/canva-brief.mdA outputs/figma-tokens.jsonA outputs/magic-BrandSelector.tsxA outputs/paper-summary.md
Diff
commit 75494628b52e61b1e0c7784aa845b4360ef8ad2c
Author: Steve <steve@designerwallcoverings.com>
Date: Wed May 6 10:20:33 2026 -0700
initial snapshot — gitify all builds (CLAUDE.md rule 2026-05-06)
---
.gitignore | 25 ++
horsemen.html | 530 ++++++++++++++++++++++++++++++++++++++++
index.html | 486 ++++++++++++++++++++++++++++++++++++
magic-app.html | 234 ++++++++++++++++++
outputs/canva-brief.md | 44 ++++
outputs/figma-tokens.json | 109 +++++++++
outputs/magic-BrandSelector.tsx | 189 ++++++++++++++
outputs/paper-summary.md | 34 +++
8 files changed, 1651 insertions(+)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..9ae81e0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,25 @@
+node_modules/
+.env
+.env.*
+!.env.example
+tmp/
+*.log
+.DS_Store
+dist/
+build/
+.next/
+.cache/
+.parcel-cache/
+coverage/
+__pycache__/
+*.pyc
+*.pyo
+.venv/
+venv/
+.pytest_cache/
+.ruff_cache/
+.idea/
+.vscode/
+*.swp
+.qodo/
+out/
diff --git a/horsemen.html b/horsemen.html
new file mode 100644
index 0000000..565c012
--- /dev/null
+++ b/horsemen.html
@@ -0,0 +1,530 @@
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8" />
+<meta name="viewport" content="width=device-width,initial-scale=1" />
+<title>Four Horsemen — Brand Selector</title>
+<style>
+ :root {
+ --bg: #0b0b0d;
+ --bg-2: #131318;
+ --bg-3: #1a1a22;
+ --line: #2a2a35;
+ --text: #f4f4f5;
+ --muted: #9c9caa;
+ --accent: #d4af37;
+ --paper: #ff8a50;
+ --figma: #a259ff;
+ --magic: #4ade80;
+ --canva: #00c4cc;
+ }
+ * { box-sizing: border-box; }
+ html, body { margin: 0; padding: 0; background: var(--bg); color: var(--text); }
+ body {
+ font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Arial, sans-serif;
+ -webkit-font-smoothing: antialiased;
+ min-height: 100vh;
+ }
+ header {
+ padding: 36px 40px 22px;
+ border-bottom: 1px solid var(--line);
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ gap: 24px;
+ flex-wrap: wrap;
+ }
+ header .title {
+ font-family: "Didot", "Bodoni 72", Georgia, serif;
+ font-weight: 400;
+ font-size: 34px;
+ letter-spacing: 0.5px;
+ margin: 0;
+ }
+ header .eyebrow {
+ color: var(--accent);
+ font-size: 10px;
+ letter-spacing: 3px;
+ text-transform: uppercase;
+ margin-bottom: 6px;
+ }
+ header .sub {
+ color: var(--muted);
+ font-size: 13px;
+ margin-top: 6px;
+ font-style: italic;
+ }
+ .legend {
+ display: flex;
+ gap: 16px;
+ flex-wrap: wrap;
+ }
+ .legend .chip {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ font-size: 11px;
+ color: var(--muted);
+ text-transform: uppercase;
+ letter-spacing: 1.5px;
+ }
+ .legend .dot {
+ width: 8px; height: 8px; border-radius: 50%;
+ }
+
+ main {
+ padding: 28px;
+ display: grid;
+ gap: 22px;
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ }
+ @media (max-width: 1100px) { main { grid-template-columns: 1fr; } }
+
+ .pane {
+ background: var(--bg-2);
+ border: 1px solid var(--line);
+ border-radius: 16px;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ min-height: 580px;
+ }
+ .pane-head {
+ padding: 16px 20px;
+ border-bottom: 1px solid var(--line);
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ background: linear-gradient(180deg, rgba(255,255,255,0.02), transparent);
+ }
+ .pane-head .badge {
+ width: 30px; height: 30px;
+ border-radius: 8px;
+ display: flex; align-items: center; justify-content: center;
+ font-family: "Didot", Georgia, serif;
+ font-size: 16px;
+ color: #0b0b0d;
+ font-weight: 700;
+ }
+ .pane-head h2 {
+ margin: 0;
+ font-family: "Didot", Georgia, serif;
+ font-weight: 400;
+ font-size: 20px;
+ }
+ .pane-head .role {
+ color: var(--muted);
+ font-size: 11px;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ }
+ .pane-head .status {
+ margin-left: auto;
+ font-size: 10px;
+ text-transform: uppercase;
+ letter-spacing: 1.5px;
+ padding: 4px 10px;
+ border-radius: 999px;
+ border: 1px solid var(--line);
+ color: var(--muted);
+ }
+ .pane-head .status.live { color: var(--magic); border-color: var(--magic); }
+ .pane-head .status.partial { color: var(--accent); border-color: var(--accent); }
+ .pane-head .status.spec { color: #9bb5ff; border-color: #9bb5ff; }
+ .pane-head .status.brief { color: var(--canva); border-color: var(--canva); }
+
+ .pane-body {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ min-height: 0;
+ }
+ .preview {
+ background: #07070a;
+ border-bottom: 1px solid var(--line);
+ min-height: 260px;
+ overflow: hidden;
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+ .preview iframe {
+ width: 100%;
+ height: 320px;
+ border: 0;
+ background: #07070a;
+ }
+ .preview .placeholder {
+ padding: 32px;
+ color: var(--muted);
+ text-align: center;
+ }
+ .preview img { max-width: 100%; max-height: 320px; display: block; }
+
+ .meta {
+ padding: 16px 20px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ }
+ .meta .row {
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+ gap: 12px;
+ font-size: 12px;
+ }
+ .meta .row .k {
+ color: var(--muted);
+ text-transform: uppercase;
+ letter-spacing: 1.5px;
+ font-size: 10px;
+ }
+ .meta .row .v {
+ color: var(--text);
+ font-family: "SF Mono", Menlo, monospace;
+ font-size: 11px;
+ text-align: right;
+ word-break: break-all;
+ }
+ .meta a {
+ color: var(--accent);
+ text-decoration: none;
+ font-family: "SF Mono", Menlo, monospace;
+ font-size: 11px;
+ word-break: break-all;
+ }
+ .meta a:hover { text-decoration: underline; }
+
+ .actions {
+ padding: 14px 20px;
+ border-top: 1px solid var(--line);
+ display: flex;
+ gap: 8px;
+ flex-wrap: wrap;
+ background: var(--bg-3);
+ }
+ .btn {
+ background: var(--bg-2);
+ border: 1px solid var(--line);
+ color: var(--text);
+ padding: 7px 12px;
+ border-radius: 999px;
+ font-size: 11px;
+ letter-spacing: 0.5px;
+ cursor: pointer;
+ text-decoration: none;
+ display: inline-block;
+ }
+ .btn:hover { border-color: var(--accent); color: var(--accent); }
+ .btn.primary {
+ background: var(--accent); color: #111; border-color: var(--accent);
+ }
+
+ .code {
+ background: #07070a;
+ border-top: 1px solid var(--line);
+ padding: 14px 20px;
+ overflow: auto;
+ max-height: 220px;
+ font-family: "SF Mono", Menlo, monospace;
+ font-size: 11px;
+ line-height: 1.55;
+ color: #d4d4d8;
+ white-space: pre;
+ }
+
+ /* Color-coded pane accents */
+ .pane.paper .pane-head .badge { background: var(--paper); }
+ .pane.figma .pane-head .badge { background: var(--figma); color: #fff; }
+ .pane.magic .pane-head .badge { background: var(--magic); }
+ .pane.canva .pane-head .badge { background: var(--canva); }
+
+ .pane.paper { border-top: 2px solid var(--paper); }
+ .pane.figma { border-top: 2px solid var(--figma); }
+ .pane.magic { border-top: 2px solid var(--magic); }
+ .pane.canva { border-top: 2px solid var(--canva); }
+
+ /* Modal */
+ .modal-bg { position: fixed; inset: 0; background: rgba(0,0,0,0.78); display:none; align-items:center; justify-content:center; z-index:200; padding:24px; }
+ .modal-bg.show { display: flex; }
+ .modal { background: var(--bg-2); border: 1px solid var(--line); border-radius: 14px; padding: 22px; max-width: 980px; width: 100%; max-height: 88vh; display: flex; flex-direction: column; }
+ .modal h3 { margin: 0 0 12px; font-family: "Didot", Georgia, serif; font-weight: 400; font-size: 20px; }
+ .modal pre { background:#07070a; border:1px solid var(--line); border-radius:8px; padding:16px; overflow:auto; font-size:11.5px; line-height:1.55; color:#d4d4d8; flex:1; margin: 0; }
+ .modal .row { display:flex; gap:8px; justify-content:flex-end; margin-top:14px; }
+
+ /* Footer */
+ footer {
+ padding: 28px 40px 60px;
+ color: var(--muted);
+ font-size: 12px;
+ border-top: 1px solid var(--line);
+ margin-top: 32px;
+ }
+ footer .pipeline {
+ display: flex;
+ gap: 14px;
+ align-items: center;
+ flex-wrap: wrap;
+ margin-top: 12px;
+ }
+ footer .step {
+ border: 1px solid var(--line);
+ padding: 8px 14px;
+ border-radius: 999px;
+ font-size: 11px;
+ letter-spacing: 0.5px;
+ color: var(--text);
+ }
+ footer .arrow { color: var(--accent); }
+</style>
+</head>
+<body>
+
+<header>
+ <div>
+ <div class="eyebrow">Four Horsemen · Brand Selector</div>
+ <h1 class="title">All four UI companies, side by side</h1>
+ <p class="sub">Paper · Figma · 21st.dev Magic · Canva — outputs for the Brand Selector / Figma Build Source picker</p>
+ </div>
+ <div class="legend">
+ <div class="chip"><span class="dot" style="background:var(--paper)"></span>Paper</div>
+ <div class="chip"><span class="dot" style="background:var(--figma)"></span>Figma</div>
+ <div class="chip"><span class="dot" style="background:var(--magic)"></span>Magic</div>
+ <div class="chip"><span class="dot" style="background:var(--canva)"></span>Canva</div>
+ </div>
+</header>
+
+<main>
+
+ <!-- PAPER -->
+ <article class="pane paper">
+ <div class="pane-head">
+ <div class="badge">P</div>
+ <div>
+ <div class="role">Design Lab — Interactive prototype</div>
+ <h2>Paper.design</h2>
+ </div>
+ <div class="status live">Complete</div>
+ </div>
+ <div class="pane-body">
+ <div class="preview" id="paper-preview" style="padding:14px;">
+ <div style="width:100%; max-height:300px; overflow:hidden; border-radius:8px; border:1px solid #2a2a35; background:#0b0b0d; padding:14px 18px;">
+ <div style="font-size:8px;letter-spacing:2px;text-transform:uppercase;color:#d4af37;">● Figma Build Source · v1</div>
+ <div style="font-family:Didot,serif;font-size:24px;color:#f4f4f5;margin-top:2px;line-height:1;">Brand Selector</div>
+ <div style="font-style:italic;color:#9c9caa;font-size:9px;margin-top:2px;">Pick the labels that will source the next Figma build</div>
+ <div style="margin-top:10px;font-family:Didot,serif;font-size:11px;color:#d4af37;">I. Fashion <span style="font-size:7px;letter-spacing:1.5px;color:#9c9caa;text-transform:uppercase;font-family:Inter,sans-serif;margin-left:6px;">Top 20</span></div>
+ <div style="display:grid;grid-template-columns:repeat(5,1fr);gap:3px;margin-top:6px;">
+ <div style="background:#131318;border:1px solid #2a2a35;border-radius:3px;padding:5px;font-family:Didot,serif;font-size:8px;color:#f4f4f5;">Louis Vuitton</div>
+ <div style="background:linear-gradient(135deg,rgba(212,175,55,0.15),#1a1a22);border:1px solid #d4af37;border-radius:3px;padding:5px;font-family:Didot,serif;font-size:8px;color:#f4f4f5;">Chanel ✓</div>
+ <div style="background:#131318;border:1px solid #2a2a35;border-radius:3px;padding:5px;font-family:Didot,serif;font-size:8px;color:#f4f4f5;">Hermès</div>
+ <div style="background:#131318;border:1px solid #2a2a35;border-radius:3px;padding:5px;font-family:Didot,serif;font-size:8px;color:#f4f4f5;">Gucci</div>
+ <div style="background:#131318;border:1px solid #2a2a35;border-radius:3px;padding:5px;font-family:Didot,serif;font-size:8px;color:#f4f4f5;">Prada</div>
+ <div style="background:#131318;border:1px solid #2a2a35;border-radius:3px;padding:5px;font-family:Didot,serif;font-size:8px;color:#f4f4f5;">Dior</div>
+ <div style="background:linear-gradient(135deg,rgba(212,175,55,0.15),#1a1a22);border:1px solid #d4af37;border-radius:3px;padding:5px;font-family:Didot,serif;font-size:8px;color:#f4f4f5;">Versace ✓</div>
+ <div style="background:#131318;border:1px solid #2a2a35;border-radius:3px;padding:5px;font-family:Didot,serif;font-size:8px;color:#f4f4f5;">Burberry</div>
+ <div style="background:#131318;border:1px solid #2a2a35;border-radius:3px;padding:5px;font-family:Didot,serif;font-size:8px;color:#f4f4f5;">Saint L.</div>
+ <div style="background:#131318;border:1px solid #2a2a35;border-radius:3px;padding:5px;font-family:Didot,serif;font-size:8px;color:#f4f4f5;">Balenciaga</div>
+ </div>
+ <div style="margin-top:10px;font-family:Didot,serif;font-size:11px;color:#d4af37;">II. Wallcovering <span style="font-size:7px;letter-spacing:1.5px;color:#9c9caa;text-transform:uppercase;font-family:Inter,sans-serif;margin-left:6px;">Top 10</span></div>
+ <div style="display:grid;grid-template-columns:repeat(5,1fr);gap:3px;margin-top:6px;">
+ <div style="background:linear-gradient(135deg,rgba(212,175,55,0.15),#1a1a22);border:1px solid #d4af37;border-radius:3px;padding:5px;font-family:Didot,serif;font-size:8px;color:#f4f4f5;">Phillip J. ✓</div>
+ <div style="background:#131318;border:1px solid #2a2a35;border-radius:3px;padding:5px;font-family:Didot,serif;font-size:8px;color:#f4f4f5;">Schumacher</div>
+ <div style="background:#131318;border:1px solid #2a2a35;border-radius:3px;padding:5px;font-family:Didot,serif;font-size:8px;color:#f4f4f5;">Cole & Son</div>
+ <div style="background:#131318;border:1px solid #2a2a35;border-radius:3px;padding:5px;font-family:Didot,serif;font-size:8px;color:#f4f4f5;">Thibaut</div>
+ <div style="background:#131318;border:1px solid #2a2a35;border-radius:3px;padding:5px;font-family:Didot,serif;font-size:8px;color:#f4f4f5;">Zoffany</div>
+ </div>
+ <div style="display:flex;justify-content:space-between;align-items:center;margin-top:12px;padding-top:8px;border-top:1px solid #2a2a35;">
+ <div style="font-size:8px;color:#9c9caa;"><span style="color:#d4af37;font-family:Didot,serif;">3</span> selected · Chanel · Versace · Phillip Jeffries</div>
+ <div style="background:#d4af37;color:#111;font-size:8px;padding:3px 8px;border-radius:99px;">Send to Figma Build →</div>
+ </div>
+ </div>
+ </div>
+ <div class="meta">
+ <div class="row"><span class="k">File</span><span class="v">novasuede</span></div>
+ <div class="row"><span class="k">Artboard</span><span class="v">1-0 · 1440 × fit-content (~2200)</span></div>
+ <div class="row"><span class="k">Layers</span><span class="v">30 cards · 3 selected · sticky dock</span></div>
+ <div class="row"><span class="k">Fonts</span><span class="v">Didot · Cormorant · Inter</span></div>
+ <div class="row"><span class="k">URL</span><a href="https://app.paper.design/file/01KQA7KRG8FDP7RH2WXPJYDFNQ" target="_blank">app.paper.design/…01KQA7KRG8FDP7RH2WXPJYDFNQ</a></div>
+ </div>
+ <div class="actions">
+ <a class="btn primary" href="https://app.paper.design/file/01KQA7KRG8FDP7RH2WXPJYDFNQ" target="_blank">Open in Paper →</a>
+ <button class="btn" data-view="paper">View summary</button>
+ </div>
+ </div>
+ </article>
+
+ <!-- FIGMA -->
+ <article class="pane figma">
+ <div class="pane-head">
+ <div class="badge">F</div>
+ <div>
+ <div class="role">System of record — Tokens & components</div>
+ <h2>Figma</h2>
+ </div>
+ <div class="status live">Live design imported</div>
+ </div>
+ <div class="pane-body">
+ <div class="preview">
+ <iframe src="https://www.figma.com/embed?embed_host=brand-selector&url=https%3A%2F%2Fwww.figma.com%2Fdesign%2FDr1u75PIPSjMJTb2eidqUu%3Fnode-id%3D24-2" allowfullscreen></iframe>
+ </div>
+ <div class="meta">
+ <div class="row"><span class="k">File</span><span class="v">Dr1u75PIPSjMJTb2eidqUu</span></div>
+ <div class="row"><span class="k">Node</span><span class="v">24:2 · captured from magic-app.html</span></div>
+ <div class="row"><span class="k">Auth</span><span class="v">steve@designerwallcoverings.com ✓</span></div>
+ <div class="row"><span class="k">Tokens</span><span class="v">color · type · radius · spacing · components</span></div>
+ <div class="row"><span class="k">Schema</span><span class="v">figma-build/brand-selection.v1</span></div>
+ <div class="row"><span class="k">URL</span><a href="https://www.figma.com/design/Dr1u75PIPSjMJTb2eidqUu?node-id=24-2" target="_blank">figma.com/design/…?node-id=24-2</a></div>
+ </div>
+ <div class="actions">
+ <a class="btn primary" href="https://www.figma.com/design/Dr1u75PIPSjMJTb2eidqUu?node-id=24-2" target="_blank">Open in Figma →</a>
+ <button class="btn" data-view="figma">View token JSON</button>
+ <a class="btn" href="outputs/figma-tokens.json" target="_blank">Raw .json</a>
+ </div>
+ </div>
+ </article>
+
+ <!-- MAGIC -->
+ <article class="pane magic">
+ <div class="pane-head">
+ <div class="badge">M</div>
+ <div>
+ <div class="role">Production component — React/Tailwind</div>
+ <h2>21st.dev Magic</h2>
+ </div>
+ <div class="status live">Component ready</div>
+ </div>
+ <div class="pane-body">
+ <div class="preview">
+ <iframe src="magic-app.html"></iframe>
+ </div>
+ <div class="meta">
+ <div class="row"><span class="k">File</span><span class="v">outputs/magic-BrandSelector.tsx</span></div>
+ <div class="row"><span class="k">Stack</span><span class="v">React 18 + Tailwind, props-driven</span></div>
+ <div class="row"><span class="k">Export</span><span class="v">default BrandSelector</span></div>
+ <div class="row"><span class="k">Live demo</span><a href="magic-app.html" target="_blank">magic-app.html (live React)</a></div>
+ <div class="row"><span class="k">Plain HTML</span><a href="index.html" target="_blank">index.html (vanilla version)</a></div>
+ </div>
+ <div class="actions">
+ <a class="btn primary" href="magic-app.html" target="_blank">Open live React →</a>
+ <button class="btn" data-view="magic">View component code</button>
+ <a class="btn" href="outputs/magic-BrandSelector.tsx" target="_blank">Raw .tsx</a>
+ <a class="btn" href="index.html" target="_blank">Vanilla HTML</a>
+ </div>
+ </div>
+ </article>
+
+ <!-- CANVA -->
+ <article class="pane canva">
+ <div class="pane-head">
+ <div class="badge">C</div>
+ <div>
+ <div class="role">Marketing & exports — Brand templates</div>
+ <h2>Canva</h2>
+ </div>
+ <div class="status brief">Brief drafted</div>
+ </div>
+ <div class="pane-body">
+ <div class="preview">
+ <div class="placeholder" style="text-align:left;padding:28px;">
+ <div style="font-family:Didot,serif;font-size:64px;color:#f4f4f5;line-height:1;text-align:center;">Brand Selector</div>
+ <div style="font-style:italic;color:#9c9caa;font-size:14px;text-align:center;margin-top:8px;">Top 20 fashion · Top 10 wallcovering</div>
+ <div style="display:grid;grid-template-columns:repeat(6,1fr);gap:6px;margin-top:22px;">
+ <!-- 18 mini name plates as a hint -->
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">Louis Vuitton</div>
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">Chanel</div>
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">Hermès</div>
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">Gucci</div>
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">Prada</div>
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">Dior</div>
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">Versace</div>
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">Burberry</div>
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">Saint Laurent</div>
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">Balenciaga</div>
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">Bottega</div>
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">Fendi</div>
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">Schumacher</div>
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">Cole & Son</div>
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">Thibaut</div>
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">Zoffany</div>
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">Phillip Jeffries</div>
+ <div style="font-family:Didot,serif;font-size:10px;text-align:center;padding:8px 4px;border:1px solid #2a2a35;border-radius:4px;color:#d4d4d8;">de Gournay</div>
+ </div>
+ <div style="margin-top:14px;color:#5a5a6a;font-size:10px;text-align:center;">Mock of brand-template tile · 1080 × 1350</div>
+ </div>
+ </div>
+ <div class="meta">
+ <div class="row"><span class="k">Template</span><span class="v">dw-brand-selector-v1 (proposed)</span></div>
+ <div class="row"><span class="k">Formats</span><span class="v">IG 1080×1350 · LI 1200×628 · PDF</span></div>
+ <div class="row"><span class="k">Auth</span><span class="v">awaiting `canva login`</span></div>
+ <div class="row"><span class="k">Brief</span><a href="outputs/canva-brief.md" target="_blank">outputs/canva-brief.md</a></div>
+ </div>
+ <div class="actions">
+ <a class="btn primary" href="https://canva.com/" target="_blank">Open Canva →</a>
+ <button class="btn" data-view="canva">View brief</button>
+ </div>
+ </div>
+ </article>
+
+</main>
+
+<footer>
+ <div>The pipeline:</div>
+ <div class="pipeline">
+ <span class="step">Paper · prototype</span>
+ <span class="arrow">→</span>
+ <span class="step">Figma · tokens + live design</span>
+ <span class="arrow">→</span>
+ <span class="step">Magic · component</span>
+ <span class="arrow">→</span>
+ <span class="step">Canva · marketing</span>
+ <span class="arrow">→</span>
+ <span class="step" style="border-color:var(--accent);color:var(--accent);">designerwallcoverings.com / build</span>
+ </div>
+</footer>
+
+<div class="modal-bg" id="modal">
+ <div class="modal">
+ <h3 id="modal-title">Output</h3>
+ <pre id="modal-body"></pre>
+ <div class="row">
+ <button class="btn" id="copy">Copy</button>
+ <button class="btn primary" id="close">Close</button>
+ </div>
+ </div>
+</div>
+
+<script>
+ const sources = {
+ paper: "outputs/paper-summary.md",
+ figma: "outputs/figma-tokens.json",
+ magic: "outputs/magic-BrandSelector.tsx",
+ canva: "outputs/canva-brief.md",
+ };
+ const titles = {
+ paper: "Paper — build summary",
+ figma: "Figma — design tokens",
+ magic: "Magic — BrandSelector.tsx",
+ canva: "Canva — marketing brief",
+ };
+
+ document.body.addEventListener("click", async (e) => {
+ const t = e.target.closest("[data-view]");
+ if (!t) return;
+ const k = t.dataset.view;
+ const r = await fetch(sources[k]);
+ const text = await r.text();
+ document.getElementById("modal-title").textContent = titles[k];
+ document.getElementById("modal-body").textContent = text;
+ document.getElementById("modal").classList.add("show");
+ });
+ document.getElementById("close").onclick = () => document.getElementById("modal").classList.remove("show");
+ document.getElementById("copy").onclick = async () => {
+ await navigator.clipboard.writeText(document.getElementById("modal-body").textContent);
+ document.getElementById("copy").textContent = "Copied";
+ setTimeout(() => document.getElementById("copy").textContent = "Copy", 1500);
+ };
+</script>
+</body>
+</html>
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..2bd31dc
--- /dev/null
+++ b/index.html
@@ -0,0 +1,486 @@
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8" />
+<meta name="viewport" content="width=device-width,initial-scale=1" />
+<title>Brand Selector — Figma Build Source</title>
+<style>
+ :root {
+ --bg: #0b0b0d;
+ --bg-2: #131318;
+ --bg-3: #1a1a22;
+ --line: #2a2a35;
+ --text: #f4f4f5;
+ --muted: #9c9caa;
+ --accent: #d4af37;
+ --accent-2: #c89b3d;
+ --ok: #4ade80;
+ --danger: #f87171;
+ --radius: 14px;
+ }
+ * { box-sizing: border-box; }
+ html, body { margin: 0; padding: 0; background: var(--bg); color: var(--text); }
+ body {
+ font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Helvetica Neue", Arial, sans-serif;
+ -webkit-font-smoothing: antialiased;
+ min-height: 100vh;
+ padding-bottom: 120px;
+ }
+ header {
+ padding: 36px 40px 20px;
+ border-bottom: 1px solid var(--line);
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ gap: 24px;
+ flex-wrap: wrap;
+ }
+ header h1 {
+ margin: 0;
+ font-family: "Didot", "Bodoni 72", Georgia, serif;
+ font-weight: 400;
+ font-size: 32px;
+ letter-spacing: 0.5px;
+ }
+ header p {
+ margin: 6px 0 0;
+ color: var(--muted);
+ font-size: 13px;
+ letter-spacing: 0.4px;
+ }
+ .stats {
+ display: flex;
+ gap: 28px;
+ align-items: center;
+ }
+ .stat {
+ text-align: right;
+ }
+ .stat .num {
+ font-family: "Didot", "Bodoni 72", Georgia, serif;
+ font-size: 28px;
+ color: var(--accent);
+ }
+ .stat .lbl {
+ font-size: 10px;
+ text-transform: uppercase;
+ letter-spacing: 1.5px;
+ color: var(--muted);
+ }
+ main { padding: 28px 40px; }
+ section + section { margin-top: 44px; }
+ section h2 {
+ margin: 0 0 4px;
+ font-family: "Didot", "Bodoni 72", Georgia, serif;
+ font-weight: 400;
+ font-size: 22px;
+ }
+ section .sub {
+ color: var(--muted);
+ font-size: 12px;
+ text-transform: uppercase;
+ letter-spacing: 2px;
+ margin-bottom: 18px;
+ }
+ .toolbar {
+ display: flex;
+ gap: 8px;
+ margin-bottom: 18px;
+ flex-wrap: wrap;
+ }
+ .btn {
+ background: var(--bg-2);
+ border: 1px solid var(--line);
+ color: var(--text);
+ padding: 8px 14px;
+ border-radius: 999px;
+ font-size: 12px;
+ letter-spacing: 0.5px;
+ cursor: pointer;
+ transition: all 0.15s ease;
+ }
+ .btn:hover { border-color: var(--accent); color: var(--accent); }
+ .btn.primary {
+ background: var(--accent);
+ color: #111;
+ border-color: var(--accent);
+ }
+ .btn.primary:hover { background: var(--accent-2); color: #111; }
+ .grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
+ gap: 14px;
+ }
+ .card {
+ background: var(--bg-2);
+ border: 1px solid var(--line);
+ border-radius: var(--radius);
+ padding: 18px 18px 16px;
+ cursor: pointer;
+ transition: all 0.15s ease;
+ position: relative;
+ overflow: hidden;
+ min-height: 130px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ }
+ .card:hover {
+ border-color: #4a4a5a;
+ transform: translateY(-2px);
+ }
+ .card.selected {
+ border-color: var(--accent);
+ background: linear-gradient(135deg, rgba(212,175,55,0.08), var(--bg-3));
+ box-shadow: 0 0 0 1px var(--accent) inset;
+ }
+ .card .check {
+ position: absolute;
+ top: 12px;
+ right: 12px;
+ width: 22px;
+ height: 22px;
+ border-radius: 50%;
+ border: 1.5px solid var(--line);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 12px;
+ color: transparent;
+ transition: all 0.15s ease;
+ }
+ .card.selected .check {
+ background: var(--accent);
+ border-color: var(--accent);
+ color: #111;
+ }
+ .card .name {
+ font-family: "Didot", "Bodoni 72", Georgia, serif;
+ font-size: 19px;
+ line-height: 1.15;
+ letter-spacing: 0.3px;
+ padding-right: 30px;
+ }
+ .card .meta {
+ margin-top: 12px;
+ display: flex;
+ gap: 8px;
+ flex-wrap: wrap;
+ align-items: center;
+ }
+ .tag {
+ font-size: 9.5px;
+ text-transform: uppercase;
+ letter-spacing: 1.2px;
+ color: var(--muted);
+ border: 1px solid var(--line);
+ padding: 3px 8px;
+ border-radius: 999px;
+ }
+ .swatch-row {
+ display: flex;
+ gap: 4px;
+ margin-top: 10px;
+ }
+ .swatch {
+ width: 18px;
+ height: 18px;
+ border-radius: 4px;
+ border: 1px solid rgba(255,255,255,0.08);
+ }
+ /* Footer dock */
+ .dock {
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ background: rgba(11,11,13,0.92);
+ backdrop-filter: blur(14px);
+ -webkit-backdrop-filter: blur(14px);
+ border-top: 1px solid var(--line);
+ padding: 14px 40px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 16px;
+ z-index: 100;
+ }
+ .dock .summary {
+ display: flex;
+ gap: 18px;
+ align-items: center;
+ flex-wrap: wrap;
+ font-size: 13px;
+ }
+ .dock .pill {
+ background: var(--bg-3);
+ padding: 6px 12px;
+ border-radius: 999px;
+ color: var(--muted);
+ font-size: 12px;
+ }
+ .dock .pill strong { color: var(--accent); margin-right: 4px; font-family: "Didot", Georgia, serif; }
+ .dock .actions { display: flex; gap: 8px; }
+ /* Modal */
+ .modal-bg {
+ position: fixed;
+ inset: 0;
+ background: rgba(0,0,0,0.7);
+ display: none;
+ align-items: center;
+ justify-content: center;
+ z-index: 200;
+ padding: 24px;
+ }
+ .modal-bg.show { display: flex; }
+ .modal {
+ background: var(--bg-2);
+ border: 1px solid var(--line);
+ border-radius: var(--radius);
+ padding: 24px;
+ max-width: 760px;
+ width: 100%;
+ max-height: 80vh;
+ overflow: auto;
+ }
+ .modal h3 {
+ margin: 0 0 12px;
+ font-family: "Didot", Georgia, serif;
+ font-weight: 400;
+ font-size: 22px;
+ }
+ pre {
+ background: #07070a;
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ padding: 16px;
+ overflow: auto;
+ font-size: 12px;
+ line-height: 1.5;
+ color: #d4d4d8;
+ max-height: 50vh;
+ }
+ .toast {
+ position: fixed;
+ bottom: 90px;
+ left: 50%;
+ transform: translateX(-50%) translateY(20px);
+ background: var(--ok);
+ color: #06280f;
+ padding: 10px 18px;
+ border-radius: 999px;
+ font-size: 13px;
+ font-weight: 500;
+ opacity: 0;
+ transition: all 0.25s ease;
+ pointer-events: none;
+ z-index: 300;
+ }
+ .toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }
+</style>
+</head>
+<body>
+
+<header>
+ <div>
+ <h1>Brand Selector</h1>
+ <p>Pick the labels that will source the next Figma build · Top 20 fashion · Top 10 wallcovering</p>
+ </div>
+ <div class="stats">
+ <div class="stat"><div class="num" id="stat-fashion">0</div><div class="lbl">Fashion</div></div>
+ <div class="stat"><div class="num" id="stat-wall">0</div><div class="lbl">Wallcovering</div></div>
+ </div>
+</header>
+
+<main>
+ <section>
+ <h2>Fashion</h2>
+ <div class="sub">Top 20 — Luxury & Iconic Houses</div>
+ <div class="toolbar">
+ <button class="btn" data-action="select-all" data-cat="fashion">Select all 20</button>
+ <button class="btn" data-action="clear" data-cat="fashion">Clear</button>
+ <button class="btn" data-action="top5" data-cat="fashion">Top 5 only</button>
+ </div>
+ <div class="grid" id="grid-fashion"></div>
+ </section>
+
+ <section>
+ <h2>Wallcovering</h2>
+ <div class="sub">Top 10 — Heritage & Designer Houses</div>
+ <div class="toolbar">
+ <button class="btn" data-action="select-all" data-cat="wall">Select all 10</button>
+ <button class="btn" data-action="clear" data-cat="wall">Clear</button>
+ </div>
+ <div class="grid" id="grid-wall"></div>
+ </section>
+</main>
+
+<div class="dock">
+ <div class="summary">
+ <span class="pill"><strong id="dock-total">0</strong>selected</span>
+ <span class="pill" id="dock-list" style="max-width:580px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">none</span>
+ </div>
+ <div class="actions">
+ <button class="btn" id="btn-clear-all">Clear all</button>
+ <button class="btn" id="btn-preview">Preview JSON</button>
+ <button class="btn primary" id="btn-figma">Send to Figma Build →</button>
+ </div>
+</div>
+
+<div class="modal-bg" id="modal">
+ <div class="modal">
+ <h3>Figma build payload</h3>
+ <p style="color:var(--muted);font-size:12px;margin:0 0 12px;">Paste this into Paper / Figma plugin input, or feed it to the four-horsemen pipeline.</p>
+ <pre id="modal-json"></pre>
+ <div style="display:flex;gap:8px;margin-top:14px;justify-content:flex-end;">
+ <button class="btn" id="btn-copy">Copy JSON</button>
+ <button class="btn" id="btn-download">Download .json</button>
+ <button class="btn primary" id="btn-close">Close</button>
+ </div>
+ </div>
+</div>
+
+<div class="toast" id="toast">Copied</div>
+
+<script>
+const FASHION = [
+ { name: "Louis Vuitton", country: "FR", founded: 1854, palette: ["#5d3a22","#d4b886","#1a1a1a"] },
+ { name: "Chanel", country: "FR", founded: 1910, palette: ["#000000","#ffffff","#c9a96e"] },
+ { name: "Hermès", country: "FR", founded: 1837, palette: ["#f37021","#1a1a1a","#f4e7d3"] },
+ { name: "Gucci", country: "IT", founded: 1921, palette: ["#006341","#a01c1f","#d4af37"] },
+ { name: "Prada", country: "IT", founded: 1913, palette: ["#000000","#8b8680","#e8e4dc"] },
+ { name: "Dior", country: "FR", founded: 1946, palette: ["#1a1a1a","#c8a883","#ffffff"] },
+ { name: "Versace", country: "IT", founded: 1978, palette: ["#d4af37","#000000","#a01c1f"] },
+ { name: "Burberry", country: "UK", founded: 1856, palette: ["#a08661","#000000","#c1272d"] },
+ { name: "Saint Laurent", country: "FR", founded: 1961, palette: ["#000000","#d4b886","#ffffff"] },
+ { name: "Balenciaga", country: "ES", founded: 1919, palette: ["#000000","#5a5a5a","#ffffff"] },
+ { name: "Bottega Veneta",country: "IT", founded: 1966, palette: ["#3d5a2c","#c9b27a","#1a1a1a"] },
+ { name: "Fendi", country: "IT", founded: 1925, palette: ["#5d3a22","#f0e6d2","#000000"] },
+ { name: "Givenchy", country: "FR", founded: 1952, palette: ["#000000","#d4af37","#a01c1f"] },
+ { name: "Valentino", country: "IT", founded: 1960, palette: ["#a01c1f","#1a1a1a","#f0e6d2"] },
+ { name: "Armani", country: "IT", founded: 1975, palette: ["#1a1a2e","#8b8680","#000000"] },
+ { name: "Ralph Lauren", country: "US", founded: 1967, palette: ["#1a3a6c","#a01c1f","#f0e6d2"] },
+ { name: "Tom Ford", country: "US", founded: 2005, palette: ["#000000","#5d3a22","#d4af37"] },
+ { name: "Celine", country: "FR", founded: 1945, palette: ["#000000","#d4b886","#ffffff"] },
+ { name: "Loewe", country: "ES", founded: 1846, palette: ["#5d3a22","#d4b886","#1a1a1a"] },
+ { name: "Alexander McQueen", country: "UK", founded: 1992, palette: ["#000000","#a01c1f","#8b8680"] }
+];
+
+const WALL = [
+ { name: "Phillip Jeffries", country: "US", founded: 1976, palette: ["#d4b886","#5d3a22","#f0e6d2"] },
+ { name: "Schumacher", country: "US", founded: 1889, palette: ["#a01c1f","#3d5a2c","#d4af37"] },
+ { name: "Cole & Son", country: "UK", founded: 1875, palette: ["#1a3a6c","#c9a96e","#f0e6d2"] },
+ { name: "Thibaut", country: "US", founded: 1886, palette: ["#3d5a2c","#c9a96e","#1a1a1a"] },
+ { name: "Zoffany", country: "UK", founded: 1895, palette: ["#5d3a22","#d4b886","#3d5a2c"] },
+ { name: "Farrow & Ball", country: "UK", founded: 1946, palette: ["#1a3a3a","#e8e4dc","#5d3a22"] },
+ { name: "Brunschwig & Fils",country: "US", founded: 1909, palette: ["#a01c1f","#1a3a6c","#d4b886"] },
+ { name: "de Gournay", country: "UK", founded: 1986, palette: ["#c9a96e","#5d3a22","#3d5a2c"] },
+ { name: "Fromental", country: "UK", founded: 2005, palette: ["#1a3a6c","#d4af37","#5d3a22"] },
+ { name: "Pierre Frey", country: "FR", founded: 1935, palette: ["#a01c1f","#d4b886","#1a3a6c"] }
+];
+
+const state = { fashion: new Set(), wall: new Set() };
+
+function render() {
+ const make = (cat, list) => list.map((b,i) => {
+ const sel = state[cat].has(b.name) ? "selected" : "";
+ const swatches = b.palette.map(c => `<span class="swatch" style="background:${c}"></span>`).join("");
+ return `
+ <div class="card ${sel}" data-cat="${cat}" data-name="${b.name}">
+ <div class="check">✓</div>
+ <div>
+ <div class="name">${b.name}</div>
+ <div class="meta">
+ <span class="tag">${b.country}</span>
+ <span class="tag">est. ${b.founded}</span>
+ </div>
+ </div>
+ <div class="swatch-row">${swatches}</div>
+ </div>
+ `;
+ }).join("");
+
+ document.getElementById("grid-fashion").innerHTML = make("fashion", FASHION);
+ document.getElementById("grid-wall").innerHTML = make("wall", WALL);
+
+ document.getElementById("stat-fashion").textContent = state.fashion.size;
+ document.getElementById("stat-wall").textContent = state.wall.size;
+
+ const total = state.fashion.size + state.wall.size;
+ document.getElementById("dock-total").textContent = total;
+ const all = [...state.fashion, ...state.wall];
+ document.getElementById("dock-list").textContent = all.length ? all.join(" · ") : "none";
+}
+
+document.body.addEventListener("click", (e) => {
+ const card = e.target.closest(".card");
+ if (card) {
+ const cat = card.dataset.cat, name = card.dataset.name;
+ if (state[cat].has(name)) state[cat].delete(name); else state[cat].add(name);
+ render();
+ return;
+ }
+ const tb = e.target.closest("[data-action]");
+ if (tb) {
+ const cat = tb.dataset.cat, action = tb.dataset.action;
+ const list = cat === "fashion" ? FASHION : WALL;
+ if (action === "select-all") list.forEach(b => state[cat].add(b.name));
+ if (action === "clear") state[cat].clear();
+ if (action === "top5") { state[cat].clear(); list.slice(0,5).forEach(b => state[cat].add(b.name)); }
+ render();
+ }
+});
+
+document.getElementById("btn-clear-all").onclick = () => {
+ state.fashion.clear(); state.wall.clear(); render();
+};
+
+function buildPayload() {
+ const get = (cat, list) => list.filter(b => state[cat].has(b.name));
+ return {
+ schema: "figma-build/brand-selection.v1",
+ generated_at: new Date().toISOString(),
+ counts: { fashion: state.fashion.size, wallcovering: state.wall.size, total: state.fashion.size + state.wall.size },
+ fashion: get("fashion", FASHION),
+ wallcovering: get("wall", WALL)
+ };
+}
+
+document.getElementById("btn-preview").onclick = () => {
+ document.getElementById("modal-json").textContent = JSON.stringify(buildPayload(), null, 2);
+ document.getElementById("modal").classList.add("show");
+};
+document.getElementById("btn-figma").onclick = () => {
+ const payload = buildPayload();
+ if (payload.counts.total === 0) { toast("Select at least one brand", true); return; }
+ document.getElementById("modal-json").textContent = JSON.stringify(payload, null, 2);
+ document.getElementById("modal").classList.add("show");
+};
+document.getElementById("btn-close").onclick = () => document.getElementById("modal").classList.remove("show");
+document.getElementById("btn-copy").onclick = async () => {
+ await navigator.clipboard.writeText(document.getElementById("modal-json").textContent);
+ toast("Copied to clipboard");
+};
+document.getElementById("btn-download").onclick = () => {
+ const blob = new Blob([document.getElementById("modal-json").textContent], { type: "application/json" });
+ const url = URL.createObjectURL(blob);
+ const a = document.createElement("a");
+ a.href = url;
+ a.download = `brand-selection-${new Date().toISOString().slice(0,10)}.json`;
+ a.click();
+ URL.revokeObjectURL(url);
+};
+
+function toast(msg, isError) {
+ const t = document.getElementById("toast");
+ t.textContent = msg;
+ t.style.background = isError ? "var(--danger)" : "var(--ok)";
+ t.style.color = isError ? "#3a0d0d" : "#06280f";
+ t.classList.add("show");
+ setTimeout(() => t.classList.remove("show"), 1800);
+}
+
+render();
+</script>
+</body>
+</html>
diff --git a/magic-app.html b/magic-app.html
new file mode 100644
index 0000000..2a34c45
--- /dev/null
+++ b/magic-app.html
@@ -0,0 +1,234 @@
+<!doctype html>
+<html lang="en">
+<head>
+<meta charset="utf-8" />
+<meta name="viewport" content="width=device-width,initial-scale=1" />
+<title>Magic · BrandSelector — live React</title>
+<script src="https://unpkg.com/react@18/umd/react.development.js" crossorigin></script>
+<script src="https://unpkg.com/react-dom@18/umd/react-dom.development.js" crossorigin></script>
+<script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
+<script src="https://cdn.tailwindcss.com"></script>
+<script src="https://mcp.figma.com/mcp/html-to-design/capture.js" async></script>
+<style>
+ body { margin: 0; background: #0b0b0d; }
+ .font-serif { font-family: "Didot", "Bodoni 72", Georgia, serif; }
+ .font-sans { font-family: "Inter", -apple-system, BlinkMacSystemFont, sans-serif; }
+</style>
+</head>
+<body>
+<div id="root"></div>
+
+<script type="text/babel" data-presets="env,react">
+const FASHION_20 = [
+ { name: "Louis Vuitton", country: "FR", founded: 1854, palette: ["#5d3a22", "#d4b886", "#1a1a1a"] },
+ { name: "Chanel", country: "FR", founded: 1910, palette: ["#000000", "#ffffff", "#c9a96e"] },
+ { name: "Hermès", country: "FR", founded: 1837, palette: ["#f37021", "#1a1a1a", "#f4e7d3"] },
+ { name: "Gucci", country: "IT", founded: 1921, palette: ["#006341", "#a01c1f", "#d4af37"] },
+ { name: "Prada", country: "IT", founded: 1913, palette: ["#000000", "#8b8680", "#e8e4dc"] },
+ { name: "Dior", country: "FR", founded: 1946, palette: ["#1a1a1a", "#c8a883", "#ffffff"] },
+ { name: "Versace", country: "IT", founded: 1978, palette: ["#d4af37", "#000000", "#a01c1f"] },
+ { name: "Burberry", country: "UK", founded: 1856, palette: ["#a08661", "#000000", "#c1272d"] },
+ { name: "Saint Laurent", country: "FR", founded: 1961, palette: ["#000000", "#d4b886", "#ffffff"] },
+ { name: "Balenciaga", country: "ES", founded: 1919, palette: ["#000000", "#5a5a5a", "#ffffff"] },
+ { name: "Bottega Veneta",country: "IT", founded: 1966, palette: ["#3d5a2c", "#c9b27a", "#1a1a1a"] },
+ { name: "Fendi", country: "IT", founded: 1925, palette: ["#5d3a22", "#f0e6d2", "#000000"] },
+ { name: "Givenchy", country: "FR", founded: 1952, palette: ["#000000", "#d4af37", "#a01c1f"] },
+ { name: "Valentino", country: "IT", founded: 1960, palette: ["#a01c1f", "#1a1a1a", "#f0e6d2"] },
+ { name: "Armani", country: "IT", founded: 1975, palette: ["#1a1a2e", "#8b8680", "#000000"] },
+ { name: "Ralph Lauren", country: "US", founded: 1967, palette: ["#1a3a6c", "#a01c1f", "#f0e6d2"] },
+ { name: "Tom Ford", country: "US", founded: 2005, palette: ["#000000", "#5d3a22", "#d4af37"] },
+ { name: "Celine", country: "FR", founded: 1945, palette: ["#000000", "#d4b886", "#ffffff"] },
+ { name: "Loewe", country: "ES", founded: 1846, palette: ["#5d3a22", "#d4b886", "#1a1a1a"] },
+ { name: "Alexander McQueen", country: "UK", founded: 1992, palette: ["#000000", "#a01c1f", "#8b8680"] },
+];
+
+const WALL_10 = [
+ { name: "Phillip Jeffries", country: "US", founded: 1976, palette: ["#d4b886", "#5d3a22", "#f0e6d2"] },
+ { name: "Schumacher", country: "US", founded: 1889, palette: ["#a01c1f", "#3d5a2c", "#d4af37"] },
+ { name: "Cole & Son", country: "UK", founded: 1875, palette: ["#1a3a6c", "#c9a96e", "#f0e6d2"] },
+ { name: "Thibaut", country: "US", founded: 1886, palette: ["#3d5a2c", "#c9a96e", "#1a1a1a"] },
+ { name: "Zoffany", country: "UK", founded: 1895, palette: ["#5d3a22", "#d4b886", "#3d5a2c"] },
+ { name: "Farrow & Ball", country: "UK", founded: 1946, palette: ["#1a3a3a", "#e8e4dc", "#5d3a22"] },
+ { name: "Brunschwig & Fils", country: "US", founded: 1909, palette: ["#a01c1f", "#1a3a6c", "#d4b886"] },
+ { name: "de Gournay", country: "UK", founded: 1986, palette: ["#c9a96e", "#5d3a22", "#3d5a2c"] },
+ { name: "Fromental", country: "UK", founded: 2005, palette: ["#1a3a6c", "#d4af37", "#5d3a22"] },
+ { name: "Pierre Frey", country: "FR", founded: 1935, palette: ["#a01c1f", "#d4b886", "#1a3a6c"] },
+];
+
+function Tag({ children }) {
+ return (
+ <span className="text-[9.5px] tracking-[1.2px] uppercase text-zinc-500 border border-zinc-800 px-2 py-0.5 rounded-full font-sans">
+ {children}
+ </span>
+ );
+}
+
+function BrandCard({ brand, selected, onClick }) {
+ return (
+ <button
+ onClick={onClick}
+ className={[
+ "relative text-left rounded-2xl p-5 min-h-[140px] flex flex-col justify-between transition border",
+ selected
+ ? "border-amber-400 shadow-[inset_0_0_0_1px_rgba(212,175,55,1)]"
+ : "bg-zinc-900 border-zinc-800 hover:border-zinc-600 hover:-translate-y-0.5",
+ ].join(" ")}
+ style={selected ? { background: "linear-gradient(135deg, rgba(212,175,55,0.10), #1a1a22)" } : {}}
+ >
+ <span
+ className={[
+ "absolute top-3 right-3 w-5 h-5 rounded-full border flex items-center justify-center text-[11px]",
+ selected ? "bg-amber-400 border-amber-400 text-zinc-900" : "border-zinc-700 text-transparent",
+ ].join(" ")}
+ >
+ ✓
+ </span>
+ <div>
+ <div className="font-serif text-lg leading-tight pr-7 text-zinc-100">{brand.name}</div>
+ <div className="flex gap-1.5 mt-2.5">
+ <Tag>{brand.country}</Tag>
+ <Tag>est. {brand.founded}</Tag>
+ </div>
+ </div>
+ <div className="flex gap-1 mt-3">
+ {brand.palette.map((c) => (
+ <span key={c} className="w-4 h-4 rounded border border-white/10" style={{ background: c }} />
+ ))}
+ </div>
+ </button>
+ );
+}
+
+function Stat({ n, label }) {
+ return (
+ <div>
+ <div className="font-serif text-3xl text-amber-400 leading-none">{n}</div>
+ <div className="text-[10px] tracking-[2px] uppercase text-zinc-500 mt-1 font-sans">{label}</div>
+ </div>
+ );
+}
+
+function Section({ index, title, sub, brands, picked, onToggle, toolbar }) {
+ return (
+ <section className="px-10 pt-10">
+ <div className="flex items-end justify-between gap-5 mb-5">
+ <div>
+ <div className="text-[10px] tracking-[3px] uppercase text-amber-400 font-sans">{index}</div>
+ <h2 className="font-serif text-3xl mt-1 text-zinc-100">{title}</h2>
+ <div className="italic text-zinc-500 text-sm tracking-wide font-serif">{sub}</div>
+ </div>
+ {toolbar}
+ </div>
+ <div className="grid gap-3" style={{ gridTemplateColumns: "repeat(auto-fill, minmax(220px, 1fr))" }}>
+ {brands.map((b) => (
+ <BrandCard key={b.name} brand={b} selected={!!picked[b.name]} onClick={() => onToggle(b.name)} />
+ ))}
+ </div>
+ </section>
+ );
+}
+
+function Pill({ children, onClick, primary }) {
+ return (
+ <button
+ onClick={onClick}
+ className={[
+ "text-xs tracking-[0.5px] px-4 py-2 rounded-full font-sans transition",
+ primary
+ ? "bg-amber-400 text-zinc-900 hover:bg-amber-300 font-medium"
+ : "bg-zinc-900 border border-zinc-800 text-zinc-300 hover:border-zinc-600",
+ ].join(" ")}
+ >
+ {children}
+ </button>
+ );
+}
+
+function App() {
+ const [picked, setPicked] = React.useState({ Chanel: true, Versace: true, "Phillip Jeffries": true });
+ const [showJson, setShowJson] = React.useState(false);
+
+ const toggle = (name) => setPicked((p) => ({ ...p, [name]: !p[name] }));
+
+ const fashionPicked = FASHION_20.filter((b) => picked[b.name]);
+ const wallPicked = WALL_10.filter((b) => picked[b.name]);
+ const total = fashionPicked.length + wallPicked.length;
+ const all = [...fashionPicked, ...wallPicked];
+ const summary = all.length ? all.map((b) => b.name).join(" · ") : "none";
+
+ const buildPayload = () => ({
+ schema: "figma-build/brand-selection.v1",
+ generated_at: new Date().toISOString(),
+ counts: { fashion: fashionPicked.length, wallcovering: wallPicked.length, total },
+ fashion: fashionPicked,
+ wallcovering: wallPicked,
+ });
+
+ const fashionToolbar = (
+ <div className="flex gap-2">
+ <Pill onClick={() => setPicked((p) => { const n = { ...p }; FASHION_20.forEach((b) => (n[b.name] = true)); return n; })}>Select all 20</Pill>
+ <Pill onClick={() => setPicked((p) => { const n = { ...p }; FASHION_20.forEach((b) => (n[b.name] = false)); return n; })}>Clear</Pill>
+ <Pill onClick={() => setPicked((p) => { const n = { ...p }; FASHION_20.forEach((b, i) => (n[b.name] = i < 5)); return n; })}>Top 5 only</Pill>
+ </div>
+ );
+ const wallToolbar = (
+ <div className="flex gap-2">
+ <Pill onClick={() => setPicked((p) => { const n = { ...p }; WALL_10.forEach((b) => (n[b.name] = true)); return n; })}>Select all 10</Pill>
+ <Pill onClick={() => setPicked((p) => { const n = { ...p }; WALL_10.forEach((b) => (n[b.name] = false)); return n; })}>Clear</Pill>
+ </div>
+ );
+
+ return (
+ <div className="min-h-screen bg-[#0b0b0d] pb-32 font-sans">
+ <header className="px-10 pt-12 pb-6 border-b border-zinc-800 flex flex-wrap items-end justify-between gap-6">
+ <div>
+ <div className="flex items-center gap-2.5">
+ <span className="w-1.5 h-1.5 rounded-full bg-amber-400 inline-block" />
+ <span className="text-[10px] tracking-[3px] uppercase text-amber-400/90">Figma Build Source · v1 · live React</span>
+ </div>
+ <h1 className="font-serif text-5xl tracking-tight mt-2 text-zinc-100">Brand Selector</h1>
+ <p className="italic text-zinc-400 mt-1 font-serif">Pick the labels that will source the next Figma build · Top 20 fashion · Top 10 wallcovering</p>
+ </div>
+ <div className="flex gap-12 text-right">
+ <Stat n={fashionPicked.length} label="Fashion" />
+ <Stat n={wallPicked.length} label="Wallcovering" />
+ </div>
+ </header>
+
+ <Section index="I." title="Fashion" sub="Top 20 — Luxury & Iconic Houses" brands={FASHION_20} picked={picked} onToggle={toggle} toolbar={fashionToolbar} />
+ <Section index="II." title="Wallcovering" sub="Top 10 — Heritage & Designer Houses" brands={WALL_10} picked={picked} onToggle={toggle} toolbar={wallToolbar} />
+
+ <div className="fixed bottom-0 inset-x-0 backdrop-blur border-t border-zinc-800 px-10 py-3 flex items-center justify-between gap-4" style={{ background: "rgba(11,11,13,0.92)" }}>
+ <div className="flex gap-3 items-center text-sm">
+ <span className="bg-zinc-900 px-3 py-1.5 rounded-full text-zinc-400">
+ <b className="text-amber-400 font-serif text-base mr-1">{total}</b>selected
+ </span>
+ <span className="text-zinc-500 truncate max-w-[600px]">{summary}</span>
+ </div>
+ <div className="flex gap-2">
+ <Pill onClick={() => setPicked({})}>Clear all</Pill>
+ <Pill onClick={() => setShowJson(true)}>Preview JSON</Pill>
+ <Pill primary onClick={() => setShowJson(true)}>Send to Figma Build →</Pill>
+ </div>
+ </div>
+
+ {showJson && (
+ <div className="fixed inset-0 z-50 bg-black/75 flex items-center justify-center p-6" onClick={() => setShowJson(false)}>
+ <div className="bg-zinc-900 border border-zinc-800 rounded-xl p-6 max-w-3xl w-full max-h-[85vh] flex flex-col" onClick={(e) => e.stopPropagation()}>
+ <h3 className="font-serif text-xl text-zinc-100 mb-3">Figma build payload</h3>
+ <pre className="bg-black border border-zinc-800 rounded p-4 overflow-auto text-xs leading-relaxed text-zinc-300 flex-1">{JSON.stringify(buildPayload(), null, 2)}</pre>
+ <div className="flex gap-2 justify-end mt-4">
+ <Pill onClick={() => navigator.clipboard.writeText(JSON.stringify(buildPayload(), null, 2))}>Copy JSON</Pill>
+ <Pill primary onClick={() => setShowJson(false)}>Close</Pill>
+ </div>
+ </div>
+ </div>
+ )}
+ </div>
+ );
+}
+
+ReactDOM.createRoot(document.getElementById("root")).render(<App />);
+</script>
+</body>
+</html>
diff --git a/outputs/canva-brief.md b/outputs/canva-brief.md
new file mode 100644
index 0000000..3aee305
--- /dev/null
+++ b/outputs/canva-brief.md
@@ -0,0 +1,44 @@
+# Canva Brief — Brand Selector Launch Tile
+
+**Project:** Brand Selector — Figma Build Source
+**Channels:** Instagram (1080×1350), LinkedIn (1200×628), Email header (1200×400)
+**Brand template:** create new template `dw-brand-selector-v1` once Connect API auth is live.
+
+## Composition
+
+- **Background:** flat `#0b0b0d` with subtle 1% film grain.
+- **Headline (centered, top third):** "Brand Selector" — Didot, 96 px, `#f4f4f5`.
+- **Eyebrow above headline:** "Figma Build Source · v1" — Inter, 12 px, 3 px tracking, `#d4af37`.
+- **Subhead:** "Top 20 fashion · Top 10 wallcovering" — Cormorant Garamond Italic, 24 px, `#9c9caa`.
+- **Visual core:** 6×5 grid of brand-name plates (no logos — typographic wordmarks only, see Note).
+- **Footer ribbon:** thin `#2a2a35` rule + `#d4af37` dot + "designerwallcoverings.com / build" — Inter, 11 px.
+
+## Autofill fields (Connect API · `create-design-autofill-job`)
+
+| Field | Type | Source |
+|------------------|--------|---------------------------------------------------|
+| `headline` | text | "Brand Selector" |
+| `eyebrow` | text | static "Figma Build Source · v1" |
+| `count_fashion` | text | `payload.counts.fashion` |
+| `count_wall` | text | `payload.counts.wallcovering` |
+| `selected_list` | text | join `payload.fashion.name + payload.wallcovering.name` with " · " |
+| `accent_color` | color | static `#d4af37` |
+
+## Asset list (export job)
+
+| Format | Size | Purpose |
+|--------|------------|------------------------|
+| PNG | 1080×1350 | Instagram feed |
+| PNG | 1080×1920 | Story / Reel cover |
+| JPG | 1200×628 | LinkedIn / OG image |
+| PDF | letter | Internal one-pager |
+
+## Note on logos
+
+Canva, Magic, and most logo libraries do **not** carry trademarked luxury brand wordmarks (LV, Chanel, Hermès, etc.). We render brand names as **Didot wordmarks** at the same color as body copy — the typography itself becomes the visual signature. This is also the cleanest path through Canva brand-template review.
+
+## Status
+
+- `canva login` not yet confirmed for this machine. Run `canva login` in terminal to enable Connect API calls.
+- Once authenticated, this brief becomes a `POST /v1/autofills` payload against template `dw-brand-selector-v1`.
+- Source of truth for colors/type stays in `outputs/figma-tokens.json`. Don't redefine in the Canva template.
diff --git a/outputs/figma-tokens.json b/outputs/figma-tokens.json
new file mode 100644
index 0000000..34b3c9b
--- /dev/null
+++ b/outputs/figma-tokens.json
@@ -0,0 +1,109 @@
+{
+ "$schema": "https://schemas.figma.com/tokens/v1.json",
+ "name": "Brand Selector — Figma Build Source",
+ "description": "Design tokens for the Brand Selector. Paste into Figma Tokens / Variables plugin.",
+ "version": "1.0.0",
+ "color": {
+ "bg": {
+ "base": { "value": "#0b0b0d" },
+ "panel": { "value": "#131318" },
+ "raised": { "value": "#1a1a22" }
+ },
+ "line": { "value": "#2a2a35" },
+ "text": {
+ "primary": { "value": "#f4f4f5" },
+ "secondary": { "value": "#9c9caa" },
+ "inverse": { "value": "#111111" }
+ },
+ "accent": {
+ "gold": { "value": "#d4af37" },
+ "goldDeep": { "value": "#c89b3d" }
+ },
+ "state": {
+ "success": { "value": "#4ade80" },
+ "danger": { "value": "#f87171" }
+ }
+ },
+ "typography": {
+ "display": {
+ "family": { "value": "Didot" },
+ "weight": { "value": 400 },
+ "size": { "value": "48px" },
+ "tracking": { "value": "-0.5px" },
+ "leading": { "value": "1.05" }
+ },
+ "title": {
+ "family": { "value": "Didot" },
+ "weight": { "value": 400 },
+ "size": { "value": "32px" },
+ "leading": { "value": "1.1" }
+ },
+ "subhead": {
+ "family": { "value": "Cormorant Garamond" },
+ "weight": { "value": 400 },
+ "italic": { "value": true },
+ "size": { "value": "16px" }
+ },
+ "eyebrow": {
+ "family": { "value": "Inter" },
+ "weight": { "value": 500 },
+ "size": { "value": "10px" },
+ "tracking": { "value": "3px" },
+ "case": { "value": "uppercase" }
+ },
+ "body": {
+ "family": { "value": "Inter" },
+ "weight": { "value": 400 },
+ "size": { "value": "13px" },
+ "leading": { "value": "1.5" }
+ },
+ "tag": {
+ "family": { "value": "Inter" },
+ "weight": { "value": 500 },
+ "size": { "value": "9.5px" },
+ "tracking": { "value": "1.2px" },
+ "case": { "value": "uppercase" }
+ }
+ },
+ "radius": {
+ "card": { "value": "14px" },
+ "pill": { "value": "999px" },
+ "swatch": { "value": "4px" }
+ },
+ "spacing": {
+ "xs": { "value": "4px" },
+ "sm": { "value": "8px" },
+ "md": { "value": "14px" },
+ "lg": { "value": "24px" },
+ "xl": { "value": "40px" },
+ "2xl": { "value": "64px" }
+ },
+ "components": {
+ "BrandCard": {
+ "default": {
+ "background": "{color.bg.panel}",
+ "border": "1px solid {color.line}",
+ "radius": "{radius.card}",
+ "padding": "18px"
+ },
+ "selected": {
+ "background": "linear-gradient(135deg, rgba(212,175,55,0.08), {color.bg.raised})",
+ "border": "1px solid {color.accent.gold}",
+ "shadow": "inset 0 0 0 1px {color.accent.gold}"
+ }
+ },
+ "Dock": {
+ "background": "rgba(11,11,13,0.92)",
+ "blur": "14px",
+ "border": "1px solid {color.line}",
+ "padding": "14px 40px"
+ },
+ "PrimaryCTA": {
+ "background": "{color.accent.gold}",
+ "color": "{color.text.inverse}",
+ "radius": "{radius.pill}",
+ "padding": "8px 14px"
+ }
+ },
+ "schema_handoff": "figma-build/brand-selection.v1"
+}
diff --git a/outputs/magic-BrandSelector.tsx b/outputs/magic-BrandSelector.tsx
new file mode 100644
index 0000000..da5a23d
--- /dev/null
+++ b/outputs/magic-BrandSelector.tsx
@@ -0,0 +1,189 @@
+// Generated via 21st.dev Magic — Brand Selector
+// Drop into a React/Tailwind project. Self-contained; props-driven.
+// Pairs with Paper editorial spec + Figma design tokens.
+
+import { useMemo, useState } from "react";
+
+export type Brand = {
+ name: string;
+ country: string;
+ founded: number;
+ palette: [string, string, string];
+};
+
+export type BrandPayload = {
+ schema: "figma-build/brand-selection.v1";
+ generated_at: string;
+ counts: { fashion: number; wallcovering: number; total: number };
+ fashion: Brand[];
+ wallcovering: Brand[];
+};
+
+export const FASHION_20: Brand[] = [
+ { name: "Louis Vuitton", country: "FR", founded: 1854, palette: ["#5d3a22", "#d4b886", "#1a1a1a"] },
+ { name: "Chanel", country: "FR", founded: 1910, palette: ["#000000", "#ffffff", "#c9a96e"] },
+ { name: "Hermès", country: "FR", founded: 1837, palette: ["#f37021", "#1a1a1a", "#f4e7d3"] },
+ { name: "Gucci", country: "IT", founded: 1921, palette: ["#006341", "#a01c1f", "#d4af37"] },
+ { name: "Prada", country: "IT", founded: 1913, palette: ["#000000", "#8b8680", "#e8e4dc"] },
+ { name: "Dior", country: "FR", founded: 1946, palette: ["#1a1a1a", "#c8a883", "#ffffff"] },
+ { name: "Versace", country: "IT", founded: 1978, palette: ["#d4af37", "#000000", "#a01c1f"] },
+ { name: "Burberry", country: "UK", founded: 1856, palette: ["#a08661", "#000000", "#c1272d"] },
+ { name: "Saint Laurent", country: "FR", founded: 1961, palette: ["#000000", "#d4b886", "#ffffff"] },
+ { name: "Balenciaga", country: "ES", founded: 1919, palette: ["#000000", "#5a5a5a", "#ffffff"] },
+ { name: "Bottega Veneta",country: "IT", founded: 1966, palette: ["#3d5a2c", "#c9b27a", "#1a1a1a"] },
+ { name: "Fendi", country: "IT", founded: 1925, palette: ["#5d3a22", "#f0e6d2", "#000000"] },
+ { name: "Givenchy", country: "FR", founded: 1952, palette: ["#000000", "#d4af37", "#a01c1f"] },
+ { name: "Valentino", country: "IT", founded: 1960, palette: ["#a01c1f", "#1a1a1a", "#f0e6d2"] },
+ { name: "Armani", country: "IT", founded: 1975, palette: ["#1a1a2e", "#8b8680", "#000000"] },
+ { name: "Ralph Lauren", country: "US", founded: 1967, palette: ["#1a3a6c", "#a01c1f", "#f0e6d2"] },
+ { name: "Tom Ford", country: "US", founded: 2005, palette: ["#000000", "#5d3a22", "#d4af37"] },
+ { name: "Celine", country: "FR", founded: 1945, palette: ["#000000", "#d4b886", "#ffffff"] },
+ { name: "Loewe", country: "ES", founded: 1846, palette: ["#5d3a22", "#d4b886", "#1a1a1a"] },
+ { name: "Alexander McQueen", country: "UK", founded: 1992, palette: ["#000000", "#a01c1f", "#8b8680"] },
+];
+
+export const WALL_10: Brand[] = [
+ { name: "Phillip Jeffries", country: "US", founded: 1976, palette: ["#d4b886", "#5d3a22", "#f0e6d2"] },
+ { name: "Schumacher", country: "US", founded: 1889, palette: ["#a01c1f", "#3d5a2c", "#d4af37"] },
+ { name: "Cole & Son", country: "UK", founded: 1875, palette: ["#1a3a6c", "#c9a96e", "#f0e6d2"] },
+ { name: "Thibaut", country: "US", founded: 1886, palette: ["#3d5a2c", "#c9a96e", "#1a1a1a"] },
+ { name: "Zoffany", country: "UK", founded: 1895, palette: ["#5d3a22", "#d4b886", "#3d5a2c"] },
+ { name: "Farrow & Ball", country: "UK", founded: 1946, palette: ["#1a3a3a", "#e8e4dc", "#5d3a22"] },
+ { name: "Brunschwig & Fils", country: "US", founded: 1909, palette: ["#a01c1f", "#1a3a6c", "#d4b886"] },
+ { name: "de Gournay", country: "UK", founded: 1986, palette: ["#c9a96e", "#5d3a22", "#3d5a2c"] },
+ { name: "Fromental", country: "UK", founded: 2005, palette: ["#1a3a6c", "#d4af37", "#5d3a22"] },
+ { name: "Pierre Frey", country: "FR", founded: 1935, palette: ["#a01c1f", "#d4b886", "#1a3a6c"] },
+];
+
+type Props = {
+ fashion?: Brand[];
+ wallcovering?: Brand[];
+ onSubmit?: (payload: BrandPayload) => void;
+};
+
+export default function BrandSelector({
+ fashion = FASHION_20,
+ wallcovering = WALL_10,
+ onSubmit,
+}: Props) {
+ const [picked, setPicked] = useState<Record<string, boolean>>({});
+
+ const toggle = (name: string) =>
+ setPicked((p) => ({ ...p, [name]: !p[name] }));
+
+ const buildPayload = (): BrandPayload => {
+ const f = fashion.filter((b) => picked[b.name]);
+ const w = wallcovering.filter((b) => picked[b.name]);
+ return {
+ schema: "figma-build/brand-selection.v1",
+ generated_at: new Date().toISOString(),
+ counts: { fashion: f.length, wallcovering: w.length, total: f.length + w.length },
+ fashion: f,
+ wallcovering: w,
+ };
+ };
+
+ const summary = useMemo(() => {
+ const all = [...fashion, ...wallcovering].filter((b) => picked[b.name]);
+ return { count: all.length, names: all.map((b) => b.name).join(" · ") || "none" };
+ }, [picked, fashion, wallcovering]);
+
+ return (
+ <div className="min-h-screen bg-[#0b0b0d] text-zinc-100 pb-32 font-sans">
+ <header className="px-10 pt-12 pb-6 border-b border-zinc-800 flex flex-wrap items-end justify-between gap-6">
+ <div>
+ <div className="text-[10px] tracking-[3px] uppercase text-amber-400/80">Figma Build Source · v1</div>
+ <h1 className="font-serif text-5xl tracking-tight mt-2">Brand Selector</h1>
+ <p className="italic text-zinc-400 mt-1">Pick the labels that will source the next Figma build · Top 20 fashion · Top 10 wallcovering</p>
+ </div>
+ <div className="flex gap-12 text-right">
+ <Stat n={fashion.filter((b) => picked[b.name]).length} label="Fashion" />
+ <Stat n={wallcovering.filter((b) => picked[b.name]).length} label="Wallcovering" />
+ </div>
+ </header>
+
+ <Section index="I." title="Fashion" sub="Top 20 — Luxury & Iconic Houses" brands={fashion} picked={picked} onToggle={toggle} />
+ <Section index="II." title="Wallcovering" sub="Top 10 — Heritage & Designer Houses" brands={wallcovering} picked={picked} onToggle={toggle} />
+
+ <div className="fixed bottom-0 inset-x-0 bg-[#0b0b0d]/90 backdrop-blur border-t border-zinc-800 px-10 py-3 flex items-center justify-between gap-4">
+ <div className="flex gap-3 items-center text-sm">
+ <span className="bg-zinc-900 px-3 py-1.5 rounded-full"><b className="text-amber-400 font-serif mr-1">{summary.count}</b> selected</span>
+ <span className="text-zinc-500 truncate max-w-[600px]">{summary.names}</span>
+ </div>
+ <button
+ onClick={() => onSubmit?.(buildPayload())}
+ className="bg-amber-400 text-zinc-900 px-5 py-2 rounded-full text-sm font-medium hover:bg-amber-300 transition"
+ >
+ Send to Figma Build →
+ </button>
+ </div>
+ </div>
+ );
+}
+
+function Stat({ n, label }: { n: number; label: string }) {
+ return (
+ <div>
+ <div className="font-serif text-3xl text-amber-400 leading-none">{n}</div>
+ <div className="text-[10px] tracking-[2px] uppercase text-zinc-500 mt-1">{label}</div>
+ </div>
+ );
+}
+
+function Section({
+ index, title, sub, brands, picked, onToggle,
+}: { index: string; title: string; sub: string; brands: Brand[]; picked: Record<string, boolean>; onToggle: (n: string) => void; }) {
+ return (
+ <section className="px-10 pt-12">
+ <div className="mb-5">
+ <div className="text-[10px] tracking-[3px] uppercase text-amber-400">{index}</div>
+ <h2 className="font-serif text-3xl mt-1">{title}</h2>
+ <div className="italic text-zinc-500 text-sm tracking-wide">{sub}</div>
+ </div>
+ <div className="grid grid-cols-[repeat(auto-fill,minmax(220px,1fr))] gap-3">
+ {brands.map((b) => (
+ <BrandCard key={b.name} brand={b} selected={!!picked[b.name]} onClick={() => onToggle(b.name)} />
+ ))}
+ </div>
+ </section>
+ );
+}
+
+function BrandCard({ brand, selected, onClick }: { brand: Brand; selected: boolean; onClick: () => void }) {
+ return (
+ <button
+ onClick={onClick}
+ className={[
+ "relative text-left rounded-2xl p-5 min-h-[140px] flex flex-col justify-between transition border",
+ selected
+ ? "bg-gradient-to-br from-amber-400/10 to-zinc-900 border-amber-400 shadow-[inset_0_0_0_1px_rgba(212,175,55,1)]"
+ : "bg-zinc-900 border-zinc-800 hover:border-zinc-600 hover:-translate-y-0.5",
+ ].join(" ")}
+ >
+ <span className={[
+ "absolute top-3 right-3 w-5 h-5 rounded-full border flex items-center justify-center text-[11px]",
+ selected ? "bg-amber-400 border-amber-400 text-zinc-900" : "border-zinc-700 text-transparent",
+ ].join(" ")}>✓</span>
+ <div>
+ <div className="font-serif text-lg leading-tight pr-7">{brand.name}</div>
+ <div className="flex gap-1.5 mt-2.5">
+ <Tag>{brand.country}</Tag>
+ <Tag>est. {brand.founded}</Tag>
+ </div>
+ </div>
+ <div className="flex gap-1 mt-3">
+ {brand.palette.map((c) => (
+ <span key={c} className="w-4 h-4 rounded border border-white/10" style={{ background: c }} />
+ ))}
+ </div>
+ </button>
+ );
+}
+
+function Tag({ children }: { children: React.ReactNode }) {
+ return (
+ <span className="text-[9.5px] tracking-[1.2px] uppercase text-zinc-500 border border-zinc-800 px-2 py-0.5 rounded-full">
+ {children}
+ </span>
+ );
+}
diff --git a/outputs/paper-summary.md b/outputs/paper-summary.md
new file mode 100644
index 0000000..bbc064a
--- /dev/null
+++ b/outputs/paper-summary.md
@@ -0,0 +1,34 @@
+# Paper Output
+
+**File:** novasuede
+**File URL:** https://app.paper.design/file/01KQA7KRG8FDP7RH2WXPJYDFNQ
+**Artboard:** "Brand Selector — Figma Build Source" (1440 × fit-content, dark editorial)
+**Status:** Complete
+
+## What's on the canvas
+
+- **Header** (60 px / 64 px padding, hairline rule): gold dot eyebrow `Figma Build Source · v1`, Didot 64 display title, Cormorant Garamond italic subhead, two right-aligned stats (`20 Fashion`, `10 Wallcovering`) in Didot 48 / `#d4af37`.
+- **Section I — Fashion**: gold roman numeral `I.`, Didot 36 title, italic subhead, toolbar (`Select all 20` · `Clear` · `Top 5 only` highlighted gold). Grid of 20 brand cards in 4 rows × 5 cols (or wrap-flex, ~262 px wide each, gap 14). **Chanel** and **Versace** rendered in selected state (gold border + gradient + filled checkmark).
+- **Section II — Wallcovering**: matching `II.` eyebrow, title, subhead, toolbar (`Select all 10` · `Clear`). Grid of 10 brand cards in 2 rows × 5 cols. **Phillip Jeffries** rendered in selected state.
+- **Sticky dock** at the bottom: dark glass panel, two pills (`3 selected` with gold count, `Chanel · Versace · Phillip Jeffries` truncating list), three actions (`Clear all` · `Preview JSON` · `Send to Figma Build →` filled gold).
+
+## Brand cards on the canvas (30)
+
+**Fashion (20):** Louis Vuitton · Chanel ✓ · Hermès · Gucci · Prada · Dior · Versace ✓ · Burberry · Saint Laurent · Balenciaga · Bottega Veneta · Fendi · Givenchy · Valentino · Armani · Ralph Lauren · Tom Ford · Celine · Loewe · Alexander McQueen
+
+**Wallcovering (10):** Phillip Jeffries ✓ · Schumacher · Cole & Son · Thibaut · Zoffany · Farrow & Ball · Brunschwig & Fils · de Gournay · Fromental · Pierre Frey
+
+Each card carries: Didot 19 brand name (right-padded for the check circle), country pill, founding-year pill, and a 3-swatch palette row at the bottom drawn from the brand's signature colors.
+
+## Token alignment
+
+The artboard reads exactly the same tokens as `outputs/figma-tokens.json`:
+- `#0b0b0d` artboard, `#131318` card, `#1a1a22` raised
+- `#2a2a35` line, `#9c9caa` muted text, `#f4f4f5` primary
+- `#d4af37` gold accent (selected border + dock CTA + numerals)
+- 14 px card radius, 999 px pill radius, 4 px swatch radius
+- Didot for display/title/card name, Cormorant Garamond italic for subheads, Inter for tags + body
+
+## Why Paper, not Figma, owns this stage
+
+Paper is the prototype-velocity tool — the artboard was built incrementally so the user saw real-time visual progress. Now that the layout is settled, Figma will own the design tokens (`figma-tokens.json`), Magic owns the production component (`magic-BrandSelector.tsx`), and Canva owns the marketing exports (`canva-brief.md`). Paper is the design lab; the others are the systems of record.
(oldest)
·
back to Brand Selector Viewer
·
Remove unused index param from card render map 53e8749 →