Models 44

Gemini 4 Argon

by Googlereasoning defaultClean
Open in new window

Running live in a sandboxed frame. Click inside it before using the keyboard.

How it went

Total time
2 min 53 s
Cost
$0.128
Output tokens
12,655
Spent thinking
0 tokens
Speed
74 tok/s
Console errors
0
First token after
2.1 s
First visible text after
2.1 s
Prompt tokens
548
Code size
35.8 KB
Finish reason
stop
Served by
Google
Model id
google/gemini-4-argon
Reasoning sent
model default
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 · 1232 lines

<!DOCTYPE html>
<html lang="en" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kanban Board</title>
<style>
  :root[data-theme="light"] {
    --bg-body: #f4f6f9;
    --bg-header: #ffffff;
    --bg-column: #ebecf0;
    --bg-card: #ffffff;
    --bg-card-hover: #f9fafc;
    --bg-input: #f1f3f6;
    --text-primary: #172b4d;
    --text-secondary: #5e6c84;
    --border-color: #dfe1e6;
    --shadow-card: 0 2px 4px rgba(9, 30, 66, 0.08), 0 0 1px rgba(9, 30, 66, 0.12);
    --shadow-card-hover: 0 6px 14px rgba(9, 30, 66, 0.14), 0 0 1px rgba(9, 30, 66, 0.16);
    --shadow-modal: 0 20px 40px rgba(9, 30, 66, 0.25);
    --accent: #4f46e5;
    --accent-hover: #4338ca;
    --accent-soft: rgba(79, 70, 229, 0.12);
    --badge-high-bg: #fee2e2;
    --badge-high-text: #dc2626;
    --badge-medium-bg: #fef3c7;
    --badge-medium-text: #d97706;
    --badge-low-bg: #dcfce7;
    --badge-low-text: #16a34a;
    --danger-hover: #fee2e2;
    --danger-text: #dc2626;
  }

  :root[data-theme="dark"] {
    --bg-body: #0f1117;
    --bg-header: #161923;
    --bg-column: #161923;
    --bg-card: #1f2433;
    --bg-card-hover: #252b3d;
    --bg-input: #0f1117;
    --text-primary: #f1f5f9;
    --text-secondary: #94a3b8;
    --border-color: #2a3143;
    --shadow-card: 0 2px 6px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(255, 255, 255, 0.05);
    --shadow-card-hover: 0 8px 18px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(99, 102, 241, 0.4);
    --shadow-modal: 0 24px 48px rgba(0, 0, 0, 0.65), 0 0 0 1px rgba(255, 255, 255, 0.08);
    --accent: #6366f1;
    --accent-hover: #4f46e5;
    --accent-soft: rgba(99, 102, 241, 0.16);
    --badge-high-bg: rgba(239, 68, 68, 0.16);
    --badge-high-text: #f87171;
    --badge-medium-bg: rgba(245, 158, 11, 0.16);
    --badge-medium-text: #fbbf24;
    --badge-low-bg: rgba(34, 197, 94, 0.16);
    --badge-low-text: #4ade80;
    --danger-hover: rgba(239, 68, 68, 0.18);
    --danger-text: #f87171;
  }

  * {
    box-sizing: border-box;
    margin: 0;
    padding: 0;
  }

  body {
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Inter, Helvetica, Arial, sans-serif;
    background-color: var(--bg-body);
    color: var(--text-primary);
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    overflow-x: hidden;
    transition: background-color 0.2s ease, color 0.2s ease;
  }

  /* Header */
  .topbar {
    background-color: var(--bg-header);
    border-bottom: 1px solid var(--border-color);
    padding: 0.85rem 1.75rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
    position: sticky;
    top: 0;
    z-index: 20;
  }

  .brand {
    display: flex;
    align-items: center;
    gap: 0.75rem;
  }

  .brand-logo {
    width: 34px;
    height: 34px;
    border-radius: 9px;
    background: linear-gradient(135deg, #6366f1, #8b5cf6);
    display: grid;
    place-items: center;
    color: #fff;
    box-shadow: 0 4px 12px rgba(99, 102, 241, 0.35);
    flex-shrink: 0;
  }

  .brand-title {
    font-size: 1.15rem;
    font-weight: 700;
    letter-spacing: -0.02em;
  }

  .brand-subtitle {
    font-size: 0.78rem;
    color: var(--text-secondary);
    font-weight: 500;
  }

  .controls {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    flex: 1;
    justify-content: flex-end;
    max-width: 640px;
  }

  .search-wrapper {
    position: relative;
    flex: 1;
    max-width: 300px;
    min-width: 150px;
  }

  .search-wrapper svg {
    position: absolute;
    left: 11px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text-secondary);
    pointer-events: none;
  }

  .search-input {
    width: 100%;
    padding: 0.55rem 2rem 0.55rem 2.25rem;
    border-radius: 8px;
    border: 1px solid var(--border-color);
    background-color: var(--bg-input);
    color: var(--text-primary);
    font-size: 0.875rem;
    outline: none;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
  }

  .search-input:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
  }

  .clear-search {
    position: absolute;
    right: 8px;
    top: 50%;
    transform: translateY(-50%);
    background: none;
    border: none;
    color: var(--text-secondary);
    cursor: pointer;
    padding: 2px;
    border-radius: 4px;
    display: none;
    align-items: center;
    justify-content: center;
  }

  .clear-search.visible {
    display: flex;
  }

  .clear-search:hover {
    color: var(--text-primary);
  }

  .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.45rem;
    padding: 0.55rem 0.95rem;
    border-radius: 8px;
    font-size: 0.875rem;
    font-weight: 600;
    cursor: pointer;
    border: 1px solid transparent;
    transition: all 0.15s ease;
    white-space: nowrap;
  }

  .btn-primary {
    background-color: var(--accent);
    color: #ffffff;
    box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3);
  }

  .btn-primary:hover {
    background-color: var(--accent-hover);
    transform: translateY(-1px);
  }

  .btn-icon {
    padding: 0.52rem;
    background-color: var(--bg-input);
    border-color: var(--border-color);
    color: var(--text-primary);
  }

  .btn-icon:hover {
    border-color: var(--accent);
    color: var(--accent);
  }

  /* Board Layout */
  .board-container {
    flex: 1;
    padding: 1.5rem 1.75rem;
    max-width: 1440px;
    width: 100%;
    margin: 0 auto;
    display: grid;
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: 1.35rem;
    align-items: start;
  }

  .column {
    background-color: var(--bg-column);
    border: 1px solid var(--border-color);
    border-radius: 12px;
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 115px);
    min-height: 460px;
    transition: border-color 0.15s ease, background-color 0.2s ease, box-shadow 0.15s ease;
  }

  .column.drag-over {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
  }

  .column-header {
    padding: 1rem 1rem 0.75rem;
    display: flex;
    align-items: center;
    justify-content: space-between;
    border-bottom: 1px solid var(--border-color);
  }

  .column-title-group {
    display: flex;
    align-items: center;
    gap: 0.55rem;
  }

  .status-dot {
    width: 9px;
    height: 9px;
    border-radius: 50%;
  }

  .status-dot.todo { background-color: #6366f1; box-shadow: 0 0 8px rgba(99, 102, 241, 0.6); }
  .status-dot.inprogress { background-color: #f59e0b; box-shadow: 0 0 8px rgba(245, 158, 11, 0.6); }
  .status-dot.done { background-color: #10b981; box-shadow: 0 0 8px rgba(16, 185, 129, 0.6); }

  .column-title {
    font-size: 0.92rem;
    font-weight: 700;
    letter-spacing: -0.01em;
  }

  .column-count {
    font-size: 0.75rem;
    font-weight: 700;
    padding: 0.12rem 0.5rem;
    border-radius: 999px;
    background-color: var(--bg-card);
    color: var(--text-secondary);
    border: 1px solid var(--border-color);
  }

  .column-quick-add {
    background: none;
    border: none;
    color: var(--text-secondary);
    cursor: pointer;
    padding: 4px;
    border-radius: 6px;
    display: grid;
    place-items: center;
    transition: background-color 0.15s, color 0.15s;
  }

  .column-quick-add:hover {
    background-color: var(--accent-soft);
    color: var(--accent);
  }

  .card-list {
    padding: 0.85rem;
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    overflow-y: auto;
    flex: 1;
    min-height: 200px;
  }

  /* Cards */
  .card {
    background-color: var(--bg-card);
    border-radius: 10px;
    padding: 0.9rem 0.95rem;
    box-shadow: var(--shadow-card);
    cursor: grab;
    user-select: none;
    display: flex;
    flex-direction: column;
    gap: 0.65rem;
    position: relative;
    transition: transform 0.14s ease, box-shadow 0.14s ease, background-color 0.15s ease, opacity 0.15s ease;
    border-left: 3px solid transparent;
  }

  .card[data-priority="high"] { border-left-color: #ef4444; }
  .card[data-priority="medium"] { border-left-color: #f59e0b; }
  .card[data-priority="low"] { border-left-color: #22c55e; }

  .card:hover {
    background-color: var(--bg-card-hover);
    box-shadow: var(--shadow-card-hover);
    transform: translateY(-2px);
  }

  .card:active {
    cursor: grabbing;
  }

  .card.dragging {
    opacity: 0.35;
    transform: scale(0.98);
  }

  .card-top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
  }

  .badge {
    font-size: 0.68rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    padding: 0.2rem 0.55rem;
    border-radius: 6px;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
  }

  .badge::before {
    content: "";
    width: 5px;
    height: 5px;
    border-radius: 50%;
    background-color: currentColor;
  }

  .badge.high {
    background-color: var(--badge-high-bg);
    color: var(--badge-high-text);
  }

  .badge.medium {
    background-color: var(--badge-medium-bg);
    color: var(--badge-medium-text);
  }

  .badge.low {
    background-color: var(--badge-low-bg);
    color: var(--badge-low-text);
  }

  .card-delete {
    background: transparent;
    border: none;
    color: var(--text-secondary);
    cursor: pointer;
    width: 26px;
    height: 26px;
    border-radius: 6px;
    display: grid;
    place-items: center;
    opacity: 0.65;
    transition: opacity 0.15s, background-color 0.15s, color 0.15s;
  }

  .card:hover .card-delete {
    opacity: 1;
  }

  .card-delete:hover {
    background-color: var(--danger-hover);
    color: var(--danger-text);
  }

  .card-title {
    font-size: 0.9rem;
    font-weight: 600;
    line-height: 1.45;
    color: var(--text-primary);
    word-break: break-word;
  }

  .card-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    font-size: 0.73rem;
    color: var(--text-secondary);
    padding-top: 0.25rem;
  }

  .card-id {
    font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
    font-weight: 600;
    opacity: 0.85;
  }

  .drag-grip {
    display: inline-flex;
    align-items: center;
    gap: 2px;
    opacity: 0.5;
  }

  /* Drop Indicator */
  .drop-indicator {
    height: 68px;
    border-radius: 10px;
    border: 2px dashed var(--accent);
    background-color: var(--accent-soft);
    pointer-events: none;
    transition: all 0.12s ease;
    flex-shrink: 0;
  }

  .empty-state {
    border: 1px dashed var(--border-color);
    border-radius: 10px;
    padding: 1.75rem 1rem;
    text-align: center;
    color: var(--text-secondary);
    font-size: 0.82rem;
    margin: auto 0;
    pointer-events: none;
  }

  /* Modal */
  .modal-backdrop {
    position: fixed;
    inset: 0;
    background-color: rgba(8, 11, 18, 0.65);
    backdrop-filter: blur(4px);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 1rem;
    z-index: 100;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.18s ease;
  }

  .modal-backdrop.open {
    opacity: 1;
    pointer-events: auto;
  }

  .modal {
    background-color: var(--bg-header);
    border: 1px solid var(--border-color);
    border-radius: 14px;
    width: 100%;
    max-width: 430px;
    padding: 1.4rem;
    box-shadow: var(--shadow-modal);
    transform: translateY(10px) scale(0.98);
    transition: transform 0.18s ease;
  }

  .modal-backdrop.open .modal {
    transform: translateY(0) scale(1);
  }

  .modal-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 1.15rem;
  }

  .modal-title {
    font-size: 1.05rem;
    font-weight: 700;
  }

  .modal-close {
    background: none;
    border: none;
    color: var(--text-secondary);
    cursor: pointer;
    padding: 4px;
    border-radius: 6px;
    display: grid;
    place-items: center;
  }

  .modal-close:hover {
    background-color: var(--bg-input);
    color: var(--text-primary);
  }

  .form-group {
    margin-bottom: 1rem;
  }

  .form-label {
    display: block;
    font-size: 0.8rem;
    font-weight: 600;
    color: var(--text-secondary);
    margin-bottom: 0.4rem;
  }

  .form-input, .form-select {
    width: 100%;
    padding: 0.65rem 0.8rem;
    border-radius: 8px;
    border: 1px solid var(--border-color);
    background-color: var(--bg-input);
    color: var(--text-primary);
    font-size: 0.9rem;
    font-family: inherit;
    outline: none;
    transition: border-color 0.15s ease, box-shadow 0.15s ease;
  }

  .form-input:focus, .form-select:focus {
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-soft);
  }

  .form-input.invalid {
    border-color: #ef4444;
    box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.18);
  }

  .error-msg {
    font-size: 0.76rem;
    color: #ef4444;
    margin-top: 0.35rem;
    display: none;
  }

  .error-msg.visible {
    display: block;
  }

  .priority-selector {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 0.5rem;
  }

  .priority-option {
    position: relative;
  }

  .priority-option input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
  }

  .priority-pill {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.35rem;
    padding: 0.55rem 0.5rem;
    border-radius: 8px;
    border: 1px solid var(--border-color);
    background-color: var(--bg-input);
    font-size: 0.82rem;
    font-weight: 600;
    cursor: pointer;
    transition: all 0.15s ease;
    text-transform: capitalize;
  }

  .priority-option input:checked + .priority-pill.low {
    border-color: #22c55e;
    background-color: var(--badge-low-bg);
    color: var(--badge-low-text);
  }

  .priority-option input:checked + .priority-pill.medium {
    border-color: #f59e0b;
    background-color: var(--badge-medium-bg);
    color: var(--badge-medium-text);
  }

  .priority-option input:checked + .priority-pill.high {
    border-color: #ef4444;
    background-color: var(--badge-high-bg);
    color: var(--badge-high-text);
  }

  .modal-actions {
    display: flex;
    justify-content: flex-end;
    gap: 0.6rem;
    margin-top: 1.35rem;
  }

  .btn-secondary {
    background-color: var(--bg-input);
    border-color: var(--border-color);
    color: var(--text-primary);
  }

  .btn-secondary:hover {
    background-color: var(--bg-card-hover);
  }

  mark.highlight {
    background-color: rgba(245, 158, 11, 0.35);
    color: inherit;
    border-radius: 3px;
    padding: 0 2px;
  }

  @media (max-width: 900px) {
    .board-container {
      grid-template-columns: 1fr;
      padding: 1rem;
    }
    .column {
      max-height: none;
      min-height: 220px;
    }
    .topbar {
      padding: 0.85rem 1rem;
    }
  }

  @media (max-width: 540px) {
    .controls {
      width: 100%;
      max-width: none;
      justify-content: space-between;
    }
    .search-wrapper {
      max-width: none;
    }
    .brand-subtitle {
      display: none;
    }
  }
