News & articles.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>TimeTask</title>
<style>
  :root {
    --navy: #1A2847;
    --navy-dark: #0F1A30;
    --cerulean: #0084D4;
    --cerulean-light: #E6F4FB;
    --cerulean-dark: #006BA8;
    --orange: #D97706;
    --orange-light: #FEF3E2;
    --bg: #F4F6FA;
    --surface: #FFFFFF;
    --surface-2: #F7F9FC;
    --border: #E2E8F0;
    --text: #1A2847;
    --text-muted: #64748B;
    --text-faint: #94A3B8;
    --success: #16A34A;
    --radius-sm: 6px;
    --radius-md: 8px;
    --radius-lg: 12px;
    --shadow-sm: 0 1px 2px rgba(26, 40, 71, 0.04);
    --shadow: 0 1px 3px rgba(26, 40, 71, 0.06), 0 1px 2px rgba(26, 40, 71, 0.04);
  }
  * { box-sizing: border-box; }
  html, body {
    margin: 0; padding: 0;
    font-family: Arial, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
    background: var(--bg);
    color: var(--text);
    -webkit-font-smoothing: antialiased;
  }
  .tt-app {
    max-width: 760px;
    margin: 0 auto;
    padding: 32px 20px 48px;
  }
  .tt-header {
    text-align: center;
    margin-bottom: 28px;
  }
  .tt-brand {
    display: inline-flex;
    align-items: center;
    gap: 12px;
    font-size: 32px;
    font-weight: 700;
    letter-spacing: -0.5px;
    color: var(--navy);
  }
  .tt-brand svg { width: 32px; height: 32px; color: var(--cerulean); }
  .tt-tagline {
    font-size: 14px;
    color: var(--text-muted);
    margin: 8px 0 0;
  }
  .card {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
  }
  .card + .card { margin-top: 12px; }
  .pad { padding: 18px 20px; }
  .pad-lg { padding: 24px; }

  /* Intake screen */
  .intake-title {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 17px;
    font-weight: 600;
    margin: 0 0 6px;
    color: var(--navy);
  }
  .intake-title svg { width: 20px; height: 20px; color: var(--cerulean); }
  .intake-sub {
    font-size: 13px;
    color: var(--text-muted);
    margin: 0 0 14px;
  }
  textarea, input[type=text], input[type=number] {
    width: 100%;
    background: var(--surface-2);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 12px 14px;
    font-family: inherit;
    font-size: 14px;
    color: var(--text);
    line-height: 1.6;
    transition: border-color 0.15s, box-shadow 0.15s;
  }
  textarea { resize: vertical; min-height: 140px; }
  textarea:focus, input:focus {
    outline: none;
    border-color: var(--cerulean);
    box-shadow: 0 0 0 3px var(--cerulean-light);
  }
  .btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 8px;
    border: none;
    border-radius: var(--radius-md);
    padding: 12px 18px;
    font-family: inherit;
    font-size: 14px;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s, transform 0.05s;
  }
  .btn:active { transform: scale(0.98); }
  .btn-primary {
    background: var(--cerulean);
    color: white;
  }
  .btn-primary:hover { background: var(--cerulean-dark); }
  .btn-block { width: 100%; padding: 14px; font-size: 15px; }
  .btn-secondary {
    background: var(--surface);
    color: var(--text);
    border: 1px solid var(--border);
  }
  .btn-secondary:hover { background: var(--surface-2); border-color: var(--text-faint); }
  .btn svg { width: 16px; height: 16px; }

  /* Tracker controls row */
  .add-row {
    display: flex;
    gap: 10px;
    align-items: center;
  }
  .add-row input { flex: 1; }

  /* Mode toggle */
  .toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    margin-bottom: 12px;
    flex-wrap: wrap;
  }
  .mode-toggle {
    display: inline-flex;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    padding: 3px;
    box-shadow: var(--shadow-sm);
  }
  .mode-toggle button {
    background: transparent;
    border: none;
    padding: 7px 14px;
    font-family: inherit;
    font-size: 13px;
    font-weight: 500;
    color: var(--text-muted);
    cursor: pointer;
    border-radius: 6px;
    transition: all 0.15s;
  }
  .mode-toggle button.active {
    background: var(--navy);
    color: white;
  }
  .pomodoro-config {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 13px;
    color: var(--text-muted);
  }
  .pomodoro-config input {
    width: 64px;
    padding: 6px 10px;
    text-align: center;
    font-size: 13px;
  }

  /* Total bar */
  .total-bar {
    background: var(--navy);
    color: white;
    border-radius: var(--radius-lg);
    padding: 18px 22px;
    margin-bottom: 14px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    box-shadow: var(--shadow);
  }
  .total-bar.pomodoro { background: var(--cerulean); }
  .total-bar-label {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: 16px;
    font-weight: 600;
  }
  .total-bar-label svg { width: 22px; height: 22px; }
  .total-bar-time {
    font-family: "Courier New", monospace;
    font-size: 24px;
    font-weight: 700;
    letter-spacing: 1px;
  }

  /* Task rows */
  .task {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    padding: 14px 16px;
    margin-bottom: 10px;
    box-shadow: var(--shadow-sm);
    transition: border-color 0.15s, box-shadow 0.15s;
  }
  .task.active {
    border: 2px solid var(--orange);
    padding: 13px 15px;
    box-shadow: 0 0 0 3px var(--orange-light);
  }
  .task.dragover {
    border: 2px dashed var(--cerulean);
    padding: 13px 15px;
  }
  .task.done { opacity: 0.6; }
  .task-row {
    display: flex;
    align-items: center;
    gap: 10px;
  }
  .handle {
    color: var(--text-faint);
    cursor: grab;
    font-size: 18px;
    line-height: 1;
    user-select: none;
    padding: 0 2px;
  }
  .handle:active { cursor: grabbing; }
  .icon-btn {
    background: transparent;
    border: none;
    padding: 6px;
    cursor: pointer;
    color: var(--text-muted);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: var(--radius-sm);
    transition: background 0.15s, color 0.15s;
  }
  .icon-btn:hover { background: var(--surface-2); color: var(--navy); }
  .icon-btn svg { width: 16px; height: 16px; }
  .check-btn svg { width: 22px; height: 22px; }
  .check-btn.checked { color: var(--success); }
  .task-label {
    flex: 1;
    font-size: 15px;
    font-weight: 500;
    color: var(--navy);
  }
  .task.done .task-label { text-decoration: line-through; color: var(--text-faint); }
  .task-label-input {
    flex: 1;
    padding: 6px 10px;
    font-size: 15px;
    font-weight: 500;
  }
  .control-btn {
    width: 38px;
    height: 38px;
    border-radius: var(--radius-md);
    border: 1px solid var(--border);
    background: var(--surface);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: var(--navy);
    transition: all 0.15s;
  }
  .control-btn:hover { background: var(--cerulean-light); border-color: var(--cerulean); }
  .control-btn svg { width: 18px; height: 18px; }
  .play-btn.playing {
    background: var(--orange-light);
    border-color: var(--orange);
    color: var(--orange);
  }
  .task-time-row {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
    padding-left: 38px;
  }
  .task-time {
    font-family: "Courier New", monospace;
    font-size: 20px;
    font-weight: 700;
    color: var(--navy);
    letter-spacing: 1px;
  }
  .task.active .task-time { color: var(--orange); }
  .task-time.warning { color: var(--orange); }
  .task-time-input {
    width: 110px;
    padding: 4px 10px;
    font-size: 16px;
    font-family: "Courier New", monospace;
    font-weight: 700;
  }

  /* Footer actions */
  .footer-actions {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
    margin-top: 14px;
  }
  .empty {
    text-align: center;
    color: var(--text-muted);
    padding: 28px 12px;
    font-size: 14px;
  }
  .copy-feedback {
    color: var(--success) !important;
  }

  @media (max-width: 540px) {
    .tt-app { padding: 20px 12px 40px; }
    .pad-lg { padding: 18px; }
    .toolbar { flex-direction: column; align-items: stretch; }
    .task-row { flex-wrap: wrap; }
    .task-label { min-width: 0; }
    .footer-actions { grid-template-columns: 1fr; }
    .total-bar-time { font-size: 20px; }
    .tt-brand { font-size: 26px; }
  }
