/* NutriDMS, Dashboard (role-aware, customizable, proactive Loraa) */
const { useState: useState_d, useMemo: useMemo_d, useEffect: useEffect_d, useRef: useRef_d } = React;

function openLoraaCommandCenter() {
  if (typeof window.__openLoraa === "function") window.__openLoraa();
  else window.dispatchEvent(new CustomEvent("nutridms-loraa-open"));
}

/* Persisted per-role widget layout */
function dashKey(role) { return "nutridms_dash_v2_" + role; }
function loadDashLayout(role, allIds) {
  try { const r = localStorage.getItem(dashKey(role)); if (r) { const s = JSON.parse(r); return allIds.filter((id) => s.includes(id)); } } catch (e) {}
  return allIds.slice();
}
function saveDashLayout(role, ids) { try { localStorage.setItem(dashKey(role), JSON.stringify(ids)); } catch (e) {} }

function dashboardRows(payload) {
  if (Array.isArray(payload)) return payload;
  if (payload && Array.isArray(payload.results)) return payload.results;
  return payload && Array.isArray(payload.members) ? payload.members : [];
}

function dashboardComplianceValue(snapshot) {
  if (snapshot && snapshot.serverAuthoritative) {
    const value = snapshot.compliancePercent;
    return value == null || !Number.isFinite(Number(value)) ? "—" : Math.round(Number(value)) + "%";
  }
  const records = Number(snapshot.recipes || 0) + Number(snapshot.ingredients || 0);
  const checks = Array.isArray(snapshot.checks) ? snapshot.checks : [];
  if (!records || !checks.length) return "—";
  const passed = checks.filter((check) => ["passed", "approved", "compliant"].includes(String(check.status || check.result || "").toLowerCase())).length;
  return Math.round((passed / checks.length) * 100) + "%";
}

function dashboardDate(value) {
  if (!value || value === "—") return null;
  const parsed = new Date(value);
  return Number.isFinite(parsed.getTime()) ? parsed : null;
}

function dashboardInitials(name) {
  return String(name || "Team member").trim().split(/\s+/).filter(Boolean).slice(0, 2).map((part) => part[0]).join("").toUpperCase() || "TM";
}

function dashboardWorkflowTotals(records) {
  const totals = { draft: 0, review: 0, approved: 0, published: 0 };
  (records || []).forEach((record) => {
    const status = String(record && record.status || "draft");
    if (status === "published") totals.published += 1;
    else if (status === "approved") totals.approved += 1;
    else if (["pending-review", "compliance-review", "changes-requested", "review"].includes(status)) totals.review += 1;
    else totals.draft += 1;
  });
  return totals;
}

function dashboardPercent(ready, total) {
  return total > 0 ? Math.round((ready / total) * 100) : null;
}

function dashboardTenantMode() {
  let embedded = false;
  let deployed = false;
  try { embedded = window.self !== window.top; } catch (e) { embedded = true; }
  try { deployed = /(^|\.)nutridms-platform[^.]*\./i.test(window.location.hostname || ""); } catch (e) {}
  const apiConnected = !!(window.NutriData && window.NutriData.isConnected && window.NutriData.isConnected());
  return !!(embedded || deployed || apiConnected || window.__nutridmsAuthenticatedUser || window.__nutridmsActiveOrganizationId);
}

function dashboardTenantRows(rows) {
  const list = Array.isArray(rows) ? rows : [];
  return dashboardTenantMode() ? list.filter((row) => row && row.__remote) : list;
}

