// olivia-admin-tracks.jsx
// Three-pane tracks studio: [Topics + Tracks sidebar] | [Track + days editor] | [Live card preview]
// Writes live to Supabase; published changes appear in the app immediately.

const { useState, useEffect } = React;

const DAY_TYPES = ['scripture', 'prayer', 'activity', 'story', 'reflection', 'memory_verse'];

const TYPE_META = {
  scripture:    { label: 'Scripture',    color: '#4E6147', bg: '#DCE4D2' },
  prayer:       { label: 'Prayer',       color: '#9E4B2E', bg: '#FAE7D8' },
  activity:     { label: 'Activity',     color: '#3D6B5E', bg: '#D4ECE5' },
  story:        { label: 'Story',        color: '#6B5A3D', bg: '#EFE7D9' },
  reflection:   { label: 'Reflection',   color: '#5E6B7A', bg: '#DDE4F0' },
  memory_verse: { label: 'Memory Verse', color: '#8A6918', bg: '#FBEFC9' },
};

function slugify(s) {
  return (s || '').toLowerCase().trim().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
}

// Local resize hook (not exported from olivia-admin.jsx)
function usePaneDrag(initial, min, max, dir = 1) {
  const [w, setW] = useState(initial);
  const onMouseDown = (e) => {
    e.preventDefault();
    const startX = e.clientX, startW = w;
    const move = (e) => setW(Math.max(min, Math.min(max, startW + (e.clientX - startX) * dir)));
    const up = () => {
      document.removeEventListener('mousemove', move);
      document.removeEventListener('mouseup', up);
      document.body.style.cursor = document.body.style.userSelect = '';
    };
    document.body.style.cursor = 'col-resize';
    document.body.style.userSelect = 'none';
    document.addEventListener('mousemove', move);
    document.addEventListener('mouseup', up);
  };
  return [w, onMouseDown];
}

function PaneDragHandle({ onMouseDown }) {
  const [hov, setHov] = useState(false);
  return (
    <div onMouseDown={onMouseDown} onMouseEnter={() => setHov(true)} onMouseLeave={() => setHov(false)}
      style={{ width: 5, flexShrink: 0, cursor: 'col-resize', background: hov ? ADMIN.accent : 'transparent', transition: 'background .15s', position: 'relative', zIndex: 10 }}>
      <div style={{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%,-50%)',
        width: 2, height: 32, borderRadius: 2, background: hov ? ADMIN.accent : ADMIN.hairStrong, transition: 'background .15s' }}/>
    </div>
  );
}

// ─── Auth gate ─────────────────────────────────────────────────
function TracksPage() {
  const [session, setSession] = useState(undefined);
  useEffect(() => { getAdminSession().then(setSession); }, []);
  if (session === undefined) return <CNote>Loading…</CNote>;
  if (!session || !session.isAdmin) {
    return <TracksSignIn signedOut={session && !session.isAdmin} onSignedIn={() => getAdminSession().then(setSession)}/>;
  }
  return <TracksManager email={session.user.email} onSignOut={async () => { await adminSignOut(); setSession(null); }}/>;
}

function CNote({ children }) {
  return (
    <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontFamily: OLIVIA.fontBody, fontSize: 14, color: ADMIN.inkMute, background: ADMIN.pageBg }}>
      {children}
    </div>
  );
}

function TracksSignIn({ onSignedIn, signedOut }) {
  const [email, setEmail] = useState('');
  const [pw, setPw] = useState('');
  const [err, setErr] = useState(signedOut ? 'This account is not an admin.' : '');
  const [busy, setBusy] = useState(false);

  const submit = async () => {
    setBusy(true); setErr('');
    const res = await adminSignIn(email.trim(), pw);
    setBusy(false);
    if (res.error) setErr(res.error); else onSignedIn();
  };

  return (
    <div style={{ flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center', background: ADMIN.pageBg }}>
      <div style={{ width: 380, background: ADMIN.panel, border: `1px solid ${ADMIN.hair}`, borderRadius: 20, padding: '32px 28px' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 6 }}>
          <Icon name="book" size={20} color={ADMIN.accent}/>
          <h2 style={{ margin: 0, fontFamily: OLIVIA.fontDisplay, fontSize: 22, fontWeight: 600, color: ADMIN.ink }}>Tracks studio</h2>
        </div>
        <p style={{ margin: '0 0 22px', fontFamily: OLIVIA.fontBody, fontSize: 13, color: ADMIN.inkSoft, lineHeight: 1.55 }}>
          Sign in with your Olivia account to create and publish track content.
        </p>
        <div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
          <input type="email" value={email} placeholder="Email" onChange={e => setEmail(e.target.value)} style={S.input}/>
          <input type="password" value={pw} placeholder="Password" onChange={e => setPw(e.target.value)}
            onKeyDown={e => e.key === 'Enter' && submit()} style={S.input}/>
          {err && <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 12.5, color: '#B85A3D' }}>{err}</div>}
          <button onClick={submit} disabled={busy || !email || !pw} style={{ ...S.primary, marginTop: 4, opacity: busy ? 0.6 : 1 }}>
            {busy ? 'Signing in…' : 'Sign in'}
          </button>
        </div>
      </div>
    </div>
  );
}

