Models 44

Nemotron 3 Ultra (free)

by NVIDIAReasoningClean
Open in new window

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

How it went

Total time
1 min 9 s
Cost
$0
Output tokens
7,881
Spent thinking
258 tokens
Speed
116.3 tok/s
Console errors
0
First token after
1.0 s
First visible text after
5.1 s
Prompt tokens
513
Code size
26.7 KB
Finish reason
stop
Served by
Nvidia
Model id
nvidia/nemotron-3-ultra-550b-a55b:free
Reasoning sent
{"effort":"high"}
Max output
32,000 tokens
Generated
Nov 9, 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 · 943 lines

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Kanban Board</title>
  <style>
    :root {
      --bg: #f4f5f7;
      --surface: #ffffff;
      --text: #172b4d;
      --text-muted: #5e6c84;
      --border: #dfe1e6;
      --column-bg: #ebecf0;
      --primary: #0052cc;
      --primary-hover: #0065ff;
      --danger: #de350b;
      --danger-hover: #ff5630;
      --shadow: rgba(9, 30, 66, 0.15);
      --shadow-hover: rgba(9, 30, 66, 0.25);
      --high: #de350b;
      --high-bg: #ffebe9;
      --medium: #ff8b00;
      --medium-bg: #fff4e6;
      --low: #00875a;
      --low-bg: #e4fcf1;
      --drop-indicator: #0052cc;
      --dragging-opacity: 0.4;
    }

    [data-theme="dark"] {
      --bg: #1a1f2e;
      --surface: #23293d;
      --text: #e4e6eb;
      --text-muted: #8b919a;
      --border: #3a3f51;
      --column-bg: #1e2333;
      --primary: #4c9aff;
      --primary-hover: #6db3ff;
      --danger: #ff6b6b;
      --danger-hover: #ff8787;
      --shadow: rgba(0, 0, 0, 0.3);
      --shadow-hover: rgba(0, 0, 0, 0.5);
      --high: #ff6b6b;
      --high-bg: #3d1a1a;
      --medium: #ffb347;
      --medium-bg: #3d2d1a;
      --low: #4ade80;
      --low-bg: #1a3d2a;
      --drop-indicator: #4c9aff;
    }

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

    body {
      font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
      background: var(--bg);
      color: var(--text);
      min-height: 100vh;
      transition: background 0.2s, color 0.2s;
      line-height: 1.5;
    }

    .app {
      max-width: 1200px;
      margin: 0 auto;
      padding: 24px 16px;
    }

    header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 24px;
      flex-wrap: wrap;
      gap: 16px;
    }

    h1 {
      font-size: 1.75rem;
      font-weight: 600;
      color: var(--text);
    }

    .header-actions {
      display: flex;
      align-items: center;
      gap: 12px;
      flex-wrap: wrap;
    }

    .search-wrapper {
      position: relative;
    }

    .search-input {
      padding: 10px 14px 10px 40px;
      border: 1px solid var(--border);
      border-radius: 8px;
      background: var(--surface);
      color: var(--text);
      font-size: 0.9rem;
      width: 280px;
      max-width: 100%;
      transition: border-color 0.2s, box-shadow 0.2s;
    }

    .search-input:focus {
      outline: none;
      border-color: var(--primary);
      box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 20%, transparent);
    }

    .search-icon {
      position: absolute;
      left: 12px;
      top: 50%;
      transform: translateY(-50%);
      color: var(--text-muted);
      pointer-events: none;
      width: 18px;
      height: 18px;
    }

    .btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 8px;
      padding: 10px 16px;
      border: none;
      border-radius: 8px;
      font-size: 0.9rem;
      font-weight: 500;
      cursor: pointer;
      transition: background 0.2s, transform 0.1s;
    }

    .btn:active {
      transform: scale(0.98);
    }

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

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

    .btn-icon {
      padding: 10px;
      background: var(--surface);
      border: 1px solid var(--border);
      color: var(--text);
      border-radius: 8px;
    }

    .btn-icon:hover {
      background: var(--column-bg);
    }

    .btn-danger {
      background: transparent;
      color: var(--danger);
      padding: 6px;
      border-radius: 6px;
    }

    .btn-danger:hover {
      background: color-mix(in srgb, var(--danger) 15%, transparent);
    }

    .board {
      display: flex;
      gap: 16px;
      overflow-x: auto;
      padding-bottom: 8px;
    }

    @media (max-width: 768px) {
      .board {
        flex-wrap: nowrap;
      }
    }

    .column {
      flex: 1;
      min-width: 280px;
      max-width: 360px;
      background: var(--column-bg);
      border-radius: 12px;
      padding: 16px;
      display: flex;
      flex-direction: column;
      height: fit-content;
      max-height: calc(100vh - 200px);
    }

    .column-header {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 0 8px 12px;
      border-bottom: 1px solid var(--border);
      margin-bottom: 12px;
    }

    .column-title {
      display: flex;
      align-items: center;
      gap: 8px;
      font-size: 0.875rem;
      font-weight: 600;
      text-transform: uppercase;
      letter-spacing: 0.5px;
      color: var(--text-muted);
    }

    .card-count {
      background: var(--primary);
      color: white;
      font-size: 0.75rem;
      font-weight: 600;
      padding: 2px 8px;
      border-radius: 12px;
      min-width: 24px;
      text-align: center;
    }

    .cards-container {
      min-height: 100px;
      display: flex;
      flex-direction: column;
      gap: 8px;
      padding: 4px 8px;
    }

    .cards-container.drag-over {
      background: color-mix(in srgb, var(--drop-indicator) 10%, transparent);
      border-radius: 8px;
    }

    .drop-indicator {
      height: 2px;
      background: var(--drop-indicator);
      border-radius: 2px;
      margin: 4px 8px;
      opacity: 0;
      transition: opacity 0.15s;
    }

    .drop-indicator.visible {
      opacity: 1;
    }

    .card {
      background: var(--surface);
      border: 1px solid var(--border);
      border-radius: 8px;
      padding: 12px;
      cursor: grab;
      box-shadow: 0 1px 2px var(--shadow);
      transition: box-shadow 0.2s, transform 0.2s, opacity 0.2s;
      position: relative;
    }

    .card:hover {
      box-shadow: 0 4px 12px var(--shadow-hover);
    }

    .card.dragging {
      opacity: var(--dragging-opacity);
      cursor: grabbing;
      transform: rotate(2deg);
      box-shadow: 0 8px 24px var(--shadow-hover);
      z-index: 100;
    }

    .card-header {
      display: flex;
      justify-content: space-between;
      align-items: flex-start;
      margin-bottom: 8px;
    }

    .card-title {
      font-size: 0.9rem;
      font-weight: 500;
      color: var(--text);
      word-break: break-word;
    }

    .card-priority {
      font-size: 0.65rem;
      font-weight: 600;
      text-transform: uppercase;
      letter-spacing: 0.3px;
      padding: 3px 8px;
      border-radius: 4px;
      flex-shrink: 0;
      margin-left: 8px;
    }

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

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

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

    .card-actions {
      display: flex;
      justify-content: flex-end;
      opacity: 0;
      transition: opacity 0.2s;
    }

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

    .modal-overlay {
      position: fixed;
      inset: 0;
      background: rgba(9, 30, 66, 0.54);
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 16px;
      z-index: 1000;
      opacity: 0;
      visibility: hidden;
      transition: opacity 0.2s, visibility 0.2s;
    }

    .modal-overlay.open {
      opacity: 1;
      visibility: visible;
    }

    .modal {
      background: var(--surface);
      border-radius: 12px;
      padding: 24px;
      width: 100%;
      max-width: 400px;
      box-shadow: 0 12px 40px var(--shadow-hover);
      transform: scale(0.95);
      transition: transform 0.2s;
    }

    .modal-overlay.open .modal {
      transform: scale(1);
    }

    .modal h2 {
      font-size: 1.25rem;
      font-weight: 600;
      margin-bottom: 8px;
    }

    .modal p {
      color: var(--text-muted);
      font-size: 0.9rem;
      margin-bottom: 20px;
    }

    .form-group {
      margin-bottom: 16px;
    }

    .form-group label {
      display: block;
      font-size: 0.875rem;
      font-weight: 500;
      margin-bottom: 6px;
    }

    .form-group input,
    .form-group select {
      width: 100%;
      padding: 10px 12px;
      border: 1px solid var(--border);
      border-radius: 8px;
      background: var(--surface);
      color: var(--text);
      font-size: 0.9rem;
      transition: border-color 0.2s, box-shadow 0.2s;
    }

    .form-group input:focus,
    .form-group select:focus {
      outline: none;
      border-color: var(--primary);
      box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 20%, transparent);
    }

    .form-group input::placeholder {
      color: var(--text-muted);
    }

    .modal-actions {
      display: flex;
      justify-content: flex-end;
      gap: 12px;
      margin-top: 8px;
    }

    .btn-secondary {
      background: var(--column-bg);
      color: var(--text);
      border: 1px solid var(--border);
    }

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

    .empty-state {
      text-align: center;
      padding: 24px 16px;
      color: var(--text-muted);
      font-size: 0.875rem;
    }

    @media (max-width: 480px) {
      .app {
        padding: 16px 12px;
      }
      .column {
        min-width: 100%;
        max-width: none;
      }
      .search-input {
        width: 100%;
      }
      header {
        flex-direction: column;
        align-items: stretch;
      }
      .header-actions {
        justify-content: space-between;
      }
    }

    .sr-only {
      position: absolute;
      width: 1px;
      height: 1px;
      padding: 0;
      margin: -1px;
      overflow: hidden;
      clip: rect(0, 0, 0, 0);
      white-space: nowrap;
      border: 0;
    }
  </style>
