// olivia-admin-panes.jsx
// The three panes of the admin: content list (left), editor (center), live preview (right).

// ─── Overview page ────────────────────────────────────────────

function OvPanel({ title, hint, children, style, action }) {
  return (
    <div style={{
      background: OV.panel, border: `1px solid ${OV.hair}`, borderRadius: 18,
      padding: 20, display: 'flex', flexDirection: 'column', ...style,
    }}>
      {(title || action) && (
        <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 2 }}>
          <h3 style={{ margin: 0, fontFamily: OLIVIA.fontDisplay, fontSize: 17, fontWeight: 600, color: OV.ink, letterSpacing: '-0.01em', whiteSpace: 'nowrap' }}>{title}</h3>
          {action}
        </div>
      )}
      {hint && <p style={{ margin: '2px 0 14px', fontFamily: OLIVIA.fontBody, fontSize: 12.5, color: OV.inkMute, lineHeight: 1.4 }}>{hint}</p>}
      {children}
    </div>
  );
}

function OvDelta({ now, prev }) {
  const up = now >= prev;
  const pct = prev ? Math.round(((now - prev) / prev) * 100) : 0;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 3,
      padding: '2px 7px', borderRadius: 999,
      background: up ? OV.sageWash : OV.roseWash,
      color: up ? OV.sageDeep : OV.rose,
      fontFamily: OLIVIA.fontBody, fontSize: 11.5, fontWeight: 700, whiteSpace: 'nowrap',
    }}>
      {up ? '▲' : '▼'} {Math.abs(pct)}%
    </span>
  );
}

function OvKpiCard({ label, value, sub, delta }) {
  return (
    <div style={{
      background: OV.panel, border: `1px solid ${OV.hair}`, borderRadius: 18,
      padding: '18px 20px', display: 'flex', flexDirection: 'column', gap: 4,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
        <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, fontWeight: 600, color: OV.inkMute, letterSpacing: '0.04em', textTransform: 'uppercase', whiteSpace: 'nowrap' }}>{label}</span>
        {delta}
      </div>
      <div style={{ fontFamily: OLIVIA.fontDisplay, fontSize: 34, fontWeight: 600, color: OV.ink, letterSpacing: '-0.02em', lineHeight: 1.05 }}>{value}</div>
      <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 13, color: OV.inkSoft }}>{sub}</span>
    </div>
  );
}

function OvKindBars() {
  const max = Math.max(...OV_DATA.byKind.map(k => k.count));
  const total = OV_DATA.byKind.reduce((s, k) => s + k.count, 0);
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 13 }}>
      {OV_DATA.byKind.map((k, i) => (
        <div key={k.kind}>
          <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 10, marginBottom: 5 }}>
            <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 13.5, fontWeight: i === 0 ? 700 : 500, color: i === 0 ? OV.ink : OV.inkSoft, whiteSpace: 'nowrap' }}>
              {k.kind}{i === 0 && <span style={{ marginLeft: 8, fontSize: 10, fontWeight: 700, color: OV.accentDeep, background: OV.accentWash, padding: '1px 7px', borderRadius: 999, letterSpacing: '0.04em', textTransform: 'uppercase' }}>Top</span>}
            </span>
            <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 12.5, color: OV.inkMute, fontVariantNumeric: 'tabular-nums', whiteSpace: 'nowrap', flexShrink: 0 }}>
              {ovFmt(k.count)} · {ovPctOf(k.count, total)}%
            </span>
          </div>
          <div style={{ height: 10, borderRadius: 999, background: OV.panelMute, overflow: 'hidden' }}>
            <div style={{ width: `${(k.count / max) * 100}%`, height: '100%', borderRadius: 999, background: k.color }}/>
          </div>
        </div>
      ))}
    </div>
  );
}

function OvStreakPanel() {
  const max = Math.max(...OV_DATA.streakBuckets.map(b => b.count));
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 16, flex: 1 }}>
      <div style={{ display: 'flex', gap: 14 }}>
        <div style={{ flex: 1, background: OV.accentWash, borderRadius: 14, padding: '14px 16px' }}>
          <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700, color: OV.accentDeep, letterSpacing: '0.06em', textTransform: 'uppercase' }}>Avg streak</div>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 6 }}>
            <span style={{ fontFamily: OLIVIA.fontDisplay, fontSize: 30, fontWeight: 600, color: OV.ink }}>{OV_DATA.avgStreak}</span>
            <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 13, color: OV.inkSoft }}>days</span>
            <OvDelta now={OV_DATA.avgStreak} prev={OV_DATA.prevAvgStreak}/>
          </div>
        </div>
        <div style={{ flex: 1, background: OV.sageWash, borderRadius: 14, padding: '14px 16px' }}>
          <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700, color: OV.sageDeep, letterSpacing: '0.06em', textTransform: 'uppercase' }}>Longest active</div>
          <div style={{ display: 'flex', alignItems: 'baseline', gap: 6 }}>
            <span style={{ fontFamily: OLIVIA.fontDisplay, fontSize: 30, fontWeight: 600, color: OV.ink }}>{OV_DATA.longestStreak}</span>
            <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 13, color: OV.inkSoft }}>days</span>
          </div>
        </div>
      </div>
      <div>
        <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700, color: OV.inkMute, letterSpacing: '0.08em', textTransform: 'uppercase', marginBottom: 10 }}>Current streak distribution</div>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 9 }}>
          {OV_DATA.streakBuckets.map(b => (
            <div key={b.label} style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
              <span style={{ width: 78, fontFamily: OLIVIA.fontBody, fontSize: 12.5, color: OV.inkSoft, flexShrink: 0 }}>{b.label}</span>
              <div style={{ flex: 1, height: 12, borderRadius: 999, background: OV.panelMute, overflow: 'hidden' }}>
                <div style={{ width: `${(b.count / max) * 100}%`, height: '100%', borderRadius: 999, background: 'linear-gradient(90deg, #E6A07E, #D97757)' }}/>
              </div>
              <span style={{ width: 52, textAlign: 'right', fontFamily: OLIVIA.fontBody, fontSize: 12.5, color: OV.inkMute, fontVariantNumeric: 'tabular-nums' }}>{ovFmt(b.count)}</span>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

function OvMilestoneFunnel() {
  const total = OV_DATA.signups.total;
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
      {OV_DATA.milestones.map((m, i) => {
        const pct = ovPctOf(m.count, total);
        const isLast = i === OV_DATA.milestones.length - 1;
        return (
          <div key={m.id} style={{ display: 'flex', alignItems: 'center', gap: 14 }}>
            <div style={{
              width: 30, height: 30, borderRadius: '50%', flexShrink: 0,
              background: isLast ? OV.amberWash : OV.accentWash,
              color: isLast ? OV.amber : OV.accentDeep,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
            }}>
              <Icon name={isLast ? 'flame' : i === 2 ? 'book' : 'sparkle'} size={15} color={isLast ? OV.amber : OV.accentDeep}/>
            </div>
            <div style={{ flex: 1 }}>
              <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', gap: 10, marginBottom: 4 }}>
                <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 13.5, fontWeight: 600, color: OV.ink, whiteSpace: 'nowrap' }}>{m.label}</span>
                <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 12.5, color: OV.inkMute, fontVariantNumeric: 'tabular-nums', whiteSpace: 'nowrap', flexShrink: 0 }}>{ovFmt(m.count)} · {pct}%</span>
              </div>
              <div style={{ height: 9, borderRadius: 999, background: OV.panelMute, overflow: 'hidden' }}>
                <div style={{ width: `${pct}%`, height: '100%', borderRadius: 999, background: isLast ? OV.amber : 'linear-gradient(90deg,#E6A07E,#D97757)' }}/>
              </div>
            </div>
          </div>
        );
      })}
    </div>
  );
}

function OvDropOffChart() {
  const data = OV_DATA.dropOff;
  const max = Math.max(...data);
  const peakIdx = data.indexOf(max);
  return (
    <div>
      <div style={{ display: 'flex', alignItems: 'flex-end', gap: 4, height: 190, padding: '0 2px' }}>
        {data.map((v, i) => {
          const peak = i === peakIdx;
          const hl = i >= 1 && i <= 4;
          return (
            <div key={i} style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 6, height: '100%', justifyContent: 'flex-end' }}>
              {peak && (
                <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700, color: OV.rose, whiteSpace: 'nowrap', marginBottom: 2 }}>{ovFmt(v)}</div>
              )}
              <div title={`Day ${i + 1}: ${ovFmt(v)} families`} style={{
                width: '100%', maxWidth: 22, borderRadius: '5px 5px 2px 2px',
                height: `${(v / max) * 100}%`,
                background: peak ? OV.rose : hl ? '#E59A7C' : '#E7C9B6',
              }}/>
              <span style={{
                fontFamily: OLIVIA.fontBody, fontSize: 9.5,
                color: peak ? OV.rose : OV.inkMute, fontWeight: peak ? 700 : 500,
                fontVariantNumeric: 'tabular-nums',
              }}>{(i + 1) % 2 === 1 || peak ? i + 1 : ''}</span>
            </div>
          );
        })}
      </div>
      <div style={{
        marginTop: 14, display: 'flex', alignItems: 'flex-start', gap: 10,
        background: OV.roseWash, border: `1px solid rgba(197,107,110,0.25)`,
        borderRadius: 12, padding: '12px 14px',
      }}>
        <Icon name="flame" size={16} color={OV.rose}/>
        <p style={{ margin: 0, fontFamily: OLIVIA.fontBody, fontSize: 13, lineHeight: 1.5, color: '#7A3A3C' }}>
          <strong>Day {peakIdx + 1} is the biggest drop-off</strong> — {ovFmt(max)} families end their streak here, inside the steep days 2–5 window. Families who reach day 7 are far likelier to stay; consider a lighter day-3 task or an encouragement nudge.
        </p>
      </div>
    </div>
  );
}

function OvLegend({ color, label }) {
  return (
    <span style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontFamily: OLIVIA.fontBody, fontSize: 12, color: OV.inkSoft }}>
      <span style={{ width: 9, height: 9, borderRadius: 3, background: color }}/>{label}
    </span>
  );
}

function OvLegendStat({ color, value, label }) {
  return (
    <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
      <span style={{ width: 12, height: 12, borderRadius: 4, background: color, flexShrink: 0 }}/>
      <div>
        <div style={{ fontFamily: OLIVIA.fontDisplay, fontSize: 18, fontWeight: 600, color: OV.ink, lineHeight: 1 }}>{value}</div>
        <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, color: OV.inkMute, marginTop: 2 }}>{label}</div>
      </div>
    </div>
  );
}

function OvBreakdownBars({ data, colors = [] }) {
  if (!data || data.length === 0) {
    return (
      <div style={{ padding: '20px 0', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 13, color: OV.inkMute }}>No data yet</span>
      </div>
    );
  }
  const total = data.reduce((s, r) => s + r.count, 0);
  const max   = Math.max(...data.map(r => r.count));
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 11, marginTop: 10 }}>
      {data.map((row, i) => (
        <div key={row.label}>
          <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 5 }}>
            <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 13, color: OV.ink, fontWeight: i === 0 ? 600 : 400 }}>{row.label}</span>
            <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, color: OV.inkMute, fontVariantNumeric: 'tabular-nums' }}>
              {ovFmt(row.count)} · {Math.round((row.count / total) * 100)}%
            </span>
          </div>
          <div style={{ height: 8, borderRadius: 999, background: OV.panelMute, overflow: 'hidden' }}>
            <div style={{ width: `${(row.count / max) * 100}%`, height: '100%', borderRadius: 999, background: colors[i] || OV.accent, transition: 'width 0.4s ease' }}/>
          </div>
        </div>
      ))}
    </div>
  );
}