// ─── Bulk import modal (CSV) ──────────────────────────────────
function BulkImportModal({ onClose, existingTopics, onImported }) {
  const [parsed,   setParsed]   = useState(null);  // grouped data ready to import
  const [errors,   setErrors]   = useState([]);
  const [fileName, setFileName] = useState('');
  const [progress, setProgress] = useState(null);
  const [dragging, setDragging] = useState(false);
  const [clearMode, setClearMode] = useState(false);
  const inputRef = React.useRef();

  const VALID_TYPES = ['scripture','prayer','activity','story','reflection','memory_verse'];
  const COLS = ['topic','track_title','age_band','track_about','track_author','day_type','day_title','body','scripture_ref','scripture_text','parent_prompt','est_minutes'];

  const downloadTemplate = () => {
    const rows = [
      COLS.join(','),
      `Prayer,"Morning Prayer Habit","4–6","A 7-day track helping families build a consistent morning prayer habit.","Olivia Team",prayer,"Starting with Thanks","Begin your morning by thanking God together.","Psalm 100:4","Enter his gates with thanksgiving and his courts with praise.","What is one thing you are thankful for today?",5`,
      `Prayer,"Morning Prayer Habit","4–6","A 7-day track helping families build a consistent morning prayer habit.","Olivia Team",scripture,"A Verse to Remember","Read this verse slowly together twice.","Philippians 4:6","Do not be anxious about anything, but in every situation present your requests to God.",,4`,
      `Prayer,"Evening Reflection","7–9","Wind down the day by reflecting on where God showed up.","Olivia Team",reflection,"What Did God Do Today?","Think back through your day together and look for where God showed up.","",,"What moment today showed you God cares for you?",5`,
      `"Faith Conversations","Big Questions","10–12","Honest conversations for families navigating the hard questions of faith.","Olivia Team",story,"Does God Really Hear Me?","Explore the story of Elijah and how God heard his prayer even in his darkest moment.","1 Kings 19:1-18",,"Have you ever felt like God wasn't listening? What happened next?",8`,
    ];
    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-tracks-template.csv'; a.click();
    URL.revokeObjectURL(url);
  };

  const processCSV = (text, name) => {
    setFileName(name || '');
    const { headers, rows } = parseCSV(text);
    const missing = COLS.filter(c => !headers.includes(c));
    if (missing.length) {
      setErrors([`Missing columns: ${missing.join(', ')}`]); setParsed(null); return;
    }

    const errs = [];
    // Group into topics → tracks → days, preserving order
    const topicMap = new Map();  // topic title → Map(trackKey → track obj)
    rows.forEach((row, i) => {
      const n = i + 2;
      const topic      = row.topic?.trim();
      const trackTitle = row.track_title?.trim();
      const ageBand    = row.age_band?.trim();
      const dayType    = row.day_type?.trim();
      const dayTitle   = row.day_title?.trim();

      if (!topic)      { errs.push(`Row ${n}: missing topic`);       return; }
      if (!trackTitle) { errs.push(`Row ${n}: missing track_title`); return; }
      if (!ageBand)    { errs.push(`Row ${n}: missing age_band`);    return; }
      if (!AGE_BANDS.includes(ageBand)) { errs.push(`Row ${n}: unknown age_band "${ageBand}". Valid: ${AGE_BANDS.join(', ')}`); return; }
      if (!VALID_TYPES.includes(dayType)) { errs.push(`Row ${n}: day_type "${dayType}" invalid. Use: ${VALID_TYPES.join(', ')}`); return; }
      if (!dayTitle)   { errs.push(`Row ${n}: missing day_title`);   return; }

      if (!topicMap.has(topic)) topicMap.set(topic, new Map());
      const trackMap = topicMap.get(topic);
      const trackKey = `${trackTitle}||${ageBand}`;
      if (!trackMap.has(trackKey)) trackMap.set(trackKey, {
        title: trackTitle,
        age_band: ageBand,
        about: row.track_about?.trim() || '',
        author: row.track_author?.trim() || '',
        days: [],
      });
      trackMap.get(trackKey).days.push({
        type: dayType,
        title: dayTitle,
        body: row.body?.trim() || '',
        scripture_ref: row.scripture_ref?.trim() || '',
        scripture_text: row.scripture_text?.trim() || '',
        parent_prompt: row.parent_prompt?.trim() || '',
        est_minutes: parseInt(row.est_minutes) || null,
      });
    });

    if (errs.length) { setErrors(errs); setParsed(null); return; }

    const grouped = [...topicMap.entries()].map(([topic, trackMap]) => ({
      topic,
      tracks: [...trackMap.values()],
    }));
    setErrors([]);
    setParsed(grouped);
  };

  const processFile = (f) => {
    const reader = new FileReader();
    reader.onload = (e) => processCSV(e.target.result, f.name);
    reader.readAsText(f);
  };

  const runImport = async () => {
    if (!parsed) return;
    const totalTracks = parsed.reduce((s, g) => s + g.tracks.length, 0);
    setProgress({ done: 0, total: totalTracks, log: [] });

    const addLog = (msg) => setProgress(p => ({ ...p, log: [...p.log, msg] }));
    const bump   = ()    => setProgress(p => ({ ...p, done: p.done + 1 }));

    for (const group of parsed) {
      const existing = existingTopics.find(t => t.title.toLowerCase() === group.topic.toLowerCase());
      let topicId;
      if (existing) {
        topicId = existing.id;
        addLog(`Topic "${group.topic}" — existing`);
      } else {
        const res = await createTopic({ title: group.topic, value_tag: slugify(group.topic) });
        if (res?.error) { addLog(`✗ Could not create topic "${group.topic}": ${res.error}`); continue; }
        topicId = res.id;
        addLog(`Topic "${group.topic}" — created`);
      }

      const existingTracks = await fetchTracksByTopic(topicId);

      if (clearMode && existingTracks.length) {
        addLog(`  Clearing ${existingTracks.length} existing track${existingTracks.length !== 1 ? 's' : ''}…`);
        for (const t of existingTracks) await deleteTrack(t.id);
        existingTracks.length = 0;
      }

      for (const track of group.tracks) {
        const patch = {
          title: track.title,
          about: track.about || null,
          author: track.author || null,
          duration_days: track.days.length,
        };
        const match = existingTracks.find(
          t => t.title.toLowerCase() === track.title.toLowerCase() && t.age_band === track.age_band
        );

        let trackId;
        if (match) {
          const uRes = await updateTrack(match.id, patch);
          if (uRes?.error) { addLog(`  ✗ "${track.title}": ${uRes.error}`); bump(); continue; }
          trackId = match.id;
          addLog(`  ↻ "${track.title}" — updated (${track.age_band})`);
        } else {
          const cRes = await createTrack({
            topic_id: topicId,
            slug: `${slugify(track.title)}-${Math.random().toString(36).slice(2,7)}`,
            age_band: track.age_band,
            is_published: false,
            ...patch,
          });
          if (cRes?.error) { addLog(`  ✗ "${track.title}": ${cRes.error}`); bump(); continue; }
          trackId = cRes.id;
          addLog(`  ✓ "${track.title}" — created (${track.age_band})`);
        }

        const dRes = await saveTrackDays(trackId, track.days);
        if (dRes?.error) addLog(`    ✗ Days: ${dRes.error}`);
        else addLog(`    ${track.days.length} day${track.days.length !== 1 ? 's' : ''} saved`);
        bump();
      }
    }

    setProgress(p => ({ ...p, done: p.total }));
    onImported();
  };

  const done = progress && progress.done >= progress.total;
  const totalTracks = parsed ? parsed.reduce((s, g) => s + g.tracks.length, 0) : 0;
  const totalDays   = parsed ? parsed.reduce((s, g) => s + g.tracks.reduce((ss, t) => ss + t.days.length, 0), 0) : 0;

  return (
    <div style={{ position: 'fixed', inset: 0, background: 'rgba(58,46,38,0.45)', zIndex: 100,
      display: 'flex', alignItems: 'center', justifyContent: 'center', padding: 24 }}>
      <div style={{ width: '100%', maxWidth: 580, background: ADMIN.panel, borderRadius: 20,
        border: `1px solid ${ADMIN.hair}`, display: 'flex', flexDirection: 'column', maxHeight: '90vh', overflow: 'hidden' }}>

        {/* Header */}
        <div style={{ padding: '20px 24px 16px', borderBottom: `1px solid ${ADMIN.hair}`, flexShrink: 0, display: 'flex', alignItems: 'center', gap: 10 }}>
          <Icon name="list" size={18} color={ADMIN.accent}/>
          <div style={{ flex: 1, fontFamily: OLIVIA.fontDisplay, fontSize: 18, fontWeight: 600, color: ADMIN.ink }}>Bulk import tracks</div>
          <button onClick={onClose} style={{ ...S.iconBtn }}>✕</button>
        </div>

        <div style={{ flex: 1, overflowY: 'auto', padding: 24 }}>
          {!progress ? (
            <>
              <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 13, color: ADMIN.inkSoft, lineHeight: 1.6, marginBottom: 16 }}>
                Each row is one day. Rows with the same <strong>topic</strong> + <strong>track_title</strong> + <strong>age_band</strong> are grouped into a track automatically.
              </div>

              {/* Download template */}
              {/* Clear mode toggle */}
              <label style={{ display: 'flex', alignItems: 'flex-start', gap: 10, cursor: 'pointer', marginBottom: 16,
                padding: '12px 14px', borderRadius: 10, background: clearMode ? '#F5DBDC' : ADMIN.pageBg,
                border: `1px solid ${clearMode ? '#E8BABB' : ADMIN.hairStrong}`, transition: 'all .15s' }}>
                <input type="checkbox" checked={clearMode} onChange={e => setClearMode(e.target.checked)}
                  style={{ marginTop: 2, accentColor: '#B85A3D', flexShrink: 0 }}/>
                <div>
                  <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 600, color: clearMode ? '#8E3236' : ADMIN.ink }}>
                    Clear existing tracks before importing
                  </div>
                  <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, color: clearMode ? '#B85A3D' : ADMIN.inkMute, marginTop: 2, lineHeight: 1.5 }}>
                    Deletes all current tracks in each matched topic, then creates fresh ones from the CSV. Use this for a clean re-import.
                  </div>
                </div>
              </label>

              <button onClick={downloadTemplate} style={{ ...S.ghost, fontSize: 12.5, padding: '8px 14px', marginBottom: 16, display: 'inline-flex', alignItems: 'center', gap: 7 }}>
                <Icon name="list" size={13}/> Download CSV template
              </button>

              {/* Drop zone */}
              <div
                onDragOver={e => { e.preventDefault(); setDragging(true); }}
                onDragLeave={() => setDragging(false)}
                onDrop={e => { e.preventDefault(); setDragging(false); const f = e.dataTransfer.files[0]; if (f) processFile(f); }}
                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 .15s', marginBottom: 14 }}>
                <input ref={inputRef} type="file" accept=".csv,text/csv" style={{ display: 'none' }}
                  onChange={e => { const f = e.target.files[0]; if (f) { processFile(f); e.target.value = ''; } }}/>
                <Icon name="list" size={26} color={fileName ? ADMIN.accent : ADMIN.inkMute}/>
                <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 13, color: fileName ? ADMIN.ink : ADMIN.inkMute, marginTop: 8, fontWeight: fileName ? 600 : 400 }}>
                  {fileName || 'Drop a CSV file here or click to browse'}
                </div>
                {fileName && <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, color: ADMIN.inkMute, marginTop: 4 }}>Click to replace</div>}
              </div>

              {/* Errors */}
              {errors.length > 0 && (
                <div style={{ background: '#F5DBDC', borderRadius: 10, padding: '12px 14px', marginBottom: 14 }}>
                  <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, fontWeight: 700, color: '#8E3236', marginBottom: 6 }}>
                    {errors.length} error{errors.length !== 1 ? 's' : ''} — fix in your spreadsheet and re-upload
                  </div>
                  {errors.slice(0, 8).map((e, i) => (
                    <div key={i} style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, color: '#8E3236', lineHeight: 1.5 }}>· {e}</div>
                  ))}
                  {errors.length > 8 && <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, color: '#8E3236', marginTop: 4 }}>…and {errors.length - 8} more</div>}
                </div>
              )}

              {/* Preview */}
              {parsed && (
                <div style={{ background: ADMIN.pageBg, borderRadius: 10, padding: '14px 16px', marginBottom: 4 }}>
                  <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
                    <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, fontWeight: 700, color: ADMIN.inkSoft, letterSpacing: '0.06em', textTransform: 'uppercase' }}>Preview</div>
                    <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, color: ADMIN.inkMute }}>
                      {parsed.length} topic{parsed.length !== 1 ? 's' : ''} · {totalTracks} track{totalTracks !== 1 ? 's' : ''} · {totalDays} days
                    </div>
                  </div>
                  {parsed.map((group, i) => (
                    <div key={i} style={{ marginBottom: 10 }}>
                      <div style={{ fontFamily: OLIVIA.fontDisplay, fontSize: 13, fontWeight: 600, color: ADMIN.ink, display: 'flex', alignItems: 'center', gap: 6 }}>
                        {group.topic}
                        {existingTopics.find(t => t.title.toLowerCase() === group.topic.toLowerCase())
                          ? <span style={{ fontSize: 10, fontWeight: 700, color: ADMIN.inkMute, background: ADMIN.hair, padding: '1px 6px', borderRadius: 5 }}>existing</span>
                          : <span style={{ fontSize: 10, fontWeight: 700, color: '#4E6147', background: '#DCE4D2', padding: '1px 6px', borderRadius: 5 }}>new</span>
                        }
                      </div>
                      {group.tracks.map((tr, j) => (
                        <div key={j} style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.inkSoft, marginTop: 4, paddingLeft: 12, display: 'flex', gap: 8, flexWrap: 'wrap' }}>
                          <span style={{ color: ADMIN.accent }}>→</span>
                          <span>{tr.title}</span>
                          <span style={{ color: ADMIN.inkMute }}>·</span>
                          <span>{tr.age_band}</span>
                          <span style={{ color: ADMIN.inkMute }}>·</span>
                          <span>{tr.days.length} day{tr.days.length !== 1 ? 's' : ''}</span>
                          {tr.author && <><span style={{ color: ADMIN.inkMute }}>·</span><span>by {tr.author}</span></>}
                        </div>
                      ))}
                    </div>
                  ))}
                </div>
              )}
            </>
          ) : (
            <>
              <div style={{ marginBottom: 16 }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 6 }}>
                  <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 600, color: ADMIN.ink }}>
                    {done ? 'Import complete' : 'Importing…'}
                  </div>
                  <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 13, color: ADMIN.inkMute }}>
                    {progress.done}/{progress.total} tracks
                  </div>
                </div>
                <div style={{ height: 6, background: ADMIN.pageBg, borderRadius: 99, overflow: 'hidden' }}>
                  <div style={{ height: '100%', background: ADMIN.accent, borderRadius: 99, transition: 'width .3s',
                    width: `${progress.total ? (progress.done / progress.total) * 100 : 0}%` }}/>
                </div>
              </div>
              <div style={{ background: ADMIN.pageBg, borderRadius: 10, padding: '12px 14px', maxHeight: 300, overflowY: 'auto' }}>
                {progress.log.map((line, i) => (
                  <div key={i} style={{ fontFamily: 'monospace', fontSize: 12, color: line.includes('✗') ? '#B85A3D' : ADMIN.ink, lineHeight: 1.7 }}>
                    {line}
                  </div>
                ))}
              </div>
            </>
          )}
        </div>

        {/* Footer */}
        <div style={{ padding: '14px 24px', borderTop: `1px solid ${ADMIN.hair}`, flexShrink: 0, display: 'flex', justifyContent: 'flex-end', gap: 10 }}>
          <button onClick={onClose} style={S.ghost}>{done ? 'Close' : 'Cancel'}</button>
          {!progress && (
            <button onClick={runImport} disabled={!parsed} style={{ ...S.primary, opacity: parsed ? 1 : 0.45, background: clearMode ? '#B85A3D' : ADMIN.accent }}>
              {clearMode ? 'Clear & import ' : 'Import '}{parsed ? `${totalTracks} track${totalTracks !== 1 ? 's' : ''}` : '…'}
            </button>
          )}
        </div>
      </div>
    </div>
  );
}

