// tasks.jsx — Tasks board: 4 columns

const { useState: useStateT } = React;

function fmtDate(d) {
  if (!d) return '—';
  return new Date(d).toLocaleDateString('en-AU', { day: '2-digit', month: '2-digit', year: 'numeric' });
}

function relDate(dateStr) {
  if (!dateStr) return null;
  const d = new Date(dateStr), today = new Date('2026-05-20');
  const diff = Math.ceil((d - today) / 86400000);
  if (diff < 0) return { label: `${Math.abs(diff)}d overdue`, overdue: true };
  if (diff === 0) return { label: 'Due today', today: true };
  if (diff === 1) return { label: 'Due tomorrow' };
  return { label: `Due ${fmtDate(dateStr)}` };
}

function classDateLabel(dateStr) {
  const today = new Date('2026-05-20'), d = new Date(dateStr);
  const diff = Math.ceil((d - today) / 86400000);
  if (diff === 0) return { text: 'Today', warn: true };
  if (diff === 1) return { text: 'Tomorrow', warn: true };
  return { text: d.toLocaleDateString('en-AU', { weekday: 'short', day: '2-digit', month: 'short' }), warn: false };
}

// ── Post-trial Card ───────────────────────────────────────────

function PostTrialCard({ student, onOpenCustomer }) {
  const [statusOpen, setStatusOpen] = useStateT(false);
  const [logOpen, setLogOpen] = useStateT(false);
  const [logForm, setLogForm] = useStateT({ action: 'Called — no answer', notes: '' });
  const [removed, setRemoved] = useStateT(false);

  if (removed) return null;
  const parent = SAMPLE_PARENTS.find(p => p.id === student.parentId);
  const cls    = SAMPLE_CLASSES.find(c => c.id === student.classId);
  const tutor  = cls ? SAMPLE_STAFF.find(s => s.id === cls.tutorId) : null;

  const lastActions = { s2: 'Called 19/05 — no answer', s6: 'SMS sent 20/05 · 9:41am' };
  const lastAction = lastActions[student.id];

  return (
    <>
      <div style={{ background: 'var(--bg-surface)', borderRadius: '8px', border: '1px solid var(--border-soft)', padding: '12px 14px' }}>
        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: '10px' }}>
          <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
            <Avatar name={`${student.firstName} ${student.lastName}`} size={30} />
            <div>
              <div style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-primary)' }}>{student.firstName} {student.lastName}</div>
              <div style={{ fontSize: '11px', color: 'var(--text-muted)', marginTop: '1px' }}>{student.campus}</div>
            </div>
          </div>
          <Badge status="post-trial" label="post-trial" />
        </div>

        {parent && (
          <div style={{ background: 'var(--bg-surface-muted)', borderRadius: '6px', padding: '8px 10px', marginBottom: '10px' }}>
            <div style={{ fontSize: '12px', fontWeight: 500, color: 'var(--text-secondary)', marginBottom: '3px' }}>{parent.firstName} {parent.lastName}</div>
            <div style={{ fontSize: '12px', color: 'var(--text-muted)', display: 'flex', flexDirection: 'column', gap: '1px' }}>
              <span>{parent.mobile}</span>
              <span>{parent.email}</span>
            </div>
          </div>
        )}

        <div style={{ fontSize: '12px', color: 'var(--text-secondary)', display: 'flex', flexDirection: 'column', gap: '3px', marginBottom: '10px' }}>
          <div><span style={{ color: 'var(--text-muted)' }}>Course </span>{student.course}</div>
          <div><span style={{ color: 'var(--text-muted)' }}>Trial  </span>{fmtDate(student.trialDate)}</div>
          {tutor && <div><span style={{ color: 'var(--text-muted)' }}>Tutor  </span>{tutor.firstName} {tutor.lastName}</div>}
          {lastAction && (
            <div style={{ marginTop: '4px', paddingTop: '6px', borderTop: '1px solid var(--border-soft)', color: 'var(--text-muted)', fontStyle: 'italic', fontSize: '11px' }}>
              {lastAction}
            </div>
          )}
        </div>

        <div style={{ display: 'flex', gap: '6px', flexWrap: 'wrap' }}>
          <Button size="sm" variant="primary" onClick={() => setStatusOpen(true)}>Update status</Button>
          <Button size="sm" variant="secondary" onClick={() => setLogOpen(true)}>Log call</Button>
          <Button size="sm" variant="ghost" onClick={() => onOpenCustomer && onOpenCustomer(student)}>Open</Button>
        </div>
      </div>

      <Modal open={statusOpen} onClose={() => setStatusOpen(false)} title="Update status">
        <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
          <p style={{ margin: 0, fontSize: '13px', color: 'var(--text-secondary)' }}>
            Update status for <strong style={{ color: 'var(--text-primary)' }}>{student.firstName} {student.lastName}</strong>
          </p>
          <TMSelect label="New status" value="" onChange={() => {}} options={[
            { value: '', label: 'Select...' },
            { value: 'enrolled',          label: 'Enrolled' },
            { value: 'dead-trial',        label: 'Dead-trial' },
            { value: 'schedule-callback', label: 'Schedule callback' },
          ]} />
          <div style={{ display: 'flex', gap: '8px', justifyContent: 'flex-end' }}>
            <Button variant="secondary" onClick={() => setStatusOpen(false)}>Cancel</Button>
            <Button variant="primary" onClick={() => { setStatusOpen(false); setRemoved(true); }}>Save</Button>
          </div>
        </div>
      </Modal>

      <Modal open={logOpen} onClose={() => setLogOpen(false)} title="Log call action">
        <div style={{ display: 'flex', flexDirection: 'column', gap: '14px' }}>
          <TMSelect label="Action taken" value={logForm.action} onChange={v => setLogForm(f => ({ ...f, action: v }))}
            options={['Called — spoke', 'Called — no answer', 'SMS sent', 'Email sent']} />
          <Textarea label="Notes (required)" value={logForm.notes} onChange={v => setLogForm(f => ({ ...f, notes: v }))}
            placeholder="What happened on this call?" rows={3} />
          <Input label="Next callback date (optional)" type="date" value="" onChange={() => {}} />
          <div style={{ display: 'flex', gap: '8px', justifyContent: 'flex-end' }}>
            <Button variant="secondary" onClick={() => setLogOpen(false)}>Cancel</Button>
            <Button variant="primary" onClick={() => setLogOpen(false)} disabled={!logForm.notes.trim()}>Save</Button>
          </div>
        </div>
      </Modal>
    </>
  );
}

