// App shell — Brand OS

const { useState: useS, useEffect: useE, createContext, useContext } = React;

// ──────────────────────────────────────────
// Tweak defaults (host-rewritable JSON block)
// ──────────────────────────────────────────
const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "showHero": true,
  "showSubtitle": true,
  "showHeadMeta": true,
  "showEyebrows": true,
  "showSearch": true,
  "showLivePill": true,
  "showExport": true,
  "showCounts": true,
  "showBrandSubnav": true,
  "showKPIs": true,
  "showQuickActions": true,
  "showActivity": true
}/*EDITMODE-END*/;

const TweakCtx = createContext(TWEAK_DEFAULTS);
window.TweakCtx = TweakCtx;
window.useTw = () => useContext(TweakCtx);

// usePersistedState — like useState, but mirrors to localStorage so the value
// survives reloads. Key is namespaced by the caller (typically per-user) so
// different users on the same machine keep separate UI state.
function usePersistedState(key, initial) {
  const [v, setV] = useS(() => {
    try {
      const raw = localStorage.getItem(key);
      if (raw != null) return JSON.parse(raw);
    } catch {}
    return typeof initial === "function" ? initial() : initial;
  });
  useE(() => {
    try { localStorage.setItem(key, JSON.stringify(v)); } catch {}
  }, [key, v]);
  return [v, setV];
}

// ──────────────────────────────────────────
// Mini router on top of the History API. Source of truth is the URL —
// useRoute() reads it and re-renders on popstate; navigate() pushes.
// ──────────────────────────────────────────
const KNOWN_VIEWS = new Set(["overview","library","studio","content","insights","people","settings","admin"]);

function parseRoute(loc = window.location) {
  const parts = loc.pathname.replace(/^\/+|\/+$/g, "").split("/").filter(Boolean);
  const first = parts[0] || "overview";
  const view  = KNOWN_VIEWS.has(first) ? first : "overview";
  return {
    view,
    path:   parts,           // full segments after host
    params: parts.slice(1),  // segments after the view
    search: new URLSearchParams(loc.search),
    hash:   loc.hash.slice(1),
  };
}

function navigate(path, { replace = false } = {}) {
  const target = path.startsWith("/") ? path : "/" + path;
  if (target === location.pathname + location.search + location.hash) return;
  if (replace) history.replaceState({}, "", target);
  else         history.pushState({}, "", target);
  window.dispatchEvent(new PopStateEvent("popstate"));
}
window.navigate = navigate;

function useRoute() {
  const [route, setRoute] = useS(() => parseRoute());
  useE(() => {
    const onPop = () => setRoute(parseRoute());
    window.addEventListener("popstate", onPop);
    return () => window.removeEventListener("popstate", onPop);
  }, []);
  return route;
}
window.useRoute = useRoute;

const NAV = [
  { id: "overview", label: "Overview",     icon: IconHome },
  { id: "library",  label: "Brand Library",icon: IconLibrary },
  { id: "studio",   label: "Studio",       icon: IconSpark },
  { id: "content",  label: "Content Plan", icon: IconCalendar },
  { id: "insights", label: "Insights",     icon: IconChart },
];

const SECONDARY = [
  { id: "people",   label: "Personer", icon: IconUsers },
  { id: "settings", label: "Settings", icon: IconGear  },
];
// Visible only when the active user has the global super-admin flag.
const SUPER_NAV = [
  { id: "admin",    label: "Admin",    icon: IconShield },
];

// ──────────────────────────────────────────
// Active user — drives permission gating across the app.
// ──────────────────────────────────────────
const UserCtx = createContext(null);
window.UserCtx = UserCtx;
window.useUser  = () => useContext(UserCtx);
window.useCan   = (perm) => {
  const u = useContext(UserCtx);
  return u ? personCan(u, perm) : false;
};

function SectionHeader({ label, open, onToggle }) {
  return (
    <button
      type="button"
      className="sb-section-header"
      aria-expanded={open}
      onClick={onToggle}
    >
      <span>{label}</span>
      <svg className="chev" width="10" height="10" viewBox="0 0 10 10" aria-hidden="true">
        <path d="M2 3.5 L5 6.5 L8 3.5" fill="none" stroke="currentColor" strokeWidth="1.4" strokeLinecap="round" strokeLinejoin="round" />
      </svg>
    </button>
  );
}

