/* app.jsx — shell: topbar, section nav, live editor + preview, gallery, ATS drawer, tweaks */
const { useEffect, useRef, useLayoutEffect, useMemo, useCallback } = React;

const ACCENTS = {
  "Brand Blue": "#1C9BE6",
  "Brand Green": "#34C77F",
  "Navy": "#0A0E39",
  "Violet": "#6D5AE0",
  "Slate": "#475569",
};

const NAV_FIXED = [
  { id: "basics", label: "Personal details", icon: "user" },
  { id: "summary", label: "Summary", icon: "text" },
];
const NAV_META = {
  experience:  { label: "Experience",               icon: "briefcase", countKey: "experience" },
  projects:    { label: "Projects",                 icon: "folder",    countKey: "projects" },
  education:   { label: "Education",                icon: "cap",       countKey: "education" },
  skills:      { label: "Skills",                   icon: "sparkle",   countKey: "skills" },
  certs:       { label: "Certifications",           icon: "award",     countKey: "certs" },
  languages:   { label: "Languages",                icon: "globe",     countKey: "languages" },
  activities:  { label: "Extracurricular Activities",icon: "star",     countKey: "activities" },
  profdegrees: { label: "Professional Credentials", icon: "badge",     countKey: "profdegrees" },
  references:  { label: "References",               icon: "users",     countKey: "references" },
};

const ADDABLE_SECTIONS = [
  { id: "languages",   label: "Languages",                desc: "English, French, Arabic…" },
  { id: "activities",  label: "Extracurricular Activities",desc: "Clubs, sports, volunteering…" },
  { id: "profdegrees", label: "Professional Credentials", desc: "CA, CMA, PMP, CFA…" },
  { id: "references",  label: "References",               desc: "Contacts or 'Available upon request'" },
];
const DEFAULT_SECTION_ORDER = ["experience", "projects", "education", "skills", "certs"];
// keep for ATS lookup
const NAV = [
  ...NAV_FIXED,
  ...DEFAULT_SECTION_ORDER.map(id => ({ id, ...NAV_META[id] })),
];