function Dashboard() {
  const { role, setPage, openRecipe } = useApp();
  const [, setDataTick] = useState_d(0);
  const recipes = dashboardTenantRows(typeof RECIPES !== "undefined" ? RECIPES : []);
  const ingredients = dashboardTenantRows(typeof INGREDIENT_ITEMS !== "undefined" ? INGREDIENT_ITEMS : []);
  const [liveSnapshot, setLiveSnapshot] = useState_d(() => ({
    connected: dashboardTenantMode(),
    apiConnected: !!(window.NutriData && window.NutriData.isConnected && window.NutriData.isConnected()),
    users: window.__nutridmsAuthenticatedUser ? 1 : 0,
    recipes: recipes.length,
    ingredients: ingredients.length,
    members: [],
    rules: [],
    checks: [],
    review: 0,
    compliancePercent: null,
    serverAuthoritative: false,
    generatedAt: null,
  }));
  useEffect_d(() => {
    let active = true;
    const fallback = () => ({
      connected: dashboardTenantMode(),
      apiConnected: !!(window.NutriData && window.NutriData.isConnected && window.NutriData.isConnected()),
      users: window.__nutridmsAuthenticatedUser ? 1 : 0,
      recipes: dashboardTenantRows(window.RECIPES).length,
      ingredients: dashboardTenantRows(window.INGREDIENT_ITEMS).length,
      members: [], rules: [], checks: [], review: null, compliancePercent: null,
      serverAuthoritative: false, generatedAt: null,
    });
    const loadSnapshot = async () => {
      const apiConnected = !!(window.NutriData && window.NutriData.isConnected && window.NutriData.isConnected());
      if (!apiConnected || !window.NutriAPI) return fallback();
      const payload = await window.NutriAPI.get("/organizations/dashboard/");
      const counts = payload && payload.counts ? payload.counts : {};
      return {
        connected: true,
        apiConnected: apiConnected || true,
        users: Number(counts.active_users || 0),
        recipes: Number(counts.recipes || 0),
        ingredients: Number(counts.ingredients || 0),
        members: [], rules: [], checks: [],
        review: Number(counts.nutrition_review || 0),
        compliancePercent: payload && payload.compliance_percent != null ? Number(payload.compliance_percent) : null,
        plan: payload && payload.plan ? payload.plan : null,
        features: payload && Array.isArray(payload.features) ? payload.features : [],
        serverAuthoritative: true,
        generatedAt: payload && payload.generated_at ? payload.generated_at : new Date().toISOString(),
      };
    };
    const applySnapshot = (next) => {
      if (active) {
        setLiveSnapshot(next);
        setDataTick((tick) => tick + 1);
      }
    };
    const refresh = async (force) => {
      const coordinator = window.NutriWorkspaceRefresh;
      try {
        const next = coordinator && coordinator.fetch
          ? await coordinator.fetch("dashboard:summary", loadSnapshot, { scope: "org", ttlMs: coordinator.DEFAULT_TTL_MS, force: force === true })
          : await loadSnapshot();
        applySnapshot(next);
      } catch (error) {
        applySnapshot(Object.assign({}, fallback(), { error: error && error.message ? error.message : "Live dashboard unavailable" }));
      }
    };
    const coordinator = window.NutriWorkspaceRefresh;
    if (coordinator && coordinator.register) {
      coordinator.register("dashboard:summary", loadSnapshot, { scope: "org", onValue: applySnapshot, active: true });
      coordinator.activate("dashboard:summary", true);
    }
    const onData = () => { refresh(false); };
    refresh(false);
    ["nutridms-backend", "nutridms-recipes", "nutridms-ingredients", "nutridms-compliance", "nutridms-entitlements"].forEach((name) => window.addEventListener(name, onData));
    return () => {
      active = false;
      if (coordinator && coordinator.activate) coordinator.activate("dashboard:summary", false);
      ["nutridms-backend", "nutridms-recipes", "nutridms-ingredients", "nutridms-compliance", "nutridms-entitlements"].forEach((name) => window.removeEventListener(name, onData));
    };
  }, []);
  const currentSnapshot = { ...liveSnapshot };
  const config = dashboardConfig(role, currentSnapshot);
  const recent = recipes.slice(0, 3);
  const queue = recipes.filter(r => ["pending-review", "compliance-review", "changes-requested"].includes(r.status)).slice(0, 5);
  const [authenticatedUser, setAuthenticatedUser] = useState_d(() => window.__nutridmsAuthenticatedUser || null);
  useEffect_d(() => {
    const syncUser = (event) => {
      const next = (event && event.detail) || window.__nutridmsAuthenticatedUser || null;
      if (next && next.name) setAuthenticatedUser(next);
    };
    syncUser();
    window.addEventListener("nutridms-user", syncUser);
    window.addEventListener("nutridms-backend", syncUser);
    return () => {
      window.removeEventListener("nutridms-user", syncUser);
      window.removeEventListener("nutridms-backend", syncUser);
    };
  }, []);
  const registeredUser = authenticatedUser || (typeof currentUser === "function" ? currentUser(role) : null);
  const firstName = registeredUser && registeredUser.name ? registeredUser.name.trim().split(/\s+/)[0] : "there";
  const [range, setRange] = useState_d("30d");
  const [updatedAt, setUpdatedAt] = useState_d(new Date());
  useEffect_d(() => { const id = setInterval(() => setUpdatedAt(new Date()), 60000); return () => clearInterval(id); }, []);
  useEffect_d(() => {
    const id = "nutridms-dashboard-premium-css";
    if (document.getElementById(id)) return;
    const core = document.querySelector('link[href*="app-source/styles.css"], link[href$="/styles.css"]');
    const source = Array.from(document.scripts || []).find((script) => /app-source\/screens\/dashboard\.(jsx|js)(\?|$)/.test(script.src || ""));
    const href = core && core.href
      ? core.href.replace(/styles\.css(?:\?.*)?$/, "dashboard-premium.css?v=20260804-reference-ui-5")
      : source && source.src
        ? source.src.replace(/screens\/dashboard\.(jsx|js)(?:\?.*)?$/, "dashboard-premium.css?v=20260804-reference-ui-5")
        : "app-source/dashboard-premium.css?v=20260804-reference-ui-5";
    const link = document.createElement("link");
    link.id = id;
    link.rel = "stylesheet";
    link.href = href;
    document.head.appendChild(link);
  }, []);
  const greeting = (() => { const h = new Date().getHours(); return h < 12 ? "Good morning" : h < 18 ? "Good afternoon" : "Good evening"; })();
  const roleLabel = (registeredUser && (registeredUser.roleLabel || registeredUser.role)) || ((typeof ROLES !== "undefined" && ROLES[role]) ? ROLES[role].label : role);
  const REVIEW = ["pending-review", "compliance-review", "changes-requested"];
  let unclaimed = 0;
  let reviewItemCount = 0;
  try {
    const reviewRecipes = dashboardTenantRows(window.RECIPES).filter((record) => REVIEW.includes(record.status));
    const reviewIngredients = dashboardTenantRows(window.INGREDIENT_ITEMS).filter((record) => REVIEW.includes(record.status));
    reviewItemCount = reviewRecipes.length + reviewIngredients.length;
    unclaimed = reviewRecipes.filter((record) => !record.reviewer).length + reviewIngredients.filter((record) => !record.reviewer).length;
  } catch (e) {}
  if (currentSnapshot.serverAuthoritative && Number.isFinite(Number(currentSnapshot.review))) {
    reviewItemCount = Number(currentSnapshot.review);
  }

  const WIDGETS = [
    { id: "stats", title: "Key metrics", icon: "layout-grid", full: true, render: () => <DashboardMetrics stats={config.stats} setPage={setPage} live={currentSnapshot.connected} /> },
    { id: "velocity", title: "Workflow velocity", icon: "chart-no-axes-combined", span: 8, render: () => <DashboardVelocity records={recipes.concat(ingredients)} range={range} setRange={setRange} setPage={setPage} /> },
    { id: "pipeline", title: "Pipeline mix", icon: "pie-chart", span: 4, render: () => <PipelineCard records={recipes} /> },
    { id: "attention", title: "Needs your attention", icon: "alert-circle", span: 7, render: () => <AttentionCard queue={queue} recent={recent} openRecipe={openRecipe} setPage={setPage} /> },
    { id: "weekly", title: "This week", icon: "calendar-days", span: 5, render: () => <DashboardWeek records={recipes.concat(ingredients)} unclaimed={unclaimed} setPage={setPage} /> },
    { id: "features", title: "Platform coverage", icon: "blocks", full: true, render: () => <DashboardFeaturePulse recipes={recipes} reviewCount={reviewItemCount} snapshot={currentSnapshot} setPage={setPage} /> },
    { id: "roi", title: "Operational value", icon: "badge-dollar-sign", span: 6, render: () => <DashboardROI snapshot={currentSnapshot} setPage={setPage} /> },
    { id: "readiness", title: "Production readiness", icon: "radar", span: 6, render: () => <DashboardReadiness recipes={recipes} ingredients={ingredients} snapshot={currentSnapshot} setPage={setPage} /> },
    { id: "list", title: config.listTitle, icon: "list", span: 7, render: () => (
      <div className="card pad dash-widget-card dash-list-card">
        <DashboardCardHead title={config.listTitle} sub={config.listSub} action="View all" onAction={() => setPage(config.listTo)} />
        <div className="col dash-list-stack">
          {(config.listKind === "queue" ? queue : recent).map((r) => <DashRow key={r.id} recipe={r} onOpen={() => openRecipe(r)} kind={config.listKind} />)}
          {(config.listKind === "queue" ? queue : recent).length === 0 && <DashboardEmpty />}
        </div>
      </div>
    ) },
    { id: "quickactions", title: "Quick actions", icon: "zap", span: 5, render: () => <QuickActionsCard actions={config.actions} setPage={setPage} /> },
    { id: "feed", title: "Activity feed", icon: "rss", span: 7, render: () => (
      <div className="card pad dash-widget-card dash-activity-card">
        <DashboardCardHead title="Activity feed" sub="Live work across your organization" action="Audit log" onAction={() => setPage("audit")} />
        <DashboardActivity records={recipes.concat(ingredients)} openRecipe={openRecipe} setPage={setPage} />
      </div>
    ) },
    { id: "team", title: "Team capacity", icon: "users-round", span: 5, render: () => <DashboardTeam snapshot={currentSnapshot} queue={queue} setPage={setPage} /> },
    { id: "deadlines", title: "Upcoming deadlines", icon: "calendar-clock", span: 4, render: () => <DashboardDeadlines setPage={setPage} /> },
    { id: "compliance", title: "Compliance snapshot", icon: "shield-check", span: 4, render: () => <DashboardCompliance snapshot={currentSnapshot} setPage={setPage} /> },
    { id: "shortcuts", title: "Loraa recommendations", icon: "sparkles", span: 4, render: () => <DashboardRecommendations role={role} setPage={setPage} /> },
  ];
  const AVAILABLE_WIDGETS = WIDGETS;
  const allIds = AVAILABLE_WIDGETS.map((w) => w.id);

  const [layout, setLayout] = useState_d(() => loadDashLayout(role, allIds));
  const [customizing, setCustomizing] = useState_d(false);
  useEffect_d(() => { setLayout(loadDashLayout(role, allIds)); }, [role]);
  const setAndSave = (ids) => { setLayout(ids); saveDashLayout(role, ids); };
  const removeW = (id) => setAndSave(layout.filter((x) => x !== id));
  const addW = (id) => { const next = allIds.filter((x) => layout.includes(x) || x === id); setAndSave(next); };
  const available = AVAILABLE_WIDGETS.filter((w) => !layout.includes(w.id));

  const active = layout.map((id) => AVAILABLE_WIDGETS.find((w) => w.id === id)).filter(Boolean);
  const fullW = active.filter((w) => w.full);
  const gridW = active.filter((w) => !w.full);

  return (
    <div className="premium-dashboard">
      <Crumbs path={[{ label: "Dashboard" }]} />
      <div className="page-head dash-page-head">
        <div className="dash-welcome">
          <div className="dash-live-kicker"><span className="dash-live-dot" /> Live workspace <span>·</span> {roleLabel}</div>
          <h1 className="page-title">{greeting}, {firstName}</h1>
          <p className="page-sub">{config.sub}</p>
        </div>
        <div className="dash-head-actions">
          {config.actions.map((a, i) => (
            a.href
              ? <a key={i} href={a.href} className={`btn ${a.variant || "primary"}`} style={{ textDecoration: "none" }}><Icon name={a.icon} size={16} stroke={2.2} /> {a.label}</a>
              : <button key={i} className={`btn ${a.variant || "primary"}`} onClick={() => setPage(a.to)}><Icon name={a.icon} size={16} stroke={2.2} /> {a.label}</button>
          ))}
          <button className={`btn ${customizing ? "primary" : "secondary"}`} onClick={() => setCustomizing((c) => !c)}>
            <Icon name={customizing ? "check" : "layout-dashboard"} size={16} stroke={2.2} /> {customizing ? "Done" : "Customize Dashboard"}
          </button>
        </div>
      </div>

      <div className="dash-brief" role="status">
        <div className="dash-brief-orb"><img src="assets/loraa-logo.png" alt="" /></div>
        <div className="dash-brief-copy">
          <span className="dash-brief-eyebrow">Your operating brief</span>
          <strong>{config.brief || `${queue.length} items are moving through your workspace.`}</strong>
          <p>{config.briefSub || "Loraa is monitoring recipes, ingredients, labels, approvals and compliance rules for changes."}</p>
        </div>
        <div className="dash-brief-signals">
          <span><i className="ok" /> Compliance {dashboardComplianceValue(currentSnapshot)}</span>
          <span><i className="live" /> {reviewItemCount} in review</span>
          <span><i className="info" /> {currentSnapshot.apiConnected ? "Loraa operations live" : "Secure data connection pending"}</span>
        </div>
        <button className="dash-brief-open" onClick={openLoraaCommandCenter}>Open Loraa <Icon name="arrow-up-right" size={14} /></button>
        <time>{updatedAt.toLocaleTimeString([], { hour: "numeric", minute: "2-digit" })}</time>
      </div>

      {customizing && (
        <div className="dash-customize">
          <div className="dash-customize-h"><Icon name="sliders-horizontal" size={15} /> Customize your dashboard, remove cards with ✕, or add more cards below.</div>
          {available.length > 0 ? (
            <div className="dash-add-row">
              {available.map((w) => <button key={w.id} className="dash-add-chip" onClick={() => addW(w.id)}><Icon name="plus" size={13} /> <Icon name={w.icon} size={13} /> {w.title}</button>)}
            </div>
          ) : <span className="dash-add-empty">All cards are shown.</span>}
          <button className="dash-reset" onClick={() => setAndSave(allIds)}>Reset to default</button>
        </div>
      )}

      {fullW.map((w) => (
        <div key={w.id} className={`dash-w ${customizing ? "editing" : ""}`} style={{ marginBottom: 20 }}>
          {customizing && <button className="dash-w-x" onClick={() => removeW(w.id)} title="Remove card"><Icon name="x" size={14} /></button>}
          {w.render()}
        </div>
      ))}

      <div className="dash-grid dash-grid-premium">
        {gridW.map((w) => (
          <div key={w.id} className={`dash-w dash-span-${w.span || 6} ${customizing ? "editing" : ""}`}>
            {customizing && <button className="dash-w-x" onClick={() => removeW(w.id)} title="Remove card"><Icon name="x" size={14} /></button>}
            {w.render()}
          </div>
        ))}
      </div>
    </div>
  );
}

