← back to Model Arena
auto-save: 2026-07-25T06:34:19 (4 files) — data/challenges.json data/costlog.jsonl data/artifacts/104018fe5adc/ data/artifacts/83e28cd01c99/
a346f46467920e94b22db1f11b3f48d883b165bb · 2026-07-25 06:34:25 -0700 · Steve Abrams
Files touched
A data/artifacts/104018fe5adc/claude-code.htmlA data/artifacts/104018fe5adc/claude-code.pngA data/artifacts/104018fe5adc/gemma3-12b.htmlA data/artifacts/104018fe5adc/gemma3-12b.pngA data/artifacts/104018fe5adc/gpt.htmlA data/artifacts/104018fe5adc/gpt.pngA data/artifacts/104018fe5adc/grok.htmlA data/artifacts/104018fe5adc/grok.pngA data/artifacts/104018fe5adc/hermes3-8b.htmlA data/artifacts/104018fe5adc/hermes3-8b.pngA data/artifacts/104018fe5adc/hf-qwen-coder-32b.htmlA data/artifacts/104018fe5adc/hf-qwen-coder-32b.pngA data/artifacts/104018fe5adc/kimi.htmlA data/artifacts/104018fe5adc/kimi.pngA data/artifacts/104018fe5adc/qwen25-7b.htmlA data/artifacts/104018fe5adc/qwen25-7b.pngA data/artifacts/104018fe5adc/qwen3-14b.htmlA data/artifacts/104018fe5adc/qwen3-14b.pngA data/artifacts/83e28cd01c99/claude-code.htmlA data/artifacts/83e28cd01c99/claude-code.pngA data/artifacts/83e28cd01c99/gemma3-12b.htmlA data/artifacts/83e28cd01c99/gemma3-12b.pngA data/artifacts/83e28cd01c99/gpt.htmlA data/artifacts/83e28cd01c99/gpt.pngA data/artifacts/83e28cd01c99/grok.htmlA data/artifacts/83e28cd01c99/grok.pngA data/artifacts/83e28cd01c99/hermes3-8b.htmlA data/artifacts/83e28cd01c99/hermes3-8b.pngA data/artifacts/83e28cd01c99/hf-qwen-coder-32b.htmlA data/artifacts/83e28cd01c99/hf-qwen-coder-32b.pngA data/artifacts/83e28cd01c99/kimi.htmlA data/artifacts/83e28cd01c99/kimi.pngA data/artifacts/83e28cd01c99/qwen25-7b.htmlA data/artifacts/83e28cd01c99/qwen25-7b.pngA data/artifacts/83e28cd01c99/qwen3-14b.htmlA data/artifacts/83e28cd01c99/qwen3-14b.pngM data/challenges.jsonM data/costlog.jsonl
Diff
commit a346f46467920e94b22db1f11b3f48d883b165bb
Author: Steve Abrams <steve@designerwallcoverings.com>
Date: Sat Jul 25 06:34:25 2026 -0700
auto-save: 2026-07-25T06:34:19 (4 files) — data/challenges.json data/costlog.jsonl data/artifacts/104018fe5adc/ data/artifacts/83e28cd01c99/
---
data/artifacts/104018fe5adc/claude-code.html | 524 ++++++++
data/artifacts/104018fe5adc/claude-code.png | Bin 0 -> 144880 bytes
data/artifacts/104018fe5adc/gemma3-12b.html | 209 ++++
data/artifacts/104018fe5adc/gemma3-12b.png | Bin 0 -> 24732 bytes
data/artifacts/104018fe5adc/gpt.html | 950 +++++++++++++++
data/artifacts/104018fe5adc/gpt.png | Bin 0 -> 138893 bytes
data/artifacts/104018fe5adc/grok.html | 1190 ++++++++++++++++++
data/artifacts/104018fe5adc/grok.png | Bin 0 -> 159637 bytes
data/artifacts/104018fe5adc/hermes3-8b.html | 122 ++
data/artifacts/104018fe5adc/hermes3-8b.png | Bin 0 -> 26486 bytes
data/artifacts/104018fe5adc/hf-qwen-coder-32b.html | 175 +++
data/artifacts/104018fe5adc/hf-qwen-coder-32b.png | Bin 0 -> 35039 bytes
data/artifacts/104018fe5adc/kimi.html | 528 ++++++++
data/artifacts/104018fe5adc/kimi.png | Bin 0 -> 57475 bytes
data/artifacts/104018fe5adc/qwen25-7b.html | 128 ++
data/artifacts/104018fe5adc/qwen25-7b.png | Bin 0 -> 17583 bytes
data/artifacts/104018fe5adc/qwen3-14b.html | 261 ++++
data/artifacts/104018fe5adc/qwen3-14b.png | Bin 0 -> 52521 bytes
data/artifacts/83e28cd01c99/claude-code.html | 672 +++++++++++
data/artifacts/83e28cd01c99/claude-code.png | Bin 0 -> 118004 bytes
data/artifacts/83e28cd01c99/gemma3-12b.html | 306 +++++
data/artifacts/83e28cd01c99/gemma3-12b.png | Bin 0 -> 32725 bytes
data/artifacts/83e28cd01c99/gpt.html | 1261 ++++++++++++++++++++
data/artifacts/83e28cd01c99/gpt.png | Bin 0 -> 197279 bytes
data/artifacts/83e28cd01c99/grok.html | 1164 ++++++++++++++++++
data/artifacts/83e28cd01c99/grok.png | Bin 0 -> 39806 bytes
data/artifacts/83e28cd01c99/hermes3-8b.html | 282 +++++
data/artifacts/83e28cd01c99/hermes3-8b.png | Bin 0 -> 24424 bytes
data/artifacts/83e28cd01c99/hf-qwen-coder-32b.html | 318 +++++
data/artifacts/83e28cd01c99/hf-qwen-coder-32b.png | Bin 0 -> 25486 bytes
data/artifacts/83e28cd01c99/kimi.html | 807 +++++++++++++
data/artifacts/83e28cd01c99/kimi.png | Bin 0 -> 38734 bytes
data/artifacts/83e28cd01c99/qwen25-7b.html | 272 +++++
data/artifacts/83e28cd01c99/qwen25-7b.png | Bin 0 -> 22853 bytes
data/artifacts/83e28cd01c99/qwen3-14b.html | 307 +++++
data/artifacts/83e28cd01c99/qwen3-14b.png | Bin 0 -> 20883 bytes
data/challenges.json | 483 ++++++--
data/costlog.jsonl | 6 +
38 files changed, 9838 insertions(+), 127 deletions(-)
diff --git a/data/artifacts/104018fe5adc/claude-code.html b/data/artifacts/104018fe5adc/claude-code.html
new file mode 100644
index 0000000..f32c96e
--- /dev/null
+++ b/data/artifacts/104018fe5adc/claude-code.html
@@ -0,0 +1,524 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Designer Wallcoverings — This or That</title>
+<style>
+ /* ============ DESIGN TOKENS ============ */
+ :root{
+ --bg:#f4f1ea; /* warm ivory ground */
+ --surface:#ffffff;
+ --ink:#1c1a17; /* darkest near-black ink */
+ --ink-soft:#3a352d;
+ --muted:#9b948a;
+ --accent:#a9884f; /* single metallic accent — antique brass */
+ --accent-deep:#8a6c38;
+ --line:rgba(28,26,23,.12);
+ --line-strong:rgba(28,26,23,.22);
+ --shadow:0 10px 40px rgba(28,26,23,.14);
+ --shadow-soft:0 4px 18px rgba(28,26,23,.08);
+ --story-bg:#161310; /* espresso story canvas */
+ --ease:cubic-bezier(.22,1,.36,1);
+
+ /* editable swatch palettes */
+ --l-ground:#33403a; /* Damask ground — deep sage */
+ --l-motif:#c8a86a; /* Damask motif — brass */
+ --r-ground:#2b3550; /* Geometric ground — deep indigo */
+ --r-motif:#e6ddcf; /* Geometric motif — cream */
+
+ --serif:'Cormorant Garamond','Playfair Display','EB Garamond',Georgia,'Times New Roman',serif;
+ --sans:-apple-system,'Segoe UI','Helvetica Neue',Helvetica,Arial,sans-serif;
+ }
+
+ *{box-sizing:border-box;margin:0;padding:0}
+ html,body{height:100%}
+ body{
+ background:
+ radial-gradient(120% 90% at 50% -10%, #faf8f3 0%, var(--bg) 55%, #ece7dc 100%);
+ color:var(--ink);
+ font-family:var(--sans);
+ -webkit-font-smoothing:antialiased;
+ min-height:100vh;
+ padding-bottom:56px;
+ }
+
+ /* ============ HEADER / WORDMARK LOCKUP ============ */
+ header.topbar{
+ position:sticky;top:0;z-index:50;
+ display:flex;align-items:center;justify-content:space-between;
+ gap:24px;
+ padding:20px 32px;
+ background:rgba(244,241,234,.82);
+ backdrop-filter:saturate(120%) blur(10px);
+ border-bottom:1px solid var(--line);
+ }
+ .wordmark{
+ font-family:var(--serif);
+ color:var(--ink); /* darkest ink — never a surface token */
+ font-weight:600;
+ font-size:44px; /* >=40px */
+ line-height:.92;
+ letter-spacing:.18em;
+ text-transform:uppercase;
+ max-width:280px; /* compact upper-left lockup, wraps to 2 lines */
+ text-indent:.18em;
+ }
+ .topbar .kicker{
+ font-family:var(--sans);
+ font-size:12px;letter-spacing:.28em;text-transform:uppercase;
+ color:var(--muted);
+ text-align:right;
+ line-height:1.7;
+ }
+ .topbar .kicker b{display:block;color:var(--accent-deep);font-weight:600;letter-spacing:.22em}
+
+ /* ============ LAYOUT ============ */
+ main{
+ max-width:520px;margin:0 auto;padding:40px 24px 0;
+ display:flex;flex-direction:column;align-items:center;gap:34px;
+ animation:fadeUp .8s var(--ease) both;
+ }
+ @keyframes fadeUp{from{opacity:0;transform:translateY(28px)}to{opacity:1;transform:none}}
+
+ /* ============ PHONE FRAME 9:16 ============ */
+ .phone{
+ position:relative;
+ width:100%;max-width:340px;
+ aspect-ratio:9/16;
+ background:#0c0a08;
+ border-radius:42px;
+ padding:12px;
+ box-shadow:var(--shadow), 0 0 0 1px rgba(28,26,23,.4), inset 0 0 0 2px rgba(255,255,255,.05);
+ }
+ .phone::before{ /* notch */
+ content:"";position:absolute;top:18px;left:50%;transform:translateX(-50%);
+ width:108px;height:26px;background:#0c0a08;border-radius:0 0 16px 16px;z-index:6;
+ }
+ .story{
+ position:relative;height:100%;width:100%;
+ border-radius:32px;overflow:hidden;
+ background:var(--story-bg);
+ display:flex;flex-direction:column;
+ color:#f5f2ea;
+ }
+
+ /* ---- Story chrome ---- */
+ .progress{position:absolute;top:12px;left:12px;right:12px;display:flex;gap:5px;z-index:5}
+ .progress span{flex:1;height:2.5px;border-radius:2px;background:rgba(255,255,255,.28);overflow:hidden}
+ .progress span i{display:block;height:100%;width:0;background:#fff;border-radius:2px}
+ .progress span:first-child i{width:64%;animation:load 6s linear forwards}
+ @keyframes load{to{width:100%}}
+
+ .story-head{
+ position:absolute;top:24px;left:14px;right:14px;z-index:5;
+ display:flex;align-items:center;gap:9px;
+ }
+ .avatar{
+ width:32px;height:32px;border-radius:50%;flex:none;
+ padding:2px;background:conic-gradient(from 210deg,var(--accent),#e7d3a6,var(--accent-deep),var(--accent));
+ }
+ .avatar div{
+ width:100%;height:100%;border-radius:50%;
+ background:var(--story-bg);
+ display:flex;align-items:center;justify-content:center;
+ font-family:var(--serif);font-size:15px;color:var(--accent);font-weight:600;
+ }
+ .handle{font-size:12.5px;font-weight:600;letter-spacing:.02em}
+ .handle b{font-weight:600}
+ .handle span{color:rgba(245,242,234,.6);font-weight:400;margin-left:6px}
+ .story-head .x{margin-left:auto;font-size:20px;color:rgba(245,242,234,.85);line-height:1;cursor:pointer}
+
+ /* ---- Story body ---- */
+ .story-body{
+ flex:1;display:flex;flex-direction:column;
+ padding:70px 18px 16px;
+ }
+ .poll-eyebrow{
+ font-size:11px;letter-spacing:.32em;text-transform:uppercase;
+ color:var(--accent);text-align:center;margin-bottom:6px;font-weight:600;
+ }
+ .poll-title{
+ font-family:var(--serif);
+ font-size:34px; /* heading >=24px */
+ line-height:1;text-align:center;font-weight:600;font-style:italic;
+ letter-spacing:.01em;
+ text-shadow:0 2px 18px rgba(0,0,0,.4);
+ }
+ .poll-sub{
+ text-align:center;color:rgba(245,242,234,.62);
+ font-size:12px;letter-spacing:.06em;margin-top:8px;
+ }
+
+ /* ---- Arena ---- */
+ .arena{
+ position:relative;flex:1;margin-top:16px;
+ display:flex;gap:2px;border-radius:20px;overflow:hidden;
+ box-shadow:0 8px 30px rgba(0,0,0,.45), inset 0 0 0 1px rgba(255,255,255,.06);
+ }
+ .swatch{
+ position:relative;flex:1;overflow:hidden;cursor:pointer;
+ display:flex;flex-direction:column;justify-content:flex-end;
+ border:none;background:transparent;
+ transition:flex .55s var(--ease);
+ isolation:isolate;
+ }
+ .swatch .ground{position:absolute;inset:0;z-index:0;transition:background-color .3s ease}
+ .swatch.left .ground{background-color:var(--l-ground)}
+ .swatch.right .ground{background-color:var(--r-ground)}
+ .swatch .tex{position:absolute;inset:0;z-index:1;opacity:.9;transition:transform .6s var(--ease)}
+ .swatch:hover .tex{transform:scale(1.06)}
+ .swatch .veil{position:absolute;inset:0;z-index:2;
+ background:linear-gradient(to top,rgba(10,8,6,.72) 0%,rgba(10,8,6,.15) 45%,rgba(10,8,6,0) 70%);}
+
+ /* rising vote fill */
+ .swatch .fill{
+ position:absolute;left:0;right:0;bottom:0;height:0;z-index:3;
+ background:linear-gradient(to top,rgba(169,136,79,.55),rgba(169,136,79,.14));
+ border-top:1.5px solid rgba(231,211,166,.9);
+ transition:height .8s var(--ease);
+ }
+ .swatch .name{
+ position:relative;z-index:4;
+ font-family:var(--serif);font-size:19px;font-weight:600;
+ text-align:center;padding:0 8px 6px;line-height:1.1;
+ text-shadow:0 2px 10px rgba(0,0,0,.6);
+ outline:none;border-radius:6px;
+ }
+ .swatch .name:focus{box-shadow:0 0 0 1px rgba(231,211,166,.7)}
+ .swatch .pct{
+ position:relative;z-index:4;text-align:center;
+ font-family:var(--serif);font-size:30px;font-weight:600;
+ color:#fff;letter-spacing:.02em;
+ height:0;opacity:0;overflow:hidden;
+ transition:opacity .5s ease .2s, height .5s var(--ease);
+ text-shadow:0 2px 12px rgba(0,0,0,.7);
+ padding-bottom:14px;
+ }
+ .voted .swatch .pct{height:40px;opacity:1}
+
+ /* VS badge */
+ .vs{
+ position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);z-index:6;
+ width:46px;height:46px;border-radius:50%;
+ background:var(--surface);color:var(--ink);
+ display:flex;align-items:center;justify-content:center;
+ font-family:var(--serif);font-style:italic;font-weight:600;font-size:19px;
+ box-shadow:0 6px 20px rgba(0,0,0,.5), 0 0 0 4px rgba(22,19,16,.9), 0 0 0 5px var(--accent);
+ }
+
+ /* winner wax seal */
+ .seal{
+ position:absolute;top:14px;left:50%;transform:translateX(-50%) scale(.4) rotate(-12deg);
+ z-index:7;width:74px;height:74px;border-radius:50%;
+ background:radial-gradient(circle at 35% 30%,#c8a86a,var(--accent) 55%,var(--accent-deep));
+ box-shadow:0 6px 18px rgba(0,0,0,.5), inset 0 0 0 2px rgba(255,255,255,.35), inset 0 0 0 6px rgba(138,108,56,.5);
+ display:flex;flex-direction:column;align-items:center;justify-content:center;
+ color:#26200f;opacity:0;pointer-events:none;
+ transition:opacity .45s ease, transform .55s var(--ease);
+ }
+ .seal small{font-size:8px;letter-spacing:.22em;text-transform:uppercase;font-weight:700}
+ .seal b{font-family:var(--serif);font-size:13px;letter-spacing:.14em;font-weight:700;line-height:1;margin-top:1px}
+ .seal.show{opacity:1;transform:translateX(-50%) scale(1) rotate(-12deg)}
+
+ /* ---- Results split bar ---- */
+ .results{margin-top:14px;height:0;opacity:0;overflow:hidden;transition:height .5s var(--ease),opacity .4s ease}
+ .voted .results{height:44px;opacity:1}
+ .bar{height:8px;border-radius:5px;overflow:hidden;display:flex;background:rgba(255,255,255,.12)}
+ .bar .bl{background:linear-gradient(90deg,var(--accent-deep),var(--accent));transition:width .8s var(--ease)}
+ .bar .br{background:rgba(245,242,234,.55);transition:width .8s var(--ease)}
+ .rlabels{display:flex;justify-content:space-between;margin-top:8px;
+ font-size:11px;letter-spacing:.08em;color:rgba(245,242,234,.75)}
+ .rlabels b{color:var(--accent);font-weight:600}
+
+ /* ---- Story footer ---- */
+ .story-foot{padding:12px 16px 18px;display:flex;align-items:center;gap:12px}
+ .reply{
+ flex:1;border:1px solid rgba(245,242,234,.4);border-radius:22px;
+ padding:9px 16px;font-size:12px;color:rgba(245,242,234,.7);letter-spacing:.04em;
+ }
+ .story-foot svg{width:22px;height:22px;stroke:rgba(245,242,234,.9);fill:none;stroke-width:1.6}
+ .tap-hint{
+ text-align:center;font-size:11px;letter-spacing:.14em;text-transform:uppercase;
+ color:var(--accent);padding:0 16px 6px;transition:opacity .3s ease;
+ }
+ .voted .tap-hint{color:rgba(245,242,234,.5)}
+ .votecount{font-weight:600;color:#fff}
+
+ /* ============ CONTROLS (ivory) ============ */
+ .controls{
+ width:100%;max-width:340px;
+ background:var(--surface);
+ border:1px solid var(--line);border-radius:16px;
+ box-shadow:var(--shadow-soft);
+ padding:22px 22px 24px;
+ }
+ .controls h2{
+ font-family:var(--serif);font-size:24px;font-weight:600;color:var(--ink);
+ letter-spacing:.01em;margin-bottom:3px;
+ }
+ .controls .lead{font-size:12.5px;color:var(--muted);letter-spacing:.02em;line-height:1.5;margin-bottom:18px}
+ .field-label{
+ font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);
+ font-weight:600;margin:16px 0 9px;
+ }
+ .pickrow{display:grid;grid-template-columns:1fr 1fr;gap:12px}
+ .pick{display:flex;flex-direction:column;gap:6px}
+ .pick span{font-size:10.5px;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-soft)}
+ .pick .swatchrow{display:flex;gap:8px}
+ .pick label{
+ display:flex;align-items:center;gap:6px;font-size:10px;color:var(--muted);
+ letter-spacing:.06em;cursor:pointer;
+ }
+ input[type=color]{
+ -webkit-appearance:none;appearance:none;
+ width:26px;height:26px;border:1px solid var(--line-strong);border-radius:50%;
+ padding:0;background:none;cursor:pointer;
+ }
+ input[type=color]::-webkit-color-swatch-wrapper{padding:0}
+ input[type=color]::-webkit-color-swatch{border:none;border-radius:50%}
+ input[type=color]::-moz-color-swatch{border:none;border-radius:50%}
+
+ .btnrow{display:flex;gap:10px;margin-top:22px}
+ button.act{
+ flex:1;font-family:var(--sans);
+ font-size:11px;letter-spacing:.18em;text-transform:uppercase;font-weight:600;
+ padding:13px 14px;border-radius:10px;cursor:pointer;border:1px solid var(--line-strong);
+ background:var(--surface);color:var(--ink);
+ transition:all .18s ease;
+ }
+ button.act:hover{border-color:var(--accent);color:var(--accent-deep)}
+ button.act.primary{background:var(--ink);color:var(--bg);border-color:var(--ink)}
+ button.act.primary:hover{background:var(--accent-deep);border-color:var(--accent-deep);color:#fff}
+
+ .foot-note{
+ max-width:340px;text-align:center;font-size:11px;letter-spacing:.12em;
+ text-transform:uppercase;color:var(--muted);margin-top:6px;
+ }
+</style>
+</head>
+<body>
+
+<header class="topbar">
+ <div class="wordmark">Designer Wallcoverings</div>
+ <div class="kicker"><b>Atelier</b>Story Poll · No.14</div>
+</header>
+
+<main>
+ <!-- PHONE -->
+ <div class="phone">
+ <div class="story" id="story">
+ <div class="progress"><span><i></i></span><span><i></i></span><span><i></i></span><span><i></i></span></div>
+
+ <div class="story-head">
+ <div class="avatar"><div>D</div></div>
+ <div class="handle"><b>designerwallcoverings</b><span>2h</span></div>
+ <div class="x">×</div>
+ </div>
+
+ <div class="story-body">
+ <div class="poll-eyebrow">This · or · That</div>
+ <div class="poll-title">Which wall<br>speaks to you?</div>
+ <div class="poll-sub">Tap a side — the room will tell.</div>
+
+ <div class="arena" id="arena">
+ <!-- LEFT -->
+ <div class="swatch left" data-side="left">
+ <div class="ground"></div>
+ <svg class="tex" viewBox="0 0 90 120" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
+ <defs>
+ <pattern id="pat-damask" width="90" height="120" patternUnits="userSpaceOnUse">
+ <g fill="var(--l-motif)" fill-opacity="0.94">
+ <path d="M45 6 C55 18 55 30 45 40 C35 30 35 18 45 6 Z"/>
+ <path d="M45 15 C61 10 71 21 66 33 C58 29 50 27 45 31 Z"/>
+ <path d="M45 15 C29 10 19 21 24 33 C32 29 40 27 45 31 Z"/>
+ <path d="M45 44 C67 56 69 85 45 101 C21 85 23 56 45 44 Z"/>
+ <path d="M45 57 C58 67 58 85 45 93 C32 85 32 67 45 57 Z" fill="var(--l-ground)"/>
+ <path d="M45 104 C55 112 58 119 52 120 L38 120 C32 119 35 112 45 104 Z"/>
+ <circle cx="8" cy="60" r="3.2"/><circle cx="82" cy="60" r="3.2"/>
+ <circle cx="0" cy="0" r="3"/><circle cx="90" cy="0" r="3"/>
+ <circle cx="0" cy="120" r="3"/><circle cx="90" cy="120" r="3"/>
+ </g>
+ </pattern>
+ </defs>
+ <rect width="90" height="120" fill="url(#pat-damask)"/>
+ </svg>
+ <div class="veil"></div>
+ <div class="fill"></div>
+ <div class="seal"><small>Winner</small><b>WINNER</b></div>
+ <div class="name" contenteditable="true" spellcheck="false" data-name="left">Damask</div>
+ <div class="pct" data-pct="left">50%</div>
+ </div>
+
+ <!-- RIGHT -->
+ <div class="swatch right" data-side="right">
+ <div class="ground"></div>
+ <svg class="tex" viewBox="0 0 108 120" preserveAspectRatio="xMidYMid slice" aria-hidden="true">
+ <defs>
+ <pattern id="pat-geo" width="54" height="54" patternUnits="userSpaceOnUse">
+ <g stroke="var(--r-motif)" stroke-width="1.6" fill="none">
+ <polygon points="27,3 51,27 27,51 3,27"/>
+ <polygon points="27,14 40,27 27,40 14,27"/>
+ </g>
+ <circle cx="27" cy="27" r="2.4" fill="var(--r-motif)"/>
+ <circle cx="0" cy="0" r="2.4" fill="var(--r-motif)"/>
+ <circle cx="54" cy="0" r="2.4" fill="var(--r-motif)"/>
+ <circle cx="0" cy="54" r="2.4" fill="var(--r-motif)"/>
+ <circle cx="54" cy="54" r="2.4" fill="var(--r-motif)"/>
+ </pattern>
+ </defs>
+ <rect width="108" height="120" fill="url(#pat-geo)"/>
+ </svg>
+ <div class="veil"></div>
+ <div class="fill"></div>
+ <div class="seal"><small>Winner</small><b>WINNER</b></div>
+ <div class="name" contenteditable="true" spellcheck="false" data-name="right">Geometric</div>
+ <div class="pct" data-pct="right">50%</div>
+ </div>
+
+ <div class="vs">vs</div>
+ </div>
+
+ <div class="results" id="results">
+ <div class="bar"><div class="bl" style="width:50%"></div><div class="br" style="width:50%"></div></div>
+ <div class="rlabels">
+ <span><b id="rl-name">Damask</b> · <span id="rl-count">0</span></span>
+ <span><span id="rr-count">0</span> · <b id="rr-name">Geometric</b></span>
+ </div>
+ </div>
+ </div>
+
+ <div class="tap-hint" id="hint">Tap a side to cast your vote</div>
+ <div class="story-foot">
+ <div class="reply">Send message…</div>
+ <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20.8 4.6a5.5 5.5 0 0 0-7.8 0L12 5.6l-1-1a5.5 5.5 0 1 0-7.8 7.8L12 21l8.8-8.6a5.5 5.5 0 0 0 0-7.8Z"/></svg>
+ <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M22 2 11 13"/><path d="M22 2 15 22l-4-9-9-4 20-7Z"/></svg>
+ </div>
+ </div>
+ </div>
+
+ <!-- CONTROLS -->
+ <div class="controls">
+ <h2>Style the Poll</h2>
+ <p class="lead">Rename either style directly on the swatch, and set each palette below. Colours flow live into the CSS-drawn wallcovering.</p>
+
+ <div class="field-label">Left — Palette</div>
+ <div class="pickrow">
+ <div class="pick"><span>Ground</span><input type="color" value="#33403a" data-var="--l-ground"></div>
+ <div class="pick"><span>Motif</span><input type="color" value="#c8a86a" data-var="--l-motif"></div>
+ </div>
+
+ <div class="field-label">Right — Palette</div>
+ <div class="pickrow">
+ <div class="pick"><span>Ground</span><input type="color" value="#2b3550" data-var="--r-ground"></div>
+ <div class="pick"><span>Motif</span><input type="color" value="#e6ddcf" data-var="--r-motif"></div>
+ </div>
+
+ <div class="btnrow">
+ <button class="act" id="reset">Reset Votes</button>
+ <button class="act primary" id="demo">Simulate Audience</button>
+ </div>
+ </div>
+
+ <p class="foot-note">Designer Wallcoverings · Interactive Story Mockup</p>
+</main>
+
+<script>
+ const story = document.getElementById('story');
+ const arena = document.getElementById('arena');
+ const results = document.getElementById('results');
+ const hint = document.getElementById('hint');
+ const root = document.documentElement;
+
+ const state = { left:0, right:0 };
+ const names = { left:'Damask', right:'Geometric' };
+
+ const el = {
+ lFill: document.querySelector('.swatch.left .fill'),
+ rFill: document.querySelector('.swatch.right .fill'),
+ lPct: document.querySelector('[data-pct="left"]'),
+ rPct: document.querySelector('[data-pct="right"]'),
+ lSeal: document.querySelector('.swatch.left .seal'),
+ rSeal: document.querySelector('.swatch.right .seal'),
+ bl: results.querySelector('.bl'),
+ br: results.querySelector('.br'),
+ rlName: document.getElementById('rl-name'),
+ rrName: document.getElementById('rr-name'),
+ rlCount: document.getElementById('rl-count'),
+ rrCount: document.getElementById('rr-count'),
+ };
+
+ function render(){
+ const total = state.left + state.right;
+ const voted = total > 0;
+ story.classList.toggle('voted', voted);
+
+ let lp = 50, rp = 50;
+ if (voted){ lp = Math.round(state.left/total*100); rp = 100 - lp; }
+
+ el.lFill.style.height = voted ? lp+'%' : '0';
+ el.rFill.style.height = voted ? rp+'%' : '0';
+ el.lPct.textContent = lp+'%';
+ el.rPct.textContent = rp+'%';
+ el.bl.style.width = lp+'%';
+ el.br.style.width = rp+'%';
+ el.rlCount.textContent = state.left;
+ el.rrCount.textContent = state.right;
+
+ // grow the leader's tile slightly
+ document.querySelector('.swatch.left').style.flex = voted ? Math.max(lp,25) : 1;
+ document.querySelector('.swatch.right').style.flex = voted ? Math.max(rp,25) : 1;
+
+ // winner seal
+ const lead = state.left === state.right ? null : (state.left > state.right ? 'left' : 'right');
+ el.lSeal.classList.toggle('show', voted && lead==='left');
+ el.rSeal.classList.toggle('show', voted && lead==='right');
+
+ hint.innerHTML = voted
+ ? `<span class="votecount">${total}</span> votes cast · tap to keep voting`
+ : 'Tap a side to cast your vote';
+ }
+
+ function vote(side){
+ state[side]++;
+ render();
+ }
+
+ arena.addEventListener('click', (e)=>{
+ if (e.target.closest('.name')) return; // editing the label, not voting
+ const sw = e.target.closest('.swatch');
+ if (sw) vote(sw.dataset.side);
+ });
+
+ // editable style names
+ document.querySelectorAll('.name').forEach(n=>{
+ n.addEventListener('input', ()=>{
+ const side = n.dataset.name;
+ names[side] = n.textContent.trim() || (side==='left'?'Style A':'Style B');
+ el.rlName.textContent = names.left;
+ el.rrName.textContent = names.right;
+ });
+ n.addEventListener('keydown', ev=>{ if(ev.key==='Enter'){ ev.preventDefault(); n.blur(); }});
+ });
+
+ // live color tokens
+ document.querySelectorAll('input[type=color]').forEach(inp=>{
+ inp.addEventListener('input', ()=> root.style.setProperty(inp.dataset.var, inp.value));
+ });
+
+ // buttons
+ document.getElementById('reset').addEventListener('click', ()=>{
+ state.left = 0; state.right = 0; render();
+ });
+ document.getElementById('demo').addEventListener('click', ()=>{
+ let n = 0;
+ const iv = setInterval(()=>{
+ vote(Math.random() < 0.56 ? 'left' : 'right');
+ if(++n >= 18) clearInterval(iv);
+ }, 70);
+ });
+
+ render();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/104018fe5adc/claude-code.png b/data/artifacts/104018fe5adc/claude-code.png
new file mode 100644
index 0000000..c1a8f45
Binary files /dev/null and b/data/artifacts/104018fe5adc/claude-code.png differ
diff --git a/data/artifacts/104018fe5adc/gemma3-12b.html b/data/artifacts/104018fe5adc/gemma3-12b.html
new file mode 100644
index 0000000..71f7f7e
--- /dev/null
+++ b/data/artifacts/104018fe5adc/gemma3-12b.html
@@ -0,0 +1,209 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
+ <title>This or That - Designer Wallcoverings</title>
+ <style>
+ :root {
+ --bg: #faf8f4; /* Ivory Gallery */
+ --surface: #ffffff;
+ --text: #1c1a17;
+ --muted: #9b948a;
+ --accent: #b0472e;
+ --line: rgba(0, 0, 0, .08);
+ --shadow: rgba(0, 0, 0, .12);
+ --ease: cubic-bezier(.22, 1, .36, 1);
+ }
+
+ body {
+ margin: 0;
+ font-family: system-ui, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
+ background-color: var(--bg);
+ color: var(--text);
+ height: 100vh;
+ overflow: hidden; /* Prevent scrollbars */
+ }
+
+ header {
+ padding: 48px;
+ display: flex;
+ align-items: center;
+ justify-content: left;
+ background-color: var(--surface);
+ box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
+ }
+
+ header h1 {
+ font-family: 'Didot', 'Bodoni MT', 'Playfair Display', Georgia, serif;
+ font-size: 67px;
+ line-height: 1.1;
+ letter-spacing: .18em;
+ margin: 0;
+ white-space: nowrap; /* Prevent wrapping */
+ }
+
+ .poll-container {
+ padding: 48px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 32px;
+ height: calc(100vh - 100px); /* Account for header height */
+ }
+
+ .choice {
+ width: 85%;
+ max-width: 400px;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 16px;
+ background-color: var(--surface);
+ padding: 32px;
+ border-radius: 14px;
+ box-shadow: 0 8px 24px var(--shadow);
+ }
+
+ .style-swatch {
+ width: 160px;
+ height: 160px;
+ border: 1px solid var(--line);
+ border-radius: 8px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ cursor: pointer; /* Indicate clickable */
+ }
+
+ .damask {
+ background: repeating-linear-gradient(45deg, #fff 0%, #fff 18px, #ccc 18px, #ccc 36px);
+ }
+
+ .geometric {
+ background: repeating-linear-gradient(45deg, #fff 0%, #fff 12px, #eee 12px, #eee 24px);
+ }
+
+
+ .vote-bar {
+ width: 100%;
+ height: 8px;
+ background-color: var(--accent);
+ border-radius: 4px;
+ transform: scaleX(0); /* Initially hidden */
+ transform-origin: left;
+ transition: transform .3s ease-in-out;
+ }
+
+ .winner-stamp {
+ position: absolute;
+ bottom: -20px;
+ left: 50%;
+ transform: translateX(-50%);
+ font-size: 18px;
+ background-color: var(--accent);
+ color: var(--surface);
+ padding: 8px 16px;
+ border-radius: 4px;
+ opacity: 0;
+ transition: opacity .3s ease-in-out;
+ }
+
+ .winner-stamp.active {
+ opacity: 1;
+ }
+
+
+ /* Mobile Responsive */
+ @media (max-width: 600px) {
+ header h1 {
+ font-size: 48px;
+ }
+ .poll-container{
+ padding: 24px;
+ height: calc(100vh - 72px);
+ }
+
+ .choice {
+ width: 95%;
+ }
+
+ .style-swatch {
+ width: 120px;
+ height: 120px;
+ }
+ }
+
+
+ </style>
+</head>
+<body>
+ <header>
+ <h1>DESIGNER WALLCOVERINGS</h1>
+ </header>
+
+ <div class="poll-container">
+ <h2>This or That?</h2>
+ <p style="text-align:center; font-size: 18px;">Choose your favorite wallcovering style!</p>
+
+ <div class="choice" data-style="damask">
+ <h3>Damask</h3>
+ <div class="style-swatch damask"></div>
+ <div class="vote-bar"></div>
+ </div>
+
+ <div class="choice" data-style="geometric">
+ <h3>Geometric</h3>
+ <div class="style-swatch geometric"></div>
+ <div class="vote-bar"></div>
+ </div>
+
+
+ <div class="winner-stamp" id="winnerStamp"></div>
+ </div>
+
+ <script>
+ const choices = document.querySelectorAll('.choice');
+ const voteBars = document.querySelectorAll('.vote-bar');
+ const winnerStamp = document.getElementById('winnerStamp');
+
+ choices.forEach(choice => {
+ choice.addEventListener('click', () => {
+ const style = choice.dataset.style;
+ const otherStyle = (style === 'damask') ? 'geometric' : 'damask';
+
+ // Animate vote bar fill
+ const voteBar = choice.querySelector('.vote-bar');
+ voteBar.style.transform = 'scaleX(1)';
+ setTimeout(() => {
+ voteBar.style.transform = 'scaleX(0)'; // Reset after animation
+ }, 300);
+
+ // Determine winner and display stamp
+ const allChoices = document.querySelectorAll('.choice');
+ let damaskVotes = 0;
+ let geometricVotes = 0;
+
+ allChoices.forEach(c => {
+ if (c.dataset.style === 'damask') {
+ damaskVotes++;
+ } else {
+ geometricVotes++;
+ }
+ });
+
+ let winningStyle = (damaskVotes > geometricVotes) ? 'damask' : 'geometric';
+
+ const winnerChoice = document.querySelector(`[data-style="${winningStyle}"]`);
+ winnerStamp.textContent = `"${winningStyle.charAt(0).toUpperCase() + winningStyle.slice(1)}" Wins!`; // Display a simple "Wins!" message
+ winnerStamp.classList.add('active');
+
+ setTimeout(() => {
+ winnerStamp.classList.remove('active');
+ winnerStamp.textContent = "";
+ }, 1500);
+ });
+ });
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/104018fe5adc/gemma3-12b.png b/data/artifacts/104018fe5adc/gemma3-12b.png
new file mode 100644
index 0000000..6ef27ff
Binary files /dev/null and b/data/artifacts/104018fe5adc/gemma3-12b.png differ
diff --git a/data/artifacts/104018fe5adc/gpt.html b/data/artifacts/104018fe5adc/gpt.html
new file mode 100644
index 0000000..038f720
--- /dev/null
+++ b/data/artifacts/104018fe5adc/gpt.html
@@ -0,0 +1,950 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <title>Designer Wallcoverings - This or That Story Poll</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <style>
+ :root {
+ /* Design tokens */
+ --bg: #0d0d0f;
+ --surface: #17171c;
+ --text: #1c1a17; /* darkest ink for wordmark (near-black warm ink) */
+ --ink-soft: #f5f2ea;
+ --muted: #8a8578;
+ --accent: #c9a961; /* metallic gold accent */
+ --accent-deep: #7d6a45;
+ --line: rgba(245, 242, 234, 0.18);
+ --shadow-soft: 0 18px 40px rgba(0, 0, 0, 0.42);
+ --shadow-subtle: 0 1px 0 rgba(255, 255, 255, 0.06), 0 0 0 1px rgba(0, 0, 0, 0.55);
+ --radius-card: 22px;
+ --radius-pill: 999px;
+ --ease-soft: cubic-bezier(.22,.9,.24,1);
+ --ease-pop: cubic-bezier(.34,1.56,.64,1);
+ --story-padding-x: 18px;
+ --story-padding-y: 14px;
+ --vote-height: 6px;
+ }
+
+ * {
+ box-sizing: border-box;
+ -webkit-tap-highlight-color: transparent;
+ }
+
+ html, body {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ font-family: "Avenir Next", "Century Gothic", Futura, "Segoe UI", sans-serif;
+ background: #000;
+ }
+
+ body {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ }
+
+ .story-frame {
+ position: relative;
+ width: min(100vw, 360px);
+ height: calc(min(100vw, 360px) * 16 / 9);
+ max-height: 640px;
+ background: radial-gradient(circle at top, #191922 0, var(--bg) 55%);
+ color: var(--ink-soft);
+ border-radius: 32px;
+ overflow: hidden;
+ box-shadow: 0 25px 70px rgba(0, 0, 0, 0.9);
+ }
+
+ /* Instagram-like chrome */
+ .status-bar {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ }
+
+ .status-top {
+ display: flex;
+ flex-direction: column;
+ padding: 10px var(--story-padding-x) 0;
+ gap: 8px;
+ }
+
+ .story-progress {
+ display: flex;
+ gap: 3px;
+ }
+
+ .story-progress-bar {
+ flex: 1;
+ height: 2px;
+ border-radius: 999px;
+ background: rgba(255,255,255,0.18);
+ overflow: hidden;
+ }
+
+ .story-progress-bar::before {
+ content: "";
+ display: block;
+ width: 55%;
+ height: 100%;
+ background: rgba(255,255,255,0.9);
+ }
+
+ .story-chrome-row {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 10px;
+ margin-top: 4px;
+ }
+
+ .story-avatar {
+ width: 26px;
+ height: 26px;
+ border-radius: 50%;
+ background:
+ radial-gradient(circle at 30% 20%, rgba(255,255,255,0.65) 0, rgba(255,255,255,0) 45%),
+ conic-gradient(from 210deg, #f7c76a, #e5528b, #7a5cff, #f7c76a);
+ padding: 2px;
+ box-shadow: 0 0 0 1px rgba(0,0,0,0.3);
+ }
+
+ .story-avatar-inner {
+ width: 100%;
+ height: 100%;
+ border-radius: inherit;
+ background:
+ radial-gradient(circle at 30% 30%, #f5f2ea 0, #d0c8b4 40%, #9a8f7a 100%);
+ }
+
+ .story-meta {
+ display: flex;
+ flex-direction: column;
+ min-width: 0;
+ }
+
+ .story-meta-primary {
+ display: flex;
+ align-items: baseline;
+ gap: 6px;
+ }
+
+ .story-username {
+ font-size: 13px;
+ font-weight: 600;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: rgba(245,242,234,0.95);
+ }
+
+ .story-time {
+ font-size: 11px;
+ color: rgba(245,242,234,0.6);
+ }
+
+ .story-viewers {
+ font-size: 11px;
+ color: rgba(245,242,234,0.6);
+ }
+
+ .story-controls {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ color: rgba(245,242,234,0.95);
+ font-size: 18px;
+ }
+
+ .story-close {
+ width: 18px;
+ height: 18px;
+ border-radius: 999px;
+ border: 1px solid rgba(245,242,234,0.65);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 11px;
+ }
+
+ .story-more {
+ width: 4px;
+ height: 14px;
+ border-radius: 999px;
+ border: 1px solid rgba(245,242,234,0.6);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 10px;
+ }
+
+ .story-more::before {
+ content: "⋮";
+ transform: translateY(-0.5px);
+ }
+
+ .story-bottom {
+ position: absolute;
+ inset-inline: var(--story-padding-x);
+ bottom: 12px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ pointer-events: none;
+ color: rgba(245,242,234,0.95);
+ font-size: 11px;
+ }
+
+ .story-send {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 5px 10px;
+ border-radius: 999px;
+ border: 1px solid rgba(245,242,234,0.6);
+ background: rgba(10,10,12,0.65);
+ backdrop-filter: blur(10px);
+ -webkit-backdrop-filter: blur(10px);
+ }
+
+ .story-send-label {
+ text-transform: uppercase;
+ letter-spacing: 0.16em;
+ font-size: 10px;
+ color: rgba(245,242,234,0.7);
+ }
+
+ .story-send-icon {
+ width: 13px;
+ height: 13px;
+ border-radius: 999px;
+ border: 1px solid rgba(245,242,234,0.7);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 9px;
+ }
+
+ .story-react {
+ display: flex;
+ gap: 7px;
+ pointer-events: auto;
+ }
+
+ .story-react span {
+ font-size: 17px;
+ line-height: 1;
+ transform-origin: center;
+ transition: transform 140ms var(--ease-pop);
+ }
+
+ .story-react span:active {
+ transform: scale(1.3);
+ }
+
+ /* Header with wordmark */
+ header.wordmark-header {
+ position: absolute;
+ inset-inline: var(--story-padding-x);
+ top: 46px;
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ z-index: 3;
+ pointer-events: none;
+ }
+
+ .wordmark-lockup {
+ max-width: 260px;
+ }
+
+ .wordmark {
+ margin: 0;
+ font-family: "Playfair Display", "Didot", "Bodoni MT", "Times New Roman", serif;
+ font-weight: 500;
+ font-size: 40px;
+ line-height: 1.1;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: var(--text);
+ text-shadow: 0 1px 0 rgba(255,255,255,0.12);
+ }
+
+ .wordmark-subline {
+ margin-top: 4px;
+ font-size: 10px;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ color: rgba(245,242,234,0.65);
+ }
+
+ .header-pill {
+ pointer-events: auto;
+ align-self: center;
+ padding: 7px 12px;
+ border-radius: var(--radius-pill);
+ border: 1px solid rgba(245,242,234,0.35);
+ background: radial-gradient(circle at top left, rgba(255,255,255,0.16) 0, rgba(255,255,255,0) 50%),
+ rgba(12,12,15,0.8);
+ backdrop-filter: blur(12px);
+ -webkit-backdrop-filter: blur(12px);
+ box-shadow: 0 12px 30px rgba(0,0,0,0.6);
+ font-size: 10px;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ color: rgba(245,242,234,0.85);
+ }
+
+ .header-pill span {
+ color: var(--accent);
+ }
+
+ /* Main poll layout */
+ main.story-main {
+ position: absolute;
+ inset: 0;
+ padding: 112px var(--story-padding-x) 60px;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ gap: 16px;
+ z-index: 1;
+ }
+
+ .poll-title-block {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ max-width: 70%;
+ }
+
+ .poll-eyebrow {
+ font-size: 11px;
+ letter-spacing: 0.2em;
+ text-transform: uppercase;
+ color: rgba(245,242,234,0.6);
+ }
+
+ .poll-title {
+ margin: 0;
+ font-family: "Playfair Display", "Didot", "Bodoni MT", "Times New Roman", serif;
+ font-size: 28px;
+ line-height: 1.15;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--ink-soft);
+ }
+
+ .poll-subtitle {
+ font-size: 13px;
+ color: rgba(245,242,234,0.7);
+ max-width: 240px;
+ }
+
+ .poll-card {
+ position: relative;
+ flex: 1;
+ min-height: 0;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 10px;
+ border-radius: var(--radius-card);
+ padding: 10px;
+ background:
+ radial-gradient(circle at top, rgba(255,255,255,0.08) 0, rgba(255,255,255,0) 48%),
+ linear-gradient(145deg, #15151b, #050507);
+ box-shadow: var(--shadow-soft);
+ border: 1px solid rgba(255,255,255,0.03);
+ overflow: hidden;
+ }
+
+ .poll-option {
+ position: relative;
+ border-radius: 18px;
+ padding: 14px 12px 16px;
+ background: radial-gradient(circle at top, rgba(255,255,255,0.15) 0, rgba(255,255,255,0.02) 55%),
+ rgba(7,7,10,0.95);
+ box-shadow: var(--shadow-subtle);
+ border: 1px solid var(--line);
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ gap: 12px;
+ cursor: pointer;
+ overflow: hidden;
+ transition: transform 220ms var(--ease-soft), box-shadow 220ms var(--ease-soft), border-color 220ms var(--ease-soft), background 220ms var(--ease-soft);
+ }
+
+ .poll-option:hover {
+ transform: translateY(-2px);
+ box-shadow: 0 20px 40px rgba(0,0,0,0.75);
+ border-color: rgba(255,255,255,0.24);
+ background: radial-gradient(circle at top, rgba(255,255,255,0.2) 0, rgba(255,255,255,0.04) 55%),
+ rgba(7,7,10,0.98);
+ }
+
+ .poll-option:active {
+ transform: translateY(0);
+ box-shadow: var(--shadow-subtle);
+ }
+
+ .poll-option.disabled {
+ cursor: default;
+ pointer-events: none;
+ opacity: 0.88;
+ }
+
+ .swatch {
+ position: relative;
+ border-radius: 14px;
+ overflow: hidden;
+ height: 120px;
+ border: 1px solid rgba(255,255,255,0.18);
+ background: #14141a;
+ box-shadow: inset 0 0 0 1px rgba(0,0,0,0.8);
+ }
+
+ /* Left swatch: Damask inspired motif using CSS + inline SVG texture */
+ .swatch-damask {
+ background:
+ radial-gradient(circle at 20% 15%, rgba(255,255,255,0.14) 0, transparent 45%),
+ radial-gradient(circle at 80% 75%, rgba(0,0,0,0.7) 0, transparent 60%),
+ repeating-radial-gradient(circle at 50% 20%, rgba(201,169,97,0.42), rgba(201,169,97,0.42) 2px, transparent 2px, transparent 6px),
+ linear-gradient(135deg, #191722, #0c0a11);
+ }
+
+ .swatch-damask::before {
+ content: "";
+ position: absolute;
+ inset: 14px 18px;
+ border-radius: 50%;
+ background-image:
+ radial-gradient(circle at 50% 0, rgba(255,255,255,0.4) 0, transparent 40%),
+ radial-gradient(circle at 0 100%, rgba(0,0,0,0.5) 0, transparent 55%);
+ mix-blend-mode: screen;
+ opacity: 0.6;
+ }
+
+ .swatch-damask::after {
+ content: "";
+ position: absolute;
+ inset: 18px 24px;
+ background:
+ radial-gradient(circle at 50% 15%, rgba(0,0,0,0.75) 0, transparent 48%),
+ radial-gradient(circle at 50% 85%, rgba(0,0,0,0.85) 0, transparent 48%);
+ mask-image: radial-gradient(circle at 50% 15%, black 0, transparent 52%),
+ radial-gradient(circle at 50% 85%, black 0, transparent 52%);
+ mask-composite: add;
+ -webkit-mask-image: radial-gradient(circle at 50% 15%, black 0, transparent 52%),
+ radial-gradient(circle at 50% 85%, black 0, transparent 52%);
+ -webkit-mask-composite: xor;
+ opacity: 0.7;
+ }
+
+ /* Overlay an inline SVG damask motif */
+ .swatch-damask-svg {
+ position: absolute;
+ inset: 10px 14px;
+ opacity: 0.9;
+ mix-blend-mode: screen;
+ }
+
+ /* Right swatch: Geometric with crisp metallic lines */
+ .swatch-geo {
+ background:
+ repeating-linear-gradient(
+ 45deg,
+ rgba(201,169,97,0.18),
+ rgba(201,169,97,0.18) 1px,
+ transparent 1px,
+ transparent 18px
+ ),
+ repeating-linear-gradient(
+ 135deg,
+ rgba(201,169,97,0.24),
+ rgba(201,169,97,0.24) 1px,
+ transparent 1px,
+ transparent 18px
+ ),
+ linear-gradient(135deg, #222128, #09080d);
+ }
+
+ .swatch-geo::before {
+ content: "";
+ position: absolute;
+ inset: 20px 18px;
+ background:
+ linear-gradient(90deg, transparent 0 8%, rgba(201,169,97,0.8) 9%, transparent 10% 19%, rgba(201,169,97,0.6) 20%, transparent 22% 29%, rgba(201,169,97,0.7) 30%, transparent 32% 39%, rgba(201,169,97,0.45) 40%, transparent 42% 49%, rgba(201,169,97,0.7) 50%, transparent 51%);
+ opacity: 0.9;
+ mix-blend-mode: screen;
+ }
+
+ .swatch-geo::after {
+ content: "";
+ position: absolute;
+ inset: 14px 16px;
+ border-radius: 10px;
+ border: 1px solid rgba(201,169,97,0.7);
+ box-shadow:
+ 0 0 0 1px rgba(0,0,0,0.85),
+ inset 0 0 18px rgba(0,0,0,0.8);
+ opacity: 0.9;
+ }
+
+ /* Option labels & vote bar */
+ .option-footer {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ }
+
+ .option-label-row {
+ display: flex;
+ justify-content: space-between;
+ align-items: baseline;
+ gap: 4px;
+ }
+
+ .option-name {
+ font-size: 12px;
+ letter-spacing: 0.19em;
+ text-transform: uppercase;
+ color: rgba(245,242,234,0.92);
+ font-weight: 600;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ }
+
+ .option-style-note {
+ font-size: 10px;
+ color: rgba(245,242,234,0.6);
+ text-transform: uppercase;
+ letter-spacing: 0.14em;
+ }
+
+ .option-percentage {
+ font-size: 11px;
+ color: rgba(245,242,234,0.85);
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ }
+
+ .vote-track {
+ position: relative;
+ height: var(--vote-height);
+ border-radius: 999px;
+ background: radial-gradient(circle at top, rgba(255,255,255,0.4) 0, transparent 65%),
+ rgba(0,0,0,0.85);
+ overflow: hidden;
+ box-shadow:
+ inset 0 0 0 1px rgba(255,255,255,0.06),
+ 0 1px 0 rgba(255,255,255,0.12);
+ }
+
+ .vote-fill {
+ position: absolute;
+ inset: 0;
+ width: 0%;
+ border-radius: inherit;
+ background: linear-gradient(90deg, rgba(201,169,97,0.1), rgba(201,169,97,0.95));
+ box-shadow: 0 0 12px rgba(201,169,97,0.6);
+ transition: width 560ms var(--ease-soft);
+ }
+
+ .poll-card.voted .poll-option {
+ opacity: 0.65;
+ transform: translateY(0);
+ }
+
+ .poll-card.voted .poll-option.selected {
+ opacity: 1;
+ box-shadow: 0 18px 40px rgba(0,0,0,0.9);
+ border-color: rgba(201,169,97,0.85);
+ background: radial-gradient(circle at top, rgba(255,255,255,0.22) 0, rgba(255,255,255,0.06) 55%),
+ rgba(7,7,10,0.99);
+ transform: translateY(-3px) scale(1.01);
+ }
+
+ .poll-card.voted .poll-option.selected .vote-fill {
+ box-shadow: 0 0 18px rgba(201,169,97,0.9);
+ }
+
+ /* Winner stamp */
+ .winner-stamp {
+ position: absolute;
+ top: 16px;
+ right: 10px;
+ padding: 5px 9px;
+ border-radius: var(--radius-pill);
+ border: 1px solid rgba(201,169,97,0.75);
+ background: radial-gradient(circle at top left, rgba(255,255,255,0.3) 0, rgba(255,255,255,0) 55%),
+ rgba(12,11,9,0.96);
+ box-shadow: 0 16px 30px rgba(0,0,0,0.8);
+ font-size: 9px;
+ letter-spacing: 0.22em;
+ text-transform: uppercase;
+ color: var(--accent);
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ opacity: 0;
+ transform: translateY(8px) scale(0.9);
+ transform-origin: 100% 0;
+ pointer-events: none;
+ transition: opacity 260ms var(--ease-soft), transform 320ms var(--ease-pop);
+ }
+
+ .winner-stamp::before {
+ content: "✶";
+ font-size: 10px;
+ color: var(--accent);
+ }
+
+ .winner-stamp.visible {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ }
+
+ .winner-stamp span {
+ color: rgba(245,242,234,0.8);
+ }
+
+ /* Overlay result card */
+ .result-overlay {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: radial-gradient(circle at top, rgba(0,0,0,0.15) 0, rgba(0,0,0,0.75) 60%);
+ pointer-events: none;
+ opacity: 0;
+ transition: opacity 260ms var(--ease-soft);
+ }
+
+ .result-overlay.visible {
+ opacity: 1;
+ pointer-events: auto;
+ }
+
+ .result-card {
+ min-width: 220px;
+ max-width: 260px;
+ border-radius: 20px;
+ padding: 14px 14px 12px;
+ background:
+ radial-gradient(circle at top left, rgba(255,255,255,0.22) 0, rgba(255,255,255,0.02) 50%),
+ rgba(8,8,10,0.96);
+ border: 1px solid rgba(201,169,97,0.7);
+ box-shadow: 0 28px 60px rgba(0,0,0,0.95);
+ }
+
+ .result-chip {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 4px 9px;
+ border-radius: var(--radius-pill);
+ border: 1px solid rgba(245,242,234,0.18);
+ background: rgba(12,12,15,0.8);
+ font-size: 10px;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: rgba(245,242,234,0.78);
+ margin-bottom: 8px;
+ }
+
+ .result-chip-dot {
+ width: 6px;
+ height: 6px;
+ border-radius: 999px;
+ background: var(--accent);
+ box-shadow: 0 0 8px rgba(201,169,97,0.9);
+ }
+
+ .result-heading {
+ font-family: "Playfair Display", "Didot", "Bodoni MT", "Times New Roman", serif;
+ font-size: 18px;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ margin: 0 0 4px;
+ color: var(--ink-soft);
+ }
+
+ .result-body {
+ font-size: 12px;
+ color: rgba(245,242,234,0.75);
+ margin: 0 0 10px;
+ }
+
+ .result-close {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ font-size: 10px;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ color: rgba(245,242,234,0.72);
+ }
+
+ .result-close-button {
+ padding: 4px 10px;
+ border-radius: var(--radius-pill);
+ border: 1px solid rgba(245,242,234,0.4);
+ background: rgba(0,0,0,0.8);
+ cursor: pointer;
+ transition: background 160ms var(--ease-soft), transform 160ms var(--ease-soft), box-shadow 160ms var(--ease-soft);
+ }
+
+ .result-close-button:hover {
+ background: rgba(201,169,97,0.14);
+ box-shadow: 0 0 12px rgba(201,169,97,0.4);
+ }
+
+ .result-close-button:active {
+ transform: translateY(1px);
+ box-shadow: none;
+ }
+ </style>
+</head>
+<body>
+ <div class="story-frame">
+ <!-- Chrome -->
+ <div class="status-bar">
+ <div class="status-top">
+ <div class="story-progress">
+ <div class="story-progress-bar"></div>
+ </div>
+ <div class="story-chrome-row">
+ <div style="display:flex;align-items:center;gap:8px;">
+ <div class="story-avatar">
+ <div class="story-avatar-inner"></div>
+ </div>
+ <div class="story-meta">
+ <div class="story-meta-primary">
+ <div class="story-username">designerwallcoverings</div>
+ <div class="story-time">12m</div>
+ </div>
+ <div class="story-viewers">Elevated poll • Curated finish</div>
+ </div>
+ </div>
+ <div class="story-controls">
+ <div class="story-more"></div>
+ <div class="story-close">×</div>
+ </div>
+ </div>
+ </div>
+ <div class="story-bottom">
+ <div class="story-send">
+ <div class="story-send-label">Send message</div>
+ <div class="story-send-icon">➤</div>
+ </div>
+ <div class="story-react">
+ <span>🤍</span>
+ <span>✨</span>
+ <span>😍</span>
+ </div>
+ </div>
+ </div>
+
+ <!-- Fixed header with wordmark -->
+ <header class="wordmark-header">
+ <div class="wordmark-lockup">
+ <h1 class="wordmark">DESIGNER<br>WALLCOVERINGS</h1>
+ <div class="wordmark-subline">Bespoke walls for luminous rooms</div>
+ </div>
+ <div class="header-pill">
+ THIS OR THAT · <span>VOTE</span>
+ </div>
+ </header>
+
+ <!-- Main poll content -->
+ <main class="story-main">
+ <section class="poll-title-block" aria-label="This or that poll">
+ <div class="poll-eyebrow">Wallcovering study</div>
+ <h2 class="poll-title">Damask or geometric?</h2>
+ <p class="poll-subtitle">Tap the style you would install for a candlelit dining room reveal.</p>
+ </section>
+
+ <section class="poll-card" aria-live="polite">
+ <!-- Winner stamp (positioned over card, toggled via JS) -->
+ <div class="winner-stamp" id="winnerStamp">
+ <span>Voted favourite</span>
+ </div>
+
+ <!-- Option A: Damask -->
+ <article class="poll-option" data-option="damask" aria-label="Vote for Damask">
+ <div class="swatch swatch-damask">
+ <!-- Simple inline SVG ornament, repeated via pattern -->
+ <svg class="swatch-damask-svg" viewBox="0 0 120 80" preserveAspectRatio="none">
+ <defs>
+ <pattern id="damaskPattern" x="0" y="0" width="0.25" height="0.5">
+ <path d="M60 5
+ C45 10 38 20 38 30
+ C38 40 45 48 60 55
+ C75 48 82 40 82 30
+ C82 20 75 10 60 5Z
+ M60 55
+ C50 60 45 66 45 72
+ C45 75 48 77 60 80
+ C72 77 75 75 75 72
+ C75 66 70 60 60 55Z"
+ fill="none"
+ stroke="rgba(201,169,97,0.75)"
+ stroke-width="1.2"
+ stroke-linejoin="round" />
+ </pattern>
+ </defs>
+ <rect x="0" y="-10" width="200" height="120" fill="url(#damaskPattern)" opacity="0.9"></rect>
+ </svg>
+ </div>
+ <div class="option-footer">
+ <div class="option-label-row">
+ <div>
+ <div class="option-name" data-label="damask-label" contenteditable="true" spellcheck="false">Damask Grande</div>
+ <div class="option-style-note">Hand-drawn scrollwork</div>
+ </div>
+ <div class="option-percentage" data-percent="damask">0%</div>
+ </div>
+ <div class="vote-track" aria-hidden="true">
+ <div class="vote-fill" data-fill="damask"></div>
+ </div>
+ </div>
+ </article>
+
+ <!-- Option B: Geometric -->
+ <article class="poll-option" data-option="geo" aria-label="Vote for Geometric">
+ <div class="swatch swatch-geo"></div>
+ <div class="option-footer">
+ <div class="option-label-row">
+ <div>
+ <div class="option-name" data-label="geo-label" contenteditable="true" spellcheck="false">Midnight Geometry</div>
+ <div class="option-style-note">Lacquered linework</div>
+ </div>
+ <div class="option-percentage" data-percent="geo">0%</div>
+ </div>
+ <div class="vote-track" aria-hidden="true">
+ <div class="vote-fill" data-fill="geo"></div>
+ </div>
+ </div>
+ </article>
+ </section>
+ </main>
+
+ <!-- Result overlay -->
+ <div class="result-overlay" id="resultOverlay">
+ <div class="result-card" role="dialog" aria-modal="true" aria-labelledby="resultHeading">
+ <div class="result-chip">
+ <div class="result-chip-dot"></div>
+ LIVE POLL
+ </div>
+ <h3 class="result-heading" id="resultHeading">Your room, your rhythm.</h3>
+ <p class="result-body" id="resultBody">
+ We saved your vote. Damask brings candlelit drama; geometry sharpens the architecture.
+ </p>
+ <div class="result-close">
+ <span>Tap to keep exploring</span>
+ <button type="button" class="result-close-button" id="resultCloseButton">Continue story</button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <script>
+ (function() {
+ const card = document.querySelector('.poll-card');
+ const options = document.querySelectorAll('.poll-option');
+ const fills = {
+ damask: document.querySelector('[data-fill="damask"]'),
+ geo: document.querySelector('[data-fill="geo"]')
+ };
+ const percents = {
+ damask: document.querySelector('[data-percent="damask"]'),
+ geo: document.querySelector('[data-percent="geo"]')
+ };
+ const winnerStamp = document.getElementById('winnerStamp');
+ const resultOverlay = document.getElementById('resultOverlay');
+ const resultBody = document.getElementById('resultBody');
+ const resultCloseButton = document.getElementById('resultCloseButton');
+
+ let hasVoted = false;
+
+ function animateVote(choice) {
+ if (hasVoted) return;
+ hasVoted = true;
+
+ const damaskShare = choice === 'damask' ? 0.68 : 0.42;
+ const geoShare = 1 - damaskShare;
+
+ fills.damask.style.width = (damaskShare * 100) + '%';
+ fills.geo.style.width = (geoShare * 100) + '%';
+
+ percents.damask.textContent = Math.round(damaskShare * 100) + '%';
+ percents.geo.textContent = Math.round(geoShare * 100) + '%';
+
+ card.classList.add('voted');
+ options.forEach(opt => {
+ if (opt.dataset.option === choice) {
+ opt.classList.add('selected');
+ }
+ });
+
+ const winningKey = damaskShare > geoShare ? 'damask' : 'geo';
+ const winnerElement = Array.from(options).find(o => o.dataset.option === winningKey);
+ if (winnerElement) {
+ const rect = winnerElement.getBoundingClientRect();
+ const cardRect = card.getBoundingClientRect();
+ const offsetTop = rect.top - cardRect.top + 6;
+ const offsetRight = cardRect.right - rect.right + 6;
+ winnerStamp.style.top = offsetTop + 'px';
+ winnerStamp.style.right = offsetRight + 'px';
+ winnerStamp.classList.add('visible');
+ }
+
+ const label = document.querySelector('[data-label="' + (choice === 'damask' ? 'damask-label' : 'geo-label') + '"]');
+ const chosenName = label ? label.textContent.trim() || (choice === 'damask' ? 'Damask' : 'Geometric') : (choice === 'damask' ? 'Damask' : 'Geometric');
+
+ if (choice === 'damask') {
+ resultBody.textContent = chosenName + " wins a slight edge tonight—soft patterning that glows under candlelight and frames heirloom pieces.";
+ } else {
+ resultBody.textContent = chosenName + " leads—graphic lines that catch the metalwork and echo the room’s architecture.";
+ }
+
+ setTimeout(() => {
+ resultOverlay.classList.add('visible');
+ }, 420);
+ }
+
+ options.forEach(option => {
+ option.addEventListener('click', () => {
+ animateVote(option.dataset.option);
+ });
+ });
+
+ resultOverlay.addEventListener('click', (e) => {
+ if (e.target === resultOverlay) {
+ resultOverlay.classList.remove('visible');
+ }
+ });
+
+ resultCloseButton.addEventListener('click', () => {
+ resultOverlay.classList.remove('visible');
+ });
+
+ // Make editable labels feel intentional
+ document.querySelectorAll('.option-name').forEach(el => {
+ el.addEventListener('focus', () => {
+ el.dataset.original = el.textContent;
+ });
+ el.addEventListener('blur', () => {
+ if (!el.textContent.trim()) {
+ el.textContent = el.dataset.original || '';
+ }
+ });
+ });
+ })();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/104018fe5adc/gpt.png b/data/artifacts/104018fe5adc/gpt.png
new file mode 100644
index 0000000..eb4f8e1
Binary files /dev/null and b/data/artifacts/104018fe5adc/gpt.png differ
diff --git a/data/artifacts/104018fe5adc/grok.html b/data/artifacts/104018fe5adc/grok.html
new file mode 100644
index 0000000..f85d7cf
--- /dev/null
+++ b/data/artifacts/104018fe5adc/grok.html
@@ -0,0 +1,1190 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Designer Wallcoverings — This or That</title>
+<style>
+:root {
+ --bg: #0d0d0f;
+ --surface: #17171c;
+ --surface-2: #1e1e24;
+ --ink: #1c1a17;
+ --text: #f5f2ea;
+ --muted: #8a8578;
+ --accent: #c9a961;
+ --accent-dim: #7d6a45;
+ --line: rgba(245, 242, 234, 0.12);
+ --line-strong: rgba(245, 242, 234, 0.22);
+ --shadow: 0 24px 48px rgba(0, 0, 0, 0.45);
+ --shadow-soft: 0 8px 24px rgba(0, 0, 0, 0.28);
+ --ease: cubic-bezier(.22, .9, .24, 1);
+ --ease-out: cubic-bezier(.22, 1, .36, 1);
+ --ease-pop: cubic-bezier(.34, 1.56, .64, 1);
+ --serif: 'Didot', 'Bodoni MT', 'Playfair Display', 'Cormorant Garamond', Georgia, 'Times New Roman', serif;
+ --sans: 'Avenir Next', 'Century Gothic', 'Helvetica Neue', 'Segoe UI', sans-serif;
+ --phone-w: 390px;
+ --phone-h: 694px;
+ --radius: 40px;
+ --hair: 1px;
+}
+
+*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
+
+html, body {
+ height: 100%;
+ background: var(--bg);
+ color: var(--text);
+ font-family: var(--sans);
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+body {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ min-height: 100vh;
+ padding: 28px 20px 48px;
+ background:
+ radial-gradient(ellipse 80% 50% at 50% -10%, rgba(201, 169, 97, 0.08), transparent 55%),
+ var(--bg);
+}
+
+/* ── Brand header (outside phone) ── */
+.site-header {
+ width: min(100%, 720px);
+ padding: 8px 4px 28px;
+ text-align: left;
+}
+
+.wordmark {
+ font-family: var(--serif);
+ font-size: clamp(22px, 4.2vw, 42px);
+ font-weight: 400;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: var(--text);
+ line-height: 1.15;
+ max-width: 100%;
+ white-space: normal;
+ word-break: break-word;
+}
+
+.wordmark-sub {
+ margin-top: 10px;
+ font-family: var(--sans);
+ font-size: 11px;
+ letter-spacing: 0.22em;
+ text-transform: uppercase;
+ color: var(--muted);
+}
+
+/* ── Stage ── */
+.stage {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 28px;
+}
+
+/* ── Phone chrome ── */
+.phone {
+ position: relative;
+ width: var(--phone-w);
+ height: var(--phone-h);
+ border-radius: var(--radius);
+ background: var(--surface);
+ box-shadow: var(--shadow), 0 0 0 1px rgba(255,255,255,0.06), inset 0 0 0 1px rgba(0,0,0,0.4);
+ overflow: hidden;
+ isolation: isolate;
+}
+
+.phone-bezel {
+ position: absolute;
+ inset: 0;
+ border-radius: var(--radius);
+ box-shadow: inset 0 0 0 2px rgba(255,255,255,0.04);
+ pointer-events: none;
+ z-index: 40;
+}
+
+/* Dynamic Island */
+.notch {
+ position: absolute;
+ top: 12px;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 118px;
+ height: 34px;
+ background: #000;
+ border-radius: 20px;
+ z-index: 50;
+ box-shadow: inset 0 0 0 1px rgba(255,255,255,0.06);
+}
+
+/* Story progress bars */
+.story-progress {
+ position: absolute;
+ top: 54px;
+ left: 14px;
+ right: 14px;
+ display: flex;
+ gap: 4px;
+ z-index: 30;
+}
+
+.story-progress span {
+ flex: 1;
+ height: 2px;
+ background: rgba(245, 242, 234, 0.22);
+ border-radius: 2px;
+ overflow: hidden;
+}
+
+.story-progress span i {
+ display: block;
+ height: 100%;
+ width: 0%;
+ background: var(--text);
+ border-radius: 2px;
+ animation: progressFill 6s linear forwards;
+}
+
+@keyframes progressFill {
+ to { width: 100%; }
+}
+
+/* Story top chrome */
+.story-top {
+ position: absolute;
+ top: 66px;
+ left: 14px;
+ right: 14px;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ z-index: 30;
+}
+
+.avatar {
+ width: 32px;
+ height: 32px;
+ border-radius: 50%;
+ background:
+ conic-gradient(from 210deg, var(--accent), var(--accent-dim), var(--accent));
+ padding: 1.5px;
+ flex-shrink: 0;
+}
+
+.avatar-inner {
+ width: 100%;
+ height: 100%;
+ border-radius: 50%;
+ background: var(--surface);
+ display: grid;
+ place-items: center;
+ font-family: var(--serif);
+ font-size: 13px;
+ color: var(--accent);
+ letter-spacing: 0.04em;
+}
+
+.story-meta {
+ flex: 1;
+ min-width: 0;
+}
+
+.story-user {
+ font-size: 12px;
+ font-weight: 600;
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+ color: var(--text);
+}
+
+.story-time {
+ font-size: 11px;
+ color: var(--muted);
+ margin-top: 1px;
+ letter-spacing: 0.04em;
+}
+
+.story-actions {
+ display: flex;
+ gap: 14px;
+ color: var(--text);
+ opacity: 0.85;
+}
+
+.story-actions svg {
+ width: 18px;
+ height: 18px;
+ stroke: currentColor;
+ fill: none;
+ stroke-width: 1.6;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+}
+
+/* Story body */
+.story-body {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ flex-direction: column;
+ padding: 118px 0 0;
+ background:
+ linear-gradient(180deg, rgba(13,13,15,0.55) 0%, transparent 18%, transparent 72%, rgba(13,13,15,0.75) 100%),
+ var(--surface);
+}
+
+.prompt {
+ text-align: center;
+ padding: 0 28px 18px;
+ z-index: 5;
+}
+
+.prompt-kicker {
+ font-family: var(--sans);
+ font-size: 10px;
+ letter-spacing: 0.28em;
+ text-transform: uppercase;
+ color: var(--accent);
+ margin-bottom: 10px;
+}
+
+.prompt-title {
+ font-family: var(--serif);
+ font-size: 28px;
+ font-weight: 400;
+ letter-spacing: 0.04em;
+ line-height: 1.15;
+ color: var(--text);
+}
+
+.prompt-title em {
+ font-style: italic;
+ color: var(--accent);
+ letter-spacing: 0.02em;
+}
+
+/* Split poll */
+.poll {
+ flex: 1;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ position: relative;
+ min-height: 0;
+ margin: 0 14px 14px;
+ border-radius: 18px;
+ overflow: hidden;
+ border: var(--hair) solid var(--line-strong);
+ box-shadow: var(--shadow-soft);
+}
+
+.poll-side {
+ position: relative;
+ cursor: pointer;
+ border: none;
+ background: transparent;
+ padding: 0;
+ outline: none;
+ overflow: hidden;
+ color: inherit;
+ font: inherit;
+ transition: filter 0.45s var(--ease);
+ -webkit-tap-highlight-color: transparent;
+}
+
+.poll-side:focus-visible {
+ outline: 2px solid var(--accent);
+ outline-offset: -2px;
+}
+
+.poll-side.is-dimmed {
+ filter: brightness(0.55) saturate(0.7);
+}
+
+.poll-side.is-winner {
+ filter: brightness(1.05);
+}
+
+.swatch {
+ position: absolute;
+ inset: 0;
+}
+
+/* Damask — ornate baroque motif via layered CSS + SVG */
+.swatch-damask {
+ background:
+ radial-gradient(ellipse 70% 50% at 50% 40%, rgba(201, 169, 97, 0.18), transparent 70%),
+ linear-gradient(165deg, #2a2218 0%, #1a1510 45%, #0f0d0b 100%);
+}
+
+.swatch-damask svg {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ opacity: 0.72;
+}
+
+/* Geometric — crisp tessellation */
+.swatch-geo {
+ background:
+ linear-gradient(145deg, #12161c 0%, #0c1016 50%, #151a22 100%);
+}
+
+.swatch-geo svg {
+ position: absolute;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ opacity: 0.85;
+}
+
+.side-label {
+ position: absolute;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ padding: 48px 14px 18px;
+ background: linear-gradient(to top, rgba(0,0,0,0.72) 0%, transparent 100%);
+ text-align: center;
+ z-index: 3;
+ pointer-events: none;
+}
+
+.side-name {
+ font-family: var(--serif);
+ font-size: 24px;
+ font-weight: 400;
+ letter-spacing: 0.06em;
+ color: var(--text);
+ line-height: 1.1;
+}
+
+.side-tag {
+ margin-top: 6px;
+ font-family: var(--sans);
+ font-size: 10px;
+ letter-spacing: 0.2em;
+ text-transform: uppercase;
+ color: rgba(245, 242, 234, 0.62);
+}
+
+/* Center OR diamond */
+.or-badge {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ z-index: 10;
+ width: 44px;
+ height: 44px;
+ display: grid;
+ place-items: center;
+ pointer-events: none;
+}
+
+.or-badge::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background: var(--ink);
+ border: var(--hair) solid var(--accent);
+ transform: rotate(45deg);
+ border-radius: 4px;
+ box-shadow: 0 6px 20px rgba(0,0,0,0.5);
+}
+
+.or-badge span {
+ position: relative;
+ font-family: var(--sans);
+ font-size: 10px;
+ font-weight: 600;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ color: var(--accent);
+}
+
+/* Vote bar */
+.vote-bar-wrap {
+ position: absolute;
+ left: 18px;
+ right: 18px;
+ bottom: 72px;
+ z-index: 12;
+ opacity: 0;
+ transform: translateY(8px);
+ transition: opacity 0.5s var(--ease-out), transform 0.5s var(--ease-out);
+ pointer-events: none;
+}
+
+.vote-bar-wrap.is-visible {
+ opacity: 1;
+ transform: translateY(0);
+}
+
+.vote-bar {
+ display: flex;
+ height: 36px;
+ border-radius: 100px;
+ overflow: hidden;
+ background: rgba(0,0,0,0.45);
+ border: var(--hair) solid var(--line-strong);
+ backdrop-filter: blur(8px);
+}
+
+.vote-fill {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 100%;
+ width: 50%;
+ transition: width 1.1s var(--ease-out);
+ position: relative;
+ overflow: hidden;
+}
+
+.vote-fill-a {
+ background: linear-gradient(90deg, #3d3224, var(--accent-dim));
+ justify-content: flex-start;
+ padding-left: 14px;
+}
+
+.vote-fill-b {
+ background: linear-gradient(90deg, #1a2430, #2a3a4e);
+ justify-content: flex-end;
+ padding-right: 14px;
+}
+
+.vote-pct {
+ font-family: var(--sans);
+ font-size: 11px;
+ font-weight: 600;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ color: var(--text);
+ white-space: nowrap;
+ opacity: 0;
+ transition: opacity 0.4s var(--ease) 0.35s;
+}
+
+.vote-bar-wrap.is-visible .vote-pct {
+ opacity: 1;
+}
+
+/* Winner stamp */
+.stamp {
+ position: absolute;
+ top: 42%;
+ left: 50%;
+ transform: translate(-50%, -50%) scale(0.6) rotate(-12deg);
+ z-index: 15;
+ opacity: 0;
+ pointer-events: none;
+ transition: none;
+}
+
+.stamp.is-show {
+ animation: stampIn 0.65s var(--ease-pop) 0.55s forwards;
+}
+
+@keyframes stampIn {
+ 0% { opacity: 0; transform: translate(-50%, -50%) scale(1.4) rotate(-18deg); }
+ 60% { opacity: 1; transform: translate(-50%, -50%) scale(0.92) rotate(-8deg); }
+ 100% { opacity: 1; transform: translate(-50%, -50%) scale(1) rotate(-12deg); }
+}
+
+.stamp-ring {
+ width: 108px;
+ height: 108px;
+ border-radius: 50%;
+ border: 2px solid var(--accent);
+ display: grid;
+ place-items: center;
+ background: rgba(13, 13, 15, 0.72);
+ box-shadow: 0 0 0 1px rgba(201, 169, 97, 0.25), 0 12px 32px rgba(0,0,0,0.4);
+ backdrop-filter: blur(6px);
+}
+
+.stamp-inner {
+ text-align: center;
+ padding: 8px;
+}
+
+.stamp-word {
+ font-family: var(--serif);
+ font-size: 22px;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ color: var(--accent);
+ line-height: 1;
+}
+
+.stamp-sub {
+ margin-top: 6px;
+ font-family: var(--sans);
+ font-size: 9px;
+ letter-spacing: 0.24em;
+ text-transform: uppercase;
+ color: var(--text);
+ opacity: 0.8;
+}
+
+/* Story bottom reply bar */
+.story-bottom {
+ position: absolute;
+ left: 14px;
+ right: 14px;
+ bottom: 18px;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ z-index: 20;
+}
+
+.reply-pill {
+ flex: 1;
+ height: 40px;
+ border-radius: 100px;
+ border: var(--hair) solid var(--line-strong);
+ background: rgba(0,0,0,0.25);
+ display: flex;
+ align-items: center;
+ padding: 0 16px;
+ font-size: 13px;
+ color: rgba(245, 242, 234, 0.45);
+ letter-spacing: 0.02em;
+}
+
+.icon-btn {
+ width: 36px;
+ height: 36px;
+ display: grid;
+ place-items: center;
+ color: var(--text);
+ background: none;
+ border: none;
+ cursor: default;
+ opacity: 0.9;
+}
+
+.icon-btn svg {
+ width: 22px;
+ height: 22px;
+ stroke: currentColor;
+ fill: none;
+ stroke-width: 1.5;
+ stroke-linecap: round;
+ stroke-linejoin: round;
+}
+
+/* Home indicator */
+.home-bar {
+ position: absolute;
+ bottom: 6px;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 120px;
+ height: 4px;
+ background: rgba(245, 242, 234, 0.35);
+ border-radius: 4px;
+ z-index: 40;
+}
+
+/* Controls under phone */
+.controls {
+ width: min(100%, var(--phone-w));
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+ padding: 4px 2px 0;
+}
+
+.controls-title {
+ font-family: var(--sans);
+ font-size: 10px;
+ letter-spacing: 0.24em;
+ text-transform: uppercase;
+ color: var(--muted);
+ text-align: center;
+}
+
+.edit-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 14px;
+}
+
+.edit-card {
+ background: var(--surface);
+ border: var(--hair) solid var(--line);
+ border-radius: 12px;
+ padding: 14px 14px 16px;
+ box-shadow: var(--shadow-soft);
+}
+
+.edit-card label {
+ display: block;
+ font-size: 10px;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: var(--muted);
+ margin-bottom: 8px;
+}
+
+.edit-card input[type="text"] {
+ width: 100%;
+ background: transparent;
+ border: none;
+ border-bottom: var(--hair) solid var(--line-strong);
+ color: var(--text);
+ font-family: var(--serif);
+ font-size: 18px;
+ letter-spacing: 0.04em;
+ padding: 4px 0 8px;
+ outline: none;
+ border-radius: 0;
+ transition: border-color 0.3s var(--ease);
+}
+
+.edit-card input[type="text"]:focus {
+ border-bottom-color: var(--accent);
+}
+
+.color-row {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-top: 12px;
+}
+
+.color-row span {
+ font-size: 10px;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+ color: var(--muted);
+ flex: 1;
+}
+
+.color-row input[type="color"] {
+ -webkit-appearance: none;
+ appearance: none;
+ width: 32px;
+ height: 32px;
+ border: var(--hair) solid var(--line-strong);
+ border-radius: 50%;
+ background: transparent;
+ padding: 0;
+ cursor: pointer;
+ overflow: hidden;
+}
+
+.color-row input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; }
+.color-row input[type="color"]::-webkit-color-swatch {
+ border: none;
+ border-radius: 50%;
+}
+.color-row input[type="color"]::-moz-color-swatch {
+ border: none;
+ border-radius: 50%;
+}
+
+.reset-row {
+ display: flex;
+ justify-content: center;
+ padding-top: 4px;
+}
+
+.btn-reset {
+ font-family: var(--sans);
+ font-size: 11px;
+ letter-spacing: 0.2em;
+ text-transform: uppercase;
+ color: var(--muted);
+ background: transparent;
+ border: var(--hair) solid var(--line);
+ border-radius: 100px;
+ padding: 10px 22px;
+ cursor: pointer;
+ transition: color 0.3s var(--ease), border-color 0.3s var(--ease), background 0.3s var(--ease);
+}
+
+.btn-reset:hover {
+ color: var(--text);
+ border-color: var(--accent-dim);
+ background: rgba(201, 169, 97, 0.06);
+}
+
+/* Result overlay (in-page, no alerts) */
+.result-overlay {
+ position: absolute;
+ inset: 0;
+ z-index: 35;
+ display: grid;
+ place-items: center;
+ background: rgba(13, 13, 15, 0.55);
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.45s var(--ease);
+ backdrop-filter: blur(2px);
+}
+
+.result-overlay.is-on {
+ opacity: 1;
+ pointer-events: auto;
+}
+
+.result-card {
+ width: min(86%, 280px);
+ background: var(--surface-2);
+ border: var(--hair) solid var(--line-strong);
+ border-radius: 16px;
+ padding: 28px 24px 22px;
+ text-align: center;
+ box-shadow: var(--shadow);
+ transform: translateY(12px) scale(0.96);
+ transition: transform 0.5s var(--ease-out);
+}
+
+.result-overlay.is-on .result-card {
+ transform: translateY(0) scale(1);
+}
+
+.result-eyebrow {
+ font-size: 10px;
+ letter-spacing: 0.26em;
+ text-transform: uppercase;
+ color: var(--accent);
+ margin-bottom: 12px;
+}
+
+.result-name {
+ font-family: var(--serif);
+ font-size: 32px;
+ letter-spacing: 0.04em;
+ color: var(--text);
+ line-height: 1.1;
+}
+
+.result-copy {
+ margin-top: 10px;
+ font-size: 13px;
+ line-height: 1.55;
+ color: var(--muted);
+}
+
+.result-actions {
+ margin-top: 20px;
+ display: flex;
+ justify-content: center;
+ gap: 8px;
+}
+
+.btn-ghost {
+ font-family: var(--sans);
+ font-size: 10px;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: var(--text);
+ background: transparent;
+ border: var(--hair) solid var(--line-strong);
+ border-radius: 100px;
+ padding: 10px 18px;
+ cursor: pointer;
+ transition: border-color 0.3s var(--ease), color 0.3s var(--ease);
+}
+
+.btn-ghost:hover {
+ border-color: var(--accent);
+ color: var(--accent);
+}
+
+/* Tap ripple */
+.ripple {
+ position: absolute;
+ width: 12px;
+ height: 12px;
+ border-radius: 50%;
+ background: rgba(201, 169, 97, 0.35);
+ transform: translate(-50%, -50%) scale(0);
+ pointer-events: none;
+ animation: rippleOut 0.7s var(--ease-out) forwards;
+ z-index: 4;
+}
+
+@keyframes rippleOut {
+ to { transform: translate(-50%, -50%) scale(28); opacity: 0; }
+}
+
+@media (max-width: 440px) {
+ :root { --phone-w: min(100vw - 32px, 390px); --phone-h: calc(var(--phone-w) * 16 / 9); }
+ body { padding: 16px 12px 36px; }
+ .wordmark { font-size: 22px; letter-spacing: 0.14em; }
+}
+</style>
+</head>
+<body>
+
+<header class="site-header">
+ <div class="wordmark">Designer Wallcoverings</div>
+ <div class="wordmark-sub">Editorial Story · This or That</div>
+</header>
+
+<div class="stage">
+ <div class="phone" id="phone" aria-label="Instagram Story poll mockup">
+ <div class="phone-bezel"></div>
+ <div class="notch" aria-hidden="true"></div>
+
+ <div class="story-progress" aria-hidden="true">
+ <span><i></i></span>
+ <span></span>
+ <span></span>
+ </div>
+
+ <div class="story-top">
+ <div class="avatar"><div class="avatar-inner">DW</div></div>
+ <div class="story-meta">
+ <div class="story-user">designer.wallcoverings</div>
+ <div class="story-time">2h</div>
+ </div>
+ <div class="story-actions" aria-hidden="true">
+ <svg viewBox="0 0 24 24"><circle cx="12" cy="5" r="1.2" fill="currentColor" stroke="none"/><circle cx="12" cy="12" r="1.2" fill="currentColor" stroke="none"/><circle cx="12" cy="19" r="1.2" fill="currentColor" stroke="none"/></svg>
+ </div>
+ </div>
+
+ <div class="story-body">
+ <div class="prompt">
+ <div class="prompt-kicker">Collection Vote</div>
+ <h1 class="prompt-title">This <em>or</em> That</h1>
+ </div>
+
+ <div class="poll" id="poll" role="group" aria-label="Style poll">
+ <button type="button" class="poll-side" id="sideA" data-side="a" aria-label="Vote Damask">
+ <div class="swatch swatch-damask" id="swatchA">
+ <!-- Ornate damask motif -->
+ <svg viewBox="0 0 200 360" preserveAspectRatio="xMidYMid slice" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
+ <defs>
+ <pattern id="damaskTile" width="100" height="120" patternUnits="userSpaceOnUse">
+ <path d="M50 8
+ C58 22 72 28 72 48
+ C72 64 60 72 50 88
+ C40 72 28 64 28 48
+ C28 28 42 22 50 8Z" fill="none" stroke="#c9a961" stroke-width="1.1" opacity="0.85"/>
+ <path d="M50 40
+ C54 46 60 50 60 58
+ C60 66 54 70 50 78
+ C46 70 40 66 40 58
+ C40 50 46 46 50 40Z" fill="#c9a961" opacity="0.22"/>
+ <circle cx="50" cy="58" r="3" fill="#c9a961" opacity="0.55"/>
+ <path d="M50 88 C54 100 62 108 62 120 M50 88 C46 100 38 108 38 120" fill="none" stroke="#c9a961" stroke-width="0.8" opacity="0.45"/>
+ <path d="M18 60 Q8 80 18 100" fill="none" stroke="#c9a961" stroke-width="0.7" opacity="0.35"/>
+ <path d="M82 60 Q92 80 82 100" fill="none" stroke="#c9a961" stroke-width="0.7" opacity="0.35"/>
+ <path d="M50 0 V8 M28 48 H18 M72 48 H82" stroke="#c9a961" stroke-width="0.6" opacity="0.3"/>
+ </pattern>
+ <linearGradient id="damaskVeil" x1="0" y1="0" x2="0" y2="1">
+ <stop offset="0%" stop-color="#2a2218" stop-opacity="0.15"/>
+ <stop offset="100%" stop-color="#0a0908" stop-opacity="0.55"/>
+ </linearGradient>
+ </defs>
+ <rect width="200" height="360" fill="url(#damaskTile)"/>
+ <rect width="200" height="360" fill="url(#damaskVeil)"/>
+ <!-- central medallion -->
+ <g transform="translate(100 150)" opacity="0.9">
+ <ellipse cx="0" cy="0" rx="42" ry="56" fill="none" stroke="#c9a961" stroke-width="1.2"/>
+ <ellipse cx="0" cy="0" rx="28" ry="38" fill="none" stroke="#c9a961" stroke-width="0.8" opacity="0.7"/>
+ <path d="M0 -48
+ C10 -30 22 -22 22 0
+ C22 22 10 30 0 48
+ C-10 30 -22 22 -22 0
+ C-22 -22 -10 -30 0 -48Z" fill="#c9a961" opacity="0.12" stroke="#c9a961" stroke-width="0.9"/>
+ <circle cx="0" cy="0" r="5" fill="#c9a961" opacity="0.5"/>
+ <path d="M0 -18 C4 -10 8 -6 8 0 C8 6 4 10 0 18 C-4 10 -8 6 -8 0 C-8 -6 -4 -10 0 -18Z" fill="#c9a961" opacity="0.35"/>
+ </g>
+ </svg>
+ </div>
+ <div class="side-label">
+ <div class="side-name" id="nameA">Damask</div>
+ <div class="side-tag">Heritage weave</div>
+ </div>
+ </button>
+
+ <button type="button" class="poll-side" id="sideB" data-side="b" aria-label="Vote Geometric">
+ <div class="swatch swatch-geo" id="swatchB">
+ <!-- Crisp geometric tessellation -->
+ <svg viewBox="0 0 200 360" preserveAspectRatio="xMidYMid slice" xmlns="http://www.w3.org/2000/svg" aria-hidden="true">
+ <defs>
+ <pattern id="geoTile" width="40" height="69.28" patternUnits="userSpaceOnUse">
+ <!-- hex lattice -->
+ <path d="M20 0 L40 11.55 L40 34.64 L20 46.19 L0 34.64 L0 11.55 Z" fill="none" stroke="#8fa4b8" stroke-width="0.9" opacity="0.55"/>
+ <path d="M20 11.55 L30 17.32 L30 28.87 L20 34.64 L10 28.87 L10 17.32 Z" fill="#6b8a9e" opacity="0.12" stroke="#a8c0d4" stroke-width="0.6"/>
+ </pattern>
+ <pattern id="geoLines" width="28" height="28" patternUnits="userSpaceOnUse" patternTransform="rotate(30)">
+ <line x1="0" y1="0" x2="0" y2="28" stroke="#7a96aa" stroke-width="0.5" opacity="0.25"/>
+ </pattern>
+ <linearGradient id="geoVeil" x1="0" y1="0" x2="1" y2="1">
+ <stop offset="0%" stop-color="#1a2836" stop-opacity="0.2"/>
+ <stop offset="100%" stop-color="#070a0e" stop-opacity="0.65"/>
+ </linearGradient>
+ </defs>
+ <rect width="200" height="360" fill="url(#geoTile)"/>
+ <rect width="200" height="360" fill="url(#geoLines)"/>
+ <rect width="200" height="360" fill="url(#geoVeil)"/>
+ <!-- large diamond focal -->
+ <g transform="translate(100 170)" opacity="0.95">
+ <polygon points="0,-58 50,0 0,58 -50,0" fill="none" stroke="#b8d0e0" stroke-width="1.3"/>
+ <polygon points="0,-36 32,0 0,36 -32,0" fill="#6b8a9e" opacity="0.15" stroke="#c5d8e6" stroke-width="0.8"/>
+ <polygon points="0,-16 14,0 0,16 -14,0" fill="#a8c0d4" opacity="0.35"/>
+ <line x1="-50" y1="0" x2="50" y2="0" stroke="#8fa4b8" stroke-width="0.5" opacity="0.5"/>
+ <line x1="0" y1="-58" x2="0" y2="58" stroke="#8fa4b8" stroke-width="0.5" opacity="0.5"/>
+ <!-- corner chevrons -->
+ <path d="M-70 -40 L-55 -40 L-55 -25" fill="none" stroke="#9bb4c6" stroke-width="1" opacity="0.4"/>
+ <path d="M70 -40 L55 -40 L55 -25" fill="none" stroke="#9bb4c6" stroke-width="1" opacity="0.4"/>
+ <path d="M-70 40 L-55 40 L-55 25" fill="none" stroke="#9bb4c6" stroke-width="1" opacity="0.4"/>
+ <path d="M70 40 L55 40 L55 25" fill="none" stroke="#9bb4c6" stroke-width="1" opacity="0.4"/>
+ </g>
+ </svg>
+ </div>
+ <div class="side-label">
+ <div class="side-name" id="nameB">Geometric</div>
+ <div class="side-tag">Modern lattice</div>
+ </div>
+ </button>
+
+ <div class="or-badge" aria-hidden="true"><span>or</span></div>
+
+ <div class="vote-bar-wrap" id="voteBar">
+ <div class="vote-bar">
+ <div class="vote-fill vote-fill-a" id="fillA" style="width:50%">
+ <span class="vote-pct" id="pctA">50%</span>
+ </div>
+ <div class="vote-fill vote-fill-b" id="fillB" style="width:50%">
+ <span class="vote-pct" id="pctB">50%</span>
+ </div>
+ </div>
+ </div>
+
+ <div class="stamp" id="stamp" aria-hidden="true">
+ <div class="stamp-ring">
+ <div class="stamp-inner">
+ <div class="stamp-word">Winner</div>
+ <div class="stamp-sub" id="stampName">—</div>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="story-bottom">
+ <div class="reply-pill">Send message…</div>
+ <div class="icon-btn" aria-hidden="true">
+ <svg viewBox="0 0 24 24"><path d="M20.8 4.6a5.5 5.5 0 0 0-7.8 0L12 5.6l-1-1a5.5 5.5 0 0 0-7.8 7.8l1 1L12 21l7.8-7.6 1-1a5.5 5.5 0 0 0 0-7.8z"/></svg>
+ </div>
+ <div class="icon-btn" aria-hidden="true">
+ <svg viewBox="0 0 24 24"><path d="M22 2L11 13"/><path d="M22 2L15 22l-4-9-9-4 20-7z"/></svg>
+ </div>
+ </div>
+ </div>
+
+ <div class="home-bar" aria-hidden="true"></div>
+
+ <div class="result-overlay" id="resultOverlay" role="dialog" aria-modal="true" aria-labelledby="resultName">
+ <div class="result-card">
+ <div class="result-eyebrow">Studio Choice</div>
+ <div class="result-name" id="resultName">Damask</div>
+ <p class="result-copy" id="resultCopy">Your vote is in. The room leans heritage — ornate, layered, timeless.</p>
+ <div class="result-actions">
+ <button type="button" class="btn-ghost" id="btnCloseResult">Dismiss</button>
+ <button type="button" class="btn-ghost" id="btnRevote">Vote again</button>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="controls">
+ <div class="controls-title">Edit styles</div>
+ <div class="edit-grid">
+ <div class="edit-card">
+ <label for="inputNameA">Style A</label>
+ <input type="text" id="inputNameA" value="Damask" maxlength="18" autocomplete="off">
+ <div class="color-row">
+ <span>Accent wash</span>
+ <input type="color" id="colorA" value="#c9a961" title="Damask accent">
+ </div>
+ </div>
+ <div class="edit-card">
+ <label for="inputNameB">Style B</label>
+ <input type="text" id="inputNameB" value="Geometric" maxlength="18" autocomplete="off">
+ <div class="color-row">
+ <span>Accent wash</span>
+ <input type="color" id="colorB" value="#6b8a9e" title="Geometric accent">
+ </div>
+ </div>
+ </div>
+ <div class="reset-row">
+ <button type="button" class="btn-reset" id="btnReset">Reset poll</button>
+ </div>
+ </div>
+</div>
+
+<script>
+(function () {
+ const sideA = document.getElementById('sideA');
+ const sideB = document.getElementById('sideB');
+ const fillA = document.getElementById('fillA');
+ const fillB = document.getElementById('fillB');
+ const pctA = document.getElementById('pctA');
+ const pctB = document.getElementById('pctB');
+ const voteBar = document.getElementById('voteBar');
+ const stamp = document.getElementById('stamp');
+ const stampName = document.getElementById('stampName');
+ const nameAEl = document.getElementById('nameA');
+ const nameBEl = document.getElementById('nameB');
+ const inputA = document.getElementById('inputNameA');
+ const inputB = document.getElementById('inputNameB');
+ const colorA = document.getElementById('colorA');
+ const colorB = document.getElementById('colorB');
+ const overlay = document.getElementById('resultOverlay');
+ const resultName = document.getElementById('resultName');
+ const resultCopy = document.getElementById('resultCopy');
+ const btnReset = document.getElementById('btnReset');
+ const btnClose = document.getElementById('btnCloseResult');
+ const btnRevote = document.getElementById('btnRevote');
+ const swatchA = document.getElementById('swatchA');
+ const swatchB = document.getElementById('swatchB');
+
+ let voted = false;
+
+ const copyFor = (side, name) => {
+ if (side === 'a') {
+ return `Your vote is in. The room leans ${name.toLowerCase()} — ornate, layered, timeless.`;
+ }
+ return `Your vote is in. The room leans ${name.toLowerCase()} — crisp lines, cool structure, modern calm.`;
+ };
+
+ function castVote(side, evt) {
+ if (voted) return;
+ voted = true;
+
+ // ripple
+ const btn = side === 'a' ? sideA : sideB;
+ const rect = btn.getBoundingClientRect();
+ const r = document.createElement('span');
+ r.className = 'ripple';
+ r.style.left = (evt.clientX - rect.left) + 'px';
+ r.style.top = (evt.clientY - rect.top) + 'px';
+ btn.appendChild(r);
+ setTimeout(() => r.remove(), 750);
+
+ // weighted random favoring the tapped side
+ const win = 58 + Math.floor(Math.random() * 22); // 58–79
+ const lose = 100 - win;
+ const aPct = side === 'a' ? win : lose;
+ const bPct = 100 - aPct;
+
+ voteBar.classList.add('is-visible');
+
+ requestAnimationFrame(() => {
+ fillA.style.width = aPct + '%';
+ fillB.style.width = bPct + '%';
+ pctA.textContent = aPct + '%';
+ pctB.textContent = bPct + '%';
+ });
+
+ const winner = aPct >= bPct ? 'a' : 'b';
+ const winName = winner === 'a' ? nameAEl.textContent.trim() : nameBEl.textContent.trim();
+
+ sideA.classList.toggle('is-winner', winner === 'a');
+ sideB.classList.toggle('is-winner', winner === 'b');
+ sideA.classList.toggle('is-dimmed', winner !== 'a');
+ sideB.classList.toggle('is-dimmed', winner !== 'b');
+
+ stampName.textContent = winName;
+ stamp.classList.add('is-show');
+
+ // position stamp over winning side
+ stamp.style.left = winner === 'a' ? '25%' : '75%';
+
+ resultName.textContent = winName;
+ resultCopy.textContent = copyFor(winner, winName);
+
+ setTimeout(() => overlay.classList.add('is-on'), 1400);
+ }
+
+ function resetPoll() {
+ voted = false;
+ voteBar.classList.remove('is-visible');
+ fillA.style.width = '50%';
+ fillB.style.width = '50%';
+ pctA.textContent = '50%';
+ pctB.textContent = '50%';
+ sideA.classList.remove('is-winner', 'is-dimmed');
+ sideB.classList.remove('is-winner', 'is-dimmed');
+ stamp.classList.remove('is-show');
+ stamp.style.left = '50%';
+ overlay.classList.remove('is-on');
+ }
+
+ sideA.addEventListener('click', (e) => castVote('a', e));
+ sideB.addEventListener('click', (e) => castVote('b', e));
+
+ btnReset.addEventListener('click', resetPoll);
+ btnClose.addEventListener('click', () => overlay.classList.remove('is-on'));
+ btnRevote.addEventListener('click', resetPoll);
+
+ inputA.addEventListener('input', () => {
+ const v = inputA.value.trim() || 'Damask';
+ nameAEl.textContent = v;
+ sideA.setAttribute('aria-label', 'Vote ' + v);
+ });
+ inputB.addEventListener('input', () => {
+ const v = inputB.value.trim() || 'Geometric';
+ nameBEl.textContent = v;
+ sideB.setAttribute('aria-label', 'Vote ' + v);
+ });
+
+ function hexToRgb(hex) {
+ const h = hex.replace('#', '');
+ const n = parseInt(h.length === 3 ? h.split('').map(c => c + c).join('') : h, 16);
+ return { r: (n >> 16) & 255, g: (n >> 8) & 255, b: n & 255 };
+ }
+
+ function applySwatchColor(which, hex) {
+ const { r, g, b } = hexToRgb(hex);
+ if (which === 'a') {
+ swatchA.style.background = `
+ radial-gradient(ellipse 70% 50% at 50% 40%, rgba(${r},${g},${b},0.28), transparent 70%),
+ linear-gradient(165deg, rgb(${Math.max(0,r-120)},${Math.max(0,g-130)},${Math.max(0,b-140)}) 0%, #1a1510 45%, #0f0d0b 100%)`;
+ const strokes = swatchA.querySelectorAll('svg [stroke]');
+ strokes.forEach(el => {
+ if (el.getAttribute('stroke') && el.getAttribute('stroke') !== 'none') {
+ el.setAttribute('stroke', hex);
+ }
+ });
+ const fills = swatchA.querySelectorAll('svg [fill="#c9a961"], svg circle[fill], svg path[fill="#c9a961"]');
+ swatchA.querySelectorAll('svg path, svg circle, svg ellipse').forEach(el => {
+ const f = el.getAttribute('fill');
+ if (f && f !== 'none' && f.startsWith('#')) el.setAttribute('fill', hex);
+ });
+ fillA.style.background = `linear-gradient(90deg, rgb(${Math.max(0,r-80)},${Math.max(0,g-90)},${Math.max(0,b-100)}), ${hex})`;
+ } else {
+ swatchB.style.background = `
+ linear-gradient(145deg, rgb(${Math.max(0,r-90)},${Math.max(0,g-80)},${Math.max(0,b-70)}) 0%, #0c1016 50%, rgb(${Math.max(0,r-70)},${Math.max(0,g-60)},${Math.max(0,b-50)}) 100%)`;
+ swatchB.querySelectorAll('svg path, svg line, svg polygon').forEach(el => {
+ const s = el.getAttribute('stroke');
+ if (s && s !== 'none') el.setAttribute('stroke', hex);
+ const f = el.getAttribute('fill');
+ if (f && f !== 'none' && f.startsWith('#')) el.setAttribute('fill', hex);
+ });
+ fillB.style.background = `linear-gradient(90deg, #1a2430, ${hex})`;
+ }
+ }
+
+ colorA.addEventListener('input', () => applySwatchColor('a', colorA.value));
+ colorB.addEventListener('input', () => applySwatchColor('b', colorB.value));
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/104018fe5adc/grok.png b/data/artifacts/104018fe5adc/grok.png
new file mode 100644
index 0000000..e68da94
Binary files /dev/null and b/data/artifacts/104018fe5adc/grok.png differ
diff --git a/data/artifacts/104018fe5adc/hermes3-8b.html b/data/artifacts/104018fe5adc/hermes3-8b.html
new file mode 100644
index 0000000..fe401a6
--- /dev/null
+++ b/data/artifacts/104018fe5adc/hermes3-8b.html
@@ -0,0 +1,122 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Wallcovering Brand Poll</title>
+<style>
+ :root {
+ --bg: #f7efe6;
+ --surface: #fff9f1;
+ --text: #3a2c22;
+ --muted: #a08c7a;
+ --accent: #e0a458;
+ --line: 1px solid #c65f38;
+ --shadow: 0 8px 24px rgba(0,0,0,.12);
+ --ease: cubic-bezier(.22,1,.36,1);
+ }
+ body {
+ background-color: var(--bg);
+ color: var(--text);
+ font-family: -apple-system, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
+ margin: 0;
+ padding: 48px;
+ }
+ header {
+ display: flex;
+ align-items: center;
+ letter-spacing: .18em;
+ color: var(--text);
+ font-family: 'Didot', 'Bodoni MT', 'Playfair Display', Georgia, serif;
+ font-size: 50px;
+ padding-left: 24px;
+ }
+ h1 {
+ margin: 0;
+ }
+ .container {
+ max-width: 640px;
+ margin: 0 auto;
+ }
+ .story {
+ display: flex;
+ gap: 48px;
+ align-items: center;
+ margin-top: 96px;
+ }
+ .swatch {
+ width: 260px;
+ height: 460px;
+ background-size: cover;
+ border-radius: 14px;
+ box-shadow: var(--shadow);
+ transition: transform 0.7s var(--ease), box-shadow 0.3s var(--ease);
+ }
+ .swatch-1 { background-image: linear-gradient(45deg, #f5d77e 0%, #c65f38 100%); }
+ .swatch-2 { background-image: linear-gradient(45deg, #fff9f1 0%, #e0a458 100%); }
+ .vote-bar {
+ height: 24px;
+ background-color: var(--accent);
+ width: 0;
+ transition: width 0.5s ease-in-out;
+ }
+ .result {
+ font-size: 96px;
+ color: var(--text);
+ opacity: 0;
+ animation: fadeUp 1s forwards;
+ }
+ @keyframes fadeUp {
+ from {
+ opacity: 0;
+ transform: translateY(28px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+ }
+</style>
+</head>
+<body>
+<header class="header">
+ <div class="container">
+ <h1>DESIGNER WALLCOVERINGS</h1>
+ </div>
+</header>
+<div class="container">
+ <div class="story">
+ <div
+ class="swatch swatch-1"
+ onclick="vote(0)"
+ ></div>
+ <div class="vote-bar vote-bar-1"></div>
+ <p class="result result-1">DAMASK</p>
+ <div
+ class="swatch swatch-2"
+ onclick="vote(1)"
+ ></div>
+ <div class="vote-bar vote-bar-2"></div>
+ <p class="result result-2">GEOmetric</p>
+ </div>
+</div>
+<script>
+ let voteCount = [0, 0];
+ const swatchHeight = 460;
+ const totalHeight = 1280;
+
+ function vote(choice) {
+ voteCount[choice]++;
+
+ const barHeight = (voteCount[choice] / totalHeight) * swatchHeight;
+ document.querySelector(`.vote-bar-${choice + 1}`).style.height = `${barHeight}px`;
+
+ if (voteCount[0] > voteCount[1]) {
+ document.querySelectorAll('.result').forEach((el, i) =>
+ el.style.opacity = i == choice ? 1 : 0
+ );
+ }
+ }
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/104018fe5adc/hermes3-8b.png b/data/artifacts/104018fe5adc/hermes3-8b.png
new file mode 100644
index 0000000..f99ee23
Binary files /dev/null and b/data/artifacts/104018fe5adc/hermes3-8b.png differ
diff --git a/data/artifacts/104018fe5adc/hf-qwen-coder-32b.html b/data/artifacts/104018fe5adc/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..62551cb
--- /dev/null
+++ b/data/artifacts/104018fe5adc/hf-qwen-coder-32b.html
@@ -0,0 +1,175 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>This or That Poll</title>
+ <style>
+ :root {
+ --bg: #faf8f4;
+ --surface: #ffffff;
+ --text: #1c1a17;
+ --muted: #9b948a;
+ --accent: #b0472e;
+ --line: #dcdcdc;
+ --shadow: 0 8px 24px rgba(0, 0, 0, .12);
+ --ease: cubic-bezier(.22, 1, .36, 1);
+ }
+ * {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+ }
+ body {
+ font-family: -apple-system, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
+ background-color: var(--bg);
+ color: var(--text);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ overflow: hidden;
+ }
+ .phone-frame {
+ width: 400px;
+ height: 712px;
+ background-color: var(--surface);
+ border-radius: 36px;
+ box-shadow: 0 15px 30px rgba(0, 0, 0, .1), 0 10px 10px rgba(0, 0, 0, .08);
+ position: relative;
+ }
+ header {
+ width: 100%;
+ padding: 24px;
+ background-color: var(--bg);
+ border-bottom: 1px solid var(--line);
+ display: flex;
+ align-items: center;
+ font-family: 'Playfair Display', Georgia, serif;
+ letter-spacing: .18em;
+ }
+ header h1 {
+ margin: 0;
+ font-size: 40px;
+ color: var(--text);
+ }
+ .content {
+ height: calc(100% - 96px);
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 32px;
+ }
+ .swatch {
+ width: 150px;
+ height: 150px;
+ border-radius: 8px;
+ position: relative;
+ cursor: pointer;
+ transition: transform .15s var(--ease);
+ }
+ .swatch:hover {
+ transform: scale(1.05);
+ }
+ .damask {
+ background-image: repeating-linear-gradient(
+ 45deg,
+ #e6ddc7, #e6ddc7 2px,
+ transparent 2px, transparent 4px
+ );
+ }
+ .geometric {
+ background-color: #f0f8ff;
+ background-image:
+ radial-gradient(circle at 30% 30%, #c9a961, transparent),
+ radial-gradient(circle at 70% 70%, #b0472e, transparent);
+ }
+ .vote-bar {
+ position: absolute;
+ bottom: -8px;
+ left: 50%;
+ transform: translateX(-50%);
+ width: calc(100% + 16px);
+ height: 8px;
+ background-color: var(--line);
+ border-radius: 4px;
+ overflow: hidden;
+ }
+ .vote-bar-fill {
+ width: 0%;
+ height: 100%;
+ background-color: var(--accent);
+ transition: width .3s var(--ease);
+ }
+ .winner-stamp {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ opacity: 0;
+ font-size: 24px;
+ color: var(--accent);
+ transition: opacity .3s var(--ease);
+ }
+ .winner-stamp.show {
+ opacity: 1;
+ }
+ </style>
+</head>
+<body>
+ <div class="phone-frame">
+ <header>
+ <h1>DESIGNER WALLCOVERINGS</h1>
+ </header>
+ <div class="content">
+ <div class="swatch damask" onclick="vote('damask')">
+ <div class="vote-bar"><div class="vote-bar-fill" id="damask-fill"></div></div>
+ <div class="winner-stamp" id="damask-stamp">WINNER</div>
+ </div>
+ <div class="swatch geometric" onclick="vote('geometric')">
+ <div class="vote-bar"><div class="vote-bar-fill" id="geometric-fill"></div></div>
+ <div class="winner-stamp" id="geometric-stamp">WINNER</div>
+ </div>
+ </div>
+ </div>
+ <script>
+ let damaskVotes = 0;
+ let geometricVotes = 0;
+
+ function vote(style) {
+ if (style === 'damask') {
+ damaskVotes++;
+ updateFill('damask-fill', damaskVotes);
+ } else {
+ geometricVotes++;
+ updateFill('geometric-fill', geometricVotes);
+ }
+ checkWinner();
+ }
+
+ function updateFill(id, votes) {
+ const totalVotes = damaskVotes + geometricVotes;
+ const percentage = (votes / totalVotes) * 100;
+ document.getElementById(id).style.width = `${percentage}%`;
+ }
+
+ function checkWinner() {
+ if (damaskVotes > geometricVotes && damaskVotes + geometricVotes > 0) {
+ showStamp('damask-stamp');
+ hideStamp('geometric-stamp');
+ } else if (geometricVotes > damaskVotes && damaskVotes + geometricVotes > 0) {
+ showStamp('geometric-stamp');
+ hideStamp('damask-stamp');
+ }
+ }
+
+ function showStamp(id) {
+ document.getElementById(id).classList.add('show');
+ }
+
+ function hideStamp(id) {
+ document.getElementById(id).classList.remove('show');
+ }
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/104018fe5adc/hf-qwen-coder-32b.png b/data/artifacts/104018fe5adc/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..21d81bf
Binary files /dev/null and b/data/artifacts/104018fe5adc/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/104018fe5adc/kimi.html b/data/artifacts/104018fe5adc/kimi.html
new file mode 100644
index 0000000..1ccd53d
--- /dev/null
+++ b/data/artifacts/104018fe5adc/kimi.html
@@ -0,0 +1,528 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Designer Wallcoverings — This or That</title>
+ <style>
+ :root {
+ /* Luxury Palette — Warm Ivory Ground */
+ --bg: #f5f2ea;
+ --surface: #ffffff;
+ --ink: #1c1a17;
+ --text: #1c1a17;
+ --muted: #8a8578;
+ --accent: #c9a961;
+ --accent-alpha: rgba(201, 169, 97, 0.35);
+
+ /* Story Dark Surface (Inside Phone) */
+ --story-bg: #0d0d0f;
+ --story-surface: #17171c;
+ --story-text: #f5f2ea;
+
+ /* Lines & Shadows */
+ --line: rgba(28, 26, 23, 0.12);
+ --hairline: 1px solid var(--line);
+ --shadow-sm: 0 1px 2px rgba(0,0,0,0.06);
+ --shadow-md: 0 8px 24px rgba(0,0,0,0.12);
+ --shadow-lg: 0 32px 80px rgba(0,0,0,0.28);
+ --shadow-glow: 0 0 24px 2px rgba(201, 169, 97, 0.35);
+
+ /* Typography */
+ --font-display: "Didot", "Bodoni MT", "Playfair Display", "Cormorant Garamond", Georgia, serif;
+ --font-sans: "Avenir Next", "Century Gothic", Futura, "Segoe UI", "Helvetica Neue", sans-serif;
+
+ /* Motion */
+ --ease-swift: cubic-bezier(.22,.9,.24,1);
+ --ease-hero: cubic-bezier(.22,1,.36,1);
+ --ease-power: cubic-bezier(.77,0,.18,1);
+ --ease-overshoot: cubic-bezier(.34,1.56,.64,1);
+ }
+
+ * { margin: 0; padding: 0; box-sizing: border-box; }
+
+ body {
+ background: var(--bg);
+ font-family: var(--font-sans);
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: flex-start;
+ padding-top: 140px;
+ padding-bottom: 60px;
+ }
+
+ /* Fixed Luxury Header */
+ header {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 1000;
+ background: var(--bg);
+ padding: 36px 48px;
+ border-bottom: var(--hairline);
+ }
+
+ .wordmark {
+ font-family: var(--font-display);
+ font-size: 46px;
+ font-weight: 400;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: var(--ink);
+ line-height: 1.05;
+ max-width: 400px;
+ }
+
+ /* Phone Frame — 9:16 Aspect */
+ .phone-frame {
+ width: 360px;
+ height: 640px;
+ background: var(--ink);
+ border-radius: 40px;
+ border: 14px solid var(--ink);
+ position: relative;
+ overflow: hidden;
+ box-shadow: var(--shadow-lg), 0 0 0 1px rgba(255,255,255,0.1) inset;
+ }
+
+ .notch {
+ position: absolute;
+ top: 0;
+ left: 50%;
+ transform: translateX(-50%);
+ width: 120px;
+ height: 28px;
+ background: var(--ink);
+ border-bottom-left-radius: 16px;
+ border-bottom-right-radius: 16px;
+ z-index: 50;
+ }
+
+ /* Story Canvas */
+ .story {
+ width: 100%;
+ height: 100%;
+ background: var(--story-bg);
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ }
+
+ /* Authentic Story Chrome */
+ .story-chrome {
+ height: 44px;
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 0 20px;
+ color: var(--story-text);
+ font-size: 14px;
+ font-weight: 600;
+ font-family: var(--font-sans);
+ position: relative;
+ z-index: 40;
+ }
+
+ .signal-dots {
+ display: flex;
+ gap: 4px;
+ align-items: center;
+ }
+
+ .dot {
+ width: 5px;
+ height: 5px;
+ background: var(--story-text);
+ border-radius: 50%;
+ }
+
+ .battery {
+ width: 22px;
+ height: 11px;
+ border: 1px solid rgba(245,242,234,0.4);
+ border-radius: 3px;
+ position: relative;
+ }
+
+ .battery::after {
+ content: '';
+ position: absolute;
+ right: -3px;
+ top: 3px;
+ width: 2px;
+ height: 5px;
+ background: rgba(245,242,234,0.4);
+ border-radius: 0 1px 1px 0;
+ }
+
+ /* Poll Layout */
+ .poll-stage {
+ flex: 1;
+ display: flex;
+ position: relative;
+ }
+
+ .option {
+ flex: 1;
+ position: relative;
+ cursor: pointer;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+ transition: filter 0.4s var(--ease-swift);
+ }
+
+ .option:active {
+ filter: brightness(0.9);
+ }
+
+ .option-left {
+ border-right: 1px solid rgba(245,242,234,0.08);
+ }
+
+ /* Swatch Containers */
+ .swatch-wrapper {
+ width: 140px;
+ height: 200px;
+ position: relative;
+ border: 1px solid rgba(245,242,234,0.15);
+ background: rgba(255,255,255,0.02);
+ box-shadow: inset 0 1px 0 rgba(255,255,255,0.05);
+ }
+
+ .pattern-fill {
+ position: absolute;
+ inset: 0;
+ }
+
+ /* Editable Style Names — Refined Sans */
+ .style-label {
+ margin-top: 32px;
+ color: var(--story-text);
+ font-family: var(--font-sans);
+ font-size: 13px;
+ font-weight: 500;
+ letter-spacing: 0.15em;
+ text-transform: uppercase;
+ border: none;
+ background: transparent;
+ text-align: center;
+ width: 120px;
+ padding: 6px;
+ border-bottom: 1px solid transparent;
+ transition: border-color 0.3s;
+ outline: none;
+ }
+
+ .style-label:focus {
+ border-bottom-color: var(--accent);
+ }
+
+ /* Vote Bars */
+ .vote-meter {
+ position: absolute;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ height: 0;
+ background: var(--accent);
+ transition: height 1s var(--ease-hero);
+ opacity: 0.95;
+ }
+
+ .vote-meter::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ height: 1px;
+ background: rgba(255,255,255,0.4);
+ box-shadow: 0 0 12px rgba(201, 169, 97, 0.6);
+ }
+
+ .percentage {
+ position: absolute;
+ bottom: 20px;
+ left: 50%;
+ transform: translateX(-50%);
+ color: var(--ink);
+ font-family: var(--font-sans);
+ font-size: 12px;
+ font-weight: 700;
+ letter-spacing: 0.12em;
+ opacity: 0;
+ transition: opacity 0.5s 0.4s;
+ z-index: 10;
+ }
+
+ .option.voted .percentage {
+ opacity: 1;
+ }
+
+ /* Winner Stamp */
+ .winner-seal {
+ position: absolute;
+ top: 35%;
+ left: 50%;
+ transform: translate(-50%, -50%) scale(0) rotate(-12deg);
+ width: 90px;
+ height: 90px;
+ border: 1.5px solid var(--accent);
+ border-radius: 50%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ color: var(--accent);
+ font-family: var(--font-sans);
+ font-size: 10px;
+ font-weight: 600;
+ letter-spacing: 0.2em;
+ text-transform: uppercase;
+ opacity: 0;
+ transition: all 0.6s var(--ease-overshoot);
+ pointer-events: none;
+ background: rgba(13,13,15,0.85);
+ backdrop-filter: blur(8px);
+ box-shadow: var(--shadow-glow), 0 4px 20px rgba(0,0,0,0.4);
+ }
+
+ .winner-seal::before {
+ content: '★';
+ font-size: 16px;
+ margin-bottom: 4px;
+ color: var(--accent);
+ }
+
+ .option.winner .winner-seal {
+ transform: translate(-50%, -50%) scale(1) rotate(-12deg);
+ opacity: 1;
+ }
+
+ /* VS Divider */
+ .vs-divider {
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ width: 40px;
+ height: 40px;
+ background: var(--story-bg);
+ border: 1px solid rgba(245,242,234,0.2);
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--muted);
+ font-family: var(--font-sans);
+ font-size: 10px;
+ font-weight: 600;
+ letter-spacing: 0.15em;
+ z-index: 20;
+ box-shadow: 0 4px 12px rgba(0,0,0,0.5);
+ }
+
+ /* Instruction overlay */
+ .tap-hint {
+ position: absolute;
+ bottom: 100px;
+ left: 50%;
+ transform: translateX(-50%);
+ color: var(--muted);
+ font-family: var(--font-sans);
+ font-size: 11px;
+ letter-spacing: 0.12em;
+ text-transform: uppercase;
+ opacity: 0.7;
+ pointer-events: none;
+ transition: opacity 0.4s;
+ }
+
+ .voted .tap-hint {
+ opacity: 0;
+ }
+
+ /* Animations */
+ @keyframes fadeUp {
+ from { opacity: 0; transform: translateY(20px); }
+ to { opacity: 1; transform: translateY(0); }
+ }
+
+ @keyframes scaleIn {
+ from { opacity: 0; transform: scale(0.9); }
+ to { opacity: 1; transform: scale(1); }
+ }
+
+ .animate-enter {
+ animation: fadeUp 0.8s var(--ease-hero) both;
+ }
+
+ .animate-enter-delay-1 { animation-delay: 0.1s; }
+ .animate-enter-delay-2 { animation-delay: 0.2s; }
+ </style>
+</head>
+<body>
+ <header>
+ <div class="wordmark">Designer Wallcoverings</div>
+ </header>
+
+ <div class="phone-frame">
+ <div class="notch"></div>
+ <div class="story" id="story">
+ <div class="story-chrome">
+ <span>9:41</span>
+ <div class="signal-dots">
+ <div class="dot"></div>
+ <div class="dot"></div>
+ <div class="dot"></div>
+ <div class="dot" style="opacity:0.4"></div>
+ </div>
+ <div class="battery"></div>
+ </div>
+
+ <div class="poll-stage" id="pollStage">
+ <div class="option option-left animate-enter" onclick="vote('left')">
+ <div class="swatch-wrapper">
+ <svg class="pattern-fill" viewBox="0 0 140 200" xmlns="http://www.w3.org/2000/svg">
+ <defs>
+ <pattern id="damask" x="0" y="0" width="70" height="100" patternUnits="userSpaceOnUse">
+ <!-- Ornamental Damask Motif -->
+ <path d="M35 10 C25 10, 15 20, 15 35 C15 45, 20 50, 25 55 C20 60, 10 65, 10 80 C10 95, 25 100, 35 100 C45 100, 60 95, 60 80 C60 65, 50 60, 45 55 C50 50, 55 45, 55 35 C55 20, 45 10, 35 10 Z"
+ fill="none" stroke="#c9a961" stroke-width="0.8" opacity="0.6"/>
+ <path d="M35 25 C30 25, 25 30, 25 38 C25 45, 30 48, 35 52 C40 48, 45 45, 45 38 C45 30, 40 25, 35 25 Z"
+ fill="none" stroke="#c9a961" stroke-width="0.5" opacity="0.4"/>
+ <path d="M35 65 C28 65, 22 72, 22 82 C22 88, 28 92, 35 95 C42 92, 48 88, 48 82 C48 72, 42 65, 35 65 Z"
+ fill="none" stroke="#c9a961" stroke-width="0.5" opacity="0.4"/>
+ <!-- Scrollwork flourishes -->
+ <path d="M35 0 Q20 0, 10 15 M35 0 Q50 0, 60 15"
+ fill="none" stroke="#c9a961" stroke-width="0.6" opacity="0.5"/>
+ <circle cx="35" cy="35" r="2" fill="#c9a961" opacity="0.3"/>
+ </pattern>
+ </defs>
+ <rect width="140" height="200" fill="url(#damask)" opacity="0.9"/>
+ <!-- Central medallion overlay -->
+ <path d="M70 60 C50 60, 35 80, 35 100 C35 120, 50 140, 70 140 C90 140, 105 120, 105 100 C105 80, 90 60, 70 60 Z M70 75 C60 75, 50 85, 50 100 C50 115, 60 125, 70 125 C80 125, 90 115, 90 100 C90 85, 80 75, 70 75 Z"
+ fill="none" stroke="#c9a961" stroke-width="1.2" opacity="0.8"/>
+ <path d="M70 85 C65 85, 60 92, 60 100 C60 108, 65 115, 70 115 C75 115, 80 108, 80 100 C80 92, 75 85, 70 85"
+ fill="none" stroke="#c9a961" stroke-width="0.6" opacity="0.5"/>
+ </svg>
+ </div>
+ <input type="text" class="style-label" value="Damask" spellcheck="false">
+ <div class="vote-meter" id="bar-left"></div>
+ <div class="percentage" id="pct-left">0%</div>
+ <div class="winner-seal">Winner</div>
+ </div>
+
+ <div class="vs-divider">VS</div>
+
+ <div class="option option-right animate-enter animate-enter-delay-1" onclick="vote('right')">
+ <div class="swatch-wrapper">
+ <svg class="pattern-fill" viewBox="0 0 140 200" xmlns="http://www.w3.org/2000/svg">
+ <defs>
+ <pattern id="geometric" x="0" y="0" width="40" height="69.28" patternUnits="userSpaceOnUse" patternTransform="rotate(0)">
+ <!-- Hexagonal Geometric Lattice -->
+ <polygon points="20,0 40,11.55 40,34.64 20,46.19 0,34.64 0,11.55"
+ fill="none" stroke="#c9a961" stroke-width="0.7" opacity="0.5"/>
+ <polygon points="20,11.55 30,17.32 30,28.87 20,34.64 10,28.87 10,17.32"
+ fill="none" stroke="#c9a961" stroke-width="0.4" opacity="0.3"/>
+ <!-- Connecting lines -->
+ <line x1="20" y1="0" x2="20" y2="11.55" stroke="#c9a961" stroke-width="0.5" opacity="0.4"/>
+ <line x1="40" y1="11.55" x2="40" y2="34.64" stroke="#c9a961" stroke-width="0.5" opacity="0.4"/>
+ <line x1="0" y1="11.55" x2="0" y2="34.64" stroke="#c9a961" stroke-width="0.5" opacity="0.4"/>
+ <!-- Accent points -->
+ <circle cx="20" cy="23.09" r="1.5" fill="#c9a961" opacity="0.4"/>
+ </pattern>
+ </defs>
+ <rect width="140" height="200" fill="url(#geometric)" opacity="0.9"/>
+ <!-- Bold geometric overlay -->
+ <polygon points="70,50 100,75 100,125 70,150 40,125 40,75"
+ fill="none" stroke="#c9a961" stroke-width="1.5" opacity="0.9"/>
+ <line x1="70" y1="50" x2="70" y2="150" stroke="#c9a961" stroke-width="0.8" opacity="0.6"/>
+ <line x1="40" y1="75" x2="100" y2="125" stroke="#c9a961" stroke-width="0.8" opacity="0.6"/>
+ <line x1="100" y1="75" x2="40" y2="125" stroke="#c9a961" stroke-width="0.8" opacity="0.6"/>
+ <circle cx="70" cy="100" r="4" fill="none" stroke="#c9a961" stroke-width="1" opacity="0.8"/>
+ </svg>
+ </div>
+ <input type="text" class="style-label" value="Geometric" spellcheck="false">
+ <div class="vote-meter" id="bar-right"></div>
+ <div class="percentage" id="pct-right">0%</div>
+ <div class="winner-seal">Winner</div>
+ </div>
+ </div>
+
+ <div class="tap-hint" id="tapHint">Tap to vote</div>
+ </div>
+ </div>
+
+ <script>
+ let hasVoted = false;
+
+ function vote(side) {
+ if (hasVoted) return;
+ hasVoted = true;
+
+ const stage = document.getElementById('pollStage');
+ const hint = document.getElementById('tapHint');
+ stage.classList.add('voted');
+
+ // Generate realistic poll results
+ const leftPercent = side === 'left' ? 68 : 32;
+ const rightPercent = side === 'right' ? 72 : 28;
+
+ const barLeft = document.getElementById('bar-left');
+ const barRight = document.getElementById('bar-right');
+ const pctLeft = document.getElementById('pct-left');
+ const pctRight = document.getElementById('pct-right');
+ const optionLeft = document.querySelector('.option-left');
+ const optionRight = document.querySelector('.option-right');
+
+ // Animate bars
+ setTimeout(() => {
+ barLeft.style.height = (leftPercent * 1.8) + 'px';
+ barRight.style.height = (rightPercent * 1.8) + 'px';
+
+ // Count up animation
+ animateValue(pctLeft, 0, leftPercent, 1000);
+ animateValue(pctRight, 0, rightPercent, 1000);
+
+ // Mark winner
+ if (leftPercent > rightPercent) {
+ optionLeft.classList.add('winner');
+ optionRight.style.opacity = '0.6';
+ } else {
+ optionRight.classList.add('winner');
+ optionLeft.style.opacity = '0.6';
+ }
+ }, 100);
+ }
+
+ function animateValue(obj, start, end, duration) {
+ let startTimestamp = null;
+ const step = (timestamp) => {
+ if (!startTimestamp) startTimestamp = timestamp;
+ const progress = Math.min((timestamp - startTimestamp) / duration, 1);
+ obj.innerHTML = Math.floor(progress * (end - start) + start) + '%';
+ if (progress < 1) {
+ window.requestAnimationFrame(step);
+ }
+ };
+ window.requestAnimationFrame(step);
+ }
+
+ // Allow editing labels without triggering vote
+ document.querySelectorAll('.style-label').forEach(input => {
+ input.addEventListener('click', (e) => {
+ e.stopPropagation();
+ input.focus();
+ });
+ });
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/104018fe5adc/kimi.png b/data/artifacts/104018fe5adc/kimi.png
new file mode 100644
index 0000000..bc25de0
Binary files /dev/null and b/data/artifacts/104018fe5adc/kimi.png differ
diff --git a/data/artifacts/104018fe5adc/qwen25-7b.html b/data/artifacts/104018fe5adc/qwen25-7b.html
new file mode 100644
index 0000000..035723b
--- /dev/null
+++ b/data/artifacts/104018fe5adc/qwen25-7b.html
@@ -0,0 +1,128 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>DESIGNER WALLCOVERINGS - This or That</title>
+<style>
+:root {
+ --bg: #17171c;
+ --surface: #f5f2ea;
+ --text: #3a2c22;
+ --accent: #b0472e;
+ --line: 1px solid var(--surface);
+ --shadow: 0 8px 24px rgba(0,0,0,.12), 0 16px 48px rgba(0,0,0,.18);
+ --ease: cubic-bezier(.22,1,.36,1);
+}
+
+body {
+ background-color: var(--bg);
+ color: var(--text);
+ font-family: 'Bodoni MT', Didot, Georgia, serif;
+ margin: 0;
+ padding: 96px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+header {
+ text-align: left;
+ margin-bottom: 48px;
+}
+
+.header-wordmark {
+ font-size: 50px;
+ letter-spacing: .18em;
+ color: var(--text);
+ white-space: pre-wrap;
+ word-break: keep-all;
+}
+
+.poll-container {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ width: 90%;
+ max-width: 640px;
+ background-color: var(--surface);
+ border-radius: 14px;
+ padding: 24px;
+ box-shadow: var(--shadow);
+}
+
+.poll-container > div {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.swatch {
+ width: 100%;
+ height: 64px;
+ border-radius: 12px;
+ background-color: var(--surface);
+ box-shadow: var(--shadow);
+ transition: transform .3s ease-in-out, opacity .5s ease-in-out;
+}
+
+.poll-bar {
+ width: 0;
+ height: 64px;
+ background-color: var(--accent);
+ transition: width .25s ease-in-out;
+}
+
+.voting-container {
+ display: none;
+ text-align: center;
+ margin-top: 32px;
+}
+
+.voting-container .winner-stamp {
+ font-size: 18px;
+ letter-spacing: .1em;
+ color: var(--accent);
+ opacity: 0;
+ transition: opacity .5s ease-in-out;
+}
+</style>
+</head>
+<body>
+
+<header>
+ <span class="header-wordmark">DESIGNER WALLCOVERINGS</span>
+</header>
+
+<div class="poll-container" id="thisOrThat">
+ <div class="swatch" style="background-image: linear-gradient(to right, var(--accent) 20%, transparent 80%);"></div>
+ <div class="swatch" style="background-image: linear-gradient(to right, var(--accent) 20%, transparent 80%);"></div>
+</div>
+
+<div id="voting-container" class="voting-container">
+ <span class="winner-stamp">WINNER:</span>
+</div>
+
+<script>
+const pollContainer = document.getElementById('thisOrThat');
+const votingContainer = document.getElementById('voting-container');
+
+pollContainer.addEventListener('click', (event) => {
+ if (event.target.classList.contains('swatch')) {
+ const winner = event.target;
+ const loser = !winner.nextElementSibling ? winner.previousElementSibling : winner.nextElementSibling;
+ const pollBarWinner = document.createElement('div');
+ pollBarWinner.className = 'poll-bar';
+ winner.appendChild(pollBarWinner);
+ setTimeout(() => {
+ pollBarWinner.style.width = '100%';
+ votingContainer.querySelector('.winner-stamp').innerText = 'WINNER: ' + (winner.style.backgroundImage ? 'Damask' : 'Geometric');
+ votingContainer.style.display = 'block';
+ }, 500);
+ }
+});
+</script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/104018fe5adc/qwen25-7b.png b/data/artifacts/104018fe5adc/qwen25-7b.png
new file mode 100644
index 0000000..b0ef0b6
Binary files /dev/null and b/data/artifacts/104018fe5adc/qwen25-7b.png differ
diff --git a/data/artifacts/104018fe5adc/qwen3-14b.html b/data/artifacts/104018fe5adc/qwen3-14b.html
new file mode 100644
index 0000000..5c18754
--- /dev/null
+++ b/data/artifacts/104018fe5adc/qwen3-14b.html
@@ -0,0 +1,261 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Wallcovering Poll</title>
+ <style>
+ :root {
+ --bg: #faf8f4;
+ --surface: #ffffff;
+ --text: #1c1a17;
+ --muted: #9b948a;
+ --accent: #b0472e;
+ --line: #e0e0e0;
+ --shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
+ --ease: cubic-bezier(0.22, 1, 0.36, 1);
+ --font-display: 'Playfair Display', Georgia, serif;
+ --font-body: -apple-system, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
+ }
+
+ * {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+ font-family: var(--font-body);
+ }
+
+ html, body {
+ height: 100%;
+ width: 100%;
+ background: var(--bg);
+ color: var(--text);
+ display: flex;
+ flex-direction: column;
+ }
+
+ header {
+ font-family: var(--font-display);
+ font-size: 40px;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ padding: 16px 24px;
+ color: var(--text);
+ background: var(--surface);
+ box-shadow: 0 2px 6px rgba(0, 0, 0, 0.06);
+ display: flex;
+ align-items: center;
+ justify-content: flex-start;
+ }
+
+ main {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ padding: 0 24px;
+ }
+
+ .container {
+ width: 100%;
+ max-width: 390px;
+ height: 844px;
+ position: relative;
+ background: var(--surface);
+ border-radius: 14px;
+ box-shadow: var(--shadow);
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 32px;
+ }
+
+ .swatch {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 50%;
+ height: 100%;
+ border: 1px solid var(--line);
+ box-shadow: inset 0 0 0 1px var(--line);
+ transition: all 0.2s var(--ease);
+ }
+
+ .swatch.left {
+ left: 0;
+ background: linear-gradient(135deg, #f8f6f3 25%, #eae0d6 50%, #f8f6f3 75%);
+ background-size: 600% 600%;
+ animation: drift 18s linear infinite;
+ }
+
+ .swatch.right {
+ right: 0;
+ background: linear-gradient(135deg, #ffb3c6 25%, #ff8fa7 50%, #ffb3c6 75%);
+ background-size: 600% 600%;
+ animation: drift 18s linear infinite;
+ }
+
+ @keyframes drift {
+ 0% {background-position: 0% 50%;}
+ 100% {background-position: 100% 50%;}
+ }
+
+ .vote-bar {
+ position: absolute;
+ bottom: 0;
+ width: 100%;
+ height: 12px;
+ background: var(--muted);
+ border-radius: 6px;
+ overflow: hidden;
+ }
+
+ .vote-fill {
+ height: 100%;
+ width: 0%;
+ background: var(--accent);
+ transition: width 0.3s var(--ease);
+ }
+
+ .stamp {
+ position: absolute;
+ top: 16px;
+ font-size: 16px;
+ text-transform: uppercase;
+ letter-spacing: 0.15em;
+ color: var(--text);
+ font-weight: bold;
+ opacity: 0;
+ transition: opacity 0.3s var(--ease);
+ }
+
+ .stamp.winner {
+ opacity: 1;
+ }
+
+ .overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0, 0, 0, 0.6);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.3s var(--ease);
+ }
+
+ .overlay.visible {
+ opacity: 1;
+ pointer-events: auto;
+ }
+
+ .overlay-content {
+ text-align: center;
+ color: var(--text);
+ font-size: 28px;
+ letter-spacing: 0.15em;
+ text-transform: uppercase;
+ }
+
+ .label {
+ position: absolute;
+ top: 16px;
+ font-size: 12px;
+ text-transform: uppercase;
+ letter-spacing: 0.15em;
+ color: var(--muted);
+ font-weight: 500;
+ }
+
+ .label.left {
+ left: 20px;
+ }
+
+ .label.right {
+ right: 20px;
+ }
+
+ .footer {
+ position: absolute;
+ bottom: 16px;
+ width: 100%;
+ text-align: center;
+ font-size: 12px;
+ letter-spacing: 0.15em;
+ color: var(--muted);
+ }
+ </style>
+</head>
+<body>
+ <header>DESIGNER WALLCOVERINGS</header>
+ <main>
+ <div class="container">
+ <div class="swatch left" id="swatch-left"></div>
+ <div class="swatch right" id="swatch-right"></div>
+ <div class="vote-bar">
+ <div class="vote-fill" id="vote-fill" style="width: 0%"></div>
+ </div>
+ <div class="stamp" id="stamp-left">DAMASK</div>
+ <div class="stamp" id="stamp-right">GEOMETRIC</div>
+ <div class="label left">DAMASK</div>
+ <div class="label right">GEOMETRIC</div>
+ <div class="overlay" id="overlay">
+ <div class="overlay-content" id="overlay-content">GEOMETRIC WINS</div>
+ </div>
+ <div class="footer">TAP TO VOTE</div>
+ </div>
+ </main>
+ <script>
+ const swatchLeft = document.getElementById('swatch-left');
+ const swatchRight = document.getElementById('swatch-right');
+ const voteFill = document.getElementById('vote-fill');
+ const stampLeft = document.getElementById('stamp-left');
+ const stampRight = document.getElementById('stamp-right');
+ const overlay = document.getElementById('overlay');
+ const overlayContent = document.getElementById('overlay-content');
+
+ let votesLeft = 0;
+ let votesRight = 0;
+
+ swatchLeft.addEventListener('click', () => {
+ votesLeft++;
+ updateVoteBar();
+ });
+
+ swatchRight.addEventListener('click', () => {
+ votesRight++;
+ updateVoteBar();
+ });
+
+ function updateVoteBar() {
+ const total = votesLeft + votesRight;
+ const percent = total > 0 ? (votesRight / total) * 100 : 0;
+ voteFill.style.width = percent + '%';
+ if (total >= 10) {
+ if (votesLeft > votesRight) {
+ overlayContent.textContent = 'DAMASK WINS';
+ } else {
+ overlayContent.textContent = 'GEOMETRIC WINS';
+ }
+ overlay.classList.add('visible');
+ } else {
+ overlay.classList.remove('visible');
+ }
+ }
+
+ // Ensure stamp shows only on win
+ if (votesLeft > votesRight) {
+ stampLeft.classList.add('winner');
+ } else if (votesRight > votesLeft) {
+ stampRight.classList.add('winner');
+ }
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/104018fe5adc/qwen3-14b.png b/data/artifacts/104018fe5adc/qwen3-14b.png
new file mode 100644
index 0000000..279c4bb
Binary files /dev/null and b/data/artifacts/104018fe5adc/qwen3-14b.png differ
diff --git a/data/artifacts/83e28cd01c99/claude-code.html b/data/artifacts/83e28cd01c99/claude-code.html
new file mode 100644
index 0000000..2488de1
--- /dev/null
+++ b/data/artifacts/83e28cd01c99/claude-code.html
@@ -0,0 +1,672 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
+<title>Designer Wallcoverings — Roller Run</title>
+<style>
+ :root{
+ /* ——— DESIGN TOKENS ——— */
+ --bg:#0d0d0f;
+ --surface:#17171c;
+ --ink:#f5f2ea;
+ --text:#1c1a17; /* darkest ink — wordmark */
+ --muted:#8a8578;
+ --accent:#c9a961; /* champagne gold */
+ --accent-deep:#7d6a45;
+ --line:rgba(201,169,97,.28);
+ --hair:rgba(245,242,234,.10);
+ --shadow:0 8px 24px rgba(0,0,0,.45);
+ --shadow-lg:0 16px 48px rgba(0,0,0,.55);
+ --glow:0 0 24px rgba(201,169,97,.35);
+ --ease:cubic-bezier(.22,1,.36,1);
+ --serif:'Didot','Bodoni MT','Playfair Display','Cormorant Garamond','EB Garamond',Georgia,serif;
+ --sans:-apple-system,'Segoe UI','Helvetica Neue',Arial,sans-serif;
+ }
+ *{box-sizing:border-box;margin:0;padding:0}
+ html,body{height:100%}
+ body{
+ background:var(--bg);
+ color:var(--ink);
+ font-family:var(--sans);
+ overflow:hidden;
+ -webkit-font-smoothing:antialiased;
+ text-rendering:optimizeLegibility;
+ }
+
+ /* ——— ambient drift ground ——— */
+ .ambient{
+ position:fixed;inset:0;z-index:0;
+ background:
+ radial-gradient(1200px 600px at 20% -10%, rgba(201,169,97,.10), transparent 60%),
+ radial-gradient(900px 500px at 100% 120%, rgba(125,106,69,.14), transparent 60%),
+ linear-gradient(180deg,#0d0d0f 0%, #121116 100%);
+ background-size:140% 140%,140% 140%,100% 100%;
+ animation:drift 18s var(--ease) infinite alternate;
+ }
+ @keyframes drift{from{background-position:0% 0%,100% 100%,0 0}to{background-position:100% 40%,0% 60%,0 0}}
+
+ /* ——— TOP BAR / WORDMARK LOCKUP ——— */
+ header.topbar{
+ position:fixed;top:0;left:0;right:0;z-index:20;
+ display:flex;align-items:center;justify-content:space-between;
+ padding:20px 32px;
+ background:linear-gradient(180deg, rgba(13,13,15,.92), rgba(13,13,15,.55) 70%, transparent);
+ pointer-events:none;
+ }
+ .lockup{
+ background:linear-gradient(145deg,#efe7d3,#f5f2ea 55%,#d8cba6);
+ border:1px solid var(--line);
+ border-radius:12px;
+ padding:12px 20px;
+ box-shadow:var(--shadow), inset 0 1px 0 rgba(255,255,255,.5);
+ max-width:288px;
+ }
+ .wordmark{
+ font-family:var(--serif);
+ color:var(--text); /* DARKEST ink — never surface */
+ font-size:42px;
+ line-height:.98;
+ font-weight:500;
+ text-transform:uppercase;
+ letter-spacing:.18em;
+ white-space:normal;
+ word-spacing:.02em;
+ }
+ .wordmark span{display:block;}
+ .wordmark .lg{font-size:42px;letter-spacing:.18em}
+ .wordmark .sm{
+ font-family:var(--sans);
+ color:var(--accent-deep);
+ font-size:10px;
+ letter-spacing:.42em;
+ font-weight:600;
+ margin-top:6px;
+ }
+
+ /* scoreboard — demoted refined sans */
+ .scoreboard{
+ text-align:right;
+ display:flex;gap:28px;align-items:flex-start;
+ }
+ .stat .k{
+ font-family:var(--sans);
+ font-size:10px;letter-spacing:.22em;text-transform:uppercase;
+ color:var(--muted);margin-bottom:4px;
+ }
+ .stat .v{
+ font-family:var(--serif);
+ font-size:32px;line-height:1;color:var(--accent);
+ text-shadow:0 0 18px rgba(201,169,97,.25);
+ }
+
+ /* ——— STAGE / CANVAS ——— */
+ #stageWrap{position:fixed;inset:0;z-index:5;display:flex;align-items:center;justify-content:center}
+ canvas{
+ display:block;width:100%;height:100%;
+ }
+
+ /* ——— OVERLAYS ——— */
+ .overlay{
+ position:fixed;inset:0;z-index:30;
+ display:flex;align-items:center;justify-content:center;
+ background:radial-gradient(900px 700px at 50% 40%, rgba(13,13,15,.72), rgba(13,13,15,.94));
+ backdrop-filter:blur(3px);
+ opacity:0;pointer-events:none;
+ transition:opacity .5s var(--ease);
+ }
+ .overlay.show{opacity:1;pointer-events:auto}
+ .panel{
+ width:min(560px,90vw);
+ background:linear-gradient(180deg,var(--surface),#101014);
+ border:1px solid var(--line);
+ border-radius:14px;
+ box-shadow:var(--shadow-lg), var(--glow);
+ padding:56px 48px;
+ text-align:center;
+ animation:fadeUp .7s var(--ease) both;
+ }
+ @keyframes fadeUp{from{opacity:0;transform:translateY(28px)}to{opacity:1;transform:none}}
+ .eyebrow{
+ font-family:var(--sans);
+ font-size:11px;letter-spacing:.34em;text-transform:uppercase;
+ color:var(--muted);margin-bottom:18px;
+ }
+ .headline{
+ font-family:var(--serif);
+ font-size:54px;line-height:1.02;color:var(--ink);
+ font-weight:500;margin-bottom:8px;
+ }
+ .headline em{color:var(--accent);font-style:italic}
+ .subline{
+ font-family:var(--sans);
+ font-size:16px;color:var(--muted);line-height:1.5;
+ max-width:400px;margin:14px auto 6px;
+ }
+ .divider{height:1px;background:var(--hair);margin:28px auto;width:70%}
+
+ .final{
+ display:flex;justify-content:center;gap:44px;margin:6px 0 26px;
+ }
+ .final .k{font-size:10px;letter-spacing:.24em;text-transform:uppercase;color:var(--muted);margin-bottom:6px}
+ .final .v{font-family:var(--serif);font-size:44px;color:var(--accent);line-height:1}
+
+ .btn{
+ font-family:var(--sans);
+ font-size:12px;letter-spacing:.22em;text-transform:uppercase;font-weight:600;
+ color:var(--text);
+ background:linear-gradient(145deg,#e8dcbc,var(--accent) 60%,var(--accent-deep));
+ border:none;border-radius:10px;
+ padding:16px 42px;cursor:pointer;
+ box-shadow:var(--shadow);
+ transition:transform .18s var(--ease), box-shadow .25s var(--ease);
+ }
+ .btn:hover{transform:translateY(-2px);box-shadow:var(--shadow-lg),var(--glow)}
+ .btn:active{transform:translateY(0)}
+
+ .hint{
+ margin-top:22px;font-size:11px;letter-spacing:.2em;text-transform:uppercase;color:var(--muted);
+ }
+ kbd{
+ font-family:var(--sans);
+ background:rgba(245,242,234,.08);border:1px solid var(--hair);border-radius:5px;
+ padding:2px 9px;color:var(--ink);font-size:11px;letter-spacing:.1em;
+ }
+
+ /* style legend during play */
+ .legend{
+ position:fixed;left:32px;bottom:24px;z-index:15;
+ display:flex;gap:20px;align-items:center;
+ opacity:.85;pointer-events:none;
+ }
+ .legend .item{display:flex;align-items:center;gap:8px}
+ .legend .sw{width:22px;height:22px;border-radius:5px;border:1px solid rgba(255,255,255,.14);box-shadow:0 2px 8px rgba(0,0,0,.4)}
+ .legend .lbl{font-size:10px;letter-spacing:.16em;text-transform:uppercase;color:var(--muted)}
+
+ @media (max-width:640px){
+ header.topbar{padding:14px 16px}
+ .wordmark,.wordmark .lg{font-size:30px}
+ .lockup{padding:9px 14px;max-width:220px}
+ .stat .v{font-size:22px}
+ .scoreboard{gap:16px}
+ .headline{font-size:38px}
+ .legend{left:16px;gap:12px;bottom:14px}
+ }
+</style>
+</head>
+<body>
+ <div class="ambient"></div>
+
+ <header class="topbar">
+ <div class="lockup">
+ <div class="wordmark">
+ <span class="lg">Designer</span>
+ <span class="lg">Wallcoverings</span>
+ <span class="sm">Roller Run</span>
+ </div>
+ </div>
+ <div class="scoreboard">
+ <div class="stat"><div class="k">Score</div><div class="v" id="scoreV">0</div></div>
+ <div class="stat"><div class="k">Best</div><div class="v" id="bestV">0</div></div>
+ </div>
+ </header>
+
+ <div id="stageWrap">
+ <canvas id="game"></canvas>
+ </div>
+
+ <div class="legend" id="legend"></div>
+
+ <!-- START OVERLAY -->
+ <div class="overlay show" id="startOverlay">
+ <div class="panel">
+ <div class="eyebrow">An Endless Atelier</div>
+ <div class="headline">Roller <em>Run</em></div>
+ <div class="subline">Roll the wall. Collect the wallpaper — each hue a design style — and vault the obstacles as the atelier quickens.</div>
+ <div class="divider"></div>
+ <button class="btn" id="startBtn">Begin the Run</button>
+ <div class="hint"><kbd>Space</kbd> / <kbd>Tap</kbd> to jump</div>
+ </div>
+ </div>
+
+ <!-- GAME OVER OVERLAY -->
+ <div class="overlay" id="overOverlay">
+ <div class="panel">
+ <div class="eyebrow">The Run Ends</div>
+ <div class="headline" id="overTitle">Curtain <em>Call</em></div>
+ <div class="final">
+ <div><div class="k">Score</div><div class="v" id="finalScore">0</div></div>
+ <div><div class="k">Best</div><div class="v" id="finalBest">0</div></div>
+ </div>
+ <div class="subline" id="overNote">A refined effort.</div>
+ <div class="divider"></div>
+ <button class="btn" id="restartBtn">Run Again</button>
+ <div class="hint"><kbd>Space</kbd> / <kbd>Tap</kbd> to restart</div>
+ </div>
+ </div>
+
+<script>
+(function(){
+ const canvas = document.getElementById('game');
+ const ctx = canvas.getContext('2d');
+
+ // ——— design styles (each a distinct visual motif + jewel/warm hue) ———
+ const STYLES = [
+ {name:'Damask', hue:'#c9a961', dark:'#7d6a45', motif:'damask'},
+ {name:'Toile', hue:'#7fa8c9', dark:'#3f5f7d', motif:'toile'},
+ {name:'Botanical', hue:'#8fbf7f', dark:'#4a6f42', motif:'leaf'},
+ {name:'Art Deco', hue:'#c98fb0', dark:'#7d4a63', motif:'deco'},
+ {name:'Moiré', hue:'#b0a0d8', dark:'#5f4f85', motif:'moire'},
+ ];
+
+ // ——— responsive DPR-aware sizing ———
+ let W=0,H=0,DPR=1,GROUND=0;
+ function resize(){
+ DPR = Math.min(window.devicePixelRatio||1, 2);
+ W = window.innerWidth; H = window.innerHeight;
+ canvas.width = W*DPR; canvas.height = H*DPR;
+ canvas.style.width=W+'px'; canvas.style.height=H+'px';
+ ctx.setTransform(DPR,0,0,DPR,0,0);
+ GROUND = H*0.80;
+ }
+ window.addEventListener('resize', resize);
+ resize();
+
+ // ——— state ———
+ const state = {
+ mode:'start', // start | play | over
+ score:0, best:0,
+ speed:0, baseSpeed:5.2,
+ t:0,
+ player:null,
+ rolls:[], obstacles:[], puffs:[], sparkles:[],
+ spawnRoll:0, spawnObs:0,
+ wallScroll:0,
+ grabbed:0,
+ };
+ state.best = parseInt(localStorage.getItem('dw_rollerrun_best')||'0',10)||0;
+ document.getElementById('bestV').textContent = state.best;
+
+ function reset(){
+ state.score=0; state.speed=state.baseSpeed; state.t=0;
+ state.rolls=[]; state.obstacles=[]; state.puffs=[]; state.sparkles=[];
+ state.spawnRoll=40; state.spawnObs=90; state.wallScroll=0; state.grabbed=0;
+ state.player = {
+ x: W*0.20, y: GROUND, r: 30,
+ vy:0, onGround:true, rot:0, jumps:0,
+ trail:[]
+ };
+ }
+
+ // ——— input ———
+ function jump(){
+ if(state.mode==='start'){ startGame(); return; }
+ if(state.mode==='over'){ startGame(); return; }
+ const p=state.player;
+ if(p.onGround || p.jumps<2){
+ // double-jump allowance for a forgiving luxe feel
+ p.vy = p.onGround ? -12.4 : -10.6;
+ p.onGround=false;
+ p.jumps = p.onGround?1:p.jumps+1;
+ spawnPuff(p.x-14, p.y+p.r, 6);
+ }
+ }
+ window.addEventListener('keydown', e=>{
+ if(e.code==='Space'||e.code==='ArrowUp'){ e.preventDefault(); jump(); }
+ });
+ canvas.addEventListener('pointerdown', e=>{ e.preventDefault(); jump(); });
+ document.getElementById('stageWrap').addEventListener('pointerdown', ()=>{ if(state.mode==='play') jump(); });
+
+ // ——— UI wiring ———
+ const startOverlay=document.getElementById('startOverlay');
+ const overOverlay=document.getElementById('overOverlay');
+ document.getElementById('startBtn').addEventListener('click', startGame);
+ document.getElementById('restartBtn').addEventListener('click', startGame);
+
+ function startGame(){
+ reset();
+ state.mode='play';
+ startOverlay.classList.remove('show');
+ overOverlay.classList.remove('show');
+ }
+
+ function gameOver(){
+ state.mode='over';
+ const sc=Math.floor(state.score);
+ if(sc>state.best){ state.best=sc; localStorage.setItem('dw_rollerrun_best',String(sc)); }
+ document.getElementById('bestV').textContent=state.best;
+ document.getElementById('finalScore').textContent=sc;
+ document.getElementById('finalBest').textContent=state.best;
+ const note = sc>=state.best && sc>0 ? 'A new house record — exquisitely done.' :
+ sc>400 ? 'Handsomely rolled.' :
+ sc>150 ? 'A refined effort.' : 'The wall awaits your return.';
+ document.getElementById('overNote').textContent=note;
+ overOverlay.classList.add('show');
+ }
+
+ // ——— spawners ———
+ function spawnRoll(){
+ const style = STYLES[(Math.random()*STYLES.length)|0];
+ const airborne = Math.random()<0.42;
+ const y = airborne ? GROUND - (70+Math.random()*120) : GROUND - 34;
+ state.rolls.push({x:W+40,y,r:19,style,got:false,bob:Math.random()*6.28});
+ }
+ function spawnObstacle(){
+ const kinds=['stack','sconce','pot'];
+ const k=kinds[(Math.random()*kinds.length)|0];
+ let w,h;
+ if(k==='stack'){ w=26; h=42+Math.random()*34; }
+ else if(k==='sconce'){ w=34; h=30; }
+ else { w=40; h=48; }
+ state.obstacles.push({x:W+50,y:GROUND,w,h,k});
+ }
+ function spawnPuff(x,y,n){
+ for(let i=0;i<n;i++){
+ state.puffs.push({x,y,vx:(Math.random()-.5)*2-state.speed*0.15,vy:-Math.random()*1.6-.3,life:1,r:3+Math.random()*5});
+ }
+ }
+ function spawnSparkle(x,y,color){
+ for(let i=0;i<10;i++){
+ const a=Math.random()*6.28, sp=1.5+Math.random()*3.5;
+ state.sparkles.push({x,y,vx:Math.cos(a)*sp,vy:Math.sin(a)*sp-1,life:1,color,r:1.5+Math.random()*2.5});
+ }
+ }
+
+ // ——— update ———
+ function update(){
+ if(state.mode!=='play') return;
+ const p=state.player;
+ state.t++;
+ // rising speed, gentle
+ state.speed = state.baseSpeed + Math.min(state.t/900, 6.5);
+ state.score += state.speed*0.05;
+ state.wallScroll = (state.wallScroll + state.speed) % 200;
+
+ // physics
+ p.vy += 0.62;
+ p.y += p.vy;
+ p.rot += state.speed*0.06;
+ if(p.y>=GROUND){ p.y=GROUND; p.vy=0; p.onGround=true; p.jumps=0; }
+ // trail
+ p.trail.unshift({x:p.x,y:p.y});
+ if(p.trail.length>10) p.trail.pop();
+
+ // spawn cadence scales with speed
+ state.spawnRoll -= state.speed;
+ if(state.spawnRoll<=0){ spawnRoll(); state.spawnRoll = 260+Math.random()*220 - Math.min(state.t/12,120); }
+ state.spawnObs -= state.speed;
+ if(state.spawnObs<=0){ spawnObstacle(); state.spawnObs = 420+Math.random()*300 - Math.min(state.t/10,180); }
+
+ // move rolls
+ for(const r of state.rolls){
+ r.x -= state.speed;
+ r.bob += 0.08;
+ if(!r.got){
+ const dx=r.x-p.x, dy=(r.y+Math.sin(r.bob)*4)-p.y;
+ if(Math.hypot(dx,dy) < r.r+p.r-4){
+ r.got=true;
+ state.score += 25;
+ state.grabbed++;
+ spawnSparkle(r.x,r.y,r.style.hue);
+ }
+ }
+ }
+ state.rolls = state.rolls.filter(r=>r.x>-60 && !r.got);
+
+ // move obstacles + collide
+ for(const o of state.obstacles){
+ o.x -= state.speed;
+ // AABB vs circle-ish
+ const px=p.x, py=p.y, pr=p.r*0.72;
+ const ox=o.x-o.w/2, oy=o.y-o.h, ow=o.w, oh=o.h;
+ const cx=Math.max(ox,Math.min(px,ox+ow));
+ const cy=Math.max(oy,Math.min(py,oy+oh));
+ if(Math.hypot(px-cx,py-cy) < pr){ gameOver(); }
+ }
+ state.obstacles = state.obstacles.filter(o=>o.x>-70);
+
+ // particles
+ for(const f of state.puffs){ f.x+=f.vx; f.y+=f.vy; f.vy+=0.04; f.life-=0.025; }
+ state.puffs=state.puffs.filter(f=>f.life>0);
+ for(const s of state.sparkles){ s.x+=s.vx; s.y+=s.vy; s.vy+=0.12; s.life-=0.03; }
+ state.sparkles=state.sparkles.filter(s=>s.life>0);
+
+ document.getElementById('scoreV').textContent=Math.floor(state.score);
+ }
+
+ // ——— drawing helpers ———
+ function drawWall(){
+ // warm near-black wall with repeating hairline damask lattice + baseboard
+ ctx.fillStyle='#121116';
+ ctx.fillRect(0,0,W,GROUND+4);
+ // subtle vertical panels
+ ctx.strokeStyle='rgba(201,169,97,.06)';
+ ctx.lineWidth=1;
+ const off=-state.wallScroll;
+ for(let x=off; x<W+200; x+=200){
+ ctx.beginPath(); ctx.moveTo(x,0); ctx.lineTo(x,GROUND); ctx.stroke();
+ // faint diamond motif mid-panel
+ const cx=x+100, cy=GROUND*0.42;
+ ctx.save();
+ ctx.globalAlpha=0.10;
+ ctx.strokeStyle='#c9a961';
+ ctx.beginPath();
+ ctx.moveTo(cx,cy-30); ctx.lineTo(cx+18,cy); ctx.lineTo(cx,cy+30); ctx.lineTo(cx-18,cy); ctx.closePath();
+ ctx.stroke();
+ ctx.restore();
+ }
+ // baseboard / floor
+ const grad=ctx.createLinearGradient(0,GROUND,0,H);
+ grad.addColorStop(0,'#1c1a17'); grad.addColorStop(1,'#0b0b0d');
+ ctx.fillStyle=grad;
+ ctx.fillRect(0,GROUND,W,H-GROUND);
+ // gold hairline rail
+ ctx.strokeStyle='rgba(201,169,97,.55)';
+ ctx.lineWidth=1;
+ ctx.beginPath(); ctx.moveTo(0,GROUND+2); ctx.lineTo(W,GROUND+2); ctx.stroke();
+ // moving floor ticks
+ ctx.strokeStyle='rgba(245,242,234,.05)';
+ for(let x=off*1.4; x<W+80; x+=80){
+ ctx.beginPath(); ctx.moveTo(x,GROUND+10); ctx.lineTo(x-24,H); ctx.stroke();
+ }
+ }
+
+ // patterned wallpaper roll
+ function drawRoll(r){
+ const y = r.y + Math.sin(r.bob)*4;
+ ctx.save();
+ ctx.translate(r.x,y);
+ // glow
+ ctx.shadowColor=r.style.hue; ctx.shadowBlur=16;
+ // roll body
+ ctx.fillStyle=r.style.dark;
+ ctx.beginPath(); ctx.arc(0,0,r.r,0,6.2832); ctx.fill();
+ ctx.shadowBlur=0;
+ // face with motif
+ ctx.fillStyle=r.style.hue;
+ ctx.beginPath(); ctx.arc(0,0,r.r*0.78,0,6.2832); ctx.fill();
+ drawMotif(r.style.motif, r.r*0.78, r.style.dark);
+ // inner core
+ ctx.fillStyle=r.style.dark;
+ ctx.beginPath(); ctx.arc(0,0,r.r*0.22,0,6.2832); ctx.fill();
+ ctx.fillStyle='rgba(255,255,255,.35)';
+ ctx.beginPath(); ctx.arc(-r.r*0.28,-r.r*0.28,r.r*0.12,0,6.2832); ctx.fill();
+ ctx.restore();
+ }
+
+ function drawMotif(kind, R, col){
+ ctx.save();
+ ctx.strokeStyle=col; ctx.fillStyle=col; ctx.lineWidth=1.4;
+ ctx.beginPath(); ctx.arc(0,0,R,0,6.2832); ctx.clip();
+ if(kind==='damask'){
+ ctx.beginPath();
+ ctx.ellipse(0,-R*0.2,R*0.3,R*0.5,0,0,6.2832);
+ ctx.stroke();
+ ctx.beginPath(); ctx.arc(0,R*0.35,R*0.22,0,3.14159); ctx.stroke();
+ } else if(kind==='toile'){
+ for(let i=-2;i<=2;i++){
+ ctx.beginPath(); ctx.arc(i*R*0.5,0,R*0.28,0,6.2832); ctx.stroke();
+ }
+ } else if(kind==='leaf'){
+ for(let a=0;a<6.28;a+=1.05){
+ ctx.beginPath();
+ ctx.ellipse(Math.cos(a)*R*0.4,Math.sin(a)*R*0.4,R*0.28,R*0.11,a,0,6.2832);
+ ctx.stroke();
+ }
+ } else if(kind==='deco'){
+ for(let i=1;i<=3;i++){
+ ctx.beginPath();
+ ctx.moveTo(-R,-R+i*R*0.55); ctx.lineTo(0,-R*0.9+i*R*0.55); ctx.lineTo(R,-R+i*R*0.55);
+ ctx.stroke();
+ }
+ } else if(kind==='moire'){
+ for(let i=1;i<=4;i++){ ctx.beginPath(); ctx.arc(-R*0.3,0,R*0.28*i/2,0,6.2832); ctx.stroke(); }
+ }
+ ctx.restore();
+ }
+
+ function drawObstacle(o){
+ const x=o.x, y=o.y;
+ ctx.save();
+ ctx.shadowColor='rgba(0,0,0,.5)'; ctx.shadowBlur=10; ctx.shadowOffsetY=4;
+ if(o.k==='stack'){
+ // stacked paint cans / books in deep tone
+ ctx.fillStyle='#2a2620';
+ ctx.fillRect(x-o.w/2,y-o.h,o.w,o.h);
+ ctx.shadowBlur=0;
+ ctx.strokeStyle='rgba(201,169,97,.5)'; ctx.lineWidth=1;
+ const seg=o.h/3;
+ for(let i=1;i<3;i++){ ctx.beginPath(); ctx.moveTo(x-o.w/2,y-seg*i); ctx.lineTo(x+o.w/2,y-seg*i); ctx.stroke(); }
+ ctx.fillStyle='var'; ctx.fillStyle='#c9a961';
+ ctx.globalAlpha=.55; ctx.fillRect(x-o.w/2,y-o.h,o.w,3); ctx.globalAlpha=1;
+ } else if(o.k==='sconce'){
+ // low table / bench
+ ctx.fillStyle='#241f1a';
+ ctx.fillRect(x-o.w/2,y-o.h,o.w,o.h*0.4);
+ ctx.fillRect(x-o.w/2+3,y-o.h*0.6,4,o.h*0.6);
+ ctx.fillRect(x+o.w/2-7,y-o.h*0.6,4,o.h*0.6);
+ ctx.shadowBlur=0;
+ ctx.strokeStyle='rgba(201,169,97,.45)'; ctx.strokeRect(x-o.w/2,y-o.h,o.w,o.h*0.4);
+ } else {
+ // urn / pot
+ ctx.fillStyle='#2a2620';
+ ctx.beginPath();
+ ctx.moveTo(x-o.w/2,y-o.h*0.55);
+ ctx.quadraticCurveTo(x-o.w*0.7,y, x-o.w/3, y);
+ ctx.lineTo(x+o.w/3,y);
+ ctx.quadraticCurveTo(x+o.w*0.7,y, x+o.w/2, y-o.h*0.55);
+ ctx.quadraticCurveTo(x,y-o.h*0.85, x-o.w/2,y-o.h*0.55);
+ ctx.closePath(); ctx.fill();
+ ctx.shadowBlur=0;
+ // greenery / gold rim
+ ctx.strokeStyle='rgba(201,169,97,.5)'; ctx.lineWidth=1.4;
+ ctx.beginPath(); ctx.ellipse(x,y-o.h*0.55,o.w*0.42,o.h*0.12,0,0,6.2832); ctx.stroke();
+ ctx.strokeStyle='#8fbf7f'; ctx.globalAlpha=.7;
+ for(let i=-1;i<=1;i++){ ctx.beginPath(); ctx.moveTo(x,y-o.h*0.55); ctx.quadraticCurveTo(x+i*10,y-o.h*0.95,x+i*16,y-o.h*0.7); ctx.stroke(); }
+ ctx.globalAlpha=1;
+ }
+ ctx.restore();
+ }
+
+ function drawPlayer(){
+ const p=state.player;
+ // trail (motion blur ghosts)
+ for(let i=p.trail.length-1;i>=0;i--){
+ const tr=p.trail[i];
+ ctx.save();
+ ctx.globalAlpha=(1-i/p.trail.length)*0.14;
+ ctx.fillStyle='#c9a961';
+ ctx.beginPath(); ctx.arc(tr.x,tr.y,p.r*0.9,0,6.2832); ctx.fill();
+ ctx.restore();
+ }
+ ctx.save();
+ ctx.translate(p.x,p.y);
+ // handle arm (fixed angle up-back)
+ ctx.strokeStyle='#e8dcbc'; ctx.lineWidth=5; ctx.lineCap='round';
+ ctx.beginPath(); ctx.moveTo(6,-p.r-2); ctx.lineTo(40,-p.r-40); ctx.lineTo(56,-p.r-30); ctx.stroke();
+ // rotate for the roller cylinder
+ ctx.save();
+ ctx.rotate(p.rot);
+ ctx.shadowColor='rgba(201,169,97,.5)'; ctx.shadowBlur=14;
+ // frame end caps
+ ctx.fillStyle='#3a342a';
+ ctx.beginPath(); ctx.arc(0,0,p.r,0,6.2832); ctx.fill();
+ ctx.shadowBlur=0;
+ // roller sleeve — champagne with tick texture
+ ctx.fillStyle='#c9a961';
+ ctx.beginPath(); ctx.arc(0,0,p.r*0.82,0,6.2832); ctx.fill();
+ ctx.strokeStyle='#7d6a45'; ctx.lineWidth=2;
+ for(let a=0;a<6.28;a+=0.5){
+ ctx.beginPath();
+ ctx.moveTo(Math.cos(a)*p.r*0.35,Math.sin(a)*p.r*0.35);
+ ctx.lineTo(Math.cos(a)*p.r*0.8,Math.sin(a)*p.r*0.8);
+ ctx.stroke();
+ }
+ ctx.fillStyle='#7d6a45';
+ ctx.beginPath(); ctx.arc(0,0,p.r*0.2,0,6.2832); ctx.fill();
+ ctx.restore();
+ // fresh-paint stripe under the roller when grounded
+ ctx.restore();
+ if(p.onGround){
+ ctx.save();
+ ctx.globalAlpha=0.5;
+ ctx.fillStyle='#c9a961';
+ ctx.fillRect(0,GROUND+p.r-2, p.x, 3);
+ ctx.restore();
+ }
+ }
+
+ function drawParticles(){
+ for(const f of state.puffs){
+ ctx.save();
+ ctx.globalAlpha=f.life*0.4;
+ ctx.fillStyle='#f5f2ea';
+ ctx.beginPath(); ctx.arc(f.x,f.y,f.r*f.life,0,6.2832); ctx.fill();
+ ctx.restore();
+ }
+ for(const s of state.sparkles){
+ ctx.save();
+ ctx.globalAlpha=s.life;
+ ctx.fillStyle=s.color;
+ ctx.shadowColor=s.color; ctx.shadowBlur=8;
+ ctx.beginPath(); ctx.arc(s.x,s.y,s.r,0,6.2832); ctx.fill();
+ ctx.restore();
+ }
+ }
+
+ function render(){
+ ctx.clearRect(0,0,W,H);
+ drawWall();
+ for(const r of state.rolls) drawRoll(r);
+ for(const o of state.obstacles) drawObstacle(o);
+ drawParticles();
+ if(state.mode!=='start') drawPlayer();
+ }
+
+ // ——— legend ———
+ (function buildLegend(){
+ const el=document.getElementById('legend');
+ STYLES.forEach(s=>{
+ const item=document.createElement('div'); item.className='item';
+ const sw=document.createElement('div'); sw.className='sw';
+ sw.style.background=`linear-gradient(145deg,${s.hue},${s.dark})`;
+ const lbl=document.createElement('div'); lbl.className='lbl'; lbl.textContent=s.name;
+ item.appendChild(sw); item.appendChild(lbl); el.appendChild(item);
+ });
+ })();
+
+ // ——— main loop (rAF, dt-normalized for 60fps feel) ———
+ let last=performance.now(), acc=0;
+ const STEP=1000/60;
+ function frame(now){
+ acc += now-last; last=now;
+ if(acc>200) acc=200;
+ while(acc>=STEP){ update(); acc-=STEP; }
+ render();
+ requestAnimationFrame(frame);
+ }
+ requestAnimationFrame(frame);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/83e28cd01c99/claude-code.png b/data/artifacts/83e28cd01c99/claude-code.png
new file mode 100644
index 0000000..446d1b9
Binary files /dev/null and b/data/artifacts/83e28cd01c99/claude-code.png differ
diff --git a/data/artifacts/83e28cd01c99/gemma3-12b.html b/data/artifacts/83e28cd01c99/gemma3-12b.html
new file mode 100644
index 0000000..c272c71
--- /dev/null
+++ b/data/artifacts/83e28cd01c99/gemma3-12b.html
@@ -0,0 +1,306 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Designer Wallcoverings - Endless Runner</title>
+ <style>
+ :root {
+ --bg: #faf8f4;
+ --surface: #ffffff;
+ --text: #3a2c22;
+ --muted: #9b948a;
+ --accent: #b0472e;
+ --line: #dcdcdc;
+ --shadow: rgba(0, 0, 0, 0.12);
+ --ease: cubic-bezier(.22,1,.36,1);
+ }
+
+ body {
+ margin: 0;
+ overflow: hidden;
+ background-color: var(--bg);
+ font-family: 'apple-system', 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
+ color: var(--text);
+ display: flex;
+ flex-direction: column;
+ height: 100vh;
+ }
+
+ header {
+ background-color: var(--surface);
+ padding: 24px;
+ text-align: left;
+ font-family: 'Playfair Display', Georgia, serif;
+ font-size: 67px; /* Adjust as needed */
+ letter-spacing: .18em;
+ text-transform: uppercase;
+ position: sticky;
+ top: 0;
+ z-index: 100;
+ }
+
+ #gameCanvas {
+ background-color: var(--surface);
+ width: 100%;
+ height: calc(100vh - 64px); /* Adjust for header height */
+ display: block;
+ }
+
+ .overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(255, 255, 255, 0.8); /* Semi-transparent white */
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ z-index: 1000;
+ font-size: 24px;
+ }
+
+ .overlay button {
+ padding: 12px 24px;
+ background-color: var(--accent);
+ color: white;
+ border: none;
+ cursor: pointer;
+ margin-top: 20px;
+ font-size: 16px;
+ text-transform: uppercase;
+ letter-spacing: .15em;
+ }
+
+ .scoreboard {
+ position: fixed;
+ bottom: 32px;
+ left: 50%;
+ transform: translateX(-50%);
+ background-color: var(--surface);
+ padding: 16px;
+ border-radius: 8px;
+ box-shadow: 0 8px 24px var(--shadow);
+ font-size: 12px;
+ text-transform: uppercase;
+ letter-spacing: .15em;
+ color: var(--muted);
+ }
+
+ </style>
+</head>
+<body>
+
+ <header>
+ DESIGNER WALLCOVERINGS
+ </header>
+
+ <div class="scoreboard">
+ Score: <span id="score">0</span> | Best: <span id="bestScore">0</span>
+ </div>
+
+ <canvas id="gameCanvas"></canvas>
+
+ <div class="overlay" id="gameOverOverlay" style="display: none;">
+ <h1>GAME OVER</h1>
+ <p>Your Score: <span id="finalScore">0</span></p>
+ <button onclick="restartGame()">RESTART</button>
+ </div>
+
+
+ <script>
+ const canvas = document.getElementById('gameCanvas');
+ const ctx = canvas.getContext('2d');
+ let score = 0;
+ let bestScore = 0;
+ let rollerX = 50;
+ let rollerY = canvas.height / 2;
+ let gravity = 0.5;
+ let velocityY = 0;
+ let jumpHeight = -15;
+ let obstacles = [];
+ let wallpaperRolls = []; // Array to store wallpaper rolls
+ let gameRunning = false;
+
+ // Load best score from localStorage
+ if (localStorage.getItem('bestScore')) {
+ bestScore = parseInt(localStorage.getItem('bestScore'));
+ }
+
+ function updateBestScore() {
+ if (score > bestScore) {
+ bestScore = score;
+ localStorage.setItem('bestScore', bestScore);
+ }
+ }
+
+ // Wallpaper Roll Styles (Visually Distinct)
+ const wallpaperStyles = [
+ { color: '#f9a758', pattern: 'radial-gradient(circle, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0))' }, // Warm
+ { color: '#6194c7', pattern: 'repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.1) 10px, transparent 10px)' }, // Cool
+ { color: '#a78d4e', pattern: 'repeating-linear-gradient(90deg, rgba(255,255,255,0.1), rgba(255,255,255,0.1) 5px, transparent 5px)' } // Earthy
+ ];
+
+ function createWallpaperRoll() {
+ const styleIndex = Math.floor(Math.random() * wallpaperStyles.length);
+ const roll = {
+ x: canvas.width + 20,
+ y: rollerY - 15, // Adjust for visual placement
+ style: wallpaperStyles[styleIndex]
+ };
+ wallpaperRolls.push(roll);
+ }
+
+
+ function createObstacle() {
+ const obstacleHeight = Math.random() * 30 + 20;
+ const obstacle = {
+ x: canvas.width,
+ y: canvas.height - obstacleHeight,
+ width: 15,
+ height: obstacleHeight
+ };
+ obstacles.push(obstacle);
+ }
+
+ function resetGame() {
+ score = 0;
+ rollerX = 50;
+ rollerY = canvas.height / 2;
+ velocityY = 0;
+ obstacles = [];
+ wallpaperRolls = [];
+
+ document.getElementById('score').textContent = '0';
+ }
+
+
+ function restartGame() {
+ resetGame();
+ gameRunning = true;
+ requestAnimationFrame(gameLoop);
+ document.getElementById('gameOverOverlay').style.display = 'none';
+ }
+
+ function gameOver() {
+ updateBestScore();
+ document.getElementById('finalScore').textContent = score;
+ document.getElementById('gameOverOverlay').style.display = 'flex';
+ }
+
+
+ function drawRoller() {
+ ctx.fillStyle = '#b0472e'; // Accent color for the roller
+ ctx.beginPath();
+ ctx.arc(rollerX, rollerY, 15, 0, Math.PI * 2);
+ ctx.fill();
+ }
+
+ function drawWallpaperRoll(roll) {
+ ctx.fillStyle = roll.style.color;
+ ctx.fillRect(roll.x, roll.y, 10, 10); // Simple square representation
+ }
+
+
+ function drawObstacle(obstacle) {
+ ctx.fillStyle = 'black';
+ ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
+ }
+
+ function gameLoop() {
+ if (!gameRunning) return;
+
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ // Draw Wallpaper Rolls
+ for (const roll of wallpaperRolls) {
+ drawWallpaperRoll(roll);
+ }
+
+ // Draw Obstacles
+ for (const obstacle of obstacles) {
+ drawObstacle(obstacle);
+ }
+
+
+ // Update Roller Position
+ velocityY += gravity;
+ rollerY += velocityY;
+
+ if (rollerY > canvas.height - 15) {
+ rollerY = canvas.height - 15;
+ velocityY = 0;
+ }
+
+ drawRoller();
+
+
+ // Update and Draw Obstacles
+ for (let i = obstacles.length - 1; i >= 0; i--) {
+ obstacles[i].x -= 5; // Adjust speed as needed
+ if (obstacles[i].x + obstacles[i].width < 0) {
+ obstacles.splice(i, 1);
+ }
+ }
+
+ // Update and Draw Wallpaper Rolls
+ for (let i = wallpaperRolls.length - 1; i >= 0; i--) {
+ wallpaperRolls[i].x -= 3; // Adjust speed as needed
+ if(wallpaperRolls[i].x + 10 < 0) {
+ wallpaperRolls.splice(i, 1);
+ createWallpaperRoll(); // Create a new wallpaper roll
+ }
+ }
+
+ // Collision Detection
+ for (const obstacle of obstacles) {
+ if (rollerX > obstacle.x && rollerX < obstacle.x + obstacle.width &&
+ rollerY > obstacle.y && rollerY < obstacle.y + obstacle.height) {
+ gameOver();
+ gameRunning = false;
+ return;
+ }
+ }
+
+ // Collect Wallpaper Rolls (Collision Detection)
+ for (let i = wallpaperRolls.length - 1; i >= 0; i--) {
+ if (rollerX > wallpaperRolls[i].x && rollerX < wallpaperRolls[i].x + 10 &&
+ rollerY > wallpaperRolls[i].y && rollerY < wallpaperRolls[i].y + 10) {
+ score++;
+ document.getElementById('score').textContent = score;
+ wallpaperRolls.splice(i, 1); // Remove collected roll
+ }
+ }
+
+ requestAnimationFrame(gameLoop);
+ }
+
+
+ // Input Handling
+ document.addEventListener('keydown', function (event) {
+ if (event.code === 'Space' && rollerY >= canvas.height - 15) {
+ velocityY = jumpHeight;
+ }
+ });
+
+ canvas.addEventListener('mousedown', function() {
+ if(rollerY >= canvas.height - 15){
+ velocityY = jumpHeight;
+ }
+ })
+
+
+ // Game Start
+ document.addEventListener('DOMContentLoaded', function () {
+ createObstacle();
+ createWallpaperRoll(); // Initial wallpaper roll
+
+ gameRunning = true;
+ requestAnimationFrame(gameLoop);
+ });
+ </script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/83e28cd01c99/gemma3-12b.png b/data/artifacts/83e28cd01c99/gemma3-12b.png
new file mode 100644
index 0000000..ece8a30
Binary files /dev/null and b/data/artifacts/83e28cd01c99/gemma3-12b.png differ
diff --git a/data/artifacts/83e28cd01c99/gpt.html b/data/artifacts/83e28cd01c99/gpt.html
new file mode 100644
index 0000000..f51d5a7
--- /dev/null
+++ b/data/artifacts/83e28cd01c99/gpt.html
@@ -0,0 +1,1261 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8" />
+<title>Designer Wallcoverings Runner</title>
+<style>
+:root {
+ /* design tokens */
+ --bg: #0d0d0f;
+ --surface: #17171c;
+ --surface-soft: #1f1f26;
+ --text: #f5f2ea;
+ --muted: #8a8578;
+ --accent: #c9a961; /* metallic gold */
+ --accent-deep: #7d6a45;
+ --line: rgba(245, 242, 234, 0.12);
+ --shadow-soft: 0 18px 40px rgba(0, 0, 0, 0.6);
+ --shadow-ambient: 0 1px 0 rgba(255, 255, 255, 0.04);
+ --radius-sm: 6px;
+ --radius-md: 14px;
+ --radius-pill: 999px;
+ --font-display: "Playfair Display", "EB Garamond", "Cormorant Garamond", "Times New Roman", Georgia, serif;
+ --font-sans: "Avenir Next", "Century Gothic", "Helvetica Neue", system-ui, sans-serif;
+ --fs-display: 50px;
+ --fs-h2: 28px;
+ --fs-body: 16px;
+ --fs-label: 12px;
+ --lh-heading: 1.1;
+ --lh-body: 1.6;
+ --ease-swift-out: cubic-bezier(.22,.9,.24,1);
+ --ease-hero: cubic-bezier(.22,1,.36,1);
+ --ease-overshoot: cubic-bezier(.34,1.56,.64,1);
+ --gap-sm: 8px;
+ --gap-md: 16px;
+ --gap-lg: 28px;
+ --gap-xl: 40px;
+}
+
+/* layout reset */
+*,
+*::before,
+*::after {
+ box-sizing: border-box;
+}
+html, body {
+ margin: 0;
+ padding: 0;
+ height: 100%;
+ background: radial-gradient(circle at top left, #181820 0, #050509 55%, #000 100%);
+ color: var(--text);
+ font-family: var(--font-sans);
+}
+body {
+ display: flex;
+ flex-direction: column;
+}
+
+/* header / wordmark */
+header {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 20;
+ backdrop-filter: blur(18px);
+ background: linear-gradient(to bottom, rgba(13,13,15,0.96), rgba(13,13,15,0.86));
+ border-bottom: 1px solid var(--line);
+ box-shadow: var(--shadow-ambient);
+}
+.header-inner {
+ max-width: 1120px;
+ margin: 0 auto;
+ padding: 14px 24px 12px;
+ display: flex;
+ align-items: flex-end;
+ justify-content: space-between;
+ gap: 24px;
+}
+.wordmark {
+ font-family: var(--font-display);
+ font-size: 44px;
+ line-height: 1.05;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: var(--text); /* darkest ink */
+ white-space: normal;
+}
+.wordmark span {
+ display: block;
+}
+.wordmark span:nth-child(2) {
+ font-size: 0.9em;
+ letter-spacing: 0.22em;
+}
+
+/* header meta */
+.header-meta {
+ display: flex;
+ gap: 18px;
+ align-items: center;
+ font-size: var(--fs-label);
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ color: var(--muted);
+}
+.meta-group {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ gap: 4px;
+}
+.meta-label {
+ font-weight: 500;
+}
+.meta-value {
+ color: var(--text);
+ font-size: 13px;
+}
+
+/* pill buttons */
+.chip {
+ border-radius: var(--radius-pill);
+ border: 1px solid var(--line);
+ padding: 5px 12px 6px;
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ background: radial-gradient(circle at top, rgba(255,255,255,0.08), transparent 55%);
+ box-shadow: 0 0 0 1px rgba(0,0,0,0.7);
+}
+.chip-dot {
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ background: radial-gradient(circle at 30% 20%, #fff, var(--accent));
+ box-shadow: 0 0 0 1px rgba(0,0,0,0.6);
+}
+
+/* main layout */
+main {
+ flex: 1;
+ display: flex;
+ align-items: stretch;
+ justify-content: center;
+ padding: 88px 16px 24px;
+}
+.shell {
+ position: relative;
+ width: 100%;
+ max-width: 1120px;
+ margin: 0 auto;
+ display: grid;
+ grid-template-columns: minmax(0, 2fr) minmax(260px, 1fr);
+ gap: 24px;
+}
+
+/* game card */
+.game-card {
+ position: relative;
+ border-radius: 24px;
+ background:
+ linear-gradient(145deg, rgba(255,255,255,0.04), transparent 40%) border-box,
+ radial-gradient(circle at top left, rgba(201,169,97,0.25), transparent 55%) border-box;
+ padding: 18px;
+ box-shadow: var(--shadow-soft);
+ border: 1px solid rgba(255,255,255,0.04);
+ overflow: hidden;
+}
+.game-frame {
+ position: relative;
+ border-radius: 18px;
+ background: radial-gradient(circle at top, #262633 0, #111118 60%, #050509 100%);
+ border: 1px solid rgba(255,255,255,0.06);
+ overflow: hidden;
+}
+canvas {
+ display: block;
+ width: 100%;
+ height: 100%;
+}
+
+/* HUD overlay within canvas frame */
+.canvas-overlay {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ display: flex;
+ flex-direction: column;
+ justify-content: space-between;
+ padding: 14px 16px;
+}
+.hud-top {
+ display: flex;
+ justify-content: space-between;
+ align-items: flex-start;
+ gap: 12px;
+ font-size: 11px;
+ letter-spacing: 0.16em;
+ text-transform: uppercase;
+ color: var(--muted);
+}
+.hud-top .score-block {
+ display: inline-flex;
+ gap: 10px;
+ padding: 4px 10px;
+ border-radius: var(--radius-pill);
+ border: 1px solid rgba(201,169,97,0.6);
+ background: linear-gradient(135deg, rgba(201,169,97,0.3), rgba(39,30,14,0.7));
+ box-shadow: 0 0 0 1px rgba(0,0,0,0.9);
+ color: #fdf7e6;
+}
+.hud-top .score-label {
+ opacity: 0.7;
+}
+.hud-top .score-value {
+ font-weight: 600;
+}
+.hud-top .best-label {
+ opacity: 0.6;
+}
+.hud-top .best-value {
+ color: var(--accent);
+}
+
+/* right rail */
+.side-panel {
+ border-radius: 24px;
+ background: linear-gradient(160deg, rgba(23,23,28,0.96), rgba(9,9,12,0.96));
+ border: 1px solid rgba(255,255,255,0.06);
+ padding: 18px 18px 16px;
+ box-shadow: var(--shadow-soft);
+ display: flex;
+ flex-direction: column;
+ gap: 20px;
+}
+.panel-header {
+ font-family: var(--font-display);
+ font-size: var(--fs-h2);
+ line-height: var(--lh-heading);
+ letter-spacing: 0.06em;
+ text-transform: uppercase;
+}
+.panel-sub {
+ font-size: 13px;
+ color: var(--muted);
+ max-width: 30rem;
+}
+
+/* control hints */
+.controls {
+ font-size: 12px;
+ text-transform: uppercase;
+ letter-spacing: 0.18em;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 8px;
+}
+.key-pill {
+ border-radius: var(--radius-pill);
+ border: 1px solid var(--line);
+ padding: 4px 10px 5px;
+ display: inline-flex;
+ align-items: center;
+ gap: 8px;
+ color: var(--muted);
+}
+.key-shape {
+ min-width: 18px;
+ height: 18px;
+ border-radius: 5px;
+ border: 1px solid rgba(255,255,255,0.5);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 11px;
+ color: var(--text);
+}
+
+/* pattern swatches */
+.swatch-grid {
+ display: grid;
+ grid-template-columns: repeat(3, minmax(0, 1fr));
+ gap: 10px;
+}
+.swatch {
+ position: relative;
+ border-radius: 14px;
+ padding: 10px;
+ border: 1px solid rgba(255,255,255,0.08);
+ background: radial-gradient(circle at top, rgba(255,255,255,0.06), rgba(7,7,10,1));
+ box-shadow: 0 14px 35px rgba(0,0,0,0.75);
+ overflow: hidden;
+}
+.swatch::before {
+ content: "";
+ position: absolute;
+ inset: 0;
+ opacity: 0.6;
+ mix-blend-mode: screen;
+}
+/* three distinct motifs */
+.swatch--marble::before {
+ background-image:
+ radial-gradient(circle at 10% 20%, rgba(255,255,255,0.16) 0, transparent 50%),
+ radial-gradient(circle at 80% 0%, rgba(201,169,97,0.45) 0, transparent 55%),
+ linear-gradient(135deg, rgba(245,242,234,0.24) 0, transparent 40%, rgba(109,95,71,0.35) 70%);
+}
+.swatch--geom::before {
+ background-image:
+ repeating-linear-gradient(135deg, rgba(201,169,97,0.8) 0 2px, transparent 2px 10px),
+ linear-gradient(135deg, rgba(0,0,0,0.1), rgba(255,255,255,0.05));
+}
+.swatch--silk::before {
+ background-image:
+ linear-gradient(120deg, rgba(255,255,255,0.32), transparent 35%, rgba(201,169,97,0.35) 60%, rgba(0,0,0,0.7)),
+ radial-gradient(circle at 0 100%, rgba(255,255,255,0.24), transparent 55%);
+}
+
+/* swatch captions */
+.swatch-label {
+ position: relative;
+ font-size: 10px;
+ letter-spacing: 0.22em;
+ text-transform: uppercase;
+ color: rgba(245,242,234,0.8);
+ z-index: 1;
+}
+
+/* primary CTA */
+.primary-btn {
+ appearance: none;
+ border: none;
+ border-radius: var(--radius-pill);
+ padding: 10px 18px 11px;
+ font-size: 11px;
+ letter-spacing: 0.22em;
+ text-transform: uppercase;
+ background: radial-gradient(circle at 20% 0%, #fff 0, #fbe9c0 18%, var(--accent) 40%, #4a3818 100%);
+ color: #1b140a;
+ box-shadow:
+ inset 0 0 0 1px rgba(255,255,255,0.35),
+ 0 14px 30px rgba(0,0,0,0.9);
+ cursor: pointer;
+ align-self: flex-start;
+ transform-origin: center;
+ transition:
+ transform 180ms var(--ease-swift-out),
+ box-shadow 180ms var(--ease-swift-out),
+ filter 180ms var(--ease-swift-out);
+}
+.primary-btn:hover {
+ transform: translateY(-1px) scale(1.02);
+ filter: saturate(1.1);
+ box-shadow:
+ inset 0 0 0 1px rgba(255,255,255,0.5),
+ 0 18px 40px rgba(0,0,0,1);
+}
+.primary-btn:active {
+ transform: translateY(1px) scale(0.99);
+ box-shadow:
+ inset 0 0 0 1px rgba(0,0,0,0.8),
+ 0 6px 18px rgba(0,0,0,0.9);
+}
+
+/* overlay for game states */
+.game-overlay {
+ position: absolute;
+ inset: 0;
+ pointer-events: none;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+.overlay-card {
+ max-width: 340px;
+ border-radius: 22px;
+ padding: 22px 22px 18px;
+ background: radial-gradient(circle at top, rgba(201,169,97,0.22), rgba(9,9,12,0.98));
+ border: 1px solid rgba(255,255,255,0.18);
+ box-shadow: var(--shadow-soft);
+ text-align: left;
+ opacity: 0;
+ transform: translateY(14px) scale(0.97);
+ transition:
+ opacity 320ms var(--ease-hero),
+ transform 320ms var(--ease-hero);
+}
+.overlay-card.visible {
+ opacity: 1;
+ transform: translateY(0) scale(1);
+ pointer-events: auto;
+}
+.overlay-title {
+ font-family: var(--font-display);
+ font-size: 28px;
+ line-height: 1.1;
+ text-transform: uppercase;
+ letter-spacing: 0.12em;
+ margin-bottom: 10px;
+}
+.overlay-body {
+ font-size: 14px;
+ color: var(--muted);
+ margin-bottom: 18px;
+}
+.overlay-metrics {
+ display: flex;
+ justify-content: space-between;
+ gap: 12px;
+ margin-bottom: 16px;
+}
+.metric {
+ flex: 1;
+ border-radius: 12px;
+ padding: 8px 10px;
+ border: 1px solid rgba(255,255,255,0.18);
+ background: rgba(0,0,0,0.55);
+}
+.metric-label {
+ font-size: 10px;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: rgba(245,242,234,0.7);
+ margin-bottom: 4px;
+}
+.metric-value {
+ font-size: 18px;
+ color: var(--accent);
+}
+
+/* overlay button */
+.overlay-actions {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ gap: 12px;
+}
+.secondary-btn {
+ flex: 1;
+ appearance: none;
+ border-radius: var(--radius-pill);
+ border: 1px solid var(--line);
+ background: rgba(10,10,14,0.9);
+ color: var(--muted);
+ font-size: 11px;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ padding: 9px 10px;
+ cursor: pointer;
+ transition:
+ background 160ms var(--ease-swift-out),
+ border-color 160ms var(--ease-swift-out),
+ color 160ms var(--ease-swift-out),
+ transform 160ms var(--ease-swift-out);
+}
+.secondary-btn:hover {
+ background: rgba(30,30,38,0.9);
+ border-color: rgba(255,255,255,0.4);
+ color: var(--text);
+ transform: translateY(-1px);
+}
+.secondary-btn:active {
+ transform: translateY(0);
+}
+
+/* responsive */
+@media (max-width: 900px) {
+ .shell {
+ grid-template-columns: minmax(0, 1fr);
+ }
+ .side-panel {
+ order: -1;
+ }
+ .header-inner {
+ align-items: flex-start;
+ }
+ .header-meta {
+ flex-direction: column;
+ align-items: flex-end;
+ }
+}
+@media (max-width: 640px) {
+ .wordmark {
+ font-size: 40px;
+ }
+ main {
+ padding-top: 96px;
+ }
+ .header-inner {
+ padding-inline: 16px;
+ }
+}
+</style>
+</head>
+<body>
+<header>
+ <div class="header-inner">
+ <div class="wordmark">
+ <span>DESIGNER</span>
+ <span>WALLCOVERINGS</span>
+ </div>
+ <div class="header-meta">
+ <div class="meta-group">
+ <div class="meta-label">Session Score</div>
+ <div class="meta-value" id="meta-score">0</div>
+ </div>
+ <div class="meta-group">
+ <div class="meta-label">Best Run</div>
+ <div class="meta-value" id="meta-best">0</div>
+ </div>
+ <div class="chip">
+ <span class="chip-dot" aria-hidden="true"></span>
+ <span>Tap / Space to Jump</span>
+ </div>
+ </div>
+ </div>
+</header>
+
+<main>
+ <div class="shell">
+ <section class="game-card">
+ <div class="game-frame">
+ <canvas id="game" width="960" height="420"></canvas>
+ <div class="canvas-overlay">
+ <div class="hud-top">
+ <div class="score-block">
+ <span class="score-label">Score</span>
+ <span class="score-value" id="hud-score">0</span>
+ </div>
+ <div>
+ <span class="best-label">Best • </span>
+ <span class="best-value" id="hud-best">0</span>
+ </div>
+ </div>
+ </div>
+ <div class="game-overlay">
+ <div class="overlay-card" id="overlay">
+ <div class="overlay-title" id="overlay-title">Roll the Wall</div>
+ <div class="overlay-body" id="overlay-body">
+ Glide your paint-roller along a lit gallery wall. Collect wallpaper rolls, avoid obstacles, and watch the pace rise.
+ </div>
+ <div class="overlay-metrics">
+ <div class="metric">
+ <div class="metric-label">Last Score</div>
+ <div class="metric-value" id="overlay-score">0</div>
+ </div>
+ <div class="metric">
+ <div class="metric-label">Best</div>
+ <div class="metric-value" id="overlay-best">0</div>
+ </div>
+ </div>
+ <div class="overlay-actions">
+ <button class="primary-btn" id="btn-primary">Tap / Space to Start</button>
+ <button class="secondary-btn" id="btn-muted">Silk Run</button>
+ </div>
+ </div>
+ </div>
+ </div>
+ </section>
+
+ <aside class="side-panel">
+ <div>
+ <div class="panel-header">Gallery Wall Run</div>
+ <div class="panel-sub">
+ A playful endless-runner set on a lacquered gallery wall. Skim past brass fixtures, collect couture rolls, and keep your stride as the pace rises.
+ </div>
+ </div>
+ <div class="controls">
+ <div class="key-pill">
+ <span class="key-shape">␣</span>
+ <span>Jump</span>
+ </div>
+ <div class="key-pill">
+ <span class="key-shape">☝</span>
+ <span>Tap Canvas</span>
+ </div>
+ </div>
+ <div class="swatch-grid">
+ <div class="swatch swatch--marble">
+ <div class="swatch-label">Marble Vein</div>
+ </div>
+ <div class="swatch swatch--geom">
+ <div class="swatch-label">Deco Lattice</div>
+ </div>
+ <div class="swatch swatch--silk">
+ <div class="swatch-label">Silk Ombre</div>
+ </div>
+ </div>
+ <button class="primary-btn" id="btn-side-start">Begin New Run</button>
+ </aside>
+ </div>
+</main>
+
+<script>
+(function() {
+ const canvas = document.getElementById('game');
+ const ctx = canvas.getContext('2d');
+
+ let width = canvas.width;
+ let height = canvas.height;
+
+ function resizeCanvas() {
+ const rect = canvas.getBoundingClientRect();
+ const scale = window.devicePixelRatio || 1;
+ const w = rect.width * scale;
+ const h = rect.height * scale;
+ canvas.width = w;
+ canvas.height = h;
+ width = w;
+ height = h;
+ }
+ resizeCanvas();
+ window.addEventListener('resize', resizeCanvas);
+
+ // game state
+ let lastTime = 0;
+ let running = false;
+ let startedOnce = false;
+ let gameOver = false;
+
+ let groundY = 0;
+ let roller = {};
+ let velocityY = 0;
+ const gravity = 2200;
+ const jumpVelocity = -900;
+
+ let scrollX = 0;
+ let speed = 360;
+ const speedIncrease = 10; // per second, scaled
+ const maxSpeed = 1000;
+
+ let obstacles = [];
+ let collectibles = [];
+ let score = 0;
+ let bestScore = 0;
+
+ const localBestKey = 'dw_runner_best';
+ try {
+ const stored = localStorage.getItem(localBestKey);
+ if (stored != null) bestScore = parseInt(stored, 10) || 0;
+ } catch (e) {}
+
+ // HUD elements
+ const hudScore = document.getElementById('hud-score');
+ const hudBest = document.getElementById('hud-best');
+ const metaScore = document.getElementById('meta-score');
+ const metaBest = document.getElementById('meta-best');
+
+ const overlay = document.getElementById('overlay');
+ const overlayTitle = document.getElementById('overlay-title');
+ const overlayBody = document.getElementById('overlay-body');
+ const overlayScore = document.getElementById('overlay-score');
+ const overlayBest = document.getElementById('overlay-best');
+ const btnPrimary = document.getElementById('btn-primary');
+ const btnMuted = document.getElementById('btn-muted');
+ const btnSideStart = document.getElementById('btn-side-start');
+
+ function updateHUD() {
+ hudScore.textContent = Math.floor(score);
+ hudBest.textContent = bestScore;
+ metaScore.textContent = Math.floor(score);
+ metaBest.textContent = bestScore;
+ overlayScore.textContent = Math.floor(score);
+ overlayBest.textContent = bestScore;
+ }
+ updateHUD();
+
+ function showOverlay(mode) {
+ overlay.classList.add('visible');
+ if (mode === 'intro') {
+ overlayTitle.textContent = 'Roll the Wall';
+ overlayBody.textContent = 'Tap or press space to launch your roller. Skim the lacquered wall, collect patterned rolls, and glide past brass fixtures.';
+ btnPrimary.textContent = 'Tap / Space to Start';
+ btnMuted.textContent = 'View Patterns';
+ } else if (mode === 'gameover') {
+ overlayTitle.textContent = 'Wall Scuffed';
+ overlayBody.textContent = 'Your roller clipped a fixture. The gallery pauses, but the wall is endless—reset your stride and chase a cleaner run.';
+ btnPrimary.textContent = 'Run Again';
+ btnMuted.textContent = 'Silk Restart';
+ }
+ }
+ function hideOverlay() {
+ overlay.classList.remove('visible');
+ }
+
+ function resetGame() {
+ const baseHeight = height;
+ groundY = baseHeight * 0.78;
+
+ const rollerRadius = baseHeight * 0.06;
+ roller = {
+ x: baseHeight * 0.22,
+ y: groundY - rollerRadius,
+ radius: rollerRadius,
+ width: rollerRadius * 1.8,
+ height: rollerRadius * 1.1
+ };
+
+ velocityY = 0;
+ scrollX = 0;
+ speed = 360;
+ obstacles = [];
+ collectibles = [];
+ score = 0;
+ gameOver = false;
+ updateHUD();
+ }
+
+ function spawnObstacle() {
+ const wallY = groundY - height * 0.08;
+ const wallHeight = height * 0.32;
+ const minWidth = 50;
+ const maxWidth = 120;
+ const widthOb = minWidth + Math.random() * (maxWidth - minWidth);
+ const heightOb = wallHeight * (0.3 + Math.random() * 0.5);
+ const gap = height * (0.09 + Math.random() * 0.06);
+ const y = groundY - gap - heightOb;
+ const x = width + (Math.random() * width * 0.4);
+ obstacles.push({
+ x,
+ y,
+ w: widthOb,
+ h: heightOb
+ });
+ }
+
+ function spawnCollectible() {
+ const radius = height * 0.035;
+ const x = width + Math.random() * width * 0.6;
+ const base = groundY - radius - height * 0.02;
+ const y = base - Math.random() * height * 0.1;
+ const styleIndex = Math.floor(Math.random() * 3);
+ collectibles.push({
+ x,
+ y,
+ r: radius,
+ collected: false,
+ style: styleIndex
+ });
+ }
+
+ function ensureSpawns(dt) {
+ const lastObs = obstacles[obstacles.length - 1];
+ if (!lastObs || lastObs.x + lastObs.w < width + 150) {
+ if (Math.random() < dt * 1.5) spawnObstacle();
+ }
+ if (Math.random() < dt * 1.4) spawnCollectible();
+ }
+
+ function rectsOverlap(a, b) {
+ return a.x < b.x + b.w && a.x + a.w > b.x && a.y < b.y + b.h && a.y + a.h > b.y;
+ }
+
+ function circleRectOverlap(cx, cy, r, rx, ry, rw, rh) {
+ const closestX = Math.max(rx, Math.min(cx, rx + rw));
+ const closestY = Math.max(ry, Math.min(cy, ry + rh));
+ const dx = cx - closestX;
+ const dy = cy - closestY;
+ return (dx * dx + dy * dy) < (r * r);
+ }
+
+ function circleCircleOverlap(ax, ay, ar, bx, by, br) {
+ const dx = ax - bx;
+ const dy = ay - by;
+ const dist2 = dx * dx + dy * dy;
+ const rr = (ar + br) * (ar + br);
+ return dist2 < rr;
+ }
+
+ function setBest(newBest) {
+ bestScore = newBest;
+ try {
+ localStorage.setItem(localBestKey, String(bestScore));
+ } catch (e) {}
+ updateHUD();
+ }
+
+ // drawing helpers
+ function clear() {
+ ctx.clearRect(0, 0, width, height);
+ const gradient = ctx.createLinearGradient(0, 0, 0, height);
+ gradient.addColorStop(0, "#181820");
+ gradient.addColorStop(0.45, "#101018");
+ gradient.addColorStop(1, "#050509");
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, 0, width, height);
+ }
+
+ function drawWall() {
+ const wallY = groundY - height * 0.08;
+ const wallHeight = height * 0.32;
+
+ // subtle vertical paneling
+ const panelWidth = width * 0.11;
+ for (let x = (scrollX * 0.2) % panelWidth - panelWidth; x < width + panelWidth; x += panelWidth) {
+ const g = ctx.createLinearGradient(x, wallY, x + panelWidth, wallY + wallHeight);
+ g.addColorStop(0, "rgba(12,12,16,0.9)");
+ g.addColorStop(0.45, "rgba(27,27,33,0.9)");
+ g.addColorStop(1, "rgba(8,8,10,0.95)");
+ ctx.fillStyle = g;
+ ctx.fillRect(x, wallY, panelWidth, wallHeight);
+ // brass trim
+ ctx.fillStyle = "rgba(201,169,97,0.45)";
+ ctx.fillRect(x, wallY, 1, wallHeight);
+ }
+
+ // picture rail
+ const railY = wallY - height * 0.03;
+ const railHeight = height * 0.02;
+ const railGrad = ctx.createLinearGradient(0, railY, 0, railY + railHeight);
+ railGrad.addColorStop(0, "#3c3221");
+ railGrad.addColorStop(0.3, "#c9a961");
+ railGrad.addColorStop(0.7, "#7d6a45");
+ railGrad.addColorStop(1, "#1b1309");
+ ctx.fillStyle = railGrad;
+ ctx.fillRect(0, railY, width, railHeight);
+
+ // ground edge
+ const groundHeight = height - groundY;
+ const floorGrad = ctx.createLinearGradient(0, groundY, 0, height);
+ floorGrad.addColorStop(0, "#07070a");
+ floorGrad.addColorStop(0.7, "#020203");
+ floorGrad.addColorStop(1, "#000");
+ ctx.fillStyle = floorGrad;
+ ctx.fillRect(0, groundY, width, groundHeight);
+
+ // subtle reflection line
+ ctx.strokeStyle = "rgba(255,255,255,0.08)";
+ ctx.lineWidth = 1;
+ ctx.beginPath();
+ ctx.moveTo(0, groundY + 0.5);
+ ctx.lineTo(width, groundY + 0.5);
+ ctx.stroke();
+
+ // glints
+ ctx.strokeStyle = "rgba(201,169,97,0.4)";
+ ctx.lineWidth = 1;
+ for (let i = 0; i < 4; i++) {
+ const gx = ((i * 0.25 + (scrollX / 1200)) % 1) * width;
+ ctx.beginPath();
+ ctx.moveTo(gx, wallY + 4);
+ ctx.lineTo(gx + 60, wallY + wallHeight * 0.4);
+ ctx.stroke();
+ }
+ }
+
+ function drawRoller() {
+ const r = roller.radius;
+ const bodyW = roller.width;
+ const bodyH = roller.height;
+
+ const x = roller.x;
+ const y = roller.y;
+
+ // roller body (rounded rect)
+ ctx.save();
+ ctx.translate(x, y);
+
+ const bodyX = -bodyW * 0.5;
+ const bodyY = -bodyH * 0.5;
+
+ const angle = (scrollX / 120) % (Math.PI * 2);
+ ctx.rotate(Math.sin(angle) * 0.08);
+
+ const grd = ctx.createLinearGradient(bodyX, bodyY, bodyX + bodyW, bodyY);
+ grd.addColorStop(0, "#d0b985");
+ grd.addColorStop(0.4, "#f9f0d3");
+ grd.addColorStop(0.7, "#c9a961");
+ grd.addColorStop(1, "#7d6a45");
+ ctx.fillStyle = grd;
+ const radius = bodyH * 0.5;
+ ctx.beginPath();
+ ctx.moveTo(bodyX + radius, bodyY);
+ ctx.lineTo(bodyX + bodyW - radius, bodyY);
+ ctx.quadraticCurveTo(bodyX + bodyW, bodyY, bodyX + bodyW, bodyY + radius);
+ ctx.lineTo(bodyX + bodyW, bodyY + bodyH - radius);
+ ctx.quadraticCurveTo(bodyX + bodyW, bodyY + bodyH, bodyX + bodyW - radius, bodyY + bodyH);
+ ctx.lineTo(bodyX + radius, bodyY + bodyH);
+ ctx.quadraticCurveTo(bodyX, bodyY + bodyH, bodyX, bodyY + bodyH - radius);
+ ctx.lineTo(bodyX, bodyY + radius);
+ ctx.quadraticCurveTo(bodyX, bodyY, bodyX + radius, bodyY);
+ ctx.closePath();
+ ctx.fill();
+
+ // texture bands
+ ctx.globalAlpha = 0.28;
+ ctx.fillStyle = "rgba(255,255,255,0.9)";
+ for (let i = 0; i < 5; i++) {
+ const yy = bodyY + 2 + (i / 4) * (bodyH - 4);
+ ctx.fillRect(bodyX + 4, yy, bodyW - 8, 1);
+ }
+ ctx.globalAlpha = 1;
+
+ // side cap
+ const capR = bodyH * 0.45;
+ const capX = bodyX + bodyW - capR * 0.2;
+ const capY = bodyY + bodyH * 0.5;
+ const capGrad = ctx.createRadialGradient(
+ capX - capR * 0.3, capY - capR * 0.4, capR * 0.1,
+ capX, capY, capR
+ );
+ capGrad.addColorStop(0, "#fffbe8");
+ capGrad.addColorStop(0.4, "#f2dfb0");
+ capGrad.addColorStop(0.85, "#7d6a45");
+ capGrad.addColorStop(1, "#1b140a");
+ ctx.fillStyle = capGrad;
+ ctx.beginPath();
+ ctx.arc(capX, capY, capR, 0, Math.PI * 2);
+ ctx.fill();
+
+ // small axle
+ ctx.fillStyle = "#0d0b08";
+ ctx.beginPath();
+ ctx.arc(capX, capY, capR * 0.18, 0, Math.PI * 2);
+ ctx.fill();
+
+ // handle arm
+ ctx.strokeStyle = "#d3ccb9";
+ ctx.lineWidth = 3;
+ ctx.lineCap = "round";
+ ctx.beginPath();
+ ctx.moveTo(bodyX - bodyW * 0.15, bodyY + bodyH * 0.2);
+ ctx.lineTo(bodyX - bodyW * 0.45, bodyY - bodyH * 0.8);
+ ctx.stroke();
+
+ // handle grip
+ const gripX = bodyX - bodyW * 0.45;
+ const gripY = bodyY - bodyH * 0.8;
+ const gripH = bodyH * 1.2;
+ const gripW = bodyW * 0.25;
+ const gripGrad = ctx.createLinearGradient(gripX, gripY, gripX, gripY + gripH);
+ gripGrad.addColorStop(0, "#24242a");
+ gripGrad.addColorStop(0.4, "#3a3a44");
+ gripGrad.addColorStop(1, "#050507");
+ ctx.fillStyle = gripGrad;
+ ctx.beginPath();
+ ctx.roundRect(gripX - gripW * 0.5, gripY, gripW, gripH, gripW * 0.45);
+ ctx.fill();
+
+ ctx.restore();
+
+ // trailing painted stripe
+ const tailWidth = bodyW * 0.6;
+ const stripeX = roller.x - bodyW * 0.3 - tailWidth;
+ const stripeY = roller.y - bodyH * 0.15;
+ const stripeH = bodyH * 0.35;
+ const stripeGrad = ctx.createLinearGradient(stripeX, stripeY, stripeX + tailWidth, stripeY);
+ stripeGrad.addColorStop(0, "rgba(201,169,97,0)");
+ stripeGrad.addColorStop(0.6, "rgba(201,169,97,0.3)");
+ stripeGrad.addColorStop(1, "rgba(201,169,97,0.7)");
+ ctx.fillStyle = stripeGrad;
+ ctx.fillRect(stripeX, stripeY, tailWidth, stripeH);
+ }
+
+ function drawObstacles() {
+ ctx.save();
+ ctx.lineWidth = 1;
+ obstacles.forEach(o => {
+ const baseGrad = ctx.createLinearGradient(o.x, o.y, o.x + o.w, o.y + o.h);
+ baseGrad.addColorStop(0, "#19161a");
+ baseGrad.addColorStop(0.5, "#332824");
+ baseGrad.addColorStop(1, "#0c0908");
+ ctx.fillStyle = baseGrad;
+ ctx.strokeStyle = "rgba(255,255,255,0.09)";
+ ctx.beginPath();
+ const r = 6;
+ const x = o.x, y = o.y, w = o.w, h = o.h;
+ ctx.moveTo(x + r, y);
+ ctx.lineTo(x + w - r, y);
+ ctx.quadraticCurveTo(x + w, y, x + w, y + r);
+ ctx.lineTo(x + w, y + h - r);
+ ctx.quadraticCurveTo(x + w, y + h, x + w - r, y + h);
+ ctx.lineTo(x + r, y + h);
+ ctx.quadraticCurveTo(x, y + h, x, y + h - r);
+ ctx.lineTo(x, y + r);
+ ctx.quadraticCurveTo(x, y, x + r, y);
+ ctx.closePath();
+ ctx.fill();
+ ctx.stroke();
+
+ // brass edge and screws
+ ctx.strokeStyle = "rgba(201,169,97,0.65)";
+ ctx.beginPath();
+ ctx.moveTo(x + 3, y + 2);
+ ctx.lineTo(x + w - 3, y + 2);
+ ctx.stroke();
+ ctx.fillStyle = "rgba(201,169,97,0.85)";
+ const screwR = 2;
+ for (let i = 0; i < 3; i++) {
+ const sx = x + (i / 2) * (w - 8) + 4;
+ const sy = y + 6;
+ ctx.beginPath();
+ ctx.arc(sx, sy, screwR, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ });
+ ctx.restore();
+ }
+
+ function drawCollectibles(deltaTime) {
+ collectibles.forEach(c => {
+ if (c.collected) return;
+ const t = (scrollX / 240 + c.x * 0.001) % (Math.PI * 2);
+ const bob = Math.sin(t) * (height * 0.006);
+ const x = c.x;
+ const y = c.y + bob;
+ const r = c.r;
+
+ ctx.save();
+ ctx.translate(x, y);
+
+ // subtle shadow
+ ctx.globalAlpha = 0.35;
+ ctx.fillStyle = "black";
+ ctx.beginPath();
+ ctx.ellipse(0, r * 1.1, r * 1.3, r * 0.5, 0, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.globalAlpha = 1;
+
+ // roll core
+ const coreR = r * 0.72;
+ let coreGrad = ctx.createRadialGradient(
+ -coreR * 0.4, -coreR * 0.6, coreR * 0.2,
+ 0, 0, coreR
+ );
+
+ if (c.style === 0) {
+ // marble vein
+ coreGrad.addColorStop(0, "#fff9e7");
+ coreGrad.addColorStop(0.3, "#e9dfc3");
+ coreGrad.addColorStop(0.6, "#c1ab83");
+ coreGrad.addColorStop(1, "#7d6a45");
+ } else if (c.style === 1) {
+ // deco lattice
+ coreGrad.addColorStop(0, "#f7efe0");
+ coreGrad.addColorStop(0.4, "#c9a961");
+ coreGrad.addColorStop(0.8, "#4a3818");
+ coreGrad.addColorStop(1, "#1b140a");
+ } else {
+ // silk ombre
+ coreGrad.addColorStop(0, "#fff8e3");
+ coreGrad.addColorStop(0.4, "#e4cfaa");
+ coreGrad.addColorStop(0.8, "#b59c72");
+ coreGrad.addColorStop(1, "#3c2f1c");
+ }
+ ctx.fillStyle = coreGrad;
+ ctx.beginPath();
+ ctx.arc(0, 0, coreR, 0, Math.PI * 2);
+ ctx.fill();
+
+ // pattern overlay by style
+ if (c.style === 0) {
+ // marble: soft veins
+ ctx.strokeStyle = "rgba(255,255,255,0.45)";
+ ctx.lineWidth = 1;
+ ctx.beginPath();
+ ctx.moveTo(-coreR * 0.9, -coreR * 0.3);
+ ctx.bezierCurveTo(-coreR * 0.4, -coreR * 0.7, coreR * 0.1, -coreR * 0.2, coreR * 0.7, -coreR * 0.5);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.moveTo(-coreR * 0.6, coreR * 0.2);
+ ctx.bezierCurveTo(-coreR * 0.1, coreR * 0.7, coreR * 0.2, coreR * 0.3, coreR * 0.9, coreR * 0.8);
+ ctx.stroke();
+ } else if (c.style === 1) {
+ // deco lattice: radiating lines
+ ctx.strokeStyle = "rgba(0,0,0,0.35)";
+ ctx.lineWidth = 1;
+ for (let i = 0; i < 8; i++) {
+ const a = (i / 8) * Math.PI * 2;
+ ctx.beginPath();
+ ctx.moveTo(Math.cos(a) * coreR * 0.2, Math.sin(a) * coreR * 0.2);
+ ctx.lineTo(Math.cos(a) * coreR, Math.sin(a) * coreR);
+ ctx.stroke();
+ }
+ ctx.strokeStyle = "rgba(255,255,255,0.5)";
+ ctx.beginPath();
+ ctx.arc(0, 0, coreR * 0.35, 0, Math.PI * 2);
+ ctx.stroke();
+ } else {
+ // silk: horizontal threads
+ ctx.strokeStyle = "rgba(255,255,255,0.5)";
+ ctx.lineWidth = 1;
+ for (let i = -2; i <= 2; i++) {
+ const yy = (i / 4) * coreR * 1.4;
+ ctx.beginPath();
+ ctx.moveTo(-coreR * 0.9, yy);
+ ctx.lineTo(coreR * 0.9, yy);
+ ctx.stroke();
+ }
+ }
+
+ // outer paper edge
+ ctx.strokeStyle = "rgba(255,255,255,0.8)";
+ ctx.lineWidth = 1.2;
+ ctx.beginPath();
+ ctx.arc(0, 0, coreR, Math.PI * 0.15, Math.PI * 0.85);
+ ctx.stroke();
+
+ ctx.restore();
+ });
+ }
+
+ function update(dt) {
+ if (!running) return;
+
+ // speed ramp
+ speed += speedIncrease * dt;
+ if (speed > maxSpeed) speed = maxSpeed;
+
+ scrollX += speed * dt;
+ score += dt * (speed / 60);
+ updateHUD();
+
+ // roller physics
+ velocityY += gravity * dt;
+ roller.y += velocityY * dt;
+ if (roller.y > groundY - roller.radius) {
+ roller.y = groundY - roller.radius;
+ velocityY = 0;
+ }
+
+ // spawn elements
+ ensureSpawns(dt);
+
+ // move entities
+ const dx = speed * dt;
+ obstacles.forEach(o => { o.x -= dx; });
+ collectibles.forEach(c => { c.x -= dx; });
+
+ // cleanup
+ obstacles = obstacles.filter(o => o.x + o.w > -80);
+ collectibles = collectibles.filter(c => !c.collected && c.x + c.r > -80);
+
+ // collisions
+ const rollerRect = {
+ x: roller.x - roller.width * 0.5,
+ y: roller.y - roller.height * 0.5,
+ w: roller.width,
+ h: roller.height
+ };
+
+ for (const o of obstacles) {
+ if (rectsOverlap(rollerRect, {x: o.x, y: o.y, w: o.w, h: o.h})) {
+ handleGameOver();
+ break;
+ }
+ }
+
+ collectibles.forEach(c => {
+ if (!c.collected && circleCircleOverlap(roller.x, roller.y, roller.radius * 0.8, c.x, c.y, c.r * 0.8)) {
+ c.collected = true;
+ score += 10;
+ if (score > bestScore) setBest(Math.floor(score));
+ updateHUD();
+ }
+ });
+ }
+
+ function handleGameOver() {
+ gameOver = true;
+ running = false;
+ if (score > bestScore) {
+ setBest(Math.floor(score));
+ }
+ updateHUD();
+ showOverlay('gameover');
+ }
+
+ function draw() {
+ clear();
+ drawWall();
+ drawObstacles();
+ drawCollectibles();
+ drawRoller();
+ }
+
+ function loop(timestamp) {
+ if (!lastTime) lastTime = timestamp;
+ const dt = Math.min(0.05, (timestamp - lastTime) / 1000);
+ lastTime = timestamp;
+
+ if (running) {
+ update(dt);
+ }
+ draw();
+ requestAnimationFrame(loop);
+ }
+
+ function jump() {
+ if (!startedOnce) {
+ startedOnce = true;
+ startGame();
+ return;
+ }
+ if (!running) {
+ if (gameOver) {
+ restartGame();
+ } else {
+ startGame();
+ }
+ return;
+ }
+ const grounded = Math.abs(roller.y - (groundY - roller.radius)) < 2;
+ if (grounded) {
+ velocityY = jumpVelocity;
+ }
+ }
+
+ function startGame() {
+ hideOverlay();
+ gameOver = false;
+ running = true;
+ }
+
+ function restartGame() {
+ resetGame();
+ hideOverlay();
+ running = true;
+ }
+
+ // input
+ window.addEventListener('keydown', (e) => {
+ if (e.code === 'Space') {
+ e.preventDefault();
+ jump();
+ }
+ });
+ canvas.addEventListener('pointerdown', (e) => {
+ e.preventDefault();
+ jump();
+ });
+
+ btnPrimary.addEventListener('click', (e) => {
+ e.preventDefault();
+ if (gameOver || !startedOnce) {
+ startedOnce = true;
+ restartGame();
+ } else {
+ startGame();
+ }
+ });
+ btnMuted.addEventListener('click', (e) => {
+ e.preventDefault();
+ // soft restart with same rules
+ startedOnce = true;
+ restartGame();
+ });
+ btnSideStart.addEventListener('click', (e) => {
+ e.preventDefault();
+ startedOnce = true;
+ restartGame();
+ });
+
+ // initial setup
+ resetGame();
+ showOverlay('intro');
+ requestAnimationFrame(loop);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/83e28cd01c99/gpt.png b/data/artifacts/83e28cd01c99/gpt.png
new file mode 100644
index 0000000..32ceec4
Binary files /dev/null and b/data/artifacts/83e28cd01c99/gpt.png differ
diff --git a/data/artifacts/83e28cd01c99/grok.html b/data/artifacts/83e28cd01c99/grok.html
new file mode 100644
index 0000000..5c9f82f
--- /dev/null
+++ b/data/artifacts/83e28cd01c99/grok.html
@@ -0,0 +1,1164 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Designer Wallcoverings — Roll</title>
+<style>
+:root {
+ --bg: #0d0d0f;
+ --surface: #17171c;
+ --ink: #1c1a17;
+ --text: #f5f2ea;
+ --muted: #8a8578;
+ --accent: #c9a961;
+ --accent2: #7d6a45;
+ --line: rgba(201, 169, 97, 0.28);
+ --shadow: 0 12px 40px rgba(0, 0, 0, 0.45);
+ --ease: cubic-bezier(.22, .9, .24, 1);
+ --ease-pop: cubic-bezier(.34, 1.56, .64, 1);
+ --ease-out: cubic-bezier(.22, 1, .36, 1);
+ --serif: 'Didot', 'Bodoni MT', 'Playfair Display', Georgia, 'Times New Roman', serif;
+ --sans: 'Avenir Next', 'Century Gothic', Futura, 'Segoe UI', sans-serif;
+ --hair: 1px;
+}
+
+*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
+
+html, body {
+ height: 100%;
+ background: var(--bg);
+ color: var(--text);
+ font-family: var(--sans);
+ overflow: hidden;
+ -webkit-font-smoothing: antialiased;
+}
+
+/* ── Top bar / wordmark ── */
+.site-header {
+ position: fixed;
+ top: 0; left: 0; right: 0;
+ z-index: 100;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 18px 28px 14px;
+ background: linear-gradient(180deg, rgba(13,13,15,0.96) 0%, rgba(13,13,15,0.82) 70%, transparent 100%);
+ pointer-events: none;
+}
+
+.wordmark {
+ font-family: var(--serif);
+ font-size: clamp(22px, 3.2vw, 42px);
+ font-weight: 400;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: var(--text);
+ line-height: 1.15;
+ max-width: min(520px, 58vw);
+ pointer-events: auto;
+}
+
+.hud {
+ display: flex;
+ gap: 28px;
+ align-items: baseline;
+ pointer-events: auto;
+}
+
+.hud-item {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ gap: 2px;
+}
+
+.hud-label {
+ font-family: var(--sans);
+ font-size: 10px;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: var(--muted);
+}
+
+.hud-value {
+ font-family: var(--sans);
+ font-size: 18px;
+ letter-spacing: 0.08em;
+ color: var(--accent);
+ font-weight: 500;
+ font-variant-numeric: tabular-nums;
+}
+
+/* ── Stage ── */
+.stage {
+ position: fixed;
+ inset: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 88px 24px 32px;
+}
+
+canvas {
+ display: block;
+ width: min(1100px, 100%);
+ height: auto;
+ max-height: calc(100vh - 120px);
+ border: var(--hair) solid var(--line);
+ border-radius: 4px;
+ box-shadow: var(--shadow);
+ background: var(--surface);
+ cursor: pointer;
+ touch-action: none;
+}
+
+/* ── Overlays ── */
+.overlay {
+ position: fixed;
+ inset: 0;
+ z-index: 200;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(13, 13, 15, 0.78);
+ backdrop-filter: blur(8px);
+ opacity: 0;
+ visibility: hidden;
+ transition: opacity 0.45s var(--ease-out), visibility 0.45s var(--ease-out);
+}
+
+.overlay.is-open {
+ opacity: 1;
+ visibility: visible;
+}
+
+.panel {
+ width: min(440px, calc(100% - 48px));
+ padding: 48px 40px 40px;
+ background: var(--surface);
+ border: var(--hair) solid var(--line);
+ border-radius: 4px;
+ box-shadow: var(--shadow);
+ text-align: center;
+ transform: translateY(16px) scale(0.98);
+ transition: transform 0.5s var(--ease-pop);
+}
+
+.overlay.is-open .panel {
+ transform: translateY(0) scale(1);
+}
+
+.panel-kicker {
+ font-family: var(--sans);
+ font-size: 11px;
+ letter-spacing: 0.2em;
+ text-transform: uppercase;
+ color: var(--accent);
+ margin-bottom: 16px;
+}
+
+.panel-title {
+ font-family: var(--serif);
+ font-size: clamp(32px, 5vw, 50px);
+ font-weight: 400;
+ letter-spacing: 0.04em;
+ color: var(--text);
+ line-height: 1.1;
+ margin-bottom: 14px;
+}
+
+.panel-body {
+ font-family: var(--sans);
+ font-size: 15px;
+ line-height: 1.65;
+ color: var(--muted);
+ margin-bottom: 28px;
+ max-width: 34ch;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+.panel-score {
+ display: flex;
+ justify-content: center;
+ gap: 36px;
+ margin-bottom: 32px;
+}
+
+.panel-score .hud-item { align-items: center; }
+.panel-score .hud-value { font-size: 28px; color: var(--text); }
+.panel-score .hud-value.accent { color: var(--accent); }
+
+.btn {
+ appearance: none;
+ border: var(--hair) solid var(--accent);
+ background: transparent;
+ color: var(--accent);
+ font-family: var(--sans);
+ font-size: 12px;
+ letter-spacing: 0.2em;
+ text-transform: uppercase;
+ padding: 14px 32px;
+ cursor: pointer;
+ border-radius: 2px;
+ transition: background 0.3s var(--ease), color 0.3s var(--ease), transform 0.25s var(--ease-pop);
+}
+
+.btn:hover {
+ background: var(--accent);
+ color: var(--ink);
+}
+
+.btn:active { transform: scale(0.97); }
+
+.swatch-row {
+ display: flex;
+ justify-content: center;
+ gap: 10px;
+ margin: 20px 0 8px;
+}
+
+.swatch {
+ width: 28px;
+ height: 28px;
+ border-radius: 2px;
+ border: var(--hair) solid var(--line);
+ box-shadow: inset 0 0 0 1px rgba(0,0,0,0.15);
+}
+
+.hint {
+ position: fixed;
+ bottom: 28px;
+ left: 50%;
+ transform: translateX(-50%);
+ z-index: 90;
+ font-family: var(--sans);
+ font-size: 11px;
+ letter-spacing: 0.18em;
+ text-transform: uppercase;
+ color: var(--muted);
+ pointer-events: none;
+ transition: opacity 0.5s var(--ease);
+}
+
+.hint.is-hidden { opacity: 0; }
+
+@media (max-width: 640px) {
+ .site-header { padding: 14px 16px 10px; }
+ .wordmark { letter-spacing: 0.14em; max-width: 55vw; }
+ .hud { gap: 16px; }
+ .hud-value { font-size: 15px; }
+ .panel { padding: 36px 24px 28px; }
+}
+</style>
+</head>
+<body>
+<header class="site-header">
+ <div class="wordmark">Designer<br>Wallcoverings</div>
+ <div class="hud">
+ <div class="hud-item">
+ <span class="hud-label">Score</span>
+ <span class="hud-value" id="scoreVal">0</span>
+ </div>
+ <div class="hud-item">
+ <span class="hud-label">Best</span>
+ <span class="hud-value" id="bestVal">0</span>
+ </div>
+ </div>
+</header>
+
+<div class="stage">
+ <canvas id="game" width="1100" height="560" aria-label="Paint roller endless runner"></canvas>
+</div>
+
+<p class="hint" id="hint">Tap or space to jump</p>
+
+<!-- Start overlay -->
+<div class="overlay is-open" id="startOverlay">
+ <div class="panel">
+ <p class="panel-kicker">Atelier Run</p>
+ <h1 class="panel-title">Roll the Wall</h1>
+ <p class="panel-body">Guide the paint roller along the atelier wall. Collect wallpaper rolls — each pattern is a different house style — and leap over scaffolding & tins.</p>
+ <div class="swatch-row" aria-hidden="true">
+ <div class="swatch" style="background: linear-gradient(135deg,#c9a961 25%,#1c1a17 25% 50%,#c9a961 50% 75%,#1c1a17 75%)"></div>
+ <div class="swatch" style="background: radial-gradient(circle at 30% 30%,#8b3a4a 0 35%,transparent 36%),radial-gradient(circle at 70% 70%,#8b3a4a 0 28%,#2a1f24 29%)"></div>
+ <div class="swatch" style="background: repeating-linear-gradient(90deg,#2c4a3e 0 3px,#a8c3b0 3px 6px)"></div>
+ <div class="swatch" style="background: repeating-linear-gradient(45deg,#3d4a6b 0 4px,#c5c9d6 4px 8px)"></div>
+ </div>
+ <button class="btn" id="startBtn" type="button">Begin</button>
+ </div>
+</div>
+
+<!-- Game over overlay -->
+<div class="overlay" id="overOverlay">
+ <div class="panel">
+ <p class="panel-kicker">Session Complete</p>
+ <h2 class="panel-title">Wall Finished</h2>
+ <p class="panel-body">The roller has stopped. Review your collection and try for a cleaner run.</p>
+ <div class="panel-score">
+ <div class="hud-item">
+ <span class="hud-label">Score</span>
+ <span class="hud-value" id="finalScore">0</span>
+ </div>
+ <div class="hud-item">
+ <span class="hud-label">Best</span>
+ <span class="hud-value accent" id="finalBest">0</span>
+ </div>
+ </div>
+ <button class="btn" id="restartBtn" type="button">Roll Again</button>
+ </div>
+</div>
+
+<script>
+(function () {
+ 'use strict';
+
+ const canvas = document.getElementById('game');
+ const ctx = canvas.getContext('2d');
+ const W = canvas.width;
+ const H = canvas.height;
+
+ const scoreEl = document.getElementById('scoreVal');
+ const bestEl = document.getElementById('bestVal');
+ const finalScoreEl = document.getElementById('finalScore');
+ const finalBestEl = document.getElementById('finalBest');
+ const startOverlay = document.getElementById('startOverlay');
+ const overOverlay = document.getElementById('overOverlay');
+ const startBtn = document.getElementById('startBtn');
+ const restartBtn = document.getElementById('restartBtn');
+ const hint = document.getElementById('hint');
+
+ const BEST_KEY = 'dw_roll_best';
+ let best = 0;
+ try { best = parseInt(localStorage.getItem(BEST_KEY) || '0', 10) || 0; } catch (_) {}
+ bestEl.textContent = String(best);
+
+ // Palette tokens mirrored for canvas
+ const C = {
+ bg: '#0d0d0f',
+ surface: '#17171c',
+ wall: '#1e1c1a',
+ wallMid: '#262320',
+ ink: '#1c1a17',
+ text: '#f5f2ea',
+ muted: '#8a8578',
+ accent: '#c9a961',
+ accent2: '#7d6a45',
+ line: 'rgba(201,169,97,0.28)',
+ ground: '#121114',
+ shadow: 'rgba(0,0,0,0.35)',
+ };
+
+ // Visually distinct wallpaper styles (motifs, not density variants)
+ const STYLES = [
+ {
+ name: 'Herringbone Gilt',
+ base: '#2a2418',
+ draw(g, s) {
+ g.fillStyle = this.base;
+ g.fillRect(0, 0, s, s);
+ g.strokeStyle = '#c9a961';
+ g.lineWidth = 1.5;
+ const step = s / 4;
+ for (let y = -step; y < s + step; y += step) {
+ for (let x = -step; x < s + step; x += step * 2) {
+ g.beginPath();
+ g.moveTo(x, y + step);
+ g.lineTo(x + step, y);
+ g.lineTo(x + step * 2, y + step);
+ g.stroke();
+ }
+ }
+ }
+ },
+ {
+ name: 'Damask Rouge',
+ base: '#2a1f24',
+ draw(g, s) {
+ g.fillStyle = this.base;
+ g.fillRect(0, 0, s, s);
+ g.fillStyle = '#8b3a4a';
+ const cx = s / 2, cy = s / 2, r = s * 0.28;
+ // quatrefoil
+ for (let i = 0; i < 4; i++) {
+ const a = (i / 4) * Math.PI * 2 - Math.PI / 2;
+ g.beginPath();
+ g.ellipse(cx + Math.cos(a) * r * 0.55, cy + Math.sin(a) * r * 0.55, r * 0.55, r * 0.42, a, 0, Math.PI * 2);
+ g.fill();
+ }
+ g.beginPath();
+ g.arc(cx, cy, r * 0.35, 0, Math.PI * 2);
+ g.fill();
+ g.strokeStyle = '#c9a961';
+ g.lineWidth = 1;
+ g.beginPath();
+ g.arc(cx, cy, r * 0.55, 0, Math.PI * 2);
+ g.stroke();
+ }
+ },
+ {
+ name: 'Botanical Sage',
+ base: '#1a2420',
+ draw(g, s) {
+ g.fillStyle = this.base;
+ g.fillRect(0, 0, s, s);
+ g.strokeStyle = '#6f9a82';
+ g.fillStyle = '#6f9a82';
+ g.lineWidth = 1.4;
+ // vine stem
+ g.beginPath();
+ g.moveTo(s * 0.2, s);
+ g.bezierCurveTo(s * 0.25, s * 0.6, s * 0.55, s * 0.55, s * 0.5, s * 0.15);
+ g.stroke();
+ // leaves
+ const leaves = [[0.35, 0.7], [0.48, 0.45], [0.42, 0.28]];
+ leaves.forEach(([lx, ly], i) => {
+ g.save();
+ g.translate(s * lx, s * ly);
+ g.rotate((-0.6 + i * 0.5));
+ g.beginPath();
+ g.ellipse(0, 0, s * 0.12, s * 0.05, 0, 0, Math.PI * 2);
+ g.fill();
+ g.restore();
+ });
+ // berry
+ g.fillStyle = '#c9a961';
+ g.beginPath();
+ g.arc(s * 0.52, s * 0.14, s * 0.045, 0, Math.PI * 2);
+ g.fill();
+ }
+ },
+ {
+ name: 'Art Deco Indigo',
+ base: '#1a1e2c',
+ draw(g, s) {
+ g.fillStyle = this.base;
+ g.fillRect(0, 0, s, s);
+ g.strokeStyle = '#a8b0d0';
+ g.lineWidth = 1.5;
+ const cx = s / 2, cy = s / 2;
+ // fan rays
+ for (let i = 0; i < 7; i++) {
+ const a = -Math.PI / 2 + (i - 3) * 0.22;
+ g.beginPath();
+ g.moveTo(cx, cy + s * 0.15);
+ g.lineTo(cx + Math.cos(a) * s * 0.42, cy + s * 0.15 + Math.sin(a) * s * 0.42);
+ g.stroke();
+ }
+ g.strokeStyle = '#c9a961';
+ g.beginPath();
+ g.arc(cx, cy + s * 0.15, s * 0.18, Math.PI * 1.15, Math.PI * 1.85);
+ g.stroke();
+ g.beginPath();
+ g.arc(cx, cy + s * 0.15, s * 0.28, Math.PI * 1.2, Math.PI * 1.8);
+ g.stroke();
+ }
+ },
+ {
+ name: 'Toile Ivory',
+ base: '#2c2820',
+ draw(g, s) {
+ g.fillStyle = this.base;
+ g.fillRect(0, 0, s, s);
+ g.strokeStyle = '#d4c9a8';
+ g.lineWidth = 1.2;
+ // small pastoral scene motif — simplified tree + house
+ g.beginPath();
+ g.moveTo(s * 0.55, s * 0.75);
+ g.lineTo(s * 0.55, s * 0.4);
+ g.stroke();
+ g.beginPath();
+ g.arc(s * 0.55, s * 0.35, s * 0.14, 0, Math.PI * 2);
+ g.stroke();
+ g.strokeRect(s * 0.22, s * 0.52, s * 0.2, s * 0.22);
+ g.beginPath();
+ g.moveTo(s * 0.2, s * 0.52);
+ g.lineTo(s * 0.32, s * 0.38);
+ g.lineTo(s * 0.44, s * 0.52);
+ g.closePath();
+ g.stroke();
+ // birds
+ g.beginPath();
+ g.moveTo(s * 0.7, s * 0.28);
+ g.quadraticCurveTo(s * 0.75, s * 0.22, s * 0.8, s * 0.28);
+ g.moveTo(s * 0.78, s * 0.34);
+ g.quadraticCurveTo(s * 0.83, s * 0.28, s * 0.88, s * 0.34);
+ g.stroke();
+ }
+ }
+ ];
+
+ // Pre-render pattern tiles
+ const patternCanvases = STYLES.map((style) => {
+ const c = document.createElement('canvas');
+ c.width = 48;
+ c.height = 48;
+ style.draw(c.getContext('2d'), 48);
+ return c;
+ });
+
+ // Game state
+ let running = false;
+ let gameOver = false;
+ let score = 0;
+ let speed = 5.2;
+ const maxSpeed = 14;
+ let distance = 0;
+ let spawnTimer = 0;
+ let collectTimer = 0;
+ let frame = 0;
+ let particles = [];
+ let obstacles = [];
+ let collectibles = [];
+ let wallOffset = 0;
+ let paintTrail = [];
+
+ const GROUND_Y = H - 78;
+ const GRAVITY = 0.72;
+ const JUMP_V = -13.2;
+
+ const roller = {
+ x: 160,
+ y: GROUND_Y,
+ vy: 0,
+ r: 26,
+ rot: 0,
+ onGround: true,
+ width: 52,
+ height: 52,
+ };
+
+ function resetGame() {
+ running = true;
+ gameOver = false;
+ score = 0;
+ speed = 5.2;
+ distance = 0;
+ spawnTimer = 80;
+ collectTimer = 40;
+ frame = 0;
+ particles = [];
+ obstacles = [];
+ collectibles = [];
+ wallOffset = 0;
+ paintTrail = [];
+ roller.y = GROUND_Y;
+ roller.vy = 0;
+ roller.rot = 0;
+ roller.onGround = true;
+ scoreEl.textContent = '0';
+ hint.classList.add('is-hidden');
+ overOverlay.classList.remove('is-open');
+ startOverlay.classList.remove('is-open');
+ }
+
+ function endGame() {
+ running = false;
+ gameOver = true;
+ if (score > best) {
+ best = score;
+ try { localStorage.setItem(BEST_KEY, String(best)); } catch (_) {}
+ bestEl.textContent = String(best);
+ }
+ finalScoreEl.textContent = String(score);
+ finalBestEl.textContent = String(best);
+ // burst
+ for (let i = 0; i < 24; i++) {
+ const a = Math.random() * Math.PI * 2;
+ const sp = 2 + Math.random() * 5;
+ particles.push({
+ x: roller.x, y: roller.y - roller.r,
+ vx: Math.cos(a) * sp, vy: Math.sin(a) * sp - 2,
+ life: 40 + Math.random() * 20,
+ max: 60,
+ color: Math.random() > 0.5 ? C.accent : C.text,
+ size: 2 + Math.random() * 3,
+ });
+ }
+ setTimeout(() => overOverlay.classList.add('is-open'), 420);
+ }
+
+ function jump() {
+ if (!running) return;
+ if (roller.onGround) {
+ roller.vy = JUMP_V;
+ roller.onGround = false;
+ }
+ }
+
+ // Input
+ function onAction(e) {
+ if (e.type === 'keydown' && e.code !== 'Space' && e.key !== ' ') return;
+ if (e.type === 'keydown') e.preventDefault();
+ if (!running && !gameOver && startOverlay.classList.contains('is-open')) {
+ resetGame();
+ return;
+ }
+ if (gameOver && overOverlay.classList.contains('is-open')) {
+ resetGame();
+ return;
+ }
+ jump();
+ }
+
+ window.addEventListener('keydown', onAction);
+ canvas.addEventListener('pointerdown', onAction);
+ startBtn.addEventListener('click', () => resetGame());
+ restartBtn.addEventListener('click', () => resetGame());
+
+ // Entities
+ function spawnObstacle() {
+ const kinds = ['tin', 'scaffold', 'ladder', 'bucket'];
+ const kind = kinds[(Math.random() * kinds.length) | 0];
+ let w = 36, h = 44;
+ if (kind === 'scaffold') { w = 70; h = 56; }
+ if (kind === 'ladder') { w = 28; h = 78; }
+ if (kind === 'bucket') { w = 40; h = 34; }
+ obstacles.push({
+ x: W + 40,
+ y: GROUND_Y - h,
+ w, h, kind,
+ passed: false,
+ });
+ }
+
+ function spawnCollectible() {
+ const style = (Math.random() * STYLES.length) | 0;
+ const air = Math.random() > 0.45;
+ const y = air ? GROUND_Y - 110 - Math.random() * 70 : GROUND_Y - 38;
+ collectibles.push({
+ x: W + 30,
+ y,
+ r: 18,
+ style,
+ rot: Math.random() * Math.PI * 2,
+ bob: Math.random() * Math.PI * 2,
+ taken: false,
+ });
+ }
+
+ function rectsOverlap(ax, ay, aw, ah, bx, by, bw, bh) {
+ return ax < bx + bw && ax + aw > bx && ay < by + bh && ay + ah > by;
+ }
+
+ function update(dt) {
+ if (!running) {
+ // still animate particles on game over
+ particles.forEach(p => {
+ p.x += p.vx; p.y += p.vy; p.vy += 0.15; p.life--;
+ });
+ particles = particles.filter(p => p.life > 0);
+ return;
+ }
+
+ frame++;
+ distance += speed;
+ wallOffset = (wallOffset + speed * 0.35) % 80;
+
+ // speed ramp
+ speed = Math.min(maxSpeed, 5.2 + distance / 2800);
+
+ // roller physics
+ roller.vy += GRAVITY;
+ roller.y += roller.vy;
+ if (roller.y >= GROUND_Y) {
+ roller.y = GROUND_Y;
+ roller.vy = 0;
+ roller.onGround = true;
+ }
+ roller.rot += speed * 0.08;
+
+ // paint trail
+ if (roller.onGround && frame % 2 === 0) {
+ paintTrail.push({ x: roller.x - 10, y: GROUND_Y + 4, life: 50, style: (score / 5) % STYLES.length | 0 });
+ }
+ paintTrail.forEach(t => { t.x -= speed; t.life--; });
+ paintTrail = paintTrail.filter(t => t.life > 0 && t.x > -20);
+
+ // spawn
+ spawnTimer--;
+ if (spawnTimer <= 0) {
+ spawnObstacle();
+ spawnTimer = 55 + Math.random() * 55 - Math.min(25, speed * 2);
+ }
+ collectTimer--;
+ if (collectTimer <= 0) {
+ spawnCollectible();
+ collectTimer = 35 + Math.random() * 50;
+ }
+
+ // obstacles
+ for (const o of obstacles) {
+ o.x -= speed;
+ if (!o.passed && o.x + o.w < roller.x - roller.r) {
+ o.passed = true;
+ score += 1;
+ scoreEl.textContent = String(score);
+ }
+ // hitbox slightly forgiving
+ const pad = 8;
+ const rx = roller.x - roller.r + pad;
+ const ry = roller.y - roller.r * 2 + pad;
+ const rw = roller.r * 2 - pad * 2;
+ const rh = roller.r * 2 - pad;
+ if (rectsOverlap(rx, ry, rw, rh, o.x + 4, o.y + 4, o.w - 8, o.h - 6)) {
+ endGame();
+ return;
+ }
+ }
+ obstacles = obstacles.filter(o => o.x > -120);
+
+ // collectibles
+ for (const c of collectibles) {
+ c.x -= speed;
+ c.rot += 0.04;
+ c.bob += 0.08;
+ const cy = c.y + Math.sin(c.bob) * 5;
+ const dx = c.x - roller.x;
+ const dy = cy - (roller.y - roller.r);
+ if (!c.taken && dx * dx + dy * dy < (c.r + roller.r - 4) ** 2) {
+ c.taken = true;
+ score += 5;
+ scoreEl.textContent = String(score);
+ // sparkle
+ for (let i = 0; i < 10; i++) {
+ const a = Math.random() * Math.PI * 2;
+ const sp = 1.5 + Math.random() * 3.5;
+ particles.push({
+ x: c.x, y: cy,
+ vx: Math.cos(a) * sp, vy: Math.sin(a) * sp - 1,
+ life: 25 + Math.random() * 15,
+ max: 40,
+ color: C.accent,
+ size: 1.5 + Math.random() * 2.5,
+ });
+ }
+ }
+ }
+ collectibles = collectibles.filter(c => !c.taken && c.x > -60);
+
+ particles.forEach(p => {
+ p.x += p.vx; p.y += p.vy; p.vy += 0.12; p.life--;
+ });
+ particles = particles.filter(p => p.life > 0);
+ }
+
+ // Drawing helpers
+ function drawBackground() {
+ // deep wall field
+ const grad = ctx.createLinearGradient(0, 0, 0, H);
+ grad.addColorStop(0, '#14131a');
+ grad.addColorStop(0.55, '#1a1816');
+ grad.addColorStop(1, '#121114');
+ ctx.fillStyle = grad;
+ ctx.fillRect(0, 0, W, H);
+
+ // subtle vertical panel lines on wall
+ ctx.save();
+ ctx.strokeStyle = 'rgba(201,169,97,0.06)';
+ ctx.lineWidth = 1;
+ const panelW = 80;
+ const off = -wallOffset % panelW;
+ for (let x = off; x < W + panelW; x += panelW) {
+ ctx.beginPath();
+ ctx.moveTo(x, 0);
+ ctx.lineTo(x, GROUND_Y + 10);
+ ctx.stroke();
+ }
+ // chair rail
+ ctx.strokeStyle = 'rgba(201,169,97,0.18)';
+ ctx.beginPath();
+ ctx.moveTo(0, GROUND_Y - 120);
+ ctx.lineTo(W, GROUND_Y - 120);
+ ctx.stroke();
+ // wainscot fill
+ ctx.fillStyle = 'rgba(0,0,0,0.18)';
+ ctx.fillRect(0, GROUND_Y - 120, W, 120);
+ ctx.restore();
+
+ // floor
+ ctx.fillStyle = C.ground;
+ ctx.fillRect(0, GROUND_Y + 8, W, H - GROUND_Y);
+ // floor line
+ ctx.strokeStyle = C.line;
+ ctx.lineWidth = 1;
+ ctx.beginPath();
+ ctx.moveTo(0, GROUND_Y + 8);
+ ctx.lineTo(W, GROUND_Y + 8);
+ ctx.stroke();
+
+ // floor planks
+ ctx.strokeStyle = 'rgba(245,242,234,0.04)';
+ const plankOff = (-distance * 0.5) % 60;
+ for (let x = plankOff; x < W; x += 60) {
+ ctx.beginPath();
+ ctx.moveTo(x, GROUND_Y + 8);
+ ctx.lineTo(x - 20, H);
+ ctx.stroke();
+ }
+
+ // soft vignette
+ const vig = ctx.createRadialGradient(W * 0.5, H * 0.4, H * 0.2, W * 0.5, H * 0.5, H * 0.85);
+ vig.addColorStop(0, 'rgba(0,0,0,0)');
+ vig.addColorStop(1, 'rgba(0,0,0,0.35)');
+ ctx.fillStyle = vig;
+ ctx.fillRect(0, 0, W, H);
+ }
+
+ function drawPaintTrail() {
+ for (const t of paintTrail) {
+ const alpha = Math.max(0, t.life / 50) * 0.55;
+ ctx.save();
+ ctx.globalAlpha = alpha;
+ const pc = patternCanvases[t.style % patternCanvases.length];
+ ctx.drawImage(pc, t.x - 14, t.y - 22, 28, 28);
+ // gold edge
+ ctx.strokeStyle = C.accent;
+ ctx.lineWidth = 1;
+ ctx.globalAlpha = alpha * 0.7;
+ ctx.strokeRect(t.x - 14, t.y - 22, 28, 28);
+ ctx.restore();
+ }
+ }
+
+ function drawRoller() {
+ const x = roller.x;
+ const y = roller.y;
+ const r = roller.r;
+
+ // shadow
+ ctx.save();
+ ctx.fillStyle = 'rgba(0,0,0,0.35)';
+ ctx.beginPath();
+ ctx.ellipse(x, GROUND_Y + 10, r * 0.9, 5, 0, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.restore();
+
+ // handle pole
+ ctx.save();
+ ctx.translate(x, y - r);
+ ctx.strokeStyle = '#3a3834';
+ ctx.lineWidth = 4;
+ ctx.lineCap = 'round';
+ ctx.beginPath();
+ ctx.moveTo(-2, -r * 0.2);
+ ctx.lineTo(-18, -r * 1.55);
+ ctx.stroke();
+ // grip
+ ctx.strokeStyle = C.accent2;
+ ctx.lineWidth = 6;
+ ctx.beginPath();
+ ctx.moveTo(-18, -r * 1.55);
+ ctx.lineTo(-22, -r * 1.95);
+ ctx.stroke();
+ // gold ferrule
+ ctx.fillStyle = C.accent;
+ ctx.fillRect(-8, -r * 0.35, 12, 5);
+ ctx.restore();
+
+ // roller cylinder
+ ctx.save();
+ ctx.translate(x, y - r);
+ ctx.rotate(roller.rot);
+
+ // body
+ const bodyGrad = ctx.createLinearGradient(-r, 0, r, 0);
+ bodyGrad.addColorStop(0, '#3d3a36');
+ bodyGrad.addColorStop(0.3, '#8a8578');
+ bodyGrad.addColorStop(0.5, '#c9c2b0');
+ bodyGrad.addColorStop(0.7, '#8a8578');
+ bodyGrad.addColorStop(1, '#3d3a36');
+ ctx.fillStyle = bodyGrad;
+ ctx.beginPath();
+ ctx.ellipse(0, 0, r, r * 0.72, 0, 0, Math.PI * 2);
+ ctx.fill();
+
+ // nap stripes (rotation feedback)
+ ctx.strokeStyle = 'rgba(28,26,23,0.25)';
+ ctx.lineWidth = 2;
+ for (let i = -3; i <= 3; i++) {
+ ctx.beginPath();
+ ctx.ellipse(0, 0, r * 0.92, r * 0.65, 0, i * 0.4, i * 0.4 + 0.15);
+ ctx.stroke();
+ }
+
+ // wet paint sheen — gold
+ ctx.strokeStyle = 'rgba(201,169,97,0.55)';
+ ctx.lineWidth = 3;
+ ctx.beginPath();
+ ctx.ellipse(0, 0, r * 0.7, r * 0.5, 0.2, -0.8, 0.6);
+ ctx.stroke();
+
+ // end caps
+ ctx.fillStyle = C.accent;
+ ctx.beginPath();
+ ctx.arc(-r + 2, 0, 4, 0, Math.PI * 2);
+ ctx.arc(r - 2, 0, 4, 0, Math.PI * 2);
+ ctx.fill();
+
+ ctx.restore();
+
+ // outline
+ ctx.save();
+ ctx.translate(x, y - r);
+ ctx.strokeStyle = 'rgba(201,169,97,0.35)';
+ ctx.lineWidth = 1;
+ ctx.beginPath();
+ ctx.ellipse(0, 0, r, r * 0.72, 0, 0, Math.PI * 2);
+ ctx.stroke();
+ ctx.restore();
+ }
+
+ function drawObstacle(o) {
+ ctx.save();
+ const x = o.x, y = o.y, w = o.w, h = o.h;
+
+ // soft shadow
+ ctx.fillStyle = 'rgba(0,0,0,0.3)';
+ ctx.beginPath();
+ ctx.ellipse(x + w / 2, GROUND_Y + 10, w * 0.45, 4, 0, 0, Math.PI * 2);
+ ctx.fill();
+
+ if (o.kind === 'tin') {
+ // paint tin
+ const g = ctx.createLinearGradient(x, y, x + w, y);
+ g.addColorStop(0, '#2a2824');
+ g.addColorStop(0.5, '#4a463e');
+ g.addColorStop(1, '#2a2824');
+ ctx.fillStyle = g;
+ roundRect(x, y + 8, w, h - 8, 3);
+ ctx.fill();
+ // lid
+ ctx.fillStyle = C.accent;
+ ctx.fillRect(x - 2, y + 4, w + 4, 8);
+ // label band
+ ctx.fillStyle = STYLES[1].base;
+ ctx.fillRect(x + 4, y + h * 0.4, w - 8, h * 0.28);
+ ctx.strokeStyle = C.line;
+ ctx.lineWidth = 1;
+ ctx.strokeRect(x + 4, y + h * 0.4, w - 8, h * 0.28);
+ // handle wire
+ ctx.strokeStyle = C.muted;
+ ctx.lineWidth = 1.5;
+ ctx.beginPath();
+ ctx.arc(x + w / 2, y + 12, w * 0.35, Math.PI, 0);
+ ctx.stroke();
+ } else if (o.kind === 'scaffold') {
+ ctx.strokeStyle = C.muted;
+ ctx.lineWidth = 3;
+ ctx.strokeRect(x + 4, y + 4, w - 8, h - 4);
+ ctx.beginPath();
+ ctx.moveTo(x + 4, y + h * 0.5);
+ ctx.lineTo(x + w - 4, y + h * 0.5);
+ ctx.moveTo(x + w * 0.35, y + 4);
+ ctx.lineTo(x + w * 0.35, y + h);
+ ctx.moveTo(x + w * 0.65, y + 4);
+ ctx.lineTo(x + w * 0.65, y + h);
+ ctx.stroke();
+ // planks
+ ctx.fillStyle = '#3a342c';
+ ctx.fillRect(x, y, w, 8);
+ ctx.fillRect(x, y + h * 0.5 - 3, w, 6);
+ ctx.fillStyle = C.accent2;
+ ctx.fillRect(x, y, w, 2);
+ } else if (o.kind === 'ladder') {
+ ctx.strokeStyle = '#6a5a40';
+ ctx.lineWidth = 4;
+ ctx.beginPath();
+ ctx.moveTo(x + 4, y + h);
+ ctx.lineTo(x + 4, y);
+ ctx.moveTo(x + w - 4, y + h);
+ ctx.lineTo(x + w - 4, y);
+ ctx.stroke();
+ ctx.lineWidth = 3;
+ for (let i = 0; i < 5; i++) {
+ const ly = y + 10 + i * ((h - 20) / 4);
+ ctx.beginPath();
+ ctx.moveTo(x + 4, ly);
+ ctx.lineTo(x + w - 4, ly);
+ ctx.stroke();
+ }
+ ctx.strokeStyle = C.accent;
+ ctx.lineWidth = 1;
+ ctx.strokeRect(x + 1, y - 1, w - 2, h + 2);
+ } else {
+ // bucket
+ ctx.fillStyle = '#3a3e48';
+ ctx.beginPath();
+ ctx.moveTo(x + 4, y + 6);
+ ctx.lineTo(x, y + h);
+ ctx.lineTo(x + w, y + h);
+ ctx.lineTo(x + w - 4, y + 6);
+ ctx.closePath();
+ ctx.fill();
+ ctx.fillStyle = C.accent;
+ ctx.fillRect(x + 2, y, w - 4, 8);
+ ctx.strokeStyle = C.muted;
+ ctx.lineWidth = 1.5;
+ ctx.beginPath();
+ ctx.arc(x + w / 2, y + 10, w * 0.4, Math.PI * 1.05, Math.PI * -0.05);
+ ctx.stroke();
+ // spill highlight
+ ctx.fillStyle = 'rgba(201,169,97,0.25)';
+ ctx.fillRect(x + 8, y + h * 0.45, w - 16, 6);
+ }
+
+ ctx.restore();
+ }
+
+ function drawCollectible(c) {
+ const bob = Math.sin(c.bob) * 5;
+ const x = c.x;
+ const y = c.y + bob;
+ const r = c.r;
+
+ ctx.save();
+ // glow
+ const glow = ctx.createRadialGradient(x, y, 2, x, y, r * 1.8);
+ glow.addColorStop(0, 'rgba(201,169,97,0.35)');
+ glow.addColorStop(1, 'rgba(201,169,97,0)');
+ ctx.fillStyle = glow;
+ ctx.beginPath();
+ ctx.arc(x, y, r * 1.8, 0, Math.PI * 2);
+ ctx.fill();
+
+ // roll body
+ ctx.translate(x, y);
+ ctx.rotate(c.rot * 0.3);
+
+ // outer ring (tube)
+ ctx.fillStyle = '#2a2824';
+ ctx.beginPath();
+ ctx.arc(0, 0, r, 0, Math.PI * 2);
+ ctx.fill();
+
+ // pattern face
+ ctx.save();
+ ctx.beginPath();
+ ctx.arc(0, 0, r - 3, 0, Math.PI * 2);
+ ctx.clip();
+ const pc = patternCanvases[c.style];
+ ctx.drawImage(pc, -r, -r, r * 2, r * 2);
+ ctx.restore();
+
+ // gold rim
+ ctx.strokeStyle = C.accent;
+ ctx.lineWidth = 2;
+ ctx.beginPath();
+ ctx.arc(0, 0, r - 1, 0, Math.PI * 2);
+ ctx.stroke();
+
+ // core hole
+ ctx.fillStyle = C.bg;
+ ctx.beginPath();
+ ctx.arc(0, 0, 4, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.strokeStyle = C.accent2;
+ ctx.lineWidth = 1;
+ ctx.stroke();
+
+ // highlight
+ ctx.strokeStyle = 'rgba(245,242,234,0.35)';
+ ctx.lineWidth = 2;
+ ctx.beginPath();
+ ctx.arc(0, 0, r - 5, -1.2, -0.3);
+ ctx.stroke();
+
+ ctx.restore();
+ }
+
+ function drawParticles() {
+ for (const p of particles) {
+ ctx.globalAlpha = Math.max(0, p.life / p.max);
+ ctx.fillStyle = p.color;
+ ctx.beginPath();
+ ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ ctx.globalAlpha = 1;
+ }
+
+ function roundRect(x, y, w, h, r) {
+ ctx.beginPath();
+ ctx.moveTo(x + r, y);
+ ctx.arcTo(x + w, y, x + w, y + h, r);
+ ctx.arcTo(x + w, y + h, x, y + h, r);
+ ctx.arcTo(x, y + h, x, y, r);
+ ctx.arcTo(x, y, x + w, y, r);
+ ctx.closePath();
+ }
+
+ function drawChrome() {
+ // thin gold frame inset
+ ctx.strokeStyle = 'rgba(201,169,97,0.2)';
+ ctx.lineWidth = 1;
+ ctx.strokeRect(8, 8, W - 16, H - 16);
+
+ // speed ticks (subtle)
+ if (running) {
+ ctx.fillStyle = 'rgba(201,169,97,0.08)';
+ for (let i = 0; i < 5; i++) {
+ const tx = (frame * speed * 0.4 + i * 90) % (W + 40) - 20;
+ ctx.fillRect(tx, 24, 30 + speed * 2, 1);
+ }
+ }
+ }
+
+ function render() {
+ drawBackground();
+ drawPaintTrail();
+ collectibles.forEach(drawCollectible);
+ obstacles.forEach(drawObstacle);
+ drawRoller();
+ drawParticles();
+ drawChrome();
+ }
+
+ // Main loop — locked ~60fps
+ let last = performance.now();
+ function loop(now) {
+ const dt = Math.min(32, now - last);
+ last = now;
+ // fixed-ish step
+ update(dt / 16.67);
+ render();
+ requestAnimationFrame(loop);
+ }
+
+ // Idle preview on start screen
+ function idlePreview() {
+ roller.rot += 0.03;
+ wallOffset = (wallOffset + 0.4) % 80;
+ if (!running && collectibles.length === 0) {
+ // a few floating samples
+ for (let i = 0; i < 4; i++) {
+ collectibles.push({
+ x: 280 + i * 180,
+ y: GROUND_Y - 80 - (i % 2) * 40,
+ r: 18,
+ style: i % STYLES.length,
+ rot: i,
+ bob: i * 1.2,
+ taken: false,
+ });
+ }
+ }
+ if (!running) {
+ collectibles.forEach(c => { c.bob += 0.05; c.rot += 0.02; });
+ }
+ }
+
+ // Kick
+ render();
+ requestAnimationFrame(function boot(now) {
+ last = now;
+ requestAnimationFrame(function tick(t) {
+ if (!running && !gameOver) idlePreview();
+ loop(t);
+ });
+ });
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/83e28cd01c99/grok.png b/data/artifacts/83e28cd01c99/grok.png
new file mode 100644
index 0000000..9235329
Binary files /dev/null and b/data/artifacts/83e28cd01c99/grok.png differ
diff --git a/data/artifacts/83e28cd01c99/hermes3-8b.html b/data/artifacts/83e28cd01c99/hermes3-8b.html
new file mode 100644
index 0000000..dc8c5f8
--- /dev/null
+++ b/data/artifacts/83e28cd01c99/hermes3-8b.html
@@ -0,0 +1,282 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Designer Wallcoverings Runner</title>
+ <style>
+ :root {
+ --bg: #faf8f4;
+ --surface: #ffffff;
+ --text: #1c1a17;
+ --muted: #9b948a;
+ --accent: #b0472e / #2f4c39;
+ --line: 1px solid var(--text);
+ --shadow: 0 8px 24px rgba(0, 0, 0, .12);
+ --ease: cubic-bezier(.22, 1, .36, 1);
+ }
+
+ body {
+ margin: 0;
+ padding: 96px;
+ background-color: var(--bg);
+ font-family: -apple-system, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
+ font-size: 16px;
+ }
+
+ header {
+ display: flex;
+ align-items: center;
+ margin-bottom: 32px;
+ }
+
+ .wordmark {
+ font-family: 'Didot', 'Bodoni MT', 'Playfair Display', Georgia, serif;
+ font-size: 50px;
+ color: var(--text);
+ letter-spacing: .18em;
+ }
+
+ h1 {
+ margin-left: 16px;
+ font-size: 38px;
+ color: var(--muted);
+ }
+
+ canvas {
+ display: block;
+ background-color: var(--surface);
+ }
+
+ .score {
+ position: fixed;
+ top: 24px;
+ right: 24px;
+ font-family: -apple-system, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
+ font-size: 20px;
+ color: var(--muted);
+ letter-spacing: .15em;
+ }
+
+ .game-over {
+ position: fixed;
+ top: 50%;
+ left: 50%;
+ transform: translate(-50%, -50%);
+ text-align: center;
+ font-size: 24px;
+ color: var(--text);
+ }
+
+ .restart-btn {
+ margin-top: 32px;
+ padding: 8px 16px;
+ background-color: var(--accent);
+ border-radius: 14px;
+ text-decoration: none;
+ color: var(--surface);
+ font-family: -apple-system, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
+ font-size: 20px;
+ letter-spacing: .15em;
+ }
+
+ .scoreboard {
+ margin-top: 32px;
+ padding: 24px;
+ background-color: var(--surface);
+ border-radius: 14px;
+ box-shadow: var(--shadow);
+ display: flex;
+ gap: 16px;
+ }
+
+ .high-score {
+ flex-grow: 1;
+ font-size: 28px;
+ color: var(--muted);
+ }
+
+ .restart-btn:hover,
+ .scoreboard {
+ background-color: var(--accent);
+ color: var(--surface);
+ }
+ </style>
+</head>
+<body>
+ <header>
+ <div class="wordmark">DESIGNER WALLCOVERINGS</div>
+ <h1>Endless Roller</h1>
+ </header>
+
+ <canvas id="game" width="480" height="320"></canvas>
+
+ <div class="score">Score: <span id="score">0</span></div>
+
+ <div class="game-over" style="display:none;">Game Over!</div>
+ <a href="#" class="restart-btn" style="display:none;">Play Again</a>
+
+ <div class="scoreboard" style="display:none;">
+ <h3>High Scores:</h3>
+ <span class="high-score" id="bestScore">0</span>
+ </div>
+
+ <script>
+ const canvas = document.getElementById('game');
+ const ctx = canvas.getContext('2d');
+
+ let score = 0;
+ let bestScore = localStorage.getItem('bestDesignerWallcoveringsScore') || 0;
+
+ const player = {
+ x: 0,
+ y: canvas.height - 100,
+ width: 50,
+ height: 20,
+ color: '#333',
+ speed: 5
+ };
+
+ const wallpaperRolls = [
+ {x: 400, y: 50, width: 80, height: 60, style: 'striped', scoreValue: 10},
+ {x: 600, y: 200, width: 100, height: 80, style: 'floral', scoreValue: 20},
+ {x: 800, y: 150, width: 70, height: 90, style: 'solid', scoreValue: 30}
+ ];
+
+ const obstacles = [
+ {x: 200, y: canvas.height - 100, width: 80, height: 20},
+ {x: 500, y: canvas.height / 2, width: 120, height: 40},
+ {x: 700, y: canvas.height * .7, width: 90, height: 30}
+ ];
+
+ const styles = [
+ {bg: '#f9d6be', fg: '#e8bc38'},
+ {bg: '#c1bdb0', fg: '#aebd97'},
+ {bg: '#80d3fa', fg: '#5b86e3'}
+ ];
+
+ function draw() {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ for (let i = 0; i < wallpaperRolls.length; i++) {
+ const roll = wallpaperRolls[i];
+ const styleIndex = i % styles.length;
+ const style = styles[styleIndex];
+
+ ctx.fillStyle = `rgb(${style.bg}, ${style.fg}), radial-gradient(circle at ${roll.x + roll.width / 2} ${roll.y + roll.height / 2}, white, transparent)`;
+ ctx.fillRect(roll.x, roll.y, roll.width, roll.height);
+ }
+
+ for (let i = 0; i < obstacles.length; i++) {
+ const obstacle = obstacles[i];
+ ctx.fillStyle = 'red';
+ ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
+ }
+
+ drawPlayer();
+ }
+
+ function drawPlayer() {
+ ctx.fillStyle = player.color;
+ ctx.fillRect(player.x, player.y, player.width, player.height);
+ }
+
+ function update() {
+ if (player.x > canvas.width) {
+ player.x -= canvas.width;
+ }
+
+ for (let i = 0; i < wallpaperRolls.length; i++) {
+ const roll = wallpaperRolls[i];
+
+ if (
+ player.x + player.width > roll.x &&
+ player.x < roll.x + roll.width &&
+ player.y + player.height > roll.y &&
+ player.y < roll.y + roll.height
+ ) {
+ score += roll.scoreValue;
+ roll.style && drawWallpaperRoll(roll, true);
+ }
+ }
+
+ for (let i = 0; i < obstacles.length; i++) {
+ const obstacle = obstacles[i];
+
+ if (
+ player.x + player.width > obstacle.x &&
+ player.x < obstacle.x + obstacle.width &&
+ player.y + player.height > obstacle.y &&
+ player.y < obstacle.y + obstacle.height
+ ) {
+ game_over();
+ }
+ }
+
+ player.x += player.speed;
+ }
+
+ function drawWallpaperRoll({x, y, width, height, style}, isCollected) {
+ const styles = [
+ {bg: '#f9d6be', fg: '#e8bc38'},
+ {bg: '#c1bdb0', fg: '#aebd97'},
+ {bg: '#80d3fa', fg: '#5b86e3'}
+ ];
+
+ const styleIndex = isCollected ? 0 : Math.floor(Math.random() * styles.length);
+ const style = styles[styleIndex];
+
+ ctx.fillStyle = `rgb(${style.bg}, ${style.fg}), radial-gradient(circle at ${x + width / 2} ${y + height / 2}, white, transparent)`;
+ ctx.fillRect(x, y, width, height);
+ }
+
+ function jump() {
+ player.y -= player.height;
+ }
+
+ function game_over() {
+ ctx.font = '48px Arial';
+ ctx.fillStyle = '#fff';
+ ctx.textAlign = 'center';
+ ctx.fillText('Game Over!', canvas.width / 2, canvas.height / 2);
+
+ document.querySelector('.game-over').style.display = 'block';
+ document.querySelector('.restart-btn').style.display = 'block';
+
+ document.querySelector('.restart-btn').addEventListener('click', () => {
+ location.reload();
+ });
+ }
+
+ function saveScore() {
+ bestScore = Math.max(score, bestScore);
+ localStorage.setItem('bestDesignerWallcoveringsScore', bestScore);
+
+ document.querySelector('.scoreboard').style.display = 'block';
+ document.querySelector('.high-score').textContent = bestScore;
+ }
+
+ document.addEventListener('keydown', (e) => {
+ if (e.code === 'Space' || e.key === ' ') {
+ jump();
+ }
+ });
+
+ function gameLoop() {
+ update();
+ draw();
+
+ scoreElement.textContent = score;
+
+ requestAnimationFrame(gameLoop);
+ }
+
+ const scoreElement = document.getElementById('score');
+
+ saveScore();
+
+ gameLoop();
+
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/83e28cd01c99/hermes3-8b.png b/data/artifacts/83e28cd01c99/hermes3-8b.png
new file mode 100644
index 0000000..ddaa1b4
Binary files /dev/null and b/data/artifacts/83e28cd01c99/hermes3-8b.png differ
diff --git a/data/artifacts/83e28cd01c99/hf-qwen-coder-32b.html b/data/artifacts/83e28cd01c99/hf-qwen-coder-32b.html
new file mode 100644
index 0000000..cbdc1b0
--- /dev/null
+++ b/data/artifacts/83e28cd01c99/hf-qwen-coder-32b.html
@@ -0,0 +1,318 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Designer Wallcoverings</title>
+ <style>
+ :root {
+ --bg: #faf8f4;
+ --surface: #ffffff;
+ --text: #1c1a17;
+ --muted: #9b948a;
+ --accent1: #b0472e;
+ --accent2: #2f4c39;
+ --line: 1px solid var(--muted);
+ --shadow: 0 8px 24px rgba(0,0,0,.12);
+ --ease: cubic-bezier(.22,1,.36,1);
+ }
+ body {
+ margin: 0;
+ font-family: -apple-system, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
+ background-color: var(--bg);
+ color: var(--text);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ overflow: hidden;
+ }
+ header {
+ position: fixed;
+ top: 0;
+ left: 0;
+ background-color: var(--surface);
+ padding: 24px;
+ box-shadow: var(--shadow);
+ z-index: 1000;
+ width: 100%;
+ text-align: left;
+ }
+ header h1 {
+ font-family: 'Playfair Display', Georgia, serif;
+ font-size: 67px;
+ letter-spacing: .18em;
+ margin: 0;
+ color: var(--text);
+ }
+ #scoreboard {
+ position: fixed;
+ top: 24px;
+ right: 32px;
+ font-family: 'Playfair Display', Georgia, serif;
+ font-size: 28px;
+ letter-spacing: .15em;
+ color: var(--muted);
+ }
+ canvas {
+ display: block;
+ background-color: var(--surface);
+ box-shadow: var(--shadow);
+ }
+ #overlay {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background-color: rgba(255,255,255,.9);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ font-family: 'Playfair Display', Georgia, serif;
+ color: var(--text);
+ letter-spacing: .15em;
+ pointer-events: none;
+ opacity: 0;
+ transition: opacity .7s var(--ease);
+ }
+ #overlay.active {
+ pointer-events: auto;
+ opacity: 1;
+ }
+ #overlay h2 {
+ font-size: 48px;
+ margin-bottom: 32px;
+ }
+ #overlay button {
+ background-color: var(--accent1);
+ color: var(--surface);
+ border: none;
+ padding: 16px 32px;
+ font-family: 'Playfair Display', Georgia, serif;
+ font-size: 24px;
+ letter-spacing: .15em;
+ cursor: pointer;
+ transition: background-color .15s ease-in-out;
+ }
+ #overlay button:hover {
+ background-color: var(--accent2);
+ }
+ </style>
+</head>
+<body>
+ <header>
+ <h1>DESIGNER WALLCOVERINGS</h1>
+ </header>
+ <div id="scoreboard">Score: 0 Best: 0</div>
+ <canvas id="gameCanvas"></canvas>
+ <div id="overlay">
+ <h2>GAME OVER</h2>
+ <button onclick="restartGame()">RESTART</button>
+ </div>
+ <script>
+ const canvas = document.getElementById('gameCanvas');
+ const ctx = canvas.getContext('2d');
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+
+ let score = 0;
+ let bestScore = localStorage.getItem('bestScore') || 0;
+ const scoreboard = document.getElementById('scoreboard');
+
+ class Player {
+ constructor() {
+ this.x = 100;
+ this.y = canvas.height - 50;
+ this.width = 32;
+ this.height = 48;
+ this.gravity = 1.5;
+ this.lift = -30;
+ this.velocity = 0;
+ this.color = '#b0472e';
+ }
+
+ jump() {
+ if (this.y === canvas.height - this.height) {
+ this.velocity += this.lift;
+ }
+ }
+
+ update() {
+ this.velocity += this.gravity;
+ this.y += this.velocity;
+
+ if (this.y + this.height > canvas.height) {
+ this.y = canvas.height - this.height;
+ this.velocity = 0;
+ }
+ }
+
+ draw() {
+ ctx.fillStyle = this.color;
+ ctx.fillRect(this.x, this.y, this.width, this.height);
+ }
+ }
+
+ class Obstacle {
+ constructor() {
+ this.x = canvas.width;
+ this.y = canvas.height - 50;
+ this.width = 48;
+ this.height = Math.random() * (canvas.height / 2) + 50;
+ this.color = '#2f4c39';
+ }
+
+ update(speed) {
+ this.x -= speed;
+ }
+
+ draw() {
+ ctx.fillStyle = this.color;
+ ctx.fillRect(this.x, this.y - this.height, this.width, this.height);
+ }
+ }
+
+ class Collectible {
+ constructor() {
+ this.x = canvas.width + Math.random() * 300;
+ this.y = canvas.height - 75;
+ this.radius = 16;
+ this.colors = ['#c9a961', '#7d6a45', '#b0472e', '#2f4c39'];
+ this.color = this.colors[Math.floor(Math.random() * this.colors.length)];
+ }
+
+ update(speed) {
+ this.x -= speed;
+ }
+
+ draw() {
+ ctx.fillStyle = this.color;
+ ctx.beginPath();
+ ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ }
+
+ const player = new Player();
+ let obstacles = [];
+ let collectibles = [];
+ let speed = 3;
+ let gameRunning = true;
+
+ function spawnObstacle() {
+ if (Math.random() < 0.02) {
+ obstacles.push(new Obstacle());
+ }
+ }
+
+ function spawnCollectible() {
+ if (Math.random() < 0.05) {
+ collectibles.push(new Collectible());
+ }
+ }
+
+ function checkCollision(player, obstacle) {
+ return player.x < obstacle.x + obstacle.width &&
+ player.x + player.width > obstacle.x &&
+ player.y < obstacle.y - obstacle.height &&
+ player.y + player.height > obstacle.y - obstacle.height;
+ }
+
+ function collectItem(player, collectible) {
+ const distance = Math.sqrt(
+ Math.pow(player.x + player.width / 2 - collectible.x, 2) +
+ Math.pow(player.y + player.height / 2 - collectible.y, 2)
+ );
+ return distance < player.width / 2 + collectible.radius;
+ }
+
+ function updateScore() {
+ score++;
+ scoreboard.textContent = `Score: ${score} Best: ${bestScore}`;
+ if (score > bestScore) {
+ bestScore = score;
+ localStorage.setItem('bestScore', bestScore);
+ }
+ }
+
+ function drawBackground() {
+ ctx.fillStyle = '#faf8f4';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+ }
+
+ function gameLoop() {
+ if (!gameRunning) return;
+
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ drawBackground();
+
+ player.update();
+ player.draw();
+
+ spawnObstacle();
+ spawnCollectible();
+
+ for (let i = obstacles.length - 1; i >= 0; i--) {
+ obstacles[i].update(speed);
+ obstacles[i].draw();
+ if (checkCollision(player, obstacles[i])) {
+ gameRunning = false;
+ showOverlay();
+ }
+ if (obstacles[i].x + obstacles[i].width < 0) {
+ obstacles.splice(i, 1);
+ }
+ }
+
+ for (let i = collectibles.length - 1; i >= 0; i--) {
+ collectibles[i].update(speed);
+ collectibles[i].draw();
+ if (collectItem(player, collectibles[i])) {
+ updateScore();
+ collectibles.splice(i, 1);
+ }
+ if (collectibles[i].x + collectibles[i].radius < 0) {
+ collectibles.splice(i, 1);
+ }
+ }
+
+ speed += 0.002;
+
+ requestAnimationFrame(gameLoop);
+ }
+
+ function showOverlay() {
+ const overlay = document.getElementById('overlay');
+ overlay.classList.add('active');
+ }
+
+ function restartGame() {
+ score = 0;
+ obstacles = [];
+ collectibles = [];
+ player.y = canvas.height - 50;
+ player.velocity = 0;
+ speed = 3;
+ gameRunning = true;
+ const overlay = document.getElementById('overlay');
+ overlay.classList.remove('active');
+ requestAnimationFrame(gameLoop);
+ }
+
+ window.addEventListener('keydown', (e) => {
+ if (e.code === 'Space' || e.code === 'Enter') {
+ player.jump();
+ }
+ });
+
+ canvas.addEventListener('click', () => {
+ player.jump();
+ });
+
+ gameLoop();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/83e28cd01c99/hf-qwen-coder-32b.png b/data/artifacts/83e28cd01c99/hf-qwen-coder-32b.png
new file mode 100644
index 0000000..53d68df
Binary files /dev/null and b/data/artifacts/83e28cd01c99/hf-qwen-coder-32b.png differ
diff --git a/data/artifacts/83e28cd01c99/kimi.html b/data/artifacts/83e28cd01c99/kimi.html
new file mode 100644
index 0000000..fe25c68
--- /dev/null
+++ b/data/artifacts/83e28cd01c99/kimi.html
@@ -0,0 +1,807 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Designer Wallcoverings - The Collection Runner</title>
+<style>
+:root {
+ /* Luxury Design Tokens - Warm Ivory Ground */
+ --ground: #f5f2ea;
+ --surface: #ffffff;
+ --ink: #1c1a17;
+ --muted: #8a8578;
+ --accent: #c9a961;
+ --accent-warm: #7d6a45;
+ --line: rgba(28, 26, 23, 0.08);
+ --shadow: rgba(28, 26, 23, 0.06);
+ --shadow-elevated: rgba(28, 26, 23, 0.12);
+
+ /* Typography */
+ --font-display: 'Playfair Display', 'Didot', 'Bodoni MT', Georgia, serif;
+ --font-sans: -apple-system, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif;
+
+ /* Motion */
+ --ease-swift: cubic-bezier(.22,.9,.24,1);
+ --ease-overshoot: cubic-bezier(.34,1.56,.64,1);
+
+ /* Scale */
+ --wordmark: 42px;
+ --label: 11px;
+ --score: 14px;
+}
+
+* {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+body {
+ font-family: var(--font-sans);
+ background: var(--ground);
+ color: var(--ink);
+ overflow: hidden;
+ touch-action: none;
+ user-select: none;
+ -webkit-user-select: none;
+}
+
+/* Fixed Header - Luxury Wordmark Lockup */
+header {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 100;
+ background: var(--ground);
+ border-bottom: 1px solid var(--line);
+ padding: 24px 32px;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.wordmark {
+ font-family: var(--font-display);
+ font-size: var(--wordmark);
+ font-weight: 400;
+ text-transform: uppercase;
+ letter-spacing: 0.18em;
+ color: var(--ink);
+ line-height: 1.1;
+}
+
+.score-board {
+ display: flex;
+ gap: 32px;
+ align-items: center;
+}
+
+.score-item {
+ font-family: var(--font-sans);
+ font-size: var(--label);
+ font-weight: 500;
+ text-transform: uppercase;
+ letter-spacing: 0.16em;
+ color: var(--muted);
+ display: flex;
+ flex-direction: column;
+ align-items: flex-end;
+ line-height: 1.4;
+}
+
+.score-value {
+ font-size: var(--score);
+ color: var(--ink);
+ font-weight: 600;
+ letter-spacing: 0.08em;
+}
+
+/* Game Container */
+#game-container {
+ position: fixed;
+ top: 91px; /* header height */
+ left: 0;
+ right: 0;
+ bottom: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--surface);
+}
+
+canvas {
+ display: block;
+ box-shadow: 0 24px 48px var(--shadow-elevated);
+}
+
+/* Overlays */
+.overlay {
+ position: absolute;
+ inset: 0;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ background: rgba(245, 242, 234, 0.94);
+ backdrop-filter: blur(8px);
+ z-index: 50;
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 0.4s var(--ease-swift);
+}
+
+.overlay.active {
+ opacity: 1;
+ pointer-events: all;
+}
+
+.overlay-content {
+ text-align: center;
+ padding: 48px;
+ border: 1px solid var(--line);
+ background: var(--surface);
+ box-shadow: 0 32px 64px var(--shadow-elevated);
+ min-width: 320px;
+}
+
+.overlay h2 {
+ font-family: var(--font-display);
+ font-size: 38px;
+ font-weight: 400;
+ text-transform: uppercase;
+ letter-spacing: 0.12em;
+ color: var(--ink);
+ margin-bottom: 8px;
+}
+
+.overlay p {
+ font-family: var(--font-sans);
+ font-size: 12px;
+ text-transform: uppercase;
+ letter-spacing: 0.16em;
+ color: var(--muted);
+ margin-bottom: 32px;
+}
+
+.btn {
+ font-family: var(--font-sans);
+ font-size: 11px;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 0.2em;
+ color: var(--surface);
+ background: var(--ink);
+ border: none;
+ padding: 16px 32px;
+ cursor: pointer;
+ transition: all 0.3s var(--ease-swift);
+ box-shadow: 0 4px 12px var(--shadow-elevated);
+}
+
+.btn:hover {
+ background: var(--accent);
+ color: var(--ink);
+ transform: translateY(-2px);
+ box-shadow: 0 8px 24px var(--shadow-elevated);
+}
+
+.instructions {
+ margin-top: 24px;
+ font-size: 11px;
+ letter-spacing: 0.12em;
+ color: var(--muted);
+ text-transform: uppercase;
+}
+
+/* Start Screen Specific */
+#start-screen.active,
+#game-over-screen.active {
+ opacity: 1;
+ pointer-events: all;
+}
+
+.final-score {
+ font-family: var(--font-display);
+ font-size: 48px;
+ color: var(--accent);
+ margin: 16px 0;
+ line-height: 1;
+}
+
+.best-score {
+ font-size: 12px;
+ letter-spacing: 0.16em;
+ color: var(--muted);
+ text-transform: uppercase;
+ margin-bottom: 32px;
+}
+</style>
+</head>
+<body>
+
+<header>
+ <div class="wordmark">Designer Wallcoverings</div>
+ <div class="score-board">
+ <div class="score-item">
+ <span>Current</span>
+ <span class="score-value" id="score-display">0000</span>
+ </div>
+ <div class="score-item">
+ <span>Best</span>
+ <span class="score-value" id="best-display">0000</span>
+ </div>
+ </div>
+</header>
+
+<div id="game-container">
+ <canvas id="game"></canvas>
+
+ <!-- Start Screen -->
+ <div id="start-screen" class="overlay active">
+ <div class="overlay-content">
+ <h2>The Collection</h2>
+ <p>Curated Patterns · Precious Metals · Endless Style</p>
+ <button class="btn" id="start-btn">Begin Journey</button>
+ <div class="instructions">Tap or Space to Jump</div>
+ </div>
+ </div>
+
+ <!-- Game Over Screen -->
+ <div id="game-over-screen" class="overlay">
+ <div class="overlay-content">
+ <h2>Collection Paused</h2>
+ <div class="final-score" id="final-score">0</div>
+ <div class="best-score" id="best-score-display">Best: 0</div>
+ <button class="btn" id="restart-btn">Continue</button>
+ <div class="instructions">Tap or Space to Restart</div>
+ </div>
+ </div>
+</div>
+
+<script>
+/* Game Constants & Setup */
+const canvas = document.getElementById('game');
+const ctx = canvas.getContext('2d');
+const startScreen = document.getElementById('start-screen');
+const gameOverScreen = document.getElementById('game-over-screen');
+const scoreDisplay = document.getElementById('score-display');
+const bestDisplay = document.getElementById('best-display');
+const finalScoreDisplay = document.getElementById('final-score');
+const bestScoreDisplay = document.getElementById('best-score-display');
+const startBtn = document.getElementById('start-btn');
+const restartBtn = document.getElementById('restart-btn');
+
+// Responsive canvas sizing
+function resize() {
+ const container = document.getElementById('game-container');
+ canvas.width = Math.min(800, container.clientWidth - 64);
+ canvas.height = Math.min(400, container.clientHeight - 64);
+}
+resize();
+window.addEventListener('resize', resize);
+
+// Game State
+let gameRunning = false;
+let gameOver = false;
+let score = 0;
+let highScore = parseInt(localStorage.getItem('dw_runner_best') || '0');
+let speed = 3;
+let speedMultiplier = 1;
+let frame = 0;
+
+// Update displays
+bestDisplay.textContent = highScore.toString().padStart(4, '0');
+
+// Physics
+const GRAVITY = 0.6;
+const JUMP_FORCE = -12;
+const GROUND_Y_OFFSET = 60;
+
+// Entities
+let player = {
+ x: 80,
+ y: 0,
+ vy: 0,
+ radius: 20,
+ grounded: false,
+ angle: 0
+};
+
+let obstacles = [];
+let collectibles = [];
+let particles = [];
+let decor = [];
+
+// Pattern definitions for wallpaper rolls (visually distinct styles)
+const patterns = [
+ { name: 'stripe', colors: ['#c9a961', '#f5f2ea'] },
+ { name: 'dot', colors: ['#7d6a45', '#f5f2ea'] },
+ { name: 'geo', colors: ['#1c1a17', '#c9a961'] },
+ { name: 'floral', colors: ['#8a8578', '#f5f2ea'] }
+];
+
+// Create decor elements (molding, baseboard details)
+function initDecor() {
+ decor = [];
+ for (let i = 0; i < 5; i++) {
+ decor.push({
+ x: i * 200,
+ width: 100,
+ height: 8
+ });
+ }
+}
+
+// Pattern drawing functions
+function drawPattern(ctx, type, x, y, w, h, colors, time) {
+ ctx.save();
+ ctx.beginPath();
+ ctx.rect(x, y, w, h);
+ ctx.clip();
+
+ // Base fill
+ ctx.fillStyle = colors[1];
+ ctx.fillRect(x, y, w, h);
+
+ ctx.fillStyle = colors[0];
+
+ switch(type) {
+ case 'stripe':
+ const stripeW = 8;
+ const offset = (time * 0.5) % (stripeW * 2);
+ for (let sx = x - stripeW * 2 + offset; sx < x + w; sx += stripeW * 2) {
+ ctx.fillRect(sx, y, stripeW, h);
+ }
+ break;
+
+ case 'dot':
+ const dotSize = 6;
+ const spacing = 20;
+ const doffset = (time * 0.3) % spacing;
+ for (let dy = y + 10; dy < y + h; dy += spacing) {
+ for (let dx = x + 10 + doffset; dx < x + w; dx += spacing) {
+ ctx.beginPath();
+ ctx.arc(dx, dy, dotSize/2, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ }
+ break;
+
+ case 'geo':
+ const size = 16;
+ const goffset = (time * 0.4) % size;
+ for (let gy = y; gy < y + h; gy += size) {
+ for (let gx = x + goffset; gx < x + w; gx += size) {
+ ctx.beginPath();
+ ctx.moveTo(gx + size/2, gy);
+ ctx.lineTo(gx + size, gy + size/2);
+ ctx.lineTo(gx + size/2, gy + size);
+ ctx.lineTo(gx, gy + size/2);
+ ctx.closePath();
+ ctx.fill();
+ }
+ }
+ break;
+
+ case 'floral':
+ ctx.strokeStyle = colors[0];
+ ctx.lineWidth = 2;
+ const fspacing = 24;
+ const foffset = (time * 0.2) % fspacing;
+ for (let fy = y + 12; fy < y + h; fy += fspacing) {
+ for (let fx = x + 12 + foffset; fx < x + w; fx += fspacing) {
+ ctx.beginPath();
+ ctx.arc(fx, fy, 8, 0, Math.PI * 2);
+ ctx.stroke();
+ ctx.beginPath();
+ ctx.moveTo(fx - 6, fy);
+ ctx.lineTo(fx + 6, fy);
+ ctx.moveTo(fx, fy - 6);
+ ctx.lineTo(fx, fy + 6);
+ ctx.stroke();
+ }
+ }
+ break;
+ }
+
+ ctx.restore();
+}
+
+// Input handling
+function jump() {
+ if (!gameRunning || gameOver) return;
+ if (player.grounded) {
+ player.vy = JUMP_FORCE;
+ player.grounded = false;
+ createParticles(player.x, player.y + player.radius, 5, '#c9a961');
+ }
+}
+
+document.addEventListener('keydown', (e) => {
+ if (e.code === 'Space') {
+ e.preventDefault();
+ if (gameRunning && !gameOver) {
+ jump();
+ } else if (gameOver && gameOverScreen.classList.contains('active')) {
+ resetGame();
+ } else if (!gameRunning && startScreen.classList.contains('active')) {
+ startGame();
+ }
+ }
+});
+
+canvas.addEventListener('mousedown', () => {
+ if (gameRunning && !gameOver) jump();
+});
+canvas.addEventListener('touchstart', (e) => {
+ e.preventDefault();
+ if (gameRunning && !gameOver) jump();
+});
+
+startBtn.addEventListener('click', startGame);
+restartBtn.addEventListener('click', resetGame);
+
+function startGame() {
+ startScreen.classList.remove('active');
+ gameRunning = true;
+ gameOver = false;
+ resetEntities();
+ loop();
+}
+
+function resetGame() {
+ gameOverScreen.classList.remove('active');
+ gameRunning = true;
+ gameOver = false;
+ score = 0;
+ speed = 3;
+ speedMultiplier = 1;
+ scoreDisplay.textContent = '0000';
+ resetEntities();
+ loop();
+}
+
+function resetEntities() {
+ player.y = canvas.height - GROUND_Y_OFFSET - player.radius;
+ player.vy = 0;
+ player.grounded = true;
+ player.angle = 0;
+ obstacles = [];
+ collectibles = [];
+ particles = [];
+ initDecor();
+}
+
+function createParticles(x, y, count, color) {
+ for (let i = 0; i < count; i++) {
+ particles.push({
+ x: x,
+ y: y,
+ vx: (Math.random() - 0.5) * 4,
+ vy: (Math.random() - 0.5) * 4,
+ life: 1,
+ color: color
+ });
+ }
+}
+
+function spawnObstacle() {
+ const type = Math.random() > 0.5 ? 'can' : 'brush';
+ obstacles.push({
+ x: canvas.width + 50,
+ y: canvas.height - GROUND_Y_OFFSET - (type === 'can' ? 35 : 30),
+ width: type === 'can' ? 30 : 25,
+ height: type === 'can' ? 40 : 35,
+ type: type,
+ passed: false
+ });
+}
+
+function spawnCollectible() {
+ const patternIdx = Math.floor(Math.random() * patterns.length);
+ const isHigh = Math.random() > 0.6;
+ collectibles.push({
+ x: canvas.width + 50,
+ y: canvas.height - GROUND_Y_OFFSET - (isHigh ? 90 : 45),
+ radius: 18,
+ pattern: patterns[patternIdx],
+ rotation: 0,
+ collected: false
+ });
+}
+
+function update() {
+ frame++;
+
+ // Speed progression
+ speedMultiplier = 1 + (score / 2000);
+ const currentSpeed = speed * speedMultiplier;
+
+ // Player physics
+ player.vy += GRAVITY;
+ player.y += player.vy;
+
+ // Ground collision
+ const groundY = canvas.height - GROUND_Y_OFFSET - player.radius;
+ if (player.y > groundY) {
+ player.y = groundY;
+ player.vy = 0;
+ player.grounded = true;
+ }
+
+ // Player rotation based on movement
+ if (player.grounded) {
+ player.angle += currentSpeed * 0.05;
+ }
+
+ // Spawn entities
+ if (frame % Math.floor(120 / speedMultiplier) === 0) {
+ if (Math.random() > 0.3) spawnObstacle();
+ }
+ if (frame % Math.floor(90 / speedMultiplier) === 0) {
+ if (Math.random() > 0.4) spawnCollectible();
+ }
+
+ // Update decor (moldings)
+ decor.forEach(d => {
+ d.x -= currentSpeed * 0.5;
+ if (d.x + d.width < 0) {
+ d.x = canvas.width + Math.random() * 200;
+ }
+ });
+
+ // Update obstacles
+ for (let i = obstacles.length - 1; i >= 0; i--) {
+ const obs = obstacles[i];
+ obs.x -= currentSpeed;
+
+ // Collision detection
+ const dx = player.x - obs.x;
+ const dy = player.y - (obs.y + obs.height/2);
+ const dist = Math.sqrt(dx*dx + dy*dy);
+
+ if (dist < player.radius + obs.width/2 && !gameOver) {
+ endGame();
+ }
+
+ if (obs.x + obs.width < 0) {
+ obstacles.splice(i, 1);
+ }
+ }
+
+ // Update collectibles
+ for (let i = collectibles.length - 1; i >= 0; i--) {
+ const col = collectibles[i];
+ col.x -= currentSpeed;
+ col.rotation += 0.02;
+
+ // Collection detection
+ const dx = player.x - col.x;
+ const dy = player.y - col.y;
+ const dist = Math.sqrt(dx*dx + dy*dy);
+
+ if (dist < player.radius + col.radius && !col.collected) {
+ col.collected = true;
+ score += 100;
+ scoreDisplay.textContent = score.toString().padStart(4, '0');
+ createParticles(col.x, col.y, 8, col.pattern.colors[0]);
+ collectibles.splice(i, 1);
+ } else if (col.x + col.radius < 0) {
+ collectibles.splice(i, 1);
+ }
+ }
+
+ // Update particles
+ for (let i = particles.length - 1; i >= 0; i--) {
+ const p = particles[i];
+ p.x += p.vx;
+ p.y += p.vy;
+ p.vy += 0.2;
+ p.life -= 0.03;
+ if (p.life <= 0) particles.splice(i, 1);
+ }
+
+ // Passive score
+ if (frame % 10 === 0 && !gameOver) {
+ score += 1;
+ scoreDisplay.textContent = score.toString().padStart(4, '0');
+ }
+}
+
+function draw() {
+ // Clear with warm ivory
+ ctx.fillStyle = '#f5f2ea';
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
+
+ // Draw subtle wall texture
+ ctx.fillStyle = 'rgba(201, 169, 97, 0.03)';
+ for (let i = 0; i < canvas.width; i += 40) {
+ ctx.fillRect(i, 0, 1, canvas.height);
+ }
+
+ // Draw decor moldings
+ ctx.fillStyle = '#1c1a17';
+ decor.forEach(d => {
+ ctx.fillRect(d.x, canvas.height - GROUND_Y_OFFSET - 60, d.width, d.height);
+ });
+
+ // Draw baseboard
+ const groundY = canvas.height - GROUND_Y_OFFSET;
+ ctx.fillStyle = '#ffffff';
+ ctx.fillRect(0, groundY, canvas.width, GROUND_Y_OFFSET);
+ ctx.strokeStyle = 'rgba(28, 26, 23, 0.08)';
+ ctx.lineWidth = 1;
+ ctx.beginPath();
+ ctx.moveTo(0, groundY);
+ ctx.lineTo(canvas.width, groundY);
+ ctx.stroke();
+
+ // Draw crown molding detail
+ ctx.fillStyle = '#1c1a17';
+ ctx.fillRect(0, groundY + 10, canvas.width, 2);
+
+ // Draw collectibles (wallpaper rolls with patterns)
+ collectibles.forEach(col => {
+ ctx.save();
+ ctx.translate(col.x, col.y);
+ ctx.rotate(col.rotation);
+
+ // Roll cylinder effect
+ const gradient = ctx.createLinearGradient(-col.radius, 0, col.radius, 0);
+ gradient.addColorStop(0, '#e8e4dc');
+ gradient.addColorStop(0.3, '#ffffff');
+ gradient.addColorStop(0.7, '#f5f2ea');
+ gradient.addColorStop(1, '#d4cfc4');
+
+ ctx.fillStyle = gradient;
+ ctx.beginPath();
+ ctx.ellipse(0, 0, col.radius, col.radius * 0.7, 0, 0, Math.PI * 2);
+ ctx.fill();
+
+ // Pattern on roll
+ drawPattern(ctx, col.pattern.name, -col.radius + 4, -col.radius * 0.5 + 4,
+ col.radius * 2 - 8, col.radius * 1.4 - 8, col.pattern.colors, frame);
+
+ // Roll ends
+ ctx.fillStyle = '#d4cfc4';
+ ctx.beginPath();
+ ctx.ellipse(-col.radius + 2, 0, 4, col.radius * 0.6, 0, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.beginPath();
+ ctx.ellipse(col.radius - 2, 0, 4, col.radius * 0.6, 0, 0, Math.PI * 2);
+ ctx.fill();
+
+ ctx.restore();
+ });
+
+ // Draw obstacles
+ obstacles.forEach(obs => {
+ if (obs.type === 'can') {
+ // Paint can
+ ctx.fillStyle = '#8a8578';
+ ctx.fillRect(obs.x, obs.y, obs.width, obs.height);
+ // Can rim
+ ctx.fillStyle = '#1c1a17';
+ ctx.fillRect(obs.x - 2, obs.y, obs.width + 4, 6);
+ // Label area
+ ctx.fillStyle = '#f5f2ea';
+ ctx.fillRect(obs.x + 4, obs.y + 12, obs.width - 8, obs.height - 20);
+ // Spill
+ ctx.fillStyle = '#c9a961';
+ ctx.beginPath();
+ ctx.moveTo(obs.x + 5, obs.y + obs.height);
+ ctx.lineTo(obs.x - 5, obs.y + obs.height + 8);
+ ctx.lineTo(obs.x + 10, obs.y + obs.height + 5);
+ ctx.lineTo(obs.x + 15, obs.y + obs.height + 10);
+ ctx.lineTo(obs.x + obs.width + 3, obs.y + obs.height + 6);
+ ctx.lineTo(obs.x + obs.width, obs.y + obs.height);
+ ctx.fill();
+ } else {
+ // Brush
+ ctx.fillStyle = '#7d6a45';
+ ctx.fillRect(obs.x + 8, obs.y, 10, obs.height - 10);
+ // Bristles
+ ctx.fillStyle = '#1c1a17';
+ ctx.beginPath();
+ ctx.moveTo(obs.x + 5, obs.y + obs.height - 10);
+ ctx.lineTo(obs.x + 21, obs.y + obs.height - 10);
+ ctx.lineTo(obs.x + 18, obs.y + obs.height);
+ ctx.lineTo(obs.x + 8, obs.y + obs.height);
+ ctx.fill();
+ // Handle
+ ctx.strokeStyle = '#8a8578';
+ ctx.lineWidth = 4;
+ ctx.beginPath();
+ ctx.moveTo(obs.x + 13, obs.y);
+ ctx.lineTo(obs.x + 13, obs.y - 15);
+ ctx.stroke();
+ }
+ });
+
+ // Draw player (paint roller)
+ ctx.save();
+ ctx.translate(player.x, player.y);
+ ctx.rotate(player.angle);
+
+ // Roller wheel
+ const wheelGrad = ctx.createRadialGradient(0, 0, 2, 0, 0, player.radius);
+ wheelGrad.addColorStop(0, '#ffffff');
+ wheelGrad.addColorStop(0.7, '#e8e4dc');
+ wheelGrad.addColorStop(1, '#c9a961');
+
+ ctx.fillStyle = wheelGrad;
+ ctx.beginPath();
+ ctx.arc(0, 0, player.radius, 0, Math.PI * 2);
+ ctx.fill();
+
+ // Roller texture lines
+ ctx.strokeStyle = 'rgba(28, 26, 23, 0.1)';
+ ctx.lineWidth = 1;
+ for (let i = -player.radius + 4; i < player.radius; i += 6) {
+ ctx.beginPath();
+ ctx.moveTo(i, -Math.sqrt(player.radius*player.radius - i*i));
+ ctx.lineTo(i, Math.sqrt(player.radius*player.radius - i*i));
+ ctx.stroke();
+ }
+
+ ctx.restore();
+
+ // Handle (behind roller visually but drawn after for simplicity, offset)
+ ctx.strokeStyle = '#8a8578';
+ ctx.lineWidth = 5;
+ ctx.lineCap = 'round';
+ ctx.beginPath();
+ ctx.moveTo(player.x, player.y);
+ ctx.lineTo(player.x - 25, player.y - 30);
+ ctx.lineTo(player.x - 45, player.y - 25);
+ ctx.stroke();
+
+ // Handle grip
+ ctx.strokeStyle = '#1c1a17';
+ ctx.lineWidth = 6;
+ ctx.beginPath();
+ ctx.moveTo(player.x - 43, player.y - 27);
+ ctx.lineTo(player.x - 47, player.y - 23);
+ ctx.stroke();
+
+ // Draw particles
+ particles.forEach(p => {
+ ctx.globalAlpha = p.life;
+ ctx.fillStyle = p.color;
+ ctx.beginPath();
+ ctx.arc(p.x, p.y, 3, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.globalAlpha = 1;
+ });
+}
+
+function endGame() {
+ gameOver = true;
+ gameRunning = false;
+
+ if (score > highScore) {
+ highScore = score;
+ localStorage.setItem('dw_runner_best', highScore.toString());
+ bestDisplay.textContent = highScore.toString().padStart(4, '0');
+ }
+
+ finalScoreDisplay.textContent = score;
+ bestScoreDisplay.textContent = `Best: ${highScore}`;
+ gameOverScreen.classList.add('active');
+}
+
+function loop() {
+ if (!gameRunning) return;
+ update();
+ draw();
+ requestAnimationFrame(loop);
+}
+
+// Initial draw
+draw();
+</script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/83e28cd01c99/kimi.png b/data/artifacts/83e28cd01c99/kimi.png
new file mode 100644
index 0000000..06ae563
Binary files /dev/null and b/data/artifacts/83e28cd01c99/kimi.png differ
diff --git a/data/artifacts/83e28cd01c99/qwen25-7b.html b/data/artifacts/83e28cd01c99/qwen25-7b.html
new file mode 100644
index 0000000..1ce0bcf
--- /dev/null
+++ b/data/artifacts/83e28cd01c99/qwen25-7b.html
@@ -0,0 +1,272 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Endless Runner</title>
+ <style>
+ :root {
+ --bg: #17171c;
+ --surface: #f5f2ea;
+ --text: #1c1a17;
+ --muted: #8a8578;
+ --accent: #c9a961;
+ --line: 1px solid var(--text);
+ --shadow: 0 8px 24px rgba(0,0,0,.12), hover 0 16px 48px rgba(0,0,0,.18);
+ --ease: cubic-bezier(.22,1,.36,1) both;
+ }
+
+ html {
+ font-family: 'Bodoni MT', Didot, Georgia, serif;
+ height: 100%;
+ background-color: var(--bg);
+ color: var(--text);
+ margin: 0;
+ padding: 0;
+ overflow: hidden;
+ scroll-behavior: smooth;
+ }
+
+ body {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ height: 100vh;
+ margin: 0;
+ text-align: center;
+ font-size: calc(16px + (32 - 16) * ((100vw - 320px) / (1280 - 320)));
+ }
+
+ header {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 56px;
+ padding-left: 48px;
+ background-color: var(--surface);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ }
+
+ .wordmark {
+ font-size: 56px;
+ letter-spacing: 0.18em;
+ color: var(--text);
+ margin-right: 24px;
+ }
+
+ canvas {
+ width: 80vw;
+ height: 80vh;
+ background-color: var(--surface);
+ border: var(--line);
+ box-shadow: var(--shadow);
+ }
+
+ .scoreboard {
+ font-size: 16px;
+ letter-spacing: 0.15em;
+ color: var(--muted);
+ }
+ </style>
+</head>
+<body>
+
+<header>
+ <span class="wordmark">DESIGNER WALLCOVERINGS</span>
+</header>
+
+<canvas id="gameCanvas"></canvas>
+
+<script>
+document.addEventListener('DOMContentLoaded', () => {
+ const canvas = document.getElementById('gameCanvas');
+ const ctx = canvas.getContext('2d');
+
+ canvas.width = 800;
+ canvas.height = 600;
+
+ let score = 0;
+ let highScore = localStorage.getItem('highscore') || 0;
+
+ class Wall {
+ constructor(x, y) {
+ this.x = x;
+ this.y = y;
+ this.rollSpeed = 2;
+ }
+
+ draw() {
+ ctx.fillStyle = '#c9a961';
+ ctx.fillRect(this.x, this.y - 50, 40, 50);
+ }
+ }
+
+ class Obstacle {
+ constructor(x) {
+ this.x = x;
+ this.y = 500;
+ this.speed = 3;
+ }
+
+ draw() {
+ ctx.fillStyle = '#b0472e';
+ ctx.fillRect(this.x, this.y - 60, 40, 60);
+ }
+ }
+
+ class Roller {
+ constructor(x) {
+ this.x = x;
+ this.y = 500;
+ this.jumpSpeed = 8;
+ }
+
+ draw() {
+ ctx.fillStyle = '#f3e9ff';
+ ctx.fillRect(this.x, this.y - 40, 40, 40);
+ }
+
+ update() {
+ if (jumping) {
+ this.y -= this.jumpSpeed;
+ this.jumpSpeed -= 1;
+ if (this.y > 500 || this.jumpSpeed < 0) {
+ jumping = false;
+ this.y = 500;
+ }
+ } else {
+ this.y += 3;
+ }
+
+ ctx.fillStyle = '#f3e9ff';
+ ctx.fillRect(this.x, this.y - 40, 40, 40);
+ }
+ }
+
+ let roller = new Roller(100);
+ let walls = [];
+ let obstacles = [];
+
+ function createWall() {
+ const x = canvas.width;
+ const wall = new Wall(x, Math.floor(Math.random() * (350 - 100 + 1)) + 100);
+ walls.push(wall);
+
+ function spawnObstacle() {
+ const obstacle = new Obstacle(x + 200);
+ obstacles.push(obstacle);
+ }
+ setTimeout(spawnObstacle, Math.random() * (2500 - 700 + 1) + 700);
+ }
+
+ function clearWallsAndObstacles() {
+ walls = [];
+ obstacles = [];
+ }
+
+ let jumping = false;
+
+ document.addEventListener('keydown', e => {
+ if (e.code === 'Space' || e.code === 'Enter') {
+ jumping = true;
+ }
+ });
+
+ const updateGame = () => {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ roller.update();
+
+ walls.forEach(wall => wall.draw());
+ obstacles.forEach(obstacle => obstacle.draw());
+
+ if (roller.y > 500) {
+ document.getElementById('gameOver').style.display = 'block';
+ localStorage.setItem('highscore', Math.max(highScore, score));
+ }
+
+ walls.forEach((wall, index) => {
+ if (wall.x < roller.x && wall.x + 40 > roller.x && !collides(wall)) {
+ score++;
+ }
+ wall.x -= wall.rollSpeed;
+ if (wall.x < -50) {
+ walls.splice(index, 1);
+ }
+ });
+
+ obstacles.forEach((obstacle, index) => {
+ obstacle.x -= obstacle.speed;
+ if (obstacle.x < -60 && !collides(obstacle)) {
+ obstacles.splice(index, 1);
+ } else if (wallHit(roller, obstacle)) {
+ document.getElementById('gameOver').style.display = 'block';
+ localStorage.setItem('highscore', Math.max(highScore, score));
+ }
+ });
+
+ requestAnimationFrame(updateGame);
+ };
+
+ const collides = (object) => {
+ return roller.x < object.x + 40 && roller.x + 40 > object.x && roller.y < object.y + 60 && roller.y + 40 > object.y;
+ };
+
+ const wallHit = (roller, obstacle) => {
+ return collides(roller) || collides(obstacle);
+ };
+
+ createWall();
+ updateGame();
+
+ function gameOver() {
+ ctx.fillStyle = '#f3e9ff';
+ ctx.fillRect(160, 240, 520, 180);
+
+ const gameoverText = document.createElement('div');
+ gameoverText.innerHTML = 'GAME OVER<br><span style="font-size: 24px">Your Score: ' + score + '</span>';
+ ctx.font = 'bold 36px "Didot", Georgia, serif';
+ ctx.fillText('Game Over', 180, 350);
+ gameoverText.style.position = 'absolute';
+ gameoverText.style.top = '270px';
+ gameoverText.style.left = '340px';
+ gameoverText.style.color = '#f3e9ff';
+ document.body.appendChild(gameoverText);
+
+ const restartButton = document.createElement('button');
+ restartButton.innerHTML = 'Restart';
+ restartButton.style.position = 'absolute';
+ restartButton.style.top = '420px';
+ restartButton.style.left = '375px';
+ restartButton.style.backgroundColor = '#c9a961';
+ restartButton.style.color = '#fff';
+ restartButton.style.border = 'none';
+ restartButton.style.padding = '12px 24px';
+ restartButton.style.fontSize = '18px';
+ restartButton.style.fontFamily = '"Didot", Georgia, serif';
+ restartButton.style.borderRadius = '14px';
+ document.body.appendChild(restartButton);
+
+ restartButton.addEventListener('click', () => {
+ location.reload();
+ });
+ }
+});
+</script>
+
+<div id="gameOver" style="display: none; position: absolute; top: 270px; left: 320px; width: 520px; height: 180px; background-color: rgba(245, 242, 234, 0.9); box-shadow: var(--shadow);">
+ <div style="position: absolute; top: -50px; left: 60px; width: 400px;">
+ <h1 style="font-size: 36px; font-family: 'Didot', Georgia, serif;">Game Over</h1>
+ <p style="font-size: 24px; margin-top: 20px;">Your Score: <span id="scoreDisplay"></span></p>
+ </div>
+</div>
+
+<script>
+document.getElementById('scoreDisplay').innerText = localStorage.getItem('highscore');
+</script>
+
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/83e28cd01c99/qwen25-7b.png b/data/artifacts/83e28cd01c99/qwen25-7b.png
new file mode 100644
index 0000000..8a3a8a9
Binary files /dev/null and b/data/artifacts/83e28cd01c99/qwen25-7b.png differ
diff --git a/data/artifacts/83e28cd01c99/qwen3-14b.html b/data/artifacts/83e28cd01c99/qwen3-14b.html
new file mode 100644
index 0000000..4b76142
--- /dev/null
+++ b/data/artifacts/83e28cd01c99/qwen3-14b.html
@@ -0,0 +1,307 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="UTF-8" />
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+ <title>Designer Wallcoverings</title>
+ <style>
+ :root {
+ --bg: #faf8f4;
+ --surface: #ffffff;
+ --text: #1c1a17;
+ --muted: #9b948a;
+ --accent: #b0472e;
+ --line: #1c1a17;
+ --shadow: 0 8px 24px rgba(0,0,0,0.12);
+ --ease: cubic-bezier(0.22, 1, 0.36, 1);
+ }
+
+ * {
+ box-sizing: border-box;
+ margin: 0;
+ padding: 0;
+ font-family: sans-serif;
+ }
+
+ body {
+ background: var(--bg);
+ color: var(--text);
+ font-size: 16px;
+ line-height: 1.5;
+ overflow: hidden;
+ }
+
+ header {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ padding: 16px;
+ background: var(--surface);
+ box-shadow: var(--shadow);
+ z-index: 1000;
+ }
+
+ header h1 {
+ font-family: 'Playfair Display', Georgia, serif;
+ font-size: 48px;
+ letter-spacing: 0.18em;
+ color: var(--text);
+ }
+
+ #game {
+ position: relative;
+ width: 100vw;
+ height: 100vh;
+ overflow: hidden;
+ }
+
+ canvas {
+ display: block;
+ width: 100%;
+ height: 100%;
+ }
+
+ #overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ width: 100%;
+ height: 100%;
+ background: rgba(0, 0, 0, 0.8);
+ color: var(--text);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-direction: column;
+ gap: 24px;
+ font-family: sans-serif;
+ font-size: 24px;
+ letter-spacing: 0.15em;
+ text-transform: uppercase;
+ visibility: hidden;
+ opacity: 0;
+ transition: opacity 0.3s var(--ease);
+ }
+
+ #overlay.show {
+ visibility: visible;
+ opacity: 1;
+ }
+
+ #overlay h2 {
+ font-size: 32px;
+ letter-spacing: 0.2em;
+ margin-bottom: 16px;
+ }
+
+ #overlay button {
+ background: var(--accent);
+ color: var(--text);
+ border: none;
+ padding: 12px 24px;
+ font-size: 18px;
+ letter-spacing: 0.1em;
+ border-radius: 8px;
+ cursor: pointer;
+ transition: background 0.2s var(--ease);
+ }
+
+ #overlay button:hover {
+ background: #a0472e;
+ }
+
+ #score {
+ position: fixed;
+ top: 16px;
+ right: 16px;
+ font-size: 20px;
+ color: var(--muted);
+ text-transform: uppercase;
+ letter-spacing: 0.1em;
+ }
+ </style>
+</head>
+<body>
+ <header>
+ <h1>DESIGNER WALLCOVERINGS</h1>
+ </header>
+ <div id="score">Score: 0 | Best: 0</div>
+ <div id="game">
+ <canvas id="canvas"></canvas>
+ </div>
+ <div id="overlay">
+ <h2>GAME OVER</h2>
+ <button>RESTART</button>
+ </div>
+
+ <script>
+ const canvas = document.getElementById('canvas');
+ const ctx = canvas.getContext('2d');
+ const overlay = document.getElementById('overlay');
+ const scoreEl = document.getElementById('score');
+
+ canvas.width = window.innerWidth;
+ canvas.height = window.innerHeight;
+
+ const gravity = 0.8;
+ const jumpStrength = -14;
+ const floorY = canvas.height - 64;
+
+ let isJumping = false;
+ let velocityY = 0;
+ let playerY = floorY;
+ let score = 0;
+ let bestScore = parseInt(localStorage.getItem('bestScore') || '0');
+
+ let obstacles = [];
+ let wallpapers = [];
+
+ const generateObstacle = () => {
+ const x = canvas.width + Math.random() * 300;
+ const height = 40 + Math.random() * 20;
+ return { x, y: floorY - height, width: 30, height };
+ };
+
+ const generateWallpaper = () => {
+ const x = canvas.width + Math.random() * 300;
+ const size = 30 + Math.random() * 20;
+ const colors = ['#c9a961', '#7d6a45', '#b0472e', '#2f4c39'];
+ const color = colors[Math.floor(Math.random() * colors.length)];
+ return { x, y: floorY - size, width: size, height: size, color };
+ };
+
+ const update = () => {
+ if (isJumping) {
+ velocityY += gravity;
+ playerY += velocityY;
+
+ if (playerY >= floorY) {
+ playerY = floorY;
+ velocityY = 0;
+ isJumping = false;
+ }
+ }
+
+ // Update obstacles
+ for (let i = obstacles.length - 1; i >= 0; i--) {
+ obstacles[i].x -= 5;
+ if (obstacles[i].x + obstacles[i].width < 0) {
+ obstacles.splice(i, 1);
+ }
+ }
+
+ // Update wallpapers
+ for (let i = wallpapers.length - 1; i >= 0; i--) {
+ wallpapers[i].x -= 4;
+ if (wallpapers[i].x + wallpapers[i].width < 0) {
+ wallpapers.splice(i, 1);
+ }
+ }
+
+ // Collision with obstacles
+ for (const obstacle of obstacles) {
+ if (
+ playerY + 32 > obstacle.y &&
+ playerY < obstacle.y + obstacle.height &&
+ playerY + 32 > obstacle.x &&
+ playerY < obstacle.x + obstacle.width
+ ) {
+ endGame();
+ return;
+ }
+ }
+
+ // Collect wallpapers
+ for (let i = wallpapers.length - 1; i >= 0; i--) {
+ const wallpaper = wallpapers[i];
+ if (
+ playerY + 32 > wallpaper.y &&
+ playerY < wallpaper.y + wallpaper.height &&
+ playerY + 32 > wallpaper.x &&
+ playerY < wallpaper.x + wallpaper.width
+ ) {
+ score++;
+ wallpapers.splice(i, 1);
+ }
+ }
+
+ if (Math.random() < 0.02) obstacles.push(generateObstacle());
+ if (Math.random() < 0.01) wallpapers.push(generateWallpaper());
+ };
+
+ const draw = () => {
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+
+ // Draw floor
+ ctx.fillStyle = '#17171c';
+ ctx.fillRect(0, floorY, canvas.width, 64);
+
+ // Draw player
+ ctx.fillStyle = '#c9a961';
+ ctx.beginPath();
+ ctx.arc(canvas.width / 2, playerY, 16, 0, Math.PI * 2);
+ ctx.fill();
+
+ // Draw obstacles
+ for (const obstacle of obstacles) {
+ ctx.fillStyle = '#8a8578';
+ ctx.fillRect(obstacle.x, obstacle.y, obstacle.width, obstacle.height);
+ }
+
+ // Draw wallpapers
+ for (const wallpaper of wallpapers) {
+ ctx.fillStyle = wallpaper.color;
+ ctx.beginPath();
+ ctx.arc(wallpaper.x + wallpaper.width / 2, wallpaper.y + wallpaper.height / 2, wallpaper.width / 2, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ };
+
+ const updateScore = () => {
+ scoreEl.textContent = `Score: ${score} | Best: ${bestScore}`;
+ };
+
+ const endGame = () => {
+ if (score > bestScore) {
+ bestScore = score;
+ localStorage.setItem('bestScore', bestScore);
+ }
+ overlay.classList.add('show');
+ };
+
+ const resetGame = () => {
+ overlay.classList.remove('show');
+ score = 0;
+ updateScore();
+ playerY = floorY;
+ velocityY = 0;
+ isJumping = false;
+ obstacles = [];
+ wallpapers = [];
+ };
+
+ document.addEventListener('keydown', (e) => {
+ if (e.code === 'Space' || e.code === 'ArrowUp') {
+ if (!isJumping) {
+ isJumping = true;
+ velocityY = jumpStrength;
+ }
+ }
+ });
+
+ document.getElementById('overlay').addEventListener('click', () => {
+ resetGame();
+ });
+
+ const animate = () => {
+ update();
+ draw();
+ requestAnimationFrame(animate);
+ };
+
+ animate();
+ updateScore();
+ </script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/83e28cd01c99/qwen3-14b.png b/data/artifacts/83e28cd01c99/qwen3-14b.png
new file mode 100644
index 0000000..378cbb8
Binary files /dev/null and b/data/artifacts/83e28cd01c99/qwen3-14b.png differ
diff --git a/data/challenges.json b/data/challenges.json
index 8fedba4..70e259c 100644
--- a/data/challenges.json
+++ b/data/challenges.json
@@ -14630,7 +14630,7 @@
"bytes": 6764,
"thumb": true,
"aiScore": 8.3,
- "aiReason": "The tool meets the challenge criteria but lacks some visual polish and animation.",
+ "aiReason": "The HTML file fulfills the challenge requirements but lacks some visual polish and animation details.",
"aiScores": {
"qwen2.5vl:7b": 7,
"minicpm-v:latest": 9.5
@@ -14648,11 +14648,11 @@
"queued_at": "2026-07-25T07:39:01.153Z",
"bytes": 6093,
"thumb": true,
- "aiScore": 6.8,
- "aiReason": "The tool is functional but lacks the animated captions and progress bar as described in the challenge.",
+ "aiScore": 7.3,
+ "aiReason": "The tool is functional but lacks the animated captions and progress bar as described.",
"aiScores": {
"qwen2.5vl:7b": 7,
- "minicpm-v:latest": 6.5
+ "minicpm-v:latest": 7.5
},
"aiSpread": 0.5
},
@@ -14667,13 +14667,13 @@
"queued_at": "2026-07-25T07:39:01.155Z",
"bytes": 4718,
"thumb": true,
- "aiScore": 8.3,
- "aiReason": "The HTML file fulfills the challenge requirements but lacks animation and interactivity.",
+ "aiScore": 7.3,
+ "aiReason": "The HTML file is functional and visually appealing but lacks the animated captions and progress bar as described in the challenge.",
"aiScores": {
"qwen2.5vl:7b": 7,
- "minicpm-v:latest": 9.5
+ "minicpm-v:latest": 7.5
},
- "aiSpread": 2.5
+ "aiSpread": 0.5
},
{
"model": "qwen25-7b",
@@ -14686,13 +14686,13 @@
"queued_at": "2026-07-25T07:39:01.158Z",
"bytes": 4104,
"thumb": true,
- "aiScore": 6.5,
- "aiReason": "The HTML file is functional but lacks the animated captions and polished aesthetic required for a high score.",
+ "aiScore": 5,
+ "aiReason": "The file is not fully functional and lacks the required content for a vertical Instagram Reel storyboard tool.",
"aiScores": {
- "qwen2.5vl:7b": 7,
+ "qwen2.5vl:7b": 4,
"minicpm-v:latest": 6
},
- "aiSpread": 1
+ "aiSpread": 2
},
{
"model": "hf-qwen-coder-32b",
@@ -14705,13 +14705,13 @@
"queued_at": "2026-07-25T07:39:01.160Z",
"bytes": 4751,
"thumb": true,
- "aiScore": 5.3,
- "aiReason": "The image is blank and does not display any content related to the challenge.",
+ "aiScore": 4.8,
+ "aiReason": "The image is near-black and does not display any content or functionality related to the challenge.",
"aiScores": {
- "qwen2.5vl:7b": 4,
+ "qwen2.5vl:7b": 3,
"minicpm-v:latest": 6.5
},
- "aiSpread": 2.5
+ "aiSpread": 3.5
},
{
"model": "claude-code",
@@ -14723,7 +14723,14 @@
"finished_at": "2026-07-25T07:42:27.546Z",
"queued_at": "2026-07-25T07:39:01.163Z",
"bytes": 15442,
- "thumb": true
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The image shows a basic concept but lacks the animated captions and progress bar required by the challenge.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 0.5
},
{
"model": "kimi",
@@ -14735,7 +14742,14 @@
"finished_at": "2026-07-25T07:42:55.117Z",
"queued_at": "2026-07-25T07:39:01.166Z",
"bytes": 18220,
- "thumb": true
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The image shows a dark-themed design with a phone preview and text, but lacks the animated captions and progress bar required by the challenge.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 2.5
},
{
"model": "gpt",
@@ -14747,7 +14761,14 @@
"finished_at": "2026-07-25T07:42:36.451Z",
"queued_at": "2026-07-25T07:39:01.168Z",
"bytes": 37564,
- "thumb": true
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The image effectively fulfills the challenge by showing a vertical Instagram Reel storyboard tool for a wallcovering brand. It includes all required elements such as scenes, captions, and progress bar",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
},
{
"model": "grok",
@@ -14759,11 +14780,19 @@
"finished_at": "2026-07-25T07:41:46.414Z",
"queued_at": "2026-07-25T07:39:01.170Z",
"bytes": 18401,
- "thumb": true
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The design is visually appealing and fulfills the challenge requirements well, but lacks some interactive elements like a progress bar.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 2.5
}
],
- "judging": true,
- "judged_at": null
+ "judging": false,
+ "judged_at": "2026-07-25T13:33:33.707Z",
+ "aiPick": "gpt"
},
{
"id": "d7f4f8ca5f8d",
@@ -26038,95 +26067,194 @@
"runs": [
{
"model": "qwen3-14b",
- "status": "queued",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
- "started_at": null,
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.725Z"
+ "seconds": 52,
+ "cost": 0,
+ "started_at": "2026-07-25T13:24:21.650Z",
+ "finished_at": "2026-07-25T13:25:13.885Z",
+ "queued_at": "2026-07-25T13:24:17.725Z",
+ "bytes": 7329,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The HTML file is functional and adheres to the design-token system for a luxury aesthetic. However, it lacks the dynamic content required by an endless runner game.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
},
{
"model": "gemma3-12b",
- "status": "queued",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
- "started_at": null,
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.734Z"
+ "seconds": 65,
+ "cost": 0,
+ "started_at": "2026-07-25T13:25:13.891Z",
+ "finished_at": "2026-07-25T13:26:18.606Z",
+ "queued_at": "2026-07-25T13:24:17.734Z",
+ "bytes": 9485,
+ "thumb": true,
+ "aiScore": 5.8,
+ "aiReason": "The HTML file fulfills most of the requirements but lacks some visual flair and does not fully utilize the luxury palette.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 4.5
+ },
+ "aiSpread": 2.5
},
{
"model": "hermes3-8b",
- "status": "queued",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
- "started_at": null,
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.743Z"
+ "seconds": 31,
+ "cost": 0,
+ "started_at": "2026-07-25T13:26:18.614Z",
+ "finished_at": "2026-07-25T13:26:49.958Z",
+ "queued_at": "2026-07-25T13:24:17.743Z",
+ "bytes": 7419,
+ "thumb": true,
+ "aiScore": 5.8,
+ "aiReason": "The HTML file meets the requirements for a luxury UI and endless runner game but lacks the dynamic painting roller and obstacle dodging mechanics.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 4.5
+ },
+ "aiSpread": 2.5
},
{
"model": "qwen25-7b",
- "status": "running",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
+ "seconds": 55,
+ "cost": 0,
"started_at": "2026-07-25T13:24:17.775Z",
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.751Z"
+ "finished_at": "2026-07-25T13:25:12.436Z",
+ "queued_at": "2026-07-25T13:24:17.751Z",
+ "bytes": 7829,
+ "thumb": true,
+ "aiScore": 5.5,
+ "aiReason": "The HTML file is self-contained and fulfills the challenge requirements well, though it lacks some of the more complex visual elements like SVG patterns or dynamic animations.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 4
+ },
+ "aiSpread": 3
},
{
"model": "hf-qwen-coder-32b",
- "status": "queued",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
- "started_at": null,
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.755Z"
+ "seconds": 101,
+ "cost": 0,
+ "started_at": "2026-07-25T13:26:49.970Z",
+ "finished_at": "2026-07-25T13:28:30.782Z",
+ "queued_at": "2026-07-25T13:24:17.755Z",
+ "bytes": 9605,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The HTML file meets most of the requirements but lacks the dynamic game elements and CSS gradients needed for the endless runner.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
},
{
"model": "claude-code",
- "status": "running",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
+ "seconds": 128,
+ "cost": 0,
"started_at": "2026-07-25T13:24:17.779Z",
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.759Z"
+ "finished_at": "2026-07-25T13:26:25.630Z",
+ "queued_at": "2026-07-25T13:24:17.759Z",
+ "bytes": 23772,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The HTML file fulfills the challenge requirements and presents a visually appealing luxury-themed endless runner game.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
},
{
"model": "kimi",
- "status": "running",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
+ "seconds": 130,
+ "cost": 0.0227,
"started_at": "2026-07-25T13:24:17.787Z",
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.763Z"
+ "finished_at": "2026-07-25T13:26:27.782Z",
+ "queued_at": "2026-07-25T13:24:17.763Z",
+ "toolCalls": [
+ "opendesign",
+ "opendesign",
+ "hyperframes"
+ ],
+ "bytes": 19884,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The design fulfills most requirements but lacks the dynamic elements and CSS gradients needed for a full game.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
},
{
"model": "gpt",
- "status": "running",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
+ "seconds": 120,
+ "cost": 0.158,
"started_at": "2026-07-25T13:24:17.792Z",
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.767Z"
+ "finished_at": "2026-07-25T13:26:17.796Z",
+ "queued_at": "2026-07-25T13:24:17.767Z",
+ "toolCalls": [
+ "opendesign",
+ "opendesign",
+ "hyperframes"
+ ],
+ "bytes": 33919,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The HTML file fulfills the challenge requirements and presents a visually appealing luxury design.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 1.5
},
{
"model": "grok",
- "status": "running",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
+ "seconds": 157,
+ "cost": 0.1842,
"started_at": "2026-07-25T13:24:17.796Z",
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.771Z"
+ "finished_at": "2026-07-25T13:26:54.931Z",
+ "queued_at": "2026-07-25T13:24:17.771Z",
+ "toolCalls": [
+ "opendesign",
+ "opendesign",
+ "hyperframes"
+ ],
+ "bytes": 31041,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The HTML file fulfills the challenge requirements well and presents a polished luxury UI with clear visual distinctions between wallpaper patterns.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 1.5
}
- ]
+ ],
+ "judging": false,
+ "aiPick": "claude-code",
+ "judged_at": "2026-07-25T13:33:43.872Z"
},
{
"id": "104018fe5adc",
@@ -26139,94 +26267,195 @@
"runs": [
{
"model": "qwen3-14b",
- "status": "queued",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
- "started_at": null,
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.829Z"
+ "seconds": 47,
+ "cost": 0,
+ "started_at": "2026-07-25T13:28:30.796Z",
+ "finished_at": "2026-07-25T13:29:18.241Z",
+ "queued_at": "2026-07-25T13:24:17.829Z",
+ "bytes": 6344,
+ "thumb": true,
+ "aiScore": 7.8,
+ "aiReason": "The HTML meets all the requirements and presents a polished Instagram Story poll mockup for wallcoverings with clear styling and visual distinction between styles.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 2.5
},
{
"model": "gemma3-12b",
- "status": "queued",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
- "started_at": null,
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.833Z"
+ "seconds": 46,
+ "cost": 0,
+ "started_at": "2026-07-25T13:29:18.252Z",
+ "finished_at": "2026-07-25T13:30:04.150Z",
+ "queued_at": "2026-07-25T13:24:17.833Z",
+ "bytes": 5356,
+ "thumb": true,
+ "aiScore": 7.8,
+ "aiReason": "The HTML meets all the requirements and presents a visually appealing luxury design poll.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 2.5
},
{
"model": "hermes3-8b",
- "status": "queued",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
- "started_at": null,
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.837Z"
+ "seconds": 16,
+ "cost": 0,
+ "started_at": "2026-07-25T13:30:04.161Z",
+ "finished_at": "2026-07-25T13:30:20.561Z",
+ "queued_at": "2026-07-25T13:24:17.837Z",
+ "bytes": 2820,
+ "thumb": true,
+ "aiScore": 6.8,
+ "aiReason": "The HTML file meets most of the requirements but lacks some visual flair and does not fully utilize the available space for a polished look.",
+ "aiScores": {
+ "qwen2.5vl:7b": 7,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 0.5
},
{
"model": "qwen25-7b",
- "status": "queued",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
- "started_at": null,
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.841Z"
+ "seconds": 26,
+ "cost": 0,
+ "started_at": "2026-07-25T13:25:12.447Z",
+ "finished_at": "2026-07-25T13:25:38.667Z",
+ "queued_at": "2026-07-25T13:24:17.841Z",
+ "bytes": 3200,
+ "thumb": true,
+ "aiScore": 7.8,
+ "aiReason": "The HTML file meets all the requirements and has a high-quality visual presentation.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 6.5
+ },
+ "aiSpread": 2.5
},
{
"model": "hf-qwen-coder-32b",
- "status": "queued",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
- "started_at": null,
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.845Z"
+ "seconds": 71,
+ "cost": 0,
+ "started_at": "2026-07-25T13:30:20.571Z",
+ "finished_at": "2026-07-25T13:31:32.052Z",
+ "queued_at": "2026-07-25T13:24:17.845Z",
+ "bytes": 5482,
+ "thumb": true,
+ "aiScore": 7.3,
+ "aiReason": "The HTML fulfills the challenge requirements well and presents a visually appealing luxury design for an Instagram Story poll.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 5.5
+ },
+ "aiSpread": 3.5
},
{
"model": "claude-code",
- "status": "queued",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
- "started_at": null,
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.849Z"
+ "seconds": 212,
+ "cost": 0,
+ "started_at": "2026-07-25T13:26:25.635Z",
+ "finished_at": "2026-07-25T13:29:57.404Z",
+ "queued_at": "2026-07-25T13:24:17.849Z",
+ "bytes": 21696,
+ "thumb": true,
+ "aiScore": 9.3,
+ "aiReason": "The HTML fulfills all DW LUXURY UI REQUIREMENTS and presents a polished Instagram Story poll mockup.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 9.5
+ },
+ "aiSpread": 0.5
},
{
"model": "kimi",
- "status": "queued",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
- "started_at": null,
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.853Z"
+ "seconds": 191,
+ "cost": 0.0294,
+ "started_at": "2026-07-25T13:26:27.795Z",
+ "finished_at": "2026-07-25T13:29:39.066Z",
+ "queued_at": "2026-07-25T13:24:17.853Z",
+ "toolCalls": [
+ "opendesign",
+ "opendesign",
+ "hyperframes",
+ "hyperframes",
+ "opendesign"
+ ],
+ "bytes": 16578,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The HTML fulfills the challenge requirements well and maintains high visual quality.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 1.5
},
{
"model": "gpt",
- "status": "queued",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
- "started_at": null,
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.857Z"
+ "seconds": 103,
+ "cost": 0.1203,
+ "started_at": "2026-07-25T13:26:17.816Z",
+ "finished_at": "2026-07-25T13:28:00.982Z",
+ "queued_at": "2026-07-25T13:24:17.857Z",
+ "toolCalls": [
+ "opendesign",
+ "opendesign",
+ "hyperframes"
+ ],
+ "bytes": 27907,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The HTML fulfills the challenge requirements with high visual quality and adherence to DW LUXURY UI guidelines.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 1.5
},
{
"model": "grok",
- "status": "queued",
+ "status": "done",
"error": null,
- "seconds": null,
- "cost": null,
- "started_at": null,
- "finished_at": null,
- "queued_at": "2026-07-25T13:24:17.861Z"
+ "seconds": 146,
+ "cost": 0.1837,
+ "started_at": "2026-07-25T13:26:54.950Z",
+ "finished_at": "2026-07-25T13:29:20.749Z",
+ "queued_at": "2026-07-25T13:24:17.861Z",
+ "toolCalls": [
+ "opendesign",
+ "opendesign",
+ "hyperframes"
+ ],
+ "bytes": 32804,
+ "thumb": true,
+ "aiScore": 8.3,
+ "aiReason": "The HTML fulfills the challenge requirements and presents a visually appealing 'This or That' poll for wallcoverings with a polished luxury aesthetic.",
+ "aiScores": {
+ "qwen2.5vl:7b": 9,
+ "minicpm-v:latest": 7.5
+ },
+ "aiSpread": 1.5
}
- ]
+ ],
+ "judging": false,
+ "aiPick": "claude-code",
+ "judged_at": "2026-07-25T13:33:43.880Z"
}
]
\ No newline at end of file
diff --git a/data/costlog.jsonl b/data/costlog.jsonl
index 56e66bd..f17a3c0 100644
--- a/data/costlog.jsonl
+++ b/data/costlog.jsonl
@@ -357,3 +357,9 @@
{"ts":"2026-07-25T13:14:46.902Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena-tools","input_tokens":5865,"output_tokens":8884,"cost_usd":0.025729}
{"ts":"2026-07-25T13:16:32.703Z","provider":"xai","model":"grok-4.5","task":"model-arena-tools","input_tokens":4057,"output_tokens":10942,"cost_usd":0.176301}
{"ts":"2026-07-25T13:16:36.723Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena-tools","input_tokens":3667,"output_tokens":6217,"cost_usd":0.017743}
+{"ts":"2026-07-25T13:26:17.793Z","provider":"openai","model":"gpt-5.1","task":"model-arena-tools","input_tokens":3289,"output_tokens":10875,"cost_usd":0.158006}
+{"ts":"2026-07-25T13:26:27.779Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena-tools","input_tokens":3616,"output_tokens":8198,"cost_usd":0.022665}
+{"ts":"2026-07-25T13:26:54.928Z","provider":"xai","model":"grok-4.5","task":"model-arena-tools","input_tokens":4043,"output_tokens":11472,"cost_usd":0.184209}
+{"ts":"2026-07-25T13:28:00.979Z","provider":"openai","model":"gpt-5.1","task":"model-arena-tools","input_tokens":3317,"output_tokens":8177,"cost_usd":0.120283}
+{"ts":"2026-07-25T13:29:20.746Z","provider":"xai","model":"grok-4.5","task":"model-arena-tools","input_tokens":4071,"output_tokens":11433,"cost_usd":0.183708}
+{"ts":"2026-07-25T13:29:39.062Z","provider":"moonshot","model":"kimi-k2.5","task":"model-arena-tools","input_tokens":6466,"output_tokens":10196,"cost_usd":0.02937}
← d33cf90 night-loop: cycle 06:24 — judged=b72f0e9e751b · fired 2 →; F
·
back to Model Arena
·
night-loop: cycle 06:36 — judged=53b12394c732 · fired 2 →; F a5c844b →