// SQSEO Settings screen: profile, security (password), workspace, and projects.
// Reads window.LTQ.boot; writes via window.LTQ.api and refreshes the shell in
// place with window.LTQ.reloadBoot. Registered as window.LTQ.Settings.
(function init(){
if(!window.LongtailIQDesignSystem_ae8f12 || !window.LTQ || !window.LTQ.Modal || !window.LTQ.useApi || !window.LTQ.t || !window.LTQ.CountrySelect){return setTimeout(init,30);}
const React = window.React;
const DS = window.LongtailIQDesignSystem_ae8f12;
const { Icon } = DS;
const { api, toast, reloadBoot, Modal, useApi, t, setLocale, getLocale, CountrySelect, LOCALE_OPTIONS } = window.LTQ;

const fieldStyle = { width: "100%", height: 42, padding: "0 12px", borderRadius: 10, background: "var(--surface-card)",
  border: "1px solid var(--border-strong)", fontFamily: "var(--font-sans)", fontSize: 14, color: "var(--text-strong)", outline: "none" };
const labelStyle = { display: "block", fontSize: 12.5, fontWeight: 600, color: "var(--text-body)", marginBottom: 6 };

function Section({ icon, title, sub, children, footer, ...rest }) {
  return (
    <div {...rest} style={{ background: "var(--paper)", border: "1px solid var(--border-subtle)", borderRadius: "var(--r-xl)", boxShadow: "var(--shadow-xs)", overflow: "hidden" }}>
      <div style={{ padding: "18px 20px" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: sub ? 3 : 14 }}>
          {icon && <span style={{ width: 30, height: 30, borderRadius: 9, display: "grid", placeItems: "center", background: "var(--ink-50)", color: "var(--text-muted)", flex: "none" }}><Icon name={icon} size={16} /></span>}
          <h3 style={{ fontSize: 15.5, fontWeight: 700, color: "var(--text-strong)" }}>{title}</h3>
        </div>
        {sub && <p style={{ fontSize: 13, color: "var(--text-muted)", margin: "0 0 14px", lineHeight: 1.5 }}>{sub}</p>}
        {children}
      </div>
      {footer && <div style={{ padding: "12px 20px", borderTop: "1px solid var(--border-subtle)", background: "var(--ink-50)", display: "flex", justifyContent: "flex-end", gap: 8 }}>{footer}</div>}
    </div>
  );
}

function PrimaryBtn({ children, onClick, busy, disabled, icon }) {
  return (
    <button onClick={onClick} disabled={busy || disabled} style={{ height: 38, padding: "0 16px", borderRadius: 9, border: "none",
      background: "var(--ink-900)", color: "#fff", cursor: disabled ? "default" : "pointer", opacity: disabled ? 0.55 : 1,
      fontFamily: "var(--font-sans)", fontSize: 13, fontWeight: 600, display: "inline-flex", alignItems: "center", gap: 7 }}>
      {busy ? <span className="lt-spin" style={{ width: 14, height: 14, border: "2px solid rgba(255,255,255,0.4)", borderTopColor: "#fff", borderRadius: 999 }} /> : icon ? <Icon name={icon} size={15} /> : null}
      {children}
    </button>
  );
}
function GhostBtn({ children, onClick, danger }) {
  return (
    <button onClick={onClick} style={{ height: 34, padding: "0 12px", borderRadius: 8, border: "1px solid var(--border-subtle)",
      background: "var(--paper)", cursor: "pointer", fontFamily: "var(--font-sans)", fontSize: 12.5, fontWeight: 600,
      color: danger ? "var(--viz-red)" : "var(--text-body)", display: "inline-flex", alignItems: "center", gap: 6 }}>{children}</button>
  );
}

function Toggle({ on, onChange, label }) {
  // Game-feel (phase 4): the thumb squashes while held (scaleX var(--squash-x))
  // and travels on the LOCKED --ease-switch bezier — squash is additive, the
  // travel curve is never re-derived. data-gf-skip: the runtime must not scale
  // the whole switch on release, the thumb is the physical part.
  const [held, setHeld] = React.useState(false);
  const release = () => setHeld(false);
  return (
    <button onClick={() => onChange(!on)} role="switch" aria-checked={on} aria-label={label} data-gf-skip="1"
      onPointerDown={() => setHeld(true)} onPointerUp={release} onPointerCancel={release} onPointerLeave={release}
      onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") setHeld(true); }} onKeyUp={release}
      style={{ width: 42, height: 24, borderRadius: 999, border: "none", cursor: "pointer", padding: 2, flex: "none",
        background: on ? "var(--accent-500)" : "var(--ink-200)", transition: "background 250ms var(--ease-switch)" }}>
      <span style={{ display: "block", width: 20, height: 20, borderRadius: 999, background: "#fff", boxShadow: "var(--shadow-sm)",
        transformOrigin: on ? "right center" : "left center",
        transform: `translateX(${on ? 18 : 0}px)${held ? " scaleX(1.15) scaleY(0.88)" : ""}`,
        transition: "transform 250ms var(--ease-switch)" }} />
    </button>
  );
}

// ---- Profile ----
function ProfileCard({ account }) {
  const [name, setName] = React.useState(account.name || "");
  const [alerts, setAlerts] = React.useState(!!account.email_alerts);
  const [news, setNews] = React.useState(account.newsletter !== false);
  const [busy, setBusy] = React.useState(false);
  // The real account record arrives after mount (boot only carries name/email;
  // the email preferences start as placeholders) — re-sync or the toggles show
  // the wrong state and Save lights up dirty on its own.
  React.useEffect(() => { setName(account.name || ""); setAlerts(!!account.email_alerts); setNews(account.newsletter !== false); }, [account]);
  const dirty = name.trim() !== (account.name || "") || alerts !== !!account.email_alerts || news !== (account.newsletter !== false);
  const save = async () => {
    setBusy(true);
    const r = await api.patch("/api/account", { name: name.trim(), email_alerts: alerts, newsletter: news });
    setBusy(false);
    if (r.ok) { await reloadBoot(); toast(t("settings.profile_saved"), { tone: "success" }); }
    else toast(r.error || t("wizard.toast.save_failed"), { tone: "error" });
  };
  return (
    <Section icon="user" title={t("settings.profile")} footer={<PrimaryBtn onClick={save} busy={busy} disabled={!dirty} icon="check">{t("settings.save_changes")}</PrimaryBtn>}>
      <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
        <div>
          <label style={labelStyle}>{t("settings.name")}</label>
          <input value={name} onChange={(e)=>setName(e.target.value)} placeholder={t("settings.name_ph")} style={fieldStyle} />
        </div>
        <div>
          <label style={labelStyle}>{t("settings.email")}</label>
          <input value={account.email || ""} readOnly style={{ ...fieldStyle, color: "var(--text-faint)", background: "var(--ink-50)" }} />
        </div>
        <div>
          <label style={labelStyle}>{t("settings.profile.language")}</label>
          <select data-testid="locale-select" value={getLocale()} onChange={(e) => setLocale(e.target.value)}
            style={{ ...fieldStyle, cursor: "pointer" }}>
            {LOCALE_OPTIONS.map((o) => <option key={o.id} value={o.id}>{o.name}</option>)}
          </select>
          <div style={{ fontSize: 12, color: "var(--text-faint)", marginTop: 6 }}>{t("settings.profile.language_hint")}</div>
        </div>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }}>
          <div>
            <div style={{ fontSize: 13.5, fontWeight: 600, color: "var(--text-strong)" }}>{t("settings.email_alerts")}</div>
            <div style={{ fontSize: 12.5, color: "var(--text-muted)" }}>{t("settings.email_alerts_sub")}</div>
          </div>
          <Toggle on={alerts} onChange={setAlerts} label={t("settings.email_alerts")} />
        </div>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }}>
          <div>
            <div style={{ fontSize: 13.5, fontWeight: 600, color: "var(--text-strong)" }}>{t("settings.newsletter")}</div>
            <div style={{ fontSize: 12.5, color: "var(--text-muted)" }}>{t("settings.newsletter_sub")}</div>
          </div>
          <Toggle on={news} onChange={setNews} label={t("settings.newsletter")} />
        </div>
      </div>
    </Section>
  );
}

