// ===== Views for Brightnest Brand OS =====

const { useState, useMemo, useEffect } = React;

/* ============================================================
   OVERVIEW
   ============================================================ */
function OverviewView({ goto }) {
  const tw = useTw();
  const u  = useUser();
  const me = u?.user;
  const firstName = (me?.name || "").split(" ")[0] || "";

  // KPI numbers derived from real data so the cards stay in sync.
  const kpis = useMemo(() => {
    const posts = u?.posts || [];
    const scheduled = posts.filter(p => p.status === "scheduled").length;
    const review    = posts.filter(p => p.status === "review").length;
    const channels  = new Set();
    for (const p of posts) for (const c of (p.channels || [])) channels.add(c);
    const photos    = (u?.imagery || []).reduce((a, f) => a + f.photos.length, 0);
    return { posts: posts.length, scheduled, review, channels: channels.size, photos };
  }, [u?.posts, u?.imagery]);

  return (
    <div className="page">
      <header className="page-head">
        <div>
          <h1>God morgon{firstName ? `, ${firstName}` : ""}.<br/><em>Här ligger varumärket.</em></h1>
        </div>
      </header>

      {tw.showKPIs && (
      <>
      <div className="kpis">
        <div className="kpi">
          <div className="label">Foton i biblioteket</div>
          <div className="num">{kpis.photos}</div>
          <div className="delta">{(u?.imagery || []).length} mappar</div>
        </div>
        <div className="kpi">
          <div className="label">Schemalagda inlägg</div>
          <div className="num">{kpis.scheduled}</div>
          <div className="delta">i {kpis.channels} kanaler</div>
        </div>
        <div className="kpi">
          <div className="label">Inväntar korrektur</div>
          <div className="num">{kpis.review.toString().padStart(2, "0")}</div>
          <div className="delta dn">behöver brand-godkännande</div>
        </div>
        <div className="kpi">
          <div className="label">Total contentplan</div>
          <div className="num">{kpis.posts}</div>
          <div className="delta up">aktiva inlägg</div>
        </div>
      </div>

      <div style={{ height: 40 }} />
      </>
      )}

      <DashboardTodo goto={goto} />

      {tw.showActivity && (
        <>
          <div style={{ height: 40 }} />
          <section>
            <div className="section-title">
              <h3>Activity</h3>
              <span className="meta">Workspace stream</span>
            </div>
            <div className="activity">
              {ACTIVITY.map((a, i) => (
                <div className="act-row" key={i}>
                  <span className="t">{a.t}</span>
                  <span className="who"><b>{a.who}</b> {a.msg}</span>
                  <span className="tag">{a.tag}</span>
                </div>
              ))}
            </div>
          </section>
        </>
      )}
    </div>
  );
}

