/* interactives-b.jsx — DragSort, Storyboard, Discussion, AIChat, FileUpload, VideoUpload */
const { useState: bState, useEffect: bEffect, useRef: bRef } = React;

/* ============ Drag-and-drop sorting ============ */
const SORT_ITEMS = [
  { id: 1, text: "Practising a hazardous lab procedure safely", bucket: "vr" },
  { id: 2, text: "Standing inside a bustling historical marketplace", bucket: "vr" },
  { id: 3, text: "Memorising a short vocabulary list", bucket: "no" },
  { id: 4, text: "Inspecting a 3D molecule from every angle", bucket: "vr" },
  { id: 5, text: "Reading a one-page news article", bucket: "no" },
  { id: 6, text: "Rehearsing a difficult patient conversation", bucket: "vr" },
];
const BUCKETS = [
  { id: "tray", label: "Drag from here", hint: "Unsorted ideas" },
  { id: "vr", label: "Stronger fit for immersive VR", hint: "Presence, space or risk matter" },
  { id: "no", label: "Often fine without VR", hint: "Text or recall is enough" },
];
function DragSort() {
  const [place, setPlace] = usePersist('sort_place', Object.fromEntries(SORT_ITEMS.map(i => [i.id, 'tray'])));
  const [checked, setChecked] = bState(false);
  const [dragId, setDragId] = bState(null);
  const move = (id, b) => { setPlace(p => ({ ...p, [id]: b })); setChecked(false); };
  const correct = SORT_ITEMS.filter(i => place[i.id] === i.bucket).length;
  const allSorted = SORT_ITEMS.every(i => place[i.id] !== 'tray');
  return (
    <Tool icon="drag" kicker="Sort &amp; decide" title="Where does immersion add value?">
      <p className="muted" style={{ marginBottom: 16 }}>Drag each idea into a column — or use the arrow buttons. Then check how your choices line up.</p>
      <div className="grid" style={{ gridTemplateColumns: 'repeat(3, 1fr)', gap: 14 }}>
        {BUCKETS.map(b => (
          <div key={b.id}
            onDragOver={e => e.preventDefault()}
            onDrop={() => { if (dragId != null) move(dragId, b.id); setDragId(null); }}
            style={{ background: b.id === 'tray' ? 'var(--surface-2)' : 'var(--lime-soft)', borderRadius: 'var(--radius)',
              border: '1.5px dashed var(--border-strong)', padding: 14, minHeight: 240 }}>
            <div style={{ marginBottom: 10 }}>
              <p style={{ fontFamily: 'var(--font-head)', fontWeight: 800, fontSize: 15.5, lineHeight: 1.25 }}>{b.label}</p>
              <span className="muted" style={{ fontSize: 12.5 }}>{b.hint}</span>
            </div>
            <div className="stack" style={{ gap: 8 }}>
              {SORT_ITEMS.filter(i => place[i.id] === b.id).map(i => {
                const ok = checked && place[i.id] === i.bucket;
                const bad = checked && place[i.id] !== i.bucket;
                return (
                  <div key={i.id} draggable onDragStart={() => setDragId(i.id)}
                    style={{ background: 'var(--surface)', borderRadius: 12, padding: '10px 12px', cursor: 'grab',
                      border: '1.5px solid ' + (ok ? 'var(--lime)' : bad ? '#E0584F' : 'var(--border)'),
                      boxShadow: 'var(--shadow)', fontSize: 14.5, fontWeight: 600, marginTop: 8,
                      display: 'flex', gap: 8, alignItems: 'flex-start' }}>
                    <Icon name="drag" size={16} style={{ color: 'var(--text-muted)', flex: 'none', marginTop: 2 }} />
                    <span style={{ flex: 1 }}>{i.text}</span>
                    {checked && (ok ? <Icon name="check" size={16} style={{ color: 'var(--lime-strong)', flex: 'none' }} />
                      : <span style={{ color: '#E0584F', fontWeight: 800, flex: 'none' }}>?</span>)}
                    <div className="col" style={{ gap: 2 }}>
                      {BUCKETS.filter(x => x.id !== b.id).map(x => (
                        <button key={x.id} onClick={() => move(i.id, x.id)} aria-label={'Move to ' + x.label}
                          style={{ border: 'none', background: 'none', padding: 2, margin: 0, cursor: 'pointer', color: 'var(--text-muted)', lineHeight: 0 }}>
                          <Icon name="arrow" size={14} style={{ transform: x.id === 'tray' ? 'rotate(180deg)' : 'none' }} />
                        </button>
                      ))}
                    </div>
                  </div>
                );
              })}
            </div>
          </div>
        ))}
      </div>
      <div className="row" style={{ justifyContent: 'space-between', marginTop: 16 }}>
        <span className="muted" style={{ fontWeight: 700 }}>{checked ? correct + ' of ' + SORT_ITEMS.length + ' match the model answer' : ' '}</span>
        <button className="btn btn-primary btn-sm" disabled={!allSorted} onClick={() => setChecked(true)}>
          <Icon name="check" size={16} /> Check my sorting
        </button>
      </div>
    </Tool>
  );
}

