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 49 s
Cost
$0
Output tokens
6,679
Spent thinking
0 tokens
Speed
61.8 tok/s
Console errors
0
First token after
906 ms
First visible text after
906 ms
Prompt tokens
513
Code size
21.5 KB
Finish reason
stop
Served by
Nvidia
Model id
nvidia/nemotron-3-ultra-550b-a55b:free
Reasoning sent
{"effort":"none"}
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 · 427 lines

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Interactive Kanban Board</title>
<style>
  :root {
    --bg-primary: #f4f5f7;
    --bg-secondary: #ffffff;
    --bg-tertiary: #ebecf0;
    --text-primary: #172b4d;
    --text-secondary: #5e6c84;
    --border-color: #dfe1e6;
    --shadow-sm: 0 1px 2px rgba(9,30,66,.15);
    --shadow-md: 0 4px 8px rgba(9,30,66,.15);
    --accent: #0052cc;
    --accent-hover: #0065ff;
    --danger: #de350b;
    --danger-hover: #ff5630;
    --priority-high: #de350b;
    --priority-medium: #ff991f;
    --priority-low: #00875a;
    --drop-zone: #0052cc;
    --modal-overlay: rgba(9,30,66,.54);
    --scrollbar-thumb: #c1c7cd;
    --scrollbar-track: transparent;
  }
  [data-theme="dark"] {
    --bg-primary: #1d2129;
    --bg-secondary: #282c34;
    --bg-tertiary: #2f333c;
    --text-primary: #ffffff;
    --text-secondary: #a0a7b0;
    --border-color: #3e4451;
    --shadow-sm: 0 1px 2px rgba(0,0,0,.3);
    --shadow-md: 0 4px 12px rgba(0,0,0,.4);
    --accent: #4c9aff;
    --accent-hover: #6aa7ff;
    --danger: #ff5630;
    --danger-hover: #ff8b6c;
    --scrollbar-thumb: #5a5f6b;
  }
  * { box-sizing: border-box; margin: 0; padding: 0; }
  html, body { height: 100%; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-size: 14px; line-height: 1.5; background: var(--bg-primary); color: var(--text-primary); transition: background .2s, color .2s; }
  .app { display: flex; flex-direction: column; height: 100vh; overflow: hidden; }
  header { display: flex; align-items: center; justify-content: space-between; padding: 12px 24px; background: var(--bg-secondary); border-bottom: 1px solid var(--border-color); flex-shrink: 0; }
  h1 { font-size: 1.25rem; font-weight: 600; }
  .header-actions { display: flex; align-items: center; gap: 12px; }
  .search-wrapper { position: relative; }
  .search-wrapper svg { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); width: 16px; height: 16px; color: var(--text-secondary); pointer-events: none; }
  #search { width: 280px; padding: 8px 12px 8px 36px; border: 1px solid var(--border-color); border-radius: 4px; background: var(--bg-primary); color: var(--text-primary); font-size: 14px; transition: border-color .2s, box-shadow .2s; }
  #search:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(0,82,204,.15); }
  @media (max-width: 768px) { #search { width: 180px; } }
  .btn { display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 8px 12px; border: none; border-radius: 4px; font-size: 14px; font-weight: 500; cursor: pointer; transition: background .2s, color .2s; }
  .btn-primary { background: var(--accent); color: #fff; }
  .btn-primary:hover { background: var(--accent-hover); }
  .btn-icon { padding: 8px; background: transparent; color: var(--text-secondary); border-radius: 4px; }
  .btn-icon:hover { background: var(--bg-tertiary); color: var(--text-primary); }
  .btn-danger { background: transparent; color: var(--danger); }
  .btn-danger:hover { background: rgba(222,53,11,.1); }
  main { flex: 1; overflow-x: auto; overflow-y: hidden; padding: 24px; gap: 24px; display: flex; align-items: flex-start; min-width: 0; }
  .board { display: flex; gap: 24px; min-width: max-content; }
  .column { width: 280px; min-height: 500px; background: var(--bg-tertiary); border-radius: 12px; display: flex; flex-direction: column; box-shadow: var(--shadow-sm); transition: background .2s; }
  .column.drag-over { background: rgba(0,82,204,.08); border: 2px dashed var(--drop-zone); }
  [data-theme="dark"] .column.drag-over { background: rgba(76,154,255,.15); }
  .column-header { display: flex; align-items: center; justify-content: space-between; padding: 12px 16px; border-bottom: 1px solid var(--border-color); border-radius: 12px 12px 0 0; }
  .column-title { font-weight: 600; font-size: 14px; text-transform: uppercase; letter-spacing: .5px; color: var(--text-secondary); }
  .card-count { background: var(--bg-primary); color: var(--text-secondary); padding: 2px 8px; border-radius: 12px; font-size: 12px; font-weight: 600; min-width: 24px; text-align: center; }
  .cards { flex: 1; overflow-y: auto; padding: 12px; display: flex; flex-direction: column; gap: 8px; min-height: 200px; }
  .cards::-webkit-scrollbar { width: 6px; }
  .cards::-webkit-scrollbar-track { background: var(--scrollbar-track); }
  .cards::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border-radius: 3px; }
  .card { background: var(--bg-secondary); border: 1px solid var(--border-color); border-radius: 8px; padding: 12px; box-shadow: var(--shadow-sm); cursor: grab; transition: box-shadow .2s, transform .1s; position: relative; }
  .card:hover { box-shadow: var(--shadow-md); }
  .card.dragging { opacity: .4; cursor: grabbing; box-shadow: var(--shadow-md); }
  .card.drag-over-bottom::after { content: ''; position: absolute; bottom: -4px; left: 8px; right: 8px; height: 2px; background: var(--drop-zone); border-radius: 1px; }
  .card.drag-over-top::before { content: ''; position: absolute; top: -4px; left: 8px; right: 8px; height: 2px; background: var(--drop-zone); border-radius: 1px; }
  .card-title { font-weight: 500; margin-bottom: 8px; word-break: break-word; }
  .card-footer { display: flex; align-items: center; justify-content: space-between; }
  .priority-badge { font-size: 11px; font-weight: 600; padding: 2px 6px; border-radius: 3px; text-transform: uppercase; letter-spacing: .3px; }
  .priority-high { background: rgba(222,53,11,.15); color: var(--priority-high); }
  .priority-medium { background: rgba(255,153,31,.15); color: var(--priority-medium); }
  .priority-low { background: rgba(0,135,90,.15); color: var(--priority-low); }
  .btn-delete { opacity: 0; padding: 4px; color: var(--text-secondary); transition: opacity .2s, color .2s, background .2s; border-radius: 4px; }
  .card:hover .btn-delete { opacity: 1; }
  .btn-delete:hover { color: var(--danger); background: rgba(222,53,11,.1); }
  .empty-state { flex: 1; display: flex; align-items: center; justify-content: center; color: var(--text-secondary); font-size: 13px; text-align: center; padding: 24px; }
  .drop-indicator { height: 2px; background: var(--drop-zone); margin: 0 12px; border-radius: 1px; opacity: 0; transition: opacity .15s; }
  .drop-indicator.visible { opacity: 1; }
  .modal-overlay { position: fixed; inset: 0; background: var(--modal-overlay); display: flex; align-items: center; justify-content: center; padding: 24px; z-index: 1000; opacity: 0; visibility: hidden; transition: opacity .2s, visibility .2s; }
  .modal-overlay.open { opacity: 1; visibility: visible; }
  .modal { background: var(--bg-secondary); border-radius: 12px; padding: 24px; width: 100%; max-width: 400px; box-shadow: var(--shadow-md); transform: scale(.95); transition: transform .2s; }
  .modal-overlay.open .modal { transform: scale(1); }
  .modal h2 { margin-bottom: 20px; font-size: 1.125rem; font-weight: 600; }
  .form-group { margin-bottom: 16px; }
  .form-group label { display: block; margin-bottom: 6px; font-size: 13px; font-weight: 500; color: var(--text-secondary); }
  .form-group input, .form-group select { width: 100%; padding: 10px 12px; border: 1px solid var(--border-color); border-radius: 4px; background: var(--bg-primary); color: var(--text-primary); font-size: 14px; transition: border-color .2s, box-shadow .2s; }
  .form-group input:focus, .form-group select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(0,82,204,.15); }
  .form-actions { display: flex; justify-content: flex-end; gap: 12px; margin-top: 8px; }
  .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; }
  @media (max-width: 1024px) { .column { width: 260px; } }
  @media (max-width: 768px) { main { padding: 16px; gap: 16px; } .column { width: 240px; } }
  @media (max-width: 480px) { .board { flex-direction: column; align-items: stretch; } .column { width: 100%; min-height: 300px; } main { overflow-x: hidden; overflow-y: auto; align-items: stretch; } }