/* ── Global Loraa assistant — MOVED to screens/loraa-center.jsx (AI Operations
   Center). window.LoraaAsk is defined there and loaded after this file. The old
   chat-modal implementation below is retained only as a disabled reference and
   is never rendered (the later definition wins). ── */
function LoraaAsk_LEGACY_UNUSED({ role, open, onClose }) {
  const loraaLoadHistory = () => [];
  const loraaSaveHistory = () => {};
  const [history, setHistory] = useState_d(loraaLoadHistory);
  const [activeId, setActiveId] = useState_d(null);   // null = new proactive chat
  const [msgs, setMsgs] = useState_d([]);
  const [typing, setTyping] = useState_d(false);
  const [input, setInput] = useState_d("");
  const bodyRef = useRef_d(null);
  const queue = useMemo_d(() => (typeof RECIPES !== "undefined" ? RECIPES.filter(r => ["pending-review", "compliance-review", "changes-requested"].includes(r.status)).slice(0, 5) : []), []);
  const name = (typeof currentUser === "function" && currentUser(role) && currentUser(role).name) ? currentUser(role).name.split(" ")[0] : "there";
  const hour = new Date().getHours();
  const greet = hour < 12 ? "Good morning" : hour < 18 ? "Good afternoon" : "Good evening";

  const ingCount = useMemo_d(() => (typeof INGREDIENTS !== "undefined" ? Math.min(24, INGREDIENTS.length) : 12), []);
  const activity = useMemo_d(() => ([
    { title: `Mapped ${ingCount} new ingredients`, sub: "to the verified master library", tag: "Auto-mapped", tone: "ok", state: "done", prompt: `Show me the ${ingCount} ingredients you auto-mapped` },
    { title: "Calculated nutrition", sub: "8 recipes · per serving + per 100g", tag: "Done", tone: "ok", state: "done", prompt: "Which recipes did you calculate nutrition for?" },
    { title: "Flagged 2 allergen conflicts", sub: "milk + wheat across 3 products", tag: "Escalated", tone: "warn", state: "done", prompt: "Explain the 2 allergen conflicts you flagged" },
    { title: "Drafted a CFIA label", sub: "ingredient statement generated", tag: "Ready to review", tone: "info", state: "done", prompt: "Show me the CFIA label you drafted" },
    { title: "Ran compliance checks", sub: "against your nutrient policies", tag: "Passed", tone: "ok", state: "done", prompt: "What compliance checks did you run?" },
    { title: `Routed ${queue.length || 5} products`, sub: "to the right staff for approval", tag: "Working…", tone: "muted", state: "working", prompt: "Which products did you route, and to whom?" },
  ]), [ingCount, queue.length]);

  const suggestions = useMemo_d(() => ([
    "What needs my attention today?",
    "Summarize my pipeline",
    role === "compliance" || role === "super-admin" ? "Which items are highest risk?" : "How is my review time trending?",
    "Draft a status update for my team",
  ]), [role]);

  const answerFor = (q) => {
    const s = q.toLowerCase();
    if (/attention|today|urgent/.test(s)) return `You have ${queue.length} item${queue.length === 1 ? "" : "s"} in the pipeline. ${queue[0] ? `“${queue[0].name}” is highest priority (${queue[0].priority}). ` : ""}I'd start there, want me to open it?`;
    if (/pipeline|summar/.test(s)) return `Your pipeline: ${queue.length} in review, and submissions are trending up. Nothing is overdue. The busiest stage is compliance review.`;
    if (/risk|highest/.test(s)) return `The highest-risk item is a recipe missing allergen tags. Two others have unverified nutrition sources. I can route them to a reviewer for you.`;
    if (/review time|trend/.test(s)) return `Your average review time is down 12% this month, faster than last. Approvals are your strongest stage.`;
    if (/draft|status|update/.test(s)) return `Here's a draft: “The team currently has ${queue.length} recipe${queue.length === 1 ? "" : "s"} in review. Compliance status should be reported only from completed tenant checks.” Want me to refine the tone?`;
    if (/auto-mapped|mapped/.test(s)) return `I matched ${ingCount} newly-added ingredients to your verified master library by name, CAS and synonyms, all above 95% confidence, so no manual review was needed. They're live in the Ingredient Library now.`;
    if (/calculate|calculated nutrition/.test(s)) return `I recalculated nutrition for 8 recipes, generating both per-serving and per-100g panels from your portion data. Two recipes shifted category thresholds, I've noted those on their compliance tabs.`;
    if (/allergen conflict|milk|wheat/.test(s)) return `Two recipes declare milk + wheat that appear across 3 shared products without a precautionary statement. I escalated both to Compliance and tagged the affected products. Want me to open the first one?`;
    if (/cfia label|drafted/.test(s)) return `I generated a CFIA-format ingredient statement (descending order by weight, allergens bolded, “Contains” line built). It's marked Ready to review in Label Studio, you just need to approve it.`;
    if (/compliance check|checks did/.test(s)) return `I ran your org's nutrient policies against every active recipe: sodium, sat-fat, added-sugar thresholds and allergen completeness. All passed except the 2 allergen items I escalated.`;
    if (/routed|route/.test(s)) return `I routed ${queue.length || 5} products to the right reviewers based on your workflow rules, nutrition items to dietitians, label items to Compliance. They're waiting in each person's queue.`;
    return `I can help with your pipeline, compliance risk, review times, and drafting updates. Try one of the suggestions above.`;
  };

  const loadChat = (h) => { setActiveId(h.id); setMsgs(h.msgs || []); };
  const newChat = () => { setActiveId(null); setMsgs([]); setInput(""); };

  const ask = (q) => {
    if (!q.trim()) return;
    const userMsg = { role: "user", text: q };
    setInput(""); setTyping(true);
    let id = activeId;
    setMsgs((m) => {
      const next = [...m, userMsg];
      // persist into history (create a new thread on first message)
      setHistory((h) => {
        let list = h.slice();
        if (!id) {
          id = "c-" + Date.now();
          list = [{ id, title: q.slice(0, 34), when: "Now", msgs: next }, ...list];
        } else {
          list = list.map((x) => x.id === id ? { ...x, msgs: next } : x);
        }
        loraaSaveHistory(list);
        return list;
      });
      if (!activeId) setActiveId(id);
      return next;
    });
    setTimeout(() => {
      setTyping(false);
      const reply = { role: "loraa", text: answerFor(q) };
      setMsgs((m) => {
        const next = [...m, reply];
        setHistory((h) => { const list = h.map((x) => x.id === id ? { ...x, msgs: next } : x); loraaSaveHistory(list); return list; });
        return next;
      });
    }, 750);
  };
  useEffect_d(() => { if (bodyRef.current) bodyRef.current.scrollTop = bodyRef.current.scrollHeight; }, [msgs, typing]);
  useEffect_d(() => { const h = (e) => { if (e.key === "Escape" && open) onClose(); }; window.addEventListener("keydown", h); return () => window.removeEventListener("keydown", h); }, [open, onClose]);

  if (!open) return null;
  return (
    <>
      <div className="loraa-modal-scrim" onClick={onClose} />
      <div className="loraa-modal" role="dialog" aria-label="Loraa assistant">
        <aside className="loraa-modal-side">
          <div className="loraa-side-brand">
            <span className="loraa-orb sm"><img src="assets/loraa-logo.png" alt="" onError={(e) => { e.currentTarget.style.display = "none"; }} /></span>
            <span className="loraa-side-name">Loraa</span>
            <span className="loraa-side-status"><i /> Working</span>
          </div>
          <button className="loraa-newchat" onClick={newChat}><Icon name="plus" size={15} /> New chat</button>
          <div className="loraa-side-h">Recent</div>
          <div className="loraa-side-list">
            {history.map((h) => (
              <button key={h.id} className={`loraa-hist${activeId === h.id ? " on" : ""}`} onClick={() => loadChat(h)}>
                <Icon name="message-circle" size={14} />
                <span className="loraa-hist-t">{h.title}</span>
                <small>{h.when}</small>
              </button>
            ))}
          </div>
          <div className="loraa-side-foot">
            <div className="loraa-side-avs">
              {["AR", "JP", "MK", "LT", "DS"].map((a, i) => <span key={i} className={`loraa-av av-${i}`}>{a}</span>)}
            </div>
            <div className="loraa-side-work"><strong>Workload −38%</strong><span>this week, with Loraa</span></div>
          </div>
        </aside>
        <section className="loraa-modal-main">
          <header className="loraa-modal-h">
            <div className="loraa-modal-htitle">
              <span className="loraa-orb xs"><img src="assets/loraa-logo.png" alt="" onError={(e) => { e.currentTarget.style.display = "none"; }} /></span>
              <div><strong>Loraa</strong><span>Your AI teammate, proactive, always on</span></div>
            </div>
            <button className="loraa-modal-x" onClick={onClose} aria-label="Close"><Icon name="x" size={18} /></button>
          </header>
          <div className="loraa-modal-body" ref={bodyRef}>
            {msgs.length === 0 ? (
              <div className="loraa-proactive">
                <div className="loraa-greet">
                  <h3>{greet}, {name}</h3>
                  <p>Here's what I've handled for you today, so your team reviews and approves instead of doing it by hand.</p>
                </div>
                <div className="loraa-activity">
                  {activity.map((a, i) => (
                    <button className="loraa-act-row" key={i} style={{ animationDelay: (i * 60) + "ms" }} onClick={() => ask(a.prompt)}>
                      <span className={`loraa-act-ic ${a.state}`}>{a.state === "working" ? <span className="loraa-act-spin" /> : <Icon name="check" size={13} stroke={3} />}</span>
                      <div className="loraa-act-tx"><strong>{a.title}</strong><span>{a.sub}</span></div>
                      <span className={`loraa-act-pill ${a.tone}`}>{a.tag}</span>
                      <Icon name="chevron-right" size={15} className="loraa-act-chev" />
                    </button>
                  ))}
                </div>
                <div className="loraa-suggests-row">
                  {suggestions.map((q, i) => <button key={i} className="loraa-suggest" onClick={() => ask(q)}><Icon name="message-circle" size={14} /> {q}</button>)}
                </div>
              </div>
            ) : (
              <div className="loraa-thread">
                {msgs.map((m, i) => (
                  <div key={i} className={`loraa-msg ${m.role}`}>
                    {m.role === "loraa" && <span className="loraa-orb xs"><img src="assets/loraa-logo.png" alt="" onError={(e) => { e.currentTarget.style.display = "none"; }} /></span>}
                    <div className="loraa-bubble">{m.text}</div>
                  </div>
                ))}
                {typing && <div className="loraa-msg loraa"><span className="loraa-orb xs"><img src="assets/loraa-logo.png" alt="" onError={(e) => { e.currentTarget.style.display = "none"; }} /></span><div className="loraa-bubble loraa-typing"><i /><i /><i /></div></div>}
              </div>
            )}
          </div>
          <footer className="loraa-modal-foot">
            <div className="loraa-input">
              <input value={input} onChange={(e) => setInput(e.target.value)} onKeyDown={(e) => { if (e.key === "Enter") ask(input); }} placeholder="Ask Loraa, or / for actions" autoFocus />
              <button className="loraa-send" onClick={() => ask(input)} disabled={!input.trim()}><Icon name="arrow-up" size={16} stroke={2.4} /></button>
            </div>
            <div className="loraa-foot-note">Uses AI. Verify results.</div>
          </footer>
        </section>
      </div>
    </>
  );
}

