/* NutriDMS, Recipe Detail */

function RecipeDetail() {
  const { activeRecipe, setActiveRecipe, role, setPage, toast, detailFocus, setDetailFocus, openQuickAssign } = useApp();
  const RD_SEC_TAB = { "Nutrition": "nutrition", "Allergens": "allergens", "Cooking Steps": "instructions", "Compliance Risk": "dietary", "Sources": "references", "References": "references", "Basics": "overview", "Ingredients": "ingredients", "Image": "media", "Media": "media", "Macronutrients": "nutrition" };
  const focus = (detailFocus && detailFocus.kind === "recipe") ? detailFocus : null;
  const [tab, setTab] = React.useState(focus ? (RD_SEC_TAB[focus.sec] || "overview") : "overview");
  const [flag, setFlag] = React.useState(focus || null);
  React.useEffect(() => { if (focus) setDetailFocus(null); }, []);
  const [comment, setComment] = React.useState("");
  const [modal, setModal] = React.useState(null); // 'approve' | 'reject' | 'changes' | 'urgent' | null
  const [urgentTo, setUrgentTo] = React.useState(null);
  const [urgentNote, setUrgentNote] = React.useState("");
  const [resolved, setResolved] = React.useState(() => new Set());

  if (!activeRecipe) return null;
  // Normalize so contributor-submitted pool recipes (which lack some fields)
  // render safely instead of crashing the detail page.
  const r = Object.assign({
    contributor: { name: "—", initials: "—" },
    ingredients: [], lineItems: [], steps: [], preparationSteps: [], healthTags: [], tags: [], diet: [],
    allergens: [], detectedAllergens: [], precautionaryAllergens: [],
    cuisine: "", category: "", timeSpent: "—", progress: null,
    calories: null, protein: null, carbs: null, fat: null,
    cover: (activeRecipe && (activeRecipe.cover || activeRecipe.image)) || "",
  }, activeRecipe || {});
  const feedback = FEEDBACK.find((f) => f.recipe === r.id)?.items || [];
  const hasChangeRequest = r.status === "changes-requested" || r.status === "compliance-review";
  const openCount = feedback.length - resolved.size;
  const allResolved = feedback.length > 0 && openCount <= 0;
  const toggleResolve = (i) => setResolved((s) => { const n = new Set(s); n.has(i) ? n.delete(i) : n.add(i); return n; });

  const canReview = ["reviewer", "dietitian", "compliance", "manager", "admin", "super-admin"].includes(role);
  const canFinalize = ["compliance", "admin", "super-admin"].includes(role);
  const me = currentUser(role);
  const urgentPeople = (URGENT_ESCALATION_BY_ROLE[role] || []).flatMap((rl) => usersByRole(rl));

  // Workflow-aware actions driven by the org's saved Publishing Workflow
  const flow = window.weFlow ? window.weFlow(r, "recipe", role) : null;
  const canRequestChanges = flow && r.__remote
    ? !!(flow.actions && flow.actions.can_request_changes)
    : canReview;
  const canEditPub = window.canEditPublished ? window.canEditPublished(role) : (role === "admin" || role === "super-admin");
  const requestChanges = () => openQuickAssign && openQuickAssign({ kind: "recipe", item: r, reason: "changes" });
  const doPrimary = () => {
    if (!flow || !flow.primary) return;
    if (flow.primary.kind === "view") { window.__libInitialStatus = "published"; setActiveRecipe(null); setPage("recipes"); return; }
    setModal("complete");
  };
  const confirmComplete = async () => {
    try {
      await window.weServerAction("recipe", r, flow);
      const refreshed = (window.RECIPES || []).find(item => String(item.id) === String(r.id));
      if (refreshed) setActiveRecipe({ ...refreshed });
      setModal(null);
      toast(flow && flow.isFinal ? "Approval recorded by NutriDMS" : "Review stage completed by NutriDMS");
    } catch (error) {
      toast((error && (error.message || error.detail)) || "The workflow action could not be completed.");
    }
  };
  const wfActions = flow ? (() => {
    const a = [];
    const isPub = r.status === "published";
    if (permAllowed(role, "edit_recipe") && (!isPub || canEditPub))
      a.push({ id: "edit", label: isPub ? "Edit (Admin)" : "Edit Recipe", icon: "pencil", onClick: () => { window.__editRecipeId = r.id; setActiveRecipe(null); setPage("edit-recipe"); } });
    if (canRequestChanges && !isPub) {
      a.push({ id: "request-changes", label: "Request Changes", icon: "message-square", onClick: requestChanges });
      if (can(role, "mark_urgent") && r.status !== "rejected")
        a.push({ id: "urgent", label: "Urgent review", icon: "alert-triangle", style: { borderColor: "#FEDF89", color: "#b54708" }, onClick: () => { setUrgentTo(urgentPeople[0] ? urgentPeople[0].initials : null); setUrgentNote(""); setModal("urgent"); } });
      if (can(role, "reject") && r.status !== "rejected")
        a.push({ id: "reject", label: "Reject", icon: "x", tone: "danger", onClick: () => setModal("reject") });
    }
    if (isPub && canEditPub)
      a.push({ id: "delete", label: "Delete", icon: "trash-2", tone: "danger", onClick: () => setModal("delete") });
    return a;
  })() : [];
  const wfPrimary = flow && flow.primary && (flow.canActPrimary || flow.primary.kind === "view")
    ? { label: flow.primary.label, icon: flow.primary.icon, onClick: doPrimary }
    : null;

  return (
    <div>
      <Crumbs path={[
        { label: "Recipe Library", onClick: () => { setActiveRecipe(null); setPage("recipes"); } },
        { label: r.name }
      ]} />

      <div className="page-head" style={{ alignItems: "flex-start" }}>
        <div style={{ minWidth: 0 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 12, marginBottom: 8 }}>
            <StatusPill status={r.status} item={r} kind="recipe" />
            <PriorityPill priority={r.priority} />
            <RefBadge kind="recipe" item={r} size="md" />
          </div>
          <h1 className="page-title">{r.name}</h1>
          <p className="page-sub" style={{ maxWidth: 720 }}>{r.description}</p>
        </div>
        <div className="rd-head-actions">
          <button className="btn secondary" onClick={() => { const p = window.__prevPage; setActiveRecipe(null); setPage(p && p !== "recipe-detail" ? p : "recipes"); }}><Icon name="arrow-left" size={16} /> Back</button>
          {flow ? (
            <WfActionBar flow={flow} actions={wfActions} primary={wfPrimary} />
          ) : (
            <>
              {permAllowed(role, "edit_recipe") && r.status !== "published" && (
                <button className="btn secondary" onClick={() => { window.__editRecipeId = r.id; setActiveRecipe(null); setPage("edit-recipe"); }}><Icon name="pencil" size={16} /> Edit Recipe</button>
              )}
              {role === "media-contributor" && r.status === "changes-requested" && (
                <button className="btn primary" onClick={() => { window.__editRecipeId = r.id; setActiveRecipe(null); setPage("edit-recipe"); }}><Icon name="pencil" size={16} /> Edit Recipe</button>
              )}
            </>
          )}
          {flow && role === "media-contributor" && r.status === "changes-requested" && (
            <button className="btn primary" onClick={() => { window.__editRecipeId = r.id; setActiveRecipe(null); setPage("edit-recipe"); }}><Icon name="pencil" size={16} /> Edit & Resubmit</button>
          )}
        </div>
      </div>

      <WorkflowBanner flow={flow} item={typeof compRecipeItem === "function" ? compRecipeItem(r) : null} kind="recipe" />

      {(() => {
        const liveCompliance = r.compliance && typeof r.compliance === "object" ? r.compliance : null;
        const blocking = liveCompliance ? Number(liveCompliance.blocking || 0) : null;
        const warnings = liveCompliance ? Number(liveCompliance.warnings || 0) : null;
        const score = liveCompliance && liveCompliance.score != null ? Number(liveCompliance.score) : null;
        const complianceTone = !liveCompliance || liveCompliance.stale || liveCompliance.not_run
          ? "warn"
          : blocking ? "fail" : warnings ? "warn" : "ok";
        const kpis = [
          { k: "Compliance", v: score == null ? "—" : score + "%", t: complianceTone },
          { k: "Calories", v: (r.calories != null ? r.calories : "—") },
          { k: "Protein", v: (r.protein != null ? r.protein + "g" : "—") },
          { k: "Blocking", v: blocking == null ? "—" : String(blocking), t: blocking ? "fail" : blocking === 0 ? "ok" : "" },
          { k: "Warnings", v: warnings == null ? "—" : String(warnings), t: warnings ? "warn" : warnings === 0 ? "ok" : "" },
          { k: "Workflow", v: (flow && flow.levelLabel) || (flow ? "In review" : "Draft") },
          { k: "Reviewer", v: (flow && flow.currentAssignee) || (r.reviewer && (r.reviewer.name || r.reviewer)) || "Unassigned" },
        ];
        return (
          <div className="rd-kpibar">
            {kpis.map((x) => (
              <div key={x.k} className="rd-kpi">
                <span className="rd-kpi-k">{x.k}</span>
                <span className={"rd-kpi-v" + (x.t ? " " + x.t : "")}>{x.v}</span>
              </div>
            ))}
          </div>
        );
      })()}
      {r.status === "published" && <PublishedLock canEdit={canEditPub} />}

      <div style={{ display: "grid", gridTemplateColumns: "1.6fr 1fr", gap: 22 }}>
        {/* LEFT */}
        <div className="col" style={{ gap: 22 }}>
          {/* Hero */}
          <div className="card" style={{ padding: 0, overflow: "hidden" }}>
            <div style={{ aspectRatio: "16 / 8", backgroundImage: `url("${r.cover}")`, backgroundSize: "cover", backgroundPosition: "center" }} />
          </div>

          {/* Tabs */}
          <div>
            <div className="tabs" style={{ marginBottom: 16 }}>
              {["overview", "ingredients", "instructions", "nutrition", "cost", "allergens", "dietary", "tags", "media", "references", "versions"].map((t) => (
                <button key={t} className={`${tab === t ? "on" : ""} ${flag && (({ "Nutrition":"nutrition","Allergens":"allergens","Cooking Steps":"instructions","Compliance Risk":"dietary","Sources":"references","References":"references","Macronutrients":"nutrition" }[flag.sec])===t) ? "tab-flagged" : ""}`} onClick={() => setTab(t)} style={{ textTransform: "capitalize" }}>{t === "dietary" ? "Dietary Rules" : t === "tags" ? "Health Tags" : t}</button>
              ))}
            </div>

            {flag && (
              <div className="detail-flag">
                <Icon name="flag" size={16} />
                <div style={{ flex: 1 }}>
                  <strong>Needs review · {flag.label}</strong>
                  <div className="detail-flag-sub">{flag.sec} › {flag.field}{flag.cur ? `, current: ${flag.cur}` : ""}</div>
                </div>
                <button className="btn ghost sm" onClick={() => setFlag(null)}><Icon name="x" size={14} /> Dismiss</button>
              </div>
            )}

            {tab === "overview" && (
              <div className="card pad">
                <KeyValue grid items={[
                  { k: "Cuisine",    v: r.cuisine },
                  { k: "Category",   v: r.category },
                  { k: "Region",     v: r.region },
                  { k: "Cooking time", v: `${r.duration} min` },
                  { k: "Servings",   v: r.servings },
                  { k: "Submitted",  v: formatDate(r.submitted) },
                ]} />
                {(() => {
                  const yieldData = r.yieldData && typeof r.yieldData === "object" ? r.yieldData : {};
                  const number = (key) => yieldData[key] != null ? yieldData[key] : "—";
                  return (
                    <React.Fragment>
                      <hr className="divider" />
                      <div className="ingn-sec-h">Saved yield calculation</div>
                      <KeyValue grid items={[
                        { k: "Raw weight", v: number("raw_weight_g") === "—" ? "—" : number("raw_weight_g") + " g" },
                        { k: "Cooked weight", v: number("cooked_weight_g") === "—" ? "—" : number("cooked_weight_g") + " g" },
                        { k: "Yield factor", v: number("yield_factor") },
                        { k: "Serving weight", v: number("serving_weight_g") === "—" ? "—" : number("serving_weight_g") + " g" },
                        { k: "Serving variance", v: number("serving_weight_variance_percent") === "—" ? "—" : number("serving_weight_variance_percent") + "%" },
                        { k: "Calculation source", v: yieldData.calculation_source || "—" },
                      ]} />
                    </React.Fragment>
                  );
                })()}
                <hr className="divider" />
                <div>
                  <div style={{ fontSize: 13, fontWeight: 700, color: "var(--gray-600)", marginBottom: 8, textTransform: "uppercase", letterSpacing: ".06em" }}>Tags</div>
                  <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
                    {r.diet.map((d) => <span key={d} className="pill brand">{d}</span>)}
                    {r.allergens.map((a) => <span key={a} className="pill warning">{a}</span>)}
                  </div>
                </div>
              </div>
            )}

            {tab === "ingredients" && (
              <div className="card pad">
                <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 14 }}>
                  <h3 style={{ fontFamily: "var(--serif)", fontSize: 20, margin: 0 }}>Ingredients</h3>
                  <span className="pill neutral">{r.ingredients.length} items · {r.servings} servings</span>
                </div>
                <div className="col" style={{ gap: 0 }}>
                  {r.ingredients.map((i, idx) => {
                    const m = String(i).match(/^([\d¼½¾⅓⅔\.\/\s]+(?:cup|cups|tbsp|tsp|g|kg|ml|l|oz|clove|cloves|can|cans|pinch|slice|slices)?\b)?\s*(.*)$/i);
                    const qty = (m && m[1] && m[1].trim()) ? m[1].trim() : "";
                    const name = (m && m[2]) ? m[2] : String(i);
                    return (
                      <div key={idx} style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 4px", borderBottom: idx < r.ingredients.length - 1 ? "1px solid var(--gray-100)" : "none" }}>
                        <div style={{ width: 30, height: 30, borderRadius: 8, background: "var(--green-50)", color: "var(--green-700)", display: "grid", placeItems: "center", flexShrink: 0 }}><Icon name="leaf" size={15} stroke={2} /></div>
                        <span style={{ flex: 1, fontSize: 14, color: "var(--text-primary)", fontWeight: 500 }}>{name}</span>
                        {qty && <span className="pill neutral" style={{ fontVariantNumeric: "tabular-nums", whiteSpace: "nowrap" }}>{qty}</span>}
                      </div>
                    );
                  })}
                </div>
                <div className="alert info" style={{ marginTop: 14 }}>
                  <Icon name="link" size={16} />
                  <div style={{ fontSize: 12.5 }}>Each line maps to a verified master ingredient, nutrition recalculates automatically when a master record changes.</div>
                </div>
              </div>
            )}

            {tab === "instructions" && (() => {
              const preparation = Array.isArray(r.preparationSteps) ? r.preparationSteps : [];
              const cooking = Array.isArray(r.steps) ? r.steps : [];
              const renderSteps = (rows, emptyText) => rows.length ? (
                <div className="col" style={{ gap: 14 }}>
                  {rows.map((step, idx) => {
                    const text = typeof step === "string" ? step : (step && (step.t || step.text)) || "";
                    return (
                      <div key={idx} style={{ display: "flex", gap: 14 }}>
                        <div style={{ width: 30, height: 30, borderRadius: 999, background: "var(--green-700)", color: "#fff", display: "grid", placeItems: "center", flexShrink: 0, fontWeight: 800, fontSize: 13 }}>{idx + 1}</div>
                        <div style={{ paddingTop: 4, fontSize: 14, color: "var(--gray-700)", lineHeight: 1.6 }}>{text}</div>
                      </div>
                    );
                  })}
                </div>
              ) : <p className="muted" style={{ margin: 0 }}>{emptyText}</p>;
              return (
                <div className="card pad">
                  <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 16 }}>
                    <h3 style={{ fontFamily: "var(--serif)", fontSize: 20, margin: 0 }}>Preparation &amp; cooking</h3>
                    <span className="pill neutral">{preparation.length + cooking.length} saved steps</span>
                  </div>
                  <div className="ingn-sec" style={{ marginTop: 0 }}>
                    <div className="ingn-sec-h">Preparation before cooking</div>
                    {renderSteps(preparation, "No preparation steps were submitted.")}
                  </div>
                  <div className="ingn-sec">
                    <div className="ingn-sec-h">Cooking method</div>
                    {renderSteps(cooking, "No cooking steps were submitted.")}
                  </div>
                </div>
              );
            })()}

            {tab === "nutrition" && (
              <NutritionPanel r={r} />
            )}

            {tab === "cost" && (() => {
              const c = (window.NutriInvLink && window.NutriInvLink.recipeCost(r)) || null;
              if (!c) return <div className="card pad"><h3 style={{ fontFamily: "var(--serif)", fontSize: 20, margin: "0 0 6px" }}>Cost &amp; margin</h3><p className="muted">Inventory costing is unavailable.</p></div>;
              const est = (window.NutriInvLink && window.NutriInvLink.estimatePrice(c.perServing, "recipe")) || null;
              const price = Number(r.sellingPrice) || (est ? est.mid : Math.round(c.perServing * 3 * 100) / 100);
              const m = (window.NutriInvLink && window.NutriInvLink.recipeMargin(r, price)) || {};
              return (
                <div className="card pad">
                  <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 14 }}>
                    <h3 style={{ fontFamily: "var(--serif)", fontSize: 20, margin: 0 }}>Cost &amp; margin</h3>
                    <span className="pill neutral">{c.coverage}% of lines costed</span>
                  </div>
                  <div className="rc-cost-kpis">
                    <div className="rc-cost-kpi"><span>Recipe cost</span><b>${c.total}</b></div>
                    <div className="rc-cost-kpi"><span>Cost / serving</span><b>${c.perServing}</b></div>
                    <div className="rc-cost-kpi"><span>Selling price</span><b>${price}</b></div>
                    <div className={"rc-cost-kpi " + (m.marginPct != null && m.marginPct < 0 ? "block" : "ok")}><span>Gross margin</span><b>{m.marginPct != null ? m.marginPct + "%" : "—"}</b></div>
                  </div>
                  {est && (
                    <div className="rc-est">
                      <div className="rc-est-h"><Icon name="calculator" size={14} /> Estimated price from margin policy <span className="pill neutral" style={{ marginLeft: "auto" }}>{est.single ? est.marginLow + "% target" : est.marginLow + "–" + est.marginHigh + "% band"}</span></div>
                      <div className="rc-est-band">
                        <div><span>At {est.marginLow}%</span><b>${est.low}</b></div>
                        {!est.single && <div className="rc-est-mid"><span>Midpoint</span><b>${est.mid}</b></div>}
                        {!est.single && <div><span>At {est.marginHigh}%</span><b>${est.high}</b></div>}
                      </div>
                      <p className="muted" style={{ fontSize: 11.5, margin: "8px 0 0" }}>Margin bands are configured per product type in Settings → Costing &amp; margins.</p>
                    </div>
                  )}
                  <table className="table" style={{ marginTop: 16, width: "100%" }}>
                    <thead><tr><th>Ingredient</th><th>Qty</th><th style={{ textAlign: "right" }}>Unit cost</th><th style={{ textAlign: "right" }}>Line cost</th></tr></thead>
                    <tbody>
                      {c.lines.map((l, idx) => (
                        <tr key={idx}>
                          <td>{l.name}{!l.mapped && <span className="pill warn" style={{ marginLeft: 6, fontSize: 10 }}>not stocked</span>}</td>
                          <td className="muted">{l.qty}{l.unit ? " " + l.unit : ""}</td>
                          <td style={{ textAlign: "right" }}>{l.unitCost != null ? "$" + l.unitCost : "—"}</td>
                          <td style={{ textAlign: "right", fontWeight: 700 }}>{l.lineCost != null ? "$" + l.lineCost : "—"}</td>
                        </tr>
                      ))}
                    </tbody>
                  </table>
                  <div className="alert info" style={{ marginTop: 14 }}><Icon name="info" size={16} /><div style={{ fontSize: 12.5 }}>Costs roll up from each ingredient's live inventory unit cost. Estimated price uses your per-type margin policy; lines marked "not stocked" aren't yet mapped to an inventory item.</div></div>
                </div>
              );
            })()}

            {tab === "media" && (
              <div className="card pad">
                <h3 style={{ fontFamily: "var(--serif)", fontSize: 20, margin: "0 0 14px" }}>Media gallery</h3>
                <div style={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)", gap: 10 }}>
                  {[0,1,2,3,4].map((i) => (
                    <div key={i} style={{ aspectRatio: "1/1", borderRadius: 10, backgroundImage: `url("${r.cover}&w=${300 + i}")`, backgroundSize: "cover", backgroundPosition: "center", border: i === 2 ? "2px solid var(--warning-500)" : "1px solid var(--gray-200)" }} title={i === 2 ? "Flagged, out of focus" : ""} />
                  ))}
                </div>
                <p className="muted" style={{ fontSize: 12, marginTop: 10 }}>Image #3 was flagged by Compliance.</p>
              </div>
            )}

            {tab === "allergens" && (() => {
              const groups = [
                ["Declared", r.allergens || [], "error"],
                ["Detected from linked ingredients", r.detectedAllergens || [], "warning"],
                ["May contain / cross-contamination", r.precautionaryAllergens || [], "neutral"],
              ];
              return (
                <div className="card pad">
                  <h3 style={{ fontFamily: "var(--serif)", fontSize: 20, margin: "0 0 14px" }}>Allergen evidence</h3>
                  {groups.map(([label, values, tone]) => (
                    <div key={label} className="ingn-sec" style={{ marginTop: 0 }}>
                      <div className="ingn-sec-h">{label}</div>
                      <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
                        {values.length ? values.map((value) => (
                          <span key={value} className={"pill " + tone}><Icon name="alert-triangle" size={12} /> {value}</span>
                        )) : <span className="muted" style={{ fontSize: 13 }}>None recorded.</span>}
                      </div>
                    </div>
                  ))}
                  <div className="alert info" style={{ marginTop: 14 }}><Icon name="shield-check" size={16} /><div style={{ fontSize: 12.5 }}>These values come from the saved recipe allergen profile and linked master ingredients.</div></div>
                </div>
              );
            })()}

            {tab === "dietary" && (() => {
              const live = r.compliance && typeof r.compliance === "object" ? r.compliance : null;
              const readiness = live && live.recipe_readiness && typeof live.recipe_readiness === "object" ? live.recipe_readiness : {};
              const readinessChecks = Array.isArray(readiness.checks) ? readiness.checks : [];
              const rules = live && Array.isArray(live.checks) ? live.checks : [];
              const statusTone = (status) => status === "passed" || status === "pass" ? "success" : status === "blocking" || status === "fail" ? "error" : status === "warning" || status === "warn" ? "warning" : "neutral";
              const observed = (value) => {
                if (value == null || value === "") return "No evidence saved";
                if (Array.isArray(value)) return value.length ? value.join(", ") : "None recorded";
                if (typeof value === "object") return Object.entries(value).map(([key, item]) => key.replace(/_/g, " ") + ": " + (Array.isArray(item) ? (item.join(", ") || "none") : String(item == null ? "—" : item))).join(" · ");
                return String(value);
              };
              return (
                <div className="card pad">
                  <div style={{ display: "flex", alignItems: "flex-start", justifyContent: "space-between", gap: 12, marginBottom: 14 }}>
                    <div>
                      <h3 style={{ fontFamily: "var(--serif)", fontSize: 20, margin: 0 }}>Compliance investigation</h3>
                      <p className="muted" style={{ margin: "5px 0 0", fontSize: 12.5 }}>Exact latest persisted checks for this recipe.</p>
                    </div>
                    <span className={"pill " + (!live ? "neutral" : live.ready ? "success" : live.blocking ? "error" : "warning")}>
                      {live && live.score != null ? live.score + "%" : "Not checked"}
                    </span>
                  </div>
                  {live ? (
                    <React.Fragment>
                      <div className="ingrev-grid">
                        {[["Passed", live.passed, "ok"], ["Warnings", live.warnings, "warn"], ["Blocking", live.blocking, "bad"], ["Not run", live.not_run, ""]].map(([label, value, tone]) => (
                          <div key={label} className="ingrev-cell"><b className={tone}>{value == null ? "—" : value}</b><span>{label}</span></div>
                        ))}
                      </div>
                      {(live.stale || live.not_run > 0) && (
                        <div className="alert warning" style={{ marginTop: 14 }}><Icon name="alert-triangle" size={16} /><div style={{ fontSize: 12.5 }}>{live.stale ? "Recipe evidence changed after the latest check. Re-run compliance." : "At least one active rule has not run."}</div></div>
                      )}
                      <div className="ingn-sec">
                        <div className="ingn-sec-h">Active organization rules</div>
                        <div className="col" style={{ gap: 8 }}>
                          {rules.length ? rules.map((rule, index) => (
                            <div key={rule.id || rule.rule + index} className="live-evidence-row">
                              <div><b>{rule.rule}</b><span>{rule.rule_type || "rule"} · {rule.updated_at ? formatDate(rule.updated_at) : "not run"}</span></div>
                              <span className={"pill " + statusTone(rule.status)}>{String(rule.status || "not run").replace(/_/g, " ")}</span>
                            </div>
                          )) : <p className="muted" style={{ margin: 0 }}>No active compliance rules were returned.</p>}
                        </div>
                      </div>
                      <div className="ingn-sec">
                        <div className="ingn-sec-h">Recipe evidence checks</div>
                        <div className="col" style={{ gap: 8 }}>
                          {readinessChecks.length ? readinessChecks.map((check) => (
                            <div key={check.key} className="live-readiness">
                              <span className={"pill " + statusTone(check.status)}>{check.status === "pass" ? "Pass" : "Issue"}</span>
                              <div><b>{check.label}</b><span>{observed(check.observed)}</span>{check.fix && check.status !== "pass" ? <em>Fix: {check.fix}</em> : null}</div>
                            </div>
                          )) : <p className="muted" style={{ margin: 0 }}>No recipe-readiness evidence has been evaluated yet.</p>}
                        </div>
                      </div>
                      <div className="ingn-sec">
                        <div className="ingn-sec-h">Cross-contamination disclosure</div>
                        <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
                          {(readiness.cross_contamination || []).length
                            ? readiness.cross_contamination.map((item) => <span key={item} className="pill warning">{item}</span>)
                            : <span className="muted" style={{ fontSize: 13 }}>No may-contain allergens recorded.</span>}
                        </div>
                      </div>
                      <p className="muted" style={{ margin: "16px 0 0", fontSize: 11.5 }}>Source: {String(live.source || "persisted checks").replace(/_/g, " ")} · counted {live.counted_at ? formatDate(live.counted_at) : "now"}</p>
                    </React.Fragment>
                  ) : <p className="muted">No persisted compliance result is available. The interface does not estimate a score.</p>}
                </div>
              );
            })()}

            {tab === "tags" && (
              <div className="card pad">
                <h3 style={{ fontFamily: "var(--serif)", fontSize: 20, margin: "0 0 14px" }}>Health &amp; dietary tags</h3>
                <div style={{ display: "flex", flexWrap: "wrap", gap: 8 }}>
                  {(r.healthTags || r.tags || []).length
                    ? (r.healthTags || r.tags).map((tag) => <span key={tag} className="pill brand">{tag}</span>)
                    : <span className="muted" style={{ fontSize: 13 }}>No health tags were submitted.</span>}
                </div>
              </div>
            )}

            {tab === "references" && (
              <div className="card pad">
                <h3 style={{ fontFamily: "var(--serif)", fontSize: 20, margin: "0 0 14px" }}>References &amp; documents</h3>
                <div className="col" style={{ gap: 8 }}>
                  {[["USDA FoodData Central", "Nutrition source"], ["Recipe origin notes.pdf", "Document · 248 KB"], ["FDA labeling guidance", "Compliance ref"]].map(([n, d]) => (
                    <div key={n} style={{ display: "flex", alignItems: "center", gap: 12, padding: "10px 12px", border: "1px solid var(--gray-200)", borderRadius: 10 }}>
                      <Icon name="file-text" size={16} /><div style={{ flex: 1 }}><div style={{ fontSize: 13.5, fontWeight: 600 }}>{n}</div><div className="muted" style={{ fontSize: 12 }}>{d}</div></div><Icon name="external-link" size={15} />
                    </div>
                  ))}
                </div>
              </div>
            )}

            {tab === "versions" && <RecipeVersionsTab recipe={r} role={role} toast={toast} />}
          </div>
        </div>

        {/* RIGHT */}
        <div className="col" style={{ gap: 18 }}>
          {/* Contributor */}
          <div className="card pad">
            <h3 style={{ fontFamily: "var(--serif)", fontSize: 18, margin: "0 0 12px" }}>Contributor performance</h3>
            {(() => {
              const performance = r.contributorPerformance && typeof r.contributorPerformance === "object" ? r.contributorPerformance : null;
              const metric = (key) => performance && performance[key] != null ? performance[key] : "—";
              const rate = performance && performance.approval_rate != null ? Number(performance.approval_rate) : null;
              return (
                <React.Fragment>
                  <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
                    <div className="avatar lg" style={{ background: "var(--gray-100)", color: "var(--gray-700)" }}>{r.contributor.initials}</div>
                    <div>
                      <div style={{ fontWeight: 700 }}>{r.contributor.name}</div>
                      <div className="muted" style={{ fontSize: 13 }}>
                        {performance ? performance.role : "Contributor"}
                        {performance && performance.org_rank != null ? " · Org ranking #" + performance.org_rank + " of " + performance.ranked_contributors : ""}
                      </div>
                    </div>
                  </div>
                  <div className="cperf-grid">
                    {[["Submitted", metric("submitted"), ""], ["Published", metric("published"), "ok"], ["Rejected", metric("rejected"), "bad"], ["Returned", metric("returned"), "warn"]].map(([label, value, tone]) => (
                      <div key={label} className="cperf-cell"><b className={tone}>{value}</b><span>{label}</span></div>
                    ))}
                  </div>
                  <div className="cperf-rate">
                    <div className="cperf-rate-top"><span>Approval rate</span><b>{rate == null ? "—" : rate + "%"}</b></div>
                    <div className="progress" style={{ background: "var(--gray-100)" }}><i style={{ width: (rate == null ? 0 : Math.max(0, Math.min(100, rate))) + "%", background: "var(--green-600)" }} /></div>
                  </div>
                  <div className="cperf-meta">
                    <span><Icon name="clock" size={12} /> Avg review {performance && performance.average_review_hours != null ? performance.average_review_hours + " hrs" : "—"}</span>
                    <span><Icon name="timer" size={12} /> {r.timeSpent} on this item</span>
                  </div>
                  <p className="muted" style={{ fontSize: 11.5, margin: "10px 0 0" }}>
                    {performance ? "Counted from live recipe and audit records" + (performance.counted_at ? " · " + formatDate(performance.counted_at) : "") : "No live contributor totals returned."}
                  </p>
                </React.Fragment>
              );
            })()}
          </div>

          {/* Ingredient review summary */}
          <div className="card pad">
            <h3 style={{ fontFamily: "var(--serif)", fontSize: 18, margin: "0 0 12px" }}>Ingredient review</h3>
            {(() => {
              const items = Array.isArray(r.lineItems) ? r.lineItems : [];
              const linked = items.filter((item) => !!(item && item.ingredient)).length;
              const canonical = items.filter((item) => !!(item && item.canonical_name)).length;
              const pending = items.filter((item) => item && item.ingredient_status && !["verified", "published", "active"].includes(String(item.ingredient_status).toLowerCase())).length;
              const confidence = Number.isFinite(Number(r.confidence)) ? Number(r.confidence) : null;
              const blocking = r.compliance && r.compliance.blocking != null ? Number(r.compliance.blocking) : null;
              const allergens = Array.from(new Set([...(r.allergens || []), ...(r.detectedAllergens || []), ...(r.precautionaryAllergens || [])]));
              return (
                <React.Fragment>
                  <div className="ingrev-grid">
                    {[["Canonical match", canonical + " / " + items.length, canonical === items.length && items.length ? "ok" : ""], ["Linked ingredients", linked + " / " + items.length, linked === items.length && items.length ? "ok" : ""], ["Pending", pending, pending ? "warn" : "ok"], ["Unlinked", items.length - linked, items.length === linked ? "ok" : "warn"], ["Blocking rules", blocking == null ? "—" : blocking, blocking ? "bad" : blocking === 0 ? "ok" : ""], ["Loraa confidence", confidence == null ? "—" : confidence + "%", confidence != null && confidence >= 80 ? "ok" : confidence != null ? "warn" : ""]].map(([label, value, tone]) => (
                      <div key={label} className="ingrev-cell"><b className={tone}>{value}</b><span>{label}</span></div>
                    ))}
                  </div>
                  <div className="ingrev-allerg">
                    <span className="ingrev-lbl">Allergen evidence</span>
                    {allergens.length ? allergens.map((allergen) => <span key={allergen} className="pill warning" style={{ fontSize: 11 }}>{allergen}</span>) : <span className="muted" style={{ fontSize: 12 }}>None recorded</span>}
                  </div>
                </React.Fragment>
              );
            })()}
          </div>

          {/* Review timeline */}
          <div className="card pad">
            <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 8 }}>
              <h3 style={{ fontFamily: "var(--serif)", fontSize: 18, margin: 0 }}>Review timeline</h3>
              <span className="pill neutral">{feedback.length} comments</span>
            </div>
            <WorkflowTimeline flow={flow} status={r.status} />
            <hr className="divider" />
            {feedback.length > 0 ? feedback.map((f, i) => {
              const isResolved = resolved.has(i);
              return (
              <div key={i} className="comment" style={isResolved ? { opacity: .62 } : {}}>
                <div className="avatar sm" style={{ background: "var(--gray-100)", color: "var(--gray-700)" }}>{f.initials}</div>
                <div className="body">
                  <div>
                    <span className="who">{f.from}</span>
                    <span className="when">{f.when}</span>
                  </div>
                  <div className="text" style={isResolved ? { textDecoration: "line-through", textDecorationColor: "var(--gray-400)" } : {}}>{f.text}</div>
                  <div className="quote">on <strong>{f.on}</strong></div>
                  <div style={{ display: "flex", alignItems: "center", gap: 8, marginTop: 8 }}>
                    {isResolved
                      ? <><span className="pill" style={{ background: "var(--green-50)", color: "var(--green-700)", border: "1px solid var(--green-200)" }}><Icon name="check" size={11} stroke={2.6} /> Resolved</span>
                          <button className="btn ghost sm" onClick={() => toggleResolve(i)} style={{ padding: "2px 8px", fontSize: 12 }}>Undo</button></>
                      : <button className="btn secondary sm" onClick={() => { toggleResolve(i); toast("Comment marked resolved"); }} style={{ padding: "4px 10px", fontSize: 12 }}><Icon name="check" size={13} /> Mark resolved</button>}
                  </div>
                </div>
              </div>
            );}) : (
              <div className="muted" style={{ fontSize: 13, padding: "8px 0" }}>No feedback yet.</div>
            )}

            <hr className="divider" />
            <textarea className="textarea" rows="3" placeholder="Leave a comment…" value={comment} onChange={(e) => setComment(e.target.value)} />
            <div style={{ display: "flex", justifyContent: "flex-end", gap: 8, marginTop: 8 }}>
              <button className="btn sm primary" disabled={!comment.trim()} onClick={() => { toast("Comment posted"); setComment(""); }}>
                <Icon name="send" size={14} /> Post
              </button>
            </div>
          </div>
        </div>
      </div>

      <ReviewCompleteModal open={modal === "complete"} flow={flow} onClose={() => setModal(null)} onConfirm={confirmComplete} onRequestChanges={requestChanges} />

      <Modal open={modal === "delete"} onClose={() => setModal(null)} title="Delete published recipe" subtitle="This removes the live recipe from the public catalog." footer={
        <>
          <button className="btn ghost" onClick={() => setModal(null)}>Cancel</button>
          <button className="btn danger" onClick={() => { setModal(null); window.wePatchItem("recipe", r.id, { status: "draft" }); toast(`Deleted “${r.name}”`); setActiveRecipe(null); setPage("recipes"); }}><Icon name="trash-2" size={14} /> Delete recipe</button>
        </>
      }>
        <div className="alert error" style={{ marginBottom: 14 }}>
          <Icon name="alert-triangle" size={18} />
          <div><strong>Admin action</strong><div style={{ marginTop: 2 }}>Published content is normally locked. Deleting it will unpublish and remove it from every locale.</div></div>
        </div>
      </Modal>

      <Modal open={modal === "approve"} onClose={() => setModal(null)} title="Approve & Publish" subtitle="Push this recipe live across all locales." footer={
        <>
          <button className="btn ghost" onClick={() => setModal(null)}>Cancel</button>
          <button className="btn primary" onClick={() => { setModal(null); toast("Recipe published"); setPage("recipes"); setActiveRecipe(null); }}><Icon name="globe" size={14} /> Publish now</button>
        </>
      }>
        <div className="alert success" style={{ marginBottom: 14 }}>
          <Icon name="shield-check" size={18} />
          <div><strong>Compliance passed</strong><div style={{ marginTop: 2 }}>All required fields are present. Nutrition macros verified by Eve Nakamura.</div></div>
        </div>
        <div className="field"><label>Publish to locales</label>
          <div style={{ display: "flex", flexWrap: "wrap", gap: 6 }}>
            {LANGUAGES.map((l) => <span key={l.code} className="pill brand">{l.flag} {l.label}</span>)}
          </div>
        </div>
      </Modal>

      <Modal open={modal === "reject"} onClose={() => setModal(null)} title="Reject Recipe" subtitle="The contributor will be notified with your reason." footer={
        <>
          <button className="btn ghost" onClick={() => setModal(null)}>Cancel</button>
          <button className="btn danger" onClick={() => { setModal(null); toast("Recipe rejected"); }}><Icon name="x" size={14} /> Reject</button>
        </>
      }>
        <div className="alert error" style={{ marginBottom: 14 }}>
          <Icon name="alert-triangle" size={18} />
          <div><strong>This action can be reversed</strong><div style={{ marginTop: 2 }}>Rejected recipes move to the contributor's drafts.</div></div>
        </div>
        <div className="field"><label>Reason</label>
          <textarea className="textarea" rows="3" placeholder="Explain why you're rejecting…" />
        </div>
      </Modal>

      <Modal open={modal === "urgent"} onClose={() => setModal(null)} title="Mark for urgent review" subtitle="Escalate this recipe so the right reviewer looks at it today." footer={
        <>
          <button className="btn ghost" onClick={() => setModal(null)}>Cancel</button>
          <button className="btn primary" disabled={!urgentTo} onClick={() => {
            const p = urgentPeople.find((x) => x.initials === urgentTo);
            urgentAdd({ key: "u-" + Date.now(), kind: "recipe", rid: r.id, name: r.name, sub: `${r.cuisine} · ${r.category}`, cover: r.cover,
              toInitials: p ? p.initials : urgentTo, toName: p ? p.name : urgentTo, toRole: p ? p.role : null,
              byName: me.name, byInitials: me.initials, reason: urgentNote.trim() || "Marked for urgent review", when: new Date().toISOString().slice(0, 10), severity: "high" });
            setModal(null); toast(`Escalated to ${p ? p.name : "reviewer"}, added to Urgent review`);
          }}><Icon name="alert-triangle" size={14} /> Escalate</button>
        </>
      }>
        <div className="alert warning" style={{ marginBottom: 14 }}>
          <Icon name="alert-triangle" size={18} />
          <div><strong>Sets priority to high</strong><div style={{ marginTop: 2 }}>It moves to the top of the chosen reviewer's queue and appears under Urgent review.</div></div>
        </div>
        <div className="field">
          <label>Escalate to <span className="muted" style={{ fontWeight: 500 }}>· available to {ROLES[role] ? ROLES[role].label : role}</span></label>
          <div className="col" style={{ gap: 6, marginTop: 4 }}>
            {urgentPeople.map((p) => (
              <button key={p.id} type="button" className="kb-assignee-opt" onClick={() => setUrgentTo(p.initials)} style={{ border: `1.5px solid ${urgentTo === p.initials ? "var(--green-400)" : "var(--gray-200)"}`, background: urgentTo === p.initials ? "var(--green-50)" : "#fff", borderRadius: 10 }}>
                <span className="avatar sm" style={{ background: "var(--gray-100)", color: "var(--gray-700)" }}>{p.initials}</span>
                <span style={{ flex: 1, textAlign: "left", minWidth: 0 }}>
                  <span style={{ display: "block", fontSize: 14, fontWeight: 700 }}>{p.name}</span>
                  <span className="muted" style={{ fontSize: 12 }}>{ROLES[p.role] ? ROLES[p.role].label : p.role} · {p.team}</span>
                </span>
                {urgentTo === p.initials && <Icon name="check" size={16} style={{ color: "var(--green-700)", flexShrink: 0 }} />}
              </button>
            ))}
            {urgentPeople.length === 0 && <span className="muted" style={{ fontSize: 13 }}>No reviewers available to escalate to from your role.</span>}
          </div>
        </div>
        <div className="field" style={{ marginTop: 12 }}><label>Why is this urgent? <span className="muted" style={{ fontWeight: 500 }}>· optional</span></label>
          <textarea className="textarea" rows="3" value={urgentNote} onChange={(e) => setUrgentNote(e.target.value)} placeholder="e.g. SLA breach, allergen risk, publishing today…" />
        </div>
      </Modal>
    </div>
  );
}

