// customers.jsx — Customers CRM section + profile drawer

const { useState: useSC, useRef: useSCRef, useEffect: useSCEffect } = React;

// ── Day abbreviation helper ───────────────────────────────────

const DAY_ABBR = { Monday:'Mon', Tuesday:'Tue', Wednesday:'Wed', Thursday:'Thu', Friday:'Fri', Saturday:'Sat', Sunday:'Sun' };

function formatClass(classObj) {
  if (!classObj) return '—';
  const day = DAY_ABBR[classObj.day] || classObj.day;
  const room = classObj.room ? ` · ${classObj.room}` : '';
  return `${day} ${classObj.startTime} – ${classObj.endTime}${room}`;
}

// Human-readable, role-prefixed customer code (e.g. S-0042 / P-0007) to
// disambiguate people who share a name. Derived from the stable row id, so
// it never changes for a given customer and is easy to quote over the phone.
function customerCode(row) {
  if (!row) return '—';
  const num = parseInt(String(row.id).replace(/\D/g, ''), 10) || 0;
  const letter = row._type === 'Parent' ? 'P' : 'S';
  return `${letter}-${String(num).padStart(4, '0')}`;
}

// Subject colour for a course (same scheme as the By-year-level dashboard).
function courseColor(course) {
  if (!course || course === '—') return null;
  if (course.includes('EXT2')) return SUBJECT_COLORS.EXT2; // red
  if (course.includes('EXT1')) return SUBJECT_COLORS.EXT1; // purple
  if (course.includes('ADVN')) return SUBJECT_COLORS.ADVN; // green
  if (course.includes('STD'))  return SUBJECT_COLORS.STD;  // teal
  if (course.includes('YR09')) return SUBJECT_COLORS.YR09; // yellow
  return SUBJECT_COLORS.YR10;                               // YR10 → blue
}

// Program/enrolment chips for a student — derived from their term class
// (delivery → in-person/online) plus any active holiday programs.
function programChips(row) {
  if (!row || row._type !== 'Student') return [];
  const chips = [];
  const cls = SAMPLE_CLASSES.find(c => c.id === row.classId);
  if (cls) chips.push({
    label: cls.delivery === 'online' ? 'Term · Online' : 'Term · In-person',
    color: cls.delivery === 'online' ? SUBJECT_COLORS.YR10 : SUBJECT_COLORS.ADVN,
  });
  (row.holiday || []).filter(h => h.status === 'active').forEach(h => {
    const p = SAMPLE_HOLIDAY_PROGRAMS.find(x => x.id === h.programId);
    if (p) chips.push({ label: `Holiday: ${p.shortName || p.name}`, color: '#B8922A' });
  });
  return chips;
}

// Days a record has been in the system (from createdAt to today). Live: grows daily.
function daysInSystem(row) {
  if (!row || !row.createdAt) return null;
  const d = new Date(row.createdAt + 'T00:00:00');
  if (isNaN(d.getTime())) return null;
  const t0 = new Date(); t0.setHours(0, 0, 0, 0);
  return Math.max(0, Math.round((t0 - d) / 86400000));
}

// At-risk colour for leads / post-trials that have been sitting too long.
function ageTone(row) {
  const n = daysInSystem(row);
  if (n == null) return null;
  if (row.status === 'lead' || row.status === 'post-trial') {
    if (n > 14) return '#B44040';  // red — chase now
    if (n >= 7) return '#B8922A';  // amber — getting stale
  }
  return null;
}

// On-brand checkbox (replaces the default white native box)
function Checkbox({ checked, onChange, title, color = 'var(--brand)' }) {
  return (
    <span
      role="checkbox" aria-checked={checked} title={title}
      onClick={e => { e.stopPropagation(); onChange(!checked); }}
      style={{
        width: 16, height: 16, borderRadius: '4px', flexShrink: 0, cursor: 'pointer',
        display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
        background: checked ? color : 'var(--bg-elevated)',
        border: `1px solid ${checked ? color : 'var(--border-default)'}`,
        transition: '100ms ease',
      }}
    >
      {checked && (
        <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="#fff" strokeWidth="3.5" strokeLinecap="round" strokeLinejoin="round">
          <path d="M20 6 9 17l-5-5" />
        </svg>
      )}
    </span>
  );
}

// Build a windowed list of page numbers for the pager (with '…' gaps).
function pageList(current, total) {
  if (total <= 9) return Array.from({ length: total }, (_, i) => i + 1);
  const out = [1];
  const lo = Math.max(2, current - 1), hi = Math.min(total - 1, current + 1);
  if (lo > 2) out.push('…');
  for (let p = lo; p <= hi; p++) out.push(p);
  if (hi < total - 1) out.push('…');
  out.push(total);
  return out;
}

// ── Status config ─────────────────────────────────────────────

const STATUS_CFG = {
  lead:         { bg: 'rgba(74,139,178,0.15)',  text: '#4A8BB2', border: 'rgba(74,139,178,0.3)'  },
  trial:        { bg: 'rgba(155,126,74,0.15)',  text: '#B8922A', border: 'rgba(155,126,74,0.3)'  },
  'post-trial': { bg: 'rgba(126,74,155,0.15)',  text: '#9B6AC0', border: 'rgba(126,74,155,0.3)'  },
  enrolled:     { bg: 'rgba(74,155,126,0.15)',  text: '#4A9B7E', border: 'rgba(74,155,126,0.3)'  },
  'dead-lead':  { bg: 'rgba(176,101,74,0.13)',  text: '#B0654A', border: 'rgba(176,101,74,0.3)'  },
  'dead-trial': { bg: 'rgba(180,60,60,0.12)',   text: '#B44040', border: 'rgba(180,60,60,0.25)'  },
  disenrolled:  { bg: 'rgba(138,134,148,0.14)', text: '#9A96A6', border: 'rgba(138,134,148,0.3)' },
  inactive:     { bg: 'rgba(122,115,103,0.12)', text: '#7A7367', border: 'rgba(122,115,103,0.25)' },
  parent:       { bg: 'rgba(122,115,103,0.12)', text: '#7A7367', border: 'rgba(122,115,103,0.2)'  },
};

const STUDENT_STATUS_OPTIONS = ['lead','trial','post-trial','enrolled','dead-lead','dead-trial','disenrolled'];

// ── Status dropdown cell ──────────────────────────────────────

function StatusDropdown({ status, onChange, isParent }) {
  const [open, setOpen] = useSC(false);
  const ref = useSCRef(null);

  useSCEffect(() => {
    if (!open) return;
    const close = e => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', close);
    return () => document.removeEventListener('mousedown', close);
  }, [open]);

  if (isParent) {
    const c = STATUS_CFG.parent;
    return (
      <span style={{
        display: 'inline-block', fontSize: '11px', fontWeight: 500,
        background: c.bg, color: c.text, border: `1px solid ${c.border}`,
        borderRadius: '4px', padding: '2px 8px',
      }}>Parent</span>
    );
  }

  const c = STATUS_CFG[status] || STATUS_CFG.inactive;

  return (
    <div ref={ref} style={{ position: 'relative', display: 'inline-block' }} onClick={e => e.stopPropagation()}>
      <div
        onClick={() => setOpen(o => !o)}
        style={{
          display: 'inline-flex', alignItems: 'center', gap: '5px',
          background: c.bg, border: `1px solid ${c.border}`,
          borderRadius: '4px', padding: '2px 6px 2px 8px',
          cursor: 'pointer', userSelect: 'none',
        }}
      >
        <span style={{ fontSize: '11px', fontWeight: 500, color: c.text }}>
          {status.charAt(0).toUpperCase() + status.slice(1)}
        </span>
        <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke={c.text} strokeWidth="3"
          strokeLinecap="round" strokeLinejoin="round"
          style={{ opacity: 1, transform: open ? 'rotate(180deg)' : 'none', transition: '150ms ease', flexShrink: 0 }}>
          <path d="M6 9l6 6 6-6" />
        </svg>
      </div>

      {open && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 4px)', left: 0, zIndex: 500,
          background: 'var(--bg-elevated)', border: '1px solid var(--border-default)',
          borderRadius: '6px', boxShadow: '0 8px 24px rgba(0,0,0,0.55)',
          minWidth: '130px', padding: '4px',
        }}>
          {STUDENT_STATUS_OPTIONS.map(o => {
            const sc = STATUS_CFG[o] || STATUS_CFG.inactive;
            const isSel = o === status;
            return (
              <div key={o}
                onClick={() => { onChange(o); setOpen(false); }}
                style={{
                  display: 'flex', alignItems: 'center', gap: '8px',
                  padding: '6px 10px', borderRadius: '4px', cursor: 'pointer',
                  background: isSel ? 'rgba(255,255,255,0.05)' : 'transparent',
                }}
                onMouseEnter={e => e.currentTarget.style.background = 'rgba(255,255,255,0.08)'}
                onMouseLeave={e => e.currentTarget.style.background = isSel ? 'rgba(255,255,255,0.05)' : 'transparent'}
              >
                <div style={{ width: 7, height: 7, borderRadius: '50%', background: sc.text, flexShrink: 0 }} />
                <span style={{ fontSize: '12px', color: 'var(--text-primary)', fontWeight: isSel ? 500 : 400, flex: 1 }}>
                  {o.charAt(0).toUpperCase() + o.slice(1)}
                </span>
                {isSel && (
                  <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="var(--text-muted)" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
                    <path d="M20 6 9 17l-5-5" />
                  </svg>
                )}
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

// ── Inline editable text cell ─────────────────────────────────

function InlineCell({ value, onCommit, placeholder = '—', numeric = false }) {
  const [editing, setEditing] = useSC(false);
  const [draft,   setDraft]   = useSC(value || '');
  const inputRef = useSCRef(null);

  useSCEffect(() => {
    if (editing && inputRef.current) { inputRef.current.focus(); inputRef.current.select(); }
  }, [editing]);

  const commit = () => {
    setEditing(false);
    if (draft !== (value || '')) onCommit(draft);
  };

  const cancel = () => { setDraft(value || ''); setEditing(false); };

  const start = e => { e.stopPropagation(); setDraft(value || ''); setEditing(true); };

  if (editing) {
    return (
      <input
        ref={inputRef}
        value={draft}
        onChange={e => setDraft(e.target.value)}
        onBlur={commit}
        onKeyDown={e => { if (e.key === 'Enter') commit(); if (e.key === 'Escape') cancel(); }}
        onClick={e => e.stopPropagation()}
        style={{
          background: 'var(--bg-elevated)',
          border: '1px solid var(--brand)',
          borderRadius: '3px',
          padding: '3px 7px',
          fontSize: '12px',
          color: 'var(--text-primary)',
          width: '100%',
          outline: 'none',
          fontFamily: 'inherit',
          boxShadow: '0 0 0 2px rgba(31,77,61,0.18)',
          fontVariantNumeric: numeric ? 'tabular-nums' : undefined,
        }}
      />
    );
  }

  return (
    <span
      onClick={start}
      title="Click to edit"
      style={{
        fontSize: '12px',
        color: value ? 'var(--text-secondary)' : 'var(--text-muted)',
        cursor: 'text',
        display: 'block',
        overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
        borderRadius: '3px',
        padding: '3px 0',
        fontVariantNumeric: numeric ? 'tabular-nums' : undefined,
      }}
    >
      {value || placeholder}
    </span>
  );
}

// ── Shared dropdown chevron (a clearly-visible affordance) ────

function DropdownChevron({ open, disabled }) {
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
      width: 18, height: 18, borderRadius: '4px', flexShrink: 0,
      background: disabled ? 'transparent' : 'var(--bg-elevated)',
      border: `1px solid ${disabled ? 'transparent' : 'var(--border-default)'}`,
      transition: '150ms ease',
    }}>
      <svg width="11" height="11" viewBox="0 0 24 24" fill="none"
        stroke={disabled ? 'var(--text-muted)' : 'var(--text-secondary)'} strokeWidth="2.75"
        strokeLinecap="round" strokeLinejoin="round"
        style={{ transform: open ? 'rotate(180deg)' : 'none', transition: '150ms ease', opacity: disabled ? 0.5 : 1 }}>
        <path d="M6 9l6 6 6-6" />
      </svg>
    </span>
  );
}

// ── Inline dropdown cell (custom, single-click) ───────────────

function InlineDropdownCell({ value, options, onCommit, placeholder = '—', colorFor }) {
  const [open, setOpen] = useSC(false);
  const ref = useSCRef(null);

  useSCEffect(() => {
    if (!open) return;
    const close = e => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', close);
    return () => document.removeEventListener('mousedown', close);
  }, [open]);

  const getLabel = val => {
    if (!val) return null;
    for (const o of options) {
      if (typeof o === 'string' && o === val) return o;
      if (typeof o === 'object' && o.value === val) return o.label;
    }
    return val;
  };

  const displayLabel = getLabel(value);
  const handleSelect = v => { setOpen(false); if (v !== (value || '')) onCommit(v); };

  return (
    <div ref={ref} style={{ position: 'relative' }} onClick={e => e.stopPropagation()}>
      <span
        onClick={() => setOpen(o => !o)}
        style={{
          fontSize: '12px', color: displayLabel ? 'var(--text-secondary)' : 'var(--text-muted)',
          cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '6px',
          borderRadius: '3px', padding: '3px 0', overflow: 'hidden',
        }}
      >
        {colorFor && displayLabel && colorFor(value) && (
          <span style={{ width: 7, height: 7, borderRadius: '50%', background: colorFor(value), flexShrink: 0 }} />
        )}
        <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 }}>
          {displayLabel || placeholder}
        </span>
        <DropdownChevron open={open} />
      </span>

      {open && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 2px)', left: '-6px', zIndex: 400,
          background: 'var(--bg-elevated)', border: '1px solid var(--border-default)',
          borderRadius: '6px', boxShadow: '0 8px 24px rgba(0,0,0,0.55)',
          minWidth: '150px', padding: '4px',
        }}>
          <div
            onClick={() => handleSelect('')}
            style={{ padding: '6px 10px', fontSize: '12px', borderRadius: '4px', color: 'var(--text-muted)', cursor: 'pointer' }}
            onMouseEnter={e => e.currentTarget.style.background = 'rgba(255,255,255,0.06)'}
            onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
          >—</div>
          {options.map(o => {
            const v = typeof o === 'string' ? o : o.value;
            const l = typeof o === 'string' ? o : o.label;
            const isSel = v === value;
            return (
              <div key={v} onClick={() => handleSelect(v)}
                style={{
                  padding: '6px 10px', fontSize: '12px', borderRadius: '4px', cursor: 'pointer',
                  color: isSel ? '#4A9B7E' : 'var(--text-primary)',
                  background: isSel ? 'rgba(74,155,126,0.1)' : 'transparent',
                  display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '8px',
                }}
                onMouseEnter={e => { if (!isSel) e.currentTarget.style.background = 'rgba(255,255,255,0.06)'; }}
                onMouseLeave={e => { if (!isSel) e.currentTarget.style.background = 'transparent'; }}
              >
                <span style={{ display: 'flex', alignItems: 'center', gap: '7px', overflow: 'hidden', minWidth: 0 }}>
                  {colorFor && colorFor(v) && (
                    <span style={{ width: 7, height: 7, borderRadius: '50%', background: colorFor(v), flexShrink: 0 }} />
                  )}
                  <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{l}</span>
                </span>
                {isSel && (
                  <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
                    <path d="M20 6 9 17l-5-5" />
                  </svg>
                )}
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

// ── Class dropdown cell (conditional on campus + course) ──────

function ClassDropdownCell({ campus, course, value, onCommit }) {
  const [open, setOpen] = useSC(false);
  const ref = useSCRef(null);

  useSCEffect(() => {
    if (!open) return;
    const close = e => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', close);
    return () => document.removeEventListener('mousedown', close);
  }, [open]);

  const hasCampus = campus && campus !== '—';
  const hasCourse = course && course !== '—';
  const ready = hasCampus && hasCourse;

  // The list of options is driven ENTIRELY by the selected campus + course.
  const eligible = ready ? SAMPLE_CLASSES.filter(c => c.campus === campus && c.course === course && !c.archived) : [];

  // Resolve the current assignment's label from the full list so it always
  // renders truthfully, and surface it in the menu even if it predates the
  // current campus/course (so the cell and menu never contradict each other).
  const current = value ? SAMPLE_CLASSES.find(c => c.id === value) : null;
  const menuClasses = current && !eligible.some(c => c.id === current.id)
    ? [current, ...eligible]
    : eligible;
  const displayLabel = current ? formatClass(current) : null;

  // Locked until both campus and course are chosen
  if (!ready) {
    return (
      <span
        title="Choose a campus and course first"
        style={{
          fontSize: '11px', color: 'var(--text-muted)', fontStyle: 'italic',
          display: 'flex', alignItems: 'center', gap: '6px', cursor: 'not-allowed',
          padding: '3px 0', overflow: 'hidden',
        }}
      >
        <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 }}>
          Set campus &amp; course
        </span>
        <DropdownChevron open={false} disabled />
      </span>
    );
  }

  const handleSelect = v => { setOpen(false); if (v !== (value || '')) onCommit(v); };

  return (
    <div ref={ref} style={{ position: 'relative' }} onClick={e => e.stopPropagation()}>
      <span
        onClick={() => setOpen(o => !o)}
        style={{
          fontSize: '12px', color: displayLabel ? 'var(--text-secondary)' : 'var(--text-muted)',
          cursor: 'pointer', display: 'flex', alignItems: 'center', gap: '6px',
          borderRadius: '3px', padding: '3px 0', overflow: 'hidden',
        }}
      >
        {current && courseColor(current.course) && (
          <span style={{ width: 7, height: 7, borderRadius: '50%', background: courseColor(current.course), flexShrink: 0 }} />
        )}
        <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 }}>
          {displayLabel || 'Select class'}
        </span>
        <DropdownChevron open={open} />
      </span>

      {open && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 2px)', left: '-6px', zIndex: 400,
          background: 'var(--bg-elevated)', border: '1px solid var(--border-default)',
          borderRadius: '6px', boxShadow: '0 8px 24px rgba(0,0,0,0.55)',
          minWidth: '210px', padding: '4px',
        }}>
          {/* What the list is filtered by */}
          <div style={{ padding: '6px 10px 7px', fontSize: '10px', color: 'var(--text-muted)',
            letterSpacing: '0.04em', textTransform: 'uppercase',
            borderBottom: '1px solid var(--border-soft)', marginBottom: '4px' }}>
            {campus} · {course}
          </div>

          {menuClasses.length === 0 ? (
            <div style={{ padding: '12px 10px', fontSize: '12px', color: 'var(--text-muted)', textAlign: 'center', lineHeight: 1.5 }}>
              No classes for this<br />campus + course yet
            </div>
          ) : (
            <React.Fragment>
              <div
                onClick={() => handleSelect('')}
                style={{ padding: '6px 10px', fontSize: '12px', borderRadius: '4px', color: 'var(--text-muted)', cursor: 'pointer' }}
                onMouseEnter={e => e.currentTarget.style.background = 'rgba(255,255,255,0.06)'}
                onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
              >— Unassign</div>
              {menuClasses.map(c => {
                const isSel = c.id === value;
                return (
                  <div key={c.id} onClick={() => handleSelect(c.id)}
                    style={{
                      padding: '6px 10px', fontSize: '12px', borderRadius: '4px', cursor: 'pointer',
                      color: isSel ? '#4A9B7E' : 'var(--text-primary)',
                      background: isSel ? 'rgba(74,155,126,0.1)' : 'transparent',
                      display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '8px',
                    }}
                    onMouseEnter={e => { if (!isSel) e.currentTarget.style.background = 'rgba(255,255,255,0.06)'; }}
                    onMouseLeave={e => { if (!isSel) e.currentTarget.style.background = 'transparent'; }}
                  >
                    <span style={{ display: 'flex', alignItems: 'center', gap: '7px', overflow: 'hidden', minWidth: 0 }}>
                      <span style={{ width: 7, height: 7, borderRadius: '50%', background: courseColor(c.course), flexShrink: 0 }} />
                      <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{formatClass(c)}</span>
                    </span>
                    {isSel && (
                      <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
                        <path d="M20 6 9 17l-5-5" />
                      </svg>
                    )}
                  </div>
                );
              })}
            </React.Fragment>
          )}
        </div>
      )}
    </div>
  );
}

