[object Object]

← back to Model Arena

night-loop: cycle 23:19 — judged=4a6e2cfce746 · (backlog 2 — skipped fire)

5917c150983743485673958bf11c7e2ff5c20438 · 2026-07-24 23:19:46 -0700 · Steve Abrams

Files touched

Diff

commit 5917c150983743485673958bf11c7e2ff5c20438
Author: Steve Abrams <steve@designerwallcoverings.com>
Date:   Fri Jul 24 23:19:46 2026 -0700

    night-loop: cycle 23:19 — judged=4a6e2cfce746 · (backlog 2 — skipped fire)
---
 data/artifacts/dfaee602df0d/claude-code.html | 473 +++++++++++++++++++++++++++
 data/artifacts/dfaee602df0d/claude-code.png  | Bin 0 -> 207113 bytes
 data/artifacts/dfaee602df0d/qwen25-7b.html   |  86 +++++
 data/artifacts/dfaee602df0d/qwen25-7b.png    | Bin 0 -> 6336 bytes
 data/artifacts/ea271db82cff/qwen25-7b.html   | 196 +++++++++++
 data/artifacts/ea271db82cff/qwen25-7b.png    | Bin 0 -> 6780 bytes
 data/artifacts/ea271db82cff/qwen3-14b.html   | 444 +++++++++++++++++++++++++
 data/artifacts/ea271db82cff/qwen3-14b.png    | Bin 0 -> 197948 bytes
 data/challenges.json                         |  53 +--
 data/landings/10c3c876aa67-grok.html         | 222 +++++++++++++
 idea-run/night-cycle.sh                      |  43 +++
 11 files changed, 1494 insertions(+), 23 deletions(-)