/* Dashboard To-do — derives the user's pending content-plan work from the
   current posts list. Categories:
     - "Att korrekturläsa"   → review status, you can approve
     - "Inväntar produktion" → draft/production, you own it
     - "Att schemalägga"     → approved (all stages passed), not yet scheduled
     - "Inväntar kund"       → customer stage current, you have approve_customer
*/
function DashboardTodo({ goto }) {
  const u = useUser();
  const me = u.user;
  const can = (p) => u.can(p);
  const posts = u.posts || [];

  // Build per-post next-stage info using the live flows table.
  const flows = u.flows || [];
  const flowBySlug = Object.fromEntries(flows.map(f => [f.id, f]));
  const nextStage = (p) => {
    const flow = flowBySlug[p.flowId] || flows.find(f => f.isDefault) || flows[0];
    if (!flow) return null;
    return flow.stages.find(s => !p.approvals?.[s.id]) || null;
  };

  const todayIso = (() => {
    const d = new Date(); return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,"0")}-${String(d.getDate()).padStart(2,"0")}`;
  })();

  const meOwns = (p) => (p.owners || []).includes(me.id);

  const buckets = [
    {
      key: "you-approve",
      title: "Du kan godkänna",
      hint: "Inlägg som väntar på din signatur enligt aktuellt steg i flödet.",
      items: posts
        .map(p => ({ p, next: nextStage(p) }))
        .filter(({ p, next }) => next && can(next.requirePermission)
          && (p.status === "review" || p.status === "production" || p.status === "approved"))
        .map(({ p, next }) => ({
          post: p, next, action: "Godkänn " + next.label.toLowerCase(),
          go: "/content/post/" + p.id,
        })),
    },
    {
      key: "your-drafts",
      title: "Dina utkast i produktion",
      hint: "Du står som ansvarig, inlägget är ännu inte färdigt.",
      items: posts
        .filter(p => meOwns(p) && (p.status === "draft" || p.status === "production"))
        .map(p => ({ post: p, action: "Öppna utkast", go: "/content/post/" + p.id })),
    },
    {
      key: "to-schedule",
      title: "Klart att schemaläggas",
      hint: "Alla godkännanden är på plats — sätt en tid och publicera.",
      items: posts
        .filter(p => p.status === "approved" && Object.keys(p.approvals || {}).length >= 2)
        .map(p => ({ post: p, action: "Sätt tid", go: "/content/post/" + p.id })),
    },
    {
      key: "soon",
      title: "Snart att publicera",
      hint: "Schemalagda inlägg inom de närmaste dagarna.",
      items: posts
        .filter(p => p.status === "scheduled" && p.date >= todayIso && p.date <= addDaysIso(todayIso, 7))
        .map(p => ({ post: p, action: "Granska", go: "/content/post/" + p.id })),
    },
  ];

  const total = buckets.reduce((a, b) => a + b.items.length, 0);

  return (
    <section className="todo-section">
      <div className="section-title">
        <h3>Att göra</h3>
        <span className="meta">{total} {total === 1 ? "uppgift" : "uppgifter"} · främst från Content Plan</span>
      </div>
      {total === 0 && (
        <div className="copy-empty">Inga öppna uppgifter just nu. Bra jobbat.</div>
      )}
      <div className="todo-buckets">
        {buckets.filter(b => b.items.length > 0).map(b => (
          <div className="todo-bucket" key={b.key}>
            <div className="todo-bucket-h">
              <span className="todo-bucket-title">{b.title}</span>
              <span className="todo-bucket-count">{b.items.length}</span>
            </div>
            <p className="todo-bucket-hint">{b.hint}</p>
            <ul className="todo-list">
              {b.items.slice(0, 6).map(({ post, next, action, go }) => (
                <li className="todo-item" key={post.id}>
                  <button type="button" className="todo-main" onClick={() => navigate(go)}>
                    <span className="todo-when">{fmtShortDate(post.date)} · {post.time}</span>
                    <span className="todo-topic">{post.topic}</span>
                    {next && <span className="todo-stage">{next.label}</span>}
                  </button>
                  <span className="todo-channels">
                    {(post.channels || []).map(c => <ChannelChip id={c} key={c} />)}
                  </span>
                  <button type="button" className="tb-btn todo-action" onClick={() => navigate(go)}>{action}</button>
                </li>
              ))}
              {b.items.length > 6 && (
                <li className="todo-more" onClick={() => navigate("/content")}>+ {b.items.length - 6} fler — visa allt i Content Plan</li>
              )}
            </ul>
          </div>
        ))}
      </div>
    </section>
  );
}

function addDaysIso(iso, n) {
  const d = new Date(iso + "T00:00"); d.setDate(d.getDate() + n);
  return `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,"0")}-${String(d.getDate()).padStart(2,"0")}`;
}
function fmtShortDate(iso) {
  const d = new Date(iso + "T00:00");
  return `${d.getDate()} ${["jan","feb","mar","apr","maj","jun","jul","aug","sep","okt","nov","dec"][d.getMonth()]}`;
}

/* ============================================================
   BRAND LIBRARY
   ============================================================ */
const LIB_TABS = [
  { id: "logos",      name: "Logos"        },
  { id: "color",      name: "Color"        },
  { id: "type",       name: "Typography"   },
  { id: "imagery",    name: "Imagery"      },
  { id: "voice",      name: "Voice & tone" },
  { id: "components", name: "Components"   },
];

function LibraryView() {
  const u = useUser();
  const route = useRoute();
  const validTabs = LIB_TABS.map(t => t.id);
  const tab = validTabs.includes(route.params[0]) ? route.params[0] : "logos";
  const setTab = (t) => navigate("/library" + (t === "logos" ? "" : "/" + t));

  // Counts come from the hydrated brand + imagery rather than from frozen
  // demo numbers, so the tab badges actually reflect what's in the workspace.
  const counts = {
    logos:      u.brand?.logos?.length      ?? 0,
    color:      u.brand?.colors?.length     ?? 0,
    type:       u.brand?.typefaces?.length  ?? 0,
    imagery:    (u.imagery || []).reduce((a, f) => a + f.photos.length, 0),
    voice:      (u.brand?.profile?.pillars?.length || 0) + (u.brand?.profile?.wordUse?.length || 0),
    components: 0,
  };

  return (
    <div className="page">
      <header className="page-head">
        <div>
          <div className="eyebrow">Brand · single source of truth</div>
          <h1>Brand <em>library</em>.</h1>
          <p className="sub">Logotyper, färger, typsnitt, bilder och språk — versionerat och redo att droppas in i Studio eller annan pipeline.</p>
        </div>
        <div className="head-meta">
          <div><b>Playbook v3.0</b> · feb 2026</div>
          <div>{counts.logos + counts.color + counts.type + counts.imagery} tillgångar i biblioteket</div>
        </div>
      </header>

      <div className="lib-tabs">
        {LIB_TABS.map(t => (
          <button key={t.id}
                  className={"lib-tab " + (tab === t.id ? "is-active" : "")}
                  onClick={() => setTab(t.id)}>
            {t.name}<span className="num">{String(counts[t.id] || 0).padStart(2,"0")}</span>
          </button>
        ))}
      </div>

      {tab === "logos"      && <LibLogos />}
      {tab === "color"      && <LibColor />}
      {tab === "type"       && <LibType />}
      {tab === "imagery"    && <LibImagery />}
      {tab === "voice"      && <LibVoice />}
      {tab === "components" && <LibComponents />}
    </div>
  );
}

function LibLogos() {
  const u = useUser();
  const logos = u.brand?.logos || [];
  const kindLabel = { wordmark: "Wordmark", mark: "Märke", lockup: "Lockup", cert: "Certifiering" };

  // Group by kind so the page reads top-down: primary wordmark, then marks.
  const groups = [
    { id: "wordmark", title: "Wordmark", hint: "Primär logotyp. Använd så ofta det funkar." },
    { id: "mark",     title: "Märke",    hint: "Compact BRTNST för små ytor: favicon, avatarer, klotter." },
    { id: "lockup",   title: "Lockup",   hint: "" },
    { id: "cert",     title: "Certifieringar", hint: "" },
  ].map(g => ({ ...g, items: logos.filter(l => l.kind === g.id) }))
   .filter(g => g.items.length > 0);

  const renderPreview = (v) => {
    if (v.format === "mov" || v.format === "mp4") {
      return <video className="lc-img" src={v.url} muted autoPlay loop playsInline />;
    }
    return <img src={v.url} alt={v.name} className="lc-img" />;
  };

  return (
    <>
      <div className="section-title">
        <h3>Logos &amp; marks</h3>
        <span className="meta">
          {logos.length} {logos.length === 1 ? "variant" : "varianter"} · klicka ett format för att ladda ned
        </span>
      </div>
      {logos.length === 0 && <div className="copy-empty">Inga logo-varianter än.</div>}
      {groups.map(g => (
        <div className="logo-group" key={g.id}>
          <div className="section-title logo-group-h">
            <h4>{g.title}</h4>
            {g.hint && <span className="meta">{g.hint}</span>}
          </div>
          <div className="logo-grid">
            {g.items.map(v => (
              <div className={"logo-card bg-" + v.background} key={v.slug}>
                <div className="lc-canvas">{renderPreview(v)}</div>
                <div className="lc-foot">
                  <div className="lc-meta">
                    <span className="lc-name">{v.name}</span>
                    <span className="lc-sub">{kindLabel[v.kind] || v.kind} · {v.background}</span>
                  </div>
                </div>
                <div className="lc-formats">
                  {(v.downloads || []).map(d => (
                    <a key={d.format + d.url} className="lc-format" href={d.url} download title={`Ladda ned ${d.format.toUpperCase()}`}>
                      {d.format.toUpperCase()}
                    </a>
                  ))}
                </div>
                {v.description && <div className="lc-desc">{v.description}</div>}
              </div>
            ))}
          </div>
        </div>
      ))}
    </>
  );
}

function LibColor() {
  const u = useUser();
  const colors = u.brand?.colors || [];
  const groups = {
    primary:   colors.filter(c => c.category === "primary"),
    secondary: colors.filter(c => c.category === "secondary"),
    accent:    colors.filter(c => c.category === "accent"),
  };
  const renderGroup = (label, list) => list.length === 0 ? null : (
    <div key={label}>
      <div className="section-title" style={{ marginTop: 16 }}>
        <h3>{label}</h3>
        <span className="meta">{list.length} {list.length === 1 ? "färg" : "färger"}</span>
      </div>
      <div className="swatches">
        {list.map(c => (
          <div className="sw" key={c.slug}>
            <div className="sw-fill" style={{ background: c.hex, borderBottom: c.hex.toLowerCase() === "#ffffff" ? "1px solid var(--rule)" : undefined }} />
            <div className="sw-meta">
              <div className="nm">{c.name}{c.role && <span className="sw-role"> · {c.role}</span>}</div>
              <div className="hex">{c.hex.toUpperCase()}</div>
              {(c.rgb || c.cmyk || c.pms) && (
                <div className="sw-specs">
                  {c.rgb  && <span>RGB {c.rgb}</span>}
                  {c.cmyk && <span>CMYK {c.cmyk}</span>}
                  {c.pms  && <span>PMS {c.pms}</span>}
                </div>
              )}
            </div>
          </div>
        ))}
      </div>
    </div>
  );
  return (
    <>
      <div className="section-title">
        <h3>Färgpalett</h3>
        <span className="meta">Tio färger + svart/vit · håll det enkelt, två färger räcker</span>
      </div>
      {renderGroup("Primärfärger", groups.primary)}
      {renderGroup("Sekundärfärger", groups.secondary)}
      {renderGroup("Accentfärg", groups.accent)}
    </>
  );
}

function LibType() {
  const u = useUser();
  const list = u.brand?.typefaces || [];
  const specimens = {
    serif: (
      <>Built slow,<br/><em>on purpose.</em></>
    ),
    sans: "Brightnest använder flexibel typografi. Minion Pro ska finnas med någonstans — t.ex. i en snygg rubbe.",
  };
  return (
    <>
      <div className="section-title">
        <h3>Typsnitt</h3>
        <span className="meta">Två familjer · Minion Pro är hjälten</span>
      </div>
      <div>
        {list.map((t, i) => {
          const isLast = i === list.length - 1;
          const specClass = t.category === "serif" ? "specimen-display"
                          : t.category === "sans"  ? "specimen-sans"
                          : "specimen-mono";
          return (
            <div className="type-row" key={t.slug} style={isLast ? { borderBottom: 0 } : undefined}>
              <div className="lbl">
                {t.category === "serif" ? "Display" : t.category === "sans" ? "Sans" : "Mono"}
                <b>{t.family}</b>
                <span className="scale">
                  {t.weights}
                  {t.note && <><br/>{t.note}</>}
                </span>
              </div>
              <div className={specClass}
                style={{ fontFamily: t.category === "serif" ? '"Minion Pro", Georgia, serif' : '"Good Sans", system-ui, sans-serif' }}>
                {specimens[t.category] || t.family}
              </div>
            </div>
          );
        })}
      </div>
    </>
  );
}

function LibImagery() {
  const u = useUser();
  const folders = u.imagery;        // null while loading
  const setFolders = u.setImagery;
  const [selSlug, setSelSlug] = useState(null);
  const [renaming, setRenaming] = useState(null);
  const [renameDraft, setRenameDraft] = useState("");
  const [confirmDelete, setConfirmDelete] = useState(null);
  const [confirmBulkDelete, setConfirmBulkDelete] = useState(false);
  const [moveDialog, setMoveDialog] = useState(false);
  // Set of photo URLs currently selected (folder-scoped — cleared on switch).
  const [selectedUrls, setSelectedUrls] = useState(() => new Set());
  const uploadRef = React.useRef(null);

  useEffect(() => {
    if (folders && folders.length > 0 && !selSlug) setSelSlug(folders[0].slug);
  }, [folders, selSlug]);
  // Selection is folder-scoped — clear when the user switches folders.
  useEffect(() => { setSelectedUrls(new Set()); }, [selSlug]);

  const toggleSelect = (url) => setSelectedUrls(prev => {
    const next = new Set(prev);
    if (next.has(url)) next.delete(url); else next.add(url);
    return next;
  });
  const clearSelection = () => setSelectedUrls(new Set());

  const selected = folders?.find(f => f.slug === selSlug) || null;
  const totalPhotos = folders?.reduce((a, f) => a + f.photos.length, 0) || 0;

  const slugify = (s) => s.toLowerCase()
    .replace(/[åä]/g, "a").replace(/ö/g, "o")
    .replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "") || "folder";

  const createFolder = async () => {
    const label = prompt("Namn på ny mapp?");
    if (!label) return;
    try {
      await u.createFolder(label.trim());
      // After refresh, pick the new folder by matching label.
      const next = (u.imagery || []).find(f => f.label === label.trim());
      if (next) setSelSlug(next.slug);
    } catch (err) { alert("Kunde inte skapa mapp: " + (err.body?.error || err.message)); }
  };

  const startRename = (f) => { setRenaming(f.slug); setRenameDraft(f.label); };
  const commitRename = async () => {
    const next = renameDraft.trim();
    const slug = renaming;
    setRenaming(null);
    if (!next || !slug) return;
    try { await u.renameFolder(slug, next); } catch (err) { alert("Kunde inte byta namn: " + (err.body?.error || err.message)); }
  };

  const removeFolder = async (slug) => {
    setConfirmDelete(null);
    try {
      await u.deleteFolder(slug);
      if (selSlug === slug) setSelSlug(null);
    } catch (err) { alert("Kunde inte ta bort: " + (err.body?.error || err.message)); }
  };

  const uploadPhotos = async (e) => {
    const files = [...(e.target.files || [])];
    e.target.value = "";
    if (!files.length || !selected) return;
    try { await u.uploadPhotos(selSlug, files); }
    catch (err) { alert("Uppladdning misslyckades: " + (err.body?.error || err.message)); }
  };

  const deletePhotosByUrl = async (urls) => {
    try { await u.deletePhotos(urls); setSelectedUrls(new Set()); }
    catch (err) { alert("Borttagning misslyckades: " + (err.body?.error || err.message)); }
  };

  const movePhotos = async (urls, toSlug) => {
    try {
      await u.movePhotos(urls, toSlug);
      setSelectedUrls(new Set());
      setSelSlug(toSlug);
      setMoveDialog(false);
    } catch (err) { alert("Flytt misslyckades: " + (err.body?.error || err.message)); }
  };

  const removePhoto = (idx) => {
    const photo = folders.find(f => f.slug === selSlug)?.photos[idx];
    setConfirmDelete(null);
    if (photo) deletePhotosByUrl([photo.url]);
  };
  const photoForConfirm = confirmDelete?.type === "photo"
    ? folders.find(f => f.slug === confirmDelete.slug)?.photos[confirmDelete.idx]
    : null;

  if (folders === null) {
    return <div className="copy-empty">Hämtar bildbiblioteket…</div>;
  }

  return (
    <>
      <div className="section-title">
        <h3>Imagery</h3>
        <span className="meta">{folders.length} mappar · {totalPhotos} foton</span>
      </div>

      <div className="imagery-browser">
        <aside className="imagery-folders">
          <div className="imagery-folders-h">
            <span>Mappar</span>
            <button type="button" className="tb-btn" onClick={createFolder}>
              <IconPlus size={12} /> Ny
            </button>
          </div>
          <ul className="imagery-folder-list">
            {folders.map(f => (
              <li key={f.slug} className={"img-folder " + (f.slug === selSlug ? "is-active" : "")}>
                {renaming === f.slug ? (
                  <input
                    autoFocus
                    className="text-input mini-rename"
                    value={renameDraft}
                    onChange={(e) => setRenameDraft(e.target.value)}
                    onBlur={commitRename}
                    onKeyDown={(e) => {
                      if (e.key === "Enter") commitRename();
                      if (e.key === "Escape") setRenaming(null);
                    }}
                  />
                ) : (
                  <button type="button" className="img-folder-btn" onClick={() => setSelSlug(f.slug)}>
                    <span className="img-folder-name">{f.label}</span>
                    <span className="img-folder-count">{f.photos.length}</span>
                  </button>
                )}
                {f.slug === selSlug && renaming !== f.slug && (
                  <span className="img-folder-ops">
                    <button type="button" className="nav-btn" onClick={() => startRename(f)} title="Byt namn">✎</button>
                    <button type="button" className="nav-btn" onClick={() => setConfirmDelete({ type: "folder", slug: f.slug })} title="Ta bort">×</button>
                  </span>
                )}
              </li>
            ))}
          </ul>
        </aside>

        <div className="imagery-pane">
          {!selected && (
            <div className="copy-empty">Skapa en mapp till vänster för att börja.</div>
          )}
          {selected && (
            <>
              <div className="imagery-pane-h">
                <div>
                  <h4 className="imagery-pane-title">{selected.label}</h4>
                  <span className="imagery-pane-meta">
                    {selected.photos.length} foton
                    {selectedUrls.size > 0 && <> · <strong>{selectedUrls.size} markerade</strong></>}
                  </span>
                </div>
                <div className="imagery-actions">
                  {selectedUrls.size > 0 && (
                    <>
                      <button type="button" className="tb-btn" onClick={clearSelection}>Avmarkera</button>
                      <button type="button" className="tb-btn" onClick={() => setMoveDialog(true)}>
                        Flytta {selectedUrls.size}
                      </button>
                      <button type="button" className="tb-btn is-destructive" onClick={() => setConfirmBulkDelete(true)}>
                        Ta bort {selectedUrls.size}
                      </button>
                    </>
                  )}
                  <input
                    type="file"
                    accept="image/*"
                    multiple
                    ref={uploadRef}
                    onChange={uploadPhotos}
                    style={{ display: "none" }}
                  />
                  <button type="button" className="tb-btn is-primary" onClick={() => uploadRef.current?.click()}>
                    <IconPlus size={12} stroke="#fff" /> Ladda upp foton
                  </button>
                </div>
              </div>
              {selected.photos.length === 0 && (
                <div className="copy-empty">Mappen är tom. Ladda upp foton för att fylla den.</div>
              )}
              {selected.photos.length > 0 && (
                <div className="imagery-grid">
                  {selected.photos.map((p, i) => {
                    const isSel = selectedUrls.has(p.url);
                    return (
                      <figure
                        className={"img-card" + (isSel ? " is-selected" : "")}
                        key={p.url + i}
                        onClick={(e) => { if (!e.target.closest(".img-remove")) toggleSelect(p.url); }}
                      >
                        <img src={p.url} alt={p.name} loading="lazy" />
                        {isSel && <span className="img-check" aria-hidden="true">✓</span>}
                        <figcaption>
                          <span className="img-name" title={p.name}>{p.name}</span>
                          <button
                            type="button"
                            className="img-remove"
                            onClick={(e) => { e.stopPropagation(); setConfirmDelete({ type: "photo", slug: selSlug, idx: i }); }}
                            title="Ta bort från mapp"
                          >×</button>
                        </figcaption>
                      </figure>
                    );
                  })}
                </div>
              )}
            </>
          )}
        </div>
      </div>

      {confirmDelete?.type === "folder" && (
        <>
          <div className="modal-backdrop" onClick={() => setConfirmDelete(null)} />
          <div className="modal" role="dialog">
            <h3 className="modal-title">Ta bort mapp?</h3>
            <p className="modal-body">
              <strong>{folders.find(f => f.slug === confirmDelete.slug)?.label}</strong> tas bort tillsammans med alla foton i mappen. Originalen på servern påverkas inte.
            </p>
            <div className="modal-actions">
              <button type="button" className="tb-btn" onClick={() => setConfirmDelete(null)}>Avbryt</button>
              <button type="button" className="tb-btn is-primary" onClick={() => removeFolder(confirmDelete.slug)}>Ta bort</button>
            </div>
          </div>
        </>
      )}

      {confirmDelete?.type === "photo" && photoForConfirm && (
        <>
          <div className="modal-backdrop" onClick={() => setConfirmDelete(null)} />
          <div className="modal" role="dialog" aria-labelledby="photo-rm-title">
            <h3 className="modal-title" id="photo-rm-title">Ta bort foto?</h3>
            <p className="modal-body">
              Vill du ta bort <strong>{photoForConfirm.name}</strong> från arkivet? Filen raderas också från servern och kan inte återställas.
            </p>
            <div className="modal-actions">
              <button type="button" className="tb-btn" onClick={() => setConfirmDelete(null)}>Avbryt</button>
              <button type="button" className="tb-btn is-primary"
                onClick={() => removePhoto(confirmDelete.idx)} autoFocus>Ta bort</button>
            </div>
          </div>
        </>
      )}

      {confirmBulkDelete && (
        <>
          <div className="modal-backdrop" onClick={() => setConfirmBulkDelete(false)} />
          <div className="modal" role="dialog">
            <h3 className="modal-title">Ta bort {selectedUrls.size} foton?</h3>
            <p className="modal-body">
              Filerna raderas permanent från servern och kan inte återställas.
            </p>
            <div className="modal-actions">
              <button type="button" className="tb-btn" onClick={() => setConfirmBulkDelete(false)}>Avbryt</button>
              <button type="button" className="tb-btn is-primary" autoFocus
                onClick={() => { setConfirmBulkDelete(false); deletePhotosByUrl([...selectedUrls]); }}>Ta bort</button>
            </div>
          </div>
        </>
      )}

      {moveDialog && (
        <>
          <div className="modal-backdrop" onClick={() => setMoveDialog(false)} />
          <div className="modal" role="dialog">
            <h3 className="modal-title">Flytta {selectedUrls.size} foton</h3>
            <p className="modal-body">Välj målmapp:</p>
            <div className="move-targets">
              {folders.filter(f => f.slug !== selSlug).map(f => (
                <button key={f.slug} type="button" className="move-target"
                  onClick={() => movePhotos([...selectedUrls], f.slug)}>
                  <span className="move-target-name">{f.label}</span>
                  <span className="move-target-count">{f.photos.length}</span>
                </button>
              ))}
            </div>
            <div className="modal-actions">
              <button type="button" className="tb-btn" onClick={() => setMoveDialog(false)}>Avbryt</button>
            </div>
          </div>
        </>
      )}
    </>
  );
}

function LibVoice() {
  const u = useUser();
  const profile = u.brand?.profile;
  if (!profile) return <div className="copy-empty">Brand-profilen hämtas…</div>;
  const groups = [
    { h: "Använd",  words: profile.wordUse,   tone: "use"   },
    { h: "Undvik",  words: profile.wordAvoid, tone: "avoid" },
    { h: "Aldrig",  words: profile.wordNever, tone: "never" },
  ];
  return (
    <>
      <div className="section-title">
        <h3>Brand på en sida</h3>
        <span className="meta">Från Playbook v3.0</span>
      </div>
      <div className="brand-on-page">
        <div className="bop-row"><span className="bop-k">Vision</span><span className="bop-v">{profile.vision}</span></div>
        <div className="bop-row"><span className="bop-k">Erbjudande</span><span className="bop-v">{profile.offering}</span></div>
        <div className="bop-row"><span className="bop-k">Mission</span><span className="bop-v">{profile.mission}</span></div>
        <div className="bop-row"><span className="bop-k">Tonalitet</span><span className="bop-v">{profile.tonality}</span></div>
        <div className="bop-row"><span className="bop-k">Kultur</span><span className="bop-v">{profile.culture}</span></div>
        {profile.playbookUrl && (
          <div className="bop-row">
            <span className="bop-k">Playbook</span>
            <span className="bop-v"><a className="bop-link" href={profile.playbookUrl} target="_blank" rel="noopener">Öppna PDF (v3.0)</a></span>
          </div>
        )}
      </div>

      <div style={{ height: 32 }} />
      <div className="section-title">
        <h3>Pelare</h3>
        <span className="meta">{profile.tonality}</span>
      </div>
      <div className="voice-grid">
        {profile.pillars.map((v, i) => (
          <div className="voice-card" key={i}>
            <div className="mono" style={{ fontSize:10, color:"var(--mute)", letterSpacing:".12em" }}>0{i+1} · PELARE</div>
            <div className="v-h">{v.h}</div>
            <div style={{ flex: 1 }} />
            <div className="v-do">   <span className="k">Gör</span><span>{v.do}</span></div>
            <div className="v-dont"> <span className="k">Inte</span><span>{v.dont}</span></div>
          </div>
        ))}
      </div>

      <div style={{ height: 32 }} />
      <div className="section-title">
        <h3>Ordlista</h3>
        <span className="meta">Använd · Undvik · Aldrig</span>
      </div>
      <div style={{ display:"grid", gridTemplateColumns:"repeat(3, 1fr)", gap: 24 }}>
        {groups.map((g, i) => (
          <div key={i}>
            <div className="mono" style={{ fontSize:10, color:"var(--mute)", letterSpacing:".12em", textTransform:"uppercase", marginBottom:10 }}>{g.h}</div>
            <div style={{ display:"flex", flexWrap:"wrap", gap:6 }}>
              {g.words.map(w => (
                <span key={w} className={"word-pill word-" + g.tone}>{w}</span>
              ))}
            </div>
          </div>
        ))}
      </div>
    </>
  );
}

function LibComponents() {
  const comps = [
    "Button / primary","Button / ghost","Tag / mono","Chip / filter",
    "Card / editorial","Card / product","Lockup / brand","Lockup / event",
    "Quote / display","Quote / inline","List / spec","Divider / hairline",
  ];
  return (
    <>
      <div className="section-title">
        <h3>Components</h3>
        <span className="meta">64 patterns · Figma + React tokens</span>
      </div>
      <div style={{ display:"grid", gridTemplateColumns:"repeat(4, 1fr)", gap: 1, background:"var(--rule)", border:"1px solid var(--rule)" }}>
        {comps.map((c, i) => (
          <div key={i} style={{ background:"var(--bg)", padding:24, minHeight:160, display:"flex", flexDirection:"column", justifyContent:"space-between" }}>
            <div style={{
              flex:1, display:"grid", placeItems:"center",
              background:"repeating-linear-gradient(135deg, var(--paper) 0 8px, var(--paper-2) 8px 16px)"
            }}>
              <span className="mono" style={{ fontSize:10, color:"var(--mute)", letterSpacing:".06em" }}>preview</span>
            </div>
            <div style={{ marginTop:12, fontSize:12, display:"flex", justifyContent:"space-between" }}>
              <span>{c}</span>
              <span className="mono" style={{ fontSize:10, color:"var(--mute)" }}>v{(i%3)+1}.{i%5}</span>
            </div>
          </div>
        ))}
      </div>
    </>
  );
}

/* ============================================================
   STUDIO (CREATE)
   ============================================================ */
/* ============================================================
   STUDIO — brief → AI copy → photos per platform → brand marks → publish
   ============================================================ */

// Brand marks the user can place on previews. Each mark has an intrinsic
// viewBox so it keeps its real aspect when scaled — no stretching to
// fill the frame. Real implementation pulls these from Logos & marks.
const BRAND_MARKS = [
  { id: "mark-b", label: "B mark", w: 60, h: 60,
    render: (c) => <text x="30" y="46" textAnchor="middle" fontFamily="Minion Pro, Georgia, serif" fontStyle="italic" fontSize="52" fill={c.fg}>B</text> },
  { id: "mark-word", label: "Wordmark", w: 180, h: 24,
    render: (c) => <text x="90" y="18" textAnchor="middle" fontFamily="Good Sans, sans-serif" fontWeight="700" fontSize="16" letterSpacing="2" fill={c.fg}>BRIGHTNEST</text> },
  { id: "mark-ember", label: "Ember swatch", w: 32, h: 32,
    render: () => <rect x="0" y="0" width="32" height="32" fill="#D9712F"/> },
  { id: "mark-moss", label: "Moss swatch", w: 32, h: 32,
    render: () => <rect x="0" y="0" width="32" height="32" fill="#3D5A40"/> },
  { id: "mark-bar", label: "Foot rule", w: 200, h: 4,
    render: (c) => <rect x="0" y="0" width="200" height="4" fill={c.fg}/> },
];
const markById = (mid) => BRAND_MARKS.find(m => m.id === mid);

// Mock AI copy generator. Replace with real Claude/OpenAI call later —
// the function signature stays the same.
function generateCopyMock(brief, channelId, tone) {
  const text = (brief || "").trim() || "vårens kollektion";
  const t = (tone || "calm");
  const head = text.charAt(0).toUpperCase() + text.slice(1);
  const variantsByChannel = {
    instagram: [
      `${head}. Materialet är vakent.`,
      `${head} — det vi gör, gör vi länge.`,
      `${head}. Mer i bio.`,
    ],
    linkedin:  [
      `${head}. En reflektion kring våra val.`,
      `Vi tänkte högt om ${text}. Här är vad vi landade i.`,
      `${head}. Inga slagord — bara konkretion.`,
    ],
    facebook:  [
      `${head} — ${t === "warm" ? "varmt välkomna in." : "kom och se."}`,
      `${head}. Helgen, väl spenderad.`,
    ],
    newsletter:[
      `Veckans utskick: ${text}. Sex sektioner, läs vidare på webben.`,
    ],
    web:       [
      `${head}. En text om hantverk, materialval och hur tider känns.`,
    ],
    tiktok:    [
      `${head} — 30 sek, ingen voice-over.`,
    ],
    youtube:   [
      `${head}. Lång klipp, kort poäng.`,
    ],
  };
  return variantsByChannel[channelId] || [head];
}

// ImageryPicker — modal that reads the imagery library out of UserCtx so
// Studio can pick an existing photo, and also drop new files into the
// currently-active folder which then live in the same archive.
function ImageryPicker({ onPick, onClose }) {
  const u = useUser();
  const folders = u.imagery || [];
  const [selSlug, setSelSlug] = useState(folders[0]?.slug || null);
  const [over, setOver] = useState(false);

  useEffect(() => {
    if (!selSlug && folders[0]) setSelSlug(folders[0].slug);
  }, [folders, selSlug]);
  useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [onClose]);

  const selected = folders.find(f => f.slug === selSlug);

  const addFilesToActive = async (files) => {
    if (!selected) return;
    const imageFiles = [...files].filter(f => /^image\//.test(f.type));
    if (!imageFiles.length) return;
    try { await u.uploadPhotos(selSlug, imageFiles); }
    catch (err) { alert("Uppladdning misslyckades: " + (err.body?.error || err.message)); }
  };

  const onDragOver  = (e) => { e.preventDefault(); setOver(true); };
  const onDragLeave = ()  => setOver(false);
  const onDrop      = (e) => {
    e.preventDefault(); setOver(false);
    addFilesToActive(e.dataTransfer.files);
  };

  return (
    <>
      <div className="modal-backdrop" onClick={onClose} />
      <div className="modal picker-modal" role="dialog" aria-label="Hämta från bildgalleri">
        <header className="picker-h">
          <div>
            <h3 className="modal-title">Hämta från bildgalleri</h3>
            <p className="picker-sub">Välj en bild ur arkivet eller släpp nya filer i fönstret — de sparas i den aktiva mappen.</p>
          </div>
          <button type="button" className="drawer-close" onClick={onClose} aria-label="Stäng">×</button>
        </header>

        <div className="picker-body">
          <aside className="imagery-folders picker-folders">
            <div className="imagery-folders-h"><span>Mappar</span></div>
            <ul className="imagery-folder-list">
              {folders.map(f => (
                <li key={f.slug} className={"img-folder " + (f.slug === selSlug ? "is-active" : "")}>
                  <button type="button" className="img-folder-btn" onClick={() => setSelSlug(f.slug)}>
                    <span className="img-folder-name">{f.label}</span>
                    <span className="img-folder-count">{f.photos.length}</span>
                  </button>
                </li>
              ))}
            </ul>
          </aside>

          <div
            className={"picker-grid-wrap " + (over ? "is-over" : "")}
            onDragOver={onDragOver}
            onDragLeave={onDragLeave}
            onDrop={onDrop}
          >
            {over && (
              <div className="picker-drop">Släpp för att spara i <strong>{selected?.label}</strong></div>
            )}
            {(!selected || selected.photos.length === 0) && !over && (
              <div className="copy-empty">Inga foton — släpp filer för att lägga till.</div>
            )}
            {selected && selected.photos.length > 0 && (
              <div className="picker-grid">
                {selected.photos.map((p, i) => (
                  <button key={p.url + i} type="button" className="picker-card"
                    onClick={() => { onPick({ url: p.url, name: p.name }); onClose(); }}
                    title={p.name}>
                    <img src={p.url} alt={p.name} loading="lazy" />
                    <span className="picker-card-name">{p.name}</span>
                  </button>
                ))}
              </div>
            )}
          </div>
        </div>
      </div>
    </>
  );
}

// MediaUploader — file input + state pill. Accepts image/* and video/*.
function MediaUploader({ label, help, value, onUpload, onPick, onClear, compact = false }) {
  const inputRef = React.useRef(null);
  const [pickerOpen, setPickerOpen] = useState(false);
  const triggerPick = () => inputRef.current?.click();
  const filename = value?.name || (value?.url ? "uppladdad fil" : null);
  return (
    <div className={"media-uploader" + (compact ? " is-compact" : "")}>
      <div className="media-uploader-head">
        <span className="media-uploader-label">{label}</span>
        {value && <span className="media-kind-tag">{value.kind === "video" ? "▶ video" : "● bild"}</span>}
      </div>
      <div className="media-uploader-row">
        <input
          type="file"
          accept="image/*,video/*"
          ref={inputRef}
          onChange={(e) => { const f = e.target.files?.[0]; if (f) onUpload(f); e.target.value = ""; }}
          style={{ display: "none" }}
        />
        <button type="button" className="tb-btn" onClick={triggerPick}>
          <IconImage size={12} /> {value ? "Byt fil" : "Ladda upp"}
        </button>
        {onPick && (
          <button type="button" className="tb-btn" onClick={() => setPickerOpen(true)}>
            Hämta från bildgalleri
          </button>
        )}
        {value && onClear && (
          <button type="button" className="tb-btn" onClick={onClear}>Ta bort</button>
        )}
        {filename && <span className="media-filename" title={filename}>{filename}</span>}
      </div>
      {help && !compact && <span className="field-help">{help}</span>}
      {pickerOpen && (
        <ImageryPicker onClose={() => setPickerOpen(false)} onPick={onPick} />
      )}
    </div>
  );
}

// FormatPreview — thumbnail with draggable object-position crop and
// draggable, scalable brand mark overlays that preserve their aspect.
// Decide which "skin" wraps the format frame. Returns a kind that maps to a
// CSS modifier and tells us whether to overlay chrome on top of the frame
// (full-bleed: story, reel, shorts, tiktok) or stack it above/below.
function chromeKindFor(channelId, formatSlug) {
  if (channelId === "instagram") {
    if (formatSlug === "ig_story") return { kind: "ig-story", fullBleed: true };
    if (formatSlug === "ig_reel")  return { kind: "ig-reel",  fullBleed: true };
    return { kind: "ig-feed", fullBleed: false }; // ig_feed_square / portrait / ad
  }
  if (channelId === "linkedin")   return { kind: "li-feed",  fullBleed: false };
  if (channelId === "facebook")   return { kind: "fb-feed",  fullBleed: false };
  if (channelId === "tiktok")     return { kind: "tt-video", fullBleed: true };
  if (channelId === "youtube") {
    if (formatSlug === "yt_shorts") return { kind: "yt-shorts", fullBleed: true };
    return { kind: "yt-video", fullBleed: false };
  }
  if (channelId === "newsletter") return { kind: "nl-hero", fullBleed: false };
  if (channelId === "web")        return { kind: "web-hero", fullBleed: false };
  return { kind: "plain", fullBleed: false };
}

// Default URL prefix per channel — placeholder shown when the workspace
// hasn't yet wired up its real account.
function defaultAccountUrlFor(channelId) {
  return ({
    instagram:  "https://instagram.com/<handle>",
    linkedin:   "https://linkedin.com/company/<handle>",
    facebook:   "https://facebook.com/<handle>",
    tiktok:     "https://tiktok.com/@<handle>",
    youtube:    "https://youtube.com/@<handle>",
    newsletter: "https://example.com/nyhetsbrev",
    web:        "https://example.com",
  })[channelId] || "";
}

// Minimal platform brand glyphs. Used inline in the chrome header so each
// preview is unmistakeably "the IG one", "the LinkedIn one" etc.
const PlatformIcons = {
  instagram: (p) => (
    <svg viewBox="0 0 24 24" width={p.size||14} height={p.size||14} fill="none" stroke="currentColor" strokeWidth="1.6">
      <rect x="3" y="3" width="18" height="18" rx="5" />
      <circle cx="12" cy="12" r="4" />
      <circle cx="17.5" cy="6.5" r="0.9" fill="currentColor" stroke="none" />
    </svg>
  ),
  linkedin: (p) => (
    <svg viewBox="0 0 24 24" width={p.size||14} height={p.size||14} fill="currentColor">
      <path d="M4.98 3.5A2.5 2.5 0 1 1 5 8.5a2.5 2.5 0 0 1 0-5zM3 9.5h4V21H3zM10 9.5h3.8v1.6h.06c.53-1 1.83-2 3.76-2C21.36 9.1 22 11 22 14.3V21h-4v-5.9c0-1.4-.03-3.2-2-3.2-2 0-2.3 1.55-2.3 3.1V21h-4z"/>
    </svg>
  ),
  facebook: (p) => (
    <svg viewBox="0 0 24 24" width={p.size||14} height={p.size||14} fill="currentColor">
      <path d="M13.5 22v-9h3l.5-4h-3.5V6.5c0-1.15.3-1.93 2-1.93H17V1.2A28 28 0 0 0 14.6 1c-2.5 0-4.1 1.5-4.1 4.3V9H7.5v4h3v9z"/>
    </svg>
  ),
  tiktok: (p) => (
    <svg viewBox="0 0 24 24" width={p.size||14} height={p.size||14} fill="currentColor">
      <path d="M16.5 2c.7 2.3 2.4 4 4.8 4.3v3.1a8.4 8.4 0 0 1-4.8-1.5v7.4a6.5 6.5 0 1 1-6.5-6.5v3.2a3.3 3.3 0 1 0 3.3 3.3V2z"/>
    </svg>
  ),
  youtube: (p) => (
    <svg viewBox="0 0 24 24" width={p.size||14} height={p.size||14} fill="currentColor">
      <path d="M22 8.2a3 3 0 0 0-2.1-2.1C18 5.6 12 5.6 12 5.6s-6 0-7.9.5A3 3 0 0 0 2 8.2 31 31 0 0 0 1.5 12 31 31 0 0 0 2 15.8a3 3 0 0 0 2.1 2.1c1.9.5 7.9.5 7.9.5s6 0 7.9-.5A3 3 0 0 0 22 15.8a31 31 0 0 0 .5-3.8A31 31 0 0 0 22 8.2zM10 15V9l5 3z"/>
    </svg>
  ),
  newsletter: (p) => (
    <svg viewBox="0 0 24 24" width={p.size||14} height={p.size||14} fill="none" stroke="currentColor" strokeWidth="1.6">
      <rect x="3" y="5" width="18" height="14" rx="1.5" />
      <path d="M3 6l9 7 9-7" />
    </svg>
  ),
  web: (p) => (
    <svg viewBox="0 0 24 24" width={p.size||14} height={p.size||14} fill="none" stroke="currentColor" strokeWidth="1.6">
      <circle cx="12" cy="12" r="9" />
      <path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18" />
    </svg>
  ),
};

// Tiny icon set used by the platform chromes. Sized in px via width/height.
const ChromeIcons = {
  heart:    (p) => <svg viewBox="0 0 24 24" width={p.size||18} height={p.size||18} fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M12 21s-7-4.35-7-10a4 4 0 0 1 7-2.6A4 4 0 0 1 19 11c0 5.65-7 10-7 10z"/></svg>,
  comment:  (p) => <svg viewBox="0 0 24 24" width={p.size||18} height={p.size||18} fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M21 12a8 8 0 1 1-3.06-6.32L21 5l-1.32 3.06A7.96 7.96 0 0 1 21 12z"/></svg>,
  share:    (p) => <svg viewBox="0 0 24 24" width={p.size||18} height={p.size||18} fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M22 2 11 13"/><path d="M22 2l-7 20-4-9-9-4 20-7z"/></svg>,
  bookmark: (p) => <svg viewBox="0 0 24 24" width={p.size||18} height={p.size||18} fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M19 21l-7-5-7 5V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2z"/></svg>,
  dots:     (p) => <svg viewBox="0 0 24 24" width={p.size||16} height={p.size||16} fill="currentColor"><circle cx="5" cy="12" r="1.6"/><circle cx="12" cy="12" r="1.6"/><circle cx="19" cy="12" r="1.6"/></svg>,
  thumb:    (p) => <svg viewBox="0 0 24 24" width={p.size||18} height={p.size||18} fill="none" stroke="currentColor" strokeWidth="1.6"><path d="M7 11V21h-3v-10zM7 11l5-9a3 3 0 0 1 3 3v5h4a2 2 0 0 1 2 2l-2 8a2 2 0 0 1-2 1h-10z"/></svg>,
  plus:     (p) => <svg viewBox="0 0 24 24" width={p.size||14} height={p.size||14} fill="currentColor"><path d="M11 5h2v14h-2zM5 11h14v2H5z"/></svg>,
  play:     (p) => <svg viewBox="0 0 24 24" width={p.size||18} height={p.size||18} fill="currentColor"><path d="M8 5v14l11-7z"/></svg>,
  globe:    (p) => <svg viewBox="0 0 24 24" width={p.size||12} height={p.size||12} fill="none" stroke="currentColor" strokeWidth="1.6"><circle cx="12" cy="12" r="9"/><path d="M3 12h18M12 3a14 14 0 0 1 0 18M12 3a14 14 0 0 0 0 18"/></svg>,
};

// Brand avatar — uses the brand logo if available, otherwise initials chip.
function BrandAvatar({ brand, orgName, size = 28, light = false }) {
  const logo = brand?.profile?.logoUrl;
  const init = (orgName || "Brightnest").split(/\s+/).map(w => w[0]).slice(0,2).join("").toUpperCase();
  const style = { width: size, height: size, flex: `0 0 ${size}px` };
  if (logo) {
    return (
      <span className={"chrome-avatar has-logo " + (light ? "is-light" : "")} style={style}>
        <img src={logo} alt="" />
      </span>
    );
  }
  return <span className={"chrome-avatar " + (light ? "is-light" : "")} style={style}>{init}</span>;
}

function FormatPreview({ channelId, format, media, crop, onCropChange, onReset, marks, posFor, onMarkMove, brand, orgName, platform, copyText }) {
  const isVideo = media?.kind === "video";
  const dragRef = React.useRef(null);

  // Pan the underlying media via object-position.
  const startCropDrag = (e) => {
    if (!media) return;
    if (e.target.closest(".fp-mark")) return;       // marks own their own drag
    e.preventDefault();
    const frame = dragRef.current; if (!frame) return;
    frame.classList.add("is-dragging");
    const rect = frame.getBoundingClientRect();
    const startX = crop.x, startY = crop.y;
    const startMx = e.clientX, startMy = e.clientY;
    const onMove = (ev) => {
      const dx = -((ev.clientX - startMx) / rect.width)  * 100;
      const dy = -((ev.clientY - startMy) / rect.height) * 100;
      onCropChange({
        x: Math.max(0, Math.min(100, startX + dx)),
        y: Math.max(0, Math.min(100, startY + dy)),
      });
    };
    const onUp = () => {
      frame.classList.remove("is-dragging");
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseup",   onUp);
    };
    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseup",   onUp);
  };

  // Move a mark within THIS format only. x/y are stored as % of the frame
  // and only affect this specific (channel, format) pair.
  const startMarkDrag = (mark) => (e) => {
    e.preventDefault();
    e.stopPropagation();
    const frame = dragRef.current; if (!frame) return;
    const rect = frame.getBoundingClientRect();
    const start = posFor(mark);
    const startMx = e.clientX, startMy = e.clientY;
    const onMove = (ev) => {
      const dx = ((ev.clientX - startMx) / rect.width)  * 100;
      const dy = ((ev.clientY - startMy) / rect.height) * 100;
      onMarkMove(mark.id, {
        x: Math.max(0, Math.min(100, start.x + dx)),
        y: Math.max(0, Math.min(100, start.y + dy)),
      });
    };
    const onUp = () => {
      window.removeEventListener("mousemove", onMove);
      window.removeEventListener("mouseup",   onUp);
    };
    window.addEventListener("mousemove", onMove);
    window.addEventListener("mouseup",   onUp);
  };

  const objectPosition = `${crop.x}% ${crop.y}%`;
  const isCropped = crop.x !== 50 || crop.y !== 50;
  const { kind, fullBleed } = chromeKindFor(channelId, format.slug);
  // Prefer the workspace's real handle (set in Settings → Plattformar) so
  // each preview reads as "@brightnest_official" instead of a synthesised
  // string. Fall back to the org name otherwise.
  const handle = platform?.handle || (orgName || "Brightnest").toLowerCase().replace(/\s+/g, "");
  const platformIcon = PlatformIcons[channelId];
  const isAd   = (format.slug || "").includes("_ad");

  // The frame itself (media + marks). Reused by every chrome below.
  const frame = (
    <div
      ref={dragRef}
      className={"fp-frame" + (media ? " has-media" : "")}
      style={{ aspectRatio: format.w + " / " + format.h }}
      onMouseDown={startCropDrag}
    >
      {!media && <div className="fp-empty">Ingen fil</div>}
      {media && !isVideo && (
        <img src={media.url} alt="" draggable={false} style={{ objectPosition }} />
      )}
      {media && isVideo && (
        <video src={media.url} muted autoPlay loop playsInline
               style={{ objectPosition }} />
      )}
      {marks?.map(m => {
        const b = markById(m.mid); if (!b) return null;
        const p = posFor(m);
        return (
          <div key={m.id} className="fp-mark"
               style={{ left: `${p.x}%`, top: `${p.y}%`, width: `${22 * m.scale}%` }}
               onMouseDown={startMarkDrag(m)}>
            <svg viewBox={`0 0 ${b.w} ${b.h}`} preserveAspectRatio="xMidYMid meet"
                 style={{ display: "block", width: "100%", height: "auto" }}>
              {b.render({ fg: "#fff" })}
            </svg>
          </div>
        );
      })}
    </div>
  );

  const renderChrome = () => {
    if (kind === "ig-feed") {
      return (
        <div className="chrome chrome-ig-feed">
          <div className="chrome-bar">
            <BrandAvatar brand={brand} orgName={orgName} size={28} />
            <div className="chrome-meta">
              <span className="chrome-name">{handle}</span>
              {isAd && <span className="chrome-sub">Sponsored</span>}
            </div>
            {platformIcon && <span className="chrome-pf">{platformIcon({ size: 14 })}</span>}
            <ChromeIcons.dots />
          </div>
          {frame}
          <div className="chrome-actions">
            <ChromeIcons.heart />
            <ChromeIcons.comment />
            <ChromeIcons.share />
            <span className="chrome-spacer" />
            <ChromeIcons.bookmark />
          </div>
          <div className="chrome-caption">
            <strong>{handle}</strong> {copyText || <em className="chrome-mute">Inläggstext visas här</em>}
          </div>
        </div>
      );
    }
    if (kind === "ig-story" || kind === "ig-reel" || kind === "tt-video" || kind === "yt-shorts") {
      return (
        <div className={"chrome chrome-full chrome-" + kind}>
          {frame}
          <div className="chrome-top-overlay">
            {kind === "ig-story" && <div className="chrome-progress"><span /><span className="dim"/><span className="dim"/></div>}
            <div className="chrome-overlay-row">
              <BrandAvatar brand={brand} orgName={orgName} size={26} light />
              <span className="chrome-name light">{handle}</span>
              {kind === "ig-story" && <span className="chrome-sub light">2 tim</span>}
              {platformIcon && <span className="chrome-pf light">{platformIcon({ size: 14 })}</span>}
            </div>
          </div>
          {(kind === "ig-reel" || kind === "tt-video" || kind === "yt-shorts") && (
            <div className="chrome-rail">
              <ChromeIcons.heart size={22} />
              <span className="chrome-rail-n">12k</span>
              <ChromeIcons.comment size={22} />
              <span className="chrome-rail-n">284</span>
              <ChromeIcons.share size={22} />
              <ChromeIcons.bookmark size={22} />
            </div>
          )}
          <div className="chrome-bottom-overlay">
            <div className="chrome-caption light">
              <strong>{handle}</strong> {copyText || <em className="chrome-mute">Inläggstext…</em>}
            </div>
            {kind === "ig-story" && <div className="chrome-story-cta">Skicka meddelande ↗</div>}
          </div>
        </div>
      );
    }
    if (kind === "li-feed") {
      return (
        <div className="chrome chrome-li-feed">
          <div className="chrome-bar">
            <BrandAvatar brand={brand} orgName={orgName} size={40} />
            <div className="chrome-meta col">
              <span className="chrome-name">{orgName || "Brightnest"}</span>
              <span className="chrome-sub">Strategi · Brand · Kommunikation</span>
              <span className="chrome-sub xs">2 tim · <ChromeIcons.globe /></span>
            </div>
            {platformIcon && <span className="chrome-pf">{platformIcon({ size: 14 })}</span>}
            <ChromeIcons.dots />
          </div>
          <div className="chrome-caption li">
            {copyText || <em className="chrome-mute">Inläggstext visas här</em>}
            {isAd && <div className="chrome-promoted">Promoted</div>}
          </div>
          {frame}
          <div className="chrome-actions li">
            <span className="chrome-react"><ChromeIcons.thumb size={16} /> Gilla</span>
            <span className="chrome-react"><ChromeIcons.comment size={16} /> Kommentera</span>
            <span className="chrome-react"><ChromeIcons.share size={16} /> Dela</span>
          </div>
        </div>
      );
    }
    if (kind === "fb-feed") {
      return (
        <div className="chrome chrome-fb-feed">
          <div className="chrome-bar">
            <BrandAvatar brand={brand} orgName={orgName} size={40} />
            <div className="chrome-meta col">
              <span className="chrome-name">{orgName || "Brightnest"}</span>
              <span className="chrome-sub">{isAd ? "Sponsored" : "2 tim"} · <ChromeIcons.globe /></span>
            </div>
            {platformIcon && <span className="chrome-pf">{platformIcon({ size: 14 })}</span>}
            <ChromeIcons.dots />
          </div>
          <div className="chrome-caption fb">
            {copyText || <em className="chrome-mute">Inläggstext visas här</em>}
          </div>
          {frame}
          <div className="chrome-actions fb">
            <span className="chrome-react"><ChromeIcons.thumb size={16} /> Gilla</span>
            <span className="chrome-react"><ChromeIcons.comment size={16} /> Kommentera</span>
            <span className="chrome-react"><ChromeIcons.share size={16} /> Dela</span>
          </div>
        </div>
      );
    }
    if (kind === "yt-video") {
      return (
        <div className="chrome chrome-yt-video">
          <div className="chrome-yt-frame">
            {frame}
            <div className="chrome-yt-play"><ChromeIcons.play size={32} /></div>
            <div className="chrome-yt-time">10:24</div>
          </div>
          <div className="chrome-yt-row">
            <BrandAvatar brand={brand} orgName={orgName} size={36} />
            <div className="chrome-meta col">
              <span className="chrome-yt-title">{copyText || <em className="chrome-mute">Videotitel…</em>}</span>
              <span className="chrome-sub">{orgName || "Brightnest"} · 1.2k visningar · 2 tim sedan</span>
            </div>
          </div>
        </div>
      );
    }
    if (kind === "nl-hero") {
      return (
        <div className="chrome chrome-nl">
          <div className="chrome-nl-head">
            <BrandAvatar brand={brand} orgName={orgName} size={22} />
            <span className="chrome-nl-from">{orgName || "Brightnest"} &lt;hello@brightnest.se&gt;</span>
            <span className="chrome-nl-date">idag · 09:00</span>
          </div>
          <div className="chrome-nl-subject">{copyText?.split("\n")[0] || <em className="chrome-mute">Ämnesrad</em>}</div>
          {frame}
        </div>
      );
    }
    if (kind === "web-hero") {
      return (
        <div className="chrome chrome-web">
          <div className="chrome-web-bar">
            <span className="chrome-web-dot" />
            <span className="chrome-web-dot" />
            <span className="chrome-web-dot" />
            <span className="chrome-web-url">brightnest.se</span>
          </div>
          {frame}
        </div>
      );
    }
    return frame;
  };

  return (
    <div className={"fp fp-kind-" + kind}>
      <div className="fp-h">
        <span className="fp-name">{format.label}</span>
        <span className="fp-dims">{format.w}×{format.h}</span>
        {isCropped && (
          <button type="button" className="fp-reset" onClick={onReset} title="Återställ crop">↺</button>
        )}
      </div>
      {renderChrome()}
    </div>
  );
}

// Studio templates — save & apply reusable "graphic recipes" (marks + their
// per-format positions/scales + optional crops). Copy is intentionally not
// part of a template; that's content, not the recipe.
function StudioTemplates({ stagedMarks, markPositions, crops, onApply }) {
  const u = useUser();
  const list = u.studioTemplates || [];
  const [saving, setSaving] = useState(false);
  const canSave = stagedMarks.length > 0;

  const save = async () => {
    if (!canSave || saving) return;
    const name = window.prompt("Namn på mallen?", "");
    if (!name?.trim()) return;
    setSaving(true);
    try {
      await u.createStudioTemplate({
        name: name.trim(),
        marks: stagedMarks,
        markPositions,
        crops,
      });
    } catch (e) {
      console.error(e);
      alert("Kunde inte spara mallen.");
    } finally {
      setSaving(false);
    }
  };
  const remove = async (id, e) => {
    e.stopPropagation();
    if (!window.confirm("Ta bort mallen?")) return;
    try { await u.deleteStudioTemplate(id); }
    catch (err) { console.error(err); alert("Kunde inte ta bort."); }
  };

  return (
    <div className="studio-templates">
      <div className="studio-templates-h">
        <span className="studio-templates-label">Mallar</span>
        <span className="meta">{list.length === 0 ? "Inga sparade än" : `${list.length} sparad${list.length === 1 ? "" : "e"}`}</span>
        <button type="button" className="tb-btn" disabled={!canSave || saving} onClick={save}>
          {saving ? "Sparar…" : "Spara som mall…"}
        </button>
      </div>
      {list.length > 0 && (
        <div className="studio-templates-list">
          {list.map(t => (
            <button key={t.id} type="button" className="studio-template-card"
                    onClick={() => onApply(t)}
                    title="Klicka för att lägga på den här mallen">
              <div className="studio-template-thumb">
                {(t.marks || []).slice(0, 4).map(m => {
                  const b = markById(m.mid);
                  if (!b) return null;
                  return (
                    <svg key={m.id} viewBox={`0 0 ${b.w} ${b.h}`} preserveAspectRatio="xMidYMid meet">
                      {b.render({ fg: "var(--ink)" })}
                    </svg>
                  );
                })}
                {(t.marks || []).length === 0 && <span className="studio-template-empty">—</span>}
              </div>
              <div className="studio-template-meta">
                <span className="studio-template-name">{t.name}</span>
                <span className="studio-template-sub">
                  {(t.marks || []).length} märk{(t.marks || []).length === 1 ? "e" : "en"}
                </span>
              </div>
              <span className="studio-template-x" onClick={(e) => remove(t.id, e)} role="button" aria-label="Ta bort">×</span>
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

function StudioView() {
  const u = useUser();
  const enabledPlatforms = u.platforms.filter(p => p.enabled);
  const [brief, setBrief] = useState("");
  const [tone, setTone] = useState("calm");
  const [targets, setTargets] = useState(() => enabledPlatforms.slice(0, 2).map(p => p.channelId));
  const [variantsByCh, setVariantsByCh] = useState({});  // channelId → [string]
  const [picked, setPicked] = useState({});               // channelId → string
  // Media is per-channel; channel "_default" is the fallback used when a
  // specific channel has no override. Each entry: { url, kind: 'image'|'video' }.
  const [media, setMedia] = useState({});
  // Crop overrides per (channelId + formatId): { x: 0–100, y: 0–100 }.
  // Defaults to {50, 50} if unset (centred — matches object-position default).
  const [crops, setCrops] = useState({});
  const [generating, setGenerating] = useState(false);
  // Active marks. (x, y) here is just the seed position — once you drag a
  // mark inside a specific format, that format gets its own override.
  // scale stays shared across formats for now (one slider per mark).
  const [stagedMarks, setStagedMarks] = useState([]); // [{ id, mid, x, y, scale }]
  // Per-format mark positions: { `${chId}__${formatId}__${markId}`: { x, y } }
  const [markPositions, setMarkPositions] = useState({});
  const [savedAt, setSavedAt] = useState(null);

  const mediaForCh = (chId) => media[chId] || media._default || null;
  const cropKey = (chId, fid) => `${chId}__${fid}`;
  const getCrop = (chId, fid) => crops[cropKey(chId, fid)] || { x: 50, y: 50 };
  const setCrop = (chId, fid, next) => setCrops(c => ({ ...c, [cropKey(chId, fid)]: next }));
  const resetCrop = (chId, fid) => setCrops(c => { const n = { ...c }; delete n[cropKey(chId, fid)]; return n; });

  const toggleTarget = (chId) => {
    setTargets(t => t.includes(chId) ? t.filter(x => x !== chId) : [...t, chId]);
  };
  const isMarkStaged = (mid) => stagedMarks.some(m => m.mid === mid);
  const toggleMark = (mid) => {
    if (stagedMarks.some(m => m.mid === mid)) {
      setStagedMarks(list => list.filter(m => m.mid !== mid));
      // Drop any per-format overrides pointing at this mark.
      setMarkPositions(p => {
        const next = { ...p };
        for (const k of Object.keys(next)) if (k.endsWith("__" + mid)) delete next[k];
        return next;
      });
    } else {
      setStagedMarks(list => [...list, { id: mid, mid, x: 12, y: 88, scale: 1 }]);
    }
  };
  const scaleMark = (id, scale) => setStagedMarks(list => list.map(m => m.id === id ? { ...m, scale } : m));
  const markKey   = (chId, fid, mid) => `${chId}__${fid}__${mid}`;
  const markPosFor = (chId, fid, m) => markPositions[markKey(chId, fid, m.id)] ?? { x: m.x, y: m.y };
  const setMarkPos = (chId, fid, mid, pos) => setMarkPositions(p => ({ ...p, [markKey(chId, fid, mid)]: pos }));
  // Has this mark been moved away from the seed in any format?
  const markHasOverrides = (mid) => Object.keys(markPositions).some(k => k.endsWith("__" + mid));
  // Wipe all per-format overrides for a mark so every format reverts to the seed.
  const resetMarkPositions = (mid) => setMarkPositions(p => {
    const next = { ...p };
    for (const k of Object.keys(next)) if (k.endsWith("__" + mid)) delete next[k];
    return next;
  });

  const generate = () => {
    setGenerating(true);
    setTimeout(() => {
      const next = {};
      for (const ch of targets) next[ch] = generateCopyMock(brief, ch, tone);
      setVariantsByCh(next);
      // Auto-pick first variant per channel.
      const auto = {};
      for (const ch of targets) auto[ch] = next[ch][0];
      setPicked(auto);
      setGenerating(false);
      setSavedAt(null);
    }, 700);
  };

  // Upload media for a target — channelId = "_default" or a specific channel.
  // Detects image vs video and revokes the previous Blob URL.
  const uploadMedia = (target, file) => {
    if (!file) return;
    const url = URL.createObjectURL(file);
    const kind = file.type.startsWith("video/") ? "video" : "image";
    setMedia(prev => {
      const old = prev[target];
      if (old?.url?.startsWith("blob:")) URL.revokeObjectURL(old.url);
      return { ...prev, [target]: { url, kind, name: file.name } };
    });
  };
  // Pick from the imagery library — the URL is served from disk so we
  // don't need to revoke anything; just swap the entry.
  const pickMediaFromLib = (target, photo) => {
    setMedia(prev => {
      const old = prev[target];
      if (old?.url?.startsWith("blob:")) URL.revokeObjectURL(old.url);
      return { ...prev, [target]: { url: photo.url, kind: "image", name: photo.name } };
    });
  };
  const clearMedia = (target) => {
    setMedia(prev => {
      const old = prev[target];
      if (old?.url?.startsWith("blob:")) URL.revokeObjectURL(old.url);
      const n = { ...prev }; delete n[target]; return n;
    });
  };

  // Group formats by channel — one row per channel.
  const formatsByChannel = useMemo(() => {
    const out = [];
    for (const ch of targets) {
      const p = enabledPlatforms.find(p => p.channelId === ch);
      if (!p) continue;
      out.push({ platform: p, formats: p.formats.filter(f => !f.disabled) });
    }
    return out;
  }, [enabledPlatforms, targets]);

  const canPublish = targets.length > 0
    && targets.every(ch => picked[ch] && picked[ch].trim());

  const pushToPlan = async () => {
    if (!canPublish) return;
    const today = new Date();
    const iso = `${today.getFullYear()}-${String(today.getMonth()+1).padStart(2,"0")}-${String(today.getDate()).padStart(2,"0")}`;
    const copy = {};
    for (const ch of targets) copy[ch] = picked[ch];
    const payload = {
      date: iso,
      time: "09:00",
      topic: brief.trim().slice(0, 60) || "Nytt utkast från Studio",
      type: targets.includes("newsletter") ? "Nyhetsbrev" : "Feed",
      sponsoring: "Organiskt",
      channels: targets,
      owners: [u.user.id],
      status: "draft",
      copy,
      sokvag: "/Studio/utkast/draft",
      notering: brief.trim(),
      flowId: u.flows.find(f => f.isDefault)?.id || u.flows[0]?.id,
      previewImage: (media[targets[0]]?.kind === "image" ? media[targets[0]]?.url : null)
                  ?? (media._default?.kind === "image" ? media._default?.url : null)
                  ?? null,
    };
    try {
      const created = await u.createPost(payload);
      setSavedAt(Date.now());
      navigate("/content/post/" + created.id);
    } catch (e) {
      console.error(e);
      alert("Kunde inte spara — försök igen.");
    }
  };

  return (
    <div className="page">
      <header className="page-head">
        <div>
          <div className="eyebrow">Studio</div>
          <h1>Skapa nytt <em>material.</em></h1>
          <p className="sub">Skriv en kort idé — Studio föreslår copy per plattform, anpassar bilden till alla format och tar med dina varumärkesmärken. Klart? Lägg det i Content Plan.</p>
        </div>
      </header>

      {/* ── 1. Brief ── */}
      <section className="studio-step">
        <div className="studio-step-h">
          <span className="step-i">1</span>
          <h3>Brief</h3>
          <span className="meta">Berätta vad inlägget ska handla om</span>
        </div>
        <div className="studio-brief">
          <textarea className="text-input brief-area"
            placeholder="T.ex. 'Vårens nya kollektion — fokus på material och långa nordiska kvällar. Undvik hype.'"
            value={brief} onChange={e => setBrief(e.target.value)} />
          <div className="brief-side">
            <section className="field">
              <label>Plattformar</label>
              <div className="brief-pills">
                {enabledPlatforms.map(p => (
                  <button key={p.id} type="button"
                    className={"pill ch-" + p.channelId + (targets.includes(p.channelId) ? " is-on" : "")}
                    onClick={() => toggleTarget(p.channelId)}>
                    <span className="ch-dot" />{p.label}
                  </button>
                ))}
              </div>
            </section>
            <section className="field">
              <label>Ton</label>
              <select className="text-input" value={tone} onChange={e => setTone(e.target.value)}>
                <option value="calm">Calm, never quiet</option>
                <option value="warm">Warm, but exact</option>
                <option value="plain">Plainly confident</option>
              </select>
            </section>
            <button className="gen-button" onClick={generate} disabled={generating || targets.length === 0 || !brief.trim()}>
              <IconSpark size={14} stroke="#fff" />
              {generating ? "Genererar…" : "Generera förslag"}
            </button>
            <span className="field-help">Mock-svar i denna preview — kopplas till Claude/OpenAI när API-nycklar finns på plats.</span>
          </div>
        </div>
      </section>

      {/* ── 2. Copy variants per channel ── */}
      {Object.keys(variantsByCh).length > 0 && (
        <section className="studio-step">
          <div className="studio-step-h">
            <span className="step-i">2</span>
            <h3>Text</h3>
            <span className="meta">Välj eller redigera per plattform</span>
          </div>
          <div className="copy-by-channel">
            {targets.map(ch => {
              const c = channelById(ch); const list = variantsByCh[ch] || [];
              return (
                <div className="cbc" key={ch}>
                  <div className="cbc-h">
                    <ChannelChip id={ch} />
                    <span className="cbc-name">{c?.label}</span>
                    <span className="cbc-count">{(picked[ch] || "").length} tecken</span>
                    <button type="button" className="tb-btn" onClick={generate}>
                      <IconSpark size={12} /> Regenerera
                    </button>
                  </div>
                  <textarea className="text-input copy-area"
                    value={picked[ch] || ""}
                    onChange={e => setPicked(p => ({ ...p, [ch]: e.target.value }))} />
                  <div className="cbc-variants">
                    {list.map((v, i) => (
                      <button key={i} type="button"
                        className={"variant-pill " + (picked[ch] === v ? "is-on" : "")}
                        onClick={() => setPicked(p => ({ ...p, [ch]: v }))}>
                        Variant {i + 1}
                      </button>
                    ))}
                  </div>
                </div>
              );
            })}
          </div>
        </section>
      )}

      {/* ── 3. Media ── */}
      <section className="studio-step">
        <div className="studio-step-h">
          <span className="step-i">3</span>
          <h3>Bild &amp; rörligt</h3>
          <span className="meta">Standardfil för alla kanaler eller byt per kanal · dra i förhandsvisningen för att justera crop</span>
        </div>
        <div className="studio-media">
          <MediaUploader
            label="Standardmaterial"
            help="Används som grund för alla kanaler. Kan vara bild eller video."
            value={media._default}
            onUpload={(file) => uploadMedia("_default", file)}
            onPick={(photo) => pickMediaFromLib("_default", photo)}
            onClear={() => clearMedia("_default")}
          />

          {targets.length === 0 && (
            <div className="copy-empty">Välj minst en plattform i steg 1 för att se förhandsvisningar.</div>
          )}

          {formatsByChannel.map(({ platform, formats }) => {
            const chMedia = mediaForCh(platform.channelId);
            const hasOverride = !!media[platform.channelId];
            return (
              <div className="channel-row" key={platform.id}>
                <div className="channel-row-h">
                  <ChannelChip id={platform.channelId} />
                  <span className="channel-row-name">{platform.label}</span>
                  <span className="channel-row-count">{formats.length} format</span>
                  <MediaUploader
                    compact
                    label={hasOverride ? "Egen fil" : "Använder standard"}
                    value={hasOverride ? media[platform.channelId] : null}
                    onUpload={(file) => uploadMedia(platform.channelId, file)}
                    onPick={(photo) => pickMediaFromLib(platform.channelId, photo)}
                    onClear={hasOverride ? () => clearMedia(platform.channelId) : null}
                  />
                </div>
                <div className="channel-row-cards">
                  {formats.map(format => (
                    <FormatPreview
                      key={format.id}
                      channelId={platform.channelId}
                      format={format}
                      media={chMedia}
                      crop={getCrop(platform.channelId, format.id)}
                      onCropChange={(next) => setCrop(platform.channelId, format.id, next)}
                      onReset={() => resetCrop(platform.channelId, format.id)}
                      marks={stagedMarks}
                      posFor={(m) => markPosFor(platform.channelId, format.id, m)}
                      onMarkMove={(mid, pos) => setMarkPos(platform.channelId, format.id, mid, pos)}
                      brand={u.brand}
                      orgName={u.orgName}
                      platform={platform}
                      copyText={picked[platform.channelId]}
                    />
                  ))}
                </div>
              </div>
            );
          })}
        </div>
      </section>

      {/* ── 4. Brand marks ── */}
      <section className="studio-step">
        <div className="studio-step-h">
          <span className="step-i">4</span>
          <h3>Märken</h3>
          <span className="meta">Klicka för att lägga på · dra i förhandsvisningen för att flytta · skala nedan</span>
        </div>

        <StudioTemplates
          stagedMarks={stagedMarks}
          markPositions={markPositions}
          crops={crops}
          onApply={(tpl) => {
            setStagedMarks(tpl.marks || []);
            setMarkPositions(tpl.markPositions || {});
            // Apply crops only if it makes sense — keep existing if the
            // template was saved without them.
            if (tpl.crops && Object.keys(tpl.crops).length) setCrops(tpl.crops);
          }}
        />

        <div className="brand-mark-grid">
          {BRAND_MARKS.map(m => (
            <button key={m.id} type="button"
              className={"brand-mark-card " + (isMarkStaged(m.id) ? "is-on" : "")}
              onClick={() => toggleMark(m.id)}>
              <svg viewBox={`0 0 ${m.w} ${m.h}`} preserveAspectRatio="xMidYMid meet">
                {m.render({ fg: "var(--ink)" })}
              </svg>
              <span className="brand-mark-name">{m.label}</span>
              <span className="brand-mark-state">{isMarkStaged(m.id) ? "✓ med" : "+ lägg till"}</span>
            </button>
          ))}
        </div>

        {stagedMarks.length > 0 && (
          <div className="staged-marks">
            <div className="staged-marks-h">Aktiva märken</div>
            {stagedMarks.map(m => {
              const b = markById(m.mid);
              if (!b) return null;
              return (
                <div className="staged-mark" key={m.id}>
                  <div className="staged-mark-thumb">
                    <svg viewBox={`0 0 ${b.w} ${b.h}`} preserveAspectRatio="xMidYMid meet">
                      {b.render({ fg: "var(--ink)" })}
                    </svg>
                  </div>
                  <span className="staged-mark-name">{b.label}</span>
                  <label className="staged-mark-scale">
                    Storlek
                    <input type="range" min="0.4" max="3" step="0.05" value={m.scale}
                      onChange={(e) => scaleMark(m.id, Number(e.target.value))} />
                    <span className="scale-val">{m.scale.toFixed(2)}×</span>
                  </label>
                  <span className="staged-mark-pos">
                    {markHasOverrides(m.mid) ? "anpassad per format" : "samma i alla format"}
                  </span>
                  {markHasOverrides(m.mid) && (
                    <button type="button" className="tb-btn" onClick={() => resetMarkPositions(m.mid)}>Återställ positioner</button>
                  )}
                  <button type="button" className="tb-btn" onClick={() => toggleMark(m.mid)}>Ta bort</button>
                </div>
              );
            })}
          </div>
        )}
      </section>

      {/* ── 5. Publish ── */}
      <section className="studio-step studio-publish">
        <div className="studio-step-h">
          <span className="step-i">5</span>
          <h3>Klar?</h3>
          <span className="meta">Lägg utkastet i Content Plan — där kör attestflödet vidare</span>
        </div>
        <div className="publish-row">
          <div className="publish-summary">
            {targets.length === 0 && <span className="copy-empty inline">Välj plattformar för att kunna lägga upp.</span>}
            {targets.length > 0 && !canPublish && <span className="copy-empty inline">Generera och välj copy för alla plattformar.</span>}
            {canPublish && <span>Klar att läggas i Content Plan: {targets.length} kanal{targets.length === 1 ? "" : "er"}, {stagedMarks.length} märke{stagedMarks.length === 1 ? "" : "n"}, {Object.keys(media).length > 0 ? `${Object.keys(media).length} mediafil${Object.keys(media).length === 1 ? "" : "er"}` : "inget media"}.</span>}
          </div>
          <button type="button" className="tb-btn is-primary" disabled={!canPublish} onClick={pushToPlan}>
            Lägg i Content Plan →
          </button>
        </div>
        {savedAt && <div className="copy-empty">Sparat. Öppnar inlägget…</div>}
      </section>
    </div>
  );
}

/* ============================================================
   CONTENT PLAN
   ============================================================ */
/* ============================================================
   CONTENT PLAN — week view default + post detail drawer
   ============================================================ */
const WEEKDAYS = ["Mån","Tis","Ons","Tor","Fre","Lör","Sön"];
const MONTHS_SV = ["Januari","Februari","Mars","April","Maj","Juni","Juli","Augusti","September","Oktober","November","December"];

const toISO = (d) =>
  `${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,"0")}-${String(d.getDate()).padStart(2,"0")}`;

const startOfWeek = (d) => {
  const x = new Date(d); x.setHours(0,0,0,0);
  const dow = (x.getDay() + 6) % 7; // Mon = 0
  x.setDate(x.getDate() - dow);
  return x;
};

const channelById = (id) => CHANNEL_OPTIONS.find(c => c.id === id);
const statusById  = (id) => STATUS_OPTIONS.find(s => s.id === id);
// People list is hydrated from the API at runtime; fall back to the seed
// constant during the very first render. window.__people is the live cache
// kept in sync by App.jsx.
const personById = (id) => (window.__people || TEAM).find(t => t.id === id);
const peopleList = () => (window.__people || TEAM);

function ChannelChip({ id }) {
  const c = channelById(id);
  if (!c) return null;
  return <span className={"ch-chip ch-" + id} title={c.label}>{c.short}</span>;
}

function StatusPill({ id }) {
  const s = statusById(id) || { label: id, tone: "muted" };
  return <span className={"st-pill st-" + s.tone}><span className="d" />{s.label}</span>;
}

/* Filler photo for each card. If the post was created in Studio with an
   uploaded image, use that — otherwise fall back to a deterministic
   placeholder from Lorem Picsum (Unsplash, free to use). */
function PostThumb({ post }) {
  const seed = encodeURIComponent(post.id || "x");
  const src = post.previewImage || `https://picsum.photos/seed/${seed}/320/176`;
  return (
    <div className="post-thumb" aria-hidden="true">
      <img
        src={src}
        srcSet={post.previewImage ? undefined : `https://picsum.photos/seed/${seed}/320/176 1x, https://picsum.photos/seed/${seed}/640/352 2x`}
        alt=""
        loading="lazy"
        decoding="async"
      />
    </div>
  );
}