function KeyValue({ items, grid }) {
  return (
    <div style={{ display: grid ? "grid" : "flex", gridTemplateColumns: grid ? "1fr 1fr" : undefined, flexDirection: grid ? undefined : "column", gap: grid ? 14 : 8 }}>
      {items.map((it, i) => (
        <div key={i} style={{ display: "flex", justifyContent: "space-between", gap: 14, fontSize: 14 }}>
          <span className="muted" style={{ fontWeight: 500 }}>{it.k}</span>
          <span style={{ fontWeight: 600, color: "var(--text-primary)", textAlign: "right" }}>{it.v}</span>
        </div>
      ))}
    </div>
  );
}

function CompletionBar({ pct }) {
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 8, minWidth: 140 }}>
      <div className="progress" style={{ flex: 1 }}><i style={{ width: `${pct}%` }} /></div>
      <span style={{ fontSize: 12, fontWeight: 700 }}>{pct}%</span>
    </div>
  );
}

function Timeline({ status }) {
  // Linear status path
  const steps = ["draft", "pending-review", "compliance-review", "approved", "published"];
  // Special: rejected, changes-requested branch, render as warning state on the current step
  const isRejected = status === "rejected";
  const isChanges = status === "changes-requested";
  const currentIdx = (() => {
    if (isRejected || isChanges) return 2;
    return Math.max(0, steps.indexOf(status));
  })();
  return (
    <div style={{ display: "flex", flexDirection: "column", gap: 8 }}>
      {steps.map((s, i) => {
        const done = i < currentIdx;
        const cur = i === currentIdx;
        const label = STATUS_PILL[s]?.label || s;
        const icon = done ? "check" : cur ? (isRejected ? "x" : isChanges ? "message-square" : "circle-dot") : "circle";
        const color = isRejected && cur ? "var(--error-600)" : isChanges && cur ? "#5925DC" : done ? "var(--green-700)" : cur ? "var(--green-700)" : "var(--gray-400)";
        const bg = isRejected && cur ? "var(--error-50)" : isChanges && cur ? "#F4F3FF" : done || cur ? "var(--green-50)" : "var(--gray-50)";
        return (
          <div key={s} style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <div style={{ width: 24, height: 24, borderRadius: 999, background: bg, display: "grid", placeItems: "center", color, border: `1px solid ${cur ? color : "transparent"}` }}>
              <Icon name={icon} size={12} stroke={2.5} />
            </div>
            <div style={{ flex: 1, fontSize: 13, fontWeight: cur ? 700 : 500, color: done || cur ? "var(--text-primary)" : "var(--gray-500)" }}>{label}</div>
            {cur && isRejected && <span className="pill error">rejected here</span>}
            {cur && isChanges && <span className="pill violet">changes asked</span>}
          </div>
        );
      })}
    </div>
  );
}