function OverviewPage({ childrenPerFamily = 1.8, liveStats = null, statsLoading = false }) {
  const [range, setRange] = React.useState('all');
  const [customStart, setCustomStart] = React.useState('');
  const [customEnd,   setCustomEnd]   = React.useState('');
  const d = OV_DATA;
  const rangeLabel = range === '7d' ? 'week' : range === '30d' ? 'month' : range === 'custom' ? 'selected period' : 'time';

  // Slice raw trend snapshots to the selected window
  const rawTrend = liveStats?.trendRaw || [];
  const sliced = range === 'all'
    ? rawTrend
    : range === 'custom'
      ? rawTrend.filter(r => (!customStart || r.date >= customStart) && (!customEnd || r.date <= customEnd))
      : rawTrend.slice(-(range === '7d' ? 7 : 30));
  const hasEnough    = sliced.length >= 2;
  const first        = sliced[0];
  const last         = sliced[sliced.length - 1];
  const signupTrend  = hasEnough ? sliced.map(r => r.signup_count)  : null;
  const activeTrend  = hasEnough ? sliced.map(r => r.active_count)  : null;

  // KPI values — "all" shows totals, other ranges show gain within the window
  const isAll = range === 'all';
  const signupVal = statsLoading ? '—' : !liveStats ? '—'
    : isAll ? ovFmt(liveStats.signupCount)
    : hasEnough ? `+${ovFmt(last.signup_count - first.signup_count)}`
    : ovFmt(liveStats.signupCount);

  const childrenVal = statsLoading ? '—' : !liveStats ? '—'
    : isAll ? ovFmt(liveStats.childrenCount)
    : hasEnough ? `+${ovFmt(last.children_count - first.children_count)}`
    : '—';

  const activeVal = statsLoading ? '—' : liveStats ? ovFmt(liveStats.activeCount) : '—';

  const avgStreakVal = statsLoading ? '—' : !liveStats ? '—'
    : hasEnough ? `${last.avg_streak} days`
    : `${liveStats.avgStreak} days`;

  const childrenSub = isAll
    ? (liveStats && liveStats.childrenCount > 0 ? 'from family profiles' : 'est. from family accounts')
    : `new children this ${rangeLabel}`;
  const longestVal = statsLoading ? '—' : liveStats ? liveStats.longestStreak : d.longestStreak;

  return (
    <div style={{ flex: 1, overflow: 'auto', background: OV.pageBg }}>
      <div style={{ padding: '22px 28px 40px', maxWidth: 1320, margin: '0 auto' }}>

        {/* Title row */}
        <div style={{ display: 'flex', alignItems: 'flex-end', justifyContent: 'space-between', marginBottom: 20 }}>
          <div>
            <h1 style={{ margin: 0, fontFamily: OLIVIA.fontDisplay, fontSize: 28, fontWeight: 600, color: OV.ink, letterSpacing: '-0.015em' }}>Overview</h1>
            <p style={{ margin: '4px 0 0', fontFamily: OLIVIA.fontBody, fontSize: 14, color: OV.inkSoft }}>How families are growing with Olivia this {rangeLabel}.</p>
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-end', gap: 8 }}>
            <div style={{ display: 'flex', gap: 2, background: OV.panel, border: `1px solid ${OV.hair}`, borderRadius: 999, padding: 3 }}>
              {[['all', 'All time'], ['7d', '7 days'], ['30d', '30 days'], ['custom', 'Custom']].map(([k, label]) => {
                const on = range === k;
                return (
                  <button key={k} onClick={() => setRange(k)} style={{
                    appearance: 'none', border: 'none', cursor: 'pointer',
                    padding: '7px 14px', borderRadius: 999,
                    background: on ? OV.ink : 'transparent', color: on ? OV.panel : OV.inkSoft,
                    fontFamily: OLIVIA.fontBody, fontSize: 12.5, fontWeight: on ? 600 : 500,
                  }}>{label}</button>
                );
              })}
            </div>
            {range === 'custom' && (
              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                <input type="date" value={customStart} onChange={e => setCustomStart(e.target.value)}
                  max={customEnd || undefined}
                  style={{
                    fontFamily: OLIVIA.fontBody, fontSize: 13, color: OV.ink,
                    background: OV.panel, border: `1px solid ${OV.hairStrong}`,
                    borderRadius: 10, padding: '6px 12px', outline: 'none', cursor: 'pointer',
                  }}/>
                <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 13, color: OV.inkMute }}>to</span>
                <input type="date" value={customEnd} onChange={e => setCustomEnd(e.target.value)}
                  min={customStart || undefined}
                  style={{
                    fontFamily: OLIVIA.fontBody, fontSize: 13, color: OV.ink,
                    background: OV.panel, border: `1px solid ${OV.hairStrong}`,
                    borderRadius: 10, padding: '6px 12px', outline: 'none', cursor: 'pointer',
                  }}/>
              </div>
            )}
          </div>
        </div>

        {/* KPI row */}
        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 14, marginBottom: 14 }}>
          <OvKpiCard label="Total sign-ups" value={signupVal}
            sub={statsLoading ? 'Loading…' : isAll ? 'all time' : `new this ${rangeLabel}`}
            delta={isAll && !statsLoading ? <OvDelta now={liveStats ? liveStats.signupCount : d.signups.week} prev={liveStats ? liveStats.prevSignupCount : d.signups.prevWeek}/> : null}/>
          <OvKpiCard label="Children reached" value={childrenVal}
            sub={statsLoading ? 'Loading…' : childrenSub}
            delta={isAll && !statsLoading ? <OvDelta now={liveStats ? liveStats.childrenCount : d.childrenCount.total} prev={liveStats ? liveStats.prevChildrenCount : d.childrenCount.prev}/> : null}/>
          <OvKpiCard label="Active families" value={activeVal}
            sub={statsLoading ? 'Loading…' : '7-day active window'}
            delta={!statsLoading ? <OvDelta now={liveStats ? liveStats.activeCount : d.active.count} prev={liveStats ? liveStats.prevActiveCount : d.active.prev}/> : null}/>
          <OvKpiCard label="Avg. streak" value={avgStreakVal}
            sub={statsLoading ? 'Loading…' : `Longest active · ${longestVal} days`}
            delta={isAll && !statsLoading ? <OvDelta now={liveStats ? liveStats.avgStreak * 10 : d.avgStreak * 10} prev={liveStats ? liveStats.prevAvgStreak * 10 : d.prevAvgStreak * 10}/> : null}/>
        </div>

        {/* Growth + What's resonating */}
        <div style={{ display: 'grid', gridTemplateColumns: '1.6fr 1fr', gap: 14 }}>
          <OvPanel title="Growth" hint="New sign-ups and active families over the last 12 weeks."
            action={
              <div style={{ display: 'flex', gap: 14 }}>
                <OvLegend color={OV.accent} label="Sign-ups"/>
                <OvLegend color={OV.sage} label="Active families"/>
              </div>
            }>
            <div style={{ marginTop: 8 }}>
              {hasEnough ? (
                <>
                  <OvAreaChart values={activeTrend} color={OV.sage} fill="rgba(111,160,106,0.12)" h={130}/>
                  <div style={{ marginTop: -8 }}>
                    <OvAreaChart values={signupTrend} color={OV.accent} fill="rgba(217,119,87,0.10)" h={70}/>
                  </div>
                </>
              ) : (
                <div style={{
                  height: 200, display: 'flex', flexDirection: 'column',
                  alignItems: 'center', justifyContent: 'center', gap: 8,
                }}>
                  <Icon name="sparkle" size={22} color={OV.inkMute}/>
                  <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 14, color: OV.inkMute, textAlign: 'center', lineHeight: 1.5 }}>
                    Not enough data to display yet.<br/>Check back tomorrow.
                  </span>
                </div>
              )}
            </div>
          </OvPanel>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
            <OvPanel title="Children by age band" hint="How old the children using Olivia are.">
              <OvBreakdownBars data={liveStats?.byAgeBand} colors={[OV.accent, OV.sage, OV.sky, OV.amber, OV.rose, OV.inkMute]}/>
            </OvPanel>
            <OvPanel title="Families by relationship" hint="Who is leading the faith journey.">
              <OvBreakdownBars data={liveStats?.byRelationship} colors={[OV.sage, OV.accent, OV.sky, OV.amber, OV.rose, OV.inkMute]}/>
            </OvPanel>
          </div>
        </div>

      </div>
    </div>
  );
}

// ─── Status pill helper ───────────────────────────────────────
function StatusPill({ status, size = 'sm' }) {
  const s = STATUSES[status] || STATUSES.draft;
  const px = size === 'sm' ? '3px 8px' : '5px 10px';
  const fs = size === 'sm' ? 11 : 12;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 6,
      padding: px, borderRadius: 999,
      background: s.bg, color: s.fg,
      fontFamily: OLIVIA.fontBody, fontSize: fs, fontWeight: 600,
      letterSpacing: '0.01em',
    }}>
      <span style={{ width: 6, height: 6, borderRadius: '50%', background: s.dot }}/>
      {s.label}
    </span>
  );
}

function PlaceholderCard() {
  const bar = (w, h = 10) => (
    <div style={{ width: w, height: h, background: ADMIN.hair, borderRadius: 4 }}/>
  );
  return (
    <div style={{
      borderRadius: 10, marginBottom: 2, padding: '10px 10px 10px 8px',
      border: `1px dashed ${ADMIN.hair}`,
      display: 'flex', gap: 6, alignItems: 'flex-start', opacity: 0.55,
    }}>
      <div style={{ paddingTop: 2, flexShrink: 0 }}>
        <Icon name="grip" size={16} color={ADMIN.inkMute}/>
      </div>
      <div style={{ flex: 1, display: 'flex', flexDirection: 'column', gap: 7 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          {bar('30%')} <div style={{ flex: 1 }}/> {bar(48, 18)}
        </div>
        {bar('80%', 12)}
        {bar('55%', 12)}
        <div style={{ display: 'flex', gap: 8 }}>
          {bar(52, 18)} <div style={{ flex: 1 }}/> {bar(40, 10)}
        </div>
      </div>
    </div>
  );
}

// ─── List pane ────────────────────────────────────────────────
function AdminListPane({ items, totalItems, selectedId, onSelect, onNewCard,
                         statusFilter, setStatusFilter, width = 300, kinds = [] }) {
  const [sortBy, setSortBy] = React.useState('newest');

  const sorted = React.useMemo(() => {
    const copy = [...items];
    return sortBy === 'alpha'
      ? copy.sort((a, b) => (a.task || '').localeCompare(b.task || ''))
      : copy.sort((a, b) => b.id.localeCompare(a.id));
  }, [items, sortBy]);

  return (
    <div style={{
      width, flexShrink: 0,
      background: ADMIN.panel,
      display: 'flex', flexDirection: 'column', minHeight: 0,
      overflow: 'hidden',
    }}>
      {/* Header */}
      <div style={{ padding: '14px 16px 8px', borderBottom: `1px solid ${ADMIN.hair}` }}>
        <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between' }}>
          <h2 style={{
            margin: 0, fontFamily: OLIVIA.fontDisplay,
            fontSize: 18, fontWeight: 600, color: ADMIN.ink, letterSpacing: '-0.01em',
          }}>Content library</h2>
          <span style={{
            fontFamily: OLIVIA.fontBody, fontSize: 11, color: ADMIN.inkMute,
            fontVariantNumeric: 'tabular-nums',
          }}>{items.length}/{totalItems}</span>
        </div>

        {/* Status filter */}
        <div style={{ marginTop: 10, display: 'flex', gap: 4, flexWrap: 'wrap' }}>
          {[
            ['all', 'All'],
            ['draft', 'Draft'],
            ['review', 'Review'],
            ['approved', 'Approved'],
          ].map(([k, label]) => {
            const on = statusFilter === k;
            return (
              <button key={k} onClick={() => setStatusFilter(k)} style={{
                appearance: 'none', border: 'none', cursor: 'pointer',
                padding: '4px 10px', borderRadius: 999,
                background: on ? ADMIN.ink : 'transparent',
                color: on ? ADMIN.panel : ADMIN.inkSoft,
                fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: on ? 600 : 500,
                letterSpacing: '0.01em',
              }}>{label}</button>
            );
          })}
        </div>

        {/* Sort toggle */}
        <div style={{ marginTop: 16 }}>
        <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700, color: ADMIN.inkMute, letterSpacing: '0.08em', textTransform: 'uppercase', marginBottom: 6 }}>Sort by</div>
        <div style={{ display: 'flex', gap: 2, background: ADMIN.pageBg, borderRadius: 8, padding: 2, alignSelf: 'flex-start', width: 'fit-content' }}>
          {[['newest', 'Newest'], ['alpha', 'A → Z']].map(([k, label]) => {
            const on = sortBy === k;
            return (
              <button key={k} onClick={() => setSortBy(k)} style={{
                appearance: 'none', border: 'none', cursor: 'pointer',
                padding: '3px 10px', borderRadius: 6,
                background: on ? ADMIN.panel : 'transparent',
                color: on ? ADMIN.ink : ADMIN.inkMute,
                fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: on ? 600 : 400,
                boxShadow: on ? `0 1px 2px rgba(58,46,38,0.08)` : 'none',
              }}>{label}</button>
            );
          })}
        </div>
      </div>
      </div>

      {/* List */}
      <div style={{ flex: 1, overflowY: 'auto', padding: '6px 8px' }}>
        {totalItems === 0 && <PlaceholderCard/>}
        {sorted.map(it => (
          <ListItem
            key={it.id} item={it} selected={it.id === selectedId}
            onSelect={() => onSelect(it.id)} kinds={kinds}
          />
        ))}
        {totalItems > 0 && items.length === 0 && (
          <div style={{ padding: '24px 16px', color: ADMIN.inkMute, fontFamily: OLIVIA.fontBody, fontSize: 13, textAlign: 'center' }}>
            No cards match these filters.
          </div>
        )}
      </div>

      {/* Footer — always-visible add button + count */}
      <div style={{
        borderTop: `1px solid ${ADMIN.hair}`, padding: '10px 12px',
        display: 'flex', alignItems: 'center', justifyContent: 'space-between',
        background: ADMIN.panelMute, gap: 8,
      }}>
        <div style={{
          display: 'flex', alignItems: 'center', gap: 6,
          fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.inkSoft,
        }}>
          <Icon name="sparkle" size={13} color={ADMIN.sage}/>
          {totalItems} {totalItems === 1 ? 'card' : 'cards'}
        </div>
        <button onClick={onNewCard} style={{
          appearance: 'none', border: 'none', cursor: 'pointer',
          background: ADMIN.accent, color: '#FFFCF7',
          borderRadius: 8, padding: '6px 12px',
          fontFamily: OLIVIA.fontBody, fontSize: 12, fontWeight: 600,
          display: 'inline-flex', alignItems: 'center', gap: 5,
        }}>
          <Icon name="plus" size={13} color="#FFFCF7" strokeWidth={2.6}/>
          Add card
        </button>
      </div>
    </div>
  );
}