/* ============ Storyboard / template builder ============ */
function Storyboard() {
  const [form, setForm] = usePersist('vr_template', { population: '', topic: '', rationale: '', implementation: '' });
  const [scenes, setScenes] = usePersist('vr_scenes', [{ id: 1, t: '' }, { id: 2, t: '' }, { id: 3, t: '' }]);
  const set = (k, v) => setForm(f => ({ ...f, [k]: v }));
  const fields = [
    { k: 'population', label: 'Population', ph: 'Who are your learners? e.g. first-year undergraduate biology students', icon: 'users' },
    { k: 'topic', label: 'Topic', ph: 'What concept or skill will this VR activity teach?', icon: 'target' },
    { k: 'rationale', label: 'Rationale', ph: 'Why is immersive VR a good fit here, rather than a traditional approach?', icon: 'bulb', area: true },
    { k: 'implementation', label: 'Implementation idea', ph: 'How will this run in your course? Logistics, prep, assessment…', icon: 'flag', area: true },
  ];
  const filled = fields.filter(f => form[f.k].trim()).length + (scenes.filter(s => s.t.trim()).length ? 1 : 0);
  const pct = Math.round((filled / (fields.length + 1)) * 100);
  return (
    <Tool icon="layers" kicker="Build your submission" title="VR activity template">
      <div className="row gap-16" style={{ marginBottom: 18 }}>
        <div style={{ flex: 1 }}><Progress value={pct} /></div>
        <span className="muted" style={{ fontWeight: 800, whiteSpace: 'nowrap' }}>{pct}% complete</span>
      </div>
      <div className="grid" style={{ gridTemplateColumns: '1fr 1fr', gap: 16 }}>
        {fields.map(f => (
          <label key={f.k} className="col" style={{ gridColumn: f.area ? 'span 2' : 'auto', gap: 7 }}>
            <span className="row gap-6" style={{ fontWeight: 800, fontFamily: 'var(--font-head)', fontSize: 15 }}>
              <Icon name={f.icon} size={17} style={{ color: 'var(--lime-strong)' }} /> {f.label}
            </span>
            {f.area
              ? <textarea value={form[f.k]} onChange={e => set(f.k, e.target.value)} placeholder={f.ph} style={taStyle(78)} />
              : <input value={form[f.k]} onChange={e => set(f.k, e.target.value)} placeholder={f.ph} style={taStyle(0, true)} />}
          </label>
        ))}
      </div>
      <div style={{ marginTop: 20 }}>
        <div className="row gap-6" style={{ fontWeight: 800, fontFamily: 'var(--font-head)', fontSize: 15, marginBottom: 10 }}>
          <Icon name="film" size={17} style={{ color: 'var(--lime-strong)' }} /> Storyboard — sketch the scenes
        </div>
        <div className="grid" style={{ gridTemplateColumns: 'repeat(auto-fill, minmax(190px, 1fr))', gap: 12 }}>
          {scenes.map((s, i) => (
            <div key={s.id} style={{ border: '1.5px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden', background: 'var(--surface-2)' }}>
              <div style={{ height: 84, background: 'repeating-linear-gradient(135deg, var(--bg-2), var(--bg-2) 10px, var(--surface-2) 10px, var(--surface-2) 20px)',
                display: 'grid', placeItems: 'center', position: 'relative' }}>
                <span style={{ position: 'absolute', top: 8, left: 10, fontFamily: 'var(--font-head)', fontWeight: 800, fontSize: 13, color: 'var(--lime-strong)' }}>Scene {i + 1}</span>
                <span style={{ fontFamily: 'monospace', fontSize: 11, color: 'var(--text-muted)' }}>[ sketch / image ]</span>
              </div>
              <textarea value={s.t} onChange={e => setScenes(sc => sc.map(x => x.id === s.id ? { ...x, t: e.target.value } : x))}
                placeholder="What happens in this scene?" style={{ ...taStyle(60), border: 'none', borderRadius: 0, background: 'var(--surface)' }} />
            </div>
          ))}
          <button onClick={() => setScenes(sc => [...sc, { id: Date.now(), t: '' }])}
            style={{ border: '1.5px dashed var(--border-strong)', borderRadius: 'var(--radius)', background: 'none', cursor: 'pointer',
              color: 'var(--text-muted)', fontWeight: 700, display: 'grid', placeItems: 'center', minHeight: 150, gap: 6 }}>
            <Icon name="plus" size={24} /> Add scene
          </button>
        </div>
      </div>
      <div className="row" style={{ justifyContent: 'flex-end', marginTop: 18, gap: 10 }}>
        <span className="muted" style={{ marginRight: 'auto', fontWeight: 700, alignSelf: 'center' }}>Auto-saved as you type</span>
        <button className="btn btn-primary"><Icon name="check" size={18} /> Submit template</button>
      </div>
    </Tool>
  );
}
function taStyle(h, single) {
  return { width: '100%', minHeight: single ? 'auto' : h, padding: single ? '11px 14px' : 12, resize: 'vertical',
    fontFamily: 'var(--font-body)', fontSize: 15.5, lineHeight: 1.5, borderRadius: 12, border: '1.5px solid var(--border)',
    background: 'var(--surface-2)', color: 'var(--text)', outline: 'none' };
}