function DashboardCardHead({ title, sub, action, onAction }) {
  return (
    <div className="dash-card-head">
      <div><h2 className="section-title" style={{ fontSize: 18 }}>{title}</h2>{sub && <p className="section-sub">{sub}</p>}</div>
      {action && <button className="dash-card-link" onClick={onAction}>{action} <Icon name="arrow-right" size={14} /></button>}
    </div>
  );
}

function DashboardSparkline({ values, tone = "green" }) {
  const max = Math.max.apply(null, values), min = Math.min.apply(null, values);
  const span = Math.max(1, max - min);
  const pts = values.map((v, i) => `${i * (92 / (values.length - 1)) + 2},${31 - ((v - min) / span) * 23}`).join(" ");
  return <svg className={`dash-spark ${tone}`} viewBox="0 0 96 36" preserveAspectRatio="none" aria-hidden="true"><polyline points={pts} fill="none" stroke="currentColor" strokeWidth="2.4" strokeLinecap="round" strokeLinejoin="round" /><circle cx="94" cy={31 - ((values[values.length - 1] - min) / span) * 23} r="2.7" fill="currentColor" /></svg>;
}

function DashboardMetrics({ stats, setPage, live }) {
  const series = [[4, 5, 4, 7, 6, 9, 10], [6, 6, 7, 5, 6, 7, 7], [3, 4, 4, 5, 7, 6, 8], [8, 7, 7, 6, 5, 5, 4]];
  return (
    <div className="stats dash-metrics">
      {stats.map((s, i) => (
        <button key={s.label} className={`stat dash-metric ${s.to ? "clickable" : ""}`} onClick={() => s.to && setPage(s.to)}>
          <div className="stat-head"><div className={`stat-icon ${s.tone || "brand"}`}><Icon name={s.icon} size={20} /></div>{s.trend && <span className={`dash-metric-trend ${s.trend.direction}`}><Icon name={s.trend.direction === "down" ? "trending-down" : s.trend.direction === "flat" ? "minus" : "trending-up"} size={13} /> {s.trend.value}</span>}</div>
          <div className="dash-metric-body"><span className="stat-label">{s.label}</span><strong className="stat-value">{s.value}</strong></div>
          <div className="dash-metric-foot">{!live && <DashboardSparkline values={series[i % series.length]} tone={s.tone || "brand"} />}<span>{live ? "Live tenant data" : (s.trend ? s.trend.label : "Live total")}</span></div>
        </button>
      ))}
    </div>
  );
}