function RingGauge({ value, size, stroke, color, track, children }) {
  size = size || 30; stroke = stroke || 3.2; color = color || "#1C9BE6"; track = track || "#E3E6EF";
  const r = (size - stroke) / 2, c = 2 * Math.PI * r;
  const off = c * (1 - Math.max(0, Math.min(100, value)) / 100);
  return (
    <div style={{ position: "relative", width: size, height: size }}>
      <svg width={size} height={size} style={{ transform: "rotate(-90deg)" }}>
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={track} strokeWidth={stroke} />
        <circle cx={size/2} cy={size/2} r={r} fill="none" stroke={color} strokeWidth={stroke}
          strokeDasharray={c} strokeDashoffset={off}
          style={{ transition: "stroke-dashoffset .6s cubic-bezier(.2,.8,.2,1)", strokeLinecap: "round" }} />
      </svg>
      <div style={{ position: "absolute", inset: 0, display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center" }}>{children}</div>
    </div>
  );
}

/* Cute robot mascot whose expression reflects the ATS score.
   90+ = happy, 80–89 = neutral, < 80 = sad. */
function ScoreRobot({ score, size }) {
  size = size || 52;
  const happy = score >= 90;
  const sad   = score < 80;
  const main  = sad ? "#8FB8D6" : "#1C9BE6";
  const dark  = sad ? "#6E9CBE" : "#1684C6";
  const ink   = "#0B2C46";
  return (
    <svg width={size} height={size} viewBox="0 0 64 64" style={{ display: "block", overflow: "visible" }}>
      {/* antenna + signal */}
      <line x1="32" y1="9" x2="32" y2="4.5" stroke={dark} strokeWidth="2" strokeLinecap="round" />
      <circle cx="32" cy="3.2" r="2.2" fill={main} />
      {happy && <>
        <path d="M27.5 3 a6 6 0 0 1 1.6 -3.2" fill="none" stroke={main} strokeWidth="1.4" strokeLinecap="round" opacity=".7" />
        <path d="M36.5 3 a6 6 0 0 0 -1.6 -3.2" fill="none" stroke={main} strokeWidth="1.4" strokeLinecap="round" opacity=".7" />
      </>}
      {/* head */}
      <rect x="9" y="9" width="46" height="31" rx="14" fill={main} />
      {/* face screen */}
      <rect x="15" y="15" width="34" height="19" rx="9" fill="#EAF6FF" />
      {/* eyes */}
      {happy
        ? <>
            <path d="M21 23 q3 3 6 0" fill="none" stroke={ink} strokeWidth="2.4" strokeLinecap="round" />
            <path d="M37 23 q3 3 6 0" fill="none" stroke={ink} strokeWidth="2.4" strokeLinecap="round" />
          </>
        : <>
            <circle cx="24" cy="23" r="2.4" fill={ink} />
            <circle cx="40" cy="23" r="2.4" fill={ink} />
          </>}
      {/* mouth */}
      {happy && <path d="M27 28 q5 4 10 0" fill="none" stroke={ink} strokeWidth="2.2" strokeLinecap="round" />}
      {!happy && !sad && <line x1="28" y1="29.5" x2="36" y2="29.5" stroke={ink} strokeWidth="2.2" strokeLinecap="round" />}
      {sad && <path d="M27 30.5 q5 -4 10 0" fill="none" stroke={ink} strokeWidth="2.2" strokeLinecap="round" />}
      {/* arms */}
      <path d="M16 45 q-6 0 -7 6" fill="none" stroke={dark} strokeWidth="4" strokeLinecap="round" />
      <path d="M48 45 q6 0 7 6" fill="none" stroke={dark} strokeWidth="4" strokeLinecap="round" />
      {/* body */}
      <rect x="16" y="40" width="32" height="16" rx="8" fill={dark} />
      {/* lightning badge */}
      <path d="M34 43 l-5 7 h3.2 l-1.2 5 5.5 -7.5 h-3.4 z" fill="#fff" />
      {/* thruster */}
      <g fill={main} opacity=".55">
        <circle cx="27" cy="60" r="1.1" /><circle cx="32" cy="62" r="1.1" /><circle cx="37" cy="60" r="1.1" />
      </g>
    </svg>
  );
}

const scoreColor = (s) => s >= 80 ? "#16A36B" : s >= 60 ? "#1C9BE6" : s >= 40 ? "#E0A013" : "#E0533D";
const uidA = () => Math.random().toString(36).slice(2, 9);
const scoreLabel = (s) => s >= 80 ? "Excellent" : s >= 60 ? "Strong" : s >= 40 ? "Needs work" : "Getting started";

const RP_SECTIONS = [
  { id: "experience",  label: "Experience",                icon: "briefcase" },
  { id: "projects",    label: "Projects",                  icon: "folder"    },
  { id: "education",   label: "Education",                 icon: "cap"       },
  { id: "skills",      label: "Skills",                    icon: "sparkle"   },
  { id: "certs",       label: "Certifications",            icon: "award"     },
  { id: "languages",   label: "Languages",                 icon: "globe"     },
  { id: "activities",  label: "Extracurricular Activities",icon: "star"      },
  { id: "profdegrees", label: "Professional Credentials",  icon: "badge"     },
  { id: "references",  label: "References",                icon: "users"     },
];

function RpColLayout({ sidebarSections, setSidebarSections, sectionOrder }) {
  const [dragId, setDragId] = React.useState(null);
  const [dragTarget, setDragTarget] = React.useState(null);

  const mainSections   = sectionOrder.filter(id => !sidebarSections.includes(id));
  const sidebarOrdered = sectionOrder.filter(id =>  sidebarSections.includes(id));

  const moveTo = (id, col) => setSidebarSections(prev =>
    col === "sidebar" ? (prev.includes(id) ? prev : [...prev, id]) : prev.filter(x => x !== id)
  );
  const handleDrop = (col) => { if (!dragId) return; moveTo(dragId, col); setDragId(null); setDragTarget(null); };

  const ColBox = ({ col, ids, label, hint }) => (
    <div
      onDragOver={e => { e.preventDefault(); setDragTarget(col); }}
      onDragLeave={() => setDragTarget(null)}
      onDrop={() => handleDrop(col)}
      style={{ border: "2px dashed", borderColor: dragTarget === col ? "var(--brand-blue)" : "var(--slate-200)", borderRadius: 8, padding: "8px 8px 4px", background: dragTarget === col ? "rgba(28,155,230,.05)" : "var(--slate-50)", marginBottom: 8, transition: "border-color .15s, background .15s", minHeight: 56 }}
    >
      <div style={{ fontSize: 10, fontWeight: 700, letterSpacing: ".06em", textTransform: "uppercase", color: "var(--slate-500)", marginBottom: 2 }}>{label}</div>
      <div style={{ fontSize: 10, color: "var(--slate-400)", marginBottom: 5 }}>{hint}</div>
      {ids.length === 0 && <div style={{ fontSize: 11, color: "var(--slate-300)", fontStyle: "italic", textAlign: "center", padding: "4px 0 6px" }}>Drop here</div>}
      {ids.map(id => {
        const s = RP_SECTIONS.find(x => x.id === id); if (!s) return null;
        return (
          <div key={id} draggable
            onDragStart={() => setDragId(id)}
            onDragEnd={() => { setDragId(null); setDragTarget(null); }}
            style={{ display: "flex", alignItems: "center", gap: 5, padding: "4px 7px", marginBottom: 4, borderRadius: 6, background: "#fff", border: "1px solid var(--slate-200)", cursor: "grab", opacity: dragId === id ? 0.4 : 1, fontSize: 12, fontWeight: 500, color: "var(--slate-700)" }}
          >
            <Icon name="grip" size={10} style={{ color: "var(--slate-300)", flexShrink: 0 }} />
            <span style={{ flex: 1 }}>{s.label}</span>
            <button
              onClick={() => moveTo(id, col === "main" ? "sidebar" : "main")}
              style={{ background: "none", border: "none", cursor: "pointer", color: "var(--slate-400)", fontSize: 13, lineHeight: 1, padding: "0 2px" }}
            >{col === "main" ? "→" : "←"}</button>
          </div>
        );
      })}
    </div>
  );

  return (
    <div style={{ marginTop: 6 }}>
      <div style={{ fontSize: 11, color: "var(--slate-500)", marginBottom: 8, lineHeight: 1.4 }}>Drag sections between columns or click → ← to move.</div>
      <ColBox col="main"    ids={mainSections}   label="Main column" hint="Wider — experience & projects" />
      <ColBox col="sidebar" ids={sidebarOrdered} label="Sidebar"     hint="Narrower — skills & education" />
    </div>
  );
}

const TWEAK_DEFAULTS = { "accent": "Brand Blue", "layout": "split", "density": "regular", "navy": "#0A0E39" };
const FREE_TEMPLATES = ["executive"]; // Prestige is the only free template
const FREE_DOWNLOAD_LIMIT = 3;          // free users get 3 PDF downloads, then must upgrade
const LANDING_URL    = (window.__LANDING_URL__ || "http://localhost:3401").replace(/\/$/, "");

/* Free-tier AI usage meter — shows how many free AI generations remain.
   Updates live on every AI click via the "bdr-ai-uses-changed" event. */
function AIUsageBar() {
  const LIMIT = window.AI_FREE_LIMIT || 10;
  const read = () => {
    const r = window.getAIUsesRemaining ? window.getAIUsesRemaining() : null;
    if (r === Infinity) return LIMIT;          // pro — handled by caller, won't render
    if (r === null || isNaN(r)) return LIMIT;  // not used yet — full allowance
    return Math.max(0, Math.min(LIMIT, r));
  };
  const [remaining, setRemaining] = useState(read);
  useEffect(() => {
    const onChange = () => setRemaining(read());
    window.addEventListener("bdr-ai-uses-changed", onChange);
    return () => window.removeEventListener("bdr-ai-uses-changed", onChange);
  }, []);

  const used = LIMIT - remaining;
  const pct  = Math.round((used / LIMIT) * 100);
  const out  = remaining <= 0;
  const low  = remaining > 0 && remaining <= 3;
  const barColor = out ? "var(--brand-blue)" : low ? "#F59E0B" : "var(--brand-green)";

  return (
    <div className="rp-card">
      <div className="rp-card-h">
        <span style={{ color: "var(--brand-blue)", display: "inline-flex", verticalAlign: "middle" }}><Icon name="credits" size={16} /></span> Free AI credits
      </div>
      <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", marginBottom: 8 }}>
        <span style={{ fontSize: 13, fontWeight: 700, color: out ? "var(--brand-blue)" : "var(--navy)" }}>
          {out ? "0 left" : remaining + " of " + LIMIT + " left"}
        </span>
        <span style={{ fontSize: 11, color: "var(--slate-400)" }}>{used}/{LIMIT} used</span>
      </div>
      <div style={{ height: 8, borderRadius: 99, background: "var(--slate-200)", overflow: "hidden" }}>
        <div style={{ height: "100%", width: pct + "%", background: barColor, borderRadius: 99, transition: "width .35s ease, background .3s" }} />
      </div>
      <div style={{ fontSize: 11.5, color: "var(--slate-500)", marginTop: 9, lineHeight: 1.5 }}>
        {out
          ? "You've used all your free AI generations."
          : "Each AI generation uses one credit."}
      </div>
      <a href={LANDING_URL + "/pricing"} className="rp-btn" style={{ display: "block", textAlign: "center", textDecoration: "none", marginTop: 10, ...(out ? { background: "var(--grad)", color: "#fff", borderColor: "transparent" } : {}) }}>
        {out ? "Upgrade to Pro →" : "Go unlimited with Pro →"}
      </a>
    </div>
  );
}

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);

  /* ── routing ──────────────────────────────────────────── */
  const [mode, setMode] = useState("editor"); // "dashboard" | "editor"

  /* ── document data ────────────────────────────────────── */
  const load = (k, fb) => { try { const v = localStorage.getItem(k); return v ? JSON.parse(v) : fb; } catch { return fb; } };
  const [docId, setDocId]       = useState(() => load("bdr_current_doc", null));
  const [data, setData]         = useState(() => load("bdr_resume", SAMPLE_RESUME));
  const [docName, setDocName]   = useState(() => load("bdr_docname", "Maya Chen — Product Design"));
  const [template, setTemplate] = useState(() => load("bdr_template", "modern"));
  const [design, setDesign]     = useState(() => load("bdr_design", { font: "poppins", accent: "#1C9BE6", scale: "normal", spacing: "normal" }));
  const [coverLetter, setCoverLetter] = useState(() => load("bdr_coverletter", CL_DEFAULTS));

  /* ── editor UI ────────────────────────────────────────── */
  const [editorTab, setEditorTab] = useState("resume"); // "resume" | "coverletter"
  const [section, setSection]   = useState("basics");
  const [openIds, setOpenIds]   = useState({});
  const [zoom, setZoom]         = useState(1);
  const [leftW,  setLeftW]  = useState(() => { try { const s = JSON.parse(localStorage.getItem("bdr_panel_w") || "{}"); return s.leftW  || 290; } catch { return 290; } });
  const [rightW, setRightW] = useState(() => { try { const s = JSON.parse(localStorage.getItem("bdr_panel_w") || "{}"); return s.rightW || 220; } catch { return 220; } });
  const [plan, setPlan]       = useState(() => { try { return localStorage.getItem("bdr_plan") || "free"; } catch { return "free"; } });
  const [sectionOrder, setSectionOrder] = useState(() => { try { const v = localStorage.getItem("bdr_section_order"); return v ? JSON.parse(v) : DEFAULT_SECTION_ORDER; } catch { return DEFAULT_SECTION_ORDER; } });
  const [sidebarSections, setSidebarSections] = useState(() => { try { const v = localStorage.getItem("bdr_sidebar_sections"); return v ? JSON.parse(v) : ["skills", "certs", "education"]; } catch { return ["skills", "certs", "education"]; } });
  const [dragSecIdx, setDragSecIdx]   = useState(null);
  const [dragOverSecIdx, setDragOverSecIdx] = useState(null);

  /* ── panel visibility ─────────────────────────────────── */
  const [showGallery,  setShowGallery]  = useState(false);
  const [showATS,      setShowATS]      = useState(false);
  const [showJobMatch, setShowJobMatch] = useState(false);
  const [showTailor,   setShowTailor]   = useState(false);
  const [showShare,    setShowShare]    = useState(false);
  const [showKeyboard, setShowKeyboard] = useState(false);
  const [saved,        setSaved]        = useState(true);
  const [pdfBusy,      setPdfBusy]      = useState(false);
  const [pdfMenu,      setPdfMenu]      = useState(false);
  const [shareCopied,  setShareCopied]  = useState("");
  const [toast,        setToast]        = useState(null);
  const [showAI,         setShowAI]         = useState(false);
  const [showImport,     setShowImport]     = useState(false);
  const [summaryBusy,    setSummaryBusy]    = useState(false);
  const isPro = !!window.IS_PRO;
  const [tabletPreview,  setTabletPreview]  = useState(false);
  const [userMenu,       setUserMenu]       = useState(false);
  const [aiNewSeen,      setAiNewSeen]      = useState(() => { try { return !!localStorage.getItem("bdr_ai_new_seen"); } catch { return false; } });
  const [colLayoutSeen,  setColLayoutSeen]  = useState(() => { try { return !!localStorage.getItem("bdr_col_layout_seen"); } catch { return false; } });
  const [sampleBanner,   setSampleBanner]   = useState(() => { try { return !localStorage.getItem("bdr_resume") && !localStorage.getItem("bdr_sample_seen"); } catch { return false; } });
  const [showProExport,    setShowProExport]    = useState(false);
  const [proReason,        setProReason]        = useState("template"); // "template" | "limit"
  const [dlCount,          setDlCount]          = useState(() => { try { return parseInt(localStorage.getItem("bdr_dl_count") || "0", 10) || 0; } catch { return 0; } });
  const [grammarBusy,      setGrammarBusy]      = useState(false);
  const [grammarDone,      setGrammarDone]      = useState(false);
  const [showInterviewPrep, setShowInterviewPrep] = useState(false);
  const [showTranslate,     setShowTranslate]     = useState(false);
  const [showSalary,        setShowSalary]        = useState(false);
  const [showAchievement,   setShowAchievement]   = useState(false);
  const [showColdEmail,     setShowColdEmail]     = useState(false);
  const [recruiterScan,     setRecruiterScan]     = useState(false);
  const [versions, setVersions] = useState(() => { try { return JSON.parse(localStorage.getItem("bdr_versions") || "[]"); } catch { return []; } });
  const [showVersionName, setShowVersionName] = useState(false);
  const [versionNameInput, setVersionNameInput] = useState("");

  /* ── undo / redo history ──────────────────────────────── */
  const histRef = useRef([data]);
  const histIdx = useRef(0);
  const [canUndo, setCanUndo] = useState(false);
  const [canRedo, setCanRedo] = useState(false);

  const refreshHist = () => {
    setCanUndo(histIdx.current > 0);
    setCanRedo(histIdx.current < histRef.current.length - 1);
  };

  const pushHistory = useCallback((next) => {
    const h = histRef.current.slice(0, histIdx.current + 1);
    h.push(next);
    if (h.length > 60) h.shift();
    histRef.current = h;
    histIdx.current = h.length - 1;
    refreshHist();
  }, []);

  const undo = useCallback(() => {
    if (histIdx.current > 0) { histIdx.current--; setData(histRef.current[histIdx.current]); refreshHist(); }
  }, []);
  const redo = useCallback(() => {
    if (histIdx.current < histRef.current.length - 1) { histIdx.current++; setData(histRef.current[histIdx.current]); refreshHist(); }
  }, []);

  const patch = useCallback((fn) => {
    setData(d => { const next = fn(d); pushHistory(next); return next; });
  }, [pushHistory]);

  const showToast = (msg, action) => { setToast({ msg, action: action || null }); setTimeout(() => setToast(null), action ? 7000 : 2200); };

  const signOut = () => {
    const form = document.createElement("form");
    form.method = "POST";
    form.action = LANDING_URL + "/api/auth/logout";
    document.body.appendChild(form);
    form.submit();
  };

  const startPanelResize = (side) => (e) => {
    e.preventDefault();
    const x0 = e.clientX;
    const startLeft = leftW, startRight = rightW;
    const move = (ev) => {
      const d = ev.clientX - x0;
      // Left handle: drag right = wider. Right handle: drag right = narrower.
      if (side === "left") setLeftW(Math.max(220, Math.min(460, startLeft + d)));
      else                 setRightW(Math.max(180, Math.min(420, startRight - d)));
    };
    const up = () => {
      window.removeEventListener("mousemove", move);
      window.removeEventListener("mouseup",  up);
      document.body.style.cursor = "";
      document.body.style.userSelect = "";
    };
    document.body.style.cursor = "col-resize";
    document.body.style.userSelect = "none";
    window.addEventListener("mousemove", move);
    window.addEventListener("mouseup",  up);
  };

  /* ── AI summary rewrite helper ────────────────────────────── */
  const rewriteSummary = useCallback(async () => {
    if (summaryBusy || !window.groqStream) return;
    setSummaryBusy(true);
    await window.groqStream({
      messages: [
        { role: "system", content: window.GROQ_SYSTEM || "" },
        { role: "user", content: "Write a compelling 2–3 sentence professional summary for this résumé. Strong language, quantify if possible, no first-person pronouns. Return ONLY the summary text:\n\n" + (window.buildResumeCtx ? window.buildResumeCtx(data) : "") },
      ],
      maxTokens: 200,
      onChunk: (_, full) => patch(d => ({ ...d, summary: full })),
      onDone: () => setSummaryBusy(false),
      onError: (e) => {
        setSummaryBusy(false);
        showToast(e === "free_limit_reached"
          ? "Free AI limit reached — upgrade to Pro for unlimited use."
          : (typeof e === "string" ? e : "AI error — couldn't rewrite summary."));
      },
    });
  }, [summaryBusy, data, patch]);

  const checkGrammar = useCallback(async () => {
    if (grammarBusy || !window.groqStream) return;
    setGrammarBusy(true);
    setGrammarDone(false);

    // Collect all plain-text snippets with keys
    const items = [];
    if (data.summary) items.push({ k: "summary", v: data.summary });
    (data.experience || []).forEach((x, xi) =>
      (x.bullets || []).forEach((b, bi) => { if (b.trim()) items.push({ k: `exp.${xi}.${bi}`, v: b }); })
    );
    (data.projects || []).forEach((x, xi) => {
      if (x.detail) items.push({ k: `proj.${xi}.detail`, v: x.detail });
      (x.bullets || []).forEach((b, bi) => { if (b.trim()) items.push({ k: `proj.${xi}.${bi}`, v: b }); });
    });
    (data.education || []).forEach((x, xi) => {
      if (x.detail) items.push({ k: `edu.${xi}.detail`, v: x.detail });
    });
    (data.activities || []).forEach((a, ai) => {
      if (a.detail) items.push({ k: `act.${ai}.detail`, v: a.detail });
    });
    (data.custom || []).forEach((c, ci) => {
      if (c.body) items.push({ k: `cust.${ci}`, v: c.body.replace(/<[^>]+>/g, " ").replace(/\s+/g, " ").trim() });
    });

    if (!items.length) { setGrammarBusy(false); return; }

    const numbered = items.map((item, i) => `${i + 1}. ${item.v}`).join("\n");
    let raw = "";
    await window.groqStream({
      messages: [
        { role: "system", content: "You are a grammar and style editor. Fix grammar, spelling, punctuation, and clarity in each numbered item. Keep meaning and tone identical. Return ONLY a JSON array of corrected strings in the same order, no explanation." },
        { role: "user", content: "Fix grammar in each numbered item:\n\n" + numbered + "\n\nReturn as JSON array: [\"corrected 1\", \"corrected 2\", ...]" },
      ],
      maxTokens: 2048,
      onChunk: (_, full) => { raw = full; },
      onDone: () => {
        try {
          const match = raw.match(/\[[\s\S]*\]/);
          if (!match) throw new Error("no array");
          const fixed = JSON.parse(match[0]);
          patch(d => {
            let nd = { ...d };
            fixed.forEach((val, i) => {
              if (!val || !items[i]) return;
              const k = items[i].k;
              if (k === "summary") { nd = { ...nd, summary: val }; return; }
              const parts = k.split(".");
              if (parts[0] === "exp") {
                const xi = +parts[1]; const bi = +parts[2];
                const exp = [...(nd.experience || [])];
                const bullets = [...(exp[xi].bullets || [])];
                bullets[bi] = val;
                exp[xi] = { ...exp[xi], bullets };
                nd = { ...nd, experience: exp };
              } else if (parts[0] === "proj") {
                const xi = +parts[1];
                const projs = [...(nd.projects || [])];
                if (parts[2] === "detail") projs[xi] = { ...projs[xi], detail: val };
                else { const bi = +parts[2]; const bullets = [...(projs[xi].bullets||[])]; bullets[bi] = val; projs[xi] = { ...projs[xi], bullets }; }
                nd = { ...nd, projects: projs };
              } else if (parts[0] === "edu") {
                const xi = +parts[1];
                const edus = [...(nd.education || [])];
                edus[xi] = { ...edus[xi], detail: val };
                nd = { ...nd, education: edus };
              } else if (parts[0] === "act") {
                const ai = +parts[1];
                const acts = [...(nd.activities || [])];
                acts[ai] = { ...acts[ai], detail: val };
                nd = { ...nd, activities: acts };
              }
            });
            return nd;
          });
        } catch(e) {
          setGrammarBusy(false);
          showToast("AI error — couldn't parse grammar fixes. Try again.");
          return;
        }
        setGrammarBusy(false);
        setGrammarDone(true);
        setTimeout(() => setGrammarDone(false), 3000);
      },
      onError: (e) => {
        setGrammarBusy(false);
        showToast(e === "free_limit_reached"
          ? "Free AI limit reached — upgrade to Pro for unlimited use."
          : (typeof e === "string" ? e : "AI error — couldn't check grammar."));
      },
    });
  }, [grammarBusy, data, patch]);

  /* ── version snapshots ────────────────────────────────── */
  const saveVersion = useCallback((name) => {
    const snap = { id: Date.now().toString(36), name: name || ("Version " + new Date().toLocaleString()), data, template, design, savedAt: new Date().toISOString() };
    setVersions(vs => {
      const next = [snap, ...vs].slice(0, 20);
      try { localStorage.setItem("bdr_versions", JSON.stringify(next)); } catch(_) {}
      return next;
    });
    showToast("Version saved: " + snap.name);
  }, [data, template, design]);

  const restoreVersion = useCallback((snap) => {
    patch(() => snap.data);
    setTemplate(snap.template || template);
    setDesign(snap.design || design);
    showToast("Restored: " + snap.name);
  }, [patch, template, design]);

  const deleteVersion = useCallback((id) => {
    setVersions(vs => {
      const next = vs.filter(v => v.id !== id);
      try { localStorage.setItem("bdr_versions", JSON.stringify(next)); } catch(_) {}
      return next;
    });
  }, []);

  /* ── multi-doc helpers ────────────────────────────────── */
  const saveCurrentDoc = useCallback(() => {
    const id = docId || uidA();
    const payload = { data, docName, template, design, coverLetter, updatedAt: new Date().toISOString() };
    saveDocData(id, payload);
    const docs = loadAllDocs();
    const idx  = docs.findIndex(d => d.id === id);
    const meta = { id, name: docName, template, updatedAt: new Date().toISOString() };
    if (idx >= 0) docs[idx] = meta; else docs.unshift(meta);
    saveAllDocs(docs);
    localStorage.setItem("bdr_current_doc", JSON.stringify(id));
    if (!docId) setDocId(id);
    /* keep legacy keys in sync */
    localStorage.setItem("bdr_resume",      JSON.stringify(data));
    localStorage.setItem("bdr_template",    JSON.stringify(template));
    localStorage.setItem("bdr_docname",     JSON.stringify(docName));
    localStorage.setItem("bdr_design",      JSON.stringify(design));
    localStorage.setItem("bdr_coverletter", JSON.stringify(coverLetter));
    showToast("Saved");
    return id;
  }, [data, docName, template, design, coverLetter, docId]);

  const openDoc = useCallback((id) => {
    const p = loadDocData(id);
    if (p) {
      setData(p.data || SAMPLE_RESUME);
      setDocName(p.docName || "Untitled");
      setTemplate(p.template || "modern");
      setDesign(p.design || { font: "auto", accent: "#1C9BE6" });
      setCoverLetter(p.coverLetter || CL_DEFAULTS);
      histRef.current = [p.data || SAMPLE_RESUME];
      histIdx.current = 0;
      refreshHist();
    }
    setDocId(id);
    localStorage.setItem("bdr_current_doc", JSON.stringify(id));
    setMode("editor"); setEditorTab("resume"); setSection("basics");
  }, []);

  const createNew = useCallback((tpl) => {
    const id   = uidA();
    let tmpl   = tpl || "modern";
    const acc  = (TEMPLATES[tmpl] && TEMPLATES[tmpl].defaultAccent) || "#1C9BE6";
    setDocId(id); setData(SAMPLE_RESUME); setDocName("New Résumé");
    setTemplate(tmpl); setDesign({ font: "poppins", accent: acc, scale: "normal", spacing: "normal" });
    setCoverLetter(CL_DEFAULTS);
    histRef.current = [SAMPLE_RESUME]; histIdx.current = 0; refreshHist();
    localStorage.setItem("bdr_current_doc", JSON.stringify(id));
    setMode("editor"); setEditorTab("resume"); setSection("basics");
  }, []);

  const goHome = useCallback(() => {
    saveCurrentDoc();
    setMode("dashboard");
  }, [saveCurrentDoc]);

  /* ── persistence ──────────────────────────────────────── */
  useEffect(() => {
    setSaved(false);
    const id = setTimeout(() => { localStorage.setItem("bdr_resume", JSON.stringify(data)); setSaved(true); }, 500);
    return () => clearTimeout(id);
  }, [data]);
  useEffect(() => { localStorage.setItem("bdr_template", JSON.stringify(template)); }, [template]);
  const prevTemplateRef = useRef(template);
  useEffect(() => {
    const prev = prevTemplateRef.current;
    prevTemplateRef.current = template;
    const isNowTwoCol = template === "split" || template === "sidebar";
    const wasTwoCol   = prev === "split" || prev === "sidebar";
    if (isNowTwoCol && !wasTwoCol && !colLayoutSeen) {
      setTimeout(() => showToast("💡 Tip: Use 'Column layout' under Design to customise what goes in each column!"), 600);
    }
  }, [template]);
  useEffect(() => { localStorage.setItem("bdr_docname",     JSON.stringify(docName));  }, [docName]);
  useEffect(() => { localStorage.setItem("bdr_design",      JSON.stringify(design));   }, [design]);
  useEffect(() => { localStorage.setItem("bdr_coverletter", JSON.stringify(coverLetter)); }, [coverLetter]);
  useEffect(() => { try { localStorage.setItem("bdr_section_order", JSON.stringify(sectionOrder)); } catch {} }, [sectionOrder]);
  useEffect(() => { try { localStorage.setItem("bdr_sidebar_sections", JSON.stringify(sidebarSections)); } catch {} }, [sidebarSections]);

  useEffect(() => {
    const id = setTimeout(() => {
      localStorage.setItem("bdr_panel_w", JSON.stringify({ leftW, rightW }));
    }, 400);
    return () => clearTimeout(id);
  }, [leftW, rightW]);

  /* Read ?plan=pro from URL (set by landing when opening builder for Pro users) */
  useEffect(() => {
    const p = new URLSearchParams(window.location.search).get("plan");
    if (p === "pro") {
      localStorage.setItem("bdr_plan", "pro");
      setPlan("pro");
      history.replaceState(null, "", window.location.pathname);
    }
  }, []);


  /* ── CSS variables ────────────────────────────────────── */
  const accent = design.accent || "#1C9BE6";
  useEffect(() => {
    document.documentElement.style.setProperty("--accent", accent);
    document.documentElement.style.setProperty("--density",
      t.density === "compact" ? ".82" : t.density === "comfy" ? "1.18" : "1");
  }, [accent, t.density]);

  const ats = useMemo(() => computeATS(data), [data]);

  /* ── keyboard shortcuts ───────────────────────────────── */
  useEffect(() => {
    const handler = (e) => {
      if (mode !== "editor") return;
      if (e.ctrlKey || e.metaKey) {
        if (e.key === "z" && !e.shiftKey) { e.preventDefault(); undo(); }
        if ((e.key === "z" && e.shiftKey) || e.key === "y") { e.preventDefault(); redo(); }
        if (e.key === "s") { e.preventDefault(); saveCurrentDoc(); }
      }
    };
    window.addEventListener("keydown", handler);
    return () => window.removeEventListener("keydown", handler);
  }, [mode, undo, redo, saveCurrentDoc]);

  /* ── preload PDF libs ─────────────────────────────────── */
  useEffect(() => {
    const idle = window.requestIdleCallback || ((cb) => setTimeout(cb, 1500));
    idle(() => { try { window.loadPdfLibs && window.loadPdfLibs(); } catch (e) {} });
  }, []);

  /* ── load from URL share link ─────────────────────────── */
  useEffect(() => {
    const hash = window.location.hash;
    if (hash.startsWith("#resume=")) {
      try {
        const json    = decodeURIComponent(atob(hash.slice("#resume=".length)));
        const payload = JSON.parse(json);
        if (payload.data) {
          setData(payload.data);
          if (payload.docName)     setDocName(payload.docName);
          if (payload.template)    setTemplate(payload.template);
          if (payload.design)      setDesign(payload.design);
          if (payload.coverLetter) setCoverLetter(payload.coverLetter);
          history.replaceState(null, "", window.location.pathname);
          showToast("Shared résumé loaded");
        }
      } catch (e) {}
    }
  }, []);

  /* ── preview auto-fit scaling ─────────────────────────── */
  const colRef  = useRef(null);
  const pageRef = useRef(null);
  const [scale, setScale] = useState(0.7);
  const [boxH,  setBoxH]  = useState(1056);

  const recompute = () => {
    if (!colRef.current) return;
    const avail = colRef.current.clientWidth - 60;
    const fit   = Math.max(0.34, Math.min(1, avail / 816));
    const s     = fit * zoom;
    setScale(s);
    const h = pageRef.current ? pageRef.current.offsetHeight : 1056;
    setBoxH(h * s);
  };
  useLayoutEffect(recompute, [zoom, data, template, design, t.layout, showATS, showJobMatch, editorTab]);
  useEffect(() => {
    const ro = new ResizeObserver(recompute);
    if (colRef.current) ro.observe(colRef.current);
    window.addEventListener("resize", recompute);
    return () => { ro.disconnect(); window.removeEventListener("resize", recompute); };
  }, [zoom]);

  /* ── PDF export & download gating ─────────────────────── */
  // Free users: only the Prestige template, and only FREE_DOWNLOAD_LIMIT total downloads.
  const isProUser = plan === "pro" || isPro;
  const downloadsLeft = Math.max(0, FREE_DOWNLOAD_LIMIT - dlCount);
  const showDlRemaining = !isProUser && FREE_TEMPLATES.includes(template);
  useEffect(() => {
    try { localStorage.setItem("bdr_dl_count", String(dlCount)); } catch (e) {}
  }, [dlCount]);

  // Returns true if a download may proceed; otherwise opens the upgrade modal and returns false.
  const checkDownloadAllowed = () => {
    if (isProUser) return true;
    if (!FREE_TEMPLATES.includes(template)) { setProReason("template"); setShowProExport(true); return false; }
    if (dlCount >= FREE_DOWNLOAD_LIMIT)      { setProReason("limit");    setShowProExport(true); return false; }
    return true;
  };
  // Count a successful free download and tell the user how many remain.
  const recordDownload = () => {
    if (isProUser) return;
    const n = dlCount + 1;
    setDlCount(n);
    const left = Math.max(0, FREE_DOWNLOAD_LIMIT - n);
    showToast(left > 0
      ? left + " free download" + (left === 1 ? "" : "s") + " left"
      : "That was your last free download — upgrade to Pro for unlimited.");
  };

  const exportPDF = async () => {
    if (pdfBusy) return;
    if (!checkDownloadAllowed()) return;
    setPdfBusy(true);
    try {
      const ok = await window.downloadResumePDF(docName, { data, template, accent, design });
      if (ok === false) showToast("PDF library unavailable — use Exact Copy instead");
      else recordDownload();
    }
    catch (e) {
      console.warn("PDF export failed", e);
      showToast("PDF export failed — use Exact Copy instead");
    }
    finally { setPdfBusy(false); }
  };
  const exportExact = async () => {
    if (pdfBusy) return;
    if (!checkDownloadAllowed()) return;
    setPdfBusy(true);
    try {
      const el = document.getElementById("resume-page");
      if (!el) throw new Error("Resume element not found");

      // Capture the fully-rendered resume HTML
      const html = el.outerHTML;

      // Collect all CSS rules from every loaded stylesheet
      let css = "";
      for (const sheet of document.styleSheets) {
        try {
          for (const rule of sheet.cssRules) css += rule.cssText + "\n";
        } catch (e) { /* cross-origin sheets */ }
      }

      // Collect CSS custom property overrides set on <html> (e.g. --accent)
      const rootStyle = document.documentElement.style;
      let rootVars = ":root{";
      for (let i = 0; i < rootStyle.length; i++) {
        const p = rootStyle.item(i);
        rootVars += p + ":" + rootStyle.getPropertyValue(p) + ";";
      }
      rootVars += "}";

      // Google Fonts links needed for correct font rendering
      const fontLinks = [...document.querySelectorAll("link[rel=stylesheet]")]
        .map(l => l.href).filter(h => h.includes("googleapis.com") || h.includes("gstatic.com"));

      let blob;
      try {
        const res = await fetch(LANDING_URL + "/api/pdf", {
          method: "POST",
          headers: { "Content-Type": "application/json" },
          body: JSON.stringify({ html, css, rootVars, fontLinks, docName }),
        });
        if (!res.ok) throw new Error(await res.text());
        blob = await res.blob();
      } catch (serverErr) {
        // The PDF server is unreachable (static/local hosting, CORS, or offline).
        // Fall back to the in-browser exact-copy exporter, which saves directly.
        console.warn("PDF server unavailable, using client-side export", serverErr);
        const ok = await window.exportExactPDF(docName);
        if (ok === false) throw new Error("client-side export unavailable");
        recordDownload();
        showToast("PDF downloaded!");
        return;
      }
      const url  = URL.createObjectURL(blob);
      const a    = document.createElement("a");
      a.href     = url;
      a.download = (docName || "resume").replace(/[^\w\s\-]/g, "").trim() + ".pdf";
      a.click();
      URL.revokeObjectURL(url);
      recordDownload();
      showToast("PDF downloaded!");
    } catch (e) {
      console.warn("PDF export failed", e);
      showToast("PDF export failed — " + e.message);
    } finally {
      setPdfBusy(false);
    }
  };

  /* ── share / export ───────────────────────────────────── */
  const exportJSON = () => {
    const payload = { data, docName, template, design, coverLetter };
    const blob    = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" });
    const url     = URL.createObjectURL(blob);
    const a       = document.createElement("a");
    a.href        = url;
    a.download    = (docName || "resume").replace(/[^\w\s]/g, "").trim().replace(/\s+/g, "_") + ".json";
    a.click();
    URL.revokeObjectURL(url);
  };
  const copyShareLink = () => {
    try {
      const encoded = btoa(encodeURIComponent(JSON.stringify({ data, docName, template, design, coverLetter })));
      const url     = window.location.href.split("#")[0] + "#resume=" + encoded;
      navigator.clipboard.writeText(url).then(() => {
        setShareCopied("link"); setTimeout(() => setShareCopied(""), 2800);
      });
    } catch (e) { alert("Could not generate link."); }
  };
  const copyJSON = () => {
    navigator.clipboard.writeText(JSON.stringify({ data, docName, template, design, coverLetter }, null, 2)).then(() => {
      setShareCopied("json"); setTimeout(() => setShareCopied(""), 2800);
    });
  };

  const pct = Math.round(scale * 100);

  /* ── DASHBOARD mode ───────────────────────────────────── */
  if (mode === "dashboard") {
    return <Dashboard onOpen={openDoc} onNew={createNew} />;
  }

  /* ══════════════════════════════════════════════════════
     EDITOR mode
  ══════════════════════════════════════════════════════ */
  return (
    <div className="app">

      {/* ── TOPBAR (light) ──────────────────────────────── */}
      <div className="topbar topbar-light">

        {/* Left group */}
        <div className="tb-left">
          <button className="tb-icon-btn" onClick={goHome} title="All documents">
            <Icon name="home" size={16} />
          </button>
          <div className="tb-brand">
            <img src="assets/logo.png" alt="" style={{ width: 24, height: 24 }} />
            <span className="tb-wordmark">bdRecruit<span className="tb-net">.net</span></span>
          </div>
          <div className="tb-sep" />
          <button className="tb-icon-btn" onClick={undo} disabled={!canUndo} title="Undo (Ctrl+Z)" style={{ opacity: canUndo ? 1 : .35, background: canUndo ? "rgba(28,155,230,.1)" : undefined, color: canUndo ? "var(--brand-blue)" : undefined, borderRadius: 6 }}>
            <Icon name="undo" size={16} />
          </button>
          <button className="tb-icon-btn" onClick={redo} disabled={!canRedo} title="Redo (Ctrl+Y)" style={{ opacity: canRedo ? 1 : .35, background: canRedo ? "rgba(28,155,230,.1)" : undefined, color: canRedo ? "var(--brand-blue)" : undefined, borderRadius: 6 }}>
            <Icon name="redo" size={16} />
          </button>
          <div className="tb-saved">
            <svg width="8" height="8" viewBox="0 0 8 8"><circle cx="4" cy="4" r="4" fill={saved ? "var(--good)" : "var(--warn)"} /></svg>
            {saved ? "Saved" : "Saving…"}
          </div>
        </div>

        {/* Center: doc title */}
        <div className="tb-center">
          <div className="tb-doc-title">
            <Icon name="doc" size={14} style={{ color: "var(--slate-400)" }} />
            <input
              value={docName}
              onChange={e => setDocName(e.target.value)}
              onFocus={e => e.target.select()}
              spellCheck={false}
              aria-label="Document name"
            />
            <Icon name="pen" size={12} style={{ color: "var(--slate-400)", flexShrink: 0, marginLeft: 2 }} />
          </div>
        </div>

        {/* Right group */}
        <div className="tb-right">
          <button className="tb-icon-btn" title="Preview résumé" onClick={() => setTabletPreview(p => !p)}>
            <Icon name="eye" size={16} />
          </button>
          <button className="tb-analyze-btn" onClick={() => { setShowATS(true); setShowJobMatch(false); setShowAI(false); }} title="ATS Analysis">
            <Icon name="chart" size={16} />
            Analyze
          </button>
          <div className="pdf-dl" style={{ display: "flex", alignItems: "center", gap: 8 }}>
            {showDlRemaining && (
              <span title={downloadsLeft + " of " + FREE_DOWNLOAD_LIMIT + " free downloads remaining"}
                style={{ display: "inline-flex", alignItems: "center", gap: 5, fontSize: 11, fontWeight: 600,
                  padding: "5px 9px", borderRadius: 7, lineHeight: 1, whiteSpace: "nowrap",
                  color: downloadsLeft === 0 ? "#FCA5A5" : "rgba(255,255,255,.82)",
                  background: downloadsLeft === 0 ? "rgba(224,83,61,.18)" : "rgba(255,255,255,.1)",
                  border: "1px solid " + (downloadsLeft === 0 ? "rgba(224,83,61,.35)" : "rgba(255,255,255,.16)") }}>
                <Icon name="bolt" size={12} />
                {downloadsLeft === 0 ? "No free downloads left" : downloadsLeft + " free left"}
              </span>
            )}
            <button className="btn btn-grad btn-sm" onClick={exportExact} disabled={pdfBusy} style={{ opacity: pdfBusy ? .85 : 1 }}>
              {pdfBusy ? <><span className="spin" /> Preparing…</> : <><Icon name="download" size={15} /> Download PDF</>}
            </button>
          </div>
          <div style={{ position: "relative" }}>
            <button className="tb-avatar" onClick={() => setUserMenu(m => !m)} title="Account">
              {((data.basics?.name || "").trim().split(/\s+/).map(w => w[0]).slice(0, 2).join("").toUpperCase()) || "?"}
            </button>
            {userMenu && (
              <>
                <div onClick={() => setUserMenu(false)} style={{ position: "fixed", inset: 0, zIndex: 99 }} />
                <div style={{ position: "absolute", top: "calc(100% + 8px)", right: 0, background: "#fff", border: "1px solid var(--slate-200)", borderRadius: "var(--r-md)", boxShadow: "var(--sh-3)", minWidth: 160, zIndex: 100, overflow: "hidden" }}>
                  <div style={{ padding: "10px 14px 8px", fontSize: 11.5, color: "var(--slate-400)", borderBottom: "1px solid var(--slate-100)" }}>My account</div>
                  <button onClick={signOut} style={{ width: "100%", display: "flex", alignItems: "center", gap: 9, padding: "10px 14px", background: "none", border: "none", fontSize: 13.5, color: "var(--slate-700)", cursor: "pointer", textAlign: "left" }}>
                    <Icon name="signout" size={15} /> Sign out
                  </button>
                </div>
              </>
            )}
          </div>
        </div>
      </div>

      {/* ── BODY (3-column) ─────────────────────────────── */}
      <div className="body body-3col">

        {/* ── LEFT PANEL ──────────────────────────────── */}
        <div className="left-panel scroll" style={{ width: leftW }}>

          {/* Builder / Templates tabs */}
          <div className="lp-tabs">
            <button className={"lp-tab" + (editorTab !== "coverletter" ? " active" : "")}
              onClick={() => { setEditorTab("resume"); }}>Builder</button>
            <button className="lp-tab" onClick={() => setShowGallery(true)}>Templates</button>
          </div>

          {/* Sample banner */}
          {sampleBanner && (
            <div className="lp-banner">
              <span>👋 <b>Sample data</b> — edit any field to make it yours.</span>
              <button className="btn btn-ghost btn-sm" style={{ fontSize: 11.5, height: 26, padding: "0 8px" }} onClick={() => {
                setSampleBanner(false);
                try { localStorage.setItem("bdr_sample_seen", "1"); } catch {}
                setShowImport(true);
              }}>Import Resume</button>
              <button className="x" style={{ marginLeft: "auto" }} onClick={() => { setSampleBanner(false); try { localStorage.setItem("bdr_sample_seen", "1"); } catch {} }}>
                <Icon name="x" size={13} />
              </button>
            </div>
          )}

          {editorTab === "coverletter" ? (
            /* ── Cover letter editor ── */
            <div className="lp-cl-wrap">
              <CoverLetterEditor cl={coverLetter} onChange={setCoverLetter} data={data} showToast={showToast} />
            </div>
          ) : (
            /* ── Section accordion ── */
            <div className="lp-accordion">
              {/* Fixed sections: Personal details + Summary */}
              {NAV_FIXED.map(n => (
                <div key={n.id} className={"accord-item" + (section === n.id ? " open" : "")}>
                  <button className="accord-head" onClick={() => setSection(section === n.id ? "" : n.id)}>
                    <span className="accord-label">{n.label}</span>
                    <span className="accord-toggle">{section === n.id ? "−" : "+"}</span>
                  </button>
                  {section === n.id && (
                    <div className="accord-body">
                      <Editor data={data} patch={patch} section={n.id} openIds={openIds} setOpenIds={setOpenIds} setSection={setSection} design={design} setDesign={setDesign} aiHelpers={{ rewriteSummary, summaryBusy, isPro, showToast }} sidebarSections={sidebarSections} setSidebarSections={setSidebarSections} sectionOrder={sectionOrder} template={template} />
                    </div>
                  )}
                </div>
              ))}

              {/* Draggable sections */}
              {sectionOrder.map((id, idx) => {
                const n = NAV_META[id]; if (!n) return null;
                const ct = n.countKey ? (data[n.countKey] || []).length : null;
                const isDragging = dragSecIdx === idx;
                const isDragOver = dragOverSecIdx === idx && dragSecIdx !== idx;
                return (
                  <div key={id}
                    className={"accord-item" + (section === id ? " open" : "")}
                    style={{ opacity: isDragging ? 0.4 : 1, outline: isDragOver ? "2px solid var(--brand-blue)" : "none", outlineOffset: -2 }}>
                    <button className="accord-head"
                      draggable
                      onDragStart={e => { e.dataTransfer.effectAllowed = "move"; setDragSecIdx(idx); }}
                      onDragOver={e => { e.preventDefault(); e.dataTransfer.dropEffect = "move"; setDragOverSecIdx(idx); }}
                      onDrop={e => { e.preventDefault(); if (dragSecIdx === null || dragSecIdx === idx) return; const next = [...sectionOrder]; const [moved] = next.splice(dragSecIdx, 1); next.splice(idx, 0, moved); setSectionOrder(next); setDragSecIdx(null); setDragOverSecIdx(null); }}
                      onDragEnd={() => { setDragSecIdx(null); setDragOverSecIdx(null); }}
                      onClick={() => setSection(section === id ? "" : id)}>
                      <Icon name="grip" size={12} style={{ color: "var(--slate-300)", flexShrink: 0, cursor: "grab" }} />
                      <span className="accord-label">{n.label}</span>
                      {ct > 0 && <span className="accord-ct">{ct}</span>}
                      <span className="accord-toggle">{section === id ? "−" : "+"}</span>
                    </button>
                    {section === id && (
                      <div className="accord-body">
                        <Editor data={data} patch={patch} section={id} openIds={openIds} setOpenIds={setOpenIds} setSection={setSection} design={design} setDesign={setDesign} aiHelpers={{ rewriteSummary, summaryBusy, isPro, showToast }} sidebarSections={sidebarSections} setSidebarSections={setSidebarSections} sectionOrder={sectionOrder} template={template} />
                      </div>
                    )}
                  </div>
                );
              })}

              {/* Custom sections */}
              {(data.custom || []).map(c => {
                const cid = "custom:" + c.id;
                return (
                  <div key={c.id} className={"accord-item" + (section === cid ? " open" : "")}>
                    <button className="accord-head" onClick={() => setSection(section === cid ? "" : cid)}>
                      <span className="accord-label">{c.heading || "Custom section"}</span>
                      <span className="accord-toggle">{section === cid ? "−" : "+"}</span>
                    </button>
                    {section === cid && (
                      <div className="accord-body">
                        <Editor data={data} patch={patch} section={cid} openIds={openIds} setOpenIds={setOpenIds} setSection={setSection} design={design} setDesign={setDesign} aiHelpers={{ rewriteSummary, summaryBusy, isPro, showToast }} sidebarSections={sidebarSections} setSidebarSections={setSidebarSections} sectionOrder={sectionOrder} template={template} />
                      </div>
                    )}
                  </div>
                );
              })}

              {/* Add section */}
              <AddSectionMenu
                sectionOrder={sectionOrder}
                setSectionOrder={setSectionOrder}
                setSection={setSection}
                setEditorTab={setEditorTab}
                setData={setData}
              />
            </div>
          )}

          {/* ── Tools ── */}
          {(() => {
            const [toolsOpen, setToolsOpen] = React.useState(true);
            return (
              <div className="lp-tools">
                <button className="lp-tools-h lp-tools-toggle" onClick={() => setToolsOpen(o => !o)}
                  style={{ width:"100%", display:"flex", alignItems:"center", background:"none", border:"none", cursor:"pointer", padding:0 }}>
                  <span style={{ flex:1 }}>Tools</span>
                  <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"
                    style={{ transition:"transform .2s", transform: toolsOpen ? "rotate(0deg)" : "rotate(-90deg)", color:"var(--slate-400)" }}>
                    <path d="M6 9l6 6 6-6"/>
                  </svg>
                </button>
                {toolsOpen && (
                  <div className="lp-tools-grid">
                    <button className={"lp-tool-chip" + (editorTab === "coverletter" ? " active" : "")} onClick={() => setEditorTab("coverletter")} title="Cover Letter">
                      <Icon name="letter" size={14} /><span>Cover Letter</span>
                    </button>
                    <button className="lp-tool-chip" onClick={() => { setShowJobMatch(true); setShowATS(false); setShowAI(false); setShowTailor(false); }} title="Job Match">
                      <Icon name="target" size={14} /><span>Job Match</span>
                    </button>
                    <button className={"lp-tool-chip" + (showTailor ? " active" : "")} onClick={() => { setShowTailor(s => !s); setShowATS(false); setShowJobMatch(false); setShowAI(false); }} title="Tailor to job">
                      <Icon name="bolt" size={14} /><span>Tailor<span className="lp-pro-badge" style={{marginLeft:3}}>Pro</span></span>
                    </button>
                    <button className={"lp-tool-chip" + (showAI ? " active" : "")} onClick={() => { setShowAI(s => { if (!s) { setShowATS(false); setShowJobMatch(false); setShowTailor(false); } return !s; }); if (!aiNewSeen) { setAiNewSeen(true); try { localStorage.setItem("bdr_ai_new_seen", "1"); } catch {} } }} title="AI Coach">
                      <RobotFace size={15} variant="brand" /><span>AI Coach</span>
                    </button>
                    <button className="lp-tool-chip" onClick={() => setShowImport(true)} title="Import CV">
                      <Icon name="import" size={14} /><span>Import CV</span>
                    </button>
                    <button className="lp-tool-chip" onClick={() => setShowInterviewPrep(true)} title="Interview Prep">
                      <Icon name="sparkle" size={14} /><span>Interview</span>
                    </button>
                    <button className="lp-tool-chip" onClick={() => setShowTranslate(true)} title="Translate Resume">
                      <Icon name="globe" size={14} /><span>Translate</span>
                    </button>
                    <button className="lp-tool-chip" onClick={() => setShowSalary(true)} title="Salary Benchmarker">
                      <Icon name="salary" size={14} /><span>Salary</span>
                    </button>
                    <button className="lp-tool-chip" onClick={() => setShowAchievement(true)} title="Quantify Bullets">
                      <Icon name="bolt" size={14} /><span>Quantify</span>
                    </button>
                    <button className="lp-tool-chip" onClick={() => setShowColdEmail(true)} title="Cold Email Generator">
                      <Icon name="letter" size={14} /><span>Cold Email</span>
                    </button>
                  </div>
                )}
              </div>
            );
          })()}
        </div>

        {/* Left panel resize handle */}
        <div className="panel-resizer" onMouseDown={startPanelResize("left")} title="Drag to resize" />

        {/* ── PREVIEW (center) ────────────────────────── */}
        <div className="preview scroll" ref={colRef}>
          <div className="preview-bar">
            <span className="nm">{editorTab === "coverletter" ? "Cover Letter preview" : (TEMPLATES[template]?.name + " template")}</span>
            <div style={{ flex: 1 }} />
            {editorTab !== "coverletter" && (
              <button
                onClick={() => setRecruiterScan(s => !s)}
                style={{ fontSize:11.5, fontWeight:700, padding:"4px 11px", borderRadius:7, border:"1.5px solid", cursor:"pointer", marginRight:8, transition:"all .15s",
                  borderColor: recruiterScan ? "#f59e0b" : "var(--slate-200)",
                  background:  recruiterScan ? "rgba(245,158,11,.1)" : "transparent",
                  color:       recruiterScan ? "#d97706" : "var(--slate-500)" }}
                title="Show 6-second recruiter scan heatmap">
                {recruiterScan ? "◉ Scan ON" : "◎ Recruiter Scan"}
              </button>
            )}
            {editorTab !== "coverletter" && (
              <div className="zoom">
                <button onClick={() => setZoom(z => Math.max(0.5, +(z - 0.1).toFixed(2)))}><Icon name="zoomout" size={15} /></button>
                <span className="val" title="Click to reset" style={{ cursor: "pointer" }} onClick={() => setZoom(1)}>{pct}%</span>
                <button onClick={() => setZoom(z => Math.min(1.6, +(z + 0.1).toFixed(2)))}><Icon name="zoomin" size={15} /></button>
              </div>
            )}
          </div>

          {editorTab === "coverletter" ? (
            <div style={{ padding: "20px 30px" }}>
              <div style={{ width: 816 * Math.min(scale, 0.85), margin: "0 auto", boxShadow: "var(--sh-3)", borderRadius: 3, overflow: "hidden" }}>
                <div style={{ transform: `scale(${Math.min(scale, 0.85)})`, transformOrigin: "top left", width: 816, background: "#fff" }}>
                  <CoverLetterPreview cl={coverLetter} basics={data.basics || {}} accent={accent} design={design} />
                </div>
              </div>
            </div>
          ) : (
            <div style={{ width: 816 * scale, height: boxH, margin: "0 auto", flexShrink: 0, position: "relative" }}>
              <div className="page-wrap" style={{ transform: `scale(${scale})`, transformOrigin: "top left" }}>
                <div className="page" ref={pageRef} id="resume-page">
                  <Resume data={data} template={template} accent={accent} design={design} sectionOrder={sectionOrder} sidebarSections={sidebarSections} />
                </div>
              </div>
              {/* Recruiter scan heatmap overlay */}
              {recruiterScan && (
                <div style={{ position:"absolute", inset:0, pointerEvents:"none", borderRadius:3, overflow:"hidden" }}>
                  {/* Zone 1 — Name / Title: top 11% — hottest */}
                  <div style={{ position:"absolute", top:0, left:0, right:0, height:"11%", background:"rgba(239,68,68,.28)", borderBottom:"2px solid rgba(239,68,68,.5)" }} />
                  {/* Zone 2 — Contact + first job header: 11–26% */}
                  <div style={{ position:"absolute", top:"11%", left:0, right:0, height:"15%", background:"rgba(249,115,22,.2)", borderBottom:"1px solid rgba(249,115,22,.35)" }} />
                  {/* Zone 3 — First 2 bullets + skills: 26–52% */}
                  <div style={{ position:"absolute", top:"26%", left:0, right:0, height:"26%", background:"rgba(234,179,8,.14)", borderBottom:"1px solid rgba(234,179,8,.28)" }} />
                  {/* Zone 4 — Rest: 52–100% — cold */}
                  <div style={{ position:"absolute", top:"52%", left:0, right:0, bottom:0, background:"rgba(99,102,241,.08)" }} />

                  {/* Legend */}
                  <div style={{ position:"absolute", bottom:12*scale, right:12*scale, background:"rgba(255,255,255,.96)", backdropFilter:"blur(8px)", borderRadius:10, padding:"10px 13px", boxShadow:"0 2px 16px rgba(0,0,0,.14)", display:"flex", flexDirection:"column", gap:5 }}>
                    <div style={{ fontSize:10, fontWeight:800, color:"var(--navy)", textTransform:"uppercase", letterSpacing:".07em", marginBottom:3 }}>6-sec Recruiter Eye</div>
                    {[["rgba(239,68,68,.6)","Name & Title","Hottest"],["rgba(249,115,22,.6)","Contact + Role","High"],["rgba(234,179,8,.5)","First bullets + Skills","Medium"],["rgba(99,102,241,.4)","Rest of resume","Low"]].map(([c,label,heat]) => (
                      <div key={label} style={{ display:"flex", alignItems:"center", gap:7 }}>
                        <div style={{ width:12, height:12, borderRadius:3, background:c, flexShrink:0 }} />
                        <span style={{ fontSize:10.5, color:"var(--slate-600)" }}>{label}</span>
                        <span style={{ fontSize:9.5, color:"var(--slate-400)", marginLeft:"auto" }}>{heat}</span>
                      </div>
                    ))}
                  </div>
                </div>
              )}
            </div>
          )}
        </div>

        {/* Right panel resize handle */}
        <div className="panel-resizer" onMouseDown={startPanelResize("right")} title="Drag to resize" />

        {/* ── RIGHT PANEL ─────────────────────────────── */}
        <div className="right-panel scroll" style={{ width: rightW }}>

          {/* ATS Score card */}
          <div className="rp-card">
            <div className="rp-card-h">
              <span style={{ color: "var(--brand-blue)", display: "inline-flex", verticalAlign: "middle" }}><Icon name="recruit" size={16} /></span> ATS Score
              <span className="rp-score-badge" style={{ background: scoreColor(ats.score) + "22", color: scoreColor(ats.score) }}>{scoreLabel(ats.score)}</span>
            </div>
            <div style={{ display: "flex", alignItems: "center", gap: 14, padding: "4px 0 10px" }}>
              <ScoreRobot score={ats.score} size={52} />
              <div>
                <div style={{ fontSize: 13.5, fontWeight: 700, color: "var(--navy)" }}>{ats.score}/100</div>
                <div style={{ fontSize: 12, color: "var(--slate-500)", marginTop: 2 }}>ATS Readiness</div>
              </div>
            </div>
            <button className="rp-btn" onClick={() => { setShowATS(true); setShowJobMatch(false); setShowAI(false); }}>
              View full report →
            </button>
          </div>

          {/* Template card */}
          <div className="rp-card">
            <div className="rp-card-h">Template</div>
            <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 6 }}>
              <span style={{ fontSize: 13, fontWeight: 650, color: "var(--navy)" }}>{TEMPLATES[template]?.name || template}</span>
              <button className="rp-btn-sm" onClick={() => setShowGallery(true)}>Change</button>
            </div>
          </div>

          {/* Design card */}
          <div className="rp-card">
            <div className="rp-card-h">Accent color</div>
            <div className="swatches" style={{ gap: 8 }}>
              {Object.entries(ACCENTS).map(([name, hex]) => (
                <button key={name} className={"swatch" + (design.accent === hex ? " sel" : "")}
                  style={{ background: hex, color: hex, width: 28, height: 28, borderRadius: 8 }}
                  onClick={() => setDesign(d => ({ ...d, accent: hex }))} title={name} />
              ))}
              <button className="swatch custom" style={{ width: 28, height: 28, borderRadius: 8 }} title="Custom color">
                <span style={{ fontSize: 14, color: "var(--slate-500)" }}>+</span>
                <input type="color" value={design.accent || "#1C9BE6"} onChange={e => setDesign(d => ({ ...d, accent: e.target.value }))} />
              </button>
            </div>

            <div className="rp-card-h" style={{ marginTop: 14 }}>Font</div>
            <select className="inp" style={{ width: "100%", fontSize: 12.5, padding: "7px 10px", cursor: "pointer" }}
              value={design.font || "poppins"} onChange={e => setDesign(d => ({ ...d, font: e.target.value }))}>
              {(window.FONTS || []).map(f => (
                <option key={f.key} value={f.key} style={{ fontFamily: f.stack || undefined }}>
                  {f.label}{f.note ? " · " + f.note : ""}
                </option>
              ))}
            </select>

            <div className="rp-card-h" style={{ marginTop: 14 }}>Column layout</div>
            {(template === "split" || template === "sidebar") ? (
              <RpColLayout sidebarSections={sidebarSections} setSidebarSections={setSidebarSections} sectionOrder={sectionOrder} />
            ) : (
              <div style={{ fontSize: 12, color: "var(--slate-400)", padding: "4px 0" }}>Switch to Split or Sidebar template to use columns.</div>
            )}
          </div>

          {/* Versions card */}
          <div className="rp-card">
            <div className="rp-card-h">
              <span style={{ color:"var(--brand-blue)", display:"inline-flex", verticalAlign:"middle" }}><Icon name="copy" size={15} /></span> Version Snapshots
            </div>
            <div style={{ fontSize:12, color:"var(--slate-500)", marginBottom:10, lineHeight:1.5 }}>
              Save named versions to compare or restore.
            </div>
            {showVersionName ? (
              <div style={{ display:"flex", gap:6, marginBottom:8 }}>
                <input className="inp" style={{ flex:1, fontSize:12.5, padding:"6px 10px" }}
                  autoFocus placeholder="e.g. Google Apply v2"
                  value={versionNameInput}
                  onChange={e => setVersionNameInput(e.target.value)}
                  onKeyDown={e => { if (e.key === "Enter") { saveVersion(versionNameInput || undefined); setShowVersionName(false); setVersionNameInput(""); } if (e.key === "Escape") { setShowVersionName(false); setVersionNameInput(""); } }}
                />
                <button className="btn btn-grad btn-sm" onClick={() => { saveVersion(versionNameInput || undefined); setShowVersionName(false); setVersionNameInput(""); }}>Save</button>
              </div>
            ) : (
              <button className="rp-btn" onClick={() => setShowVersionName(true)}>+ Save current version</button>
            )}
            {versions.length > 0 && (
              <div style={{ marginTop:10, display:"flex", flexDirection:"column", gap:4 }}>
                {versions.slice(0, 5).map(v => (
                  <div key={v.id} style={{ display:"flex", alignItems:"center", gap:6, padding:"7px 10px", background:"var(--slate-50)", borderRadius:8, border:"1px solid var(--slate-100)" }}>
                    <div style={{ flex:1, minWidth:0 }}>
                      <div style={{ fontSize:12.5, fontWeight:650, color:"var(--navy)", whiteSpace:"nowrap", overflow:"hidden", textOverflow:"ellipsis" }}>{v.name}</div>
                      <div style={{ fontSize:11, color:"var(--slate-400)" }}>{new Date(v.savedAt).toLocaleDateString()}</div>
                    </div>
                    <button className="btn btn-ghost btn-sm" style={{ fontSize:11, padding:"3px 8px", flexShrink:0 }} onClick={() => restoreVersion(v)}>Restore</button>
                    <button className="icon-btn danger" style={{ flexShrink:0 }} onClick={() => deleteVersion(v.id)}><Icon name="x" size={13} /></button>
                  </div>
                ))}
                {versions.length > 5 && <div style={{ fontSize:11, color:"var(--slate-400)", textAlign:"center" }}>+{versions.length-5} more</div>}
              </div>
            )}
          </div>

          {/* Free AI usage meter (free users only) */}
          {!isPro && <AIUsageBar />}

          {/* Grammar check card */}
          <div className="rp-card">
            <div className="rp-card-h">
              <span style={{ color: "var(--brand-blue)", display: "inline-flex", verticalAlign: "middle" }}><Icon name="writeai" size={16} /></span> AI Writing Tools
            </div>
            <div style={{ fontSize: 12, color: "var(--slate-500)", marginBottom: 10, lineHeight: 1.5 }}>
              Scan and fix grammar, spelling, and phrasing across your entire résumé in one click.
            </div>
            <button className="rp-btn" style={{ background: grammarDone ? "var(--brand-green)" : undefined, color: grammarDone ? "#fff" : undefined, borderColor: grammarDone ? "var(--brand-green)" : undefined, transition: "all .3s" }}
              disabled={grammarBusy || !window.groqStream} onClick={checkGrammar}>
              {grammarBusy
                ? <><span className="spin" style={{ width: 13, height: 13, borderColor: "rgba(28,155,230,.25)", borderTopColor: "var(--brand-blue)", display: "inline-block", verticalAlign: "middle", marginRight: 6 }} />Checking…</>
                : grammarDone ? "✓ Fixed!" : <><Icon name="writeai" size={14} style={{ verticalAlign: "middle", marginRight: 6 }} />Check & Fix Grammar</>}
            </button>
          </div>

          {/* Keyboard shortcuts */}
          <div style={{ padding: "0 16px 8px" }}>
            <button className="rp-btn" style={{ color: "var(--slate-500)", background: "transparent", border: "1px solid var(--slate-200)" }} onClick={() => setShowKeyboard(true)}>
              ⌨ Keyboard shortcuts
            </button>
          </div>
        </div>

        {showAI && <AIPanel data={data} patch={patch} coverLetter={coverLetter} setCoverLetter={setCoverLetter}
          showToast={showToast}
          onGotoCoverLetter={() => { setEditorTab("coverletter"); setShowAI(false); }}
          onClose={() => setShowAI(false)} />}
      </div>

      {/* ── MODALS & DRAWERS ───────────────────────────── */}
      {showGallery && (
        <Gallery template={template} plan={plan}
          onPick={(k) => { setTemplate(k); const da = TEMPLATES[k] && TEMPLATES[k].defaultAccent; if (da) setDesign(d => ({ ...d, accent: da })); }}
          onClose={() => setShowGallery(false)}
          data={data} accent={accent} design={design} />
      )}
      {showATS           && <ATSDrawer ats={ats} onClose={() => setShowATS(false)} setSection={setSection} />}
      {showJobMatch      && <JobMatchDrawer data={data} onClose={() => setShowJobMatch(false)} />}
      {showTailor        && <TailorDrawer data={data} patch={patch} coverLetter={coverLetter} setCoverLetter={setCoverLetter} showToast={showToast} onGotoCoverLetter={() => { setEditorTab("coverletter"); setShowTailor(false); }} onClose={() => setShowTailor(false)} />}
      {showInterviewPrep && <InterviewPrepDrawer data={data} onClose={() => setShowInterviewPrep(false)} />}
      {showTranslate     && <TranslateDrawer data={data} patch={patch} onClose={() => setShowTranslate(false)} />}
      {showSalary        && <SalaryDrawer data={data} onClose={() => setShowSalary(false)} />}
      {showAchievement   && <AchievementDrawer data={data} patch={patch} onClose={() => setShowAchievement(false)} />}
      {showColdEmail     && <ColdEmailDrawer data={data} onClose={() => setShowColdEmail(false)} />}
      {showShare    && (
        <ShareModal docName={docName} copied={shareCopied}
          onClose={() => setShowShare(false)}
          onDownloadJSON={() => { exportJSON(); setShowShare(false); }}
          onCopyLink={copyShareLink}
          onCopyJSON={copyJSON}
          onImport={(payload) => {
            if (payload.data) setData(payload.data);
            if (payload.docName) setDocName(payload.docName);
            if (payload.template) setTemplate(payload.template);
            if (payload.design) setDesign(payload.design);
            if (payload.coverLetter) setCoverLetter(payload.coverLetter);
            setShowShare(false); showToast("Résumé imported");
          }}
        />
      )}
      {showKeyboard && <KeyboardShortcutsModal onClose={() => setShowKeyboard(false)} />}
      {showProExport && (
        <div className="modal-back" onClick={() => setShowProExport(false)}>
          <div className="modal" style={{ width: 400, padding: "36px 32px 28px", textAlign: "center" }} onClick={e => e.stopPropagation()}>
            <div style={{ width: 52, height: 52, borderRadius: "50%", background: "var(--grad)", display: "flex", alignItems: "center", justifyContent: "center", margin: "0 auto 16px" }}>
              <Icon name="lock" size={22} style={{ color: "#fff" }} />
            </div>
            <h3 style={{ margin: "0 0 8px", fontSize: 18 }}>
              {proReason === "limit" ? "Free Downloads Used Up" : "Pro Access Needed to Export"}
            </h3>
            <p style={{ margin: "0 0 24px", color: "var(--slate-500)", fontSize: 13.5, lineHeight: 1.55 }}>
              {proReason === "limit"
                ? <>You've used all <strong>{FREE_DOWNLOAD_LIMIT}</strong> free downloads. Upgrade to Pro for unlimited PDF downloads and every premium template.</>
                : <>You're using <strong>{TEMPLATES[template]?.name}</strong>, a Pro template. The free plan includes the <strong>Prestige</strong> template with {FREE_DOWNLOAD_LIMIT} downloads — switch to it, or upgrade for every template.</>}
            </p>
            <a href={LANDING_URL + "/pricing"} target="_blank" rel="noopener noreferrer"
              className="btn btn-grad" style={{ textDecoration: "none", display: "inline-flex", alignItems: "center", gap: 7, height: 40, padding: "0 22px", fontSize: 14, fontWeight: 600 }}>
              Upgrade to Pro →
            </a>
            <div style={{ marginTop: 12 }}>
              <button className="btn btn-ghost btn-sm" onClick={() => setShowProExport(false)}>Maybe later</button>
            </div>
          </div>
        </div>
      )}
      {showImport && <ImportCVModal
        onApply={(imported) => {
          const snapshot = data;
          patch(() => imported);
          setShowImport(false);
          setSection("basics");
          setEditorTab("resume");
          showToast("CV imported — review and edit your details.", {
            label: "Undo",
            fn: () => { patch(() => snapshot); showToast("Import undone."); }
          });
        }}
        onClose={() => setShowImport(false)} />}

      {tabletPreview && (
        <div className="modal-back" onClick={() => setTabletPreview(false)} style={{ alignItems: "flex-start", padding: "52px 12px 12px" }}>
          <div onClick={e => e.stopPropagation()} style={{ width: "100%", maxWidth: 640, background: "#EBEDF4", borderRadius: 14, padding: "48px 12px 16px", position: "relative" }}>
            <button className="x" onClick={() => setTabletPreview(false)}
              style={{ position: "absolute", top: 10, right: 10, background: "rgba(255,255,255,.7)" }}>
              <Icon name="x" size={18} />
            </button>
            <div style={{ overflowX: "auto" }}>
              <div style={{ transform: `scale(${Math.min(0.65, (window.innerWidth - 40) / 816)})`, transformOrigin: "top left", width: 816, flexShrink: 0 }}>
                <div className="page">
                  <Resume data={data} template={template} accent={accent} design={design} sectionOrder={sectionOrder} sidebarSections={sidebarSections} />
                </div>
              </div>
            </div>
            <div style={{ display: "flex", gap: 10, marginTop: 16, justifyContent: "center" }}>
              <button className="btn btn-grad btn-sm" onClick={() => { setTabletPreview(false); exportExact(); }}>
                <Icon name="eye" size={14} /> Exact PDF
              </button>
              <button className="btn btn-ghost btn-sm" onClick={() => { setTabletPreview(false); exportPDF(); }}>
                <Icon name="bolt" size={14} /> Quick ATS PDF
              </button>
            </div>
          </div>
        </div>
      )}

      {toast && (
        <div className="toast">
          {toast.msg}
          {toast.action && (
            <button onClick={() => { toast.action.fn(); setToast(null); }}
              style={{ marginLeft: 12, fontWeight: 700, color: "var(--brand-blue)", background: "none", border: "none", cursor: "pointer", fontSize: 13, padding: 0, textDecoration: "underline" }}>
              {toast.action.label}
            </button>
          )}
        </div>
      )}

      <TweaksPanel>
        <TweakSection label="Workspace" />
        <TweakRadio label="Layout" value={t.layout}
          options={["split", "focus", "preview"]} onChange={(v) => setTweak("layout", v)} />
        <TweakRadio label="Form density" value={t.density}
          options={["compact", "regular", "comfy"]} onChange={(v) => setTweak("density", v)} />
        <TweakButton label="Reset sample résumé" onClick={() => { setData(SAMPLE_RESUME); }} />
      </TweaksPanel>
    </div>
  );
}