// ─── Manager ──────────────────────────────────────────────────
function TracksManager({ email, onSignOut }) {
  const [topics,      setTopics]      = useState([]);
  const [selTopicId,  setSelTopicId]  = useState(null);
  const [tracks,     setTracks]     = useState([]);
  const [selTrackId, setSelTrackId] = useState(null);
  const [days,        setDays]        = useState([]);
  const [selDayIdx,   setSelDayIdx]   = useState(null);
  const [newTopic,       setNewTopic]       = useState('');
  const [newTrack,      setNewTrack]      = useState('');
  const [trackSearch,    setTrackSearch]    = useState('');
  const [importOpen,     setImportOpen]     = useState(false);
  const [topicEditOpen,  setTopicEditOpen]  = useState(false);
  const [flash,          setFlash]          = useState('');
  const [errMsg,         setErrMsg]         = useState('');
  const [dirty,          setDirty]          = useState(false);
  const [iconUploading,  setIconUploading]  = useState(false);

  const [topicsW,  onTopicsDrag]  = usePaneDrag(200, 160, 320);
  const [tracksW,  onTracksDrag]  = usePaneDrag(240, 180, 360);
  const [rightW,   onRightDrag]   = usePaneDrag(300, 240, 460, -1);

  const selTopic  = topics.find(t => t.id === selTopicId) || null;
  const selTrack = tracks.find(c => c.id === selTrackId) || null;
  const selDay    = selDayIdx !== null ? days[selDayIdx] : null;

  const ok  = (m) => { setFlash(m); setTimeout(() => setFlash(''), 2000); };
  const bad = (e) => { setErrMsg(e); setTimeout(() => setErrMsg(''), 5000); };
  const g   = (res) => { if (res?.error) { bad(res.error); return false; } return true; };

  const loadTopics = () => fetchTopics().then(setTopics);
  useEffect(() => { loadTopics(); }, []);
  useEffect(() => {
    if (!selTopicId) { setTracks([]); setSelTrackId(null); return; }
    setTopicEditOpen(false);
    setTrackSearch('');
    fetchTracksByTopic(selTopicId).then(setTracks); setSelTrackId(null);
  }, [selTopicId]);
  useEffect(() => {
    if (!selTrackId) { setDays([]); setSelDayIdx(null); return; }
    fetchTrackDays(selTrackId).then(d => { setDays(d); setSelDayIdx(d.length ? 0 : null); setDirty(false); });
  }, [selTrackId]);

  // Topics CRUD
  const addTopic = async () => {
    if (!newTopic.trim()) return;
    const res = await createTopic({ title: newTopic.trim(), value_tag: slugify(newTopic) });
    if (!g(res)) return;
    setNewTopic(''); await loadTopics(); setSelTopicId(res.id); ok('Topic created');
  };
  const saveTopic = async (patch) => {
    if (!selTopic || !g(await updateTopic(selTopic.id, patch))) return;
    setTopics(ts => ts.map(t => t.id === selTopic.id ? { ...t, ...patch } : t)); ok('Saved');
  };
  const removeTopic = async () => {
    if (!selTopic || !window.confirm(`Delete "${selTopic.title}" and all its tracks?`)) return;
    if (!g(await deleteTopic(selTopic.id))) return;
    setSelTopicId(null); await loadTopics(); ok('Topic deleted');
  };

  // Tracks CRUD
  const addTrack = async () => {
    if (!newTrack.trim() || !selTopicId) return;
    const res = await createTrack({ topic_id: selTopicId, title: newTrack.trim(),
      slug: `${slugify(newTrack)}-${Date.now().toString(36)}`, age_band: AGE_BANDS[2], duration_days: 0, is_published: false });
    if (!g(res)) return;
    setNewTrack(''); const list = await fetchTracksByTopic(selTopicId); setTracks(list);
    setSelTrackId(res.id); ok('Track created');
  };
  const saveTrack = async (patch) => {
    if (!selTrack || !g(await updateTrack(selTrack.id, patch))) return;
    setTracks(cs => cs.map(c => c.id === selTrack.id ? { ...c, ...patch } : c)); ok('Saved');
  };
  const removeTrack = async () => {
    if (!selTrack || !window.confirm(`Delete "${selTrack.title}"?`)) return;
    if (!g(await deleteTrack(selTrack.id))) return;
    setSelTrackId(null); const list = await fetchTracksByTopic(selTopicId); setTracks(list); ok('Deleted');
  };

  // Days
  const addDay = () => {
    const nd = [...days, { type: 'activity', title: '', body: '', scripture_ref: '', scripture_text: '', parent_prompt: '', est_minutes: null }];
    setDays(nd); setSelDayIdx(nd.length - 1); setDirty(true);
  };
  const updateDay = (i, patch) => { setDays(ds => ds.map((d, j) => j === i ? { ...d, ...patch } : d)); setDirty(true); };
  const removeDay = (i) => {
    const nd = days.filter((_, j) => j !== i);
    setDays(nd); setSelDayIdx(nd.length ? Math.min(i, nd.length - 1) : null); setDirty(true);
  };
  const moveDay = (i, dir) => {
    const j = i + dir;
    if (j < 0 || j >= days.length) return;
    const copy = [...days]; [copy[i], copy[j]] = [copy[j], copy[i]];
    setDays(copy); setSelDayIdx(j); setDirty(true);
  };
  const saveDays = async () => {
    if (!selTrack || !g(await saveTrackDays(selTrack.id, days))) return;
    setTracks(cs => cs.map(c => c.id === selTrack.id ? { ...c, duration_days: days.length } : c));
    setDirty(false); ok(`Saved ${days.length} task${days.length === 1 ? '' : 's'}`);
  };

  const clearAllDrafts = async () => {
    if (!window.confirm('Delete ALL draft tracks across every topic? This cannot be undone.')) return;
    const allTopics = await fetchTopics();
    let count = 0;
    for (const t of allTopics) {
      const list = await fetchTracksByTopic(t.id);
      for (const tr of list) {
        if (!tr.is_published) { await deleteTrack(tr.id); count++; }
      }
    }
    setSelTrackId(null);
    const refreshed = selTopicId ? await fetchTracksByTopic(selTopicId) : [];
    setTracks(refreshed);
    ok(`${count} draft${count !== 1 ? 's' : ''} cleared`);
  };

  return (
    <div style={{ flex: 1, display: 'flex', minHeight: 0, background: ADMIN.pageBg }}>

      {/* ── Pane 1: Topics ───────────────────────────────────── */}
      <div style={{ width: topicsW, flexShrink: 0, background: ADMIN.panel, borderRight: `1px solid ${ADMIN.hair}`, display: 'flex', flexDirection: 'column', minHeight: 0 }}>

        {/* Header */}
        <div style={{ padding: '14px 12px 10px', borderBottom: `1px solid ${ADMIN.hair}`, flexShrink: 0 }}>
          <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10 }}>
            <SideLabel>Topics</SideLabel>
            <button onClick={() => setImportOpen(true)} style={{ ...S.ghost, fontSize: 11, padding: '3px 9px', display: 'inline-flex', alignItems: 'center', gap: 4 }}>
              <Icon name="list" size={11}/> Import
            </button>
          </div>
          <div style={{ display: 'flex', gap: 6 }}>
            <input value={newTopic} onChange={e => setNewTopic(e.target.value)} onKeyDown={e => e.key === 'Enter' && addTopic()}
              placeholder="New topic…" maxLength={40} style={{ ...S.input, padding: '7px 10px', fontSize: 12.5, flex: 1 }}/>
            <CPlusBtn onClick={addTopic}/>
          </div>
        </div>

        {/* Topic list */}
        <div style={{ flex: 1, overflowY: 'auto', padding: '6px 8px' }}>
          {topics.map(t => (
            <TopicRow key={t.id} topic={t} selected={t.id === selTopicId} onClick={() => setSelTopicId(t.id)}/>
          ))}
          {!topics.length && <SideEmpty pad>No topics yet.</SideEmpty>}
        </div>

        {/* Footer */}
        <div style={{ borderTop: `1px solid ${ADMIN.hair}`, flexShrink: 0 }}>