function Sidebar({ view, setView }) {
  const t = useContext(TweakCtx);
  const u = useContext(UserCtx);
  // Persist per-user (or per-client when anon) so toggling a section sticks
  // across reloads. Different users on the same machine keep separate state.
  const sidebarKey = `bos.sidebar.open.${u?.user?.id || "anon"}`;
  const [open, setOpen] = usePersistedState(sidebarKey, { workspace: false, brand: false, system: false, admin: false });
  const toggle = (k) => setOpen((o) => ({ ...o, [k]: !o[k] }));

  const renderLink = (n) => {
    const Ico = n.icon;
    const active = view === n.id;
    return (
      <button key={n.id}
              className={"sb-link " + (active ? "is-active" : "")}
              onClick={() => setView(n.id)}>
        <Ico className="ico" />
        <span>{n.label}</span>
      </button>
    );
  };

  return (
    <aside className="sidebar" data-screen-label="sidebar">
      <SidebarBrand />


      <div className="sb-workspace">
        <div className="ws">
          <span className="ws-dot" />
          <span className="ws-name">Brightnest</span>
        </div>
        <span className="ws-role">v 4.2</span>
      </div>

      <div className={"sb-section " + (open.workspace ? "is-open" : "is-closed")}>
        <SectionHeader label="Workspace" open={open.workspace} onToggle={() => toggle("workspace")} />
        <div className="sb-section-body" aria-hidden={!open.workspace}>
          <nav className="sb-nav" inert={open.workspace ? undefined : ""}>{NAV.map(renderLink)}</nav>
        </div>
      </div>

      {t.showBrandSubnav && (
        <div className={"sb-section " + (open.brand ? "is-open" : "is-closed")}>
          <SectionHeader label="Brand" open={open.brand} onToggle={() => toggle("brand")} />
          <div className="sb-section-body" aria-hidden={!open.brand}>
            <nav className="sb-nav" inert={open.brand ? undefined : ""}>
              {/* Each Brand sub-link routes to the matching Library tab so
                  the URL bar reflects what's open and the tab highlights
                  match. The Logos link is also the default Library page,
                  so it stays at /library without a trailing slug. */}
              <button className="sb-link" onClick={() => navigate("/library")}>
                <IconBookmark className="ico" /><span>Logos &amp; marks</span>
              </button>
              <button className="sb-link" onClick={() => navigate("/library/color")}>
                <IconPalette className="ico" /><span>Color</span>
              </button>
              <button className="sb-link" onClick={() => navigate("/library/type")}>
                <IconType className="ico" /><span>Type</span>
              </button>
              <button className="sb-link" onClick={() => navigate("/library/imagery")}>
                <IconImage className="ico" /><span>Imagery</span>
              </button>
              <button className="sb-link" onClick={() => navigate("/library/voice")}>
                <IconMic className="ico" /><span>Voice &amp; tone</span>
              </button>
            </nav>
          </div>
        </div>
      )}

      <div className={"sb-section " + (open.system ? "is-open" : "is-closed")}>
        <SectionHeader label="System" open={open.system} onToggle={() => toggle("system")} />
        <div className="sb-section-body" aria-hidden={!open.system}>
          <nav className="sb-nav" inert={open.system ? undefined : ""}>
            {SECONDARY.map(renderLink)}
          </nav>
        </div>
      </div>

      {/* Admin is its own collapsible section, but only super-admins ever see
          it. The server enforces this independently on every /api/admin/* call. */}
      {u?.user?.isSuperAdmin && (
        <div className={"sb-section " + (open.admin ? "is-open" : "is-closed")}>
          <SectionHeader label="Admin" open={open.admin} onToggle={() => toggle("admin")} />
          <div className="sb-section-body" aria-hidden={!open.admin}>
            <nav className="sb-nav" inert={open.admin ? undefined : ""}>
              {SUPER_NAV.map(renderLink)}
            </nav>
          </div>
        </div>
      )}

      <SidebarFoot />
    </aside>
  );
}

// Top of the sidebar: real logo when the brand profile is hydrated, with the
// "· Brand OS" suffix in display-italic so we keep the workspace identity.
function SidebarBrand() {
  const u = useContext(UserCtx);
  const logo = u?.brand?.profile?.logoUrl;
  return (
    <div className="sb-brand">
      {logo
        ? <img src={logo} alt="Brightnest" className="sb-logo" />
        : <><div className="sb-mark">B</div><span className="sb-name">Bright<em>nest</em></span></>}
      <span className="sb-suffix">Brand &amp; Marketing</span>
    </div>
  );
}