/* ── Share / Export modal ──────────────────────────────────── */
function ShareModal({ docName, onClose, onDownloadJSON, onCopyLink, onCopyJSON, onImport, copied }) {
  const fileRef = useRef(null);
  const handleImport = (e) => {
    const f = e.target.files && e.target.files[0];
    if (!f) return;
    const reader = new FileReader();
    reader.onload = (ev) => {
      try { onImport(JSON.parse(ev.target.result)); } catch { alert("Invalid résumé file."); }
    };
    reader.readAsText(f);
    e.target.value = "";
  };
  return (
    <div className="modal-back" onClick={onClose}>
      <div className="modal share-modal" onClick={e => e.stopPropagation()}>
        <div className="modal-h">
          <div>
            <h3>Share &amp; Export</h3>
            <p>Export your résumé data or generate a shareable link.</p>
          </div>
          <div style={{ flex: 1 }} />
          <button className="x" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="share-body">
          <div className="share-row" onClick={onDownloadJSON}>
            <span className="share-ic grad"><Icon name="export" size={18} /></span>
            <div className="share-tx">
              <b>Download JSON</b>
              <span>Save all résumé data as a portable file. Import it back here any time.</span>
            </div>
            <Icon name="chevR" size={16} style={{ color: "var(--slate-400)" }} />
          </div>

          <div className="share-row" onClick={() => fileRef.current && fileRef.current.click()}>
            <span className="share-ic"><Icon name="import" size={18} /></span>
            <div className="share-tx">
              <b>Import JSON</b>
              <span>Load a previously exported résumé and replace the current one.</span>
            </div>
            <Icon name="chevR" size={16} style={{ color: "var(--slate-400)" }} />
          </div>
          <input ref={fileRef} type="file" accept=".json" style={{ display: "none" }} onChange={handleImport} />

          <div className="share-divider">or share</div>

          <div className="share-row" onClick={onCopyLink}>
            <span className="share-ic" style={copied === "link" ? { background: "rgba(22,163,107,.12)", color: "var(--good)" } : {}}>
              <Icon name={copied === "link" ? "check" : "link"} size={18} />
            </span>
            <div className="share-tx">
              <b>{copied === "link" ? "Link copied!" : "Copy share link"}</b>
              <span>Generates a URL with your résumé data embedded — share directly in a browser.</span>
            </div>
            <Icon name="chevR" size={16} style={{ color: "var(--slate-400)" }} />
          </div>

          <div className="share-row" onClick={onCopyJSON}>
            <span className="share-ic" style={copied === "json" ? { background: "rgba(22,163,107,.12)", color: "var(--good)" } : {}}>
              <Icon name={copied === "json" ? "check" : "copy"} size={18} />
            </span>
            <div className="share-tx">
              <b>{copied === "json" ? "Copied!" : "Copy data to clipboard"}</b>
              <span>Copy the raw JSON — paste it into another import dialog.</span>
            </div>
            <Icon name="chevR" size={16} style={{ color: "var(--slate-400)" }} />
          </div>

          <div className="share-note">
            <Icon name="alert" size={13} style={{ flexShrink: 0, color: "var(--warn)" }} />
            The share link embeds your full résumé data in the URL. Only share with people you trust.
          </div>
        </div>
      </div>
    </div>
  );
}

