/* Campaigns — plan a multi-post campaign end to end: brief → AI ideas (with the
   logic + why it works) → refine in chat → drafted post sequence (text, schedule,
   media, status) → confirm, which creates tagged posts that flow into Posts,
   Calendar, Board and the Queue. All AI is simulated (window.CroprGen). */
(function () {
  const S = window.CroprStore;
  const D = window.CROPR_DATA;
  const G = window.CroprGen;

  const fieldStyle = { width: "100%", boxSizing: "border-box", fontFamily: "inherit", fontSize: 13.5, color: "var(--fg1)", background: "var(--surface)", border: "1px solid var(--border-strong)", borderRadius: 10, padding: "9px 11px", outline: "none" };
  const Label = ({ children }) => <span style={{ fontSize: 12, fontWeight: 600, color: "var(--fg2)", display: "block", marginBottom: 6 }}>{children}</span>;
  const readFile = async (f, cb) => { if (!f) return; const m = await window.LeapUpload.readAndHost(f); if (m) cb(m); };
  const fmtDay = (iso) => { try { return new Date(iso).toLocaleDateString("en-GB", { day: "2-digit", month: "short" }); } catch (e) { return "—"; } };
  const toLocalInput = (iso) => { try { const d = new Date(iso); const p = (n) => String(n).padStart(2, "0"); return `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())}T${p(d.getHours())}:${p(d.getMinutes())}`; } catch (e) { return ""; } };
  const A = window.CroprAnalytics;
  const compact = (n) => (n >= 1000 ? (n / 1000).toFixed(n >= 10000 ? 0 : 1).replace(/\.0$/, "") + "K" : "" + (n || 0));
  const nf = (n) => Number(n || 0).toLocaleString("en-GB");

  // the live posts belonging to a campaign (by launched id, campaign name or tag)
  function campaignPosts(c) {
    const ids = new Set(c.launchedPostIds || []);
    return S.getAll().filter((p) => ids.has(p.id) || (c.name && p.campaign === c.name) || (c.tag && (p.tags || []).includes(c.tag)));
  }
  function campaignAgg(c) {
    const posts = campaignPosts(c);
    const pub = posts.filter((p) => A.isPublished(p));
    const sum = (k) => pub.reduce((s, p) => s + (A.metricsFor(p)[k] || 0), 0);
    return { impressions: sum("impressions"), engagement: sum("engagement"), likes: sum("likes"), shares: sum("shares"), comments: sum("comments"), postCount: posts.length, publishedCount: pub.length };
  }
  function campaignPhase(c) {
    if (c.status !== "launched") return "planned";
    const now = Date.now(); const end = c.endDate ? new Date(c.endDate).getTime() : null;
    return end && end < now ? "past" : "active";
  }
  function campaignChannels(c) {
    if (c.channels && c.channels.length) return c.channels;
    const set = new Set(); campaignPosts(c).forEach((p) => (p.channels || []).forEach((ch) => set.add(ch))); return [...set];
  }
  // per-platform aggregate (metrics split across a post's platforms)
  function campaignByPlatform(c) {
    const map = {};
    campaignPosts(c).filter((p) => A.isPublished(p)).forEach((p) => {
      const plats = A.platformsOf(p); const m = A.metricsFor(p); const div = plats.length || 1;
      plats.forEach((pl) => { const e = map[pl] = map[pl] || { impressions: 0, engagement: 0, likes: 0, shares: 0, posts: 0 };
        e.impressions += Math.round(m.impressions / div); e.engagement += Math.round(m.engagement / div); e.likes += Math.round(m.likes / div); e.shares += Math.round(m.shares / div); e.posts += 1; });
    });
    return map;
  }
  const PHASES = [{ id: "active", label: "Active", icon: "radio", color: "#12B76A" }, { id: "planned", label: "Planned", icon: "pencil-ruler", color: "#0EA5B7" }, { id: "past", label: "Past", icon: "archive", color: "#6B7280" }];
  const PERF_COLS = [{ id: "impressions", label: "Impr" }, { id: "engagement", label: "Eng" }, { id: "likes", label: "Likes" }, { id: "shares", label: "Shares" }, { id: "comments", label: "Comm" }];

  function ChannelChips({ selected, onToggle }) {
    return (
      <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
        {D.CHANNEL_ORDER.map((id) => {
          const meta = D.CHANNELS[id]; const on = selected.includes(id);
          return (
            <button key={id} type="button" onClick={() => onToggle(id)}
              style={{ display: "inline-flex", alignItems: "center", gap: 6, border: `1px solid ${on ? "var(--leap-teal)" : "var(--border-strong)"}`, background: on ? "var(--accent-weak)" : "transparent", color: on ? "var(--accent)" : "var(--fg2)", borderRadius: 999, padding: "4px 11px 4px 5px", fontSize: 12, fontWeight: 600, cursor: "pointer", fontFamily: "inherit" }}>
              <PlatformLogo platform={meta.platform} size={16} style={{ borderRadius: 4 }} />{D.label(D.PLATFORMS, meta.platform)}
            </button>
          );
        })}
      </div>
    );
  }

  // per-post media: choose from the asset library (primary) or upload (fallback)
  function MediaButton({ media, onChange, onToast }) {
    const fileRef = React.useRef(null);
    const [pick, setPick] = React.useState(false);
    const cur = (media || [])[0];
    return (
      <div style={{ position: "relative" }}>
        {cur ? (
          <div onClick={() => setPick(true)} title="Change visual" style={{ position: "relative", width: 64, height: 64, borderRadius: 10, overflow: "hidden", border: "1px solid var(--border)", cursor: "pointer" }}>
            {cur.kind === "video" ? <video src={cur.url} muted style={{ width: "100%", height: "100%", objectFit: "cover" }} /> : <img src={cur.url} alt="" style={{ width: "100%", height: "100%", objectFit: "cover" }} />}
            <button onClick={(e) => { e.stopPropagation(); onChange([]); }} title="Remove" style={{ position: "absolute", top: 2, right: 2, width: 18, height: 18, borderRadius: 999, border: 0, background: "rgba(11,59,66,.7)", cursor: "pointer", display: "grid", placeItems: "center" }}><Icon name="x" size={11} color="#fff" /></button>
          </div>
        ) : (
          <div style={{ display: "flex", flexDirection: "column", gap: 4 }}>
            <button onClick={() => setPick(true)} style={{ ...fieldStyle, width: 64, height: 40, cursor: "pointer", display: "grid", placeItems: "center", padding: 0, borderColor: "var(--accent)" }} title="Choose from Assets"><Icon name="images" size={16} color="var(--accent)" /></button>
            <button onClick={() => fileRef.current && fileRef.current.click()} style={{ fontSize: 10.5, color: "var(--fg3)", background: "none", border: 0, cursor: "pointer", fontFamily: "inherit", fontWeight: 600 }}>Upload</button>
          </div>
        )}
        <input ref={fileRef} type="file" accept="image/*,video/*" style={{ display: "none" }} onChange={(e) => { readFile((e.target.files || [])[0], (m) => onChange([{ ...m, status: "ready" }])); e.target.value = ""; }} />
        <AssetPicker open={pick} onClose={() => setPick(false)} selectedUrls={cur ? [cur.url] : []}
          onPick={(a) => { S.useAsset(a.id); onChange([{ url: a.url, kind: a.kind, status: "ready", altText: a.name }]); onToast && onToast(`Used “${a.name}”`); }} />
      </div>
    );
  }

  // campaign-level visuals: choose several from Assets (primary) or upload
  function VisualsPicker({ visuals, onChange, onToast }) {
    const fileRef = React.useRef(null);
    const [pick, setPick] = React.useState(false);
    const list = visuals || [];
    const toggle = (a) => {
      if (list.some((v) => v.url === a.url)) { onChange(list.filter((v) => v.url !== a.url)); return; }
      S.useAsset(a.id); onChange([...list, { url: a.url, kind: a.kind, status: "ready", name: a.name, altText: a.name }]);
    };
    return (
      <div>
        <div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 12 }}>
          <Button size="sm" variant="primary" icon="images" onClick={() => setPick(true)}>Choose from Assets</Button>
          <Button size="sm" variant="secondary" icon="upload" onClick={() => fileRef.current && fileRef.current.click()}>Upload</Button>
        </div>
        <input ref={fileRef} type="file" accept="image/*,video/*" style={{ display: "none" }} onChange={(e) => { readFile((e.target.files || [])[0], (m) => onChange([...list, m])); e.target.value = ""; }} />
        <div style={{ display: "flex", gap: 10, flexWrap: "wrap", alignItems: "center" }}>
          {list.map((v, i) => (
            <div key={i} style={{ position: "relative", width: 84, height: 84, borderRadius: 12, overflow: "hidden", border: "1px solid var(--border)" }}>
              {v.kind === "video" ? <video src={v.url} muted style={{ width: "100%", height: "100%", objectFit: "cover" }} /> : <img src={v.url} alt="" style={{ width: "100%", height: "100%", objectFit: "cover" }} />}
              <button onClick={() => onChange(list.filter((_, j) => j !== i))} style={{ position: "absolute", top: 3, right: 3, width: 18, height: 18, borderRadius: 999, border: 0, background: "rgba(11,59,66,.7)", cursor: "pointer", display: "grid", placeItems: "center" }}><Icon name="x" size={11} color="#fff" /></button>
            </div>
          ))}
          {list.length === 0 && <div style={{ fontSize: 12, color: "var(--fg4)" }}>No visuals yet — choose from your assets or upload.</div>}
        </div>
        <AssetPicker open={pick} onClose={() => setPick(false)} onToggle={toggle} selectedUrls={list.map((v) => v.url)}
          title="Choose campaign visuals" subtitle="Pick key visuals from your asset library. Click to add or remove." />
      </div>
    );
  }

  const STEPS = [{ k: "brief", label: "Brief" }, { k: "ideas", label: "Idea" }, { k: "concept", label: "Sequence" }, { k: "review", label: "Confirm" }];

  function Stepper({ step, onStep }) {
    return (
      <div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 20, flexWrap: "wrap" }}>
        {STEPS.map((s, i) => (
          <React.Fragment key={s.k}>
            <button onClick={() => onStep(i)} style={{ display: "inline-flex", alignItems: "center", gap: 7, border: 0, background: "none", cursor: "pointer", fontFamily: "inherit" }}>
              <span style={{ width: 24, height: 24, borderRadius: 999, display: "grid", placeItems: "center", fontSize: 12, fontWeight: 700, background: i <= step ? "var(--gradient-core)" : "var(--bg-muted)", color: i <= step ? "#fff" : "var(--fg3)" }}>{i + 1}</span>
              <span style={{ fontSize: 13, fontWeight: 600, color: i === step ? "var(--fg1)" : "var(--fg3)" }}>{s.label}</span>
            </button>
            {i < STEPS.length - 1 && <span style={{ width: 22, height: 2, background: "var(--border-strong)" }} />}
          </React.Fragment>
        ))}
      </div>
    );
  }

  // ---------------- Builder ----------------------------------------------
  function CampaignBuilder({ id, onExit, onToast }) {
    const existing = id ? S.getCampaign(id) : null;
    const [c, setC] = React.useState(existing || { name: "", intent: "Educate", goal: "awareness", audience: "", startDate: "", endDate: "", channels: ["x_cropr"], visuals: [], ideaOptions: [], selectedIdeaId: null, chat: [], concept: null, tag: "" });
    const [step, setStep] = React.useState(0);
    const [busy, setBusy] = React.useState(false);
    const [chatDraft, setChatDraft] = React.useState("");
    const [manual, setManual] = React.useState({ open: false, title: "", angle: "", logic: "", why: "" });
    const patch = (u) => setC((x) => ({ ...x, ...u }));
    const persist = (extra) => { const saved = S.saveCampaign({ ...c, ...(extra || {}) }); if (!c.id) setC((x) => ({ ...x, id: saved.id })); return saved; };
    const selectedIdea = (c.ideaOptions || []).find((i) => i.id === c.selectedIdeaId) || null;

    const toggleChan = (id2) => patch({ channels: c.channels.includes(id2) ? c.channels.filter((x) => x !== id2) : [...c.channels, id2] });
    // Live AI when a Leap API is configured; else the local generator (offline demo).
    const liveOrg = () => { try { const cl = S.getActiveClient && S.getActiveClient(); return cl && cl._orgId ? cl._orgId : null; } catch (e) { return null; } };
    const canLive = () => { const A2 = window.LeapAPI; return !!(A2 && A2.configured() && liveOrg()); };
    const campaignInput = () => {
      const brand = (S.getBrand && S.getBrand()) || {};
      const goalLabel = ((D.CAMPAIGN_GOALS || []).find((g) => g.id === c.goal) || {}).label || c.goal || null;
      return { name: c.name, goal: goalLabel, intent: c.intent, audience: c.audience, campaignBrief: c.campaignBrief, channels: c.channels || [], startDate: c.startDate, endDate: c.endDate, brandBrief: brand.brandBrief, brandVoice: brand.brandVoice };
    };
    // Server returns post CONTENT; fill in per-channel format + a schedule spread
    // across the campaign date range here (where CROPR_DATA lives), like the local gen.
    const conceptFromAI = (ai) => {
      const D2 = window.CROPR_DATA, channels = (c.channels && c.channels.length) ? c.channels : ["x_cropr"];
      const start = c.startDate ? new Date(c.startDate) : new Date();
      let end = c.endDate ? new Date(c.endDate) : new Date(start.getTime() + 7 * 864e5);
      if (end <= start) end = new Date(start.getTime() + 7 * 864e5);
      const span = end.getTime() - start.getTime(), aiPosts = ai.posts || [], n = aiPosts.length || 1;
      const posts = aiPosts.map((p, i) => {
        const channel = (p.channel && channels.includes(p.channel)) ? p.channel : channels[i % channels.length];
        const format = D2.defaultFormat((D2.CHANNELS[channel] || {}).platform);
        const at = new Date(start.getTime() + (n === 1 ? 0 : span * (i / (n - 1)))); at.setHours(i % 2 ? 15 : 9, 0, 0, 0);
        return { id: "cp_" + i, channel, format, phase: p.phase || "", title: p.title || (c.name + " — " + (p.phase || "Post")), body: p.body || "", media: [], status: "ready", visualIdea: p.visualIdea || "", scheduleAt: at.toISOString() };
      });
      return { summary: ai.summary || "", posts };
    };

    const genIdeas = async (append) => {
      if (!c.name.trim()) { onToast("Name your campaign first", "error"); return; }
      setBusy(true);
      try {
        let fresh = null;
        if (canLive()) {
          const r = await window.LeapAPI.assistant.campaignIdeas(liveOrg(), { ...campaignInput(), want: 4, avoidTitles: append ? (c.ideaOptions || []).map((i) => i.title) : [] });
          if (r && r.enabled && r.ideas && r.ideas.length) fresh = r.ideas.map((it, i) => ({ id: "ai-" + (c.ideaOptions || []).length + "-" + i + "-" + it.title.length, baseKey: null, fitsIntent: !!it.fitsIntent, title: it.title, angle: it.angle, logic: it.logic, why: it.why }));
        }
        if (!fresh) { const existingKeys = append ? (c.ideaOptions || []).map((i) => i.baseKey || i.id) : []; fresh = G.campaignIdeas({ ...c, brand: S.getBrand() }, existingKeys, 4); }
        const ideas = append ? [...(c.ideaOptions || []), ...fresh] : fresh;
        patch({ ideaOptions: ideas }); persist({ ideaOptions: ideas });
        onToast(append ? "Added 4 more ideas" : "Generated fresh ideas");
      } catch (e) { onToast((e && e.message) || "Idea generation failed", "error"); }
      finally { setBusy(false); }
    };
    const buildConcept = async () => {
      if (canLive()) {
        const r = await window.LeapAPI.assistant.campaignConcept(liveOrg(), { ...campaignInput(), idea: selectedIdea ? { title: selectedIdea.title, angle: selectedIdea.angle, logic: selectedIdea.logic, why: selectedIdea.why } : null });
        if (r && r.enabled && r.posts && r.posts.length) return conceptFromAI(r);
      }
      return G.campaignConcept({ ...c, brand: S.getBrand() }, selectedIdea);
    };
    const sendChat = async () => {
      if (!chatDraft.trim()) return;
      const msgs = [...(c.chat || []), { role: "user", text: chatDraft.trim() }];
      patch({ chat: msgs }); setChatDraft("");
      let reply = null;
      try { if (canLive()) { const r = await window.LeapAPI.assistant.campaignChat(liveOrg(), { ...campaignInput(), messages: msgs, idea: selectedIdea ? { title: selectedIdea.title, angle: selectedIdea.angle } : null }); if (r && r.enabled && r.reply) reply = r.reply; } } catch (e) { /* fall back */ }
      if (!reply) reply = G.campaignChatReply(msgs, c, selectedIdea);
      const next = [...msgs, { role: "ai", text: reply }]; patch({ chat: next }); persist({ chat: next });
    };
    const genConcept = async () => { setBusy(true); try { const concept = await buildConcept(); patch({ concept }); persist({ concept }); onToast("Drafted the post sequence"); } catch (e) { onToast((e && e.message) || "Draft failed", "error"); } finally { setBusy(false); } };
    // Lock the selected idea → AI drafts the full campaign (text + visual idea per post).
    const lockAndDraft = async () => { setBusy(true); try { const concept = await buildConcept(); patch({ concept, ideaLocked: true }); persist({ concept, ideaLocked: true }); setStep(2); onToast("Idea locked — AI drafted the posts, text & visuals"); } catch (e) { onToast((e && e.message) || "Draft failed", "error"); } finally { setBusy(false); } };
    const saveManualIdea = () => {
      if (!manual.title.trim() && !manual.angle.trim()) { onToast("Add a title or an idea first", "error"); return; }
      const idea = { id: "manual-" + Date.now().toString(36), baseKey: "manual", manual: true, fitsIntent: false,
        title: manual.title.trim() || "My idea", angle: manual.angle.trim() || manual.title.trim(),
        logic: manual.logic.trim() || "Defined manually — your own campaign concept.", why: manual.why.trim() || "Your call — you know your audience." };
      const ideas = [...(c.ideaOptions || []), idea];
      patch({ ideaOptions: ideas, selectedIdeaId: idea.id }); persist({ ideaOptions: ideas, selectedIdeaId: idea.id });
      setManual({ open: false, title: "", angle: "", logic: "", why: "" }); onToast("Your idea added & selected");
    };
    const patchPost = (pid, u) => { const posts = c.concept.posts.map((p) => p.id === pid ? { ...p, ...u } : p); const concept = { ...c.concept, posts }; patch({ concept }); };
    const removePost = (pid) => { const concept = { ...c.concept, posts: c.concept.posts.filter((p) => p.id !== pid) }; patch({ concept }); };
    const addPost = () => { const ch = c.channels[0] || "x_cropr"; const np = { id: "cp_" + Date.now().toString(36), channel: ch, format: D.defaultFormat((D.CHANNELS[ch] || {}).platform), phase: "Extra", title: `${c.name} — post`, body: "", media: [], status: "draft", visualIdea: "On-brand image that matches the post", scheduleAt: c.startDate ? new Date(c.startDate).toISOString() : new Date().toISOString() }; patch({ concept: { ...c.concept, posts: [...c.concept.posts, np] } }); };
    const launch = () => { const saved = persist(); const r = S.launchCampaign(saved.id); if (r.ok) { onToast(r.synced ? `Campaign updated — ${r.count} post${r.count === 1 ? "" : "s"} synced` : `Campaign launched — ${r.count} posts into Posts, Calendar & Queue`); patch({ status: "launched", launchedPostIds: r.ids }); setStep(3); } else onToast(r.reason || "Could not launch", "error"); };
    const next = () => { persist(); setStep((s) => Math.min(3, s + 1)); };
    const back = () => setStep((s) => Math.max(0, s - 1));

    return (
      <div style={{ maxWidth: "var(--page-w)", margin: "0 auto" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 18 }}>
          <Button variant="ghost" icon="arrow-left" onClick={() => { persist(); onExit(); }}>Campaigns</Button>
          <div style={{ flex: 1 }} />
          {c.status === "launched" && <Badge color="#0E8A50" bg="var(--success-bg)" dot>Launched</Badge>}
        </div>
        <Stepper step={step} onStep={(i) => { persist(); setStep(i); }} />

        {step === 0 && (
          <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
            <Card pad={20}>
              <SectionTitle icon="megaphone">Campaign brief</SectionTitle>
              <div style={{ display: "grid", gridTemplateColumns: "1fr 200px", gap: 12 }} className="profile-grid">
                <div><Label>Campaign name</Label><input value={c.name} onChange={(e) => patch({ name: e.target.value })} placeholder="e.g. Spring product launch" style={fieldStyle} /></div>
                <div><Label>Goal</Label><Select value={c.goal} onChange={(v) => patch({ goal: v })} options={D.CAMPAIGN_GOALS.map((g) => ({ value: g.id, label: g.label }))} /></div>
              </div>
              <div style={{ marginTop: 12 }}>
                <Label>Intent</Label>
                <div style={{ display: "flex", flexWrap: "wrap", gap: 6, marginBottom: 8 }}>
                  {D.CAMPAIGN_INTENTS.map((t) => <button key={t} onClick={() => patch({ intent: t })} style={{ border: `1px solid ${c.intent === t ? "var(--leap-teal)" : "var(--border-strong)"}`, background: c.intent === t ? "var(--accent-weak)" : "transparent", color: c.intent === t ? "var(--accent)" : "var(--fg2)", borderRadius: 999, padding: "4px 11px", fontSize: 12, fontWeight: 600, cursor: "pointer", fontFamily: "inherit" }}>{t}</button>)}
                </div>
                <input value={c.intent} onChange={(e) => patch({ intent: e.target.value })} placeholder="What should this campaign make people feel or do?" style={fieldStyle} />
              </div>
              <div style={{ marginTop: 12 }}><Label>Target audience</Label><TextArea value={c.audience} onChange={(v) => patch({ audience: v })} rows={2} placeholder="Who is this for? e.g. busy social media managers at small brands" /></div>
              <div style={{ marginTop: 12 }}><Label>Campaign brief <span style={{ color: "var(--fg4)", fontWeight: 500 }}>· the AI uses this + your brand brief to generate ideas</span></Label><TextArea value={c.campaignBrief || ""} onChange={(v) => patch({ campaignBrief: v })} rows={3} placeholder="Specifics for this campaign — the offer, key messages, must-includes, do's & don'ts." /></div>
              <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginTop: 12 }} className="profile-grid">
                <div><Label>Start</Label><input type="date" value={c.startDate} onChange={(e) => patch({ startDate: e.target.value })} style={fieldStyle} /></div>
                <div><Label>End</Label><input type="date" value={c.endDate} onChange={(e) => patch({ endDate: e.target.value })} style={fieldStyle} /></div>
              </div>
              <div style={{ marginTop: 12 }}><Label>Channels involved</Label><ChannelChips selected={c.channels} onToggle={toggleChan} /></div>
              <div style={{ marginTop: 12 }}><Label>Campaign tag</Label><input value={c.tag} onChange={(e) => patch({ tag: e.target.value })} placeholder={c.name ? c.name.toLowerCase().replace(/\s+/g, "-") : "campaign-tag"} style={{ ...fieldStyle, maxWidth: 280 }} /></div>
            </Card>
            <Card pad={20}>
              <SectionTitle icon="image">Campaign visuals</SectionTitle>
              <VisualsPicker visuals={c.visuals} onChange={(v) => patch({ visuals: v })} onToast={onToast} />
              <div style={{ fontSize: 11.5, color: "var(--fg4)", marginTop: 10 }}>Pick key visuals from your assets (or upload) — reuse them on individual posts in the next steps.</div>
            </Card>
          </div>
        )}

        {step === 1 && (
          <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
              {!c.ideaOptions.length
                ? <Button variant="primary" icon="sparkles" onClick={() => genIdeas(false)} disabled={busy}>Generate ideas</Button>
                : <React.Fragment>
                    <Button variant="primary" icon="plus" onClick={() => genIdeas(true)} disabled={busy}>Generate more ideas</Button>
                    <Button variant="secondary" icon="refresh-cw" onClick={() => genIdeas(false)} disabled={busy}>Start over</Button>
                  </React.Fragment>}
              <span style={{ fontSize: 12, color: "var(--fg3)" }}>Angles for “{c.name || "your campaign"}”, fitting the <b>{c.intent}</b> intent{(S.getBrand().brandBrief || c.campaignBrief) ? ", your brand brief and campaign brief" : ""} — pick one, then refine in chat.</span>
            </div>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 14 }} className="profile-grid">
              {c.ideaOptions.map((idea) => {
                const on = c.selectedIdeaId === idea.id;
                return (
                  <Card key={idea.id} pad={16} onClick={() => patch({ selectedIdeaId: idea.id })} hover style={{ cursor: "pointer", border: `1.5px solid ${on ? "var(--leap-teal)" : "var(--border)"}` }}>
                    <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }}>
                      <Icon name="lightbulb" size={17} color={on ? "var(--accent)" : "var(--fg3)"} />
                      <span style={{ fontSize: 14.5, fontWeight: 700, flex: 1, minWidth: 0 }}>{idea.title}</span>
                      {on ? <Badge color="var(--accent)" bg="var(--accent-weak)" dot>Selected</Badge>
                        : idea.fitsIntent ? <span style={{ fontSize: 10, fontWeight: 700, color: "#0E8A50", background: "var(--success-bg)", padding: "2px 7px", borderRadius: 999, whiteSpace: "nowrap" }}>Fits intent</span> : null}
                    </div>
                    <div style={{ fontSize: 12.5, color: "var(--fg2)", fontWeight: 600, marginBottom: 10 }}>{idea.angle}</div>
                    <div style={{ fontSize: 11, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 3 }}>The logic</div>
                    <div style={{ fontSize: 12.5, color: "var(--fg2)", lineHeight: 1.5, marginBottom: 8 }}>{idea.logic}</div>
                    <div style={{ fontSize: 11, fontWeight: 700, color: "#0E8A50", textTransform: "uppercase", letterSpacing: ".05em", marginBottom: 3 }}>Why it works</div>
                    <div style={{ fontSize: 12.5, color: "var(--fg2)", lineHeight: 1.5 }}>{idea.why}</div>
                  </Card>
                );
              })}
              {!c.ideaOptions.length && <Card pad={30} style={{ gridColumn: "1 / -1", textAlign: "center", color: "var(--fg3)" }}><Icon name="lightbulb" size={26} color="var(--fg4)" /><div style={{ fontSize: 13.5, fontWeight: 600, marginTop: 8 }}>No ideas yet</div><div style={{ fontSize: 12.5, marginTop: 3 }}>Generate a set of angles, or write your own below.</div></Card>}
            </div>

            {/* write your own idea — skip the AI generator */}
            <Card pad={16} style={{ border: "1.5px dashed var(--border-strong)" }}>
              {!manual.open ? (
                <button onClick={() => setManual((m) => ({ ...m, open: true }))} style={{ display: "flex", alignItems: "center", gap: 8, border: 0, background: "none", cursor: "pointer", fontFamily: "inherit", fontSize: 13.5, fontWeight: 600, color: "var(--fg1)", padding: 0 }}>
                  <span style={{ width: 26, height: 26, borderRadius: 999, background: "var(--accent-weak)", display: "grid", placeItems: "center" }}><Icon name="pencil" size={15} color="var(--accent)" /></span>
                  Write your own idea — skip the AI generator
                </button>
              ) : (
                <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
                  <SectionTitle icon="pencil">Your campaign idea</SectionTitle>
                  <input value={manual.title} onChange={(e) => setManual((m) => ({ ...m, title: e.target.value }))} placeholder="Idea title — e.g. 30-day teardown" style={fieldStyle} />
                  <TextArea value={manual.angle} onChange={(v) => setManual((m) => ({ ...m, angle: v }))} rows={2} placeholder="The idea / angle — what's the throughline across the posts?" />
                  <TextArea value={manual.logic} onChange={(v) => setManual((m) => ({ ...m, logic: v }))} rows={2} placeholder="The logic (optional) — how it's structured" />
                  <TextArea value={manual.why} onChange={(v) => setManual((m) => ({ ...m, why: v }))} rows={2} placeholder="Why it works (optional)" />
                  <div style={{ display: "flex", gap: 8 }}>
                    <Button variant="primary" icon="check" onClick={saveManualIdea}>Use this idea</Button>
                    <Button variant="ghost" onClick={() => setManual({ open: false, title: "", angle: "", logic: "", why: "" })}>Cancel</Button>
                  </div>
                </div>
              )}
            </Card>

            {selectedIdea && (
              <Card pad={16} style={{ background: "var(--gradient-hero)", border: 0, color: "#fff", display: "flex", alignItems: "center", gap: 14, flexWrap: "wrap" }}>
                <Icon name="lock" size={20} color="#fff" />
                <div style={{ flex: 1, minWidth: 180 }}>
                  <div style={{ fontSize: 14, fontWeight: 700 }}>Lock “{selectedIdea.title}” &amp; draft the campaign</div>
                  <div style={{ fontSize: 12.5, opacity: .95 }}>The AI writes the full sequence — post text and a visual idea for each post.</div>
                </div>
                <Button variant="secondary" icon="wand-2" onClick={lockAndDraft} disabled={busy}>Lock &amp; draft</Button>
              </Card>
            )}

            {selectedIdea && (
              <Card pad={18}>
                <SectionTitle icon="messages-square">Refine “{selectedIdea.title}”</SectionTitle>
                <div style={{ display: "flex", flexDirection: "column", gap: 8, maxHeight: 240, overflow: "auto", marginBottom: 10 }}>
                  {(c.chat || []).length === 0 && <div style={{ fontSize: 12.5, color: "var(--fg4)" }}>Tell the AI how to shape this idea — tone, emphasis, what to lead with. It'll adjust the sequence you draft next.</div>}
                  {(c.chat || []).map((m, i) => (
                    <div key={i} style={{ alignSelf: m.role === "user" ? "flex-end" : "flex-start", maxWidth: "80%", background: m.role === "user" ? "var(--gradient-core)" : "var(--bg-muted)", color: m.role === "user" ? "#fff" : "var(--fg1)", padding: "8px 12px", borderRadius: 12, fontSize: 13, lineHeight: 1.45 }}>{m.text}</div>
                  ))}
                </div>
                <div style={{ display: "flex", gap: 8 }}>
                  <TextInput value={chatDraft} onChange={setChatDraft} placeholder="e.g. make it more playful, lead with the discount…" style={{ flex: 1 }} onKeyDown={(e) => { if (e.key === "Enter") sendChat(); }} />
                  <Button variant="primary" icon="send" onClick={sendChat}>Send</Button>
                </div>
              </Card>
            )}
          </div>
        )}

        {step === 2 && (
          <div style={{ display: "flex", flexDirection: "column", gap: 14 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
              <Button variant="primary" icon="wand-2" onClick={genConcept} disabled={busy}>{c.concept ? "Redraft sequence" : "Draft the sequence"}</Button>
              {c.concept && <Button variant="secondary" icon="plus" onClick={addPost}>Add post</Button>}
              {c.concept && <span style={{ fontSize: 12, color: "var(--fg3)" }}>{c.concept.summary}</span>}
            </div>
            {!c.concept && <Card pad={30} style={{ textAlign: "center", color: "var(--fg3)" }}><Icon name="list-ordered" size={26} color="var(--fg4)" /><div style={{ fontSize: 13.5, fontWeight: 600, marginTop: 8 }}>No sequence yet</div><div style={{ fontSize: 12.5, marginTop: 3 }}>Draft a full posting sequence from your brief{selectedIdea ? ` and the “${selectedIdea.title}” angle` : ""}.</div></Card>}
            {c.concept && c.concept.posts.map((p, idx) => {
              const meta = D.CHANNELS[p.channel] || {};
              return (
                <Card key={p.id} pad={14}>
                  {/* meta row — position, channel, phase, status, remove */}
                  <div style={{ display: "flex", gap: 8, flexWrap: "wrap", alignItems: "center", marginBottom: 10 }}>
                    <span style={{ width: 26, height: 26, borderRadius: 999, background: "var(--accent-weak)", color: "var(--accent)", display: "grid", placeItems: "center", fontSize: 12, fontWeight: 700, flex: "none" }}>{idx + 1}</span>
                    <Select value={p.channel} onChange={(v) => patchPost(p.id, { channel: v, format: D.defaultFormat((D.CHANNELS[v] || {}).platform) })} options={D.CHANNEL_ORDER.map((id) => ({ value: id, label: D.label(D.PLATFORMS, (D.CHANNELS[id] || {}).platform) }))} style={{ width: 130 }} />
                    <span style={{ fontSize: 11, fontFamily: "var(--font-mono)", color: "var(--fg4)", textTransform: "uppercase", letterSpacing: ".08em" }}>{p.phase}</span>
                    <div style={{ flex: 1 }} />
                    <Select value={p.status} onChange={(v) => patchPost(p.id, { status: v })} options={[{ value: "draft", label: "Draft" }, { value: "ready", label: "Ready" }]} style={{ width: 100 }} />
                    <button onClick={() => removePost(p.id)} title="Remove post" style={{ border: 0, background: "none", cursor: "pointer" }}><Icon name="trash-2" size={16} color="var(--fg3)" /></button>
                  </div>

                  {/* the SAME per-channel content editor used in the Posts composer */}
                  <VariantEditor variant={p} hookType="specific_claim" pillar={c.name} series={c.tag || ""} brief={c.campaignBrief || c.name} cta={p.cta} hideChannelOptions onChange={(vp) => patchPost(p.id, vp)} onToast={onToast} />

                  {/* campaign post-level: CTA, tags, schedule, AI visual idea */}
                  <div style={{ display: "flex", gap: 8, marginTop: 10 }} className="profile-grid">
                    <input value={p.cta || ""} onChange={(e) => patchPost(p.id, { cta: e.target.value })} placeholder="Call to action (optional)" style={{ ...fieldStyle, flex: 1, padding: "8px 10px", fontSize: 13 }} />
                    <input value={typeof p.tags === "string" ? p.tags : (p.tags || []).join(", ")} onChange={(e) => patchPost(p.id, { tags: e.target.value })} placeholder="Tags to @ (comma-separated)" style={{ ...fieldStyle, flex: 1, padding: "8px 10px", fontSize: 13 }} />
                  </div>
                  <div style={{ display: "flex", gap: 10, alignItems: "center", marginTop: 8, flexWrap: "wrap" }}>
                    <span style={{ display: "inline-flex", alignItems: "center", gap: 6, fontSize: 12, color: "var(--fg3)" }}><Icon name="calendar-clock" size={15} color="var(--fg3)" /><input type="datetime-local" value={toLocalInput(p.scheduleAt)} onChange={(e) => patchPost(p.id, { scheduleAt: e.target.value ? new Date(e.target.value).toISOString() : null })} style={{ ...fieldStyle, width: 210, padding: "6px 9px" }} /></span>
                    {p.visualIdea && <span style={{ display: "inline-flex", alignItems: "center", gap: 5, fontSize: 11.5, color: "var(--fg4)" }} title="AI's suggested visual — attach the actual asset in the editor above"><Icon name="image" size={13} color="var(--fg4)" />{p.visualIdea}</span>}
                  </div>
                </Card>
              );
            })}
          </div>
        )}

        {step === 3 && (
          <div style={{ display: "flex", flexDirection: "column", gap: 16 }}>
            <Card pad={20} style={{ background: c.status === "launched" ? "var(--success-bg)" : "var(--gradient-hero)", border: 0, color: c.status === "launched" ? "var(--fg1)" : "#fff" }}>
              <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 6 }}><Icon name={c.status === "launched" ? "check-circle" : "rocket"} size={20} color={c.status === "launched" ? "#0E8A50" : "#fff"} /><span style={{ fontSize: 15, fontWeight: 700 }}>{c.status === "launched" ? "Campaign launched" : "Ready to launch"}</span></div>
              <div style={{ fontSize: 13, lineHeight: 1.55, opacity: c.status === "launched" ? 1 : .95 }}>{c.status === "launched" ? `${(c.launchedPostIds || []).length} posts created — tagged “${c.name}” and flowing through Posts, Calendar, Board and the Queue.` : `Confirm to create ${(c.concept ? c.concept.posts.length : 0)} posts, tagged “${c.name}”. Ready posts with a time go straight to the Queue; drafts land in Posts to finish later.`}</div>
            </Card>
            <Card pad={0}>
              <div style={{ display: "grid", gridTemplateColumns: "34px 1fr 120px 90px", gap: 10, padding: "10px 16px", background: "var(--bg-subtle)", borderBottom: "1px solid var(--border)", borderRadius: "16px 16px 0 0" }}>
                {["#", "Post", "When", "Status"].map((h, i) => <span key={i} style={{ fontSize: 10.5, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em" }}>{h}</span>)}
              </div>
              {(c.concept ? c.concept.posts : []).map((p, i) => {
                const meta = D.CHANNELS[p.channel] || {};
                return (
                  <div key={p.id} style={{ display: "grid", gridTemplateColumns: "34px 1fr 120px 90px", gap: 10, padding: "11px 16px", borderBottom: "1px solid var(--border)", alignItems: "center" }}>
                    <span style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)" }}>{i + 1}</span>
                    <div style={{ minWidth: 0 }}>
                      <div style={{ display: "flex", gap: 8, alignItems: "center" }}><PlatformLogo platform={meta.platform} size={18} style={{ borderRadius: 5, flex: "none" }} />{(p.media || [])[0] && <Icon name="paperclip" size={13} color="var(--fg4)" />}<span style={{ fontSize: 12.5, color: "var(--fg2)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{(p.body || "").split("\n")[0] || p.title}</span></div>
                      {p.visualIdea && <div style={{ display: "flex", gap: 5, alignItems: "center", marginTop: 3 }}><Icon name="image" size={11} color="var(--fg4)" /><span style={{ fontSize: 11, color: "var(--fg4)", overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{p.visualIdea}</span></div>}
                    </div>
                    <span style={{ fontSize: 12, color: "var(--fg3)" }}>{p.scheduleAt ? new Date(p.scheduleAt).toLocaleString("en-GB", { day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" }) : "—"}</span>
                    <Badge color={p.status === "ready" ? "#0E8A50" : "var(--fg3)"} bg={p.status === "ready" ? "var(--success-bg)" : "var(--bg-muted)"} dot>{p.status === "ready" ? "Ready" : "Draft"}</Badge>
                  </div>
                );
              })}
            </Card>
            {c.status !== "launched"
              ? <div><Button variant="primary" icon="rocket" onClick={launch} disabled={!c.concept || !c.concept.posts.length}>Confirm & launch campaign</Button></div>
              : <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}><Button variant="primary" icon="refresh-cw" onClick={launch} disabled={!c.concept || !c.concept.posts.length}>Update live posts</Button><Button variant="secondary" icon="arrow-left" onClick={onExit}>Back to campaigns</Button></div>}
          </div>
        )}

        <div style={{ display: "flex", gap: 10, marginTop: 22, borderTop: "1px solid var(--border)", paddingTop: 16 }}>
          <Button variant="secondary" icon="arrow-left" onClick={back} disabled={step === 0}>Back</Button>
          <div style={{ flex: 1 }} />
          <Button variant="ghost" onClick={() => { persist(); onToast(c.status === "launched" ? "Changes saved — use “Update live posts” to sync" : "Draft saved"); }}>{c.status === "launched" ? "Save changes" : "Save draft"}</Button>
          {step < 3 && <Button variant="primary" iconRight="arrow-right" onClick={next} disabled={step === 1 && !c.selectedIdea && !c.selectedIdeaId}>Next</Button>}
        </div>
      </div>
    );
  }

  // ---------------- List (Planned / Active / Past · cards & list) ---------
  const PerfCells = ({ agg, mode }) => PERF_COLS.map((k) => (
    <div key={k.id} style={{ display: "flex", flexDirection: "column", minWidth: mode === "row" ? 50 : 0 }}>
      <span style={{ fontSize: 10, color: "var(--fg3)", fontWeight: 600 }}>{k.label}</span>
      <span style={{ fontSize: 13, fontWeight: 700, fontVariantNumeric: "tabular-nums" }}>{compact(agg[k.id])}</span>
    </div>
  ));
  const ChanRow = ({ channels, size = 20 }) => <div style={{ display: "flex", marginLeft: 2 }}>{(channels || []).slice(0, 7).map((id, i) => <span key={id} style={{ marginLeft: i ? -6 : 0 }}><PlatformLogo platform={(D.CHANNELS[id] || {}).platform} size={size} style={{ borderRadius: 5, boxShadow: "0 0 0 2px var(--surface)" }} /></span>)}</div>;
  const delBtn = (c, onToast) => <button onClick={(e) => { e.stopPropagation(); if (confirm("Delete campaign “" + c.name + "”?")) { S.removeCampaign(c.id); onToast("Campaign deleted"); } }} style={{ border: 0, background: "none", cursor: "pointer" }}><Icon name="trash-2" size={15} color="var(--fg4)" /></button>;

  function CampaignCard({ c, onOpen, onToast }) {
    const sm = D.CAMPAIGN_STATUS.find((x) => x.id === c.status) || D.CAMPAIGN_STATUS[0];
    const agg = campaignAgg(c); const launched = c.status === "launched";
    return (
      <Card pad={18} onClick={() => onOpen(c.id)} hover style={{ cursor: "pointer" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 8 }}>
          <span style={{ fontSize: 15, fontWeight: 700, flex: 1, minWidth: 0, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{c.name || "Untitled campaign"}</span>
          <Badge color={sm.color} bg={sm.color + "1e"} dot>{sm.label}</Badge>
        </div>
        <div style={{ fontSize: 12.5, color: "var(--fg3)", marginBottom: 12 }}>{D.label(D.CAMPAIGN_GOALS, c.goal)} · {c.intent}{(c.startDate || c.endDate) ? " · " + fmtDay(c.startDate) + "–" + fmtDay(c.endDate) : ""}</div>
        <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap", marginBottom: launched && agg.publishedCount ? 12 : 0 }}>
          <ChanRow channels={campaignChannels(c)} /><div style={{ flex: 1 }} />
          <span style={{ fontSize: 11.5, color: "var(--fg3)" }}>{agg.postCount} post{agg.postCount === 1 ? "" : "s"}</span>
        </div>
        {launched && agg.publishedCount > 0 && <div style={{ display: "grid", gridTemplateColumns: "repeat(5,1fr)", gap: 8, background: "var(--bg-subtle)", borderRadius: 10, padding: "10px 12px" }}><PerfCells agg={agg} mode="card" /></div>}
        {launched && agg.publishedCount === 0 && <div style={{ fontSize: 11.5, color: "var(--fg4)" }}>Launched — awaiting performance data.</div>}
        <div style={{ display: "flex", justifyContent: "flex-end", marginTop: 10 }}>{delBtn(c, onToast)}</div>
      </Card>
    );
  }

  function CampaignRow({ c, onOpen, onToast }) {
    const sm = D.CAMPAIGN_STATUS.find((x) => x.id === c.status) || D.CAMPAIGN_STATUS[0];
    const agg = campaignAgg(c); const perf = c.status === "launched" && agg.publishedCount > 0;
    return (
      <div onClick={() => onOpen(c.id)} style={{ display: "flex", alignItems: "center", gap: 14, padding: "13px 16px", borderBottom: "1px solid var(--border)", cursor: "pointer" }}
        onMouseEnter={(e) => (e.currentTarget.style.background = "var(--bg-subtle)")} onMouseLeave={(e) => (e.currentTarget.style.background = "transparent")}>
        <div style={{ minWidth: 0, flex: 1 }}>
          <div style={{ fontSize: 14, fontWeight: 700, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{c.name || "Untitled campaign"}</div>
          <div style={{ fontSize: 11.5, color: "var(--fg3)", marginTop: 2 }}>{D.label(D.CAMPAIGN_GOALS, c.goal)} · {c.intent}{(c.startDate || c.endDate) ? " · " + fmtDay(c.startDate) + "–" + fmtDay(c.endDate) : ""}</div>
        </div>
        <ChanRow channels={campaignChannels(c)} />
        <span style={{ fontSize: 12, color: "var(--fg3)", width: 58, textAlign: "center" }}>{agg.postCount} post{agg.postCount === 1 ? "" : "s"}</span>
        {perf ? <PerfCells agg={agg} mode="row" /> : <span style={{ fontSize: 11.5, color: "var(--fg4)", width: 290, textAlign: "center" }}>{c.status === "launched" ? "awaiting data" : "not launched"}</span>}
        <Badge color={sm.color} bg={sm.color + "1e"} dot>{sm.label}</Badge>
        {delBtn(c, onToast)}
      </div>
    );
  }

  function CampaignList({ onNew, onOpen, onToast }) {
    const [view, setView] = React.useState("cards");
    const campaigns = S.getCampaigns();
    const groups = PHASES.map((ph) => ({ ph, items: campaigns.filter((c) => campaignPhase(c) === ph.id) }));
    return (
      <div style={{ maxWidth: "var(--page-w)", margin: "0 auto" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 18, flexWrap: "wrap" }}>
          <div style={{ fontSize: 13, color: "var(--fg3)", flex: 1, minWidth: 200 }}>Planned, active and past campaigns — with dates, posts, channels, performance and drill-down.</div>
          <Segmented value={view} onChange={setView} options={[{ value: "cards", label: "Cards", icon: "layout-grid" }, { value: "list", label: "List", icon: "list" }]} />
          <Button variant="primary" icon="plus" onClick={onNew}>New campaign</Button>
        </div>
        {campaigns.length === 0 ? (
          <Card pad={44} style={{ textAlign: "center" }}>
            <span style={{ width: 54, height: 54, borderRadius: 15, background: "var(--accent-weak)", display: "inline-grid", placeItems: "center" }}><Icon name="megaphone" size={24} color="var(--accent)" /></span>
            <div style={{ fontSize: 16, fontWeight: 700, marginTop: 12 }}>No campaigns yet</div>
            <div style={{ fontSize: 13, color: "var(--fg3)", marginTop: 4, marginBottom: 16 }}>Start a campaign: define the brief, let AI propose angles, draft the sequence and launch it.</div>
            <Button variant="primary" icon="plus" onClick={onNew}>New campaign</Button>
          </Card>
        ) : groups.filter((g) => g.items.length).map((g) => (
          <div key={g.ph.id} style={{ marginBottom: 26 }}>
            <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 12 }}>
              <Icon name={g.ph.icon} size={16} color={g.ph.color} />
              <span style={{ fontSize: 13, fontWeight: 700 }}>{g.ph.label}</span>
              <span style={{ fontSize: 11.5, fontWeight: 700, color: "var(--fg3)", background: "var(--bg-muted)", borderRadius: 999, padding: "1px 8px" }}>{g.items.length}</span>
            </div>
            {view === "cards"
              ? <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill,minmax(280px,1fr))", gap: 16 }}>{g.items.map((c) => <CampaignCard key={c.id} c={c} onOpen={onOpen} onToast={onToast} />)}</div>
              : <Card pad={0}>{g.items.map((c) => <CampaignRow key={c.id} c={c} onOpen={onOpen} onToast={onToast} />)}</Card>}
          </div>
        ))}
      </div>
    );
  }

  // ---------------- Detail (drill-down: performance · platforms · posts) ---
  function KpiTile({ label, value }) {
    return <div style={{ background: "var(--surface)", border: "1px solid var(--border)", borderRadius: 14, padding: "12px 15px", boxShadow: "var(--shadow-sm)" }}>
      <div style={{ fontSize: 11, color: "var(--fg3)", fontWeight: 600, textTransform: "uppercase", letterSpacing: ".04em" }}>{label}</div>
      <div style={{ fontSize: 22, fontWeight: 700, fontVariantNumeric: "tabular-nums", marginTop: 2 }}>{nf(value)}</div>
    </div>;
  }

  function CampaignDetail({ id, onBack, onEdit, onOpenPost, onToast }) {
    const c = S.getCampaign(id);
    if (!c) return null;
    const sm = D.CAMPAIGN_STATUS.find((x) => x.id === c.status) || D.CAMPAIGN_STATUS[0];
    const phase = PHASES.find((p) => p.id === campaignPhase(c));
    const agg = campaignAgg(c); const posts = campaignPosts(c);
    const platEntries = Object.entries(campaignByPlatform(c)).map(([pl, v]) => ({ pl, ...v })).sort((a, b) => b.impressions - a.impressions);
    const launched = c.status === "launched";
    return (
      <div style={{ maxWidth: "var(--page-w)", margin: "0 auto" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 16, flexWrap: "wrap" }}>
          <Button variant="ghost" icon="arrow-left" onClick={onBack}>Campaigns</Button>
          <div style={{ flex: 1 }} />
          {phase && <Badge color={phase.color} bg={phase.color + "1e"} dot>{phase.label}</Badge>}
          <Button size="sm" variant="secondary" icon="pencil" onClick={() => onEdit(c.id)}>Edit campaign</Button>
        </div>
        <Card pad={20} style={{ marginBottom: 16 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 10, flexWrap: "wrap" }}>
            <span style={{ fontSize: 19, fontWeight: 700, fontFamily: "var(--font-display)" }}>{c.name || "Untitled campaign"}</span>
            <Badge color={sm.color} bg={sm.color + "1e"} dot>{sm.label}</Badge>
          </div>
          <div style={{ display: "flex", gap: 18, marginTop: 10, fontSize: 12.5, color: "var(--fg3)", flexWrap: "wrap", alignItems: "center" }}>
            <span><b style={{ color: "var(--fg2)" }}>Goal</b> {D.label(D.CAMPAIGN_GOALS, c.goal)}</span>
            <span><b style={{ color: "var(--fg2)" }}>Intent</b> {c.intent}</span>
            {(c.startDate || c.endDate) && <span><b style={{ color: "var(--fg2)" }}>Dates</b> {fmtDay(c.startDate)} – {fmtDay(c.endDate)}</span>}
            <span><b style={{ color: "var(--fg2)" }}>Posts</b> {agg.postCount}</span>
            <span style={{ display: "inline-flex", alignItems: "center", gap: 6 }}><b style={{ color: "var(--fg2)" }}>Channels</b> <ChanRow channels={campaignChannels(c)} size={18} /></span>
          </div>
          {c.audience && <div style={{ fontSize: 12.5, color: "var(--fg2)", marginTop: 10, paddingTop: 10, borderTop: "1px solid var(--border)" }}>{c.audience}</div>}
        </Card>

        {launched && (
          <React.Fragment>
            <div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fit,minmax(120px,1fr))", gap: 12, marginBottom: 16 }}>
              <KpiTile label="Impressions" value={agg.impressions} /><KpiTile label="Engagement" value={agg.engagement} /><KpiTile label="Likes" value={agg.likes} /><KpiTile label="Shares" value={agg.shares} /><KpiTile label="Comments" value={agg.comments} />
            </div>
            {platEntries.length > 0 && (
              <Card pad={0} style={{ marginBottom: 16 }}>
                <div style={{ padding: "10px 16px", fontSize: 11, fontWeight: 700, color: "var(--fg3)", textTransform: "uppercase", letterSpacing: ".05em", borderBottom: "1px solid var(--border)", background: "var(--bg-subtle)", borderRadius: "16px 16px 0 0" }}>By social media</div>
                {platEntries.map((e) => (
                  <div key={e.pl} style={{ display: "flex", alignItems: "center", gap: 12, padding: "11px 16px", borderBottom: "1px solid var(--border)" }}>
                    <PlatformLogo platform={e.pl} size={20} style={{ borderRadius: 5 }} />
                    <span style={{ fontSize: 13, fontWeight: 600, flex: 1 }}>{D.label(D.PLATFORMS, e.pl)}</span>
                    <span style={{ fontSize: 12, color: "var(--fg3)", width: 62 }}>{e.posts} post{e.posts === 1 ? "" : "s"}</span>
                    <span style={{ fontSize: 13, fontWeight: 700, width: 72, textAlign: "right" }}>{compact(e.impressions)}<span style={{ fontSize: 10, color: "var(--fg3)", fontWeight: 500 }}> impr</span></span>
                    <span style={{ fontSize: 13, fontWeight: 700, width: 72, textAlign: "right" }}>{compact(e.engagement)}<span style={{ fontSize: 10, color: "var(--fg3)", fontWeight: 500 }}> eng</span></span>
                  </div>
                ))}
              </Card>
            )}
          </React.Fragment>
        )}

        <SectionTitle icon="layout-list">Posts{launched ? " · click to open & edit" : " · planned sequence"}</SectionTitle>
        {launched ? (
          <Card pad={0}>
            {posts.length === 0 && <div style={{ padding: 30, textAlign: "center", color: "var(--fg3)", fontSize: 13 }}>No posts found for this campaign.</div>}
            {posts.map((p) => { const m = A.metricsFor(p); const pub = A.isPublished(p); return (
              <div key={p.id} onClick={() => onOpenPost && onOpenPost(p.id)} style={{ display: "flex", alignItems: "center", gap: 12, padding: "12px 16px", borderBottom: "1px solid var(--border)", cursor: "pointer" }}
                onMouseEnter={(e) => (e.currentTarget.style.background = "var(--bg-subtle)")} onMouseLeave={(e) => (e.currentTarget.style.background = "transparent")}>
                <HoverImage src={window.postThumb(p) || window.NO_VISUAL} size={40} radius={8} label={p.title} />
                <div style={{ display: "flex" }}>{(p.channels || []).map((ch, i) => <span key={ch} style={{ marginLeft: i ? -6 : 0 }}><PlatformLogo platform={(D.CHANNELS[ch] || {}).platform} size={18} style={{ borderRadius: 5, boxShadow: "0 0 0 2px var(--surface)" }} /></span>)}</div>
                <div style={{ minWidth: 0, flex: 1 }}>
                  <div style={{ fontSize: 13.5, fontWeight: 700, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{p.title}</div>
                  <div style={{ fontSize: 11, color: "var(--fg3)" }}>{p.scheduleAt ? new Date(p.scheduleAt).toLocaleDateString("en-GB", { day: "2-digit", month: "short", hour: "2-digit", minute: "2-digit" }) : "—"}</div>
                </div>
                {pub ? PERF_COLS.map((k) => <div key={k.id} style={{ display: "flex", flexDirection: "column", width: 48 }}><span style={{ fontSize: 10, color: "var(--fg3)", fontWeight: 600 }}>{k.label}</span><span style={{ fontSize: 12.5, fontWeight: 700 }}>{compact(m[k.id])}</span></div>)
                  : <span style={{ fontSize: 11.5, color: "var(--fg4)", width: 250, textAlign: "center" }}>not published yet</span>}
                <StatusBadge status={p.status} />
              </div>
            ); })}
          </Card>
        ) : (
          <Card pad={0}>
            {!(c.concept && c.concept.posts || []).length && <div style={{ padding: 30, textAlign: "center", color: "var(--fg3)", fontSize: 13 }}>No sequence drafted yet — open the builder to plan it.</div>}
            {(c.concept && c.concept.posts || []).map((p, i) => { const meta = D.CHANNELS[p.channel] || {}; return (
              <div key={p.id} style={{ display: "flex", alignItems: "center", gap: 12, padding: "12px 16px", borderBottom: "1px solid var(--border)" }}>
                <span style={{ fontSize: 12, fontWeight: 700, color: "var(--fg3)", width: 18 }}>{i + 1}</span>
                <PlatformLogo platform={meta.platform} size={18} style={{ borderRadius: 5 }} />
                <span style={{ fontSize: 11, fontFamily: "var(--font-mono)", color: "var(--fg4)", textTransform: "uppercase", width: 56 }}>{p.phase}</span>
                <span style={{ fontSize: 13, color: "var(--fg2)", flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" }}>{(p.body || "").split("\n")[0] || p.title}</span>
                <span style={{ fontSize: 11.5, color: "var(--fg3)" }}>{p.scheduleAt ? new Date(p.scheduleAt).toLocaleDateString("en-GB", { day: "2-digit", month: "short" }) : "—"}</span>
              </div>
            ); })}
            <div style={{ padding: 12, textAlign: "center" }}><Button size="sm" variant="secondary" icon="pencil" onClick={() => onEdit(c.id)}>Open builder to edit</Button></div>
          </Card>
        )}
      </div>
    );
  }

  function CampaignsView({ onToast, onOpenPost }) {
    const [build, setBuild] = React.useState(null);
    const [detailId, setDetailId] = React.useState(null);
    if (build) return <CampaignBuilder id={build.id || null} onExit={() => setBuild(null)} onToast={onToast} />;
    if (detailId) return <CampaignDetail id={detailId} onBack={() => setDetailId(null)} onEdit={(cid) => { setDetailId(null); setBuild({ id: cid }); }} onOpenPost={onOpenPost} onToast={onToast} />;
    return <CampaignList onNew={() => setBuild({ new: true })} onOpen={(cid) => setDetailId(cid)} onToast={onToast} />;
  }
  window.CampaignsView = CampaignsView;
})();