/* ============ Discussion board — Supabase-backed, scoped to lesson ============ */
const SEED_POSTS = [
  { id: 1, who: 'James O.', role: 'Chemistry', avatar: 'JO', when: '2 days ago', text: "The molecule-from-any-angle idea is exactly my pain point — students struggle with stereochemistry on paper. Going to prototype this.", likes: 7 },
  { id: 2, who: 'Aisha R.', role: 'Nursing', avatar: 'AR', when: '1 day ago', text: "Rehearsing difficult patient conversations in VR feels powerful. Has anyone tried recording the session for reflection afterwards?", likes: 12 },
  { id: 3, who: 'Dr. Priya Nair', role: 'Instructor', avatar: 'PN', when: '4 hours ago', text: "Great threads! Remember the rule of thumb: reach for immersion when presence, space, or safe practice is the point. Keep it short for first sessions.", likes: 9, pin: true },
];

function Discussion({ id }) {
  const [posts, setPosts] = bState([]);
  const [liked, setLiked] = usePersist('disc_liked_' + id, []);
  const [draft, setDraft] = bState('');
  const [loading, setLoading] = bState(true);
  const [posting, setPosting] = bState(false);

  bEffect(() => {
    async function load() {
      if (!window.supabaseClient) { setPosts(SEED_POSTS); setLoading(false); return; }
      const { data } = await window.supabaseClient
        .from('community_threads')
        .select(`
          id, body, helpful_count, created_at, kind,
          profiles:author_id (name, role, avatar)
        `)
        .eq('lesson_id', id)
        .order('created_at', { ascending: true });

      if (data && data.length > 0) {
        setPosts(data.map(t => ({
          id: t.id,
          who: t.profiles?.name || 'Unknown',
          role: t.profiles?.role || '',
          avatar: t.profiles?.avatar || '?',
          when: new Date(t.created_at).toLocaleDateString(),
          text: t.body,
          likes: t.helpful_count || 0,
          pin: t.kind === 'pinned'
        })));
      } else {
        setPosts(SEED_POSTS);
      }
      setLoading(false);
    }
    load();
  }, [id]);

  const post = async () => {
    if (!draft.trim() || posting) return;
    const newPost = {
      id: 'temp-' + Date.now(), who: window.EDSTUTIA.learner.name,
      role: window.EDSTUTIA.learner.role, avatar: window.EDSTUTIA.learner.avatar,
      when: 'just now', text: draft.trim(), likes: 0, mine: true
    };
    setPosting(true);
    setPosts(p => [...p, newPost]);
    setDraft('');

    if (window.supabaseClient && window.ME_UUID) {
      const { data } = await window.supabaseClient
        .from('community_threads')
        .insert({ author_id: window.ME_UUID, kind: 'discussion', title: draft.trim().substring(0, 60), body: draft.trim(), lesson_id: id })
        .select('id')
        .maybeSingle();
      if (data) setPosts(p => p.map(x => x.id === newPost.id ? { ...x, id: data.id } : x));
    }
    setPosting(false);
  };

  const like = async (pid) => {
    const isLiked = liked.includes(pid);
    const delta = isLiked ? -1 : 1;
    if (isLiked) setLiked(liked.filter(x => x !== pid));
    else setLiked([...liked, pid]);
    setPosts(p => p.map(x => x.id === pid ? { ...x, likes: x.likes + delta } : x));
    if (window.supabaseClient && !String(pid).startsWith('temp-') && typeof pid !== 'number') {
      const post = posts.find(p => p.id === pid);
      if (post) await window.supabaseClient.from('community_threads').update({ helpful_count: post.likes + delta }).eq('id', pid);
    }
  };

  return (
    <Tool icon="chat" kicker="Shared board" title="Cohort discussion">
      {/* Composer */}
      <div className="row gap-10" style={{ alignItems: 'flex-start', marginBottom: 18 }}>
        <div className="avatar">{window.EDSTUTIA.learner.avatar}</div>
        <div style={{ flex: 1 }}>
          <textarea value={draft} onChange={e => setDraft(e.target.value)}
            onKeyDown={e => { if (e.key === 'Enter' && (e.ctrlKey || e.metaKey)) post(); }}
            placeholder="Share an idea, question, or example with your cohort… (Ctrl+Enter to post)"
            style={{ ...taStyle(54), minHeight: 54 }} />
          <div className="row" style={{ justifyContent: 'flex-end', marginTop: 8 }}>
            <button className="btn btn-primary btn-sm" onClick={post} disabled={!draft.trim() || posting}>
              <Icon name="send" size={15} /> {posting ? 'Posting…' : 'Post'}
            </button>
          </div>
        </div>
      </div>

      {loading ? (
        <div className="stack" style={{ gap: 12 }}>
          {[1,2,3].map(i => <Skeleton key={i} height={80} radius={12} />)}
        </div>
      ) : (
        <div className="stack" style={{ gap: 12 }}>
          {[...posts].sort((a, b) => (b.pin ? 1 : 0) - (a.pin ? 1 : 0)).map(p => (
            <div key={p.id} className="row gap-10" style={{ alignItems: 'flex-start', padding: 14, borderRadius: 'var(--radius)',
              background: p.pin ? 'var(--lime-soft)' : 'var(--surface-2)', border: '1px solid var(--border)', marginTop: 12 }}>
              <div className="avatar" style={{ background: p.role === 'Instructor' ? 'linear-gradient(135deg,#6BA8E8,#2C5E94)' : undefined }}>{p.avatar}</div>
              <div style={{ flex: 1 }}>
                <div className="row gap-6" style={{ flexWrap: 'wrap' }}>
                  <strong style={{ fontFamily: 'var(--font-head)' }}>{p.who}</strong>
                  <span className="pill" style={{ fontSize: 11, padding: '2px 9px' }}>{p.role}</span>
                  {p.pin && <span className="row gap-6" style={{ color: 'var(--lime-strong)', fontWeight: 800, fontSize: 12.5 }}><Icon name="pin" size={14} /> Pinned</span>}
                  <span className="muted" style={{ fontSize: 13, marginLeft: 'auto' }}>{p.when}</span>
                </div>
                <p style={{ marginTop: 6, fontSize: 16, lineHeight: 1.5 }}>{p.text}</p>
                <div className="row gap-16" style={{ marginTop: 8 }}>
                  <button onClick={() => like(p.id)} className="row gap-6" style={{ border: 'none', background: 'none', padding: 0, cursor: 'pointer', fontWeight: 700, fontSize: 14,
                    color: liked.includes(p.id) ? 'var(--lime-strong)' : 'var(--text-muted)' }}>
                    <Icon name="spark" size={16} /> {p.likes}
                  </button>
                </div>
              </div>
            </div>
          ))}
          {posts.length === 0 && <p className="muted" style={{ textAlign: 'center', padding: '20px 0', fontStyle: 'italic' }}>No posts yet — be the first to share!</p>}
        </div>
      )}
    </Tool>
  );
}