// ── Trial Date Cell (mini calendar picker) ───────────────────

function TrialDateCell({ value, onCommit }) {
  const [open, setOpen] = useSC(false);
  const [viewDate, setViewDate] = useSC(() => {
    const d = value ? new Date(value + 'T00:00:00') : new Date();
    return { year: d.getFullYear(), month: d.getMonth() };
  });
  const ref = useSCRef(null);

  const TODAY = new Date();
  const todayStr = `${TODAY.getFullYear()}-${String(TODAY.getMonth()+1).padStart(2,'0')}-${String(TODAY.getDate()).padStart(2,'0')}`;

  useSCEffect(() => {
    if (!open) return;
    const close = e => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', close);
    return () => document.removeEventListener('mousedown', close);
  }, [open]);

  // Friendly date ("April 5") + how many days away. Past trials show no countdown.
  const MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December'];
  const trial = (() => {
    if (!value) return null;
    const d = new Date(value + 'T00:00:00');
    if (isNaN(d.getTime())) return { label: value, rel: null, urgent: false };
    const label = `${MONTHS[d.getMonth()]} ${d.getDate()}`;
    const t0 = new Date(); t0.setHours(0, 0, 0, 0);
    const diff = Math.round((d - t0) / 86400000);
    let rel = null, urgent = false;
    if (diff > 1)        rel = `in ${diff} days`;
    else if (diff === 1) { rel = 'tomorrow'; urgent = true; }
    else if (diff === 0) { rel = 'today';    urgent = true; }
    // diff < 0 → trial already happened, so no countdown
    return { label, rel, urgent };
  })();

  const { year, month } = viewDate;
  const firstDay = new Date(year, month, 1).getDay();
  const daysInMonth = new Date(year, month + 1, 0).getDate();
  const monthName = new Date(year, month, 1).toLocaleDateString('en-AU', { month: 'long', year: 'numeric' });

  const prevMonth = () => setViewDate(v => {
    const d = new Date(v.year, v.month - 1, 1);
    return { year: d.getFullYear(), month: d.getMonth() };
  });
  const nextMonth = () => setViewDate(v => {
    const d = new Date(v.year, v.month + 1, 1);
    return { year: d.getFullYear(), month: d.getMonth() };
  });

  const handleDayClick = day => {
    const dateStr = `${year}-${String(month+1).padStart(2,'0')}-${String(day).padStart(2,'0')}`;
    onCommit(dateStr);
    setOpen(false);
  };

  // Build grid cells (week starts Sunday)
  const cells = [];
  for (let i = 0; i < firstDay; i++) cells.push(null);
  for (let d = 1; d <= daysInMonth; d++) cells.push(d);
  while (cells.length % 7 !== 0) cells.push(null);

  return (
    <div ref={ref} style={{ position: 'relative' }} onClick={e => e.stopPropagation()}>
      <span
        onClick={() => {
          if (!open) {
            const d = value ? new Date(value + 'T00:00:00') : new Date();
            setViewDate({ year: d.getFullYear(), month: d.getMonth() });
          }
          setOpen(o => !o);
        }}
        title="Click to set trial date"
        style={{
          fontSize: '12px', color: trial ? 'var(--text-secondary)' : 'var(--text-muted)',
          cursor: 'pointer', display: 'block',
          borderRadius: '3px', padding: '3px 0',
        }}
      >
        {trial ? (
          <span style={{ display: 'flex', flexDirection: 'column', lineHeight: 1.25 }}>
            <span>{trial.label}</span>
            {trial.rel && (
              <span style={{ fontSize: '10px', fontWeight: 500, color: trial.urgent ? '#B8922A' : 'var(--text-muted)' }}>
                {trial.rel}
              </span>
            )}
          </span>
        ) : '—'}
      </span>

      {open && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 4px)', left: 0, zIndex: 400,
          background: 'var(--bg-elevated)', border: '1px solid var(--border-default)',
          borderRadius: '8px', boxShadow: '0 8px 24px rgba(0,0,0,0.45)',
          padding: '12px', width: '214px',
        }}>
          {/* Month navigation */}
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '10px' }}>
            <button onClick={prevMonth} style={{
              background: 'none', border: 'none', cursor: 'pointer',
              color: 'var(--text-muted)', padding: '2px 7px', borderRadius: '3px',
              fontSize: '15px', fontFamily: 'inherit', lineHeight: 1,
            }}
              onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover)'}
              onMouseLeave={e => e.currentTarget.style.background = 'none'}
            >‹</button>
            <span style={{ fontSize: '12px', fontWeight: 600, color: 'var(--text-primary)' }}>{monthName}</span>
            <button onClick={nextMonth} style={{
              background: 'none', border: 'none', cursor: 'pointer',
              color: 'var(--text-muted)', padding: '2px 7px', borderRadius: '3px',
              fontSize: '15px', fontFamily: 'inherit', lineHeight: 1,
            }}
              onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover)'}
              onMouseLeave={e => e.currentTarget.style.background = 'none'}
            >›</button>
          </div>

          {/* Day-of-week labels */}
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: '2px', marginBottom: '3px' }}>
            {['Su','Mo','Tu','We','Th','Fr','Sa'].map(d => (
              <div key={d} style={{ textAlign: 'center', fontSize: '10px', fontWeight: 500, color: 'var(--text-muted)', padding: '1px 0' }}>{d}</div>
            ))}
          </div>

          {/* Day grid */}
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(7, 1fr)', gap: '2px' }}>
            {cells.map((day, idx) => {
              if (!day) return <div key={`e${idx}`} />;
              const dateStr = `${year}-${String(month+1).padStart(2,'0')}-${String(day).padStart(2,'0')}`;
              const isToday    = dateStr === todayStr;
              const isSelected = dateStr === value;
              return (
                <button
                  key={day}
                  onClick={() => handleDayClick(day)}
                  style={{
                    background: isSelected ? 'var(--brand)' : isToday ? 'rgba(31,77,61,0.22)' : 'none',
                    border: isToday && !isSelected ? '1px solid rgba(31,77,61,0.55)' : '1px solid transparent',
                    borderRadius: '4px', padding: '3px 0', fontSize: '11px',
                    color: isSelected ? '#fff' : isToday ? '#4A9B7E' : 'var(--text-secondary)',
                    cursor: 'pointer', fontFamily: 'inherit', textAlign: 'center',
                    transition: '80ms ease',
                  }}
                  onMouseEnter={e => { if (!isSelected) e.currentTarget.style.background = 'rgba(255,255,255,0.08)'; }}
                  onMouseLeave={e => { if (!isSelected) e.currentTarget.style.background = isToday ? 'rgba(31,77,61,0.22)' : 'none'; }}
                >
                  {day}
                </button>
              );
            })}
          </div>

          {/* Clear */}
          {value && (
            <button
              onClick={() => { onCommit(''); setOpen(false); }}
              style={{
                marginTop: '10px', width: '100%', background: 'none',
                border: '1px solid var(--border-default)', borderRadius: '4px',
                padding: '5px', fontSize: '11px', color: 'var(--text-muted)',
                cursor: 'pointer', fontFamily: 'inherit', transition: '80ms ease',
              }}
              onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover)'}
              onMouseLeave={e => e.currentTarget.style.background = 'none'}
            >Clear date</button>
          )}
        </div>
      )}
    </div>
  );
}

// ── Trial Email Dropdown ──────────────────────────────────────

function TrialEmailDropdown({ student }) {
  const [open, setOpen] = useSC(false);
  // Track WHEN it was last sent and WHICH trial date it was sent for,
  // so we can flag a needed resend if the trial date later changes.
  const [sentAt,      setSentAt]      = useSC(null);
  const [sentForDate, setSentForDate] = useSC(null);
  const ref = useSCRef(null);

  useSCEffect(() => {
    if (!open) return;
    const close = e => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', close);
    return () => document.removeEventListener('mousedown', close);
  }, [open]);

  const fmtDate = d => {
    if (!d) return '—';
    try { return new Date(d + 'T00:00:00').toLocaleDateString('en-AU', { day: '2-digit', month: '2-digit', year: 'numeric' }); }
    catch (e) { return d; }
  };

  const hasSent      = !!sentAt;
  const currentTrial = student.trialDate || '';
  const dateChanged  = hasSent && currentTrial !== (sentForDate || '');

  // Conditional logic: the trial email can't be sent until all the details it
  // must include are present — Campus, Course, Class and Trial date.
  const has = v => v && v !== '—';
  const requirements = [
    ['Campus',     has(student.campus)],
    ['Course',     has(student.course)],
    ['Class',      !!student.classId],
    ['Trial date', has(student.trialDate)],
  ];
  const missing = requirements.filter(([, ok]) => !ok).map(([l]) => l);
  const canSend = missing.length === 0;

  const handleSend = () => {
    const now = new Date().toLocaleString('en-AU',
      { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' });
    setSentAt(now);
    setSentForDate(currentTrial);
    setOpen(false);
    // Phase-2 hook: this is where the automated trial-email workflow fires.
    console.log(`[Trial email ${hasSent ? 'RESENT' : 'sent'}] → ${student.firstName} ${student.lastName} + parent · trial ${fmtDate(currentTrial)}`);
  };

  // Button tone: neutral (never sent) / green (sent & up to date) / amber (needs resend)
  const tone = !hasSent ? 'neutral' : dateChanged ? 'warn' : 'sent';
  const toneCfg = {
    neutral: { bg: 'var(--bg-surface-muted)', border: 'var(--border-default)', color: 'var(--text-secondary)', label: 'Trial Email' },
    sent:    { bg: 'rgba(74,155,126,0.12)',   border: 'rgba(74,155,126,0.35)', color: '#4A9B7E',             label: '✓ Sent' },
    warn:    { bg: 'rgba(184,146,42,0.14)',    border: 'rgba(184,146,42,0.40)', color: '#B8922A',             label: '⚠ Resend' },
  }[tone];

  return (
    <div ref={ref} style={{ position: 'relative' }} onClick={e => e.stopPropagation()}>
      <button
        onClick={() => setOpen(o => !o)}
        title={hasSent ? `Last sent ${sentAt}` : 'Send the trial lesson confirmation email'}
        style={{
          display: 'inline-flex', alignItems: 'center', gap: '5px',
          background: toneCfg.bg, border: `1px solid ${toneCfg.border}`,
          borderRadius: '4px', padding: '4px 9px', fontSize: '11px', fontWeight: 500,
          color: toneCfg.color, cursor: 'pointer', fontFamily: 'inherit', whiteSpace: 'nowrap', transition: '100ms ease',
        }}
      >
        {!canSend && (
          <svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0, opacity: 0.7 }}>
            <rect x="3" y="11" width="18" height="11" rx="2" />
            <path d="M7 11V7a5 5 0 0 1 10 0v4" />
          </svg>
        )}
        {toneCfg.label}
        <svg width="9" height="9" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
          <path d="M6 9l6 6 6-6" />
        </svg>
      </button>

      {open && (
        <div style={{
          position: 'absolute', top: 'calc(100% + 4px)', right: 0, zIndex: 300,
          background: 'var(--bg-elevated)', border: '1px solid var(--border-default)',
          borderRadius: '8px', boxShadow: '0 6px 16px rgba(0,0,0,0.35)',
          minWidth: '236px', overflow: 'hidden',
        }}>
          {/* Recipients */}
          <div style={{ padding: '9px 12px 8px', borderBottom: '1px solid var(--border-soft)' }}>
            <div style={{ fontSize: '10px', fontWeight: 500, color: 'var(--text-muted)', letterSpacing: '0.06em', textTransform: 'uppercase' }}>Trial email</div>
            <div style={{ fontSize: '12px', color: 'var(--text-secondary)', marginTop: '3px' }}>
              To {student.firstName} {student.lastName} + parent
            </div>
          </div>

          {/* Send history + resend context */}
          {hasSent && (
            <div style={{ padding: '8px 12px', borderBottom: '1px solid var(--border-soft)', fontSize: '11px', lineHeight: 1.5 }}>
              <div style={{ color: 'var(--text-muted)' }}>Last sent <span style={{ color: 'var(--text-secondary)' }}>{sentAt}</span></div>
              <div style={{ color: 'var(--text-muted)' }}>For trial on <span style={{ color: 'var(--text-secondary)' }}>{fmtDate(sentForDate)}</span></div>
              {dateChanged && (
                <div style={{ marginTop: '6px', padding: '6px 8px', borderRadius: '4px',
                  background: 'rgba(184,146,42,0.14)', border: '1px solid rgba(184,146,42,0.3)', color: '#B8922A' }}>
                  Trial date changed to {fmtDate(currentTrial)} — resend to update them.
                </div>
              )}
            </div>
          )}

          {/* Action — locked until all required details are present */}
          <button
            onClick={() => { if (canSend) handleSend(); }}
            disabled={!canSend}
            title={canSend ? '' : `Missing: ${missing.join(', ')}`}
            style={{
              width: '100%', padding: '10px 12px 4px', background: 'none', border: 'none',
              textAlign: 'left', fontSize: '12px', fontWeight: 600,
              color: !canSend ? 'var(--text-muted)' : (dateChanged ? '#B8922A' : 'var(--text-primary)'),
              cursor: canSend ? 'pointer' : 'not-allowed', fontFamily: 'inherit',
              display: 'flex', alignItems: 'center', gap: '8px',
            }}
            onMouseEnter={e => { if (canSend) e.currentTarget.style.background = 'var(--bg-hover)'; }}
            onMouseLeave={e => { e.currentTarget.style.background = 'none'; }}
          >
            {canSend ? (
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
                <path d="M22 2L11 13M22 2L15 22l-4-9-9-4 20-7z" />
              </svg>
            ) : (
              <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
                <rect x="3" y="11" width="18" height="11" rx="2" />
                <path d="M7 11V7a5 5 0 0 1 10 0v4" />
              </svg>
            )}
            <span style={{ flex: 1 }}>{hasSent ? 'Resend trial email' : 'Send trial email'}</span>
          </button>
          {!canSend ? (
            <div style={{ padding: '2px 12px 10px 33px', fontSize: '10px', color: '#B8922A', lineHeight: 1.45 }}>
              Fill out customer details.
            </div>
          ) : hasSent ? (
            <div style={{ padding: '2px 12px 10px 33px', fontSize: '10px', color: 'var(--text-muted)', lineHeight: 1.45 }}>
              Re-sends the confirmation with the latest lesson details.
            </div>
          ) : null}
        </div>
      )}
    </div>
  );
}