function DashboardVelocity({ records, range, setRange, setPage }) {
  const days = range === "7d" ? 7 : range === "90d" ? 90 : 30;
  const bucketDays = Math.max(1, Math.ceil(days / 7));
  const primary = [0, 0, 0, 0, 0, 0, 0];
  const secondary = [0, 0, 0, 0, 0, 0, 0];
  const now = new Date();
  (records || []).forEach((record) => {
    const created = dashboardDate(record && (record.submitted || record.created_at || record.updated));
    if (!created) return;
    const ageDays = Math.floor((now.getTime() - created.getTime()) / 86400000);
    if (ageDays < 0 || ageDays >= days) return;
    const bucket = Math.max(0, 6 - Math.floor(ageDays / bucketDays));
    primary[bucket] += 1;
    if (["approved", "published"].includes(String(record.status || ""))) secondary[bucket] += 1;
  });
  const all = primary.concat(secondary), max = Math.max(1, Math.max.apply(null, all));
  const point = (v, i, arr) => [26 + i * (574 / (arr.length - 1)), 168 - (v / max) * 124];
  const primaryPts = primary.map((v, i) => point(v, i, primary));
  const secondaryPts = secondary.map((v, i) => point(v, i, secondary));
  const path = (pts) => pts.map((p, i) => `${i ? "L" : "M"}${p[0]} ${p[1]}`).join(" ");
  const area = `${path(primaryPts)} L600 174 L26 174 Z`;
  return (
    <div className="card pad dash-widget-card dash-velocity-card">
      <div className="dash-chart-head">
        <div><h3 className="section-title" style={{ fontSize: 18 }}>Workflow velocity</h3><p className="section-sub">Submitted work compared with completed approvals</p></div>
        <div className="dash-range">{["7d", "30d", "90d"].map(r => <button key={r} className={range === r ? "on" : ""} onClick={() => setRange(r)}>{r}</button>)}</div>
      </div>
      <div className="dash-chart-legend"><span><i className="submitted" /> Created</span><span><i className="completed" /> Approved or published</span><b>{primary.reduce((a,b) => a+b, 0)} live records</b></div>
      <button className="dash-velocity-chart" onClick={() => setPage("analytics")} aria-label="Open workflow analytics">
        <svg viewBox="0 0 628 196" preserveAspectRatio="none">
          <defs><linearGradient id="dashArea" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stopColor="#33A045" stopOpacity=".24" /><stop offset="1" stopColor="#33A045" stopOpacity="0" /></linearGradient></defs>
          {[44, 87, 130, 173].map(y => <line key={y} x1="26" y1={y} x2="600" y2={y} className="dash-chart-gridline" />)}
          <path d={area} fill="url(#dashArea)" />
          <path d={path(primaryPts)} className="dash-chart-line primary" />
          <path d={path(secondaryPts)} className="dash-chart-line secondary" />
          {primaryPts.map((p,i) => <circle key={i} cx={p[0]} cy={p[1]} r="3.2" className="dash-chart-point" />)}
        </svg>
        <div className="dash-chart-days">{[6,5,4,3,2,1,0].map((offset) => { const d = new Date(now.getTime() - offset * bucketDays * 86400000); const label = range === "7d" ? d.toLocaleDateString([], { weekday: "short" }) : d.toLocaleDateString([], { month: "short", day: "numeric" }); return <span key={offset}>{label}</span>; })}</div>
      </button>
      <div className="dash-roi-proof"><Icon name="database" size={14} /> Based on active-organization record dates and current workflow status.</div>
    </div>
  );
}

function DashboardWeek({ records, unclaimed, setPage }) {
  const cutoff = Date.now() - 7 * 86400000;
  const recent = (records || []).filter((record) => { const d = dashboardDate(record && (record.submitted || record.created_at || record.updated)); return d && d.getTime() >= cutoff; });
  const totals = dashboardWorkflowTotals(recent);
  const cells = [
    ["Created", recent.length, "upload-cloud", "recipes"],
    ["In review", totals.review, "clipboard-check", "review-queue"],
    ["Approved", totals.approved, "check-circle-2", "recipes"],
    ["Published", totals.published, "globe", "published"],
  ];
  return (
    <div className="card pad dash-widget-card">
      <DashboardCardHead title="This week" sub="Your current operating pulse" action="Calendar" onAction={() => setPage("calendar")} />
      <div className="dash-mini-grid">
        {cells.map(([l,v,ic,to]) => <button key={l} className="dash-mini dash-mini-btn" onClick={() => setPage(to)}><span className="dash-mini-ic"><Icon name={ic} size={15} /></span><span className="dash-mini-v">{v}</span><span className="dash-mini-k">{l}</span></button>)}
      </div>
      <button className={`dash-unclaimed ${unclaimed ? "has-work" : "clear"}`} onClick={() => setPage("review-queue")}><span className="dash-unclaimed-ic"><Icon name={unclaimed ? "hand" : "check-check"} size={15} /></span><span className="dash-unclaimed-body"><strong>{unclaimed ? `${unclaimed} unclaimed in the pool` : "Review pool is clear"}</strong><em>{unclaimed ? "Submitted with no reviewer — claim one" : "No work is waiting without an owner"}</em></span><Icon name="arrow-right" size={15} /></button>
    </div>
  );
}