// ── Printing Card ─────────────────────────────────────────────

function PrintingCard({ lesson }) {
  const [printOpen, setPrintOpen] = useStateT(false);
  const [ticked, setTicked] = useStateT({ th: lesson.printedTH, qz: lesson.printedQZ, hw: lesson.printedHW });
  const [removed, setRemoved] = useStateT(false);

  if (removed) return null;
  const dl = classDateLabel(lesson.date);
  const allDone = ticked.th && ticked.qz && ticked.hw;

  const tick = (k, v) => {
    const next = { ...ticked, [k]: v };
    setTicked(next);
    if (next.th && next.qz && next.hw) setTimeout(() => setRemoved(true), 900);
  };

  return (
    <>
      <div style={{ background: 'var(--bg-surface)', borderRadius: '8px',
        border: `1px solid ${allDone ? 'rgba(74,155,126,0.35)' : 'var(--border-soft)'}`, padding: '12px 14px' }}>
        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: '8px' }}>
          <div>
            <div style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-primary)' }}>{lesson.className}</div>
            <div style={{ fontSize: '11px', color: 'var(--text-muted)', marginTop: '2px' }}>{lesson.campus} · {lesson.tutor}</div>
          </div>
          <span style={{ fontSize: '11px', fontWeight: 500, padding: '2px 8px', borderRadius: '4px', flexShrink: 0,
            background: dl.warn ? 'rgba(212,160,58,0.12)' : 'var(--bg-surface-muted)',
            color: dl.warn ? '#D4A03A' : 'var(--text-muted)',
            border: `1px solid ${dl.warn ? 'rgba(212,160,58,0.25)' : 'var(--border-soft)'}` }}>
            {dl.text}
          </span>
        </div>

        <div style={{ fontSize: '12px', color: 'var(--text-secondary)', marginBottom: '10px' }}>
          <div style={{ fontFamily: 'monospace', fontSize: '11px', color: 'var(--text-muted)', marginBottom: '2px' }}>{lesson.lessonCode}</div>
          <div>{lesson.topic} · {lesson.subtopic}</div>
          <div style={{ marginTop: '2px', color: 'var(--text-muted)' }}>{lesson.studentCount} students enrolled</div>
        </div>

        <div style={{ display: 'flex', gap: '12px', marginBottom: '10px' }}>
          {[['th','TH'],['qz','QZ'],['hw','HW']].map(([k,l]) => (
            <label key={k} style={{ display: 'flex', alignItems: 'center', gap: '4px', cursor: 'pointer', fontSize: '12px', fontWeight: 500 }}>
              <input type="checkbox" checked={ticked[k]} onChange={e => tick(k, e.target.checked)} style={{ accentColor: '#1F4D3D' }} />
              <span style={{ color: ticked[k] ? '#4A9B7E' : 'var(--text-secondary)', textDecoration: ticked[k] ? 'line-through' : 'none' }}>{l}</span>
            </label>
          ))}
        </div>

        <div style={{ display: 'flex', gap: '6px' }}>
          <Button size="sm" variant="primary" onClick={() => setPrintOpen(true)}>Print now</Button>
          <Button size="sm" variant="ghost">Open class</Button>
        </div>
      </div>

      <Modal open={printOpen} onClose={() => setPrintOpen(false)} title="Print lesson material">
        <div style={{ display: 'flex', flexDirection: 'column', gap: '14px' }}>
          <div style={{ background: 'var(--bg-surface-muted)', borderRadius: '8px', padding: '12px' }}>
            <div style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-primary)' }}>{lesson.className}</div>
            <div style={{ fontSize: '12px', color: 'var(--text-muted)', marginTop: '3px' }}>{lesson.lessonCode} · {lesson.subtopic}</div>
          </div>
          {[['TH — Theory','A4'],['QZ — Quiz','A4'],['HW — Homework','A4']].map(([f, def]) => (
            <div key={f} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between',
              padding: '10px 12px', background: 'var(--bg-surface-muted)', borderRadius: '6px' }}>
              <span style={{ fontSize: '13px', fontWeight: 500, color: 'var(--text-primary)' }}>{f}</span>
              <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
                <TMSelect value={def} onChange={() => {}} options={['A4','A3']} style={{ padding: '4px 8px', fontSize: '12px', width: 'auto' }} />
                <input type="number" defaultValue={lesson.studentCount} style={{ width: '56px', textAlign: 'center',
                  background: 'var(--bg-elevated)', border: '1px solid var(--border-default)', borderRadius: '4px',
                  padding: '4px 8px', fontSize: '13px', color: 'var(--text-primary)', fontFamily: 'inherit', outline: 'none' }} />
              </div>
            </div>
          ))}
          <TMSelect label="Printer" value="Parramatta printer" onChange={() => {}}
            options={['Parramatta printer', 'Bella Vista printer']} />
          <div style={{ display: 'flex', gap: '8px', justifyContent: 'flex-end' }}>
            <Button variant="secondary" onClick={() => setPrintOpen(false)}>Cancel</Button>
            <Button variant="primary" onClick={() => {
              setPrintOpen(false); setTicked({ th: true, qz: true, hw: true });
              setTimeout(() => setRemoved(true), 900);
            }}>Send to printer</Button>
          </div>
        </div>
      </Modal>
    </>
  );
}