<button onClick={clearAllDrafts} style={{
            width: '100%', padding: '9px 14px', appearance: 'none', border: 'none',
            background: 'transparent', cursor: 'pointer', borderBottom: `1px solid ${ADMIN.hair}`,
            fontFamily: OLIVIA.fontBody, fontSize: 12, color: '#B85A3D', fontWeight: 600,
            textAlign: 'left', display: 'flex', alignItems: 'center', gap: 6,
          }}>
            Clear all drafts
          </button>
          <div style={{ padding: '9px 12px', display: 'flex', alignItems: 'center', gap: 6 }}>
            <div style={{ width: 6, height: 6, borderRadius: '50%', background: ADMIN.sage, flexShrink: 0 }}/>
            <div style={{ flex: 1, fontFamily: OLIVIA.fontBody, fontSize: 11, color: ADMIN.inkMute, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{email}</div>
            <button onClick={onSignOut} style={{ appearance: 'none', border: 'none', background: 'none', cursor: 'pointer', fontFamily: OLIVIA.fontBody, fontSize: 11, color: '#B85A3D', fontWeight: 600, padding: 0 }}>Sign out</button>
          </div>
        </div>
      </div>

      <PaneDragHandle onMouseDown={onTopicsDrag}/>

      {/* ── Pane 2: Tracks ───────────────────────────────────── */}
      <div style={{ width: tracksW, flexShrink: 0, background: ADMIN.panel, borderRight: `1px solid ${ADMIN.hair}`, display: 'flex', flexDirection: 'column', minHeight: 0 }}>

        {!selTopic ? (
          <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: 24, gap: 8 }}>
            <Icon name="book" size={28} color={ADMIN.hair}/>
            <SideEmpty>Select a topic.</SideEmpty>
          </div>
        ) : (
          <>
            {/* Topic header */}
            <div style={{ flexShrink: 0, borderBottom: `1px solid ${ADMIN.hair}` }}>
              <div style={{ padding: '12px 12px 10px', display: 'flex', alignItems: 'center', gap: 10 }}>
                <div style={{ width: 38, height: 38, borderRadius: 9, overflow: 'hidden', flexShrink: 0,
                  background: ADMIN.pageBg, border: `1px solid ${ADMIN.hair}`,
                  display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
                  {selTopic.icon
                    ? <img src={selTopic.icon} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
                    : <Icon name="leaf" size={16} color={ADMIN.inkMute}/>
                  }
                </div>
                <div style={{ flex: 1, minWidth: 0 }}>
                  <div style={{ fontFamily: OLIVIA.fontDisplay, fontSize: 14, fontWeight: 600, color: ADMIN.ink, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{selTopic.title}</div>
                  {selTopic.value_tag && <div style={{ fontFamily: 'monospace', fontSize: 10, color: ADMIN.inkMute, marginTop: 1 }}>{selTopic.value_tag}</div>}
                </div>
                <button onClick={() => setTopicEditOpen(o => !o)} title="Edit topic"
                  style={{ ...S.iconBtn, fontSize: 12, color: topicEditOpen ? ADMIN.accent : ADMIN.inkMute }}>
                  {topicEditOpen ? '▲' : '✎'}
                </button>
              </div>

              {/* Collapsible topic editor */}
              {topicEditOpen && (
                <div style={{ padding: '0 12px 12px', display: 'flex', flexDirection: 'column', gap: 8, borderTop: `1px solid ${ADMIN.hair}`, paddingTop: 10 }}>
                  <CField label="Tag">
                    <input defaultValue={selTopic.value_tag || ''} key={selTopic.id + 't'} placeholder="value-tag"
                      onBlur={e => e.target.value !== (selTopic.value_tag || '') && saveTopic({ value_tag: e.target.value })}
                      style={{ ...S.input, fontSize: 12, padding: '6px 9px' }}/>
                  </CField>
                  <CField label="Icon" top>
                    <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
                      <input type="file" accept="image/*" id={`icon-${selTopic.id}`} style={{ display: 'none' }}
                        onChange={async (e) => {
                          const file = e.target.files?.[0]; if (!file) return;
                          setIconUploading(true);
                          const res = await uploadTopicIcon(file, selTopic.id);
                          setIconUploading(false); e.target.value = '';
                          if (res.error) bad(res.error);
                          else { await saveTopic({ icon: res.url }); ok('Icon saved'); }
                        }}/>
                      <label htmlFor={`icon-${selTopic.id}`} style={{ ...S.ghost, cursor: 'pointer', fontSize: 11, padding: '5px 10px', display: 'inline-flex', alignItems: 'center', gap: 5, opacity: iconUploading ? 0.5 : 1 }}>
                        <Icon name="plus" size={11}/> {iconUploading ? 'Uploading…' : selTopic.icon ? 'Replace' : 'Upload icon'}
                      </label>
                      {selTopic.icon && <button onClick={() => saveTopic({ icon: null })} style={S.dangerLink}>Remove</button>}
                    </div>
                  </CField>
                  <CField label="Description" top>
                    <textarea defaultValue={selTopic.description || ''} key={selTopic.id + 'd'} placeholder="Short description" rows={2}
                      onBlur={e => e.target.value !== (selTopic.description || '') && saveTopic({ description: e.target.value })}
                      style={{ ...S.input, fontSize: 12, padding: '6px 9px', resize: 'vertical' }}/>
                  </CField>
                  <button onClick={removeTopic} style={{ ...S.dangerLink, marginTop: 2 }}>Delete topic</button>
                </div>
              )}
            </div>

            {/* New track + search */}
            <div style={{ padding: '10px 10px 8px', flexShrink: 0, borderBottom: `1px solid ${ADMIN.hair}`, display: 'flex', flexDirection: 'column', gap: 6 }}>
              <div style={{ display: 'flex', gap: 6 }}>
                <input value={newTrack} onChange={e => setNewTrack(e.target.value)} onKeyDown={e => e.key === 'Enter' && addTrack()}
                  placeholder="New track…" style={{ ...S.input, padding: '7px 10px', fontSize: 12.5, flex: 1 }}/>
                <CPlusBtn onClick={addTrack}/>
              </div>
              <div style={{ position: 'relative' }}>
                <Icon name="search" size={13} color={ADMIN.inkMute} style={{ position: 'absolute', left: 9, top: '50%', transform: 'translateY(-50%)', pointerEvents: 'none' }}/>
                <input
                  value={trackSearch}
                  onChange={e => setTrackSearch(e.target.value)}
                  placeholder="Search tracks…"
                  style={{ ...S.input, padding: '7px 10px 7px 28px', fontSize: 12.5 }}
                />
                {trackSearch && (
                  <button onClick={() => setTrackSearch('')} style={{ position: 'absolute', right: 8, top: '50%', transform: 'translateY(-50%)', appearance: 'none', border: 'none', background: 'none', cursor: 'pointer', color: ADMIN.inkMute, fontSize: 14, lineHeight: 1, padding: 0 }}>✕</button>
                )}
              </div>
            </div>

            {/* Track list */}
            {(() => {
              const q = trackSearch.toLowerCase().trim();
              const filtered = q ? tracks.filter(c =>
                c.title?.toLowerCase().includes(q) ||
                c.author?.toLowerCase().includes(q) ||
                c.age_band?.toLowerCase().includes(q)
              ) : tracks;
              return (
                <div style={{ flex: 1, overflowY: 'auto', padding: '6px 8px' }}>
                  {!tracks.length && <SideEmpty pad>No tracks yet.</SideEmpty>}
                  {tracks.length > 0 && !filtered.length && <SideEmpty pad>No tracks match "{trackSearch}".</SideEmpty>}
                  {filtered.map(c => (
                    <TrackRow key={c.id} track={c} selected={c.id === selTrackId} onClick={() => setSelTrackId(c.id)}/>
                  ))}
                </div>
              );
            })()}
          </>
        )}
      </div>

      <PaneDragHandle onMouseDown={onTracksDrag}/>

      {/* ── Pane 3: Editor ───────────────────────────────────── */}
      <div style={{ flex: 1, minWidth: 0, overflowY: 'auto', display: 'flex', flexDirection: 'column' }}>
        {(flash || errMsg) && (
          <div style={{ position: 'sticky', top: 0, zIndex: 5, padding: '9px 20px', textAlign: 'center', flexShrink: 0,
            background: errMsg ? '#F5DBDC' : '#DCE4D2', color: errMsg ? '#8E3236' : '#4E6147',
            fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 600 }}>{errMsg || flash}</div>
        )}
        {!selTrack ? (
          <EditorBlank hasTopic={!!selTopic}/>
        ) : (
          <div style={{ padding: '28px 32px 48px', maxWidth: 740 }}>
            {/* Breadcrumb */}
            <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, color: ADMIN.inkMute, marginBottom: 18, display: 'flex', gap: 6, alignItems: 'center' }}>
              <span>{selTopic?.title}</span>
              <span style={{ color: ADMIN.hair }}>›</span>
              <span style={{ color: ADMIN.ink, fontWeight: 600 }}>{selTrack.title}</span>
            </div>

            <TrackEditorCard track={selTrack} onSave={saveTrack} onDelete={removeTrack} dayCount={days.length}/>

            {/* Daily tasks */}
            <div style={{ marginTop: 32 }}>
              <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 4 }}>
                <div>
                  <h3 style={{ margin: 0, fontFamily: OLIVIA.fontDisplay, fontSize: 18, fontWeight: 600, color: ADMIN.ink }}>Daily tasks</h3>
                  <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, color: dirty ? ADMIN.accent : ADMIN.inkMute, marginTop: 2 }}>
                    {days.length} task{days.length !== 1 ? 's' : ''}{dirty ? ' · Unsaved changes' : ' · Up to date'}
                  </div>
                </div>
                <button onClick={saveDays} style={{ ...S.primary, opacity: dirty ? 1 : 0.45, display: 'flex', alignItems: 'center', gap: 6 }}>
                  {dirty ? (
                    <><Icon name="leaf" size={13} color="#FFFCF7"/> Save tasks</>
                  ) : '✓ Saved'}
                </button>
              </div>

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

              {days.map((d, i) => (
                <DayCard key={i} day={d} index={i} total={days.length}
                  selected={selDayIdx === i}
                  onSelect={() => setSelDayIdx(i)}
                  onChange={p => updateDay(i, p)}
                  onRemove={() => removeDay(i)}
                  onMove={dir => moveDay(i, dir)}/>
              ))}

              <button onClick={addDay} style={S.dashed}>
                <Icon name="plus" size={14}/> Add day
              </button>
            </div>
          </div>
        )}
      </div>

      <PaneDragHandle onMouseDown={onRightDrag}/>

      {/* ── Pane 4: Preview ──────────────────────────────────── */}
      <div style={{ width: rightW, flexShrink: 0, background: ADMIN.panel, borderLeft: `1px solid ${ADMIN.hair}`, display: 'flex', flexDirection: 'column', minHeight: 0 }}>
        <div style={{ padding: '14px 16px 12px', borderBottom: `1px solid ${ADMIN.hair}`, flexShrink: 0 }}>
          <SideLabel>Card preview</SideLabel>
          {days.length > 0 && (
            <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4, marginTop: 10 }}>
              {days.map((_, i) => (
                <button key={i} onClick={() => setSelDayIdx(i)} style={{
                  appearance: 'none', cursor: 'pointer', borderRadius: 7, padding: '4px 9px',
                  border: `1px solid ${selDayIdx === i ? ADMIN.accent : ADMIN.hair}`,
                  background: selDayIdx === i ? ADMIN.accentWash : 'transparent',
                  color: selDayIdx === i ? ADMIN.accentDeep : ADMIN.inkMute,
                  fontFamily: OLIVIA.fontBody, fontSize: 11.5, fontWeight: 600,
                }}>D{i + 1}</button>
              ))}
            </div>
          )}
        </div>
        <div style={{ flex: 1, overflowY: 'auto', padding: 16 }}>
          <TrackCardPreview track={selTrack}/>
          <TaskCardPreview day={selDay} dayNumber={(selDayIdx ?? 0) + 1} trackName={selTrack?.title}/>
        </div>
      </div>

      {importOpen && (
        <BulkImportModal
          existingTopics={topics}
          onClose={() => setImportOpen(false)}
          onImported={() => { loadTopics(); setImportOpen(false); }}
        />
      )}
    </div>
  );
}