</style>
</head>
<body>

<div class="tt-app">

  <div class="tt-header">
    <div class="tt-brand">
      <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><polyline points="12 7 12 12 15 14"/></svg>
      <span>TimeTask</span>
    </div>
    <p class="tt-tagline">Track time for each task, stay focused</p>
  </div>

  <div id="tt-screen"></div>

</div>

<script>
(function() {
  'use strict';

  const STORAGE_KEY = 'timetask.v1';

  const state = {
    view: 'intake',
    listName: '',
    tasks: [],
    activeId: null,
    mode: 'stopwatch', // 'stopwatch' | 'pomodoro'
    pomodoroMinutes: 25,
    tickHandle: null,
    drag: { id: null, overId: null }
  };

  // ---------- Persistence ----------
  function persist() {
    try {
      const snap = {
        view: state.view,
        listName: state.listName,
        tasks: state.tasks.map(t => ({
          id: t.id, label: t.label, elapsed: t.elapsed, done: t.done
        })),
        mode: state.mode,
        pomodoroMinutes: state.pomodoroMinutes,
        savedAt: Date.now(),
        activeId: state.activeId,
        activeStartedAt: state.activeId ? Date.now() : null,
        activeAccrued: state.activeId
          ? (state.tasks.find(t => t.id === state.activeId)?.elapsed || 0) +
            (state.tasks.find(t => t.id === state.activeId)?.startedAt
              ? (Date.now() - state.tasks.find(t => t.id === state.activeId).startedAt) / 1000
              : 0)
          : null
      };
      // Save the elapsed-at-pause for the active one rather than baking in time after refresh
      if (state.activeId) {
        const t = state.tasks.find(x => x.id === state.activeId);
        if (t && t.startedAt) {
          // bake current accrued into elapsed for storage
          const baked = t.elapsed + (Date.now() - t.startedAt) / 1000;
          snap.tasks = snap.tasks.map(x => x.id === t.id ? { ...x, elapsed: baked } : x);
        }
      }
      localStorage.setItem(STORAGE_KEY, JSON.stringify(snap));
    } catch (e) {
      // Storage may be disabled (private mode, quota). Silent fail is fine.
    }
  }

  function restore() {
    try {
      const raw = localStorage.getItem(STORAGE_KEY);
      if (!raw) return;
      const snap = JSON.parse(raw);
      if (!snap || !Array.isArray(snap.tasks)) return;
      state.view = snap.view || 'intake';
      state.listName = snap.listName || '';
      state.tasks = snap.tasks.map(t => ({
        id: t.id, label: t.label,
        elapsed: Number(t.elapsed) || 0,
        startedAt: null,
        done: !!t.done
      }));
      state.mode = snap.mode === 'pomodoro' ? 'pomodoro' : 'stopwatch';
      state.pomodoroMinutes = Number(snap.pomodoroMinutes) || 25;
      // Always restore paused — don't auto-resume timers across reloads.
      state.activeId = null;
    } catch (e) {}
  }

  // ---------- Time helpers ----------
  function fmt(s) {
    s = Math.max(0, Math.floor(s));
    const m = Math.floor(s / 60);
    const sec = s % 60;
    return m + ' : ' + String(sec).padStart(2, '0');
  }
  function fmtLong(s) {
    s = Math.max(0, Math.floor(s));
    const h = Math.floor(s / 3600);
    const m = Math.floor((s % 3600) / 60);
    const sec = s % 60;
    if (h > 0) return h + ':' + String(m).padStart(2, '0') + ':' + String(sec).padStart(2, '0');
    return m + ':' + String(sec).padStart(2, '0');
  }
  function liveSecs(t) {
    let s = t.elapsed;
    if (t.id === state.activeId && t.startedAt) s += (Date.now() - t.startedAt) / 1000;
    return s;
  }
  function totalSeconds() {
    return state.tasks.reduce((acc, t) => acc + liveSecs(t), 0);
  }
  function pomoTarget() { return state.pomodoroMinutes * 60; }
  function pomoRemaining(t) { return Math.max(0, pomoTarget() - liveSecs(t)); }

  // ---------- Timer loop ----------
  function startTicking() {
    if (state.tickHandle) return;
    state.tickHandle = setInterval(() => {
      // In pomodoro mode, auto-stop when target reached
      if (state.mode === 'pomodoro' && state.activeId) {
        const t = state.tasks.find(x => x.id === state.activeId);
        if (t && liveSecs(t) >= pomoTarget()) {
          // ding & auto-pause
          pauseActive();
          notifyDone(t.label);
          render();
          return;
        }
      }
      render();
    }, 250);
  }
  function maybeStopTicking() {
    if (!state.activeId && state.tickHandle) {
      clearInterval(state.tickHandle);
      state.tickHandle = null;
    }
  }

  function pauseActive() {
    if (!state.activeId) return;
    const t = state.tasks.find(x => x.id === state.activeId);
    if (t && t.startedAt) {
      t.elapsed += (Date.now() - t.startedAt) / 1000;
      t.startedAt = null;
    }
    state.activeId = null;
    maybeStopTicking();
    persist();
  }

  function notifyDone(label) {
    // Try a soft beep via WebAudio; fall back to title flash
    try {
      const ctx = new (window.AudioContext || window.webkitAudioContext)();
      const o = ctx.createOscillator();
      const g = ctx.createGain();
      o.connect(g); g.connect(ctx.destination);
      o.type = 'sine'; o.frequency.value = 880;
      g.gain.setValueAtTime(0.0001, ctx.currentTime);
      g.gain.exponentialRampToValueAtTime(0.2, ctx.currentTime + 0.02);
      g.gain.exponentialRampToValueAtTime(0.0001, ctx.currentTime + 0.6);
      o.start(); o.stop(ctx.currentTime + 0.65);
    } catch (e) {}
    const orig = document.title;
    document.title = '✅ ' + label.slice(0, 40);
    setTimeout(() => { document.title = orig; }, 4000);
  }

  // ---------- Actions ----------
  function play(id) {
    if (state.activeId && state.activeId !== id) pauseActive();
    const t = state.tasks.find(x => x.id === id);
    if (!t) return;
    if (state.activeId === id) {
      // pause
      t.elapsed += (Date.now() - t.startedAt) / 1000;
      t.startedAt = null;
      state.activeId = null;
      maybeStopTicking();
    } else {
      // In pomodoro, refuse to start if already at/over target
      if (state.mode === 'pomodoro' && liveSecs(t) >= pomoTarget()) {
        if (!confirm('This task already hit its Pomodoro target. Reset and start a new one?')) return;
        t.elapsed = 0;
      }
      t.startedAt = Date.now();
      state.activeId = id;
      startTicking();
    }
    persist();
    render();
  }

  function reset(id) {
    const t = state.tasks.find(x => x.id === id);
    if (!t) return;
    t.elapsed = 0;
    if (state.activeId === id) t.startedAt = Date.now();
    persist();
    render();
  }

  function toggleDone(id) {
    const t = state.tasks.find(x => x.id === id);
    if (!t) return;
    t.done = !t.done;
    if (t.done && state.activeId === id) pauseActive();
    persist();
    render();
  }

  function deleteTask(id) {
    if (state.activeId === id) {
      state.activeId = null;
      maybeStopTicking();
    }
    state.tasks = state.tasks.filter(t => t.id !== id);
    persist();
    render();
  }

  function startTracking() {
    const txt = document.getElementById('tt-paste').value;
    const lines = txt.split('\n').map(l => l.trim()).filter(Boolean);
    if (lines.length === 0) return;
    state.tasks = lines.map((label, i) => ({
      id: 'task-' + Date.now() + '-' + i,
      label, elapsed: 0, startedAt: null, done: false
    }));
    state.view = 'tracker';
    persist();
    render();
  }

  function addTask() {
    const input = document.getElementById('tt-add');
    const label = input.value.trim();
    if (!label) return;
    state.tasks.push({
      id: 'task-' + Date.now(),
      label, elapsed: 0, startedAt: null, done: false
    });
    input.value = '';
    persist();
    render();
    // refocus for fast entry
    const refocus = document.getElementById('tt-add');
    if (refocus) refocus.focus();
  }

  function clearAll() {
    if (!confirm('Clear all tasks and start a new list?')) return;
    state.tasks = [];
    state.activeId = null;
    state.listName = '';
    maybeStopTicking();
    state.view = 'intake';
    persist();
    render();
  }

  function copyReport() {
    const lines = [];
    if (state.listName) lines.push(state.listName);
    lines.push('TimeTask Report — ' + new Date().toLocaleString());
    lines.push('');
    state.tasks.forEach(t => {
      const status = t.done ? '[x]' : '[ ]';
      lines.push('- ' + status + ' ' + t.label + '  —  ' + fmtLong(liveSecs(t)));
    });
    lines.push('');
    lines.push('Total: ' + fmtLong(totalSeconds()));
    const txt = lines.join('\n');
    if (navigator.clipboard && navigator.clipboard.writeText) {
      navigator.clipboard.writeText(txt);
    } else {
      // Fallback
      const ta = document.createElement('textarea');
      ta.value = txt; document.body.appendChild(ta);
      ta.select(); document.execCommand('copy'); document.body.removeChild(ta);
    }
    const btn = document.getElementById('tt-copy');
    if (btn) {
      const orig = btn.innerHTML;
      btn.innerHTML = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><polyline points="20 6 9 17 4 12"/></svg>Copied';
      btn.classList.add('copy-feedback');
      setTimeout(() => {
        if (btn) { btn.innerHTML = orig; btn.classList.remove('copy-feedback'); }
      }, 1400);
    }
  }

  function setMode(mode) {
    state.mode = mode;
    persist();
    render();
  }

  function setPomodoroMinutes(val) {
    const n = Math.max(1, Math.min(180, parseInt(val, 10) || 25));
    state.pomodoroMinutes = n;
    persist();
    render();
  }

  // ---------- Inline editing ----------
  let editing = { type: null, id: null }; // 'label' | 'time'

  function beginEditLabel(id) { editing = { type: 'label', id }; render(); }
  function beginEditTime(id) { editing = { type: 'time', id }; render(); }
  function cancelEdit() { editing = { type: null, id: null }; render(); }

  function commitLabel(id, val) {
    const t = state.tasks.find(x => x.id === id);
    if (t && val.trim()) t.label = val.trim();
    editing = { type: null, id: null };
    persist(); render();
  }
  function commitTime(id, val) {
    const t = state.tasks.find(x => x.id === id);
    if (!t) { editing = { type: null, id: null }; render(); return; }
    const parts = String(val).split(':').map(p => parseInt(p, 10));
    let secs = 0;
    if (parts.length === 3) secs = (parts[0]||0)*3600 + (parts[1]||0)*60 + (parts[2]||0);
    else if (parts.length === 2) secs = (parts[0]||0)*60 + (parts[1]||0);
    else if (parts.length === 1) secs = (parts[0]||0)*60;
    if (!isNaN(secs) && secs >= 0) {
      t.elapsed = secs;
      if (state.activeId === id) t.startedAt = Date.now();
    }
    editing = { type: null, id: null };
    persist(); render();
  }

  // ---------- Drag & drop ----------
  function onDragStart(e, id) {
    state.drag.id = id;
    e.dataTransfer.effectAllowed = 'move';
    try { e.dataTransfer.setData('text/plain', id); } catch(_) {}
  }
  function onDragOver(e, id) {
    e.preventDefault();
    if (state.drag.overId !== id) { state.drag.overId = id; render(); }
  }
  function onDrop(e, id) {
    e.preventDefault();
    const fromId = state.drag.id, toId = id;
    state.drag.id = null; state.drag.overId = null;
    if (!fromId || fromId === toId) { render(); return; }
    const fromIdx = state.tasks.findIndex(t => t.id === fromId);
    const toIdx = state.tasks.findIndex(t => t.id === toId);
    if (fromIdx < 0 || toIdx < 0) { render(); return; }
    const [moved] = state.tasks.splice(fromIdx, 1);
    state.tasks.splice(toIdx, 0, moved);
    persist(); render();
  }
  function onDragEnd() { state.drag.id = null; state.drag.overId = null; render(); }

  // ---------- Render ----------
  function escapeHtml(s) {
    return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
  }

  // SVG icon set (no external font needed)
  const ICON = {
    list:    '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M11 7l9 0"/><path d="M11 12l9 0"/><path d="M11 17l9 0"/><polyline points="3 7 4 8 6 5"/><polyline points="3 12 4 13 6 10"/><polyline points="3 17 4 18 6 15"/></svg>',
    clock:   '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><polyline points="12 7 12 12 15 14"/></svg>',
    play:    '<svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><polygon points="6 4 20 12 6 20 6 4"/></svg>',
    pause:   '<svg viewBox="0 0 24 24" fill="currentColor" stroke="none"><rect x="6" y="4" width="4" height="16" rx="1"/><rect x="14" y="4" width="4" height="16" rx="1"/></svg>',
    refresh: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg>',
    circle:  '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/></svg>',
    check:   '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="9"/><polyline points="8 12 11 15 16 9"/></svg>',
    pencil:  '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20h9"/><path d="M16.5 3.5a2.121 2.121 0 0 1 3 3L7 19l-4 1 1-4 12.5-12.5z"/></svg>',
    x:       '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>',
    copy:    '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="9" y="9" width="13" height="13" rx="2"/><path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"/></svg>',
    timer:   '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="13" r="8"/><polyline points="12 9 12 13 15 15"/><path d="M9 2h6"/><path d="M12 2v3"/></svg>'
  };

  function renderIntake() {
    return `
      <div class="card pad-lg">
        <div class="intake-title">${ICON.list}<span>Paste your to-do list</span></div>
        <p class="intake-sub">Enter one task per line, then track time for each</p>
        <textarea id="tt-paste" placeholder="Review project documents&#10;Write meeting summary&#10;Update client presentation&#10;Prepare quarterly report"></textarea>
        <button id="tt-start" class="btn btn-primary btn-block" style="margin-top:14px;">Start Tracking</button>
      </div>
    `;
  }

  function renderTaskRow(t) {
    const isActive = state.activeId === t.id;
    const secs = liveSecs(t);
    const isDragOver = state.drag.overId === t.id && state.drag.id && state.drag.id !== t.id;

    let displaySecs, timeColor, warningClass = '';
    if (state.mode === 'pomodoro') {
      displaySecs = pomoRemaining(t);
      if (displaySecs <= 60 && displaySecs > 0) warningClass = 'warning';
    } else {
      displaySecs = secs;
    }

    const editingLabel = editing.type === 'label' && editing.id === t.id;
    const editingTime  = editing.type === 'time'  && editing.id === t.id;

    const labelHtml = editingLabel
      ? `<input type="text" class="task-label-input" id="edit-label-${t.id}" value="${escapeHtml(t.label)}" autofocus />`
      : `<span class="task-label">${escapeHtml(t.label)}</span>`;

    const timeHtml = editingTime
      ? `<input type="text" class="task-time-input" id="edit-time-${t.id}" value="${fmtLong(secs)}" autofocus placeholder="mm:ss" />`
      : `<span class="task-time ${warningClass}">${fmtLong(displaySecs)}</span>`;

    let cls = 'task';
    if (isActive) cls += ' active';
    if (isDragOver) cls += ' dragover';
    if (t.done) cls += ' done';

    return `
      <div class="${cls}" draggable="true" data-id="${t.id}">
        <div class="task-row">
          <span class="handle" aria-hidden="true">⋮⋮</span>
          <button class="icon-btn check-btn ${t.done ? 'checked' : ''}" data-act="done" data-id="${t.id}" aria-label="Mark complete">
            ${t.done ? ICON.check : ICON.circle}
          </button>
          ${labelHtml}
          ${editingLabel ? '' : `<button class="icon-btn" data-act="edit-label" data-id="${t.id}" aria-label="Rename">${ICON.pencil}</button>`}
          <button class="control-btn play-btn ${isActive ? 'playing' : ''}" data-act="play" data-id="${t.id}" aria-label="${isActive ? 'Pause' : 'Start'}">
            ${isActive ? ICON.pause : ICON.play}
          </button>
          <button class="control-btn" data-act="reset" data-id="${t.id}" aria-label="Reset">${ICON.refresh}</button>
          <button class="icon-btn" data-act="delete" data-id="${t.id}" aria-label="Delete">${ICON.x}</button>
        </div>
        <div class="task-time-row">
          ${timeHtml}
          ${editingTime ? '' : `<button class="icon-btn" data-act="edit-time" data-id="${t.id}" aria-label="Edit time">${ICON.pencil}</button>`}
        </div>
      </div>
    `;
  }

  function renderTracker() {
    const total = totalSeconds();
    const totalLabel = state.mode === 'pomodoro'
      ? 'Total Focused Time'
      : 'Total Time';
    const rows = state.tasks.length
      ? state.tasks.map(renderTaskRow).join('')
      : '<div class="card empty">No tasks yet — add one above.</div>';

    return `
      <div class="card pad">
        <input type="text" id="tt-listname" value="${escapeHtml(state.listName)}" placeholder="Name this task list (optional)" />
      </div>

      <div class="card pad">
        <div class="add-row">
          <input type="text" id="tt-add" placeholder="Add a new task..." />
          <button id="tt-addbtn" class="btn btn-primary">Add Task</button>
        </div>
      </div>

      <div class="toolbar" style="margin-top: 14px;">
        <div class="mode-toggle" role="tablist" aria-label="Timer mode">
          <button class="${state.mode === 'stopwatch' ? 'active' : ''}" data-mode="stopwatch">Stopwatch</button>
          <button class="${state.mode === 'pomodoro' ? 'active' : ''}" data-mode="pomodoro">Pomodoro</button>
        </div>
        ${state.mode === 'pomodoro' ? `
          <div class="pomodoro-config">
            <span>Focus for</span>
            <input type="number" id="tt-pomo-min" min="1" max="180" value="${state.pomodoroMinutes}" />
            <span>minutes</span>
          </div>
        ` : ''}
      </div>

      <div class="total-bar ${state.mode === 'pomodoro' ? 'pomodoro' : ''}">
        <div class="total-bar-label">${ICON.clock}<span>${totalLabel}</span></div>
        <span class="total-bar-time">${fmtLong(total)}</span>
      </div>

      <div id="tt-rows">${rows}</div>

      <div class="footer-actions">
        <button id="tt-copy" class="btn btn-secondary">${ICON.copy}Copy Report</button>
        <button id="tt-clear" class="btn btn-secondary">Clear All & Start New List</button>
      </div>
    `;
  }

  function attach() {
    if (state.view === 'intake') {
      const start = document.getElementById('tt-start');
      if (start) start.onclick = startTracking;
      const ta = document.getElementById('tt-paste');
      if (ta) {
        ta.focus();
        ta.onkeydown = (e) => {
          if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) startTracking();
        };
      }
      return;
    }

    const ln = document.getElementById('tt-listname');
    if (ln) ln.oninput = (e) => { state.listName = e.target.value; persist(); };

    const addBtn = document.getElementById('tt-addbtn');
    const addInp = document.getElementById('tt-add');
    if (addBtn) addBtn.onclick = addTask;
    if (addInp) addInp.onkeydown = (e) => { if (e.key === 'Enter') addTask(); };

    const copyBtn = document.getElementById('tt-copy');
    if (copyBtn) copyBtn.onclick = copyReport;
    const clearBtn = document.getElementById('tt-clear');
    if (clearBtn) clearBtn.onclick = clearAll;

    document.querySelectorAll('.mode-toggle button').forEach(b => {
      b.onclick = () => setMode(b.dataset.mode);
    });
    const pomoMin = document.getElementById('tt-pomo-min');
    if (pomoMin) {
      pomoMin.onchange = (e) => setPomodoroMinutes(e.target.value);
      pomoMin.onkeydown = (e) => { if (e.key === 'Enter') setPomodoroMinutes(e.target.value); };
    }

    // Action delegation on rows
    document.querySelectorAll('[data-act]').forEach(b => {
      const id = b.dataset.id;
      const act = b.dataset.act;
      b.onclick = () => {
        if (act === 'play') play(id);
        else if (act === 'reset') reset(id);
        else if (act === 'done') toggleDone(id);
        else if (act === 'delete') deleteTask(id);
        else if (act === 'edit-label') beginEditLabel(id);
        else if (act === 'edit-time') beginEditTime(id);
      };
    });

    // Inline edit inputs
    if (editing.type === 'label' && editing.id) {
      const inp = document.getElementById('edit-label-' + editing.id);
      if (inp) {
        inp.focus();
        inp.select();
        inp.onkeydown = (e) => {
          if (e.key === 'Enter') commitLabel(editing.id, inp.value);
          else if (e.key === 'Escape') cancelEdit();
        };
        inp.onblur = () => commitLabel(editing.id, inp.value);
      }
    }
    if (editing.type === 'time' && editing.id) {
      const inp = document.getElementById('edit-time-' + editing.id);
      if (inp) {
        inp.focus();
        inp.select();
        inp.onkeydown = (e) => {
          if (e.key === 'Enter') commitTime(editing.id, inp.value);
          else if (e.key === 'Escape') cancelEdit();
        };
        inp.onblur = () => commitTime(editing.id, inp.value);
      }
    }

    // Drag & drop
    document.querySelectorAll('.task').forEach(row => {
      const id = row.dataset.id;
      row.ondragstart = (e) => onDragStart(e, id);
      row.ondragover  = (e) => onDragOver(e, id);
      row.ondrop      = (e) => onDrop(e, id);
      row.ondragend   = onDragEnd;
    });
  }

  function render() {
    const root = document.getElementById('tt-screen');
    if (!root) return;
    root.innerHTML = state.view === 'intake' ? renderIntake() : renderTracker();
    attach();
  }

  // Save before unload (in case timer was running)
  window.addEventListener('beforeunload', persist);

  // Boot
  restore();
  render();
})();
</script>

