// ── Control-plane: tenant detail (tabbed full page) ───────────────────────

const TENANT_TABS = [
  { key: "overview", label: "Overview" },
  { key: "branding", label: "Branding" },
  { key: "contact", label: "Contact" },
  { key: "admin", label: "Admin login" },
  { key: "features", label: "Feature flags" },
  { key: "students", label: "Students" },
  { key: "activity", label: "Activity" },
];

function TenantDetail({ tenantId, tab, onTab, flagsCatalog, onBack, onChanged, onResume }) {
  const notify = useToast();
  const [data, setData] = React.useState(null);
  const [err, setErr] = React.useState(null);
  const [provJob, setProvJob] = React.useState(null); // job id while provisioning
  const [deleting, setDeleting] = React.useState(false);
  const active = tab || "overview";

  const load = () => cpApi.getTenant(tenantId).then(setData).catch((e) => setErr(e.message));
  React.useEffect(() => { load(); }, [tenantId]);
  // Keep the page live while a job runs (progress survives closing the modal).
  const liveJob = !!(data && data.jobs[0] && ["queued", "running"].includes(data.jobs[0].status));
  React.useEffect(() => {
    if (!liveJob) return;
    const id = setTimeout(load, 3000);
    return () => clearTimeout(id);
  }, [data]);

  if (err) return <div className="panel pad" style={{ color: "var(--danger)" }}>{err}</div>;
  if (!data) return <div style={{ color: "var(--ink-mute)" }}><Spinner /> Loading…</div>;

  const t = data.tenant;

  const afterChange = (resp) => {
    if (resp && resp.tenant) setData({ ...data, tenant: resp.tenant });
    else load();
    onChanged && onChanged();
  };

  const toggleFlag = async (key, next) => {
    try {
      afterChange(await cpApi.setFlag(t.id, key, next)); // atomic per flag
      notify(`${key} ${next ? "enabled" : "disabled"} for ${t.slug}`);
    } catch (e) { notify(e.message); }
  };

  const setStatus = async (status) => {
    try {
      afterChange(await cpApi.setStatus(t.id, status));
      notify(`${t.slug} ${status === "suspended" ? "suspended" : "resumed"}`);
    } catch (e) { notify(e.message); }
  };

  const provision = async () => {
    try {
      const { jobId } = await cpApi.provisionTenant(t.id);
      setProvJob(jobId);
    } catch (e) { notify(e.message); }
  };

  const afterDelete = (r) => {
    setDeleting(false);
    if (r && r.removed) { onBack(); onChanged && onChanged(); } else afterChange(r);
  };

  return (
    <div>
      <button className="btn sm ghost" onClick={onBack} style={{ marginBottom: 14, paddingLeft: 0 }}>← All tenants</button>

      <div className="panel pad" style={{ marginBottom: 16 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12, flexWrap: "wrap" }}>
          <h1 style={{ fontSize: 22 }}>{t.name}</h1>
          <StatusPill status={t.status} />
          {t.isTenantZero && <Pill tone="accent">tenant zero</Pill>}
          {t.status !== "deleted" && <Pill tone={t.live ? "ok" : "mute"}>{t.live ? "live" : "offline"}</Pill>}
          {t.deletion && <Pill tone="danger">{{ offline: "offline · data kept", deleting_data: "deleting data…", data_deleted: "data deleted" }[t.deletion.stage]}</Pill>}
          <div style={{ flex: 1 }} />
          {!t.isTenantZero && t.status === "active" &&
            <button className="btn sm danger" onClick={() => setStatus("suspended")}>Suspend</button>}
          {t.status === "suspended" &&
            <button className="btn sm" onClick={() => setStatus("active")}>Resume</button>}
          {!t.isTenantZero && (t.status === "draft" || t.status === "error") &&
            <button className="btn sm primary" onClick={provision}>
              {t.status === "error" ? "Retry provisioning" : "Create ecosystem"}
            </button>}
          {!t.isTenantZero && t.status !== "deleted" &&
            <button className="btn sm ghost" style={{ color: "var(--danger)" }} onClick={() => setDeleting(true)}>Delete</button>}
        </div>
        <div style={{ color: "var(--ink-mute)", fontSize: 13, marginTop: 6 }} className="mono">
          {t.primarySubdomain} · db <code>{t.dbName}</code> · plan {t.plan}
          {t.atlas && t.atlas.projectId && (t.atlas.mock
            ? " · mock (no Atlas)"
            : ` · Atlas ${t.atlas.projectId}${t.atlas.clusterName ? ` / ${t.atlas.clusterName}` : ""} · ${t.atlas.tier || "M0"}`)}
          {t.customDomain ? ` · custom ${t.customDomain} (${t.customDomainStatus})` : ""}
        </div>
        {(t.status === "active" || t.status === "suspended") && !t.isTenantZero && (
          <div style={{ color: "var(--ink-mute)", fontSize: 12, marginTop: 4 }}>
            Suspend, feature-flag and domain changes take effect within about a minute on every server.
          </div>
        )}
      </div>

      {t.status === "draft" && (
        <div className="panel pad" style={{ marginBottom: 16, background: "var(--accent-soft)", border: "1px solid var(--accent)" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
            <strong style={{ flex: 1 }}>Details incomplete — not provisioned.</strong>
            {onResume && <button className="btn sm primary" onClick={() => onResume(t)}>Continue setup</button>}
          </div>
          <div style={{ fontSize: 13, color: "var(--ink-soft)", marginTop: 4 }}>
            Add branding, contacts and the admin email in the tabs below, then click
            <strong> Create ecosystem</strong> to build the database.
          </div>
        </div>
      )}
      {t.status === "error" && (
        <div className="panel pad" style={{ marginBottom: 16, background: "var(--danger-soft)", border: "1px solid var(--danger)" }}>
          <strong>Provisioning failed.</strong>
          <div style={{ fontSize: 13, marginTop: 4 }}>See the last job under Overview, then click <strong>Retry provisioning</strong>.</div>
        </div>
      )}

      <TabBar tabs={TENANT_TABS} active={active} onChange={onTab} />

      <div style={{ marginTop: 20 }}>
        {active === "overview" && <OverviewTab data={data} onChanged={afterChange} />}
        {active === "branding" && (
          <BrandingTab tenant={t} onSave={async (branding, logoAssetId) => {
            try { afterChange(await cpApi.setBranding(t.id, branding, logoAssetId)); notify("Branding saved"); }
            catch (e) { notify(e.message); }
          }} />
        )}
        {active === "contact" && <ContactTab tenantId={t.id} onChanged={onChanged} />}
        {active === "admin" && <AdminLoginTab tenant={t} onChanged={afterChange} />}
        {active === "features" && <FeatureFlagsTab tenant={t} catalog={flagsCatalog} onToggle={toggleFlag} />}
        {active === "students" && <StudentsTab tenant={t} />}
        {active === "activity" && <ActivityTab tenantId={t.id} />}
      </div>

      {deleting && <DeleteTenantModal tenant={t} onClose={() => setDeleting(false)} onDone={afterDelete} />}
      {provJob && (
        <ProvisioningView jobId={provJob} tenant={{ id: t.id, name: t.name }}
          onClose={() => { setProvJob(null); load(); onChanged && onChanged(); }}
          onDone={() => { load(); onChanged && onChanged(); }} />
      )}
    </div>
  );
}

// ── Domain panel ──────────────────────────────────────────────────────────
function DomainPanel({ tenant, onChanged }) {
  const notify = useToast();
  const DROPLET_IP = "68.183.86.119"; // dev droplet — swap for the prod edge in Milestone E
  const [editing, setEditing] = React.useState(false);
  const [val, setVal] = React.useState(tenant.customDomain || "");
  const [busy, setBusy] = React.useState(false);

  const save = async (domain) => {
    setBusy(true);
    try { onChanged(await cpApi.setDomain(tenant.id, domain)); notify(domain ? "Custom domain set" : "Custom domain removed"); setEditing(false); }
    catch (e) { notify(e.message); }
    setBusy(false);
  };

  const primary = tenant.customDomain && tenant.customDomainStatus === "active"
    ? tenant.customDomain : tenant.primarySubdomain;

  return (
    <div className="panel pad">
      <SectionHeader title="Domain" desc="Where this school's LMS is reached." />
      <div style={{ display: "flex", alignItems: "baseline", gap: 10, marginBottom: 6 }}>
        <span className="mono" style={{ fontSize: 15, fontWeight: 600 }}>https://{primary}</span>
        <Pill tone="mute">subdomain — always works</Pill>
      </div>
      <div style={{ fontSize: 13, color: "var(--ink-mute)", marginBottom: 16 }}>
        <span className="mono">{tenant.primarySubdomain}</span> · live once provisioned (changes reach every server within about a minute)
      </div>

      {!tenant.customDomain && !editing && (
        <button className="btn sm" onClick={() => setEditing(true)}>+ Add a custom domain</button>
      )}

      {tenant.customDomain && !editing && (
        <div className="panel" style={{ padding: 14, background: "var(--panel-2)" }}>
          <div style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <span className="mono" style={{ fontWeight: 600 }}>{tenant.customDomain}</span>
            <Pill tone={tenant.customDomainStatus === "active" ? "ok" : "warn"}>{tenant.customDomainStatus.replace("_", " ")}</Pill>
            <div style={{ flex: 1 }} />
            <button className="btn sm ghost" onClick={() => setEditing(true)}>Edit</button>
            <button className="btn sm ghost" style={{ color: "var(--danger)" }} disabled={busy} onClick={() => save("")}>Remove</button>
          </div>
          {tenant.customDomainStatus !== "active" && (
            <div style={{ marginTop: 12, fontSize: 13, color: "var(--ink-soft)" }}>
              At the domain's DNS provider, add:
              <pre style={{ background: "var(--panel)", border: "1px solid var(--line)", borderRadius: 7, padding: "8px 10px", margin: "6px 0 0", fontSize: 12 }}>
{`A   ${tenant.customDomain}   →   ${DROPLET_IP}`}</pre>
              <div style={{ marginTop: 6, color: "var(--ink-mute)" }}>DNS verification + TLS certificate issuance is automated in Milestone E.</div>
            </div>
          )}
        </div>
      )}

      {editing && (
        <div style={{ display: "flex", gap: 8, alignItems: "flex-start", maxWidth: 460 }}>
          <input value={val} onChange={(e) => setVal(e.target.value.trim().toLowerCase())}
            placeholder="lms.northwood.com"
            style={{ flex: 1, padding: "9px 11px", borderRadius: 7, border: "1px solid var(--line-strong)", background: "var(--panel)" }} />
          <button className="btn sm primary" disabled={busy || !val} onClick={() => save(val)}>Save</button>
          <button className="btn sm" onClick={() => { setEditing(false); setVal(tenant.customDomain || ""); }}>Cancel</button>
        </div>
      )}
    </div>
  );
}

// ── Atlas / database panel ────────────────────────────────────────────────
// ── Delete confirmation (tenants list, tenant header, Database panel) ─────
// Type the slug, get a one-time code (printed in the server terminal for
// now), enter it. mode "delete": takes the tenant offline — a never-provisioned
// draft is removed outright — and can also destroy its database. mode "atlas":
// only the database of an already-deleted tenant.
function DeleteTenantModal({ tenant, mode = "delete", onClose, onDone }) {
  const notify = useToast();
  const a = tenant.atlas || {};
  const hasDb = !!(a.projectId && !a.projectDeletedAt);
  const isDraft = tenant.status === "draft" && !a.projectId;
  const dbWhat = a.mock ? `the mock database ${tenant.dbName}` : `Atlas cluster ${a.clusterName || "—"} and project ${a.projectId}`;
  const [slug, setSlug] = React.useState("");
  const [alsoDb, setAlsoDb] = React.useState(false);
  const [sent, setSent] = React.useState(null); // { ticket, email }
  const [code, setCode] = React.useState("");
  const [busy, setBusy] = React.useState(false);
  const [err, setErr] = React.useState(null);
  const [jobId, setJobId] = React.useState(null);
  const slugOk = slug.trim() === tenant.slug;
  const destroysData = mode === "atlas" || (alsoDb && hasDb);

  const sendCode = async () => {
    setBusy(true); setErr(null);
    try { setSent(await cpApi.deleteCode(tenant.id, mode === "atlas" ? "atlas" : "delete")); setCode(""); }
    catch (e) { setErr(e.message); }
    setBusy(false);
  };

  const confirm = async () => {
    setBusy(true); setErr(null);
    try {
      const body = { confirmSlug: slug.trim(), otpTicket: sent.ticket, otpCode: code };
      const r = mode === "atlas"
        ? await cpApi.deleteAtlasProject(tenant.id, body)
        : await cpApi.deleteTenant(tenant.id, { ...body, alsoDeleteAtlas: alsoDb && hasDb });
      notify(mode === "atlas" ? `Deleting ${tenant.slug}'s database…` : r.removed ? `${tenant.slug} removed` : `${tenant.slug} deleted`);
      if (r.jobId) setJobId(r.jobId); else onDone(r);
    } catch (e) { setErr(e.message); }
    setBusy(false);
  };

  if (jobId) {
    return <ProvisioningView jobId={jobId} tenant={{ id: tenant.id, name: tenant.name }} stay
      title={`Deleting ${tenant.slug}'s database`} onClose={() => onDone({})} onDone={() => {}} />;
  }

  return (
    <Modal title={mode === "atlas" ? `Delete ${tenant.name}'s database?` : `Delete ${tenant.name}?`} onClose={busy ? () => {} : onClose}>
      <div className="panel pad" style={{ background: "var(--danger-soft)", border: "1px solid var(--danger)", fontSize: 13, lineHeight: 1.6, marginBottom: 16 }}>
        {mode === "atlas"
          ? <>Permanently destroys {dbWhat} — all of this school's data. This can't be undone.</>
          : isDraft
            ? <>It was never provisioned — the record and its contacts are removed.</>
            : <><strong>{tenant.primarySubdomain}</strong> goes offline immediately. The record is kept as "deleted".</>}
      </div>

      {mode === "delete" && hasDb && (
        <label style={{ display: "flex", gap: 10, alignItems: "flex-start", fontSize: 14, marginBottom: 16, cursor: "pointer" }}>
          <input type="checkbox" checked={alsoDb} onChange={(e) => setAlsoDb(e.target.checked)} style={{ marginTop: 3 }} />
          <span>Also permanently delete {dbWhat}
            <div style={{ fontSize: 12, color: alsoDb ? "var(--danger)" : "var(--ink-mute)" }}>
              {alsoDb ? "All of the school's data is destroyed. This can't be undone." : "Unticked: the data is kept — you can delete it later from the Database panel."}
            </div>
          </span>
        </label>
      )}

      <Field label={<>Type <code>{tenant.slug}</code> to confirm</>}>
        <input value={slug} onChange={(e) => setSlug(e.target.value)} autoFocus placeholder={tenant.slug} disabled={!!sent} />
      </Field>

      {!sent ? (
        <button className="btn" disabled={!slugOk || busy} onClick={sendCode}>{busy ? <Spinner /> : "Send confirmation code"}</button>
      ) : (
        <Field label="Confirmation code"
          hint={<>Printed in the server terminal (for {sent.email}). Expires in 10 minutes. <a href="#" onClick={(e) => { e.preventDefault(); sendCode(); }}>Send a new code</a></>}>
          <input value={code} inputMode="numeric" maxLength={6} autoFocus placeholder="6-digit code" className="mono"
            onChange={(e) => setCode(e.target.value.replace(/\D/g, "").slice(0, 6))}
            onKeyDown={(e) => { if (e.key === "Enter" && code.length === 6) confirm(); }} />
        </Field>
      )}

      {err && <div style={{ color: "var(--danger)", fontSize: 13, marginTop: 10 }}>{err}</div>}

      <div className="wizard-foot">
        <button className="btn ghost" disabled={busy} onClick={onClose}>Cancel</button>
        <div className="spacer" />
        <button className="btn danger" disabled={busy || !slugOk || !sent || code.length !== 6} onClick={confirm}>
          {busy && sent ? <Spinner /> : destroysData ? "Delete permanently" : isDraft ? "Remove tenant" : "Delete tenant"}
        </button>
      </div>
    </Modal>
  );
}
window.DeleteTenantModal = DeleteTenantModal;

// Shows where the tenant's data lives and checks the connection. Never shows
// the connection string or any password (the API never returns them).
function AtlasPanel({ tenant, onChanged }) {
  const notify = useToast();
  const [health, setHealth] = React.useState(null);
  const [checking, setChecking] = React.useState(false);
  const [deleting, setDeleting] = React.useState(false);
  const a = tenant.atlas || {};

  const check = async () => {
    setChecking(true);
    try { setHealth(await cpApi.tenantHealth(tenant.id)); }
    catch (e) { setHealth({ ok: false, error: e.message }); }
    setChecking(false);
  };

  const rows = [
    ["Mode", a.projectId ? (a.mock ? "mock (local database, no Atlas)" : "Atlas") : "not provisioned"],
    ["Project", a.projectId ? <span className="mono">{a.projectId}</span> : "—"],
    ["Cluster", a.clusterName ? <span className="mono">{a.clusterName}</span> : "—"],
    ["Tier", a.tier || "—"],
    ["Database", <span className="mono">{tenant.dbName}</span>],
    ["DB user", a.dbUser ? <span className="mono">{a.dbUser}</span> : "—"],
  ];
  if (a.projectDeletedAt) rows.push(["Atlas project deleted", new Date(a.projectDeletedAt).toLocaleString()]);

  return (
    <div className="panel pad">
      <SectionHeader title="Database" desc="Where this school's data lives. Credentials are stored encrypted and never shown." />
      <dl style={{ display: "grid", gridTemplateColumns: "170px 1fr", rowGap: 10, margin: "0 0 14px", fontSize: 14 }}>
        {rows.map(([k, v], i) => (
          <React.Fragment key={i}>
            <dt style={{ color: "var(--ink-mute)" }}>{k}</dt>
            <dd style={{ margin: 0 }}>{v}</dd>
          </React.Fragment>
        ))}
      </dl>
      <div style={{ display: "flex", gap: 10, alignItems: "center", flexWrap: "wrap" }}>
        <button className="btn sm" disabled={checking} onClick={check}>{checking ? <Spinner /> : "Check connection"}</button>
        {health && (health.ok
          ? <Pill tone="ok">reachable · {health.latencyMs} ms</Pill>
          : <span style={{ color: "var(--danger)", fontSize: 13 }}>Unreachable{health.error ? `: ${health.error}` : ""}</span>)}
        <div style={{ flex: 1 }} />
        {tenant.deletion && tenant.deletion.stage === "offline" && (
          <button className="btn sm ghost" style={{ color: "var(--danger)" }} onClick={() => setDeleting(true)}>
            {a.mock ? "Drop mock database" : "Delete Atlas project"}
          </button>
        )}
      </div>
      {deleting && (
        <DeleteTenantModal tenant={tenant} mode="atlas" onClose={() => setDeleting(false)}
          onDone={() => { setDeleting(false); onChanged && onChanged(); }} />
      )}
    </div>
  );
}

// ── Overview ──────────────────────────────────────────────────────────────
function OverviewTab({ data, onChanged }) {
  const t = data.tenant;
  const rows = [
    ["Slug", <span className="mono">{t.slug}</span>],
    ["Admin email", t.adminEmail || <span style={{ color: "var(--ink-mute)" }}>not set</span>],
    ["Contacts", t.contactsCount != null ? t.contactsCount : "—"],
    ["Schema version", <span className="mono">{t.schemaVersion || "—"}</span>],
    ["Provisioned", t.provisionedAt ? new Date(t.provisionedAt).toLocaleString() : "not yet"],
    ["Created", new Date(t.createdAt).toLocaleString()],
    ["Feature overrides", <span className="mono">{Object.keys(t.featureFlags).length ? JSON.stringify(t.featureFlags) : "none (all on)"}</span>],
  ];
  return (
    <div style={{ display: "grid", gap: 18 }}>
      <DomainPanel tenant={t} onChanged={onChanged} />
      {!t.isTenantZero && <AtlasPanel tenant={t} onChanged={onChanged} />}
      <div className="panel pad">
        <SectionHeader title="Details" />
        <dl style={{ display: "grid", gridTemplateColumns: "170px 1fr", rowGap: 10, margin: 0, fontSize: 14 }}>
          {rows.map(([k, v], i) => (
            <React.Fragment key={i}>
              <dt style={{ color: "var(--ink-mute)" }}>{k}</dt>
              <dd style={{ margin: 0 }}>{v}</dd>
            </React.Fragment>
          ))}
        </dl>
        {t.notes && <p style={{ marginTop: 14, color: "var(--ink-soft)" }}>{t.notes}</p>}
      </div>

      <JobsPanel jobs={data.jobs} />
    </div>
  );
}

// ── Provisioning jobs: latest as a live checklist, older ones compact ─────
function JobsPanel({ jobs }) {
  const [latest, ...older] = jobs;
  const tone = (s) => (s === "done" ? "ok" : s === "error" ? "danger" : "accent");
  return (
    <div className="panel pad">
      <h3 style={{ fontSize: 15, marginBottom: 12 }}>Provisioning jobs</h3>
      {!latest ? <p style={{ color: "var(--ink-mute)", margin: 0 }}>No jobs yet.</p> : (
        <>
          <div style={{ display: "flex", gap: 10, alignItems: "center", marginBottom: 12, fontSize: 13 }}>
            <strong className="mono">#{latest.id} {latest.kind}</strong>
            <Pill tone={tone(latest.status)}>{latest.status}</Pill>
            <span style={{ color: "var(--ink-mute)" }}>started {timeAgo(latest.created_at)}</span>
          </div>
          <JobSteps job={latest} />
        </>
      )}
      {older.length > 0 && (
        <details style={{ marginTop: 16 }}>
          <summary style={{ cursor: "pointer", fontSize: 13, color: "var(--ink-soft)" }}>Earlier jobs ({older.length})</summary>
          <div className="tablewrap">
            <table className="grid" style={{ marginTop: 8 }}>
              <thead><tr><th>Job</th><th>Status</th><th>When</th><th>Result</th></tr></thead>
              <tbody>
                {older.map((j) => (
                  <tr key={j.id}>
                    <td className="mono">#{j.id} {j.kind}</td>
                    <td><Pill tone={tone(j.status)}>{j.status}</Pill></td>
                    <td style={{ color: "var(--ink-mute)" }}>{timeAgo(j.created_at)}</td>
                    <td style={{ color: "var(--ink-soft)", fontSize: 13 }}>{j.error || "—"}</td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
        </details>
      )}
    </div>
  );
}

// ── Feature flags ─────────────────────────────────────────────────────────
function FeatureFlagsTab({ tenant, catalog, onToggle }) {
  const groups = {};
  (catalog || []).forEach((f) => { (groups[f.group] = groups[f.group] || []).push(f); });
  return (
    <div>
      <p style={{ color: "var(--ink-mute)", marginTop: 0 }}>
        Every capability is on by default. Turning one off hides it in this tenant's LMS and
        (from Milestone C) blocks its API. Tenant zero is unaffected.
      </p>
      {Object.entries(groups).map(([group, flags]) => (
        <div key={group} className="panel" style={{ marginBottom: 16 }}>
          <h3 style={{ fontSize: 12, textTransform: "uppercase", letterSpacing: "0.05em", color: "var(--ink-mute)", padding: "14px 18px 2px" }}>{group}</h3>
          <div className="tablewrap">
            <table className="grid">
              <tbody>
                {flags.map((f) => {
                  const on = tenant.resolvedFlags[f.key] !== false;
                  return (
                    <tr key={f.key}>
                      <td style={{ width: "45%" }}>
                        <div style={{ fontWeight: 600 }}>{f.label}</div>
                        <div style={{ fontSize: 12, color: "var(--ink-mute)" }}>{f.help}</div>
                      </td>
                      <td className="mono" style={{ color: "var(--ink-mute)", fontSize: 12 }}>{f.key}</td>
                      <td style={{ textAlign: "right" }}>
                        <div style={{ display: "inline-flex", alignItems: "center", gap: 10 }}>
                          <span style={{ fontSize: 12, color: on ? "var(--ok)" : "var(--ink-mute)", fontWeight: 600 }}>{on ? "On" : "Off"}</span>
                          <Toggle on={on} onChange={(next) => onToggle(f.key, next)} />
                        </div>
                      </td>
                    </tr>
                  );
                })}
              </tbody>
            </table>
          </div>
        </div>
      ))}
    </div>
  );
}

// ── Branding ──────────────────────────────────────────────────────────────
function BrandingTab({ tenant, onSave }) {
  const notify = useToast();
  const b = tenant.branding || {};
  const [form, setForm] = React.useState({
    name: b.name || "", supportEmail: b.supportEmail || "",
    mailFrom: b.mailFrom || "", razorpayName: b.razorpayName || "",
    accent: (b.colors && b.colors.coral) || "",
  });
  const [logoUrl, setLogoUrl] = React.useState(tenant.logoUrl || "");
  const [logoAssetId, setLogoAssetId] = React.useState(tenant.logoAssetId || null);
  const [uploading, setUploading] = React.useState(false);
  const set = (k) => (e) => setForm({ ...form, [k]: e.target.value });

  const onFile = async ({ file_name, file_data }) => {
    setUploading(true);
    try {
      const r = await cpApi.uploadLogo(file_name, file_data);
      setLogoAssetId(r.id); setLogoUrl(r.url);
      notify("Logo uploaded — remember to Save");
    } catch (e) { notify(e.message); }
    setUploading(false);
  };
  const clearLogo = () => { setLogoAssetId(null); setLogoUrl(""); notify("Logo cleared — Save to apply"); };

  const submit = (e) => {
    e.preventDefault();
    const branding = {
      name: form.name, supportEmail: form.supportEmail,
      mailFrom: form.mailFrom, razorpayName: form.razorpayName,
    };
    if (!logoAssetId && logoUrl) branding.logoUrl = logoUrl;
    if (form.accent.trim()) branding.colors = { coral: form.accent.trim() };
    onSave(branding, logoAssetId);
  };

  return (
    <form className="panel pad" style={{ maxWidth: 620 }} onSubmit={submit}>
      <p style={{ color: "var(--ink-mute)", marginTop: 0 }}>
        Stored now; applied to the tenant's LMS in Milestone D (server-injected <code>window.__TENANT__</code>).
      </p>

      <SectionHeader title="Logo" desc="Shown in the LMS header, on certificates and emails." />
      <UploadCard
        preview={logoUrl || null}
        busy={uploading}
        hint="PNG, JPG, SVG or WebP · up to 5 MB"
        onFile={onFile}
        onClear={clearLogo}
      />
      {!logoUrl && (
        <div className="checkrow" style={{ marginTop: 12 }}>
          <input type="checkbox" checked readOnly />
          <span>No logo set — the LMS will use the default EpiqMinds logo.</span>
        </div>
      )}

      <SectionHeader title="Identity" />
      <Field label="Display name" hint="Overrides “EpiqMinds” across the LMS." max={120} value={form.name}>
        <input value={form.name} onChange={set("name")} placeholder={tenant.name} maxLength={140} />
      </Field>
      <Field label="Accent colour" hint="Any CSS colour — e.g. oklch(0.72 0.16 35) or #2563eb.">
        <input value={form.accent} onChange={set("accent")} placeholder="#2563eb" />
      </Field>

      <SectionHeader title="Communications" />
      <div className="row2">
        <Field label="Support email"><input value={form.supportEmail} onChange={set("supportEmail")} placeholder="help@school.edu" /></Field>
        <Field label="Razorpay merchant name" hint="On the payment sheet."><input value={form.razorpayName} onChange={set("razorpayName")} /></Field>
      </div>
      <Field label="Email “From”" hint="e.g. Acme School &lt;no-reply@acme.edu&gt;">
        <input value={form.mailFrom} onChange={set("mailFrom")} />
      </Field>

      <div style={{ marginTop: 8 }}>
        <button className="btn primary" type="submit" disabled={uploading}>Save branding</button>
      </div>
    </form>
  );
}

// ── Contact (commit 2 — placeholder wired to API) ─────────────────────────
function ContactTab({ tenantId, onChanged }) {
  const notify = useToast();
  const [list, setList] = React.useState(null);
  const [adding, setAdding] = React.useState(false);

  const load = () => cpApi.getContacts(tenantId).then((d) => setList(d.data || d)).catch((e) => notify(e.message));
  React.useEffect(() => { load(); }, [tenantId]);

  const save = async (person) => {
    try {
      if (person.id) await cpApi.updateContact(tenantId, person.id, person);
      else await cpApi.addContact(tenantId, person);
      setAdding(false); load(); onChanged && onChanged();
      notify("Contact saved");
    } catch (e) { notify(e.message); }
  };
  const del = async (person) => {
    try { await cpApi.deleteContact(tenantId, person.id); load(); onChanged && onChanged(); notify("Contact removed"); }
    catch (e) { notify(e.message); }
  };
  const makePrimary = (person) => save({ ...person, isPrimary: true });

  if (!list) return <div style={{ color: "var(--ink-mute)" }}><Spinner /> Loading…</div>;

  return (
    <div style={{ maxWidth: 620 }}>
      <div style={{ display: "flex", alignItems: "center", marginBottom: 14 }}>
        <div style={{ flex: 1, color: "var(--ink-mute)" }}>Points of contact for this school.</div>
        {!adding && <button className="btn sm primary" onClick={() => setAdding(true)}>+ Add contact</button>}
      </div>
      <div style={{ display: "grid", gap: 12 }}>
        {list.map((p) => (
          <PersonCard key={p.id} person={p} onSave={save} onDelete={del} onMakePrimary={makePrimary} />
        ))}
        {adding && <PersonCard person={{}} onSave={save} onDelete={() => setAdding(false)} />}
        {list.length === 0 && !adding && (
          <EmptyState title="No contacts yet" body='Click "Add contact" to record the school’s point of contact.' />
        )}
      </div>
    </div>
  );
}

// ── Admin login (commit 2 pre-provision; commit 3 live) ───────────────────
function AdminLoginTab({ tenant, onChanged }) {
  const notify = useToast();
  const [info, setInfo] = React.useState(null);
  const [email, setEmail] = React.useState("");
  const [otp, setOtp] = React.useState(null); // one-time password after reset

  const load = () => cpApi.getAdminLogin(tenant.id).then((d) => {
    setInfo(d);
    setEmail(d.adminEmail || (d.superadmin && d.superadmin.email) || "");
  }).catch((e) => notify(e.message));
  React.useEffect(() => { load(); }, [tenant.id]);

  if (!info) return <div style={{ color: "var(--ink-mute)" }}><Spinner /> Loading…</div>;

  const saveEmail = async () => {
    try { await cpApi.updateTenant(tenant.id, { admin_email: email }); notify("Admin email saved"); onChanged && onChanged(); }
    catch (e) { notify(e.message); }
  };
  const reset = async () => {
    try { const r = await cpApi.resetAdminPassword(tenant.id); setOtp(r); notify("Password reset"); }
    catch (e) { notify(e.message); }
  };

  const unreachableLive = info.mode === "live" && !info.reachable;

  return (
    <div className="panel pad" style={{ maxWidth: 560 }}>
      <SectionHeader title="School administrator"
        desc="The superadmin account for this school's LMS." />

      {info.mode === "pre-provision" ? (
        <>
          <Field label="Intended admin email" hint="Used as the school superadmin's email when this tenant is provisioned. Stored on the tenant until then.">
            <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
          </Field>
          <button className="btn primary" onClick={saveEmail} disabled={!email.trim()}>Save admin email</button>
        </>
      ) : unreachableLive ? (
        <div style={{ color: "var(--danger)" }}>Tenant database unreachable — {info.error || "cannot load the admin account"}.</div>
      ) : (
        <>
          <dl style={{ display: "grid", gridTemplateColumns: "150px 1fr", rowGap: 10, margin: "0 0 18px", fontSize: 14 }}>
            <dt style={{ color: "var(--ink-mute)" }}>Email</dt><dd style={{ margin: 0 }}>{info.superadmin.email}</dd>
            <dt style={{ color: "var(--ink-mute)" }}>Status</dt><dd style={{ margin: 0 }}><Pill tone={info.superadmin.status === "active" ? "ok" : "warn"}>{info.superadmin.status}</Pill></dd>
            <dt style={{ color: "var(--ink-mute)" }}>Last login</dt><dd style={{ margin: 0 }}>{info.superadmin.lastLoginAt ? timeAgo(info.superadmin.lastLoginAt) : "never"}</dd>
            <dt style={{ color: "var(--ink-mute)" }}>Must change pw</dt><dd style={{ margin: 0 }}>{info.superadmin.mustChangePassword ? "yes" : "no"}</dd>
          </dl>
          <div style={{ display: "flex", gap: 8 }}>
            <button className="btn" onClick={reset}>Reset password</button>
            <button className="btn" disabled title="Enabled when transactional email lands (Milestone F)">Send credentials</button>
          </div>
        </>
      )}

      {otp && (
        <div style={{ marginTop: 18 }}>
          <SectionHeader title="One-time password" desc="Shown once. The admin must set a new password on next sign-in." />
          <CopyBox value={otp.oneTimePassword} />
        </div>
      )}
    </div>
  );
}

// StudentsTab lives in cp-students.jsx (window.StudentsTab)

// ── Activity ─────────────────────────────────────────────────────────────
function ActivityTab({ tenantId }) {
  const [rows, setRows] = React.useState(null);
  React.useEffect(() => {
    cpApi.audit({ limit: 50, tenantId }).then((d) => setRows(d.data)).catch(() => setRows([]));
  }, [tenantId]);
  if (!rows) return <div style={{ color: "var(--ink-mute)" }}><Spinner /> Loading…</div>;
  if (rows.length === 0) return <EmptyState title="No activity" body="Nothing logged for this tenant yet." />;
  return (
    <div className="panel tablewrap">
      <table className="grid">
        <thead><tr><th>When</th><th>Operator</th><th>Action</th><th>Detail</th></tr></thead>
        <tbody>
          {rows.map((r) => (
            <tr key={r.id}>
              <td style={{ color: "var(--ink-mute)", whiteSpace: "nowrap" }}>{timeAgo(r.created_at)}</td>
              <td style={{ fontSize: 13 }}>{r.actor_email || "—"}</td>
              <td className="mono" style={{ fontSize: 12 }}>{r.action}</td>
              <td className="mono" style={{ fontSize: 11, color: "var(--ink-mute)" }}>
                {r.detail && Object.keys(r.detail).length ? JSON.stringify(r.detail) : "—"}
              </td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

window.TenantDetail = TenantDetail;