// ─── Unsplash helper ──────────────────────────────────────────
async function pickUnsplashNaturePhoto(accessKey, excludeIds) {
  for (let page = 1; page <= 3; page++) {
    const res = await fetch(
      `https://api.unsplash.com/search/photos?query=nature&per_page=30&orientation=portrait&page=${page}`,
      { headers: { Authorization: `Client-ID ${accessKey}` } }
    );
    if (!res.ok) return { error: `Unsplash ${res.status}: check your access key` };
    const json = await res.json();
    const photo = (json.results || []).find(p => !excludeIds.has(p.id));
    if (photo) return { photo };
  }
  return { error: 'No unused nature photos found — all 90 results already used.' };
}

// ─── Track settings card ──────────────────────────────────────
function TrackEditorCard({ track, onSave, onDelete, dayCount }) {
  const [f, setF] = useState(track);
  const [imgUploading,        setImgUploading]        = useState(false);
  const [authorImgUploading,  setAuthorImgUploading]  = useState(false);
  const [unsplashLoading,     setUnsplashLoading]     = useState(false);
  const [unsplashErr,       setUnsplashErr]       = useState('');
  const [unsplashKeyInput,  setUnsplashKeyInput]  = useState('');
  const [showKeyPrompt,     setShowKeyPrompt]     = useState(false);
  const [sessionSkipped,    setSessionSkipped]    = useState(new Set());
  useEffect(() => { setF(track); setUnsplashErr(''); }, [track.id]);
  const set = (k, v) => setF(p => ({ ...p, [k]: v }));

  const handleUnsplash = async () => {
    const key = localStorage.getItem('olivia_unsplash_key');
    if (!key) { setShowKeyPrompt(true); return; }
    setUnsplashLoading(true); setUnsplashErr('');
    const usedIds = await fetchUsedUnsplashIds();
    // Also exclude anything skipped this session
    const exclude = new Set([...usedIds, ...sessionSkipped]);
    // Exclude current photo so clicking again cycles to a new one
    if (f.unsplash_photo_id) exclude.add(f.unsplash_photo_id);
    const { photo, error } = await pickUnsplashNaturePhoto(key, exclude);
    if (error) {
      if (error.includes('401')) { localStorage.removeItem('olivia_unsplash_key'); setShowKeyPrompt(true); }
      setUnsplashErr(error); setUnsplashLoading(false); return;
    }
    // Fetch image and upload to Supabase Storage
    try {
      const imgRes = await fetch(`${photo.urls.raw}&w=640&h=960&fit=crop&crop=entropy`);
      const blob = await imgRes.blob();
      const file = new File([blob], `unsplash-${photo.id}.jpg`, { type: 'image/jpeg' });
      const uploadRes = await uploadTrackImage(file, track.id);
      if (uploadRes.error) { setUnsplashErr(uploadRes.error); setUnsplashLoading(false); return; }
      set('cover_image', uploadRes.url);
      set('unsplash_photo_id', photo.id);
      onSave({ cover_image: uploadRes.url, unsplash_photo_id: photo.id });
      // Remember previous photo as skipped so next click doesn't re-pick it
      if (f.unsplash_photo_id) setSessionSkipped(s => new Set([...s, f.unsplash_photo_id]));
    } catch (e) {
      setUnsplashErr('Could not fetch image from Unsplash.');
    }
    setUnsplashLoading(false);
  };

  const live = f.is_published;

  return (
    <div style={{ background: ADMIN.panel, borderRadius: 16, border: `1px solid ${ADMIN.hair}`, padding: 24 }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 20 }}>
        <h2 style={{ margin: 0, fontFamily: OLIVIA.fontDisplay, fontSize: 20, fontWeight: 600, color: ADMIN.ink }}>Track settings</h2>
        <div style={{ display: 'flex', gap: 8 }}>
          <button onClick={() => onSave({ is_published: !f.is_published })} style={{
            appearance: 'none', cursor: 'pointer', borderRadius: 9, padding: '7px 14px',
            border: `1px solid ${live ? '#7A8B6F' : ADMIN.hairStrong}`,
            background: live ? '#DCE4D2' : ADMIN.panel,
            color: live ? '#4E6147' : ADMIN.inkSoft,
            fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 600,
            display: 'flex', alignItems: 'center', gap: 6,
          }}>
            <div style={{ width: 7, height: 7, borderRadius: '50%', background: live ? '#7A8B6F' : ADMIN.hairStrong }}/>
            {live ? 'Live' : 'Draft'}
          </button>
          <button onClick={onDelete} style={{ ...S.ghost, color: '#B85A3D', borderColor: 'transparent', padding: '7px 12px', fontSize: 13 }}>
            Delete
          </button>
        </div>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 14, marginBottom: 14 }}>
        <CField label="Title">
          <input value={f.title || ''} onChange={e => set('title', e.target.value)}
            onBlur={() => f.title !== track.title && onSave({ title: f.title })} style={S.input}/>
        </CField>
        <CField label="Age band">
          <select value={f.age_band} onChange={e => { set('age_band', e.target.value); onSave({ age_band: e.target.value }); }} style={S.input}>
            <option value="all">All ages</option>
            {AGE_BANDS.map(b => <option key={b}>{b}</option>)}
          </select>
        </CField>
        <CField label="Slug">
          <input value={f.slug || ''} onChange={e => set('slug', e.target.value)}
            onBlur={() => f.slug !== track.slug && onSave({ slug: f.slug })}
            style={{ ...S.input, fontFamily: 'monospace', fontSize: 12 }}/>
        </CField>
        <CField label="Duration">
          <div style={{ padding: '10px 0', fontFamily: OLIVIA.fontBody, fontSize: 14, color: ADMIN.inkSoft }}>
            {dayCount} day{dayCount !== 1 ? 's' : ''}
            <span style={{ fontSize: 12, color: ADMIN.inkMute }}> (set by tasks)</span>
          </div>
        </CField>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 14, marginTop: 14 }}>
        <CField label="Author">
          <input value={f.author || ''} onChange={e => set('author', e.target.value)}
            onBlur={() => f.author !== track.author && onSave({ author: f.author })}
            placeholder="e.g. Pastor James" style={S.input}/>
        </CField>
        <CField label="Language">
          <input value={f.language || ''} onChange={e => set('language', e.target.value)}
            onBlur={() => f.language !== track.language && onSave({ language: f.language })}
            placeholder="English" style={S.input}/>
        </CField>
      </div>

      <div style={{ display: 'grid', gridTemplateColumns: '2fr 1fr', gap: 14, marginTop: 14 }}>
        <CField label="Author location">
          <input value={f.author_location || ''} onChange={e => set('author_location', e.target.value)}
            onBlur={() => f.author_location !== track.author_location && onSave({ author_location: f.author_location })}
            placeholder="e.g. Lagos, Nigeria" style={S.input}/>
        </CField>
        <CField label="Author photo" top>
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <div style={{
              width: 48, height: 48, borderRadius: '50%', overflow: 'hidden', flexShrink: 0,
              background: ADMIN.pageBg, border: `1px dashed ${ADMIN.hairStrong}`,
              display: 'flex', alignItems: 'center', justifyContent: 'center',
            }}>
              {f.author_photo_url
                ? <img src={f.author_photo_url} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
                : <Icon name="profile" size={20} color={ADMIN.inkMute}/>
              }
            </div>
            <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
              <input type="file" accept="image/*" id={`author-img-${track.id}`} style={{ display: 'none' }}
                onChange={async (e) => {
                  const file = e.target.files?.[0]; if (!file) return;
                  setAuthorImgUploading(true);
                  const res = await uploadAuthorImage(file, track.id);
                  setAuthorImgUploading(false); e.target.value = '';
                  if (res.error) alert(res.error);
                  else { set('author_photo_url', res.url); onSave({ author_photo_url: res.url }); }
                }}
              />
              <label htmlFor={`author-img-${track.id}`} style={{
                ...S.ghost, cursor: 'pointer', fontSize: 12, padding: '5px 10px',
                opacity: authorImgUploading ? 0.5 : 1, pointerEvents: authorImgUploading ? 'none' : 'auto',
                display: 'inline-flex', alignItems: 'center', gap: 5,
              }}>
                <Icon name="plus" size={11}/> {authorImgUploading ? 'Uploading…' : f.author_photo_url ? 'Replace' : 'Upload'}
              </label>
              {f.author_photo_url && (
                <button onClick={() => { set('author_photo_url', null); onSave({ author_photo_url: null }); }}
                  style={S.dangerLink}>Remove</button>
              )}
            </div>
          </div>
        </CField>
      </div>

      <CField label="About author" top>
        <textarea value={f.author_bio || ''} onChange={e => set('author_bio', e.target.value)}
          onBlur={() => f.author_bio !== track.author_bio && onSave({ author_bio: f.author_bio })}
          rows={3} placeholder="Short bio about the author…" style={{ ...S.input, resize: 'vertical', width: '100%' }}/>
      </CField>

      <CField label="About" top>
        <textarea value={f.about || ''} onChange={e => set('about', e.target.value)}
          onBlur={() => f.about !== track.about && onSave({ about: f.about })}
          rows={3} style={{ ...S.input, resize: 'vertical', width: '100%' }}/>
      </CField>

      <CField label="Cover image" top>
        <div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
          {/* Preview */}
          <div style={{ width: 90, height: 60, borderRadius: 10, overflow: 'hidden', flexShrink: 0,
            background: ADMIN.pageBg, border: `1px dashed ${ADMIN.hairStrong}`,
            display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            {f.cover_image
              ? <img src={f.cover_image} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
              : <Icon name="leaf" size={20} color={ADMIN.inkMute}/>
            }
          </div>
          {/* Actions */}
          <div style={{ display: 'flex', flexDirection: 'column', gap: 5 }}>
            <div style={{ display: 'flex', gap: 6 }}>
              {/* Manual upload */}
              <input type="file" accept="image/*" id={`cover-${track.id}`} style={{ display: 'none' }}
                onChange={async (e) => {
                  const file = e.target.files?.[0]; if (!file) return;
                  setImgUploading(true);
                  const res = await uploadTrackImage(file, track.id);
                  setImgUploading(false); e.target.value = '';
                  if (res.error) alert(res.error);
                  else { set('cover_image', res.url); set('unsplash_photo_id', null); onSave({ cover_image: res.url, unsplash_photo_id: null }); }
                }}
              />
              <label htmlFor={`cover-${track.id}`} style={{
                ...S.ghost, cursor: 'pointer', fontSize: 12, padding: '6px 12px',
                opacity: imgUploading ? 0.5 : 1, pointerEvents: (imgUploading || unsplashLoading) ? 'none' : 'auto',
                display: 'inline-flex', alignItems: 'center', gap: 6,
              }}>
                <Icon name="plus" size={12}/> {imgUploading ? 'Uploading…' : f.cover_image ? 'Replace' : 'Upload'}
              </label>
              {/* Unsplash */}
              <button onClick={handleUnsplash} disabled={unsplashLoading || imgUploading} style={{
                ...S.ghost, fontSize: 12, padding: '6px 12px',
                opacity: (unsplashLoading || imgUploading) ? 0.5 : 1,
                display: 'inline-flex', alignItems: 'center', gap: 6,
                color: ADMIN.accent, borderColor: ADMIN.accent,
              }}>
                {unsplashLoading ? 'Finding…' : f.unsplash_photo_id ? '↻ Try another' : '✦ Use Unsplash'}
              </button>
            </div>
            {f.cover_image && (
              <button onClick={() => { set('cover_image', null); set('unsplash_photo_id', null); onSave({ cover_image: null, unsplash_photo_id: null }); }}
                style={S.dangerLink}>Remove</button>
            )}
            {unsplashErr && (
              <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 11.5, color: '#B85A3D', display: 'flex', alignItems: 'center', gap: 8 }}>
                {unsplashErr}
                <button onClick={() => { localStorage.removeItem('olivia_unsplash_key'); setShowKeyPrompt(true); setUnsplashErr(''); }}
                  style={{ ...S.dangerLink, fontSize: 11.5 }}>Change key</button>
              </div>
            )}
          </div>
        </div>

        {/* Unsplash key prompt */}
        {showKeyPrompt && (
          <div style={{ marginTop: 12, padding: '12px 14px', background: ADMIN.pageBg, borderRadius: 10, border: `1px solid ${ADMIN.hairStrong}` }}>
            <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 12, fontWeight: 600, color: ADMIN.ink, marginBottom: 6 }}>
              Enter your Unsplash Access Key
            </div>
            <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 11.5, color: ADMIN.inkMute, marginBottom: 8, lineHeight: 1.5 }}>
              Get a free key at unsplash.com/developers. Saved locally in your browser.
            </div>
            <div style={{ display: 'flex', gap: 6 }}>
              <input value={unsplashKeyInput} onChange={e => setUnsplashKeyInput(e.target.value)}
                placeholder="Client-ID key…" style={{ ...S.input, fontSize: 12, padding: '6px 10px', flex: 1 }}/>
              <button onClick={() => {
                if (!unsplashKeyInput.trim()) return;
                localStorage.setItem('olivia_unsplash_key', unsplashKeyInput.trim());
                setShowKeyPrompt(false); setUnsplashKeyInput('');
                handleUnsplash();
              }} style={{ ...S.primary, fontSize: 12, padding: '6px 14px' }}>Save & use</button>
              <button onClick={() => { setShowKeyPrompt(false); setUnsplashKeyInput(''); }} style={{ ...S.ghost, fontSize: 12, padding: '6px 10px' }}>Cancel</button>
            </div>
          </div>
        )}
      </CField>
    </div>
  );
}