function ListItem({ item, selected, onSelect, kinds = [] }) {
  const kindObj = kinds.find(k => k.name === item.kind);
  const ageLabel = !kindObj ? '' :
    kindObj.ageRanges.includes('all') ? 'All ages' :
    kindObj.ageRanges.join(' · ');

  return (
    <button onClick={onSelect} style={{
      appearance: 'none', border: 'none', cursor: 'pointer',
      display: 'flex', width: '100%', textAlign: 'left',
      padding: '10px 12px', borderRadius: 10, marginBottom: 2,
      background: selected ? ADMIN.accentWash : 'transparent',
      outline: selected ? `1px solid ${ADMIN.accent}` : 'none',
      outlineOffset: -1, flexDirection: 'column', gap: 4,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
        {ageLabel && (
          <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, color: ADMIN.inkMute }}>
            {ageLabel}
          </span>
        )}
        <div style={{ flex: 1 }}/>
        <StatusPill status={item.status}/>
      </div>
      <div style={{
        fontFamily: OLIVIA.fontDisplay, fontSize: 14, fontWeight: 500,
        color: ADMIN.ink, lineHeight: 1.35,
        overflow: 'hidden', display: '-webkit-box',
        WebkitLineClamp: 2, WebkitBoxOrient: 'vertical',
      }}>{(item.task || 'New card').replace(/\{child\}/g, '[child]')}</div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
        <span style={{
          padding: '2px 7px', borderRadius: 999,
          background: ADMIN.panelMute, color: ADMIN.inkSoft,
          fontFamily: OLIVIA.fontBody, fontSize: 10, fontWeight: 600,
          letterSpacing: '0.02em',
        }}>{item.kind}</span>
        <span style={{
          fontFamily: OLIVIA.fontBody, fontSize: 11, color: ADMIN.inkMute,
          marginLeft: 'auto',
        }}>{item.updatedAt}</span>
      </div>
    </button>
  );
}

// ─── Editor pane ──────────────────────────────────────────────
function AdminEditorPane({ item, onChange, onTheology, onNewCard, onDelete, kinds = [], dayNum }) {
  const [saved, setSaved] = React.useState(false);
  const saveTimer = React.useRef(null);

  React.useEffect(() => {
    setSaved(false);
    clearTimeout(saveTimer.current);
  }, [item?.id]);

  React.useEffect(() => () => clearTimeout(saveTimer.current), []);

  const triggerSave = () => {
    setSaved(true);
    clearTimeout(saveTimer.current);
    saveTimer.current = setTimeout(() => setSaved(false), 2000);
  };

  if (!item) return (
    <div style={{
      flex: 1, minWidth: 0, background: ADMIN.pageBg,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
    }}>
      <span style={{
        fontFamily: OLIVIA.fontBody, fontSize: 14, color: ADMIN.inkMute,
      }}>No card selected</span>
    </div>
  );

  const locked = item.status === 'approved' || item.status === 'shipped';
  const setField = (k) => (v) => { if (locked && k !== 'notes') return; onChange({ [k]: v }); triggerSave(); };
  const handleTheology = (id, v) => { if (locked) return; onTheology(id, v); triggerSave(); };

  return (
    <div style={{
      flex: 1, minWidth: 0, overflow: 'auto',
      background: ADMIN.pageBg,
    }}>
      {/* Editor header */}
      <div style={{
        position: 'sticky', top: 0, zIndex: 5,
        background: 'rgba(245,239,230,0.94)', backdropFilter: 'blur(10px)',
        borderBottom: `1px solid ${ADMIN.hair}`,
        padding: '14px 28px',
        display: 'flex', alignItems: 'center', gap: 12,
      }}>
        <div style={{ flex: 1 }}>
          <div style={{
            fontFamily: OLIVIA.fontDisplay, fontSize: 19, fontWeight: 600,
            color: ADMIN.ink, letterSpacing: '-0.01em',
          }}>{item.kind || 'New card'}</div>
        </div>
        <span style={{
          fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.sage,
          display: 'inline-flex', alignItems: 'center', gap: 4,
          opacity: saved ? 1 : 0, transition: 'opacity 0.4s',
        }}>
          <Icon name="check" size={12} color={ADMIN.sage} strokeWidth={2.5}/>
          Saved
        </span>
        <StatusPill status={item.status} size="md"/>
        {item.status === 'review' && (
          <React.Fragment>
            {(() => {
              const allCleared = THEOLOGY_CHECKS.every(c => item.theology?.[c.id] === true);
              return (
                <button
                  onClick={() => onChange({ status: 'approved' })}
                  disabled={!allCleared}
                  title={allCleared ? '' : 'Complete the theology checklist before approving'}
                  style={{
                    ...editorBtn(true),
                    opacity: allCleared ? 1 : 0.4,
                    cursor: allCleared ? 'pointer' : 'not-allowed',
                  }}>
                  <Icon name="check" size={14} color="#FFFCF7" strokeWidth={2.6}/> Approve
                </button>
              );
            })()}
            <button onClick={() => onChange({ status: 'draft' })} style={editorBtn(false)}>
              Request changes
            </button>
          </React.Fragment>
        )}
        {item.status === 'draft' && (() => {
          const ready = (item.task || '').trim().length > 0 && (item.tip || '').trim().length > 0;
          return (
            <button
              onClick={() => onChange({ status: 'review' })}
              disabled={!ready}
              title={ready ? '' : 'Task prompt and contextual tip are required'}
              style={{
                ...editorBtn(true),
                opacity: ready ? 1 : 0.4,
                cursor: ready ? 'pointer' : 'not-allowed',
              }}>
              Submit for review →
            </button>
          );
        })()}
        <button
          onClick={() => { if (window.confirm('Delete this card? This cannot be undone.')) onDelete(); }}
          title="Delete card"
          style={{
            appearance: 'none', border: `1px solid ${ADMIN.hair}`, cursor: 'pointer',
            background: ADMIN.panel, color: ADMIN.inkMute,
            borderRadius: 9, padding: '7px 10px',
            display: 'inline-flex', alignItems: 'center',
          }}>
          <Icon name="trash" size={15} strokeWidth={1.8}/>
        </button>
      </div>

      {locked && (
        <div style={{
          margin: '14px 28px 0',
          background: ADMIN.panelMute, border: `1px solid ${ADMIN.hair}`,
          borderRadius: 12, padding: '10px 14px',
          display: 'flex', alignItems: 'center', gap: 8,
        }}>
          <Icon name="lock" size={14} color={ADMIN.inkMute}/>
          <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 13, color: ADMIN.inkSoft, flex: 1 }}>
            This card is <strong>{item.status}</strong>. Only internal notes can be edited.
          </span>
          <button
            onClick={() => {
              if (!window.confirm('Reopen this card for editing? It will return to Draft and need to go through review again.')) return;
              onChange({ status: 'draft' });
            }}
            style={{
              appearance: 'none', border: `1px solid ${ADMIN.hair}`, cursor: 'pointer',
              background: ADMIN.panel, color: ADMIN.inkSoft,
              borderRadius: 8, padding: '5px 12px',
              fontFamily: OLIVIA.fontBody, fontSize: 12, fontWeight: 600,
              whiteSpace: 'nowrap',
            }}>
            Reopen for editing
          </button>
        </div>
      )}

      {item.status === 'draft' && item.reviewNote && (
        <div style={{
          margin: '14px 28px 0', background: '#F5DBDC', border: `1px solid #E5B8BB`,
          borderRadius: 12, padding: '12px 14px',
          display: 'flex', alignItems: 'flex-start', gap: 10,
        }}>
          <Icon name="heart" size={16} color="#8E3236"/>
          <div style={{ flex: 1 }}>
            <div style={{
              fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700,
              color: '#8E3236', letterSpacing: '0.06em', textTransform: 'uppercase',
            }}>{item.reviewer} requested changes</div>
            <p style={{
              fontFamily: OLIVIA.fontDisplay, fontStyle: 'italic',
              fontSize: 14, lineHeight: 1.5, color: '#5C2024',
              margin: '4px 0 0',
            }}>{item.reviewNote}</p>
          </div>
        </div>
      )}

      {/* Metadata row */}
      {(() => {
        const kindObj = kinds.find(k => k.name === item.kind);
        const ageRanges = kindObj?.ageRanges || ['all'];
        return (
          <div style={{ padding: '20px 28px 0', display: 'grid', gridTemplateColumns: '1fr 1fr 1fr', gap: 14 }}>
            <Field label="Kind">
              <Select value={item.kind} onChange={setField('kind')} options={kinds.map(k => k.name)} disabled={locked}/>
            </Field>
            <Field label="Age range">
              <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap', paddingTop: 2 }}>
                {ageRanges.map(r => (
                  <span key={r} style={{
                    padding: '4px 10px', borderRadius: 999,
                    background: ADMIN.panelMute, border: `1px solid ${ADMIN.hair}`,
                    fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.inkSoft,
                  }}>{r === 'all' ? 'All ages' : r}</span>
                ))}
              </div>
              <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, color: ADMIN.inkMute, marginTop: 5 }}>set on the Kind</div>
            </Field>
            <Field label="Estimated time">
              <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                <NumberInput value={item.estMinutes} onChange={setField('estMinutes')} small disabled={locked}/>
                <span style={{ color: ADMIN.inkMute, fontFamily: OLIVIA.fontBody, fontSize: 13 }}>min</span>
              </div>
            </Field>
          </div>
        );
      })()}

      {/* Task */}
      <Field padding label="Task prompt"
        hint={`Shown to the parent on the home screen. Use {child} as a placeholder — it'll be replaced with the kid's name. 1–2 sentences.`}>
        <TextArea value={item.task} onChange={setField('task')} rows={3} disabled={locked}/>
        <Counter value={item.task} max={200}/>
      </Field>

      {/* Tip */}
      <Field padding label="Contextual tip"
        hint={`Why this matters. 2–4 sentences. Coaches the parent, never the child.`}>
        <TextArea value={item.tip} onChange={setField('tip')} rows={4} disabled={locked}/>
        <Counter value={item.tip} max={400}/>
      </Field>

      {/* Prayer */}
      <Field padding label="Prayer text" optional
        hint="Lines the parent reads aloud with the child. Short, plain words. Leave empty for non-prayer cards.">
        <PrayerEditor value={item.prayer} onChange={setField('prayer')} locked={locked}/>
      </Field>

      {/* Theology checklist — only visible once submitted for review */}
      {item.status !== 'draft' && (
        <div style={{ padding: '24px 28px 16px' }}>
          <div style={{
            display: 'flex', alignItems: 'baseline', justifyContent: 'space-between',
            marginBottom: 10,
          }}>
            <h3 style={{
              margin: 0, fontFamily: OLIVIA.fontDisplay, fontSize: 16, fontWeight: 600, color: ADMIN.ink,
            }}>Theology checklist</h3>
            <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.inkMute }}>
              {THEOLOGY_CHECKS.filter(c => item.theology?.[c.id] === true).length}/{THEOLOGY_CHECKS.length} cleared
            </span>
          </div>
          <div style={{
            background: ADMIN.panel, borderRadius: 14, border: `1px solid ${ADMIN.hair}`,
            padding: '4px 0', overflow: 'hidden',
          }}>
            {THEOLOGY_CHECKS.map((c, i) => (
              <CheckRow key={c.id} check={c}
                value={item.theology?.[c.id]}
                onChange={(v) => handleTheology(c.id, v)}
                isLast={i === THEOLOGY_CHECKS.length - 1}
                disabled={locked}/>
            ))}
          </div>
        </div>
      )}

      {/* Internal notes */}
      <Field padding label="Internal notes" optional
        hint="Reviewer ↔ creator back-and-forth. Not shown in the app.">
        <TextArea value={item.notes || ''} onChange={setField('notes')} rows={3}/>
      </Field>
      <div style={{ height: 28 }}/>
    </div>
  );
}