function NutritionPanel({ r }) {
  const [info, setInfo] = React.useState(null);
  const val = (keys) => { for (const k of keys) { if (r[k] != null && r[k] !== "") return r[k]; } return null; };
  const fmt = (v, u) => v == null ? <span className="ingn-na">—</span> : <span>{v}<em>{u}</em></span>;
  const macroBars = [
    { key: "Protein", value: val(["protein"]), total: 65, color: "#1E7A49", ic: "beef" },
    { key: "Carbs", value: val(["carbs"]), total: 300, color: "#E0902A", ic: "wheat" },
    { key: "Fat", value: val(["fat"]), total: 70, color: "#6938EF", ic: "droplet" },
  ];
  const SECTIONS = [
    { title: "Macronutrients", items: [
      ["Calories", val(["calories", "kcal"]), "kcal", "#F04438", "flame"],
      ["Protein", val(["protein", "p"]), "g", "#1E7A49", "beef"],
      ["Carbohydrates", val(["carbs", "c"]), "g", "#E0902A", "wheat"],
      ["Total fat", val(["fat", "f"]), "g", "#6938EF", "droplet"],
      ["Fibre", val(["fiber", "fibre"]), "g", "#0E9384", "sprout"],
      ["Sugar", val(["sugar", "sugars"]), "g", "#EC4899", "candy"],
    ]},
    { title: "Fatty acids", items: [
      ["Saturated", val(["satFat", "saturated"]), "g", "#A855F7", "droplets"],
      ["Monounsaturated", val(["monoFat", "mono"]), "g", "#8B5CF6", "droplets"],
      ["Polyunsaturated", val(["polyFat", "poly"]), "g", "#7C3AED", "droplets"],
      ["Trans fat", val(["transFat", "trans"]), "g", "#DB2777", "droplets"],
      ["Cholesterol", val(["cholesterol"]), "mg", "#9333EA", "activity"],
    ]},
    { title: "Micronutrients", items: [
      ["Sodium", val(["sodium"]), "mg", "#0EA5B7", "waves"],
      ["Potassium", val(["potassium"]), "mg", "#0891B2", "zap"],
      ["Calcium", val(["calcium"]), "mg", "#64748B", "bone"],
      ["Iron", val(["iron"]), "mg", "#B45309", "magnet"],
      ["Vitamin C", val(["vitC", "vitaminC"]), "mg", "#F59E0B", "citrus"],
      ["Vitamin A", val(["vitA", "vitaminA"]), "µg", "#EA580C", "carrot"],
      ["Vitamin D", val(["vitD", "vitaminD"]), "µg", "#EAB308", "sun"],
    ]},
  ];
  const EDU = (typeof window !== "undefined" && window.NUTRIENT_EDU) || {};
  const kcal = val(["calories", "kcal"]);
  const hasNutrition = [kcal, val(["protein", "p"]), val(["carbs", "c"]), val(["fat", "f"])].some((value) => value != null);
  const nutritionSource = r.nutritionSource || (r.nutrition && r.nutrition.source) || null;
  return (
    <div className="card pad">
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 16 }}>
        <h3 style={{ fontFamily: "var(--serif)", fontSize: 20, margin: 0 }}>Nutrition · per serving</h3>
        <span className={"pill " + (hasNutrition ? "success" : "neutral")}><Icon name={hasNutrition ? "shield-check" : "info"} size={12} stroke={2.4} /> {hasNutrition ? (nutritionSource ? String(nutritionSource).replace(/_/g, " ") : "Saved nutrition") : "Nutrition missing"}</span>
      </div>
      <div className="ingn-hero">
        <div className="energy-summary">
          <span className={"energy-pill" + (kcal == null ? " empty" : "")}><Icon name="flame" size={15} stroke={2.3} /><span>Energy</span><b>{kcal == null ? "—" : kcal}</b><em>kcal / serving</em></span>
          <CalorieRing kcal={kcal} />
        </div>
        <div className="ingn-bars">
          {macroBars.map((m) => (
            <div key={m.key} className="ingn-bar">
              <div className="ingn-bar-top">
                <span className="ingn-bar-k"><span className="ingn-bar-ic" style={{ background: m.color + "1A", color: m.color }}><Icon name={m.ic} size={12} stroke={2.4} /></span>{m.key}</span>
                <span className="ingn-bar-v">{m.value == null ? "—" : m.value + "g"} <em>/ {m.total}g rec.</em></span>
              </div>
              <div className="progress" style={{ background: "var(--gray-100)" }}>
                <i style={{ width: `${m.value == null ? 0 : Math.min(100, (m.value / m.total) * 100)}%`, background: m.color }} />
              </div>
            </div>
          ))}
        </div>
      </div>
      {(() => {
        const pValue = val(["protein", "p"]), cValue = val(["carbs", "c"]), fValue = val(["fat", "f"]);
        const complete = [pValue, cValue, fValue].every((value) => value != null && Number.isFinite(Number(value)));
        const p = complete ? Number(pValue) : 0, c = complete ? Number(cValue) : 0, f = complete ? Number(fValue) : 0;
        const pk = p * 4, ck = c * 4, fk = f * 9, totalMacroKcal = pk + ck + fk;
        const available = complete && totalMacroKcal > 0;
        const pctP = available ? Math.round(pk / totalMacroKcal * 100) : 0;
        const pctF = available ? Math.round(fk / totalMacroKcal * 100) : 0;
        const pctC = available ? 100 - pctP - pctF : 0;
        return (
          <div className="ingn-sec">
            <div className="ingn-sec-h">Energy distribution</div>
            <div className={"enddist-bar" + (available ? "" : " empty")} aria-label={available ? "Live energy distribution from saved macros" : "Energy distribution unavailable"}>
              {available ? (
                <React.Fragment>
                  <span style={{ width: pctP + "%", background: "#1E7A49" }} title={"Protein " + pctP + "%"} />
                  <span style={{ width: pctF + "%", background: "#6938EF" }} title={"Fat " + pctF + "%"} />
                  <span style={{ width: pctC + "%", background: "#E0902A" }} title={"Carbs " + pctC + "%"} />
                </React.Fragment>
              ) : <span className="enddist-empty-fill" />}
            </div>
            {available ? (
              <div className="enddist-legend">
                <span><i style={{ background: "#1E7A49" }} /> Protein <b>{pctP}%</b></span>
                <span><i style={{ background: "#6938EF" }} /> Fat <b>{pctF}%</b></span>
                <span><i style={{ background: "#E0902A" }} /> Carbs <b>{pctC}%</b></span>
                <span className="enddist-tot">{Math.round(totalMacroKcal)} kcal from saved macros</span>
              </div>
            ) : <p className="muted" style={{ fontSize: 12.5, margin: "8px 0 0" }}>Protein, carbohydrate, and fat must all be saved before the distribution can be calculated.</p>}
          </div>
        );
      })()}
      {SECTIONS.map((sec) => (
        <div key={sec.title} className="ingn-sec">
          <div className="ingn-sec-h">{sec.title}</div>
          <div className="ingn-grid">
            {sec.items.map(([k, v, u, c, ic]) => (
              <button key={k} type="button" disabled={v == null} className={"ingn-chip" + (v == null ? " na" : " clickable")} style={v != null ? { background: c + "10", borderColor: c + "2E" } : null}
                onClick={() => { if (v != null) setInfo({ k, v, u, c, ic }); }}>
                <span className="ingn-chip-ic" style={{ background: (v != null ? c : "#98A2B3") + "1F", color: v != null ? c : "#98A2B3" }}><Icon name={ic} size={13} stroke={2.2} /></span>
                <span className="ingn-chip-tx"><b style={v != null ? { color: c } : null}>{fmt(v, u)}</b><span>{k}</span></span>
              </button>
            ))}
          </div>
        </div>
      ))}
      <div className={"alert " + (hasNutrition ? "info" : "warning")} style={{ marginTop: 6 }}>
        <Icon name={hasNutrition ? "info" : "alert-triangle"} size={18} />
        <div><strong>{hasNutrition ? "Live saved values." : "No serving profile is attached."}</strong><div style={{ marginTop: 2 }}>{hasNutrition ? "Values are returned by the recipe nutrition profile and shown per serving." : "Add or calculate nutrition before relying on this panel."}</div></div>
      </div>

      {info && (() => {
        const edu = EDU[info.k] || { what: "A tracked nutrient in this recipe.", why: "Contributes to the recipe's overall nutrition profile.", who: "Considered across the organization's dietary programs." };
        return ReactDOM.createPortal((
          <div className="nutedu-scrim" onMouseDown={(e) => { if (e.target === e.currentTarget) setInfo(null); }}>
            <div className="nutedu-drawer" style={{ "--c": info.c }}>
              <div className="nutedu-head" style={{ background: info.c + "12" }}>
                <span className="nutedu-ic" style={{ background: info.c + "22", color: info.c }}><Icon name={info.ic} size={20} stroke={2.2} /></span>
                <div className="nutedu-head-tx">
                  <span className="nutedu-k">{info.k}</span>
                  <span className="nutedu-v" style={{ color: info.c }}>{info.v}{info.u} <em>per serving</em></span>
                </div>
                <button className="nutedu-x" onClick={() => setInfo(null)}><Icon name="x" size={18} /></button>
              </div>
              <div className="nutedu-body">
                <div className="nutedu-sec"><div className="nutedu-sec-h" style={{ color: info.c }}><Icon name="info" size={14} stroke={2.2} /> What it is</div><p>{edu.what}</p></div>
                <div className="nutedu-sec"><div className="nutedu-sec-h" style={{ color: info.c }}><Icon name="heart-pulse" size={14} stroke={2.2} /> Why it matters</div><p>{edu.why}</p></div>
                <div className="nutedu-sec"><div className="nutedu-sec-h" style={{ color: info.c }}><Icon name="users" size={14} stroke={2.2} /> Who it affects</div><p>{edu.who}</p></div>
                {edu.dv && <div className="nutedu-facts">
                  <div className="nutedu-fact"><span><Icon name="target" size={13} /> Daily value</span><b>{edu.dv}</b></div>
                  {edu.sources && <div className="nutedu-fact"><span><Icon name="apple" size={13} /> Food sources</span><b>{edu.sources}</b></div>}
                </div>}
                {edu.low && <div className="nutedu-sec"><div className="nutedu-sec-h" style={{ color: "#B54708" }}><Icon name="trending-down" size={14} stroke={2.2} /> Too little</div><p>{edu.low}</p></div>}
                {edu.high && <div className="nutedu-sec"><div className="nutedu-sec-h" style={{ color: "#B54708" }}><Icon name="trending-up" size={14} stroke={2.2} /> Too much</div><p>{edu.high}</p></div>}
                {edu.policy && <div className="nutedu-policy"><Icon name="shield-check" size={14} stroke={2.2} /><span><b>Compliance relevance.</b> {edu.policy}</span></div>}
                <div className="nutedu-inhere" style={{ borderColor: info.c + "33", background: info.c + "0A" }}>
                  <Icon name="flask-conical" size={15} stroke={2.2} style={{ color: info.c }} />
                  <span><b>{r.name}</b> provides <b style={{ color: info.c }}>{info.v}{info.u}</b> of {info.k.toLowerCase()} per serving — curated &amp; dietitian-reviewed.</span>
                </div>
              </div>
            </div>
          </div>
        ), document.body);
      })()}
    </div>
  );
}

