Models 44

Gemini 3.8 Flash

by GoogleReasoningClean
Open in new window

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

How it went

Total time
57.6 s
Cost
$0.040
Output tokens
10,583
Spent thinking
1,532 tokens
Speed
193.5 tok/s
Console errors
0
First token after
2.9 s
First visible text after
13.7 s
Prompt tokens
507
Code size
26.3 KB
Finish reason
stop
Served by
Google
Model id
google/gemini-3.8-flash
Reasoning sent
{"effort":"medium"}
Max output
65,536 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 · 981 lines

<!DOCTYPE html>
<html lang="en" data-theme="light">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Kanban Studio</title>
  <style>
    :root {
      --bg: #f1f5f9;
      --surface: #ffffff;
      --surface-subtle: #f8fafc;
      --column-bg: #e2e8f0;
      --border: #cbd5e1;
      --border-subtle: #e2e8f0;
      --text-main: #0f172a;
      --text-muted: #64748b;
      --text-inverse: #ffffff;
      --accent: #3b82f6;
      --accent-hover: #2563eb;
      --shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.06);
      --shadow-md: 0 4px 6px -1px rgba(15, 23, 42, 0.08), 0 2px 4px -2px rgba(15, 23, 42, 0.06);
      --shadow-lg: 0 10px 15px -3px rgba(15, 23, 42, 0.1), 0 4px 6px -4px rgba(15, 23, 42, 0.08);

      --p-low-bg: #ecfdf5;
      --p-low-text: #047857;
      --p-low-border: #a7f3d0;

      --p-med-bg: #fffbeb;
      --p-med-text: #b45309;
      --p-med-border: #fde68a;

      --p-high-bg: #fef2f2;
      --p-high-text: #b91c1c;
      --p-high-border: #fecaca;

      --drop-indicator: #3b82f6;
    }

    [data-theme="dark"] {
      --bg: #090d16;
      --surface: #131b2e;
      --surface-subtle: #1a243c;
      --column-bg: #0f172a;
      --border: #24304f;
      --border-subtle: #1e293b;
      --text-main: #f8fafc;
      --text-muted: #94a3b8;
      --text-inverse: #0f172a;
      --accent: #3b82f6;
      --accent-hover: #60a5fa;
      --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.4);
      --shadow-md: 0 4px 8px -1px rgba(0, 0, 0, 0.5);
      --shadow-lg: 0 12px 20px -3px rgba(0, 0, 0, 0.6);

      --p-low-bg: rgba(16, 185, 129, 0.15);
      --p-low-text: #34d399;
      --p-low-border: rgba(16, 185, 129, 0.3);

      --p-med-bg: rgba(245, 158, 11, 0.15);
      --p-med-text: #fbbf24;
      --p-med-border: rgba(245, 158, 11, 0.3);

      --p-high-bg: rgba(239, 68, 68, 0.15);
      --p-high-text: #f87171;
      --p-high-border: rgba(239, 68, 68, 0.3);

      --drop-indicator: #60a5fa;
    }

    * {
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
      transition: background-color 0.2s ease, border-color 0.2s ease, color 0.15s ease;
    }

    body {
      background-color: var(--bg);
      color: var(--text-main);
      min-height: 100vh;
      display: flex;
      flex-direction: column;
      overflow-x: hidden;
    }

    /* Top Navigation Bar */
    header {
      background-color: var(--surface);
      border-bottom: 1px solid var(--border-subtle);
      padding: 12px 24px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: 16px;
      flex-wrap: wrap;
      box-shadow: var(--shadow-sm);
      z-index: 10;
    }

    .brand {
      display: flex;
      align-items: center;
      gap: 10px;
    }

    .brand-logo {
      width: 30px;
      height: 30px;
      background: linear-gradient(135deg, #3b82f6, #1d4ed8);
      border-radius: 8px;
      display: flex;
      align-items: center;
      justify-content: center;
      color: #fff;
      font-weight: 800;
      font-size: 16px;
      box-shadow: 0 2px 4px rgba(37, 99, 235, 0.3);
    }

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

    .toolbar {
      display: flex;
      align-items: center;
      gap: 12px;
      flex: 1;
      max-width: 580px;
      justify-content: flex-end;
    }

    .search-box {
      position: relative;
      flex: 1;
      max-width: 320px;
    }

    .search-box svg {
      position: absolute;
      left: 10px;
      top: 50%;
      transform: translateY(-50%);
      width: 15px;
      height: 15px;
      fill: none;
      stroke: var(--text-muted);
      stroke-width: 2;
      pointer-events: none;
    }

    .search-box input {
      width: 100%;
      padding: 7px 12px 7px 32px;
      border-radius: 8px;
      border: 1px solid var(--border);
      background-color: var(--surface-subtle);
      color: var(--text-main);
      font-size: 0.85rem;
      outline: none;
    }

    .search-box input:focus {
      border-color: var(--accent);
      box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
      background-color: var(--surface);
    }

    .btn {
      display: inline-flex;
      align-items: center;
      gap: 6px;
      padding: 7px 14px;
      border-radius: 8px;
      font-size: 0.85rem;
      font-weight: 600;
      cursor: pointer;
      border: none;
      outline: none;
      white-space: nowrap;
    }

    .btn-primary {
      background-color: var(--accent);
      color: #ffffff;
      box-shadow: 0 1px 3px rgba(37, 99, 235, 0.25);
    }

    .btn-primary:hover {
      background-color: var(--accent-hover);
    }

    .btn-secondary {
      background-color: var(--surface-subtle);
      border: 1px solid var(--border);
      color: var(--text-main);
    }

    .btn-secondary:hover {
      background-color: var(--border-subtle);
    }

    .theme-btn {
      padding: 7px;
      border-radius: 8px;
      background-color: var(--surface-subtle);
      border: 1px solid var(--border);
      color: var(--text-main);
      cursor: pointer;
      display: flex;
      align-items: center;
      justify-content: center;
      width: 34px;
      height: 34px;
    }

    .theme-btn:hover {
      background-color: var(--border-subtle);
    }

    /* Main Board Layout */
    main {
      flex: 1;
      padding: 24px;
      display: flex;
      gap: 20px;
      align-items: flex-start;
      overflow-x: auto;
    }

    .column {
      flex: 1;
      min-width: 290px;
      max-width: 420px;
      background-color: var(--column-bg);
      border-radius: 12px;
      display: flex;
      flex-direction: column;
      max-height: calc(100vh - 120px);
      border: 1px solid var(--border-subtle);
    }

    .column-header {
      padding: 14px 16px 10px;
      display: flex;
      align-items: center;
      justify-content: space-between;
      user-select: none;
    }

    .column-title-group {
      display: flex;
      align-items: center;
      gap: 8px;
    }

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

    .dot-todo { background-color: #64748b; }
    .dot-inprogress { background-color: #3b82f6; }
    .dot-done { background-color: #10b981; }

    .column-header h2 {
      font-size: 0.92rem;
      font-weight: 700;
      text-transform: uppercase;
      letter-spacing: 0.04em;
      color: var(--text-main);
    }

    .card-count {
      font-size: 0.75rem;
      font-weight: 700;
      background-color: var(--surface);
      color: var(--text-muted);
      border-radius: 20px;
      padding: 2px 8px;
      border: 1px solid var(--border);
    }

    .card-list {
      padding: 8px 12px 14px;
      flex: 1;
      overflow-y: auto;
      display: flex;
      flex-direction: column;
      gap: 10px;
      min-height: 120px;
      position: relative;
    }

    /* Cards */
    .card {
      background-color: var(--surface);
      border-radius: 10px;
      padding: 12px 14px;
      box-shadow: var(--shadow-sm);
      border: 1px solid var(--border-subtle);
      cursor: grab;
      user-select: none;
      display: flex;
      flex-direction: column;
      gap: 10px;
      position: relative;
    }

    .card:hover {
      box-shadow: var(--shadow-md);
      border-color: var(--border);
      transform: translateY(-1px);
    }

    .card.dragging {
      opacity: 0.45;
      transform: scale(0.98);
      cursor: grabbing;
      box-shadow: none;
    }

    .card.hidden-by-search {
      display: none !important;
    }

    .card-top {
      display: flex;
      align-items: flex-start;
      justify-content: space-between;
      gap: 10px;
    }

    .card-title {
      font-size: 0.88rem;
      font-weight: 500;
      line-height: 1.38;
      color: var(--text-main);
      word-break: break-word;
    }

    .delete-btn {
      background: none;
      border: none;
      color: var(--text-muted);
      cursor: pointer;
      padding: 3px;
      border-radius: 4px;
      display: flex;
      align-items: center;
      justify-content: center;
      opacity: 0.6;
      flex-shrink: 0;
    }

    .delete-btn:hover {
      opacity: 1;
      background-color: var(--border-subtle);
      color: #ef4444;
    }

    .card-footer {
      display: flex;
      align-items: center;
      justify-content: space-between;
      margin-top: 2px;
    }

    .badge {
      font-size: 0.72rem;
      font-weight: 600;
      padding: 2px 7px;
      border-radius: 5px;
      text-transform: capitalize;
      letter-spacing: 0.02em;
      border: 1px solid transparent;
    }

    .priority-low {
      background-color: var(--p-low-bg);
      color: var(--p-low-text);
      border-color: var(--p-low-border);
    }

    .priority-medium {
      background-color: var(--p-med-bg);
      color: var(--p-med-text);
      border-color: var(--p-med-border);
    }

    .priority-high {
      background-color: var(--p-high-bg);
      color: var(--p-high-text);
      border-color: var(--p-high-border);
    }

    /* Drop Indicator Bar */
    .drop-indicator {
      height: 3px;
      background: var(--drop-indicator);
      border-radius: 2px;
      margin: -3px 0;
      box-shadow: 0 0 8px rgba(59, 130, 246, 0.6);
      pointer-events: none;
    }

    /* Modal Form */
    .modal-backdrop {
      position: fixed;
      inset: 0;
      background-color: rgba(15, 23, 42, 0.55);
      backdrop-filter: blur(2px);
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 16px;
      z-index: 100;
      opacity: 0;
      pointer-events: none;
      transition: opacity 0.2s ease;
    }

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

    .modal {
      background-color: var(--surface);
      border-radius: 12px;
      width: 100%;
      max-width: 440px;
      box-shadow: var(--shadow-lg);
      border: 1px solid var(--border);
      overflow: hidden;
      transform: translateY(12px) scale(0.98);
      transition: transform 0.2s cubic-bezier(0.16, 1, 0.3, 1);
    }

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

    .modal-header {
      padding: 16px 20px;
      border-bottom: 1px solid var(--border-subtle);
      display: flex;
      justify-content: space-between;
      align-items: center;
    }

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

    .close-btn {
      background: none;
      border: none;
      font-size: 1.3rem;
      line-height: 1;
      color: var(--text-muted);
      cursor: pointer;
    }

    .close-btn:hover {
      color: var(--text-main);
    }

    .modal-body {
      padding: 20px;
      display: flex;
      flex-direction: column;
      gap: 16px;
    }

    .form-group {
      display: flex;
      flex-direction: column;
      gap: 6px;
    }

    .form-group label {
      font-size: 0.8rem;
      font-weight: 600;
      color: var(--text-muted);
      text-transform: uppercase;
      letter-spacing: 0.03em;
    }

    .form-group input,
    .form-group select {
      padding: 9px 12px;
      border-radius: 8px;
      border: 1px solid var(--border);
      background-color: var(--surface-subtle);
      color: var(--text-main);
      font-size: 0.9rem;
      outline: none;
    }

    .form-group input:focus,
    .form-group select:focus {
      border-color: var(--accent);
      background-color: var(--surface);
      box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
    }

    .modal-footer {
      padding: 14px 20px;
      background-color: var(--surface-subtle);
      border-top: 1px solid var(--border-subtle);
      display: flex;
      justify-content: flex-end;
      gap: 10px;
    }

    /* Empty state */
    .empty-message {
      padding: 28px 12px;
      text-align: center;
      font-size: 0.82rem;
      color: var(--text-muted);
      border: 1px dashed var(--border);
      border-radius: 8px;
      user-select: none;
    }

    @media (max-width: 820px) {
      main {
        flex-direction: column;
        align-items: stretch;
      }
      .column {
        max-width: 100%;
        max-height: none;
      }
      .toolbar {
        width: 100%;
        max-width: 100%;
        justify-content: space-between;
      }
      .search-box {
        max-width: 100%;
      }
    }
  </style>
</head>
<body>

  <header>
    <div class="brand">
      <div class="brand-logo">K</div>
      <h1>Sprint Board</h1>
    </div>

    <div class="toolbar">
      <div class="search-box">
        <svg viewBox="0 0 24 24"><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" placeholder="Filter tasks by title..." autocomplete="off">
      </div>
      <button class="btn btn-primary" id="openModalBtn">
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="12" y1="5" x2="12" y2="19"></line><line x1="5" y1="12" x2="19" y2="12"></line></svg>
        <span>Add Task</span>
      </button>
      <button class="theme-btn" id="themeToggleBtn" aria-label="Toggle light/dark theme">
        <svg id="themeIcon" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
          <!-- Moon icon for light mode -->
          <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 id="board">
    <!-- To do Column -->
    <div class="column" data-col-id="todo">
      <div class="column-header">
        <div class="column-title-group">
          <span class="status-dot dot-todo"></span>
          <h2>To Do</h2>
        </div>
        <span class="card-count" id="count-todo">0</span>
      </div>
      <div class="card-list" id="list-todo"></div>
    </div>

    <!-- In progress Column -->
    <div class="column" data-col-id="inprogress">
      <div class="column-header">
        <div class="column-title-group">
          <span class="status-dot dot-inprogress"></span>
          <h2>In Progress</h2>
        </div>
        <span class="card-count" id="count-inprogress">0</span>
      </div>
      <div class="card-list" id="list-inprogress"></div>
    </div>

    <!-- Done Column -->
    <div class="column" data-col-id="done">
      <div class="column-header">
        <div class="column-title-group">
          <span class="status-dot dot-done"></span>
          <h2>Done</h2>
        </div>
        <span class="card-count" id="count-done">0</span>
      </div>
      <div class="card-list" id="list-done"></div>
    </div>
  </main>

  <!-- Add Card Modal -->
  <div class="modal-backdrop" id="modalBackdrop">
    <div class="modal">
      <div class="modal-header">
        <h3>Create New Task</h3>
        <button class="close-btn" id="closeModalBtn">&times;</button>
      </div>
      <form id="addCardForm">
        <div class="modal-body">
          <div class="form-group">
            <label for="taskTitle">Task Title *</label>
            <input type="text" id="taskTitle" placeholder="e.g. Audit accessibility compliance" required maxlength="120" autocomplete="off">
          </div>
          <div class="form-group">
            <label for="taskPriority">Priority</label>
            <select id="taskPriority">
              <option value="low">Low Priority</option>
              <option value="medium" selected>Medium Priority</option>
              <option value="high">High Priority</option>
            </select>
          </div>
          <div class="form-group">
            <label for="taskColumn">Target Column</label>
            <select id="taskColumn">
              <option value="todo">To Do</option>
              <option value="inprogress">In Progress</option>
              <option value="done">Done</option>
            </select>
          </div>
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-secondary" id="cancelModalBtn">Cancel</button>
          <button type="submit" class="btn btn-primary">Create Task</button>
        </div>
      </form>
    </div>
  </div>

  <script>
    // State lives entirely in memory
    const state = {
      theme: 'light',
      filterQuery: '',
      cards: [
        {
          id: 'card-1',
          title: 'Implement OAuth2 with Google & GitHub providers',
          priority: 'high',
          column: 'todo'
        },
        {
          id: 'card-2',
          title: 'Design zero-data dashboard illustrations',
          priority: 'low',
          column: 'todo'
        },
        {
          id: 'card-3',
          title: 'Refactor Redis caching layer for API gateway',
          priority: 'high',
          column: 'inprogress'
        },
        {
          id: 'card-4',
          title: 'Optimize JS bundle chunking & tree-shaking',
          priority: 'medium',
          column: 'inprogress'
        },
        {
          id: 'card-5',
          title: 'Set up automated CI/CD with GitHub Actions',
          priority: 'medium',
          column: 'done'
        },
        {
          id: 'card-6',
          title: 'Generate semantic design tokens for light & dark palettes',
          priority: 'low',
          column: 'done'
        }
      ]
    };

    // DOM Elements
    const root = document.documentElement;
    const searchInput = document.getElementById('searchInput');
    const themeToggleBtn = document.getElementById('themeToggleBtn');
    const themeIcon = document.getElementById('themeIcon');
    const modalBackdrop = document.getElementById('modalBackdrop');
    const openModalBtn = document.getElementById('openModalBtn');
    const closeModalBtn = document.getElementById('closeModalBtn');
    const cancelModalBtn = document.getElementById('cancelModalBtn');
    const addCardForm = document.getElementById('addCardForm');
    const taskTitleInput = document.getElementById('taskTitle');
    const taskPrioritySelect = document.getElementById('taskPriority');
    const taskColumnSelect = document.getElementById('taskColumn');

    const columnLists = {
      todo: document.getElementById('list-todo'),
      inprogress: document.getElementById('list-inprogress'),
      done: document.getElementById('list-done')
    };

    const countBadges = {
      todo: document.getElementById('count-todo'),
      inprogress: document.getElementById('count-inprogress'),
      done: document.getElementById('count-done')
    };

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

    // Theme Toggle
    themeToggleBtn.addEventListener('click', () => {
      state.theme = state.theme === 'light' ? 'dark' : 'light';
      root.setAttribute('data-theme', state.theme);
      if (state.theme === 'dark') {
        // Sun icon
        themeIcon.innerHTML = `
          <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>
        `;
      } else {
        // Moon icon
        themeIcon.innerHTML = `<path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>`;
      }
    });

    // Render Function
    function renderBoard() {
      const counts = { todo: 0, inprogress: 0, done: 0 };
      const q = state.filterQuery.trim().toLowerCase();

      // Clear lists
      Object.values(columnLists).forEach(list => list.innerHTML = '');

      // Populate cards
      state.cards.forEach(card => {
        const matchesSearch = !q || card.title.toLowerCase().includes(q);
        if (matchesSearch) {
          counts[card.column]++;
        }

        const cardEl = createCardElement(card, matchesSearch);
        columnLists[card.column].appendChild(cardEl);
      });

      // Update Column Badges
      Object.keys(counts).forEach(colId => {
        countBadges[colId].textContent = counts[colId];
        
        // Show subtle empty message if no matching visible cards
        if (counts[colId] === 0) {
          const empty = document.createElement('div');
          empty.className = 'empty-message';
          empty.textContent = q ? 'No matching cards' : 'No tasks yet';
          columnLists[colId].appendChild(empty);
        }
      });
    }

    function createCardElement(card, isVisible) {
      const el = document.createElement('div');
      el.className = 'card';
      if (!isVisible) el.classList.add('hidden-by-search');
      el.draggable = true;
      el.dataset.id = card.id;

      el.innerHTML = `
        <div class="card-top">
          <span class="card-title">${escapeHTML(card.title)}</span>
          <button class="delete-btn" aria-label="Delete card" title="Delete card">
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"></polyline><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"></path></svg>
          </button>
        </div>
        <div class="card-footer">
          <span class="badge priority-${card.priority}">${card.priority}</span>
        </div>
      `;

      // Drag event listeners on card
      el.addEventListener('dragstart', (e) => {
        draggedCardId = card.id;
        el.classList.add('dragging');
        e.dataTransfer.effectAllowed = 'move';
        e.dataTransfer.setData('text/plain', card.id);
      });

      el.addEventListener('dragend', () => {
        draggedCardId = null;
        el.classList.remove('dragging');
        if (dropIndicator.parentNode) {
          dropIndicator.parentNode.removeChild(dropIndicator);
        }
      });

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

      return el;
    }

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

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

    // Modal Control
    function openModal() {
      taskTitleInput.value = '';
      taskPrioritySelect.value = 'medium';
      modalBackdrop.classList.add('open');
      taskTitleInput.focus();
    }

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

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

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

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

    addCardForm.addEventListener('submit', (e) => {
      e.preventDefault();
      const title = taskTitleInput.value.trim();
      if (!title) return;

      const newCard = {
        id: 'card-' + Date.now(),
        title: title,
        priority: taskPrioritySelect.value,
        column: taskColumnSelect.value
      };

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

    // Search filter input
    searchInput.addEventListener('input', (e) => {
      state.filterQuery = e.target.value;
      renderBoard();
    });

    // Drag and Drop Handling on Columns
    Object.keys(columnLists).forEach(colId => {
      const listEl = columnLists[colId];

      listEl.addEventListener('dragover', (e) => {
        e.preventDefault();
        e.dataTransfer.dropEffect = 'move';

        if (!draggedCardId) return;

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

      listEl.addEventListener('dragleave', (e) => {
        // If leaving column list completely, remove indicator
        if (!listEl.contains(e.relatedTarget)) {
          if (dropIndicator.parentNode === listEl) {
            listEl.removeChild(dropIndicator);
          }
        }
      });

      listEl.addEventListener('drop', (e) => {
        e.preventDefault();
        if (!draggedCardId) return;

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

        const targetCard = state.cards[cardIndex];
        targetCard.column = colId;

        // Determine destination index inside state.cards for ordering
        const indicatorParent = dropIndicator.parentNode;
        let insertBeforeId = null;

        if (indicatorParent === listEl && dropIndicator.nextElementSibling) {
          insertBeforeId = dropIndicator.nextElementSibling.dataset.id;
        }

        // Remove card and re-insert into array
        state.cards.splice(cardIndex, 1);

        if (insertBeforeId) {
          const targetIndex = state.cards.findIndex(c => c.id === insertBeforeId);
          if (targetIndex !== -1) {
            state.cards.splice(targetIndex, 0, targetCard);
          } else {
            state.cards.push(targetCard);
          }
        } else {
          state.cards.push(targetCard);
        }

        if (dropIndicator.parentNode) {
          dropIndicator.parentNode.removeChild(dropIndicator);
        }

        renderBoard();
      });
    });

    // Helper to calculate exact insert slot under cursor
    function getDragAfterElement(container, y) {
      const draggableElements = [...container.querySelectorAll('.card:not(.dragging):not(.hidden-by-search)')];

      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: offset, element: child };
        } else {
          return closest;
        }
      }, { offset: Number.NEGATIVE_INFINITY }).element;
    }

    // Initial render
    renderBoard();
  </script>
</body>
</html>
Reasoning trace (1,532 tokens)
**Developing Interactive Board**

I'm currently focusing on building the core functionality for an interactive Kanban board using only vanilla JavaScript, HTML, and CSS. The aim is to implement drag-and-drop functionality for cards across the three specified columns: "To do," "In progress," and "Done," while ensuring the dynamic card count badges update accurately with each movement. Pre-populating with six realistic cards is also underway.

**Implementing Core Features**

I'm now focused on integrating drag-and-drop functionality for cards between columns, ensuring the placeholder accurately indicates drop zones. Work also continues on dynamically updating card counts and adding the delete button to each card, with a clean visual design in mind.

**Styling and State**

I'm detailing the UI themes, prioritizing color palettes and text contrast for both light and dark modes. I'm also defining the in-memory state structure for cards, including their properties and current column, and planning the initial header elements with their functionalities.

**Implementing Drag and Drop**

I'm deep in the weeds of the HTML5 Drag and Drop API, fine-tuning `dragstart` and `dragover` events to ensure a smooth, ghost-free visual indicator. My focus is on accurately determining drop positions within columns and between cards. Simultaneously, I'm setting up the modal and form structure for adding new cards.

**Enhancing Card Functionality**

I'm solidifying the delete card mechanism, opting for an immediate removal with a subtle visual transition rather than a `confirm()` prompt. Furthermore, I'm refining the search filtering to ensure card counts update gracefully and are visually represented.