// ---- Security ----
function SecurityCard() {
  const [cur, setCur] = React.useState("");
  const [next, setNext] = React.useState("");
  const [confirm, setConfirm] = React.useState("");
  const [busy, setBusy] = React.useState(false);
  const valid = cur && next.length >= 8 && next === confirm;
  const submit = async () => {
    if (!valid) { if (next !== confirm) toast(t("settings.pw_mismatch"), { tone: "error" }); return; }
    setBusy(true);
    const r = await api.post("/api/account/password", { current: cur, next });
    setBusy(false);
    if (r.ok) { setCur(""); setNext(""); setConfirm(""); toast(t("settings.pw_updated"), { tone: "success" }); }
    else toast(r.error || t("settings.pw_failed"), { tone: "error" });
  };
  return (
    <Section icon="lock" title={t("settings.password")} sub={t("settings.password_sub")}
      footer={<PrimaryBtn onClick={submit} busy={busy} disabled={!valid} icon="shield-check">{t("settings.update_password")}</PrimaryBtn>}>
      <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
        <div><label style={labelStyle}>{t("settings.current_pw")}</label><input type="password" value={cur} onChange={(e)=>setCur(e.target.value)} autoComplete="current-password" style={fieldStyle} /></div>
        <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}>
          <div><label style={labelStyle}>{t("settings.new_pw")}</label><input type="password" value={next} onChange={(e)=>setNext(e.target.value)} autoComplete="new-password" style={fieldStyle} /></div>
          <div><label style={labelStyle}>{t("settings.confirm_pw")}</label><input type="password" value={confirm} onChange={(e)=>setConfirm(e.target.value)} autoComplete="new-password" style={fieldStyle} /></div>
        </div>
        {next && next.length < 8 && <div style={{ fontSize: 12, color: "var(--viz-amber)" }}>{t("settings.pw_min")}</div>}
        {next.length >= 8 && confirm && next !== confirm && <div style={{ fontSize: 12, color: "var(--viz-amber)" }}>{t("settings.pw_mismatch")}</div>}
      </div>
    </Section>
  );
}

// ---- Workspace ----
function WorkspaceCard() {
  const boot = window.LTQ.boot || {};
  const ws = boot.workspace || {};
  const [name, setName] = React.useState(ws.name || "");
  const [busy, setBusy] = React.useState(false);
  const dirty = name.trim() && name.trim() !== (ws.name || "");
  const save = async () => {
    setBusy(true);
    const r = await api.patch("/api/workspace", { name: name.trim() });
    setBusy(false);
    if (r.ok) { await reloadBoot(); toast(t("settings.ws_renamed"), { tone: "success" }); }
    else toast(r.error || t("saved.rename_failed"), { tone: "error" });
  };
  const planName = (ws.plan || "free").replace(/^./, (c) => c.toUpperCase());
  return (
    <Section icon="layers" title={t("settings.workspace")} footer={<PrimaryBtn onClick={save} busy={busy} disabled={!dirty} icon="check">{t("research.save")}</PrimaryBtn>}>
      <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
        <div><label style={labelStyle}>{t("settings.ws_name")}</label><input value={name} onChange={(e)=>setName(e.target.value)} style={fieldStyle} /></div>
        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }}>
          <div>
            <div style={{ fontSize: 13.5, fontWeight: 600, color: "var(--text-strong)" }}>{t("settings.plan")}</div>
            <div style={{ fontSize: 12.5, color: "var(--text-muted)" }}>{t("settings.plan_on", { plan: planName })}</div>
          </div>
          <span style={{ display: "inline-flex", alignItems: "center", gap: 6, height: 26, padding: "0 11px", borderRadius: 999,
            background: ws.plan === "free" ? "var(--ink-100)" : "var(--ink-900)", color: ws.plan === "free" ? "var(--text-body)" : "#fff", fontSize: 12, fontWeight: 700 }}>
            {ws.plan !== "free" && <Icon name="zap" size={12} />}{planName}
          </span>
        </div>
      </div>
    </Section>
  );
}