function SidebarFoot() {
  const u = useContext(UserCtx);
  if (!u) return null;
  return (
    <div className="sb-foot">
      <div className="sb-avatar">{u.user.initials}</div>
      <div className="sb-user">
        <span className="nm">{u.user.name}</span>
        <span className="em">{u.role?.name || ""}</span>
      </div>
      <button type="button" className="gear" onClick={u.logout} title="Logga ut" aria-label="Logga ut">
        <IconArrowR size={14} />
      </button>
    </div>
  );
}

const CRUMBS = {
  overview: ["Workspace", "Overview"],
  library:  ["Workspace", "Brand Library"],
  studio:   ["Workspace", "Studio"],
  content:  ["Workspace", "Content Plan"],
  insights: ["Workspace", "Insights"],
  people:   ["System", "Personer"],
  settings: ["System", "Settings"],
  admin:    ["System", "Admin"],
};

function Topbar({ view, setView }) {
  const t = useContext(TweakCtx);
  const crumbs = CRUMBS[view] || ["Workspace"];
  const primaryActions = {
    overview: { lbl: "New brief",      go: "studio" },
    library:  { lbl: "Add asset",      go: "library" },
    studio:   { lbl: "Save draft",     go: "content" },
    // Content Plan: open the new-post picker (manual vs Studio) by setting
    // ?new=1 — ContentPlanView shows the modal when that flag is present.
    content:  { lbl: "Schedule post",  url: "/content?new=1" },
    insights: { lbl: "Export report",  go: "insights" },
    people:   { lbl: "Bjud in",        go: "people" },
    settings: { lbl: "Spara",          go: "settings" },
    admin:    { lbl: "Ny organisation",url: "/admin?new=1" },
  };
  const a = primaryActions[view] || { lbl: "Skapa", go: view };
  const onPrimary = () => a.url ? navigate(a.url) : setView(a.go);

  return (
    <div className="topbar">
      <div className="crumbs">
        {crumbs.map((c, i) => (
          <React.Fragment key={i}>
            <span className={i === crumbs.length - 1 ? "cur" : ""}>{c}</span>
            {i < crumbs.length - 1 && <span className="sep">/</span>}
          </React.Fragment>
        ))}
      </div>

      {t.showSearch && (
        <div className="cmdbar">
          <IconSearch size={12} />
          <span>Search brand, assets, briefs…</span>
          <kbd>⌘K</kbd>
        </div>
      )}

      <div className="topbar-actions">
        <button className="tb-btn is-primary" onClick={onPrimary}>
          <IconPlus size={12} stroke="#fff" /> {a.lbl}
        </button>
      </div>
    </div>
  );
}

// ──────────────────────────────────────────
// "Visa som" — switches the active user so role-based gating
// can be demoed without real auth.
// ──────────────────────────────────────────
function ViewAsSwitcher() {
  const u = useContext(UserCtx);
  const [open, setOpen] = useS(false);
  const ref = React.useRef(null);
  useE(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    const onKey = (e) => { if (e.key === "Escape") setOpen(false); };
    document.addEventListener("mousedown", onDoc);
    document.addEventListener("keydown", onKey);
    return () => { document.removeEventListener("mousedown", onDoc); document.removeEventListener("keydown", onKey); };
  }, [open]);
  if (!u) return null;
  // group people by org for the menu
  const grouped = {};
  for (const p of u.people) (grouped[p.org] ??= []).push(p);
  return (
    <div className="view-as" ref={ref}>
      <button type="button" className="view-as-btn" onClick={() => setOpen(o => !o)}>
        <span className="view-as-label">Visa som</span>
        <span className="view-as-avatar">{u.user.initials}</span>
        <span className="view-as-meta">
          <span className="view-as-name">{u.user.name.split(" ")[0]}</span>
          <span className="view-as-role">{u.role?.name}</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="view-as-menu">
          {Object.entries(grouped).map(([org, list]) => (
            <div className="view-as-group" key={org}>
              <div className="view-as-org">{org}</div>
              {list.map(p => {
                const r = u.roles.find(rr => rr.id === p.roleId);
                return (
                  <button key={p.id} type="button"
                    className={"dd-item " + (p.id === u.user.id ? "is-active" : "")}
                    onClick={() => { u.setActiveUserId(p.id); setOpen(false); }}>
                    <span className="person-avatar">{p.initials}</span>
                    <span className="view-as-row">
                      <span className="view-as-name">{p.name}</span>
                      <span className="view-as-role">{r?.name}</span>
                    </span>
                  </button>
                );
              })}
            </div>
          ))}
        </div>
      )}
    </div>
  );
}