// ── Request Card ──────────────────────────────────────────────

function RequestCard({ request }) {
  const [resolveOpen, setResolveOpen] = useStateT(false);
  const [notes, setNotes] = useStateT('');
  const [removed, setRemoved] = useStateT(false);

  if (removed) return null;
  const tutor = SAMPLE_STAFF.find(s => s.id === request.tutorId);
  const created = new Date(request.createdAt).toLocaleDateString('en-AU', { day: '2-digit', month: '2-digit' });

  return (
    <>
      <div style={{ background: 'var(--bg-surface)', borderRadius: '8px', border: '1px solid var(--border-soft)', padding: '12px 14px' }}>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '8px' }}>
          <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
            {tutor && <Avatar name={`${tutor.firstName} ${tutor.lastName}`} size={28} />}
            <div>
              <div style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-primary)' }}>
                {tutor ? `${tutor.firstName} ${tutor.lastName}` : 'Unknown'}
              </div>
              <div style={{ fontSize: '11px', color: 'var(--text-muted)' }}>
                {request.campus}{request.room ? ` · ${request.room}` : ''}
              </div>
            </div>
          </div>
          <Badge status={request.urgency} label={request.urgency} />
        </div>

        <div style={{ display: 'flex', alignItems: 'center', gap: '6px', marginBottom: '8px' }}>
          <span style={{ fontSize: '11px', padding: '2px 8px', borderRadius: '4px', fontWeight: 500,
            background: 'var(--bg-surface-muted)', color: 'var(--text-secondary)', border: '1px solid var(--border-default)' }}>
            {request.category}
          </span>
          <span style={{ fontSize: '11px', color: 'var(--text-muted)' }}>{created}</span>
        </div>

        <p style={{ margin: '0 0 10px', fontSize: '12px', color: 'var(--text-secondary)', lineHeight: 1.55 }}>
          {request.description}
        </p>

        <div style={{ display: 'flex', gap: '6px' }}>
          <Button size="sm" variant="primary" onClick={() => setResolveOpen(true)}>Resolve</Button>
          <Button size="sm" variant="ghost">Convert to task</Button>
        </div>
      </div>

      <Modal open={resolveOpen} onClose={() => setResolveOpen(false)} title="Resolve request">
        <div style={{ display: 'flex', flexDirection: 'column', gap: '14px' }}>
          <div style={{ background: 'var(--bg-surface-muted)', borderRadius: '6px', padding: '10px 12px', fontSize: '12px', color: 'var(--text-secondary)' }}>
            {request.category} · {request.campus} — <span style={{ color: 'var(--text-muted)' }}>{request.description.slice(0, 60)}…</span>
          </div>
          <Textarea label="Resolution notes (required)" value={notes} onChange={setNotes}
            placeholder="How was this resolved?" rows={3} />
          <div style={{ display: 'flex', gap: '8px', justifyContent: 'flex-end' }}>
            <Button variant="secondary" onClick={() => setResolveOpen(false)}>Cancel</Button>
            <Button variant="primary" onClick={() => { setResolveOpen(false); setRemoved(true); }} disabled={!notes.trim()}>Mark resolved</Button>
          </div>
        </div>
      </Modal>
    </>
  );
}