/* ───────── Recipe Versions tab (PRD §8) ───────── */
function RvStatusPill({ status }) {
  const m = (typeof RV_STATUS !== "undefined" && RV_STATUS[status]) || { label: status, tone: "neutral" };
  return <span className={`pill ${m.tone}`} style={{ fontSize: 11 }}>{m.label}</span>;
}
function RecipeVersionsTab({ recipe, role, toast }) {
  const [, bump] = React.useState(0);
  React.useEffect(() => {
    const h = () => bump((n) => n + 1);
    window.addEventListener("nutridms-versions", h);
    return () => window.removeEventListener("nutridms-versions", h);
  }, []);
  const versions = (typeof rvLoad === "function") ? rvLoad(recipe) : [];
  const ordered = versions.slice().reverse(); // newest first
  const [cmp, setCmp] = React.useState([]);   // selected version ids to compare
  const [snapFor, setSnapFor] = React.useState(null);
  const canLock = ["compliance", "admin", "super-admin"].includes(role);
  const canManage = ["dietitian", "manager", "compliance", "admin", "super-admin"].includes(role);

  const toggleCmp = (id) => setCmp((c) => c.includes(id) ? c.filter((x) => x !== id) : c.length >= 2 ? [c[1], id] : [...c, id]);
  const cmpA = versions.find((v) => v.id === cmp[0]);
  const cmpB = versions.find((v) => v.id === cmp[1]);
  const diff = (cmpA && cmpB && typeof rvCompare === "function") ? rvCompare(cmpA, cmpB) : null;

  const act = (fn, msg) => { fn(); toast(msg); };

  return (
    <div className="card pad rv-tab">
      <div className="rv-head">
        <div>
          <h3 style={{ fontFamily: "var(--serif)", fontSize: 20, margin: 0 }}>Version history</h3>
          <p className="rv-sub">Every change forks a traceable version. Approved labels stay tied to locked versions.</p>
        </div>
        {cmp.length === 2 && <button className="btn ghost sm" onClick={() => setCmp([])}><Icon name="x" size={13} /> Clear compare</button>}
      </div>

      {diff && (
        <div className="rv-compare">
          <div className="rv-compare-h"><Icon name="git-compare" size={15} /> Comparing <b>{cmpA.n}</b> ↔ <b>{cmpB.n}</b></div>
          <table className="rv-difftable">
            <thead><tr><th>Field</th><th>{cmpA.n}</th><th>{cmpB.n}</th></tr></thead>
            <tbody>
              {diff.map((d) => (
                <tr key={d.field} className={d.changed ? "changed" : ""}>
                  <td className="rv-diff-f">{d.field}</td><td>{d.a}</td><td>{d.b}</td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}

      <ol className="rv-timeline">
        {ordered.map((v) => (
          <li key={v.id} className={`rv-node ${v.current ? "current" : ""} ${v.status}`}>
            <div className="rv-node-dot"><Icon name={v.status === "locked" ? "lock" : v.status === "approved" ? "check" : v.status === "archived" ? "archive" : "circle-dot"} size={13} /></div>
            <div className="rv-node-body">
              <div className="rv-node-top">
                <span className="rv-node-n">{v.n}</span>
                <RvStatusPill status={v.status} />
                {v.current && <span className="rv-cur-badge">Current</span>}
                <span className="grow" />
                <label className="rv-cmp-check"><input type="checkbox" checked={cmp.includes(v.id)} onChange={() => toggleCmp(v.id)} /> Compare</label>
              </div>
              <div className="rv-node-event">{v.event}</div>
              <div className="rv-node-meta">{v.by} · {v.at}</div>
              {v.changes && v.changes.length > 0 && (
                <div className="rv-changes">
                  {v.changes.map((c, i) => <span key={i} className="rv-change"><b>{c.field}</b>: {c.from} → {c.to}</span>)}
                </div>
              )}
              <div className="rv-node-actions">
                {v.snapshot && <button className="rv-act" onClick={() => setSnapFor(v)}><Icon name="camera" size={13} /> Lock snapshot</button>}
                {canManage && <button className="rv-act" onClick={() => act(() => rvDuplicate(recipe, v), `Duplicated ${v.n} as new draft`)}><Icon name="copy" size={13} /> Duplicate as draft</button>}
                {canManage && v.status === "archived" && <button className="rv-act" onClick={() => act(() => rvRestore(recipe, v), `Restored ${v.n}`)}><Icon name="rotate-ccw" size={13} /> Restore</button>}
                {canLock && v.status === "approved" && <button className="rv-act lock" onClick={() => act(() => rvLock(recipe, v.id), `${v.n} locked`)}><Icon name="lock" size={13} /> Lock version</button>}
                {canManage && v.status !== "archived" && v.status !== "locked" && !v.current && <button className="rv-act" onClick={() => act(() => rvArchive(recipe, v.id), `${v.n} archived`)}><Icon name="archive" size={13} /> Archive</button>}
              </div>
            </div>
          </li>
        ))}
      </ol>

      {snapFor && <RvSnapshotModal version={snapFor} onClose={() => setSnapFor(null)} />}
    </div>
  );
}

function RvSnapshotModal({ version, onClose }) {
  const s = version.snapshot || {};
  const rows = [
    ["Recipe version", s.recipeVersion], ["Ingredient versions", s.ingredientVersions],
    ["Yield settings", s.yieldSettings], ["Retention settings", s.retentionSettings],
    ["Serving size", s.servingSize], ["DV table version", s.dvTable],
    ["Rounding version", s.roundingVersion], ["NFt template version", s.nftTemplateVersion],
    ["Allergen output", s.allergenOutput], ["Claims output", s.claimsOutput],
    ["FOP result", s.fopResult], ["Export files", s.exportFiles], ["Approval history", s.approvalHistory],
  ];
  return (
    <div className="rv-modal-scrim" onClick={onClose}>
      <div className="rv-modal" onClick={(e) => e.stopPropagation()}>
        <div className="rv-modal-h">
          <div><div className="rv-modal-t"><Icon name="lock" size={15} /> Version-lock snapshot · {version.n}</div><div className="rv-modal-sub">Immutable record stored when this version was locked (§24.3)</div></div>
          <button className="icon-btn" onClick={onClose}><Icon name="x" size={18} /></button>
        </div>
        <div className="rv-modal-body">
          {rows.map(([k, val]) => (
            <div key={k} className="rv-snap-row"><span className="rv-snap-k">{k}</span><span className="rv-snap-v">{val || "—"}</span></div>
          ))}
        </div>
      </div>
    </div>
  );
}

function CalorieRing({ kcal, unit = "kcal / serving", max = 2000 }) {
  const R = 56, C = 2 * Math.PI * R;
  const numeric = kcal != null && Number.isFinite(Number(kcal)) ? Number(kcal) : null;
  const pct = numeric == null ? 0 : Math.max(0, Math.min(100, (numeric / max) * 100));
  const dash = (pct / 100) * C;
  return (
    <svg width="150" height="150" viewBox="0 0 150 150" aria-label={numeric == null ? "Energy value unavailable" : numeric + " kilocalories per serving"}>
      <circle cx="75" cy="75" r={R} fill="none" stroke="var(--gray-100)" strokeWidth="14" />
      {numeric != null && pct > 0 ? <circle cx="75" cy="75" r={R} fill="none" stroke="var(--green-700)" strokeWidth="14"
        strokeDasharray={`${dash} ${C - dash}`} strokeLinecap="round"
        transform="rotate(-90 75 75)" /> : null}
      <text x="75" y="70" textAnchor="middle" fontFamily="Manrope" fontWeight="700" fontSize="26" fill="var(--text-primary)">{numeric == null ? "—" : numeric}</text>
      <text x="75" y="90" textAnchor="middle" fontFamily="Manrope" fontWeight="600" fontSize="10.5" fill="var(--gray-500)">{unit}</text>
    </svg>
  );
}

Object.assign(window, { RecipeDetail, Timeline, KeyValue, CalorieRing, CompletionBar });