</body>
</html>

How business management app is reshaping the future of work.

In today's fast-paced digital landscape, managing your finances has never been to the area more crucial.

Business

Agency

How business management app is reshaping the future of work.

In today's fast-paced digital landscape, managing your finances has never been to the area more crucial.

Business

Agency

How business management app is reshaping the future of work.

In today's fast-paced digital landscape, managing your finances has never been to the area more crucial.

Business

Agency

How business management app is reshaping the future of work.

In today's fast-paced digital landscape, managing your finances has never been to the area more crucial.

Business

Agency

The game changing business management solution.

In today's fast-paced digital landscape, managing your finances has never been to the area more crucial.

Business

Agency

The game changing business management solution.

In today's fast-paced digital landscape, managing your finances has never been to the area more crucial.

Business

Agency

The game changing business management solution.

In today's fast-paced digital landscape, managing your finances has never been to the area more crucial.

Business

Agency

The game changing business management solution.

In today's fast-paced digital landscape, managing your finances has never been to the area more crucial.

Business

Agency

Success with Hive's comprehensive business management tools.

In today's fast-paced digital landscape, managing your finances has never been to the area more crucial.

Business

Agency

Success with Hive's comprehensive business management tools.

In today's fast-paced digital landscape, managing your finances has never been to the area more crucial.

Business

Agency

Success with Hive's comprehensive business management tools.

In today's fast-paced digital landscape, managing your finances has never been to the area more crucial.

Business

Agency

Success with Hive's comprehensive business management tools.

In today's fast-paced digital landscape, managing your finances has never been to the area more crucial.

Business

Agency

Supercharge your business with Hive!

From streamlined project coordination to enhanced team collaboration, Hive empowers your organization to reach new heights. Experience the transformation, boost productivity, increase profitability.

Supercharge your business with Hive!

From streamlined project coordination to enhanced team collaboration, Hive empowers your organization to reach new heights. Experience the transformation, boost productivity, increase profitability.

Supercharge your business with Hive!

From streamlined project coordination to enhanced team collaboration, Hive empowers your organization to reach new heights. Experience the transformation, boost productivity, increase profitability.

Supercharge your business with Hive!

From streamlined project coordination to enhanced team collaboration, Hive empowers your organization to reach new heights. Experience the transformation, boost productivity, increase profitability.