// ── Self-directed Task Card ────────────────────────────────────

function SelfTaskCard({ task }) {
  const [done, setDone] = useStateT(false);
  const rel = relDate(task.dueDate);
  if (done) return null;
  const typeBadge = { 'Follow up with customer': 'follow-up', 'Print supplementary material': 'print', 'Generic task': 'task' };

  return (
    <div style={{ background: 'var(--bg-surface)', borderRadius: '8px', border: '1px solid var(--border-soft)', padding: '12px 14px' }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: '6px', gap: '8px' }}>
        <div style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-primary)', lineHeight: 1.4 }}>{task.title}</div>
        <span style={{ fontSize: '11px', padding: '2px 8px', borderRadius: '4px', flexShrink: 0, fontWeight: 500,
          background: 'rgba(91,143,207,0.12)', color: '#5B8FCF', border: '1px solid rgba(91,143,207,0.25)' }}>
          {typeBadge[task.type] || 'task'}
        </span>
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '10px' }}>
        <Avatar name={task.assignee} size={20} />
        <span style={{ fontSize: '12px', color: 'var(--text-muted)' }}>{task.assignee}</span>
        {rel && (
          <span style={{ fontSize: '12px', marginLeft: 'auto', fontWeight: rel.overdue || rel.today ? 500 : 400,
            color: rel.overdue ? '#C05656' : rel.today ? '#D4A03A' : 'var(--text-muted)' }}>
            {rel.label}
          </span>
        )}
      </div>
      <div style={{ display: 'flex', gap: '6px' }}>
        <Button size="sm" variant="primary" onClick={() => setDone(true)}>Mark done</Button>
        <Button size="sm" variant="ghost">Edit</Button>
        <Button size="sm" variant="ghost">Reassign</Button>
      </div>
    </div>
  );
}

// ── Absence follow-up Card ────────────────────────────────────
// Auto-generated when a student is marked absent in the most recent held week.
// Logging the call outcome resolves it (and clears the card).

function AbsenceCard({ item, onOpenCustomer }) {
  const [logOpen, setLogOpen] = useStateT(false);
  const [logForm, setLogForm] = useStateT({ action: 'Called — no answer', notes: '' });
  const [removed, setRemoved] = useStateT(false);
  if (removed) return null;

  const { student, cls, week, date, key } = item;
  const parent = SAMPLE_PARENTS.find(p => p.id === student.parentId);
  const save = () => { ABSENCE_RESOLVED.add(key); setLogOpen(false); setRemoved(true); };

  return (
    <>
      <div style={{ background: 'var(--bg-surface)', borderRadius: '8px', border: '1px solid rgba(180,60,60,0.28)', padding: '12px 14px' }}>
        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: '10px' }}>
          <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
            <Avatar name={`${student.firstName} ${student.lastName}`} size={30} />
            <div>
              <div style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-primary)' }}>{student.firstName} {student.lastName}</div>
              <div style={{ fontSize: '11px', color: 'var(--text-muted)', marginTop: '1px' }}>{student.campus}</div>
            </div>
          </div>
          <span style={{ fontSize: '11px', fontWeight: 600, padding: '2px 8px', borderRadius: '4px', flexShrink: 0,
            background: 'rgba(180,60,60,0.12)', color: '#B44040', border: '1px solid rgba(180,60,60,0.3)' }}>Absent</span>
        </div>

        <div style={{ background: 'var(--bg-surface-muted)', borderRadius: '6px', padding: '8px 10px', marginBottom: '10px', fontSize: '12px', color: 'var(--text-secondary)' }}>
          Missed <strong style={{ color: 'var(--text-primary)', fontWeight: 500 }}>{classLabel(cls)}</strong>
          <div style={{ color: 'var(--text-muted)', marginTop: '2px' }}>Week {week} · {date}</div>
        </div>

        {parent && (
          <div style={{ fontSize: '12px', color: 'var(--text-secondary)', display: 'flex', flexDirection: 'column', gap: '1px', marginBottom: '10px' }}>
            <span style={{ fontWeight: 500 }}>{parent.firstName} {parent.lastName}</span>
            <span style={{ color: 'var(--text-muted)' }}>{parent.mobile}</span>
          </div>
        )}

        <div style={{ display: 'flex', gap: '6px' }}>
          <Button size="sm" variant="primary" onClick={() => setLogOpen(true)}>Log call</Button>
          <Button size="sm" variant="ghost" onClick={() => onOpenCustomer && onOpenCustomer(student)}>Open</Button>
        </div>
      </div>

      <Modal open={logOpen} onClose={() => setLogOpen(false)} title={`Follow up — ${student.firstName} ${student.lastName}`}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: '14px' }}>
          <div style={{ background: 'var(--bg-surface-muted)', borderRadius: '6px', padding: '10px 12px', fontSize: '12px', color: 'var(--text-secondary)' }}>
            Absent from <strong style={{ color: 'var(--text-primary)' }}>{classLabel(cls)}</strong> · Week {week}
          </div>
          <TMSelect label="Outcome" value={logForm.action} onChange={v => setLogForm(f => ({ ...f, action: v }))}
            options={['Called — spoke', 'Called — no answer', 'SMS sent', 'Email sent']} />
          <Textarea label="Notes (required)" value={logForm.notes} onChange={v => setLogForm(f => ({ ...f, notes: v }))}
            placeholder="Why were they away? Any make-up needed?" rows={3} />
          <div style={{ display: 'flex', gap: '8px', justifyContent: 'flex-end' }}>
            <Button variant="secondary" onClick={() => setLogOpen(false)}>Cancel</Button>
            <Button variant="primary" onClick={save} disabled={!logForm.notes.trim()}>Save &amp; resolve</Button>
          </div>
        </div>
      </Modal>
    </>
  );
}