/* ── Keyboard shortcuts modal ──────────────────────────────── */
function KeyboardShortcutsModal({ onClose }) {
  const shortcuts = [
    { keys: ["Ctrl", "Z"],        label: "Undo" },
    { keys: ["Ctrl", "Y"],        label: "Redo" },
    { keys: ["Ctrl", "Shift", "Z"], label: "Redo (alternate)" },
    { keys: ["Ctrl", "S"],        label: "Save to local storage" },
  ];
  return (
    <div className="modal-back" onClick={onClose}>
      <div className="modal" style={{ width: 400 }} onClick={e => e.stopPropagation()}>
        <div className="modal-h">
          <div><h3>Keyboard shortcuts</h3></div>
          <div style={{ flex: 1 }} />
          <button className="x" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div style={{ padding: "16px 24px 24px", display: "flex", flexDirection: "column", gap: 10 }}>
          {shortcuts.map((s, i) => (
            <div key={i} style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
              <span style={{ fontSize: 13.5, color: "var(--slate-700)" }}>{s.label}</span>
              <span style={{ display: "flex", gap: 4 }}>
                {s.keys.map((k, j) => <kbd key={j} className="kbd">{k}</kbd>)}
              </span>
            </div>
          ))}
          <div style={{ marginTop: 8, padding: "10px 12px", background: "var(--slate-50)", borderRadius: "var(--r-sm)", fontSize: 12.5, color: "var(--slate-500)" }}>
            Tip: Mac users, use <kbd className="kbd">⌘</kbd> instead of <kbd className="kbd">Ctrl</kbd>.
          </div>
        </div>
      </div>
    </div>
  );
}