// ──────────────────────────────────────────
// Tweaks panel
// ──────────────────────────────────────────
function TweaksUI({ tweaks, setTweak }) {
  return (
    <TweaksPanel title="Tweaks">
      <TweakSection label="Page header" />
      <TweakToggle label="Hero heading"   value={tweaks.showHero}        onChange={(v) => setTweak("showHero", v)} />
      <TweakToggle label="Subtitle"        value={tweaks.showSubtitle}    onChange={(v) => setTweak("showSubtitle", v)} />
      <TweakToggle label="Right meta"      value={tweaks.showHeadMeta}    onChange={(v) => setTweak("showHeadMeta", v)} />
      <TweakToggle label="Section eyebrows"value={tweaks.showEyebrows}    onChange={(v) => setTweak("showEyebrows", v)} />

      <TweakSection label="Topbar" />
      <TweakToggle label="Search bar"      value={tweaks.showSearch}      onChange={(v) => setTweak("showSearch", v)} />
      <TweakToggle label="Live status"     value={tweaks.showLivePill}    onChange={(v) => setTweak("showLivePill", v)} />
      <TweakToggle label="Export button"   value={tweaks.showExport}      onChange={(v) => setTweak("showExport", v)} />

      <TweakSection label="Sidebar" />
      <TweakToggle label="Item counts"     value={tweaks.showCounts}      onChange={(v) => setTweak("showCounts", v)} />
      <TweakToggle label="Brand sub-nav"   value={tweaks.showBrandSubnav} onChange={(v) => setTweak("showBrandSubnav", v)} />

      <TweakSection label="Overview" />
      <TweakToggle label="KPI bar"         value={tweaks.showKPIs}        onChange={(v) => setTweak("showKPIs", v)} />
      <TweakToggle label="Quick actions"   value={tweaks.showQuickActions}onChange={(v) => setTweak("showQuickActions", v)} />
      <TweakToggle label="Activity stream" value={tweaks.showActivity}    onChange={(v) => setTweak("showActivity", v)} />

      <TweakSection label="Presets" />
      <TweakButton label="Minimal" onClick={() => setTweak({
        showHero: false, showSubtitle: false, showHeadMeta: false, showEyebrows: false,
        showSearch: false, showLivePill: false, showExport: false,
        showCounts: false, showBrandSubnav: false,
        showKPIs: false, showQuickActions: false, showActivity: false,
      })} />
      <TweakButton label="Reset" onClick={() => setTweak({
        showHero: true, showSubtitle: true, showHeadMeta: true, showEyebrows: true,
        showSearch: true, showLivePill: true, showExport: true,
        showCounts: true, showBrandSubnav: true,
        showKPIs: true, showQuickActions: true, showActivity: true,
      })} />
    </TweaksPanel>
  );
}