function DashboardFeaturePulse({ recipes, reviewCount, snapshot, setPage }) {
  const ingredientCount = Number(snapshot && snapshot.ingredients || 0);
  const complianceValue = dashboardComplianceValue(snapshot || {});
  const completedChecks = (snapshot && snapshot.checks || []).filter((check) => ["pass", "passed", "approved", "compliant"].includes(String(check.result || check.status || "").toLowerCase())).length;
  const loraaReady = !!(snapshot && snapshot.apiConnected);
  const modules = [
    ["Recipes & products", recipes.length, "utensils-crossed", "recipes", "live"],
    ["Ingredient mapping", ingredientCount, "leaf", "ingredients", "live"],
    ["Label Studio", "Not measured", "tag", "label-studio", "empty"],
    ["Compliance", complianceValue, "shield-check", "compliance-dashboard", complianceValue === "—" ? "empty" : "live"],
    ["Inventory & costing", "Not measured", "package-search", "inventory", "empty"],
    ["Meal programs", "Not measured", "calendar-days", "meal-programs", "empty"],
    ["Team & approvals", Number(reviewCount || 0), "users-round", "review-queue", reviewCount ? "review" : "live"],
    ["Loraa Command Center", loraaReady ? "Ready" : "Connecting", "sparkles", "loraa-command-center", loraaReady ? "agent" : "empty"],
    ["Reports & ROI", completedChecks ? `${completedChecks} checks` : "Not measured", "chart-no-axes-combined", "analytics", completedChecks ? "live" : "empty"],
    ["Integrations", "Not measured", "plug", "integrations", "empty"],
    ["GS1 & barcodes", "Not measured", "barcode", "gs1", "empty"],
    ["Customer experience", "Not measured", "scan-line", "customer-dashboard", "empty"],
  ];
  return (
    <div className="card pad dash-widget-card dash-feature-card">
      <DashboardCardHead title="Platform coverage" sub="Every NutriDMS operation in one live control surface" action="View reports" onAction={() => setPage("analytics")} />
      <div className="dash-feature-grid">{modules.map(([label,value,icon,to,state]) => <button key={label} className="dash-feature" onClick={() => to === "loraa-command-center" ? openLoraaCommandCenter() : setPage(to)}><span className="dash-feature-icon"><Icon name={icon} size={17} /></span><span className="dash-feature-copy"><strong>{label}</strong><small>{value}</small></span><span className={`dash-feature-state ${state}`}><i />{state === "review" ? "Review" : state === "agent" ? "AI live" : state === "empty" ? "No data" : "Live"}</span><Icon name="chevron-right" size={14} /></button>)}</div>
    </div>
  );
}

function DashboardROI({ snapshot, setPage }) {
  const completed = (snapshot && snapshot.checks || []).filter((check) => ["pass", "passed", "approved", "compliant"].includes(String(check.result || check.status || "").toLowerCase())).length;
  const values = [[completed, "Completed checks", "Tenant records", "shield-check"], ["—", "Hours saved", "Not measured", "clock-3"], ["—", "Estimated value", "Not measured", "badge-dollar-sign"], ["—", "Manual work reduced", "Not measured", "wand-sparkles"]];
  return <div className="card pad dash-widget-card"><DashboardCardHead title="Operational value" sub="Auditable results only" action="Open analytics" onAction={() => setPage("analytics")} /><div className="dash-roi-grid">{values.map(([v,l,d,ic]) => <button key={l} className="dash-roi-cell" onClick={() => setPage("analytics")}><Icon name={ic} size={16} /><strong>{v}</strong><span>{l}</span><small>{d}</small></button>)}</div><div className="dash-roi-proof"><Icon name="shield-check" size={14} /> Unmeasured values stay blank until an auditable metric exists.</div></div>;
}

function DashboardReadiness({ recipes, ingredients, snapshot, setPage }) {
  const allRecords = (recipes || []).concat(ingredients || []);
  const mapped = (ingredients || []).filter((item) => item.canonicalId || (item.canonical && item.canonical !== "—")).length;
  const nutritionReady = allRecords.filter((item) => item.__derived === false).length;
  const compliance = dashboardComplianceValue(snapshot || {});
  const rows = [
    ["Ingredient mapping", dashboardPercent(mapped, (ingredients || []).length), "ingredients"],
    ["Nutrition data", dashboardPercent(nutritionReady, allRecords.length), "nutrition-insights"],
    ["Compliance checks", compliance === "—" ? null : Number(String(compliance).replace("%", "")), "compliance-dashboard"],
    ["Inventory traceability", null, "inventory"],
  ];
  const measured = rows.filter((row) => row[1] != null).length;
  return <div className="card pad dash-widget-card"><DashboardCardHead title="Production readiness" sub="Data quality across publish-critical modules" action="Resolve gaps" onAction={() => setPage("compliance-dashboard")} /><div className="dash-readiness">{rows.map(([l,v,to]) => <button key={l} onClick={() => setPage(to)}><span>{l}</span><b>{v == null ? "—" : v + "%"}</b><i><em style={{width:(v == null ? 0 : v)+"%"}} /></i></button>)}</div><div className="dash-readiness-foot"><span><i className="ok" /> {measured} of {rows.length} modules measured</span><span><i className="warn" /> Missing measurements are not estimated</span></div></div>;
}

function DashboardTeam({ snapshot, queue, setPage }) {
  const members = snapshot && Array.isArray(snapshot.members) ? snapshot.members : [];
  const activeCount = Number(snapshot && snapshot.users || 0);
  const rows = members.map((member) => {
    const id = String(member.user || member.user_id || member.id || "");
    const name = member.user_name || member.name || member.user_email || "Team member";
    const assigned = (queue || []).filter((item) => item.reviewer && String(item.reviewer.id || item.reviewer) === id).length;
    return { id: id || name, name, initials: member.initials || dashboardInitials(name), role: member.role_label || member.role_key || "Active member", assigned };
  });
  const emptyTitle = activeCount > 0 ? activeCount + " active team member" + (activeCount === 1 ? "" : "s") : "No active members";
  const emptyCopy = activeCount > 0
    ? "Live count from the active organization. Member details appear when the roster service returns them."
    : "No active users were returned by the live organization.";
  return <div className="card pad dash-widget-card dash-team-card"><DashboardCardHead title="Team capacity" sub="Active members and current review ownership" action="Manage team" onAction={() => setPage("users")} />{rows.length ? <div className="dash-team-list">{rows.map((row,i) => <button key={row.id} onClick={() => setPage("users")}><span className="dash-team-rank">{i+1}</span><span className="avatar sm">{row.initials}</span><span className="dash-team-person"><strong>{row.name}</strong><small>{row.role} · {row.assigned} assigned</small></span><span className="dash-team-meter"><i style={{width:Math.min(100, row.assigned * 20)+"%"}} /></span></button>)}</div> : <DashboardNoData icon="users" title={emptyTitle} copy={emptyCopy} />}</div>;
}

function DashboardDeadlines({ setPage }) {
  return <div className="card pad dash-widget-card dash-deadline-card"><DashboardCardHead title="Upcoming deadlines" sub="What is due next" action="Open plan" onAction={() => setPage("calendar")} /><DashboardNoData icon="calendar-clock" title="No deadlines scheduled" copy="Organization deadlines will appear here after they are added to the publishing calendar." /></div>;
}

