<!-- Tasks Module — Trinity Console -->
<!-- Chronicler #78 | April 11, 2026 -->
<!-- Features 1-7: Code Agent | April 14, 2026 -->

<style>
  .slide-panel { transform: translateX(100%); transition: transform 0.3s ease; }
  .slide-panel.open { transform: translateX(0); }
  .chip { cursor:pointer; padding:2px 10px; border-radius:9999px; font-size:11px; font-weight:500; border:1px solid transparent; transition:all 0.15s; }
  .chip.active { background:#06b6d4; color:#fff; border-color:#06b6d4; }
  .chip.inactive { background:#374151; color:#9ca3af; border-color:#4b5563; }
  .chip:hover { opacity:0.85; }
  .sort-btn { cursor:pointer; font-size:11px; padding:2px 8px; border-radius:4px; transition:all 0.15s; }
  .sort-btn.active { background:#06b6d4; color:#fff; }
  .sort-btn.inactive { background:#374151; color:#9ca3af; }
  .kanban-col { min-height:200px; }
  .kanban-card { background:#2d2d2d; border:1px solid #374151; border-radius:8px; padding:10px; margin-bottom:8px; cursor:pointer; transition:border-color 0.15s; }
  .kanban-card:hover { border-color:#06b6d4; }
</style>

<!-- Session Summary Badge -->
<% if (completedToday > 0) { %>
<div class="mb-4 px-4 py-2 bg-green-900/20 border border-green-700/30 rounded-lg inline-block">
  <span class="text-green-400 text-sm font-medium">✅ <%= completedToday %> task<%= completedToday !== 1 ? 's' : '' %> completed today</span>
</div>
<% } %>

<!-- Stats -->
<div class="grid grid-cols-2 md:grid-cols-5 gap-4 mb-6">
  <div class="bg-white dark:bg-darkcard rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4">
    <div class="text-sm text-gray-500 dark:text-gray-400">Active</div>
    <div class="text-2xl font-bold mt-1"><%= stats.active %></div>
  </div>
  <div class="bg-white dark:bg-darkcard rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4">
    <div class="text-sm text-gray-500 dark:text-gray-400">In Progress</div>
    <div class="text-2xl font-bold mt-1 text-frost"><%= stats.in_progress %></div>
  </div>
  <div class="bg-white dark:bg-darkcard rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4">
    <div class="text-sm text-gray-500 dark:text-gray-400">Blocked</div>
    <div class="text-2xl font-bold mt-1 text-red-500"><%= stats.blocked %></div>
  </div>
  <div class="bg-white dark:bg-darkcard rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4">
    <div class="text-sm text-gray-500 dark:text-gray-400">High Priority</div>
    <div class="text-2xl font-bold mt-1 text-fire"><%= stats.high_priority %></div>
  </div>
  <div class="bg-white dark:bg-darkcard rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 p-4">
    <div class="text-sm text-gray-500 dark:text-gray-400">Completed</div>
    <div class="text-2xl font-bold mt-1 text-green-500"><%= stats.done %></div>
  </div>
</div>

<!-- View Toggle + Presets + New Task -->
<div class="flex flex-wrap gap-3 mb-4 items-center">
  <div class="flex bg-gray-800 rounded-md overflow-hidden">
    <button onclick="setView('list')" id="view-list-btn" class="px-3 py-1.5 text-xs font-medium transition">📋 List</button>
    <button onclick="setView('kanban')" id="view-kanban-btn" class="px-3 py-1.5 text-xs font-medium transition">📊 Kanban</button>
  </div>

  <div class="relative">
    <button onclick="document.getElementById('presets-dropdown').classList.toggle('hidden')" class="bg-gray-800 text-gray-300 px-3 py-1.5 rounded-md text-xs hover:bg-gray-700 transition">
      ⚡ Presets ▾
    </button>
    <div id="presets-dropdown" class="hidden absolute top-full left-0 mt-1 bg-gray-800 border border-gray-600 rounded-md shadow-lg z-20 min-w-[180px]">
      <button onclick="applyPreset('launch-fires')" class="block w-full text-left px-3 py-2 text-xs text-gray-300 hover:bg-gray-700">🔥 Launch Fires</button>
      <button onclick="applyPreset('code-queue')" class="block w-full text-left px-3 py-2 text-xs text-gray-300 hover:bg-gray-700">💻 Code Queue</button>
      <button onclick="applyPreset('post-launch')" class="block w-full text-left px-3 py-2 text-xs text-gray-300 hover:bg-gray-700">🌙 Post-Launch</button>
      <button onclick="applyPreset('all-open')" class="block w-full text-left px-3 py-2 text-xs text-gray-300 hover:bg-gray-700">📂 All Open</button>
      <button onclick="applyPreset('clear')" class="block w-full text-left px-3 py-2 text-xs text-gray-400 hover:bg-gray-700 border-t border-gray-700">✕ Clear Filters</button>
    </div>
  </div>

  <div class="ml-auto">
    <button onclick="document.getElementById('create-modal').style.display='flex'" class="bg-gradient-to-r from-fire to-frost text-white px-4 py-1.5 rounded-md text-xs hover:opacity-90 transition font-medium">
      + New Task
    </button>
  </div>
</div>

<!-- Filter Chips -->
<div class="mb-4 space-y-2">
  <div class="flex flex-wrap items-center gap-1.5">
    <span class="text-[10px] text-gray-500 uppercase tracking-wider mr-1">Status:</span>
    <button onclick="toggleChip('status','all')" class="chip" data-filter="status" data-value="all">All</button>
    <button onclick="toggleChip('status','open')" class="chip" data-filter="status" data-value="open">Open</button>
    <button onclick="toggleChip('status','in_progress')" class="chip" data-filter="status" data-value="in_progress">In Progress</button>
    <button onclick="toggleChip('status','blocked')" class="chip" data-filter="status" data-value="blocked">Blocked</button>
    <button onclick="toggleChip('status','done')" class="chip" data-filter="status" data-value="done">Done</button>
    <button onclick="toggleChip('status','obsolete')" class="chip" data-filter="status" data-value="obsolete">Obsolete</button>
  </div>
  <div class="flex flex-wrap items-center gap-1.5">
    <span class="text-[10px] text-gray-500 uppercase tracking-wider mr-1">Priority:</span>
    <button onclick="toggleChip('priority','all')" class="chip" data-filter="priority" data-value="all">All</button>
    <button onclick="toggleChip('priority','critical')" class="chip" data-filter="priority" data-value="critical">Critical</button>
    <button onclick="toggleChip('priority','high')" class="chip" data-filter="priority" data-value="high">High</button>
    <button onclick="toggleChip('priority','medium')" class="chip" data-filter="priority" data-value="medium">Medium</button>
    <button onclick="toggleChip('priority','low')" class="chip" data-filter="priority" data-value="low">Low</button>
    <button onclick="toggleChip('priority','wish')" class="chip" data-filter="priority" data-value="wish">Wish</button>
  </div>
</div>

<!-- Sort Controls -->
<div class="flex flex-wrap items-center gap-1.5 mb-4" id="sort-controls">
  <span class="text-[10px] text-gray-500 uppercase tracking-wider mr-1">Sort:</span>
  <button onclick="setSort('task_number')" class="sort-btn" data-sort="task_number">Number</button>
  <button onclick="setSort('priority')" class="sort-btn" data-sort="priority">Priority</button>
  <button onclick="setSort('status')" class="sort-btn" data-sort="status">Status</button>
  <button onclick="setSort('updated_at')" class="sort-btn" data-sort="updated_at">Updated</button>
</div>

<!-- LIST VIEW -->
<div id="list-view">
<div class="bg-white dark:bg-darkcard rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden">
  <table class="w-full">
    <thead>
      <tr class="border-b border-gray-200 dark:border-gray-700 text-left text-xs text-gray-500 dark:text-gray-400 uppercase tracking-wider">
        <th class="px-4 py-3 w-16">#</th>
        <th class="px-4 py-3">Task</th>
        <th class="px-4 py-3 w-24">Priority</th>
        <th class="px-4 py-3 w-28">Status</th>
        <th class="px-4 py-3 w-24">Owner</th>
        <th class="px-4 py-3 w-32">Actions</th>
      </tr>
    </thead>
    <tbody class="divide-y divide-gray-200 dark:divide-gray-700" id="task-tbody">
      <% if (tasks.length === 0) { %>
        <tr><td colspan="6" class="px-4 py-8 text-center text-gray-500">No tasks match your filters.</td></tr>
      <% } %>
      <% tasks.forEach(task => {
        var priColors = { critical:'bg-red-500/20 text-red-500', high:'bg-fire/20 text-fire', medium:'bg-yellow-500/20 text-yellow-500', low:'bg-blue-500/20 text-blue-400', wish:'bg-purple-500/20 text-purple-400' };
        var staColors = { open:'bg-gray-500/20 text-gray-400', in_progress:'bg-frost/20 text-frost', blocked:'bg-red-500/20 text-red-500', done:'bg-green-500/20 text-green-500', obsolete:'bg-gray-700/20 text-gray-600' };
        var priClass = priColors[task.priority] || 'bg-gray-500/20 text-gray-400';
        var staClass = staColors[task.status] || 'bg-gray-500/20 text-gray-400';
        var taskDesc = task.description || '';
        var taskTags = task.tags || [];
        var taskSpecPath = task.spec_path || '';
        var taskCompletedBy = task.completed_by || '';
        var taskCreatedAt = task.created_at ? new Date(task.created_at).toLocaleDateString() : '';
        var taskUpdatedAt = task.updated_at ? new Date(task.updated_at).toLocaleDateString() : '';
        var taskCompletedAt = task.completed_at ? new Date(task.completed_at).toLocaleDateString() : '';
      %>
        <tr class="hover:bg-gray-50 dark:hover:bg-gray-800/50 transition group task-row"
            data-status="<%= task.status %>" data-priority="<%= task.priority %>"
            data-task-number="<%= task.task_number %>"
            data-updated="<%= task.updated_at || '' %>"
            data-tags="<%= taskTags.join(',') %>"
            data-title="<%= task.title %>">
          <td class="px-4 py-3 text-sm font-mono text-gray-500"><%= task.task_number %></td>
          <td class="px-4 py-3">
            <div class="font-medium text-sm text-gray-200 cursor-pointer hover:text-cyan-400 transition" onclick="openDetail(<%= JSON.stringify(JSON.stringify(task)) %>)"><%= task.title %></div>
            <% if (taskDesc) { %>
              <div class="text-xs text-gray-500 mt-0.5 truncate max-w-md"><%= taskDesc.substring(0, 80) %><%= taskDesc.length > 80 ? '...' : '' %></div>
            <% } %>
            <% if (taskTags.length > 0) { %>
              <div class="flex gap-1 mt-1">
                <% taskTags.forEach(function(tag) { %>
                  <span class="text-[9px] px-1.5 py-0.5 rounded bg-gray-700 text-gray-400"><%= tag %></span>
                <% }); %>
              </div>
            <% } %>
          </td>
          <td class="px-4 py-3">
            <span class="inline-block px-2 py-0.5 text-xs rounded-full font-medium <%= priClass %>"><%= task.priority %></span>
          </td>
          <td class="px-4 py-3">
            <form method="POST" action="/admin/tasks/update/<%= task.id %>" class="inline">
              <select name="status" onchange="this.form.submit()" class="bg-transparent border-0 text-xs cursor-pointer <%= staClass %> rounded-full px-2 py-0.5 font-medium appearance-none">
                <% statuses.forEach(function(s) { %>
                  <option value="<%= s %>" <%= task.status === s ? 'selected' : '' %> class="bg-gray-800 text-white"><%= s.replace('_', ' ') %></option>
                <% }); %>
              </select>
            </form>
          </td>
          <td class="px-4 py-3">
            <form method="POST" action="/admin/tasks/update/<%= task.id %>" class="inline">
              <select name="owner" onchange="this.form.submit()" class="bg-transparent border-0 text-xs cursor-pointer text-gray-400 appearance-none">
                <% owners.forEach(function(o) { %>
                  <option value="<%= o %>" <%= task.owner === o ? 'selected' : '' %> class="bg-gray-800 text-white"><%= o %></option>
                <% }); %>
              </select>
            </form>
          </td>
          <td class="px-4 py-3">
            <% if (task.status !== 'done' && task.status !== 'obsolete') { %>
              <form method="POST" action="/admin/tasks/update/<%= task.id %>" class="inline">
                <input type="hidden" name="status" value="done">
                <button type="submit" class="text-xs text-green-500 hover:text-green-400 transition opacity-0 group-hover:opacity-100">✓ Done</button>
              </form>
            <% } else if (taskCompletedBy) { %>
              <span class="text-[10px] text-gray-600"><%= taskCompletedBy %></span>
            <% } %>
          </td>
        </tr>
      <% }); %>
    </tbody>
  </table>
</div>
<div class="text-center text-xs text-gray-600 mt-4">
  <span id="task-count"><%= tasks.length %></span> task(s) shown · Source: PostgreSQL · Also available via /tasks in Discord
</div>
</div>

<!-- KANBAN VIEW -->
<div id="kanban-view" style="display:none">
<div class="grid grid-cols-1 md:grid-cols-4 gap-4">
  <% var kanbanCols = [
    { key: 'open', label: 'Open', color: 'text-gray-400', border: 'border-gray-600' },
    { key: 'in_progress', label: 'In Progress', color: 'text-cyan-400', border: 'border-cyan-600' },
    { key: 'blocked', label: 'Blocked', color: 'text-red-400', border: 'border-red-600' },
    { key: 'done', label: 'Done', color: 'text-green-400', border: 'border-green-600' }
  ]; %>
  <% kanbanCols.forEach(function(col) { %>
    <div>
      <h3 class="text-xs font-bold uppercase tracking-wider mb-3 <%= col.color %>">
        <%= col.label %>
        <span class="ml-1 text-gray-600">(<%= allTasks.filter(function(t) { return t.status === col.key; }).length %>)</span>
      </h3>
      <div class="kanban-col border-t-2 <%= col.border %> pt-3">
        <% allTasks.filter(function(t) { return t.status === col.key; }).forEach(function(task) {
          var kPriColors = { critical:'text-red-500', high:'text-fire', medium:'text-yellow-500', low:'text-blue-400', wish:'text-purple-400' };
          var kPriClass = kPriColors[task.priority] || 'text-gray-400';
        %>
          <div class="kanban-card" onclick="openDetail(<%= JSON.stringify(JSON.stringify(task)) %>)">
            <div class="flex items-center justify-between mb-1">
              <span class="text-[10px] font-mono text-gray-500">#<%= task.task_number %></span>
              <span class="text-[10px] font-medium <%= kPriClass %>"><%= task.priority %></span>
            </div>
            <div class="text-xs font-medium text-gray-200"><%= task.title %></div>
            <% if (task.owner && task.owner !== 'unassigned') { %>
              <div class="text-[10px] text-gray-500 mt-1"><%= task.owner %></div>
            <% } %>
          </div>
        <% }); %>
      </div>
    </div>
  <% }); %>
</div>
</div>

<!-- Slide-Out Detail Panel -->
<div id="detail-overlay" class="fixed inset-0 bg-black/50 z-40" style="display:none" onclick="closeDetail()"></div>
<div id="detail-panel" class="slide-panel fixed top-0 right-0 h-full w-full max-w-md bg-darkcard border-l border-gray-700 z-50 overflow-y-auto shadow-2xl">
  <div class="p-6">
    <div class="flex items-center justify-between mb-6">
      <span class="text-xs font-mono text-gray-500" id="detail-number"></span>
      <button onclick="closeDetail()" class="text-gray-400 hover:text-white text-lg">✕</button>
    </div>
    <h2 class="text-lg font-bold text-white mb-4" id="detail-title"></h2>
    <div class="flex gap-2 mb-4">
      <span id="detail-status" class="px-2 py-0.5 text-xs rounded-full font-medium"></span>
      <span id="detail-priority" class="px-2 py-0.5 text-xs rounded-full font-medium"></span>
    </div>
    <div class="space-y-4 text-sm">
      <div>
        <span class="text-[10px] text-gray-500 uppercase tracking-wider block mb-1">Owner</span>
        <span class="text-gray-300" id="detail-owner"></span>
      </div>
      <div>
        <span class="text-[10px] text-gray-500 uppercase tracking-wider block mb-1">Description</span>
        <div class="text-gray-300 text-xs whitespace-pre-wrap" id="detail-description"></div>
      </div>
      <div id="detail-tags-section">
        <span class="text-[10px] text-gray-500 uppercase tracking-wider block mb-1">Tags</span>
        <div id="detail-tags" class="flex flex-wrap gap-1"></div>
      </div>
      <div class="grid grid-cols-2 gap-4">
        <div>
          <span class="text-[10px] text-gray-500 uppercase tracking-wider block mb-1">Created</span>
          <span class="text-gray-400 text-xs" id="detail-created"></span>
        </div>
        <div>
          <span class="text-[10px] text-gray-500 uppercase tracking-wider block mb-1">Updated</span>
          <span class="text-gray-400 text-xs" id="detail-updated"></span>
        </div>
      </div>
      <div id="detail-completed-section" style="display:none">
        <div class="grid grid-cols-2 gap-4">
          <div>
            <span class="text-[10px] text-gray-500 uppercase tracking-wider block mb-1">Completed</span>
            <span class="text-green-400 text-xs" id="detail-completed-at"></span>
          </div>
          <div>
            <span class="text-[10px] text-gray-500 uppercase tracking-wider block mb-1">Completed By</span>
            <span class="text-green-400 text-xs" id="detail-completed-by"></span>
          </div>
        </div>
      </div>
      <div id="detail-spec-section" style="display:none">
        <span class="text-[10px] text-gray-500 uppercase tracking-wider block mb-1">Spec Path</span>
        <span class="text-cyan-400 text-xs font-mono" id="detail-spec"></span>
      </div>
    </div>
  </div>
</div>

<!-- Create Task Modal -->
<div id="create-modal" style="display:none" class="fixed inset-0 z-50 flex items-center justify-center bg-black/70 backdrop-blur-sm" onclick="if(event.target===this)this.style.display='none'">
  <div class="bg-white dark:bg-darkcard border border-gray-200 dark:border-gray-700 rounded-lg shadow-2xl w-full max-w-lg p-6">
    <h2 class="text-xl font-bold mb-4 text-gray-200">New Task</h2>
    <form method="POST" action="/admin/tasks/create">
      <div class="space-y-4">
        <div>
          <label class="block text-xs text-gray-500 mb-1">Title *</label>
          <input type="text" name="title" required
            class="w-full bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md px-3 py-2 text-sm"
            placeholder="What needs to be done?">
        </div>
        <div>
          <label class="block text-xs text-gray-500 mb-1">Description</label>
          <textarea name="description" rows="3"
            class="w-full bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md px-3 py-2 text-sm"
            placeholder="Optional details..."></textarea>
        </div>
        <div class="grid grid-cols-2 gap-4">
          <div>
            <label class="block text-xs text-gray-500 mb-1">Priority</label>
            <select name="priority" class="w-full bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md px-3 py-2 text-sm">
              <option value="medium" selected>Medium</option>
              <option value="critical">Critical</option>
              <option value="high">High</option>
              <option value="low">Low</option>
              <option value="wish">Wish List</option>
            </select>
          </div>
          <div>
            <label class="block text-xs text-gray-500 mb-1">Owner</label>
            <select name="owner" class="w-full bg-gray-100 dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-md px-3 py-2 text-sm">
              <option value="Michael">Michael</option>
              <option value="Holly">Holly</option>
              <option value="Meg">Meg</option>
              <option value="Trinity">Trinity</option>
              <option value="unassigned">Unassigned</option>
            </select>
          </div>
        </div>
      </div>
      <div class="flex justify-end gap-3 mt-6">
        <button type="button" onclick="document.getElementById('create-modal').style.display='none'"
          class="px-4 py-2 text-gray-500 hover:text-gray-300 transition text-sm">Cancel</button>
        <button type="submit"
          class="bg-gradient-to-r from-fire to-frost text-white px-6 py-2 rounded-md text-sm font-medium hover:opacity-90 transition">
          Create Task
        </button>
      </div>
    </form>
  </div>
</div>

<script>
// --- State ---
var activeFilters = { status: ['all'], priority: ['all'] };
var currentSort = localStorage.getItem('taskSort') || 'task_number';
var currentSortDir = localStorage.getItem('taskSortDir') || 'asc';
var currentView = localStorage.getItem('taskView') || 'list';

// --- View Toggle ---
function setView(view) {
  currentView = view;
  localStorage.setItem('taskView', view);
  document.getElementById('list-view').style.display = view === 'list' ? '' : 'none';
  document.getElementById('kanban-view').style.display = view === 'kanban' ? '' : 'none';
  document.getElementById('view-list-btn').className = 'px-3 py-1.5 text-xs font-medium transition ' + (view === 'list' ? 'bg-cyan-500 text-white' : 'text-gray-400 hover:text-white');
  document.getElementById('view-kanban-btn').className = 'px-3 py-1.5 text-xs font-medium transition ' + (view === 'kanban' ? 'bg-cyan-500 text-white' : 'text-gray-400 hover:text-white');
}

// --- Filter Chips ---
function toggleChip(type, value) {
  if (value === 'all') {
    activeFilters[type] = ['all'];
  } else {
    var idx = activeFilters[type].indexOf(value);
    // Remove 'all' if selecting specific
    var allIdx = activeFilters[type].indexOf('all');
    if (allIdx > -1) activeFilters[type].splice(allIdx, 1);
    if (idx > -1) {
      activeFilters[type].splice(idx, 1);
      if (activeFilters[type].length === 0) activeFilters[type] = ['all'];
    } else {
      activeFilters[type].push(value);
    }
  }
  applyFilters();
}

function applyFilters() {
  // Update chip visuals
  document.querySelectorAll('.chip').forEach(function(el) {
    var filter = el.getAttribute('data-filter');
    var value = el.getAttribute('data-value');
    var isActive = activeFilters[filter].indexOf(value) > -1;
    el.className = 'chip ' + (isActive ? 'active' : 'inactive');
  });

  // Filter rows
  var rows = document.querySelectorAll('.task-row');
  var visibleCount = 0;
  rows.forEach(function(row) {
    var rowStatus = row.getAttribute('data-status');
    var rowPriority = row.getAttribute('data-priority');
    var statusMatch = activeFilters.status.indexOf('all') > -1 || activeFilters.status.indexOf(rowStatus) > -1;
    var priorityMatch = activeFilters.priority.indexOf('all') > -1 || activeFilters.priority.indexOf(rowPriority) > -1;
    if (statusMatch && priorityMatch) {
      row.style.display = '';
      visibleCount++;
    } else {
      row.style.display = 'none';
    }
  });
  document.getElementById('task-count').textContent = visibleCount;
}

// --- Sorting ---
function setSort(field) {
  if (currentSort === field) {
    currentSortDir = currentSortDir === 'asc' ? 'desc' : 'asc';
  } else {
    currentSort = field;
    currentSortDir = 'asc';
  }
  localStorage.setItem('taskSort', currentSort);
  localStorage.setItem('taskSortDir', currentSortDir);
  applySorting();
}

function applySorting() {
  // Update button visuals
  document.querySelectorAll('.sort-btn').forEach(function(el) {
    var sort = el.getAttribute('data-sort');
    var isActive = currentSort === sort;
    el.className = 'sort-btn ' + (isActive ? 'active' : 'inactive');
    if (isActive) {
      el.textContent = el.textContent.replace(/ [▲▼]/, '') + (currentSortDir === 'asc' ? ' ▲' : ' ▼');
    } else {
      el.textContent = el.textContent.replace(/ [▲▼]/, '');
    }
  });

  var tbody = document.getElementById('task-tbody');
  var rows = Array.from(tbody.querySelectorAll('.task-row'));
  var priOrder = { critical:1, high:2, medium:3, low:4, wish:5 };
  var staOrder = { in_progress:1, blocked:2, open:3, done:4, obsolete:5 };

  rows.sort(function(a, b) {
    var aVal, bVal;
    if (currentSort === 'task_number') {
      aVal = parseInt(a.getAttribute('data-task-number'));
      bVal = parseInt(b.getAttribute('data-task-number'));
    } else if (currentSort === 'priority') {
      aVal = priOrder[a.getAttribute('data-priority')] || 99;
      bVal = priOrder[b.getAttribute('data-priority')] || 99;
    } else if (currentSort === 'status') {
      aVal = staOrder[a.getAttribute('data-status')] || 99;
      bVal = staOrder[b.getAttribute('data-status')] || 99;
    } else if (currentSort === 'updated_at') {
      aVal = a.getAttribute('data-updated') || '';
      bVal = b.getAttribute('data-updated') || '';
      if (aVal < bVal) return currentSortDir === 'asc' ? -1 : 1;
      if (aVal > bVal) return currentSortDir === 'asc' ? 1 : -1;
      return 0;
    }
    var diff = aVal - bVal;
    return currentSortDir === 'asc' ? diff : -diff;
  });

  rows.forEach(function(row) { tbody.appendChild(row); });
}

// --- Presets ---
function applyPreset(name) {
  document.getElementById('presets-dropdown').classList.add('hidden');
  if (name === 'launch-fires') {
    activeFilters = { status: ['open'], priority: ['critical', 'high'] };
  } else if (name === 'code-queue') {
    activeFilters = { status: ['all'], priority: ['all'] };
    applyFilters();
    // Further filter by tags containing 'code' or title containing 'Code'
    document.querySelectorAll('.task-row').forEach(function(row) {
      var tags = row.getAttribute('data-tags') || '';
      var title = row.getAttribute('data-title') || '';
      if (tags.indexOf('code') === -1 && title.indexOf('Code') === -1) {
        row.style.display = 'none';
      }
    });
    updateVisibleCount();
    return;
  } else if (name === 'post-launch') {
    activeFilters = { status: ['open'], priority: ['low', 'wish'] };
  } else if (name === 'all-open') {
    activeFilters = { status: ['open', 'in_progress', 'blocked'], priority: ['all'] };
  } else if (name === 'clear') {
    activeFilters = { status: ['all'], priority: ['all'] };
  }
  applyFilters();
}

function updateVisibleCount() {
  var count = 0;
  document.querySelectorAll('.task-row').forEach(function(row) {
    if (row.style.display !== 'none') count++;
  });
  document.getElementById('task-count').textContent = count;
}

// --- Detail Panel ---
function openDetail(taskJson) {
  var task = JSON.parse(taskJson);
  var priColors = { critical:'bg-red-500/20 text-red-500', high:'bg-fire/20 text-fire', medium:'bg-yellow-500/20 text-yellow-500', low:'bg-blue-500/20 text-blue-400', wish:'bg-purple-500/20 text-purple-400' };
  var staColors = { open:'bg-gray-500/20 text-gray-400', in_progress:'bg-frost/20 text-frost', blocked:'bg-red-500/20 text-red-500', done:'bg-green-500/20 text-green-500', obsolete:'bg-gray-700/20 text-gray-600' };

  document.getElementById('detail-number').textContent = '#' + task.task_number;
  document.getElementById('detail-title').textContent = task.title;
  document.getElementById('detail-owner').textContent = task.owner || 'Unassigned';
  document.getElementById('detail-description').textContent = task.description || 'No description.';

  var statusEl = document.getElementById('detail-status');
  statusEl.textContent = (task.status || '').replace('_', ' ');
  statusEl.className = 'px-2 py-0.5 text-xs rounded-full font-medium ' + (staColors[task.status] || '');

  var priEl = document.getElementById('detail-priority');
  priEl.textContent = task.priority || '';
  priEl.className = 'px-2 py-0.5 text-xs rounded-full font-medium ' + (priColors[task.priority] || '');

  // Tags
  var tagsContainer = document.getElementById('detail-tags');
  tagsContainer.innerHTML = '';
  var tagsSection = document.getElementById('detail-tags-section');
  if (task.tags && task.tags.length > 0) {
    tagsSection.style.display = '';
    task.tags.forEach(function(tag) {
      var span = document.createElement('span');
      span.className = 'text-[9px] px-1.5 py-0.5 rounded bg-gray-700 text-gray-400';
      span.textContent = tag;
      tagsContainer.appendChild(span);
    });
  } else {
    tagsSection.style.display = 'none';
  }

  // Dates
  document.getElementById('detail-created').textContent = task.created_at ? new Date(task.created_at).toLocaleString() : 'Unknown';
  document.getElementById('detail-updated').textContent = task.updated_at ? new Date(task.updated_at).toLocaleString() : 'Unknown';

  // Completed
  var compSection = document.getElementById('detail-completed-section');
  if (task.completed_at) {
    compSection.style.display = '';
    document.getElementById('detail-completed-at').textContent = new Date(task.completed_at).toLocaleString();
    document.getElementById('detail-completed-by').textContent = task.completed_by || 'Unknown';
  } else {
    compSection.style.display = 'none';
  }

  // Spec path
  var specSection = document.getElementById('detail-spec-section');
  if (task.spec_path) {
    specSection.style.display = '';
    document.getElementById('detail-spec').textContent = task.spec_path;
  } else {
    specSection.style.display = 'none';
  }

  document.getElementById('detail-overlay').style.display = '';
  document.getElementById('detail-panel').classList.add('open');
}

function closeDetail() {
  document.getElementById('detail-panel').classList.remove('open');
  document.getElementById('detail-overlay').style.display = 'none';
}

// --- Init ---
document.addEventListener('DOMContentLoaded', function() {
  setView(currentView);
  applyFilters();
  applySorting();

  // Close presets dropdown when clicking outside
  document.addEventListener('click', function(e) {
    var dropdown = document.getElementById('presets-dropdown');
    if (!e.target.closest('.relative')) {
      dropdown.classList.add('hidden');
    }
  });
});
</script>