function editorBtn(primary) {
  return {
    appearance: 'none', border: primary ? 'none' : `1px solid ${ADMIN.hair}`,
    cursor: 'pointer',
    background: primary ? ADMIN.accent : ADMIN.panel,
    color: primary ? '#FFFCF7' : ADMIN.ink,
    borderRadius: 9, padding: '7px 12px',
    fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 600,
    display: 'inline-flex', alignItems: 'center', gap: 6,
  };
}

function Field({ label, hint, optional, children, padding }) {
  return (
    <div style={{ padding: padding ? '18px 28px 0' : 0 }}>
      <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between', marginBottom: 6 }}>
        <div style={{
          fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700,
          color: ADMIN.inkSoft, letterSpacing: '0.08em', textTransform: 'uppercase',
        }}>
          {label}{optional && <span style={{ color: ADMIN.inkMute, marginLeft: 8, textTransform: 'none', letterSpacing: 0, fontWeight: 500 }}>optional</span>}
        </div>
      </div>
      {hint && (
        <p style={{
          margin: '0 0 8px', fontFamily: OLIVIA.fontBody, fontSize: 12, lineHeight: 1.5,
          color: ADMIN.inkMute, textWrap: 'pretty',
        }}>{hint}</p>
      )}
      {children}
    </div>
  );
}

function TextArea({ value, onChange, rows = 3, disabled }) {
  return (
    <textarea value={value || ''} onChange={(e) => onChange(e.target.value)} rows={rows}
      disabled={disabled}
      style={{
        width: '100%', boxSizing: 'border-box',
        background: ADMIN.panel, border: `1px solid ${ADMIN.hair}`,
        borderRadius: 12, padding: '12px 14px',
        fontFamily: OLIVIA.fontDisplay, fontSize: 16, lineHeight: 1.5,
        color: ADMIN.ink, outline: 'none', resize: 'vertical',
        opacity: disabled ? 0.55 : 1, cursor: disabled ? 'not-allowed' : 'auto',
      }}
      onFocus={(e) => { if (!disabled) e.target.style.borderColor = ADMIN.accent; }}
      onBlur={(e) => e.target.style.borderColor = ADMIN.hair}
    />
  );
}

function TextInput({ value, onChange, placeholder }) {
  return (
    <input value={value} onChange={(e) => onChange(e.target.value)} placeholder={placeholder}
      style={{
        width: '100%', boxSizing: 'border-box',
        background: ADMIN.panel, border: `1px solid ${ADMIN.hair}`,
        borderRadius: 10, padding: '10px 14px',
        fontFamily: OLIVIA.fontBody, fontSize: 14, color: ADMIN.ink, outline: 'none',
      }}
      onFocus={(e) => e.target.style.borderColor = ADMIN.accent}
      onBlur={(e) => e.target.style.borderColor = ADMIN.hair}
    />
  );
}

function NumberInput({ value, onChange, small, disabled }) {
  return (
    <input type="number" value={value} onChange={(e) => onChange(parseInt(e.target.value, 10) || 0)}
      disabled={disabled}
      style={{
        width: small ? 64 : '100%', boxSizing: 'border-box',
        background: ADMIN.panel, border: `1px solid ${ADMIN.hair}`,
        borderRadius: 10, padding: '8px 12px',
        fontFamily: OLIVIA.fontBody, fontSize: 14, color: ADMIN.ink, outline: 'none',
        fontVariantNumeric: 'tabular-nums',
        opacity: disabled ? 0.55 : 1, cursor: disabled ? 'not-allowed' : 'auto',
      }}/>
  );
}

function Select({ value, onChange, options, disabled }) {
  return (
    <select value={value} onChange={(e) => onChange(e.target.value)}
      disabled={disabled}
      style={{
        width: '100%', boxSizing: 'border-box',
        background: ADMIN.panel, border: `1px solid ${ADMIN.hair}`,
        borderRadius: 10, padding: '8px 12px',
        fontFamily: OLIVIA.fontBody, fontSize: 14, color: ADMIN.ink, outline: 'none',
        appearance: 'none',
        backgroundImage: `url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%237A6E66' stroke-width='2'><path d='M6 9l6 6 6-6'/></svg>")`,
        backgroundRepeat: 'no-repeat', backgroundPosition: 'right 10px center',
        paddingRight: 28,
        opacity: disabled ? 0.55 : 1, cursor: disabled ? 'not-allowed' : 'auto',
      }}>
      {options.map(o => <option key={o} value={o}>{o}</option>)}
    </select>
  );
}

function Counter({ value, max }) {
  const len = (value || '').length;
  const warn = len > max;
  return (
    <div style={{
      marginTop: 6, textAlign: 'right',
      fontFamily: OLIVIA.fontBody, fontSize: 11,
      color: warn ? ADMIN.accentDeep : ADMIN.inkMute,
      fontVariantNumeric: 'tabular-nums',
    }}>{len} / {max}</div>
  );
}

function PrayerEditor({ value, onChange, locked }) {
  const lines = value || [];
  const setLine = (i, v) => onChange(lines.map((l, j) => j === i ? v : l));
  const removeLine = (i) => onChange(lines.filter((_, j) => j !== i));
  const addLine = () => onChange([...lines, '']);
  return (
    <div style={{
      background: ADMIN.panel, border: `1px solid ${ADMIN.hair}`, borderRadius: 12,
      padding: 8, opacity: locked ? 0.55 : 1,
    }}>
      {lines.length === 0 && (
        <div style={{ padding: '20px 14px', textAlign: 'center', color: ADMIN.inkMute, fontFamily: OLIVIA.fontBody, fontSize: 13 }}>
          No prayer for this card.
        </div>
      )}
      {lines.map((line, i) => (
        <div key={i} style={{
          display: 'flex', alignItems: 'center', gap: 8,
          padding: '4px 6px', borderRadius: 8,
        }}>
          <span style={{
            width: 18, textAlign: 'center', color: ADMIN.inkMute,
            fontFamily: OLIVIA.fontBody, fontSize: 10, fontWeight: 700,
          }}>{i + 1}</span>
          <input value={line} onChange={(e) => !locked && setLine(i, e.target.value)}
            readOnly={locked}
            style={{
              flex: 1, border: 'none', outline: 'none', background: 'transparent',
              fontFamily: OLIVIA.fontDisplay, fontStyle: i === 0 || i === lines.length - 1 ? 'normal' : 'italic',
              fontWeight: i === 0 || i === lines.length - 1 ? 500 : 400,
              fontSize: 15, color: ADMIN.ink, padding: '6px 4px',
              cursor: locked ? 'not-allowed' : 'auto',
            }}/>
          {!locked && (
            <button onClick={() => removeLine(i)} style={{
              appearance: 'none', border: 'none', background: 'transparent',
              cursor: 'pointer', color: ADMIN.inkMute, padding: 4,
            }}>
              <Icon name="close" size={14}/>
            </button>
          )}
        </div>
      ))}
      {!locked && (
        <button onClick={addLine} style={{
          appearance: 'none', border: `1px dashed ${ADMIN.hair}`, background: 'transparent',
          cursor: 'pointer', borderRadius: 8, width: '100%', padding: '8px',
          color: ADMIN.inkSoft, fontFamily: OLIVIA.fontBody, fontSize: 12, fontWeight: 600,
          marginTop: 4,
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6,
        }}>
          <Icon name="plus" size={14}/> Add a line
        </button>
      )}
    </div>
  );
}