// ─── Day card ──────────────────────────────────────────────────
function DayCard({ day, index, total, selected, onSelect, onChange, onRemove, onMove }) {
  const meta = TYPE_META[day.type] || TYPE_META.activity;

  return (
    <div onClick={onSelect} style={{
      background: ADMIN.panel, borderRadius: 14, marginBottom: 10, cursor: 'default',
      border: `1px solid ${selected ? ADMIN.accent : ADMIN.hair}`,
      boxShadow: selected ? `0 0 0 3px ${ADMIN.accentWash}` : 'none',
      transition: 'box-shadow .15s, border-color .15s',
    }}>
      {/* Header row */}
      <div style={{ padding: '11px 14px', display: 'flex', alignItems: 'center', gap: 10, borderBottom: `1px solid ${ADMIN.hair}` }}>
        <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700, color: ADMIN.inkMute,
          background: ADMIN.pageBg, borderRadius: 6, padding: '3px 8px', letterSpacing: '0.03em', flexShrink: 0 }}>
          Day {index + 1}
        </span>
        <select value={day.type} onChange={e => onChange({ type: e.target.value })} onClick={e => e.stopPropagation()}
          style={{ appearance: 'none', border: 'none', cursor: 'pointer', padding: '4px 12px', borderRadius: 100,
            fontFamily: OLIVIA.fontBody, fontSize: 11.5, fontWeight: 700,
            background: meta.bg, color: meta.color, flexShrink: 0 }}>
          {DAY_TYPES.map(t => <option key={t} value={t}>{t.replace('_', ' ')}</option>)}
        </select>
        <div style={{ flex: 1, fontFamily: OLIVIA.fontDisplay, fontSize: 13.5, color: day.title ? ADMIN.ink : ADMIN.inkMute,
          fontStyle: day.title ? 'normal' : 'italic', whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>
          {day.title || 'Untitled'}
        </div>
        <div style={{ display: 'flex', gap: 4, flexShrink: 0 }}>
          <button onClick={e => { e.stopPropagation(); onMove(-1); }} disabled={index === 0} style={S.iconBtn}>↑</button>
          <button onClick={e => { e.stopPropagation(); onMove(1); }} disabled={index === total - 1} style={S.iconBtn}>↓</button>
          <button onClick={e => { e.stopPropagation(); onRemove(); }} style={S.iconBtn}><Icon name="close" size={13}/></button>
        </div>
      </div>

      {/* Fields */}
      <div style={{ padding: 14, display: 'flex', flexDirection: 'column', gap: 9 }}>
        <input value={day.title || ''} onChange={e => onChange({ title: e.target.value })}
          placeholder="Task title" onClick={e => e.stopPropagation()}
          style={{ ...S.input, fontFamily: OLIVIA.fontDisplay, fontSize: 15 }}/>
        <textarea value={day.body || ''} onChange={e => onChange({ body: e.target.value })}
          placeholder="Body — what the parent does" rows={2} onClick={e => e.stopPropagation()}
          style={{ ...S.input, fontSize: 13, resize: 'vertical' }}/>
        <div style={{ display: 'grid', gridTemplateColumns: '1fr 2fr', gap: 8 }}>
          <input value={day.scripture_ref || ''} onChange={e => onChange({ scripture_ref: e.target.value })}
            placeholder="Ref (e.g. John 3:16)" onClick={e => e.stopPropagation()}
            style={{ ...S.input, fontSize: 13 }}/>
          <input value={day.scripture_text || ''} onChange={e => onChange({ scripture_text: e.target.value })}
            placeholder="Scripture text" onClick={e => e.stopPropagation()}
            style={{ ...S.input, fontSize: 13 }}/>
        </div>
        <textarea value={day.parent_prompt || ''} onChange={e => onChange({ parent_prompt: e.target.value })}
          placeholder="Conversation starter (optional)" rows={2} onClick={e => e.stopPropagation()}
          style={{ ...S.input, fontSize: 13, resize: 'vertical' }}/>
        <input type="number" value={day.est_minutes || ''} onChange={e => onChange({ est_minutes: parseInt(e.target.value, 10) || null })}
          placeholder="Est. minutes" onClick={e => e.stopPropagation()}
          style={{ ...S.input, fontSize: 13, width: 140 }}/>
      </div>
    </div>
  );
}