// ── Overdue-invoice chase (auto-task when an invoice is > N days overdue) ─────
const INVOICE_CHASE_DONE = new Set();
function overdueInvoiceTasks() {
  return SAMPLE_INVOICES
    .filter(i => { const ov = invoiceOverdueInfo(i); return ov.flag && !INVOICE_CHASE_DONE.has(i.id); })
    .map(i => ({ inv: i, ov: invoiceOverdueInfo(i) }));
}

function OverdueInvoiceCard({ item, onOpenCustomer }) {
  const [removed, setRemoved] = useStateT(false);
  const [logOpen, setLogOpen] = useStateT(false);
  const [notes,   setNotes]   = useStateT('');
  if (removed) return null;
  const { inv, ov } = item;
  const student = invoicePrimaryStudent(inv);
  const parent  = invoiceBillTo(inv);
  const markPaid = () => { recordPayment(inv, { amount: invoiceBalance(inv), method: inv.paymentMethod || 'bank' }); logActivity({ action: 'Marked invoice paid', record: inv.number }); setRemoved(true); };
  const resolve  = () => { INVOICE_CHASE_DONE.add(inv.id); logActivity({ action: 'Chased overdue invoice', record: inv.number, detail: notes || 'contacted' }); setLogOpen(false); setRemoved(true); };

  return (
    <>
      <div style={{ background: 'var(--bg-surface)', borderRadius: '8px', border: '1px solid rgba(192,86,86,0.32)', padding: '12px 14px' }}>
        <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: '10px' }}>
          <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
            {student && <Avatar name={`${student.firstName} ${student.lastName}`} size={30} />}
            <div>
              <div style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-primary)' }}>{student ? `${student.firstName} ${student.lastName}` : 'Unknown'}</div>
              <div style={{ fontSize: '11px', color: 'var(--text-muted)', marginTop: '1px' }}>{inv.number} · {inv.term}</div>
            </div>
          </div>
          <span style={{ fontSize: '11px', fontWeight: 700, padding: '2px 8px', borderRadius: '4px', flexShrink: 0, whiteSpace: 'nowrap',
            background: 'rgba(192,86,86,0.12)', color: '#C05656', border: '1px solid rgba(192,86,86,0.3)' }}>⚑ {ov.daysOverdue}d overdue</span>
        </div>
        <div style={{ background: 'var(--bg-surface-muted)', borderRadius: '6px', padding: '8px 10px', marginBottom: '10px', fontSize: '12px', color: 'var(--text-secondary)' }}>
          Owing <strong style={{ color: 'var(--text-primary)', fontWeight: 600 }}>{fmtAUD(inv.totalInc)}</strong> · due {fmtAUDate(inv.dueDate)}
          {parent && <div style={{ color: 'var(--text-muted)', marginTop: '2px' }}>{parent.firstName} {parent.lastName} · {parent.mobile}</div>}
        </div>
        <div style={{ display: 'flex', gap: '6px', flexWrap: 'wrap' }}>
          <Button size="sm" variant="primary" onClick={() => setLogOpen(true)}>Log call</Button>
          <Button size="sm" variant="secondary" onClick={markPaid}>Mark paid</Button>
          <Button size="sm" variant="ghost" onClick={() => onOpenCustomer && student && onOpenCustomer(student)}>Open</Button>
        </div>
      </div>

      <Modal open={logOpen} onClose={() => setLogOpen(false)} title={`Chase payment — ${inv.number}`}>
        <div style={{ display: 'flex', flexDirection: 'column', gap: '14px' }}>
          <div style={{ background: 'var(--bg-surface-muted)', borderRadius: '6px', padding: '10px 12px', fontSize: '12px', color: 'var(--text-secondary)' }}>
            {student ? `${student.firstName} ${student.lastName}` : ''} owes <strong style={{ color: 'var(--text-primary)' }}>{fmtAUD(inv.totalInc)}</strong> — {ov.daysOverdue} days overdue.
          </div>
          <Textarea label="Notes (required)" value={notes} onChange={setNotes} placeholder="What did the parent say? When will they pay?" rows={3} />
          <div style={{ display: 'flex', gap: '8px', justifyContent: 'flex-end' }}>
            <Button variant="secondary" onClick={() => setLogOpen(false)}>Cancel</Button>
            <Button variant="primary" onClick={resolve} disabled={!notes.trim()}>Save &amp; resolve</Button>
          </div>
        </div>
      </Modal>
    </>
  );
}