/* ============ File Upload tool (learner PDF/document submission) ============ */
function FileUpload({ lessonId, label = 'Upload your document', hint = 'Accepted: PDF, DOCX, PPTX — up to 50 MB', onComplete }) {
  const [submission, setSubmission] = bState(null);
  const [uploading, setUploading] = bState(false);
  const [progress, setProgress] = bState(0);
  const [error, setError] = bState('');
  const fileRef = bRef(null);

  bEffect(() => {
    async function load() {
      if (!window.supabaseClient || !window.ME_UUID) return;
      const { data } = await window.supabaseClient
        .from('submissions')
        .select('file_name, storage_path, submitted_at')
        .eq('user_id', window.ME_UUID)
        .eq('lesson_id', lessonId)
        .maybeSingle();
      if (data) { setSubmission(data); onComplete?.(); }
    }
    load();
  }, [lessonId]);

  const handleFile = async (e) => {
    const file = e.target.files[0];
    if (!file) return;
    if (file.size > 50 * 1024 * 1024) { setError('File exceeds 50 MB limit.'); return; }
    setError(''); setUploading(true); setProgress(10);

    const path = `submissions/${window.ME_UUID || 'anon'}/${lessonId}/${Date.now()}_${file.name.replace(/[^a-zA-Z0-9._-]/g, '_')}`;

    if (window.supabaseClient) {
      setProgress(40);
      const { error: upErr } = await window.supabaseClient.storage.from('media').upload(path, file, { upsert: true });
      if (upErr) { setError('Upload failed: ' + upErr.message); setUploading(false); return; }
      setProgress(80);
      await window.supabaseClient.from('submissions').upsert({
        user_id: window.ME_UUID,
        lesson_id: lessonId,
        storage_path: path,
        file_name: file.name,
        file_type: file.type,
        size_bytes: file.size,
        submitted_at: new Date().toISOString()
      }, { onConflict: 'user_id,lesson_id' });
      setSubmission({ file_name: file.name, storage_path: path, submitted_at: new Date().toISOString() });
      onComplete?.();
    }
    setProgress(100); setUploading(false);
  };

  const getPublicUrl = (path) => {
    if (!window.supabaseClient) return '#';
    const { data: { publicUrl } } = window.supabaseClient.storage.from('media').getPublicUrl(path);
    return publicUrl;
  };

  return (
    <Tool icon="upload" kicker="Assignment submission" title={label}>
      <p className="muted" style={{ marginBottom: 20 }}>{hint}</p>

      {submission ? (
        <div className="card-pad row gap-16" style={{ background: 'var(--lime-soft)', borderRadius: 'var(--radius)', alignItems: 'center', marginBottom: 20 }}>
          <div style={{ width: 44, height: 44, borderRadius: 12, background: 'var(--lime)', display: 'grid', placeItems: 'center', flex: 'none' }}>
            <Icon name="check" size={22} style={{ color: '#14180D' }} />
          </div>
          <div style={{ flex: 1, minWidth: 0 }}>
            <strong style={{ display: 'block', fontSize: 15 }}>Submitted</strong>
            <span className="muted" style={{ fontSize: 13.5 }}>{submission.file_name}</span>
            <span className="muted" style={{ fontSize: 12, display: 'block', marginTop: 2 }}>
              {new Date(submission.submitted_at).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit' })}
            </span>
          </div>
          <a href={getPublicUrl(submission.storage_path)} target="_blank" className="btn btn-ghost btn-sm">
            <Icon name="eye" size={15} /> View
          </a>
        </div>
      ) : null}

      {error && <div style={{ padding: 12, marginBottom: 16, background: 'rgba(224,88,79,.1)', color: '#C0392B', borderRadius: 8, fontWeight: 600, fontSize: 14 }}>{error}</div>}

      {uploading && (
        <div style={{ marginBottom: 16 }}>
          <div className="row" style={{ justifyContent: 'space-between', marginBottom: 8 }}>
            <span className="muted" style={{ fontSize: 14 }}>Uploading…</span>
            <span style={{ fontWeight: 700, fontSize: 14 }}>{progress}%</span>
          </div>
          <div className="progress"><i style={{ width: progress + '%', transition: 'width .4s' }} /></div>
        </div>
      )}

      <div style={{ border: '2px dashed var(--border-strong)', borderRadius: 'var(--radius)', padding: '32px 24px', textAlign: 'center', background: 'var(--surface-2)' }}>
        <Icon name="upload" size={36} style={{ color: 'var(--text-muted)', marginBottom: 12 }} />
        <p style={{ fontWeight: 700, fontSize: 16, marginBottom: 6 }}>{submission ? 'Replace submission' : 'Drop your file here'}</p>
        <p className="muted" style={{ fontSize: 14, marginBottom: 16 }}>or click to browse</p>
        <label className="btn btn-primary" style={{ cursor: 'pointer', display: 'inline-flex' }}>
          <Icon name="upload" size={17} /> {submission ? 'Replace file' : 'Choose file'}
          <input ref={fileRef} type="file" accept=".pdf,.doc,.docx,.ppt,.pptx" style={{ display: 'none' }} onChange={handleFile} disabled={uploading} />
        </label>
      </div>
    </Tool>
  );
}

/* ============ Video Upload tool (learner video reflection submission) ============ */
function VideoUpload({ lessonId, label = 'Upload your video reflection', hint = 'Record a 5-8 minute reflection. Accepted: MP4, MOV, WEBM — up to 500 MB', onComplete }) {
  const [submission, setSubmission] = bState(null);
  const [uploading, setUploading] = bState(false);
  const [progress, setProgress] = bState(0);
  const [error, setError] = bState('');

  bEffect(() => {
    async function load() {
      if (!window.supabaseClient || !window.ME_UUID) return;
      const { data } = await window.supabaseClient
        .from('submissions')
        .select('file_name, storage_path, submitted_at, file_type')
        .eq('user_id', window.ME_UUID)
        .eq('lesson_id', lessonId + '_video')
        .maybeSingle();
      if (data) { setSubmission(data); onComplete?.(); }
    }
    load();
  }, [lessonId]);

  const handleFile = async (e) => {
    const file = e.target.files[0];
    if (!file) return;
    if (file.size > 500 * 1024 * 1024) { setError('File exceeds 500 MB limit.'); return; }
    if (!file.type.startsWith('video/')) { setError('Please choose a video file (MP4, MOV, or WEBM).'); return; }
    setError(''); setUploading(true); setProgress(5);

    const path = `submissions/${window.ME_UUID || 'anon'}/${lessonId}/video_${Date.now()}_${file.name.replace(/[^a-zA-Z0-9._-]/g, '_')}`;

    if (window.supabaseClient) {
      // Chunked-style progress simulation (Supabase JS doesn't expose upload progress natively)
      const ticker = setInterval(() => setProgress(p => Math.min(p + 5, 85)), 500);
      const { error: upErr } = await window.supabaseClient.storage.from('media').upload(path, file, { upsert: true });
      clearInterval(ticker);
      if (upErr) { setError('Upload failed: ' + upErr.message); setUploading(false); setProgress(0); return; }
      setProgress(95);
      await window.supabaseClient.from('submissions').upsert({
        user_id: window.ME_UUID,
        lesson_id: lessonId + '_video',
        storage_path: path,
        file_name: file.name,
        file_type: file.type,
        size_bytes: file.size,
        submitted_at: new Date().toISOString()
      }, { onConflict: 'user_id,lesson_id' });
      setSubmission({ file_name: file.name, storage_path: path, submitted_at: new Date().toISOString(), file_type: file.type });
      onComplete?.();
    }
    setProgress(100); setUploading(false);
  };

  const getPublicUrl = (path) => {
    if (!window.supabaseClient) return '#';
    const { data: { publicUrl } } = window.supabaseClient.storage.from('media').getPublicUrl(path);
    return publicUrl;
  };

  return (
    <Tool icon="film" kicker="Video submission" title={label}>
      <p className="muted" style={{ marginBottom: 20 }}>{hint}</p>

      {submission && (
        <div style={{ marginBottom: 20 }}>
          <div className="card-pad row gap-16" style={{ background: 'var(--lime-soft)', borderRadius: 'var(--radius)', alignItems: 'center', marginBottom: 12 }}>
            <div style={{ width: 44, height: 44, borderRadius: 12, background: 'var(--lime)', display: 'grid', placeItems: 'center', flex: 'none' }}>
              <Icon name="check" size={22} style={{ color: '#14180D' }} />
            </div>
            <div style={{ flex: 1 }}>
              <strong style={{ display: 'block', fontSize: 15 }}>Video submitted</strong>
              <span className="muted" style={{ fontSize: 13.5 }}>{submission.file_name}</span>
              <span className="muted" style={{ fontSize: 12, display: 'block', marginTop: 2 }}>
                {new Date(submission.submitted_at).toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric', hour: '2-digit', minute: '2-digit' })}
              </span>
            </div>
          </div>
          {/* Video preview */}
          <video controls src={getPublicUrl(submission.storage_path)}
            style={{ width: '100%', borderRadius: 'var(--radius)', background: '#000', maxHeight: 360 }}>
            Your browser doesn't support video playback.
          </video>
        </div>
      )}

      {error && <div style={{ padding: 12, marginBottom: 16, background: 'rgba(224,88,79,.1)', color: '#C0392B', borderRadius: 8, fontWeight: 600, fontSize: 14 }}>{error}</div>}

      {uploading && (
        <div style={{ marginBottom: 16 }}>
          <div className="row" style={{ justifyContent: 'space-between', marginBottom: 8 }}>
            <span className="muted" style={{ fontSize: 14 }}>Uploading video… this may take a moment</span>
            <span style={{ fontWeight: 700, fontSize: 14 }}>{progress}%</span>
          </div>
          <div className="progress"><i style={{ width: progress + '%', transition: 'width .6s' }} /></div>
        </div>
      )}

      <div style={{ border: '2px dashed var(--border-strong)', borderRadius: 'var(--radius)', padding: '32px 24px', textAlign: 'center', background: 'var(--surface-2)' }}>
        <Icon name="film" size={36} style={{ color: 'var(--text-muted)', marginBottom: 12 }} />
        <p style={{ fontWeight: 700, fontSize: 16, marginBottom: 6 }}>{submission ? 'Replace video' : 'Drop your video here'}</p>
        <p className="muted" style={{ fontSize: 14, marginBottom: 16 }}>MP4, MOV, or WEBM · up to 500 MB</p>
        <label className="btn btn-primary" style={{ cursor: 'pointer', display: 'inline-flex' }}>
          <Icon name="film" size={17} /> {submission ? 'Replace video' : 'Choose video'}
          <input type="file" accept="video/mp4,video/mov,video/quicktime,video/webm" style={{ display: 'none' }} onChange={handleFile} disabled={uploading} />
        </label>
      </div>
    </Tool>
  );
}

/* ============ AI persona chat sandbox — real Claude API ============ */
const PERSONAS = [
  { id: 'sora', name: 'Professor Sora', tag: 'Socratic biology tutor', avatar: 'PS',
    sys: "You are Professor Sora, a warm, patient Socratic biology tutor inside a teacher-training course. Keep replies to 2-4 short sentences. Guide with questions rather than just giving answers. Never break character.",
    hello: "Hello! I'm Professor Sora. Rather than hand you answers, I like to ask the next good question. What biology idea shall we untangle together?" },
  { id: 'patient', name: 'Mr. Alvarez', tag: 'Standardised patient', avatar: 'MA',
    sys: "You role-play Mr. Alvarez, a nervous 58-year-old patient in a nursing communication exercise. You are worried about a new diagnosis. Reply in-character in 2-3 sentences, showing realistic emotion. Never give clinical advice or break character.",
    hello: "Oh… hello. The nurse said you'd talk me through my results. I'll be honest, I haven't slept much. Is it serious, doctor?" },
  { id: 'coach', name: 'Coach Vega', tag: 'Debate practice partner', avatar: 'CV',
    sys: "You are Coach Vega, an energetic debate coach. Challenge the learner's reasoning respectfully, ask them to defend claims, and offer one tip per reply. Keep it to 2-3 sentences. Stay in character.",
    hello: "Welcome to the floor! Give me a claim — any claim — and I'll push back so you can sharpen how you defend it. Ready?" },
];

const FALLBACK = {
  sora: "Good thinking. What evidence would you look for to test that idea? Try saying it back in your own words first.",
  patient: "I appreciate you explaining it slowly. Could you tell me what happens next — and will it hurt?",
  coach: "Solid start — but a sceptic would ask for your strongest piece of evidence. What's the one fact you'd lead with?"
};

function AIChat() {
  // AI_CHAT_DISABLED — remove this block to re-enable
  return (
    <Tool icon="robot" kicker="AI persona sandbox" title="Talk to an AI persona">
      <div style={{ textAlign: 'center', padding: '40px 24px', color: 'var(--text-muted)' }}>
        <Icon name="robot" size={40} style={{ marginBottom: 16, opacity: .4 }} />
        <p style={{ fontWeight: 700, fontSize: 17, marginBottom: 6 }}>Coming soon</p>
        <p style={{ fontSize: 15 }}>AI persona conversations will be available in an upcoming release.</p>
      </div>
    </Tool>
  );
  // eslint-disable-next-line no-unreachable
  const [pid, setPid] = bState('sora');
  const persona = PERSONAS.find(p => p.id === pid);
  const [msgs, setMsgs] = bState([{ role: 'ai', text: persona.hello }]);
  const [input, setInput] = bState('');
  const [busy, setBusy] = bState(false);
  const [apiError, setApiError] = bState('');
  const scroller = bRef(null);

  bEffect(() => { setMsgs([{ role: 'ai', text: persona.hello }]); setApiError(''); }, [pid]);
  bEffect(() => { if (scroller.current) scroller.current.scrollTop = scroller.current.scrollHeight; }, [msgs, busy]);

  const send = async () => {
    const text = input.trim();
    if (!text || busy) return;
    const next = [...msgs, { role: 'me', text }];
    setMsgs(next); setInput(''); setBusy(true); setApiError('');
    try {
      // Build messages in Anthropic format (alternate user/assistant)
      const apiMessages = next.map(m => ({ role: m.role === 'me' ? 'user' : 'assistant', content: m.text }));

      const res = await fetch('/api/chat', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ system: persona.sys, messages: apiMessages })
      });

      if (!res.ok) {
        const errData = await res.json().catch(() => ({}));
        throw new Error(errData.error || 'AI service unavailable');
      }

      const { reply } = await res.json();
      setMsgs(m => [...m, { role: 'ai', text: reply || FALLBACK[persona.id] }]);
    } catch (err) {
      const isMisconfigured = err.message?.includes('ANTHROPIC_API_KEY');
      setApiError(isMisconfigured
        ? 'AI not yet configured. Add ANTHROPIC_API_KEY to your Vercel environment variables.'
        : 'Could not reach AI service. Using sample response.');
      setMsgs(m => [...m, { role: 'ai', text: FALLBACK[persona.id] }]);
    } finally { setBusy(false); }
  };

  return (
    <Tool icon="robot" kicker="AI persona sandbox" title="Talk to an AI persona">
      <p className="muted" style={{ marginBottom: 14 }}>Pick a persona and have a short conversation. Notice how its goals and tone shape the interaction — you'll design your own next.</p>

      {apiError && (
        <div style={{ padding: 10, marginBottom: 14, background: 'rgba(224,88,79,.08)', border: '1px solid rgba(224,88,79,.25)', borderRadius: 8, color: '#c0392b', fontSize: 13.5, fontWeight: 600 }}>
          {apiError}
        </div>
      )}

      {/* Persona selector */}
      <div className="row gap-10" style={{ marginBottom: 14, flexWrap: 'wrap' }}>
        {PERSONAS.map(p => (
          <button key={p.id} onClick={() => setPid(p.id)} className="row gap-10"
            style={{ cursor: 'pointer', padding: '8px 14px 8px 8px', borderRadius: 'var(--radius-pill)', textAlign: 'left',
              border: '1.5px solid ' + (pid === p.id ? 'var(--lime)' : 'var(--border)'),
              background: pid === p.id ? 'var(--lime-soft)' : 'var(--surface)',
              transition: 'all .15s' }}>
            <div className="avatar" style={{ width: 34, height: 34, fontSize: 13 }}>{p.avatar}</div>
            <div className="col">
              <strong style={{ fontFamily: 'var(--font-head)', fontSize: 14.5, lineHeight: 1.1 }}>{p.name}</strong>
              <span className="muted" style={{ fontSize: 12.5 }}>{p.tag}</span>
            </div>
          </button>
        ))}
      </div>

      {/* Chat window */}
      <div style={{ border: '1px solid var(--border)', borderRadius: 'var(--radius)', overflow: 'hidden', background: 'var(--surface-2)' }}>
        <div ref={scroller} style={{ maxHeight: 340, minHeight: 220, overflowY: 'auto', padding: 16, display: 'flex', flexDirection: 'column', gap: 12 }}>
          {msgs.map((m, i) => (
            <div key={i} className="row gap-10" style={{ alignItems: 'flex-end', flexDirection: m.role === 'me' ? 'row-reverse' : 'row' }}>
              {m.role === 'ai' && <div className="avatar" style={{ width: 32, height: 32, fontSize: 12, flex: 'none' }}>{persona.avatar}</div>}
              <div style={{ maxWidth: '76%', padding: '11px 15px', borderRadius: 18, fontSize: 16, lineHeight: 1.5,
                background: m.role === 'me' ? 'var(--lime)' : 'var(--surface)', color: m.role === 'me' ? '#14180D' : 'var(--text)',
                borderBottomRightRadius: m.role === 'me' ? 4 : 18, borderBottomLeftRadius: m.role === 'me' ? 18 : 4,
                border: m.role === 'ai' ? '1px solid var(--border)' : 'none', fontWeight: m.role === 'me' ? 600 : 500 }}>
                {m.text}
              </div>
            </div>
          ))}
          {busy && (
            <div className="row gap-10">
              <div className="avatar" style={{ width: 32, height: 32, fontSize: 12 }}>{persona.avatar}</div>
              <div style={{ padding: '12px 16px', borderRadius: 18, background: 'var(--surface)', border: '1px solid var(--border)' }}>
                <span className="typing">•••</span>
              </div>
            </div>
          )}
        </div>
        <div className="row gap-10" style={{ padding: 12, borderTop: '1px solid var(--border)', background: 'var(--surface)' }}>
          <input value={input} onChange={e => setInput(e.target.value)}
            onKeyDown={e => e.key === 'Enter' && !e.shiftKey && send()}
            placeholder={'Message ' + persona.name + '…'}
            style={{ flex: 1, border: 'none', outline: 'none', background: 'none', fontFamily: 'var(--font-body)', fontSize: 16, color: 'var(--text)' }} />
          <button className="btn btn-primary btn-sm" onClick={send} disabled={busy || !input.trim()}>
            <Icon name="send" size={16} /> Send
          </button>
        </div>
      </div>
      <p className="muted" style={{ fontSize: 12.5, marginTop: 8, textAlign: 'center' }}>Powered by Claude AI · Responses are generated and may not always be accurate</p>
    </Tool>
  );
}

Object.assign(window, { DragSort, Storyboard, Discussion, FileUpload, VideoUpload, AIChat });