/* ── Add section menu ──────────────────────────────────────── */
function AddSectionMenu({ sectionOrder, setSectionOrder, setSection, setEditorTab, setData }) {
  const [open, setOpen] = React.useState(false);
  const addTyped = (id) => {
    if (!sectionOrder.includes(id)) setSectionOrder(s => [...s, id]);
    setEditorTab("resume"); setSection(id); setOpen(false);
  };
  const addCustom = () => {
    const id = uidA();
    setData(d => ({ ...d, custom: [...(d.custom || []), { id, heading: "Custom Section", body: "" }] }));
    setEditorTab("resume"); setSection("custom:" + id); setOpen(false);
  };
  return (
    <div style={{ position: "relative" }}>
      {open && <div onClick={() => setOpen(false)} style={{ position: "fixed", inset: 0, zIndex: 49 }} />}
      <button className="accord-add-btn" onClick={() => setOpen(o => !o)}>
        <Icon name="plus" size={14} /> Add section
      </button>
      {open && (
        <div className="add-sec-menu">
          {ADDABLE_SECTIONS.map(s => {
            const already = sectionOrder.includes(s.id);
            return (
              <button key={s.id} className={"add-sec-item" + (already ? " done" : "")}
                onClick={() => !already && addTyped(s.id)} disabled={already}>
                <div className="add-sec-label">{s.label}{already && <span className="add-sec-check">✓</span>}</div>
                <div className="add-sec-desc">{s.desc}</div>
              </button>
            );
          })}
          <div className="add-sec-divider" />
          <button className="add-sec-item" onClick={addCustom}>
            <div className="add-sec-label">Custom section</div>
            <div className="add-sec-desc">Blank section with rich text</div>
          </button>
        </div>
      )}
    </div>
  );
}