// ─── Track browse card preview ────────────────────────────────
const CARD_PALETTE = ['#DCE4D2', '#FAE7D8', '#DDE4F0', '#FBEFC9', '#EFE7D9', '#D4ECE5', '#F0DDE4'];
function cardColor(id) {
  let h = 0;
  for (let i = 0; i < (id || '').length; i++) h = ((h * 31) + id.charCodeAt(i)) >>> 0;
  return CARD_PALETTE[h % CARD_PALETTE.length];
}

function TrackCardPreview({ track }) {
  if (!track) return null;
  const bg = track.cover_image
    ? `url(${track.cover_image}) center/cover no-repeat`
    : cardColor(track.id);
  const meta = [
    track.rating_count > 0 ? `${Number(track.rating_avg).toFixed(1)}★` : null,
    track.duration_days ? `${track.duration_days} days` : null,
  ].filter(Boolean).join(' · ');

  return (
    <div style={{ marginBottom: 20 }}>
      <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 10, fontWeight: 700, color: ADMIN.inkMute,
        letterSpacing: '0.07em', textTransform: 'uppercase', marginBottom: 8 }}>Browse card</div>
      <div style={{
        borderRadius: 16, overflow: 'hidden', height: 180, position: 'relative',
        background: bg,
      }}>
        {/* Gradient overlay */}
        <div style={{
          position: 'absolute', left: 0, right: 0, bottom: 0, height: '70%',
          background: 'linear-gradient(to bottom, transparent, rgba(0,0,0,0.72))',
          display: 'flex', flexDirection: 'column', justifyContent: 'flex-end',
          padding: '0 16px 16px',
        }}>
          {meta && (
            <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 600, color: 'rgba(255,255,255,0.78)', marginBottom: 4 }}>
              {meta}
            </div>
          )}
          <div style={{ fontFamily: OLIVIA.fontDisplay, fontSize: 17, fontWeight: 600, color: '#FFFCF7', lineHeight: 1.25 }}>
            {track.title || <span style={{ opacity: 0.5, fontStyle: 'italic' }}>No title</span>}
          </div>
          {track.author && (
            <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, color: 'rgba(255,255,255,0.65)', marginTop: 3 }}>
              {track.author}
            </div>
          )}
        </div>
        {/* More button */}
        <div style={{
          position: 'absolute', top: 10, right: 10, width: 30, height: 30, borderRadius: '50%',
          background: 'rgba(0,0,0,0.28)', display: 'flex', alignItems: 'center', justifyContent: 'center',
          color: '#fff', fontSize: 16, letterSpacing: 1,
        }}>⋮</div>
      </div>
    </div>
  );
}

// ─── Live card preview ─────────────────────────────────────────
function TaskCardPreview({ day, dayNumber, trackName }) {
  if (!day) {
    return (
      <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center',
        minHeight: 240, gap: 10 }}>
        <Icon name="book" size={30} color={ADMIN.hair}/>
        <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 13, color: ADMIN.inkMute, textAlign: 'center', lineHeight: 1.5 }}>
          {trackName ? 'Select a day to preview.' : 'Select a track to preview.'}
        </div>
      </div>
    );
  }

  const meta = TYPE_META[day.type] || TYPE_META.activity;

  return (
    <div style={{ background: '#F5EFE6', borderRadius: 20, border: `1px solid rgba(58,46,38,0.10)`, overflow: 'hidden' }}>
      {/* Status bar */}
      <div style={{ background: '#F5EFE6', padding: '12px 20px 6px', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
        <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700, color: '#3A2E26' }}>9:41</div>
        <div style={{ display: 'flex', gap: 3, alignItems: 'center' }}>
          {[3, 2, 1].map(h => (
            <div key={h} style={{ width: 3, height: 3 + h * 3, background: '#3A2E26', borderRadius: 1, opacity: 0.6 }}/>
          ))}
          <div style={{ marginLeft: 4, width: 18, height: 9, border: '1.5px solid #3A2E26', borderRadius: 3, position: 'relative', opacity: 0.6 }}>
            <div style={{ position: 'absolute', left: 2, top: 2, right: 4, bottom: 2, background: '#3A2E26', borderRadius: 1 }}/>
          </div>
        </div>
      </div>

      {/* Back nav */}
      <div style={{ padding: '4px 16px', display: 'flex', alignItems: 'center' }}>
        <span style={{ fontSize: 22, color: '#A89E94', lineHeight: 1 }}>‹</span>
      </div>

      {/* Content */}
      <div style={{ padding: '4px 20px 24px' }}>
        {/* Type badge */}
        <div style={{ display: 'inline-flex', alignItems: 'center', gap: 6, marginBottom: 14,
          background: meta.bg, borderRadius: 100, padding: '5px 13px' }}>
          <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 700, color: meta.color, letterSpacing: '0.05em', textTransform: 'uppercase' }}>
            {meta.label}
          </span>
          {day.est_minutes ? <span style={{ fontSize: 11, color: '#A89E94' }}>· {day.est_minutes} min</span> : null}
        </div>

        {/* Track / day label */}
        {trackName && (
          <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 10, color: '#A89E94', letterSpacing: '0.07em', textTransform: 'uppercase', marginBottom: 4 }}>
            {trackName} · Day {dayNumber}
          </div>
        )}

        {/* Title */}
        <div style={{ fontFamily: OLIVIA.fontDisplay, fontSize: 21, fontWeight: 600, color: '#3A2E26', lineHeight: 1.25, marginBottom: 12 }}>
          {day.title || <span style={{ color: '#C8BFB8', fontStyle: 'italic' }}>Task title</span>}
        </div>

        {/* Body */}
        {day.body ? (
          <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 14, color: '#7A6E66', lineHeight: 1.6, marginBottom: 14 }}>
            {day.body}
          </div>
        ) : null}

        {/* Scripture */}
        {(day.scripture_ref || day.scripture_text) ? (
          <div style={{ background: '#DCE4D2', borderRadius: 12, padding: '12px 14px', marginBottom: 12 }}>
            {day.scripture_ref && (
              <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 10, fontWeight: 700, color: '#4E6147', letterSpacing: '0.08em', textTransform: 'uppercase', marginBottom: 5 }}>
                {day.scripture_ref}
              </div>
            )}
            {day.scripture_text && (
              <div style={{ fontFamily: OLIVIA.fontDisplay, fontSize: 14, color: '#3A2E26', lineHeight: 1.5, fontStyle: 'italic' }}>
                "{day.scripture_text}"
              </div>
            )}
          </div>
        ) : null}

        {/* Parent prompt */}
        {day.parent_prompt ? (
          <div style={{ background: '#FAE7D8', borderRadius: 12, padding: '12px 14px', marginBottom: 12 }}>
            <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 10, fontWeight: 700, color: '#9E4B2E', letterSpacing: '0.08em', textTransform: 'uppercase', marginBottom: 5 }}>
              Ask your child
            </div>
            <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 14, color: '#3A2E26', lineHeight: 1.5 }}>
              {day.parent_prompt}
            </div>
          </div>
        ) : null}

        {/* CTA */}
        <div style={{ marginTop: 18, background: '#D97757', borderRadius: 14, padding: 14, textAlign: 'center',
          fontFamily: OLIVIA.fontBody, fontSize: 15, fontWeight: 700, color: '#FFFCF7' }}>
          We did it ✓
        </div>
      </div>
    </div>
  );
}

