// ─── Mission Control / Dashboard ───
// Today's mission = task list pulled from any project where task.today === true.
// Auto-checks from completion. Sprint launch button on each. Active projects
// list with progress bars. AI Chief of Staff card. Current sprint resume card
// if one is paused/minimized.

const { useState, useEffect } = React;
const useLFState = window.useLFState;
const MODES = window.MODES;
const modeOf = window.modeOf;
const taskProgress = window.taskProgress;
const projectProgress = window.projectProgress;
const updateTaskById = window.updateTaskById;

function TimerRing({ totalSecs = 1500, elapsed = 0, size = 140, active = false, color = '#3B82F6' }) {
  const r = 54;
  const circ = 2 * Math.PI * r;
  const pct = Math.min(elapsed / totalSecs, 1);
  const remaining = Math.max(0, totalSecs - elapsed);
  const m = Math.floor(remaining / 60);
  const s = remaining % 60;
  return (
    <div style={{ position: 'relative', width: size, height: size }}>
      <svg width={size} height={size} viewBox="0 0 120 120" style={active ? { filter: `drop-shadow(0 0 10px ${color}66)` } : {}}>
        <circle cx="60" cy="60" r={r} fill="none" stroke="#161616" strokeWidth="5" />
        <circle cx="60" cy="60" r={r} fill="none" stroke={active ? color : '#252525'} strokeWidth="5"
          strokeDasharray={circ} strokeDashoffset={circ * (1 - pct)} strokeLinecap="round"
          transform="rotate(-90 60 60)"
          style={{ transition: 'stroke-dashoffset 1s linear, stroke 0.4s' }} />
      </svg>
      <div style={{ position: 'absolute', inset: 0, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center' }}>
        <div style={{ fontSize: 26, fontWeight: 800, letterSpacing: -1, fontVariantNumeric: 'tabular-nums', color: active ? '#e8e8e8' : '#444' }}>
          {String(m).padStart(2, '0')}:{String(s).padStart(2, '0')}
        </div>
        <div style={{ fontSize: 10, color: '#3a3a3a', fontWeight: 600, letterSpacing: '0.08em', textTransform: 'uppercase' }}>
          {active ? 'remaining' : 'paused'}
        </div>
      </div>
    </div>
  );
}

// Compact list of tasks (and their subtasks) marked as today's mission.
// Auto-checks visually from done state. Click checkbox toggles done. Each row
// has a sprint button; clicking opens the launcher. Drag rows up/down to reorder.
function TodayMission({ tasks, onSprint, order, setOrder }) {
  const [dragKey, setDragKey] = useState(null);
  const [collapsedSet, setCollapsedSet] = useLFState('missionCollapsed', {}); // {key: true}
  const isCollapsed = (key) => !!collapsedSet[key];
  const toggleCollapsed = (key) => setCollapsedSet((s) => ({ ...s, [key]: !s[key] }));
  if (!tasks.length) {
    return (
      <div style={{ padding: 24, textAlign: 'center', color: '#3a3a3a', fontSize: 12, fontStyle: 'italic' }}>
        No tasks on today's mission. Open a task and click ★ Add to today.
      </div>
    );
  }
  const keyOf = (r) => `${r.project.id}:${r.milestone.id}:${r.task.id}`;
  const moveTo = (from, to) => {
    if (from === to) return;
    const keys = tasks.map(keyOf);
    const i = keys.indexOf(from), j = keys.indexOf(to);
    if (i === -1 || j === -1) return;
    const next = [...keys];
    const [moved] = next.splice(i, 1);
    next.splice(j, 0, moved);
    setOrder(next);
  };
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 8 }}>
      {tasks.map((row) => {
        const { project, milestone, task } = row;
        const key = keyOf(row);
        const m = modeOf(task.mode);
        const tp = taskProgress(task);
        const isDone = task.done || tp === 100;
        // Urgency for THIS task (own due_date). 6+ days returns null → no neon.
        const taskDays = window.daysUntil?.(task.dueDate);
        const taskUrgency = window.urgencyColor?.(task.dueDate);
        const taskUrgent = taskDays !== null && taskDays !== undefined && taskDays <= 5 && !isDone;
        // Has any subtask with ≤5 days but this task itself has no urgent dueDate
        const subtaskTriangleItem = (!taskUrgent && task.subtasks?.length)
          ? { subtasks: task.subtasks } : null;
        return (
          <div key={key}
            draggable
            onDragStart={(e) => { setDragKey(key); e.dataTransfer.effectAllowed = 'move'; e.dataTransfer.setData('text/plain', key); }}
            onDragEnd={() => setDragKey(null)}
            onDragOver={(e) => { e.preventDefault(); e.dataTransfer.dropEffect = 'move'; }}
            onDrop={(e) => { e.preventDefault(); const from = e.dataTransfer.getData('text/plain'); if (from && from !== key) moveTo(from, key); setDragKey(null); }}
            style={{
              background: '#0a0a0a',
              border: `1px solid ${dragKey === key ? m.color : (taskUrgent ? taskUrgency : (isDone ? 'rgba(34,197,94,0.2)' : '#161616'))}`,
              borderRadius: 10, padding: 12, cursor: 'grab',
              opacity: dragKey === key ? 0.5 : 1,
              boxShadow: taskUrgent ? `0 0 14px ${taskUrgency}55` : 'none',
              transition: 'all 0.15s'
            }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
              <input type="checkbox" checked={!!task.done}
                onClick={(e) => e.stopPropagation()}
                onChange={(e) => window.setTaskDoneCascade(project.id, milestone.id, task.id, e.target.checked)}
                style={{ width: 18, height: 18, accentColor: taskUrgent ? taskUrgency : '#22C55E', cursor: 'pointer', flexShrink: 0 }} />
              {task.subtasks?.length > 0 && (
                <button onClick={(e) => { e.stopPropagation(); toggleCollapsed(key); }}
                  title={isCollapsed(key) ? 'Show sub-tasks' : 'Hide sub-tasks'}
                  style={{ background: 'transparent', border: 'none', color: '#666', cursor: 'pointer', fontSize: 12, padding: 2, width: 18, height: 18, display: 'flex', alignItems: 'center', justifyContent: 'center', transform: isCollapsed(key) ? 'rotate(-90deg)' : 'rotate(0deg)', transition: 'transform 0.15s' }}>▾</button>
              )}
              <div style={{ flex: 1, minWidth: 0 }}>
                <div style={{ fontSize: 9, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: 3 }}>
                  <span style={{ color: project.color }}>{project.name}</span>
                  <span style={{ margin: '0 6px', color: '#222' }}>›</span>
                  <span style={{ color: milestone.color }}>{milestone.label}</span>
                </div>
                <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
                  <span style={{ fontSize: 14, fontWeight: 700, color: isDone ? '#555' : '#e8e8e8', textDecoration: isDone ? 'line-through' : 'none', lineHeight: 1.4 }}>{task.label}</span>
                  {subtaskTriangleItem && <window.UrgencyTriangle item={subtaskTriangleItem} includeSelf={false} size={12} />}
                </div>
              </div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, flexShrink: 0 }}>
                {task.dueDate && <window.DueDateChip date={task.dueDate} compact />}
                {task.subtasks?.length > 0 && (
                  <span style={{ fontSize: 10, color: '#666', fontWeight: 600 }}>
                    {task.subtasks.filter((s) => s.done).length}/{task.subtasks.length}
                  </span>
                )}
                {tp > 0 && tp < 100 && (
                  <div style={{ width: 50, height: 4, background: '#161616', borderRadius: 2, overflow: 'hidden' }}>
                    <div style={{ height: '100%', width: `${tp}%`, background: m.color }} />
                  </div>
                )}
                <button onClick={() => onSprint({ project, milestone, task })}
                  style={{ background: m.color, color: '#fff', border: 'none', borderRadius: 6, padding: '5px 12px', fontSize: 11, fontWeight: 700, cursor: 'pointer', fontFamily: 'inherit' }}>▶ Sprint</button>
              </div>
            </div>

            {/* Subtasks rendered inline if any, collapsible */}
            {task.subtasks?.length > 0 && !isCollapsed(key) && (
              <div style={{ marginTop: 10, marginLeft: 30, display: 'flex', flexDirection: 'column', gap: 4 }}>
                {task.subtasks.map((s) => {
                  const sUrgency = window.urgencyColor?.(s.dueDate);
                  const sDays = window.daysUntil?.(s.dueDate);
                  const sUrgent = sDays !== null && sDays !== undefined && sDays <= 5 && !s.done;
                  return (
                    <div key={s.id} style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '4px 8px', background: s.done ? 'rgba(34,197,94,0.04)' : 'transparent', borderRadius: 5 }}>
                      <input type="checkbox" checked={!!s.done}
                        onChange={(e) => {
                          updateTaskById(project.id, milestone.id, task.id, (t) => ({
                            ...t, subtasks: t.subtasks.map((ss) => ss.id === s.id ? { ...ss, done: e.target.checked } : ss),
                          }));
                        }}
                        style={{ width: 14, height: 14, accentColor: sUrgent ? sUrgency : '#22C55E', cursor: 'pointer' }} />
                      <span style={{ fontSize: 12, color: s.done ? '#555' : '#999', textDecoration: s.done ? 'line-through' : 'none', flex: 1 }}>{s.title}</span>
                      {s.dueDate && <window.DueDateChip date={s.dueDate} compact />}
                      <button onClick={() => onSprint({ project, milestone, task, subtask: s })}
                        style={{ background: 'transparent', border: `1px solid ${m.color}55`, color: m.color, borderRadius: 4, padding: '2px 7px', fontSize: 10, fontWeight: 700, cursor: 'pointer', fontFamily: 'inherit' }}>▶</button>
                    </div>
                  );
                })}
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
}

function Dashboard({ onRescue, dashVariant = 'A' }) {
  const [projects] = useLFState('projects', []);
  const [activeSprint, setActiveSprint] = useLFState('activeSprint', null);
  const [cogState, setCogState] = useLFState('cognitiveState', { focus: 7, energy: 6, stress: 4, mood: 8 });
  const [launchingShow, setLaunchingShow] = useState(null);
  const [missionOrder, setMissionOrder] = useLFState('missionOrder', []);
  const [dailyBrief, setDailyBrief] = useLFState('dailyBrief', null);
  const [apiKey] = useLFState('apiKey', '');
  const [apiModel] = useLFState('apiModel', 'claude-sonnet-4-6');
  const today = new Date().toISOString().slice(0, 10);
  const briefFresh = dailyBrief && dailyBrief.date === today;
  // Cognitive state check-in. answers held in component state; whether the
  // state was already recorded today is a persisted timestamp so the UI
  // remembers across reloads.
  const [answers, setAnswers] = useLFState('checkinAnswers', { mood: '', energy: '', pulling: '' });
  const [cogRecordedAt, setCogRecordedAt] = useLFState('cogRecordedAt', '');
  const cogRecordedToday = cogRecordedAt && cogRecordedAt.startsWith(today);
  const [editingState, setEditingState] = useState(!cogRecordedToday);
  const [recording, setRecording] = useState(false);
  const [briefing, setBriefing] = useState(false);

  // The brief is invalid if cog state has been re-recorded since brief was written
  const briefStillFresh = briefFresh && dailyBrief?.recordedAt === cogRecordedAt;
  // Freeform note above the mission tasks. Editable here, also settable
  // from the Debrief tab as "tomorrow's note".
  const [missionTopText, setMissionTopText] = useLFState('missionTopText', '');
  const [editingTopText, setEditingTopText] = useState(false);
  const [topTextDraft, setTopTextDraft] = useState(missionTopText);
  useEffect(() => { setTopTextDraft(missionTopText); }, [missionTopText]);

  // Pull today's mission tasks from any project. Auto-elevate tasks whose
  // due_date is today or in the past (and not yet done) — "due today must
  // be part of today's mission" is the rule.
  let todayTasks = [];
  const seenKeys = new Set();
  const pushTask = (p, m, t) => {
    const k = `${p.id}:${m.id}:${t.id}`;
    if (seenKeys.has(k)) return;
    seenKeys.add(k);
    todayTasks.push({ project: p, milestone: m, task: t });
  };
  for (const p of projects) for (const m of (p.branches || [])) {
    if (m.archived) continue;
    for (const t of (m.leaves || [])) {
      if (t.today) pushTask(p, m, t);
      else if (t.dueDate && !t.done) {
        const d = window.daysUntil?.(t.dueDate);
        if (d !== null && d !== undefined && d <= 0) pushTask(p, m, t);
      }
    }
  }
  // Compute "due soon" — anything with a date 1–5 days out (today/overdue
  // are already in Today's Mission). Includes milestones, tasks, subtasks,
  // and task-typed inbox captures.
  const dueSoon = [];
  for (const p of projects) for (const m of (p.branches || [])) {
    if (m.archived) continue;
    const md = window.daysUntil?.(m.dueDate);
    if (md !== null && md !== undefined && md >= 1 && md <= 5) {
      dueSoon.push({ kind: 'milestone', label: `${p.name} › ${m.label}`, dueDate: m.dueDate, days: md, project: p, milestone: m });
    }
    for (const t of (m.leaves || [])) {
      if (t.done) continue;
      const td = window.daysUntil?.(t.dueDate);
      if (td !== null && td !== undefined && td >= 1 && td <= 5) {
        dueSoon.push({ kind: 'task', label: `${p.name} › ${m.label} › ${t.label}`, dueDate: t.dueDate, days: td, project: p, milestone: m, task: t });
      }
      for (const s of (t.subtasks || [])) {
        if (s.done) continue;
        const sd = window.daysUntil?.(s.dueDate);
        if (sd !== null && sd !== undefined && sd >= 1 && sd <= 5) {
          dueSoon.push({ kind: 'subtask', label: `${p.name} › ${m.label} › ${t.label} › ${s.title}`, dueDate: s.dueDate, days: sd, project: p, milestone: m, task: t, subtask: s });
        }
      }
    }
  }
  // Inbox task captures with dates (orphans)
  try {
    const raw = localStorage.getItem('lf.inboxItems');
    if (raw) {
      const items = JSON.parse(raw) || [];
      for (const it of items) {
        if (it.type !== 'task' || !it.dueDate) continue;
        const d = window.daysUntil?.(it.dueDate);
        if (d !== null && d !== undefined && d >= 1 && d <= 5) {
          dueSoon.push({ kind: 'capture', label: it.text || '(unnamed capture)', dueDate: it.dueDate, days: d, inboxId: it.id });
        }
      }
    }
  } catch {}
  dueSoon.sort((a, b) => a.days - b.days);
  // Apply missionOrder
  const keyOf = (r) => `${r.project.id}:${r.milestone.id}:${r.task.id}`;
  todayTasks = [...todayTasks].sort((a, b) => {
    const ai = missionOrder.indexOf(keyOf(a));
    const bi = missionOrder.indexOf(keyOf(b));
    if (ai === -1 && bi === -1) return 0;
    if (ai === -1) return 1;
    if (bi === -1) return -1;
    return ai - bi;
  });
  const completedToday = todayTasks.filter(({ task }) => task.done || taskProgress(task) === 100).length;
  const missionPct = todayTasks.length ? Math.round((completedToday / todayTasks.length) * 100) : 0;

  const onSprint = ({ project, milestone, task, subtask }) => {
    setLaunchingShow({
      projectId: project.id, milestoneId: milestone.id, taskId: task.id,
      subtaskId: subtask?.id,
      label: subtask?.title || task.label || 'Untitled task',
      defaultMode: task.mode || 'production',
    });
  };

  // Action 1: read the 3 text answers, ask Claude to estimate
  // focus/energy/stress/mood numbers, save them as the current cog state.
  const recordCogState = async () => {
    if (recording) return;
    const t = {
      mood: (answers.mood || '').trim(),
      energy: (answers.energy || '').trim(),
      pulling: (answers.pulling || '').trim(),
    };
    if (!t.mood && !t.energy && !t.pulling) {
      // Nothing to record — just stamp now and use current numbers
      setCogRecordedAt(new Date().toISOString());
      setEditingState(false);
      return;
    }
    setRecording(true);
    try {
      if (!apiKey) {
        // No-key path: try parsing "1-10" out of energy text, leave the rest
        const ns = { ...cogState };
        const en = parseInt((t.energy.match(/\d+/) || [])[0], 10);
        if (!isNaN(en) && en >= 1 && en <= 10) ns.energy = en;
        setCogState(ns);
      } else {
        const prompt = `Quick check-in answers:
- Mood: ${t.mood || '(no answer)'}
- Energy: ${t.energy || '(no answer)'}
- What's pulling on attention: ${t.pulling || '(no answer)'}

Estimate the operator's current cognitive state on a 1-10 scale for each of: focus, energy, stress, mood. Use clues in the text (e.g. low-energy words → lower energy; scattered/anxious words → higher stress / lower focus).

Respond ONLY with raw JSON, no preface or fences:
{ "focus": 1-10, "energy": 1-10, "stress": 1-10, "mood": 1-10 }`;
        const result = await window.callClaude({
          apiKey, model: apiModel,
          system: window.USER_PROFILE_SYSTEM + '\n\nFor this turn estimate cognitive-state numbers from a short check-in. Output ONLY raw JSON.',
          messages: [{ role: 'user', content: prompt }],
        });
        const text = (result.content || []).filter((b) => b.type === 'text').map((b) => b.text).join('');
        const m = text.match(/\{[\s\S]*\}/);
        if (m) {
          try {
            const p = JSON.parse(m[0]);
            const ns = { ...cogState };
            if (typeof p.focus === 'number')  ns.focus  = Math.max(1, Math.min(10, p.focus));
            if (typeof p.energy === 'number') ns.energy = Math.max(1, Math.min(10, p.energy));
            if (typeof p.stress === 'number') ns.stress = Math.max(1, Math.min(10, p.stress));
            if (typeof p.mood === 'number')   ns.mood   = Math.max(1, Math.min(10, p.mood));
            setCogState(ns);
          } catch {}
        }
      }
      setCogRecordedAt(new Date().toISOString());
      setEditingState(false);
    } catch (e) {
      // Fall back to recording without updating numbers
      setCogRecordedAt(new Date().toISOString());
      setEditingState(false);
    } finally {
      setRecording(false);
    }
  };

  // Action 2: generate (or re-generate) a brief using the current cog state +
  // today's mission tasks. Picks a starting task when there's a clear signal.
  const generateBrief = async () => {
    if (briefing) return;
    setBriefing(true);
    try {
      if (!apiKey) {
        setDailyBrief({ date: today, brief: 'No API key — kick some ass today anyway.', suggestedTaskId: null, recordedAt: cogRecordedAt });
        return;
      }
      const taskList = todayTasks.length
        ? todayTasks.map((r) => {
            const due = r.task.dueDate ? ` due=${r.task.dueDate}(${window.daysUntil?.(r.task.dueDate)}d)` : '';
            return `- ${r.task.id}: ${r.task.label} (mode=${r.task.mode}, priority=${r.task.priority || 'medium'}, done=${r.task.done || taskProgress(r.task) === 100}${due})`;
          }).join('\n')
        : '(none — no tasks on today\'s mission)';
      const remaining = todayTasks.filter(({ task }) => !task.done && taskProgress(task) < 100).length;
      const dueSoonSummary = dueSoon.length
        ? dueSoon.slice(0, 8).map((d) => `- [${d.kind}] ${d.label} → in ${d.days}d (${d.dueDate})`).join('\n')
        : '(nothing in 1–5 days)';
      const overdueCount = todayTasks.filter(({ task }) => {
        if (task.done) return false;
        const d = window.daysUntil?.(task.dueDate);
        return d !== null && d !== undefined && d < 0;
      }).length;
      const dueTodayCount = todayTasks.filter(({ task }) => {
        if (task.done) return false;
        const d = window.daysUntil?.(task.dueDate);
        return d === 0;
      }).length;
      const prompt = `Current cognitive state (1-10): focus ${cogState.focus}, energy ${cogState.energy}, stress ${cogState.stress}, mood ${cogState.mood}.
Latest self-check answers:
- Mood: ${(answers.mood || '').trim() || '(none)'}
- Energy: ${(answers.energy || '').trim() || '(none)'}
- Pulling: ${(answers.pulling || '').trim() || '(none)'}

Today's mission (${remaining} remaining${overdueCount ? `, ${overdueCount} OVERDUE` : ''}${dueTodayCount ? `, ${dueTodayCount} due today` : ''}):
${taskList}

Coming up (1–5 days):
${dueSoonSummary}

Pick ONE remaining task as the recommended next move. Weighting:
1. OVERDUE items first — they don't wait for great cognitive state.
2. Then DUE TODAY (red) — match to current cog state if possible (high focus → cognitive task; low energy → low-load task).
3. Then "due in 1-2 days" if cog state permits.
4. Otherwise pick by best fit to cog state.

In the brief (1-2 sharp sentences), be explicit about deadline pressure if any item is overdue or due-today. Don't soften it.

Output ONLY raw JSON:
{ "brief": "1-2 sharp sentences", "suggestedTaskId": "..." or null }`;
      const result = await window.callClaude({
        apiKey, model: apiModel,
        system: window.USER_PROFILE_SYSTEM + '\n\nFor this turn generate a daily brief. Output ONLY raw JSON.',
        messages: [{ role: 'user', content: prompt }],
      });
      const text = (result.content || []).filter((b) => b.type === 'text').map((b) => b.text).join('');
      const m = text.match(/\{[\s\S]*\}/);
      let brief = 'Kick some ass today.';
      let suggestedTaskId = null;
      if (m) {
        try {
          const p = JSON.parse(m[0]);
          brief = p.brief || brief;
          suggestedTaskId = p.suggestedTaskId || null;
        } catch {}
      } else if (text) brief = text;
      setDailyBrief({ date: today, brief, suggestedTaskId, recordedAt: cogRecordedAt });
    } catch (e) {
      setDailyBrief({ date: today, brief: `⚠ Brief failed: ${e.message}. Kick some ass anyway.`, suggestedTaskId: null, recordedAt: cogRecordedAt });
    } finally {
      setBriefing(false);
    }
  };

  const suggestedRow = briefStillFresh && dailyBrief?.suggestedTaskId
    ? todayTasks.find((r) => r.task.id === dailyBrief.suggestedTaskId)
    : null;

  // First active project for the "primary" mission card on top
  const activeProject = projects.find((p) => p.status !== 'archived');

  return (
    <div className="screen screen-enter">
      {/* Top row: CSS Grid so both columns share row height. Left column
          stacks Mission + DueSoon; right column is CogState (single card)
          which stretches to match the left column's total height. */}
      <div style={{ display: 'grid', gridTemplateColumns: 'minmax(360px, 2fr) minmax(260px, 380px)', gap: 14, alignItems: 'stretch' }}>
      <div style={{ display: 'flex', flexDirection: 'column', gap: 14, minWidth: 0 }}>
        {/* Mission summary */}
        <div className="card" style={{ borderLeft: `3px solid ${activeProject?.color || '#3B82F6'}` }}>
          <div style={{ display: 'flex', alignItems: 'center', marginBottom: 14 }}>
            <div>
              <div className="card-label">Today's Mission</div>
              <div style={{ fontSize: 19, fontWeight: 700, lineHeight: 1.35, marginTop: 4 }}>
                {todayTasks.length ? `${completedToday} of ${todayTasks.length} done` : 'No mission set'}
              </div>
            </div>
            <div style={{ marginLeft: 'auto', textAlign: 'right' }}>
              <div style={{ fontSize: 28, fontWeight: 800, color: missionPct === 100 ? '#22C55E' : (activeProject?.color || '#3B82F6'), fontVariantNumeric: 'tabular-nums' }}>{missionPct}%</div>
              <div style={{ fontSize: 10, color: '#3a3a3a', letterSpacing: '0.08em', textTransform: 'uppercase' }}>complete</div>
            </div>
          </div>
          <div className="prog-bar" style={{ height: 4, marginBottom: 14 }}>
            <div className="prog-bar-fill" style={{ width: `${missionPct}%`, background: missionPct === 100 ? '#22C55E' : (activeProject?.color || '#3B82F6') }} />
          </div>

          {/* Freeform note — your own pinned line above the mission tasks.
              Settable here OR from Daily Debrief. */}
          {editingTopText ? (
            <div style={{ marginBottom: 14 }}>
              <textarea
                autoFocus
                value={topTextDraft}
                onChange={(e) => setTopTextDraft(e.target.value)}
                onBlur={() => { setMissionTopText(topTextDraft); setEditingTopText(false); }}
                onKeyDown={(e) => { if (e.key === 'Escape') { setTopTextDraft(missionTopText); setEditingTopText(false); } }}
                placeholder="What's today about? One line — pinned above the mission."
                style={{ width: '100%', minHeight: 50, background: '#050505', border: '1px solid #1c1c1c', borderRadius: 8, padding: 10, color: '#e8e8e8', fontFamily: 'inherit', fontSize: 13, lineHeight: 1.5, outline: 'none', boxSizing: 'border-box', resize: 'vertical' }}
              />
            </div>
          ) : (missionTopText ? (
            <div onClick={() => setEditingTopText(true)}
              style={{ marginBottom: 14, padding: '10px 14px', background: 'rgba(234,179,8,0.06)', borderLeft: '2px solid #EAB308', borderRadius: 6, fontSize: 13, lineHeight: 1.55, color: '#ccc', cursor: 'text', fontStyle: 'italic' }}>
              {missionTopText}
            </div>
          ) : (
            <div onClick={() => setEditingTopText(true)}
              style={{ marginBottom: 14, padding: '8px 14px', border: '1px dashed #1c1c1c', borderRadius: 6, fontSize: 11, color: '#3a3a3a', cursor: 'text', fontStyle: 'italic' }}>
              + Add your own note (or set from Daily Debrief)
            </div>
          ))}


          <TodayMission tasks={todayTasks} onSprint={onSprint} order={missionOrder} setOrder={setMissionOrder} />
        </div>

        {/* Due Soon — items due in 1-5 days (today/overdue auto-elevate above) */}
        <div className="card" style={{ borderLeft: dueSoon.length ? `3px solid ${window.urgencyColor?.(dueSoon[0].dueDate) || '#E8E8E8'}` : '3px solid #1c1c1c' }}>
          <div style={{ display: 'flex', alignItems: 'center', marginBottom: dueSoon.length ? 12 : 0 }}>
            <div className="card-label" style={{ marginBottom: 0 }}>Due Soon</div>
            <div style={{ marginLeft: 'auto', fontSize: 10, color: '#3a3a3a', letterSpacing: '0.08em', textTransform: 'uppercase', fontWeight: 700 }}>
              {dueSoon.length ? `${dueSoon.length} in 1–5d` : 'clear runway'}
            </div>
          </div>
          {dueSoon.length === 0 ? (
            <div style={{ padding: 16, textAlign: 'center', color: '#3a3a3a', fontSize: 11, fontStyle: 'italic' }}>
              Nothing due in the next 5 days. Anything due today shows up in Today's Mission above.
            </div>
          ) : (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 6 }}>
              {dueSoon.map((it, i) => {
                const c = window.urgencyColor?.(it.dueDate) || '#E8E8E8';
                const onClick = () => {
                  // Tasks/subtasks → launch a sprint. Milestones/captures → no-op for now.
                  if (it.kind === 'task') onSprint({ project: it.project, milestone: it.milestone, task: it.task });
                  else if (it.kind === 'subtask') onSprint({ project: it.project, milestone: it.milestone, task: it.task, subtask: it.subtask });
                };
                const clickable = it.kind === 'task' || it.kind === 'subtask';
                return (
                  <div key={i}
                    onClick={clickable ? onClick : undefined}
                    style={{
                      display: 'flex', alignItems: 'center', gap: 10,
                      padding: '7px 10px',
                      background: '#0a0a0a',
                      border: `1px solid ${c}55`,
                      borderLeft: `3px solid ${c}`,
                      borderRadius: 7,
                      boxShadow: `0 0 8px ${c}22`,
                      cursor: clickable ? 'pointer' : 'default',
                    }}
                    title={clickable ? 'Click to launch a sprint' : it.label}
                  >
                    <window.UrgencyTriangle item={{ dueDate: it.dueDate }} size={12} />
                    <span style={{ fontSize: 9, color: c, fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', flexShrink: 0 }}>{it.kind}</span>
                    <span style={{ flex: 1, minWidth: 0, fontSize: 12, color: '#d8d8d8', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{it.label}</span>
                    <window.DueDateChip date={it.dueDate} compact />
                  </div>
                );
              })}
            </div>
          )}
        </div>
      </div>

        {/* Cognitive State + daily check-in flow — stretches to align with left column bottom */}
        <div className="card" style={{ display: 'flex', flexDirection: 'column', minWidth: 0 }}>
          <div className="card-label">Cognitive State</div>

          {/* TOP: input fields OR "Recorded" toggle pill */}
          {editingState ? (
            <div style={{ marginBottom: 14, padding: '10px 12px', background: 'rgba(139,92,246,0.06)', borderLeft: '2px solid #8B5CF6', borderRadius: 6 }}>
              <div style={{ fontSize: 9, fontWeight: 700, color: '#8B5CF6', letterSpacing: '0.12em', textTransform: 'uppercase', marginBottom: 8 }}>Daily check-in · {today}</div>
              <div style={{ marginBottom: 8 }}>
                <div style={{ fontSize: 9, fontWeight: 700, color: '#8B5CF6cc', letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: 4 }}>How are you feeling?</div>
                <input
                  value={answers.mood}
                  onChange={(e) => setAnswers({ ...answers, mood: e.target.value })}
                  placeholder="One word or a short sentence"
                  style={{ width: '100%', background: '#050505', border: '1px solid #1c1c1c', borderRadius: 7, padding: '7px 11px', color: '#e8e8e8', fontFamily: 'inherit', fontSize: 13, outline: 'none', boxSizing: 'border-box' }} />
              </div>
              <div style={{ marginBottom: 8 }}>
                <div style={{ fontSize: 9, fontWeight: 700, color: '#8B5CF6cc', letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: 4 }}>Energy?</div>
                <input
                  value={answers.energy}
                  onChange={(e) => setAnswers({ ...answers, energy: e.target.value })}
                  placeholder="1-10 or a description"
                  style={{ width: '100%', background: '#050505', border: '1px solid #1c1c1c', borderRadius: 7, padding: '7px 11px', color: '#e8e8e8', fontFamily: 'inherit', fontSize: 13, outline: 'none', boxSizing: 'border-box' }} />
              </div>
              <div style={{ marginBottom: 10 }}>
                <div style={{ fontSize: 9, fontWeight: 700, color: '#8B5CF6cc', letterSpacing: '0.1em', textTransform: 'uppercase', marginBottom: 4 }}>Anything pulling on your attention?</div>
                <input
                  value={answers.pulling}
                  onChange={(e) => setAnswers({ ...answers, pulling: e.target.value })}
                  placeholder="Leave blank if you're clear"
                  style={{ width: '100%', background: '#050505', border: '1px solid #1c1c1c', borderRadius: 7, padding: '7px 11px', color: '#e8e8e8', fontFamily: 'inherit', fontSize: 13, outline: 'none', boxSizing: 'border-box' }} />
              </div>
              <button onClick={recordCogState} disabled={recording} className="btn btn-primary btn-sm" style={{ width: '100%', background: '#8B5CF6', border: 'none' }}>
                {recording ? 'Recording…' : '● Record cognitive state'}
              </button>
            </div>
          ) : (
            <button onClick={() => setEditingState(true)}
              style={{ width: '100%', marginBottom: 14, padding: '8px 12px', background: 'rgba(34,197,94,0.06)', border: '1px solid rgba(34,197,94,0.25)', borderRadius: 6, color: '#22C55E', cursor: 'pointer', fontFamily: 'inherit', fontSize: 11, fontWeight: 700, letterSpacing: '0.08em', textTransform: 'uppercase', display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 8 }}
              title="Update — state changes through the day">
              <span style={{ width: 6, height: 6, borderRadius: '50%', background: '#22C55E', boxShadow: '0 0 6px #22C55E' }} />
              Cognitive state recorded · click to update
            </button>
          )}

          {/* MIDDLE: the state bars */}
          {[['Focus', cogState.focus, '#3B82F6'], ['Energy', cogState.energy, '#F59E0B'], ['Stress', cogState.stress, '#22C55E'], ['Mood', cogState.mood, '#22C55E']].map(([lbl, v, col]) => (
            <div key={lbl} style={{ marginBottom: 12 }}>
              <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, color: '#555', marginBottom: 5 }}>
                <span>{lbl}</span><span style={{ color: col, fontWeight: 700 }}>{v}/10</span>
              </div>
              <div className="prog-bar"><div className="prog-bar-fill" style={{ width: `${v * 10}%`, background: col }} /></div>
            </div>
          ))}

          {/* BOTTOM: brief display OR Get-my-brief button */}
          {briefStillFresh ? (
            <div style={{ marginTop: 12, padding: '10px 12px', background: 'rgba(139,92,246,0.06)', borderLeft: '2px solid #8B5CF6', borderRadius: 6 }}>
              <div style={{ fontSize: 9, fontWeight: 700, color: '#8B5CF6', letterSpacing: '0.12em', textTransform: 'uppercase', marginBottom: 6 }}>AI · Brief</div>
              <div style={{ fontSize: 12.5, lineHeight: 1.55, color: '#ccc', whiteSpace: 'pre-wrap' }}>{dailyBrief?.brief}</div>
              {suggestedRow && (
                <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginTop: 10, padding: '8px 10px', background: '#050505', borderRadius: 6 }}>
                  <div style={{ fontSize: 9, color: '#8B5CF6', fontWeight: 700, letterSpacing: '0.1em', textTransform: 'uppercase', flexShrink: 0 }}>Start</div>
                  <div style={{ flex: 1, fontSize: 12, fontWeight: 600, color: '#e8e8e8', minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{suggestedRow.task.label}</div>
                  <button onClick={() => onSprint(suggestedRow)} style={{ background: '#8B5CF6', color: '#fff', border: 'none', borderRadius: 5, padding: '4px 10px', fontSize: 10, fontWeight: 700, cursor: 'pointer', fontFamily: 'inherit' }}>▶</button>
                </div>
              )}
              <button onClick={generateBrief} disabled={briefing} style={{ marginTop: 8, background: 'transparent', border: 'none', color: '#3a3a3a', fontSize: 10, cursor: briefing ? 'default' : 'pointer', fontFamily: 'inherit', letterSpacing: '0.06em' }}>
                {briefing ? 'Re-thinking…' : '↻ Refresh brief'}
              </button>
            </div>
          ) : (
            <button onClick={generateBrief} disabled={briefing} className="btn btn-primary btn-sm" style={{ width: '100%', marginTop: 6, background: '#8B5CF6', border: 'none' }}>
              {briefing ? 'Thinking…' : '✦ Get my brief'}
            </button>
          )}
        </div>
      </div>

      {/* Active sprint card (only when a sprint exists — shown when overlay is minimized) */}
      {activeSprint && activeSprint.minimized && (() => {
        const m = modeOf(activeSprint.mode);
        return (
          <div className="card" style={{ background: `${m.color}0c`, borderColor: `${m.color}33`, display: 'flex', alignItems: 'center', gap: 14 }}>
            <div style={{ width: 8, height: 8, borderRadius: '50%', background: m.color, boxShadow: `0 0 8px ${m.color}`, animation: activeSprint.pausedAt ? 'none' : 'pulse 2s infinite' }} />
            <div style={{ flex: 1 }}>
              <div style={{ fontSize: 10, fontWeight: 700, color: m.color, letterSpacing: '0.14em', textTransform: 'uppercase', marginBottom: 3 }}>
                {m.label} · {activeSprint.pausedAt ? 'Paused' : 'Running'}
              </div>
              <div style={{ fontSize: 13, fontWeight: 600 }}>{activeSprint.label}</div>
            </div>
            <button onClick={() => setActiveSprint({ ...activeSprint, minimized: false })}
              style={{ background: m.color, color: '#fff', border: 'none', borderRadius: 7, padding: '6px 14px', fontSize: 11, fontWeight: 700, cursor: 'pointer', fontFamily: 'inherit' }}>Resume</button>
          </div>
        );
      })()}

      {/* Active projects */}
      <div className="card">
        <div style={{ display: 'flex', alignItems: 'center', marginBottom: 12 }}>
          <div className="card-label" style={{ marginBottom: 0 }}>Active Projects</div>
          <div style={{ marginLeft: 'auto', fontSize: 10, color: '#3a3a3a' }}>{projects.length} project{projects.length !== 1 ? 's' : ''}</div>
        </div>
        {projects.length === 0 ? (
          <div style={{ padding: 24, textAlign: 'center', color: '#3a3a3a', fontSize: 12, fontStyle: 'italic' }}>No projects yet — add one in Projetecture.</div>
        ) : (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
            {projects.map((p) => {
              const pct = projectProgress(p);
              return (
                <div key={p.id} style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
                  <div style={{ width: 8, height: 8, borderRadius: '50%', background: p.color, boxShadow: `0 0 6px ${p.color}55` }} />
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 5 }}>
                      <span style={{ fontSize: 13, color: '#ccc', fontWeight: 600 }}>{p.name}</span>
                      <span style={{ fontSize: 12, color: p.color, fontWeight: 700 }}>{pct}%</span>
                    </div>
                    <div className="prog-bar" style={{ height: 4 }}>
                      <div className="prog-bar-fill" style={{ width: `${pct}%`, background: p.color }} />
                    </div>
                  </div>
                </div>
              );
            })}
          </div>
        )}
      </div>

      {/* AI Chief of Staff card */}
      <div className="card" style={{ background: 'rgba(139,92,246,0.04)', borderColor: 'rgba(139,92,246,0.18)', position: 'relative', overflow: 'hidden' }}>
        <div style={{ position: 'absolute', top: 0, right: 0, width: 200, height: 200, background: 'radial-gradient(circle at top right, rgba(139,92,246,0.1) 0%, transparent 70%)', pointerEvents: 'none' }} />
        <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 12 }}>
          <div style={{ width: 6, height: 6, borderRadius: '50%', background: '#8B5CF6', boxShadow: '0 0 6px #8B5CF6' }} />
          <div className="card-label" style={{ color: '#8B5CF6', marginBottom: 0 }}>AI · Chief of Staff</div>
        </div>
        <div style={{ fontSize: 13, lineHeight: 1.65, color: '#aaa' }}>
          Open the AI panel (right side) to talk through what to attack first. Your live state — projects, today's tasks, mode — is fed into every reply.
        </div>
      </div>

      {/* Sprint launcher modal */}
      {launchingShow && <window.SprintLauncherModal launch={launchingShow} onClose={() => setLaunchingShow(null)} />}
    </div>
  );
}

Object.assign(window, { Dashboard, TimerRing });