// ── Pending absence-credit claims (reception approval) ────────
function pendingCreditClaims() { return SAMPLE_CREDIT_CLAIMS.filter(c => c.status === 'pending'); }

function CreditClaimCard({ claim, onOpenCustomer }) {
  const [removed, setRemoved] = useStateT(false);
  if (removed) return null;
  const student = SAMPLE_STUDENTS.find(s => s.id === claim.studentId);
  const attended = studentAttendedWeek(claim.studentId, claim.term, claim.week);   // true = present that week → flag
  const approve = () => { claim.status = 'approved'; if (student) { student.creditBalance = (student.creditBalance || 0) + claim.amount; student.creditClaimedTerm = claim.term; } logActivity({ action: 'Approved absence credit', record: student ? `${student.firstName} ${student.lastName}` : '', detail: fmtAUD(claim.amount) }); setRemoved(true); };
  const reject  = () => { claim.status = 'rejected'; setRemoved(true); };
  return (
    <div style={{ background: 'var(--bg-surface)', borderRadius: '8px', border: `1px solid ${attended === true ? 'rgba(192,86,86,0.45)' : 'rgba(74,155,126,0.3)'}`, padding: '12px 14px' }}>
      <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: '8px' }}>
        <div style={{ display: 'flex', gap: '8px', alignItems: 'center' }}>
          {student && <Avatar name={`${student.firstName} ${student.lastName}`} size={30} />}
          <div>
            <div style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-primary)' }}>{student ? `${student.firstName} ${student.lastName}` : 'Unknown'}</div>
            <div style={{ fontSize: '11px', color: 'var(--text-muted)' }}>Absence credit · {claim.term} · Wk {claim.week}</div>
          </div>
        </div>
        <span style={{ fontSize: '12px', fontWeight: 700, color: '#4A9B7E', fontVariantNumeric: 'tabular-nums' }}>{fmtAUD(claim.amount)}</span>
      </div>
      <p style={{ margin: '0 0 10px', fontSize: '12px', color: 'var(--text-secondary)', lineHeight: 1.5 }}>{claim.reason}</p>
      {attended === true && (
        <div style={{ display: 'flex', gap: '7px', alignItems: 'flex-start', background: 'rgba(192,86,86,0.1)', border: '1px solid rgba(192,86,86,0.4)', borderRadius: '6px', padding: '8px 10px', marginBottom: '10px' }}>
          <span style={{ fontSize: '13px' }}>⚠</span>
          <span style={{ fontSize: '11.5px', color: '#C05656', fontWeight: 500, lineHeight: 1.4 }}>Attendance shows {student ? student.firstName : 'this student'} was <strong>marked present</strong> in {claim.term} · Wk {claim.week}. Double-check before approving.</span>
        </div>
      )}
      <div style={{ display: 'flex', gap: '6px' }}>
        <Button size="sm" variant="primary" onClick={approve}>Approve credit</Button>
        <Button size="sm" variant="ghost" onClick={reject}>Reject</Button>
      </div>
    </div>
  );
}

// ── Reception safeguards (#16) ────────────────────────────────
// (a) enrolled students with no invoice for the billing term; (b) invoices created but
// never emailed. Both surface as flags so revenue isn't lost and sends aren't forgotten.
const UNINVOICED_DISMISSED = new Set();
function uninvoicedStudents() {
  const term = defaultBillingTerm(INVOICE_TODAY);
  const list = SAMPLE_STUDENTS.filter(s => s.status === 'enrolled' && s.classId && !UNINVOICED_DISMISSED.has(s.id)
    && !SAMPLE_INVOICES.some(inv => !inv.void && inv.term === term && (inv.studentIds || []).indexOf(s.id) !== -1));
  return { term, list };
}
function unsentInvoices() { return SAMPLE_INVOICES.filter(i => !i.void && !i.sent && !i.writeOff && !i.gapDismissed); }