function OwnerAvatars({ ids, max = 3 }) {
  const shown = ids.slice(0, max);
  const rest  = ids.length - shown.length;
  return (
    <span className="owner-row">
      {shown.map((id) => {
        const p = personById(id);
        if (!p) return null;
        return <span className="owner-avatar" key={id} title={p.name}>{p.initials}</span>;
      })}
      {rest > 0 && <span className="owner-avatar is-more">+{rest}</span>}
    </span>
  );
}

/* ────────── Dropdowns ────────── */
function useClickOutside(open, ref, onClose) {
  useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) onClose(); };
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("mousedown", onDoc);
    document.addEventListener("keydown", onKey);
    return () => { document.removeEventListener("mousedown", onDoc); document.removeEventListener("keydown", onKey); };
  }, [open]);
}

function StatusSelect({ value, onChange }) {
  const [open, setOpen] = useState(false);
  const ref = React.useRef(null);
  useClickOutside(open, ref, () => setOpen(false));
  const s = statusById(value);
  return (
    <div className="dd" ref={ref}>
      <button type="button" className="dd-trigger" onClick={() => setOpen(o => !o)}>
        <StatusPill id={value} />
        <svg className="chev" width="10" height="10" viewBox="0 0 10 10"><path d="M2 3.5 L5 6.5 L8 3.5" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" /></svg>
      </button>
      {open && (
        <div className="dd-menu">
          {STATUS_OPTIONS.map(opt => (
            <button key={opt.id} type="button"
              className={"dd-item " + (opt.id === value ? "is-active" : "")}
              onClick={() => { onChange(opt.id); setOpen(false); }}>
              <StatusPill id={opt.id} />
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

function ChannelSelect({ value, onChange }) {
  const [open, setOpen] = useState(false);
  const ref = React.useRef(null);
  useClickOutside(open, ref, () => setOpen(false));
  const toggle = (id) =>
    onChange(value.includes(id) ? value.filter(x => x !== id) : [...value, id]);
  return (
    <div className="dd" ref={ref}>
      <button type="button" className="dd-trigger is-multi" onClick={() => setOpen(o => !o)}>
        <span className="dd-chips">
          {value.length === 0 && <span className="dd-placeholder">Välj kanaler…</span>}
          {value.map(id => <ChannelChip id={id} key={id} />)}
        </span>
        <svg className="chev" width="10" height="10" viewBox="0 0 10 10"><path d="M2 3.5 L5 6.5 L8 3.5" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" /></svg>
      </button>
      {open && (
        <div className="dd-menu">
          {CHANNEL_OPTIONS.map(opt => (
            <button key={opt.id} type="button"
              className={"dd-item " + (value.includes(opt.id) ? "is-active" : "")}
              onClick={() => toggle(opt.id)}>
              <span className="dd-check">{value.includes(opt.id) ? "✓" : ""}</span>
              <ChannelChip id={opt.id} />
              <span className="dd-label">{opt.label}</span>
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

function PlainSelect({ value, options, onChange, placeholder }) {
  const [open, setOpen] = useState(false);
  const ref = React.useRef(null);
  useClickOutside(open, ref, () => setOpen(false));
  return (
    <div className="dd" ref={ref}>
      <button type="button" className="dd-trigger is-plain" onClick={() => setOpen(o => !o)}>
        <span>{value || <span className="dd-placeholder">{placeholder}</span>}</span>
        <svg className="chev" width="10" height="10" viewBox="0 0 10 10"><path d="M2 3.5 L5 6.5 L8 3.5" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" /></svg>
      </button>
      {open && (
        <div className="dd-menu">
          {options.map(opt => (
            <button key={opt} type="button"
              className={"dd-item is-plain " + (opt === value ? "is-active" : "")}
              onClick={() => { onChange(opt); setOpen(false); }}>
              {opt}
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

// Searchable single-select with keyboard navigation. Drop-in replacement for
// any place we'd otherwise force the user to type a value verbatim (timezones,
// long enum lists, etc.).
function Combobox({ value, options, onChange, placeholder, disabled }) {
  const [open, setOpen] = useState(false);
  const [q, setQ] = useState("");
  const [active, setActive] = useState(0);
  const ref = React.useRef(null);
  const searchRef = React.useRef(null);
  const listRef = React.useRef(null);
  useClickOutside(open, ref, () => { setOpen(false); setQ(""); });

  // Normalise options to {value, label}. Accept plain strings too.
  const opts = options.map(o => typeof o === "string" ? { value: o, label: o } : o);
  const norm = (s) => (s || "").toLowerCase();
  const filtered = q
    ? opts.filter(o => norm(o.label).includes(norm(q)) || norm(o.value).includes(norm(q)))
    : opts;

  useEffect(() => { setActive(0); }, [q, open]);
  useEffect(() => {
    if (open) requestAnimationFrame(() => searchRef.current?.focus());
  }, [open]);
  useEffect(() => {
    if (!open) return;
    const row = listRef.current?.children?.[active];
    row?.scrollIntoView({ block: "nearest" });
  }, [active, open]);

  const pick = (opt) => { onChange(opt.value); setOpen(false); setQ(""); };
  const onKey = (e) => {
    if (e.key === "ArrowDown") { e.preventDefault(); setActive(i => Math.min(filtered.length - 1, i + 1)); }
    else if (e.key === "ArrowUp")   { e.preventDefault(); setActive(i => Math.max(0, i - 1)); }
    else if (e.key === "Enter")     { e.preventDefault(); const o = filtered[active]; if (o) pick(o); }
    else if (e.key === "Tab")       { setOpen(false); }
  };

  const selectedLabel = opts.find(o => o.value === value)?.label || value;
  return (
    <div className="dd" ref={ref}>
      <button type="button" className="dd-trigger is-plain" disabled={disabled}
              onClick={() => !disabled && setOpen(o => !o)}>
        <span>{selectedLabel || <span className="dd-placeholder">{placeholder}</span>}</span>
        <svg className="chev" width="10" height="10" viewBox="0 0 10 10"><path d="M2 3.5 L5 6.5 L8 3.5" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" /></svg>
      </button>
      {open && (
        <div className="dd-menu combobox-menu">
          <input ref={searchRef} className="people-search" placeholder="Sök…"
                 value={q} onChange={(e) => setQ(e.target.value)} onKeyDown={onKey} />
          <div className="combobox-list" ref={listRef}>
            {filtered.length === 0 && <div className="people-empty">Inga träffar</div>}
            {filtered.map((opt, i) => (
              <button key={opt.value} type="button"
                onMouseEnter={() => setActive(i)}
                onClick={() => pick(opt)}
                className={"dd-item is-plain " +
                  (opt.value === value ? "is-active " : "") +
                  (i === active ? "is-highlighted" : "")}>
                {opt.label}
              </button>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

// Cached list of IANA timezones. Falls back to a sensible default if the
// Intl helper is missing (older browsers).
const TIMEZONES = (() => {
  try {
    if (typeof Intl.supportedValuesOf === "function") {
      return Intl.supportedValuesOf("timeZone");
    }
  } catch {}
  return ["Europe/Stockholm", "Europe/Oslo", "Europe/Copenhagen", "Europe/Helsinki",
          "Europe/London", "Europe/Berlin", "Europe/Paris", "UTC",
          "America/New_York", "America/Los_Angeles", "Asia/Tokyo"];
})();

function OwnerPicker({ value, onChange }) {
  const [open, setOpen] = useState(false);
  const [q, setQ] = useState("");
  const ref = React.useRef(null);
  useClickOutside(open, ref, () => { setOpen(false); setQ(""); });
  const remove = (id) => onChange(value.filter(x => x !== id));
  const add    = (id) => { onChange([...value, id]); setQ(""); };
  const matches = peopleList().filter(t =>
    !value.includes(t.id) && (t.name.toLowerCase().includes(q.toLowerCase()) || (t.initials || "").toLowerCase().includes(q.toLowerCase()))
  );
  return (
    <div className="dd dd-people" ref={ref}>
      <div className="people-row">
        {value.map(id => {
          const p = personById(id); if (!p) return null;
          return (
            <span className="person-chip" key={id}>
              <span className="person-avatar">{p.initials}</span>
              <span className="person-name">{p.name}</span>
              <button type="button" className="person-x" onClick={() => remove(id)} aria-label="Remove">×</button>
            </span>
          );
        })}
        <button type="button" className="person-add" onClick={() => setOpen(true)}>+ Lägg till</button>
      </div>
      {open && (
        <div className="dd-menu people-menu">
          <input className="people-search" autoFocus
            placeholder="Sök person…"
            value={q} onChange={(e) => setQ(e.target.value)} />
          <div className="people-list">
            {matches.length === 0 && <div className="people-empty">Ingen träff</div>}
            {matches.map(p => (
              <button key={p.id} type="button" className="dd-item" onClick={() => add(p.id)}>
                <span className="person-avatar">{p.initials}</span>
                <span className="dd-label">{p.name}</span>
              </button>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

/* ────────── Approval pipeline ──────────
   Shared between ContentPlan (drawer) and any future surface
   that handles approvals (Studio, brief workflows, etc.). */
function approvalSummary(post) {
  const flow = flowForPost(post);
  if (!flow) return { done: 0, total: 0 };
  return {
    done:  flow.stages.filter(s => post.approvals?.[s.id]).length,
    total: flow.stages.length,
  };
}

function ApprovalPipeline({ post, onChange }) {
  const u = useUser();
  if (!u) return null;
  const flow = u.flows.find(f => f.id === post.flowId) || u.flows.find(f => f.isDefault) || u.flows[0];
  if (!flow || !flow.stages.length) {
    return (
      <section className="field">
        <label>Attestflöde</label>
        <div className="copy-empty">Detta flöde saknar steg. Lägg till steg i Settings · Publiceringsflöden.</div>
      </section>
    );
  }
  const approveStage = (s) => {
    const approvals = { ...(post.approvals || {}), [s.id]: { by: u.user.id, at: new Date().toISOString() } };
    onChange(post.id, { approvals });
  };
  const revokeFrom = (s) => {
    // Revoke this stage and every later stage so the flow stays consistent.
    const approvals = { ...(post.approvals || {}) };
    let cleared = false;
    for (const st of flow.stages) {
      if (st.id === s.id) cleared = true;
      if (cleared) delete approvals[st.id];
    }
    onChange(post.id, { approvals });
  };
  return (
    <section className="field">
      <div className="pipeline-label">
        <label>Attestflöde · {flow.name}</label>
        <span className="pl-progress">{flow.stages.filter(s => post.approvals?.[s.id]).length}/{flow.stages.length} klar</span>
      </div>
      <div className="pipeline">
        {flow.stages.map((s, i) => {
          const state = stageStatus(post, s.id);
          const approval = post.approvals?.[s.id];
          const signer = approval ? u.people.find(p => p.id === approval.by) : null;
          const canApprove = state === "current" && u.can(s.requirePermission);
          const canRevoke  = !!approval && (u.can(s.requirePermission) || approval.by === u.user.id);
          return (
            <div key={s.id} className={"pl-stage is-" + state}>
              <div className="pl-rail">
                <span className="pl-dot" aria-hidden="true">
                  {state === "done"    && <svg viewBox="0 0 12 12" width="12" height="12"><path d="M2.5 6.5 5 9 9.5 3.5" fill="none" stroke="#fff" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round"/></svg>}
                  {state === "current" && <span className="pl-pulse" />}
                </span>
                {i < flow.stages.length - 1 && <span className="pl-line" />}
              </div>
              <div className="pl-body">
                <div className="pl-label">{s.label}</div>
                <div className="pl-meta">
                  {approval && signer && <>Godkänd av <b>{signer.name}</b></>}
                  {!approval && state === "current"  && "Väntar på godkännande"}
                  {!approval && state === "upcoming" && "Kommer"}
                </div>
                <div className="pl-action">
                  {canApprove && (
                    <button type="button" className="tb-btn is-primary" onClick={() => approveStage(s)}>
                      Godkänn
                    </button>
                  )}
                  {!canApprove && state === "current" && (
                    <span className="pl-need">Kräver: {PERMISSIONS.find(p => p.id === s.requirePermission)?.label || s.requirePermission}</span>
                  )}
                  {canRevoke && (
                    <button type="button" className="tb-btn" onClick={() => revokeFrom(s)}>
                      Ångra
                    </button>
                  )}
                </div>
              </div>
            </div>
          );
        })}
      </div>
    </section>
  );
}

function ApprovalIndicator({ post }) {
  const { done, total } = approvalSummary(post);
  if (!total) return null;
  return (
    <span className="approval-indicator" title={`${done} av ${total} steg godkända`}>
      {[...Array(total)].map((_, i) => (
        <span key={i} className={"ai-pip " + (i < done ? "is-done" : "")} />
      ))}
      <span className="ai-text">{done}/{total}</span>
    </span>
  );
}

/* ────────── Move-confirm dialog ────────── */
const fmtDateSv = (iso) => {
  const d = new Date(iso + "T00:00");
  return `${WEEKDAYS[(d.getDay()+6)%7]} ${d.getDate()} ${MONTHS_SV[d.getMonth()].toLowerCase()} ${d.getFullYear()}`;
};

function MoveConfirm({ move, post, onConfirm, onCancel }) {
  useEffect(() => {
    const onKey = (e) => {
      if (e.key === "Escape") onCancel();
      if (e.key === "Enter")  onConfirm();
    };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [onCancel, onConfirm]);

  if (!move || !post) return null;
  return (
    <>
      <div className="modal-backdrop" onClick={onCancel} />
      <div className="modal" role="dialog" aria-modal="true" aria-labelledby="move-title">
        <h3 className="modal-title" id="move-title">Flytta inlägg?</h3>
        <p className="modal-body">
          Vill du flytta <strong>{post.topic}</strong> från
          {" "}<span className="modal-date">{fmtDateSv(move.fromIso)}</span> till
          {" "}<span className="modal-date">{fmtDateSv(move.toIso)}</span>?
        </p>
        <div className="modal-actions">
          <button type="button" className="tb-btn" onClick={onCancel}>Avbryt</button>
          <button type="button" className="tb-btn is-primary" onClick={onConfirm} autoFocus>Flytta</button>
        </div>
      </div>
    </>
  );
}

/* ────────── Drawer ────────── */
function PostDrawer({ post, onClose, onChange }) {
  const u = useUser();
  const open = !!post;
  // Lock body scroll while open
  useEffect(() => {
    if (!open) return;
    const prev = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => { document.body.style.overflow = prev; };
  }, [open]);
  if (!post) return null;
  const flowsList = u?.flows || APPROVAL_FLOWS;
  const date = new Date(post.date + "T00:00");
  const dateLabel = `${WEEKDAYS[(date.getDay()+6)%7]} ${date.getDate()} ${MONTHS_SV[date.getMonth()].toLowerCase()} ${date.getFullYear()}`;
  const set = (patch) => onChange(post.id, patch);
  const setCopy = (k, v) => onChange(post.id, { copy: { ...post.copy, [k]: v } });

  return (
    <>
      <div className="drawer-backdrop" onClick={onClose} />
      <aside className="drawer" role="dialog" aria-label="Inläggsdetaljer">
        <header className="drawer-head">
          <div>
            <div className="drawer-eyebrow">Inlägg · {post.id}</div>
            <h2 className="drawer-title">{post.topic || "Nytt inlägg"}</h2>
            <div className="drawer-when">{dateLabel} · kl {post.time}</div>
          </div>
          <button type="button" className="drawer-close" onClick={onClose} aria-label="Stäng">×</button>
        </header>

        <div className="drawer-body">
          <section className="field">
            <label>Ämne</label>
            <input className="text-input" value={post.topic}
              placeholder="T.ex. Vårens kollektion — linne, ek, långa kvällar"
              onChange={(e) => set({ topic: e.target.value })} />
          </section>

          <ApprovalPipeline post={post} onChange={onChange} />

          <div className="field-row">
            <section className="field">
              <label>Status</label>
              <StatusSelect value={post.status} onChange={(v) => set({ status: v })} />
            </section>
            <section className="field">
              <label>Publiceringsflöde</label>
              <PlainSelect
                value={(flowsList.find(f => f.id === post.flowId) || {}).name}
                options={flowsList.map(f => f.name)}
                placeholder="Välj flöde…"
                onChange={(name) => {
                  const f = flowsList.find(ff => ff.name === name);
                  if (f) set({ flowId: f.id, approvals: {} });
                }}
              />
            </section>
          </div>

          <div className="field-row">
            <section className="field">
              <label>Datum</label>
              <input type="date" className="text-input" value={post.date}
                onChange={(e) => set({ date: e.target.value })} />
            </section>
            <section className="field">
              <label>Tid</label>
              <input type="time" className="text-input" value={post.time}
                onChange={(e) => set({ time: e.target.value })} />
            </section>
          </div>

          <section className="field">
            <label>Kanaler</label>
            <ChannelSelect value={post.channels} onChange={(v) => set({ channels: v })} />
          </section>

          <div className="field-row">
            <section className="field">
              <label>Inläggstyp</label>
              <PlainSelect value={post.type} options={POSTTYPE_OPTIONS}
                placeholder="Välj typ…" onChange={(v) => set({ type: v })} />
            </section>
            <section className="field">
              <label>Sponsring</label>
              <PlainSelect value={post.sponsoring} options={SPONSORING_OPTIONS}
                placeholder="Välj…" onChange={(v) => set({ sponsoring: v })} />
            </section>
          </div>

          <section className="field">
            <label>Ansvarig</label>
            <OwnerPicker value={post.owners} onChange={(v) => set({ owners: v })} />
          </section>

          <section className="field">
            <label>Inläggstext</label>
            <div className="copy-tabs">
              {post.channels.length === 0 && <div className="copy-empty">Välj minst en kanal för att skriva inläggstext.</div>}
              {post.channels.map(cid => {
                const c = channelById(cid); if (!c) return null;
                return (
                  <div className="copy-block" key={cid}>
                    <div className="copy-head">
                      <ChannelChip id={cid} />
                      <span className="copy-name">{c.label}</span>
                      <span className="copy-count">{(post.copy?.[cid] || "").length} tecken</span>
                    </div>
                    <textarea className="text-input copy-area"
                      value={post.copy?.[cid] || ""}
                      onChange={(e) => setCopy(cid, e.target.value)}
                      placeholder={`Skriv copy för ${c.label}…`} />
                  </div>
                );
              })}
            </div>
          </section>

          <div className="field-row">
            <section className="field">
              <label>Länkrubrik</label>
              <input className="text-input" value={post.linkRubrik}
                onChange={(e) => set({ linkRubrik: e.target.value })} placeholder="Rubrik på länken…" />
            </section>
            <section className="field">
              <label>URL</label>
              <input className="text-input" value={post.url}
                onChange={(e) => set({ url: e.target.value })} placeholder="https://…" />
            </section>
          </div>

          <section className="field">
            <label>Sökväg</label>
            <input className="text-input" value={post.sokvag}
              onChange={(e) => set({ sokvag: e.target.value })} placeholder="/Internt/Jobb/…" />
          </section>

          <section className="field">
            <label>Notering</label>
            <textarea className="text-input" rows={3} value={post.notering}
              onChange={(e) => set({ notering: e.target.value })} placeholder="Interna noteringar…" />
          </section>

          <section className="field">
            <label>Feedback</label>
            <textarea className="text-input" rows={3} value={post.feedback}
              onChange={(e) => set({ feedback: e.target.value })} placeholder="Feedback från korrektur…" />
          </section>
        </div>
      </aside>
    </>
  );
}

/* ────────── Post card ────────── */
function PostCard({ post, onClick, onDragStart, onDragEnd, isDragging }) {
  return (
    <div
      role="button" tabIndex={0}
      className={"post-card st-" + (statusById(post.status)?.tone || "muted") + (isDragging ? " is-dragging" : "")}
      onClick={onClick}
      onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); onClick(); } }}
      draggable
      onDragStart={onDragStart}
      onDragEnd={onDragEnd}
    >
      <PostThumb post={post} />
      <div className="post-card-body">
        <div className="post-card-top">
          <span className="post-time">{post.time}</span>
          <StatusPill id={post.status} />
        </div>
        <div className="post-topic">{post.topic}</div>
        <div className="post-card-bot">
          <span className="post-channels">
            {post.channels.map(id => <ChannelChip id={id} key={id} />)}
          </span>
          <OwnerAvatars ids={post.owners} />
        </div>
        <ApprovalIndicator post={post} />
      </div>
    </div>
  );
}

function ContentPlanView() {
  const route = useRoute();
  // Route segments: /content, /content/week, /content/month, /content/list, /content/post/:id
  const seg1 = route.params[0];
  const seg2 = route.params[1];
  const view = (seg1 === "month" || seg1 === "list" || seg1 === "week") ? seg1 : "week";
  const setView = (v) => navigate("/content" + (v === "week" ? "" : "/" + v));
  const selectedId = seg1 === "post" ? seg2 : null;
  const openPost  = (id) => navigate("/content/post/" + id);
  const closePost = () => navigate("/content" + (view === "week" ? "" : "/" + view));
  // ?new=1 → show the "manual vs Studio" picker. The topbar's Schedule-post
  // button sets this flag; closing the picker clears it.
  const showNewPicker = route.search.get("new") === "1";
  const closeNewPicker = () => navigate("/content" + (view === "week" ? "" : "/" + view));

  const [anchor, setAnchor] = useState(() => new Date());
  const u = useUser();
  const posts = u.posts;
  const setPosts = u.setPosts;
  const updatePost = (id, patch) => u.updatePost(id, patch);
  // Only show channels whose platform is enabled in System · Plattformar.
  const activeChannels = useMemo(
    () => (u?.platforms || PLATFORMS).filter(p => p.enabled).map(p => p.channelId),
    [u?.platforms]
  );
  const channelOptions = useMemo(
    () => CHANNEL_OPTIONS.filter(c => activeChannels.includes(c.id)),
    [activeChannels]
  );
  // channel filter: id -> bool. Default all enabled platforms visible.
  const [chFilter, setChFilter] = useState(
    () => Object.fromEntries(CHANNEL_OPTIONS.map(c => [c.id, true]))
  );
  // Re-sync filter when platform list changes (turn off filter for disabled platforms).
  useEffect(() => {
    setChFilter(f => {
      const next = { ...f };
      for (const c of CHANNEL_OPTIONS) {
        if (!activeChannels.includes(c.id)) next[c.id] = false;
      }
      return next;
    });
  }, [activeChannels.join(",")]);
  const toggleCh = (id) => setChFilter(f => ({ ...f, [id]: !f[id] }));
  // updatePost is wired to the API via UserCtx (above).

  // Drag-and-drop reschedule between days. Drop opens a confirmation
  // dialog before mutating the post so accidental drags can be undone.
  const [dragId, setDragId] = useState(null);
  const [dragOverIso, setDragOverIso] = useState(null);
  const [pendingMove, setPendingMove] = useState(null); // { id, fromIso, toIso }
  const onDragStart = (postId) => (e) => {
    e.dataTransfer.effectAllowed = "move";
    e.dataTransfer.setData("text/plain", postId);
    setDragId(postId);
  };
  const onDragEnd = () => { setDragId(null); setDragOverIso(null); };
  const onDragOver = (iso) => (e) => {
    e.preventDefault();
    e.dataTransfer.dropEffect = "move";
    if (dragOverIso !== iso) setDragOverIso(iso);
  };
  const onDragLeave = (iso) => () => {
    setDragOverIso(prev => prev === iso ? null : prev);
  };
  const onDrop = (iso) => (e) => {
    e.preventDefault();
    const id = e.dataTransfer.getData("text/plain") || dragId;
    if (id) {
      const p = posts.find(x => x.id === id);
      if (p && p.date !== iso) setPendingMove({ id, fromIso: p.date, toIso: iso });
    }
    setDragOverIso(null);
    setDragId(null);
  };
  const confirmMove = () => {
    if (pendingMove) updatePost(pendingMove.id, { date: pendingMove.toIso });
    setPendingMove(null);
  };
  const cancelMove = () => setPendingMove(null);

  const visible = useMemo(
    () => posts.filter(p => p.channels.some(c => chFilter[c])),
    [posts, chFilter]
  );
  const byDate = useMemo(() => {
    const m = {};
    for (const p of visible) (m[p.date] ??= []).push(p);
    for (const k in m) m[k].sort((a,b) => a.time.localeCompare(b.time));
    return m;
  }, [visible]);

  const wkStart = useMemo(() => startOfWeek(anchor), [anchor]);
  const weekDays = useMemo(() => {
    const d = [];
    for (let i = 0; i < 7; i++) {
      const dd = new Date(wkStart); dd.setDate(dd.getDate() + i);
      d.push(dd);
    }
    return d;
  }, [wkStart]);

  const monthCells = useMemo(() => {
    const first = new Date(anchor.getFullYear(), anchor.getMonth(), 1);
    const start = startOfWeek(first);
    return [...Array(42)].map((_, i) => {
      const d = new Date(start); d.setDate(d.getDate() + i);
      return d;
    }).filter((_, i, arr) => {
      const last = new Date(arr[arr.length - 1]);
      const lastDay = last.getDate();
      return i < 35 || lastDay <= 7; // 5 or 6 rows depending on month
    });
  }, [anchor]);

  const today = toISO(new Date());

  const shiftBy = (days) => {
    const d = new Date(anchor); d.setDate(d.getDate() + days); setAnchor(d);
  };
  const shiftMonth = (months) => {
    const d = new Date(anchor); d.setMonth(d.getMonth() + months); setAnchor(d);
  };

  // Header label
  const weekEnd = new Date(wkStart); weekEnd.setDate(weekEnd.getDate() + 6);
  const weekLabel = view === "month"
    ? `${MONTHS_SV[anchor.getMonth()]} ${anchor.getFullYear()}`
    : `${wkStart.getDate()} – ${weekEnd.getDate()} ${MONTHS_SV[weekEnd.getMonth()].toLowerCase()} ${weekEnd.getFullYear()}`;

  const selectedPost = posts.find(p => p.id === selectedId);

  return (
    <div className="page">
      <header className="page-head">
        <div>
          <h1>Content <em>plan.</em></h1>
        </div>
      </header>

      <div className="plan-toolbar">
        <div className="left">
          <button className="nav-btn" onClick={() => view === "month" ? shiftMonth(-1) : shiftBy(-7)} aria-label="Föregående"><IconArrowL size={12} /></button>
          <button className="nav-btn" onClick={() => view === "month" ? shiftMonth(1)  : shiftBy(7)}  aria-label="Nästa"><IconArrowR size={12} /></button>
          <button className="tb-btn" onClick={() => setAnchor(new Date())}>Idag</button>
          <div className="month">{weekLabel}</div>
        </div>
        <div className="filter-pills">
          {channelOptions.map(c => (
            <button key={c.id} className={"pill ch-" + c.id + (chFilter[c.id] ? " is-on" : "")} onClick={() => toggleCh(c.id)}>
              <span className="ch-dot" />{c.label}
            </button>
          ))}
        </div>
        <div className="view-switch">
          <button className={"tb-btn " + (view === "week"  ? "is-primary" : "")} onClick={() => setView("week")}>Vecka</button>
          <button className={"tb-btn " + (view === "month" ? "is-primary" : "")} onClick={() => setView("month")}>Månad</button>
          <button className={"tb-btn " + (view === "list"  ? "is-primary" : "")} onClick={() => setView("list")}>Lista</button>
        </div>
      </div>

      {view === "week" && (
        <div className="week">
          {weekDays.map((d, i) => {
            const iso = toISO(d);
            const items = byDate[iso] || [];
            const isOver = dragOverIso === iso;
            return (
              <div
                key={i}
                className={
                  "week-col " +
                  (iso === today ? "is-today " : "") +
                  (isOver ? "is-drop-target" : "")
                }
                onDragOver={onDragOver(iso)}
                onDragLeave={onDragLeave(iso)}
                onDrop={onDrop(iso)}
              >
                <div className="week-dh">
                  <span className="dh-day">{WEEKDAYS[i]}</span>
                  <span className="dh-num">{d.getDate()}</span>
                </div>
                <div className="week-cards">
                  {items.length === 0 && <div className="week-empty">—</div>}
                  {items.map(p => (
                    <PostCard
                      key={p.id}
                      post={p}
                      onClick={() => openPost(p.id)}
                      onDragStart={onDragStart(p.id)}
                      onDragEnd={onDragEnd}
                      isDragging={dragId === p.id}
                    />
                  ))}
                </div>
              </div>
            );
          })}
        </div>
      )}

      {view === "month" && (
        <div className="cal">
          {WEEKDAYS.map(d => (<div className="cal-dh" key={d}>{d}</div>))}
          {monthCells.map((d, i) => {
            const iso = toISO(d);
            const items = byDate[iso] || [];
            const isOver = dragOverIso === iso;
            return (
              <div
                key={i}
                className={
                  "cal-cell " +
                  (d.getMonth() !== anchor.getMonth() ? "is-otherm " : "") +
                  (iso === today ? "is-today " : "") +
                  (isOver ? "is-drop-target" : "")
                }
                onDragOver={onDragOver(iso)}
                onDragLeave={onDragLeave(iso)}
                onDrop={onDrop(iso)}
              >
                <span className="daynum">{d.getDate()}</span>
                {items.map(p => (
                  <div
                    key={p.id}
                    role="button" tabIndex={0}
                    className={"cal-event st-" + (statusById(p.status)?.tone || "muted") + (dragId === p.id ? " is-dragging" : "")}
                    onClick={() => openPost(p.id)}
                    onKeyDown={(e) => { if (e.key === "Enter" || e.key === " ") { e.preventDefault(); openPost(p.id); } }}
                    draggable
                    onDragStart={onDragStart(p.id)}
                    onDragEnd={onDragEnd}
                  >
                    {p.channels[0] && <ChannelChip id={p.channels[0]} />}
                    <span className="ev-t">{p.topic}</span>
                    <span className="ev-time">{p.time}</span>
                  </div>
                ))}
              </div>
            );
          })}
        </div>
      )}

      {view === "list" && (
        <div className="post-list">
          <div className="post-list-head">
            <span>Datum</span><span>Ämne</span><span>Kanal</span><span>Typ</span><span>Status</span><span>Ansvarig</span>
          </div>
          {[...visible].sort((a,b) => (a.date + a.time).localeCompare(b.date + b.time)).map(p => {
            const d = new Date(p.date + "T00:00");
            return (
              <button key={p.id} type="button" className="post-list-row" onClick={() => openPost(p.id)}>
                <span className="mono">{`${d.getDate()} ${MONTHS_SV[d.getMonth()].slice(0,3).toLowerCase()} · ${p.time}`}</span>
                <span className="row-topic">{p.topic}</span>
                <span className="row-channels">{p.channels.map(id => <ChannelChip id={id} key={id} />)}</span>
                <span className="mono">{p.type}</span>
                <span><StatusPill id={p.status} /></span>
                <span><OwnerAvatars ids={p.owners} /></span>
              </button>
            );
          })}
        </div>
      )}

      <PostDrawer post={selectedPost} onClose={closePost} onChange={updatePost} />
      <MoveConfirm
        move={pendingMove}
        post={pendingMove ? posts.find(p => p.id === pendingMove.id) : null}
        onConfirm={confirmMove}
        onCancel={cancelMove}
      />
      {showNewPicker && <NewPostPicker onClose={closeNewPicker} />}
    </div>
  );
}

/* ──────────────────────────────────────────────────────────────────────
   Schedule-post picker. Two paths:
     1. Manuellt — server creates an empty draft, we navigate straight
        into the existing PostDrawer so the user fills in the same fields
        as when editing.
     2. Studio   — jump to /studio for AI-assisted creation.
   ────────────────────────────────────────────────────────────────────── */
function NewPostPicker({ onClose }) {
  const u = useUser();
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState(null);

  // Same ESC / outside-click affordances as the other modals.
  useEffect(() => {
    const onKey = (e) => { if (e.key === "Escape") onClose(); };
    document.addEventListener("keydown", onKey);
    return () => document.removeEventListener("keydown", onKey);
  }, [onClose]);

  const createManual = async () => {
    if (busy) return;
    setBusy(true); setError(null);
    const today = new Date();
    const iso = `${today.getFullYear()}-${String(today.getMonth()+1).padStart(2,"0")}-${String(today.getDate()).padStart(2,"0")}`;
    const defaultFlow = (u.flows.find(f => f.isDefault) || u.flows[0])?.id || null;
    try {
      const created = await u.createPost({
        date: iso, time: "09:00",
        topic: "", type: "Feed", sponsoring: "Organiskt",
        channels: [], owners: [u.user.id],
        status: "draft", copy: {},
        flowId: defaultFlow,
      });
      navigate("/content/post/" + created.id);
    } catch (e) {
      setError(e.body?.error === "forbidden"
        ? "Din roll saknar rättigheten att skapa inlägg."
        : "Kunde inte skapa utkast — försök igen.");
      setBusy(false);
    }
  };

  const goStudio = () => {
    onClose();
    navigate("/studio");
  };

  return (
    <>
      <div className="modal-backdrop" onClick={onClose} />
      <div className="modal new-post-picker" role="dialog" aria-labelledby="new-post-title">
        <h3 className="modal-title" id="new-post-title">Schemalägg inlägg</h3>
        <p className="modal-body">Hur vill du börja?</p>
        <div className="new-post-options">
          <button type="button" className="new-post-option" onClick={createManual} disabled={busy}>
            <span className="new-post-h">Skapa manuellt</span>
            <span className="new-post-d">
              Fyll i fälten själv — kanaler, datum, copy per plattform, ansvarig och status.
              Bra när du redan vet vad du vill säga.
            </span>
            <span className="new-post-cta">{busy ? "Skapar…" : "Öppna formulär →"}</span>
          </button>
          <button type="button" className="new-post-option" onClick={goStudio} disabled={busy}>
            <span className="new-post-h">Skapa via Studio</span>
            <span className="new-post-d">
              Brief in, agent ut. Studio föreslår copy per kanal, croppar bilder per format
              och tar med varumärkesmärken.
            </span>
            <span className="new-post-cta">Öppna Studio →</span>
          </button>
        </div>
        {error && <div className="auth-error" style={{ marginTop: 12 }}>{error}</div>}
        <div className="modal-actions">
          <button type="button" className="tb-btn" onClick={onClose} disabled={busy}>Avbryt</button>
        </div>
      </div>
    </>
  );
}

/* ============================================================
   INSIGHTS (light, brand-health view)
   ============================================================ */
function InsightsView() {
  const series = [12, 18, 14, 22, 19, 28, 31, 26, 34, 30, 38, 42, 40];
  const max = Math.max(...series);
  const W = 760, H = 220, P = 20;
  const pts = series.map((v, i) => {
    const x = P + (i / (series.length - 1)) * (W - P*2);
    const y = H - P - (v / max) * (H - P*2);
    return [x, y];
  });
  const path = pts.map((p, i) => (i === 0 ? `M${p[0]} ${p[1]}` : `L${p[0]} ${p[1]}`)).join(" ");
  const area = path + ` L${pts[pts.length-1][0]} ${H-P} L${pts[0][0]} ${H-P} Z`;

  const channels = [
    { nm:"Editorial", v: 84 },
    { nm:"Social",    v: 71 },
    { nm:"Email",     v: 66 },
    { nm:"Paid",      v: 54 },
  ];

  return (
    <div className="page">
      <header className="page-head">
        <div>
          <div className="eyebrow">Insights · brand health</div>
          <h1>How the <em>brand</em> is performing.</h1>
          <p className="sub">Volume, voice consistency, token coverage. Numbers are directional — the goal is not louder, it&rsquo;s clearer.</p>
        </div>
        <div className="head-meta">
          <div>Range · last 13 weeks</div>
          <div>Updated 09:30 CEST</div>
        </div>
      </header>

      <div className="section-title">
        <h3>Output cadence</h3>
        <span className="meta">Assets shipped per week · 13w</span>
      </div>
      <div className="chart">
        <svg viewBox={`0 0 ${W} ${H}`}>
          <path d={area} fill="var(--paper)" />
          <path d={path} stroke="var(--ink)" strokeWidth="1.5" fill="none" />
          {pts.map((p, i) => (
            <circle key={i} cx={p[0]} cy={p[1]} r="2.5" fill="var(--ink)" />
          ))}
          {/* baseline ticks */}
          {pts.map((p, i) => (
            <text key={i} x={p[0]} y={H-4} textAnchor="middle" fontSize="9" fontFamily="Geist Mono" fill="var(--mute)">w{9+i}</text>
          ))}
        </svg>
      </div>

      <div style={{ height: 32 }} />

      <div style={{ display:"grid", gridTemplateColumns:"1.4fr 1fr", gap: 40 }}>
        <div>
          <div className="section-title">
            <h3>Voice consistency by channel</h3>
            <span className="meta">% within tone guardrails</span>
          </div>
          {channels.map(c => (
            <div className="bar-row" key={c.nm}>
              <div className="nm">{c.nm}</div>
              <div className="bar-track"><div className="bar-fill" style={{ width: c.v + "%" }} /></div>
              <div className="vol">{c.v}%</div>
            </div>
          ))}
        </div>
        <div>
          <div className="section-title">
            <h3>Library usage</h3>
            <span className="meta">Top 5 — last 30 days</span>
          </div>
          {[
            { nm:"Bright/nest lockup · primary", v: 142 },
            { nm:"Color · Ink", v: 121 },
            { nm:"Type · Display / Italic", v: 88 },
            { nm:"Imagery · Founders 03",  v: 64 },
            { nm:"Component · Card / editorial", v: 41 },
          ].map((c, i) => (
            <div className="bar-row" key={i}>
              <div className="nm">{c.nm}</div>
              <div className="bar-track"><div className="bar-fill" style={{ width: (c.v / 142 * 100) + "%" }} /></div>
              <div className="vol">{c.v}×</div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

/* ============================================================
   SETTINGS (minimal stub)
   ============================================================ */
/* ============================================================
   PEOPLE — invite + role management
   ============================================================ */
function PeopleView() {
  const u = useUser();
  if (!u) return null;
  const canManage = u.can("manage_users");

  const [invite, setInvite] = useState({ name: "", email: "", roleId: "customer_viewer", org: "" });
  const [orgFilter, setOrgFilter] = useState("Alla");

  const allOrgs = useMemo(() => {
    const set = new Set(u.people.map(p => p.org).filter(Boolean));
    return ["Alla", ...Array.from(set)];
  }, [u.people]);

  const list = u.people.filter(p => orgFilter === "Alla" || p.org === orgFilter);

  const submit = async (e) => {
    e.preventDefault();
    if (!canManage) return;
    if (!invite.name || !invite.email) return;
    try {
      await u.invitePerson({ name: invite.name, email: invite.email, org: invite.org, roleSlug: invite.roleId });
      setInvite({ name: "", email: "", roleId: invite.roleId, org: invite.org });
    } catch (err) { alert("Kunde inte bjuda in: " + (err.body?.error || err.message)); }
  };

  return (
    <div className="page">
      <header className="page-head">
        <div>
          <div className="eyebrow">System · personer</div>
          <h1>Personer.</h1>
          <p className="sub">Bjud in kollegor och kunder, sätt vad de får läsa, ändra och godkänna. Roller styr vad var och en ser i Brand OS.</p>
        </div>
      </header>

      <section className="people-section">
        <div className="section-title">
          <h3>Bjud in</h3>
          <span className="meta">Skicka inbjudan via mejl</span>
        </div>
        {!canManage && <div className="role-warning">Din nuvarande roll har inte rättigheten <em>Hantera personer & roller</em>. Visa som någon med den rättigheten för att bjuda in.</div>}
        <form className="invite-form" onSubmit={submit}>
          <input className="text-input" placeholder="Namn"  value={invite.name}
                 onChange={(e) => setInvite(i => ({ ...i, name: e.target.value }))} disabled={!canManage} />
          <input className="text-input" placeholder="E-post" type="email" value={invite.email}
                 onChange={(e) => setInvite(i => ({ ...i, email: e.target.value }))} disabled={!canManage} />
          <input className="text-input" placeholder="Organisation" value={invite.org}
                 onChange={(e) => setInvite(i => ({ ...i, org: e.target.value }))} disabled={!canManage} />
          <select className="text-input" value={invite.roleId} disabled={!canManage}
                  onChange={(e) => setInvite(i => ({ ...i, roleId: e.target.value }))}>
            {u.roles.map(r => <option key={r.id} value={r.id}>{r.name}</option>)}
          </select>
          <button type="submit" className="tb-btn is-primary" disabled={!canManage}>
            <IconPlus size={12} stroke="#fff" /> Bjud in
          </button>
        </form>
      </section>

      <section className="people-section">
        <div className="section-title">
          <h3>Alla personer</h3>
          <span className="meta">{list.length} {list.length === 1 ? "person" : "personer"}</span>
        </div>
        <div className="people-filter">
          {allOrgs.map(o => (
            <button key={o} type="button"
              className={"pill " + (orgFilter === o ? "is-on" : "")}
              onClick={() => setOrgFilter(o)}>{o}</button>
          ))}
        </div>
        <div className="people-table">
          <div className="ppl-row ppl-head">
            <span>Person</span><span>E-post</span><span>Organisation</span><span>Roll</span><span>Status</span>
          </div>
          {list.map(p => {
            const role = u.roles.find(r => r.id === p.roleId);
            return (
              <div className="ppl-row" key={p.id}>
                <span className="ppl-person">
                  <span className="person-avatar">{p.initials}</span>
                  <span className="ppl-name">{p.name}</span>
                </span>
                <span className="ppl-em">{p.email}</span>
                <span className="ppl-org">{p.org}</span>
                <span>
                  <select className="text-input" value={p.roleId}
                          disabled={!canManage}
                          onChange={(e) => u.updateMember(p.id, { roleSlug: e.target.value })}>
                    {u.roles.map(r => <option key={r.id} value={r.id}>{r.name}</option>)}
                  </select>
                </span>
                <span className={"ppl-status is-" + p.status}>{p.status === "invited" ? "Inbjuden" : "Aktiv"}</span>
              </div>
            );
          })}
        </div>
      </section>
    </div>
  );
}

/* ============================================================
   SETTINGS — tabs: Allmänt, Roller, Publiceringsflöden
   ============================================================ */
function SettingsView() {
  const route = useRoute();
  const allowedTabs = ["general","roles","flows","platforms","agents"];
  const tab = allowedTabs.includes(route.params[0]) ? route.params[0] : "general";
  const setTab = (t) => navigate("/settings" + (t === "general" ? "" : "/" + t));
  return (
    <div className="page">
      <header className="page-head">
        <div>
          <div className="eyebrow">System · settings</div>
          <h1>Workspace <em>settings.</em></h1>
          <p className="sub">Hantera roller, godkännandeflöden, plattformar och AI-agenter.</p>
        </div>
      </header>

      <div className="settings-tabs">
        <button className={"lib-tab " + (tab === "general"   ? "is-active" : "")} onClick={() => setTab("general")}>Allmänt</button>
        <button className={"lib-tab " + (tab === "roles"     ? "is-active" : "")} onClick={() => setTab("roles")}>Roller & rättigheter</button>
        <button className={"lib-tab " + (tab === "flows"     ? "is-active" : "")} onClick={() => setTab("flows")}>Publiceringsflöden</button>
        <button className={"lib-tab " + (tab === "platforms" ? "is-active" : "")} onClick={() => setTab("platforms")}>Plattformar & format</button>
        <button className={"lib-tab " + (tab === "agents"    ? "is-active" : "")} onClick={() => setTab("agents")}>Agenter</button>
      </div>

      {tab === "general"   && <SettingsGeneral />}
      {tab === "roles"     && <SettingsRoles   />}
      {tab === "flows"     && <SettingsFlows   />}
      {tab === "platforms" && <SettingsPlatforms />}
      {tab === "agents"    && <SettingsAgents />}
    </div>
  );
}

function SettingsGeneral() {
  const [tz, setTz] = useState("Europe/Stockholm");
  return (
    <div className="settings-pane">
      <div className="section-title"><h3>Workspace</h3></div>
      <div className="field-row">
        <section className="field">
          <label>Namn</label>
          <input className="text-input" defaultValue="Brightnest" />
        </section>
        <section className="field">
          <label>Tidszon</label>
          <Combobox value={tz} options={TIMEZONES} onChange={setTz} placeholder="Välj tidszon…" />
        </section>
      </div>
      <section className="field">
        <label>Brand-token versioning</label>
        <input className="text-input" defaultValue="v 4.2 · publicerad 12 maj 2026" disabled />
      </section>
    </div>
  );
}

function SettingsRoles() {
  const u = useUser();
  const canManage = u.can("manage_users");
  const [selectedId, setSelectedId] = useState(u.roles[0]?.id);
  const role = u.roles.find(r => r.id === selectedId);

  const togglePerm = async (permId) => {
    if (!canManage || !role) return;
    if (role.builtin && role.id === "owner") return; // owner is locked
    const perms = role.perms.includes(permId)
      ? role.perms.filter(x => x !== permId)
      : [...role.perms, permId];
    await u.updateRole(role.id, { perms });
  };

  const addRole = async () => {
    if (!canManage) return;
    const updated = await u.createRole("Ny roll");
    const last = updated[updated.length - 1];
    if (last) setSelectedId(last.id);
  };

  return (
    <div className="settings-pane two-col">
      <aside className="roles-list">
        <div className="section-title"><h3>Roller</h3><span className="meta">{u.roles.length}</span></div>
        {u.roles.map(r => (
          <button key={r.id} type="button"
            className={"role-row " + (r.id === selectedId ? "is-active" : "")}
            onClick={() => setSelectedId(r.id)}>
            <span className="role-row-name">{r.name}</span>
            <span className="role-row-meta">
              {r.scope === "customer" ? "Kund" : "Intern"} · {r.perms.length} rätt.
              {r.builtin && <span className="role-tag">inbyggd</span>}
            </span>
          </button>
        ))}
        <button type="button" className="tb-btn role-add" onClick={addRole} disabled={!canManage}>
          <IconPlus size={12} /> Ny roll
        </button>
      </aside>

      <div className="roles-detail">
        {role && (
          <>
            <div className="section-title">
              <h3>{role.name}</h3>
              <span className="meta">{role.builtin ? "inbyggd · ej raderbar" : "egen roll"}</span>
            </div>
            <section className="field">
              <label>Namn</label>
              <input className="text-input" defaultValue={role.name}
                disabled={!canManage || role.builtin}
                onBlur={(e) => { if (e.target.value !== role.name) u.updateRole(role.id, { name: e.target.value }); }} />
            </section>
            <section className="field">
              <label>Räckvidd</label>
              <select className="text-input" value={role.scope}
                disabled={!canManage || role.builtin}
                onChange={(e) => u.updateRole(role.id, { scope: e.target.value })}>
                <option value="internal">Intern (workspace)</option>
                <option value="customer">Kund</option>
              </select>
            </section>
            <div className="section-title"><h3>Rättigheter</h3><span className="meta">Vad rollen får göra</span></div>
            <div className="perm-grid">
              {PERMISSIONS.map(perm => {
                const on = role.perms.includes(perm.id);
                return (
                  <label key={perm.id} className={"perm-row " + (on ? "is-on" : "")}>
                    <input type="checkbox" checked={on}
                      disabled={!canManage || (role.builtin && role.id === "owner")}
                      onChange={() => togglePerm(perm.id)} />
                    <span className="perm-meta">
                      <span className="perm-label">{perm.label}</span>
                      <span className="perm-desc">{perm.desc}</span>
                    </span>
                  </label>
                );
              })}
            </div>
          </>
        )}
      </div>
    </div>
  );
}

function SettingsFlows() {
  const u = useUser();
  const canManage = u.can("manage_users");
  const [selectedId, setSelectedId] = useState(u.flows[0]?.id);
  const flow = u.flows.find(f => f.id === selectedId);

  const update = (patch) => u.updateFlow(flow.id, patch);
  const moveStage = (idx, dir) => {
    const stages = [...flow.stages];
    const j = idx + dir;
    if (j < 0 || j >= stages.length) return;
    [stages[idx], stages[j]] = [stages[j], stages[idx]];
    update({ stages });
  };
  const removeStage = (idx) => update({ stages: flow.stages.filter((_, i) => i !== idx) });
  const addStage    = () => update({
    stages: [...flow.stages, { id: "s" + Date.now().toString(36), label: "Nytt steg", requirePermission: "approve_brand" }],
  });
  const addFlow = () => alert("Att skapa nya flöden från UI:t implementeras snart — för nu, redigera de inbyggda.");
  const setDefault = () => update({ isDefault: true });

  return (
    <div className="settings-pane two-col">
      <aside className="roles-list">
        <div className="section-title"><h3>Flöden</h3><span className="meta">{u.flows.length}</span></div>
        {u.flows.map(f => (
          <button key={f.id} type="button"
            className={"role-row " + (f.id === selectedId ? "is-active" : "")}
            onClick={() => setSelectedId(f.id)}>
            <span className="role-row-name">{f.name}</span>
            <span className="role-row-meta">
              {f.stages.length} steg
              {f.isDefault && <span className="role-tag">standard</span>}
            </span>
          </button>
        ))}
        <button type="button" className="tb-btn role-add" onClick={addFlow} disabled={!canManage}>
          <IconPlus size={12} /> Nytt flöde
        </button>
      </aside>

      <div className="roles-detail">
        {flow && (
          <>
            <div className="section-title">
              <h3>{flow.name}</h3>
              <span className="meta">{flow.isDefault ? "Standardflöde" : ""}</span>
            </div>
            <section className="field">
              <label>Namn</label>
              <input className="text-input" defaultValue={flow.name} disabled={!canManage}
                onBlur={(e) => { if (e.target.value !== flow.name) update({ name: e.target.value }); }} />
            </section>
            <section className="field">
              <label>Beskrivning</label>
              <input className="text-input" defaultValue={flow.description || ""} disabled={!canManage}
                onBlur={(e) => { if (e.target.value !== (flow.description || "")) update({ description: e.target.value }); }} />
            </section>
            <section className="field">
              <button type="button" className="tb-btn"
                disabled={!canManage || flow.isDefault} onClick={setDefault}>
                Använd som standard
              </button>
            </section>

            <div className="section-title"><h3>Steg</h3><span className="meta">Ordningen som krävs för publicering</span></div>
            <div className="flow-stages">
              {flow.stages.map((s, idx) => (
                <div className="flow-stage" key={s.id}>
                  <span className="flow-stage-idx">{idx + 1}</span>
                  <div className="flow-stage-body">
                    <input className="text-input" value={s.label} disabled={!canManage}
                      onChange={(e) => update({ stages: flow.stages.map((x, i) => i === idx ? { ...x, label: e.target.value } : x) })} />
                    <select className="text-input" value={s.requirePermission} disabled={!canManage}
                      onChange={(e) => update({ stages: flow.stages.map((x, i) => i === idx ? { ...x, requirePermission: e.target.value } : x) })}>
                      {PERMISSIONS.map(p => <option key={p.id} value={p.id}>Kräver: {p.label}</option>)}
                    </select>
                  </div>
                  <div className="flow-stage-ops">
                    <button type="button" className="nav-btn" disabled={!canManage || idx === 0} onClick={() => moveStage(idx, -1)} aria-label="Upp">↑</button>
                    <button type="button" className="nav-btn" disabled={!canManage || idx === flow.stages.length - 1} onClick={() => moveStage(idx,  1)} aria-label="Ner">↓</button>
                    <button type="button" className="nav-btn" disabled={!canManage} onClick={() => removeStage(idx)} aria-label="Ta bort">×</button>
                  </div>
                </div>
              ))}
              <button type="button" className="tb-btn" disabled={!canManage} onClick={addStage}>
                <IconPlus size={12} /> Lägg till steg
              </button>
            </div>
          </>
        )}
      </div>
    </div>
  );
}

/* ============================================================
   AGENTS — pick from system-defined roles, tune their instructions,
   try them in a small playground. The role's tool allowlist is read-only.
   ============================================================ */
function SettingsAgents() {
  const u = useUser();
  const canManage = u.can("manage_users");
  const canRun    = u.can("use_agents");
  const agents    = u.agents || [];
  const [selectedId, setSelectedId] = useState(agents[0]?.id);
  const agent = agents.find(a => a.id === selectedId);

  // Local edit buffer so we don't fire a PATCH on every keystroke.
  const [draft, setDraft] = useState(null);
  useEffect(() => {
    if (agent) setDraft({
      name: agent.name, instructions: agent.instructions || "",
      temperature: agent.temperature ?? 0.7, enabled: agent.enabled,
    });
  }, [agent?.id]);

  if (!agents.length) return <div className="copy-empty">Inga agenter konfigurerade. Kör npm run seed.</div>;
  if (!agent || !draft) return null;

  const dirty = draft.name !== agent.name
             || draft.instructions !== (agent.instructions || "")
             || draft.temperature !== agent.temperature
             || draft.enabled !== agent.enabled;

  const save = () => u.updateAgent(agent.id, {
    name: draft.name,
    instructions: draft.instructions,
    temperature: Number(draft.temperature),
    enabled: !!draft.enabled,
  });
  const revert = () => setDraft({
    name: agent.name, instructions: agent.instructions || "",
    temperature: agent.temperature ?? 0.7, enabled: agent.enabled,
  });

  return (
    <div className="settings-pane two-col">
      <aside className="roles-list">
        <div className="section-title"><h3>Agenter</h3><span className="meta">{agents.length} st</span></div>
        {agents.map(a => (
          <button key={a.id} type="button"
            className={"role-row " + (a.id === selectedId ? "is-active" : "")}
            onClick={() => setSelectedId(a.id)}>
            <span className="role-row-name">
              {a.name}
              <span className={"role-tag " + (a.enabled ? "is-on" : "is-off")}>{a.enabled ? "på" : "av"}</span>
            </span>
            <span className="role-row-meta">{a.role.name}</span>
          </button>
        ))}
        <div className="field-help" style={{ margin: "8px 6px 0" }}>
          Roller är fördefinierade av systemet. Du kan döpa om, av/på-slå, och ge
          instruktioner — men inte byta vilka data agenten ser.
        </div>
      </aside>

      <div className="roles-detail">
        <div className="section-title">
          <h3>{agent.role.name}</h3>
          <span className="meta">{agent.role.description}</span>
        </div>

        <section className="field">
          <label>Namn</label>
          <input className="text-input" value={draft.name} disabled={!canManage}
            onChange={(e) => setDraft(d => ({ ...d, name: e.target.value }))} />
        </section>

        <section className="field">
          <label className="toggle-row">
            <input type="checkbox" checked={draft.enabled} disabled={!canManage}
              onChange={(e) => setDraft(d => ({ ...d, enabled: e.target.checked }))} />
            <span>Aktivera <strong>{agent.role.name}</strong> i workspacet</span>
          </label>
        </section>

        <section className="field">
          <label>Standardprompt (från systemet, ej redigerbar)</label>
          <pre className="agent-default-prompt">{agent.role.defaultPrompt}</pre>
        </section>

        <section className="field">
          <label>Dina instruktioner (max 4000 tecken)</label>
          <textarea className="text-input" rows={6}
            placeholder="T.ex. 'Lyft alltid att vi gör kreativa lösningar på strategiska affärsutmaningar.'"
            value={draft.instructions} disabled={!canManage} maxLength={4000}
            onChange={(e) => setDraft(d => ({ ...d, instructions: e.target.value }))} />
          <span className="field-help">{draft.instructions.length} / 4000 tecken</span>
        </section>

        <div className="field-row">
          <section className="field">
            <label>Temperatur</label>
            <input type="range" min="0" max="1.5" step="0.05"
              value={draft.temperature} disabled={!canManage}
              onChange={(e) => setDraft(d => ({ ...d, temperature: Number(e.target.value) }))} />
            <span className="field-help">{draft.temperature.toFixed(2)} · lägre = striktare, högre = friare</span>
          </section>
          <section className="field">
            <label>Tillåtna verktyg</label>
            <div className="tool-chips">
              {agent.role.tools.map(t => <span key={t} className="tool-chip">{t}</span>)}
            </div>
            <span className="field-help">Allowlist från systemet — kan inte ändras.</span>
          </section>
        </div>

        <div className="agent-save-row">
          <button type="button" className="tb-btn" onClick={revert} disabled={!dirty}>Återställ</button>
          <button type="button" className="tb-btn is-primary" onClick={save} disabled={!canManage || !dirty}>Spara</button>
        </div>

        <AgentPlayground agent={agent} canRun={canRun} />
      </div>
    </div>
  );
}

function AgentPlayground({ agent, canRun }) {
  const u = useUser();
  const [busy, setBusy] = useState(false);
  const [error, setError] = useState(null);
  const [result, setResult] = useState(null);

  // Each role gets its own input form so the prompt is shaped right.
  const placeholders = {
    copywriter:    "Brief — t.ex. 'Vårens kollektion: linne, ek, långa kvällar'.",
    reviewer:      "Klistra in copy som ska granskas.",
    image_curator: "Beskriv inläggets ämne så bildsättaren plockar passande bilder.",
    sparring:      "Kasta in en idé så får du 3–5 vinklar tillbaka.",
  };
  const inputKey = {
    copywriter: "brief", reviewer: "text", image_curator: "brief", sparring: "idea",
  };
  const [input, setInput] = useState("");

  const run = async () => {
    setBusy(true); setError(null); setResult(null);
    try {
      const payload = { [inputKey[agent.role.id] || "text"]: input };
      const r = await u.runAgent(agent.id, payload);
      setResult(r);
    } catch (e) {
      setError(e.body?.error || e.message);
    } finally { setBusy(false); }
  };

  return (
    <div className="agent-play">
      <div className="section-title" style={{ marginTop: 16 }}>
        <h3>Test-kör</h3>
        <span className="meta">Provkör agenten utan att skapa något permanent</span>
      </div>
      {!canRun && (
        <div className="role-warning">Din nuvarande roll saknar rättigheten <em>Använda agenter</em>.</div>
      )}
      <textarea className="text-input" rows={3}
        placeholder={placeholders[agent.role.id] || "Skriv in agentens input…"}
        value={input} onChange={(e) => setInput(e.target.value)} disabled={!canRun || !agent.enabled} />
      <div className="agent-play-actions">
        <button type="button" className="tb-btn is-primary" onClick={run}
          disabled={!canRun || !agent.enabled || busy || !input.trim()}>
          {busy ? "Kör…" : "Kör agent"}
        </button>
        {!agent.enabled && <span className="field-help">Agenten är avstängd — slå på den ovan först.</span>}
      </div>
      {error && <div className="auth-error" style={{ marginTop: 8 }}>{error}</div>}
      {result && <AgentResult output={result.output} ms={result.durationMs} />}
    </div>
  );
}

function AgentResult({ output, ms }) {
  if (!output) return null;
  return (
    <div className="agent-result">
      <div className="agent-result-h">Resultat <span className="meta">({ms} ms)</span></div>
      {output.kind === "copy" && (
        <ul className="agent-copy">
          {output.variants.map((v, i) => (
            <li key={i}><span className="agent-tag">{v.channel}</span> {v.text}</li>
          ))}
          {output.notes && <li className="agent-note">{output.notes}</li>}
        </ul>
      )}
      {output.kind === "feedback" && (
        <ul className="agent-issues">
          {output.issues.map((it, i) => (
            <li key={i} className={"agent-issue is-" + it.severity}>
              <span className="agent-tag">{it.severity}</span> {it.message}
            </li>
          ))}
        </ul>
      )}
      {output.kind === "image_picks" && (
        <div className="agent-picks">
          {output.picks.map((p, i) => (
            <figure key={i}>
              <img src={p.url} alt={p.name} loading="lazy" />
              <figcaption>{p.reason}</figcaption>
            </figure>
          ))}
        </div>
      )}
      {output.kind === "ideas" && (
        <ul className="agent-ideas">
          {output.angles.map((a, i) => (
            <li key={i}>
              <span className="agent-idea-h">{a.title}</span>
              <span className="agent-idea-t">{a.text}</span>
            </li>
          ))}
        </ul>
      )}
      {output.kind === "raw" && <pre className="agent-raw">{output.text}</pre>}
    </div>
  );
}

function SettingsPlatforms() {
  const u = useUser();
  const canManage = u.can("manage_users");
  const [selectedId, setSelectedId] = useState(u.platforms[0]?.id);
  const platform = u.platforms.find(p => p.id === selectedId);

  // All format edits replay the full list back through the API so the
  // server has the canonical state.
  const replaceFormats = (mut) => {
    const formats = mut(platform.formats);
    return u.updatePlatform(platform.id, { formats });
  };
  const togglePlatform = (id) => {
    if (!canManage) return;
    const p = u.platforms.find(x => x.id === id);
    if (!p) return;
    u.updatePlatform(id, { enabled: !p.enabled });
  };
  const toggleFormat  = (fid) => canManage && platform && replaceFormats(fs => fs.map(f => f.id === fid ? { ...f, disabled: !f.disabled } : f));
  const updateFormat  = (fid, patch) => replaceFormats(fs => fs.map(f => f.id === fid ? { ...f, ...patch } : f));
  const addFormat     = () => canManage && platform && replaceFormats(fs => [...fs, { id: "fmt_" + Date.now().toString(36), label: "Nytt format", w: 1080, h: 1080, kind: "organic" }]);
  const removeFormat  = (fid) => replaceFormats(fs => fs.filter(f => f.id !== fid));

  return (
    <div className="settings-pane two-col">
      <aside className="roles-list">
        <div className="section-title"><h3>Plattformar</h3><span className="meta">{u.platforms.length}</span></div>
        {u.platforms.map(p => (
          <button key={p.id} type="button"
            className={"role-row " + (p.id === selectedId ? "is-active" : "")}
            onClick={() => setSelectedId(p.id)}>
            <span className="role-row-name">
              <span className={"ch-dot ch-" + p.channelId} />
              {p.label}
            </span>
            <span className="role-row-meta">
              {p.formats.length} format
              <span className={"role-tag " + (p.enabled ? "is-on" : "is-off")}>{p.enabled ? "på" : "av"}</span>
            </span>
          </button>
        ))}
      </aside>

      <div className="roles-detail">
        {platform && (
          <>
            <div className="section-title">
              <h3>{platform.label}</h3>
              <span className="meta">{platform.enabled ? "aktiv" : "inaktiverad"}</span>
            </div>

            <section className="field">
              <label className="toggle-row">
                <input type="checkbox" checked={platform.enabled} disabled={!canManage}
                  onChange={() => togglePlatform(platform.id)} />
                <span>Använd <strong>{platform.label}</strong> i Studio och Content Plan</span>
              </label>
              <span className="field-help">När en plattform är inaktiverad försvinner den från filterpills och Studio-targets, men befintliga inlägg behåller sina kanaler.</span>
            </section>

            <div className="section-title"><h3>Konto</h3><span className="meta">Visa rätt handle och länk i Studio</span></div>
            <div className="field-row">
              <section className="field">
                <label>Handle</label>
                <input className="text-input" disabled={!canManage}
                  placeholder={"t.ex. brightnest_official"}
                  defaultValue={platform.handle || ""}
                  onBlur={(e) => {
                    const v = e.target.value.trim().replace(/^@/, "");
                    if (v !== (platform.handle || "")) u.updatePlatform(platform.id, { handle: v });
                  }} />
                <span className="field-help">Utan @-tecken. Visas som "@&lt;handle&gt;" i förhandsvisningar.</span>
              </section>
              <section className="field">
                <label>Länk till kontot</label>
                <input className="text-input" disabled={!canManage}
                  placeholder={defaultAccountUrlFor(platform.channelId)}
                  defaultValue={platform.accountUrl || ""}
                  onBlur={(e) => {
                    const v = e.target.value.trim();
                    if (v !== (platform.accountUrl || "")) u.updatePlatform(platform.id, { accountUrl: v });
                  }} />
                <span className="field-help">Hela URL:en — t.ex. https://instagram.com/brightnest_official</span>
              </section>
            </div>

            <div className="section-title"><h3>Format</h3><span className="meta">Storlekar plattformen accepterar</span></div>
            <div className="format-list">
              {platform.formats.map(f => {
                const ratio = (f.w / f.h).toFixed(2);
                return (
                  <div className={"format-row " + (f.disabled ? "is-off" : "")} key={f.id}>
                    <div className="format-aspect" style={{ aspectRatio: f.w + " / " + f.h }} />
                    <div className="format-body">
                      <input className="text-input" defaultValue={f.label} disabled={!canManage}
                        onBlur={(e) => e.target.value !== f.label && updateFormat(f.id, { label: e.target.value })} />
                      <div className="format-dims">
                        <input className="text-input mini" type="number" defaultValue={f.w} disabled={!canManage}
                          onBlur={(e) => Number(e.target.value) !== f.w && updateFormat(f.id, { w: Number(e.target.value) })} />
                        <span>×</span>
                        <input className="text-input mini" type="number" defaultValue={f.h} disabled={!canManage}
                          onBlur={(e) => Number(e.target.value) !== f.h && updateFormat(f.id, { h: Number(e.target.value) })} />
                        <span className="ratio">{ratio}:1</span>
                        <select className="text-input" value={f.kind} disabled={!canManage}
                          onChange={(e) => updateFormat(f.id, { kind: e.target.value })}>
                          <option value="organic">Organiskt</option>
                          <option value="ad">Annons</option>
                          <option value="email">E-post</option>
                          <option value="web">Webb</option>
                        </select>
                      </div>
                    </div>
                    <div className="format-ops">
                      <button type="button" className="nav-btn" disabled={!canManage}
                        onClick={() => toggleFormat(f.id)} title={f.disabled ? "Aktivera" : "Inaktivera"}>
                        {f.disabled ? "↺" : "−"}
                      </button>
                      <button type="button" className="nav-btn" disabled={!canManage}
                        onClick={() => removeFormat(f.id)} title="Ta bort">×</button>
                    </div>
                  </div>
                );
              })}
              <button type="button" className="tb-btn" disabled={!canManage} onClick={addFormat}>
                <IconPlus size={12} /> Lägg till format
              </button>
            </div>
          </>
        )}
      </div>
    </div>
  );
}

/* ============================================================
   ADMIN — super-admin only. Lists every org, lets you spin up a new
   customer workspace, and step into one to manage it.
   ============================================================ */
function AdminView() {
  const u = useUser();
  const route = useRoute();
  const [orgs, setOrgs]   = useState(null);
  const [busy, setBusy]   = useState(false);
  const [error, setError] = useState(null);
  const [draft, setDraft] = useState({ name: "", kind: "customer" });
  // The topbar's Ny-knapp sets ?new=1; pre-open the form when present.
  const openOnLoad = route.search.get("new") === "1";

  const reload = async () => { try { setOrgs(await api.get("/admin/orgs")); } catch (e) { setError(e.body?.error || e.message); } };
  useEffect(() => { reload(); }, []);

  // Guard the view itself — server enforces it, but UI should be honest.
  if (!u.user.isSuperAdmin) {
    return (
      <div className="page">
        <header className="page-head"><div><h1>Admin</h1></div></header>
        <div className="role-warning">Den här sektionen är reserverad för systemägare.</div>
      </div>
    );
  }

  const submit = async (e) => {
    e?.preventDefault?.();
    if (!draft.name.trim() || busy) return;
    setBusy(true); setError(null);
    try {
      const r = await api.post("/admin/orgs", { name: draft.name.trim(), kind: draft.kind });
      setOrgs(r.orgs);
      setDraft({ name: "", kind: draft.kind });
      navigate("/admin");
    } catch (err) {
      setError(err.body?.error || err.message);
    } finally { setBusy(false); }
  };

  const switchInto = async (org) => {
    try {
      await api.post("/auth/switch-org", { orgId: org.id });
      // Hydrate everything for the newly active org.
      await u.refresh();
      navigate("/");
    } catch (err) { alert("Kunde inte byta organisation: " + (err.body?.error || err.message)); }
  };

  const grouped = orgs ? {
    internal: orgs.filter(o => o.kind === "internal"),
    customer: orgs.filter(o => o.kind === "customer"),
  } : { internal: [], customer: [] };

  return (
    <div className="page">
      <header className="page-head">
        <div>
          <div className="eyebrow">System · admin</div>
          <h1>Admin.</h1>
          <p className="sub">Skapa och hantera arbetsytor. Varje ny kund får sin egen contentplan, brand-bibliotek och uppsättning roller — isolerat från andras data.</p>
        </div>
      </header>

      <section className="admin-create">
        <div className="section-title">
          <h3>Skapa ny organisation</h3>
          <span className="meta">Bootstrappar standardroller, flöden, plattformar och agenter</span>
        </div>
        <form className="invite-form admin-create-form" onSubmit={submit}
              style={{ gridTemplateColumns: "2fr 1fr auto" }}
              {...(openOnLoad ? { autoFocus: true } : {})}>
          <input className="text-input" placeholder="Företagsnamn"
                 value={draft.name} autoFocus={openOnLoad}
                 onChange={(e) => setDraft(d => ({ ...d, name: e.target.value }))} />
          <select className="text-input" value={draft.kind}
                  onChange={(e) => setDraft(d => ({ ...d, kind: e.target.value }))}>
            <option value="customer">Kund</option>
            <option value="internal">Internt (eget bolag)</option>
          </select>
          <button type="submit" className="tb-btn is-primary" disabled={busy || !draft.name.trim()}>
            <IconPlus size={12} stroke="#fff" /> {busy ? "Skapar…" : "Skapa"}
          </button>
        </form>
        {error && <div className="auth-error" style={{ marginTop: 8 }}>{error}</div>}
      </section>

      {orgs === null && <div className="copy-empty" style={{ marginTop: 24 }}>Hämtar organisationer…</div>}

      {orgs && [
        { key: "internal", title: "Interna arbetsytor", hint: "Egna bolag och projekt." },
        { key: "customer", title: "Kunder",             hint: "En arbetsyta per kund — egen contentplan och brand-bibliotek." },
      ].map(g => grouped[g.key].length === 0 ? null : (
        <section className="admin-group" key={g.key}>
          <div className="section-title">
            <h3>{g.title}</h3>
            <span className="meta">{g.hint} · {grouped[g.key].length} st</span>
          </div>
          <div className="admin-table">
            <div className="adm-row adm-head">
              <span>Namn</span><span>Slug</span><span>Medlemmar</span><span>Inlägg</span><span>Skapad</span><span></span>
            </div>
            {grouped[g.key].map(o => (
              <div className="adm-row" key={o.id}>
                <span className="adm-name">{o.name}{o.id === u.user.id ? "" : ""}</span>
                <span className="mono adm-slug">{o.slug}</span>
                <span className="mono">{o.member_count}</span>
                <span className="mono">{o.post_count}</span>
                <span className="mono adm-date">{(o.created_at || "").slice(0,10)}</span>
                <span className="adm-actions">
                  {o.id === route?.activeOrgId ? <span className="adm-active-pill">aktiv</span> :
                    <button type="button" className="tb-btn" onClick={() => switchInto(o)}>Gå in</button>}
                </span>
              </div>
            ))}
          </div>
        </section>
      ))}
    </div>
  );
}

Object.assign(window, {
  OverviewView, LibraryView, StudioView, ContentPlanView, InsightsView,
  PeopleView, SettingsView, SettingsGeneral, SettingsRoles, SettingsFlows, SettingsPlatforms,
  AdminView,
  LIB_TABS,
});