</style>
</head>
<body>
<div class="app">
  <header>
    <h1>Kanban Board</h1>
    <div class="header-actions">
      <div class="search-wrapper" role="search">
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
        <input type="search" id="search" placeholder="Search cards by title..." aria-label="Search cards">
      </div>
      <button class="btn btn-icon" id="themeToggle" aria-label="Toggle dark mode" title="Toggle theme">
        <svg id="sunIcon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display:none"><circle cx="12" cy="12" r="5"/><path d="M12 1v2M12 21v2M4.22 4.22l1.42 1.42M18.36 18.36l1.42 1.42M1 12h2M21 12h2M4.22 19.78l1.42-1.42M18.36 5.64l1.42-1.42"/></svg>
        <svg id="moonIcon" xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
      </button>
      <button class="btn btn-primary" id="addCardBtn" aria-haspopup="dialog">+ Add Card</button>
    </div>
  </header>
  <main>
    <div class="board" id="board" role="list" aria-label="Kanban columns">
    </div>
  </main>
</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>
    <form id="cardForm">
      <div class="form-group">
        <label for="cardTitle">Title <span aria-hidden="true">*</span></label>
        <input type="text" id="cardTitle" required maxlength="100" autocomplete="off" aria-required="true">
      </div>
      <div class="form-group">
        <label for="cardColumn">Column</label>
        <select id="cardColumn">
          <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">
          <option value="low">Low</option>
          <option value="medium" selected>Medium</option>
          <option value="high">High</option>
        </select>
      </div>
      <div class="form-actions">
        <button type="button" class="btn btn-danger" id="cancelBtn">Cancel</button>
        <button type="submit" class="btn btn-primary">Add Card</button>
      </div>
    </form>
  </div>