</style>
</head>
<body>

  <header class="topbar">
    <div class="brand">
      <div class="brand-logo" aria-hidden="true">
        <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round">
          <rect x="3" y="3" width="7" height="9" rx="1"></rect>
          <rect x="14" y="3" width="7" height="5" rx="1"></rect>
          <rect x="14" y="12" width="7" height="9" rx="1"></rect>
          <rect x="3" y="16" width="7" height="5" rx="1"></rect>
        </svg>
      </div>
      <div>
        <div class="brand-title">FlowBoard</div>
        <div class="brand-subtitle">Q3 Product Sprint &bull; Drag cards to reorder</div>
      </div>
    </div>

    <div class="controls">
      <div class="search-wrapper">
        <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
          <circle cx="11" cy="11" r="8"></circle>
          <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
        </svg>
        <input type="text" id="searchInput" class="search-input" placeholder="Search cards by title..." aria-label="Search cards by title">
        <button type="button" id="clearSearchBtn" class="clear-search" title="Clear search" aria-label="Clear search">
          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
            <line x1="18" y1="6" x2="6" y2="18"></line>
            <line x1="6" y1="6" x2="18" y2="18"></line>
          </svg>
        </button>
      </div>

      <button type="button" id="openModalBtn" class="btn btn-primary">
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
          <line x1="12" y1="5" x2="12" y2="19"></line>
          <line x1="5" y1="12" x2="19" y2="12"></line>
        </svg>
        <span>New Card</span>
      </button>

      <button type="button" id="themeToggleBtn" class="btn btn-icon" title="Toggle light/dark theme" aria-label="Toggle light/dark theme">
        <svg id="themeIconSun" width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
          <circle cx="12" cy="12" r="5"></circle>
          <line x1="12" y1="1" x2="12" y2="3"></line>
          <line x1="12" y1="21" x2="12" y2="23"></line>
          <line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
          <line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
          <line x1="1" y1="12" x2="3" y2="12"></line>
          <line x1="21" y1="12" x2="23" y2="12"></line>
          <line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
          <line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
        </svg>
        <svg id="themeIconMoon" width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:none;">
          <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
        </svg>
      </button>
    </div>
  </header>

  <main class="board-container" id="board">
    <!-- To do Column -->
    <section class="column" data-column="todo">
      <div class="column-header">
        <div class="column-title-group">
          <span class="status-dot todo"></span>
          <h2 class="column-title">To do</h2>
          <span class="column-count" id="count-todo">0</span>
        </div>
        <button type="button" class="column-quick-add" data-add-col="todo" title="Add card to To do" aria-label="Add card to To do">
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round">
            <line x1="12" y1="5" x2="12" y2="19"></line>
            <line x1="5" y1="12" x2="19" y2="12"></line>
          </svg>
        </button>
      </div>
      <div class="card-list" id="list-todo" data-column="todo"></div>
    </section>

    <!-- In progress Column -->
    <section class="column" data-column="inprogress">
      <div class="column-header">
        <div class="column-title-group">
          <span class="status-dot inprogress"></span>
          <h2 class="column-title">In progress</h2>
          <span class="column-count" id="count-inprogress">0</span>
        </div>
        <button type="button" class="column-quick-add" data-add-col="inprogress" title="Add card to In progress" aria-label="Add card to In progress">
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round">
            <line x1="12" y1="5" x2="12" y2="19"></line>
            <line x1="5" y1="12" x2="19" y2="12"></line>
          </svg>
        </button>
      </div>
      <div class="card-list" id="list-inprogress" data-column="inprogress"></div>
    </section>

    <!-- Done Column -->
    <section class="column" data-column="done">
      <div class="column-header">
        <div class="column-title-group">
          <span class="status-dot done"></span>
          <h2 class="column-title">Done</h2>
          <span class="column-count" id="count-done">0</span>
        </div>
        <button type="button" class="column-quick-add" data-add-col="done" title="Add card to Done" aria-label="Add card to Done">
          <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.3" stroke-linecap="round" stroke-linejoin="round">
            <line x1="12" y1="5" x2="12" y2="19"></line>
            <line x1="5" y1="12" x2="19" y2="12"></line>
          </svg>
        </button>
      </div>
      <div class="card-list" id="list-done" data-column="done"></div>
    </section>
  </main>

  <!-- Add Card Modal -->
  <div class="modal-backdrop" id="modalBackdrop" role="dialog" aria-modal="true" aria-labelledby="modalTitle">
    <div class="modal">
      <div class="modal-header">
        <h3 class="modal-title" id="modalTitle">Create New Card</h3>
        <button type="button" class="modal-close" id="closeModalBtn" aria-label="Close modal">
          <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
            <line x1="18" y1="6" x2="6" y2="18"></line>
            <line x1="6" y1="6" x2="18" y2="18"></line>
          </svg>
        </button>
      </div>

      <form id="cardForm" novalidate>
        <div class="form-group">
          <label class="form-label" for="cardTitleInput">Card Title *</label>
          <input type="text" id="cardTitleInput" class="form-input" placeholder="e.g., Implement OAuth2 PKCE flow" maxlength="140" required>
          <div class="error-msg" id="titleError">Please enter a card title.</div>
        </div>

        <div class="form-group">
          <label class="form-label">Priority</label>
          <div class="priority-selector">
            <label class="priority-option">
              <input type="radio" name="priority" value="low">
              <span class="priority-pill low">Low</span>
            </label>
            <label class="priority-option">
              <input type="radio" name="priority" value="medium" checked>
              <span class="priority-pill medium">Medium</span>
            </label>
            <label class="priority-option">
              <input type="radio" name="priority" value="high">
              <span class="priority-pill high">High</span>
            </label>
          </div>
        </div>

        <div class="form-group">
          <label class="form-label" for="cardColumnSelect">Column</label>
          <select id="cardColumnSelect" class="form-select">
            <option value="todo">To do</option>
            <option value="inprogress">In progress</option>
            <option value="done">Done</option>
          </select>
        </div>

        <div class="modal-actions">
          <button type="button" class="btn btn-secondary" id="cancelModalBtn">Cancel</button>
          <button type="submit" class="btn btn-primary">Add Card</button>
        </div>
      </form>
    </div>
  </div>

  <script>
    (() => {
      // In-memory state pre-populated with 6 realistic cards across the 3 columns
      const state = {
        theme: 'dark',
        searchQuery: '',
        nextNumber: 107,
        cards: [
          {
            id: 'FLW-101',
            title: 'Implement rate limiting middleware for public REST API endpoints',
            priority: 'high',
            column: 'todo'
          },
          {
            id: 'FLW-102',
            title: 'Design onboarding checklist empty-state illustrations',
            priority: 'medium',
            column: 'todo'
          },
          {
            id: 'FLW-103',
            title: 'Migrate billing webhook handlers to idempotent queue workers',
            priority: 'high',
            column: 'inprogress'
          },
          {
            id: 'FLW-104',
            title: 'Add keyboard navigation & ARIA live announcements to command palette',
            priority: 'low',
            column: 'inprogress'
          },
          {
            id: 'FLW-105',
            title: 'Configure automated PostgreSQL read-replica failover alerts',
            priority: 'medium',
            column: 'done'
          },
          {
            id: 'FLW-106',
            title: 'Audit bundle size and tree-shake legacy chart dependencies',
            priority: 'low',
            column: 'done'
          }
        ]
      };

      const columns = ['todo', 'inprogress', 'done'];

      // DOM Elements
      const searchInput = document.getElementById('searchInput');
      const clearSearchBtn = document.getElementById('clearSearchBtn');
      const themeToggleBtn = document.getElementById('themeToggleBtn');
      const themeIconSun = document.getElementById('themeIconSun');
      const themeIconMoon = document.getElementById('themeIconMoon');

      const modalBackdrop = document.getElementById('modalBackdrop');
      const openModalBtn = document.getElementById('openModalBtn');
      const closeModalBtn = document.getElementById('closeModalBtn');
      const cancelModalBtn = document.getElementById('cancelModalBtn');
      const cardForm = document.getElementById('cardForm');
      const cardTitleInput = document.getElementById('cardTitleInput');
      const cardColumnSelect = document.getElementById('cardColumnSelect');
      const titleError = document.getElementById('titleError');

      // Drag and drop state
      let draggedCardId = null;
      const dropIndicator = document.createElement('div');
      dropIndicator.className = 'drop-indicator';

      function escapeHTML(str) {
        return str.replace(/[&<>"']/g, ch => ({
          '&': '&amp;',
          '<': '&lt;',
          '>': '&gt;',
          '"': '&quot;',
          "'": '&#39;'
        }[ch]));
      }

      function highlightMatch(title, query) {
        if (!query) return escapeHTML(title);
        const escapedTitle = escapeHTML(title);
        const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
        const regex = new RegExp(`(${escapedQuery})`, 'gi');
        return escapedTitle.replace(regex, '<mark class="highlight">$1</mark>');
      }

      function renderBoard() {
        const query = state.searchQuery.trim().toLowerCase();

        columns.forEach(col => {
          const listEl = document.getElementById(`list-${col}`);
          const countEl = document.getElementById(`count-${col}`);
          listEl.innerHTML = '';

          const colCards = state.cards.filter(card => {
            if (card.column !== col) return false;
            if (query && !card.title.toLowerCase().includes(query)) return false;
            return true;
          });

          countEl.textContent = colCards.length;

          if (colCards.length === 0) {
            const empty = document.createElement('div');
            empty.className = 'empty-state';
            empty.textContent = query ? 'No matching cards' : 'Drop cards here or click + to add';
            listEl.appendChild(empty);
            return;
          }

          colCards.forEach(card => {
            const cardEl = document.createElement('article');
            cardEl.className = 'card';
            cardEl.draggable = true;
            cardEl.dataset.id = card.id;
            cardEl.dataset.priority = card.priority;

            cardEl.innerHTML = `
              <div class="card-top">
                <span class="badge ${card.priority}">${card.priority}</span>
                <button type="button" class="card-delete" data-delete-id="${card.id}" title="Delete card" aria-label="Delete card">
                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round">
                    <polyline points="3 6 5 6 21 6"></polyline>
                    <path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6"></path>
                    <path d="M10 11v6"></path>
                    <path d="M14 11v6"></path>
                  </svg>
                </button>
              </div>
              <div class="card-title">${highlightMatch(card.title, state.searchQuery.trim())}</div>
              <div class="card-footer">
                <span class="card-id">${card.id}</span>
                <span class="drag-grip" aria-hidden="true">
                  <svg width="13" height="13" viewBox="0 0 24 24" fill="currentColor">
                    <circle cx="9" cy="6" r="1.6"/><circle cx="15" cy="6" r="1.6"/>
                    <circle cx="9" cy="12" r="1.6"/><circle cx="15" cy="12" r="1.6"/>
                    <circle cx="9" cy="18" r="1.6"/><circle cx="15" cy="18" r="1.6"/>
                  </svg>
                </span>
              </div>
            `;

            // Drag listeners on card
            cardEl.addEventListener('dragstart', handleDragStart);
            cardEl.addEventListener('dragend', handleDragEnd);

            // Delete button listener
            const deleteBtn = cardEl.querySelector('.card-delete');
            deleteBtn.addEventListener('click', (e) => {
              e.stopPropagation();
              deleteCard(card.id);
            });

            listEl.appendChild(cardEl);
          });
        });
      }

      function deleteCard(id) {
        const idx = state.cards.findIndex(c => c.id === id);
        if (idx !== -1) {
          state.cards.splice(idx, 1);
          renderBoard();
        }
      }

      // Drag and Drop Handlers
      function handleDragStart(e) {
        const cardEl = e.currentTarget;
        draggedCardId = cardEl.dataset.id;
        e.dataTransfer.effectAllowed = 'move';
        e.dataTransfer.setData('text/plain', draggedCardId);

        // Set indicator height to match the dragged card
        dropIndicator.style.height = `${cardEl.offsetHeight}px`;

        requestAnimationFrame(() => {
          cardEl.classList.add('dragging');
        });
      }

      function handleDragEnd(e) {
        e.currentTarget.classList.remove('dragging');
        draggedCardId = null;
        if (dropIndicator.parentNode) {
          dropIndicator.parentNode.removeChild(dropIndicator);
        }
        document.querySelectorAll('.column.drag-over').forEach(col => {
          col.classList.remove('drag-over');
        });
      }

      function getDragAfterElement(container, y) {
        const draggableElements = [...container.querySelectorAll('.card:not(.dragging)')];
        return draggableElements.reduce((closest, child) => {
          const box = child.getBoundingClientRect();
          const offset = y - box.top - box.height / 2;
          if (offset < 0 && offset > closest.offset) {
            return { offset, element: child };
          } else {
            return closest;
          }
        }, { offset: Number.NEGATIVE_INFINITY }).element;
      }

      document.querySelectorAll('.column').forEach(columnEl => {
        const listEl = columnEl.querySelector('.card-list');

        columnEl.addEventListener('dragover', e => {
          e.preventDefault();
          if (!draggedCardId) return;
          e.dataTransfer.dropEffect = 'move';

          columnEl.classList.add('drag-over');

          const emptyEl = listEl.querySelector('.empty-state');
          if (emptyEl) emptyEl.style.display = 'none';

          const afterElement = getDragAfterElement(listEl, e.clientY);
          if (afterElement == null) {
            listEl.appendChild(dropIndicator);
          } else {
            listEl.insertBefore(dropIndicator, afterElement);
          }
        });

        columnEl.addEventListener('dragleave', e => {
          if (!columnEl.contains(e.relatedTarget)) {
            columnEl.classList.remove('drag-over');
            if (dropIndicator.parentNode === listEl) {
              listEl.removeChild(dropIndicator);
            }
            const emptyEl = listEl.querySelector('.empty-state');
            if (emptyEl) emptyEl.style.display = '';
          }
        });

        columnEl.addEventListener('drop', e => {
          e.preventDefault();
          columnEl.classList.remove('drag-over');

          const targetColumn = columnEl.dataset.column;
          const cardIndex = state.cards.findIndex(c => c.id === draggedCardId);
          if (cardIndex === -1) return;

          const [movedCard] = state.cards.splice(cardIndex, 1);
          movedCard.column = targetColumn;

          const afterElement = getDragAfterElement(listEl, e.clientY);
          if (dropIndicator.parentNode) {
            dropIndicator.parentNode.removeChild(dropIndicator);
          }

          if (afterElement && afterElement.dataset.id) {
            const targetIdx = state.cards.findIndex(c => c.id === afterElement.dataset.id);
            if (targetIdx !== -1) {
              state.cards.splice(targetIdx, 0, movedCard);
            } else {
              state.cards.push(movedCard);
            }
          } else {
            state.cards.push(movedCard);
          }

          draggedCardId = null;
          renderBoard();
        });
      });

      // Modal Controls
      function openModal(defaultColumn = 'todo') {
        cardForm.reset();
        cardColumnSelect.value = defaultColumn;
        cardTitleInput.classList.remove('invalid');
        titleError.classList.remove('visible');
        modalBackdrop.classList.add('open');
        setTimeout(() => cardTitleInput.focus(), 60);
      }

      function closeModal() {
        modalBackdrop.classList.remove('open');
      }

      openModalBtn.addEventListener('click', () => openModal('todo'));
      closeModalBtn.addEventListener('click', closeModal);
      cancelModalBtn.addEventListener('click', closeModal);

      document.querySelectorAll('[data-add-col]').forEach(btn => {
        btn.addEventListener('click', () => openModal(btn.dataset.addCol));
      });

      modalBackdrop.addEventListener('click', e => {
        if (e.target === modalBackdrop) closeModal();
      });

      document.addEventListener('keydown', e => {
        if (e.key === 'Escape' && modalBackdrop.classList.contains('open')) {
          closeModal();
        }
      });

      cardTitleInput.addEventListener('input', () => {
        if (cardTitleInput.value.trim()) {
          cardTitleInput.classList.remove('invalid');
          titleError.classList.remove('visible');
        }
      });

      cardForm.addEventListener('submit', e => {
        e.preventDefault();
        const title = cardTitleInput.value.trim();
        if (!title) {
          cardTitleInput.classList.add('invalid');
          titleError.classList.add('visible');
          cardTitleInput.focus();
          return;
        }

        const priorityData = new FormData(cardForm).get('priority') || 'medium';
        const column = cardColumnSelect.value || 'todo';

        const newCard = {
          id: `FLW-${state.nextNumber++}`,
          title,
          priority: priorityData,
          column
        };

        state.cards.unshift(newCard);
        closeModal();
        renderBoard();
      });

      // Search Filtering
      searchInput.addEventListener('input', e => {
        state.searchQuery = e.target.value;
        clearSearchBtn.classList.toggle('visible', state.searchQuery.length > 0);
        renderBoard();
      });

      clearSearchBtn.addEventListener('click', () => {
        state.searchQuery = '';
        searchInput.value = '';
        clearSearchBtn.classList.remove('visible');
        searchInput.focus();
        renderBoard();
      });

      // Theme Toggle
      function updateThemeUI() {
        document.documentElement.setAttribute('data-theme', state.theme);
        if (state.theme === 'dark') {
          themeIconSun.style.display = 'block';
          themeIconMoon.style.display = 'none';
        } else {
          themeIconSun.style.display = 'none';
          themeIconMoon.style.display = 'block';
        }
      }

      themeToggleBtn.addEventListener('click', () => {
        state.theme = state.theme === 'dark' ? 'light' : 'dark';
        updateThemeUI();
      });

      // Initial render
      updateThemeUI();
      renderBoard();
    })();
  </script>
</body>
</html>