/* ── Template gallery ──────────────────────────────────────── */
function Gallery({ template, onPick, onClose, data, accent, design, plan }) {
  const [filter,     setFilter]     = React.useState("all");
  const [previewing, setPreviewing] = React.useState(template);
  const [previewScale, setPreviewScale] = React.useState(0.85);
  const canvasRef = React.useRef(null);

  React.useLayoutEffect(() => {
    if (!canvasRef.current) return;
    const obs = new ResizeObserver(entries => {
      const w = entries[0].contentRect.width - 48;
      setPreviewScale(Math.min(1, w / 816));
    });
    obs.observe(canvasRef.current);
    return () => obs.disconnect();
  }, []);

  React.useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [onClose]);

  const allKeys  = Object.keys(TEMPLATES);
  const freeKeys = allKeys.filter(k => FREE_TEMPLATES.includes(k));
  const proKeys  = allKeys.filter(k => !FREE_TEMPLATES.includes(k));
  const orderedAll = [...freeKeys, ...proKeys]; // free template(s) shown first
  const visible  = filter === "free" ? freeKeys : filter === "pro" ? proKeys : orderedAll;
  const prevTpl  = TEMPLATES[previewing];
  const isProPrev = !FREE_TEMPLATES.includes(previewing);

  return (
    <div className="modal-back" onClick={onClose}>
      <div className="modal gal-modal" onClick={e => e.stopPropagation()}>

        {/* ── Left panel: thumbnail list ── */}
        <div className="gal-left">
          <div className="gal-left-head">
            <h3 className="gal-title">Templates</h3>
            <button className="x" onClick={onClose}><Icon name="x" size={17} /></button>
          </div>

          {/* Filter tabs */}
          <div className="gal-tabs">
            {[["all","All",allKeys.length],["free","Free",freeKeys.length],["pro","Pro",proKeys.length]].map(([id,label,count]) => (
              <button key={id} className={"gal-tab"+(filter===id?" active":"")} onClick={() => setFilter(id)}>
                {label} <span className="gal-tab-count">{count}</span>
              </button>
            ))}
          </div>

          {/* Thumbnail grid */}
          <div className="gal-thumb-list scroll">
            {visible.map(key => {
              const isProTpl = !FREE_TEMPLATES.includes(key);
              const isSel    = template === key;
              const isPrev   = previewing === key;
              return (
                <div key={key}
                  className={"gal-thumb-card"+(isPrev?" prev":"")+(isSel?" sel":"")}
                  onMouseEnter={() => setPreviewing(key)}
                  onClick={() => { onPick(key); onClose(); }}>
                  <div className="gal-thumb-wrap">
                    <div className="mini">
                      <div style={{ width: 816, background: "#fff", pointerEvents: "none" }}>
                        <Resume data={window.GALLERY_RESUME} template={key}
                          accent={TEMPLATES[key]?.defaultAccent || "#1C9BE6"}
                          design={{ font: "auto", accent: TEMPLATES[key]?.defaultAccent || "#1C9BE6" }} />
                      </div>
                    </div>
                    {isSel && <div className="gal-sel-check"><Icon name="check" size={11} style={{ color:"#fff" }} /></div>}
                    {isProTpl && <div className="tpl-pro-corner"><span>Pro</span></div>}
                  </div>
                  <div className="gal-thumb-name">{TEMPLATES[key].name}</div>
                </div>
              );
            })}
          </div>

          {/* Bottom CTA */}
          <div className="gal-left-foot">
            <button className={"btn btn-grad"+(template===previewing?" btn-ghost":"")} style={{ width:"100%", justifyContent:"center" }}
              onClick={() => { onPick(previewing); onClose(); }}>
              {template === previewing ? "Continue Editing" : "Use This Template →"}
            </button>
          </div>
        </div>

        {/* ── Right panel: full live preview ── */}
        <div className="gal-right">
          <div className="gal-preview-bar">
            <span className="gal-preview-name">{prevTpl?.name}</span>
            {TEMPLATES[previewing]?.badge && <span className="badge" style={{ fontSize:11, color:"var(--slate-500)" }}>{TEMPLATES[previewing].badge}</span>}
            {isProPrev && <span className="tpl-meta-pro">Pro</span>}
            <span className="ats" style={{ marginLeft:"auto", fontSize:11, fontWeight:700, color:"var(--good)", background:"rgba(22,163,107,.1)", padding:"2px 9px", borderRadius:99 }}>
              <Icon name="check" size={11} style={{ verticalAlign:"-1px" }} /> ATS Ready
            </span>
          </div>
          <div className="gal-preview-canvas scroll" ref={canvasRef}>
            <div style={{ padding: "24px", display: "flex", justifyContent: "center" }}>
              <div style={{ width: 816 * previewScale, flexShrink: 0, overflow: "hidden", borderRadius: 4, boxShadow: "0 4px 32px rgba(15,23,60,.12)" }}>
                <div style={{ width: 816, transformOrigin: "top left", transform: `scale(${previewScale})`, pointerEvents: "none", background: "#fff" }}>
                  <Resume data={window.GALLERY_RESUME} template={previewing}
                    accent={TEMPLATES[previewing]?.defaultAccent || "#1C9BE6"}
                    design={{ font: "auto", accent: TEMPLATES[previewing]?.defaultAccent || "#1C9BE6" }} />
                </div>
              </div>
            </div>
          </div>
        </div>

      </div>
    </div>
  );
}