function CheckRow({ check, value, onChange, isLast, disabled }) {
  const [hint, setHint] = useState(false);
  return (
    <div style={{
      padding: '10px 14px',
      borderBottom: isLast ? 'none' : `1px solid ${ADMIN.hair}`,
    }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
        <ThreeWaySwitch value={value} onChange={onChange} disabled={disabled}/>
        <div style={{ flex: 1 }}>
          <button onClick={() => setHint(!hint)} style={{
            appearance: 'none', border: 'none', background: 'transparent',
            cursor: 'pointer', padding: 0,
            fontFamily: OLIVIA.fontBody, fontSize: 14, fontWeight: 500,
            color: ADMIN.ink, textAlign: 'left',
          }}>{check.label}</button>
        </div>
      </div>
      {hint && (
        <p style={{
          margin: '6px 0 0 56px', fontFamily: OLIVIA.fontBody, fontSize: 12,
          lineHeight: 1.5, color: ADMIN.inkMute,
        }}>{check.hint}</p>
      )}
    </div>
  );
}

function ThreeWaySwitch({ value, onChange, disabled }) {
  const states = [
    { id: false, label: '✕', color: '#C56B6E', bg: '#F5DBDC' },
    { id: null,  label: '·', color: ADMIN.inkMute, bg: ADMIN.panelMute },
    { id: true,  label: '✓', color: '#4E6147', bg: '#DCE4D2' },
  ];
  return (
    <div style={{
      display: 'inline-flex', gap: 0, background: ADMIN.pageBg,
      border: `1px solid ${ADMIN.hair}`, borderRadius: 999,
      padding: 2, overflow: 'hidden', opacity: disabled ? 0.55 : 1,
    }}>
      {states.map(s => {
        const on = value === s.id;
        return (
          <button key={String(s.id)} onClick={() => !disabled && onChange(s.id)} style={{
            appearance: 'none', border: 'none',
            cursor: disabled ? 'not-allowed' : 'pointer',
            width: 28, height: 22, borderRadius: 999,
            background: on ? s.bg : 'transparent',
            color: on ? s.color : ADMIN.inkMute,
            fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 700,
            display: 'inline-flex', alignItems: 'center', justifyContent: 'center',
          }}>{s.label}</button>
        );
      })}
    </div>
  );
}

function ActivityRow({ author, when, text, isLast }) {
  return (
    <div style={{
      padding: '10px 14px', display: 'flex', gap: 10,
      borderBottom: isLast ? 'none' : `1px solid ${ADMIN.hair}`,
    }}>
      <div style={{
        width: 28, height: 28, borderRadius: '50%',
        background: ADMIN.sage, color: '#FFFCF7',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700,
        flexShrink: 0,
      }}>{(author || '?').split(' ').map(s => s[0]).join('').slice(0, 2)}</div>
      <div style={{ flex: 1 }}>
        <div style={{
          display: 'flex', alignItems: 'baseline', gap: 8,
          fontFamily: OLIVIA.fontBody, fontSize: 13,
        }}>
          <span style={{ fontWeight: 600, color: ADMIN.ink }}>{author || 'Unknown'}</span>
          <span style={{ color: ADMIN.inkMute, fontSize: 12 }}>{when}</span>
        </div>
        <p style={{
          margin: '2px 0 0', fontFamily: OLIVIA.fontBody, fontSize: 13,
          color: ADMIN.inkSoft, lineHeight: 1.4,
        }}>{text}</p>
      </div>
    </div>
  );
}

// ─── Preview pane ─────────────────────────────────────────────
function AdminPreviewPane({ item, previewChild, setPreviewChild, previewScreen, setPreviewScreen, width = 420 }) {
  const live = useMemo(() => ({
    day: item?.day || 1,
    kind: item?.kind || '',
    task: item?.task || '',
    tip: item?.tip || '',
    prayer: item?.prayer || [],
    estMinutes: item?.estMinutes || 3,
  }), [item]);

  useEffect(() => {
    window.OLIVIA_TODAY = live;
  }, [live]);

  if (!item) return (
    <div style={{
      width, flexShrink: 0, borderLeft: `1px solid ${ADMIN.hair}`,
      background: ADMIN.panelMute,
      display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
      gap: 10,
    }}>
      <div style={{ opacity: 0.18 }}>
        <svg width={80} height={80} viewBox="0 0 375 812" fill="none">
          <rect x={1} y={1} width={373} height={810} rx={54} stroke={ADMIN.ink} strokeWidth={8}/>
          <rect x={140} y={18} width={95} height={14} rx={7} fill={ADMIN.ink}/>
        </svg>
      </div>
      <div style={{
        fontFamily: OLIVIA.fontBody, fontSize: 13, color: ADMIN.inkMute,
        textAlign: 'center', maxWidth: 200, lineHeight: 1.55,
      }}>Preview will appear here once you open a card.</div>
    </div>
  );

  return (
    <div style={{
      width, flexShrink: 0, borderLeft: `1px solid ${ADMIN.hair}`,
      background: ADMIN.panelMute,
      display: 'flex', flexDirection: 'column', minHeight: 0,
    }}>
      {/* Preview header */}
      <div style={{
        padding: '12px 18px', borderBottom: `1px solid ${ADMIN.hair}`,
        background: ADMIN.panel, display: 'flex', flexDirection: 'column', gap: 10,
      }}>
        {/* Top row: label + metadata + child input */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <div style={{
            fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700,
            color: ADMIN.inkMute, letterSpacing: '0.1em', textTransform: 'uppercase',
            flex: 1,
          }}>Device Preview</div>
          <label style={{
            display: 'flex', alignItems: 'center', gap: 5,
            fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.inkSoft, flexShrink: 0,
          }}>
            <span>as</span>
            <input value={previewChild} onChange={(e) => setPreviewChild(e.target.value)}
              style={{
                width: 72, border: `1px solid ${ADMIN.hair}`, borderRadius: 6,
                padding: '3px 8px', background: ADMIN.pageBg,
                fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.ink, outline: 'none',
              }}/>
          </label>
        </div>

        {/* Screen toggle */}
        <div style={{
          display: 'flex', gap: 2, background: ADMIN.pageBg,
          border: `1px solid ${ADMIN.hair}`, borderRadius: 999, padding: 2,
          alignSelf: 'flex-start',
        }}>
          {[['task', 'Task', false], ['prayer', 'Prayer', live.prayer?.length === 0]].map(([k, label, isEmpty]) => {
            const on = previewScreen === k;
            return (
              <button key={k} onClick={() => setPreviewScreen(k)} style={{
                appearance: 'none', border: 'none', cursor: 'pointer',
                padding: '4px 14px', borderRadius: 999,
                background: on ? ADMIN.ink : 'transparent',
                color: on ? ADMIN.panel : ADMIN.inkSoft,
                fontFamily: OLIVIA.fontBody, fontSize: 12, fontWeight: on ? 600 : 500,
                display: 'inline-flex', alignItems: 'center', gap: 5,
              }}>
                {label}
                {isEmpty && (
                  <span style={{
                    fontFamily: OLIVIA.fontBody, fontSize: 10, fontWeight: 600,
                    color: on ? 'rgba(255,255,255,0.55)' : ADMIN.inkMute,
                    letterSpacing: '0.02em',
                  }}>empty</span>
                )}
              </button>
            );
          })}
        </div>
      </div>

      {/* Device — scaled to fit */}
      <div style={{
        flex: 1, overflow: 'auto',
        display: 'flex', justifyContent: 'center', alignItems: 'flex-start',
        padding: '24px 0',
      }}>
        <div style={{ transform: 'scale(0.76)', transformOrigin: 'top center', marginBottom: -210 }}>
          <IOSDevice>
            {previewScreen === 'task' ? (
              <ScreenTaskExpanded childName={previewChild} task={live} onClose={() => {}} onOpenPrayer={() => setPreviewScreen('prayer')} onMarkDone={() => {}}/>
            ) : (
              <ScreenPrayer childName={previewChild} task={live} onClose={() => setPreviewScreen('task')} onDone={() => {}}/>
            )}
          </IOSDevice>
        </div>
      </div>

    </div>
  );
}

// ─── Kinds page ───────────────────────────────────────────────
function AgeRangeSelect({ value, onChange }) {
  const isAll = !value || value.includes('all');

  const toggle = (band) => {
    if (band === 'all') { onChange(['all']); return; }
    const current = isAll ? [] : value;
    const has = current.includes(band);
    const next = has ? current.filter(b => b !== band) : [...current, band];
    onChange(next.length === 0 || next.length === AGE_BANDS.length ? ['all'] : next);
  };

  return (
    <div style={{ display: 'flex', gap: 4, flexWrap: 'wrap' }}>
      {['all', ...AGE_BANDS].map(band => {
        const on = band === 'all' ? isAll : (!isAll && value.includes(band));
        return (
          <button key={band} onClick={() => toggle(band)} style={{
            appearance: 'none', border: 'none', cursor: 'pointer',
            padding: '3px 10px', borderRadius: 999,
            background: on ? ADMIN.ink : ADMIN.pageBg,
            color: on ? ADMIN.panel : ADMIN.inkSoft,
            fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: on ? 600 : 500,
            border: `1px solid ${on ? ADMIN.ink : ADMIN.hair}`,
          }}>{band === 'all' ? 'All ages' : band}</button>
        );
      })}
    </div>
  );
}

function KindsPage({ kinds, setKinds, items = [], onRenameKind }) {
  const [saved, setSaved] = React.useState(false);
  const saveTimer = React.useRef(null);
  const triggerSave = () => {
    setSaved(true);
    clearTimeout(saveTimer.current);
    saveTimer.current = setTimeout(() => setSaved(false), 2000);
  };
  React.useEffect(() => () => clearTimeout(saveTimer.current), []);

  const addKind = () => { setKinds(prev => [...prev, { name: '', ageRanges: ['all'] }]); triggerSave(); };

  const updateKind = (i, field, val) => {
    if (field === 'name' && onRenameKind) onRenameKind(kinds[i].name, val);
    setKinds(prev => prev.map((k, j) => j === i ? { ...k, [field]: val } : k));
    triggerSave();
  };

  const removeKind = (i) => {
    if (!window.confirm('Delete this kind? Cards using it will keep the old label.')) return;
    setKinds(prev => prev.filter((_, j) => j !== i));
    triggerSave();
  };

  return (
    <div style={{ flex: 1, overflow: 'auto', background: ADMIN.pageBg }}>
      <div style={{ maxWidth: 900, margin: '0 auto', padding: '48px 32px' }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 8 }}>
          <h1 style={{
            margin: 0, fontFamily: OLIVIA.fontDisplay,
            fontSize: 28, fontWeight: 600, color: ADMIN.ink, letterSpacing: '-0.02em',
          }}>Kinds</h1>
          <span style={{
            fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.sage,
            display: 'inline-flex', alignItems: 'center', gap: 4,
            opacity: saved ? 1 : 0, transition: 'opacity 0.4s',
          }}>
            <Icon name="check" size={12} color={ADMIN.sage} strokeWidth={2.5}/>
            Saved
          </span>
        </div>
        <p style={{
          margin: '0 0 32px', fontFamily: OLIVIA.fontBody,
          fontSize: 14, color: ADMIN.inkSoft, lineHeight: 1.6,
        }}>
          Kinds categorise task cards and define which age groups they're designed for.
          Renaming a kind updates all cards that use it.
        </p>

        <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 10 }}>
          {kinds.map((kind, i) => {
            const count = items.filter(c => c.kind === kind.name).length;
            return (
              <div key={i} style={{
                background: ADMIN.panel, border: `1px solid ${ADMIN.hair}`,
                borderRadius: 16, padding: '16px 18px',
              }}>
                {/* Name row */}
                <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 12 }}>
                  <div style={{
                    width: 32, height: 32, borderRadius: 10, flexShrink: 0,
                    background: ADMIN.accentWash,
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                  }}>
                    <Icon name="leaf" size={16} color={ADMIN.accent}/>
                  </div>
                  <input
                    value={kind.name}
                    onChange={e => updateKind(i, 'name', e.target.value)}
                    placeholder="Kind name…"
                    style={{
                      flex: 1, border: 'none', outline: 'none', background: 'transparent',
                      fontFamily: OLIVIA.fontDisplay, fontSize: 17, fontWeight: 600,
                      color: ADMIN.ink, letterSpacing: '-0.01em',
                    }}
                  />
                  <button onClick={() => removeKind(i)} style={{
                    appearance: 'none', border: 'none', background: 'transparent',
                    cursor: 'pointer', color: ADMIN.inkMute, padding: 4,
                    display: 'flex', alignItems: 'center',
                  }}>
                    <Icon name="close" size={14}/>
                  </button>
                </div>

                {/* Stats row */}
                <div style={{
                  display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12,
                }}>
                  <span style={{
                    display: 'inline-flex', alignItems: 'center', gap: 5,
                    background: ADMIN.panelMute, border: `1px solid ${ADMIN.hair}`,
                    borderRadius: 999, padding: '2px 10px',
                    fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.inkSoft,
                    fontVariantNumeric: 'tabular-nums',
                  }}>
                    <Icon name="sparkle" size={11} color={ADMIN.sage}/>
                    {count} {count === 1 ? 'card' : 'cards'}
                  </span>
                </div>

                {/* Age range */}
                <div>
                  <div style={{
                    fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700,
                    color: ADMIN.inkMute, letterSpacing: '0.08em', textTransform: 'uppercase',
                    marginBottom: 7,
                  }}>Age range</div>
                  <AgeRangeSelect
                    value={kind.ageRanges}
                    onChange={val => updateKind(i, 'ageRanges', val)}
                  />
                </div>
              </div>
            );
          })}
        </div>

        <button onClick={addKind} style={{
          appearance: 'none', border: `1px dashed ${ADMIN.hairStrong}`,
          background: 'transparent', cursor: 'pointer',
          borderRadius: 14, width: '100%', padding: '14px',
          color: ADMIN.inkSoft, fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 600,
          marginTop: 10,
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 8,
        }}>
          <Icon name="plus" size={15} color={ADMIN.inkSoft}/> Add kind
        </button>
      </div>
    </div>
  );
}