// ── Comms log (static per student) ───────────────────────────

const COMMS = {
  s2: [
    { date: '20/05/2026', user: 'Maya R.',  action: 'Called — no answer',  notes: 'Tried at 10:15am, no answer. Will retry tomorrow.' },
    { date: '18/05/2026', user: 'System',   action: 'Status changed',       notes: 'Trial date passed → status set to post-trial automatically.' },
    { date: '12/05/2026', user: 'Jamie L.', action: 'Note added',           notes: 'Trial booked for 18 May at Bella Vista. Parent confirmed via SMS.' },
  ],
  s6: [
    { date: '20/05/2026', user: 'Taylor K.', action: 'SMS sent',            notes: 'Sent enrolment follow-up SMS at 9:41am.' },
    { date: '19/05/2026', user: 'System',    action: 'Status changed',       notes: 'Trial date passed → status set to post-trial automatically.' },
    { date: '15/05/2026', user: 'Maya R.',   action: 'Note added',           notes: 'Called parent Helen. Very interested — just waiting on school timetable.' },
  ],
};
const DEFAULT_COMMS = [
  { date: '15/05/2026', user: 'Maya R.', action: 'Note added', notes: 'Record created during initial data import.' },
];

// ── Drawer edit field (click-to-edit row) ────────────────────

function DrawerEditField({ label, value, onChange, type = 'text', options = null }) {
  const [editing, setEditing] = useSC(false);
  const [draft,   setDraft]   = useSC(value || '');
  const inputRef = useSCRef(null);

  useSCEffect(() => { setDraft(value || ''); }, [value]);
  useSCEffect(() => {
    if (editing && inputRef.current) {
      inputRef.current.focus();
      if (inputRef.current.select) inputRef.current.select();
    }
  }, [editing]);

  const commit = () => { setEditing(false); if (draft !== (value || '')) onChange(draft); };
  const cancel = () => { setDraft(value || ''); setEditing(false); };

  const baseInput = {
    background: 'var(--bg-elevated)', border: '1px solid var(--brand)',
    borderRadius: '4px', padding: '4px 8px', fontSize: '13px',
    color: 'var(--text-primary)', fontFamily: 'inherit', outline: 'none',
    width: '100%', boxShadow: '0 0 0 2px rgba(31,77,61,0.18)', colorScheme: 'dark',
  };

  const displayValue = type === 'date' && value
    ? (() => { try { return new Date(value + 'T00:00:00').toLocaleDateString('en-AU', { day: '2-digit', month: '2-digit', year: 'numeric' }); } catch(e) { return value; } })()
    : value;

  return (
    <div style={{ display: 'flex', alignItems: 'center', padding: '10px 0', borderBottom: '1px solid var(--border-soft)' }}>
      <span style={{ width: '110px', flexShrink: 0, fontSize: '12px', color: 'var(--text-muted)' }}>{label}</span>
      <div style={{ flex: 1, minWidth: 0 }}>
        {options ? (
          <TMSelect value={value} onChange={onChange}
            options={options.map(o => ({ value: o, label: o.charAt(0).toUpperCase() + o.slice(1) }))}
            buttonStyle={{ padding: '6px 10px' }} />
        ) : editing ? (
          <input
            ref={inputRef}
            type={type}
            value={draft}
            onChange={e => setDraft(e.target.value)}
            onBlur={commit}
            onKeyDown={e => { if (e.key === 'Enter') commit(); if (e.key === 'Escape') cancel(); }}
            style={baseInput}
          />
        ) : (
          <span
            onClick={() => { setDraft(value || ''); setEditing(true); }}
            title="Click to edit"
            style={{
              display: 'block', fontSize: '13px',
              color: displayValue ? 'var(--text-primary)' : 'var(--text-muted)',
              cursor: 'text', padding: '4px 6px', borderRadius: '4px',
              overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
              transition: 'background 100ms ease', margin: '0 -6px',
            }}
            onMouseEnter={e => e.currentTarget.style.background = 'rgba(255,255,255,0.05)'}
            onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
          >
            {displayValue || '—'}
          </span>
        )}
      </div>
    </div>
  );
}

// ── Invoice history (made-up past invoices + accordion) ───────

function buildInvoices(student) {
  // Deterministic pseudo-random seeded from the student id so the
  // made-up history is stable between opens.
  let s = (parseInt(String(student.id).replace(/\D/g, ''), 10) || 1) * 7919 + 13;
  const rnd = () => { s = (s * 48271) % 2147483647; return s / 2147483647; };
  const pick = arr => arr[Math.floor(rnd() * arr.length)];

  const terms    = ['Term 2, 2026', 'Term 1, 2026', 'Term 4, 2025', 'Term 3, 2025', 'Term 2, 2025'];
  const paidOn   = ['02/05/2026', '05/02/2026', '14/10/2025', '22/07/2025', '15/04/2025'];
  const methods  = ['Direct debit', 'Card', 'Bank transfer'];
  const discounts = ['None', 'None', 'None', '$20 off/term', '10% sibling discount'];
  const course   = student.course && student.course !== '—' ? student.course : 'YR11 ADVN';
  const count    = 3 + Math.floor(rnd() * 3); // 3–5 invoices
  const base     = 100 + (Math.floor(rnd() * 800));

  const list = [];
  for (let i = 0; i < count; i++) {
    list.push({
      number: `INV${String(base + i).padStart(4, '0')}`,
      term: terms[i] || `Term ${5 - i}, 2025`,
      course,
      status: 'paid',
      amount: 480 + Math.floor(rnd() * 6) * 60,
      credits: rnd() < 0.25 ? (1 + Math.floor(rnd() * 4)) * 20 : 0,
      discount: pick(discounts),
      paymentMethod: pick(methods),
      paidOn: paidOn[i] || '—',
    });
  }
  return list; // index 0 = most recent
}

function InvoiceAccordion({ invoices, showChild }) {
  const [openIdx, setOpenIdx] = useSC(0); // most-recent open by default

  if (!invoices.length) return <EmptyState message="No invoices yet." />;

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
      {invoices.map((inv, i) => {
        const open = openIdx === i;
        return (
          <div key={inv.number} style={{ border: '1px solid var(--border-soft)', borderRadius: '8px', overflow: 'hidden', background: 'var(--bg-surface-muted)' }}>
            {/* Row header */}
            <div
              onClick={() => setOpenIdx(open ? -1 : i)}
              style={{ display: 'flex', alignItems: 'center', gap: '10px', padding: '10px 12px', cursor: 'pointer' }}
              onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover)'}
              onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
            >
              <span style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-primary)', fontVariantNumeric: 'tabular-nums' }}>{inv.number}</span>
              <span style={{ fontSize: '12px', color: 'var(--text-muted)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                {showChild && inv.childName ? `${inv.childName} · ` : ''}{inv.term} · {inv.course}
              </span>
              <div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: '8px', flexShrink: 0 }}>
                <Badge status={inv.status} label="Paid" />
                <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="var(--text-muted)" strokeWidth="2.5"
                  strokeLinecap="round" strokeLinejoin="round"
                  style={{ transform: open ? 'rotate(180deg)' : 'none', transition: '150ms ease' }}>
                  <path d="M6 9l6 6 6-6" />
                </svg>
              </div>
            </div>

            {/* Accordion body */}
            {open && (
              <div style={{ padding: '2px 12px 10px', borderTop: '1px solid var(--border-soft)' }}>
                {[
                  ['Amount', `$${inv.amount}`],
                  ['Credit applied', inv.credits > 0 ? <span style={{ color: '#4A9B7E', fontWeight: 500 }}>+${inv.credits}</span> : 'None'],
                  ['Discount', inv.discount],
                  ['Payment method', inv.paymentMethod],
                  ['Paid on', inv.paidOn],
                ].map(([l, v]) => (
                  <div key={l} style={{ display: 'flex', justifyContent: 'space-between', padding: '7px 0', borderBottom: '1px solid var(--border-soft)', fontSize: '13px' }}>
                    <span style={{ color: 'var(--text-muted)' }}>{l}</span>
                    <span style={{ color: 'var(--text-primary)' }}>{v}</span>
                  </div>
                ))}
              </div>
            )}
          </div>
        );
      })}
    </div>
  );
}

// Term recency rank, e.g. "Term 2, 2026" → 20262 (higher = more recent)
function termRank(t) {
  const m = /Term\s*(\d).*?(\d{4})/.exec(t || '');
  return m ? parseInt(m[2], 10) * 10 + parseInt(m[1], 10) : 0;
}

// Merge every child's invoices into one family list, newest first
function buildParentInvoices(children) {
  const all = [];
  children.forEach(c => buildInvoices(c).forEach(inv => all.push({ ...inv, childName: `${c.firstName} ${c.lastName}` })));
  return all.sort((a, b) => termRank(b.term) - termRank(a.term));
}

// ── Make-up class scheduler (mock UX) ─────────────────────────
// Books a one-off make-up in another class of the same course for a given
// week. In the real build this would add the student to that class's roll
// (tutor app) for that week only.
function MakeupSection({ student, homeClass }) {
  const [makeups, setMakeups] = useSC([]);   // [{ week, classId }]
  const [adding,  setAdding]  = useSC(false);
  const [week,    setWeek]    = useSC('Week 1');
  const [classId, setClassId] = useSC('');

  const WEEKS = Array.from({ length: 10 }, (_, i) => `Week ${i + 1}`);
  const eligible = SAMPLE_CLASSES.filter(c => c.course === student.course && c.id !== (homeClass && homeClass.id) && !c.archived);

  const add = () => {
    if (!classId) return;
    const c = SAMPLE_CLASSES.find(x => x.id === classId);
    setMakeups(m => [...m, { week, classId }]);
    setAdding(false); setClassId('');
    // Phase-2 hook: add the student to that class's roll for this week only.
    console.log(`[Make-up scheduled] ${student.firstName} ${student.lastName} → ${week} · ${c ? formatClass(c) : ''}`);
  };
  const remove = i => setMakeups(m => m.filter((_, idx) => idx !== i));

  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'center', gap: '7px', fontSize: '11px', fontWeight: 600, letterSpacing: '0.05em', textTransform: 'uppercase', color: 'var(--text-secondary)', marginBottom: '8px' }}>
        <Icon name="classes" size={13} color="var(--text-muted)" /> Make-up classes
      </div>

      {makeups.map((m, i) => {
        const c = SAMPLE_CLASSES.find(x => x.id === m.classId);
        return (
          <div key={i} style={{ display: 'flex', alignItems: 'center', gap: '10px', background: 'var(--bg-elevated)',
            border: '1px solid var(--border-default)', borderRadius: '6px', padding: '10px 12px', marginBottom: '8px' }}>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-primary)' }}>{m.week} · {c ? formatClass(c) : '—'}</div>
              <div style={{ fontSize: '11px', color: 'var(--text-muted)', marginTop: '2px' }}>
                {c ? `${c.campus} · ${c.course}` : ''} — on the roll for this week only
              </div>
            </div>
            <span style={{ fontSize: '10px', fontWeight: 600, color: '#4A9B7E', background: 'rgba(74,155,126,0.12)',
              border: '1px solid rgba(74,155,126,0.3)', borderRadius: '4px', padding: '2px 7px', flexShrink: 0 }}>On roll</span>
            <button onClick={() => remove(i)} title="Remove make-up"
              style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-muted)', fontSize: '16px', lineHeight: 1, flexShrink: 0, fontFamily: 'inherit' }}>×</button>
          </div>
        );
      })}

      {adding ? (
        <div style={{ background: 'var(--bg-surface-muted)', border: '1px solid var(--border-default)', borderRadius: '8px', padding: '12px', marginTop: '4px' }}>
          {eligible.length === 0 ? (
            <div style={{ fontSize: '12px', color: 'var(--text-muted)' }}>No other {student.course} classes to make up in.</div>
          ) : (
            <div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '10px' }}>
                <div>
                  <label style={{ fontSize: '11px', color: 'var(--text-muted)', display: 'block', marginBottom: '4px' }}>Week</label>
                  <FormSelect value={week} onChange={setWeek} options={WEEKS} buttonStyle={{ padding: '7px 10px' }} />
                </div>
                <div>
                  <label style={{ fontSize: '11px', color: 'var(--text-muted)', display: 'block', marginBottom: '4px' }}>Make-up class</label>
                  <FormSelect value={classId} onChange={setClassId} placeholder="Select a class…"
                    options={eligible.map(c => ({ value: c.id, label: `${formatClass(c)} · ${c.campus}` }))} buttonStyle={{ padding: '7px 10px' }} />
                </div>
              </div>
              <div style={{ display: 'flex', justifyContent: 'flex-end', gap: '8px' }}>
                <button onClick={() => { setAdding(false); setClassId(''); }}
                  style={{ background: 'none', border: '1px solid var(--border-default)', borderRadius: '6px', padding: '7px 14px', fontSize: '12px', color: 'var(--text-secondary)', cursor: 'pointer', fontFamily: 'inherit' }}>Cancel</button>
                <button onClick={add} disabled={!classId}
                  style={{ background: classId ? 'var(--brand)' : 'var(--bg-elevated)', border: 'none', borderRadius: '6px', padding: '7px 16px', fontSize: '12px', fontWeight: 600, color: classId ? '#fff' : 'var(--text-muted)', cursor: classId ? 'pointer' : 'default', fontFamily: 'inherit' }}>Schedule make-up</button>
              </div>
            </div>
          )}
        </div>
      ) : (
        <button onClick={() => setAdding(true)}
          style={{ display: 'inline-flex', alignItems: 'center', gap: '5px', background: 'var(--bg-surface-muted)',
            border: '1px solid var(--border-default)', borderRadius: '6px', padding: '7px 12px', fontSize: '12px', fontWeight: 500,
            color: 'var(--text-secondary)', cursor: 'pointer', fontFamily: 'inherit' }}>
          + Schedule a make-up
        </button>
      )}
    </div>
  );
}

// ── Customer profile drawer ───────────────────────────────────