function DashboardCompliance({ snapshot, setPage }) {
  const value = dashboardComplianceValue(snapshot || {});
  const checks = snapshot && Array.isArray(snapshot.checks) ? snapshot.checks : [];
  const rules = snapshot && Array.isArray(snapshot.rules) ? snapshot.rules : [];
  const attention = checks.filter((check) => check.requires_review || ["warn", "warning", "fail", "failed"].includes(String(check.result || check.status || "").toLowerCase())).length;
  const regions = Array.from(new Set(rules.map((rule) => rule.region || rule.jurisdiction).filter(Boolean)));
  return <div className="card pad dash-widget-card dash-compliance-card"><DashboardCardHead title="Compliance snapshot" sub="Across active organization records" action="Open center" onAction={() => setPage("compliance-dashboard")} /><button className="dash-compliance-main" onClick={() => setPage("compliance-dashboard")}><div className="dash-comp-ring" style={{"--v":value === "—" ? "0%" : value}}><span>{value}</span></div><div><strong>{value === "—" ? "Not measured" : attention ? "Needs review" : "Passing"}</strong><p>{attention} check{attention === 1 ? "" : "s"} need attention</p><small><i /> {rules.length} active rule{rules.length === 1 ? "" : "s"}</small></div></button><div className="dash-compliance-countries">{regions.length ? regions.slice(0, 3).map((region) => <span key={region}>{region} <b>{rules.filter((rule) => (rule.region || rule.jurisdiction) === region).length}</b></span>) : <span>No active jurisdictions <b>—</b></span>}</div></div>;
}

function DashboardRecommendations({ role }) {
  const prompts = role === "compliance" ? ["Review the highest-risk label","Explain today’s compliance delta","Prepare an audit summary"] : ["Validate my next recipe","Show inventory risks","Summarize my approvals"];
  return <div className="card pad dash-widget-card dash-recommend-card"><DashboardCardHead title="Loraa recommendations" sub="Personalized to your role and workload" /><div className="dash-recommend-list">{prompts.map((p,i) => <button key={p} onClick={openLoraaCommandCenter}><span>{i+1}</span><strong>{p}</strong><Icon name="arrow-up-right" size={14} /></button>)}</div><button className="dash-ask-loraa" onClick={openLoraaCommandCenter}><Icon name="sparkles" size={15} /> Ask Loraa about today</button></div>;
}

function DashboardNoData({ icon, title, copy }) {
  return <div className="empty dash-empty" style={{ padding: 24 }}><div className="icon"><Icon name={icon} size={22} /></div><h3>{title}</h3><p>{copy}</p></div>;
}

function DashboardActivity({ records, openRecipe, setPage }) {
  const items = (records || []).map((record) => ({ record, date: dashboardDate(record && (record.updated || record.submitted || record.created_at)) })).filter((item) => item.date).sort((a, b) => b.date - a.date).slice(0, 6);
  if (!items.length) return <DashboardNoData icon="rss" title="No activity yet" copy="New organization activity will appear here after records are created or updated." />;
  return <div>{items.map(({ record, date }) => {
    const isRecipe = (window.RECIPES || []).some((item) => String(item.id) === String(record.id));
    const type = record.status === "published" ? "published" : record.status === "approved" ? "approved" : "submitted";
    return <ActivityRow key={String(record.id)} type={type} who={record.contributor && record.contributor.name || "Organization member"} what={record.name || "Untitled record"} when={date.toLocaleDateString()} onOpen={() => isRecipe ? openRecipe(record) : setPage("ingredients")} />;
  })}</div>;
}

function DashboardEmpty() { return <div className="empty dash-empty"><div className="icon"><Icon name="check-check" size={24} /></div><h3>Queue is empty</h3><p>No items awaiting review.</p></div>; }

function AttentionCard({ queue, recent, openRecipe, setPage }) {
  const items = queue.slice(0, 3);
  return (
    <div className="card pad dash-widget-card dash-attention">
      <div className="section-head" style={{ margin: 0, marginBottom: 14 }}>
        <div><h2 className="section-title" style={{ fontSize: 18 }}>Needs your attention</h2><p className="section-sub">Loraa picked these out for you</p></div>
        <span className="dash-att-badge">{items.length}</span>
      </div>
      <div className="col" style={{ gap: 8 }}>
        {items.map((r) => (
          <button key={r.id} className="dash-att-row" onClick={() => openRecipe(r)}>
            <span className={`dash-att-dot ${r.priority}`} />
            <span className="dash-att-nm">{r.name}</span>
            <StatusPill status={r.status} />
            <Icon name="chevron-right" size={15} style={{ color: "var(--gray-400)" }} />
          </button>
        ))}
        {items.length === 0 && <div className="empty" style={{ padding: 24 }}><div className="icon"><Icon name="check-check" size={22} /></div><h3>You're all caught up</h3></div>}
      </div>
    </div>
  );
}

function QuickActionsCard({ actions, setPage }) {
  return (
    <div className="card pad dash-widget-card">
      <h3 className="section-title" style={{ fontSize: 18, marginBottom: 12 }}>Quick actions</h3>
      <div className="dash-qa-grid">
        {actions.map((a, i) => (
          a.href
            ? <a key={i} href={a.href} className="dash-qa" style={{ textDecoration: "none" }}><span className="dash-qa-ic"><Icon name={a.icon} size={18} /></span>{a.label}</a>
            : <button key={i} className="dash-qa" onClick={() => setPage(a.to)}><span className="dash-qa-ic"><Icon name={a.icon} size={18} /></span>{a.label}</button>
        ))}
      </div>
    </div>
  );
}

function StatusDonut({ value = 0, status }) {
  const colorMap = { "pending-review": "#dc6803", "compliance-review": "#2A54E5", "changes-requested": "#6938EF", "approved": "#3b7c0f", "published": "#3b7c0f", "rejected": "#d92d20", "draft": "#717680" };
  const c = colorMap[status] || "#3b7c0f";
  const r = 17, circ = 2 * Math.PI * r, pct = Math.max(0, Math.min(100, value));
  return (
    <div style={{ position: "relative", width: 44, height: 44, flexShrink: 0 }}>
      <svg width="44" height="44" viewBox="0 0 44 44">
        <circle cx="22" cy="22" r={r} fill="none" stroke="var(--gray-150, #eceee9)" strokeWidth="4" />
        <circle cx="22" cy="22" r={r} fill="none" stroke={c} strokeWidth="4" strokeLinecap="round" strokeDasharray={circ} strokeDashoffset={circ - (pct / 100) * circ} transform="rotate(-90 22 22)" style={{ transition: "stroke-dashoffset .5s ease" }} />
      </svg>
      <span style={{ position: "absolute", inset: 0, display: "flex", alignItems: "center", justifyContent: "center", fontSize: 9.5, fontWeight: 800, color: "var(--text-primary)", lineHeight: 1, whiteSpace: "nowrap", letterSpacing: "-0.02em" }}>{pct}%</span>
    </div>
  );
}

function DashRow({ recipe, onOpen, kind }) {
  return (
    <div onClick={onOpen} style={{ display: "flex", alignItems: "center", gap: 14, padding: 12, border: "1px solid var(--gray-200)", borderRadius: 12, cursor: "pointer", transition: "border-color .12s ease, background .12s ease" }}
      onMouseEnter={(e) => { e.currentTarget.style.background = "#FAFCF7"; e.currentTarget.style.borderColor = "var(--green-300)"; }}
      onMouseLeave={(e) => { e.currentTarget.style.background = "transparent"; e.currentTarget.style.borderColor = "var(--gray-200)"; }}>
      <div className="thumb" style={{ backgroundImage: `url("${recipe.cover}")` }} />
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ fontWeight: 700, fontSize: 14, marginBottom: 2 }}>{recipe.name}</div>
        <div className="recipe-meta" style={{ fontSize: 12 }}>
          <span><Icon name="user" size={12} /> {recipe.contributor.name}</span>
          <span><Icon name="clock" size={12} /> {recipe.duration} min</span>
          <span>{recipe.cuisine}</span>
        </div>
      </div>
      <div className={`dash-pills ${kind === "queue" ? "stretch" : ""}`} style={{ display: "flex", flexDirection: kind === "queue" ? "column" : "row", alignItems: "center", gap: kind === "queue" ? 8 : 14, minWidth: kind === "queue" ? 150 : "auto" }}>
        <StatusPill status={recipe.status} />
        {kind === "queue" ? <PriorityPill priority={recipe.priority} /> : <StatusDonut value={recipe.progress} status={recipe.status} />}
      </div>
    </div>
  );
}