// ─── Sequence page ────────────────────────────────────────────
function SequencePage({ sequences, setSequences, items, kinds = [], onShipSequence, shippedBands = {}, shippedCards = {}, lastShippedSequence = {} }) {
  const [activeBand, setActiveBand] = React.useState(AGE_BANDS[0]);
  const [dragId, setDragId] = React.useState(null);
  const [dragOverIdx, setDragOverIdx] = React.useState(null);
  const [addOpen, setAddOpen] = React.useState(false);

  const isCompatible = (item, band) => {
    const kindObj = kinds.find(k => k.name === item.kind);
    if (!kindObj) return true;
    return kindObj.ageRanges.includes('all') || kindObj.ageRanges.includes(band);
  };

  const bandSeq = sequences[activeBand] || [];
  const bandSnapshots = shippedCards[activeBand] || [];

  const seqItems = bandSeq.map(id => {
    const snapshot = bandSnapshots.find(s => s.id === id);
    if (snapshot) {
      return { ...snapshot, _shipped: true, _sourceDeleted: !items.find(it => it.id === id) };
    }
    const live = items.find(it => it.id === id);
    return live ? { ...live, _shipped: false, _sourceDeleted: false } : null;
  }).filter(Boolean);

  const unsequenced = items.filter(it =>
    it.status === 'approved' &&
    !bandSeq.includes(it.id) &&
    isCompatible(it, activeBand)
  );
  const bandStatusOf = (item) => item._shipped ? 'shipped' : item.status;
  const shippableCount = seqItems.filter(it => !it._shipped && it.status === 'approved').length;
  const outOfRangeCount = seqItems.filter(it => !isCompatible(it, activeBand)).length;
  const isShipped = !!shippedBands[activeBand];
  const isDirty = isShipped && JSON.stringify(bandSeq) !== JSON.stringify(lastShippedSequence[activeBand] || []);
  const canShip = outOfRangeCount === 0 && (shippableCount > 0 || isDirty);
  const shipLabel = !isShipped ? `Ship ${activeBand} sequence`
    : (isDirty || shippableCount > 0) ? `Update ${activeBand} sequence`
    : `${activeBand} up to date`;
  const shipBlockReason = outOfRangeCount > 0
    ? `Remove ${outOfRangeCount} out-of-range card${outOfRangeCount === 1 ? '' : 's'} before shipping`
    : seqItems.length === 0 && !isDirty ? 'Add cards to this sequence before shipping'
    : isShipped && !isDirty && shippableCount === 0 ? 'No changes since last ship'
    : '';

  const reorder = (draggedId, targetId) => {
    setSequences(prev => {
      const seq = [...(prev[activeBand] || [])];
      const from = seq.indexOf(draggedId);
      const to = seq.indexOf(targetId);
      if (from === -1 || to === -1) return prev;
      const [moved] = seq.splice(from, 1);
      seq.splice(to, 0, moved);
      return { ...prev, [activeBand]: seq };
    });
  };

  const removeFromSeq = (id) => {
    setSequences(prev => ({
      ...prev,
      [activeBand]: (prev[activeBand] || []).filter(i => i !== id),
    }));
  };

  const addToSeq = (id) => {
    setSequences(prev => ({
      ...prev,
      [activeBand]: [...(prev[activeBand] || []), id],
    }));
  };

  return (
    <div style={{ flex: 1, overflow: 'auto', background: ADMIN.pageBg }}>
      <div style={{ maxWidth: 720, margin: '0 auto', padding: '48px 32px' }}>
        <h1 style={{
          margin: '0 0 8px', fontFamily: OLIVIA.fontDisplay,
          fontSize: 28, fontWeight: 600, color: ADMIN.ink, letterSpacing: '-0.02em',
        }}>Sequence</h1>
        <p style={{
          margin: '0 0 28px', fontFamily: OLIVIA.fontBody,
          fontSize: 14, color: ADMIN.inkSoft, lineHeight: 1.6,
        }}>
          Define the order cards are presented to each age band. Each band has its own independent sequence.
        </p>

        {/* Age band tabs + ship button */}
        <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 28, width: 'fit-content' }}>
          <div style={{
            display: 'flex', gap: 2, background: ADMIN.panel,
            border: `1px solid ${ADMIN.hair}`, borderRadius: 12, padding: 4,
          }}>
            {AGE_BANDS.map(band => {
              const on = activeBand === band;
              const count = (sequences[band] || []).length;
              return (
                <button key={band} onClick={() => setActiveBand(band)} style={{
                  appearance: 'none', border: 'none', cursor: 'pointer',
                  padding: '7px 14px', borderRadius: 9,
                  background: on ? ADMIN.ink : 'transparent',
                  color: on ? ADMIN.panel : ADMIN.inkSoft,
                  fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: on ? 600 : 500,
                  display: 'inline-flex', alignItems: 'center', gap: 6,
                  transition: 'all 0.1s',
                }}>
                  {band}
                  {count > 0 && (
                    <span style={{
                      background: on ? 'rgba(255,255,255,0.18)' : ADMIN.pageBg,
                      color: on ? '#fff' : ADMIN.inkMute,
                      borderRadius: 999, padding: '1px 6px',
                      fontFamily: OLIVIA.fontBody, fontSize: 10, fontWeight: 700,
                      fontVariantNumeric: 'tabular-nums',
                    }}>{count}</span>
                  )}
                  {shippedBands[band] && (
                    <div style={{
                      width: 16, height: 16, borderRadius: '50%', flexShrink: 0,
                      background: on ? 'rgba(78,97,71,0.7)' : ADMIN.sageDeep,
                      display: 'flex', alignItems: 'center', justifyContent: 'center',
                    }}>
                      <Icon name="check" size={10} color="#fff" strokeWidth={2.8}/>
                    </div>
                  )}
                </button>
              );
            })}
          </div>

          <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start', gap: 4 }}>
            <button
              disabled={!canShip}
              title={shipBlockReason}
              onClick={() => {
                const msg = !isShipped
                  ? `Ship the ${activeBand} sequence? This publishes ${seqItems.length} card${seqItems.length === 1 ? '' : 's'} to users in this age band.`
                  : `Update the ${activeBand} sequence?${shippableCount > 0 ? ` ${shippableCount} new approved card${shippableCount === 1 ? '' : 's'} will be added.` : ''} ${isDirty ? 'Changes to order or membership will go live.' : ''}`.trim();
                if (!window.confirm(msg)) return;
                onShipSequence(activeBand);
              }}
              style={{
                appearance: 'none', border: 'none', cursor: canShip ? 'pointer' : 'not-allowed',
                background: isShipped && canShip ? ADMIN.sage : ADMIN.accent, color: '#FFFCF7',
                borderRadius: 10, padding: '9px 16px',
                fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 600,
                display: 'inline-flex', alignItems: 'center', gap: 7,
                opacity: canShip ? 1 : 0.35,
                boxShadow: canShip ? `0 4px 12px rgba(${isShipped ? '122,139,111' : '217,119,87'},0.28)` : 'none',
                transition: 'all 0.15s',
              }}>
              <Icon name={isShipped && canShip ? 'arrow-right' : 'play'} size={13} color="#FFFCF7"/>
              {shipLabel}
              {shippableCount > 0 && (
                <span style={{
                  background: 'rgba(255,255,255,0.2)', borderRadius: 999,
                  padding: '1px 7px', fontSize: 11, fontWeight: 700,
                }}>{shippableCount}</span>
              )}
            </button>
            {shipBlockReason && (
              <div style={{
                fontFamily: OLIVIA.fontBody, fontSize: 11, color: outOfRangeCount > 0 ? '#8E3236' : ADMIN.inkMute,
                display: 'flex', alignItems: 'center', gap: 4,
              }}>
                {outOfRangeCount > 0 && <Icon name="lock" size={11} color="#8E3236"/>}
                {shipBlockReason}
              </div>
            )}
          </div>
        </div>

        {/* Sequence list */}
        {seqItems.length === 0 ? (
          <div style={{
            background: ADMIN.panel, border: `1px dashed ${ADMIN.hairStrong}`,
            borderRadius: 16, padding: '40px 24px', textAlign: 'center', marginBottom: 16,
          }}>
            <div style={{
              fontFamily: OLIVIA.fontDisplay, fontSize: 18, color: ADMIN.inkSoft, marginBottom: 6,
            }}>No cards in this sequence yet</div>
            <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 13, color: ADMIN.inkMute }}>
              Add cards below to build the {activeBand} sequence.
            </div>
          </div>
        ) : (
          <div style={{ display: 'flex', flexDirection: 'column', gap: 4, marginBottom: 16 }}>
            {seqItems.map((item, idx) => (
              <div
                key={item.id}
                draggable
                onDragStart={() => setDragId(item.id)}
                onDragOver={(e) => { e.preventDefault(); setDragOverIdx(idx); }}
                onDrop={() => { if (dragId && dragId !== item.id) reorder(dragId, item.id); setDragId(null); setDragOverIdx(null); }}
                onDragEnd={() => { setDragId(null); setDragOverIdx(null); }}
                style={{
                  background: ADMIN.panel,
                  border: dragOverIdx === idx ? `2px solid ${ADMIN.accent}` : `1px solid ${ADMIN.hair}`,
                  borderRadius: 12, padding: '12px 14px',
                  display: 'flex', alignItems: 'center', gap: 12,
                  opacity: dragId === item.id ? 0.4 : 1,
                  transition: 'opacity 0.15s',
                }}>
                {/* Day badge */}
                <div style={{
                  width: 38, height: 38, borderRadius: 10, flexShrink: 0,
                  background: ADMIN.pageBg, border: `1px solid ${ADMIN.hair}`,
                  display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
                }}>
                  <div style={{
                    fontFamily: OLIVIA.fontBody, fontSize: 9, fontWeight: 700,
                    color: ADMIN.inkMute, letterSpacing: '0.06em', textTransform: 'uppercase', lineHeight: 1,
                  }}>Day</div>
                  <div style={{
                    fontFamily: OLIVIA.fontBody, fontSize: 14, fontWeight: 700,
                    color: ADMIN.ink, fontVariantNumeric: 'tabular-nums', lineHeight: 1, marginTop: 2,
                  }}>{idx + 1}</div>
                </div>
                {/* Grip */}
                <div style={{ color: ADMIN.inkMute, cursor: 'grab', flexShrink: 0 }}>
                  <Icon name="grip" size={16} color={ADMIN.inkMute}/>
                </div>
                {/* Content */}
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{
                    fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 600,
                    color: ADMIN.inkMute, letterSpacing: '0.04em', marginBottom: 2,
                  }}>{item.kind}</div>
                  <div style={{
                    fontFamily: OLIVIA.fontDisplay, fontSize: 14, fontWeight: 500,
                    color: ADMIN.ink, lineHeight: 1.3,
                    overflow: 'hidden', display: '-webkit-box',
                    WebkitLineClamp: 1, WebkitBoxOrient: 'vertical',
                  }}>{(item.task || '(no task)').replace(/\{child\}/g, '[child]')}</div>
                </div>
                <StatusPill status={bandStatusOf(item)}/>
                {item._sourceDeleted && (
                  <span style={{
                    fontFamily: OLIVIA.fontBody, fontSize: 10, fontWeight: 700,
                    color: ADMIN.inkMute, background: ADMIN.pageBg,
                    border: `1px solid ${ADMIN.hair}`, borderRadius: 999,
                    padding: '2px 8px', letterSpacing: '0.04em', whiteSpace: 'nowrap',
                  }}>source deleted</span>
                )}
                {!isCompatible(item, activeBand) && (
                  <span style={{
                    fontFamily: OLIVIA.fontBody, fontSize: 10, fontWeight: 700,
                    color: '#8E3236', background: '#F5DBDC',
                    border: '1px solid #E5B8BB', borderRadius: 999,
                    padding: '2px 8px', letterSpacing: '0.04em', whiteSpace: 'nowrap',
                  }}>out of range</span>
                )}
                {/* Remove */}
                <button onClick={() => removeFromSeq(item.id)} style={{
                  appearance: 'none', border: `1px solid ${ADMIN.hair}`,
                  background: 'transparent', cursor: 'pointer',
                  color: ADMIN.inkMute, borderRadius: 8, padding: '5px 8px',
                  display: 'flex', alignItems: 'center',
                }}>
                  <Icon name="close" size={13}/>
                </button>
              </div>
            ))}
          </div>
        )}

        {/* Add cards panel */}
        <div style={{
          background: ADMIN.panel, border: `1px solid ${ADMIN.hair}`,
          borderRadius: 16, overflow: 'hidden',
        }}>
          <button onClick={() => setAddOpen(v => !v)} style={{
            appearance: 'none', border: 'none', background: 'transparent',
            cursor: 'pointer', width: '100%', padding: '14px 18px',
            display: 'flex', alignItems: 'center', gap: 10,
          }}>
            <Icon name="plus" size={15} color={ADMIN.inkSoft}/>
            <span style={{
              fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 600,
              color: ADMIN.inkSoft, flex: 1, textAlign: 'left',
            }}>
              Add cards to {activeBand} sequence
              {unsequenced.length > 0 && (
                <span style={{ fontWeight: 400, color: ADMIN.inkMute, marginLeft: 6 }}>
                  — {unsequenced.length} available
                </span>
              )}
            </span>
            <Icon name={addOpen ? 'chevron-down' : 'chevron-right'} size={14} color={ADMIN.inkMute}/>
          </button>
          {addOpen && (
            <div style={{ borderTop: `1px solid ${ADMIN.hair}`, padding: 8 }}>
              {unsequenced.length === 0 ? (
                <div style={{
                  padding: 16, textAlign: 'center',
                  fontFamily: OLIVIA.fontBody, fontSize: 13, color: ADMIN.inkMute,
                }}>No approved, age-compatible cards to add.</div>
              ) : unsequenced.map(item => (
                <button key={item.id} onClick={() => addToSeq(item.id)} style={{
                  appearance: 'none', border: 'none', background: 'transparent',
                  cursor: 'pointer', width: '100%', padding: '10px 12px',
                  display: 'flex', alignItems: 'center', gap: 10,
                  borderRadius: 10, textAlign: 'left',
                }}
                onMouseEnter={e => e.currentTarget.style.background = ADMIN.pageBg}
                onMouseLeave={e => e.currentTarget.style.background = 'transparent'}
                >
                  <Icon name="plus" size={14} color={ADMIN.inkSoft}/>
                  <div style={{ flex: 1, minWidth: 0 }}>
                    <div style={{
                      fontFamily: OLIVIA.fontBody, fontSize: 11,
                      color: ADMIN.inkMute, marginBottom: 1,
                    }}>{item.kind}</div>
                    <div style={{
                      fontFamily: OLIVIA.fontDisplay, fontSize: 14, fontWeight: 500,
                      color: ADMIN.ink, overflow: 'hidden', display: '-webkit-box',
                      WebkitLineClamp: 1, WebkitBoxOrient: 'vertical',
                    }}>{(item.task || '(no task)').replace(/\{child\}/g, '[child]')}</div>
                  </div>
                  <StatusPill status={item.status}/>
                </button>
              ))}
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ─── Milestones page ──────────────────────────────────────────
function MilestoneCard({ milestone, kinds, onChange, onDelete }) {
  return (
    <div style={{
      background: ADMIN.panel, border: `1px solid ${ADMIN.hair}`,
      borderRadius: 16, padding: '16px 18px',
      display: 'flex', flexDirection: 'column', gap: 12,
    }}>
      {/* Title row */}
      <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
        <div style={{
          width: 32, height: 32, borderRadius: 10, flexShrink: 0,
          background: ADMIN.accentWash,
          display: 'flex', alignItems: 'center', justifyContent: 'center',
        }}>
          <Icon name="star" size={15} color={ADMIN.accent}/>
        </div>
        <input
          value={milestone.title}
          onChange={e => onChange('title', e.target.value)}
          placeholder="Milestone title…"
          style={{
            flex: 1, border: 'none', outline: 'none', background: 'transparent',
            fontFamily: OLIVIA.fontDisplay, fontSize: 16, fontWeight: 600,
            color: ADMIN.ink, letterSpacing: '-0.01em',
          }}
        />
        <button onClick={onDelete} style={{
          appearance: 'none', border: 'none', background: 'transparent',
          cursor: 'pointer', color: ADMIN.inkMute, padding: 4,
          display: 'flex', alignItems: 'center',
        }}>
          <Icon name="close" size={14}/>
        </button>
      </div>

      {/* Description */}
      <textarea
        value={milestone.description}
        onChange={e => onChange('description', e.target.value)}
        placeholder="What the family is working toward…"
        rows={2}
        style={{
          width: '100%', boxSizing: 'border-box',
          border: `1px solid ${ADMIN.hair}`, borderRadius: 10,
          background: ADMIN.pageBg, padding: '8px 10px',
          fontFamily: OLIVIA.fontBody, fontSize: 13, color: ADMIN.inkSoft,
          lineHeight: 1.5, resize: 'none', outline: 'none',
        }}
        onFocus={e => e.target.style.borderColor = ADMIN.accent}
        onBlur={e => e.target.style.borderColor = ADMIN.hair}
      />

      {/* Kind + Count */}
      <div style={{ display: 'grid', gridTemplateColumns: '1fr auto', gap: 8, alignItems: 'end' }}>
        <div>
          <div style={{
            fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700,
            color: ADMIN.inkMute, letterSpacing: '0.08em', textTransform: 'uppercase',
            marginBottom: 5,
          }}>Kind</div>
          <Select value={milestone.kind} onChange={v => onChange('kind', v)} options={kinds.map(k => k.name)}/>
        </div>
        <div>
          <div style={{
            fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700,
            color: ADMIN.inkMute, letterSpacing: '0.08em', textTransform: 'uppercase',
            marginBottom: 5,
          }}>Times</div>
          <NumberInput value={milestone.count} onChange={v => onChange('count', Math.max(1, v))} small/>
        </div>
      </div>

      {/* Age range — multi select */}
      <div>
        <div style={{
          fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700,
          color: ADMIN.inkMute, letterSpacing: '0.08em', textTransform: 'uppercase',
          marginBottom: 7,
        }}>Age range</div>
        <AgeRangeSelect
          value={milestone.ageBands}
          onChange={v => onChange('ageBands', v)}
        />
      </div>
    </div>
  );
}

function MilestonesPage({ milestones, setMilestones, kinds = [] }) {
  const [saved, setSaved] = React.useState(false);
  const saveTimer = React.useRef(null);
  const triggerSave = () => {
    setSaved(true);
    clearTimeout(saveTimer.current);
    saveTimer.current = setTimeout(() => setSaved(false), 2000);
  };
  React.useEffect(() => () => clearTimeout(saveTimer.current), []);

  const addMilestone = () => {
    setMilestones(prev => [...prev, {
      id: `m-${Date.now()}`,
      title: '',
      description: '',
      kind: kinds[0]?.name || '',
      count: 1,
      ageBands: ['all'],
    }]);
    triggerSave();
  };

  const updateMilestone = (id, field, val) => {
    setMilestones(prev => prev.map(m => m.id === id ? { ...m, [field]: val } : m));
    triggerSave();
  };

  const removeMilestone = (id) => {
    if (!window.confirm('Delete this milestone?')) return;
    setMilestones(prev => prev.filter(m => m.id !== id));
    triggerSave();
  };

  return (
    <div style={{ flex: 1, overflow: 'auto', background: ADMIN.pageBg }}>
      <div style={{ maxWidth: 900, margin: '0 auto', padding: '48px 32px' }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 12, marginBottom: 8 }}>
          <h1 style={{
            margin: 0, fontFamily: OLIVIA.fontDisplay,
            fontSize: 28, fontWeight: 600, color: ADMIN.ink, letterSpacing: '-0.02em',
          }}>Milestones</h1>
          <span style={{
            fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.sage,
            display: 'inline-flex', alignItems: 'center', gap: 4,
            opacity: saved ? 1 : 0, transition: 'opacity 0.4s',
          }}>
            <Icon name="check" size={12} color={ADMIN.sage} strokeWidth={2.5}/>
            Saved
          </span>
        </div>
        <p style={{
          margin: '0 0 32px', fontFamily: OLIVIA.fontBody,
          fontSize: 14, color: ADMIN.inkSoft, lineHeight: 1.6,
        }}>
          Milestones celebrate families as they build habits. Each one unlocks when a kind of task has been completed a set number of times.
        </p>

        {milestones.length === 0 ? (
          <div style={{
            background: ADMIN.panel, border: `1px dashed ${ADMIN.hairStrong}`,
            borderRadius: 16, padding: '48px 24px', textAlign: 'center', marginBottom: 16,
          }}>
            <div style={{
              width: 48, height: 48, borderRadius: 14, background: ADMIN.accentWash,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              margin: '0 auto 14px',
            }}>
              <Icon name="star" size={24} color={ADMIN.accent}/>
            </div>
            <div style={{
              fontFamily: OLIVIA.fontDisplay, fontSize: 18, fontWeight: 600,
              color: ADMIN.ink, marginBottom: 6,
            }}>No milestones yet</div>
            <div style={{
              fontFamily: OLIVIA.fontBody, fontSize: 13, color: ADMIN.inkMute,
            }}>Create your first milestone below.</div>
          </div>
        ) : (
          <div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)', gap: 10, marginBottom: 10 }}>
            {milestones.map(m => (
              <MilestoneCard
                key={m.id} milestone={m} kinds={kinds}
                onChange={(field, val) => updateMilestone(m.id, field, val)}
                onDelete={() => removeMilestone(m.id)}
              />
            ))}
          </div>
        )}

        <button onClick={addMilestone} style={{
          appearance: 'none', border: `1px dashed ${ADMIN.hairStrong}`,
          background: 'transparent', cursor: 'pointer',
          borderRadius: 14, width: '100%', padding: '14px',
          color: ADMIN.inkSoft, fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 600,
          display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 8,
        }}>
          <Icon name="plus" size={15} color={ADMIN.inkSoft}/>
          Add milestone
        </button>
      </div>
    </div>
  );
}