// Billing panel for the student drawer's Invoices tab: credit balance + absence-credit
// claims (reception-approved, max one per term), assigned discounts, and this student's
// invoices. Mutates the shared student record / SAMPLE_CREDIT_CLAIMS in place.
function StudentBilling({ student }) {
  const [v, setV] = useSC(0);
  const force = () => setV(x => x + 1);
  const [chgOpen, setChgOpen] = useSC(false);
  const [chg, setChg] = useSC({ newCourse: '', remaining: '' });
  const [stmtOpen, setStmtOpen] = useSC(false);
  const cls = SAMPLE_CLASSES.find(c => c.id === student.classId);
  const term = (cls && cls.term) || defaultBillingTerm();
  const lessonCredit = Math.round(lessonHours(cls) * INVOICE_SETTINGS.hourlyRate);
  const claimedThisTerm = student.creditClaimedTerm === term;
  const invoices = SAMPLE_INVOICES.filter(i => invoiceCoversStudent(i, student.id));
  const pending = SAMPLE_CREDIT_CLAIMS.filter(c => c.studentId === student.id && c.status === 'pending');
  const addable = SAMPLE_DISCOUNT_TYPES.filter(t => !(student.discounts || []).some(d => d.typeId === t.id));

  const approve = c => { c.status = 'approved'; student.creditBalance = (student.creditBalance || 0) + c.amount; student.creditClaimedTerm = c.term; logActivity({ action: 'Approved absence credit', record: `${student.firstName} ${student.lastName}`, detail: fmtAUD(c.amount) }); force(); };
  const reject  = c => { c.status = 'rejected'; force(); };
  const addCredit = () => { if (claimedThisTerm) return; student.creditBalance = (student.creditBalance || 0) + lessonCredit; student.creditClaimedTerm = term; logActivity({ action: 'Added absence credit', record: `${student.firstName} ${student.lastName}`, detail: fmtAUD(lessonCredit) }); force(); };
  const clearCredit = () => { student.creditBalance = 0; student.creditClaimedTerm = null; force(); };
  const addDiscount = id => { const t = SAMPLE_DISCOUNT_TYPES.find(x => x.id === id); if (!t) return; student.discounts = [...(student.discounts || []), { id: `${student.id}-${t.id}-${Date.now()}`, typeId: t.id, name: t.name, kind: t.kind, value: t.value, scope: 'ongoing', used: false }]; force(); };
  const removeDiscount = id => { student.discounts = (student.discounts || []).filter(d => d.id !== id); force(); };
  const setScope = (d, sc) => { d.scope = sc; d.used = false; force(); };
  // #8 — course change (e.g. EXT1 → ADVN): if the new course is cheaper, credit the price
  // difference × remaining weeks to the balance (no refund). Uses per-course rates.
  const tw = currentTermWeek(INVOICE_TODAY);
  const remainingDefault = Math.max(0, termWeeks(term).length - (tw.inTerm ? tw.week : 1) + 1);
  // Use each course's DEFINED hours (COURSE_HOURS via expectedCourseHours) so the credit reflects
  // the course difference — not the seeded class length (which is a flat 2h in sample data).
  const oldHours = expectedCourseHours(student.course) || lessonHours(cls) || 2, oldRate = courseRate(student.course);
  const newHours = chg.newCourse ? (expectedCourseHours(chg.newCourse) || oldHours) : oldHours;
  const newRate = chg.newCourse ? courseRate(chg.newCourse) : oldRate;
  const remainingN = parseInt(chg.remaining, 10) || 0;
  const chgCredit = Math.max(0, Math.round((oldHours * oldRate - newHours * newRate) * remainingN * 100) / 100);
  const applyCourseChange = () => {
    if (!chg.newCourse || chg.newCourse === student.course) return;
    const fromCourse = student.course;
    if (chgCredit > 0) student.creditBalance = Math.round(((student.creditBalance || 0) + chgCredit) * 100) / 100;
    student.course = chg.newCourse;
    logActivity({ action: 'Changed course', record: `${student.firstName} ${student.lastName}`, detail: `${fromCourse} → ${chg.newCourse}${chgCredit > 0 ? ` · credit ${fmtAUD(chgCredit)} (${remainingN} wks)` : ''}` });
    setChgOpen(false); force();
  };
  const parent = SAMPLE_PARENTS.find(p => p.id === student.parentId);
  const familyInvoices = SAMPLE_INVOICES.filter(i => i.billToParentId === student.parentId && !i.void);
  const miniLbl = { fontSize: '10px', color: 'var(--text-muted)', display: 'block', marginBottom: '3px' };
  const miniInp = { width: '100%', boxSizing: 'border-box', background: 'var(--bg-surface)', border: '1px solid var(--border-default)', borderRadius: '4px', padding: '6px 8px', fontSize: '12px', color: 'var(--text-primary)', fontFamily: 'inherit', outline: 'none' };

  const card = { border: '1px solid var(--border-soft)', borderRadius: '10px', background: 'var(--bg-surface-muted)', padding: '14px', marginBottom: '14px' };
  const head = { fontSize: '11px', fontWeight: 700, letterSpacing: '0.05em', textTransform: 'uppercase', color: 'var(--text-muted)', marginBottom: '10px' };
  const segS = on => ({ padding: '3px 9px', fontSize: '11px', fontWeight: 600, fontFamily: 'inherit', cursor: 'pointer', borderRadius: '5px', background: on ? 'rgba(31,77,61,0.15)' : 'transparent', color: on ? '#4A9B7E' : 'var(--text-muted)', border: `1px solid ${on ? 'rgba(31,77,61,0.4)' : 'var(--border-default)'}` });

  return (
    <div style={{ paddingTop: '6px' }}>
      {/* Credit balance */}
      <div style={card}>
        <div style={head}>Credit balance</div>
        <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', gap: '12px' }}>
          <span style={{ fontSize: '24px', fontWeight: 700, color: student.creditBalance > 0 ? '#4A9B7E' : 'var(--text-muted)', fontVariantNumeric: 'tabular-nums' }}>{fmtAUD(student.creditBalance || 0)}</span>
          <div style={{ display: 'flex', gap: '8px', flexWrap: 'wrap', justifyContent: 'flex-end' }}>
            <Button size="sm" variant="secondary" disabled={claimedThisTerm} onClick={addCredit} title={claimedThisTerm ? 'Already one credit this term' : ''}>+ Absence credit ({fmtAUD(lessonCredit)})</Button>
            <Button size="sm" variant="ghost" onClick={() => { setChg({ newCourse: '', remaining: String(remainingDefault) }); setChgOpen(o => !o); }}>Change course</Button>
            {student.creditBalance > 0 && <Button size="sm" variant="ghost" onClick={clearCredit}>Clear</Button>}
          </div>
        </div>
        <div style={{ fontSize: '11px', color: 'var(--text-muted)', marginTop: '6px' }}>Auto-applies to the next invoice. Max one absence credit per term{claimedThisTerm ? ` · already claimed for ${term}` : ''}. Credits don’t expire and carry across terms.</div>
        {chgOpen && (
          <div style={{ marginTop: '10px', borderTop: '1px solid var(--border-soft)', paddingTop: '10px' }}>
            <div style={{ fontSize: '11px', color: 'var(--text-muted)', marginBottom: '8px' }}>Move {student.firstName} to a different course (e.g. EXT1 → ADVN). If the new course is cheaper, the difference for the remaining weeks is credited to their balance — no refund.</div>
            <div style={{ display: 'grid', gridTemplateColumns: '1.3fr 0.9fr auto', gap: '8px', alignItems: 'end' }}>
              <div><label style={miniLbl}>New course</label><FormSelect value={chg.newCourse} onChange={val => setChg({ ...chg, newCourse: val })} placeholder="Select…" options={INVOICE_COURSES.filter(c => c !== student.course).map(c => ({ value: c, label: c }))} buttonStyle={{ padding: '6px 8px' }} /></div>
              <div><label style={miniLbl}>Remaining weeks</label><input type="number" value={chg.remaining} onChange={e => setChg({ ...chg, remaining: e.target.value })} style={miniInp} /></div>
              <Button size="sm" variant="primary" disabled={!chg.newCourse} onClick={applyCourseChange}>Change{chgCredit > 0 ? ` · +${fmtAUD(chgCredit)}` : ''}</Button>
            </div>
            {chg.newCourse && <div style={{ fontSize: '11px', color: 'var(--text-muted)', marginTop: '8px' }}>{student.course} ({oldHours}h × {fmtAUD(oldRate)}) → {chg.newCourse} ({newHours}h × {fmtAUD(newRate)}) over {remainingN} week{remainingN === 1 ? '' : 's'} → credit <strong style={{ color: chgCredit > 0 ? '#4A9B7E' : 'var(--text-muted)' }}>{fmtAUD(chgCredit)}</strong></div>}
          </div>
        )}
        {pending.length > 0 && (
          <div style={{ marginTop: '12px', borderTop: '1px solid var(--border-soft)', paddingTop: '10px' }}>
            <div style={{ fontSize: '11px', fontWeight: 600, color: '#B8922A', marginBottom: '6px' }}>⚑ Pending claim{pending.length === 1 ? '' : 's'}</div>
            {pending.map(c => { const attended = studentAttendedWeek(c.studentId, c.term, c.week); return (
              <div key={c.id} style={{ background: 'var(--bg-surface)', border: `1px solid ${attended === true ? 'rgba(192,86,86,0.45)' : 'var(--border-soft)'}`, borderRadius: '6px', padding: '8px 10px', marginBottom: '6px' }}>
                <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: '12px', color: 'var(--text-primary)' }}>{c.term} · Week {c.week} · {fmtAUD(c.amount)}</div>
                    <div style={{ fontSize: '11px', color: 'var(--text-muted)' }}>{c.reason}</div>
                  </div>
                  <Button size="sm" variant="primary" onClick={() => approve(c)}>Approve</Button>
                  <Button size="sm" variant="ghost" onClick={() => reject(c)}>Reject</Button>
                </div>
                {attended === true && <div style={{ marginTop: '7px', fontSize: '11px', color: '#C05656', fontWeight: 500, lineHeight: 1.4 }}>⚠ Attendance shows {student.firstName} was marked present that week — verify before approving.</div>}
              </div>
            ); })}
          </div>
        )}
      </div>

      {/* Assigned discounts */}
      <div style={card}>
        <div style={head}>Assigned discounts</div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: '6px' }}>
          {(student.discounts || []).length === 0 && <div style={{ fontSize: '12px', color: 'var(--text-muted)' }}>No discounts assigned.</div>}
          {(student.discounts || []).map(d => (
            <div key={d.id} style={{ display: 'flex', alignItems: 'center', gap: '8px', background: 'var(--bg-surface)', border: '1px solid var(--border-soft)', borderRadius: '6px', padding: '7px 8px 7px 10px' }}>
              <span style={{ fontSize: '12px', color: 'var(--text-primary)', flex: 1, minWidth: 0 }}>{d.name} <span style={{ color: 'var(--text-muted)' }}>· {d.kind === 'percent' ? `${d.value}%` : fmtAUD(d.value)}</span>{d.scope === 'once' && d.used ? <span style={{ marginLeft: 6, fontSize: '10px', color: 'var(--text-muted)' }}>used</span> : null}</span>
              <div style={{ display: 'flex', gap: '4px' }}>
                <button onClick={() => setScope(d, 'ongoing')} style={segS(d.scope === 'ongoing')}>Ongoing</button>
                <button onClick={() => setScope(d, 'once')} style={segS(d.scope === 'once')}>Once-off</button>
              </div>
              <button onClick={() => removeDiscount(d.id)} title="Remove" style={{ background: 'none', border: 'none', cursor: 'pointer', color: 'var(--text-muted)', fontSize: '15px', lineHeight: 1, fontFamily: 'inherit' }}>×</button>
            </div>
          ))}
        </div>
        {addable.length > 0 && (
          <div style={{ marginTop: '8px' }}>
            <TMSelect value="" placeholder="+ Assign a discount…" onChange={addDiscount}
              options={addable.map(t => ({ value: t.id, label: `${t.name} (${t.kind === 'percent' ? t.value + '%' : '$' + t.value})` }))} />
          </div>
        )}
      </div>

      {/* This student's invoices */}
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '10px' }}>
        <span style={{ ...head, marginBottom: 0 }}>Invoices</span>
        {familyInvoices.length > 0 && <Button size="sm" variant="ghost" onClick={() => setStmtOpen(true)}>Statement of account</Button>}
      </div>
      {invoices.length > 0 ? (
        <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
          {invoices.map(inv => (
            <div key={inv.id} style={{ display: 'flex', alignItems: 'center', gap: '10px', padding: '11px 12px', background: 'var(--bg-surface-muted)', border: '1px solid var(--border-soft)', borderRadius: '8px' }}>
              <span style={{ fontSize: '12px', fontFamily: 'ui-monospace, monospace', color: 'var(--text-secondary)' }}>{inv.number}</span>
              <span style={{ fontSize: '12px', color: 'var(--text-muted)', flex: 1, minWidth: 0, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{inv.term} · {invoiceWeeksLabel(inv)}{(inv.studentIds || []).length > 1 ? ' · family' : ''}</span>
              <span style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-primary)', fontVariantNumeric: 'tabular-nums' }}>{fmtAUD(inv.totalInc)}</span>
              <InvoiceStatusPill inv={inv} />
            </div>
          ))}
        </div>
      ) : (
        <div>
          <div style={{ fontSize: '12px', color: 'var(--text-muted)', marginBottom: '8px' }}>No invoices created in-app yet — past history below.</div>
          <InvoiceAccordion key={student.id} invoices={buildInvoices(student)} />
        </div>
      )}

      {/* F10 — Statement of account (whole family) */}
      {stmtOpen && (() => {
        const billed = Math.round(familyInvoices.reduce((a, i) => a + i.totalInc, 0) * 100) / 100;
        const paid = Math.round(familyInvoices.reduce((a, i) => a + amountPaid(i), 0) * 100) / 100;
        const bal = Math.round((billed - paid) * 100) / 100;
        const tot = { display: 'flex', justifyContent: 'space-between', padding: '6px 0', fontSize: '13px' };
        return (
          <Modal open={true} onClose={() => setStmtOpen(false)} title={`Statement — ${parent ? parent.firstName + ' ' + parent.lastName : 'family'}`} width={620}>
            <div style={{ border: '1px solid var(--border-soft)', borderRadius: '8px', overflow: 'hidden', marginBottom: '14px' }}>
              <div style={{ display: 'grid', gridTemplateColumns: '120px 1fr 90px 90px 90px', background: 'var(--bg-surface-muted)', padding: '8px 12px', fontSize: '10px', fontWeight: 700, letterSpacing: '0.05em', textTransform: 'uppercase', color: 'var(--text-muted)' }}>
                <span>Invoice</span><span>Detail</span><span style={{ textAlign: 'right' }}>Total</span><span style={{ textAlign: 'right' }}>Paid</span><span style={{ textAlign: 'right' }}>Balance</span>
              </div>
              {familyInvoices.map((i, k) => (
                <div key={i.id} style={{ display: 'grid', gridTemplateColumns: '120px 1fr 90px 90px 90px', padding: '9px 12px', alignItems: 'center', borderTop: k ? '1px solid var(--border-soft)' : 'none', fontSize: '12px' }}>
                  <span style={{ fontFamily: 'ui-monospace, monospace', color: 'var(--text-secondary)' }}>{i.number}</span>
                  <span style={{ color: 'var(--text-muted)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{fmtAUDate(i.issueDate)} · {(i.studentIds || []).map(id => { const x = SAMPLE_STUDENTS.find(y => y.id === id); return x ? x.firstName : ''; }).join(', ')}</span>
                  <span style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums' }}>{fmtAUD(i.totalInc)}</span>
                  <span style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums', color: '#4A9B7E' }}>{fmtAUD(amountPaid(i))}</span>
                  <span style={{ textAlign: 'right', fontVariantNumeric: 'tabular-nums', fontWeight: 600 }}>{fmtAUD(invoiceBalance(i))}</span>
                </div>
              ))}
              {familyInvoices.length === 0 && <div style={{ padding: '20px', textAlign: 'center', fontSize: '12px', color: 'var(--text-muted)' }}>No invoices.</div>}
            </div>
            <div style={{ maxWidth: '260px', marginLeft: 'auto' }}>
              <div style={tot}><span style={{ color: 'var(--text-muted)' }}>Total billed</span><span style={{ fontVariantNumeric: 'tabular-nums' }}>{fmtAUD(billed)}</span></div>
              <div style={tot}><span style={{ color: 'var(--text-muted)' }}>Total paid</span><span style={{ color: '#4A9B7E', fontVariantNumeric: 'tabular-nums' }}>{fmtAUD(paid)}</span></div>
              <div style={{ ...tot, borderTop: '1px solid var(--border-soft)', fontWeight: 700, color: bal > 0 ? '#C05656' : 'var(--text-primary)' }}><span>Balance owing</span><span style={{ fontVariantNumeric: 'tabular-nums' }}>{fmtAUD(bal)}</span></div>
            </div>
          </Modal>
        );
      })()}
    </div>
  );
}

function CustomerDrawer({ customer, open, onClose, onUpdate, onOpenParent }) {
  const [tab,          setTab]          = useSC('Details');
  const [draftDetails, setDraftDetails] = useSC({});
  const [isDirty,      setIsDirty]      = useSC(false);
  const [localComms,   setLocalComms]   = useSC([]);
  const [addingNote,   setAddingNote]   = useSC(false);
  const [noteText,     setNoteText]     = useSC('');
  const noteRef = useSCRef(null);

  React.useEffect(() => { if (open) setTab('Details'); }, [open]);

  // Reset all local state whenever a different customer is opened
  React.useEffect(() => {
    if (!customer) return;
    setDraftDetails({
      firstName: customer.firstName  || '',
      lastName:  customer.lastName   || '',
      mobile:    customer.mobile     || '',
      email:     customer.email      || '',
      school:    customer.school     || '',
      campus:    customer.campus !== '—' ? (customer.campus || '') : '',
      course:    customer.course !== '—' ? (customer.course || '') : '',
      status:    customer.status     || '',
      trialDate: customer.trialDate  || '',
    });
    setIsDirty(false);
    setLocalComms(COMMS[customer.id] ? [...COMMS[customer.id]] : [...DEFAULT_COMMS]);
    setAddingNote(false);
    setNoteText('');
  }, [customer && customer.id]);

  useSCEffect(() => {
    if (addingNote && noteRef.current) noteRef.current.focus();
  }, [addingNote]);

  if (!customer) return <Drawer open={false} onClose={onClose} title="" />;

  const parent   = SAMPLE_PARENTS.find(p => p.id === customer.parentId);
  const cls      = SAMPLE_CLASSES.find(c => c.id === customer.classId);
  const tutor    = cls ? SAMPLE_STAFF.find(s => s.id === cls.tutorId) : null;
  const siblings = SAMPLE_STUDENTS.filter(s => s.parentId === customer.parentId && s.id !== customer.id);
  const invoice  = SAMPLE_INVOICES.find(i => i.studentId === customer.id);

  const handleFieldChange = (field, value) => {
    setDraftDetails(prev => ({ ...prev, [field]: value }));
    setIsDirty(true);
  };

  const handleSave = () => {
    if (onUpdate) onUpdate(draftDetails);
    setIsDirty(false);
  };

  const handleDiscard = () => {
    setDraftDetails({
      firstName: customer.firstName  || '',
      lastName:  customer.lastName   || '',
      mobile:    customer.mobile     || '',
      email:     customer.email      || '',
      school:    customer.school     || '',
      campus:    customer.campus !== '—' ? (customer.campus || '') : '',
      course:    customer.course !== '—' ? (customer.course || '') : '',
      status:    customer.status     || '',
      trialDate: customer.trialDate  || '',
    });
    setIsDirty(false);
  };

  const handleAddNote = () => {
    if (!noteText.trim()) return;
    const today = new Date().toLocaleDateString('en-AU', { day: '2-digit', month: '2-digit', year: 'numeric' });
    setLocalComms(prev => [{ date: today, user: 'You', action: 'Note added', notes: noteText.trim() }, ...prev]);
    setNoteText('');
    setAddingNote(false);
  };

  return (
    <Drawer open={open} onClose={onClose}
      title={`${customer.firstName} ${customer.lastName}`}
      subtitle={`${customerCode(customer)} · ${customer.campus} · ${customer.course}`}
      width={520}>

      {/* Status row */}
      <div style={{ display: 'flex', alignItems: 'center', gap: '10px', padding: '14px 0', borderBottom: '1px solid var(--border-soft)' }}>
        <Avatar name={`${customer.firstName} ${customer.lastName}`} size={44} />
        <div>
          <div style={{ fontSize: '16px', fontWeight: 600, color: 'var(--text-primary)' }}>
            {customer.firstName} {customer.lastName}
          </div>
          <div style={{ marginTop: '4px', display: 'flex', gap: '6px', alignItems: 'center' }}>
            <Badge status={customer.status} label={customer.status} />
            <span style={{ fontSize: '12px', color: 'var(--text-muted)' }}>{customer.school}</span>
          </div>
        </div>
      </div>

      <Tabs tabs={['Details', 'Family', 'Class', 'Comms log', 'Invoices']} active={tab} onChange={setTab} />

      {/* ── Details ── */}
      {tab === 'Details' && (
        <div style={{ display: 'flex', flexDirection: 'column' }}>
          <DrawerEditField label="First name" value={draftDetails.firstName} onChange={v => handleFieldChange('firstName', v)} />
          <DrawerEditField label="Last name"  value={draftDetails.lastName}  onChange={v => handleFieldChange('lastName',  v)} />
          <DrawerEditField label="Mobile"     value={draftDetails.mobile}    onChange={v => handleFieldChange('mobile',    v)} />
          <DrawerEditField label="Email"      value={draftDetails.email}     onChange={v => handleFieldChange('email',     v)} />
          <DrawerEditField label="School"     value={draftDetails.school}    onChange={v => handleFieldChange('school',    v)} />
          <DrawerEditField label="Campus"     value={draftDetails.campus}    onChange={v => handleFieldChange('campus',    v)} options={CAMPUS_OPTIONS} />
          <DrawerEditField label="Course"     value={draftDetails.course}    onChange={v => handleFieldChange('course',    v)} options={COURSE_OPTIONS} />
          <DrawerEditField label="Status"     value={draftDetails.status}    onChange={v => handleFieldChange('status',    v)} options={STUDENT_STATUS_OPTIONS} />
          <DrawerEditField label="Trial date" value={draftDetails.trialDate} onChange={v => handleFieldChange('trialDate', v)} type="date" />

          {isDirty && (
            <div style={{
              position: 'sticky', bottom: 0,
              marginTop: '12px', padding: '14px 0 4px',
              background: 'linear-gradient(to bottom, transparent 0%, var(--bg-surface) 32%)',
              display: 'flex', justifyContent: 'flex-end', gap: '8px',
            }}>
              <button
                onClick={handleDiscard}
                style={{
                  background: 'none', border: '1px solid var(--border-default)',
                  borderRadius: '6px', padding: '8px 16px', fontSize: '13px',
                  color: 'var(--text-secondary)', cursor: 'pointer', fontFamily: 'inherit',
                  transition: '100ms ease',
                }}
                onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover)'}
                onMouseLeave={e => e.currentTarget.style.background = 'none'}
              >Discard</button>
              <button
                onClick={handleSave}
                style={{
                  background: 'var(--brand)', border: 'none', borderRadius: '6px',
                  padding: '8px 20px', fontSize: '13px', fontWeight: 600,
                  color: '#fff', cursor: 'pointer', fontFamily: 'inherit',
                  boxShadow: '0 2px 8px rgba(31,77,61,0.4)', transition: '120ms ease',
                }}
                onMouseEnter={e => e.currentTarget.style.background = 'var(--brand-hover)'}
                onMouseLeave={e => e.currentTarget.style.background = 'var(--brand)'}
              >Save changes</button>
            </div>
          )}
        </div>
      )}

      {/* ── Family ── */}
      {tab === 'Family' && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: '12px' }}>
          {parent ? (
            <div
              onClick={() => onOpenParent && onOpenParent(parent.id)}
              title={onOpenParent ? 'Open parent profile' : undefined}
              style={{ background: 'var(--bg-surface-muted)', borderRadius: '8px', padding: '14px',
                cursor: onOpenParent ? 'pointer' : 'default', transition: '100ms ease', border: '1px solid transparent' }}
              onMouseEnter={e => { if (onOpenParent) { e.currentTarget.style.background = 'var(--bg-hover)'; e.currentTarget.style.borderColor = 'var(--border-default)'; } }}
              onMouseLeave={e => { e.currentTarget.style.background = 'var(--bg-surface-muted)'; e.currentTarget.style.borderColor = 'transparent'; }}
            >
              <div style={{ display: 'flex', alignItems: 'center', gap: '10px', marginBottom: '10px' }}>
                <Avatar name={`${parent.firstName} ${parent.lastName}`} size={36} />
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontSize: '14px', fontWeight: 600, color: 'var(--text-primary)' }}>{parent.firstName} {parent.lastName}</div>
                  <div style={{ fontSize: '12px', color: 'var(--text-muted)' }}>Primary contact</div>
                </div>
                {onOpenParent && (
                  <span style={{ display: 'inline-flex', alignItems: 'center', gap: '3px', fontSize: '11px', color: '#4A9B7E', flexShrink: 0 }}>
                    View profile
                    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M9 18l6-6-6-6" /></svg>
                  </span>
                )}
              </div>
              {[['Mobile', parent.mobile], ['Email', parent.email]].map(([l, v]) => (
                <div key={l} style={{ fontSize: '13px', color: 'var(--text-secondary)', marginBottom: '4px' }}>
                  <span style={{ color: 'var(--text-muted)' }}>{l}: </span>{v}
                </div>
              ))}
            </div>
          ) : <EmptyState message="No parent linked" />}

          {siblings.length > 0 && (
            <div>
              <div style={{ fontSize: '11px', fontWeight: 500, color: 'var(--text-muted)', textTransform: 'uppercase', letterSpacing: '0.05em', marginBottom: '8px' }}>Siblings</div>
              {siblings.map(sib => (
                <div key={sib.id} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between',
                  padding: '8px 12px', background: 'var(--bg-surface-muted)', borderRadius: '6px', marginBottom: '6px' }}>
                  <div style={{ display: 'flex', alignItems: 'center', gap: '8px' }}>
                    <Avatar name={`${sib.firstName} ${sib.lastName}`} size={24} />
                    <span style={{ fontSize: '13px', color: 'var(--text-primary)' }}>{sib.firstName} {sib.lastName}</span>
                  </div>
                  <Badge status={sib.status} label={sib.status} />
                </div>
              ))}
            </div>
          )}
        </div>
      )}

      {/* ── Class ── */}
      {tab === 'Class' && (
        cls ? (
          <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
          <div style={{ background: 'var(--bg-surface-muted)', borderRadius: '8px', padding: '16px' }}>
            <div style={{ fontSize: '14px', fontWeight: 600, color: 'var(--text-primary)', marginBottom: '12px' }}>{classLabel(cls)}</div>
            {[
              ['Campus', cls.campus],
              ['Day & time', `${cls.day} ${cls.startTime}–${cls.endTime}`],
              ['Term', cls.term],
              ['Tutor', tutor ? `${tutor.firstName} ${tutor.lastName}` : '—'],
              ['Students enrolled', cls.studentCount],
            ].map(([l, v]) => (
              <div key={l} style={{ display: 'flex', justifyContent: 'space-between', padding: '7px 0', borderBottom: '1px solid var(--border-soft)', fontSize: '13px' }}>
                <span style={{ color: 'var(--text-muted)' }}>{l}</span>
                <span style={{ color: 'var(--text-primary)', fontWeight: 500 }}>{v}</span>
              </div>
            ))}
          </div>

          {/* ── Attendance (this term) — for parent conversations ── */}
          {customer.status === 'enrolled' && (() => {
            const held = classWeeks().filter(w => w.past);
            if (!held.length) return null;
            const present = held.filter(w => attendanceOf(cls.id, w.week, customer.id) === 'present').length;
            const rate = Math.round((present / held.length) * 100);
            return (
              <div style={{ background: 'var(--bg-surface-muted)', borderRadius: '8px', padding: '16px' }}>
                <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: '10px' }}>
                  <span style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-primary)' }}>Attendance <span style={{ fontWeight: 400, color: 'var(--text-muted)' }}>· this term</span></span>
                  <span style={{ fontSize: '12px', fontWeight: 600, fontVariantNumeric: 'tabular-nums',
                    color: rate >= 90 ? '#4A9B7E' : rate >= 75 ? '#B8922A' : '#B44040' }}>{present}/{held.length} · {rate}%</span>
                </div>
                <div style={{ display: 'flex', flexDirection: 'column' }}>
                  {held.map(w => {
                    const absent = attendanceOf(cls.id, w.week, customer.id) === 'absent';
                    return (
                      <div key={w.week} style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '6px 0', borderBottom: '1px solid var(--border-soft)', fontSize: '12px' }}>
                        <span style={{ color: 'var(--text-secondary)' }}>Week {w.week} · {w.label}</span>
                        <span style={{ fontSize: '11px', fontWeight: 600, padding: '2px 8px', borderRadius: '4px', whiteSpace: 'nowrap',
                          background: absent ? 'rgba(180,60,60,0.12)' : 'rgba(74,155,126,0.12)', color: absent ? '#B44040' : '#4A9B7E',
                          border: `1px solid ${absent ? 'rgba(180,60,60,0.3)' : 'rgba(74,155,126,0.3)'}` }}>{absent ? 'Absent' : 'Present'}</span>
                      </div>
                    );
                  })}
                </div>
              </div>
            );
          })()}

          <MakeupSection key={customer.id} student={customer} homeClass={cls} />
          </div>
        ) : <EmptyState message="Not yet assigned to a class" />
      )}

      {/* ── Comms log ── */}
      {tab === 'Comms log' && (
        <div>
          {addingNote ? (
            <div style={{
              marginBottom: '16px', background: 'var(--bg-surface-muted)',
              border: '1px solid var(--border-default)', borderRadius: '8px', padding: '12px',
            }}>
              <textarea
                ref={noteRef}
                value={noteText}
                onChange={e => setNoteText(e.target.value)}
                placeholder="Write a note…"
                rows={3}
                style={{
                  width: '100%', background: 'transparent', border: 'none', outline: 'none',
                  resize: 'none', fontFamily: 'inherit', fontSize: '13px',
                  color: 'var(--text-primary)', lineHeight: 1.55,
                }}
                onKeyDown={e => {
                  if (e.key === 'Escape') { setAddingNote(false); setNoteText(''); }
                  if (e.key === 'Enter' && (e.metaKey || e.ctrlKey)) handleAddNote();
                }}
              />
              <div style={{ display: 'flex', gap: '8px', justifyContent: 'space-between', alignItems: 'center',
                marginTop: '8px', borderTop: '1px solid var(--border-soft)', paddingTop: '8px' }}>
                <span style={{ fontSize: '11px', color: 'var(--text-muted)' }}>⌘↵ to save</span>
                <div style={{ display: 'flex', gap: '8px' }}>
                  <button
                    onClick={() => { setAddingNote(false); setNoteText(''); }}
                    style={{
                      background: 'none', border: '1px solid var(--border-default)', borderRadius: '5px',
                      padding: '6px 14px', fontSize: '12px', color: 'var(--text-secondary)',
                      cursor: 'pointer', fontFamily: 'inherit',
                    }}
                  >Cancel</button>
                  <button
                    onClick={handleAddNote}
                    disabled={!noteText.trim()}
                    style={{
                      background: noteText.trim() ? 'var(--brand)' : 'var(--bg-elevated)',
                      border: 'none', borderRadius: '5px', padding: '6px 14px',
                      fontSize: '12px', fontWeight: 600,
                      color: noteText.trim() ? '#fff' : 'var(--text-muted)',
                      cursor: noteText.trim() ? 'pointer' : 'default',
                      fontFamily: 'inherit', transition: '100ms ease',
                    }}
                  >Add note</button>
                </div>
              </div>
            </div>
          ) : (
            <div style={{ marginBottom: '16px' }}>
              <button
                onClick={() => setAddingNote(true)}
                style={{
                  display: 'inline-flex', alignItems: 'center', gap: '5px',
                  background: 'var(--bg-surface-muted)', border: '1px solid var(--border-default)',
                  borderRadius: '5px', padding: '6px 12px', fontSize: '12px', fontWeight: 500,
                  color: 'var(--text-secondary)', cursor: 'pointer', fontFamily: 'inherit',
                  transition: '100ms ease',
                }}
                onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover)'}
                onMouseLeave={e => e.currentTarget.style.background = 'var(--bg-surface-muted)'}
              >+ Add note</button>
            </div>
          )}

          <div style={{ display: 'flex', flexDirection: 'column', gap: '0' }}>
            {localComms.map((entry, i) => (
              <div key={i} style={{ display: 'flex', gap: '12px', paddingBottom: '16px' }}>
                <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', gap: '4px', flexShrink: 0 }}>
                  <div style={{ width: 28, height: 28, borderRadius: '50%', background: 'var(--bg-surface-muted)',
                    border: '1px solid var(--border-default)', display: 'flex', alignItems: 'center', justifyContent: 'center',
                    fontSize: '10px', fontWeight: 600, color: 'var(--text-muted)' }}>
                    {entry.user.split(' ').map(w => w[0]).join('').slice(0,2)}
                  </div>
                  {i < localComms.length - 1 && <div style={{ width: 1, flex: 1, background: 'var(--border-soft)', minHeight: '12px' }} />}
                </div>
                <div style={{ flex: 1, paddingTop: '4px' }}>
                  <div style={{ display: 'flex', gap: '8px', alignItems: 'baseline', marginBottom: '4px', flexWrap: 'wrap' }}>
                    <span style={{ fontSize: '12px', fontWeight: 500, color: 'var(--text-primary)' }}>{entry.action}</span>
                    <span style={{ fontSize: '11px', color: 'var(--text-muted)' }}>{entry.user} · {entry.date}</span>
                  </div>
                  <p style={{ margin: 0, fontSize: '12px', color: 'var(--text-secondary)', lineHeight: 1.55 }}>{entry.notes}</p>
                </div>
              </div>
            ))}
          </div>
        </div>
      )}

      {/* ── Invoices + billing ── */}
      {tab === 'Invoices' && <StudentBilling key={customer.id} student={customer} />}
    </Drawer>
  );
}