function App() {
  const route = useRoute();
  const view = route.view;
  const setView = (id) => navigate(id === "overview" ? "/" : "/" + id);
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);

  // Auth + hydration ----------------------------------------------------------
  const [authState, setAuthState] = useS("checking"); // 'checking' | 'anon' | 'authed' | 'hydrating'
  const [me, setMe]               = useS(null);
  const [posts, setPosts]         = useS([]);
  const [people, setPeople]       = useS([]);
  const [roles, setRoles]         = useS([]);
  const [permissions, setPermissions] = useS([]);
  const [flows, setFlows]         = useS([]);
  const [platforms, setPlatforms] = useS([]);
  const [imagery, setImagery]     = useS([]);
  const [brand, setBrand]         = useS(null);
  const [agents, setAgents]       = useS([]);
  const [studioTemplates, setStudioTemplates] = useS([]);

  const refresh = async () => {
    const [p, pe, r, perms, f, pl, im, br, ag, st] = await Promise.all([
      api.get("/posts"), api.get("/people"), api.get("/roles"),
      api.get("/roles/permissions"), api.get("/flows"), api.get("/platforms"),
      api.get("/imagery"), api.get("/brand"), api.get("/agents"),
      api.get("/studio/templates"),
    ]);
    setPosts(p); setPeople(pe); setRoles(r); setPermissions(perms);
    setFlows(f); setPlatforms(pl); setImagery(im); setBrand(br); setAgents(ag);
    setStudioTemplates(st);
    window.__people = pe;     // legacy lookup cache for personById/peopleList
    window.__roles  = r;
  };

  // First load: check session, hydrate if authed.
  useE(() => {
    (async () => {
      try {
        const m = await api.get("/auth/me");
        setMe(m);
        setAuthState("hydrating");
        await refresh();
        setAuthState("authed");
      } catch (err) {
        if (err.status === 401) setAuthState("anon");
        else { console.error(err); setAuthState("anon"); }
      }
    })();
  }, []);

  // Tweak body data attributes (visibility toggles)
  useE(() => {
    const b = document.body;
    b.dataset.twHero     = tweaks.showHero      ? "on" : "off";
    b.dataset.twSubtitle = tweaks.showSubtitle  ? "on" : "off";
    b.dataset.twHeadmeta = tweaks.showHeadMeta  ? "on" : "off";
    b.dataset.twEyebrows = tweaks.showEyebrows  ? "on" : "off";
  }, [tweaks.showHero, tweaks.showSubtitle, tweaks.showHeadMeta, tweaks.showEyebrows]);

  useE(() => {
    const onKey = (e) => {
      const map = { "1":"overview","2":"library","3":"studio","4":"content","5":"insights" };
      if (map[e.key] && (e.metaKey || e.ctrlKey)) { e.preventDefault(); setView(map[e.key]); }
    };
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, []);

  // Mutation helpers — every write goes through the API and the canonical
  // server response replaces local state. Keeps client + DB in lockstep.
  const ctxApi = {
    async logout() {
      await api.post("/auth/logout", {});
      setMe(null); setAuthState("anon");
    },
    async refresh() { await refresh(); },

    // Posts
    async updatePost(id, patch) {
      // Capture the previous post so we can roll back if the server rejects
      // the patch (403, 500, validation). Without this, viewers who don't
      // have `edit` would see an illusion of a successful change.
      const prev = posts.find(p => p.id === id);
      setPosts(list => list.map(p => p.id === id ? { ...p, ...patch } : p));
      try {
        const updated = await api.patch("/posts/" + id, patch);
        setPosts(list => list.map(p => p.id === id ? updated : p));
        return updated;
      } catch (err) {
        if (prev) setPosts(list => list.map(p => p.id === id ? prev : p));
        if (err.status === 403) alert("Din roll saknar rättighet att ändra inlägget.");
        throw err;
      }
    },
    async createPost(payload) {
      const created = await api.post("/posts", payload);
      setPosts(prev => [created, ...prev]);
      return created;
    },
    async deletePost(id) {
      await api.delete("/posts/" + id);
      setPosts(prev => prev.filter(p => p.id !== id));
    },
    async approveStage(postId, stageSlug) {
      const post = posts.find(p => p.id === postId);
      if (!post) return;
      const approvals = { ...(post.approvals || {}), [stageSlug]: { by: me.user.id } };
      return await ctxApi.updatePost(postId, { approvals });
    },
    async revokeStageAndLater(postId, stageSlug) {
      const post = posts.find(p => p.id === postId);
      const flow = flows.find(f => f.id === post.flowId) || flows.find(f => f.isDefault) || flows[0];
      const order = flow?.stages.map(s => s.id) || [];
      const approvals = { ...(post.approvals || {}) };
      let drop = false;
      for (const sid of order) { if (sid === stageSlug) drop = true; if (drop) delete approvals[sid]; }
      return await ctxApi.updatePost(postId, { approvals });
    },

    // People
    async invitePerson(payload) {
      const r = await api.post("/people/invite", payload);
      setPeople(r.people); window.__people = r.people;
    },
    async updateMember(userId, patch) {
      const r = await api.patch("/people/" + userId, patch);
      setPeople(r.people); window.__people = r.people;
    },

    // Roles
    async createRole(name) {
      const r = await api.post("/roles", { name });
      setRoles(r.roles); window.__roles = r.roles;
      return r.roles;
    },
    async updateRole(roleId, patch) {
      const r = await api.patch("/roles/" + roleId, patch);
      setRoles(r.roles); window.__roles = r.roles;
    },

    // Flows
    async updateFlow(slug, patch) {
      const r = await api.patch("/flows/" + slug, patch);
      setFlows(r.flows);
    },

    // Platforms
    async updatePlatform(slug, patch) {
      const r = await api.patch("/platforms/" + slug, patch);
      setPlatforms(r.platforms);
    },

    // Imagery
    async createFolder(label) {
      const r = await api.post("/imagery/folders", { label });
      setImagery(r.imagery);
    },
    async renameFolder(slug, label) {
      const r = await api.patch("/imagery/folders/" + slug, { label });
      setImagery(r.imagery);
    },
    async deleteFolder(slug) {
      const r = await api.delete("/imagery/folders/" + slug);
      setImagery(r.imagery);
    },
    async deletePhotos(urls) {
      const fsUrls = urls.filter(u => u.startsWith("/assets/imagery/"));
      const blobUrls = urls.filter(u => u.startsWith("blob:"));
      for (const u of blobUrls) try { URL.revokeObjectURL(u); } catch {}
      if (!fsUrls.length) return;
      const r = await api.delete("/imagery/photos", { urls: fsUrls });
      setImagery(r.imagery);
    },
    async movePhotos(urls, toSlug) {
      const fsUrls = urls.filter(u => u.startsWith("/assets/imagery/"));
      if (!fsUrls.length) return;
      const r = await api.post("/imagery/photos/move", { urls: fsUrls, toSlug });
      setImagery(r.imagery);
    },
    async uploadPhotos(slug, files) {
      const r = await api.upload(`/imagery/folders/${slug}/upload`, files);
      setImagery(r.imagery);
    },

    // Brand
    async updateBrandColor(slug, patch) {
      const r = await api.patch("/brand/colors/" + slug, patch);
      setBrand(r.brand);
    },
    async updateBrandProfile(patch) {
      const r = await api.patch("/brand/profile", patch);
      setBrand(r.brand);
    },

    // Agents
    async updateAgent(id, patch) {
      const r = await api.patch("/agents/" + id, patch);
      setAgents(r.agents);
    },
    async runAgent(id, input) {
      return await api.post("/agents/" + id + "/run", { input });
    },

    // Studio templates
    async createStudioTemplate(payload) {
      const r = await api.post("/studio/templates", payload);
      setStudioTemplates(r.templates);
      return r.templates.find(t => t.name === payload.name && t.marks?.length === (payload.marks || []).length) || r.templates[0];
    },
    async deleteStudioTemplate(id) {
      const r = await api.delete("/studio/templates/" + id);
      setStudioTemplates(r.templates);
    },
  };

  const userCtx = me ? {
    user: me.user,
    role: me.role,
    perms: new Set(me.perms || []),
    can: (perm) => (me.perms || []).includes(perm),
    orgId:   me.orgId,
    orgName: (me.memberships || []).find(m => m.org_id === me.orgId)?.org_name || "Workspace",
    memberships: me.memberships || [],
    posts, setPosts,
    people, setPeople,
    roles,  setRoles,
    permissions,
    flows,  setFlows,
    platforms, setPlatforms,
    imagery, setImagery,
    brand,    setBrand,
    agents,   setAgents,
    studioTemplates,
    ...ctxApi,
  } : null;

  if (authState === "checking" || authState === "hydrating") {
    return <div className="app-loading">Laddar…</div>;
  }
  if (authState === "anon") {
    return <LoginScreen onAuthed={async () => {
      const m = await api.get("/auth/me"); setMe(m);
      setAuthState("hydrating"); await refresh(); setAuthState("authed");
    }} />;
  }

  return (
    <TweakCtx.Provider value={tweaks}>
      <UserCtx.Provider value={userCtx}>
        <div className="app" data-screen-label={`app · ${view}`}>
          <Sidebar view={view} setView={setView} />
          <main className="main">
            <Topbar view={view} setView={setView} />
            <div data-screen-label={`view · ${view}`}>
              {view === "overview" && <OverviewView goto={setView} />}
              {view === "library"  && <LibraryView />}
              {view === "studio"   && <StudioView />}
              {view === "content"  && <ContentPlanView />}
              {view === "insights" && <InsightsView />}
              {view === "people"   && <PeopleView />}
              {view === "settings" && <SettingsView />}
              {view === "admin"    && <AdminView />}
            </div>
          </main>
          <TweaksUI tweaks={tweaks} setTweak={setTweak} />
        </div>
      </UserCtx.Provider>
    </TweakCtx.Provider>
  );
}

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