</head>
<body>
  <div class="app">
    <header>
      <h1>Kanban Board</h1>
      <div class="header-actions">
        <div class="search-wrapper">
          <svg class="search-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <circle cx="11" cy="11" r="8"></circle>
            <line x1="21" y1="21" x2="16.65" y2="16.65"></line>
          </svg>
          <input type="search" class="search-input" id="searchInput" placeholder="Search cards..." aria-label="Search cards by title">
        </div>
        <button class="btn btn-icon" id="themeToggle" aria-label="Toggle theme">
          <svg id="sunIcon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="display:none">
            <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="moonIcon" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
            <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"></path>
          </svg>
        </button>
        <button class="btn btn-primary" id="addCardBtn">
          <svg width="18" height="18" 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>
          Add Card
        </button>
      </div>
    </header>

    <div class="board" id="board" role="list" aria-label="Kanban columns">
      <!-- Columns rendered by JS -->
    </div>
  </div>

  <div class="modal-overlay" id="modalOverlay" role="dialog" aria-modal="true" aria-labelledby="modalTitle">
    <div class="modal">
      <h2 id="modalTitle">Add New Card</h2>
      <p>Fill in the details below to create a new card.</p>
      <form id="cardForm">
        <div class="form-group">
          <label for="cardTitle">Title <span aria-hidden="true">*</span></label>
          <input type="text" id="cardTitle" name="title" required placeholder="Enter card title" autocomplete="off">
        </div>
        <div class="form-group">
          <label for="cardColumn">Column</label>
          <select id="cardColumn" name="column">
            <option value="todo">To Do</option>
            <option value="inprogress">In Progress</option>
            <option value="done">Done</option>
          </select>
        </div>
        <div class="form-group">
          <label for="cardPriority">Priority</label>
          <select id="cardPriority" name="priority">
            <option value="low">Low</option>
            <option value="medium" selected>Medium</option>
            <option value="high">High</option>
          </select>
        </div>
        <div class="modal-actions">
          <button type="button" class="btn btn-secondary" id="cancelBtn">Cancel</button>
          <button type="submit" class="btn btn-primary">Add Card</button>
        </div>
      </form>
    </div>
  </div>

  <script>
    // Initial data
    const initialCards = [
      { id: 1, title: "Design new landing page", column: "todo", priority: "high" },
      { id: 2, title: "Set up CI/CD pipeline", column: "todo", priority: "medium" },
      { id: 3, title: "Write API documentation", column: "inprogress", priority: "low" },
      { id: 4, title: "Fix authentication bug", column: "inprogress", priority: "high" },
      { id: 5, title: "Deploy v2.1 to production", column: "done", priority: "medium" },
      { id: 6, title: "Code review for PR #234", column: "done", priority: "low" },
    ];

    // State
    let cards = [...initialCards];
    let nextId = 7;
    let draggedCardId = null;
    let dragOverCardId = null;
    let dragOverPosition = null; // 'before' or 'after'

    // DOM elements
    const board = document.getElementById('board');
    const searchInput = document.getElementById('searchInput');
    const modalOverlay = document.getElementById('modalOverlay');
    const cardForm = document.getElementById('cardForm');
    const addCardBtn = document.getElementById('addCardBtn');
    const cancelBtn = document.getElementById('cancelBtn');
    const themeToggle = document.getElementById('themeToggle');
    const sunIcon = document.getElementById('sunIcon');
    const moonIcon = document.getElementById('moonIcon');

    // Column definitions
    const columns = [
      { id: 'todo', title: 'To Do' },
      { id: 'inprogress', title: 'In Progress' },
      { id: 'done', title: 'Done' },
    ];

    // Priority config
    const priorityConfig = {
      high: { label: 'High', class: 'priority-high' },
      medium: { label: 'Medium', class: 'priority-medium' },
      low: { label: 'Low', class: 'priority-low' },
    };

    // Theme management
    function initTheme() {
      const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
      document.documentElement.setAttribute('data-theme', prefersDark ? 'dark' : 'light');
      updateThemeIcons(prefersDark);
    }

    function updateThemeIcons(isDark) {
      sunIcon.style.display = isDark ? 'block' : 'none';
      moonIcon.style.display = isDark ? 'none' : 'block';
    }

    function toggleTheme() {
      const current = document.documentElement.getAttribute('data-theme');
      const isDark = current === 'dark';
      document.documentElement.setAttribute('data-theme', isDark ? 'light' : 'dark');
      updateThemeIcons(!isDark);
    }

    // Rendering
    function getFilteredCards() {
      const query = searchInput.value.toLowerCase().trim();
      if (!query) return cards;
      return cards.filter(card => card.title.toLowerCase().includes(query));
    }

    function createCardElement(card) {
      const cardEl = document.createElement('article');
      cardEl.className = 'card';
      cardEl.draggable = true;
      cardEl.dataset.id = card.id;
      cardEl.setAttribute('role', 'listitem');
      cardEl.setAttribute('aria-label', `${card.title}, ${priorityConfig[card.priority].label} priority`);

      cardEl.innerHTML = `
        <div class="card-header">
          <h3 class="card-title">${escapeHtml(card.title)}</h3>
          <span class="card-priority ${priorityConfig[card.priority].class}">${priorityConfig[card.priority].label}</span>
        </div>
        <div class="card-actions">
          <button class="btn btn-danger" aria-label="Delete ${escapeHtml(card.title)}" data-delete="${card.id}">
            <svg width="16" height="16" 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>
      `;

      // Drag events
      cardEl.addEventListener('dragstart', handleDragStart);
      cardEl.addEventListener('dragend', handleDragEnd);
      cardEl.addEventListener('dragover', handleCardDragOver);
      cardEl.addEventListener('dragleave', handleCardDragLeave);
      cardEl.addEventListener('drop', handleCardDrop);

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

      return cardEl;
    }

    function createDropIndicator() {
      const indicator = document.createElement('div');
      indicator.className = 'drop-indicator';
      return indicator;
    }

    function renderBoard() {
      const filteredCards = getFilteredCards();
      board.innerHTML = '';

      columns.forEach(col => {
        const columnCards = filteredCards.filter(c => c.column === col.id);
        const columnEl = document.createElement('section');
        columnEl.className = 'column';
        columnEl.dataset.column = col.id;
        columnEl.setAttribute('role', 'region');
        columnEl.setAttribute('aria-label', `${col.title} column`);

        const cardsContainer = document.createElement('div');
        cardsContainer.className = 'cards-container';
        cardsContainer.dataset.column = col.id;

        const dropIndicator = createDropIndicator();
        cardsContainer.appendChild(dropIndicator);

        if (columnCards.length === 0) {
          const emptyState = document.createElement('div');
          emptyState.className = 'empty-state';
          emptyState.textContent = 'No cards';
          cardsContainer.appendChild(emptyState);
        } else {
          columnCards.forEach(card => {
            cardsContainer.appendChild(createCardElement(card));
          });
        }

        // Column drag events
        cardsContainer.addEventListener('dragover', handleColumnDragOver);
        cardsContainer.addEventListener('dragleave', handleColumnDragLeave);
        cardsContainer.addEventListener('drop', handleColumnDrop);

        columnEl.innerHTML = `
          <div class="column-header">
            <div class="column-title">${col.title}</div>
            <span class="card-count">${columnCards.length}</span>
          </div>
        `;
        columnEl.appendChild(cardsContainer);
        board.appendChild(columnEl);
      });
    }

    function updateCardCounts() {
      const filteredCards = getFilteredCards();
      columns.forEach(col => {
        const count = filteredCards.filter(c => c.column === col.id).length;
        const countEl = board.querySelector(`[data-column="${col.id}"] .card-count`);
        if (countEl) countEl.textContent = count;
      });
    }

    // Card operations
    function addCard(title, column, priority) {
      const card = { id: nextId++, title, column, priority };
      cards.push(card);
      renderBoard();
    }

    function deleteCard(id) {
      cards = cards.filter(c => c.id !== id);
      renderBoard();
    }

    function moveCard(cardId, targetColumn, insertBeforeId = null) {
      const cardIndex = cards.findIndex(c => c.id === cardId);
      if (cardIndex === -1) return;

      const card = cards[cardIndex];
      if (card.column === targetColumn && !insertBeforeId) return;

      // Remove from current position
      cards.splice(cardIndex, 1);

      // Find insert position in target column
      const targetCards = cards.filter(c => c.column === targetColumn);
      let insertIndex = cards.length; // default to end

      if (insertBeforeId) {
        const beforeIndex = cards.findIndex(c => c.id === insertBeforeId);
        if (beforeIndex !== -1) {
          insertIndex = beforeIndex;
        }
      } else if (targetCards.length > 0) {
        // Insert after last card in target column
        const lastTargetCard = targetCards[targetCards.length - 1];
        insertIndex = cards.findIndex(c => c.id === lastTargetCard.id) + 1;
      }

      card.column = targetColumn;
      cards.splice(insertIndex, 0, card);
      renderBoard();
    }

    // Drag and drop handlers
    function handleDragStart(e) {
      draggedCardId = parseInt(e.target.closest('.card').dataset.id, 10);
      e.target.classList.add('dragging');
      e.dataTransfer.effectAllowed = 'move';
      e.dataTransfer.setData('text/plain', draggedCardId.toString());
    }

    function handleDragEnd(e) {
      e.target.classList.remove('dragging');
      // Clean up all drop indicators
      document.querySelectorAll('.drop-indicator.visible').forEach(el => el.classList.remove('visible'));
      document.querySelectorAll('.cards-container.drag-over').forEach(el => el.classList.remove('drag-over'));
      draggedCardId = null;
      dragOverCardId = null;
      dragOverPosition = null;
    }

    function handleCardDragOver(e) {
      e.preventDefault();
      e.dataTransfer.dropEffect = 'move';

      const cardEl = e.target.closest('.card');
      if (!cardEl || parseInt(cardEl.dataset.id, 10) === draggedCardId) return;

      const rect = cardEl.getBoundingClientRect();
      const midY = rect.top + rect.height / 2;
      dragOverPosition = e.clientY < midY ? 'before' : 'after';
      dragOverCardId = parseInt(cardEl.dataset.id, 10);

      // Show indicator on the card
      document.querySelectorAll('.drop-indicator.visible').forEach(el => el.classList.remove('visible'));
      const indicator = cardEl.querySelector('.drop-indicator') || createDropIndicatorForCard(cardEl);
      if (dragOverPosition === 'before') {
        cardEl.insertBefore(indicator, cardEl.firstChild);
      } else {
        cardEl.appendChild(indicator);
      }
      indicator.classList.add('visible');
    }

    function createDropIndicatorForCard(cardEl) {
      const indicator = document.createElement('div');
      indicator.className = 'drop-indicator';
      indicator.style.margin = '4px 0';
      return indicator;
    }

    function handleCardDragLeave(e) {
      // Only hide if leaving the card entirely
      if (!e.currentTarget.contains(e.relatedTarget)) {
        const indicator = e.currentTarget.querySelector('.drop-indicator.visible');
        if (indicator) indicator.classList.remove('visible');
      }
    }

    function handleCardDrop(e) {
      e.preventDefault();
      if (dragOverCardId && draggedCardId !== dragOverCardId) {
        const targetCard = cards.find(c => c.id === dragOverCardId);
        if (targetCard) {
          moveCard(draggedCardId, targetCard.column, dragOverPosition === 'before' ? dragOverCardId : null);
        }
      }
      document.querySelectorAll('.drop-indicator.visible').forEach(el => el.classList.remove('visible'));
    }

    function handleColumnDragOver(e) {
      e.preventDefault();
      e.dataTransfer.dropEffect = 'move';

      const container = e.currentTarget;
      if (draggedCardId === null) return;

      container.classList.add('drag-over');
      const indicator = container.querySelector('.drop-indicator');
      indicator.classList.add('visible');

      // Position indicator based on mouse position relative to cards
      const cards = container.querySelectorAll('.card:not(.dragging)');
      let placed = false;

      for (const card of cards) {
        const rect = card.getBoundingClientRect();
        if (e.clientY < rect.top + rect.height / 2) {
          container.insertBefore(indicator, card);
          placed = true;
          break;
        }
      }

      if (!placed) {
        container.appendChild(indicator);
      }
    }

    function handleColumnDragLeave(e) {
      if (!e.currentTarget.contains(e.relatedTarget)) {
        e.currentTarget.classList.remove('drag-over');
        e.currentTarget.querySelector('.drop-indicator').classList.remove('visible');
      }
    }

    function handleColumnDrop(e) {
      e.preventDefault();
      const container = e.currentTarget;
      const targetColumn = container.dataset.column;

      container.classList.remove('drag-over');
      container.querySelector('.drop-indicator').classList.remove('visible');

      if (draggedCardId) {
        // Check if dropped on a specific card position
        const indicator = container.querySelector('.drop-indicator');
        const nextCard = indicator.nextElementSibling;
        if (nextCard && nextCard.classList.contains('card')) {
          const insertBeforeId = parseInt(nextCard.dataset.id, 10);
          moveCard(draggedCardId, targetColumn, insertBeforeId);
        } else {
          moveCard(draggedCardId, targetColumn);
        }
      }
    }

    // Modal handlers
    function openModal() {
      modalOverlay.classList.add('open');
      document.getElementById('cardTitle').focus();
      document.body.style.overflow = 'hidden';
    }

    function closeModal() {
      modalOverlay.classList.remove('open');
      cardForm.reset();
      document.body.style.overflow = '';
    }

    function handleFormSubmit(e) {
      e.preventDefault();
      const formData = new FormData(cardForm);
      const title = formData.get('title').trim();
      if (!title) return;

      const column = formData.get('column');
      const priority = formData.get('priority');
      addCard(title, column, priority);
      closeModal();
    }

    // Search
    function handleSearch() {
      renderBoard();
    }

    // Event listeners
    addCardBtn.addEventListener('click', openModal);
    cancelBtn.addEventListener('click', closeModal);
    modalOverlay.addEventListener('click', (e) => {
      if (e.target === modalOverlay) closeModal();
    });
    cardForm.addEventListener('submit', handleFormSubmit);
    searchInput.addEventListener('input', handleSearch);
    themeToggle.addEventListener('click', toggleTheme);

    // Keyboard support for modal
    document.addEventListener('keydown', (e) => {
      if (e.key === 'Escape' && modalOverlay.classList.contains('open')) {
        closeModal();
      }
    });

    // Utility
    function escapeHtml(text) {
      const div = document.createElement('div');
      div.textContent = text;
      return div.innerHTML;
    }

    // Initialize
    initTheme();
    renderBoard();
  </script>
</body>
</html>
Reasoning trace (258 tokens)
The user wants an interactive Kanban board with specific requirements. Let me plan this out:

1. Three columns: To Do, In Progress, Done with card counts
2. 6 pre-populated cards across columns
3. Cards have title + priority badge (low/medium/high)
4. Modal form to add cards (title required)
5. Delete button on each card
6. Drag and drop between columns with visual drop indicator
7. Search field filtering by title
8. Light/dark theme toggle
9. State in memory only
10. No external libraries, vanilla JS only
11. Must work in 1280x800 and down to 375px
12. No horizontal scrollbar
13. Meaningful content on first paint
14. No storage APIs, no alert/confirm/prompt

Let me build this with clean, modern CSS and vanilla JS. I'll use CSS variables for theming, native HTML5 drag and drop API, and keep it all in one HTML file.

For the drag and drop visual indicator, I'll show a line or highlight the drop zone when dragging over.

Let me create the HTML structure, CSS, and JS all in one file.