// ── Parent profile drawer ─────────────────────────────────────

function ParentDrawer({ parent, open, onClose, onUpdate, allRows, onOpenStudent }) {
  const [tab,          setTab]          = useSC('Details');
  const [draftDetails, setDraftDetails] = useSC({});
  const [isDirty,      setIsDirty]      = useSC(false);

  React.useEffect(() => { if (open) setTab('Details'); }, [open]);

  React.useEffect(() => {
    if (!parent) return;
    setDraftDetails({
      firstName: parent.firstName || '',
      lastName:  parent.lastName  || '',
      mobile:    parent.mobile    || '',
      email:     parent.email     || '',
    });
    setIsDirty(false);
  }, [parent && parent.id]);

  if (!parent) return <Drawer open={false} onClose={onClose} title="" />;

  const children = (allRows || []).filter(r => r._type === 'Student' && r.parentId === parent.id);

  const handleFieldChange = (field, value) => {
    setDraftDetails(prev => ({ ...prev, [field]: value }));
    setIsDirty(true);
  };

  const handleSave = () => { if (onUpdate) onUpdate(draftDetails); setIsDirty(false); };
  const handleDiscard = () => {
    setDraftDetails({
      firstName: parent.firstName || '',
      lastName:  parent.lastName  || '',
      mobile:    parent.mobile    || '',
      email:     parent.email     || '',
    });
    setIsDirty(false);
  };

  return (
    <Drawer open={open} onClose={onClose}
      title={`${parent.firstName} ${parent.lastName}`}
      subtitle={`${customerCode(parent)} · Parent`}
      width={520}>

      {/* Header row */}
      <div style={{ display: 'flex', alignItems: 'center', gap: '10px', padding: '14px 0', borderBottom: '1px solid var(--border-soft)' }}>
        <Avatar name={`${parent.firstName} ${parent.lastName}`} size={44} />
        <div>
          <div style={{ fontSize: '16px', fontWeight: 600, color: 'var(--text-primary)' }}>
            {parent.firstName} {parent.lastName}
          </div>
          <div style={{ marginTop: '4px' }}>
            <Badge status="parent" label="Parent" />
          </div>
        </div>
      </div>

      <Tabs tabs={['Details', 'Family', 'Invoices']} active={tab} onChange={setTab} />

      {/* ── Details ── */}
      {tab === 'Details' && (
        <div style={{ display: 'flex', flexDirection: 'column' }}>
          <DrawerEditField label="First name" value={draftDetails.firstName} onChange={v => handleFieldChange('firstName', v)} />
          <DrawerEditField label="Last name"  value={draftDetails.lastName}  onChange={v => handleFieldChange('lastName',  v)} />
          <DrawerEditField label="Mobile"     value={draftDetails.mobile}    onChange={v => handleFieldChange('mobile',    v)} />
          <DrawerEditField label="Email"      value={draftDetails.email}     onChange={v => handleFieldChange('email',     v)} />

          {isDirty && (
            <div style={{
              position: 'sticky', bottom: 0, marginTop: '12px', padding: '14px 0 4px',
              background: 'linear-gradient(to bottom, transparent 0%, var(--bg-surface) 32%)',
              display: 'flex', justifyContent: 'flex-end', gap: '8px',
            }}>
              <button onClick={handleDiscard}
                style={{
                  background: 'none', border: '1px solid var(--border-default)',
                  borderRadius: '6px', padding: '8px 16px', fontSize: '13px',
                  color: 'var(--text-secondary)', cursor: 'pointer', fontFamily: 'inherit', transition: '100ms ease',
                }}
                onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover)'}
                onMouseLeave={e => e.currentTarget.style.background = 'none'}
              >Discard</button>
              <button onClick={handleSave}
                style={{
                  background: 'var(--brand)', border: 'none', borderRadius: '6px',
                  padding: '8px 20px', fontSize: '13px', fontWeight: 600,
                  color: '#fff', cursor: 'pointer', fontFamily: 'inherit',
                  boxShadow: '0 2px 8px rgba(31,77,61,0.4)', transition: '120ms ease',
                }}
                onMouseEnter={e => e.currentTarget.style.background = 'var(--brand-hover)'}
                onMouseLeave={e => e.currentTarget.style.background = 'var(--brand)'}
              >Save changes</button>
            </div>
          )}
        </div>
      )}

      {/* ── Family ── */}
      {tab === 'Family' && (
        <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
          {children.length === 0 ? (
            <EmptyState message="No children linked to this parent" />
          ) : children.map(child => {
            const cls   = SAMPLE_CLASSES.find(c => c.id === child.classId);
            const tutor = cls ? SAMPLE_STAFF.find(s => s.id === cls.tutorId) : null;
            return (
              <div key={child.id}
                onClick={() => onOpenStudent && onOpenStudent(child.id)}
                title={onOpenStudent ? 'Open student profile' : undefined}
                style={{ background: 'var(--bg-surface-muted)', borderRadius: '8px', padding: '14px',
                  cursor: onOpenStudent ? 'pointer' : 'default', transition: '100ms ease', border: '1px solid transparent' }}
                onMouseEnter={e => { if (onOpenStudent) e.currentTarget.style.borderColor = 'var(--border-default)'; }}
                onMouseLeave={e => { e.currentTarget.style.borderColor = 'transparent'; }}
              >
                {/* Child header */}
                <div style={{ display: 'flex', alignItems: 'center', gap: '10px', marginBottom: '12px' }}>
                  <Avatar name={`${child.firstName} ${child.lastName}`} size={36} />
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{ fontSize: '14px', fontWeight: 600, color: 'var(--text-primary)' }}>
                      {child.firstName} {child.lastName}
                    </div>
                    <div style={{ marginTop: '4px', display: 'flex', gap: '6px', alignItems: 'center' }}>
                      <Badge status={child.status} label={child.status} />
                      <span style={{ fontSize: '12px', color: 'var(--text-muted)' }}>{child.course}</span>
                    </div>
                  </div>
                  {onOpenStudent && (
                    <span style={{ display: 'inline-flex', alignItems: 'center', gap: '3px', fontSize: '11px', color: '#4A9B7E', flexShrink: 0 }}>
                      View profile
                      <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round"><path d="M9 18l6-6-6-6" /></svg>
                    </span>
                  )}
                </div>

                {/* Contact details — own labelled, lighter panel */}
                <div style={{ display: 'flex', alignItems: 'center', gap: '7px', fontSize: '11px', fontWeight: 600, letterSpacing: '0.05em', textTransform: 'uppercase', color: 'var(--text-secondary)', marginBottom: '6px' }}>
                  <Icon name="customers" size={13} color="var(--text-muted)" /> Contact
                </div>
                <div style={{ background: 'var(--bg-elevated)', border: '1px solid var(--border-default)', borderRadius: '6px', padding: '2px 12px', marginBottom: '16px' }}>
                  {[
                    ['Mobile', child.mobile || '—'],
                    ['Email',  child.email  || '—'],
                    ['School', child.school || '—'],
                  ].map(([l, v], idx, arr) => (
                    <div key={l} style={{ display: 'flex', justifyContent: 'space-between', gap: '12px', padding: '7px 0', borderBottom: idx < arr.length - 1 ? '1px solid var(--border-soft)' : 'none', fontSize: '13px' }}>
                      <span style={{ color: 'var(--text-muted)', flexShrink: 0 }}>{l}</span>
                      <span style={{ color: 'var(--text-primary)', fontWeight: 500, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', textAlign: 'right' }}>{v}</span>
                    </div>
                  ))}
                </div>

                {/* Class details — own labelled, lighter panel */}
                <div style={{ display: 'flex', alignItems: 'center', gap: '7px', fontSize: '11px', fontWeight: 600, letterSpacing: '0.05em', textTransform: 'uppercase', color: 'var(--text-secondary)', marginBottom: '6px' }}>
                  <Icon name="classes" size={13} color="var(--text-muted)" /> Class
                </div>
                {cls ? (
                  <div style={{ background: 'var(--bg-elevated)', border: '1px solid var(--border-default)', borderRadius: '6px', padding: '2px 12px' }}>
                    {[
                      ['Campus',            cls.campus],
                      ['Day & time',        `${cls.day} ${cls.startTime}–${cls.endTime}`],
                      ['Term',              cls.term],
                      ['Tutor',             tutor ? `${tutor.firstName} ${tutor.lastName}` : '—'],
                      ['Students enrolled', cls.studentCount],
                    ].map(([l, v]) => (
                      <div key={l} style={{
                        display: 'flex', justifyContent: 'space-between',
                        padding: '6px 0', borderBottom: '1px solid var(--border-soft)', fontSize: '13px',
                      }}>
                        <span style={{ color: 'var(--text-muted)' }}>{l}</span>
                        <span style={{ color: 'var(--text-primary)', fontWeight: 500 }}>{v}</span>
                      </div>
                    ))}
                  </div>
                ) : (
                  <div style={{ fontSize: '12px', color: 'var(--text-muted)', fontStyle: 'italic' }}>
                    Not yet assigned to a class
                  </div>
                )}
              </div>
            );
          })}
        </div>
      )}

      {/* ── Invoices (across all children) ── */}
      {tab === 'Invoices' && (
        <InvoiceAccordion key={parent.id} invoices={buildParentInvoices(children)} showChild />
      )}
    </Drawer>
  );
}

