// Content Manager surface: sidebar, profile, media library, course-scoped
// quizzes and the CM home. Split out of features-more.jsx.

// ── Content Manager ────────────────────────────────────────────────────────────
const CM_NAV = [
  ["Courses",  I.book,    "/app/content"],
  ["Upload",   I.rocket,  "/app/content/upload"],
  ["Media Library", I.file, "/app/content/all-files"],
  ["Text Library", I.text, "/app/content/text"],
  ["Quizzes",  I.puzzle,  "/app/content/quizzes"],
  ["Profile",  I.user,    "/app/content/profile"]
];

// Reusable account/profile panel — shows the signed-in user's details (incl.
// their usr-UID) and lets them change their password. Usable by any role.
const ProfilePanel = ({ session }) => {
  const u = session || {};
  const initial = String(u.name || u.email || "?").trim().charAt(0).toUpperCase();
  const [pw, setPw] = React.useState({ current:"", next:"", confirm:"" });
  const [pwSaving, setPwSaving] = React.useState(false);
  const [pwMsg, setPwMsg] = React.useState(null);
  const [copied, setCopied] = React.useState(false);
  const copyUid = () => { if (!u.publicUid) return; navigator.clipboard?.writeText(u.publicUid); setCopied(true); setTimeout(()=>setCopied(false), 1200); };
  const changePassword = async () => {
    setPwMsg(null);
    if (!pw.current || !pw.next) { setPwMsg({ type:"err", text:"Fill in both password fields" }); return; }
    if (pw.next !== pw.confirm) { setPwMsg({ type:"err", text:"New passwords don't match" }); return; }
    setPwSaving(true);
    const res = await apiFetch("/auth/change-password", { method:"POST", body: JSON.stringify({ currentPassword: pw.current, newPassword: pw.next }) });
    setPwSaving(false);
    if (!res.ok) { const d = await res.json().catch(()=>({})); setPwMsg({ type:"err", text: d.error || "Could not change password" }); return; }
    setPw({ current:"", next:"", confirm:"" });
    setPwMsg({ type:"ok", text:"Password updated." });
  };
  const inputS = { width:"100%", padding:"9px 12px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:13, color:"var(--ink)", fontFamily:"var(--font-ui)", boxSizing:"border-box", outline:"none" };
  const roleLabel = String(u.role || "").replace(/_/g, " ");
  const Row = ({ label, value }) => (
    <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", padding:"10px 0", borderBottom:"var(--border-thin)" }}>
      <span style={{ fontSize:12, color:"var(--ink-mute)" }}>{label}</span>
      <span style={{ fontSize:13, fontWeight:600, color:"var(--ink)" }}>{value || "—"}</span>
    </div>
  );
  return (
    <div style={{ flex:1, overflow:"auto", padding:28, background:"var(--paper)" }}>
      <div style={{ maxWidth:640, margin:"0 auto", display:"flex", flexDirection:"column", gap:20 }}>
        <div className="card-flat" style={{ padding:22, display:"flex", alignItems:"center", gap:16 }}>
          <div style={{ width:60, height:60, borderRadius:16, background:"var(--coral)", color:"white", display:"grid", placeItems:"center", fontSize:26, fontWeight:800 }}>{initial}</div>
          <div style={{ flex:1, minWidth:0 }}>
            <div className="display" style={{ fontSize:22 }}>{u.name || "—"}</div>
            <div style={{ fontSize:13, color:"var(--ink-mute)" }}>{u.email}</div>
          </div>
          <span className="chip" style={{ fontSize:11, padding:"3px 10px", borderRadius:999, background:"var(--paper-deep)", color:"var(--ink-soft)", fontWeight:700, textTransform:"capitalize" }}>{roleLabel}</span>
        </div>

        <div className="card-flat" style={{ padding:22 }}>
          <div className="mono" style={{ fontSize:10, color:"var(--ink-mute)", letterSpacing:".06em", marginBottom:6 }}>ACCOUNT</div>
          <Row label="Name" value={u.name}/>
          <Row label="Email" value={u.email}/>
          {u.username && <Row label="Username" value={u.username}/>}
          <Row label="Role" value={roleLabel}/>
          <div style={{ display:"flex", justifyContent:"space-between", alignItems:"center", padding:"10px 0" }}>
            <span style={{ fontSize:12, color:"var(--ink-mute)" }}>User UID</span>
            <span style={{ display:"flex", alignItems:"center", gap:8 }}>
              <span className="mono" style={{ fontSize:13, fontWeight:600 }}>{u.publicUid || "—"}</span>
              {u.publicUid && <button onClick={copyUid} className="btn btn-ghost" style={{ padding:"3px 10px", fontSize:11 }}>{copied?"Copied":"Copy"}</button>}
            </span>
          </div>
        </div>

        <div className="card-flat" style={{ padding:22 }}>
          <div className="mono" style={{ fontSize:10, color:"var(--ink-mute)", letterSpacing:".06em", marginBottom:12 }}>CHANGE PASSWORD</div>
          <div style={{ display:"flex", flexDirection:"column", gap:10 }}>
            <input type="password" value={pw.current} onChange={e=>setPw(p=>({...p,current:e.target.value}))} placeholder="Current password" style={inputS}/>
            <input type="password" value={pw.next} onChange={e=>setPw(p=>({...p,next:e.target.value}))} placeholder="New password" style={inputS}/>
            <input type="password" value={pw.confirm} onChange={e=>setPw(p=>({...p,confirm:e.target.value}))} placeholder="Confirm new password" style={inputS}/>
            {pwMsg && <div style={{ fontSize:12, color: pwMsg.type==="ok" ? "var(--moss)" : "var(--coral)" }}>{pwMsg.text}</div>}
            <button onClick={changePassword} disabled={pwSaving} className="btn btn-primary" style={{ padding:"9px 0", fontSize:13 }}>{pwSaving?"Saving…":"Update password"}</button>
          </div>
        </div>
      </div>
    </div>
  );
};

const ContentManagerSidebar = ({ active, go, session }) => (
  <div style={{ width:220, background:"var(--paper-card)", borderRight:"var(--border-thin)", display:"flex", flexDirection:"column", height:"100%" }}>
    <div style={{ padding:"18px 16px 12px", borderBottom:"var(--border-thin)" }}>
      <div className="display" style={{ fontSize:16 }}>{session?.name || session?.email || "Content Manager"}</div>
      <div style={{ fontSize:11, color:"var(--ink-mute)", marginTop:2 }}>Content Manager</div>
    </div>
    <div style={{ flex:1, padding:10 }}>
      {CM_NAV.map(([label, Icon, path]) => {
        const on = active === path;
        return (
          <button key={path} onClick={() => go(path)} style={{
            display:"flex", alignItems:"center", gap:10, width:"100%", padding:"9px 12px",
            borderRadius:8, border:"none", marginBottom:2, cursor:"pointer", textAlign:"left",
            background: on ? "var(--coral)" : "transparent",
            color: on ? "white" : "var(--ink-soft)", fontWeight: on ? 700 : 400, fontSize:13,
          }}>
            {typeof Icon === "function" ? Icon({ size:16 }) : null} {label}
          </button>
        );
      })}
    </div>
  </div>
);

// Lesson kinds a module can hold. "text" is written straight into the LMS with
// the rich text editor — no file upload involved.
const LESSON_TYPES = ["video","file","text","quiz","homework","project"];
const LESSON_TYPE_COLOR = { video:"var(--coral)", file:"var(--sky)", text:"var(--moss)", quiz:"var(--sky)", homework:"var(--gold)", project:"var(--plum)" };

const ALLOWED_TYPES = ["pdf","ppt","pptx","txt","doc","docx","png","jpg","jpeg","webp","gif"];
const MIME_ICONS = { pdf:"📄", ppt:"📊", pptx:"📊", txt:"📝", doc:"📝", docx:"📝", png:"🖼️", jpg:"🖼️", jpeg:"🖼️", webp:"🖼️", gif:"🖼️" };

// ── Media Library (Finder-style grid with image previews) ───────────────────────
const MM_IMG_TYPES = ["png","jpg","jpeg","webp","gif"];
const isImageType = (t) => MM_IMG_TYPES.includes(String(t||"").toLowerCase());
const fmtBytes = (b) => b > 1048576 ? `${(b/1048576).toFixed(1)} MB` : `${(b/1024).toFixed(0)} KB`;

// Lazily fetches and renders an image thumbnail; non-images show a type icon.
const MediaThumb = ({ f }) => {
  const [url, setUrl] = React.useState(null);
  React.useEffect(() => {
    let dead = false, made = null;
    if (isImageType(f.file_type) && f.course_id != null) {
      apiFetch(`/library-assets/${f.course_id}/${encodeURIComponent(f.id)}/data`)
        .then(r => r.ok ? r.json() : null)
        .then(d => {
          if (!d || dead) return;
          const bin = atob(d.file_data || ""), bytes = new Uint8Array(bin.length);
          for (let i=0;i<bin.length;i++) bytes[i] = bin.charCodeAt(i);
          made = URL.createObjectURL(new Blob([bytes], { type: f.mime_type || "image/*" }));
          setUrl(made);
        }).catch(()=>{});
    }
    return () => { dead = true; if (made) URL.revokeObjectURL(made); };
  }, [f.id, f.course_id, f.file_type]);
  return (
    <div style={{ width:"100%", height:120, borderRadius:10, overflow:"hidden", background:"var(--paper-deep)", display:"grid", placeItems:"center" }}>
      {isImageType(f.file_type)
        ? (url ? <img src={url} alt={f.title} style={{ width:"100%", height:"100%", objectFit:"cover" }}/> : <span style={{ fontSize:28, opacity:.4 }}>🖼️</span>)
        : <span style={{ fontSize:44 }}>{MIME_ICONS[f.file_type] || "📄"}</span>}
    </div>
  );
};

const MediaLibrary = ({ session, go }) => {
  const route = window.location.hash.slice(1);
  const { data: initFiles, loading } = useApi("/library-assets");
  const { data: allCourses } = useApi("/courses/list");
  const [filesLocal, setFilesLocal] = React.useState(null);
  const files = filesLocal ?? initFiles;
  const refresh = () => apiFetch("/library-assets").then(r=>r.json()).then(setFilesLocal).catch(()=>{});

  const [query, setQuery] = React.useState("");
  const [kind, setKind] = React.useState("all");   // all | images | docs
  const [courseF, setCourseF] = React.useState(""); // "" = all courses
  const [sort, setSort] = React.useState("new");    // new | name | size

  // Distinct courses present, for the course filter.
  const courses = [];
  (files || []).forEach(f => { if (f.course_id != null && !courses.some(c=>c.id===f.course_id)) courses.push({ id:f.course_id, name:f.course_name||`Course ${f.course_id}` }); });

  let list = (files || []).filter(f => {
    if (kind === "images" && !isImageType(f.file_type)) return false;
    if (kind === "docs" && isImageType(f.file_type)) return false;
    if (courseF && String(f.course_id) !== String(courseF)) return false;
    if (!query) return true;
    const s = query.toLowerCase();
    return (f.title||"").toLowerCase().includes(s) || (f.public_uid||"").toLowerCase().includes(s) || (f.course_name||"").toLowerCase().includes(s);
  });
  list = [...list].sort((a,b) =>
    sort === "name" ? String(a.title||"").localeCompare(String(b.title||""))
    : sort === "size" ? (Number(b.size_bytes)||0) - (Number(a.size_bytes)||0)
    : new Date(b.created_at) - new Date(a.created_at));

  const openFile = async (f) => {
    if (f.course_id == null) return;
    const r = await apiFetch(`/library-assets/${f.course_id}/${encodeURIComponent(f.id)}/data`);
    if (!r.ok) return;
    const d = await r.json();
    const bin = atob(d.file_data || ""), bytes = new Uint8Array(bin.length);
    for (let i=0;i<bin.length;i++) bytes[i] = bin.charCodeAt(i);
    const u = URL.createObjectURL(new Blob([bytes], { type: f.mime_type || fileMimeType(f.file_type || d.file_type) }));
    window.open(u, "_blank"); setTimeout(()=>URL.revokeObjectURL(u), 60000);
  };

  const renameFile = async (f) => {
    const t = prompt("Rename file", f.title);
    if (t == null || !t.trim() || t.trim() === f.title) return;
    const r = await apiFetch(`/library-assets/${f.course_id}/${encodeURIComponent(f.id)}`, { method:"PATCH", body: JSON.stringify({ title: t.trim() }) });
    if (!r.ok) { const d=await r.json().catch(()=>({})); alert(d.error||"Could not rename"); return; }
    refresh();
  };

  // Assign a file to a module as a file-type lesson. The file can go to any
  // course's unit, not just the course it was uploaded under (the lesson just
  // references the underlying file id).
  const [assignFile, setAssignFile] = React.useState(null);
  const [assignUnits, setAssignUnits] = React.useState([]);
  const [assignForm, setAssignForm] = React.useState({ course_id:"", unit_id:"", title:"" });
  const [assignSaving, setAssignSaving] = React.useState(false);
  const [assignMsg, setAssignMsg] = React.useState(null);
  const loadUnitsFor = async (courseId) => {
    if (!courseId) { setAssignUnits([]); return; }
    const units = await apiFetch(`/content/${courseId}`).then(r=>r.ok?r.json():[]).catch(()=>[]);
    setAssignUnits(Array.isArray(units) ? units : []);
  };
  const openAssign = async (f) => {
    setAssignFile(f); setAssignMsg(null);
    setAssignForm({ course_id: f.course_id != null ? String(f.course_id) : "", unit_id:"", title:f.title });
    await loadUnitsFor(f.course_id);
  };
  const onAssignCourse = async (cid) => {
    setAssignForm(f => ({ ...f, course_id: cid, unit_id:"" }));
    setAssignMsg(null);
    await loadUnitsFor(cid);
  };
  const doAssign = async () => {
    if (!assignForm.course_id) { setAssignMsg({ type:"err", text:"Pick a course" }); return; }
    if (!assignForm.unit_id) { setAssignMsg({ type:"err", text:"Pick a module" }); return; }
    if (!assignForm.title.trim()) { setAssignMsg({ type:"err", text:"Lesson title required" }); return; }
    setAssignSaving(true);
    const r = await apiFetch(`/units/${assignForm.unit_id}/lessons`, { method:"POST", body: JSON.stringify({ title: assignForm.title.trim(), type:"file", file_id: assignFile.course_file_id }) });
    setAssignSaving(false);
    if (!r.ok) { const d=await r.json().catch(()=>({})); setAssignMsg({ type:"err", text:d.error||"Failed to assign" }); return; }
    setAssignMsg({ type:"ok", text:"Added as a lesson ✓" });
    setTimeout(()=>setAssignFile(null), 900);
  };

  // Upload straight from the library — pick a course to store the file under,
  // then it can be assigned to any course's unit above.
  const [showUpload, setShowUpload] = React.useState(false);
  const [upCourse, setUpCourse] = React.useState("");
  const [uploading, setUploading] = React.useState(false);
  const [uploadMsg, setUploadMsg] = React.useState(null);
  const upInputRef = React.useRef();
  const openUpload = () => { setUpCourse(""); setUploadMsg(null); setShowUpload(true); };
  const handleUpload = async (rawFiles) => {
    if (!upCourse) { setUploadMsg({ type:"err", text:"Pick a course first" }); return; }
    if (!rawFiles || !rawFiles.length) return;
    setUploadMsg(null); setUploading(true);
    let okCount = 0;
    for (const file of Array.from(rawFiles)) {
      const ext = file.name.split(".").pop().toLowerCase();
      if (!ALLOWED_TYPES.includes(ext)) { setUploadMsg({ type:"err", text:`${file.name}: unsupported type. Allowed: ${ALLOWED_TYPES.join(", ")}` }); continue; }
      if (file.size > 35 * 1024 * 1024) { setUploadMsg({ type:"err", text:`${file.name}: ${(file.size/1048576).toFixed(1)} MB — max is 35 MB` }); continue; }
      const base64 = await new Promise(resolve => {
        const reader = new FileReader();
        reader.onload = e => resolve(String(e.target.result).split(",")[1]);
        reader.onerror = () => resolve(null);
        reader.readAsDataURL(file);
      });
      if (base64 == null) { setUploadMsg({ type:"err", text:`${file.name}: could not read file` }); continue; }
      const res = await apiFetch("/library-assets", { method:"POST", body: JSON.stringify({ course_id: parseInt(upCourse), title: file.name, file_type: ext, file_data: base64 }) });
      if (!res.ok) { const d = await res.json().catch(()=>({})); setUploadMsg({ type:"err", text:`${file.name}: ${d.error || (res.status===413 ? "too large for the server" : "upload failed ("+res.status+")")}` }); }
      else okCount++;
    }
    setUploading(false);
    if (upInputRef.current) upInputRef.current.value = "";
    if (okCount) { setUploadMsg({ type:"ok", text:`Uploaded ${okCount} file${okCount===1?"":"s"} ✓` }); refresh(); }
  };

  return (
    <div style={{ width:"100%", height:"100%", display:"flex", fontFamily:"var(--font-ui)", background:"var(--paper)" }}>
      {assignFile && (
        <Modal title={`Add "${assignFile.title}" to a module`} onClose={()=>setAssignFile(null)}>
          <div style={{ display:"flex", flexDirection:"column", gap:10 }}>
            <select value={assignForm.course_id} onChange={e=>onAssignCourse(e.target.value)}
              style={{ width:"100%", padding:"9px 12px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:13, color:"var(--ink)", fontFamily:"var(--font-ui)" }}>
              <option value="">— select course —</option>
              {(allCourses||[]).map(c=><option key={c.id} value={c.id}>{c.name}{c.level?` · ${c.level}`:""}</option>)}
            </select>
            <select value={assignForm.unit_id} onChange={e=>setAssignForm(f=>({...f,unit_id:e.target.value}))}
              style={{ width:"100%", padding:"9px 12px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:13, color:"var(--ink)", fontFamily:"var(--font-ui)" }}>
              <option value="">— select module —</option>
              {assignUnits.map(u=><option key={u.id} value={u.id}>{u.unit}</option>)}
            </select>
            <input value={assignForm.title} onChange={e=>setAssignForm(f=>({...f,title:e.target.value}))} placeholder="Lesson title"
              style={{ width:"100%", padding:"9px 12px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:13, color:"var(--ink)", fontFamily:"var(--font-ui)", boxSizing:"border-box", outline:"none" }}/>
            {assignForm.course_id && assignUnits.length===0 && <div style={{ fontSize:11, color:"var(--coral)" }}>This course has no modules yet — add one under Courses → Units & Lessons.</div>}
            {assignMsg && <div style={{ fontSize:12, color: assignMsg.type==="ok"?"var(--moss)":"var(--coral)" }}>{assignMsg.text}</div>}
            <div style={{ display:"flex", justifyContent:"flex-end", gap:8 }}>
              <button onClick={()=>setAssignFile(null)} className="btn btn-ghost">Cancel</button>
              <button onClick={doAssign} disabled={assignSaving} className="btn btn-primary">{assignSaving?"Adding…":"Add as lesson"}</button>
            </div>
          </div>
        </Modal>
      )}
      {showUpload && (
        <Modal title="Upload to library" onClose={()=>setShowUpload(false)}>
          <div style={{ display:"flex", flexDirection:"column", gap:10 }}>
            <div style={{ fontSize:12, color:"var(--ink-mute)" }}>Choose the course to store the file under. You can assign it to any course's module afterward.</div>
            <select value={upCourse} onChange={e=>{ setUpCourse(e.target.value); setUploadMsg(null); }}
              style={{ width:"100%", padding:"9px 12px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:13, color:"var(--ink)", fontFamily:"var(--font-ui)" }}>
              <option value="">— select course —</option>
              {(allCourses||[]).map(c=><option key={c.id} value={c.id}>{c.name}{c.level?` · ${c.level}`:""}</option>)}
            </select>
            <input ref={upInputRef} type="file" multiple disabled={!upCourse || uploading}
              accept={ALLOWED_TYPES.map(t=>"."+t).join(",")}
              onChange={e=>handleUpload(e.target.files)}
              style={{ fontSize:12.5, color:"var(--ink)" }}/>
            <div style={{ fontSize:11, color:"var(--ink-mute)" }}>Allowed: {ALLOWED_TYPES.join(", ")} · max 35 MB each</div>
            {uploading && <div style={{ fontSize:12, color:"var(--ink-mute)" }}>Uploading…</div>}
            {uploadMsg && <div style={{ fontSize:12, color: uploadMsg.type==="ok"?"var(--moss)":"var(--coral)" }}>{uploadMsg.text}</div>}
            <div style={{ display:"flex", justifyContent:"flex-end", gap:8 }}>
              <button onClick={()=>setShowUpload(false)} className="btn btn-ghost">Done</button>
            </div>
          </div>
        </Modal>
      )}
      <ContentManagerSidebar active={route} go={go} session={session}/>
      <div style={{ flex:1, display:"flex", flexDirection:"column", minWidth:0 }}>
        <div style={{ padding:"14px 22px", borderBottom:"var(--border-thin)", background:"var(--paper-card)", display:"flex", alignItems:"center", gap:10, flexWrap:"wrap" }}>
          <div>
            <div className="display" style={{ fontSize:20 }}>Media Library</div>
            <div style={{ fontSize:12, color:"var(--ink-mute)" }}>{loading ? "Loading…" : `${list.length} file${list.length===1?"":"s"}`}</div>
          </div>
          <div style={{ flex:1 }}/>
          <button onClick={openUpload} className="btn btn-primary" style={{ padding:"8px 14px", fontSize:13 }}>Upload</button>
          <input value={query} onChange={e=>setQuery(e.target.value)} placeholder="Search…"
            style={{ width:200, padding:"8px 12px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:13, color:"var(--ink)", fontFamily:"var(--font-ui)", outline:"none" }}/>
          <select value={kind} onChange={e=>setKind(e.target.value)} style={{ padding:"8px 10px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:12.5, color:"var(--ink)", fontFamily:"var(--font-ui)" }}>
            <option value="all">All types</option>
            <option value="images">Images</option>
            <option value="docs">Documents</option>
          </select>
          <select value={courseF} onChange={e=>setCourseF(e.target.value)} style={{ maxWidth:180, padding:"8px 10px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:12.5, color:"var(--ink)", fontFamily:"var(--font-ui)" }}>
            <option value="">All courses</option>
            {courses.map(c=><option key={c.id} value={c.id}>{c.name}</option>)}
          </select>
          <select value={sort} onChange={e=>setSort(e.target.value)} style={{ padding:"8px 10px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:12.5, color:"var(--ink)", fontFamily:"var(--font-ui)" }}>
            <option value="new">Newest</option>
            <option value="name">Name A–Z</option>
            <option value="size">Largest</option>
          </select>
        </div>
        <div style={{ flex:1, overflow:"auto", padding:22 }}>
          {loading ? <Spinner/> : list.length === 0 ? (
            <div style={{ textAlign:"center", padding:40, color:"var(--ink-mute)", fontSize:13 }}>No files yet — upload some under Courses.</div>
          ) : (
            <div style={{ display:"grid", gridTemplateColumns:"repeat(auto-fill, minmax(160px, 1fr))", gap:16 }}>
              {list.map(f => (
                <div key={f.id} onClick={()=>openFile(f)} title={`Open ${f.title}`} className="card-flat" style={{ padding:10, cursor:"pointer", display:"flex", flexDirection:"column", gap:8 }}>
                  <MediaThumb f={f}/>
                  <div style={{ minWidth:0 }}>
                    <div style={{ fontWeight:700, fontSize:12.5, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{f.title}</div>
                    <div style={{ fontSize:10, color:"var(--ink-mute)", overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{f.course_name || "—"}</div>
                    <div style={{ display:"flex", alignItems:"center", gap:6, marginTop:3 }}>
                      <span style={{ padding:"1px 6px", borderRadius:999, background:"var(--paper-deep)", fontWeight:700, textTransform:"uppercase", fontSize:9 }}>{f.file_type}</span>
                      {f.public_uid && <span className="mono" title="Copy UID" onClick={(e)=>{ e.stopPropagation(); navigator.clipboard?.writeText(f.public_uid); }} style={{ fontSize:9, color:"var(--ink-mute)", cursor:"pointer" }}>{f.public_uid}</span>}
                    </div>
                    <div style={{ fontSize:9, color:"var(--ink-mute)", marginTop:2 }}>{fmtBytes(f.size_bytes)}</div>
                    <div style={{ display:"flex", gap:6, marginTop:6 }}>
                      <button onClick={(e)=>{ e.stopPropagation(); renameFile(f); }} className="btn btn-ghost" style={{ padding:"3px 8px", fontSize:10 }}>Rename</button>
                      <button onClick={(e)=>{ e.stopPropagation(); openAssign(f); }} className="btn btn-ghost" style={{ padding:"3px 8px", fontSize:10 }}>Assign</button>
                    </div>
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

// ── Text Library ──────────────────────────────────────────────────────────────
// The text counterpart of the Media Library: write a document once, then assign
// it into any course's module. Assigned lessons reference the document rather
// than copying it, so editing here updates every module using it.
//
// Master-detail rather than a modal — the shared Modal is a fixed 420px, which
// is too cramped to actually write in.
const TextLibrary = ({ session, go }) => {
  const route = window.location.hash.slice(1);
  const { data: initDocs, loading } = useApi("/text-docs");
  const { data: allCourses } = useApi("/courses/list");
  const [docsLocal, setDocsLocal] = React.useState(null);
  const docs = docsLocal ?? initDocs;
  const refresh = () => apiFetch("/text-docs").then(r => r.json()).then(setDocsLocal).catch(() => {});

  const [query, setQuery] = React.useState("");
  const [courseF, setCourseF] = React.useState("");
  const [sort, setSort] = React.useState("new");
  const [flash, setFlash] = React.useState(null);
  const say = (text, type = "ok") => { setFlash({ text, type }); setTimeout(() => setFlash(null), 2600); };

  // Courses present among the documents, for the filter.
  const courses = [];
  (docs || []).forEach(d => { if (d.course_id != null && !courses.some(c => c.id === d.course_id)) courses.push({ id: d.course_id, name: d.course_name || `Course ${d.course_id}` }); });

  let list = (docs || []).filter(d => {
    if (courseF && String(d.course_id) !== String(courseF)) return false;
    if (!query) return true;
    const s = query.toLowerCase();
    return (d.title || "").toLowerCase().includes(s) || (d.excerpt || "").toLowerCase().includes(s) || (d.course_name || "").toLowerCase().includes(s);
  });
  list = [...list].sort((a, b) =>
    sort === "name" ? String(a.title || "").localeCompare(String(b.title || ""))
    : sort === "used" ? (Number(b.usage_count) || 0) - (Number(a.usage_count) || 0)
    : new Date(b.updated_at) - new Date(a.updated_at));

  // ── Editor (new / edit) ─────────────────────────────────────────────────────
  const [editor, setEditor] = React.useState(null);   // {mode, id, key}
  const [form, setForm] = React.useState({ title: "", course_id: "", body: "" });
  const [saving, setSaving] = React.useState(false);
  const [editMsg, setEditMsg] = React.useState(null);

  const openNew = () => {
    setForm({ title: "", course_id: "", body: "" });
    setEditMsg(null);
    setEditor({ mode: "new", key: `new-${Date.now()}` });
  };
  const openEdit = async (d) => {
    setEditMsg(null);
    setEditor({ mode: "edit", id: d.id, key: `doc-${d.id}`, loading: true });
    const r = await apiFetch(`/text-docs/${d.id}`);
    if (!r.ok) { setEditor(null); say("Could not open that document.", "err"); return; }
    const full = await r.json();
    setForm({ title: full.title || "", course_id: full.course_id != null ? String(full.course_id) : "", body: full.body || "" });
    setEditor({ mode: "edit", id: d.id, key: `doc-${d.id}`, usedBy: full.used_by || [] });
  };
  const saveDoc = async () => {
    if (!form.title.trim()) { setEditMsg({ type: "err", text: "Give the document a title" }); return; }
    if (!stripHtml(form.body)) { setEditMsg({ type: "err", text: "Add some content" }); return; }
    setSaving(true);
    const payload = { title: form.title.trim(), body: form.body, course_id: form.course_id ? parseInt(form.course_id) : null };
    const r = editor.mode === "new"
      ? await apiFetch("/text-docs", { method: "POST", body: JSON.stringify(payload) })
      : await apiFetch(`/text-docs/${editor.id}`, { method: "PATCH", body: JSON.stringify(payload) });
    setSaving(false);
    if (!r.ok) { const d = await r.json().catch(() => ({})); setEditMsg({ type: "err", text: d.error || "Could not save" }); return; }
    const usedCount = editor.mode === "edit" ? (editor.usedBy || []).length : 0;
    setEditor(null);
    refresh();
    say(usedCount
      ? `Saved ✓ — updated in ${usedCount} lesson${usedCount === 1 ? "" : "s"}`
      : editor.mode === "new" ? "Document created ✓" : "Saved ✓");
  };

  // ── Assign to a module ──────────────────────────────────────────────────────
  const [assignDoc, setAssignDoc] = React.useState(null);
  const [assignUnits, setAssignUnits] = React.useState([]);
  const [assignForm, setAssignForm] = React.useState({ course_id: "", unit_id: "", title: "" });
  const [assignSaving, setAssignSaving] = React.useState(false);
  const [assignMsg, setAssignMsg] = React.useState(null);
  const loadUnitsFor = async (courseId) => {
    if (!courseId) { setAssignUnits([]); return; }
    const units = await apiFetch(`/content/${courseId}`).then(r => r.ok ? r.json() : []).catch(() => []);
    setAssignUnits(Array.isArray(units) ? units : []);
  };
  const openAssign = async (d) => {
    setAssignDoc(d); setAssignMsg(null);
    setAssignForm({ course_id: d.course_id != null ? String(d.course_id) : "", unit_id: "", title: d.title });
    await loadUnitsFor(d.course_id);
  };
  const onAssignCourse = async (cid) => {
    setAssignForm(f => ({ ...f, course_id: cid, unit_id: "" }));
    setAssignMsg(null);
    await loadUnitsFor(cid);
  };
  const doAssign = async () => {
    if (!assignForm.course_id) { setAssignMsg({ type: "err", text: "Pick a course" }); return; }
    if (!assignForm.unit_id) { setAssignMsg({ type: "err", text: "Pick a module" }); return; }
    if (!assignForm.title.trim()) { setAssignMsg({ type: "err", text: "Lesson title required" }); return; }
    setAssignSaving(true);
    const r = await apiFetch(`/units/${assignForm.unit_id}/lessons`, {
      method: "POST",
      body: JSON.stringify({ title: assignForm.title.trim(), type: "text", text_doc_id: assignDoc.id }),
    });
    setAssignSaving(false);
    if (!r.ok) { const d = await r.json().catch(() => ({})); setAssignMsg({ type: "err", text: d.error || "Failed to assign" }); return; }
    setAssignMsg({ type: "ok", text: "Added as a lesson ✓ (starts as a draft)" });
    refresh();
    setTimeout(() => setAssignDoc(null), 1100);
  };

  // ── Delete ──────────────────────────────────────────────────────────────────
  // The API refuses to delete a document that's still assigned, so on a 409 we
  // re-read the document to show exactly which lessons are holding it.
  const [blocked, setBlocked] = React.useState(null);
  const deleteDoc = async (d) => {
    if (!confirm(`Delete "${d.title}"?\n\nThis cannot be undone.`)) return;
    const r = await apiFetch(`/text-docs/${d.id}`, { method: "DELETE" });
    if (r.ok) { refresh(); say("Document deleted ✓"); return; }
    const err = await r.json().catch(() => ({}));
    if (r.status === 409) {
      const full = await apiFetch(`/text-docs/${d.id}`).then(x => x.ok ? x.json() : null).catch(() => null);
      setBlocked({ doc: d, message: err.error, usedBy: (full && full.used_by) || [] });
      return;
    }
    say(err.error || "Could not delete", "err");
  };

  const selStyle = { padding: "8px 10px", border: "var(--border-thin)", borderRadius: 8, background: "var(--paper-deep)", fontSize: 12.5, color: "var(--ink)", fontFamily: "var(--font-ui)" };
  const inputStyle = { width: "100%", padding: "9px 12px", border: "var(--border-thin)", borderRadius: 8, background: "var(--paper-deep)", fontSize: 13, color: "var(--ink)", fontFamily: "var(--font-ui)", boxSizing: "border-box", outline: "none" };

  // ── Editor view ─────────────────────────────────────────────────────────────
  if (editor) {
    const usedBy = editor.usedBy || [];
    return (
      <div style={{ width: "100%", height: "100%", display: "flex", fontFamily: "var(--font-ui)", background: "var(--paper)" }}>
        <ContentManagerSidebar active={route} go={go} session={session}/>
        <div style={{ flex: 1, display: "flex", flexDirection: "column", minWidth: 0 }}>
          <div style={{ padding: "14px 22px", borderBottom: "var(--border-thin)", background: "var(--paper-card)", display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
            <button onClick={() => setEditor(null)} className="btn btn-ghost" style={{ padding: "7px 12px", fontSize: 12.5 }}>← Back</button>
            <div>
              <div className="display" style={{ fontSize: 20 }}>{editor.mode === "new" ? "New document" : "Edit document"}</div>
              <div style={{ fontSize: 12, color: "var(--ink-mute)" }}>
                {usedBy.length
                  ? `Used by ${usedBy.length} lesson${usedBy.length === 1 ? "" : "s"} — saving updates all of them`
                  : "Not assigned to any module yet"}
              </div>
            </div>
            <div style={{ flex: 1 }}/>
            {editMsg && <div style={{ fontSize: 12, fontWeight: 600, color: editMsg.type === "ok" ? "var(--moss)" : "var(--coral)" }}>{editMsg.text}</div>}
            <button onClick={saveDoc} disabled={saving} className="btn btn-primary" style={{ padding: "8px 16px", fontSize: 13 }}>{saving ? "Saving…" : "Save"}</button>
          </div>
          <div style={{ flex: 1, overflow: "auto", padding: 22 }}>
            {editor.loading ? <Spinner/> : (
              <div style={{ maxWidth: 900, margin: "0 auto", display: "flex", flexDirection: "column", gap: 12 }}>
                <input value={form.title} onChange={e => setForm(f => ({ ...f, title: e.target.value }))}
                  placeholder="Document title" style={{ ...inputStyle, fontSize: 16, fontWeight: 700, padding: "12px 14px" }}/>
                <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
                  <span className="mono" style={{ fontSize: 10, fontWeight: 700, letterSpacing: ".06em", color: "var(--ink-mute)" }}>FILE UNDER</span>
                  <select value={form.course_id} onChange={e => setForm(f => ({ ...f, course_id: e.target.value }))} style={{ ...selStyle, maxWidth: 260 }}>
                    <option value="">— unfiled —</option>
                    {(allCourses || []).map(c => <option key={c.id} value={c.id}>{c.name}{c.level ? ` · ${c.level}` : ""}</option>)}
                  </select>
                  <span style={{ fontSize: 11, color: "var(--ink-mute)" }}>Organisation only — you can assign it to any course's module.</span>
                </div>
                <RichTextEditor docKey={editor.key} value={form.body} minHeight={380}
                  placeholder="Write the lesson…" onChange={html => setForm(f => ({ ...f, body: html }))}/>
                {usedBy.length > 0 && (
                  <div className="card-flat" style={{ padding: 14 }}>
                    <div className="mono" style={{ fontSize: 10, fontWeight: 700, letterSpacing: ".06em", color: "var(--ink-mute)", marginBottom: 8 }}>USED BY</div>
                    <div style={{ display: "flex", flexDirection: "column", gap: 6 }}>
                      {usedBy.map(u => (
                        <div key={u.id} style={{ fontSize: 12.5, display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap" }}>
                          <span style={{ fontWeight: 600 }}>{u.title}</span>
                          <span style={{ color: "var(--ink-mute)" }}>{u.course_name || "—"} · {u.unit}</span>
                          <span style={{ padding: "1px 7px", borderRadius: 999, fontSize: 9, fontWeight: 700, textTransform: "uppercase", background: "var(--paper-deep)", color: "var(--ink-mute)" }}>{u.status}</span>
                        </div>
                      ))}
                    </div>
                  </div>
                )}
              </div>
            )}
          </div>
        </div>
      </div>
    );
  }

  // ── List view ───────────────────────────────────────────────────────────────
  return (
    <div style={{ width: "100%", height: "100%", display: "flex", fontFamily: "var(--font-ui)", background: "var(--paper)" }}>
      {assignDoc && (
        <Modal title={`Add "${assignDoc.title}" to a module`} onClose={() => setAssignDoc(null)}>
          <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            <div style={{ fontSize: 12, color: "var(--ink-mute)" }}>The lesson stays linked to this document, so future edits reach it automatically.</div>
            <select value={assignForm.course_id} onChange={e => onAssignCourse(e.target.value)} style={{ ...selStyle, width: "100%", padding: "9px 12px", fontSize: 13 }}>
              <option value="">— select course —</option>
              {(allCourses || []).map(c => <option key={c.id} value={c.id}>{c.name}{c.level ? ` · ${c.level}` : ""}</option>)}
            </select>
            <select value={assignForm.unit_id} onChange={e => setAssignForm(f => ({ ...f, unit_id: e.target.value }))} style={{ ...selStyle, width: "100%", padding: "9px 12px", fontSize: 13 }}>
              <option value="">— select module —</option>
              {assignUnits.map(u => <option key={u.id} value={u.id}>{u.unit}</option>)}
            </select>
            <input value={assignForm.title} onChange={e => setAssignForm(f => ({ ...f, title: e.target.value }))} placeholder="Lesson title" style={inputStyle}/>
            {assignForm.course_id && assignUnits.length === 0 && <div style={{ fontSize: 11, color: "var(--coral)" }}>This course has no modules yet — add one under Courses → Units &amp; Lessons.</div>}
            {assignMsg && <div style={{ fontSize: 12, color: assignMsg.type === "ok" ? "var(--moss)" : "var(--coral)" }}>{assignMsg.text}</div>}
            <div style={{ display: "flex", justifyContent: "flex-end", gap: 8 }}>
              <button onClick={() => setAssignDoc(null)} className="btn btn-ghost">Cancel</button>
              <button onClick={doAssign} disabled={assignSaving} className="btn btn-primary">{assignSaving ? "Adding…" : "Add as lesson"}</button>
            </div>
          </div>
        </Modal>
      )}
      {blocked && (
        <Modal title="Still in use" onClose={() => setBlocked(null)}>
          <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>
            <div style={{ fontSize: 13, color: "var(--ink)" }}>{blocked.message}</div>
            <div style={{ display: "flex", flexDirection: "column", gap: 6, maxHeight: "40vh", overflow: "auto" }}>
              {blocked.usedBy.map(u => (
                <div key={u.id} className="card-flat" style={{ padding: "8px 10px", fontSize: 12.5 }}>
                  <div style={{ fontWeight: 600 }}>{u.title}</div>
                  <div style={{ color: "var(--ink-mute)", fontSize: 11 }}>{u.course_name || "—"} · {u.unit}</div>
                </div>
              ))}
            </div>
            <div style={{ fontSize: 11.5, color: "var(--ink-mute)" }}>Delete those lessons under Courses → Units &amp; Lessons first, then remove the document.</div>
            <div style={{ display: "flex", justifyContent: "flex-end" }}>
              <button onClick={() => setBlocked(null)} className="btn btn-ghost">Close</button>
            </div>
          </div>
        </Modal>
      )}
      <ContentManagerSidebar active={route} go={go} session={session}/>
      <div style={{ flex: 1, display: "flex", flexDirection: "column", minWidth: 0 }}>
        <div style={{ padding: "14px 22px", borderBottom: "var(--border-thin)", background: "var(--paper-card)", display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
          <div>
            <div className="display" style={{ fontSize: 20 }}>Text Library</div>
            <div style={{ fontSize: 12, color: "var(--ink-mute)" }}>{loading ? "Loading…" : `${list.length} document${list.length === 1 ? "" : "s"}`}</div>
          </div>
          <div style={{ flex: 1 }}/>
          {flash && <div style={{ fontSize: 12, fontWeight: 600, color: flash.type === "ok" ? "var(--moss)" : "var(--coral)" }}>{flash.text}</div>}
          <button onClick={openNew} className="btn btn-primary" style={{ padding: "8px 14px", fontSize: 13 }}>+ New document</button>
          <input value={query} onChange={e => setQuery(e.target.value)} placeholder="Search…"
            style={{ width: 200, padding: "8px 12px", border: "var(--border-thin)", borderRadius: 8, background: "var(--paper-deep)", fontSize: 13, color: "var(--ink)", fontFamily: "var(--font-ui)", outline: "none" }}/>
          <select value={courseF} onChange={e => setCourseF(e.target.value)} style={{ ...selStyle, maxWidth: 180 }}>
            <option value="">All courses</option>
            {courses.map(c => <option key={c.id} value={c.id}>{c.name}</option>)}
          </select>
          <select value={sort} onChange={e => setSort(e.target.value)} style={selStyle}>
            <option value="new">Recently edited</option>
            <option value="name">Name A–Z</option>
            <option value="used">Most used</option>
          </select>
        </div>
        <div style={{ flex: 1, overflow: "auto", padding: 22 }}>
          {loading ? <Spinner/> : list.length === 0 ? (
            <div style={{ textAlign: "center", padding: 40, color: "var(--ink-mute)", fontSize: 13 }}>
              {(docs || []).length === 0
                ? "No documents yet — write one with “+ New document”, then assign it to any course's module."
                : "No documents match that search."}
            </div>
          ) : (
            <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(300px, 1fr))", gap: 16 }}>
              {list.map(d => (
                <div key={d.id} onClick={() => openEdit(d)} title={`Edit ${d.title}`} className="card-flat" style={{ padding: 14, cursor: "pointer", display: "flex", flexDirection: "column", gap: 8, minWidth: 0 }}>
                  <div style={{ display: "flex", alignItems: "flex-start", gap: 8 }}>
                    <span style={{ fontSize: 18, lineHeight: 1.2 }}>📝</span>
                    <div style={{ minWidth: 0, flex: 1 }}>
                      <div style={{ fontWeight: 700, fontSize: 13.5, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{d.title}</div>
                      <div style={{ fontSize: 10.5, color: "var(--ink-mute)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{d.course_name || "Unfiled"}</div>
                    </div>
                  </div>
                  <div style={{ fontSize: 11.5, color: "var(--ink-mute)", lineHeight: 1.5, display: "-webkit-box", WebkitLineClamp: 3, WebkitBoxOrient: "vertical", overflow: "hidden" }}>
                    {d.excerpt || "—"}
                  </div>
                  <div style={{ display: "flex", alignItems: "center", gap: 6, flexWrap: "wrap" }}>
                    <span style={{ padding: "1px 7px", borderRadius: 999, fontSize: 9, fontWeight: 700, textTransform: "uppercase", background: Number(d.usage_count) ? "var(--moss)" : "var(--paper-deep)", color: Number(d.usage_count) ? "white" : "var(--ink-mute)" }}>
                      {Number(d.usage_count) ? `in ${d.usage_count} lesson${Number(d.usage_count) === 1 ? "" : "s"}` : "unused"}
                    </span>
                    <span style={{ fontSize: 9.5, color: "var(--ink-mute)" }}>{d.updated_at ? new Date(d.updated_at).toLocaleDateString() : ""}</span>
                  </div>
                  <div style={{ display: "flex", gap: 6, marginTop: 2 }}>
                    <button onClick={e => { e.stopPropagation(); openEdit(d); }} className="btn btn-ghost" style={{ padding: "3px 8px", fontSize: 10 }}>Edit</button>
                    <button onClick={e => { e.stopPropagation(); openAssign(d); }} className="btn btn-ghost" style={{ padding: "3px 8px", fontSize: 10 }}>Assign</button>
                    <button onClick={e => { e.stopPropagation(); deleteDoc(d); }} className="btn btn-ghost" style={{ padding: "3px 8px", fontSize: 10, color: "var(--coral)" }}>Delete</button>
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>
      </div>
    </div>
  );
};

// Renders a quiz question's media-library image (fetched via the question id so
// learners can see it without library permissions). Shared with the student player.
const QuizQImage = ({ qid, style }) => {
  const [url, setUrl] = React.useState(null);
  React.useEffect(() => {
    let dead = false, made = null;
    apiFetch(`/quiz-questions/${qid}/image`).then(r => r.ok ? r.json() : null).then(d => {
      if (!d || dead) return;
      const bin = atob(d.file_data || ""), by = new Uint8Array(bin.length);
      for (let i=0;i<bin.length;i++) by[i] = bin.charCodeAt(i);
      made = URL.createObjectURL(new Blob([by], { type: "image/*" }));
      setUrl(made);
    }).catch(()=>{});
    return () => { dead = true; if (made) URL.revokeObjectURL(made); };
  }, [qid]);
  return url ? <img src={url} alt="" style={style || { maxWidth:"100%", maxHeight:220, borderRadius:8, display:"block" }}/> : null;
};
window.QuizQImage = QuizQImage;

// ── Quizzes (course-scoped authoring for content managers) ──────────────────────
const CMQuizzes = ({ session, go }) => {
  const route = window.location.hash.slice(1);
  const { data: courses } = useApi("/courses/list");
  const courseList = courses || [];
  const [courseId, setCourseId] = React.useState("");
  React.useEffect(() => { if (!courseId && courseList.length) setCourseId(String(courseList[0].id)); }, [courseList]);

  const { data: initQuizzes, loading: qzLoading } = useApi(courseId ? `/courses/${courseId}/quizzes` : null);
  const [quizzes, setQuizzes] = React.useState(null);
  const quizList = quizzes ?? initQuizzes ?? [];
  const refreshQuizzes = () => courseId && apiFetch(`/courses/${courseId}/quizzes`).then(r=>r.json()).then(setQuizzes).catch(()=>{});

  const [activeQuiz, setActiveQuiz] = React.useState(null);
  const [questions, setQuestions] = React.useState([]);
  const loadQuestions = (id) => apiFetch(`/quizzes/${id}/questions`).then(r=>r.json()).then(setQuestions).catch(()=>setQuestions([]));
  React.useEffect(() => { setQuizzes(null); setActiveQuiz(null); setQuestions([]); }, [courseId]);
  const openQuiz = (qz) => { setActiveQuiz(qz); loadQuestions(qz.id); };

  const [showCreate, setShowCreate] = React.useState(false);
  const [form, setForm] = React.useState({ title:"", time_limit:"20 minutes" });
  const [creating, setCreating] = React.useState(false);
  const [createErr, setCreateErr] = React.useState("");
  const createQuiz = async () => {
    if (!form.title.trim()) { setCreateErr("Title required"); return; }
    setCreateErr(""); setCreating(true);
    const res = await apiFetch("/quizzes", { method:"POST", body: JSON.stringify({ title: form.title.trim(), course_id: parseInt(courseId), time_limit: form.time_limit }) });
    setCreating(false);
    if (!res.ok) { const d=await res.json().catch(()=>({})); setCreateErr(d.error||"Failed to create quiz"); return; }
    setForm({ title:"", time_limit:"20 minutes" }); setShowCreate(false); refreshQuizzes();
  };

  const [qForm, setQForm] = React.useState({ question:"", options:["",""], correct:0, video_url:"", image_ref:null, image_course_id:null, image_preview:null });
  const [qSaving, setQSaving] = React.useState(false);
  const [qErr, setQErr] = React.useState("");
  // Image picker (choose from this course's library images)
  const [showPicker, setShowPicker] = React.useState(false);
  const { data: courseFiles } = useApi(courseId ? `/library-assets/${courseId}` : null);
  const courseImages = (courseFiles || []).filter(f => isImageType(f.file_type));
  const pickImage = async (f) => {
    setShowPicker(false);
    setQForm(s => ({ ...s, image_ref: f.id, image_course_id: parseInt(courseId), image_preview: null }));
    const r = await apiFetch(`/library-assets/${courseId}/${encodeURIComponent(f.id)}/data`);
    if (r.ok) {
      const d = await r.json(); const bin = atob(d.file_data||""), by = new Uint8Array(bin.length);
      for (let i=0;i<bin.length;i++) by[i] = bin.charCodeAt(i);
      const u = URL.createObjectURL(new Blob([by], { type: f.mime_type || "image/*" }));
      setQForm(s => ({ ...s, image_preview: u }));
    }
  };
  const clearImage = () => setQForm(s => ({ ...s, image_ref:null, image_course_id:null, image_preview:null }));
  const setOpt = (i,v) => setQForm(f=>{ const o=[...f.options]; o[i]=v; return {...f,options:o}; });
  const addOpt = () => setQForm(f=> f.options.length<6 ? {...f,options:[...f.options,""]} : f);
  const removeOpt = (i) => setQForm(f=>{ if(f.options.length<=2) return f; const o=f.options.filter((_,j)=>j!==i); return {...f, options:o, correct: f.correct>=o.length ? o.length-1 : f.correct}; });
  const addQuestion = async () => {
    const opts = qForm.options.map(o=>o.trim()).filter(Boolean);
    if (!qForm.question.trim()) { setQErr("Question required"); return; }
    if (opts.length < 2) { setQErr("At least 2 options"); return; }
    if (qForm.correct >= opts.length) { setQErr("Mark the correct option"); return; }
    setQErr(""); setQSaving(true);
    const res = await apiFetch(`/quizzes/${activeQuiz.id}/questions`, { method:"POST", body: JSON.stringify({
      question: qForm.question.trim(), type:"mcq", options: opts, correct_ans: qForm.correct,
      video_url: qForm.video_url.trim() || null, image_ref: qForm.image_ref || null, image_course_id: qForm.image_course_id || null,
    }) });
    setQSaving(false);
    if (!res.ok) { const d=await res.json().catch(()=>({})); setQErr(d.error||"Failed to add question"); return; }
    setQForm({ question:"", options:["",""], correct:0, video_url:"", image_ref:null, image_course_id:null, image_preview:null });
    loadQuestions(activeQuiz.id); refreshQuizzes();
  };
  const deleteQuestion = async (id) => {
    if (!confirm("Delete this question?")) return;
    await apiFetch(`/quiz-questions/${id}`, { method:"DELETE" });
    loadQuestions(activeQuiz.id); refreshQuizzes();
  };
  const deleteQuiz = async (qz) => {
    if (!confirm(`Delete quiz "${qz.title}"?\n\nIts questions and every student result are removed too. This cannot be undone.`)) return;
    const r = await apiFetch(`/quizzes/${qz.id}`, { method:"DELETE" });
    if (!r.ok) { const d = await r.json().catch(()=>({})); alert(d.error || "Could not delete this quiz."); return; }
    if (activeQuiz?.id === qz.id) { setActiveQuiz(null); setQuestions([]); }
    refreshQuizzes();
  };

  const inS = { width:"100%", padding:"8px 12px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:13, color:"var(--ink)", fontFamily:"var(--font-ui)", boxSizing:"border-box", outline:"none" };
  return (
    <div style={{ width:"100%", height:"100%", display:"flex", fontFamily:"var(--font-ui)", background:"var(--paper)" }}>
      {showPicker && (
        <Modal title="Pick an image from this course" onClose={()=>setShowPicker(false)}>
          {courseImages.length===0 ? (
            <div style={{ padding:20, textAlign:"center", color:"var(--ink-mute)", fontSize:13 }}>No images in this course yet. Upload images under Courses → Files.</div>
          ) : (
            <div style={{ display:"grid", gridTemplateColumns:"repeat(auto-fill,minmax(110px,1fr))", gap:10, maxHeight:380, overflow:"auto" }}>
              {courseImages.map(f => (
                <button key={f.id} onClick={()=>pickImage(f)} title={f.title} style={{ border:"var(--border-thin)", borderRadius:10, padding:6, background:"var(--paper-card)", cursor:"pointer", textAlign:"center" }}>
                  <MediaThumb f={f}/>
                  <div style={{ fontSize:10, marginTop:4, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{f.title}</div>
                </button>
              ))}
            </div>
          )}
        </Modal>
      )}
      <ContentManagerSidebar active={route} go={go} session={session}/>
      <div style={{ flex:1, display:"flex", flexDirection:"column", minWidth:0 }}>
        <div style={{ padding:"14px 22px", borderBottom:"var(--border-thin)", background:"var(--paper-card)", display:"flex", alignItems:"center", gap:12, flexWrap:"wrap" }}>
          <div>
            <div className="display" style={{ fontSize:20 }}>Quizzes</div>
            <div style={{ fontSize:12, color:"var(--ink-mute)" }}>Assigned to every student in the selected course</div>
          </div>
          <div style={{ flex:1 }}/>
          <select value={courseId} onChange={e=>setCourseId(e.target.value)} style={{ ...inS, width:220 }}>
            {courseList.length===0 && <option value="">No courses</option>}
            {courseList.map(c=><option key={c.id} value={c.id}>{c.name}</option>)}
          </select>
          <button className="btn btn-primary" onClick={()=>{ setShowCreate(v=>!v); setCreateErr(""); }} disabled={!courseId} style={{ padding:"8px 14px", fontSize:13 }}>{showCreate?"Cancel":"+ New quiz"}</button>
        </div>

        {showCreate && (
          <div style={{ padding:"12px 22px", borderBottom:"var(--border-thin)", background:"var(--paper-card)", display:"flex", gap:8, alignItems:"center", flexWrap:"wrap" }}>
            <input value={form.title} onChange={e=>setForm(f=>({...f,title:e.target.value}))} placeholder="Quiz title" style={{ ...inS, width:280 }}/>
            <input value={form.time_limit} onChange={e=>setForm(f=>({...f,time_limit:e.target.value}))} placeholder="Time limit (e.g. 20 minutes)" style={{ ...inS, width:200 }}/>
            <button disabled={creating} onClick={createQuiz} className="btn btn-primary" style={{ padding:"8px 16px", fontSize:13 }}>{creating?"Creating…":"Create"}</button>
            {createErr && <span style={{ color:"var(--coral)", fontSize:12 }}>{createErr}</span>}
          </div>
        )}

        <div style={{ flex:1, display:"flex", minHeight:0 }}>
          {/* Quiz list */}
          <div style={{ width:280, borderRight:"var(--border-thin)", overflow:"auto", padding:12, background:"var(--paper-card)" }}>
            {qzLoading ? <Spinner/> : quizList.length===0 ? (
              <div style={{ textAlign:"center", padding:24, color:"var(--ink-mute)", fontSize:12 }}>No quizzes yet — create one above.</div>
            ) : quizList.map(qz=>{
              const on = activeQuiz?.id===qz.id;
              return (
                <div key={qz.id} style={{
                  display:"flex", alignItems:"center", gap:6, width:"100%", padding:"10px 12px", borderRadius:8, marginBottom:4,
                  border:`1.5px solid ${on?"var(--coral)":"transparent"}`, background: on?"rgba(var(--coral-rgb),.07)":"transparent",
                }}>
                  <div onClick={()=>openQuiz(qz)} style={{ flex:1, minWidth:0, cursor:"pointer", textAlign:"left" }}>
                    <div style={{ fontWeight:700, fontSize:13, color:"var(--ink)" }}>{qz.title}</div>
                    <div style={{ fontSize:11, color:"var(--ink-mute)" }}>{qz.questions} question{qz.questions===1?"":"s"} · {qz.assigned}</div>
                  </div>
                  <button title="Delete quiz" onClick={()=>deleteQuiz(qz)} style={{...CTRL_BTN, color:"var(--coral)"}}>🗑</button>
                </div>
              );
            })}
          </div>

          {/* Quiz detail / questions */}
          <div style={{ flex:1, overflow:"auto", padding:22, minWidth:0 }}>
            {!activeQuiz ? (
              <div style={{ display:"flex", flexDirection:"column", alignItems:"center", justifyContent:"center", height:"100%", gap:10, color:"var(--ink-mute)" }}>
                <div style={{ fontSize:44 }}>📝</div>
                <div style={{ fontSize:15, fontWeight:700, color:"var(--ink)" }}>Select or create a quiz</div>
                <div style={{ fontSize:13 }}>Add multiple-choice questions; students of this course get the quiz automatically.</div>
              </div>
            ) : (
              <div style={{ display:"flex", flexDirection:"column", gap:16 }}>
                <div className="display" style={{ fontSize:18 }}>{activeQuiz.title}</div>

                {/* Existing questions */}
                {questions.length===0 ? (
                  <div style={{ fontSize:13, color:"var(--ink-mute)" }}>No questions yet — add the first below.</div>
                ) : questions.map((qq,i)=>(
                  <div key={qq.id} className="card-flat" style={{ padding:14 }}>
                    <div style={{ display:"flex", gap:8 }}>
                      <span style={{ fontWeight:800, color:"var(--coral)" }}>{i+1}.</span>
                      <div style={{ flex:1, minWidth:0 }}>
                        <div style={{ fontWeight:700, fontSize:13, marginBottom:6 }}>{qq.q}</div>
                        {qq.image_ref && <div style={{ margin:"4px 0 8px" }}><QuizQImage qid={qq.id} style={{ maxHeight:130, borderRadius:8, display:"block" }}/></div>}
                        {qq.video_url && <div style={{ maxWidth:340, margin:"4px 0 8px" }}><SafeYouTube url={qq.video_url} title="Question video"/></div>}
                        {(qq.opts||[]).map((o,oi)=>(
                          <div key={oi} style={{ fontSize:12, color: oi===qq.ans ? "var(--moss)" : "var(--ink-soft)", fontWeight: oi===qq.ans?700:400 }}>
                            {oi===qq.ans ? "✓ " : "• "}{o}
                          </div>
                        ))}
                      </div>
                      <button title="Delete question" onClick={()=>deleteQuestion(qq.id)} style={{...CTRL_BTN, color:"var(--coral)"}}>🗑</button>
                    </div>
                  </div>
                ))}

                {/* Add question */}
                <div className="card-flat" style={{ padding:16 }}>
                  <div className="mono" style={{ fontSize:10, color:"var(--ink-mute)", letterSpacing:".06em", marginBottom:10 }}>ADD QUESTION (MCQ)</div>
                  <div style={{ display:"flex", flexDirection:"column", gap:10 }}>
                    <input value={qForm.question} onChange={e=>setQForm(f=>({...f,question:e.target.value}))} placeholder="Question text" style={inS}/>
                    {qForm.options.map((o,i)=>(
                      <div key={i} style={{ display:"flex", alignItems:"center", gap:8 }}>
                        <input type="radio" name="cm-correct" checked={qForm.correct===i} onChange={()=>setQForm(f=>({...f,correct:i}))} title="Mark correct"/>
                        <input value={o} onChange={e=>setOpt(i,e.target.value)} placeholder={`Option ${i+1}`} style={{ ...inS, flex:1 }}/>
                        {qForm.options.length>2 && <button onClick={()=>removeOpt(i)} style={CTRL_BTN} title="Remove option">✕</button>}
                      </div>
                    ))}
                    <div style={{ display:"flex", alignItems:"center", gap:10 }}>
                      <button onClick={addOpt} className="btn btn-ghost" style={{ padding:"5px 12px", fontSize:12 }} disabled={qForm.options.length>=6}>+ Option</button>
                      <span style={{ fontSize:11, color:"var(--ink-mute)" }}>Select the radio next to the correct answer.</span>
                    </div>
                    <input value={qForm.video_url} onChange={e=>setQForm(f=>({...f,video_url:e.target.value}))} placeholder="Video link (optional — YouTube watch / youtu.be / embed)" style={inS}/>
                    <div style={{ display:"flex", alignItems:"center", gap:10 }}>
                      {qForm.image_ref ? (
                        <>
                          {qForm.image_preview
                            ? <img src={qForm.image_preview} alt="" style={{ width:56, height:56, objectFit:"cover", borderRadius:8, border:"var(--border-thin)" }}/>
                            : <span style={{ fontSize:11, color:"var(--ink-mute)" }}>Image attached</span>}
                          <button onClick={clearImage} className="btn btn-ghost" style={{ padding:"5px 12px", fontSize:12 }}>Remove image</button>
                        </>
                      ) : (
                        <button onClick={()=>setShowPicker(true)} className="btn btn-ghost" style={{ padding:"5px 12px", fontSize:12 }} disabled={!courseId}>+ Add image from library</button>
                      )}
                    </div>
                    {qErr && <div style={{ color:"var(--coral)", fontSize:12 }}>{qErr}</div>}
                    <button disabled={qSaving} onClick={addQuestion} className="btn btn-primary" style={{ padding:"9px 0", fontSize:13 }}>{qSaving?"Saving…":"Add question"}</button>
                  </div>
                </div>
              </div>
            )}
          </div>
        </div>
      </div>
    </div>
  );
};

const ContentManagerHome = ({ go, session }) => {
  const route = window.location.hash.slice(1);
  // Use /courses/list — it returns id/name/level (what the sidebar renders).
  // /courses returns a combined `title` instead, which left the labels blank.
  const { data: courses, loading: cLoading } = useApi("/courses/list");
  const [coursesLocal, setCoursesLocal] = React.useState(null);
  const courseItems = coursesLocal ?? courses;
  const refreshCourses = async () => setCoursesLocal(asArray(await apiList("/courses/list")));
  const [showNewCourse, setShowNewCourse] = React.useState(false);
  const [newCourse, setNewCourse] = React.useState({ name:"", level:"Beginner" });
  const [courseSaving, setCourseSaving] = React.useState(false);
  const [courseError, setCourseError] = React.useState("");
  const createCourse = async () => {
    if (!newCourse.name.trim()) { setCourseError("Course name required"); return; }
    setCourseError(""); setCourseSaving(true);
    const res = await apiFetch("/courses/list", { method:"POST", body: JSON.stringify({ name:newCourse.name.trim(), level:newCourse.level.trim()||"Beginner" }) });
    if (!res.ok) { const d=await res.json().catch(()=>({})); setCourseError(d.error||"Failed to create course"); setCourseSaving(false); return; }
    setNewCourse({ name:"", level:"Beginner" }); setShowNewCourse(false); setCourseSaving(false);
    await refreshCourses();
  };
  const [selectedCourse, setSelectedCourse] = React.useState(null);
  const [cmTab, setCmTab] = React.useState("files");
  const { data: initFiles, loading: fLoading } = useApi(selectedCourse ? `/library-assets/${selectedCourse.id}` : null);
  const [files, setFiles] = React.useState(null);
  const fileList = files ?? initFiles;
  const lessonFiles = (fileList || []).filter(f => !!f.lesson_file_id);

  // Units state
  const { data: initUnits } = useApi(selectedCourse ? `/content/${selectedCourse.id}` : null);
  const [units, setUnits] = React.useState(null);
  const unitList = units ?? initUnits ?? [];
  const refreshUnits = () => selectedCourse && apiFetch(`/content/${selectedCourse.id}`).then(r=>r.json()).then(setUnits);
  const [unitTitle, setUnitTitle] = React.useState("");
  const [unitSaving, setUnitSaving] = React.useState(false);
  const [unitError, setUnitError] = React.useState("");
  const [lessonForm, setLessonForm] = React.useState({ title:"", unit_id:"", type:"video", youtube_url:"", duration:"", file_id:"", body:"" });
  // Bumped after each save so the (uncontrolled) rich text editor re-seeds itself.
  const [lessonDocKey, setLessonDocKey] = React.useState(0);
  const [lessonSaving, setLessonSaving] = React.useState(false);
  const [lessonError, setLessonError] = React.useState("");
  const [showLessonForm, setShowLessonForm] = React.useState(false);

  const [uploading, setUploading] = React.useState(false);
  const [uploadError, setUploadError] = React.useState("");
  const [dragOver, setDragOver] = React.useState(false);
  const fileInputRef = React.useRef();

  React.useEffect(() => { setFiles(null); setUnits(null); }, [selectedCourse?.id]);

  const refreshFiles = () => {
    if (!selectedCourse) return;
    apiFetch(`/library-assets/${selectedCourse.id}`).then(r=>r.json()).then(setFiles);
  };

  const cmSaveUnit = async () => {
    if (!unitTitle.trim()) { setUnitError("Title required"); return; }
    setUnitError(""); setUnitSaving(true);
    const res = await apiFetch(`/courses/${selectedCourse.id}/units`, { method:"POST", body: JSON.stringify({ title: unitTitle.trim() }) });
    if (!res.ok) { const d=await res.json(); setUnitError(d.error||"Failed"); setUnitSaving(false); return; }
    setUnitTitle(""); setUnitSaving(false); refreshUnits();
  };

  const cmSaveLesson = async () => {
    if (!lessonForm.title.trim()) { setLessonError("Title required"); return; }
    if (!lessonForm.unit_id) { setLessonError("Select a unit"); return; }
    if (lessonForm.type==="file" && !lessonForm.file_id) { setLessonError("Pick an uploaded file (or upload one in the Files tab first)"); return; }
    if (lessonForm.type==="text" && !stripHtml(lessonForm.body)) { setLessonError("Write some content for this text lesson"); return; }
    setLessonError(""); setLessonSaving(true);
    const res = await apiFetch(`/units/${lessonForm.unit_id}/lessons`, { method:"POST", body: JSON.stringify({ title:lessonForm.title, type:lessonForm.type, youtube_url:lessonForm.youtube_url, duration:lessonForm.duration, file_id: lessonForm.file_id||null, body: lessonForm.type==="text" ? lessonForm.body : null }) });
    if (!res.ok) { const d=await res.json(); setLessonError(d.error||"Failed"); setLessonSaving(false); return; }
    setLessonForm({ title:"", unit_id:lessonForm.unit_id, type:"video", youtube_url:"", duration:"", file_id:"", body:"" });
    setLessonDocKey(k => k + 1);
    setShowLessonForm(false); setLessonSaving(false); refreshUnits();
  };

  // ── Edit / delete / reorder / publish ───────────────────────────────────────
  const [editLesson, setEditLesson] = React.useState(null);
  const [editLForm, setEditLForm] = React.useState({});
  const [editLSaving, setEditLSaving] = React.useState(false);
  // Lightweight in-app text editor (txt files only)
  const [editTextFile, setEditTextFile] = React.useState(null);
  const [editTextValue, setEditTextValue] = React.useState("");
  const [editTextLoading, setEditTextLoading] = React.useState(false);
  const [editTextSaving, setEditTextSaving] = React.useState(false);
  const renameCourse = async (c) => {
    const t = prompt("Rename course", c.name);
    if (t == null || !t.trim() || t.trim() === c.name) return;
    const res = await apiFetch(`/courses/${c.id}`, { method:"PATCH", body: JSON.stringify({ name: t.trim() }) });
    if (!res.ok) { const d = await res.json().catch(()=>({})); alert(d.error || "Could not rename course"); return; }
    const updated = await res.json();
    setSelectedCourse(s => (s && s.id === c.id ? { ...s, name: updated.name } : s));
    await refreshCourses();
  };
  const renameUnit = async (u) => {
    const t = prompt("Rename module", u.unit);
    if (t == null || !t.trim()) return;
    await apiFetch(`/units/${u.id}`, { method:"PATCH", body: JSON.stringify({ title: t.trim() }) });
    refreshUnits();
  };
  const moveUnit = async (u, direction) => { await apiFetch(`/units/${u.id}/move`, { method:"POST", body: JSON.stringify({ direction }) }); refreshUnits(); };
  const deleteUnit = async (u) => {
    const n = (u.lessons||[]).length;
    if (!confirm(`Delete module "${u.unit}"${n ? ` and its ${n} lesson${n>1?"s":""}` : ""}? This cannot be undone.`)) return;
    await apiFetch(`/units/${u.id}`, { method:"DELETE" }); refreshUnits();
  };
  const moveLesson = async (l, direction) => { await apiFetch(`/lessons/${l.id}/move`, { method:"POST", body: JSON.stringify({ direction }) }); refreshUnits(); };
  const deleteLesson = async (l) => { if (!confirm(`Delete lesson "${l.title}"?`)) return; await apiFetch(`/lessons/${l.id}`, { method:"DELETE" }); refreshUnits(); };
  const togglePublish = async (l) => { await apiFetch(`/lessons/${l.id}`, { method:"PATCH", body: JSON.stringify({ status: l.status==="published" ? "draft" : "published" }) }); refreshUnits(); };
  const openEditLesson = (l) => { setEditLesson(l); setEditLForm({ title:l.title, type:l.type, youtube_url: l.youtube ? `https://youtu.be/${l.youtube}` : "", duration: l.duration||"", file_id: l.file_id||"", status: l.status, body: l.body||"" }); };
  const saveEditLesson = async () => {
    if (!editLForm.title.trim()) return;
    if (editLForm.type === "text" && !stripHtml(editLForm.body)) { alert("Write some content for this text lesson."); return; }
    setEditLSaving(true);
    const body = { title: editLForm.title, type: editLForm.type, duration: editLForm.duration, status: editLForm.status };
    if (editLForm.type === "video") body.youtube_url = editLForm.youtube_url;
    if (editLForm.type === "file") body.file_id = editLForm.file_id || null;
    if (editLForm.type === "text") body.body = editLForm.body;
    const res = await apiFetch(`/lessons/${editLesson.id}`, { method:"PATCH", body: JSON.stringify(body) });
    setEditLSaving(false);
    if (!res.ok) { const d = await res.json().catch(()=>({})); alert(d.error || "Could not save lesson"); return; }
    setEditLesson(null); refreshUnits();
  };

  const handleFiles = async (rawFiles) => {
    if (!selectedCourse) { setUploadError("Select a course first"); return; }
    setUploadError("");
    setUploading(true);
    for (const file of Array.from(rawFiles)) {
      const ext = file.name.split(".").pop().toLowerCase();
      if (!ALLOWED_TYPES.includes(ext)) { setUploadError(`${file.name}: unsupported type. Allowed: ${ALLOWED_TYPES.join(", ")}`); continue; }
      if (file.size > 35 * 1024 * 1024) { setUploadError(`${file.name}: ${(file.size/1048576).toFixed(1)} MB — max is 35 MB`); continue; }
      const reader = new FileReader();
      await new Promise(resolve => {
        reader.onload = async (e) => {
          // Strip the "data:...;base64," prefix — store raw base64 so the viewer
          // (which re-adds the prefix) doesn't double it up and corrupt the file.
          const base64 = String(e.target.result).split(",")[1];
          const res = await apiFetch("/library-assets", { method:"POST", body: JSON.stringify({ course_id: selectedCourse.id, title: file.name, file_type: ext, file_data: base64 }) });
          if (!res.ok) {
            const d = await res.json().catch(() => ({}));
            setUploadError(`${file.name}: ${d.error || (res.status === 413 ? "too large for the server" : "upload failed ("+res.status+")")}`);
          }
          resolve();
        };
        reader.onerror = () => { setUploadError(`${file.name}: could not read file`); resolve(); };
        reader.readAsDataURL(file);
      });
    }
    setUploading(false);
    refreshFiles();
  };

  const deleteFile = async (id) => {
    if (!confirm("Delete this file?")) return;
    await apiFetch(`/library-assets/${encodeURIComponent(id)}`, { method:"DELETE" });
    refreshFiles();
  };

  const cmFileBlobUrl = async (fileId) => {
    const r = await apiFetch(`/library-assets/${selectedCourse.id}/${encodeURIComponent(fileId)}/data`);
    if (!r.ok) return null;
    const { file_data, file_type } = await r.json();
    const bin = atob(file_data), bytes = new Uint8Array(bin.length);
    for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
    return URL.createObjectURL(new Blob([bytes], { type: fileMimeType(file_type) }));
  };
  const previewFile = async (id) => {
    const url = await cmFileBlobUrl(id);
    if (!url) { setUploadError("Could not open file."); return; }
    window.open(url, "_blank"); setTimeout(() => URL.revokeObjectURL(url), 60000);
  };
  const downloadFile = async (id, title) => {
    const url = await cmFileBlobUrl(id);
    if (!url) { setUploadError("Could not download file."); return; }
    const a = document.createElement("a"); a.href = url; a.download = title; a.click();
    setTimeout(() => URL.revokeObjectURL(url), 60000);
  };

  // ── In-app text editor (txt only) ───────────────────────────────────────────
  const b64ToText = (b64) => { try { return decodeURIComponent(escape(atob(b64))); } catch { return atob(b64); } };
  const textToB64 = (text) => btoa(unescape(encodeURIComponent(text)));
  const openTextEditor = async (f) => {
    setEditTextFile(f); setEditTextValue(""); setEditTextLoading(true);
    const r = await apiFetch(`/library-assets/${selectedCourse.id}/${encodeURIComponent(f.id)}/data`);
    if (!r.ok) { setEditTextLoading(false); setEditTextFile(null); setUploadError("Could not open file."); return; }
    const { file_data } = await r.json();
    setEditTextValue(b64ToText(file_data || "")); setEditTextLoading(false);
  };
  const saveTextEditor = async () => {
    setEditTextSaving(true);
    const r = await apiFetch(`/library-assets/${selectedCourse.id}/${encodeURIComponent(editTextFile.id)}/data`, { method:"PUT", body: JSON.stringify({ file_data: textToB64(editTextValue) }) });
    setEditTextSaving(false);
    if (!r.ok) { const d = await r.json().catch(()=>({})); alert(d.error || "Could not save file"); return; }
    setEditTextFile(null); refreshFiles();
  };

  const fmtSize = b => b > 1048576 ? `${(b/1048576).toFixed(1)} MB` : `${(b/1024).toFixed(0)} KB`;

  const editInput = { width:"100%", padding:"8px 12px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:13, color:"var(--ink)", fontFamily:"var(--font-ui)", boxSizing:"border-box", outline:"none" };

  if (route === "/app/content/all-files") {
    return <MediaLibrary session={session} go={go}/>;
  }

  if (route === "/app/content/text") {
    return <TextLibrary session={session} go={go}/>;
  }

  if (route === "/app/content/quizzes") {
    return <CMQuizzes session={session} go={go}/>;
  }

  if (route === "/app/content/profile") {
    return (
      <div style={{ width:"100%", height:"100%", display:"flex", fontFamily:"var(--font-ui)", background:"var(--paper)" }}>
        <ContentManagerSidebar active={route} go={go} session={session}/>
        <ProfilePanel session={session}/>
      </div>
    );
  }

  return (
    <div style={{ width:"100%", height:"100%", display:"flex", fontFamily:"var(--font-ui)", background:"var(--paper)" }}>
      {editLesson && (
        <Modal title="Edit lesson" onClose={()=>setEditLesson(null)}>
          <div style={{ display:"flex", flexDirection:"column", gap:10 }}>
            <input value={editLForm.title} onChange={e=>setEditLForm(f=>({...f,title:e.target.value}))} placeholder="Lesson title" style={editInput}/>
            <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:8 }}>
              <select value={editLForm.type} onChange={e=>setEditLForm(f=>({...f,type:e.target.value}))} style={editInput}>
                {LESSON_TYPES.map(t=><option key={t} value={t}>{t}</option>)}
              </select>
              <select value={editLForm.status} onChange={e=>setEditLForm(f=>({...f,status:e.target.value}))} style={editInput}>
                {["draft","published"].map(s=><option key={s} value={s}>{s}</option>)}
              </select>
            </div>
            {editLForm.type==="video" && <input value={editLForm.youtube_url} onChange={e=>setEditLForm(f=>({...f,youtube_url:e.target.value}))} placeholder="YouTube link (watch, youtu.be, embed or shorts)" style={editInput}/>}
            {/* A library-backed lesson has no body of its own — the document is
                the single source of truth, so send the author there instead of
                showing an editor that would look empty. */}
            {editLForm.type==="text" && (editLesson.text_doc_id ? (
              <div className="card-flat" style={{ padding:12, display:"flex", flexDirection:"column", gap:8 }}>
                <div style={{ fontSize:12.5, color:"var(--ink)" }}>
                  📝 Linked to the Text Library document <strong>{editLesson.text_doc_title || "—"}</strong>.
                </div>
                <div style={{ fontSize:11.5, color:"var(--ink-mute)" }}>Editing it there updates this lesson and every other module using it.</div>
                <button onClick={()=>{ setEditLesson(null); go("/app/content/text"); }} className="btn btn-ghost" style={{ alignSelf:"flex-start", padding:"5px 10px", fontSize:11.5 }}>Open Text Library</button>
              </div>
            ) : (
              <RichTextEditor docKey={`edit-${editLesson.id}`} value={editLForm.body}
                onChange={v=>setEditLForm(f=>({...f,body:v}))} minHeight={220}/>
            ))}
            {editLForm.type==="file" && ((lessonFiles&&lessonFiles.length) ? (
              <select value={editLForm.file_id} onChange={e=>setEditLForm(f=>({...f,file_id:e.target.value}))} style={editInput}>
                <option value="">— select an uploaded file —</option>
                {lessonFiles.map(f=><option key={f.id} value={f.lesson_file_id}>{(MIME_ICONS[f.file_type]||"📎")} {f.title}</option>)}
              </select>
            ) : <div style={{ fontSize:12, color:"var(--coral)" }}>No files uploaded — add one in the Files tab.</div>)}
            <input value={editLForm.duration} onChange={e=>setEditLForm(f=>({...f,duration:e.target.value}))} placeholder="Duration (optional, e.g. 8:22)" style={editInput}/>
            <div style={{ display:"flex", gap:8, justifyContent:"flex-end", marginTop:4 }}>
              <button onClick={()=>setEditLesson(null)} className="btn btn-ghost">Cancel</button>
              <button onClick={saveEditLesson} disabled={editLSaving} className="btn btn-primary">{editLSaving?"Saving…":"Save"}</button>
            </div>
          </div>
        </Modal>
      )}
      {editTextFile && (
        <Modal title={`Edit · ${editTextFile.title}`} onClose={()=>setEditTextFile(null)}>
          <div style={{ display:"flex", flexDirection:"column", gap:10 }}>
            {editTextLoading ? <Spinner/> : (
              <textarea value={editTextValue} onChange={e=>setEditTextValue(e.target.value)} spellCheck={false}
                style={{ width:"100%", minHeight:340, padding:"10px 12px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:13, color:"var(--ink)", fontFamily:"var(--font-mono, monospace)", boxSizing:"border-box", outline:"none", resize:"vertical", lineHeight:1.5 }}/>
            )}
            <div style={{ display:"flex", gap:8, justifyContent:"flex-end" }}>
              <button onClick={()=>setEditTextFile(null)} className="btn btn-ghost">Cancel</button>
              <button onClick={saveTextEditor} disabled={editTextSaving || editTextLoading} className="btn btn-primary">{editTextSaving?"Saving…":"Save"}</button>
            </div>
          </div>
        </Modal>
      )}
      <ContentManagerSidebar active={route} go={go} session={session}/>
      <div style={{ flex:1, display:"flex", minWidth:0 }}>

        {/* Course list panel */}
        <div style={{ width:280, borderRight:"var(--border-thin)", display:"flex", flexDirection:"column", background:"var(--paper-card)" }}>
          <div style={{ padding:"12px 16px", borderBottom:"var(--border-thin)" }}>
            <div style={{ display:"flex", alignItems:"center", gap:8 }}>
              <div style={{ flex:1 }}>
                <div style={{ fontWeight:700, fontSize:13 }}>Courses</div>
                <div style={{ fontSize:11, color:"var(--ink-mute)" }}>Select to manage content</div>
              </div>
              <button onClick={()=>{ setShowNewCourse(v=>!v); setCourseError(""); }} className="btn btn-ghost" style={{ padding:"4px 10px", fontSize:11 }}>{showNewCourse?"Cancel":"+ New"}</button>
            </div>
            {showNewCourse && (
              <div style={{ marginTop:10, display:"flex", flexDirection:"column", gap:6 }}>
                <input value={newCourse.name} onChange={e=>setNewCourse(c=>({...c,name:e.target.value}))} placeholder="Course name (e.g. Robotics)"
                  onKeyDown={e=>{ if(e.key==="Enter"){ e.preventDefault(); createCourse(); } }}
                  style={{ width:"100%", padding:"7px 10px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:12, color:"var(--ink)", fontFamily:"var(--font-ui)", boxSizing:"border-box", outline:"none" }}/>
                <select value={newCourse.level} onChange={e=>setNewCourse(c=>({...c,level:e.target.value}))}
                  style={{ width:"100%", padding:"7px 10px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:12, color:"var(--ink)", fontFamily:"var(--font-ui)" }}>
                  {["Beginner","Intermediate","Advanced"].map(l=><option key={l} value={l}>{l}</option>)}
                </select>
                {courseError && <div style={{ color:"var(--coral)", fontSize:11 }}>{courseError}</div>}
                <button disabled={courseSaving} onClick={createCourse} className="btn btn-primary" style={{ padding:"7px 0", fontSize:12 }}>{courseSaving?"Creating…":"Create course"}</button>
              </div>
            )}
          </div>
          <div style={{ flex:1, overflow:"auto", padding:10 }}>
            {cLoading ? <Spinner/> : courseItems.length === 0 ? (
              <div style={{ textAlign:"center", padding:24, color:"var(--ink-mute)", fontSize:12 }}>No courses yet — create one above.</div>
            ) : courseItems.map(c => {
              const on = selectedCourse?.id === c.id;
              return (
                <button key={c.id} onClick={() => setSelectedCourse(c)} style={{
                  display:"flex", alignItems:"center", gap:10, width:"100%", padding:"10px 12px",
                  borderRadius:8, border:`1.5px solid ${on?"var(--coral)":"transparent"}`,
                  background: on ? "rgba(var(--coral-rgb),.07)" : "transparent",
                  cursor:"pointer", textAlign:"left", marginBottom:4,
                }}>
                  <div style={{ width:36, height:36, borderRadius:10, background: on ? "var(--coral)" : "var(--paper-deep)", display:"grid", placeItems:"center", color: on ? "white" : "var(--ink-mute)", flexShrink:0 }}>
                    {I.book({ size:18 })}
                  </div>
                  <div>
                    <div style={{ fontWeight:700, fontSize:13, color:"var(--ink)" }}>{c.name}</div>
                    <div style={{ fontSize:11, color:"var(--ink-mute)" }}>{c.level}</div>
                  </div>
                </button>
              );
            })}
          </div>
        </div>

        {/* File management panel */}
        <div style={{ flex:1, display:"flex", flexDirection:"column", minWidth:0, overflow:"auto" }}>
          {!selectedCourse ? (
            <div style={{ flex:1, display:"flex", alignItems:"center", justifyContent:"center", flexDirection:"column", gap:12, color:"var(--ink-mute)", padding:40 }}>
              <div style={{ fontSize:48 }}>📚</div>
              <div style={{ fontSize:16, fontWeight:700, color:"var(--ink)" }}>Select a course</div>
              <div style={{ fontSize:13 }}>Pick a course from the left to upload and manage its materials</div>
            </div>
          ) : (
            <>
              <div style={{ padding:"14px 22px", borderBottom:"var(--border-thin)", background:"var(--paper-card)", display:"flex", alignItems:"center", gap:12 }}>
                <div>
                  <div style={{ display:"flex", alignItems:"center", gap:8 }}>
                    <div className="display" style={{ fontSize:20 }}>{selectedCourse.name}</div>
                    {hasPerm("manage_courses") && (
                      <button title="Rename course" onClick={()=>renameCourse(selectedCourse)} style={CTRL_BTN}>✏️</button>
                    )}
                  </div>
                  <div style={{ fontSize:12, color:"var(--ink-mute)", display:"flex", alignItems:"center", gap:8, flexWrap:"wrap" }}>
                    {selectedCourse.public_uid && <span className="mono" title="Copy course UID" onClick={()=>navigator.clipboard?.writeText(selectedCourse.public_uid)} style={{ cursor:"pointer", padding:"1px 7px", borderRadius:999, background:"var(--paper-deep)", fontSize:11, letterSpacing:".04em" }}>{selectedCourse.public_uid}</span>}
                    <span>{selectedCourse.level} · {unitList.length} units · {fileList?.length ?? "—"} files</span>
                  </div>
                </div>
                <div style={{ flex:1 }}/>
                <Tabs items={["Files","Units & Lessons"]} active={cmTab==="files"?"Files":"Units & Lessons"} onChange={v=>setCmTab(v==="Files"?"files":"units")}/>
                {cmTab==="files" && (
                  <button className="btn btn-primary" onClick={() => fileInputRef.current?.click()} style={{ padding:"8px 16px", fontSize:13 }}>
                    {I.plus({ size:14 })} Upload files
                  </button>
                )}
                <input ref={fileInputRef} type="file" multiple accept=".pdf,.ppt,.pptx,.txt,.doc,.docx,.png,.jpg,.jpeg,.webp,.gif" style={{ display:"none" }} onChange={e => handleFiles(e.target.files)}/>
              </div>

              <div style={{ padding:22, flex:1, overflow:"auto" }}>
              {cmTab === "units" && (
                <div style={{ display:"flex", flexDirection:"column", gap:16 }}>
                  {/* Add unit */}
                  <div className="card-flat" style={{ padding:16 }}>
                    <div className="mono" style={{ fontSize:10, color:"var(--ink-mute)", marginBottom:8, letterSpacing:".06em" }}>ADD UNIT</div>
                    <div style={{ display:"flex", gap:8 }}>
                      <input value={unitTitle} onChange={e=>setUnitTitle(e.target.value)} placeholder="e.g. Unit 1 · Getting Started"
                        style={{ flex:1, padding:"8px 12px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:13, color:"var(--ink)", fontFamily:"var(--font-ui)", outline:"none" }}
                        onKeyDown={async e => { if(e.key==="Enter"){ e.preventDefault(); await cmSaveUnit(); } }}/>
                      <button disabled={unitSaving} onClick={cmSaveUnit} className="btn btn-primary" style={{ padding:"8px 16px", fontSize:13 }}>{unitSaving?"…":"Add unit"}</button>
                    </div>
                    {unitError && <div style={{ color:"var(--coral)", fontSize:12, marginTop:6 }}>{unitError}</div>}
                  </div>

                  {/* Add lesson */}
                  <div className="card-flat" style={{ padding:16 }}>
                    <div style={{ display:"flex", alignItems:"center", justifyContent:"space-between", marginBottom: showLessonForm?12:0 }}>
                      <div className="mono" style={{ fontSize:10, color:"var(--ink-mute)", letterSpacing:".06em" }}>ADD LESSON</div>
                      <button onClick={()=>setShowLessonForm(v=>!v)} className="btn btn-ghost" style={{ padding:"4px 10px", fontSize:11 }}>{showLessonForm?"Cancel":"+ Add lesson"}</button>
                    </div>
                    {showLessonForm && (
                      <div style={{ display:"flex", flexDirection:"column", gap:10 }}>
                        <input value={lessonForm.title} onChange={e=>setLessonForm(f=>({...f,title:e.target.value}))} placeholder="Lesson title"
                          style={{ width:"100%", padding:"8px 12px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:13, color:"var(--ink)", fontFamily:"var(--font-ui)", boxSizing:"border-box", outline:"none" }}/>
                        <div style={{ display:"grid", gridTemplateColumns:"1fr 1fr", gap:8 }}>
                          <select value={lessonForm.unit_id} onChange={e=>setLessonForm(f=>({...f,unit_id:e.target.value}))}
                            style={{ padding:"8px 12px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:13, color:"var(--ink)", fontFamily:"var(--font-ui)" }}>
                            <option value="">— select unit —</option>
                            {unitList.map(u=><option key={u.id} value={u.id}>{u.unit}</option>)}
                          </select>
                          <select value={lessonForm.type} onChange={e=>setLessonForm(f=>({...f,type:e.target.value}))}
                            style={{ padding:"8px 12px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:13, color:"var(--ink)", fontFamily:"var(--font-ui)" }}>
                            {LESSON_TYPES.map(t=><option key={t} value={t}>{t}</option>)}
                          </select>
                        </div>
                        {lessonForm.type==="video" && (
                          <input value={lessonForm.youtube_url} onChange={e=>setLessonForm(f=>({...f,youtube_url:e.target.value}))} placeholder="YouTube link (watch, youtu.be, embed or shorts)"
                            style={{ width:"100%", padding:"8px 12px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:13, color:"var(--ink)", fontFamily:"var(--font-ui)", boxSizing:"border-box", outline:"none" }}/>
                        )}
                        {lessonForm.type==="text" && (
                          <RichTextEditor docKey={`new-${lessonDocKey}`} value={lessonForm.body}
                            onChange={v=>setLessonForm(f=>({...f,body:v}))}
                            placeholder="Write the lesson — headings, lists, quotes and code blocks are all supported."/>
                        )}
                        {lessonForm.type==="file" && (
                          (lessonFiles && lessonFiles.length) ? (
                            <select value={lessonForm.file_id} onChange={e=>setLessonForm(f=>({...f,file_id:e.target.value}))}
                              style={{ width:"100%", padding:"8px 12px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:13, color:"var(--ink)", fontFamily:"var(--font-ui)" }}>
                              <option value="">— select an uploaded file —</option>
                              {lessonFiles.map(f=><option key={f.id} value={f.lesson_file_id}>{(MIME_ICONS[f.file_type]||"📎")} {f.title}</option>)}
                            </select>
                          ) : (
                            <div style={{ fontSize:12, color:"var(--coral)" }}>No files uploaded yet — add one in the <b>Files</b> tab, then come back.</div>
                          )
                        )}
                        <input value={lessonForm.duration} onChange={e=>setLessonForm(f=>({...f,duration:e.target.value}))} placeholder="Duration (optional, e.g. 8:22)"
                          style={{ width:"100%", padding:"8px 12px", border:"var(--border-thin)", borderRadius:8, background:"var(--paper-deep)", fontSize:13, color:"var(--ink)", fontFamily:"var(--font-ui)", boxSizing:"border-box", outline:"none" }}/>
                        {lessonError && <div style={{ color:"var(--coral)", fontSize:12 }}>{lessonError}</div>}
                        <button disabled={lessonSaving} onClick={cmSaveLesson} className="btn btn-primary" style={{ padding:"9px 0", fontSize:13 }}>{lessonSaving?"Saving…":"Save lesson"}</button>
                      </div>
                    )}
                  </div>

                  {/* Unit list */}
                  {unitList.length === 0 ? (
                    <div style={{ textAlign:"center", padding:32, color:"var(--ink-mute)", fontSize:13 }}>No units yet — add one above.</div>
                  ) : unitList.map((u,ui)=>{
                    const lessons = u.lessons||[];
                    const pubCount = lessons.filter(l=>l.status==="published").length;
                    return (
                    <div key={u.id} className="card-flat" style={{ padding:14 }}>
                      <div style={{ display:"flex", alignItems:"center", gap:8, marginBottom:8 }}>
                        <span title="Module order (auto-updates when reordered)" style={{ minWidth:24, height:24, padding:"0 6px", borderRadius:7, background:"var(--coral)", color:"white", display:"inline-grid", placeItems:"center", fontWeight:800, fontSize:12, flexShrink:0 }}>{ui+1}</span>
                        <div style={{ flex:1, minWidth:0 }}>
                          <div style={{ fontWeight:700, fontSize:14 }}>{u.unit}</div>
                          {u.public_uid && <span className="mono" title="Copy module UID" onClick={()=>navigator.clipboard?.writeText(u.public_uid)} style={{ fontSize:10, color:"var(--ink-mute)", cursor:"pointer", letterSpacing:".04em" }}>{u.public_uid}</span>}
                        </div>
                        <span style={{ fontSize:10, color:"var(--ink-mute)", marginRight:4 }}>{pubCount}/{lessons.length} published</span>
                        <button title="Move up" onClick={()=>moveUnit(u,"up")} disabled={ui===0} style={{...CTRL_BTN, opacity: ui===0?.3:1}}>↑</button>
                        <button title="Move down" onClick={()=>moveUnit(u,"down")} disabled={ui===unitList.length-1} style={{...CTRL_BTN, opacity: ui===unitList.length-1?.3:1}}>↓</button>
                        <button title="Rename module" onClick={()=>renameUnit(u)} style={CTRL_BTN}>✏️</button>
                        {hasPerm("administer_courses") && <button title="Delete module (admin)" onClick={()=>deleteUnit(u)} style={{...CTRL_BTN, color:"var(--coral)"}}>🗑</button>}
                      </div>
                      {lessons.length === 0
                        ? <div style={{ fontSize:12, color:"var(--ink-mute)" }}>No lessons yet.</div>
                        : lessons.map((l,li)=>(
                          <div key={l.id||li} style={{ display:"flex", alignItems:"center", gap:6, padding:"6px 8px", borderRadius:7, marginBottom:3, background:"var(--paper-deep)" }}>
                            <span title="Lesson order (auto-updates when reordered)" style={{ fontSize:11, color:"var(--ink-mute)", fontWeight:700, flexShrink:0, minWidth:30 }}>{ui+1}.{li+1}</span>
                            <div title={l.type} style={{ width:24, height:24, borderRadius:6, background: LESSON_TYPE_COLOR[l.type] || "var(--gold)", color:"white", display:"grid", placeItems:"center", flexShrink:0, fontSize:11 }}>
                              {l.type==="video"?I.play({size:10}):l.type==="quiz"?I.puzzle({size:10}):l.type==="text"?"¶":I.book({size:10})}
                            </div>
                            <div style={{ flex:1, minWidth:0 }}>
                              <div style={{ fontSize:12, fontWeight:600, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{l.title}</div>
                              {l.public_uid && <span className="mono" title="Copy lesson UID" onClick={()=>navigator.clipboard?.writeText(l.public_uid)} style={{ fontSize:9, color:"var(--ink-mute)", cursor:"pointer", letterSpacing:".04em" }}>{l.public_uid}</span>}
                            </div>
                            <span className="chip" style={{ fontSize:9, padding:"1px 6px", borderRadius:999, background: l.status==="published"?"var(--moss)":"var(--gold)", color:"white", flexShrink:0 }}>{l.status==="published"?"published":"draft"}</span>
                            <button title={l.status==="published"?"Unpublish (hide from students)":"Publish (show to students)"} onClick={()=>togglePublish(l)} style={CTRL_BTN}>{l.status==="published"?"🙈":"👁"}</button>
                            <button title="Move up" onClick={()=>moveLesson(l,"up")} disabled={li===0} style={{...CTRL_BTN, opacity: li===0?.3:1}}>↑</button>
                            <button title="Move down" onClick={()=>moveLesson(l,"down")} disabled={li===lessons.length-1} style={{...CTRL_BTN, opacity: li===lessons.length-1?.3:1}}>↓</button>
                            <button title="Edit lesson" onClick={()=>openEditLesson(l)} style={CTRL_BTN}>✏️</button>
                            <button title="Delete lesson" onClick={()=>deleteLesson(l)} style={{...CTRL_BTN, color:"var(--coral)"}}>🗑</button>
                          </div>
                        ))
                      }
                    </div>
                  )})}
                </div>
              )}
              {cmTab === "files" && (<>
                {/* Drop zone */}
                <div
                  onDragOver={e => { e.preventDefault(); setDragOver(true); }}
                  onDragLeave={() => setDragOver(false)}
                  onDrop={e => { e.preventDefault(); setDragOver(false); handleFiles(e.dataTransfer.files); }}
                  onClick={() => fileInputRef.current?.click()}
                  style={{
                    borderRadius:14, border:`2px dashed ${dragOver ? "var(--coral)" : "var(--rule-bold)"}`,
                    background: dragOver ? "rgba(var(--coral-rgb),.06)" : "var(--paper-card)",
                    padding:"32px 22px", textAlign:"center", cursor:"pointer", transition:"border .15s, background .15s",
                    marginBottom:20,
                  }}
                >
                  <div style={{ fontSize:36, marginBottom:8 }}>📂</div>
                  <div style={{ fontWeight:700, fontSize:14, color:"var(--ink)" }}>Drop files here or click to browse</div>
                  <div style={{ fontSize:12, color:"var(--ink-mute)", marginTop:4 }}>PDF, PPT, PPTX, TXT, DOC, DOCX, PNG, JPG, WEBP, GIF · Max 35 MB each</div>
                  {uploading && <div style={{ marginTop:10, fontSize:12, color:"var(--coral)", fontWeight:600 }}>Uploading…</div>}
                  {uploadError && <div style={{ marginTop:8, fontSize:12, color:"var(--coral)" }}>{uploadError}</div>}
                </div>

                {/* File list */}
                {fLoading ? <Spinner/> : !fileList?.length ? (
                  <div style={{ textAlign:"center", padding:"24px 0", color:"var(--ink-mute)", fontSize:13 }}>No files yet. Upload the first one above.</div>
                ) : (
                  <div style={{ display:"flex", flexDirection:"column", gap:8 }}>
                    {fileList.map(f => (
                      <div key={f.id} className="card-flat" style={{ padding:"12px 16px", display:"flex", alignItems:"center", gap:14 }}>
                        <div style={{ fontSize:28, flexShrink:0 }}>{MIME_ICONS[f.file_type] || "📁"}</div>
                        <div style={{ flex:1, minWidth:0 }}>
                          <div style={{ fontWeight:700, fontSize:13, overflow:"hidden", textOverflow:"ellipsis", whiteSpace:"nowrap" }}>{f.title}</div>
                          <div style={{ fontSize:11, color:"var(--ink-mute)", marginTop:2 }}>
                            <span style={{ padding:"1px 7px", borderRadius:999, background:"var(--paper-deep)", fontWeight:700, textTransform:"uppercase", fontSize:10, marginRight:6 }}>{f.file_type}</span>
                            {f.public_uid && (
                              <span title="Copy file UID" onClick={()=>{ navigator.clipboard?.writeText(f.public_uid); }}
                                className="mono" style={{ padding:"1px 7px", borderRadius:999, background:"var(--paper-deep)", fontSize:10, marginRight:6, cursor:"pointer", letterSpacing:".04em" }}>{f.public_uid}</span>
                            )}
                            {fmtSize(f.size_bytes)} · {new Date(f.created_at).toLocaleDateString("en-GB",{day:"numeric",month:"short",year:"numeric"})}
                          </div>
                        </div>
                        {f.file_type === "txt" && (
                          <button onClick={() => openTextEditor(f)} style={{ padding:"5px 12px", borderRadius:6, border:"var(--border-thin)", background:"var(--gold)", color:"white", cursor:"pointer", fontSize:12, fontWeight:600, flexShrink:0 }}>Edit</button>
                        )}
                        <button onClick={() => previewFile(f.id)} style={{ padding:"5px 12px", borderRadius:6, border:"var(--border-thin)", background:"var(--sky)", color:"white", cursor:"pointer", fontSize:12, fontWeight:600, flexShrink:0 }}>Preview</button>
                        <button onClick={() => downloadFile(f.id, f.title)} style={{ padding:"5px 12px", borderRadius:6, border:"var(--border-thin)", background:"var(--moss)", color:"white", cursor:"pointer", fontSize:12, fontWeight:600, flexShrink:0 }}>Download</button>
                        {f.can_delete ? (
                          <button onClick={() => deleteFile(f.id)} style={{ padding:"5px 12px", borderRadius:6, border:"var(--border-thin)", background:"transparent", color:"var(--coral)", cursor:"pointer", fontSize:12, fontWeight:600, flexShrink:0 }}>Delete</button>
                        ) : (
                          <span style={{ fontSize:11, color:"var(--ink-mute)", flexShrink:0 }}>Preserved</span>
                        )}
                      </div>
                    ))}
                  </div>
                )}
              </>)}
              </div>
            </>
          )}
        </div>
      </div>
    </div>
  );
};

Object.assign(window, { ContentManagerHome });