// ---- Plan: Reddit citations add-on ($5/mo) ----
// Deep-linked from the sidebar upsell (window.LTQ.settingsAnchor === "plan-reddit"):
// on mount it scrolls the card into view and flashes a brief emerald ring so the
// user lands exactly on the upgrade offer.
function PlanCard() {
  const [status, setStatus] = React.useState(null);
  const [busy, setBusy] = React.useState(false);
  React.useEffect(() => {
    fetch("/api/billing/status", { credentials: "same-origin" })
      .then((r) => (r.ok ? r.json() : null)).then(setStatus).catch(() => {});
  }, []);
  const active = !!(status && status.active);
  // Checkout / portal both return { url } to redirect to. A 503 means Stripe
  // isn't configured yet → fall back to the pricing page (waitlist).
  const go = async (path) => {
    setBusy(true);
    try {
      const r = await fetch(path, { method: "POST", credentials: "same-origin" });
      const d = await r.json().catch(() => ({}));
      if (r.ok && d.url) { window.location.href = d.url; return; }
      if (r.status === 503) window.open("/pricing", "_blank", "noopener");
      else toast(d.message || t("wizard.toast.save_failed"), { tone: "error" });
    } catch { window.open("/pricing", "_blank", "noopener"); }
    setBusy(false);
  };
  React.useEffect(() => {
    if (window.LTQ.settingsAnchor !== "plan-reddit") return;
    window.LTQ.settingsAnchor = null;
    const id = setTimeout(() => {
      const el = document.getElementById("plan-reddit");
      if (!el) return;
      el.scrollIntoView({ behavior: "smooth", block: "center" });
      el.style.transition = "box-shadow .4s var(--ease-out)";
      el.style.boxShadow = "0 0 0 3px var(--accent-500)";
      setTimeout(() => { el.style.boxShadow = "var(--shadow-xs)"; }, 1500);
    }, 160);
    return () => clearTimeout(id);
  }, []);
  const footer = active
    ? <PrimaryBtn onClick={() => go("/api/billing/portal")} busy={busy} icon="settings">{t("reddit.plan.manage")}</PrimaryBtn>
    : <PrimaryBtn onClick={() => go("/api/billing/checkout")} busy={busy} icon="zap">{t("reddit.plan.cta")}</PrimaryBtn>;
  return (
    <Section id="plan-reddit" icon="message-square" title={t("reddit.plan.title")} sub={t("reddit.plan.sub")} footer={footer}>
      <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
        <div style={{ display: "flex", alignItems: "baseline", gap: 8 }}>
          <span style={{ fontFamily: "var(--font-mono)", fontSize: 28, fontWeight: 700, letterSpacing: "-0.02em", color: "var(--text-strong)" }}>{t("reddit.plan.price")}</span>
          <span style={{ fontSize: 13, color: "var(--text-muted)" }}>{t("reddit.plan.per")}</span>
          {active && <span style={{ marginLeft: "auto", display: "inline-flex", alignItems: "center", gap: 5, padding: "3px 10px", borderRadius: 999, background: "var(--accent-50,#ecfdf5)", color: "var(--accent-700,#0a7150)", fontSize: 11.5, fontWeight: 700 }}><Icon name="check" size={12} />{t("reddit.plan.active")}</span>}
        </div>
        <ul style={{ display: "flex", flexDirection: "column", gap: 10, listStyle: "none", margin: 0, padding: 0 }}>
          {["reddit.plan.feat_1", "reddit.plan.feat_2", "reddit.plan.feat_3"].map((k) => (
            <li key={k} style={{ display: "flex", alignItems: "flex-start", gap: 9, fontSize: 13.5, color: "var(--text-body)", lineHeight: 1.5 }}>
              <Icon name="check" size={15} style={{ color: "var(--accent-500)", marginTop: 2, flex: "none" }} />
              <span>{t(k)}</span>
            </li>
          ))}
        </ul>
      </div>
    </Section>
  );
}