// ── New Customer Drawer ───────────────────────────────────────

const CAMPUS_OPTIONS = ['Parramatta', 'Bella Vista'];
const COURSE_OPTIONS = ['YR09', 'YR10', 'YR11 STD', 'YR11 ADVN', 'YR11 EXT1', 'YR12 STD', 'YR12 ADVN', 'YR12 EXT1', 'YR12 EXT2'];
const STATUS_NEW_OPT = ['lead', 'trial', 'post-trial', 'enrolled'];

let _newIdCounter = 5000; // high base so manually-added IDs never collide with generated demo data
function genId(prefix) { return `${prefix}${++_newIdCounter}`; }

// Form-field dropdown = the canonical TMSelect with no field label. Kept as a
// named wrapper so the many existing call sites stay unchanged; all dropdown
// styling now lives in one place (TMSelect, shared.jsx).
function FormSelect(props) {
  return <TMSelect {...props} />;
}

function NewCustomerDrawer({ open, onClose, onAdd }) {
  const blankStudent = { firstName: '', lastName: '', mobile: '', email: '',
    school: '', campus: 'Parramatta', course: 'YR11 ADVN', status: 'lead', trialDate: '', classId: '', holidayId: '' };
  const blankParent  = { firstName: '', lastName: '', mobile: '', email: '' };

  const [student,          setStudent]          = useSC(blankStudent);
  const [parent,           setParent]           = useSC(blankParent);
  const [parentMode,       setParentMode]       = useSC('new'); // 'new' | 'existing'
  const [existingParentId, setExistingParentId] = useSC('');
  const [sErr,             setSErr]             = useSC({});
  const [pErr,             setPErr]             = useSC({});

  useSCEffect(() => {
    if (open) {
      setStudent(blankStudent); setParent(blankParent);
      setParentMode('new'); setExistingParentId('');
      setSErr({}); setPErr({});
    }
  }, [open]);

  const setS = (k, v) => setStudent(prev => ({ ...prev, [k]: v }));
  const setP = (k, v) => setParent(prev => ({ ...prev, [k]: v }));

  const validate = () => {
    const se = {}, pe = {};
    if (!student.firstName.trim()) se.firstName = 'Required';
    if (!student.lastName.trim())  se.lastName  = 'Required';
    if (parentMode === 'new') {
      if (!parent.firstName.trim()) pe.firstName = 'Required';
      if (!parent.lastName.trim())  pe.lastName  = 'Required';
    } else if (!existingParentId) {
      pe.existing = 'Please select a parent';
    }
    setSErr(se); setPErr(pe);
    return !Object.keys(se).length && !Object.keys(pe).length;
  };

  const handleAdd = () => {
    if (!validate()) return;
    let parentId;
    if (parentMode === 'existing') {
      parentId = existingParentId;
    } else {
      parentId = genId('p');
      onAdd({ id: parentId,
        firstName: parent.firstName.trim(), lastName: parent.lastName.trim(),
        mobile: parent.mobile.trim(), email: parent.email.trim(),
        status: 'parent', course: '—', campus: '—', classId: null, parentId: null, _type: 'Parent' });
    }
    onAdd({ id: genId('s'),
      firstName: student.firstName.trim(), lastName: student.lastName.trim(),
      mobile: student.mobile.trim(), email: student.email.trim(),
      school: student.school.trim(), campus: student.campus, course: student.course,
      status: student.status, trialDate: student.trialDate || null,
      classId: student.classId || null,
      holiday: student.holidayId ? [{ programId: student.holidayId, status: 'active' }] : [],
      parentId, _type: 'Student' });
    onClose();
  };

  // ── shared styles ──
  const inp = {
    background: 'var(--bg-surface-muted)', border: '1px solid var(--border-default)',
    borderRadius: '4px', padding: '8px 10px', fontSize: '13px',
    color: 'var(--text-primary)', fontFamily: 'inherit', outline: 'none', width: '100%',
  };
  const focusIn  = e => { e.target.style.borderColor = '#1F4D3D'; e.target.style.boxShadow = '0 0 0 3px rgba(31,77,61,0.2)'; };
  const focusOut = e => { e.target.style.borderColor = 'var(--border-default)'; e.target.style.boxShadow = 'none'; };

  const sectionHead = {
    display: 'flex', alignItems: 'center', gap: '10px',
    fontSize: '14px', fontWeight: 700, letterSpacing: '0.01em',
    color: 'var(--text-primary)', marginBottom: '16px',
  };
  const sectionNum = {
    width: 22, height: 22, borderRadius: '50%', flexShrink: 0,
    background: 'var(--brand)', border: '1px solid var(--brand-hover)',
    display: 'flex', alignItems: 'center', justifyContent: 'center',
    fontSize: '11px', fontWeight: 700, color: '#FBFAF6',
  };

  // Reusable field wrapper (not a component — called inline)
  const fld = (label, err, children) => (
    <div style={{ display: 'flex', flexDirection: 'column', gap: '5px' }}>
      <label style={{ fontSize: '12px', fontWeight: 500, color: 'var(--text-muted)' }}>{label}</label>
      {children}
      {err && <span style={{ fontSize: '11px', color: '#C05656' }}>{err}</span>}
    </div>
  );

  // Selected existing parent card
  const existingParent = SAMPLE_PARENTS.find(p => p.id === existingParentId);

  return (
    <Drawer open={open} onClose={onClose} title="New customer" width={480}>
      <div style={{ paddingTop: '8px' }}>

        {/* ════════════════ Section 01: Student ════════════════ */}
        <div style={{ marginBottom: '28px' }}>
          <div style={sectionHead}>
            <div style={sectionNum}>1</div>
            Student
          </div>

          <div style={{ display: 'flex', flexDirection: 'column', gap: '14px' }}>
            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '12px' }}>
              {fld('First name', sErr.firstName,
                <input value={student.firstName} placeholder="e.g. Sarah"
                  onChange={e => { setS('firstName', e.target.value); if (sErr.firstName) setSErr(p => ({ ...p, firstName: null })); }}
                  onFocus={focusIn} onBlur={focusOut}
                  style={{ ...inp, borderColor: sErr.firstName ? '#C05656' : undefined }} />
              )}
              {fld('Last name', sErr.lastName,
                <input value={student.lastName} placeholder="e.g. Nguyen"
                  onChange={e => { setS('lastName', e.target.value); if (sErr.lastName) setSErr(p => ({ ...p, lastName: null })); }}
                  onFocus={focusIn} onBlur={focusOut}
                  style={{ ...inp, borderColor: sErr.lastName ? '#C05656' : undefined }} />
              )}
            </div>

            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '12px' }}>
              {fld('Mobile', null,
                <input value={student.mobile} placeholder="04XX XXX XXX"
                  onChange={e => setS('mobile', e.target.value)} onFocus={focusIn} onBlur={focusOut} style={inp} />
              )}
              {fld('Email', null,
                <input type="email" value={student.email} placeholder="email@example.com"
                  onChange={e => setS('email', e.target.value)} onFocus={focusIn} onBlur={focusOut} style={inp} />
              )}
            </div>

            {fld('School', null,
              <input value={student.school} placeholder="e.g. Parramatta High"
                onChange={e => setS('school', e.target.value)} onFocus={focusIn} onBlur={focusOut} style={inp} />
            )}

            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '12px' }}>
              {fld('Campus', null,
                <FormSelect value={student.campus} onChange={v => setS('campus', v)} options={CAMPUS_OPTIONS} />
              )}
              {fld('Course', null,
                <FormSelect value={student.course} onChange={v => setS('course', v)} options={COURSE_OPTIONS} />
              )}
            </div>

            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '12px' }}>
              {fld('Status', null,
                <FormSelect value={student.status} onChange={v => setS('status', v)}
                  options={STATUS_NEW_OPT.map(o => ({ value: o, label: o.charAt(0).toUpperCase() + o.slice(1) }))} />
              )}
              {fld('Trial date', null,
                <input type="date" value={student.trialDate}
                  onChange={e => setS('trialDate', e.target.value)}
                  onFocus={focusIn} onBlur={focusOut}
                  style={{ ...inp, colorScheme: 'dark' }} />
              )}
            </div>

            <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '12px' }}>
              {fld('Term class', null,
                <FormSelect value={student.classId} onChange={v => setS('classId', v)} placeholder="Not assigned yet"
                  options={[{ value: '', label: 'Not assigned yet' },
                    ...SAMPLE_CLASSES.filter(c => c.campus === student.campus && c.course === student.course && !c.archived)
                      .map(c => ({ value: c.id, label: `${formatClass(c)} · ${c.delivery === 'online' ? 'Online' : 'In-person'}` }))]} />
              )}
              {fld('Holiday program', null,
                <FormSelect value={student.holidayId} onChange={v => setS('holidayId', v)} placeholder="None"
                  options={[{ value: '', label: 'None' },
                    ...SAMPLE_HOLIDAY_PROGRAMS.filter(p => !p.archived).map(p => ({ value: p.id, label: p.shortName || p.name }))]} />
              )}
            </div>
          </div>
        </div>

        {/* Divider */}
        <div style={{ borderTop: '1px solid var(--border-soft)', marginBottom: '24px' }} />

        {/* ════════════════ Section 02: Parent ════════════════ */}
        <div style={{ marginBottom: '24px' }}>
          <div style={sectionHead}>
            <div style={sectionNum}>2</div>
            Parent / Guardian
          </div>

          {/* Mode toggle */}
          <div style={{ display: 'flex', gap: '8px', marginBottom: '16px' }}>
            {[['new', 'Create new'], ['existing', 'Link existing']].map(([mode, label]) => (
              <button key={mode} onClick={() => setParentMode(mode)} style={{
                flex: 1, padding: '7px', borderRadius: '4px', fontSize: '13px', fontWeight: 500,
                fontFamily: 'inherit', cursor: 'pointer', transition: '120ms ease',
                background: parentMode === mode ? 'rgba(31,77,61,0.18)' : 'var(--bg-surface-muted)',
                border: parentMode === mode ? '1px solid rgba(31,77,61,0.45)' : '1px solid var(--border-default)',
                color: parentMode === mode ? '#4A9B7E' : 'var(--text-secondary)',
              }}>{label}</button>
            ))}
          </div>

          {parentMode === 'new' && (
            <div style={{ display: 'flex', flexDirection: 'column', gap: '14px' }}>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '12px' }}>
                {fld('First name', pErr.firstName,
                  <input value={parent.firstName} placeholder="e.g. Linda"
                    onChange={e => { setP('firstName', e.target.value); if (pErr.firstName) setPErr(p => ({ ...p, firstName: null })); }}
                    onFocus={focusIn} onBlur={focusOut}
                    style={{ ...inp, borderColor: pErr.firstName ? '#C05656' : undefined }} />
                )}
                {fld('Last name', pErr.lastName,
                  <input value={parent.lastName} placeholder="e.g. Nguyen"
                    onChange={e => { setP('lastName', e.target.value); if (pErr.lastName) setPErr(p => ({ ...p, lastName: null })); }}
                    onFocus={focusIn} onBlur={focusOut}
                    style={{ ...inp, borderColor: pErr.lastName ? '#C05656' : undefined }} />
                )}
              </div>
              <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: '12px' }}>
                {fld('Mobile', null,
                  <input value={parent.mobile} placeholder="04XX XXX XXX"
                    onChange={e => setP('mobile', e.target.value)} onFocus={focusIn} onBlur={focusOut} style={inp} />
                )}
                {fld('Email', null,
                  <input type="email" value={parent.email} placeholder="email@example.com"
                    onChange={e => setP('email', e.target.value)} onFocus={focusIn} onBlur={focusOut} style={inp} />
                )}
              </div>
            </div>
          )}

          {parentMode === 'existing' && (
            <div style={{ display: 'flex', flexDirection: 'column', gap: '10px' }}>
              {fld('Search existing parents', pErr.existing,
                <FormSelect value={existingParentId} error={pErr.existing}
                  onChange={v => { setExistingParentId(v); setPErr({}); }}
                  placeholder="— Select a parent —"
                  options={SAMPLE_PARENTS.map(p => ({ value: p.id, label: `${p.firstName} ${p.lastName} · ${p.mobile}` }))} />
              )}
              {existingParent && (
                <div style={{
                  display: 'flex', alignItems: 'center', gap: '10px',
                  background: 'var(--bg-surface-muted)', borderRadius: '6px', padding: '10px 12px',
                  border: '1px solid var(--border-soft)',
                }}>
                  <Avatar name={`${existingParent.firstName} ${existingParent.lastName}`} size={30} />
                  <div>
                    <div style={{ fontSize: '13px', fontWeight: 500, color: 'var(--text-primary)' }}>
                      {existingParent.firstName} {existingParent.lastName}
                    </div>
                    <div style={{ fontSize: '11px', color: 'var(--text-muted)', marginTop: '2px' }}>
                      {existingParent.mobile} · {existingParent.email}
                    </div>
                  </div>
                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="#4A9B7E" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" style={{ marginLeft: 'auto', flexShrink: 0 }}>
                    <path d="M20 6 9 17l-5-5" />
                  </svg>
                </div>
              )}
            </div>
          )}
        </div>

        {/* Actions */}
        <div style={{ borderTop: '1px solid var(--border-soft)', paddingTop: '16px', display: 'flex', gap: '10px', justifyContent: 'flex-end' }}>
          <Button variant="secondary" onClick={onClose}>Cancel</Button>
          <Button variant="primary" onClick={handleAdd}>Add customer</Button>
        </div>
      </div>
    </Drawer>
  );
}