</div>
<script>
(function() {
  'use strict';
  let cards = [];
  let nextId = 1;
  let draggedCard = null;
  let dragOverCard = null;
  let dragOverPosition = null;
  const searchTerm = { current: '' };
  const theme = { current: 'light' };
  const columns = [
    { id: 'todo', title: 'To do' },
    { id: 'inprogress', title: 'In progress' },
    { id: 'done', title: 'Done' }
  ];
  const boardEl = document.getElementById('board');
  const searchEl = document.getElementById('search');
  const modalOverlay = document.getElementById('modalOverlay');
  const cardForm = document.getElementById('cardForm');
  const cardTitleEl = document.getElementById('cardTitle');
  const cancelBtn = document.getElementById('cancelBtn');
  const addCardBtn = document.getElementById('addCardBtn');
  const themeToggle = document.getElementById('themeToggle');
  const sunIcon = document.getElementById('sunIcon');
  const moonIcon = document.getElementById('moonIcon');
  function init() {
    const savedTheme = 'light';
    theme.current = savedTheme;
    applyTheme();
    seedCards();
    renderBoard();
    bindEvents();
  }
  function seedCards() {
    const seedData = [
      { title: 'Design new landing page', priority: 'high', column: 'todo' },
      { title: 'Set up CI/CD pipeline', priority: 'medium', column: 'todo' },
      { title: 'Write API documentation', priority: 'low', column: 'inprogress' },
      { title: 'Refactor authentication module', priority: 'high', column: 'inprogress' },
      { title: 'Fix mobile layout bug', priority: 'medium', column: 'done' },
      { title: 'Update dependencies', priority: 'low', column: 'done' }
    ];
    seedData.forEach(d => {
      cards.push({ id: nextId++, title: d.title, priority: d.priority, column: d.column });
    });
  }
  function applyTheme() {
    document.documentElement.setAttribute('data-theme', theme.current);
    sunIcon.style.display = theme.current === 'dark' ? 'block' : 'none';
    moonIcon.style.display = theme.current === 'dark' ? 'none' : 'block';
    themeToggle.setAttribute('aria-label', theme.current === 'dark' ? 'Switch to light mode' : 'Switch to dark mode');
  }
  function toggleTheme() {
    theme.current = theme.current === 'light' ? 'dark' : 'light';
    applyTheme();
  }
  function bindEvents() {
    searchEl.addEventListener('input', e => {
      searchTerm.current = e.target.value.toLowerCase();
      renderBoard();
    });
    themeToggle.addEventListener('click', toggleTheme);
    addCardBtn.addEventListener('click', openModal);
    cancelBtn.addEventListener('click', closeModal);
    modalOverlay.addEventListener('click', e => {
      if (e.target === modalOverlay) closeModal();
    });
    cardForm.addEventListener('submit', e => {
      e.preventDefault();
      const title = cardTitleEl.value.trim();
      if (!title) return;
      const column = document.getElementById('cardColumn').value;
      const priority = document.getElementById('cardPriority').value;
      cards.push({ id: nextId++, title, priority, column });
      closeModal();
      renderBoard();
    });
    document.addEventListener('keydown', e => {
      if (e.key === 'Escape' && modalOverlay.classList.contains('open')) closeModal();
      if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
        e.preventDefault();
        searchEl.focus();
      }
    });
  }
  function openModal() {
    cardForm.reset();
    modalOverlay.classList.add('open');
    cardTitleEl.focus();
    document.body.style.overflow = 'hidden';
  }
  function closeModal() {
    modalOverlay.classList.remove('open');
    document.body.style.overflow = '';
  }
  function getFilteredCards() {
    if (!searchTerm.current) return cards;
    return cards.filter(c => c.title.toLowerCase().includes(searchTerm.current));
  }
  function getCardsByColumn(columnId) {
    return getFilteredCards().filter(c => c.column === columnId);
  }
  function createPriorityBadge(priority) {
    const span = document.createElement('span');
    span.className = `priority-badge priority-${priority}`;
    span.textContent = priority;
    return span;
  }
  function createCardElement(card) {
    const article = document.createElement('article');
    article.className = 'card';
    article.draggable = true;
    article.dataset.id = card.id;
    article.setAttribute('role', 'listitem');
    article.setAttribute('aria-label', `${card.title}, priority ${card.priority}`);
    article.innerHTML = `
      <div class="card-title">${escapeHtml(card.title)}</div>
      <div class="card-footer">
        <div class="priority-badge priority-${card.priority}">${priorityLabel(card.priority)}</div>
        <button class="btn-delete" aria-label="Delete ${escapeHtml(card.title)}" title="Delete card">
          <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="3 6 5 6 21 6"/><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"/></svg>
        </button>
      </div>
    `;
    const deleteBtn = article.querySelector('.btn-delete');
    deleteBtn.addEventListener('click', e => {
      e.stopPropagation();
      deleteCard(card.id);
    });
    article.addEventListener('dragstart', e => handleDragStart(e, card));
    article.addEventListener('dragend', handleDragEnd);
    article.addEventListener('dragover', e => handleDragOver(e, card));
    article.addEventListener('dragleave', handleDragLeave);
    article.addEventListener('drop', e => handleDrop(e, card));
    return article;
  }
  function priorityLabel(p) {
    return p.charAt(0).toUpperCase() + p.slice(1);
  }
  function escapeHtml(str) {
    return str.replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
  }
  function renderBoard() {
    boardEl.innerHTML = '';
    columns.forEach(col => {
      const columnCards = getCardsByColumn(col.id);
      const columnEl = document.createElement('section');
      columnEl.className = 'column';
      columnEl.dataset.column = col.id;
      columnEl.setAttribute('role', 'list');
      columnEl.setAttribute('aria-label', `${col.title} column`);
      columnEl.innerHTML = `
        <div class="column-header">
          <h2 class="column-title">${col.title}</h2>
          <span class="card-count">${columnCards.length}</span>
        </div>
        <div class="cards" data-column="${col.id}"></div>
        <div class="drop-indicator" data-column="${col.id}"></div>
      `;
      const cardsContainer = columnEl.querySelector('.cards');
      const dropIndicator = columnEl.querySelector('.drop-indicator');
      if (columnCards.length === 0) {
        cardsContainer.innerHTML = '<div class="empty-state">No cards. Drag here or add new.</div>';
      } else {
        columnCards.forEach(card => {
          cardsContainer.appendChild(createCardElement(card));
        });
      }
      columnEl.addEventListener('dragover', e => handleColumnDragOver(e, dropIndicator));
      columnEl.addEventListener('dragleave', e => handleColumnDragLeave(e, dropIndicator));
      columnEl.addEventListener('drop', e => handleColumnDrop(e, col.id));
      boardEl.appendChild(columnEl);
    });
  }
  function handleDragStart(e, card) {
    draggedCard = card;
    e.dataTransfer.effectAllowed = 'move';
    e.dataTransfer.setData('text/plain', card.id.toString());
    setTimeout(() => {
      const el = document.querySelector(`.card[data-id="${card.id}"]`);
      if (el) el.classList.add('dragging');
    }, 0);
  }
  function handleDragEnd(e) {
    document.querySelectorAll('.card.dragging').forEach(el => el.classList.remove('dragging'));
    document.querySelectorAll('.card.drag-over-top, .card.drag-over-bottom').forEach(el => {
      el.classList.remove('drag-over-top', 'drag-over-bottom');
    });
    document.querySelectorAll('.column.drag-over').forEach(el => el.classList.remove('drag-over'));
    document.querySelectorAll('.drop-indicator.visible').forEach(el => el.classList.remove('visible'));
    draggedCard = null;
    dragOverCard = null;
    dragOverPosition = null;
  }
  function handleDragOver(e, targetCard) {
    e.preventDefault();
    e.dataTransfer.dropEffect = 'move';
    const rect = e.currentTarget.getBoundingClientRect();
    const midY = rect.top + rect.height / 2;
    dragOverCard = targetCard;
    dragOverPosition = e.clientY < midY ? 'before' : 'after';
    document.querySelectorAll('.card.drag-over-top, .card.drag-over-bottom').forEach(el => {
      el.classList.remove('drag-over-top', 'drag-over-bottom');
    });
    const targetEl = document.querySelector(`.card[data-id="${targetCard.id}"]`);
    if (targetEl) {
      targetEl.classList.add(dragOverPosition === 'before' ? 'drag-over-top' : 'drag-over-bottom');
    }
  }
  function handleDragLeave(e) {
    if (!e.currentTarget.contains(e.relatedTarget)) {
      e.currentTarget.classList.remove('drag-over-top', 'drag-over-bottom');
    }
  }
  function handleDrop(e, targetCard) {
    e.preventDefault();
    if (!draggedCard || draggedCard.id === targetCard.id) return;
    const cardsInColumn = getFilteredCards().filter(c => c.column === targetCard.column);
    const fromIndex = cardsInColumn.findIndex(c => c.id === draggedCard.id);
    const toIndex = cardsInColumn.findIndex(c => c.id === targetCard.id);
    if (fromIndex === -1 || toIndex === -1) return;
    const newIndex = dragOverPosition === 'before' ? toIndex : toIndex + 1;
    moveCardInArray(cardsInColumn, fromIndex, newIndex);
    renderBoard();
  }
  function moveCardInArray(arr, from, to) {
    const [item] = arr.splice(from, 1);
    arr.splice(to, 0, item);
    const allCards = getFilteredCards();
    const colId = arr[0]?.column;
    const otherCards = allCards.filter(c => c.column !== colId);
    cards = [...otherCards, ...arr];
  }
  function handleColumnDragOver(e, dropIndicator) {
    e.preventDefault();
    e.dataTransfer.dropEffect = 'move';
    e.currentTarget.classList.add('drag-over');
    dropIndicator.classList.add('visible');
  }
  function handleColumnDragLeave(e, dropIndicator) {
    if (!e.currentTarget.contains(e.relatedTarget)) {
      e.currentTarget.classList.remove('drag-over');
      dropIndicator.classList.remove('visible');
    }
  }
  function handleColumnDrop(e, columnId) {
    e.preventDefault();
    e.currentTarget.classList.remove('drag-over');
    e.currentTarget.querySelector('.drop-indicator').classList.remove('visible');
    if (!draggedCard) return;
    const cardIndex = cards.findIndex(c => c.id === draggedCard.id);
    if (cardIndex !== -1) {
      cards[cardIndex].column = columnId;
      renderBoard();
    }
  }
  function deleteCard(id) {
    cards = cards.filter(c => c.id !== id);
    renderBoard();
  }
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }
})();
</script>
</body>
</html>