/* ── ATS report drawer ─────────────────────────────────────── */
const ATS_FIX_SECTIONS = {
  "Add missing contact details": "basics",
  "Parseable email address": "basics",
  "Tighten or add a summary": "summary",
  "Quantify more achievements": "experience",
  "Start bullets with action verbs": "experience",
  "Add more relevant keywords": "skills",
};

function ATSDrawer({ ats, onClose, setSection }) {
  return (
    <>
      <div className="drawer-back" onClick={onClose} />
      <div className="drawer">
        <div className="drawer-h" style={{ display: "flex", alignItems: "center" }}>
          <div>
            <h3 style={{ margin: 0, fontSize: 16, fontWeight: 700, color: "var(--navy)" }}>ATS report</h3>
            <p style={{ margin: "3px 0 0", fontSize: 12.5, color: "var(--slate-500)" }}>How well a parser &amp; recruiter will read this résumé.</p>
          </div>
          <div style={{ flex: 1 }} />
          <button className="x" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="drawer-body scroll">
          <div className="score-hero">
            <RingGauge value={ats.score} size={84} stroke={7} color={scoreColor(ats.score)}>
              <div className="n"><b>{ats.score}</b><span>/ 100</span></div>
            </RingGauge>
            <div>
              <div style={{ fontSize: 16, fontWeight: 700, color: scoreColor(ats.score) }}>{scoreLabel(ats.score)}</div>
              <p style={{ margin: "5px 0 0", fontSize: 12.8, color: "var(--slate-600)", lineHeight: 1.45 }}>
                Fix the flagged items below to climb toward a 90+ score.
              </p>
            </div>
          </div>

          <div className="cat-h" style={{ marginTop: 0 }}>Category breakdown</div>
          {ats.categories.map(c => (
            <div className="cat" key={c.key} style={{ marginBottom: 12 }}>
              <div style={{ display: "flex", justifyContent: "space-between", fontSize: 12.5, fontWeight: 600, color: "var(--slate-700)", marginBottom: 5 }}>
                <span>{c.key}</span><span style={{ color: "var(--slate-500)" }}>{Math.round(c.score * 100)}%</span>
              </div>
              <div className="bar"><i style={{ width: `${c.score * 100}%` }} /></div>
            </div>
          ))}

          <div className="cat-h">Checklist</div>
          {ats.checks.map((c, i) => {
            const fixSection = c.state !== "ok" ? ATS_FIX_SECTIONS[c.title] : null;
            return (
              <div className="check" key={i}>
                <span className={"ic " + c.state}>
                  <Icon name={c.state === "ok" ? "check" : c.state === "wn" ? "alert" : "minus"} size={13} strokeWidth={2.4} />
                </span>
                <div className="tx">
                  <h5 style={{ display: "flex", alignItems: "center", gap: 8 }}>
                    {c.title}
                    {fixSection && (
                      <button onClick={() => { onClose(); setSection(fixSection); }}
                        style={{ fontSize: 11, fontWeight: 600, color: "var(--brand-blue)", background: "none", border: "none", cursor: "pointer", padding: "0 4px", flexShrink: 0 }}>
                        Fix it →
                      </button>
                    )}
                  </h5>
                  <p>{c.desc}</p>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </>
  );
}

ReactDOM.createRoot(document.getElementById("root")).render(<App />);