// ---- Projects ----
function ProjectsCard() {
  const boot = window.LTQ.boot || {};
  const projects = boot.projects || [];
  const NewProjectModal = window.LTQ.NewProjectModal;
  const [adding, setAdding] = React.useState(false);
  const [confirmDel, setConfirmDel] = React.useState(null); // project or null
  const [busy, setBusy] = React.useState("");

  const makePrimary = async (p) => {
    setBusy(p.id);
    const r = await api.patch("/api/projects/" + p.id, { is_primary: true });
    setBusy("");
    if (r.ok) { await reloadBoot(); toast(t("settings.now_primary", { name: p.name })); }
    else toast(r.error || t("settings.update_failed"), { tone: "error" });
  };
  const doDelete = async () => {
    const p = confirmDel; setConfirmDel(null); setBusy(p.id);
    const r = await api.del("/api/projects/" + p.id);
    setBusy("");
    if (r.ok) { await reloadBoot(); toast(t("settings.project_deleted"), { tone: "success" }); }
    else toast(r.error || t("brief.delete_failed"), { tone: "error" });
  };

  // Company country lives in the ACTIVE project's onboarding blob (same field
  // the wizard writes); it steers research gl for that project.
  const active = boot.project || null;
  const country = (active && active.onboarding && active.onboarding.country) || "";
  const saveCountry = async (code) => {
    const r = await api.patch("/api/onboarding/wizard", { country: code });
    if (r.ok) { await reloadBoot(); toast(t("settings.projects.country") + " ✓", { tone: "success" }); }
    else toast(r.error || t("settings.update_failed"), { tone: "error" });
  };

  return (
    <Section icon="folder" title={t("chrome.projects")} sub={t("settings.projects_sub")}
      footer={<GhostBtn onClick={() => setAdding(true)}><Icon name="plus" size={14} /> {t("chrome.new_project")}</GhostBtn>}>
      {active && (
        <div style={{ marginBottom: 14 }}>
          <label style={labelStyle}>{t("settings.projects.country")} <span style={{ color: "var(--text-faint)", fontWeight: 500 }}>· {active.name}</span></label>
          <CountrySelect value={country} onChange={saveCountry} placeholder={t("wizard.business.country_ph")} />
          <div style={{ fontSize: 12, color: "var(--text-faint)", marginTop: 6 }}>{t("settings.projects.country_hint")}</div>
        </div>
      )}
      <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
        {projects.map((p) => (
          <div key={p.id} style={{ display: "flex", alignItems: "center", gap: 11, padding: "10px 12px", borderRadius: 10, border: "1px solid var(--border-subtle)" }}>
            <span style={{ width: 30, height: 30, flex: "none", borderRadius: 8, display: "grid", placeItems: "center", background: "var(--ink-50)", color: "var(--text-muted)" }}><Icon name="globe" size={15} /></span>
            <div style={{ flex: 1, minWidth: 0 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
                <span style={{ fontSize: 13.5, fontWeight: 600, color: "var(--text-strong)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{p.name}</span>
                {p.is_primary && <span style={{ fontSize: 10, fontWeight: 700, color: "var(--accent-600)", background: "var(--accent-100)", borderRadius: 5, padding: "1px 6px" }}>{t("settings.primary")}</span>}
              </div>
              {(p.site_url || p.label) && <div style={{ fontSize: 12, color: "var(--text-faint)" }}>{[p.site_url, p.label].filter(Boolean).join(" · ")}</div>}
            </div>
            {!p.is_primary && <GhostBtn onClick={() => makePrimary(p)}>{busy === p.id ? "…" : t("settings.make_primary")}</GhostBtn>}
            {projects.length > 1 && (
              <button onClick={() => setConfirmDel(p)} aria-label={t("settings.delete_project")} style={{ width: 32, height: 32, display: "grid", placeItems: "center", border: "1px solid var(--border-subtle)", background: "var(--paper)", borderRadius: 8, cursor: "pointer", color: "var(--text-faint)" }}>
                <Icon name="trash-2" size={15} />
              </button>
            )}
          </div>
        ))}
        {projects.length === 0 && <div style={{ fontSize: 13, color: "var(--text-faint)", padding: "8px 2px" }}>{t("settings.no_projects")}</div>}
      </div>
      {adding && NewProjectModal && <NewProjectModal onClose={() => setAdding(false)} />}
      {confirmDel && (
        <Modal title={t("settings.delete_q", { name: confirmDel.name })} sub={t("settings.delete_sub")} onClose={() => setConfirmDel(null)} width={440}>
          <div style={{ display: "flex", justifyContent: "flex-end", gap: 8 }}>
            <GhostBtn onClick={() => setConfirmDel(null)}>{t("chrome.cancel")}</GhostBtn>
            <button onClick={doDelete} style={{ height: 38, padding: "0 16px", borderRadius: 9, border: "none", background: "var(--viz-red)", color: "#fff", cursor: "pointer", fontFamily: "var(--font-sans)", fontSize: 13, fontWeight: 600, display: "inline-flex", alignItems: "center", gap: 7 }}>
              <Icon name="trash-2" size={15} /> {t("settings.delete_project")}
            </button>
          </div>
        </Modal>
      )}
    </Section>
  );
}

// ---- Site profile (Phase 16 — Site Intelligence) ----
// Shows what the scanner learned from the active project's site (identity,
// offerings, keywords, tech, languages) and lets the user (re-)scan. The scan
// runs server-side; while pending/running we poll GET /api/site-intel/scan.
function ProfileChip({ children, tone }) {
  return (
    <span style={{ display: "inline-flex", alignItems: "center", padding: "3px 9px", borderRadius: 999, border: "1px solid var(--border-subtle)", background: tone === "accent" ? "var(--accent-100)" : "var(--ink-50)", fontSize: 12, fontWeight: 600, color: tone === "accent" ? "var(--accent-600)" : "var(--text-body)", whiteSpace: "nowrap" }}>
      {children}
    </span>
  );
}

function ProfileRow({ label, children }) {
  return (
    <div style={{ display: "flex", alignItems: "baseline", gap: 10 }}>
      <span style={{ flex: "none", width: 110, fontSize: 12, fontWeight: 600, color: "var(--text-faint)", textTransform: "uppercase", letterSpacing: "0.04em" }}>{label}</span>
      <span style={{ fontSize: 13, color: "var(--text-body)", display: "flex", flexWrap: "wrap", gap: 6, alignItems: "center", minWidth: 0 }}>{children}</span>
    </div>
  );
}

function SiteProfileCard() {
  const boot = window.LTQ.boot || {};
  const project = boot.project || null;
  const prof = useApi("/api/site-intel/profile");
  const [scan, setScan] = React.useState(null);
  const [busy, setBusy] = React.useState(false);

  const refreshScan = React.useCallback(async () => {
    const r = await api.get("/api/site-intel/scan");
    if (r.ok) setScan(r.data.scan);
    return r.ok ? r.data.scan : null;
  }, []);
  React.useEffect(() => { refreshScan(); }, [refreshScan]);

  const activeScan = scan && (scan.status === "pending" || scan.status === "running");
  React.useEffect(() => {
    if (!activeScan) return;
    const id = setInterval(async () => {
      const s = await refreshScan();
      if (s && s.status === "complete") { prof.reload(); toast(t("siteintel.scan_done"), { tone: "success" }); }
      else if (s && s.status === "failed") toast(s.error || t("siteintel.scan_failed"), { tone: "error" });
    }, 2500);
    return () => clearInterval(id);
  }, [activeScan]);

  const start = async () => {
    setBusy(true);
    const r = await api.post("/api/site-intel/scan");
    setBusy(false);
    if (r.ok) setScan(r.data.scan);
    else toast(r.error || t("siteintel.scan_failed"), { tone: "error" });
  };

  const data = prof.data || null;
  const p = data && data.profile;
  const noUrl = project && !project.site_url;
  const scannedWhen = data ? new Date(data.scanned_at).toLocaleDateString() : "";

  return (
    <Section icon="radar" title={t("siteintel.title")} sub={t("siteintel.sub")}
      footer={<PrimaryBtn onClick={start} busy={busy || !!activeScan} disabled={noUrl} icon="radar">{activeScan ? t("siteintel.scanning") : p ? t("siteintel.rescan") : t("siteintel.scan_now")}</PrimaryBtn>}>
      {activeScan && (
        <div style={{ display: "flex", alignItems: "center", gap: 9, marginBottom: p ? 14 : 0 }}>
          <span className="lt-shimmer" style={{ width: 14, height: 14, borderRadius: 999, background: "var(--accent-100)", flex: "none" }} />
          <span style={{ fontSize: 13, color: "var(--text-muted)" }}>{t("siteintel.progress", { done: scan.pages_crawled || 0, total: scan.pages_total || "…" })}</span>
        </div>
      )}
      {!p && !activeScan && (
        <div style={{ fontSize: 13, color: "var(--text-faint)", padding: "4px 2px" }}>
          {noUrl ? t("siteintel.no_url") : t("siteintel.empty")}
        </div>
      )}
      {p && (
        <div style={{ display: "flex", flexDirection: "column", gap: 11 }}>
          <ProfileRow label={t("siteintel.identity")}>
            <span style={{ fontWeight: 600, color: "var(--text-strong)" }}>{(p.identity && p.identity.name && p.identity.name.value) || p.domain}</span>
            <ProfileChip>{p.siteType}</ProfileChip>
            {p.tech && p.tech.platform && <ProfileChip>{p.tech.platform.value}</ProfileChip>}
            {(p.locale && p.locale.languages || []).map((l) => <ProfileChip key={l}>{l}</ProfileChip>)}
          </ProfileRow>
          {p.identity && p.identity.tagline && (
            <ProfileRow label={t("siteintel.tagline")}><span style={{ color: "var(--text-muted)" }}>{p.identity.tagline.value}</span></ProfileRow>
          )}
          {p.offerings && p.offerings.products.length > 0 && (
            <ProfileRow label={t("siteintel.products", { n: p.offerings.products.length })}>
              {p.offerings.products.slice(0, 6).map((x) => <ProfileChip key={x.name}>{x.name}</ProfileChip>)}
              {p.offerings.products.length > 6 && <span style={{ fontSize: 12, color: "var(--text-faint)" }}>+{p.offerings.products.length - 6}</span>}
            </ProfileRow>
          )}
          {p.offerings && (p.offerings.categories.length > 0 || p.offerings.services.length > 0) && (
            <ProfileRow label={t("siteintel.categories")}>
              {p.offerings.categories.concat(p.offerings.services).slice(0, 8).map((x) => <ProfileChip key={x.value}>{x.value}</ProfileChip>)}
            </ProfileRow>
          )}
          {p.keywords && p.keywords.length > 0 && (
            <ProfileRow label={t("siteintel.keywords")}>
              {p.keywords.slice(0, 10).map((k) => <ProfileChip key={k.keyword}>{k.keyword}</ProfileChip>)}
            </ProfileRow>
          )}
          {p.seedTopics && p.seedTopics.length > 0 && (
            <ProfileRow label={t("siteintel.seeds")}>
              {p.seedTopics.map((s) => <ProfileChip key={s} tone="accent">{s}</ProfileChip>)}
            </ProfileRow>
          )}
          <div style={{ fontSize: 12, color: "var(--text-faint)" }}>{t("siteintel.scanned", { when: scannedWhen, pages: data.pages_crawled })}</div>
        </div>
      )}
    </Section>
  );
}

// ---- Search Console (Phase 7) ----
function SearchConsoleCard() {
  const { loading, data, reload } = useApi("/api/gsc/status");
  const st = data || {};
  const [syncing, setSyncing] = React.useState(false);
  const [sites, setSites] = React.useState(null);
  const last = st.lastSync;
  const sync = async () => {
    setSyncing(true);
    const r = await api.post("/api/sync");
    setSyncing(false);
    const d = r.data || {};
    if (r.ok && d.ok) { await reloadBoot(); reload(); toast(t("wizard.search.synced_toast", { pages: d.pages, queries: d.queries, source: t("wizard.search.live") }), { tone: "success" }); }
    else if (d.error === "not_connected") toast(t("wizard.search.title"), { tone: "warning" });
    else toast(r.error || t("wizard.search.sync_failed"), { tone: "error" });
  };
  const disconnect = async () => { await api.post("/api/gsc/disconnect"); setSites(null); reload(); toast(t("settings.gsc_disconnected")); };
  const loadSites = async () => { const r = await api.get("/api/gsc/sites"); setSites(r.ok ? r.data.sites || [] : []); };
  const setProp = async (p) => { const r = await api.post("/api/gsc/property", { property: p }); if (r.ok) { reload(); toast(t("settings.property_set"), { tone: "success" }); } };

  const dot = (color) => <span style={{ width: 8, height: 8, borderRadius: 999, background: color, flex: "none" }} />;
  return (
    <Section data-tour="gsc-card" icon="line-chart" title={t("wizard.steps.search")}
      sub={t("settings.gsc_sub")}
      footer={<PrimaryBtn onClick={sync} busy={syncing} icon="refresh-cw">{t("perf.sync_now")}</PrimaryBtn>}>
      {loading ? <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>{[0, 1].map((i) => <span key={i} className="lt-shimmer" style={{ height: 16, borderRadius: 6, background: "var(--ink-100)" }} />)}</div> : (
        <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
          {/* connection status row */}
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 9 }}>
              {st.connected ? dot("var(--viz-green)") : st.configured ? dot("var(--viz-amber)") : dot("var(--text-faint)")}
              <div>
                <div style={{ fontSize: 13.5, fontWeight: 600, color: "var(--text-strong)" }}>
                  {st.connected ? t("dash.hint_connected") : st.configured ? t("wizard.review.not_connected") : t("settings.sample_mode")}
                </div>
                <div style={{ fontSize: 12.5, color: "var(--text-muted)" }}>
                  {st.connected ? (st.property || t("settings.property_auto"))
                    : st.configured ? t("settings.gsc_connect_note")
                    : t("settings.gsc_disabled_note")}
                </div>
              </div>
            </div>
            {st.configured && (st.connected
              ? <GhostBtn onClick={disconnect} danger><Icon name="unlink" size={14} /> {t("settings.disconnect")}</GhostBtn>
              : <a href="/oauth/google/authorize" style={{ textDecoration: "none" }}><span style={{ height: 38, padding: "0 16px", borderRadius: 9, background: "var(--ink-900)", color: "#fff", fontFamily: "var(--font-sans)", fontSize: 13, fontWeight: 600, display: "inline-flex", alignItems: "center", gap: 7 }}><Icon name="plug" size={15} /> {t("wizard.search.connect_btn")}</span></a>)}
          </div>

          {st.needs_reconnect ? (
            <div style={{ display: "flex", alignItems: "center", gap: 10, padding: "10px 12px", background: "var(--red-50)", borderRadius: 10 }}>
              <Icon name="alert-circle" size={15} style={{ color: "var(--viz-red)", flex: "none" }} />
              <span style={{ flex: 1, fontSize: 12.5, color: "var(--text-body)" }}>{st.lastError || t("settings.gsc_revoked")} <b style={{ color: "var(--text-strong)" }}>{t("settings.autosync_paused")}</b></span>
              <a href="/oauth/google/authorize" style={{ textDecoration: "none", flex: "none" }}><span style={{ height: 32, padding: "0 13px", borderRadius: 8, background: "var(--ink-900)", color: "#fff", fontFamily: "var(--font-sans)", fontSize: 12.5, fontWeight: 600, display: "inline-flex", alignItems: "center", gap: 6 }}><Icon name="plug" size={13} /> {t("dash.reconnect")}</span></a>
            </div>
          ) : st.lastError ? (
            <div style={{ display: "flex", alignItems: "flex-start", gap: 8, padding: "10px 12px", background: "var(--red-50)", borderRadius: 10, fontSize: 12.5, color: "var(--viz-red)" }}><Icon name="alert-circle" size={14} style={{ marginTop: 1, flex: "none" }} />{st.lastError}</div>
          ) : null}

          {/* property picker (when connected) */}
          {st.connected && (
            <div>
              {sites === null
                ? <button onClick={loadSites} style={{ height: 32, padding: "0 11px", borderRadius: 8, border: "1px solid var(--border-subtle)", background: "var(--paper)", cursor: "pointer", fontSize: 12.5, fontWeight: 600, color: "var(--text-body)", display: "inline-flex", alignItems: "center", gap: 6 }}><Icon name="list" size={13} /> {t("settings.choose_property")}</button>
                : sites.length === 0 ? <div style={{ fontSize: 12.5, color: "var(--text-faint)" }}>{t("settings.no_properties")}</div>
                : <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>{sites.map((s) => (
                    <button key={s.siteUrl} onClick={() => setProp(s.siteUrl)} style={{ height: 30, padding: "0 10px", borderRadius: 999, border: "1px solid " + (s.siteUrl === st.property ? "var(--ink-900)" : "var(--border-subtle)"), background: s.siteUrl === st.property ? "var(--ink-900)" : "var(--paper)", color: s.siteUrl === st.property ? "#fff" : "var(--text-body)", fontSize: 12, fontWeight: 600, cursor: "pointer", fontFamily: "var(--font-mono)" }}>{s.siteUrl}</button>
                  ))}</div>}
            </div>
          )}

          {/* last sync */}
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "10px 12px", background: "var(--ink-50)", borderRadius: 10 }}>
            <span style={{ fontSize: 12.5, color: "var(--text-muted)", display: "inline-flex", alignItems: "center", gap: 7 }}>
              <Icon name="history" size={14} />
              {last ? <>{t("wizard.search.last_sync")} <b style={{ color: "var(--text-strong)", fontWeight: 600 }}>{t(last.source === "gsc" ? "wizard.search.live" : "wizard.search.sample")}</b> · {t("wizard.search.pages_queries", { pages: last.pages, queries: last.queries })}</> : t("settings.not_synced")}
            </span>
            {last && last.source === "sample" && <span style={{ fontSize: 11, fontWeight: 700, color: "var(--viz-amber)", background: "var(--amber-50)", borderRadius: 6, padding: "2px 7px" }}>{t("settings.sample_data_badge")}</span>}
          </div>
          {/* sync history (Phase 2): the pipeline is visible, not a black box */}
          {Array.isArray(st.runs) && st.runs.length > 1 && (
            <div style={{ border: "1px solid var(--border-subtle)", borderRadius: 10, overflow: "hidden" }}>
              <div style={{ padding: "8px 12px", fontSize: 11, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.05em", color: "var(--text-faint)", background: "var(--ink-50)", borderBottom: "1px solid var(--border-subtle)" }}>{t("settings.recent_syncs")}</div>
              {st.runs.slice(0, 5).map((r, i) => (
                <div key={i} style={{ display: "flex", alignItems: "center", gap: 9, padding: "7px 12px", borderBottom: i < Math.min(st.runs.length, 5) - 1 ? "1px solid var(--border-subtle)" : "none", fontSize: 12 }}>
                  <span style={{ width: 7, height: 7, borderRadius: 999, flex: "none", background: r.status === "ok" ? "var(--viz-green)" : r.status === "running" ? "var(--viz-amber)" : "var(--viz-red)" }} />
                  <span style={{ fontWeight: 600, color: "var(--text-body)", width: 52 }}>{t(r.source === "gsc" ? "wizard.search.live" : "wizard.search.sample")}</span>
                  <span className="lt-num" style={{ fontFamily: "var(--font-mono)", color: "var(--text-muted)", flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>
                    {r.status === "error" ? (r.error || t("settings.failed")) : t("wizard.search.pages_queries", { pages: r.pages, queries: r.queries })}
                  </span>
                  <span style={{ color: "var(--text-faint)", fontFamily: "var(--font-mono)", fontSize: 11, flex: "none" }}>{r.finished_at ? window.LTQ.fmtDate(r.finished_at, { month: "short", day: "numeric", hour: "2-digit", minute: "2-digit" }) : "…"}</span>
                </div>
              ))}
            </div>
          )}
          {st.readerEmail && st.configured && !st.connected && (
            <div style={{ fontSize: 12, color: "var(--text-faint)" }}>{t("settings.reader_email")} <span style={{ fontFamily: "var(--font-mono)" }}>{st.readerEmail}</span></div>
          )}
        </div>
      )}
    </Section>
  );
}

function EmailCard() {
  const { loading, data, reload } = useApi("/api/email/status");
  const st = data || {};
  const [digest, setDigest] = React.useState(false);
  const [mode, setMode] = React.useState("test");
  const [testTo, setTestTo] = React.useState("");
  const [busy, setBusy] = React.useState("");
  React.useEffect(() => { if (data) { setDigest(!!data.weekly_digest); setMode(data.mode || "test"); setTestTo(data.test_to || ""); } }, [data]);

  const toggleDigest = async (on) => {
    setDigest(on);
    const r = await api.post("/api/email/digest", { enabled: on });
    if (r.ok) toast(t(on ? "settings.digest_on" : "settings.digest_off"), { tone: "success" });
    else { setDigest(!on); toast(r.error || t("settings.update_failed"), { tone: "error" }); }
  };
  const sendTest = async () => {
    setBusy("test");
    const r = await api.post("/api/email/test");
    setBusy("");
    toast(r.data && r.data.ok ? t("settings.test_sent") : (r.data && r.data.error) || r.error || t("settings.send_failed"), { tone: r.data && r.data.ok ? "success" : "warning" });
  };
  const sendDigestNow = async () => {
    setBusy("digest");
    const r = await api.post("/api/email/digest/send");
    setBusy("");
    toast(r.data && r.data.ok && r.data.sent ? t("settings.digest_sent") : (r.data && r.data.error) || t("settings.digest_no_data"), { tone: r.data && r.data.sent ? "success" : "warning" });
  };
  const saveAdmin = async () => {
    setBusy("admin");
    const r = await api.post("/api/email/settings", { mode, test_to: testTo.trim() });
    setBusy("");
    if (r.ok) { reload(); toast(t("settings.email_saved"), { tone: "success" }); }
    else toast(r.error || t("wizard.toast.save_failed"), { tone: "error" });
  };
  const preview = (kind) => window.open("/api/email/preview?type=" + kind, "_blank", "noopener");

  const dot = (color) => <span style={{ width: 8, height: 8, borderRadius: 999, background: color, flex: "none" }} />;
  const statusText = !st.configured
    ? t("settings.email_disabled")
    : st.mode === "live"
      ? t("settings.email_live")
      : t("settings.email_test", { to: st.test_to || t("settings.unset_inbox") });

  return (
    <Section icon="mail" title={t("settings.email_title")}
      sub={t("settings.email_sub")}
      footer={<PrimaryBtn onClick={sendTest} busy={busy === "test"} disabled={!st.configured} icon="send">{t("settings.send_test")}</PrimaryBtn>}>
      {loading ? <div style={{ display: "flex", flexDirection: "column", gap: 10 }}>{[0, 1].map((i) => <span key={i} className="lt-shimmer" style={{ height: 16, borderRadius: 6, background: "var(--ink-100)" }} />)}</div> : (
        <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
          {/* status */}
          <div style={{ display: "flex", alignItems: "center", gap: 9 }}>
            {st.configured ? (st.mode === "live" ? dot("var(--viz-green)") : dot("var(--viz-amber)")) : dot("var(--text-faint)")}
            <div>
              <div style={{ fontSize: 13.5, fontWeight: 600, color: "var(--text-strong)" }}>{st.configured ? (st.mode === "live" ? t("wizard.search.live") : t("settings.test_mode")) : t("settings.not_configured")}</div>
              <div style={{ fontSize: 12.5, color: "var(--text-muted)" }}>{statusText}</div>
            </div>
          </div>

          {/* weekly digest toggle (workspace owner) */}
          {st.owns_workspace && (
            <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, padding: "12px 14px", border: "1px solid var(--border-subtle)", borderRadius: 10 }}>
              <div style={{ minWidth: 0 }}>
                <div style={{ fontSize: 13.5, fontWeight: 600, color: "var(--text-strong)" }}>{t("settings.weekly_digest")}</div>
                <div style={{ fontSize: 12.5, color: "var(--text-muted)" }}>{t("settings.weekly_digest_sub")}</div>
              </div>
              <div style={{ display: "flex", alignItems: "center", gap: 10, flex: "none" }}>
                {digest && <button onClick={sendDigestNow} disabled={busy === "digest"} style={{ height: 30, padding: "0 11px", borderRadius: 8, border: "1px solid var(--border-subtle)", background: "var(--paper)", cursor: "pointer", fontSize: 12, fontWeight: 600, color: "var(--text-body)" }}>{busy === "digest" ? t("settings.sending") : t("settings.send_now")}</button>}
                <Toggle on={digest} onChange={toggleDigest} label={t("settings.weekly_digest")} />
              </div>
            </div>
          )}

          {/* preview links */}
          <div style={{ display: "flex", alignItems: "center", gap: 8, flexWrap: "wrap", fontSize: 12.5, color: "var(--text-muted)" }}>
            <span style={{ fontWeight: 600 }}>{t("settings.preview")}</span>
            {[["digest", t("settings.preview_digest")], ["report", t("settings.preview_report")], ["reset", t("settings.preview_reset")]].map(([pt, l]) => (
              <button key={pt} onClick={() => preview(pt)} style={{ height: 28, padding: "0 10px", borderRadius: 999, border: "1px solid var(--border-subtle)", background: "var(--paper)", cursor: "pointer", fontSize: 12, fontWeight: 600, color: "var(--text-body)", display: "inline-flex", alignItems: "center", gap: 5 }}><Icon name="eye" size={12} />{l}</button>
            ))}
          </div>

          {/* admin: delivery mode + test recipient */}
          {st.is_admin && (
            <div style={{ display: "flex", flexDirection: "column", gap: 10, padding: "14px", border: "1px dashed var(--border-strong)", borderRadius: 10 }}>
              <div style={{ fontSize: 11.5, fontWeight: 700, textTransform: "uppercase", letterSpacing: "0.05em", color: "var(--text-faint)" }}>{t("settings.admin_delivery")}</div>
              <div style={{ display: "flex", gap: 8, alignItems: "center" }}>
                <div style={{ display: "inline-flex", border: "1px solid var(--border-subtle)", borderRadius: 9, overflow: "hidden" }}>
                  {["test", "live"].map((m) => <button key={m} onClick={() => setMode(m)} style={{ border: "none", padding: "7px 13px", fontSize: 12.5, fontWeight: 600, cursor: "pointer", background: mode === m ? "var(--ink-900)" : "var(--paper)", color: mode === m ? "#fff" : "var(--text-muted)" }}>{m === "test" ? t("settings.test") : t("wizard.search.live")}</button>)}
                </div>
                <input value={testTo} onChange={(e) => setTestTo(e.target.value)} placeholder={t("settings.test_inbox_ph")} style={{ ...fieldStyle, height: 36, flex: 1 }} />
                <PrimaryBtn onClick={saveAdmin} busy={busy === "admin"} icon="check">{t("research.save")}</PrimaryBtn>
              </div>
              {mode === "live" && <div style={{ fontSize: 12, color: "var(--viz-amber)", display: "inline-flex", alignItems: "center", gap: 6 }}><Icon name="alert-triangle" size={13} /> {t("settings.live_warning")}</div>}
            </div>
          )}
        </div>
      )}
    </Section>
  );
}

function ApiKeysCard() {
  const { loading, data, error, reload } = useApi("/api/keys");
  const [open, setOpen] = React.useState(false);
  const [name, setName] = React.useState("");
  const [busy, setBusy] = React.useState(false);
  const [secret, setSecret] = React.useState(""); // the new key's plaintext (shown once)
  const keys = (data && data.keys) || [];
  const ownerOnly = !loading && !data && /owner/i.test(error || "");

  const create = async () => {
    setBusy(true);
    const r = await api.post("/api/keys", { name: name.trim() });
    setBusy(false);
    if (r.ok) { setSecret(r.data.secret); reload(); }
    else toast(r.error || t("settings.key_create_failed"), { tone: "error" });
  };
  const closeModal = () => { setOpen(false); setName(""); setSecret(""); };
  const [confirmRevoke, setConfirmRevoke] = React.useState(null); // key pending revoke confirmation
  const revoke = async (k) => {
    setConfirmRevoke(null);
    const r = await api.del("/api/keys/" + k.id);
    if (r.ok) { toast(t("settings.key_revoked"), { tone: "success" }); reload(); }
    else toast(r.error || t("settings.revoke_failed"), { tone: "error" });
  };
  const copy = async (val) => { try { await navigator.clipboard.writeText(val); toast(t("settings.copied"), { tone: "success" }); } catch { toast(t("settings.copy_manual"), { tone: "warning" }); } };

  return (
    <Section icon="key-round" title={t("settings.api_access")}
      sub={t("settings.api_sub")}
      footer={!ownerOnly ? <PrimaryBtn onClick={() => setOpen(true)} icon="plus">{t("settings.create_key")}</PrimaryBtn> : null}>
      {loading ? <span className="lt-shimmer" style={{ display: "block", height: 40, borderRadius: 8, background: "var(--ink-100)" }} /> : ownerOnly ? (
        <div style={{ fontSize: 13, color: "var(--text-muted)" }}>{t("settings.keys_owner_only")}</div>
      ) : (
        <div style={{ display: "flex", flexDirection: "column", gap: 12 }}>
          {keys.length === 0 ? (
            <div style={{ fontSize: 13, color: "var(--text-faint)" }}>{t("settings.no_keys")} <a href="/v1/openapi.json" target="_blank" rel="noopener" style={{ color: "var(--accent-600)", fontWeight: 600 }}>/v1 API</a></div>
          ) : keys.map((k) => (
            <div key={k.id} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, padding: "11px 13px", border: "1px solid var(--border-subtle)", borderRadius: 10 }}>
              <div style={{ minWidth: 0 }}>
                <div style={{ fontSize: 13.5, fontWeight: 600, color: "var(--text-strong)" }}>{k.name}</div>
                <div style={{ fontFamily: "var(--font-mono)", fontSize: 11.5, color: "var(--text-faint)" }}>{k.prefix}…{k.last4} · {t("settings.created")} {(k.created_at || "").slice(0, 10)}{k.last_used_at ? " · " + t("settings.last_used") + " " + k.last_used_at.slice(0, 10) : " · " + t("settings.never_used")}</div>
              </div>
              <GhostBtn onClick={() => setConfirmRevoke(k)} danger><Icon name="trash-2" size={14} /> {t("settings.revoke")}</GhostBtn>
            </div>
          ))}
          <div style={{ fontSize: 12, color: "var(--text-faint)" }}>{t("settings.docs")} <a href="/v1/openapi.json" target="_blank" rel="noopener" style={{ color: "var(--accent-600)", fontWeight: 600 }}>{t("settings.openapi_spec")}</a> · {t("settings.quota", { n: (data && data.quota) || 1000 })}</div>
        </div>
      )}

      {confirmRevoke && (
        <Modal title={t("settings.delete_q", { name: confirmRevoke.name })} sub={t("settings.revoke_sub")} onClose={() => setConfirmRevoke(null)} width={440}>
          <div style={{ display: "flex", justifyContent: "flex-end", gap: 8 }}>
            <GhostBtn onClick={() => setConfirmRevoke(null)}>{t("chrome.cancel")}</GhostBtn>
            <button onClick={() => revoke(confirmRevoke)} style={{ height: 38, padding: "0 16px", borderRadius: 9, border: "none", background: "var(--viz-red)", color: "#fff", cursor: "pointer", fontFamily: "var(--font-sans)", fontSize: 13, fontWeight: 600, display: "inline-flex", alignItems: "center", gap: 7 }}>
              <Icon name="trash-2" size={15} /> {t("settings.revoke")}
            </button>
          </div>
        </Modal>
      )}

      {open && (
        <Modal title={secret ? t("settings.save_key") : t("settings.create_api_key")} sub={secret ? t("settings.copy_now") : t("settings.key_name_hint")} onClose={closeModal} width={460}>
          {secret ? (
            <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 8, padding: "11px 13px", borderRadius: 10, background: "var(--ink-50)", border: "1px solid var(--border-subtle)" }}>
                <code style={{ flex: 1, fontFamily: "var(--font-mono)", fontSize: 12.5, color: "var(--text-strong)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{secret}</code>
                <button onClick={() => copy(secret)} style={{ flex: "none", height: 30, padding: "0 11px", borderRadius: 8, border: "1px solid var(--border-subtle)", background: "var(--paper)", cursor: "pointer", fontSize: 12.5, fontWeight: 600, color: "var(--text-body)" }}><Icon name="copy" size={13} /> {t("settings.copy")}</button>
              </div>
              <div style={{ fontSize: 12.5, color: "var(--text-muted)", display: "flex", alignItems: "flex-start", gap: 7 }}><Icon name="shield-check" size={14} style={{ color: "var(--viz-green)", marginTop: 1, flex: "none" }} />{t("settings.bearer_note_1")} <code style={{ fontFamily: "var(--font-mono)" }}>Authorization: Bearer …</code>. {t("settings.bearer_note_2")}</div>
              <div style={{ display: "flex", justifyContent: "flex-end" }}><PrimaryBtn onClick={closeModal} icon="check">{t("settings.done")}</PrimaryBtn></div>
            </div>
          ) : (
            <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
              <input value={name} onChange={(e) => setName(e.target.value)} maxLength={60} placeholder={t("settings.key_name_ph")} style={fieldStyle} autoFocus />
              <div style={{ display: "flex", justifyContent: "flex-end", gap: 8 }}>
                <GhostBtn onClick={closeModal}>{t("chrome.cancel")}</GhostBtn>
                <PrimaryBtn onClick={create} busy={busy} icon="key-round">{t("settings.create_key")}</PrimaryBtn>
              </div>
            </div>
          )}
        </Modal>
      )}
    </Section>
  );
}