// ─── CSV import helpers ───────────────────────────────────────
function parseCSVRow(line) {
  const result = [];
  let cur = '', inQ = false;
  for (let i = 0; i < line.length; i++) {
    const ch = line[i];
    if (ch === '"') {
      if (inQ && line[i + 1] === '"') { cur += '"'; i++; }
      else inQ = !inQ;
    } else if (ch === ',' && !inQ) { result.push(cur); cur = ''; }
    else cur += ch;
  }
  result.push(cur);
  return result;
}

function parseCSV(text) {
  const lines = text.split(/\r?\n/).filter(l => l.trim());
  if (lines.length < 2) return { headers: [], rows: [] };
  const headers = parseCSVRow(lines[0]).map(h => h.trim());
  const rows = lines.slice(1).map(line => {
    const vals = parseCSVRow(line);
    return Object.fromEntries(headers.map((h, i) => [h, (vals[i] || '').trim()]));
  });
  return { headers, rows };
}

// ─── Import modal ─────────────────────────────────────────────
function ImportModal({ onClose, onImport, kinds }) {
  const [file, setFile] = React.useState(null);
  const [parsed, setParsed] = React.useState(null);
  const [dragging, setDragging] = React.useState(false);
  const inputRef = React.useRef();

  const kindNames = kinds.map(k => k.name);
  const prayerKinds = kindNames.filter(n => n.toLowerCase().includes('prayer'));

  const downloadTemplate = () => {
    const rows = [
      'kind,task,tip,prayer,estMinutes,notes',
      `Prayer time,"Ask your child what they're thankful for today.","Get down to their level and make eye contact.","Thank you God for today|Help us see your goodness",5,`,
      `Bible story,"Read Genesis 1 together. Ask: what does this tell us about God?","Let them ask questions — no need to have all the answers.",,8,Good for curious kids`,
    ];
    const blob = new Blob([rows.join('\n')], { type: 'text/csv' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = 'olivia-cards-template.csv'; a.click();
    URL.revokeObjectURL(url);
  };

  const processFile = (f) => {
    setFile(f);
    const reader = new FileReader();
    reader.onload = (e) => {
      const { rows } = parseCSV(e.target.result);
      const valid = [], errors = [];
      rows.forEach((row, i) => {
        const n = i + 2;
        const kind = row.kind || '';
        const task = row.task || '';
        const tip  = row.tip  || '';
        const prayer = row.prayer || '';
        if (!kind)                                { errors.push(`Row ${n}: missing kind`); return; }
        if (!kindNames.includes(kind))            { errors.push(`Row ${n}: kind "${kind}" not recognised`); return; }
        if (!task)                                { errors.push(`Row ${n}: missing task`); return; }
        if (!tip)                                 { errors.push(`Row ${n}: missing tip`); return; }
        if (prayerKinds.includes(kind) && !prayer){ errors.push(`Row ${n}: prayer lines required for "${kind}"`); return; }
        valid.push({
          id: `c-import-${Date.now()}-${i}`,
          kind, task, tip,
          prayer: prayer ? prayer.split('|').map(l => l.trim()).filter(Boolean) : [],
          notes: row.notes || '',
          estMinutes: parseInt(row.estMinutes) || 3,
          status: 'draft',
          author: 'Import', reviewer: null,
          updatedAt: 'just now',
          theology: { denomAgnostic: null, ageAppropriate: null, parentFacing: null, scriptureAccurate: null, nonPrescriptive: null },
        });
      });
      setParsed({ valid, errors });
    };
    reader.readAsText(f);
  };

  const handleDrop = (e) => {
    e.preventDefault(); setDragging(false);
    const f = e.dataTransfer.files[0];
    if (f) processFile(f);
  };

  return (
    <div onClick={e => { if (e.target === e.currentTarget) onClose(); }} style={{
      position: 'fixed', inset: 0, zIndex: 200,
      background: 'rgba(58,46,38,0.36)', backdropFilter: 'blur(4px)',
      display: 'flex', alignItems: 'center', justifyContent: 'center',
    }}>
      <div style={{
        width: 540, maxHeight: '82vh', background: ADMIN.panel, borderRadius: 20,
        display: 'flex', flexDirection: 'column', overflow: 'hidden',
        boxShadow: '0 24px 60px rgba(58,46,38,0.22)',
      }}>
        {/* Header */}
        <div style={{ padding: '22px 24px 16px', borderBottom: `1px solid ${ADMIN.hair}`, display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between' }}>
          <div>
            <h2 style={{ margin: 0, fontFamily: OLIVIA.fontDisplay, fontSize: 20, fontWeight: 600, color: ADMIN.ink, letterSpacing: '-0.01em' }}>Import cards</h2>
            <p style={{ margin: '3px 0 0', fontFamily: OLIVIA.fontBody, fontSize: 13, color: ADMIN.inkSoft }}>Upload a CSV to bulk-create draft cards.</p>
          </div>
          <button onClick={onClose} style={{ appearance: 'none', border: 'none', background: 'transparent', cursor: 'pointer', padding: 4, marginTop: 2 }}>
            <Icon name="close" size={18} color={ADMIN.inkMute}/>
          </button>
        </div>

        {/* Body */}
        <div style={{ flex: 1, overflowY: 'auto', padding: '20px 24px', display: 'flex', flexDirection: 'column', gap: 14 }}>

          {/* Template row */}
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', background: ADMIN.pageBg, borderRadius: 12, padding: '13px 16px' }}>
            <div>
              <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 600, color: ADMIN.ink }}>Download template</div>
              <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.inkMute, marginTop: 2 }}>olivia-cards-template.csv · includes example rows</div>
            </div>
            <button onClick={downloadTemplate} style={{
              appearance: 'none', border: `1px solid ${ADMIN.hairStrong}`, cursor: 'pointer',
              background: ADMIN.panel, color: ADMIN.ink, borderRadius: 9, padding: '7px 14px',
              fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 600,
            }}>Download</button>
          </div>

          {/* Drop zone */}
          <div
            onDragOver={e => { e.preventDefault(); setDragging(true); }}
            onDragLeave={() => setDragging(false)}
            onDrop={handleDrop}
            onClick={() => inputRef.current.click()}
            style={{
              border: `2px dashed ${dragging ? ADMIN.accent : ADMIN.hairStrong}`,
              borderRadius: 14, padding: '32px 20px', textAlign: 'center', cursor: 'pointer',
              background: dragging ? ADMIN.accentWash : ADMIN.pageBg,
              transition: 'all 0.15s',
            }}>
            <input ref={inputRef} type="file" accept=".csv" style={{ display: 'none' }}
              onChange={e => { if (e.target.files[0]) processFile(e.target.files[0]); }}/>
            <Icon name="plus" size={22} color={dragging ? ADMIN.accent : ADMIN.inkMute}/>
            <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 14, fontWeight: 600, color: dragging ? ADMIN.accentDeep : ADMIN.ink, marginTop: 8 }}>
              {file ? file.name : 'Drop CSV here or click to browse'}
            </div>
            {!file && <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.inkMute, marginTop: 3 }}>.csv files only</div>}
          </div>

          {/* Results */}
          {parsed && (
            <div style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>

              {/* Summary pills */}
              <div style={{ display: 'flex', gap: 10 }}>
                <div style={{ flex: 1, background: '#E8F5E3', borderRadius: 12, padding: '12px 16px' }}>
                  <div style={{ fontFamily: OLIVIA.fontDisplay, fontSize: 26, fontWeight: 600, color: ADMIN.sageDeep, lineHeight: 1 }}>{parsed.valid.length}</div>
                  <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.inkSoft, marginTop: 3 }}>cards ready to import</div>
                </div>
                <div style={{ flex: 1, background: parsed.errors.length ? '#FFF0F0' : ADMIN.pageBg, borderRadius: 12, padding: '12px 16px' }}>
                  <div style={{ fontFamily: OLIVIA.fontDisplay, fontSize: 26, fontWeight: 600, color: parsed.errors.length ? '#9E2A2A' : ADMIN.inkMute, lineHeight: 1 }}>{parsed.errors.length}</div>
                  <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.inkSoft, marginTop: 3 }}>rows skipped</div>
                </div>
              </div>

              {/* Errors */}
              {parsed.errors.length > 0 && (
                <div style={{ background: '#FFF0F0', borderRadius: 10, padding: '12px 14px' }}>
                  {parsed.errors.map((err, i) => (
                    <div key={i} style={{ fontFamily: OLIVIA.fontBody, fontSize: 12.5, color: '#7A2A2A', lineHeight: 1.7 }}>{err}</div>
                  ))}
                </div>
              )}

              {/* Card preview */}
              {parsed.valid.length > 0 && (
                <div style={{ border: `1px solid ${ADMIN.hair}`, borderRadius: 12, overflow: 'hidden' }}>
                  {parsed.valid.slice(0, 5).map((card, i) => (
                    <div key={i} style={{
                      padding: '11px 16px',
                      borderBottom: i < Math.min(parsed.valid.length, 5) - 1 ? `1px solid ${ADMIN.hair}` : 'none',
                    }}>
                      <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
                        <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700, color: ADMIN.accentDeep, background: ADMIN.accentWash, borderRadius: 999, padding: '1px 8px' }}>{card.kind}</span>
                        <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, color: ADMIN.inkMute }}>{card.estMinutes} min</span>
                      </div>
                      <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 13, color: ADMIN.ink, lineHeight: 1.4 }}>{card.task}</div>
                    </div>
                  ))}
                  {parsed.valid.length > 5 && (
                    <div style={{ padding: '9px 16px', background: ADMIN.pageBg, fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.inkMute }}>
                      + {parsed.valid.length - 5} more card{parsed.valid.length - 5 !== 1 ? 's' : ''}
                    </div>
                  )}
                </div>
              )}
            </div>
          )}
        </div>

        {/* Footer */}
        <div style={{ padding: '14px 24px', borderTop: `1px solid ${ADMIN.hair}`, display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
          <button onClick={onClose} style={{ appearance: 'none', border: 'none', background: 'transparent', cursor: 'pointer', fontFamily: OLIVIA.fontBody, fontSize: 13, color: ADMIN.inkSoft, padding: '8px 0' }}>Cancel</button>
          <button
            onClick={() => { onImport(parsed.valid); onClose(); }}
            disabled={!parsed || parsed.valid.length === 0}
            style={{
              appearance: 'none', border: 'none',
              cursor: parsed?.valid.length > 0 ? 'pointer' : 'not-allowed',
              background: parsed?.valid.length > 0 ? ADMIN.accent : ADMIN.hair,
              color: parsed?.valid.length > 0 ? '#FFFCF7' : ADMIN.inkMute,
              borderRadius: 10, padding: '10px 20px',
              fontFamily: OLIVIA.fontBody, fontSize: 14, fontWeight: 600,
              boxShadow: parsed?.valid.length > 0 ? '0 4px 12px rgba(217,119,87,0.28)' : 'none',
              transition: 'all 0.15s',
            }}>
            {parsed?.valid.length > 0 ? `Import ${parsed.valid.length} card${parsed.valid.length !== 1 ? 's' : ''} as drafts` : 'Import cards'}
          </button>
        </div>
      </div>
    </div>
  );
}