const flagCard = { background: 'var(--bg-surface)', borderRadius: '8px', border: '1px solid rgba(212,160,58,0.4)', padding: '12px 14px' };
const flagBadge = { fontSize: '11px', fontWeight: 700, padding: '2px 8px', borderRadius: '4px', background: 'rgba(212,160,58,0.14)', color: '#B8922A', border: '1px solid rgba(212,160,58,0.35)', whiteSpace: 'nowrap' };
const flagMiniBtn = { background: 'none', border: '1px solid var(--border-default)', borderRadius: '5px', padding: '3px 9px', fontSize: '11px', color: 'var(--text-secondary)', cursor: 'pointer', fontFamily: 'inherit' };

function UninvoicedFlagCard({ onOpenCustomer }) {
  const [, setTick] = useStateT(0);
  const { term, list } = uninvoicedStudents();
  if (list.length === 0) return null;
  const shown = list.slice(0, 5);
  return (
    <div style={flagCard}>
      <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: '8px' }}>
        <div style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-primary)' }}>⚑ Not yet invoiced</div>
        <span style={flagBadge}>{list.length}</span>
      </div>
      <div style={{ fontSize: '12px', color: 'var(--text-secondary)', marginBottom: '10px', lineHeight: 1.5 }}>{list.length} enrolled student{list.length === 1 ? '' : 's'} have no invoice for {term} yet.</div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: '5px', marginBottom: '8px' }}>
        {shown.map(s => (
          <div key={s.id} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '8px', fontSize: '12px' }}>
            <span style={{ color: 'var(--text-primary)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{s.firstName} {s.lastName} <span style={{ color: 'var(--text-muted)' }}>· {s.course}</span></span>
            <span style={{ display: 'flex', gap: '6px', flexShrink: 0 }}>
              <button onClick={() => onOpenCustomer && onOpenCustomer(s)} style={flagMiniBtn}>Open</button>
              <button onClick={() => { UNINVOICED_DISMISSED.add(s.id); setTick(t => t + 1); }} style={flagMiniBtn}>Dismiss</button>
            </span>
          </div>
        ))}
        {list.length > shown.length && <div style={{ fontSize: '11px', color: 'var(--text-muted)' }}>+{list.length - shown.length} more</div>}
      </div>
      <div style={{ fontSize: '11px', color: 'var(--text-muted)' }}>Bill them in Invoices → Batch create.</div>
    </div>
  );
}

function UnsentInvoiceFlagCard() {
  const [, setTick] = useStateT(0);
  const list = unsentInvoices();
  if (list.length === 0) return null;
  const emailNow = inv => { emailInvoice(inv); setTick(t => t + 1); };
  return (
    <div style={flagCard}>
      <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: '8px' }}>
        <div style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-primary)' }}>⚑ Created but not emailed</div>
        <span style={flagBadge}>{list.length}</span>
      </div>
      <div style={{ fontSize: '12px', color: 'var(--text-secondary)', marginBottom: '10px', lineHeight: 1.5 }}>{list.length} invoice{list.length === 1 ? '' : 's'} {list.length === 1 ? 'is' : 'are'} still a draft — created but never sent to the customer.</div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: '5px' }}>
        {list.slice(0, 5).map(inv => { const s = invoicePrimaryStudent(inv); return (
          <div key={inv.id} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '8px', fontSize: '12px' }}>
            <span style={{ color: 'var(--text-primary)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{inv.number} <span style={{ color: 'var(--text-muted)' }}>· {s ? `${s.firstName} ${s.lastName}` : ''}</span></span>
            <button onClick={() => emailNow(inv)} style={{ ...flagMiniBtn, color: '#4A9B7E', borderColor: 'rgba(74,155,126,0.4)', flexShrink: 0 }}>Email now</button>
          </div>
        ); })}
        {list.length > 5 && <div style={{ fontSize: '11px', color: 'var(--text-muted)' }}>+{list.length - 5} more</div>}
      </div>
    </div>
  );
}