// ─── Small shared components ──────────────────────────────────
function SideHeader({ children }) {
  return (
    <div style={{ padding: '14px 16px 10px', display: 'flex', alignItems: 'center', justifyContent: 'space-between' }}>
      <SideLabel>{children}</SideLabel>
    </div>
  );
}
function SideLabel({ children }) {
  return <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 10, fontWeight: 700, color: ADMIN.inkMute, letterSpacing: '0.08em', textTransform: 'uppercase' }}>{children}</div>;
}
function SideEmpty({ children, pad }) {
  return <div style={{ padding: pad ? '20px 12px' : '6px 12px', textAlign: 'center', color: ADMIN.inkMute, fontFamily: OLIVIA.fontBody, fontSize: 12 }}>{children}</div>;
}
function CPlusBtn({ onClick }) {
  return (
    <button onClick={onClick} style={{ appearance: 'none', border: 'none', cursor: 'pointer',
      background: ADMIN.accent, color: '#FFFCF7', borderRadius: 8, width: 32, height: 32, flexShrink: 0,
      display: 'inline-flex', alignItems: 'center', justifyContent: 'center' }}>
      <Icon name="plus" size={14} color="#FFFCF7" strokeWidth={2.6}/>
    </button>
  );
}
function TopicRow({ topic, selected, onClick }) {
  return (
    <button onClick={onClick} style={{
      appearance: 'none', border: 'none', cursor: 'pointer', width: '100%', textAlign: 'left',
      padding: '8px 10px', borderRadius: 9, marginBottom: 2,
      background: selected ? ADMIN.accentWash : 'transparent',
      outline: selected ? `1.5px solid ${ADMIN.accent}` : 'none', outlineOffset: -1,
      display: 'flex', alignItems: 'center', gap: 9,
    }}>
      {/* Icon thumbnail */}
      <div style={{ width: 30, height: 30, borderRadius: 7, overflow: 'hidden', flexShrink: 0,
        background: selected ? 'rgba(0,0,0,0.06)' : ADMIN.pageBg,
        border: `1px solid ${selected ? ADMIN.accent : ADMIN.hair}`,
        display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
        {topic.icon
          ? <img src={topic.icon} alt="" style={{ width: '100%', height: '100%', objectFit: 'cover' }}/>
          : <Icon name="leaf" size={13} color={selected ? ADMIN.accent : ADMIN.inkMute}/>
        }
      </div>
      <div style={{ fontFamily: OLIVIA.fontDisplay, fontSize: 13, fontWeight: selected ? 600 : 500, color: selected ? ADMIN.accentDeep : ADMIN.ink, flex: 1, lineHeight: 1.2 }}>
        {topic.title || 'Untitled'}
      </div>
    </button>
  );
}
function TrackRow({ track, selected, onClick }) {
  const live = track.is_published;
  return (
    <button onClick={onClick} style={{
      appearance: 'none', border: 'none', cursor: 'pointer', width: '100%', textAlign: 'left',
      padding: '10px 11px', borderRadius: 10, marginBottom: 3,
      background: selected ? ADMIN.accentWash : 'transparent',
      outline: selected ? `1.5px solid ${ADMIN.accent}` : 'none', outlineOffset: -1,
    }}>
      <div style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 6, marginBottom: 5 }}>
        <div style={{ fontFamily: OLIVIA.fontDisplay, fontSize: 13.5, fontWeight: selected ? 600 : 500, color: selected ? ADMIN.accentDeep : ADMIN.ink, flex: 1, lineHeight: 1.3 }}>
          {track.title || 'Untitled'}
        </div>
        <div style={{ flexShrink: 0, fontSize: 10, fontWeight: 700, fontFamily: OLIVIA.fontBody, borderRadius: 5, padding: '2px 7px',
          background: live ? '#DCE4D2' : ADMIN.pageBg, color: live ? '#4E6147' : ADMIN.inkMute, border: `1px solid ${live ? '#BDD4B4' : ADMIN.hair}` }}>
          {live ? 'Live' : 'Draft'}
        </div>
      </div>
      <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
        <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, fontWeight: 600, color: ADMIN.inkSoft,
          background: ADMIN.pageBg, borderRadius: 5, padding: '2px 7px', border: `1px solid ${ADMIN.hair}` }}>
          {track.age_band}
        </span>
        <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, color: ADMIN.inkMute }}>
          {track.duration_days} day{track.duration_days !== 1 ? 's' : ''}
        </span>
        {track.author && (
          <span style={{ fontFamily: OLIVIA.fontBody, fontSize: 11, color: ADMIN.inkMute, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', flex: 1 }}>
            · {track.author}
          </span>
        )}
      </div>
    </button>
  );
}
function CField({ label, children, top }) {
  return (
    <div style={{ marginTop: top ? 10 : 0 }}>
      <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 10, fontWeight: 700, color: ADMIN.inkMute,
        letterSpacing: '0.07em', textTransform: 'uppercase', marginBottom: 5 }}>{label}</div>
      {children}
    </div>
  );
}
function EditorBlank({ hasTopic }) {
  return (
    <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', padding: 40, gap: 12 }}>
      <Icon name="book" size={32} color={ADMIN.hair}/>
      <div style={{ fontFamily: OLIVIA.fontBody, fontSize: 14, color: ADMIN.inkMute, textAlign: 'center', lineHeight: 1.6 }}>
        {hasTopic ? 'Select or create a track to edit its tasks.' : 'Select a topic, then choose a track.'}
      </div>
    </div>
  );
}

// ─── Style constants ──────────────────────────────────────────
const S = {
  input: {
    width: '100%', boxSizing: 'border-box', background: ADMIN.pageBg,
    border: `1px solid ${ADMIN.hairStrong}`, borderRadius: 9, padding: '9px 13px',
    fontFamily: OLIVIA.fontBody, fontSize: 13.5, color: ADMIN.ink, outline: 'none',
  },
  primary: {
    appearance: 'none', border: 'none', cursor: 'pointer',
    background: ADMIN.accent, color: '#FFFCF7', borderRadius: 9,
    padding: '8px 16px', fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 600,
  },
  ghost: {
    appearance: 'none', cursor: 'pointer', borderRadius: 9, padding: '8px 14px',
    border: `1px solid ${ADMIN.hairStrong}`, background: ADMIN.panel,
    color: ADMIN.inkSoft, fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 600,
  },
  dashed: {
    appearance: 'none', border: `1px dashed ${ADMIN.hairStrong}`, cursor: 'pointer', background: 'transparent',
    color: ADMIN.inkSoft, borderRadius: 10, width: '100%', padding: 12,
    fontFamily: OLIVIA.fontBody, fontSize: 13, fontWeight: 600,
    display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 6,
  },
  iconBtn: {
    appearance: 'none', border: `1px solid ${ADMIN.hair}`, cursor: 'pointer', background: 'transparent',
    color: ADMIN.inkMute, borderRadius: 7, padding: '4px 7px',
    fontFamily: OLIVIA.fontBody, fontSize: 13, display: 'inline-flex', alignItems: 'center',
  },
  dangerLink: {
    appearance: 'none', border: 'none', cursor: 'pointer', background: 'transparent',
    color: '#B85A3D', fontFamily: OLIVIA.fontBody, fontSize: 12, fontWeight: 600,
    padding: 0, textDecoration: 'underline',
  },
};

Object.assign(window, { TracksPage });