diff --git a/data/artifacts/dfaee602df0d/claude-code.html b/data/artifacts/dfaee602df0d/claude-code.html
new file mode 100644
index 0000000..fb8fdfb
--- /dev/null
+++ b/data/artifacts/dfaee602df0d/claude-code.html
@@ -0,0 +1,473 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Agent Swarm // Snake</title>
+<style>
+  :root{
+    --bg:#05070d;
+    --grid:#0c1220;
+    --neon:#00e5ff;
+    --neon2:#7cff6b;
+    --task:#ff2fb0;
+    --task2:#ffd84a;
+    --ink:#cde8ff;
+    --dim:#4a6a86;
+    --danger:#ff3b5c;
+  }
+  *{box-sizing:border-box;margin:0;padding:0}
+  html,body{height:100%}
+  body{
+    background:
+      radial-gradient(1200px 700px at 50% -10%, #0a1830 0%, transparent 60%),
+      radial-gradient(900px 600px at 90% 110%, #180a2e 0%, transparent 55%),
+      var(--bg);
+    color:var(--ink);
+    font-family:'Segoe UI',Roboto,-apple-system,system-ui,sans-serif;
+    display:flex;flex-direction:column;align-items:center;justify-content:center;
+    min-height:100%;
+    overflow:hidden;
+    -webkit-font-smoothing:antialiased;
+  }
+  .wrap{
+    display:flex;flex-direction:column;align-items:center;gap:14px;
+    padding:18px;
+  }
+  header{
+    display:flex;flex-direction:column;align-items:center;gap:2px;
+  }
+  h1{
+    font-size:22px;font-weight:800;letter-spacing:3px;
+    text-transform:uppercase;
+    background:linear-gradient(90deg,var(--neon),var(--neon2));
+    -webkit-background-clip:text;background-clip:text;color:transparent;
+    filter:drop-shadow(0 0 10px rgba(0,229,255,.35));
+  }
+  .sub{font-size:10px;letter-spacing:4px;color:var(--dim);text-transform:uppercase}
+
+  .hud{
+    display:flex;gap:10px;flex-wrap:wrap;justify-content:center;width:100%;
+  }
+  .card{
+    background:rgba(10,20,36,.7);
+    border:1px solid rgba(0,229,255,.18);
+    border-radius:10px;
+    padding:8px 14px;min-width:96px;text-align:center;
+    box-shadow:inset 0 0 20px rgba(0,229,255,.05);
+    backdrop-filter:blur(4px);
+  }
+  .card .lbl{font-size:9px;letter-spacing:2px;color:var(--dim);text-transform:uppercase}
+  .card .val{font-size:20px;font-weight:800;color:var(--neon);text-shadow:0 0 12px rgba(0,229,255,.5)}
+  .card.alt .val{color:var(--neon2);text-shadow:0 0 12px rgba(124,255,107,.5)}
+  .card.tok .val{color:var(--task);text-shadow:0 0 12px rgba(255,47,176,.5)}
+
+  .stage{position:relative}
+  canvas{
+    display:block;
+    border-radius:14px;
+    border:1px solid rgba(0,229,255,.25);
+    box-shadow:0 0 40px rgba(0,229,255,.12), inset 0 0 60px rgba(0,0,0,.6);
+    background:var(--grid);
+    touch-action:none;
+  }
+  .overlay{
+    position:absolute;inset:0;
+    display:flex;flex-direction:column;align-items:center;justify-content:center;
+    gap:14px;text-align:center;
+    background:radial-gradient(circle at 50% 45%, rgba(6,10,20,.72), rgba(3,5,10,.92));
+    border-radius:14px;
+    backdrop-filter:blur(3px);
+    transition:opacity .25s ease;
+  }
+  .overlay.hide{opacity:0;pointer-events:none}
+  .overlay h2{
+    font-size:30px;font-weight:900;letter-spacing:2px;text-transform:uppercase;
+    background:linear-gradient(90deg,var(--neon),var(--task));
+    -webkit-background-clip:text;background-clip:text;color:transparent;
+    filter:drop-shadow(0 0 14px rgba(255,47,176,.35));
+  }
+  .overlay p{font-size:12px;color:var(--dim);max-width:320px;line-height:1.6;letter-spacing:.5px}
+  .overlay .big{font-size:14px;color:var(--ink)}
+  .keys{display:flex;gap:6px;flex-wrap:wrap;justify-content:center;margin-top:4px}
+  .key{
+    font-size:10px;letter-spacing:1px;color:var(--neon);
+    border:1px solid rgba(0,229,255,.3);border-radius:6px;padding:4px 8px;
+    background:rgba(0,229,255,.06);
+  }
+  button{
+    margin-top:8px;
+    font-family:inherit;font-size:12px;font-weight:800;letter-spacing:2px;
+    text-transform:uppercase;color:#03121a;cursor:pointer;
+    background:linear-gradient(90deg,var(--neon),var(--neon2));
+    border:none;border-radius:9px;padding:12px 28px;
+    box-shadow:0 0 24px rgba(0,229,255,.35);
+    transition:transform .1s ease, box-shadow .2s ease;
+  }
+  button:hover{transform:translateY(-2px);box-shadow:0 0 34px rgba(0,229,255,.55)}
+  button:active{transform:translateY(0)}
+  .foot{font-size:10px;color:var(--dim);letter-spacing:2px}
+</style>
+</head>
+<body>
+  <div class="wrap">
+    <header>
+      <h1>Agent Swarm</h1>
+      <div class="sub">distributed task-collection protocol</div>
+    </header>
+
+    <div class="hud">
+      <div class="card"><div class="lbl">Score</div><div class="val" id="score">0</div></div>
+      <div class="card tok"><div class="lbl">Tokens Processed</div><div class="val" id="tokens">0</div></div>
+      <div class="card alt"><div class="lbl">Swarm Size</div><div class="val" id="size">1</div></div>
+      <div class="card"><div class="lbl">Best</div><div class="val" id="best">0</div></div>
+    </div>
+
+    <div class="stage">
+      <canvas id="game" width="520" height="520"></canvas>
+      <div class="overlay" id="overlay">
+        <h2 id="ovTitle">Deploy Swarm</h2>
+        <p id="ovText">Steer your chain of glowing agent nodes across the grid. Consume pulsing <b style="color:var(--task)">task tokens</b> — each one grows the swarm and increments tokens processed. Avoid colliding with your own agents.</p>
+        <div class="keys">
+          <span class="key">↑ ← ↓ →</span><span class="key">W A S D</span><span class="key">SPACE pause</span>
+        </div>
+        <button id="startBtn">Initialize</button>
+      </div>
+    </div>
+
+    <div class="foot">60fps · no external assets · self-collision = protocol failure</div>
+  </div>
+
+<script>
+(() => {
+  const canvas = document.getElementById('game');
+  const ctx = canvas.getContext('2d');
+  const COLS = 26, ROWS = 26;
+  const CELL = canvas.width / COLS;
+
+  const el = {
+    score: document.getElementById('score'),
+    tokens: document.getElementById('tokens'),
+    size: document.getElementById('size'),
+    best: document.getElementById('best'),
+    overlay: document.getElementById('overlay'),
+    ovTitle: document.getElementById('ovTitle'),
+    ovText: document.getElementById('ovText'),
+    startBtn: document.getElementById('startBtn'),
+  };
+
+  let snake, dir, nextDir, task, score, tokens, best = 0;
+  let state = 'menu'; // menu | playing | paused | dead
+  let moveInterval = 120;   // ms per grid step (movement logic)
+  let acc = 0, last = 0;
+  let prevSnake = [];       // positions at start of current step (for interpolation)
+  let particles = [];
+  let shake = 0;
+  let taskPulse = 0;
+
+  function reset() {
+    const cx = Math.floor(COLS/2), cy = Math.floor(ROWS/2);
+    snake = [{x:cx,y:cy},{x:cx-1,y:cy},{x:cx-2,y:cy}];
+    prevSnake = snake.map(s => ({...s}));
+    dir = {x:1,y:0};
+    nextDir = {x:1,y:0};
+    score = 0; tokens = 0;
+    moveInterval = 120;
+    particles = [];
+    spawnTask();
+    updateHUD();
+  }
+
+  function spawnTask() {
+    let p;
+    do {
+      p = { x: Math.floor(Math.random()*COLS), y: Math.floor(Math.random()*ROWS) };
+    } while (snake.some(s => s.x===p.x && s.y===p.y));
+    task = p;
+  }
+
+  function updateHUD() {
+    el.score.textContent = score;
+    el.tokens.textContent = tokens;
+    el.size.textContent = snake.length;
+    el.best.textContent = best;
+  }
+
+  function step() {
+    prevSnake = snake.map(s => ({...s}));
+    dir = nextDir;
+    const head = snake[0];
+    const nx = head.x + dir.x;
+    const ny = head.y + dir.y;
+
+    // wall wrap? No — walls are collisions via bounds -> game over on edge would be harsh;
+    // classic feel: hitting wall ends run.
+    if (nx < 0 || nx >= COLS || ny < 0 || ny >= ROWS) { die(); return; }
+
+    // self collision (ignore tail cell that will move away unless growing)
+    for (let i = 0; i < snake.length - 1; i++) {
+      if (snake[i].x === nx && snake[i].y === ny) { die(); return; }
+    }
+
+    const newHead = {x:nx, y:ny};
+    snake.unshift(newHead);
+
+    if (nx === task.x && ny === task.y) {
+      score += 10;
+      tokens += 1 + Math.floor(snake.length/4);
+      burst(task.x, task.y);
+      shake = 6;
+      if (moveInterval > 65) moveInterval -= 2.2;
+      spawnTask();
+    } else {
+      snake.pop();
+    }
+    updateHUD();
+  }
+
+  function die() {
+    state = 'dead';
+    shake = 16;
+    for (const s of snake) burst(s.x, s.y, true);
+    if (score > best) best = score;
+    updateHUD();
+    el.ovTitle.textContent = 'Protocol Failure';
+    el.ovText.innerHTML = `The swarm collapsed at <b style="color:var(--neon2)">${snake.length}</b> agents.<br>Score <b style="color:var(--neon)">${score}</b> · Tokens processed <b style="color:var(--task)">${tokens}</b>.`;
+    el.startBtn.textContent = 'Redeploy';
+    el.overlay.classList.remove('hide');
+  }
+
+  function burst(gx, gy, red=false) {
+    const px = gx*CELL + CELL/2, py = gy*CELL + CELL/2;
+    const n = red ? 5 : 12;
+    for (let i=0;i<n;i++){
+      const a = Math.random()*Math.PI*2;
+      const sp = 0.6 + Math.random()*2.2;
+      particles.push({
+        x:px, y:py,
+        vx:Math.cos(a)*sp, vy:Math.sin(a)*sp,
+        life:1, r:1.5+Math.random()*2.5,
+        red
+      });
+    }
+  }
+
+  // ---- Rendering ----
+  function drawGrid() {
+    ctx.strokeStyle = 'rgba(0,229,255,0.045)';
+    ctx.lineWidth = 1;
+    ctx.beginPath();
+    for (let i=0;i<=COLS;i++){
+      ctx.moveTo(i*CELL+.5,0); ctx.lineTo(i*CELL+.5,canvas.height);
+    }
+    for (let j=0;j<=ROWS;j++){
+      ctx.moveTo(0,j*CELL+.5); ctx.lineTo(canvas.width,j*CELL+.5);
+    }
+    ctx.stroke();
+  }
+
+  function lerp(a,b,t){ return a + (b-a)*t; }
+
+  function render(t) {
+    ctx.clearRect(0,0,canvas.width,canvas.height);
+
+    ctx.save();
+    if (shake > 0) {
+      ctx.translate((Math.random()-.5)*shake, (Math.random()-.5)*shake);
+    }
+
+    drawGrid();
+
+    // task token (pulsing)
+    taskPulse += 0.08;
+    if (task) {
+      const tx = task.x*CELL + CELL/2, ty = task.y*CELL + CELL/2;
+      const pulse = 0.5 + Math.sin(taskPulse)*0.5;
+      const rad = CELL*0.34 + pulse*CELL*0.14;
+      const g = ctx.createRadialGradient(tx,ty,0,tx,ty,rad*2.2);
+      g.addColorStop(0,'rgba(255,47,176,0.9)');
+      g.addColorStop(0.4,'rgba(255,216,74,0.4)');
+      g.addColorStop(1,'rgba(255,47,176,0)');
+      ctx.fillStyle = g;
+      ctx.beginPath(); ctx.arc(tx,ty,rad*2.2,0,Math.PI*2); ctx.fill();
+
+      ctx.fillStyle = '#ffd84a';
+      ctx.shadowColor = '#ff2fb0'; ctx.shadowBlur = 18;
+      ctx.beginPath(); ctx.arc(tx,ty,rad*0.6,0,Math.PI*2); ctx.fill();
+      ctx.shadowBlur = 0;
+    }
+
+    // interpolation factor for smooth movement
+    const it = (state === 'playing') ? Math.min(1, acc / moveInterval) : 1;
+
+    // snake as connecting glowing chain
+    const pts = [];
+    for (let i=0;i<snake.length;i++){
+      const cur = snake[i];
+      const prev = prevSnake[i] || cur;
+      const x = lerp(prev.x, cur.x, it)*CELL + CELL/2;
+      const y = lerp(prev.y, cur.y, it)*CELL + CELL/2;
+      pts.push({x,y});
+    }
+
+    // connective link
+    if (pts.length > 1) {
+      ctx.lineCap = 'round';
+      ctx.lineJoin = 'round';
+      ctx.strokeStyle = 'rgba(0,229,255,0.28)';
+      ctx.lineWidth = CELL*0.7;
+      ctx.shadowColor = '#00e5ff'; ctx.shadowBlur = 14;
+      ctx.beginPath();
+      ctx.moveTo(pts[0].x,pts[0].y);
+      for (let i=1;i<pts.length;i++) ctx.lineTo(pts[i].x,pts[i].y);
+      ctx.stroke();
+      ctx.shadowBlur = 0;
+    }
+
+    // agent nodes
+    for (let i=pts.length-1;i>=0;i--){
+      const p = pts[i];
+      const head = i===0;
+      const frac = i/Math.max(1,pts.length);
+      const baseR = head ? CELL*0.44 : CELL*0.30*(1-frac*0.35);
+      // color gradient from cyan head to green tail
+      const c1 = head ? '#aef8ff' : mix('#00e5ff','#7cff6b',frac);
+      const c2 = head ? '#00e5ff' : mix('#0090c0','#3fae4a',frac);
+
+      const g = ctx.createRadialGradient(p.x,p.y,0,p.x,p.y,baseR*1.6);
+      g.addColorStop(0,c1);
+      g.addColorStop(0.6,c2);
+      g.addColorStop(1,'rgba(0,20,30,0)');
+      ctx.fillStyle = g;
+      ctx.beginPath(); ctx.arc(p.x,p.y,baseR*1.6,0,Math.PI*2); ctx.fill();
+
+      ctx.fillStyle = c1;
+      ctx.beginPath(); ctx.arc(p.x,p.y,baseR*0.55,0,Math.PI*2); ctx.fill();
+
+      // head "eye" indicating direction
+      if (head) {
+        ctx.fillStyle = '#031018';
+        const ex = p.x + dir.x*baseR*0.4;
+        const ey = p.y + dir.y*baseR*0.4;
+        ctx.beginPath(); ctx.arc(ex,ey,baseR*0.16,0,Math.PI*2); ctx.fill();
+      }
+    }
+
+    // particles
+    for (const pr of particles) {
+      ctx.globalAlpha = Math.max(0,pr.life);
+      ctx.fillStyle = pr.red ? '#ff3b5c' : '#ffd84a';
+      ctx.beginPath(); ctx.arc(pr.x,pr.y,pr.r,0,Math.PI*2); ctx.fill();
+    }
+    ctx.globalAlpha = 1;
+
+    ctx.restore();
+  }
+
+  function mix(a,b,t){
+    const pa=[parseInt(a.slice(1,3),16),parseInt(a.slice(3,5),16),parseInt(a.slice(5,7),16)];
+    const pb=[parseInt(b.slice(1,3),16),parseInt(b.slice(3,5),16),parseInt(b.slice(5,7),16)];
+    const r=Math.round(lerp(pa[0],pb[0],t));
+    const g=Math.round(lerp(pa[1],pb[1],t));
+    const bl=Math.round(lerp(pa[2],pb[2],t));
+    return `rgb(${r},${g},${bl})`;
+  }
+
+  // ---- Main loop (60fps via rAF) ----
+  function loop(ts) {
+    const dt = Math.min(50, ts - last || 0);
+    last = ts;
+
+    if (state === 'playing') {
+      acc += dt;
+      while (acc >= moveInterval) {
+        acc -= moveInterval;
+        step();
+        if (state !== 'playing') break;
+      }
+    }
+
+    // update particles + shake every frame
+    for (let i=particles.length-1;i>=0;i--){
+      const pr=particles[i];
+      pr.x+=pr.vx; pr.y+=pr.vy;
+      pr.vx*=0.94; pr.vy*=0.94;
+      pr.life-=0.03;
+      if (pr.life<=0) particles.splice(i,1);
+    }
+    if (shake>0) shake*=0.86;
+    if (shake<0.3) shake=0;
+
+    render(ts);
+    requestAnimationFrame(loop);
+  }
+
+  // ---- Input ----
+  const DIRS = {
+    ArrowUp:{x:0,y:-1}, ArrowDown:{x:0,y:1}, ArrowLeft:{x:-1,y:0}, ArrowRight:{x:1,y:0},
+    w:{x:0,y:-1}, s:{x:0,y:1}, a:{x:-1,y:0}, d:{x:1,y:0},
+    W:{x:0,y:-1}, S:{x:0,y:1}, A:{x:-1,y:0}, D:{x:1,y:0}
+  };
+
+  window.addEventListener('keydown', (e) => {
+    if (e.key === ' ') {
+      e.preventDefault();
+      if (state === 'playing') { state='paused'; showOverlay('Paused','Swarm on standby. Press SPACE or Resume.','Resume'); }
+      else if (state === 'paused') { hideOverlay(); state='playing'; }
+      return;
+    }
+    if (e.key === 'Enter' && state !== 'playing') { start(); return; }
+    const nd = DIRS[e.key];
+    if (nd) {
+      e.preventDefault();
+      // prevent 180° reversal
+      if (nd.x === -dir.x && nd.y === -dir.y) return;
+      if (nd.x === dir.x && nd.y === dir.y) return;
+      nextDir = nd;
+    }
+  }, {passive:false});
+
+  // touch / swipe
+  let touchStart = null;
+  canvas.addEventListener('touchstart', e => {
+    touchStart = {x:e.touches[0].clientX, y:e.touches[0].clientY};
+  }, {passive:true});
+  canvas.addEventListener('touchmove', e => {
+    if (!touchStart) return;
+    const dx = e.touches[0].clientX - touchStart.x;
+    const dy = e.touches[0].clientY - touchStart.y;
+    if (Math.abs(dx)+Math.abs(dy) < 24) return;
+    let nd;
+    if (Math.abs(dx) > Math.abs(dy)) nd = {x:dx>0?1:-1,y:0};
+    else nd = {x:0,y:dy>0?1:-1};
+    if (!(nd.x===-dir.x&&nd.y===-dir.y) && !(nd.x===dir.x&&nd.y===dir.y)) nextDir = nd;
+    touchStart = null;
+  }, {passive:true});
+
+  function showOverlay(title,text,btn){
+    el.ovTitle.textContent = title;
+    el.ovText.innerHTML = text;
+    el.startBtn.textContent = btn;
+    el.overlay.classList.remove('hide');
+  }
+  function hideOverlay(){ el.overlay.classList.add('hide'); }
+
+  function start() {
+    reset();
+    hideOverlay();
+    acc = 0;
+    state = 'playing';
+  }
+
+  el.startBtn.addEventListener('click', () => {
+    if (state === 'paused') { hideOverlay(); state='playing'; }
+    else start();
+  });
+
+  // boot
+  reset();
+  requestAnimationFrame(loop);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/dfaee602df0d/claude-code.png b/data/artifacts/dfaee602df0d/claude-code.png
new file mode 100644
index 0000000..492449d
Binary files /dev/null and b/data/artifacts/dfaee602df0d/claude-code.png differ
diff --git a/data/artifacts/dfaee602df0d/qwen25-7b.html b/data/artifacts/dfaee602df0d/qwen25-7b.html
new file mode 100644
index 0000000..f268e58
--- /dev/null
+++ b/data/artifacts/dfaee602df0d/qwen25-7b.html
@@ -0,0 +1,86 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>AI Swarm Snake</title>
+    <style>
+        body { margin: 0; overflow: hidden; background: #0a192f; }
+        canvas { display: block; width: 100%; height: 100% }
+        .node, .task { fill: #67c23a; stroke: #fff; stroke-width: 1.5px; opacity: 0.8; transition: all 0.1s ease; }
+        .node:hover, .task:hover { opacity: 1; }
+        .game-over { position: absolute; top: 40%; left: 32%; width: 36%; text-align: center; font-family: sans-serif; color: #87ceeb; }
+    </style>
+</head>
+<body>
+<canvas id="gameCanvas"></canvas>
+<script>
+const canvas = document.getElementById('gameCanvas');
+const ctx = canvas.getContext('2d');
+canvas.width = 400;
+canvas.height = 400;
+
+let nodes = [{x:150, y:150}];
+let tasks = [];
+let score = 0;
+let dir = {x:0, y:0};
+let gameSpeed = 60;
+
+function drawNode(x, y) {
+    ctx.fillStyle = '#67c23a';
+    ctx.fillRect(x * 10 + 1, y * 10 + 1, 8, 8);
+}
+
+function drawTask(x, y) {
+    ctx.fillStyle = '#ff5722';
+    ctx.fillRect(x * 10 + 1, y * 10 + 1, 8, 8);
+}
+
+function update() {
+    if (!dir.x && !dir.y) return;
+    let headX = Math.floor((nodes[0].x += dir.x) / 10) % 40;
+    let headY = Math.floor((nodes[0].y += dir.y) / 10) % 40;
+
+    if (headX < 0 || headX >= 40 || headY < 0 || headY >= 40 || nodes.some(n => n.x === headX && n.y === headY)) {
+        alert('Game Over');
+        return;
+    }
+
+    let newNode = {x: headX, y: headY};
+    if (tasks.length > 0) {
+        tasks.forEach(task => task.active && (task.active = false));
+        while (nodes.some(n => n.x === newNode.x && n.y === newNode.y)) newNode = {x: Math.floor(Math.random() * 40), y: Math.floor(Math.random() * 40)};
+        nodes.unshift(newNode);
+        tasks.push({x:newNode.x, y:newNode.y, active:true});
+        score++;
+    } else {
+        nodes.unshift(newNode);
+        nodes.pop();
+    }
+}
+
+function draw() {
+    ctx.clearRect(0, 0, canvas.width, canvas.height);
+    nodes.forEach(drawNode);
+    tasks.forEach((task) => task.active && drawTask(task.x, task.y));
+    document.title = `AI Swarm Snake | Score: ${score}`;
+}
+
+document.addEventListener('keydown', e => {
+    if (e.key === 'ArrowUp' || e.key === 'w') dir = {x:0, y:-1};
+    else if (e.key === 'ArrowDown' || e.key === 's') dir = {x:0, y:1};
+    else if (e.key === 'ArrowLeft' || e.key === 'a') dir = {x:-1, y:0};
+    else if (e.key === 'ArrowRight' || e.key === 'd') dir = {x:1, y:0};
+});
+
+setInterval(() => {
+    update();
+    draw();
+}, 1000 / gameSpeed);
+</script>
+<div class="game-over">Game Over. Score: <span id="score">0</span></div>
+<script>
+document.getElementById('score').innerText = score;
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/dfaee602df0d/qwen25-7b.png b/data/artifacts/dfaee602df0d/qwen25-7b.png
new file mode 100644
index 0000000..8980ea8
Binary files /dev/null and b/data/artifacts/dfaee602df0d/qwen25-7b.png differ
diff --git a/data/artifacts/ea271db82cff/qwen25-7b.html b/data/artifacts/ea271db82cff/qwen25-7b.html
new file mode 100644
index 0000000..589cf9a
--- /dev/null
+++ b/data/artifacts/ea271db82cff/qwen25-7b.html
@@ -0,0 +1,196 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Metaball Field</title>
+<style>
+body { margin: 0; overflow: hidden; }
+canvas { display: block; background: #222; width: 100vw; height: 100vh; cursor: none; }
+#controls { position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); }
+</style>
+</head>
+<body>
+<div id="controls">
+<label for="theme">Theme:</label>
+<select id="theme">
+<option value="classic">Classic</option>
+<option value="summer">Summer</option>
+<option value="winter">Winter</option>
+</select>
+<br><br>
+<label for="count">Blob Count:</label>
+<input type="range" min="10" max="50" step="1" id="count">
+<span id="countVal">25</span>
+</div>
+<canvas></canvas>
+<script>
+const canvas = document.querySelector('canvas');
+const ctx = canvas.getContext('2d');
+
+const THEMES = {
+    classic: { bg: ['#000', '#111'] },
+    summer: { bg: ['#fff', '#ff9'] },
+    winter: { bg: ['#0f0', '#0ff'] }
+};
+
+let theme = THEMES.classic;
+let count = 25;
+
+function buildRamp(theme) {
+    const ramp = new Uint8ClampedArray(768);
+    for (let i = 0; i < 256; i++) {
+        const p = i / 255;
+        let a = theme.bg[0], b = theme.bg[1];
+        if (p >= stops[0][0] && p <= stops[stops.length - 1][0]) { a = stops[0]; b = stops[stops.length - 1]; }
+        const span = (b[0] - a[0]) || 1;
+        const f = (p - a[0]) / span;
+        ramp[i * 3]   = a[1] + (b[1] - a[1]) * f;
+        ramp[i * 3 + 1] = a[2] + (b[2] - a[2]) * f;
+    }
+}
+
+const stops = [[0, '#000'], [1, '#fff']];
+buildRamp(theme);
+
+let W = canvas.width = Math.max(1, window.innerWidth);
+let H = canvas.height = Math.max(1, window.innerHeight);
+let minDim = Math.min(W, H);
+let scale = 0.22;
+let imgData = null, buf = null;
+let bgGrad = ctx.createLinearGradient(0, 0, 0, H);
+bgGrad.addColorStop(0, theme.bg[0]);
+bgGrad.addColorStop(1, theme.bg[1]);
+
+function resize() {
+    W = canvas.width = Math.max(1, window.innerWidth);
+    H = canvas.height = Math.max(1, window.innerHeight);
+    minDim = Math.min(W, H);
+    scale = 0.26 < 200 / Math.max(W, H) ? 0.26 : 200 / Math.max(W, H);
+    lowW = Math.round(W * scale);
+    lowH = Math.round(H * scale);
+    imgData = offCtx.createImageData(lowW, lowH);
+    buf = imgData.data;
+    bgGrad = ctx.createLinearGradient(0, 0, 0, H);
+    bgGrad.addColorStop(0, theme.bg[0]);
+    bgGrad.addColorStop(1, theme.bg[1]);
+}
+
+function makeBlob() {
+    return { x: Math.random() * W, y: Math.random() * H, rFrac: 0.05 + Math.random() * 0.055, vs: 0.25 + Math.random() * 0.5, vp: Math.random() * Math.PI * 2, vamp: 45 + Math.random() * 70, hs: 0.15 + Math.random() * 0.35, hp: Math.random() * Math.PI * 2, hamp: 18 + Math.random() * 34, ps: 0.4 + Math.random() * 0.8, r: 40 };
+}
+
+function setCount(n) {
+    while (blobs.length < n) blobs.push(makeBlob());
+    while (blobs.length > n) blobs.pop();
+}
+
+const blobs = [];
+
+function recomputeRadius(b) {
+    b.r = b.rFrac * minDim;
+}
+
+function update(t, dt) {
+    for (const b of blobs) {
+        const vy = Math.sin(t * b.vs + b.vp) * b.vamp;
+        const vx = Math.sin(t * b.hs + b.hp) * b.hamp;
+        b.x += vx * dt;
+        b.y += vy * dt;
+
+        const pad = b.r * 0.35;
+        if (b.x < pad) { b.x = pad; b.hp += Math.PI; }
+        else if (b.x > W - pad) { b.x = W - pad; b.hp += Math.PI; }
+        if (b.y < pad) { b.y = pad; b.vp += Math.PI; }
+        else if (b.y > H - pad) { b.y = H - pad; b.vp += Math.PI; }
+
+        b.rr = b.r * (1 + Math.sin(t * b.ps + b.pp) * 0.14);
+    }
+}
+
+function renderField(t) {
+    const n = blobs.length;
+    const bx = new Float32Array(n), by = new Float32Array(n), br2 = new Float32Array(n);
+    for (let i = 0; i < n; i++) {
+        const b = blobs[i];
+        bx[i] = b.x * scale;
+        by[i] = b.y * scale;
+        const rl = b.rr * scale;
+        br2[i] = rl * rl;
+    }
+
+    let p = 0;
+    for (let y = 0; y < lowH; y++) {
+        for (let x = 0; x < lowW; x++) {
+            let sum = 0;
+            for (let i = 0; i < n; i++) {
+                const dx = x - bx[i], dy = y - by[i];
+                const d2 = dx * dx + dy * dy + 0.0001;
+                sum += br2[i] / d2;
+            }
+            let a;
+            if (sum < 0.65) {
+                a = 0;
+            } else {
+                a = sum < 1.05 ? (sum - 0.65) / 0.4 : 1;
+                const ct = (sum - 0.7) / 2.6;
+                if (ct < 0) ct = 0; else if (ct > 1) ct = 1;
+                const ci = (ct * 255) | 0;
+                const r3 = ci * 3;
+                buf[p]     = ramp[r3];
+                buf[p + 1] = ramp[r3 + 1];
+                buf[p + 2] = ramp[r3 + 2];
+            }
+            buf[p + 3] = a * 255;
+            p += 4;
+        }
+    }
+    offCtx.putImageData(imgData, 0, 0);
+}
+
+let last = performance.now();
+function frame(now) {
+    let dt = (now - last) / 1000;
+    if (dt > 0.05) dt = 0.05;
+    last = now;
+    const t = now / 1000;
+
+    update(t, dt);
+    renderField(t);
+
+    ctx.globalCompositeOperation = "source-over";
+    ctx.fillStyle = bgGrad;
+    ctx.fillRect(0, 0, W, H);
+    ctx.imageSmoothingEnabled = true;
+    ctx.imageSmoothingQuality = "high";
+    ctx.drawImage(off, 0, 0, lowW, lowH, 0, 0, W, H);
+    ctx.globalCompositeOperation = "lighter";
+    ctx.globalAlpha = 0.28;
+    ctx.drawImage(off, 0, 0, lowW, lowH, 0, 0, W, H);
+    ctx.globalAlpha = 1;
+    ctx.globalCompositeOperation = "source-over";
+
+    requestAnimationFrame(frame);
+}
+
+setCount(count);
+resize();
+requestAnimationFrame(frame);
+
+document.getElementById('theme').addEventListener('change', function() {
+    theme = THEMES[this.value] || THEMES.classic;
+    buildRamp(theme);
+    bgGrad = ctx.createLinearGradient(0, 0, 0, H);
+    bgGrad.addColorStop(0, theme.bg[0]);
+    bgGrad.addColorStop(1, theme.bg[1]);
+});
+
+document.getElementById('count').addEventListener('input', function() {
+    countVal.textContent = this.value;
+    setCount(parseInt(this.value, 10));
+});
+
+window.addEventListener('resize', resize);
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/ea271db82cff/qwen25-7b.png b/data/artifacts/ea271db82cff/qwen25-7b.png
new file mode 100644
index 0000000..d8ea6f1
Binary files /dev/null and b/data/artifacts/ea271db82cff/qwen25-7b.png differ
diff --git a/data/artifacts/ea271db82cff/qwen3-14b.html b/data/artifacts/ea271db82cff/qwen3-14b.html
new file mode 100644
index 0000000..2f46210
--- /dev/null
+++ b/data/artifacts/ea271db82cff/qwen3-14b.html
@@ -0,0 +1,444 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+<meta charset="UTF-8">
+<meta name="viewport" content="width=device-width, initial-scale=1.0">
+<title>Metaball Lava Lamp</title>
+<style>
+  * { margin: 0; padding: 0; box-sizing: border-box; }
+  html, body { width: 100%; height: 100%; overflow: hidden; background: #000; }
+  body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; }
+
+  #lamp {
+    display: block;
+    width: 100vw;
+    height: 100vh;
+  }
+
+  #panel {
+    position: fixed;
+    top: 18px;
+    left: 18px;
+    padding: 16px 18px 18px;
+    border-radius: 16px;
+    background: rgba(20, 12, 30, 0.42);
+    backdrop-filter: blur(14px) saturate(140%);
+    -webkit-backdrop-filter: blur(14px) saturate(140%);
+    border: 1px solid rgba(255, 255, 255, 0.12);
+    box-shadow: 0 10px 40px rgba(0, 0, 0, 0.45);
+    color: #f4eef8;
+    width: 224px;
+    user-select: none;
+    z-index: 10;
+    transition: opacity 0.3s;
+  }
+
+  #panel h1 {
+    font-size: 13px;
+    font-weight: 600;
+    letter-spacing: 1.6px;
+    text-transform: uppercase;
+    opacity: 0.85;
+    margin-bottom: 14px;
+    display: flex;
+    align-items: center;
+    gap: 8px;
+  }
+
+  #panel h1 .dot {
+    width: 9px;
+    height: 9px;
+    border-radius: 50%;
+    background: radial-gradient(circle at 30% 30%, #ffe066, #ff2d55);
+    box-shadow: 0 0 10px #ff5e3a;
+  }
+
+  .row {
+    margin-bottom: 14px;
+  }
+
+  .row:last-child {
+    margin-bottom: 0;
+  }
+
+  label {
+    display: block;
+    font-size: 11px;
+    letter-spacing: 0.5px;
+    opacity: 0.7;
+    margin-bottom: 7px;
+    display: flex;
+    justify-content: space-between;
+  }
+
+  label b {
+    opacity: 0.95;
+    font-weight: 600;
+  }
+
+  select {
+    width: 100%;
+    padding: 8px 10px;
+    border-radius: 9px;
+    background: rgba(255, 255, 255, 0.07);
+    color: #f4eef8;
+    border: 1px solid rgba(255, 255, 255, 0.14);
+    font-size: 13px;
+    outline: none;
+    cursor: pointer;
+    appearance: none;
+    -webkit-appearance: none;
+    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'><path d='M2 4l4 4 4-4' stroke='%23f4eef8' stroke-width='1.6' fill='none' stroke-linecap='round' stroke-linejoin='round'/></svg>");
+    background-repeat: no-repeat;
+    background-position: right 10px center;
+  }
+
+  select option {
+    background: #1a1024;
+    color: #f4eef8;
+  }
+
+  input[type=range] {
+    width: 100%;
+    -webkit-appearance: none;
+    appearance: none;
+    height: 5px;
+    border-radius: 5px;
+    background: rgba(255, 255, 255, 0.18);
+    outline: none;
+    cursor: pointer;
+  }
+
+  input[type=range]::-webkit-slider-thumb {
+    -webkit-appearance: none;
+    width: 17px;
+    height: 17px;
+    border-radius: 50%;
+    background: radial-gradient(circle at 32% 30%, #ffe9a8, #ff7a00);
+    border: 2px solid rgba(255, 255, 255, 0.5);
+    box-shadow: 0 0 8px rgba(255, 120, 40, 0.7);
+  }
+
+  input[type=range]::-moz-range-thumb {
+    width: 15px;
+    height: 15px;
+    border-radius: 50%;
+    background: radial-gradient(circle at 32% 30%, #ffe9a8, #ff7a00);
+    border: 2px solid rgba(255, 255, 255, 0.5);
+    box-shadow: 0 0 8px rgba(255, 120, 40, 0.7);
+  }
+
+  #hint {
+    position: fixed;
+    bottom: 14px;
+    left: 0;
+    right: 0;
+    text-align: center;
+    color: rgba(255, 255, 255, 0.28);
+    font-size: 11px;
+    letter-spacing: 1px;
+    z-index: 5;
+    pointer-events: none;
+  }
+</style>
+</head>
+<body>
+<canvas id="lamp"></canvas>
+
+<div id="panel">
+  <h1><span class="dot"></span>Lava Lamp</h1>
+  <div class="row">
+    <label>Color Theme</label>
+    <select id="theme">
+      <option value="classic">Classic Lava</option>
+      <option value="ember">Ember Glow</option>
+      <option value="sunset">Sunset</option>
+      <option value="amethyst">Amethyst</option>
+      <option value="ocean">Deep Ocean</option>
+      <option value="toxic">Toxic</option>
+    </select>
+  </div>
+  <div class="row">
+    <label>Blobs <b id="countVal">12</b></label>
+    <input type="range" id="count" min="3" max="26" value="12">
+  </div>
+</div>
+
+<div id="hint">metaballs rise · fall · merge</div>
+
+<script>
+(function () {
+  "use strict";
+
+  const canvas = document.getElementById("lamp");
+  const ctx = canvas.getContext("2d");
+
+  const off = document.createElement("canvas");
+  const offCtx = off.getContext("2d");
+
+  // Themes with more refined gradients and color stops
+  const THEMES = {
+    classic: {
+      bg: ["#2a0a3a", "#160326"],
+      stops: [
+        [0, "#7a0f3a"],
+        [0.35, "#ff2d55"],
+        [0.7, "#ff8a00"],
+        [1, "#ffe066"]
+      ]
+    },
+    ember: {
+      bg: ["#220a05", "#120302"],
+      stops: [
+        [0, "#7a1500"],
+        [0.4, "#ff3d00"],
+        [0.72, "#ff7a00"],
+        [1, "#ffc46b"]
+      ]
+    },
+    sunset: {
+      bg: ["#1b1035", "#360d38"],
+      stops: [
+        [0, "#5b1250"],
+        [0.35, "#ff4d6d"],
+        [0.7, "#ff9e5e"],
+        [1, "#ffd76e"]
+      ]
+    },
+    amethyst: {
+      bg: ["#14052a", "#26063f"],
+      stops: [
+        [0, "#5a1a8a"],
+        [0.4, "#c04dff"],
+        [0.72, "#ff5edb"],
+        [1, "#ffd1f5"]
+      ]
+    },
+    ocean: {
+      bg: ["#041b2d", "#06263a"],
+      stops: [
+        [0, "#053a5c"],
+        [0.38, "#00c8ff"],
+        [0.72, "#2b8cff"],
+        [1, "#bfeaff"]
+      ]
+    },
+    toxic: {
+      bg: ["#071a07", "#0a250a"],
+      stops: [
+        [0, "#2c5c00"],
+        [0.4, "#7bd400"],
+        [0.72, "#39ff14"],
+        [1, "#eaffb0"]
+      ]
+    }
+  };
+
+  let theme = THEMES.classic;
+  let ramp = new Uint8Array(256 * 3);
+
+  function hexToRgb(h) {
+    h = h.replace("#", "");
+    return [
+      parseInt(h.slice(0, 2), 16),
+      parseInt(h.slice(2, 4), 16),
+      parseInt(h.slice(4, 6), 16)
+    ];
+  }
+
+  function buildRamp(t) {
+    const stops = t.stops;
+    for (let i = 0; i < 256; i++) {
+      const p = i / 255;
+      let a = stops[0], b = stops[stops.length - 1];
+      for (let s = 0; s < stops.length - 1; s++) {
+        if (p >= stops[s][0] && p <= stops[s + 1][0]) {
+          a = stops[s]; b = stops[s + 1]; break;
+        }
+      }
+      const span = (b[0] - a[0]) || 1;
+      const f = (p - a[0]) / span;
+      const ca = hexToRgb(a[1]), cb = hexToRgb(b[1]);
+      ramp[i * 3] = ca[0] + (cb[0] - ca[0]) * f;
+      ramp[i * 3 + 1] = ca[1] + (cb[1] - ca[1]) * f;
+      ramp[i * 3 + 2] = ca[2] + (cb[2] - ca[2]) * f;
+    }
+  }
+
+  buildRamp(theme);
+
+  let W = 0, H = 0, minDim = 0;
+  let lowW = 0, lowH = 0, scale = 0.22;
+  let imgData = null, buf = null;
+  let bgGrad = null;
+
+  function resize() {
+    W = canvas.width = Math.max(1, window.innerWidth);
+    H = canvas.height = Math.max(1, window.innerHeight);
+    minDim = Math.min(W, H);
+
+    scale = Math.min(0.26, 200 / Math.max(W, H));
+    lowW = Math.max(2, Math.round(W * scale));
+    lowH = Math.max(2, Math.round(H * scale));
+    off.width = lowW;
+    off.height = lowH;
+    imgData = offCtx.createImageData(lowW, lowH);
+    buf = imgData.data;
+
+    bgGrad = ctx.createLinearGradient(0, 0, 0, H);
+    bgGrad.addColorStop(0, theme.bg[0]);
+    bgGrad.addColorStop(1, theme.bg[1]);
+
+    for (const b of blobs) recomputeRadius(b);
+  }
+
+  const blobs = [];
+
+  function recomputeRadius(b) {
+    b.r = b.rFrac * minDim;
+  }
+
+  function makeBlob() {
+    const b = {
+      x: Math.random() * (W || 800),
+      y: Math.random() * (H || 600),
+      rFrac: 0.05 + Math.random() * 0.055,
+      vs: 0.25 + Math.random() * 0.5,   // vertical osc speed
+      vp: Math.random() * Math.PI * 2,  // vertical phase
+      vamp: 45 + Math.random() * 70,    // vertical amplitude (px/s)
+      hs: 0.15 + Math.random() * 0.35,  // horizontal speed
+      hp: Math.random() * Math.PI * 2,
+      hamp: 18 + Math.random() * 34,    // horizontal drift
+      ps: 0.4 + Math.random() * 0.8,    // pulse speed
+      pp: Math.random() * Math.PI * 2,
+      r: 40
+    };
+    recomputeRadius(b);
+    return b;
+  }
+
+  function setCount(n) {
+    while (blobs.length < n) blobs.push(makeBlob());
+    while (blobs.length > n) blobs.pop();
+  }
+
+  function update(t, dt) {
+    for (const b of blobs) {
+      const vy = Math.sin(t * b.vs + b.vp) * b.vamp;
+      const vx = Math.sin(t * b.hs + b.hp) * b.hamp;
+      b.x += vx * dt;
+      b.y += vy * dt;
+
+      const pad = b.r * 0.35;
+      if (b.x < pad) {
+        b.x = pad;
+        b.hp += Math.PI;
+      } else if (b.x > W - pad) {
+        b.x = W - pad;
+        b.hp += Math.PI;
+      }
+      if (b.y < pad) {
+        b.y = pad;
+        b.vp += Math.PI;
+      } else if (b.y > H - pad) {
+        b.y = H - pad;
+        b.vp += Math.PI;
+      }
+
+      b.rr = b.r * (1 + Math.sin(t * b.ps + b.pp) * 0.14); // pulsating radius
+    }
+  }
+
+  function renderField(t) {
+    const n = blobs.length;
+    const bx = new Float32Array(n), by = new Float32Array(n), br2 = new Float32Array(n);
+    for (let i = 0; i < n; i++) {
+      const b = blobs[i];
+      bx[i] = b.x * scale;
+      by[i] = b.y * scale;
+      const rl = b.rr * scale;
+      br2[i] = rl * rl;
+    }
+
+    let p = 0;
+    for (let y = 0; y < lowH; y++) {
+      for (let x = 0; x < lowW; x++) {
+        let sum = 0;
+        for (let i = 0; i < n; i++) {
+          const dx = x - bx[i], dy = y - by[i];
+          const d2 = dx * dx + dy * dy + 0.0001;
+          sum += br2[i] / d2;
+        }
+        let a;
+        if (sum < 0.65) {
+          a = 0;
+        } else {
+          a = sum < 1.05 ? (sum - 0.65) / 0.4 : 1;
+          let ct = (sum - 0.7) / 2.6; // color param edge->core
+          if (ct < 0) ct = 0; else if (ct > 1) ct = 1;
+          const ci = (ct * 255) | 0;
+          const r3 = ci * 3;
+          buf[p] = ramp[r3];
+          buf[p + 1] = ramp[r3 + 1];
+          buf[p + 2] = ramp[r3 + 2];
+        }
+        buf[p + 3] = a * 255;
+        p += 4;
+      }
+    }
+    offCtx.putImageData(imgData, 0, 0);
+  }
+
+  let last = performance.now();
+  function frame(now) {
+    let dt = (now - last) / 1000;
+    if (dt > 0.05) dt = 0.05;
+    last = now;
+    const t = now / 1000;
+
+    update(t, dt);
+    renderField(t);
+
+    ctx.globalCompositeOperation = "source-over";
+    ctx.fillStyle = bgGrad;
+    ctx.fillRect(0, 0, W, H);
+
+    ctx.imageSmoothingEnabled = true;
+    ctx.imageSmoothingQuality = "high";
+    ctx.drawImage(off, 0, 0, lowW, lowH, 0, 0, W, H);
+
+    ctx.globalCompositeOperation = "lighter";
+    ctx.globalAlpha = 0.28;
+    ctx.drawImage(off, 0, 0, lowW, lowH, 0, 0, W, H);
+    ctx.globalAlpha = 1;
+    ctx.globalCompositeOperation = "source-over";
+
+    requestAnimationFrame(frame);
+  }
+
+  const themeSel = document.getElementById("theme");
+  const countInp = document.getElementById("count");
+  const countVal = document.getElementById("countVal");
+
+  themeSel.addEventListener("change", function () {
+    theme = THEMES[themeSel.value] || THEMES.classic;
+    buildRamp(theme);
+    bgGrad = ctx.createLinearGradient(0, 0, 0, H);
+    bgGrad.addColorStop(0, theme.bg[0]);
+    bgGrad.addColorStop(1, theme.bg[1]);
+  });
+
+  countInp.addEventListener("input", function () {
+    countVal.textContent = countInp.value;
+    setCount(parseInt(countInp.value, 10));
+  });
+
+  window.addEventListener("resize", resize);
+
+  setCount(parseInt(countInp.value, 10));
+  resize();
+  requestAnimationFrame(frame);
+})();
+</script>
+</body>
+</html>
\ No newline at end of file
diff --git a/data/artifacts/ea271db82cff/qwen3-14b.png b/data/artifacts/ea271db82cff/qwen3-14b.png
new file mode 100644
index 0000000..ed7d3e7
Binary files /dev/null and b/data/artifacts/ea271db82cff/qwen3-14b.png differ
diff --git a/data/challenges.json b/data/challenges.json
index cc7e775..7ac2bba 100644
--- a/data/challenges.json
+++ b/data/challenges.json
@@ -11411,22 +11411,24 @@
     "runs": [
       {
         "model": "qwen3-14b",
-        "status": "running",
+        "status": "done",
         "error": null,
-        "seconds": 0,
-        "cost": null,
+        "seconds": 95,
+        "cost": 0,
         "started_at": "2026-07-25T06:17:53.534Z",
-        "finished_at": null,
+        "finished_at": "2026-07-25T06:19:28.457Z",
         "queued_at": "2026-07-25T06:17:53.526Z",
-        "resumes": 1
+        "resumes": 1,
+        "bytes": 11236,
+        "thumb": true
       },
       {
         "model": "gemma3-12b",
-        "status": "queued",
+        "status": "running",
         "error": null,
         "seconds": 0,
         "cost": null,
-        "started_at": null,
+        "started_at": "2026-07-25T06:19:28.462Z",
         "finished_at": null,
         "queued_at": "2026-07-25T06:17:53.528Z",
         "resumes": 1
@@ -11444,14 +11446,16 @@
       },
       {
         "model": "qwen25-7b",
-        "status": "running",
+        "status": "done",
         "error": null,
-        "seconds": 0,
-        "cost": null,
+        "seconds": 52,
+        "cost": 0,
         "started_at": "2026-07-25T06:17:53.541Z",
-        "finished_at": null,
+        "finished_at": "2026-07-25T06:18:45.048Z",
         "queued_at": "2026-07-25T06:17:53.531Z",
-        "resumes": 1
+        "resumes": 1,
+        "bytes": 6014,
+        "thumb": true
       },
       {
         "model": "hf-qwen-coder-32b",
@@ -11509,13 +11513,15 @@
       },
       {
         "model": "qwen25-7b",
-        "status": "queued",
+        "status": "done",
         "error": null,
-        "seconds": null,
-        "cost": null,
-        "started_at": null,
-        "finished_at": null,
-        "queued_at": "2026-07-25T06:18:24.462Z"
+        "seconds": 23,
+        "cost": 0,
+        "started_at": "2026-07-25T06:18:45.053Z",
+        "finished_at": "2026-07-25T06:19:07.734Z",
+        "queued_at": "2026-07-25T06:18:24.462Z",
+        "bytes": 2758,
+        "thumb": true
       },
       {
         "model": "hf-qwen-coder-32b",
@@ -11529,13 +11535,14 @@
       },
       {
         "model": "claude-code",
-        "status": "running",
+        "status": "done",
         "error": null,
-        "seconds": null,
-        "cost": null,
+        "seconds": 80,
+        "cost": 0,
         "started_at": "2026-07-25T06:18:24.466Z",
-        "finished_at": null,
-        "queued_at": "2026-07-25T06:18:24.465Z"
+        "finished_at": "2026-07-25T06:19:44.371Z",
+        "queued_at": "2026-07-25T06:18:24.465Z",
+        "bytes": 15575
       }
     ]
   }
diff --git a/data/landings/10c3c876aa67-grok.html b/data/landings/10c3c876aa67-grok.html
new file mode 100644
index 0000000..4ca21b6
--- /dev/null
+++ b/data/landings/10c3c876aa67-grok.html
@@ -0,0 +1,222 @@
+<!doctype html><html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
+<title>Living Fractal Tree — built by Grok 4.5 · Model Arena</title>
+<style>*{box-sizing:border-box}html,body{margin:0;height:100%;background:#0b0d10;color:#e6ecf2;font:14px/1.5 ui-monospace,Menlo,monospace}
+header{display:flex;align-items:center;gap:14px;padding:13px 20px;border-bottom:1px solid #1c2430;background:#0e1116}
+header .k{color:#7cf;font-weight:700;letter-spacing:2px;text-transform:uppercase;font-size:12px;white-space:nowrap}
+header h1{font-size:15px;margin:0;font-weight:600}header .by{color:#8a93a0;font-size:12px;margin-left:auto;white-space:nowrap}
+.wrap{height:calc(100% - 51px)}iframe{width:100%;height:100%;border:0;background:#000;display:block}</style></head><body>
+<header><span class="k">⚔ Model Arena</span><h1>Living Fractal Tree</h1><span class="by">built by <b>Grok 4.5</b></span></header>
+<div class="wrap"><iframe sandbox="allow-scripts allow-pointer-lock" srcdoc="&lt;!DOCTYPE html&gt;
+&lt;html lang=&quot;en&quot;&gt;
+&lt;head&gt;
+&lt;meta charset=&quot;UTF-8&quot;&gt;
+&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot;&gt;
+&lt;title&gt;Fractal Tree&lt;/title&gt;
+&lt;style&gt;
+*{margin:0;padding:0;box-sizing:border-box}
+body{overflow:hidden;background:#0a0a12;font-family:system-ui,sans-serif}
+canvas{display:block;width:100vw;height:100vh}
+.ui{position:fixed;bottom:24px;left:50%;transform:translateX(-50%);background:rgba(20,20,35,0.85);backdrop-filter:blur(12px);padding:14px 28px;border-radius:16px;border:1px solid rgba(255,255,255,0.08);display:flex;align-items:center;gap:16px;z-index:10;box-shadow:0 8px 32px rgba(0,0,0,0.4)}
+.ui label{color:#c8c8d8;font-size:13px;white-space:nowrap;letter-spacing:0.03em}
+.ui input[type=range]{width:160px;accent-color:#7cb87c;cursor:pointer}
+.ui span{color:#8fbc8f;font-size:13px;font-variant-numeric:tabular-nums;min-width:18px}
+&lt;/style&gt;
+&lt;/head&gt;
+&lt;body&gt;
+&lt;canvas id=&quot;c&quot;&gt;&lt;/canvas&gt;
+&lt;div class=&quot;ui&quot;&gt;
+&lt;label&gt;Branch Depth&lt;/label&gt;
+&lt;input type=&quot;range&quot; id=&quot;depth&quot; min=&quot;1&quot; max=&quot;12&quot; value=&quot;9&quot; step=&quot;1&quot;&gt;
+&lt;span id=&quot;dval&quot;&gt;9&lt;/span&gt;
+&lt;/div&gt;
+&lt;script&gt;
+const canvas=document.getElementById('c');
+const ctx=canvas.getContext('2d');
+const depthSlider=document.getElementById('depth');
+const dval=document.getElementById('dval');
+
+let W,H,maxDepth=9,time=0,growProgress=0;
+const leaves=[];
+const falling=[];
+const SEASON_COLORS=[
+  ['#2d5a27','#3d7a37','#4a8f3f','#5ba84a'],
+  ['#c45c26','#d4763a','#e8a04a','#b84020','#8b4513','#cd853f'],
+  ['#e8e8f0','#d0d0e0','#c0c8d8','#a8b0c0'],
+  ['#3a7a30','#4a9a3a','#5cb84a','#2d6a28','#6bc45a']
+];
+let season=0;
+let seasonTimer=0;
+
+function resize(){
+  W=canvas.width=window.innerWidth;
+  H=canvas.height=window.innerHeight;
+}
+window.addEventListener('resize',resize);
+resize();
+
+depthSlider.addEventListener('input',()=&gt;{
+  maxDepth=+depthSlider.value;
+  dval.textContent=maxDepth;
+  growProgress=0;
+  leaves.length=0;
+  falling.length=0;
+});
+
+function wind(y,t){
+  return Math.sin(t*0.7+y*0.008)*0.12+Math.sin(t*1.3+y*0.015)*0.06+Math.sin(t*0.3)*0.04;
+}
+
+function drawBranch(x,y,len,angle,depth,t){
+  if(depth&gt;maxDepth||len&lt;2)return;
+  const grow=Math.min(1,Math.max(0,(growProgress-depth*0.08)/(0.5)));
+  if(grow&lt;=0)return;
+  const w=wind(y,t)*(1+depth*0.15);
+  const a=angle+w;
+  const el=len*grow;
+  const x2=x+Math.sin(a)*el;
+  const y2=y-Math.cos(a)*el;
+
+  const trunk=depth===0;
+  const lw=Math.max(0.5, (maxDepth-depth+1)*1.6*grow);
+  const tcol=Math.floor(40+depth*8);
+  ctx.beginPath();
+  ctx.moveTo(x,y);
+  ctx.lineTo(x2,y2);
+  ctx.strokeStyle=trunk?`rgb(${90+tcol*0.3},${60+tcol*0.2},${30})`:`rgb(${70+tcol},${50+tcol*0.6},${25+depth*2})`;
+  ctx.lineWidth=lw;
+  ctx.lineCap='round';
+  ctx.stroke();
+
+  if(depth&gt;=maxDepth-3&amp;&amp;grow&gt;0.7){
+    const cols=SEASON_COLORS[season%4];
+    const n=depth===maxDepth?3:1;
+    for(let i=0;i&lt;n;i++){
+      const lx=x2+(Math.random()-0.5)*8;
+      const ly=y2+(Math.random()-0.5)*8;
+      const col=cols[Math.floor(Math.random()*cols.length)];
+      const r=2+Math.random()*3;
+      leaves.push({x:lx,y:ly,r,col,ox:lx,oy:ly,phase:Math.random()*Math.PI*2,depth});
+      if(leaves.length&gt;800)leaves.shift();
+    }
+  }
+
+  const shrink=0.68+Math.sin(depth+t*0.1)*0.04;
+  const spread=0.45+Math.sin(depth*1.3)*0.08;
+  drawBranch(x2,y2,len*shrink,a-spread,depth+1,t);
+  drawBranch(x2,y2,len*shrink,a+spread,depth+1,t);
+  if(depth&gt;2&amp;&amp;depth&lt;maxDepth-1&amp;&amp;Math.sin(depth*7.3)&gt;0.3){
+    drawBranch(x2,y2,len*shrink*0.75,a+spread*0.3*(depth%2?1:-1),depth+1,t);
+  }
+}
+
+function spawnFalling(){
+  if(leaves.length===0)return;
+  if(Math.random()&gt;0.15)return;
+  const src=leaves[Math.floor(Math.random()*leaves.length)];
+  const cols=SEASON_COLORS[season%4];
+  falling.push({
+    x:src.x,y:src.y,
+    vx:(Math.random()-0.5)*1.2,
+    vy:0.3+Math.random()*0.6,
+    r:src.r*(0.7+Math.random()*0.5),
+    col:cols[Math.floor(Math.random()*cols.length)],
+    rot:Math.random()*Math.PI*2,
+    rv:(Math.random()-0.5)*0.08,
+    life:1
+  });
+  if(falling.length&gt;300)falling.shift();
+}
+
+function updateFalling(t){
+  for(let i=falling.length-1;i&gt;=0;i--){
+    const p=falling[i];
+    const w=wind(p.y,t)*40;
+    p.vx+=w*0.02;
+    p.vx*=0.98;
+    p.vy+=0.015;
+    p.x+=p.vx+Math.sin(t*2+p.rot)*0.4;
+    p.y+=p.vy;
+    p.rot+=p.rv;
+    p.life-=0.002;
+    if(p.y&gt;H+20||p.life&lt;=0)falling.splice(i,1);
+  }
+}
+
+function drawLeaves(t){
+  for(const l of leaves){
+    const w=wind(l.y,t);
+    const sx=l.ox+Math.sin(t*1.5+l.phase)*3+w*20;
+    const sy=l.oy+Math.cos(t*1.2+l.phase)*2;
+    l.x=sx;l.y=sy;
+    ctx.beginPath();
+    ctx.arc(sx,sy,l.r,0,Math.PI*2);
+    ctx.fillStyle=l.col;
+    ctx.globalAlpha=0.75;
+    ctx.fill();
+  }
+  ctx.globalAlpha=1;
+}
+
+function drawFalling(){
+  for(const p of falling){
+    ctx.save();
+    ctx.translate(p.x,p.y);
+    ctx.rotate(p.rot);
+    ctx.globalAlpha=Math.min(1,p.life)*0.85;
+    ctx.fillStyle=p.col;
+    ctx.beginPath();
+    ctx.ellipse(0,0,p.r,p.r*0.6,0,0,Math.PI*2);
+    ctx.fill();
+    ctx.restore();
+  }
+  ctx.globalAlpha=1;
+}
+
+function drawGround(){
+  const grd=ctx.createLinearGradient(0,H*0.85,0,H);
+  grd.addColorStop(0,'rgba(20,40,20,0)');
+  grd.addColorStop(0.3,'rgba(25,50,25,0.4)');
+  grd.addColorStop(1,'rgba(15,35,15,0.7)');
+  ctx.fillStyle=grd;
+  ctx.fillRect(0,H*0.85,W,H*0.15);
+}
+
+function drawSky(){
+  const grd=ctx.createLinearGradient(0,0,0,H);
+  const s=season%4;
+  if(s===0){grd.addColorStop(0,'#0c1a10');grd.addColorStop(1,'#1a2a18');}
+  else if(s===1){grd.addColorStop(0,'#1a1008');grd.addColorStop(1,'#2a1a10');}
+  else if(s===2){grd.addColorStop(0,'#0a0e18');grd.addColorStop(1,'#121828');}
+  else{grd.addColorStop(0,'#0a1810');grd.addColorStop(1,'#142818');}
+  ctx.fillStyle=grd;
+  ctx.fillRect(0,0,W,H);
+}
+
+function frame(ts){
+  time=ts*0.001;
+  growProgress=Math.min(1.5,growProgress+0.006);
+  seasonTimer+=0.001;
+  if(seasonTimer&gt;12){seasonTimer=0;season++;leaves.length=0;}
+
+  drawSky();
+  drawGround();
+
+  const baseX=W/2;
+  const baseY=H*0.88;
+  const trunkLen=Math.min(W,H)*0.18;
+
+  leaves.length=0;
+  drawBranch(baseX,baseY,trunkLen,0,0,time);
+  drawLeaves(time);
+
+  spawnFalling();
+  updateFalling(time);
+  drawFalling();
+
+  requestAnimationFrame(frame);
+}
+requestAnimationFrame(frame);
+&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;" title="Living Fractal Tree"></iframe></div>
+</body></html>
\ No newline at end of file
diff --git a/idea-run/night-cycle.sh b/idea-run/night-cycle.sh
new file mode 100755
index 0000000..ee6070d
--- /dev/null
+++ b/idea-run/night-cycle.sh
@@ -0,0 +1,43 @@
+#!/usr/bin/env bash
+# night-cycle.sh — one iteration of the all-night DW/Agent-Abrams challenge loop.
+#   1. AI-judge any settled-but-unjudged battle (crowns it → surfaces in Arcade)
+#   2. fire the next idea ONLY if the Ollama backlog is small (self-limiting)
+#   3. commit new data
+# All $0 (local + claude-code Max plan). Prints a one-line status.
+set -uo pipefail
+ROOT="$HOME/Projects/model-arena"; cd "$ROOT"
+A="admin:DW2024!"; BASE="http://127.0.0.1:9758"
+
+# how many challenges still have running/queued runs?
+ACTIVE=$(curl -s -m 10 -u "$A" "$BASE/api/challenges" \
+  | python3 -c "import json,sys
+try: d=json.load(sys.stdin)
+except: print(0); sys.exit()
+ch=d if isinstance(d,list) else d.get('challenges',[])
+n=sum(1 for c in ch if any(r.get('status') in ('running','queued') for r in c.get('runs',[])))
+print(n)" 2>/dev/null || echo 0)
+
+# judge the most-recent settled Games/Real-Work battle that has thumbs but no aiPick
+JUDGED=$(curl -s -m 10 -u "$A" "$BASE/api/challenges" \
+  | python3 -c "import json,sys
+try: d=json.load(sys.stdin)
+except: sys.exit()
+ch=d if isinstance(d,list) else d.get('challenges',[])
+ch=[c for c in ch if not any(r.get('status') in ('running','queued') for r in c.get('runs',[]))]
+for c in ch:
+    if c.get('aiPick'): continue
+    if any(r.get('status')=='done' and r.get('thumb') for r in c.get('runs',[])):
+        print(c['id']); break" 2>/dev/null)
+if [ -n "${JUDGED:-}" ]; then
+  curl -s -m 90 -u "$A" -X POST "$BASE/api/challenges/$JUDGED/judge" >/dev/null 2>&1
+fi
+
+FIRED="(backlog $ACTIVE — skipped fire)"
+if [ "${ACTIVE:-0}" -le 1 ]; then
+  FIRED=$(bash "$ROOT/idea-run/run-next.sh" 2>&1 | tail -1)
+fi
+
+git add -A >/dev/null 2>&1
+git commit -q -m "night-loop: cycle $(date '+%H:%M') — judged=${JUDGED:-none} · $FIRED" >/dev/null 2>&1 && C="committed" || C="nothing to commit"
+GAMES=$(curl -s -m 8 -u "$A" "$BASE/api/arcade" | python3 -c "import json,sys;print(json.load(sys.stdin)['count'])" 2>/dev/null || echo '?')
+echo "CYCLE $(date '+%H:%M') | active=$ACTIVE | judged=${JUDGED:-none} | $FIRED | arcade=$GAMES games | $C"

← 0e921bc Add Games Arcade: /arcade gallery + /api/arcade auto-discove  ·  back to Model Arena  ·  auto-save: 2026-07-24T23:30:18 (21 files) — data/challenges. 3d2d7af →