function Settings() {
  const boot = window.LTQ.boot || {};
  const [account, setAccount] = React.useState(() => ({ name: (boot.user && boot.user.name) || "", email: (boot.user && boot.user.email) || "", email_alerts: true }));
  const signOut = async () => { await api.post("/api/auth/logout"); window.location.href = "/app/login.html"; };

  React.useEffect(() => {
    let alive = true;
    api.get("/api/account").then((r) => { if (alive && r.ok) setAccount(r.data.account); });
    return () => { alive = false; };
  }, []);

  return (
    <div style={{ maxWidth: 760, margin: "0 auto", display: "flex", flexDirection: "column", gap: 16 }}>
      <div>
        <div className="lt-eyebrow" style={{ marginBottom: 6 }}>{t("palette.account")}</div>
        <h2 style={{ fontSize: 24, fontWeight: 700, letterSpacing: "-0.02em", color: "var(--text-strong)" }}>{t("nav.settings")}</h2>
        <p style={{ fontSize: 14, color: "var(--text-muted)", marginTop: 5 }}>{t("settings.page_sub")}</p>
      </div>
      <ProfileCard account={account} />
      <SecurityCard />
      <WorkspaceCard />
      <PlanCard />
      <SearchConsoleCard />
      <SiteProfileCard />
      <EmailCard />
      <ApiKeysCard />
      <ProjectsCard />
      <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center", padding: "4px 2px 8px" }}>
        <span style={{ fontSize: 12.5, color: "var(--text-faint)" }}>{t("settings.signed_in_as", { email: account.email })}</span>
        <GhostBtn onClick={signOut} danger><Icon name="log-out" size={14} /> {t("chrome.sign_out")}</GhostBtn>
      </div>
    </div>
  );
}
window.LTQ.Settings = Settings;
})();