// ── Customers Section ─────────────────────────────────────────

const FILTER_TABS = ['All Contacts', 'Students', 'Parents', 'Lead', 'Trial', 'Post-Trial'];

// ── Customers dashboard (key business metrics above the table) ─

function CustomersDashboard({ students }) {
  // Term-enrolled only (must hold a term class) — excludes holiday-only customers
  const enrolled = students.filter(s => s.status === 'enrolled' && s.classId);

  // Per-campus split of enrolled students
  const byCampus = { Parramatta: 0, 'Bella Vista': 0 };
  enrolled.forEach(s => { if (byCampus[s.campus] != null) byCampus[s.campus]++; });

  // Per-year-level split, with course-stream (ADVN/EXT1/…) breakdown inside each year
  const years = {};
  enrolled.forEach(s => {
    const parts  = (s.course || '—').split(' ');
    const yr     = parts[0] || '—';
    const stream = parts.slice(1).join(' ') || 'General';
    if (!years[yr]) years[yr] = { total: 0, streams: {} };
    years[yr].total++;
    years[yr].streams[stream] = (years[yr].streams[stream] || 0) + 1;
  });
  // Only show the three core year levels in the dashboard for now
  const yearOrder = ['YR10', 'YR11', 'YR12'].filter(yr => years[yr]);

  // Pipeline counts
  const pipe = {
    lead:         students.filter(s => s.status === 'lead').length,
    trial:        students.filter(s => s.status === 'trial').length,
    'post-trial': students.filter(s => s.status === 'post-trial').length,
  };

  const card = { background: 'var(--bg-surface)', border: '1px solid var(--border-soft)', borderRadius: '8px', padding: '14px 16px', minWidth: 0 };
  // Prominent, colour-coded card heading so the three cards read as distinct sections
  const CardHeading = ({ icon, accent, children }) => (
    <div style={{ display: 'flex', alignItems: 'center', gap: '8px', marginBottom: '12px' }}>
      <span style={{ width: 24, height: 24, borderRadius: '7px', background: `${accent}22`, border: `1px solid ${accent}55`,
        display: 'flex', alignItems: 'center', justifyContent: 'center', flexShrink: 0 }}>
        <Icon name={icon} size={14} color={accent} />
      </span>
      <span style={{ fontSize: '13px', fontWeight: 700, color: 'var(--text-primary)', letterSpacing: '0.04em', textTransform: 'uppercase' }}>{children}</span>
    </div>
  );
  // App-wide subject palette lookup
  const subjectColor = key => SUBJECT_COLORS[key] || 'var(--text-muted)';

  return (
    <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: '12px', marginBottom: '18px' }}>

      {/* Enrolled students + per-campus */}
      <div style={card}>
        <CardHeading icon="customers" accent="#4A9B7E">Enrolled students</CardHeading>
        <div style={{ display: 'flex', alignItems: 'center', gap: '20px' }}>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: '8px' }}>
            <span style={{ fontSize: '44px', fontWeight: 700, color: 'var(--text-primary)', lineHeight: 1, letterSpacing: '-0.02em' }}>{enrolled.length}</span>
            <span style={{ fontSize: '13px', color: 'var(--text-muted)' }}>total</span>
          </div>
          <div style={{ display: 'flex', gap: '24px', marginLeft: 'auto' }}>
            {[['Parramatta', byCampus.Parramatta], ['Bella Vista', byCampus['Bella Vista']]].map(([label, n]) => (
              <div key={label} style={{ textAlign: 'right' }}>
                <div style={{ fontSize: '24px', fontWeight: 700, color: 'var(--text-secondary)', lineHeight: 1.1 }}>{n}</div>
                <div style={{ fontSize: '11px', color: 'var(--text-muted)', marginTop: '2px' }}>{label}</div>
              </div>
            ))}
          </div>
        </div>
      </div>

      {/* By year level — one compact line per year: label · breakdown · total */}
      <div style={card}>
        <CardHeading icon="classes" accent="#4A8BB2">By year level</CardHeading>
        <div style={{ display: 'flex', flexDirection: 'column', gap: '4px' }}>
          {yearOrder.length === 0 && <span style={{ fontSize: '12px', color: 'var(--text-muted)' }}>No enrolled students</span>}
          {yearOrder.map(yr => {
            const y = years[yr];
            const streams = Object.entries(y.streams).sort((a, b) => b[1] - a[1]);
            return (
              <div key={yr} style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
                <span style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-primary)', width: '38px', flexShrink: 0 }}>{yr}</span>
                <div style={{ flex: 1, display: 'flex', flexWrap: 'wrap', alignItems: 'center', gap: '10px', minWidth: 0 }}>
                  {streams.map(([st, c]) => {
                    const isGen = st === 'General';      // single-stream years (YR09/YR10/STD)
                    const label = isGen ? yr : st;        // show the year name, not "Standard"
                    const col   = subjectColor(isGen ? yr : st);
                    return (
                      <span key={st} style={{ display: 'inline-flex', alignItems: 'center', gap: '5px', fontSize: '11px', color: 'var(--text-secondary)', whiteSpace: 'nowrap' }}>
                        <span style={{ width: 8, height: 8, borderRadius: '50%', background: col, flexShrink: 0 }} />
                        <span style={{ fontWeight: 700, color: 'var(--text-primary)' }}>{c}</span> {label}
                      </span>
                    );
                  })}
                </div>
                <span style={{ fontSize: '11px', color: 'var(--text-muted)', whiteSpace: 'nowrap', flexShrink: 0 }}>
                  <span style={{ fontSize: '14px', fontWeight: 700, color: 'var(--text-primary)' }}>{y.total}</span> enrolled
                </span>
              </div>
            );
          })}
        </div>
      </div>

      {/* Pipeline */}
      <div style={card}>
        <CardHeading icon="tasks" accent="#B8922A">Pipeline</CardHeading>
        <div style={{ display: 'flex', gap: '8px' }}>
          {[['Leads', pipe.lead, STATUS_CFG.lead.text],
            ['Trial', pipe.trial, STATUS_CFG.trial.text],
            ['Post-trial', pipe['post-trial'], STATUS_CFG['post-trial'].text]].map(([label, n, color]) => (
            <div key={label} style={{ flex: 1, minWidth: 0 }}>
              <div style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
                <span style={{ width: 8, height: 8, borderRadius: '50%', background: color, flexShrink: 0 }} />
                <span style={{ fontSize: '20px', fontWeight: 700, color: 'var(--text-primary)', lineHeight: 1 }}>{n}</span>
              </div>
              <div style={{ fontSize: '10px', color: 'var(--text-muted)', marginTop: '4px', whiteSpace: 'nowrap' }}>{label}</div>
            </div>
          ))}
        </div>
      </div>

    </div>
  );
}