// ─── Settings page ────────────────────────────────────────────
function SettingsPage({ adminProfile, setAdminProfile, childrenPerFamily, setChildrenPerFamily, appLinks, setAppLinks, items, setItems }) {
  const [saved, setSaved] = React.useState(false);
  const saveTimer = React.useRef(null);

  const triggerSave = () => {
    setSaved(false);
    clearTimeout(saveTimer.current);
    saveTimer.current = setTimeout(() => setSaved(true), 900);
  };

  const patchProfile = (patch) => { setAdminProfile(prev => ({ ...prev, ...patch })); triggerSave(); };
  const patchLinks   = (patch) => { setAppLinks(prev => ({ ...prev, ...patch })); triggerSave(); };

  const exportCards = () => {
    const blob = new Blob([JSON.stringify(items, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url; a.download = 'olivia-cards.json'; a.click();
    URL.revokeObjectURL(url);
  };

  const clearDrafts = () => {
    const count = items.filter(i => i.status === 'draft').length;
    if (!count) return;
    if (window.confirm(`Delete ${count} draft card${count !== 1 ? 's' : ''}? This cannot be undone.`)) {
      setItems(prev => prev.filter(i => i.status !== 'draft'));
    }
  };

  const inputStyle = {
    width: '100%', border: `1px solid ${ADMIN.hairStrong}`,
    borderRadius: 10, padding: '10px 14px',
    fontFamily: OLIVIA.fontBody, fontSize: 14, color: ADMIN.ink,
    background: ADMIN.panel, outline: 'none', boxSizing: 'border-box',
  };

  const SectionHead = ({ title }) => (
    <h2 style={{ margin: '36px 0 14px', fontFamily: OLIVIA.fontDisplay, fontSize: 19, fontWeight: 600, color: ADMIN.ink, letterSpacing: '-0.01em' }}>{title}</h2>
  );

  const Field = ({ label, hint, children }) => (
    <div style={{ marginBottom: 18 }}>
      <label style={{ display: 'block', fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 600, color: ADMIN.ink, marginBottom: hint ? 4 : 7 }}>{label}</label>
      {hint && <p style={{ margin: '0 0 8px', fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.inkMute, lineHeight: 1.5 }}>{hint}</p>}
      {children}
    </div>
  );

  const draftCount = items.filter(i => i.status === 'draft').length;
  const initials = (adminProfile.name || 'A').split(' ').map(w => w[0]).join('').slice(0, 2).toUpperCase();

  return (
    <div style={{ flex: 1, overflow: 'auto', background: ADMIN.pageBg }}>
      <div style={{ maxWidth: 640, margin: '0 auto', padding: '40px 32px 60px' }}>

        {/* Header */}
        <div style={{ display: 'flex', alignItems: 'baseline', justifyContent: 'space-between' }}>
          <div>
            <h1 style={{ margin: '0 0 4px', fontFamily: OLIVIA.fontDisplay, fontSize: 28, fontWeight: 600, color: ADMIN.ink, letterSpacing: '-0.015em' }}>Settings</h1>
            <p style={{ margin: 0, fontFamily: OLIVIA.fontBody, fontSize: 14, color: ADMIN.inkSoft }}>Manage your account and studio configuration.</p>
          </div>
          {saved && (
            <span style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontFamily: OLIVIA.fontBody, fontSize: 13, color: ADMIN.sageDeep }}>
              <Icon name="check" size={13} color={ADMIN.sageDeep} strokeWidth={2.5}/> Saved
            </span>
          )}
        </div>

        {/* Account */}
        <SectionHead title="Account"/>
        <div style={{ background: ADMIN.panel, border: `1px solid ${ADMIN.hair}`, borderRadius: 16, padding: '22px 24px' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 16, marginBottom: 22, paddingBottom: 20, borderBottom: `1px solid ${ADMIN.hair}` }}>
            <div style={{
              width: 52, height: 52, borderRadius: '50%', flexShrink: 0,
              background: ADMIN.sage, color: '#fff',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontFamily: OLIVIA.fontBody, fontSize: 18, fontWeight: 700,
            }}>{initials}</div>
            <div>
              <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 15, fontWeight: 600, color: ADMIN.ink }}>{adminProfile.name || 'Admin'}</div>
              <span style={{
                display: 'inline-block', marginTop: 4,
                background: ADMIN.accentWash, color: ADMIN.accentDeep,
                borderRadius: 999, padding: '2px 9px',
                fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700, letterSpacing: '0.04em',
              }}>Admin</span>
            </div>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14 }}>
            <Field label="Name">
              <input value={adminProfile.name} onChange={e => patchProfile({ name: e.target.value })}
                placeholder="Your name" style={inputStyle}/>
            </Field>
            <Field label="Email">
              <input value={adminProfile.email} onChange={e => patchProfile({ email: e.target.value })}
                type="email" placeholder="you@example.com" style={inputStyle}/>
            </Field>
          </div>
        </div>

        {/* App config */}
        <SectionHead title="App config"/>
        <div style={{ background: ADMIN.panel, border: `1px solid ${ADMIN.hair}`, borderRadius: 16, padding: '22px 24px' }}>
          <Field label="Avg. children per family"
            hint="Used to estimate 'Children reached' on the Overview page until live Supabase data is connected.">
            <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
              <input
                type="number" min="1" max="10" step="0.1"
                value={childrenPerFamily}
                onChange={e => { setChildrenPerFamily(parseFloat(e.target.value) || 1); triggerSave(); }}
                style={{ ...inputStyle, width: 100 }}
              />
              <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 13, color: ADMIN.inkMute }}>
                ≈ {ovFmt(Math.round(OV_DATA.signups.total * childrenPerFamily))} children est.
              </span>
            </div>
          </Field>
          <div style={{ height: 1, background: ADMIN.hair, margin: '2px 0 20px' }}/>
          <Field label="iOS App Store link">
            <input value={appLinks.ios} onChange={e => patchLinks({ ios: e.target.value })}
              placeholder="https://apps.apple.com/…" style={inputStyle}/>
          </Field>
          <Field label="Android Play Store link">
            <input value={appLinks.android} onChange={e => patchLinks({ android: e.target.value })}
              placeholder="https://play.google.com/…" style={inputStyle}/>
          </Field>
        </div>

        {/* Export */}
        <SectionHead title="Export &amp; data"/>
        <div style={{ background: ADMIN.panel, border: `1px solid ${ADMIN.hair}`, borderRadius: 16, overflow: 'hidden' }}>
          {/* Export row */}
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '18px 24px' }}>
            <div>
              <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 14, fontWeight: 600, color: ADMIN.ink }}>Export all cards</div>
              <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.inkMute, marginTop: 3 }}>{items.length} card{items.length !== 1 ? 's' : ''} · JSON</div>
            </div>
            <button onClick={exportCards} style={{
              appearance: 'none', border: `1px solid ${ADMIN.hairStrong}`, cursor: 'pointer',
              background: ADMIN.panel, color: ADMIN.ink,
              borderRadius: 9, padding: '8px 16px',
              fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 600,
            }}>Download JSON</button>
          </div>

          {/* Divider */}
          <div style={{ height: 1, background: ADMIN.hair, margin: '0 24px' }}/>

          {/* Danger row */}
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '18px 24px' }}>
            <div>
              <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 14, fontWeight: 600, color: '#9E2A2A' }}>Clear draft cards</div>
              <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.inkMute, marginTop: 3 }}>
                {draftCount} draft{draftCount !== 1 ? 's' : ''} will be deleted permanently
              </div>
            </div>
            <button onClick={clearDrafts} disabled={draftCount === 0} style={{
              appearance: 'none', border: `1px solid ${draftCount > 0 ? 'rgba(158,42,42,0.3)' : ADMIN.hair}`, cursor: draftCount > 0 ? 'pointer' : 'not-allowed',
              background: draftCount > 0 ? '#FFF0F0' : ADMIN.panelMute,
              color: draftCount > 0 ? '#9E2A2A' : ADMIN.inkMute,
              borderRadius: 9, padding: '8px 16px',
              fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 600,
            }}>Delete drafts</button>
          </div>
        </div>

      </div>
    </div>
  );
}

Object.assign(window, {
  AdminListPane, AdminEditorPane, AdminPreviewPane, StatusPill,
  KindsPage, SequencePage, MilestonesPage, OverviewPage, SettingsPage, ImportModal,
});