// Credit-claims notification (full review lives in Invoices → Credit claims).
function CreditClaimsFlagCard() {
  const claims = pendingCreditClaims();
  if (claims.length === 0) return null;
  const flagged = claims.filter(c => creditClaimAlreadyThisTerm(c) || studentAttendedWeek(c.studentId, c.term, c.week) === true).length;
  const shown = claims.slice(0, 5);
  return (
    <div style={flagCard}>
      <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', marginBottom: '8px' }}>
        <div style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-primary)' }}>⚑ Credit claims to review</div>
        <span style={flagBadge}>{claims.length}</span>
      </div>
      <div style={{ fontSize: '12px', color: 'var(--text-secondary)', marginBottom: '8px', lineHeight: 1.5 }}>{claims.length} absence-credit request{claims.length === 1 ? '' : 's'} awaiting review{flagged ? ` · ${flagged} need a closer look` : ''}.</div>
      <div style={{ display: 'flex', flexDirection: 'column', gap: '4px', marginBottom: '8px' }}>
        {shown.map(c => { const s = SAMPLE_STUDENTS.find(x => x.id === c.studentId); const danger = creditClaimAlreadyThisTerm(c) || studentAttendedWeek(c.studentId, c.term, c.week) === true; return (
          <div key={c.id} style={{ fontSize: '12px', color: 'var(--text-primary)' }}>{s ? `${s.firstName} ${s.lastName}` : 'Unknown'} <span style={{ color: 'var(--text-muted)' }}>· {c.term} · Wk {c.week}</span>{danger && <span style={{ color: '#C05656', marginLeft: 6 }}>⚠</span>}</div>
        ); })}
        {claims.length > shown.length && <div style={{ fontSize: '11px', color: 'var(--text-muted)' }}>+{claims.length - shown.length} more</div>}
      </div>
      <div style={{ fontSize: '11px', color: 'var(--text-muted)' }}>Review &amp; decide in Invoices → Credit claims.</div>
    </div>
  );
}

// ── Board Column ──────────────────────────────────────────────

function BoardColumn({ title, count, emptyMsg, children }) {
  return (
    <div style={{ flex: '1 1 0', minWidth: 0, background: 'var(--bg-surface-muted)', borderRadius: '8px',
      padding: '12px', display: 'flex', flexDirection: 'column', gap: '8px' }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        paddingBottom: '10px', borderBottom: '1px solid var(--border-soft)', flexShrink: 0 }}>
        <span style={{ fontSize: '13px', fontWeight: 500, color: 'var(--text-secondary)' }}>{title}</span>
        <span style={{ fontSize: '11px', color: 'var(--text-muted)', background: 'var(--bg-surface)',
          padding: '1px 8px', borderRadius: '10px', border: '1px solid var(--border-soft)' }}>{count}</span>
      </div>
      {count === 0
        ? <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center',
            fontSize: '12px', color: 'var(--text-muted)', textAlign: 'center', padding: '24px 8px', minHeight: '100px' }}>
            {emptyMsg}
          </div>
        : <div style={{ display: 'flex', flexDirection: 'column', gap: '8px', overflowY: 'auto', flex: 1 }}>
            {children}
          </div>
      }
    </div>
  );
}

// ── Tasks Board ───────────────────────────────────────────────

function TasksBoard({ showData, onOpenCustomer }) {
  const postTrial  = showData ? SAMPLE_STUDENTS.filter(s => s.status === 'post-trial') : [];
  const printing   = showData ? SAMPLE_LESSONS_PRINT : [];
  const requests   = showData ? SAMPLE_REQUESTS : [];
  const selfTasks  = showData ? SAMPLE_SELF_TASKS : [];
  const absences   = showData ? absenceFollowUps() : [];
  const overdueInv = showData ? overdueInvoiceTasks() : [];
  const claims     = showData ? pendingCreditClaims() : [];
  const uninvN     = showData ? uninvoicedStudents().list.length : 0;
  const unsentN    = showData ? unsentInvoices().length : 0;

  return (
    <div style={{ display: 'flex', gap: '12px', height: 'calc(100vh - 116px)', paddingBottom: '16px', overflow: 'hidden' }}>
      <BoardColumn title="Post-trial" count={postTrial.length} emptyMsg="Nothing to follow up — nice work.">
        {postTrial.map(s => <PostTrialCard key={s.id} student={s} onOpenCustomer={onOpenCustomer} />)}
      </BoardColumn>

      <BoardColumn title="Printing" count={printing.length} emptyMsg="All materials printed — you're ahead.">
        {printing.map(l => <PrintingCard key={l.id} lesson={l} />)}
      </BoardColumn>

      <BoardColumn title="Requests" count={requests.length} emptyMsg="No open tutor requests.">
        {requests.map(r => <RequestCard key={r.id} request={r} />)}
      </BoardColumn>

      <BoardColumn title="Tasks" count={selfTasks.length + absences.length + overdueInv.length + (claims.length ? 1 : 0) + (uninvN ? 1 : 0) + (unsentN ? 1 : 0)} emptyMsg="All tasks done — great work.">
        {claims.length > 0 && <CreditClaimsFlagCard key="claims" />}
        {unsentN > 0 && <UnsentInvoiceFlagCard key="unsent" />}
        {uninvN > 0 && <UninvoicedFlagCard key="uninvoiced" onOpenCustomer={onOpenCustomer} />}
        {overdueInv.map(it => <OverdueInvoiceCard key={`ov-${it.inv.id}`} item={it} onOpenCustomer={onOpenCustomer} />)}
        {absences.map(a => <AbsenceCard key={a.key} item={a} onOpenCustomer={onOpenCustomer} />)}
        {selfTasks.map(t => <SelfTaskCard key={t.id} task={t} />)}
      </BoardColumn>
    </div>
  );
}

Object.assign(window, { TasksBoard });