function ActivityRow({ type, who, what, when, onOpen }) {
  const map = {
    approved:  { icon: "check-circle-2", tone: "success", verb: "approved" },
    submitted: { icon: "upload-cloud",   tone: "brand",   verb: "submitted" },
    comment:   { icon: "message-circle", tone: "info",    verb: "commented on" },
    published: { icon: "globe",          tone: "brand",   verb: "published" },
    rejected:  { icon: "x-circle",       tone: "error",   verb: "rejected" },
  };
  const m = map[type];
  return (
    <div onClick={onOpen} className={onOpen ? "activity-row clickable" : "activity-row"}
      style={{ display: "flex", gap: 12, padding: "10px 6px", margin: "0 -6px", borderBottom: "1px solid var(--gray-100)", cursor: onOpen ? "pointer" : "default", borderRadius: 8, transition: "background .12s ease" }}
      onMouseEnter={(e) => { if (onOpen) e.currentTarget.style.background = "#FAFCF7"; }}
      onMouseLeave={(e) => { e.currentTarget.style.background = "transparent"; }}>
      <div className={`stat-icon ${m.tone}`} style={{ width: 32, height: 32, borderRadius: 9 }}><Icon name={m.icon} size={14} /></div>
      <div style={{ flex: 1 }}>
        <div style={{ fontSize: 13, lineHeight: 1.45 }}><strong>{who}</strong> <span className="muted">{m.verb}</span> <strong style={{ color: "var(--brand-700)" }}>{what}</strong></div>
        <div className="muted" style={{ fontSize: 11, marginTop: 2 }}>{when}</div>
      </div>
      {onOpen && <Icon name="chevron-right" size={15} className="activity-chev" />}
    </div>
  );
}

function PipelineCard({ records }) {
  const totals = dashboardWorkflowTotals(records || []);
  const segs = [
    { label: "Published", value: totals.published, color: "var(--green-700)" },
    { label: "Approved", value: totals.approved, color: "#2A54E5" },
    { label: "In review", value: totals.review, color: "var(--warning-500)" },
    { label: "Draft", value: totals.draft, color: "var(--gray-400)" },
  ];
  const total = segs.reduce((a, b) => a + b.value, 0);
  const divisor = Math.max(1, total);
  let acc = 0;
  const R = 56, C = 2 * Math.PI * R;
  return (
    <div className="card pad dash-widget-card dash-pipeline-card">
      <h3 className="section-title" style={{ fontSize: 18, marginBottom: 4 }}>Pipeline mix</h3>
      <p className="section-sub" style={{ marginBottom: 16 }}>Current recipe workflow status</p>
      <div style={{ display: "flex", alignItems: "center", gap: 20 }}>
        <svg width="140" height="140" viewBox="0 0 140 140" style={{ flexShrink: 0 }}>
          <circle cx="70" cy="70" r={R} fill="none" stroke="var(--gray-100)" strokeWidth="16" />
          {segs.map((s, i) => { const dash = (s.value / divisor) * C; const off = (acc / divisor) * C; acc += s.value; return (<circle key={i} cx="70" cy="70" r={R} fill="none" stroke={s.color} strokeWidth="16" strokeDasharray={`${dash} ${C - dash}`} strokeDashoffset={-off} transform="rotate(-90 70 70)" strokeLinecap="butt" />); })}
          <text x="70" y="70" textAnchor="middle" dominantBaseline="central" fontSize="22" fontWeight="700" fontFamily="Manrope" fill="var(--text-primary)">{total}</text>
          <text x="70" y="92" textAnchor="middle" fontSize="11" fill="var(--gray-500)" fontWeight="600" fontFamily="Manrope">recipes</text>
        </svg>
        <div style={{ flex: 1 }}>
          {segs.map((s, i) => (
            <div key={i} style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "6px 0", fontSize: 13 }}>
              <div style={{ display: "flex", alignItems: "center", gap: 8 }}><span style={{ width: 8, height: 8, borderRadius: 2, background: s.color }} /><span style={{ color: "var(--gray-700)", fontWeight: 500 }}>{s.label}</span></div>
              <strong>{s.value}</strong>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

function dashboardConfig(role, liveStats) {
  const snapshot = liveStats || {};
  const reviewStatuses = ["pending-review", "compliance-review", "changes-requested"];
  const reviewCount = snapshot.serverAuthoritative && Number.isFinite(Number(snapshot.review))
    ? Number(snapshot.review)
    : dashboardTenantRows(window.RECIPES).filter((item) => reviewStatuses.includes(item.status)).length
      + dashboardTenantRows(window.INGREDIENT_ITEMS).filter((item) => reviewStatuses.includes(item.status)).length;
  const userCount = Number(snapshot.users || 0);
  const recipeCount = Number(snapshot.recipes || 0);
  const ingredientCount = Number(snapshot.ingredients || 0);
  const compliance = dashboardComplianceValue(snapshot);
  const reviewerRole = ["dietitian", "compliance"].includes(role);
  const actions = reviewerRole
    ? [ { label: "Open Review Queue", icon: "clipboard-check", to: "review-queue" }, { label: "Add Ingredient", icon: "leaf", variant: "secondary", to: "add-ingredient" } ]
    : role === "admin" || role === "super-admin"
      ? [ { label: "Invite User", icon: "user-plus", to: "users" }, { label: "Bulk Import", icon: "file-spreadsheet", variant: "secondary", to: "bulk-import" } ]
      : [ { label: "Create New Recipe", icon: "plus", to: "upload" }, { label: "Add Ingredient", icon: "leaf", variant: "secondary", to: "add-ingredient" } ];
  return {
    greeting: "Dashboard",
    sub: `${userCount} active team member${userCount === 1 ? "" : "s"} · ${recipeCount} recipe${recipeCount === 1 ? "" : "s"} · ${ingredientCount} ingredient${ingredientCount === 1 ? "" : "s"}`,
    brief: reviewCount ? `${reviewCount} item${reviewCount === 1 ? " is" : "s are"} waiting in Nutrition Review.` : "There are no items waiting in Nutrition Review.",
    briefSub: "Counts and status come directly from the active organization. No sample records or estimated metrics are included.",
    actions,
    stats: [
      { label: "Total users", value: userCount, icon: "users", tone: "info", to: "users" },
      { label: "Total recipes", value: recipeCount, icon: "utensils-crossed", tone: "brand", to: "recipes" },
      { label: "Compliance", value: compliance, icon: "shield-check", tone: "success", to: "compliance-dashboard" },
      { label: "System health", value: snapshot.apiConnected ? "Connected" : "Connecting", icon: "activity", tone: "violet", to: "integrations" },
    ],
    listKind: reviewCount ? "queue" : "recent",
    listTitle: reviewCount ? "Nutrition review queue" : "Recent recipes",
    listSub: "Live organization records",
    listTo: reviewCount ? "review-queue" : "recipes",
  };
}

Object.assign(window, { Dashboard });
