Muse Ember
by MetaReasoningClean
Running live in a sandboxed frame. Click inside it before using the keyboard.
How it went
- Total time
- 14.6 s
- Cost
- $0.0035
- Output tokens
- 6,812
- Spent thinking
- 45 tokens
- Speed
- 513.7 tok/s
- Console errors
- 0
- First token after
- 1.3 s
- First visible text after
- 1.3 s
- Prompt tokens
- 511
- Code size
- 21.7 KB
- Finish reason
- stop
- Served by
- Meta
- Model id
meta/muse-ember- Reasoning sent
{"effort":"minimal"}- Max output
- 943,718 tokens
- Generated
- Nov 12, 2026
What it was asked
The prompt
Build an interactive Kanban board in vanilla HTML, CSS and JavaScript (no framework, no library). Requirements: - Three columns: "To do", "In progress", "Done", each showing its card count. - Pre-populate the board with 6 realistic cards spread across the columns so the first screenshot looks alive. - Each card has a title and a priority (low / medium / high) shown as a colored badge. - Add a card through a small modal form (title required), and delete a card with a button on it. - Drag and drop cards between columns with the mouse, with a clear visual drop indicator. - A search field that filters cards live by title. - A light / dark theme toggle. - State lives in memory only (no storage APIs).
System prompt, sent with every prompt
You are generating a self-contained web demo. It will be rendered automatically in a sandboxed iframe and compared side by side with the output of other AI models given the exact same task. Output rules: - Reply with exactly ONE fenced code block containing the complete file: ```html (or ```svg when the task asks for an SVG). No text before or after the code block. - Everything must be inline: CSS in <style>, JavaScript in <script>. No external files, images, fonts or network requests, except the CDN URLs explicitly allowed by the task. - Do not use localStorage, sessionStorage, cookies, IndexedDB, alert(), confirm(), prompt() or window.open(): they are blocked in the sandbox and will throw. - The page must look right in a 1280×800 viewport and adapt gracefully down to 375px wide. No horizontal scrollbar. - Something meaningful must be visible on first paint: a screenshot is taken automatically about 3 seconds after load, without any interaction. - Everything that makes the demo interesting must be visible within the first seconds: nothing hidden behind a high score, a later level or a long play session. - Games must start in a self-playing demo mode (a simple AI plays) behind a "Click or press a key to start" message, so the game can be judged in action without playing. - The iframe only receives keyboard events after the user clicks it: if the demo uses the keyboard, display a clear "Click or press a key to start" message. - The code must run with zero errors in the console of the latest Chrome.
Code run-1.html · 307 lines
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Flowboard — Kanban</title>
<style>
*{box-sizing:border-box;margin:0;padding:0}
:root{
--bg:#f4f5f7; --bg-grad: radial-gradient(900px 500px at 15% -10%, #dbeafe 0%, transparent 60%), radial-gradient(800px 500px at 100% 0%, #fce7f3 0%, transparent 55%), #f4f5f7;
--surface:#ffffff; --surface2:#f1f2f6; --border:#e6e7ee; --text:#171a26; --muted:#6b7280; --muted2:#9aa0b0;
--accent:#6366f1; --accent2:#4f46e5; --shadow:0 6px 24px rgba(23,26,38,.08),0 1px 3px rgba(23,26,38,.06);
--shadow-lg:0 20px 50px rgba(23,26,38,.18); --input:#fff; --hover:#f7f7fa;
--low:#059669; --low-bg:#ecfdf5; --low-bd:#a7f3d0;
--med:#d97706; --med-bg:#fffbeb; --med-bd:#fde68a;
--high:#dc2626; --high-bg:#fef2f2; --high-bd:#fecaca;
--drop:#6366f1; --col-bg:#eceef3;
}
html[data-theme="dark"]{
--bg:#0d0f17; --bg-grad: radial-gradient(900px 500px at 15% -10%, #1e1b4b 0%, transparent 60%), radial-gradient(800px 500px at 100% 0%, #312e81 0%, transparent 55%), #0d0f17;
--surface:#171a28; --surface2:#1f2336; --border:#2a2f47; --text:#eef0ff; --muted:#9aa3c0; --muted2:#636b8a;
--accent:#818cf8; --accent2:#6366f1; --shadow:0 6px 24px rgba(0,0,0,.4),0 1px 3px rgba(0,0,0,.3);
--shadow-lg:0 20px 50px rgba(0,0,0,.6); --input:#1c2033; --hover:#21263d;
--low:#34d399; --low-bg:#052e22; --low-bd:#065f46;
--med:#fbbf24; --med-bg:#3a2a05; --med-bd:#92400e;
--high:#f87171; --high-bg:#3b1111; --high-bd:#7f1d1d;
--col-bg:#151826;
}
body{font-family:Inter,ui-sans-serif,system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;background:var(--bg-grad);background-color:var(--bg);color:var(--text);min-height:100vh;transition:background .3s,color .3s}
a{color:var(--accent)}
.wrap{max-width:1280px;margin:0 auto;padding:28px 24px 40px}
/* Header */
header.top{display:flex;align-items:center;gap:16px;flex-wrap:wrap;margin-bottom:22px}
.logo{display:flex;align-items:center;gap:12px;margin-right:auto}
.logo-mark{width:42px;height:42px;border-radius:11px;background:linear-gradient(135deg,#6366f1,#a855f7);display:grid;place-items:center;color:#fff;box-shadow:0 8px 20px rgba(99,102,241,.35);flex:0 0 auto}
.logo h1{font-size:23px;letter-spacing:-.02em;line-height:1}
.logo p{font-size:12.5px;color:var(--muted);margin-top:4px}
.search{position:relative;flex:1 1 280px;max-width:380px}
.search svg{position:absolute;left:13px;top:50%;transform:translateY(-50%);color:var(--muted2);pointer-events:none}
.search input{width:100%;padding:11px 36px 11px 40px;border:1.5px solid var(--border);border-radius:11px;background:var(--surface);color:var(--text);font:inherit;font-size:14px;outline:none;transition:.2s}
.search input:focus{border-color:var(--accent);box-shadow:0 0 0 4px rgba(99,102,241,.15)}
.search input::placeholder{color:var(--muted2)}
.clear-x{position:absolute;right:8px;top:50%;transform:translateY(-50%);border:none;background:var(--surface2);color:var(--muted);width:24px;height:24px;border-radius:50%;cursor:pointer;display:none;place-items:center;font-size:14px;line-height:1}
.clear-x.show{display:grid}
.btn{border:none;cursor:pointer;font:inherit;font-weight:600;font-size:14px;border-radius:11px;padding:11px 16px;display:inline-flex;align-items:center;gap:8px;transition:.18s;white-space:nowrap}
.btn-primary{background:var(--accent);color:#fff;box-shadow:0 6px 16px rgba(99,102,241,.3)}
.btn-primary:hover{background:var(--accent2);transform:translateY(-1px)}
.btn-ghost{background:var(--surface);color:var(--text);border:1.5px solid var(--border)}
.btn-ghost:hover{background:var(--hover)}
.theme-btn{width:44px;height:44px;padding:0;justify-content:center;border-radius:50%;font-size:18px}
.stats{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:18px}
.stat{background:var(--surface);border:1px solid var(--border);border-radius:11px;padding:9px 14px;font-size:13px;color:var(--muted);display:flex;gap:8px;align-items:center}
.stat b{color:var(--text);font-size:15px}
.dot{width:8px;height:8px;border-radius:50%}
/* Board */
.board{display:grid;grid-template-columns:repeat(3,1fr);gap:18px;align-items:start}
.col{background:var(--col-bg);border:1px solid var(--border);border-radius:16px;padding:14px;min-height:340px;transition:.2s}
.col.drag-over{border-color:var(--accent);box-shadow:0 0 0 4px rgba(99,102,241,.15);background:var(--surface2)}
.col-head{display:flex;align-items:center;gap:9px;padding:4px 6px 12px}
.col-ico{width:28px;height:28px;border-radius:8px;display:grid;place-items:center;font-size:14px;flex:0 0 auto}
.col h2{font-size:13px;text-transform:uppercase;letter-spacing:.07em;flex:1}
.count{background:var(--surface);border:1px solid var(--border);color:var(--muted);font-size:12px;font-weight:700;min-width:26px;height:24px;padding:0 7px;border-radius:999px;display:grid;place-items:center}
.add-mini{border:none;background:transparent;color:var(--muted);font-size:20px;width:28px;height:28px;border-radius:8px;cursor:pointer;line-height:1}
.add-mini:hover{background:var(--surface);color:var(--text)}
.cards{display:flex;flex-direction:column;gap:11px;min-height:80px}
.empty{border:1.5px dashed var(--border);border-radius:12px;padding:22px 12px;text-align:center;color:var(--muted2);font-size:13px;line-height:1.5}
.card{background:var(--surface);border:1px solid var(--border);border-radius:13px;padding:14px;box-shadow:var(--shadow);cursor:grab;position:relative;transition:box-shadow .18s,transform .18s,opacity .18s}
.card:hover{transform:translateY(-1px);box-shadow:0 10px 26px rgba(23,26,38,.1)}
.card:active{cursor:grabbing}
.card.dragging{opacity:.35;transform:rotate(1.5deg) scale(.98);box-shadow:none}
.card.drag-src{opacity:.35}
.card h3{font-size:14.5px;line-height:1.4;padding-right:28px;letter-spacing:-.01em}
.card p{font-size:13px;color:var(--muted);line-height:1.55;margin-top:6px}
.card-foot{display:flex;align-items:center;gap:8px;margin-top:12px}
.badge{font-size:11px;font-weight:700;letter-spacing:.04em;text-transform:uppercase;padding:4px 9px;border-radius:999px;border:1px solid;display:inline-flex;align-items:center;gap:5px}
.badge i{width:6px;height:6px;border-radius:50%;background:currentColor;display:inline-block}
.badge.low{color:var(--low);background:var(--low-bg);border-color:var(--low-bd)}
.badge.medium{color:var(--med);background:var(--med-bg);border-color:var(--med-bd)}
.badge.high{color:var(--high);background:var(--high-bg);border-color:var(--high-bd)}
.meta{margin-left:auto;font-size:11.5px;color:var(--muted2)}
.del{position:absolute;top:9px;right:9px;width:26px;height:26px;border:none;border-radius:7px;background:transparent;color:var(--muted2);cursor:pointer;font-size:15px;display:grid;place-items:center;opacity:0;transition:.15s}
.card:hover .del,.del:focus-visible{opacity:1}
.del:hover{background:var(--high-bg);color:var(--high)}
.drop-line{height:3px;border-radius:99px;background:var(--drop);box-shadow:0 0 0 3px rgba(99,102,241,.2);margin:-5px 0}
.skeleton{border-radius:13px;border:2px dashed rgba(99,102,241,.45);background:rgba(99,102,241,.06);min-height:92px;display:grid;place-items:center;color:var(--accent);font-size:12.5px;font-weight:600}
/* Modal */
.overlay{position:fixed;inset:0;background:rgba(13,15,23,.55);backdrop-filter:blur(4px);display:none;place-items:center;padding:20px;z-index:50}
.overlay.open{display:grid}
.modal{background:var(--surface);border:1px solid var(--border);border-radius:18px;width:100%;max-width:460px;padding:24px;box-shadow:var(--shadow-lg);animation:pop .22s ease}
@keyframes pop{from{transform:scale(.96) translateY(8px);opacity:0}to{transform:none;opacity:1}}
.modal h2{font-size:19px;letter-spacing:-.02em}
.modal .sub{font-size:13.5px;color:var(--muted);margin:6px 0 18px}
label.fld{display:block;font-size:12.5px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;color:var(--muted);margin:14px 0 7px}
label.fld:first-of-type{margin-top:0}
.modal input,.modal textarea,.modal select{width:100%;padding:11px 13px;border:1.5px solid var(--border);border-radius:11px;background:var(--input);color:var(--text);font:inherit;font-size:14px;outline:none;transition:.2s;resize:vertical}
.modal input:focus,.modal textarea:focus,.modal select:focus{border-color:var(--accent);box-shadow:0 0 0 4px rgba(99,102,241,.15)}
.modal input.err{border-color:var(--high);box-shadow:0 0 0 4px rgba(220,38,38,.12)}
.hint{font-size:12.5px;color:var(--high);margin-top:6px;display:none}
.hint.show{display:block}
.pri-row{display:flex;gap:8px}
.pri-opt{flex:1;border:1.5px solid var(--border);background:var(--surface);border-radius:11px;padding:10px;cursor:pointer;font:inherit;font-size:13px;font-weight:600;color:var(--muted);display:flex;align-items:center;justify-content:center;gap:6px;transition:.15s}
.pri-opt.sel-low{border-color:var(--low);background:var(--low-bg);color:var(--low)}
.pri-opt.sel-medium{border-color:var(--med);background:var(--med-bg);color:var(--med)}
.pri-opt.sel-high{border-color:var(--high);background:var(--high-bg);color:var(--high)}
.modal-actions{display:flex;gap:10px;justify-content:flex-end;margin-top:22px}
.col-sel{margin-top:2px}
footer{margin-top:22px;font-size:12.5px;color:var(--muted2);text-align:center}
.no-results{display:none;text-align:center;color:var(--muted);padding:34px 16px;background:var(--surface);border:1px dashed var(--border);border-radius:14px;grid-column:1/-1;font-size:14px}
.no-results.show{display:block}
@media(max-width:860px){.board{grid-template-columns:1fr}.col{min-height:0}.wrap{padding:20px 14px 32px}.logo p{display:none}}
</style>
</head>
<body>
<div class="wrap">
<header class="top">
<div class="logo">
<div class="logo-mark" aria-hidden="true">
<svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><rect x="3" y="4" width="5" height="16" rx="1.5"/><rect x="10" y="4" width="5" height="10" rx="1.5"/><rect x="17" y="4" width="4" height="13" rx="1.5"/></svg>
</div>
<div><h1>Flowboard</h1><p>Drag cards between columns · in-memory state</p></div>
</div>
<div class="search">
<svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>
<input id="search" type="search" placeholder="Search cards by title…" aria-label="Search cards">
<button class="clear-x" id="clearSearch" aria-label="Clear search">×</button>
</div>
<button class="btn btn-ghost theme-btn" id="themeBtn" aria-label="Toggle dark / light theme" title="Toggle theme"><span id="themeIco">🌙</span></button>
<button class="btn btn-primary" id="addBtn">+ Add card</button>
</header>
<div class="stats" id="stats"></div>
<main class="board" id="board"></main>
<div class="no-results" id="noResults">🔍 No cards match your search. <button id="resetSearch" style="border:none;background:none;color:var(--accent);font:inherit;font-weight:700;cursor:pointer;text-decoration:underline">Clear search</button></div>
<footer>Tip: drag a card onto another column — the dashed slot shows exactly where it will land. New cards live only in memory.</footer>
</div>
<div class="overlay" id="overlay" role="dialog" aria-modal="true" aria-labelledby="modalTitle">
<form class="modal" id="cardForm" novalidate>
<h2 id="modalTitle">Add a new card</h2>
<p class="sub">It will appear at the top of the selected column.</p>
<label class="fld" for="fTitle">Title <span style="color:var(--high)">*</span></label>
<input id="fTitle" type="text" placeholder="e.g. Design onboarding flow" autocomplete="off" maxlength="90">
<div class="hint" id="titleHint">A title is required.</div>
<label class="fld" for="fDesc">Description <span style="text-transform:none;font-weight:500">(optional)</span></label>
<textarea id="fDesc" rows="3" placeholder="Add a short note, acceptance criteria, links…" maxlength="220"></textarea>
<label class="fld">Priority</label>
<div class="pri-row" id="priRow">
<button type="button" class="pri-opt" data-p="low">● Low</button>
<button type="button" class="pri-opt sel-medium" data-p="medium">● Medium</button>
<button type="button" class="pri-opt" data-p="high">● High</button>
</div>
<label class="fld" for="fCol">Column</label>
<select id="fCol" class="col-sel">
<option value="todo">To do</option><option value="progress">In progress</option><option value="done">Done</option>
</select>
<div class="modal-actions">
<button type="button" class="btn btn-ghost" id="cancelBtn">Cancel</button>
<button type="submit" class="btn btn-primary">Add card</button>
</div>
</form>
</div>
<script>
(function(){
"use strict";
const COLS=[
{id:"todo",name:"To do",color:"#64748b",bg:"#e2e8f0",ico:"○"},
{id:"progress",name:"In progress",color:"#d97706",bg:"#fef3c7",ico:"◐"},
{id:"done",name:"Done",color:"#059669",bg:"#d1fae5",ico:"●"}
];
const ICONS={todo:"📋",progress:"⚡",done:"✅"};
let seq=100;
let cards=[
{id:1,col:"todo",title:"Design new onboarding flow",desc:"Sketch 3 variants in Figma before Friday's design review.",p:"high",tag:"UX · 2d"},
{id:2,col:"todo",title:"Fix contrast on settings page",desc:"Muted text fails WCAG AA on mobile.",p:"medium",tag:"A11y"},
{id:3,col:"progress",title:"Build drag-and-drop board UI",desc:"Mouse events + placeholder indicator for drop position.",p:"high",tag:"Frontend"},
{id:4,col:"progress",title:"Write API for card search",desc:"GET /cards?q= with title filter and pagination.",p:"medium",tag:"Backend · 4d"},
{id:5,col:"done",title:"Ship dark mode toggle",desc:"CSS variables + prefers-color-scheme default.",p:"low",tag:"Shipped Tue"},
{id:6,col:"done",title:"Audit bundle size",desc:"Tree-shook icons, dropped 84 KB gzipped.",p:"low",tag:"Perf"}
];
let query="", draggedId=null, selectedPri="medium", presetCol="todo";
const board=document.getElementById("board"), stats=document.getElementById("stats"),
searchEl=document.getElementById("search"), clearBtn=document.getElementById("clearSearch"),
noRes=document.getElementById("noResults"), overlay=document.getElementById("overlay"),
form=document.getElementById("cardForm"), titleEl=document.getElementById("fTitle"),
descEl=document.getElementById("fDesc"), colEl=document.getElementById("fCol"),
hint=document.getElementById("titleHint");
function visible(){ return cards.filter(c=>c.title.toLowerCase().includes(query)); }
function esc(s){ return s.replace(/[&<>"']/g,m=>({"&":"&","<":"<",">":">",'"':""","'":"'"}[m])); }
function render(){
const vis=visible();
board.innerHTML="";
COLS.forEach(col=>{
const list=vis.filter(c=>c.col===col.id);
const el=document.createElement("section");
el.className="col"; el.dataset.col=col.id;
el.innerHTML=`<div class="col-head">
<span class="col-ico" style="background:${col.bg};color:${col.color}">${ICONS[col.id]}</span>
<h2>${col.name}</h2><span class="count" aria-label="${list.length} cards">${list.length}</span>
<button class="add-mini" data-add="${col.id}" title="Add card to ${col.name}" aria-label="Add card to ${col.name}">+</button></div>
<div class="cards"></div>`;
const wrap=el.querySelector(".cards");
if(!list.length){
const e=document.createElement("div"); e.className="empty";
e.innerHTML=query?`Nothing here matches “${esc(query)}”`:`Drop cards here<br><span style="font-size:12px">or click + to add one</span>`;
wrap.appendChild(e);
}
list.forEach(c=>{
const card=document.createElement("article");
card.className="card"; card.draggable=true; card.dataset.id=c.id;
card.setAttribute("aria-label",c.title);
card.innerHTML=`<button class="del" data-del="${c.id}" title="Delete card" aria-label="Delete ${esc(c.title)}">×</button>
<h3>${esc(c.title)}</h3>${c.desc?`<p>${esc(c.desc)}</p>`:""}
<div class="card-foot"><span class="badge ${c.p}"><i></i>${c.p}</span><span class="meta">${esc(c.tag||"")}</span></div>`;
card.addEventListener("dragstart",e=>{draggedId=c.id;card.classList.add("dragging");e.dataTransfer.effectAllowed="move";try{e.dataTransfer.setData("text/plain",""+c.id)}catch(_){}});
card.addEventListener("dragend",()=>{cleanupDrop();});
card.addEventListener("dragover",e=>{e.preventDefault();e.stopPropagation();showIndicator(wrap,card);});
card.addEventListener("drop",e=>{e.preventDefault();e.stopPropagation();moveTo(c.id,col.id,beforeId(card));});
wrap.appendChild(card);
});
wrap.addEventListener("dragover",e=>{e.preventDefault();});
wrap.addEventListener("drop",e=>{e.preventDefault();if(e.target===wrap||e.target.classList.contains("empty"))moveTo(null,col.id,null);});
el.addEventListener("dragover",e=>{e.preventDefault();el.classList.add("drag-over");});
el.addEventListener("dragleave",e=>{if(!el.contains(e.relatedTarget))el.classList.remove("drag-over");});
el.addEventListener("drop",e=>{e.preventDefault();el.classList.remove("drag-over");});
board.appendChild(el);
});
// stats
const total=cards.length, hi=cards.filter(c=>c.p==="high").length, doneN=cards.filter(c=>c.col==="done").length;
stats.innerHTML=`<span class="stat"><span class="dot" style="background:var(--accent)"></span><b>${total}</b> total cards</span>
<span class="stat"><span class="dot" style="background:var(--high)"></span><b>${hi}</b> high priority</span>
<span class="stat"><span class="dot" style="background:var(--low)"></span><b>${doneN}</b> completed (${total?Math.round(doneN/total*100):0}%)</span>
${query?`<span class="stat">🔍 <b>${vis.length}</b> match “${esc(query)}”</span>`:""}`;
noRes.classList.toggle("show", vis.length===0 && cards.length>0);
clearBtn.classList.toggle("show", !!query);
board.querySelectorAll("[data-add]").forEach(b=>b.addEventListener("click",()=>openModal(b.dataset.add)));
board.querySelectorAll("[data-del]").forEach(b=>b.addEventListener("click",e=>{e.stopPropagation();cards=cards.filter(c=>c.id!=b.dataset.del);render();}));
}
function beforeId(cardEl){ const id=+cardEl.dataset.id; return id===draggedId?null:id; }
function showIndicator(wrap,beforeEl){
cleanupDrop(true);
const line=document.createElement("div"); line.className="drop-line";
if(beforeEl) wrap.insertBefore(line,beforeEl);
else { const ph=document.createElement("div"); ph.className="skeleton"; ph.textContent="Drop here"; wrap.appendChild(ph); }
board.querySelectorAll(".col").forEach(c=>c.classList.add("drag-over"));
}
function cleanupDrop(keep){
document.querySelectorAll(".drop-line,.skeleton").forEach(n=>n.remove());
document.querySelectorAll(".card.dragging").forEach(n=>n.classList.remove("dragging"));
if(!keep){ document.querySelectorAll(".col.drag-over").forEach(n=>n.classList.remove("drag-over")); draggedId=null; }
}
function moveTo(beforeCardId,colId,insertBeforeId){
if(draggedId==null) return;
const idx=cards.findIndex(c=>c.id===draggedId); if(idx<0) return;
const [card]=cards.splice(idx,1); card.col=colId;
if(insertBeforeId!=null){
const ti=cards.findIndex(c=>c.id===insertBeforeId);
if(ti>=0) cards.splice(ti,0,card); else cards.push(card);
} else if(beforeCardId!=null){
const ti=cards.findIndex(c=>c.id===beforeCardId);
if(ti>=0) cards.splice(ti,0,card); else cards.push(card);
} else {
// append to end of target column (before first card of next column in array order is hard; push then stable sort by visual order)
cards.push(card);
}
draggedId=null; cleanupDrop(); render();
}
/* Search — live filter */
searchEl.addEventListener("input",()=>{query=searchEl.value.trim().toLowerCase();render();});
clearBtn.addEventListener("click",()=>{searchEl.value="";query="";render();searchEl.focus();});
document.getElementById("resetSearch").addEventListener("click",()=>{searchEl.value="";query="";render();});
/* Theme */
const themeBtn=document.getElementById("themeBtn"), themeIco=document.getElementById("themeIco");
themeBtn.addEventListener("click",()=>{
const dark=document.documentElement.dataset.theme==="dark";
document.documentElement.dataset.theme=dark?"":"dark";
themeIco.textContent=dark?"🌙":"☀️";
});
/* Modal */
function openModal(col){ presetCol=col||"todo"; colEl.value=presetCol; titleEl.value=""; descEl.value=""; titleEl.classList.remove("err"); hint.classList.remove("show"); setPri("medium"); overlay.classList.add("open"); setTimeout(()=>titleEl.focus(),30); }
function closeModal(){ overlay.classList.remove("open"); }
document.getElementById("addBtn").addEventListener("click",()=>openModal("todo"));
document.getElementById("cancelBtn").addEventListener("click",closeModal);
overlay.addEventListener("click",e=>{ if(e.target===overlay) closeModal(); });
document.addEventListener("keydown",e=>{ if(e.key==="Escape") closeModal(); });
function setPri(p){ selectedPri=p; document.querySelectorAll(".pri-opt").forEach(b=>{ b.className="pri-opt"+(b.dataset.p===p?" sel-"+p:""); }); }
document.getElementById("priRow").addEventListener("click",e=>{ const b=e.target.closest(".pri-opt"); if(b) setPri(b.dataset.p); });
titleEl.addEventListener("input",()=>{ titleEl.classList.remove("err"); hint.classList.remove("show"); });
form.addEventListener("submit",e=>{
e.preventDefault();
const t=titleEl.value.trim();
if(!t){ titleEl.classList.add("err"); hint.classList.add("show"); titleEl.focus(); return; }
cards.unshift({id:++seq,col:colEl.value,title:t,desc:descEl.value.trim(),p:selectedPri,tag:"New"});
closeModal(); render();
});
render();
})();
</script>
</body>
</html>