function CustomersSection({ showData }) {
  const [activeFilter, setActiveFilter] = useSC('All Contacts');
  const [search,       setSearch]       = useSC('');
  const [selectedId,       setSelectedId]       = useSC(null);
  const [drawerOpen,       setDrawerOpen]       = useSC(false);
  const [selectedParentId, setSelectedParentId] = useSC(null);
  const [parentDrawerOpen, setParentDrawerOpen] = useSC(false);
  const [newOpen,          setNewOpen]          = useSC(false);
  const [pageSize,         setPageSize]         = useSC(50);
  const [page,             setPage]             = useSC(1);
  const [sort,             setSort]             = useSC(null);          // { key, dir }
  const [selected,         setSelected]         = useSC(() => new Set());
  const [programFilter,    setProgramFilter]    = useSC('all');         // all | term-inperson | term-online | holiday:<id>

  // Mutable local copy of all rows so edits apply live
  const [rowData, setRowData] = useSC(() => [
    ...SAMPLE_STUDENTS.map(s => ({ ...s, _type: 'Student' })),
    ...SAMPLE_PARENTS.map(p  => ({ ...p, _type: 'Parent', status: 'parent', course: '—', campus: '—', classId: null })),
  ]);

  const updateRow = (id, field, value) =>
    setRowData(prev => prev.map(r => r.id === id ? { ...r, [field]: value } : r));

  const handleFilterClick = f => {
    if (f === activeFilter) return;
    setActiveFilter(f);
    // Leads tab always opens ordered most-recent → longest-standing
    if (f === 'Lead') setSort({ key: 'In system', dir: 'asc' });
  };

  const toggleSort = key => setSort(s => (s && s.key === key)
    ? { key, dir: s.dir === 'asc' ? 'desc' : 'asc' }
    : { key, dir: 'asc' });

  const handleAddCustomer = newRow => {
    setRowData(prev => [newRow, ...prev]);
  };

  // One predicate per tab — drives both the visible rows and the live tab counts.
  const matchesTab = (r, f) => {
    switch (f) {
      case 'Students':   return r._type === 'Student' && r.status === 'enrolled' && r.classId;
      case 'Parents':    return r._type === 'Parent' && rowData.some(s => s._type === 'Student' && s.parentId === r.id && s.status === 'enrolled');
      case 'Lead':       return r._type === 'Student' && r.status === 'lead';
      case 'Trial':      return r._type === 'Student' && r.status === 'trial';
      case 'Post-Trial': return r._type === 'Student' && r.status === 'post-trial';
      default:           return true; // All Contacts
    }
  };
  const matchesSearch = r => {
    if (!search) return true;
    const q = search.toLowerCase().trim();
    if (`${r.firstName} ${r.lastName} ${r.mobile} ${r.email}`.toLowerCase().includes(q)) return true;
    // Digits-only mobile match, so "0412345678" finds "0412 345 678"
    const qDigits = q.replace(/\D/g, '');
    return !!qDigits && (r.mobile || '').replace(/\D/g, '').includes(qDigits);
  };
  // Program (enrolment) filter — separate axis from the lifecycle tabs.
  const matchesProgram = r => {
    if (programFilter === 'all') return true;
    if (r._type !== 'Student') return false;
    const cls = SAMPLE_CLASSES.find(c => c.id === r.classId);
    if (programFilter === 'term-inperson') return !!cls && cls.delivery !== 'online';
    if (programFilter === 'term-online')   return !!cls && cls.delivery === 'online';
    if (programFilter.startsWith('holiday:')) {
      const pid = programFilter.slice(8);
      return (r.holiday || []).some(h => h.status === 'active' && h.programId === pid);
    }
    return true;
  };

  const rows = !showData ? [] : rowData.filter(r => matchesTab(r, activeFilter) && matchesProgram(r) && matchesSearch(r));
  // Live per-tab counts straight off the data (independent of the search box)
  const tabCounts = Object.fromEntries(FILTER_TABS.map(f => [f, showData ? rowData.filter(r => matchesTab(r, f)).length : 0]));

  // Sorting (click a column header; Leads tab auto-sorts by age)
  const sortValue = (r, key) => {
    switch (key) {
      case 'ID':         return parseInt(String(r.id).replace(/\D/g, ''), 10) || 0;
      case 'Name':       return `${r.lastName} ${r.firstName}`.toLowerCase();
      case 'Status':     return r.status || '';
      case 'In system':  { const n = daysInSystem(r); return n == null ? -1 : n; }
      case 'Role':       return r._type || '';
      case 'Mobile':     return (r.mobile || '').toLowerCase();
      case 'Email':      return (r.email || '').toLowerCase();
      case 'Campus':     return (r.campus === '—' ? '' : (r.campus || '')).toLowerCase();
      case 'Course':     return (r.course === '—' ? '' : (r.course || '')).toLowerCase();
      case 'Class':      { const c = SAMPLE_CLASSES.find(x => x.id === r.classId); return c ? formatClass(c).toLowerCase() : ''; }
      case 'Trial Date': return r.trialDate || '';
      default:           return 0;
    }
  };
  const sortedRows = sort
    ? [...rows].sort((a, b) => {
        const av = sortValue(a, sort.key), bv = sortValue(b, sort.key);
        const d = sort.dir === 'asc' ? 1 : -1;
        if (av < bv) return -d; if (av > bv) return d; return 0;
      })
    : rows;

  // Pagination — render only one page of rows so large datasets stay snappy
  const totalPages = Math.max(1, Math.ceil(sortedRows.length / pageSize));
  const current    = Math.min(page, totalPages);
  const startIdx   = (current - 1) * pageSize;
  const pageRows   = sortedRows.slice(startIdx, startIdx + pageSize);
  React.useEffect(() => { setPage(1); }, [activeFilter, search, pageSize, showData, programFilter]);

  // Selection + bulk actions
  const toggleSelect = id => setSelected(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; });
  const toggleSelectAll = checked => setSelected(prev => {
    const n = new Set(prev);
    pageRows.forEach(r => { if (checked) n.add(r.id); else n.delete(r.id); });
    return n;
  });
  const exportCSV = list => {
    const head = ['ID', 'First name', 'Last name', 'Status', 'Role', 'Mobile', 'Email', 'Campus', 'Course', 'Trial date', 'Days in system'];
    const esc = v => `"${String(v ?? '').replace(/"/g, '""')}"`;
    const lines = [head.map(esc).join(',')];
    list.forEach(r => lines.push([customerCode(r), r.firstName, r.lastName, r.status, r._type, r.mobile, r.email,
      r.campus === '—' ? '' : r.campus, r.course === '—' ? '' : r.course, r.trialDate, daysInSystem(r)].map(esc).join(',')));
    const blob = new Blob([lines.join('\n')], { type: 'text/csv;charset=utf-8' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = `customers-${new Date().toISOString().slice(0,10)}.csv`; a.click();
    URL.revokeObjectURL(url);
  };
  const smallBtn = { background: 'var(--bg-surface-muted)', border: '1px solid var(--border-default)', borderRadius: '4px',
    padding: '5px 10px', fontSize: '12px', color: 'var(--text-secondary)', fontFamily: 'inherit', cursor: 'pointer' };
  const pagerNum = active => ({ minWidth: 30, height: 30, padding: '0 8px', borderRadius: '6px',
    border: active ? '1px solid var(--border-default)' : '1px solid transparent',
    background: active ? 'var(--bg-elevated)' : 'transparent',
    color: active ? 'var(--text-primary)' : 'var(--text-secondary)',
    fontSize: '13px', fontWeight: active ? 600 : 400, fontFamily: 'inherit', cursor: 'pointer', transition: '100ms ease' });
  const pagerNav = disabled => ({ height: 30, padding: '0 10px', borderRadius: '6px', border: 'none', background: 'transparent',
    color: disabled ? 'var(--text-muted)' : 'var(--text-secondary)', fontSize: '13px', fontFamily: 'inherit',
    cursor: disabled ? 'default' : 'pointer', opacity: disabled ? 0.5 : 1 });

  // Always look up the live version of the selected customer
  const selectedCustomer = rowData.find(r => r.id === selectedId) || null;
  const selectedParent   = rowData.find(r => r.id === selectedParentId && r._type === 'Parent') || null;

  // Resizable column widths — drag the dividers in the header row to adjust.
  // Order: Name | Status | Role | Mobile | Email | Campus | Course | Class | Trial Date | Trial Email
  const COL_GAP = '1cm'; // breathing room so dropdown chevrons never crowd the next column's text
  // "In system" (lead aging) is only shown on the Leads tab.
  const showAge = activeFilter === 'Lead';
  const ALL_COLS = [
    { key: 'ID', w: 104 }, { key: 'Name', w: 190 }, { key: 'Status', w: 110 },
    { key: 'In system', w: 120 }, { key: 'Role', w: 72 }, { key: 'Mobile', w: 130 },
    { key: 'Email', w: 190 }, { key: 'Campus', w: 120 }, { key: 'Course', w: 112 },
    { key: 'Class', w: 172 }, { key: 'Program', w: 210 }, { key: 'Trial Date', w: 132 }, { key: 'Trial Email', w: 120 },
  ];
  const [colWidths, setColWidths] = useSC(() => Object.fromEntries(ALL_COLS.map(c => [c.key, c.w])));
  const visibleCols = ALL_COLS.filter(c => c.key !== 'In system' || showAge);
  const HDRS = visibleCols.map(c => c.key);
  const gridTemplate = visibleCols.map(c => `${colWidths[c.key]}px`).join(' ');

  const startResize = (key, e) => {
    e.preventDefault(); e.stopPropagation();
    const startX = e.clientX;
    const startW = colWidths[key];
    const onMove = ev => {
      const delta = ev.clientX - startX;
      setColWidths(prev => ({ ...prev, [key]: Math.max(56, startW + delta) })); // 56px floor
    };
    const onUp = () => {
      document.removeEventListener('mousemove', onMove);
      document.removeEventListener('mouseup', onUp);
      document.body.style.cursor = '';
      document.body.style.userSelect = '';
    };
    document.addEventListener('mousemove', onMove);
    document.addEventListener('mouseup', onUp);
    document.body.style.cursor = 'col-resize';
    document.body.style.userSelect = 'none';
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', height: '100%', minHeight: 0 }}>
      <CustomersDashboard students={showData ? rowData.filter(r => r._type === 'Student') : []} />

      <div style={{ display: 'flex', gap: '6px', marginBottom: '16px', alignItems: 'center', flexWrap: 'wrap' }}>
        {FILTER_TABS.map(f => <Chip key={f} label={f} count={tabCounts[f]} active={activeFilter === f} onClick={() => handleFilterClick(f)} />)}
        <div style={{ marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: '10px' }}>
          <div style={{ width: '190px' }}>
            <FormSelect value={programFilter} onChange={setProgramFilter} buttonStyle={{ padding: '6px 10px' }} options={[
              { value: 'all', label: 'All programs' },
              { value: 'term-inperson', label: 'Term · In-person' },
              { value: 'term-online', label: 'Term · Online' },
              ...SAMPLE_HOLIDAY_PROGRAMS.filter(p => !p.archived).map(p => ({ value: `holiday:${p.id}`, label: `Holiday: ${p.shortName || p.name}` })),
            ]} />
          </div>
          <div style={{ position: 'relative' }}>
            <Icon name="search" size={14} color="var(--text-muted)" style={{ position: 'absolute', left: 10, top: '50%', transform: 'translateY(-50%)' }} />
            <input value={search} onChange={e => setSearch(e.target.value)} placeholder="Search name, mobile, email…"
              style={{ background: 'var(--bg-surface-muted)', border: '1px solid var(--border-default)', borderRadius: '4px',
                padding: '6px 12px 6px 32px', fontSize: '13px', color: 'var(--text-primary)', fontFamily: 'inherit',
                outline: 'none', width: '260px' }} />
          </div>
          <Button variant="primary" onClick={() => setNewOpen(true)}>+ New customer</Button>
        </div>
      </div>

      {/* Bulk-action bar — above the table, like Invoices */}
      {selected.size > 0 && (
        <div style={{ display: 'flex', alignItems: 'center', gap: '12px', flexWrap: 'wrap', marginBottom: '12px',
          background: 'rgba(31,77,61,0.12)', border: '1px solid rgba(31,77,61,0.4)', borderRadius: '8px', padding: '8px 14px' }}>
          <span style={{ fontSize: '13px', fontWeight: 600, color: 'var(--text-primary)' }}>{selected.size} selected</span>
          <Button size="sm" variant="primary" onClick={() => exportCSV(rowData.filter(r => selected.has(r.id)))}>Export CSV</Button>
          <button onClick={() => setSelected(new Set())} style={{ background: 'none', border: 'none', color: 'var(--text-muted)', fontSize: '12px', cursor: 'pointer', fontFamily: 'inherit', marginLeft: 'auto' }}>Clear selection</button>
        </div>
      )}

      {/* Outer bounding card: inner data grid + pagination footer (HubSpot-style) */}
      <div style={{ flex: 1, minHeight: 0, display: 'flex', flexDirection: 'column',
        border: '1px solid var(--border-default)', borderRadius: '10px',
        background: 'var(--bg-surface-muted)', padding: '8px', gap: '8px' }}>

        {/* Inner box — the customer list (scrolls vertically + horizontally) */}
        <div style={{ flex: 1, minHeight: 0, overflow: 'auto',
          border: '1px solid var(--border-soft)', borderRadius: '8px', background: 'var(--bg-surface)' }}>
          <div style={{ minWidth: 'max-content' }}>

          {/* Header (sticky while the list scrolls) */}
          <div style={{
            position: 'sticky', top: 0, zIndex: 10,
            display: 'grid', gridTemplateColumns: gridTemplate, columnGap: COL_GAP,
            background: 'var(--bg-surface-muted)', padding: '9px 16px',
            borderBottom: '1px solid var(--border-soft)', fontSize: '11px', fontWeight: 500,
            color: 'var(--text-secondary)', letterSpacing: '0.05em', textTransform: 'uppercase',
          }}>
            {HDRS.map((h, i) => {
              const sortable = h !== 'Trial Email';
              return (
              <span key={h || i} style={{ position: 'relative', display: 'flex', alignItems: 'center', gap: '8px' }}>
                {h === 'ID' && (
                  <Checkbox
                    checked={pageRows.length > 0 && pageRows.every(r => selected.has(r.id))}
                    onChange={checked => toggleSelectAll(checked)}
                    title="Select all on this page" />
                )}
                <span
                  onClick={() => sortable && toggleSort(h)}
                  style={{ display: 'inline-flex', alignItems: 'center', gap: '4px', cursor: sortable ? 'pointer' : 'default', userSelect: 'none' }}
                >
                  {h}
                  {sort && sort.key === h && (
                    <span style={{ fontSize: '9px', color: 'var(--text-secondary)' }}>{sort.dir === 'asc' ? '▲' : '▼'}</span>
                  )}
                  {h === 'Name' && (
                    <span style={{ fontSize: '9px', fontWeight: 400, color: 'var(--text-muted)', letterSpacing: '0.02em', textTransform: 'none' }}>
                      → opens profile
                    </span>
                  )}
                </span>
                {/* Drag-to-resize handle — sits in the gap to this column's right */}
                {i < HDRS.length - 1 && (
                  <span
                    onMouseDown={e => startResize(h, e)}
                    title="Drag to resize column"
                    style={{
                      position: 'absolute', top: '-9px', bottom: '-9px', right: 'calc(-0.5cm - 4px)',
                      width: '9px', cursor: 'col-resize', display: 'flex', justifyContent: 'center', zIndex: 5,
                    }}
                    onMouseEnter={e => { e.currentTarget.firstChild.style.background = 'var(--brand-hover)'; }}
                    onMouseLeave={e => { e.currentTarget.firstChild.style.background = 'var(--border-default)'; }}
                  >
                    {/* Visible by default so it's clear columns can be dragged */}
                    <span style={{ width: '2px', height: '100%', background: 'var(--border-default)', borderRadius: '1px', transition: '120ms ease' }} />
                  </span>
                )}
              </span>
              );
            })}
          </div>

          {rows.length === 0 ? (
            <div style={{ padding: '48px', textAlign: 'center', fontSize: '13px', color: 'var(--text-muted)' }}>
              {!showData ? 'Enable sample data to see customers' : 'No customers match your filters'}
            </div>
          ) : pageRows.map((r, i) => {
            const cls = SAMPLE_CLASSES.find(c => c.id === r.classId) || null;
            return (
              <div
                key={r.id}
                style={{
                  display: 'grid', gridTemplateColumns: gridTemplate, columnGap: COL_GAP,
                  padding: '9px 16px', alignItems: 'center',
                  background: 'var(--bg-surface)',
                  borderBottom: i < pageRows.length - 1 ? '1px solid var(--border-soft)' : 'none',
                  transition: '80ms ease',
                }}
                onMouseEnter={e => e.currentTarget.style.background = 'var(--bg-hover)'}
                onMouseLeave={e => e.currentTarget.style.background = 'var(--bg-surface)'}
              >
                {/* ── Select + ID (one cell, tight gap) ── */}
                <span style={{ display: 'flex', alignItems: 'center', gap: '8px', minWidth: 0 }}>
                  <Checkbox checked={selected.has(r.id)} onChange={() => toggleSelect(r.id)} />
                  <span style={{ fontSize: '12px', color: 'var(--text-muted)', fontFamily: 'ui-monospace, SFMono-Regular, Menlo, monospace', fontVariantNumeric: 'tabular-nums', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
                    {customerCode(r)}
                  </span>
                </span>

                {/* ── Name — clicks open profile drawer ── */}
                <div
                  onClick={() => {
                    if (r._type === 'Student') { setSelectedId(r.id); setDrawerOpen(true); }
                    else if (r._type === 'Parent') { setSelectedParentId(r.id); setParentDrawerOpen(true); }
                  }}
                  style={{
                    display: 'flex', alignItems: 'center', gap: '9px', minWidth: 0,
                    cursor: 'pointer',
                  }}
                >
                  <Avatar name={`${r.firstName} ${r.lastName}`} size={26} />
                  <span style={{
                    fontSize: '13px', fontWeight: 500,
                    color: 'var(--text-primary)',
                    overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap',
                    textDecoration: 'underline',
                    textDecorationColor: 'rgba(255,255,255,0.18)',
                    textUnderlineOffset: '2px',
                  }}>
                    {r.firstName} {r.lastName}
                  </span>
                </div>

                {/* ── Status — dropdown ── */}
                <div onClick={e => e.stopPropagation()}>
                  <StatusDropdown
                    status={r.status}
                    onChange={val => updateRow(r.id, 'status', val)}
                    isParent={r._type === 'Parent'}
                  />
                </div>

                {/* ── In system (lead aging) — Leads tab only ── */}
                {showAge && (() => {
                  const n = daysInSystem(r);
                  const overdue = n != null && n > 7;
                  return (
                    <span style={{ display: 'flex', alignItems: 'center', gap: '5px', fontSize: '12px',
                      color: overdue ? '#B44040' : 'var(--text-muted)', fontWeight: overdue ? 600 : 400, whiteSpace: 'nowrap' }}>
                      {overdue && (
                        <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
                          <path d="M10.29 3.86 1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z" />
                          <line x1="12" y1="9" x2="12" y2="13" />
                          <line x1="12" y1="17" x2="12.01" y2="17" />
                        </svg>
                      )}
                      {n == null ? '—' : `${n} ${n === 1 ? 'day' : 'days'}`}
                    </span>
                  );
                })()}

                {/* ── Role — read-only ── */}
                <span style={{ fontSize: '12px', color: 'var(--text-muted)' }}>{r._type}</span>

                {/* ── Mobile ── */}
                <InlineCell
                  value={r.mobile}
                  onCommit={val => updateRow(r.id, 'mobile', val)}
                  numeric
                />

                {/* ── Email ── */}
                <InlineCell
                  value={r.email}
                  onCommit={val => updateRow(r.id, 'email', val)}
                />

                {/* ── Campus ── */}
                {r._type === 'Student' ? (
                  <InlineDropdownCell
                    value={r.campus !== '—' ? r.campus : ''}
                    options={CAMPUS_OPTIONS}
                    onCommit={val => updateRow(r.id, 'campus', val)}
                  />
                ) : <span style={{ fontSize: '12px', color: 'var(--text-muted)' }}>—</span>}

                {/* ── Course (with subject-colour dot) ── */}
                {r._type === 'Student' ? (
                  <InlineDropdownCell
                    value={r.course !== '—' ? r.course : ''}
                    options={COURSE_OPTIONS}
                    onCommit={val => updateRow(r.id, 'course', val)}
                    colorFor={courseColor}
                  />
                ) : <span style={{ fontSize: '12px', color: 'var(--text-muted)' }}>—</span>}

                {/* ── Class — conditional on campus + course ── */}
                {r._type === 'Student' ? (
                  <ClassDropdownCell
                    campus={r.campus}
                    course={r.course}
                    value={r.classId || ''}
                    onCommit={val => updateRow(r.id, 'classId', val || null)}
                  />
                ) : <span style={{ fontSize: '12px', color: 'var(--text-muted)' }}>—</span>}

                {/* ── Program (enrolment type) ── */}
                {r._type === 'Student' ? (() => {
                  const chips = programChips(r);
                  return chips.length ? (
                    <span style={{ display: 'flex', flexWrap: 'wrap', gap: '4px', minWidth: 0 }}>
                      {chips.map((c, ci) => (
                        <span key={ci} title={c.label} style={{ display: 'inline-flex', alignItems: 'center', gap: '4px',
                          fontSize: '10px', fontWeight: 600, padding: '2px 7px', borderRadius: '4px', whiteSpace: 'nowrap',
                          background: `${c.color}22`, color: c.color, border: `1px solid ${c.color}55` }}>
                          <span style={{ width: 6, height: 6, borderRadius: '50%', background: c.color, flexShrink: 0 }} />
                          {c.label}
                        </span>
                      ))}
                    </span>
                  ) : <span style={{ fontSize: '12px', color: 'var(--text-muted)' }}>—</span>;
                })() : <span style={{ fontSize: '12px', color: 'var(--text-muted)' }}>—</span>}

                {/* ── Trial Date ── */}
                <TrialDateCell
                  value={r.trialDate || ''}
                  onCommit={val => updateRow(r.id, 'trialDate', val || null)}
                />

                {/* ── Trial Email ── */}
                {r._type === 'Student' ? <TrialEmailDropdown student={r} /> : <span />}
              </div>
            );
          })}
          </div>
        </div>

        {/* Footer inside the outer card — pagination */}
        {rows.length > 0 && (
          <div style={{ paddingTop: '2px' }}>

          {/* HubSpot-style: centred numbered pager, page-size dropdown pinned right */}
          <div style={{ position: 'relative', display: 'flex', alignItems: 'center', justifyContent: 'center', paddingBottom: '4px', minHeight: '34px' }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: '4px' }}>
              <button onClick={() => setPage(p => Math.max(1, p - 1))} disabled={current <= 1} style={pagerNav(current <= 1)}
                onMouseEnter={e => { if (current > 1) e.currentTarget.style.color = 'var(--text-primary)'; }}
                onMouseLeave={e => { e.currentTarget.style.color = current <= 1 ? 'var(--text-muted)' : 'var(--text-secondary)'; }}>‹ Prev</button>
              {pageList(current, totalPages).map((p, idx) => p === '…'
                ? <span key={`g${idx}`} style={{ padding: '0 4px', color: 'var(--text-muted)' }}>…</span>
                : <button key={p} onClick={() => setPage(p)} style={pagerNum(p === current)}
                    onMouseEnter={e => { if (p !== current) e.currentTarget.style.background = 'var(--bg-hover)'; }}
                    onMouseLeave={e => { if (p !== current) e.currentTarget.style.background = 'transparent'; }}>{p}</button>
              )}
              <button onClick={() => setPage(p => Math.min(totalPages, p + 1))} disabled={current >= totalPages} style={pagerNav(current >= totalPages)}
                onMouseEnter={e => { if (current < totalPages) e.currentTarget.style.color = 'var(--text-primary)'; }}
                onMouseLeave={e => { e.currentTarget.style.color = current >= totalPages ? 'var(--text-muted)' : 'var(--text-secondary)'; }}>Next ›</button>
            </div>
            <div style={{ position: 'absolute', right: 0, top: '50%', transform: 'translateY(-50%)', width: '128px' }}>
              <FormSelect value={pageSize} onChange={v => { setPageSize(v); setPage(1); }}
                options={[50, 100, 150, 200].map(n => ({ value: n, label: `${n} per page` }))}
                buttonStyle={{ padding: '5px 10px', fontSize: '12px' }} />
            </div>
          </div>
          </div>
        )}
      </div>

      <CustomerDrawer
        customer={selectedCustomer}
        open={drawerOpen}
        onClose={() => setDrawerOpen(false)}
        onUpdate={updates => Object.entries(updates).forEach(([f, v]) => updateRow(selectedId, f, v))}
        onOpenParent={pid => { setDrawerOpen(false); setSelectedParentId(pid); setParentDrawerOpen(true); }}
      />

      <ParentDrawer
        parent={selectedParent}
        open={parentDrawerOpen}
        onClose={() => setParentDrawerOpen(false)}
        onUpdate={updates => Object.entries(updates).forEach(([f, v]) => updateRow(selectedParentId, f, v))}
        allRows={rowData}
        onOpenStudent={sid => { setParentDrawerOpen(false); setSelectedId(sid); setDrawerOpen(true); }}
      />

      <NewCustomerDrawer
        open={newOpen}
        onClose={() => setNewOpen(false)}
        onAdd={handleAddCustomer}
      />
    </div>
  );
}

Object.assign(window, { CustomersSection, CustomerDrawer, NewCustomerDrawer });
